diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de8c45b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/*.o +bin/* diff --git a/Makefile b/Makefile index 4bdd3a3..cf29f19 100644 --- a/Makefile +++ b/Makefile @@ -5,22 +5,18 @@ FOLDER=/usr/local/include/src SOURCES = $(wildcard examples/*.cpp) OBJECTS = $(SOURCES:.cpp=.o) -CFLAGS = -Wall -Wextra -O2 -std=c++0x -I$(CURDIR) -pedantic -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wformat=2 -Winit-self -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror +CFLAGS = -Wall -Wextra -Og -g -std=c++11 -I$(CURDIR) -pedantic -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wformat=2 -Winit-self -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wstrict-overflow=2 -Wswitch-default -Wundef -Werror -Iexamples/ LDFLAGS = -lm install: - @if [ -a $(FOLDER) ]; then echo "Folder src already exists in \"/usr/local/include\", sorry but I don't know what to do, I hope it's me :-)" && exit -1; else exit 0; fi; + @if [ -d $(FOLDER) ]; then echo "Folder src already exists in \"/usr/local/include\", sorry but I don't know what to do, I hope it's me :-)" && exit 1; else exit 0; fi; @sudo ln -s $(CURDIR)/src/ /usr/local/include/src @sudo ln -s $(CURDIR)/inputGenerator.hpp /usr/local/include/inputGenerator.hpp @echo "Install OK" clean: - @mkdir tmp @rm -rf examples/*.dSYM - @mv examples/*.cpp tmp/ - @rm -f examples/* - @mv tmp/* examples/ - @rm -rf tmp + @rm -f examples/*.o @rm -f src/*.gch @echo "Clean OK" @@ -37,10 +33,17 @@ lint: find . -name "*.hpp" | xargs python2 cpplint.py --filter=-legal --counting=detailed benchmark: - $(CXX) $(CFLAGS) examples/benchmark.cpp -o examples/benchmark $(LDFLAGS) - ./examples/benchmark + $(CXX) $(CFLAGS) -DINPUT_GENERATOR_DEBUG examples/benchmark.cpp -o bin/benchmark $(LDFLAGS) + ./bin/benchmark + +countfefete: + $(CXX) $(CFLAGS) -DINPUT_GENERATOR_DEBUG examples/countfefete.cpp -o bin/countfefete $(LDFLAGS) + @./bin/countfefete 17 10000 1000000000 0 + @./bin/countfefete 18 40000 1000000000 0 + @./bin/countfefete 19 70000 1000000000 0 + @./bin/countfefete 20 100000 1000000000 0 %.o: %.cpp $(CXX) $(CFLAGS) $< -o $@ $(LDFLAGS) -test: $(OBJECTS) benchmark +test: $(OBJECTS) benchmark countfefete diff --git a/examples/benchmark.cpp b/examples/benchmark.cpp index c0535fd..41dd96e 100644 --- a/examples/benchmark.cpp +++ b/examples/benchmark.cpp @@ -8,22 +8,9 @@ #include "../inputGenerator.hpp" -using namespace std::chrono; - -template -double time_elapsed(const duration_type & d) { - return duration_cast>(d).count(); -} - -template -double time_taken(function f) { - auto start = system_clock::now(); +#include "benchmark.hpp" - f(); - auto end = system_clock::now(); - - return time_elapsed(end - start); -} +using namespace std::chrono; void testRandomInt() { std::cout << "Generating 100.000.000 numbers in range 0 MAX_INT: " << std::endl; diff --git a/examples/benchmark.hpp b/examples/benchmark.hpp new file mode 100644 index 0000000..a5c06ac --- /dev/null +++ b/examples/benchmark.hpp @@ -0,0 +1,18 @@ +#include + +using namespace std::chrono; + +template +double time_elapsed(const duration_type & d) { + return duration_cast>(d).count(); +} + +template +double time_taken(function f) { + auto start = system_clock::now(); + + f(); + auto end = system_clock::now(); + + return time_elapsed(end - start); +} diff --git a/examples/countfefete.cpp b/examples/countfefete.cpp new file mode 100644 index 0000000..03d6083 --- /dev/null +++ b/examples/countfefete.cpp @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include + +#include "../inputGenerator.hpp" + +#include "benchmark.hpp" + +using namespace std; +using namespace inputGenerator; + +using node_value_t = int; + +using gen = AdvancedTreeGenerator; +using graph_t = gen::graph_t; +using Tree = gen::Tree; +using HTree = gen::HTree; +using Chain = gen::Chain; +using Star = gen::Star; +using RandomTree = gen::RandomTree; +using WideRandomTree = gen::WideRandomTree; + +void init(int seed = 1337) +{ + Seed::logging = false; + Seed::create(seed); +} + +bool random_flip(double p) +{ + uniform_real_distributiond (0, 1); + return d(Generator::getGenerator()) < p; +} + + + +string format(graph_t V) +{ + ostringstream cout; + V.Index(1); + cout << V.size() << "\n"; + int req = V.size() - 1; + + + for (const auto& n : V) { + cout << n.data() << " "; + } + cout.seekp(-1, ios_base::end); + cout << "\n"; + + for (const auto& e : V.edges()) + { + cout << e.from().index() << " " << e.to().index() << "\n"; + req--; + } + + assert (req == 0); + + return cout.str(); +} + +struct countfefete +{ + int n; + + countfefete(int n): + n(n) + { } + + Tree build(Tree acum) + { + while (true) { + assert (acum->size() <= n); // Too many nodes already. Abort! + + if (acum->size() == n) { + return acum; + } + + int rem = n - acum->size(); + + assert (rem > 0); + + // Base cases, because it's hard to calculate the exact node count + // for the fancier types of trees. + // + // +1 for rem because when two trees are merged, one of the nodes is dropped. + if (rem <= 5) { + acum = RandomTree::make(rem + 1) + .combine(acum); + continue; + } + if (rem <= 15) { + acum = WideRandomTree::make(rem + 1, rem / 2 + 1) + .combine(acum); + continue; + } + + auto comb = [&] (Tree t) + { + if (t->size() <= rem + 1) { + acum = t.combine(acum); + } + assert (acum->size() <= n); + }; + + vector> transitions = { + [&] () { + int lstar = random_int(2, 10); + comb(Star::make(lstar)); + }, + [&]() { + int lchain = random_int(2, 10); + comb(Chain::make(lchain)); + }, + [&]() { + comb(HTree::make_nchains(10, 3, 3)); + }, + [&]() { + comb(HTree::make_nchains(10, 5, 5)); + }, + [&]() { + comb(WideRandomTree::make(30, 10)); + }, + [&]() { + comb(RandomTree::make(30)); + }, + [&]() { + comb(RandomTree::make(10)); + }, + [&]() { + comb(WideRandomTree::make(50, 30)); + }, + [&]() { + comb(countfefete(sqrt(n)).build(Chain::make(2))); + } + }; + + // Randomly choose a transition and then continue. + (*random_choice(transitions))(); + } + } + + static Tree generate(int seed, int N, int maxval, double p) + { + init(seed); + int x = 1; + auto next_value = [&] () -> int + { + // Give it an old value. + if (random_flip(p)) { + return random_int(1, x); + } + + // Otherwise give it a new, unique id. + // Note that this might be reused at a later point. + return x++; + }; + + auto tree = countfefete(N).build(Chain::make(2)); + + for (auto &node : tree.m_graph) { + node.data() = next_value(); + } + + assert (tree->size() == N); + + set values_that_appear; + + for (const auto & node : tree.m_graph) { + values_that_appear.insert(node.data()); + } + + assert (maxval >= static_cast(values_that_appear.size())); // Impossible to assign values otherwise. + + // We want to keep the relative ordering of the node values. + // e.g. val_for_perm[x] <= val_for_perm[y] <-> x <= y. + // This is because of the way the trees are structured, built on layers upon layers. + // So the outermost leaves will have the lowest values, then their descendants and so on and so forth. + map val_for_perm; + { + + set incl; + for (size_t i = 0; i < values_that_appear.size(); ++i) { + int val; + do{ + val = random_int(1, maxval); + } while (incl.count(val)); + incl.insert(val); + } + + assert (incl.size() == values_that_appear.size()); + + auto src = values_that_appear.begin(); + auto dst = incl.begin(); + + while (src != values_that_appear.end() && + dst != incl.end()) { + val_for_perm[*src] = *dst; + ++src; + ++dst; + } + + assert (src == values_that_appear.end()); + assert (dst == incl.end()); + } + + for (auto & node : tree.m_graph) { + node.data() = val_for_perm[node.data()]; + assert (1 <= node.data() && node.data() <= maxval); + } + + return tree; + } +}; + +int main(int argc, char* argv[]) { + if (argc != 5) { + cerr << "Usage: " << argv[0] << " seed N maxval prob\n"; + cerr << "Generate a tree with N nodes, with values from 0..valmax in nodes.\n"; + cerr << "Each node has a probability of `prob` to be assigned an old value and (1 - prob) to be given a new one\n"; + exit(-1); + } + vector arg(argv, argv + argc); + int seed = stoi(arg[1]); + int N = stoi(arg[2]); + int maxval = stoi(arg[3]); + double p = stod(arg[4]); + + std::cout + << "Generating a countfefete input with " << N << " nodes takes " << + time_taken([&]() + { + countfefete::generate(seed, N, maxval, p); + }) << std::endl; + + return 0; +} diff --git a/inputGenerator.hpp b/inputGenerator.hpp index 1ecba1f..061bf08 100644 --- a/inputGenerator.hpp +++ b/inputGenerator.hpp @@ -10,3 +10,4 @@ #include "src/tree.hpp" #include "src/bipartite.hpp" #include "src/undirected_graph.hpp" +#include "src/advanced_tree.hpp" diff --git a/src/advanced_tree.hpp b/src/advanced_tree.hpp new file mode 100644 index 0000000..0e33308 --- /dev/null +++ b/src/advanced_tree.hpp @@ -0,0 +1,233 @@ +#ifndef INPUT_GENERATOR_ADVANCED_TREE_HPP_ +#define INPUT_GENERATOR_ADVANCED_TREE_HPP_ + +#include +#include +#include +#include +#include + +#include "tree.hpp" +#include "generator.hpp" + +namespace inputGenerator { + +template +static int random_int(int_t left, int_t right) +{ + std::uniform_int_distribution distribution(left, right); + return distribution(Generator::getGenerator()); +} + +template +typename std::vector::const_iterator +random_choice(const std::vector& v) +{ + assert (!v.empty()); + return v.begin() + random_int(0, static_cast(v.size() - 1)); +} + + +template +struct AdvancedTreeGenerator { + + using graph_t = Graph; + using node_t = typename graph_t::Node; + + static std::vector leaves(const graph_t& V) + { + std::vector ret; + for (auto &node : V) { + if (node.edges().size() == 1) { + ret.push_back(node); + } + } + + if (V.size() == 1) { + ret = {V[0]}; + } + + assert (!ret.empty()); + return ret; + } + + struct Tree { + graph_t m_graph; + std::vector m_leaves; + + void init(graph_t graph) + { + m_graph = graph; + m_leaves = leaves(m_graph); + } + + // For two graphs of the same shape, this should return isomorphic nodes. + const std::vector& nodes_of_interest() const + { + return m_leaves; + } + + typename std::vector::const_iterator + random_node_of_interest() const + { + return random_choice(nodes_of_interest()); + } + + operator graph_t&() + { + return m_graph; + } + + node_t& operator[] (int idx) + { + return m_graph[idx]; + } + + graph_t* operator-> () + { + return &m_graph; + } + + Tree& combine(Tree& v) + { + if (v->size() > m_graph.size()) { + return v.combine(*this); + } + auto it_exile = random_node_of_interest(); + node_t exile = *it_exile; + m_leaves.erase(it_exile); + m_graph.expandNode(exile, graph_t(v), {*v.random_node_of_interest()}); + + m_leaves.insert(m_leaves.end(), v.m_leaves.begin(), v.m_leaves.end()); + return *this; + } + }; + + struct Chain : public Tree { + int m_nnodes; + + static Chain make(int nnodes) + { + Chain c; + c.init(chain(nnodes, Boolean::False)); + c.m_nnodes = nnodes; + return c; + } + }; + + struct HTree : public Tree { + using Tree::m_graph; + Chain m_left; + Chain m_right; + + static HTree make(int lchain, int lleft, int lright) + { + HTree h; + + graph_t lant = Chain::make(lchain); + { + Chain add = Chain::make(lleft); + lant.expandNode(lant[0], add, {add[lleft / 2]}); + h.m_left = add; + } + + { + Chain add = Chain::make(lright); + lant.expandNode(lant[lchain - 1], add, {add[lright / 2]}); + h.m_right = add; + } + h.init(lant); + + return h; + } + + /// Merge the left side of *this* with the right side of oth. + HTree& merge_left(HTree oth) + { + assert (m_left->size() == oth.m_right->size()); // Impossible to merge otherwise. + + std::vector> mapping; + + { + for (auto it1 = m_left->begin(), it2 = oth.m_right->begin(); + it1 != m_left->end() && it2 != oth.m_right->end(); + ++it1, ++it2) { + mapping.push_back({*it1, *it2}); + } + } + + m_graph.fuseGraph(oth.m_graph, mapping, Boolean::True); + m_left = oth.m_left; + return *this; + } + + /// Merge the right side of *this* with the left side of oth. + HTree& merge_right(HTree oth) + { + assert (m_right->size() == oth.m_left->size()); // Impossible to merge otherwise. + + std::vector> mapping; + + { + for (auto it1 = m_right->begin(), it2 = oth.m_left->begin(); + it1 != m_right->end() && it2 != oth.m_left->end(); + ++it1, ++it2) { + mapping.push_back({*it1, *it2}); + } + } + + m_graph.fuseGraph(oth.m_graph, mapping, Boolean::True); + m_right = oth.m_right; + return *this; + } + // Chain `nchains` H graphs together. + static HTree make_nchains(int nchains, int lchain, int lwings) + { + HTree ret = HTree::make(lchain, lwings, lwings); + for (int i = 0; i < nchains; ++i) { + ret.merge_left(HTree::make(lchain, lwings, lwings)); + } + return ret; + } + }; + + struct RandomTree : public Tree { + static RandomTree make(int nnodes) + { + RandomTree ret; + ret.init(tree(nnodes)); + return ret; + } + }; + + struct WideRandomTree : public Tree { + static WideRandomTree make(int nnodes, int mindiam) + { + WideRandomTree ret; + ret.init(wideTree(nnodes, mindiam)); + return ret; + } + }; + + struct Star : public Tree { + int m_mid; + + static Star make(int nnodes) + { + graph_t g(nnodes); + + for (int i = 1; i < nnodes; ++i) { + addEdge(g[0], g[i]); + } + + Star bc; + bc.m_mid = 0; + bc.init(g); + + return bc; + } + }; +}; + +} // namespace inputGenerator +#endif // INPUT_GENERATOR_ADVANCED_TREE_HPP_ diff --git a/src/graph.hpp b/src/graph.hpp index 15c8255..bc14ce2 100644 --- a/src/graph.hpp +++ b/src/graph.hpp @@ -148,7 +148,7 @@ class Graph { } int size() const { - return nodes.size(); + return static_cast(nodes.size()); } std::vector arcs(const bool &forceSearch = true) const;