cpp: improved constexpr, added struct, improved tuple, data_structure: improved graph

This commit is contained in:
Ciro Duran Santillli 2013-09-20 23:09:09 +02:00
parent 5d73516a57
commit fdeb187369
8 changed files with 1718 additions and 917 deletions

78
boost/graph.cpp Normal file
View File

@ -0,0 +1,78 @@
//=======================================================================
// Copyright 2001 Jeremy G. Siek, Andrew Lumsdaine, Lie-Quan Lee,
//
// 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)
//=======================================================================
#include <boost/config.hpp>
#include <iostream>
#include <fstream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
using namespace boost;
int
main(int, char *[])
{
typedef adjacency_list < listS, vecS, directedS,
no_property, property < edge_weight_t, int > > graph_t;
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;
typedef graph_traits < graph_t >::edge_descriptor edge_descriptor;
typedef std::pair<int, int> Edge;
const int num_nodes = 5;
enum nodes { A, B, C, D, E };
char name[] = "ABCDE";
Edge edge_array[] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E),
Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A), Edge(E, B)
};
int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1 };
int num_arcs = sizeof(edge_array) / sizeof(Edge);
graph_t g(edge_array, edge_array + num_arcs, weights, num_nodes);
property_map<graph_t, edge_weight_t>::type weightmap = get(edge_weight, g);
std::vector<vertex_descriptor> p(num_vertices(g));
std::vector<int> d(num_vertices(g));
vertex_descriptor s = vertex(A, g);
dijkstra_shortest_paths(g, s,
predecessor_map(boost::make_iterator_property_map(p.begin(), get(boost::vertex_index, g))).
distance_map(boost::make_iterator_property_map(d.begin(), get(boost::vertex_index, g))));
std::cout << "distances and parents:" << std::endl;
graph_traits < graph_t >::vertex_iterator vi, vend;
for (boost::tie(vi, vend) = vertices(g); vi != vend; ++vi) {
std::cout << "distance(" << name[*vi] << ") = " << d[*vi] << ", ";
std::cout << "parent(" << name[*vi] << ") = " << name[p[*vi]] << std::
endl;
}
std::cout << std::endl;
std::ofstream dot_file("figs/dijkstra-eg.dot");
dot_file << "digraph D {\n"
<< " rankdir=LR\n"
<< " size=\"4,3\"\n"
<< " ratio=\"fill\"\n"
<< " edge[style=\"bold\"]\n" << " node[shape=\"circle\"]\n";
graph_traits < graph_t >::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
graph_traits < graph_t >::edge_descriptor e = *ei;
graph_traits < graph_t >::vertex_descriptor
u = source(e, g), v = target(e, g);
dot_file << name[u] << " -> " << name[v]
<< "[label=\"" << get(weightmap, e) << "\"";
if (p[v] == u)
dot_file << ", color=\"black\"";
else
dot_file << ", color=\"grey\"";
dot_file << "]";
}
dot_file << "}";
return EXIT_SUCCESS;
}

31
boost/main.cpp Normal file
View File

@ -0,0 +1,31 @@
/**
Boost is the most important C++ utilities library.
It has very widespread use, and some of its features have been included or are candidates for inclusion
on newer versions of the STL.
Full list of libs: <http://www.boost.org/doc/libs/>
Some of boost libraries may be in separate packages / shared objects than others.
The convention is: `-lboost_XXX` to include libs in gcc, for exapmle `-lboost_graph`
*/
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/iterator/counting_iterator.hpp>
using namespace boost;
int main(int, char *[])
{
//#counting_iterator
{
std::vector<int> v(boost::counting_iterator<int>(0), boost::counting_iterator<int>(3));
assert((v == std::vector<int>{0, 1, 2}));
}
return EXIT_SUCCESS;
}

1
boost/makefile Symbolic link
View File

@ -0,0 +1 @@
../makefile.individual_out

2
boost/makefile_params Normal file
View File

@ -0,0 +1,2 @@
LIBS := -lboost_graph -lm
PHONY := install-deps-ubuntu

4
boost/makefile_targets Normal file
View File

@ -0,0 +1,4 @@
install-deps-ubuntu:
sudo apt-get install -y libboost-dbg
sudo apt-get install -y libboost-doc
sudo apt-get install -y libboost-graph-dev

122
c/c.c
View File

