From e5fc102add575aab3e4b08b448144a0a89eba7e7 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Wed, 15 Mar 2017 22:18:55 +0300 Subject: [PATCH 01/10] variant --- variant.h | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 variant.h diff --git a/variant.h b/variant.h new file mode 100644 index 0000000..bac1ef1 --- /dev/null +++ b/variant.h @@ -0,0 +1,265 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace jngen { + +namespace variant_detail { + +constexpr static int NO_TYPE = -1; + +template +struct PlainType { + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +class VariantImpl; + +template +class VariantImpl { +public: + VariantImpl() { + type_ = NO_TYPE; + } + +private: + int type_; + char data_[Size]; + +protected: + int& type() { return type_; } + int type() const { return type_; } + + char* data() { return data_; } + const char* data() const { return data_; } + + void doDestroy() { + throw; + } + + template + constexpr static int typeId() { + return NO_TYPE; + } + + void copy(char*) const { + throw; + } + + void move(char* dst) const { + memmove(dst, data(), Size); + } + + template + void applyVisitor(V&&) const { + throw; + } + + void assign() {} +}; + +template +class VariantImpl : public VariantImpl< + (sizeof(T) > Size ? sizeof(T) : Size), + Args... + > +{ + using Base = VariantImpl<(sizeof(T) > Size ? sizeof(T) : Size), Args...>; + + constexpr static size_t MY_ID = sizeof...(Args); + +protected: + void doDestroy() { + if (this->type() == MY_ID) { + this->type() = NO_TYPE; + reinterpret_cast(this->data())->~T(); + } else { + Base::doDestroy(); + } + } + + template + constexpr static int typeId() { + return std::is_same::value ? + MY_ID : + Base::template typeId

(); + } + + void copy(char* dst) const { + if (this->type() == MY_ID) { + new(dst) T(*reinterpret_cast(this->data())); + } else { + Base::copy(dst); + } + } + + template + void applyVisitor(V&& v) const { + if (this->type() == MY_ID) { + v(*reinterpret_cast(this->data())); + } else { + Base::applyVisitor(v); + } + } + + using Base::assign; + + void assign(const T& t) { + if (this->type() == NO_TYPE) { + new(this->data()) T; + this->type() = MY_ID; + } + + ref() = t; + } + +private: + T& ref() { return *reinterpret_cast(this->data()); } + +public: + operator T() const { + if (this->type() == MY_ID) { + return *reinterpret_cast(this->data()); + } else { + return T(); + } + } +}; + +template +class Variant : public VariantImpl<0, Args...> { + using Base = VariantImpl<0, Args...>; + +public: + Variant() {} + + Variant(const Variant& other) { + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + } + + Variant& operator=(const Variant& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + return *this; + } + + Variant(Variant&& other) { + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + } + + Variant& operator=(Variant&& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + return *this; + } + + ~Variant() { + if (type() != NO_TYPE) { + this->doDestroy(); + } + } + + template + Variant(const T& t) : Variant() { + this->assign(t); + } + + template + T& ref() { + return *ptr(); + } + + template + const T& cref() { + auto ptr = cptr(); + if (ptr == 0) { + throw std::logic_error("jngen::Variant: taking a reference for" + " a type which is not active now"); + } + return *ptr; + } + + template + void applyVisitor(V&& v) const { + Base::applyVisitor(v); + } + + int type() const { return Base::type(); } + +private: + template::type> + T* ptr() { + if (type() != this->template typeId()) { + if (type() != NO_TYPE) { + this->doDestroy(); + } + ::new(this->data()) T; + unsafeType() = this->template typeId(); + } + return reinterpret_cast(this->data()); + } + + template::type> + const T* cptr() const { + if (type() != this->template typeId()) { + return nullptr; + } + return reinterpret_cast(this->data()); + } + + int& unsafeType() { + return Base::type(); + } +}; + +struct OstreamVisitor { + template + void operator()(const T& t) { + out << t; + } + std::ostream& out; +}; + +} // namespace variant_detail + +using variant_detail::Variant; + +} // namespace jngen + +using jngen::Variant; + +template +std::ostream& operator<<(std::ostream& out, const Variant& v) { + if (v.type() == jngen::variant_detail::NO_TYPE) { + out << "{empty variant}"; + } else { + v.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); + } + + return out; +} From 1d6a2891c65dce309859af2c2c1effc12a732fc2 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Thu, 16 Mar 2017 18:52:28 +0300 Subject: [PATCH 02/10] VariantArray --- geometry.h | 5 +++-- jngen.h | 19 +++++++++++++++---- printers.h | 3 ++- random.h | 7 +++++++ repr.h | 4 +++- variant.h | 34 ++++++++++++++++++++-------------- variant_array.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 variant_array.h diff --git a/geometry.h b/geometry.h index fdc8591..b1aed17 100644 --- a/geometry.h +++ b/geometry.h @@ -135,8 +135,9 @@ using Point = TPoint; using Pointf = TPoint; template -std::ostream& operator<<(std::ostream& out, const TPoint& t) { - return out << t.x << " " << t.y; +JNGEN_DECLARE_SIMPLE_PRINTER(TPoint, 3) { + (void)mod; + out << t.x << " " << t.y; } // TODO: make polygon a class to support, e.g., shifting by a point diff --git a/jngen.h b/jngen.h index 573f4e5..26ebbdd 100644 --- a/jngen.h +++ b/jngen.h @@ -602,6 +602,13 @@ struct TypedRandom : public BaseTypedRandom { char next(char l, char r) { return random.next(l, r); } }; +template +struct TypedRandom : public BaseTypedRandom { + using BaseTypedRandom::BaseTypedRandom; + template + T next(Args... args) { return random.next(args...); } +}; + struct OrderedPairTag {} opair; @@ -751,8 +758,10 @@ class Repr { OutputModifier mod_; }; +class BaseReprProxy {}; + template -class ReprProxy { +class ReprProxy : public BaseReprProxy { friend std::ostream& operator<<(std::ostream& out, const ReprProxy& proxy) { Repr repr(static_cast(proxy)); return out << repr; @@ -849,6 +858,7 @@ namespace jngen { namespace detail { // TODO: maybe make it more clear SFINAE, like boost::has_left_shift? +// TODO: make these defines namespace independent #define JNGEN_DEFINE_FUNCTION_CHECKER(name, expr)\ template\ @@ -1007,7 +1017,7 @@ namespace namespace_for_fake_operator_ltlt { template auto operator<<(std::ostream& out, const T& t) -> typename std::enable_if< - !JNGEN_HAS_OSTREAM() && !std::is_base_of, T>::value, + !JNGEN_HAS_OSTREAM() && !std::is_base_of::value, std::ostream& >::type { @@ -1768,8 +1778,9 @@ using Point = TPoint; using Pointf = TPoint; template -std::ostream& operator<<(std::ostream& out, const TPoint& t) { - return out << t.x << " " << t.y; +JNGEN_DECLARE_SIMPLE_PRINTER(TPoint, 3) { + (void)mod; + out << t.x << " " << t.y; } // TODO: make polygon a class to support, e.g., shifting by a point diff --git a/printers.h b/printers.h index 138e6b0..b8e7a5c 100644 --- a/printers.h +++ b/printers.h @@ -13,6 +13,7 @@ namespace jngen { namespace detail { // TODO: maybe make it more clear SFINAE, like boost::has_left_shift? +// TODO: make these defines namespace independent #define JNGEN_DEFINE_FUNCTION_CHECKER(name, expr)\ template\ @@ -171,7 +172,7 @@ namespace namespace_for_fake_operator_ltlt { template auto operator<<(std::ostream& out, const T& t) -> typename std::enable_if< - !JNGEN_HAS_OSTREAM() && !std::is_base_of, T>::value, + !JNGEN_HAS_OSTREAM() && !std::is_base_of::value, std::ostream& >::type { diff --git a/random.h b/random.h index eaca4f2..08da05b 100644 --- a/random.h +++ b/random.h @@ -190,6 +190,13 @@ struct TypedRandom : public BaseTypedRandom { char next(char l, char r) { return random.next(l, r); } }; +template +struct TypedRandom : public BaseTypedRandom { + using BaseTypedRandom::BaseTypedRandom; + template + T next(Args... args) { return random.next(args...); } +}; + struct OrderedPairTag {} opair; diff --git a/repr.h b/repr.h index e406b39..1b209eb 100644 --- a/repr.h +++ b/repr.h @@ -91,8 +91,10 @@ class Repr { OutputModifier mod_; }; +class BaseReprProxy {}; + template -class ReprProxy { +class ReprProxy : public BaseReprProxy { friend std::ostream& operator<<(std::ostream& out, const ReprProxy& proxy) { Repr repr(static_cast(proxy)); return out << repr; diff --git a/variant.h b/variant.h index bac1ef1..cce8e78 100644 --- a/variant.h +++ b/variant.h @@ -1,11 +1,12 @@ #pragma once -#include -#include -#include +#include "repr.h" +#include "printers.h" +#include #include -#include +#include +#include namespace jngen { @@ -211,6 +212,11 @@ class Variant : public VariantImpl<0, Args...> { int type() const { return Base::type(); } + template + constexpr static bool hasType() { + return Base::template typeId() != NO_TYPE; + } + private: template::type> T* ptr() { @@ -240,7 +246,8 @@ class Variant : public VariantImpl<0, Args...> { struct OstreamVisitor { template void operator()(const T& t) { - out << t; + auto mod = OutputModifier(); + JNGEN_PRINT(t); } std::ostream& out; }; @@ -249,17 +256,16 @@ struct OstreamVisitor { using variant_detail::Variant; -} // namespace jngen - -using jngen::Variant; - template -std::ostream& operator<<(std::ostream& out, const Variant& v) { - if (v.type() == jngen::variant_detail::NO_TYPE) { +JNGEN_DECLARE_SIMPLE_PRINTER(Variant, 5) { + (void)mod; + if (t.type() == jngen::variant_detail::NO_TYPE) { out << "{empty variant}"; } else { - v.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); + t.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); } - - return out; } + +} // namespace jngen + +using jngen::Variant; diff --git a/variant_array.h b/variant_array.h new file mode 100644 index 0000000..51b91f5 --- /dev/null +++ b/variant_array.h @@ -0,0 +1,49 @@ +#pragma once + +#include "array.h" +#include "repr.h" +#include "variant.h" + +#include +#include + +namespace jngen { + +template +class VariantArray : public GenericArray> { +public: + using Base = GenericArray>; + using BaseVariant = Variant; + + using Base::Base; + + VariantArray() {} + + /* implicit */ VariantArray(const Base& base) : + Base(base) + { } + + template()>::type> + VariantArray(const GenericArray& other) { + std::copy(other.begin(), other.end(), std::back_inserter(*this)); + } + + template()>::type> + VariantArray(GenericArray&& other) { + std::move(other.begin(), other.end(), std::back_inserter(*this)); + GenericArray().swap(other); + } + + template()>::type> + operator GenericArray() const + { + return GenericArray(this->begin(), this->end()); + } +}; + +} // namespace jngen + +using jngen::VariantArray; From 2a68d9459568104fec0f60c9b72b1f60dd8c431a Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Thu, 16 Mar 2017 18:53:51 +0300 Subject: [PATCH 03/10] jngen.h rebuilt --- jngen.h | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) diff --git a/jngen.h b/jngen.h index 26ebbdd..bfebea5 100644 --- a/jngen.h +++ b/jngen.h @@ -2605,3 +2605,316 @@ JNGEN_DECLARE_SIMPLE_PRINTER(Graph, 2) { } // namespace jngen using jngen::Graph; + + +#include +#include +#include +#include + +namespace jngen { + +namespace variant_detail { + +constexpr static int NO_TYPE = -1; + +template +struct PlainType { + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +class VariantImpl; + +template +class VariantImpl { +public: + VariantImpl() { + type_ = NO_TYPE; + } + +private: + int type_; + char data_[Size]; + +protected: + int& type() { return type_; } + int type() const { return type_; } + + char* data() { return data_; } + const char* data() const { return data_; } + + void doDestroy() { + throw; + } + + template + constexpr static int typeId() { + return NO_TYPE; + } + + void copy(char*) const { + throw; + } + + void move(char* dst) const { + memmove(dst, data(), Size); + } + + template + void applyVisitor(V&&) const { + throw; + } + + void assign() {} +}; + +template +class VariantImpl : public VariantImpl< + (sizeof(T) > Size ? sizeof(T) : Size), + Args... + > +{ + using Base = VariantImpl<(sizeof(T) > Size ? sizeof(T) : Size), Args...>; + + constexpr static size_t MY_ID = sizeof...(Args); + +protected: + void doDestroy() { + if (this->type() == MY_ID) { + this->type() = NO_TYPE; + reinterpret_cast(this->data())->~T(); + } else { + Base::doDestroy(); + } + } + + template + constexpr static int typeId() { + return std::is_same::value ? + MY_ID : + Base::template typeId

(); + } + + void copy(char* dst) const { + if (this->type() == MY_ID) { + new(dst) T(*reinterpret_cast(this->data())); + } else { + Base::copy(dst); + } + } + + template + void applyVisitor(V&& v) const { + if (this->type() == MY_ID) { + v(*reinterpret_cast(this->data())); + } else { + Base::applyVisitor(v); + } + } + + using Base::assign; + + void assign(const T& t) { + if (this->type() == NO_TYPE) { + new(this->data()) T; + this->type() = MY_ID; + } + + ref() = t; + } + +private: + T& ref() { return *reinterpret_cast(this->data()); } + +public: + operator T() const { + if (this->type() == MY_ID) { + return *reinterpret_cast(this->data()); + } else { + return T(); + } + } +}; + +template +class Variant : public VariantImpl<0, Args...> { + using Base = VariantImpl<0, Args...>; + +public: + Variant() {} + + Variant(const Variant& other) { + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + } + + Variant& operator=(const Variant& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + return *this; + } + + Variant(Variant&& other) { + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + } + + Variant& operator=(Variant&& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + return *this; + } + + ~Variant() { + if (type() != NO_TYPE) { + this->doDestroy(); + } + } + + template + Variant(const T& t) : Variant() { + this->assign(t); + } + + template + T& ref() { + return *ptr(); + } + + template + const T& cref() { + auto ptr = cptr(); + if (ptr == 0) { + throw std::logic_error("jngen::Variant: taking a reference for" + " a type which is not active now"); + } + return *ptr; + } + + template + void applyVisitor(V&& v) const { + Base::applyVisitor(v); + } + + int type() const { return Base::type(); } + + template + constexpr static bool hasType() { + return Base::template typeId() != NO_TYPE; + } + +private: + template::type> + T* ptr() { + if (type() != this->template typeId()) { + if (type() != NO_TYPE) { + this->doDestroy(); + } + ::new(this->data()) T; + unsafeType() = this->template typeId(); + } + return reinterpret_cast(this->data()); + } + + template::type> + const T* cptr() const { + if (type() != this->template typeId()) { + return nullptr; + } + return reinterpret_cast(this->data()); + } + + int& unsafeType() { + return Base::type(); + } +}; + +struct OstreamVisitor { + template + void operator()(const T& t) { + auto mod = OutputModifier(); + JNGEN_PRINT(t); + } + std::ostream& out; +}; + +} // namespace variant_detail + +using variant_detail::Variant; + +template +JNGEN_DECLARE_SIMPLE_PRINTER(Variant, 5) { + (void)mod; + if (t.type() == jngen::variant_detail::NO_TYPE) { + out << "{empty variant}"; + } else { + t.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); + } +} + +} // namespace jngen + +using jngen::Variant; + + +#include +#include + +namespace jngen { + +template +class VariantArray : public GenericArray> { +public: + using Base = GenericArray>; + using BaseVariant = Variant; + + using Base::Base; + + VariantArray() {} + + /* implicit */ VariantArray(const Base& base) : + Base(base) + { } + + template()>::type> + VariantArray(const GenericArray& other) { + std::copy(other.begin(), other.end(), std::back_inserter(*this)); + } + + template()>::type> + VariantArray(GenericArray&& other) { + std::move(other.begin(), other.end(), std::back_inserter(*this)); + GenericArray().swap(other); + } + + template()>::type> + operator GenericArray() const + { + return GenericArray(this->begin(), this->end()); + } +}; + +} // namespace jngen + +using jngen::VariantArray; From 3246671060df1420411efc54d8cd14d5f0674d8e Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Mon, 20 Mar 2017 14:15:49 +0300 Subject: [PATCH 04/10] connected() switch; fix a bug with finalized_ --- dsu.h | 2 +- generic_graph.h | 2 +- graph.h | 32 ++++++++++++++++++-------------- tests/dsu.cpp | 6 +++--- tests/generic_graph.cpp | 6 +++--- tests/tree.cpp | 6 +++--- tree.h | 2 +- 7 files changed, 30 insertions(+), 26 deletions(-) diff --git a/dsu.h b/dsu.h index 8865451..d669983 100644 --- a/dsu.h +++ b/dsu.h @@ -35,7 +35,7 @@ class Dsu { return true; } - bool connected() const { return components <= 1; } + bool isConnected() const { return components <= 1; } private: std::vector parent; diff --git a/generic_graph.h b/generic_graph.h index 91360aa..3a9468d 100644 --- a/generic_graph.h +++ b/generic_graph.h @@ -21,7 +21,7 @@ class GenericGraph { virtual int m() const { return numEdges_; } virtual void addEdge(int u, int v); - virtual bool connected() const { return dsu_.connected(); } + virtual bool isConnected() const { return dsu_.isConnected(); } virtual int vertexLabel(int v) const { return vertexLabel_[v]; } virtual int vertexByLabel(int v) const { return vertexByLabel_[v]; } diff --git a/graph.h b/graph.h index 05a6b33..e8bdf29 100644 --- a/graph.h +++ b/graph.h @@ -36,14 +36,15 @@ class Graph : public ReprProxy, public GenericGraph { Graph& allowLoops(bool value = true); Graph& allowMulti(bool value = true); + Graph& connected(bool value = true); int n() const { return self().GenericGraph::n(); } int m() const { return self().GenericGraph::m(); } void addEdge(int u, int v) { self().GenericGraph::addEdge(u, v); } - bool connected() const { - return self().GenericGraph::connected(); + bool isConnected() const { + return self().GenericGraph::isConnected(); } Array edges(int v) const { return self().GenericGraph::edges(v); @@ -81,8 +82,8 @@ class GraphBuilder { return graph_; } - GraphBuilder(int n, int m, bool connected) : - n_(n), m_(m), connected_(connected) + GraphBuilder(int n, int m) : + n_(n), m_(m) { } void allowLoops(bool value) { @@ -93,16 +94,20 @@ class GraphBuilder { multiEdges_ = value; } + void connected(bool value) { + connected_ = value; + } + private: void build(); int n_; int m_; - bool connected_; + bool connected_ = false; bool multiEdges_ = false; bool loops_ = false; - bool finalized_; + bool finalized_ = false; Graph graph_; }; @@ -123,6 +128,12 @@ inline Graph& Graph::allowMulti(bool value) { return *this; } +inline Graph& Graph::connected(bool value) { + ensure(builder_, "Cannot modify the graph which is already built"); + builder_->connected(value); + return *this; +} + inline void GraphBuilder::build() { // the probability distribution is not uniform in some cases // but we forget about it for now. @@ -179,14 +190,7 @@ inline void GraphBuilder::build() { Graph Graph::random(int n, int m) { Graph g; - auto builder = std::make_shared(n, m, false); - g.setBuilder(builder); - return g; -} - -Graph Graph::randomConnected(int n, int m) { - Graph g; - auto builder = std::make_shared(n, m, true); + auto builder = std::make_shared(n, m); g.setBuilder(builder); return g; } diff --git a/tests/dsu.cpp b/tests/dsu.cpp index 0c367da..b676a1f 100644 --- a/tests/dsu.cpp +++ b/tests/dsu.cpp @@ -7,17 +7,17 @@ using jngen::Dsu; BOOST_AUTO_TEST_CASE(dsu) { Dsu d; - BOOST_CHECK(d.connected()); + BOOST_CHECK(d.isConnected()); d.getParent(4); - BOOST_CHECK(!d.connected()); + BOOST_CHECK(!d.isConnected()); BOOST_CHECK(d.link(1, 2)); BOOST_CHECK(d.link(3, 4)); BOOST_CHECK(d.link(1, 4)); BOOST_CHECK(!d.link(2, 3)); BOOST_CHECK(d.link(0, 3)); - BOOST_CHECK(d.connected()); + BOOST_CHECK(d.isConnected()); BOOST_CHECK(!d.link(0, 0)); } diff --git a/tests/generic_graph.cpp b/tests/generic_graph.cpp index aa7ca20..40d43aa 100644 --- a/tests/generic_graph.cpp +++ b/tests/generic_graph.cpp @@ -11,7 +11,7 @@ BOOST_AUTO_TEST_CASE(basics) { BOOST_CHECK(gg.n() == 0); BOOST_CHECK(gg.m() == 0); - BOOST_CHECK(gg.connected()); + BOOST_CHECK(gg.isConnected()); BOOST_CHECK(gg.edges().empty()); gg.addEdge(0, 2); @@ -21,10 +21,10 @@ BOOST_AUTO_TEST_CASE(basics) { BOOST_CHECK(gg.edges()[1] == std::make_pair(1, 3)); BOOST_CHECK(gg.n() == 4); BOOST_CHECK(gg.m() == 2); - BOOST_CHECK(!gg.connected()); + BOOST_CHECK(!gg.isConnected()); gg.addEdge(0, 3); - BOOST_CHECK(gg.connected()); + BOOST_CHECK(gg.isConnected()); gg.addEdge(0, 3); BOOST_CHECK(gg.m() == 4); diff --git a/tests/tree.cpp b/tests/tree.cpp index 8d43088..acb717c 100644 --- a/tests/tree.cpp +++ b/tests/tree.cpp @@ -12,11 +12,11 @@ BOOST_AUTO_TEST_CASE(manual_construction) { BOOST_CHECK_EQUAL(t.n(), 1); BOOST_CHECK_EQUAL(t.m(), 0); - BOOST_CHECK(t.connected()); + BOOST_CHECK(t.isConnected()); t.addEdge(0, 1); t.addEdge(2, 3); - BOOST_CHECK(!t.connected()); + BOOST_CHECK(!t.isConnected()); t.addEdge(1, 2); @@ -51,7 +51,7 @@ int findDiameter( std::vector& centers, std::vector& dist) { - BOOST_REQUIRE(t.connected()); + BOOST_REQUIRE(t.isConnected()); std::vector dist1(t.n(), -1); std::vector dist2(t.n(), -1); diff --git a/tree.h b/tree.h index 037aefc..1ed90d4 100644 --- a/tree.h +++ b/tree.h @@ -90,7 +90,7 @@ Tree Tree::glue(int vInThis, const Tree& other, int vInOther) { } JNGEN_DECLARE_SIMPLE_PRINTER(Tree, 2) { - ensure(t.connected(), "Tree is not connected :("); + ensure(t.isConnected(), "Tree is not connected :("); if (mod.printParents) { out << "Printing parents is not supported yet"; From 9b1d699284df96c9312ecf8addf749cb275a710c Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Mon, 20 Mar 2017 16:58:31 +0300 Subject: [PATCH 05/10] Store edges list in graph --- generic_graph.h | 101 +++++++++++++++++++++++++++++++++--------------- graph.h | 3 ++ tree.h | 5 +++ 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/generic_graph.h b/generic_graph.h index 3a9468d..fcf1c7a 100644 --- a/generic_graph.h +++ b/generic_graph.h @@ -20,12 +20,15 @@ class GenericGraph { virtual int n() const { return adjList_.size(); } virtual int m() const { return numEdges_; } + // u, v: labels virtual void addEdge(int u, int v); virtual bool isConnected() const { return dsu_.isConnected(); } virtual int vertexLabel(int v) const { return vertexLabel_[v]; } virtual int vertexByLabel(int v) const { return vertexByLabel_[v]; } + // v: label + // return: array

