|
|
|
|
|
#ifndef DLIB_GRAPH_KERNEl_1_ |
|
#define DLIB_GRAPH_KERNEl_1_ |
|
|
|
#include <memory> |
|
#include <vector> |
|
|
|
#include "../serialize.h" |
|
#include "../noncopyable.h" |
|
#include "../std_allocator.h" |
|
#include "../algs.h" |
|
#include "graph_kernel_abstract.h" |
|
#include "../is_kind.h" |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
template <typename node_type, typename graph, bool is_checked> |
|
struct graph_checker_helper |
|
{ |
|
|
|
|
|
|
|
|
|
static void check_neighbor ( |
|
unsigned long edge_index, |
|
const node_type& self |
|
) |
|
{ |
|
|
|
DLIB_CASSERT(edge_index < self.number_of_neighbors(), |
|
"\tnode_type& graph::node_type::neighbor(edge_index)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tedge_index: " << edge_index |
|
<< "\n\tnumber_of_neighbors(): " << self.number_of_neighbors() |
|
<< "\n\tthis: " << &self |
|
); |
|
} |
|
|
|
static void check_edge ( |
|
unsigned long edge_index, |
|
const node_type& self |
|
) |
|
{ |
|
|
|
DLIB_CASSERT(edge_index < self.number_of_neighbors(), |
|
"\tE& graph::node_type::edge(edge_index)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tedge_index: " << edge_index |
|
<< "\n\tnumber_of_neighbors(): " << self.number_of_neighbors() |
|
<< "\n\tthis: " << &self |
|
); |
|
} |
|
|
|
static void check_node ( |
|
unsigned long index, |
|
const graph& self |
|
) |
|
{ |
|
|
|
DLIB_CASSERT(index < self.number_of_nodes(), |
|
"\tnode_type& graph::node(index)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tindex: " << index |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
} |
|
|
|
static void check_has_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2, |
|
const graph& self |
|
) |
|
{ |
|
|
|
DLIB_CASSERT(node_index1 < self.number_of_nodes() && |
|
node_index2 < self.number_of_nodes(), |
|
"\tvoid graph::has_edge(node_index1, node_index2)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tnode_index1: " << node_index1 |
|
<< "\n\tnode_index2: " << node_index2 |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
} |
|
|
|
static void check_add_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2, |
|
const graph& self |
|
) |
|
{ |
|
DLIB_CASSERT(node_index1 < self.number_of_nodes() && |
|
node_index2 < self.number_of_nodes(), |
|
"\tvoid graph::add_edge(node_index1, node_index2)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tnode_index1: " << node_index1 |
|
<< "\n\tnode_index2: " << node_index2 |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
|
|
DLIB_CASSERT( self.has_edge(node_index1, node_index2) == false, |
|
"\tvoid graph::add_edge(node_index1, node_index2)" |
|
<< "\n\tYou can't add an edge if it already exists in the graph" |
|
<< "\n\tnode_index1: " << node_index1 |
|
<< "\n\tnode_index2: " << node_index2 |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
|
|
} |
|
|
|
static void check_remove_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2, |
|
const graph& self |
|
) |
|
{ |
|
DLIB_CASSERT(node_index1 < self.number_of_nodes() && |
|
node_index2 < self.number_of_nodes(), |
|
"\tvoid graph::remove_edge(node_index1, node_index2)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tnode_index1: " << node_index1 |
|
<< "\n\tnode_index2: " << node_index2 |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
|
|
DLIB_CASSERT( self.has_edge(node_index1, node_index2) == true, |
|
"\tvoid graph::remove_edge(node_index1, node_index2)" |
|
<< "\n\tYou can't remove an edge if it isn't in the graph" |
|
<< "\n\tnode_index1: " << node_index1 |
|
<< "\n\tnode_index2: " << node_index2 |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
|
|
} |
|
|
|
static void check_remove_node ( |
|
unsigned long index, |
|
const graph& self |
|
) |
|
{ |
|
|
|
DLIB_CASSERT(index < self.number_of_nodes(), |
|
"\tvoid graph::remove_node(index)" |
|
<< "\n\tYou have specified an invalid index" |
|
<< "\n\tindex: " << index |
|
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes() |
|
<< "\n\tthis: " << &self |
|
); |
|
} |
|
}; |
|
|
|
template <typename node_type, typename graph> |
|
struct graph_checker_helper <node_type, graph, false> |
|
{ |
|
static inline void check_edge ( unsigned long , const node_type& ) { } |
|
static inline void check_neighbor ( unsigned long , const node_type& ) { } |
|
static inline void check_node ( unsigned long , const graph& ) { } |
|
static inline void check_has_edge ( unsigned long , unsigned long , const graph& ) { } |
|
static inline void check_add_edge ( unsigned long , unsigned long , const graph& ) { } |
|
static inline void check_remove_edge ( unsigned long , unsigned long , const graph& ) { } |
|
static inline void check_remove_node ( unsigned long , const graph& ) { } |
|
}; |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E = char, |
|
typename mem_manager = default_memory_manager, |
|
bool is_checked = true |
|
> |
|
class graph_kernel_1 : noncopyable |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
struct node_type; |
|
|
|
private: |
|
typedef graph_checker_helper<node_type, graph_kernel_1, is_checked> checker; |
|
|
|
|
|
public: |
|
|
|
typedef T type; |
|
typedef E edge_type; |
|
typedef mem_manager mem_manager_type; |
|
|
|
graph_kernel_1( |
|
) {} |
|
|
|
virtual ~graph_kernel_1( |
|
) {} |
|
|
|
void clear( |
|
) { nodes.clear(); } |
|
|
|
void set_number_of_nodes ( |
|
unsigned long new_size |
|
); |
|
|
|
unsigned long number_of_nodes ( |
|
) const { return nodes.size(); } |
|
|
|
node_type& node ( |
|
unsigned long index |
|
) { checker::check_node(index,*this); return *nodes[index]; } |
|
|
|
const node_type& node ( |
|
unsigned long index |
|
) const { checker::check_node(index,*this); return *nodes[index]; } |
|
|
|
bool has_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
) const; |
|
|
|
void add_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
); |
|
|
|
void remove_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
); |
|
|
|
unsigned long add_node ( |
|
); |
|
|
|
void remove_node ( |
|
unsigned long index |
|
); |
|
|
|
void swap ( |
|
graph_kernel_1& item |
|
) { nodes.swap(item.nodes); } |
|
|
|
public: |
|
|
|
struct node_type |
|
{ |
|
T data; |
|
typedef graph_kernel_1 graph_type; |
|
|
|
unsigned long index( |
|
) const { return idx; } |
|
|
|
unsigned long number_of_neighbors ( |
|
) const { return neighbors.size(); } |
|
|
|
const node_type& neighbor ( |
|
unsigned long edge_index |
|
) const { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; } |
|
|
|
node_type& neighbor ( |
|
unsigned long edge_index |
|
) { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; } |
|
|
|
const E& edge ( |
|
unsigned long edge_index |
|
) const { checker::check_edge(edge_index,*this); return *edges[edge_index]; } |
|
|
|
E& edge ( |
|
unsigned long edge_index |
|
) { checker::check_edge(edge_index,*this); return *edges[edge_index]; } |
|
|
|
private: |
|
friend class graph_kernel_1; |
|
typedef std_allocator<node_type*,mem_manager> alloc_type; |
|
typedef std_allocator<std::shared_ptr<E>,mem_manager> alloc_edge_type; |
|
std::vector<node_type*,alloc_type> neighbors; |
|
std::vector<std::shared_ptr<E>,alloc_edge_type> edges; |
|
unsigned long idx; |
|
}; |
|
|
|
private: |
|
|
|
typedef std_allocator<std::shared_ptr<node_type>,mem_manager> alloc_type; |
|
typedef std::vector<std::shared_ptr<node_type>, alloc_type> vector_type; |
|
vector_type nodes; |
|
}; |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
inline void swap ( |
|
graph_kernel_1<T,E,mem_manager,is_checked>& a, |
|
graph_kernel_1<T,E,mem_manager,is_checked>& b |
|
) { a.swap(b); } |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
struct is_graph<graph_kernel_1<T,E,mem_manager, is_checked> > |
|
{ |
|
static const bool value = true; |
|
}; |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void serialize ( |
|
const graph_kernel_1<T,E,mem_manager,is_checked>& item, |
|
std::ostream& out |
|
) |
|
{ |
|
try |
|
{ |
|
serialize(item.number_of_nodes(), out); |
|
|
|
|
|
for (unsigned long i = 0; i < item.number_of_nodes(); ++i) |
|
{ |
|
serialize(item.node(i).data, out); |
|
|
|
|
|
for (unsigned long n = 0; n < item.node(i).number_of_neighbors(); ++n) |
|
{ |
|
|
|
if (item.node(i).neighbor(n).index() >= i) |
|
{ |
|
serialize(item.node(i).neighbor(n).index(), out); |
|
serialize(item.node(i).edge(n), out); |
|
} |
|
} |
|
const unsigned long stop_mark = 0xFFFFFFFF; |
|
serialize(stop_mark, out); |
|
} |
|
} |
|
catch (serialization_error& e) |
|
{ |
|
throw serialization_error(e.info + "\n while serializing object of type graph_kernel_1"); |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void deserialize ( |
|
graph_kernel_1<T,E,mem_manager,is_checked>& item, |
|
std::istream& in |
|
) |
|
{ |
|
try |
|
{ |
|
unsigned long size; |
|
deserialize(size, in); |
|
|
|
item.clear(); |
|
item.set_number_of_nodes(size); |
|
|
|
|
|
for (unsigned long i = 0; i < item.number_of_nodes(); ++i) |
|
{ |
|
deserialize(item.node(i).data, in); |
|
|
|
const unsigned long stop_mark = 0xFFFFFFFF; |
|
|
|
unsigned long index; |
|
deserialize(index, in); |
|
while (index != stop_mark) |
|
{ |
|
item.add_edge(i, index); |
|
|
|
unsigned long j = 0; |
|
for (j = 0; j < item.node(i).number_of_neighbors(); ++j) |
|
if (item.node(i).neighbor(j).index() == index) |
|
break; |
|
|
|
deserialize(item.node(i).edge(j), in); |
|
deserialize(index, in); |
|
} |
|
|
|
} |
|
} |
|
catch (serialization_error& e) |
|
{ |
|
throw serialization_error(e.info + "\n while deserializing object of type graph_kernel_1"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
set_number_of_nodes ( |
|
unsigned long new_size |
|
) |
|
{ |
|
try |
|
{ |
|
nodes.resize(new_size); |
|
for (unsigned long i = 0; i < nodes.size(); ++i) |
|
{ |
|
nodes[i].reset(new node_type); |
|
nodes[i]->idx = i; |
|
} |
|
} |
|
catch (...) |
|
{ |
|
clear(); |
|
throw; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
bool graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
has_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
) const |
|
{ |
|
checker::check_has_edge(node_index1, node_index2, *this); |
|
|
|
node_type& n = *nodes[node_index1]; |
|
|
|
|
|
for (unsigned long i = 0; i < n.neighbors.size(); ++i) |
|
{ |
|
if (n.neighbors[i]->idx == node_index2) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
add_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
) |
|
{ |
|
checker::check_add_edge(node_index1, node_index2, *this); |
|
try |
|
{ |
|
node_type& n1 = *nodes[node_index1]; |
|
node_type& n2 = *nodes[node_index2]; |
|
|
|
n1.neighbors.push_back(&n2); |
|
|
|
std::shared_ptr<E> e(new E); |
|
n1.edges.push_back(e); |
|
|
|
|
|
if (node_index1 != node_index2) |
|
{ |
|
n2.neighbors.push_back(&n1); |
|
n2.edges.push_back(e); |
|
} |
|
} |
|
catch (...) |
|
{ |
|
clear(); |
|
throw; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
remove_edge ( |
|
unsigned long node_index1, |
|
unsigned long node_index2 |
|
) |
|
{ |
|
checker::check_remove_edge(node_index1, node_index2, *this); |
|
|
|
node_type& n1 = *nodes[node_index1]; |
|
node_type& n2 = *nodes[node_index2]; |
|
|
|
|
|
unsigned long pos = static_cast<unsigned long>(find(n1.neighbors.begin(), n1.neighbors.end(), &n2) - n1.neighbors.begin()); |
|
n1.neighbors.erase(n1.neighbors.begin() + pos); |
|
n1.edges.erase(n1.edges.begin() + pos); |
|
|
|
|
|
if (node_index1 != node_index2) |
|
{ |
|
|
|
unsigned long pos = static_cast<unsigned long>(find(n2.neighbors.begin(), n2.neighbors.end(), &n1) - n2.neighbors.begin()); |
|
n2.neighbors.erase(n2.neighbors.begin() + pos); |
|
n2.edges.erase(n2.edges.begin() + pos); |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
unsigned long graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
add_node ( |
|
) |
|
{ |
|
try |
|
{ |
|
std::shared_ptr<node_type> n(new node_type); |
|
n->idx = nodes.size(); |
|
nodes.push_back(n); |
|
return n->idx; |
|
} |
|
catch (...) |
|
{ |
|
clear(); |
|
throw; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename E, |
|
typename mem_manager, |
|
bool is_checked |
|
> |
|
void graph_kernel_1<T,E,mem_manager,is_checked>:: |
|
remove_node ( |
|
unsigned long index |
|
) |
|
{ |
|
checker::check_remove_node(index,*this); |
|
|
|
node_type& n = *nodes[index]; |
|
|
|
|
|
for (unsigned long i = 0; i < n.neighbors.size(); ++i) |
|
{ |
|
|
|
unsigned long pos = static_cast<unsigned long>(find(n.neighbors[i]->neighbors.begin(), n.neighbors[i]->neighbors.end(), &n) - |
|
n.neighbors[i]->neighbors.begin()); |
|
n.neighbors[i]->neighbors.erase(n.neighbors[i]->neighbors.begin() + pos); |
|
n.neighbors[i]->edges.erase(n.neighbors[i]->edges.begin() + pos); |
|
} |
|
|
|
|
|
nodes[index] = nodes[nodes.size()-1]; |
|
|
|
|
|
nodes[index]->idx = index; |
|
|
|
|
|
nodes.pop_back(); |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|