@ -396,12 +396,14 @@ int setjmp_func( bool jmp, jmp_buf env_buf )
If those are used for documentation purposes, they don't need to match those of the definition.
This is highly confusing however.
Definitions need parameter names.
Definitions need parameter names. This rule changes in C++.
*/
void decl_def_args( int, float, char c );
void decl_def_args( int i, float f, char d ){}
//void def_no_argname( int ){}
/* One major application of forward declarations is to break loops */
int factorial2Funcs1(int);
@ -3006,12 +3008,14 @@ int main( int argc, char **argv )
assert( ( 1 && 1 ) == 1 );
/*
For `||` and `&&`, the second side is only evaluated if needed.
#short circuit evaluation
On this example:
For operators `||`, `&&` and `?`, the second side is only evaluated if needed.
- 1 is evaulated to true
- || does not need to go any further, so i++ is not evaluated
On this example:
- 1 is evaulated to true
- || does not need to go any further, so i++ is not evaluated
*/
{
int i = 0;
@ -3658,9 +3662,35 @@ int main( int argc, char **argv )
}
/*
store array length in variables
#store array length in variables
Before C99, it was not possible to store array length in variables,
not even if they are const.
The two workarounds were:
- enum
- macros
C99 introduces VLA which allows that.
In C++, this changes, even if there is no VLA as of C++11.
*/
{
{
//int n = 2;
//int isVla[n] = { 1, 2 };
//ERROR
//cannot be initialized
}
{
//const int n = 2;
//int isVla[n] = { 1, 2 };
//ERROR
//cannot be initialized
}
//enum
{
enum M {M=3};
@ -3668,12 +3698,15 @@ int main( int argc, char **argv )
is[2] = 1;
}
//define
/*
macro
Shares the disadvantage of every macro of having no scope.
Use enum instead.
*/
{
#define DEFINESIZE 3
//BAD
//*no scope*, so you can't use N anymore.
//use enum instead
int is[DEFINESIZE];
is[2] = 1;
}
@ -3697,20 +3730,6 @@ int main( int argc, char **argv )
//OK
int isVla[n];
}
{
//int n = 2;
//int isVla[n] = { 1, 2 };
//ERROR
//cannot be initialized
}
{
//const int n = 2;
//int isVla[n] = { 1, 2 };
//ERROR
//cannot be initialized
}
}
}
@ -4387,19 +4406,58 @@ int main( int argc, char **argv )
{
//#if
{
if ( -1 )
{
assert( 1 );
}
if ( 0 )
{
// Only 0 counts as false.
if ( 0 ) {
assert( 0 );
}
if ( 1 )
{
if (1){
assert( 1 );
}
if (-1) {
} else {
assert(0);
}
if (-1) {
} else if (0) {
assert(0);
} else {
assert(0);
}
//can ommit braces for single statements
{
{
if (0) assert(0);
if (0)
assert(0);
if (0) { assert(0); }
}
{
if (0)
assert(0);
else
assert(1);
}
//possible but very ugly to use only one pair of braces
{
if (0) {
assert(0);
} else
assert(1);
if (0)
assert(0);
else {
assert(1);
}
}
}
//scope
{
int i = 0;

2195
c/cpp.cpp

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,14 @@
but sketches of how to implement data structures.
The goal of those implementations is only educational.
Obviously, don't reimplement standard data structers, but use the existing STL ones instead.
It is a great exercise to try and implement the fundamental data structures yourself,
as you will probably meet new needs and difficulties which will lead you to learn more C++.
Obviously, don't reimplement existing STL data structers but use the existing ones instead.
*/
#include <algorithm>
#include <cassert>
#include <cmath> //ceil
#include <limits>
#include <functional>
#include <iostream>
#include <list>
@ -418,7 +420,7 @@ class BST : Map<KEY,VAL>
- if the node is found
- if it is not the current node, its parent
- if it is not the current node, its pasrent
- else, NULL
- else, the last searched parent node
@ -700,26 +702,130 @@ class Hash {
};
/**
Represents a graph via adjency lists.
Graph
Implementation notes:
- undirected: store edges only once on the edge with smaller index.
*/
/**
Represents a directed graph via adjency lists.
*/
class GraphList {
public:
typedef std::vector<int>::size_type EdgeNumberType ;
class Edge {
public:
Edge(){}
Edge( EdgeNumberType to, int weight ) : to(to), weight(weight) {}
GraphList(){
// To which vertex this edge goes to.
EdgeNumberType to;
int weight;
};
typedef std::pair<EdgeNumberType,std::vector<Edge> > NodeEdgesPair;
GraphList() {}
GraphList(std::initializer_list<NodeEdgesPair> pairs) {
for (auto& pair : pairs)
this->add( pair );
}
/**
Adds a node / edges pair to the graph.
If the node number is larger than the current number of nodes, the current number of
nodes is increased to fit that new list of edges.
No error check is done to see if one of the edges points to a destination node that is not in the graph,
If you add such an edge, you must also explicitly add the node afterwards, even if it has no edges coming
out of it for example via:
graph.add({1234, {}});
*/
void add(NodeEdgesPair pair) {
EdgeNumberType node_number = pair.first;
if (nodes.size() < node_number + 1) {
nodes.resize(node_number + 1);
}
nodes[node_number] = pair.second;
}
std::string str() const {
std::stringstream ss;
for (EdgeNumberType i = 0; i < nodes.size(); ++i) {
ss << i << ": ";
for (auto& edge : nodes[i]) {
ss << edge.to << "," << edge.weight << " ";
}
ss << std::endl;
}
return ss.str();
}
/**
Calculates the shortest path from one node to another.
Recommended for dense graphs (unordered list implementation).
@param[out] path contains the shortest from from to to
@return true iff a path was found. The path may not exist in case of a non connex input.
This algorithm can detect that case.
*/
bool dijikstra(const EdgeNumberType& from,
const EdgeNumberType& to,
std::vector<EdgeNumberType>& path) {
std::list<EdgeNumberType> not_visited(nodes.size(), false);
std::vector<int> distances(nodes.size(), std::numeric_limits<int>::max());
std::vector<EdgeNumberType> previous(nodes.size());
EdgeNumberType cur(from);
distances[cur] = 0;
for (EdgeNumberType i = 0; i < nodes.size(); ++i)
not_visited.push_back(i);
while (true) {
// Find non visited min and make it current.
auto it = std::min_element(not_visited.begin(), not_visited.end());
previous[*it] = cur;
cur = *it;
not_visited.erase(it);
if (cur == to)
break;
if (not_visited.empty())
return false;
// Update weights of nodes neighbour to cur.
for (auto& edge : nodes[cur]) {
//if (!not_visited[edge.to]) { //could maintain a visited array
//this would prevent useless new_distance calculations
//however this is not worth it as it would use more memory
//and require that that array be kept up to date
int new_distance = distances[cur] + edge.weight;
if (new_distance < distances[edge.to]) {
distances[edge.to] = new_distance;
}
//}
}
}
// Rebuild the path.
path = std::vector<EdgeNumberType>();
cur = to;
path.push_back(cur);
while (cur != from) {
cur = previous[cur];
path.push_back(cur);
}
std::reverse(path.begin(), path.end()); //TODO with a push_front container this would not be necessary.
//so what to do? force the users to give push_front containers?
return true;
}
private:
std::vector<std::vector<Edge> > nodes;
std::vector<int> lists;
};
class GraphMatrix {
public:
private:
friend std::ostream& operator<<(std::ostream& os, const GraphList& rhs) {
return os << rhs.str();
}
};
int main(int argc, char** argv)
@ -727,12 +833,12 @@ int main(int argc, char** argv)
typedef Hash<int,int> map_t;
//map_t mapOrig(0, 1);
map_t mapOrig{
std::pair<int,int>(0,1),
std::pair<int,int>(1,2),
std::pair<int,int>(2,3),
std::pair<int,int>(3,4),
std::pair<int,int>(4,5),
std::pair<int,int>(-1,0),
{ 0, 1},
{ 1, 2},
{ 2, 3},
{ 3, 4},
{ 4, 5},
{-1, 0},
};
map_t map;
map_t mapExpect;
@ -790,7 +896,7 @@ int main(int argc, char** argv)
map = mapOrig;
assert( map == mapOrig );
map.add( 5, 6);
map.add(5, 6);
assert( map != mapOrig );
@ -814,10 +920,10 @@ int main(int argc, char** argv)
//add at powers of 2 the 0 hash so they clutter at hash 0
map = map_t( 0, 1 );
map.add( 1, 2 );
map.add( 2, 3 );
map.add( 4, 5 );
map.add( 8, 9 );
map.add( 1, 2 );
map.add( 2, 3 );
map.add( 4, 5 );
map.add( 8, 9 );
map.add( 16, 17 );
//find
@ -829,9 +935,49 @@ int main(int argc, char** argv)
assert( ! map.find( 0, val ) );
}
/*
// Graphs.
{
GraphList g;
// Dijikstra tests.
{
// Input graphs and origin dest pair and expected output shortest paths.
std::tuple<GraphList,
std::pair<GraphList::EdgeNumberType,GraphList::EdgeNumberType>,
std::vector<GraphList::EdgeNumberType> >in_outs[]{
{
{
{0, {{0, 1}}},
{1, {}},
},
{0, 1},
{0, 1}
},
{
{
{0, {{0, 1}}},
{1, {{1, 2}}},
{2, {{2, 3}}},
{3, {}},
},
{0, 3},
{0, 1, 2, 3}
},
};
for (auto& in_out : in_outs) {
auto& graph = std::get<0>(in_out);
auto& origin_destination = std::get<1>(in_out);
auto& expected_path = std::get<2>(in_out);
std::vector<GraphList::EdgeNumberType> path;
std::cout << graph << std::endl;
std::cout << "dijikstra path:" << std::endl;
graph.dijikstra(origin_destination.first, origin_destination.second, path);
for (auto& node_number : path)
std::cout << node_number << std::endl;
std::cout << std::endl;
assert(path == expected_path);
}
}
}
*/
return EXIT_SUCCESS;
}