(); + } - for (const auto& e: other.edges()) { - t.addEdge(newLabel(e.first), newLabel(e.second)); + void copy(char* dst) const { + if (this->type() == MY_ID) { + new(dst) T(*reinterpret_cast(this->data())); + } else { + Base::copy(dst); + } } - assert(t.n() == n() + other.n() - 1); + void setType(int typeIndex) { + if (typeIndex == MY_ID) { + if (this->type() != NO_TYPE) { + throw; + } + assign(T{}); + } else { + Base::setType(typeIndex); + } + } - return t; -} + template + void applyVisitor(V&& v) const { + if (this->type() == MY_ID) { + v(*reinterpret_cast(this->data())); + } else { + Base::applyVisitor(v); + } + } -JNGEN_DECLARE_SIMPLE_PRINTER(Tree, 2) { - ensure(t.connected(), "Tree is not connected :("); + using Base::assign; - if (mod.printParents) { - out << "Printing parents is not supported yet"; - } else if (mod.printEdges) { - t.doPrintEdges(out, mod); + void assign(const T& t) { + if (this->type() == NO_TYPE) { + new(this->data()) T; + this->type() = MY_ID; + } + + ref() = t; + } + +private: + T& ref() { return *reinterpret_cast(this->data()); } + +public: + operator T() const { + if (this->type() == MY_ID) { + return *reinterpret_cast(this->data()); + } else { + return T(); + } + } +}; + +template +class Variant : public VariantImpl<0, Args...> { + using Base = VariantImpl<0, Args...>; + +public: + Variant() {} + + Variant(const Variant& other) { + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + } + + Variant& operator=(const Variant& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.copy(this->data()); + unsafeType() = other.type(); + } + return *this; + } + + Variant(Variant&& other) { + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + } + + Variant& operator=(Variant&& other) { + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + if (other.type() != NO_TYPE) { + other.move(this->data()); + unsafeType() = other.type(); + other.unsafeType() = NO_TYPE; + } + return *this; + } + + ~Variant() { + if (type() != NO_TYPE) { + this->doDestroy(); + } + } + + template + Variant(const T& t) : Variant() { + this->assign(t); + } + + template + T& ref() { + return *ptr(); + } + + template + const T& cref() { + auto ptr = cptr(); + if (ptr == 0) { + throw std::logic_error("jngen::Variant: taking a reference for" + " a type which is not active now"); + } + return *ptr; + } + + template + void applyVisitor(V&& v) const { + Base::applyVisitor(v); + } + + int type() const { return Base::type(); } + + void setType(int typeIndex) { + if (typeIndex == NO_TYPE) { + throw std::logic_error("jngen::Variant::setType():" + " calling with NO_TYPE is invalid"); + } + if (this->type() == typeIndex) { + return; + } + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + Base::setType(typeIndex); + } + + bool empty() const { return Base::type() == NO_TYPE; } + + template + constexpr static bool hasType() { + return Base::template typeId() != NO_TYPE; + } + +private: + template::type> + T* ptr() { + if (type() != this->template typeId()) { + if (type() != NO_TYPE) { + this->doDestroy(); + } + ::new(this->data()) T; + unsafeType() = this->template typeId(); + } + return reinterpret_cast(this->data()); + } + + template::type> + const T* cptr() const { + if (type() != this->template typeId()) { + return nullptr; + } + return reinterpret_cast(this->data()); + } + + int& unsafeType() { + return Base::type(); + } +}; + +struct OstreamVisitor { + template + void operator()(const T& t) { + JNGEN_PRINT(t); + } + std::ostream& out; + const OutputModifier& mod; +}; + +} // namespace variant_detail + +using variant_detail::Variant; + +template +JNGEN_DECLARE_SIMPLE_PRINTER(Variant, 5) { + if (t.type() == jngen::variant_detail::NO_TYPE) { + out << "{empty variant}"; } else { - ensure(false, "Print mode is unknown"); + t.applyVisitor(jngen::variant_detail::OstreamVisitor{out, mod}); } } -// Tree generators go here +} // namespace jngen -inline Tree Tree::bamboo(size_t size) { - Tree t; - for (size_t i = 0; i + 1 < size; ++i) { - t.addEdge(i, i+1); + +#include +#include + +namespace jngen { + +template +class VariantArray : public GenericArray> { +public: + using Base = GenericArray>; + using BaseVariant = Variant; + + using Base::Base; + + VariantArray() {} + + /* implicit */ VariantArray(const Base& base) : + Base(base) + { } + + template()>::type> + VariantArray(const GenericArray& other) { + std::copy(other.begin(), other.end(), std::back_inserter(*this)); } - return t; + + template()>::type> + VariantArray(GenericArray&& other) { + std::move(other.begin(), other.end(), std::back_inserter(*this)); + GenericArray().swap(other); + } + + template()>::type> + operator GenericArray() const + { + return GenericArray(this->begin(), this->end()); + } + + bool hasNonEmpty() const { + for (const auto& x: *this) { + if (!x.empty()) { + return true; + } + } + return false; + } + + int anyType() const { + for (const auto& x: *this) { + if (!x.empty()) { + return x.type(); + } + } + return 0; + } + +}; + +} // namespace jngen + +#include +#include + + +namespace jngen { + +#define JNGEN_DEFAULT_WEIGHT_TYPES int, double, std::string, std::pair + +#if defined(JNGEN_EXTRA_WEIGHT_TYPES) +#define JNGEN_WEIGHT_TYPES JNGEN_DEFAULT_WEIGHT_TYPES , JNGEN_EXTRA_WEIGHT_TYPES +#else +#define JNGEN_WEIGHT_TYPES JNGEN_DEFAULT_WEIGHT_TYPES +#endif + +using Weight = Variant; +using WeightArray = VariantArray; + +} // namespace jngen + +using jngen::Weight; +using jngen::WeightArray; + + +#include +#include +#include +#include +#include +#include + +namespace jngen { + +class GenericGraph { +public: + virtual ~GenericGraph() {} + + virtual int n() const { return adjList_.size(); } + virtual int m() const { return numEdges_; } + + // u, v: labels + virtual void addEdge(int u, int v, const Weight& w = Weight{}); + virtual bool isConnected() const { return dsu_.isConnected(); } + + virtual int vertexLabel(int v) const { return vertexLabel_[v]; } + virtual int vertexByLabel(int v) const { return vertexByLabel_[v]; } + + // v: label + // return: array

