Add graph references

This commit is contained in:
Abdelrahman Said
2026-06-28 13:49:01 +01:00
parent 0a9807e448
commit a11edf0c53
2578 changed files with 868045 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,111 @@
/*
igraph library.
Copyright (C) 2021 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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"
IGRAPH_BEGIN_C_DECLS
igraph_error_t igraph_i_attribute_init(
igraph_t *graph, const igraph_attribute_record_list_t *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_int_t nv,
const igraph_attribute_record_list_t *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,
const igraph_attribute_record_list_t *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_get_type(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);
IGRAPH_END_C_DECLS
#endif /* IGRAPH_GRAPH_ATTRIBUTES_H */
@@ -0,0 +1,63 @@
/*
igraph library.
Copyright (C) 2005-2012 Gabor Csardi <csardi.gabor@gmail.com>
334 Harvard street, Cambridge, MA 02139 USA
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "igraph_datatype.h"
#include "igraph_types.h"
#include "igraph_interface.h"
#include "igraph_structural.h"
/**
* \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_int_t v1, igraph_int_t v2,
igraph_bool_t *res) {
igraph_int_t nov = igraph_vcount(graph);
igraph_int_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;
}
+214
View File
@@ -0,0 +1,214 @@
/*
igraph library.
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "igraph_interface.h"
#include "graph/caching.h"
#include <assert.h>
/****** 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 functions, 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.
*
* </para><para>
* Use <code>1 << IGRAPH_PROP_SOMETHING</code> 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;
}
@@ -0,0 +1,52 @@
/*
igraph library.
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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 <string.h> /* memset */
IGRAPH_BEGIN_C_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
);
IGRAPH_END_C_DECLS
#endif /* IGRAPH_CACHING_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
/*
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);
}
@@ -0,0 +1,32 @@
/*
igraph library.
Copyright (C) 2021 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef IGRAPH_GRAPH_INTERNAL_H
#define IGRAPH_GRAPH_INTERNAL_H
#include "igraph_datatype.h"
#include "igraph_decls.h"
#include "igraph_error.h"
IGRAPH_BEGIN_C_DECLS
igraph_error_t igraph_i_reverse(igraph_t *graph);
IGRAPH_END_C_DECLS
#endif /* IGRAPH_GRAPH_INTERNAL_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,243 @@
/*
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.
*
* </para><para>
* The most basic constructor, all the other constructors should call
* this to create a minimal graph object. Our use of the term "empty graph"
* in the above description should be distinguished from the mathematical
* definition of the empty or null graph. Strictly speaking, the empty or null
* graph in graph theory is the graph with no vertices and no edges. However
* by "empty graph" as used in \a 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_int_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.
*
* </para><para>
* This function changes the IDs of the vertices (except in some very
* special cases, but these should not be relied on anyway).
*
* </para><para>
* This function invalidates all iterators.
*
* \param graph The graph to work on.
* \param vertices The IDs of the vertices to remove, in a vector. The vector
* may contain the same ID more than once.
* \return Error code:
* \c IGRAPH_EINVVID: invalid vertex ID.
*
* Time complexity: O(|V|+|E|), |V| and |E| are the number of vertices and
* edges in the original graph.
*
* \example examples/simple/igraph_delete_vertices.c
*/
igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) {
return igraph_delete_vertices_map(graph, vertices, /* idx= */ 0, /* invidx= */ 0);
}
/**
* \function igraph_delete_vertices_idx
* \brief Removes some vertices (with all their edges) from the graph (deprecated alias).
*
* \deprecated-by igraph_delete_vertices_map 0.11.0
*/
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
) {
return igraph_delete_vertices_map(graph, vertices, idx, invidx);
}
/**
* \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_int_t. The tail (source) of
* the edge will be placed here.
* \param to Pointer to an \type igraph_int_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.</para><para>
*
* Time complexity: O(1).
*/
igraph_error_t igraph_edge(
const igraph_t *graph, igraph_int_t eid,
igraph_int_t *from, igraph_int_t *to
) {
if (eid < 0 || eid >= igraph_ecount(graph)) {
IGRAPH_ERROR("Cannot retrieve edge endpoints.", IGRAPH_EINVEID);
}
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.
* \param bycol Boolean constant. If true, the edges will be returned
* columnwise, e.g. the first edge is
* <code>res[0]->res[|E|]</code>, the second is
* <code>res[1]->res[|E|+1]</code>, etc. Supply false to get
* the edge list in a format compatible with \ref igraph_add_edges().
* \return Error code.
* \sa \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_bool_t bycol
) {
igraph_eit_t eit;
igraph_int_t n, ptr = 0, ptr2;
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 (bycol) {
ptr2 = n;
if (igraph_is_directed(graph)) {
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
igraph_int_t e = IGRAPH_EIT_GET(eit);
VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
VECTOR(*edges)[ptr2++] = IGRAPH_TO(graph, e);
}
} else {
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
igraph_int_t e = IGRAPH_EIT_GET(eit);
VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
VECTOR(*edges)[ptr2++] = IGRAPH_FROM(graph, e);
}
}
} else {
if (igraph_is_directed(graph)) {
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
igraph_int_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_int_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.
*
* </para><para>
* 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.
*
* </para><para>
* 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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,661 @@
/*
igraph library.
Copyright (C) 2006-2023 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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.
*
* </para><para>
* If not all vertices can be reached from the supplied root vertex,
* then additional root vertices will be used, in the order of their
* vertex IDs.
*
* </para><para>
* 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 Boolean, 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_int_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_int_t no_of_nodes = igraph_vcount(graph);
igraph_error_t ret;
igraph_dqueue_int_t Q;
igraph_int_t actroot = 0;
igraph_bitset_t added;
igraph_lazy_adjlist_t adjlist;
igraph_int_t act_rank = 0;
igraph_int_t pred_vec = -1;
igraph_int_t rootpos = 0;
igraph_int_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_int_t i, n = igraph_vector_int_size(restricted);
igraph_bitset_fill(&added, true);
for (i = 0; i < n; i++) {
igraph_int_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_int_t actvect = igraph_dqueue_int_pop(&Q);
igraph_int_t actdist = igraph_dqueue_int_pop(&Q);
igraph_int_t succ_vec;
igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect);
IGRAPH_CHECK_OOM(neis, "Failed to query neighbors.");
const igraph_int_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_int_t i = 0; i < n; i++) {
igraph_int_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_int_t root, igraph_neimode_t mode,
igraph_vector_int_t *order, igraph_vector_int_t *layers,
igraph_vector_int_t *parents
) {
const igraph_int_t no_of_nodes = igraph_vcount(graph);
igraph_dqueue_int_t q;
igraph_int_t num_visited = 0;
igraph_vector_int_t neis;
igraph_bitset_t added;
igraph_int_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_int_t actvect = igraph_dqueue_int_pop(&q);
igraph_int_t actdist = igraph_dqueue_int_pop(&q);
IGRAPH_CHECK(igraph_neighbors(
graph, &neis, actvect, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE
));
igraph_int_t nei_count = igraph_vector_int_size(&neis);
for (igraph_int_t i = 0; i < nei_count; i++) {
const igraph_int_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.
*
* </para><para>
* If not all vertices can be reached from the supplied root vertex,
* then additional root vertices will be used, in the order of their
* vertex IDs.
*
* \param graph The input graph.
* \param root The id of the root vertex.
* \param mode For directed graphs, it defines which edges to follow.
* \c IGRAPH_OUT means following the direction of the edges,
* \c IGRAPH_IN means the opposite, and
* \c IGRAPH_ALL ignores the direction of the edges.
* This parameter is ignored for undirected graphs.
* \param unreachable Boolean, 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_int_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_int_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_int_t act_rank = 0;
igraph_int_t rank_out = 0;
igraph_int_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_int_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_int_t actvect = igraph_stack_int_top(&stack);
igraph_int_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_int_t n = igraph_vector_int_size(neis);
/* Search for a neighbor that was not yet visited */
igraph_bool_t any = false;
igraph_int_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;
}