(); +inline Tree Tree::star(size_t size) { + Tree t; + for (size_t i = 1; i < size; ++i) { + t.addEdge(0, i); } + t.normalizeEdges(); + return t; +} - void copy(char* dst) const { - if (this->type() == MY_ID) { - new(dst) T(*reinterpret_cast(this->data())); - } else { - Base::copy(dst); - } +inline Tree Tree::caterpillar(size_t length, size_t size) { + ensure(length <= size); + Tree t = Tree::bamboo(length); + for (size_t i = length; i < size; ++i) { + t.addEdge(rnd.next(length), i); } + t.normalizeEdges(); + return t; +} - template - void applyVisitor(V&& v) const { - if (this->type() == MY_ID) { - v(*reinterpret_cast(this->data())); - } else { - Base::applyVisitor(v); - } - } +} // namespace jngen - using Base::assign; +using jngen::Tree; - void assign(const T& t) { - if (this->type() == NO_TYPE) { - new(this->data()) T; - this->type() = MY_ID; - } - ref() = t; - } +#include +#include +#include +#include -private: - T& ref() { return *reinterpret_cast(this->data()); } +/* Directed graphs are not supported yet, and Graph class itself + * is pretty useless. Sorry for now. + */ + +namespace jngen { + +// TODO: make GraphBuilder subclass of Graph +class GraphBuilder; +class Graph : public ReprProxy, public GenericGraph { + friend class GraphBuilder; public: - operator T() const { - if (this->type() == MY_ID) { - return *reinterpret_cast(this->data()); - } else { - return T(); - } + virtual ~Graph() {} + Graph() {} + + Graph(int n) { + extend(n); } -}; -template -class Variant : public VariantImpl<0, Args...> { - using Base = VariantImpl<0, Args...>; + Graph(const GenericGraph& gg) : GenericGraph(gg) {} + + void setN(int n); -public: - Variant() {} + static Graph random(int n, int m); - Variant(const Variant& other) { - if (other.type() != NO_TYPE) { - other.copy(this->data()); - unsafeType() = other.type(); - } - } + Graph& allowLoops(bool value = true); + Graph& allowMulti(bool value = true); + Graph& connected(bool value = true); - Variant& operator=(const Variant& other) { - if (this->type() != NO_TYPE) { - this->doDestroy(); - } - if (other.type() != NO_TYPE) { - other.copy(this->data()); - unsafeType() = other.type(); - } - return *this; + int n() const override { return self().GenericGraph::n(); } + int m() const override { return self().GenericGraph::m(); } + void addEdge(int u, int v, const Weight& w = Weight{}) override { + self().GenericGraph::addEdge(u, v, w); } - - Variant(Variant&& other) { - if (other.type() != NO_TYPE) { - other.move(this->data()); - unsafeType() = other.type(); - other.unsafeType() = NO_TYPE; - } + bool isConnected() const override { + return self().GenericGraph::isConnected(); } - - Variant& operator=(Variant&& other) { - if (this->type() != NO_TYPE) { - this->doDestroy(); - } - if (other.type() != NO_TYPE) { - other.move(this->data()); - unsafeType() = other.type(); - other.unsafeType() = NO_TYPE; - } - return *this; + Array edges(int v) const override { + return self().GenericGraph::edges(v); } - - ~Variant() { - if (type() != NO_TYPE) { - this->doDestroy(); - } + Arrayp edges() const override { + return self().GenericGraph::edges(); } - - template - Variant(const T& t) : Variant() { - this->assign(t); + int vertexLabel(int v) const override { + return self().GenericGraph::vertexLabel(v); } - - template - T& ref() { - return *ptr(); + int vertexByLabel(int v) const override { + return self().GenericGraph::vertexByLabel(v); } - template - const T& cref() { - auto ptr = cptr(); - if (ptr == 0) { - throw std::logic_error("jngen::Variant: taking a reference for" - " a type which is not active now"); - } - return *ptr; - } + Graph& shuffle(); + Graph shuffled() const; - template - void applyVisitor(V&& v) const { - Base::applyVisitor(v); +private: + void setBuilder(std::shared_ptr builder) { + builder_ = builder; } - int type() const { return Base::type(); } + const Graph& self() const; + Graph& self(); - template - constexpr static bool hasType() { - return Base::template typeId() != NO_TYPE; - } + std::shared_ptr builder_; +}; -private: - template::type> - T* ptr() { - if (type() != this->template typeId()) { - if (type() != NO_TYPE) { - this->doDestroy(); - } - ::new(this->data()) T; - unsafeType() = this->template typeId(); +class GraphBuilder { +public: + const Graph& graph() { + if (!finalized_) { + build(); } - return reinterpret_cast(this->data()); + return graph_; } - template::type> - const T* cptr() const { - if (type() != this->template typeId()) { - return nullptr; - } - return reinterpret_cast(this->data()); + GraphBuilder(int n, int m) : + n_(n), m_(m) + { } + + void allowLoops(bool value) { + loops_ = value; } - int& unsafeType() { - return Base::type(); + void allowMulti(bool value) { + multiEdges_ = value; } -}; -struct OstreamVisitor { - template - void operator()(const T& t) { - auto mod = OutputModifier(); - JNGEN_PRINT(t); + void connected(bool value) { + connected_ = value; } - std::ostream& out; + +private: + void build(); + + int n_; + int m_; + bool connected_ = false; + bool multiEdges_ = false; + bool loops_ = false; + + bool finalized_ = false; + Graph graph_; }; -} // namespace variant_detail +inline void Graph::setN(int n) { + ensure(n >= this->n(), "Cannot lessen number of vertices in the graph"); + extend(n); +} -using variant_detail::Variant; +inline Graph& Graph::allowLoops(bool value) { + ensure(builder_, "Cannot modify the graph which is already built"); + builder_->allowLoops(value); + return *this; +} -template -JNGEN_DECLARE_SIMPLE_PRINTER(Variant, 5) { - (void)mod; - if (t.type() == jngen::variant_detail::NO_TYPE) { - out << "{empty variant}"; - } else { - t.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); - } +inline Graph& Graph::allowMulti(bool value) { + ensure(builder_, "Cannot modify the graph which is already built"); + builder_->allowMulti(value); + return *this; } -} // namespace jngen +inline Graph& Graph::connected(bool value) { + ensure(builder_, "Cannot modify the graph which is already built"); + builder_->connected(value); + return *this; +} -using jngen::Variant; +inline void GraphBuilder::build() { + // the probability distribution is not uniform in some cases + // but we forget about it for now. + ensure(!finalized_); + finalized_ = true; -#include -#include + int n = n_; + int m = m_; -namespace jngen { + if (!multiEdges_) { + long long maxEdges = static_cast(n) * + (n + (loops_ ? 1 : -1)) / 2; + ensure(m_ <= maxEdges, "Too many edges in the graph"); + } -template -class VariantArray : public GenericArray> { -public: - using Base = GenericArray>; - using BaseVariant = Variant; + std::set> usedEdges; - using Base::Base; + if (connected_) { + ensure(m_ >= n_ - 1, "Not enough edges for a connected graph"); + auto treeEdges = Tree::randomPrufer(n).edges(); + usedEdges.insert(treeEdges.begin(), treeEdges.end()); + ensure(usedEdges.size() == static_cast(n - 1)); + } - VariantArray() {} + auto edgeIsGood = [&usedEdges, this](const std::pair& edge) { + if (!loops_ && edge.first == edge.second) { + return false; + } + if (!multiEdges_ && usedEdges.count(edge)) { + return false; + } + return true; + }; - /* implicit */ VariantArray(const Base& base) : - Base(base) - { } + Arrayp result(usedEdges.begin(), usedEdges.end()); - template()>::type> - VariantArray(const GenericArray& other) { - std::copy(other.begin(), other.end(), std::back_inserter(*this)); + while (result.size() < static_cast(m)) { + auto edge = rnd.tnext>(n, opair); + if (edgeIsGood(edge)) { + usedEdges.insert(edge); + result.push_back(edge); + } } - template()>::type> - VariantArray(GenericArray&& other) { - std::move(other.begin(), other.end(), std::back_inserter(*this)); - GenericArray().swap(other); + ensure(result.size() == static_cast(m), + "[INTERNAL ASSERT] Not enough edges found"); + + graph_.setN(n); + for (const auto& edge: result) { + graph_.addEdge(edge.first, edge.second); } - template()>::type> - operator GenericArray() const - { - return GenericArray(this->begin(), this->end()); + graph_.normalizeEdges(); +} + +Graph Graph::random(int n, int m) { + Graph g; + auto builder = std::make_shared(n, m); + g.setBuilder(builder); + return g; +} + +inline Graph& Graph::shuffle() { + self().doShuffle(); + return *this; +} + +inline Graph Graph::shuffled() const { + Graph g = self(); + return g.shuffle(); +} + +const Graph& Graph::self() const { + return builder_ ? builder_->graph() : *this; +} + +Graph& Graph::self() { + if (builder_) { + *this = builder_->graph(); + builder_.reset(); } -}; + return *this; +} + +JNGEN_DECLARE_SIMPLE_PRINTER(Graph, 2) { + t.doPrintEdges(out, mod); +} } // namespace jngen -using jngen::VariantArray; +using jngen::Graph; diff --git a/printers.h b/printers.h index b8e7a5c..c482c4c 100644 --- a/printers.h +++ b/printers.h @@ -77,6 +77,9 @@ void printValue(std::ostream& out, const type& t,\ #define JNGEN_PRINT(value)\ printValue(out, value, mod, PTagMax{}) +#define JNGEN_PRINT_NO_MOD(value)\ +printValue(out, value, OutputModifier{}, PTagMax{}) + JNGEN_DECLARE_PRINTER(!JNGEN_HAS_OSTREAM(), 0) { // can't just write 'false' here because assertion always fails diff --git a/tests/graph.cpp b/tests/graph.cpp index cac096b..8f8c57c 100644 --- a/tests/graph.cpp +++ b/tests/graph.cpp @@ -14,5 +14,5 @@ BOOST_AUTO_TEST_CASE(output) { std::ostringstream ss; ss << g.printN().printM().add1() << std::endl; - BOOST_CHECK_EQUAL(ss.str(), "4 6\n1 3\n1 4\n1 4\n2 4\n2 3\n2 3\n"); + BOOST_CHECK_EQUAL(ss.str(), "4 6\n1 3\n2 4\n1 4\n1 4\n2 3\n3 2\n"); } diff --git a/tests/repr.cpp b/tests/repr.cpp index 58b2e4f..6deac38 100644 --- a/tests/repr.cpp +++ b/tests/repr.cpp @@ -46,10 +46,10 @@ BOOST_AUTO_TEST_CASE(output_mod_graph_and_tree) { g.addEdge(0, 2); g.setN(4); - checkOutput(g, "0 1\n0 2\n1 2"); - checkOutput(g.printN(), "4\n0 1\n0 2\n1 2"); - checkOutput(g.printN().printM(), "4 3\n0 1\n0 2\n1 2"); - checkOutput(g.printM().add1(), "3\n1 2\n1 3\n2 3"); + checkOutput(g, "0 1\n1 2\n0 2"); + checkOutput(g.printN(), "4\n0 1\n1 2\n0 2"); + checkOutput(g.printN().printM(), "4 3\n0 1\n1 2\n0 2"); + checkOutput(g.printM().add1(), "3\n1 2\n2 3\n1 3"); Tree t = Tree::bamboo(3); checkOutput(t, "0 1\n1 2"); diff --git a/tests/tree.cpp b/tests/tree.cpp index acb717c..91ea29c 100644 --- a/tests/tree.cpp +++ b/tests/tree.cpp @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(manual_construction) { std::ostringstream ss; ss << t.printN().add1() << std::endl; - BOOST_CHECK_EQUAL(ss.str(), "4\n1 2\n2 3\n3 4\n"); + BOOST_CHECK_EQUAL(ss.str(), "4\n1 2\n3 4\n2 3\n"); } void dfs(int v, int anc, const Tree& t, std::vector& dist) { diff --git a/tree.h b/tree.h index f7ef7c9..daad67d 100644 --- a/tree.h +++ b/tree.h @@ -18,7 +18,7 @@ class Tree : public ReprProxy, public GenericGraph { extend(1); } - void addEdge(int u, int v); + void addEdge(int u, int v, const Weight& w = Weight{}) override; Tree& shuffle(); Tree shuffled() const; @@ -33,7 +33,7 @@ class Tree : public ReprProxy, public GenericGraph { static Tree caterpillar(size_t length, size_t size); }; -inline void Tree::addEdge(int u, int v) { +inline void Tree::addEdge(int u, int v, const Weight& w) { extend(std::max(u, v) + 1); u = vertexByLabel(u); @@ -43,6 +43,10 @@ inline void Tree::addEdge(int u, int v) { ensure(ret, "A cycle appeared in the tree :("); addEdgeUnsafe(u, v); + + if (!w.empty()) { + setEdgeWeight(m() - 1, w); + } } inline Tree& Tree::shuffle() { diff --git a/variant.h b/variant.h index cce8e78..f53fb20 100644 --- a/variant.h +++ b/variant.h @@ -58,6 +58,10 @@ class VariantImpl { memmove(dst, data(), Size); } + void setType(int) { + throw; + } + template void applyVisitor(V&&) const { throw; @@ -101,6 +105,17 @@ class VariantImpl : public VariantImpl< } } + void setType(int typeIndex) { + if (typeIndex == MY_ID) { + if (this->type() != NO_TYPE) { + throw; + } + assign(T{}); + } else { + Base::setType(typeIndex); + } + } + template void applyVisitor(V&& v) const { if (this->type() == MY_ID) { @@ -212,6 +227,22 @@ class Variant : public VariantImpl<0, Args...> { int type() const { return Base::type(); } + void setType(int typeIndex) { + if (typeIndex == NO_TYPE) { + throw std::logic_error("jngen::Variant::setType():" + " calling with NO_TYPE is invalid"); + } + if (this->type() == typeIndex) { + return; + } + if (this->type() != NO_TYPE) { + this->doDestroy(); + } + Base::setType(typeIndex); + } + + bool empty() const { return Base::type() == NO_TYPE; } + template constexpr static bool hasType() { return Base::template typeId() != NO_TYPE; @@ -246,10 +277,10 @@ class Variant : public VariantImpl<0, Args...> { struct OstreamVisitor { template void operator()(const T& t) { - auto mod = OutputModifier(); JNGEN_PRINT(t); } std::ostream& out; + const OutputModifier& mod; }; } // namespace variant_detail @@ -258,14 +289,11 @@ using variant_detail::Variant; template JNGEN_DECLARE_SIMPLE_PRINTER(Variant, 5) { - (void)mod; if (t.type() == jngen::variant_detail::NO_TYPE) { out << "{empty variant}"; } else { - t.applyVisitor(jngen::variant_detail::OstreamVisitor{out}); + t.applyVisitor(jngen::variant_detail::OstreamVisitor{out, mod}); } } } // namespace jngen - -using jngen::Variant; diff --git a/variant_array.h b/variant_array.h index 51b91f5..4bcfbe3 100644 --- a/variant_array.h +++ b/variant_array.h @@ -42,8 +42,25 @@ class VariantArray : public GenericArray> { { return GenericArray(this->begin(), this->end()); } + + bool hasNonEmpty() const { + for (const auto& x: *this) { + if (!x.empty()) { + return true; + } + } + return false; + } + + int anyType() const { + for (const auto& x: *this) { + if (!x.empty()) { + return x.type(); + } + } + return 0; + } + }; } // namespace jngen - -using jngen::VariantArray; From d6fcdfee3c3befe5f53d398d36d6f4dd17cdc982 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Tue, 21 Mar 2017 20:42:22 +0300 Subject: [PATCH 09/10] Test for edge weights --- generic_graph.h | 12 ++++++------ geometry.h | 3 +++ graph.h | 18 ++++++++++++++++++ jngen.h | 33 +++++++++++++++++++++++++++------ tests/graph.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 12 deletions(-) diff --git a/generic_graph.h b/generic_graph.h index 80c958c..29ad170 100644 --- a/generic_graph.h +++ b/generic_graph.h @@ -37,7 +37,7 @@ class GenericGraph { // order: by labels // TODO: think about ordering here - void setVertexWeights(const WeightArray& weights) { + virtual void setVertexWeights(const WeightArray& weights) { ensure(static_cast(weights.size()) == n()); vertexWeights_.resize(n()); for (int i = 0; i < n(); ++i) { @@ -46,7 +46,7 @@ class GenericGraph { } // v: label - void setVertexWeight(int v, const Weight& weight) { + virtual void setVertexWeight(int v, const Weight& weight) { ensure(v < n()); v = vertexByLabel(v); @@ -54,19 +54,19 @@ class GenericGraph { vertexWeights_[v] = weight; } - void setEdgeWeights(const WeightArray& weights) { + virtual void setEdgeWeights(const WeightArray& weights) { ensure(static_cast(weights.size()) == m()); edgeWeights_ = weights; } - void setEdgeWeight(size_t index, const Weight& weight) { + virtual void setEdgeWeight(size_t index, const Weight& weight) { ensure(static_cast(index) < m()); edgeWeights_.extend(index + 1); edgeWeights_[index] = weight; } // v: label - Weight vertexWeight(int v) const { + virtual Weight vertexWeight(int v) const { size_t index = vertexByLabel(v); if (index < vertexWeights_.size()) { return Weight{}; @@ -74,7 +74,7 @@ class GenericGraph { return vertexWeights_[index]; } - Weight edgeWeight(size_t index) const { + virtual Weight edgeWeight(size_t index) const { if (index < edgeWeights_.size()) { return Weight{}; } diff --git a/geometry.h b/geometry.h index b1aed17..23aefdf 100644 --- a/geometry.h +++ b/geometry.h @@ -281,6 +281,9 @@ class GeometryRandom { using jngen::Point; using jngen::Pointf; +using jngen::Polygon; +using jngen::Polygonf; + using jngen::rndg; using jngen::eps; diff --git a/graph.h b/graph.h index 699d040..52a38a9 100644 --- a/graph.h +++ b/graph.h @@ -52,6 +52,24 @@ class Graph : public ReprProxy, public GenericGraph { Arrayp edges() const override { return self().GenericGraph::edges(); } + virtual void setVertexWeights(const WeightArray& weights) override { + self().GenericGraph::setVertexWeights(weights); + } + virtual void setVertexWeight(int v, const Weight& weight) override { + self().GenericGraph::setVertexWeight(v, weight); + } + virtual void setEdgeWeights(const WeightArray& weights) override { + self().GenericGraph::setEdgeWeights(weights); + } + virtual void setEdgeWeight(size_t index, const Weight& weight) override { + self().GenericGraph::setEdgeWeight(index, weight); + } + virtual Weight vertexWeight(int v) const override { + return self().GenericGraph::vertexWeight(v); + } + virtual Weight edgeWeight(size_t index) const override { + return self().GenericGraph::edgeWeight(index); + } int vertexLabel(int v) const override { return self().GenericGraph::vertexLabel(v); } diff --git a/jngen.h b/jngen.h index 3bbae15..d050c74 100644 --- a/jngen.h +++ b/jngen.h @@ -1770,6 +1770,9 @@ class GeometryRandom { using jngen::Point; using jngen::Pointf; +using jngen::Polygon; +using jngen::Polygonf; + using jngen::rndg; using jngen::eps; @@ -2474,7 +2477,7 @@ class GenericGraph { // order: by labels // TODO: think about ordering here - void setVertexWeights(const WeightArray& weights) { + virtual void setVertexWeights(const WeightArray& weights) { ensure(static_cast(weights.size()) == n()); vertexWeights_.resize(n()); for (int i = 0; i < n(); ++i) { @@ -2483,7 +2486,7 @@ class GenericGraph { } // v: label - void setVertexWeight(int v, const Weight& weight) { + virtual void setVertexWeight(int v, const Weight& weight) { ensure(v < n()); v = vertexByLabel(v); @@ -2491,19 +2494,19 @@ class GenericGraph { vertexWeights_[v] = weight; } - void setEdgeWeights(const WeightArray& weights) { + virtual void setEdgeWeights(const WeightArray& weights) { ensure(static_cast(weights.size()) == m()); edgeWeights_ = weights; } - void setEdgeWeight(size_t index, const Weight& weight) { + virtual void setEdgeWeight(size_t index, const Weight& weight) { ensure(static_cast(index) < m()); edgeWeights_.extend(index + 1); edgeWeights_[index] = weight; } // v: label - Weight vertexWeight(int v) const { + virtual Weight vertexWeight(int v) const { size_t index = vertexByLabel(v); if (index < vertexWeights_.size()) { return Weight{}; @@ -2511,7 +2514,7 @@ class GenericGraph { return vertexWeights_[index]; } - Weight edgeWeight(size_t index) const { + virtual Weight edgeWeight(size_t index) const { if (index < edgeWeights_.size()) { return Weight{}; } @@ -3063,6 +3066,24 @@ class Graph : public ReprProxy, public GenericGraph { Arrayp edges() const override { return self().GenericGraph::edges(); } + virtual void setVertexWeights(const WeightArray& weights) override { + self().GenericGraph::setVertexWeights(weights); + } + virtual void setVertexWeight(int v, const Weight& weight) override { + self().GenericGraph::setVertexWeight(v, weight); + } + virtual void setEdgeWeights(const WeightArray& weights) override { + self().GenericGraph::setEdgeWeights(weights); + } + virtual void setEdgeWeight(size_t index, const Weight& weight) override { + self().GenericGraph::setEdgeWeight(index, weight); + } + virtual Weight vertexWeight(int v) const override { + return self().GenericGraph::vertexWeight(v); + } + virtual Weight edgeWeight(size_t index) const override { + return self().GenericGraph::edgeWeight(index); + } int vertexLabel(int v) const override { return self().GenericGraph::vertexLabel(v); } diff --git a/tests/graph.cpp b/tests/graph.cpp index 8f8c57c..665d2c0 100644 --- a/tests/graph.cpp +++ b/tests/graph.cpp @@ -16,3 +16,50 @@ BOOST_AUTO_TEST_CASE(output) { BOOST_CHECK_EQUAL(ss.str(), "4 6\n1 3\n2 4\n1 4\n1 4\n2 3\n3 2\n"); } + +BOOST_AUTO_TEST_CASE(weights_and_labelling) { + rnd.seed(123); + + Graph g = Graph::random(10, 20); + + g.setVertexWeights(Array::random(g.n(), 100)); + g.setEdgeWeights(Arrayf::random(g.m(), 1.5, 1.8)); + + g.setVertexWeight(5, "five"); + g.setVertexWeight(8, "eight"); + g.addEdge(5, 8, "5-8"); + + g.shuffle(); + + std::stringstream ss; + ss << g.printN().printM() << std::endl; + + int v1, v2; + int v5 = -1, v8 = -1; + std::string s; + ss >> v1 >> v2; + BOOST_CHECK_EQUAL(v1, g.n()); + BOOST_CHECK_EQUAL(v2, g.m()); + + for (int i = 0; i < g.n(); ++i) { + ss >> s; + if (s == "five") { + v5 = i; + } else if (s == "eight") { + v8 = i; + } + } + + BOOST_CHECK(v5 != -1 && v8 != -1); + + int count = 0; + for (int i = 0; i < g.m(); ++i) { + ss >> v1 >> v2 >> s; + if (s == "5-8") { + ++count; + BOOST_CHECK( (v1 == v5 && v2 == v8) || (v1 == v8 && v2 == v5) ); + } + } + + BOOST_CHECK_EQUAL(count, 1); +} From b8226cf536195b7dfbb199ef62d84d5e7b79af98 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Tue, 21 Mar 2017 23:15:56 +0300 Subject: [PATCH 10/10] extra semicolon --- jngen.h | 2 +- random.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jngen.h b/jngen.h index d050c74..960441c 100644 --- a/jngen.h +++ b/jngen.h @@ -471,7 +471,7 @@ Result uniformRandom(Result bound, Random& random, Source (Random::*method)()) { } } #endif -}; +} class Random { public: diff --git a/random.h b/random.h index 08da05b..eb0d71f 100644 --- a/random.h +++ b/random.h @@ -59,7 +59,7 @@ Result uniformRandom(Result bound, Random& random, Source (Random::*method)()) { } } #endif -}; +} class Random { public: