Files
problemas-para-competicao-p…/metro/src/jngen.h

7293 lines
179 KiB
C++

/*
Copyright (c) 2016-2018 Ivan Smirnov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
This file is automatically generated and is not supposed to be edited
directly. Source code can be found on https://github.com/ifsmirnov/jngen.
Follow the same link for docs and reference.
*/
#define JNGEN_VERSION 0.1
// https://github.com/ifsmirnov/jngen/issues/5
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
// this warning is buggy in clang >= 5
#if __clang_major__ >= 5
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-lambda-capture"
#endif
#define JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, name) \
int _ ## name = 0; \
Class& name(int val = 1) { _ ## name = val; return *this; }
#define JNGEN_CHAINING_TRAITS_INNER_0(Class)
#define JNGEN_CHAINING_TRAITS_INNER_1(Class, arg) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg)
#define JNGEN_CHAINING_TRAITS_INNER_2(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_1(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_3(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_2(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_4(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_3(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_5(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_4(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_6(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_5(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_7(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_6(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_8(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_7(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_9(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_8(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_10(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_9(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_11(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_10(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_12(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_11(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_13(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_12(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_14(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_13(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_15(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_14(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_16(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_15(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_17(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_16(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_18(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_17(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_19(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_18(Class, __VA_ARGS__)
#define JNGEN_CHAINING_TRAITS_INNER_20(Class, arg, ...) JNGEN_DEFINE_CHAINING_TRAITS_FIELD(Class, arg) JNGEN_CHAINING_TRAITS_INNER_19(Class, __VA_ARGS__)
#define JNGEN_GET_CHAINING_TRAITS_INNER_IMPL(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, x, ...) x
#define JNGEN_GET_CHAINING_TRAITS_INNER(...) JNGEN_GET_CHAINING_TRAITS_INNER_IMPL(\
__VA_ARGS__,\
JNGEN_CHAINING_TRAITS_INNER_20,\
JNGEN_CHAINING_TRAITS_INNER_19,\
JNGEN_CHAINING_TRAITS_INNER_18,\
JNGEN_CHAINING_TRAITS_INNER_17,\
JNGEN_CHAINING_TRAITS_INNER_16,\
JNGEN_CHAINING_TRAITS_INNER_15,\
JNGEN_CHAINING_TRAITS_INNER_14,\
JNGEN_CHAINING_TRAITS_INNER_13,\
JNGEN_CHAINING_TRAITS_INNER_12,\
JNGEN_CHAINING_TRAITS_INNER_11,\
JNGEN_CHAINING_TRAITS_INNER_10,\
JNGEN_CHAINING_TRAITS_INNER_9,\
JNGEN_CHAINING_TRAITS_INNER_8,\
JNGEN_CHAINING_TRAITS_INNER_7,\
JNGEN_CHAINING_TRAITS_INNER_6,\
JNGEN_CHAINING_TRAITS_INNER_5,\
JNGEN_CHAINING_TRAITS_INNER_4,\
JNGEN_CHAINING_TRAITS_INNER_3,\
JNGEN_CHAINING_TRAITS_INNER_2,\
JNGEN_CHAINING_TRAITS_INNER_1,\
JNGEN_CHAINING_TRAITS_INNER_0)
#define JNGEN_CHAINING_TRAITS(Class, ...) \
struct Class { JNGEN_GET_CHAINING_TRAITS_INNER(__VA_ARGS__)(Class, __VA_ARGS__) };
namespace jngen {
struct Config {
bool generateLargeObjects = false;
bool largeOptionIndices = false;
bool normalizeEdges = true;
};
#ifdef JNGEN_DECLARE_ONLY
extern
#endif
Config config;
} // namespace jngen
using jngen::config;
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
#ifdef JNGEN_DECLARE_ONLY
#define JNGEN_EXTERN extern
#else
#define JNGEN_EXTERN
#endif
namespace jngen {
class Exception : public std::runtime_error {
public:
explicit Exception(const std::string& s) :
std::runtime_error("Assertion `" + s + "' failed.")
{ }
Exception(const std::string& assertMsg, const std::string& expl) :
std::runtime_error(expl + " (assertion `" + assertMsg + "' failed).")
{ }
};
class InternalException : public Exception {
public:
explicit InternalException(const std::string& s) : Exception(s) {}
InternalException(const std::string& assertMsg, const std::string& expl) :
Exception(assertMsg, expl)
{ }
};
} // namespace jngen
#define JNGEN_ENSURE1(exType, cond)\
do\
if (!(cond)) {\
throw exType(#cond);\
}\
while (false)
#define JNGEN_ENSURE2(exType, cond, msg)\
do\
if (!(cond)) {\
throw exType(#cond, msg);\
}\
while (false)
#define JNGEN_GET_MACRO(_1, _2, NAME, ...) NAME
#define ensure(...) JNGEN_GET_MACRO(__VA_ARGS__, JNGEN_ENSURE2, JNGEN_ENSURE1)\
(jngen::Exception, __VA_ARGS__)
#define ENSURE(...) JNGEN_GET_MACRO(__VA_ARGS__, JNGEN_ENSURE2, JNGEN_ENSURE1)\
(jngen::InternalException, __VA_ARGS__)
namespace jngen {
template<typename ... Args>
std::string format(const std::string& fmt, Args... args) {
constexpr static char BUF_SIZE = 64;
static char BUFFER[BUF_SIZE];
int bufSize = BUF_SIZE;
char *buf = BUFFER;
while (true) {
int ret = std::snprintf(buf, bufSize, fmt.c_str(), args...);
if (ret < bufSize) {
break;
}
if (bufSize != BUF_SIZE) {
delete[] buf;
}
bufSize *= 2;
buf = new char[bufSize];
}
std::string result(buf);
if (bufSize != BUF_SIZE) {
delete[] buf;
}
return result;
}
class ContextTimer {
public:
ContextTimer(const std::string& name) : name_(name) {
start_ = std::chrono::steady_clock::now();
}
ContextTimer() : ContextTimer("") {}
ContextTimer(const ContextTimer&) = delete;
ContextTimer& operator=(const ContextTimer&) = delete;
ContextTimer(ContextTimer&&) = delete;
ContextTimer& operator=(ContextTimer&&) = delete;
~ContextTimer() {
auto dif = std::chrono::steady_clock::now() - start_;
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(dif);
if (!name_.empty()) {
std::cerr << "[" << name_ << "] ";
}
std::cerr << ms.count() << " ms\n";
}
private:
std::string name_;
std::chrono::steady_clock::time_point start_;
};
template<typename F>
auto distribution(int n, F&& f) -> std::map<decltype(f()), int> {
std::map<decltype(f()), int> dist;
for (int i = 0; i < n; ++i) {
++dist[f()];
}
return dist;
}
inline void checkLargeParameter(int n) {
if (!config.generateLargeObjects) {
constexpr static int BOUND = 5e6;
ensure(
n <= BOUND,
"If you want to generate an object of size > 5'000'000, please set "
"'config.generateLargeObjects = true'.");
}
}
// Some type traits helpers. Based on ideas from TCPPPL v4.
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template<typename T>
using decay_t = typename std::decay<T>::type;
namespace util {
inline long long gcd(long long a, long long b) {
if (a < 0) {
a = -a;
}
if (b < 0) {
b = -b;
}
while (a && b) {
if (a > b) {
a %= b;
} else {
b %= a;
}
}
return a + b;
}
inline std::vector<std::string> split(std::string s, char delimiter) {
auto strip = [](std::string s) {
size_t l = 0;
while (l < s.size() && s[l] == ' ') {
++l;
}
s = s.substr(l);
while (!s.empty() && s.back() == ' ') {
s.pop_back();
}
return s;
};
std::vector<std::string> result;
s += delimiter;
std::string cur;
for (char c: s) {
if (c == delimiter) {
result.push_back(strip(cur));
cur.clear();
} else {
cur += c;
}
}
return result;
}
} // namespace util
} // namespace jngen
using jngen::format;
using jngen::ContextTimer;
using jngen::distribution;
#include <string>
#include <utility>
#include <vector>
#include <string>
namespace jngen {
namespace drawing {
using Color = std::string;
struct DrawingEngineState {
double width;
Color stroke;
Color fill;
double opacity;
};
class DrawingEngine {
public:
DrawingEngine() {}
virtual ~DrawingEngine() {}
virtual void drawPoint(double x, double y) = 0;
virtual void drawCircle(double x, double y, double r) = 0;
virtual void drawSegment(double x1, double y1, double x2, double y2) = 0;
virtual void drawPolygon(
const std::vector<std::pair<double, double>>& vertices) = 0;
virtual void drawText(
double x, double y, const std::string& s) = 0;
virtual void setWidth(double width) = 0;
virtual void setStroke(Color color) = 0;
virtual void setFill(Color color) = 0;
virtual void setOpacity(double opacity) = 0;
virtual double width() const = 0;
virtual Color stroke() const = 0;
virtual Color fill() const = 0;
virtual double opacity() const = 0;
DrawingEngineState saveState() {
return { width(), stroke(), fill(), opacity() };
}
void restoreState(const DrawingEngineState& state) {
setWidth(state.width);
setStroke(state.stroke);
setFill(state.fill);
setOpacity(state.opacity);
}
};
}} // namespace jngen::drawing
#include <sstream>
#include <string>
namespace jngen {
namespace drawing {
class SvgEngine : public DrawingEngine {
public:
SvgEngine(double x1 = 0, double y1 = 0, double x2 = 50, double y2 = 50);
virtual ~SvgEngine() {}
virtual void drawPoint(double x, double y) override;
virtual void drawCircle(double x, double y, double r) override;
virtual void drawSegment(
double x1, double y1, double x2, double y2) override;
virtual void drawPolygon(
const std::vector<std::pair<double, double>>& vertices) override;
virtual void drawText(
double x, double y, const std::string& s) override;
virtual void setWidth(double width) override;
virtual void setStroke(Color color) override;
virtual void setFill(Color color) override;
virtual void setOpacity(double opacity) override;
virtual double width() const override { return width_; }
virtual Color stroke() const override { return strokeColor_; }
virtual Color fill() const override { return fillColor_; }
virtual double opacity() const override { return opacity_; }
std::string serialize() const;
private:
double lerpX(double x) const;
double lerpY(double y) const;
double scaleSize(double size) const;
std::string getStyle() const;
std::ostringstream output_;
double width_;
Color strokeColor_;
Color fillColor_;
double opacity_;
double x1_, y1_, x2_, y2_; // borders
};
}} // namespace jngen::drawing
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_SVG_ENGINE_INL_H
#ifndef JNGEN_INCLUDE_SVG_ENGINE_INL_H
#error File "svg_engine_inl.h" must not be included directly.
#endif
#include <cmath>
#include <cstdlib>
namespace jngen {
namespace drawing {
static char buf[10000];
constexpr static double WIDTH_SCALE = 8;
constexpr static double CANVAS_SIZE = 2000;
constexpr static int FONT_SIZE = 64;
namespace {
const char* colorToString(const Color& color) {
const static char* NONE = "none";
if (color.empty()) {
return NONE;
}
return color.c_str();
}
// Given x \in [l, r], return linear interpolation to [L, R]
double lerp(double x, double l, double r, double L, double R) {
return L + (R - L) * ((x - l) / (r - l));
}
} // namespace
SvgEngine::SvgEngine(double x1, double y1, double x2, double y2) :
width_(1.0),
opacity_(1.0),
x1_(x1),
y1_(y1),
x2_(x2),
y2_(y2)
{ }
void SvgEngine::drawPoint(double x, double y) {
x = lerpX(x);
y = lerpY(y);
double w = width_ * WIDTH_SCALE * 1.5;
std::sprintf(
buf,
"<circle cx='%f' cy='%f' r='%f' fill='%s' opacity='%f'/>",
x, y, w, colorToString(strokeColor_), opacity_
);
output_ << buf << "\n";
}
void SvgEngine::drawCircle(double x, double y, double r) {
x = lerpX(x);
y = lerpY(y);
r = scaleSize(r);
std::sprintf(
buf,
"<circle cx='%f' cy='%f' r='%f' style='%s'/>",
x, y, r, getStyle().c_str()
);
output_ << buf << "\n";
}
void SvgEngine::drawPolygon(
const std::vector<std::pair<double, double>>& points)
{
output_ << "<polygon points='";
for (const auto& point: points) {
output_ << lerpX(point.first) << "," << lerpY(point.second) << " ";
}
output_ << "' style='" << getStyle() << "'/>\n";
}
void SvgEngine::drawSegment(
double x1, double y1, double x2, double y2)
{
x1 = lerpX(x1);
y1 = lerpY(y1);
x2 = lerpX(x2);
y2 = lerpY(y2);
if (std::fabs(x1 - x2) < 1e-9) {
x1 = std::round(x1);
x2 = std::round(x2);
}
if (std::fabs(y1 - y2) < 1e-9) {
y1 = std::round(y1);
y2 = std::round(y2);
}
std::sprintf(
buf,
"<line x1='%f' y1='%f' x2='%f' y2='%f' style='%s'/>",
x1, y1, x2, y2, getStyle().c_str()
);
output_ << buf << "\n";
}
void SvgEngine::drawText(
double x, double y, const std::string& s)
{
x = std::round(lerpX(x));
y = std::round(lerpY(y));
std::sprintf(
buf,
"<text x='%f' y='%f' font-size='%d' font-family='Helvetica'>%s</text>",
x, y, FONT_SIZE, s.c_str()
);
output_ << buf << "\n";
}
void SvgEngine::setWidth(double width) {
width_ = width;
}
void SvgEngine::setStroke(Color color) {
strokeColor_ = color;
}
void SvgEngine::setFill(Color color) {
fillColor_ = color;
}
void SvgEngine::setOpacity(double opacity) {
opacity_ = opacity;
}
std::string SvgEngine::serialize() const {
int offset = std::sprintf(
buf,
"<svg xmlns='http://www.w3.org/2000/svg' "
"viewBox='%f %f %f %f'>\n",
0.0, 0.0, CANVAS_SIZE, CANVAS_SIZE * (y2_ - y1_) / (x2_ - x1_)
);
std::sprintf(
buf + offset,
"<circle cx='%f' cy='%f' r='%f' fill='white'/>\n",
CANVAS_SIZE/2, CANVAS_SIZE/2,
std::hypot(CANVAS_SIZE, CANVAS_SIZE * (y2_ - y1_) / (x2_ - x1_))
);
return buf + output_.str() + "</svg>\n";
}
double SvgEngine::lerpX(double x) const {
return lerp(x, x1_, x2_, 0., CANVAS_SIZE);
}
double SvgEngine::lerpY(double y) const {
return lerp(y, y2_, y1_, 0., CANVAS_SIZE * (y2_ - y1_) / (x2_ - x1_));
}
double SvgEngine::scaleSize(double size) const {
return size * CANVAS_SIZE / (x2_ - x1_);
}
std::string SvgEngine::getStyle() const {
static char buf[1024];
std::sprintf(
buf,
"stroke-width:%f;stroke:%s;fill:%s;opacity:%f",
width_ * WIDTH_SCALE,
colorToString(strokeColor_),
colorToString(fillColor_),
opacity_
);
return buf;
}
}} // namespace jngen::drawing
#undef JNGEN_INCLUDE_SVG_ENGINE_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <algorithm>
#include <cmath>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace jngen {
namespace drawing {
class Drawer {
public:
Drawer();
template<typename P>
void point(const P& p);
template<typename T>
void point(T x, T y);
template<typename P>
void circle(const P& p, double radius);
template<typename T>
void circle(T x, T y, double radius);
template<typename P>
void segment(const P& p1, const P& p2);
template<typename T>
void segment(T x1, T y1, T x2, T y2);
template<typename P>
void polygon(const std::vector<P>& points);
template<typename P>
void polygon(std::initializer_list<P> points);
void setWidth(double width);
void setColor(const std::string& color);
void setStroke(const std::string& color);
void setFill(const std::string& color);
void setOpacity(double opacity);
void enableGrid(bool value) {
gridEnabled_ = value;
}
void dumpSvg(const std::string& filename);
private:
struct Point {
double x, y;
Point() {}
Point(double x, double y) : x(x), y(y) {}
};
template<typename T>
Point extractCoords(const T& pt) {
return Point(pt.x, pt.y);
}
template<typename T>
Point extractCoords(const std::pair<T, T>& pt) {
return Point(pt.first, pt.second);
}
typedef std::function<void(DrawingEngine*)> DrawRequest;
typedef std::pair<Point, Point> Bbox;
static Bbox emptyBbox();
static Bbox unite(const Bbox& lhs, const Bbox& rhs);
static Bbox bbox(const Point& p);
static Bbox bbox(const std::pair<Point, double>& circle);
Bbox getBbox() const;
static Bbox viewportByBbox(const Bbox& bbox);
void drawAll();
void drawGrid(const Bbox& bbox);
std::vector<DrawRequest> requests_;
DrawingEngine* engine_;
Bbox bbox_;
int requestId_ = 0;
bool gridEnabled_ = true;
};
template<typename P>
void Drawer::point(const P& p_) {
Point p = extractCoords(p_);
bbox_ = unite(bbox_ , bbox(Point(p.x, p.y)));
requests_.push_back([p](DrawingEngine* engine) {
engine->drawPoint(p.x, p.y);
});
}
template<typename T>
void Drawer::point(T x, T y) {
point(Point(x, y));
}
template<typename P>
void Drawer::circle(const P& p_, double radius) {
Point p = extractCoords(p_);
bbox_ = unite(bbox_ , bbox({Point(p.x, p.y), radius}));
requests_.push_back([p, radius](DrawingEngine* engine) {
engine->drawCircle(p.x, p.y, radius);
});
}
template<typename T>
void Drawer::circle(T x, T y, double radius) {
circle(Point(x, y), radius);
}
template<typename P>
void Drawer::segment(const P& p1_, const P& p2_) {
Point p1 = extractCoords(p1_);
Point p2 = extractCoords(p2_);
bbox_ = unite(bbox_ , bbox(Point(p1.x, p1.y)));
bbox_ = unite(bbox_ , bbox(Point(p2.x, p2.y)));
requests_.push_back([p1, p2](DrawingEngine* engine) {
engine->drawSegment(p1.x, p1.y, p2.x, p2.y);
});
}
template<typename T>
void Drawer::segment(T x1, T y1, T x2, T y2) {
segment(Point(x1, y1), Point(x2, y2));
}
template<typename P>
void Drawer::polygon(const std::vector<P>& points) {
for (const auto& p: points) {
bbox_ = unite(bbox_, bbox(extractCoords(p)));
}
requests_.push_back([points, this](DrawingEngine* engine) {
std::vector<std::pair<double, double>> enginePoints;
for (const auto& p: points) {
Point pt = extractCoords(p);
enginePoints.emplace_back(pt.x, pt.y);
}
engine->drawPolygon(enginePoints);
});
}
template<typename P>
void Drawer::polygon(std::initializer_list<P> points) {
polygon(std::vector<P>(points.begin(), points.end()));
}
#ifndef JNGEN_DECLARE_ONLY
Drawer::Drawer() : bbox_(emptyBbox()) {
setFill("");
setStroke("black");
}
void Drawer::setWidth(double width) {
requests_.push_back([width](DrawingEngine* engine) {
engine->setWidth(width);
});
}
void Drawer::setColor(const std::string& color) {
setStroke(color);
setFill(color);
}
void Drawer::setStroke(const std::string& color) {
requests_.push_back([color](DrawingEngine* engine) {
engine->setStroke(color);
});
}
void Drawer::setFill(const std::string& color) {
requests_.push_back([color](DrawingEngine* engine) {
engine->setFill(color);
});
}
void Drawer::setOpacity(double opacity) {
requests_.push_back([opacity](DrawingEngine* engine) {
engine->setOpacity(opacity);
});
}
Drawer::Bbox Drawer::emptyBbox() {
const static double inf = 1e18;
return { Point{inf, inf}, Point{-inf, -inf} };
}
Drawer::Bbox Drawer::unite(const Bbox& lhs, const Bbox& rhs) {
return Bbox{
Point{
std::min(lhs.first.x, rhs.first.x),
std::min(lhs.first.y, rhs.first.y)},
Point{
std::max(lhs.second.x, rhs.second.x),
std::max(lhs.second.y, rhs.second.y)}
};
}
Drawer::Bbox Drawer::bbox(const Point& p) {
return {p, p};
}
Drawer::Bbox Drawer::bbox(const std::pair<Point, double>& circle) {
Point p;
double radius;
std::tie(p, radius) = circle;
return {
Point{p.x - radius, p.y - radius},
Point{p.x + radius, p.y + radius}
};
}
/*
Given a bbox of points, returns a bbox with following properties:
- at least 5% margin at each side is blank
- side lengths differ by at most 1.6
- side length is at least 10
- if it is possible to include (0, 0), include it explicitly
*/
Drawer::Bbox Drawer::viewportByBbox(const Bbox& bbox) {
constexpr static double MIN_SIZE = 10.0;
constexpr static double MAX_RATIO = 1.6;
constexpr static double MARGIN_RATIO = 0.05;
constexpr static double MAX_RELATIVE_DISTANCE_TO_ZERO = 0.2;
double lx = bbox.first.x;
double rx = bbox.second.x;
double ly = bbox.first.y;
double ry = bbox.second.y;
auto extendToSize = [&](double& l, double &r, double size) {
double shift = (size - (r - l)) / 2;
l -= shift;
r += shift;
};
auto extendInterval = [&](double& l, double &r) {
if (r - l < MIN_SIZE) {
if (l >= -1e-9 && r < MIN_SIZE) {
l = 0;
r = MIN_SIZE;
} else if (r <= 1e-9 && l >= -MIN_SIZE) {
l = -MIN_SIZE;
r = 0;
} else {
extendToSize(l, r, MIN_SIZE);
}
}
if ((l > 0 || r < 0) && std::min(std::abs(l), std::abs(r)) <=
(r - l) * MAX_RELATIVE_DISTANCE_TO_ZERO)
{
if (l > 0) {
l = 0;
} else {
r = 0;
}
}
double margin = (r - l) * MARGIN_RATIO;
l -= margin;
r += margin;
};
extendInterval(lx, rx);
extendInterval(ly, ry);
if ((rx - lx) / (ry - ly) > MAX_RATIO) {
extendToSize(ly, ry, (rx - lx) / MAX_RATIO);
} else if ((ry - ly) / (rx - lx) > MAX_RATIO) {
extendToSize(lx, rx, (ry - ly) / MAX_RATIO);
}
return { Point(lx, ly), Point(rx, ry) };
}
void Drawer::drawAll() {
for (const auto& request: requests_) {
request(engine_);
}
}
void Drawer::drawGrid(const Bbox& bbox) {
const static std::vector<int> STEP_DELTA = {20, 25, 20};
// Step goes like 1, 2, 5, 10, 20, 50, 100, ...
constexpr static int SMALL_IN_BIG = 5;
constexpr static int THRESHOLD = 8;
constexpr static int MAX_SPREAD_TO_DRAW_ALL_TICKS = 13;
constexpr static double TEXT_OFFSET_RATIO = 0.01;
auto savedState = engine_->saveState();
int step = 5;
double spread = std::min(
bbox.second.x - bbox.first.x,
bbox.second.y - bbox.first.y);
size_t deltaPos = 2;
while (spread / step > THRESHOLD) {
step = step * STEP_DELTA[deltaPos] / 10;
if (++deltaPos == STEP_DELTA.size()) {
deltaPos = 0;
}
}
engine_->setWidth(0.5);
engine_->setStroke("lightgrey");
double smallStep = 1.0 * step / SMALL_IN_BIG;
for (
double tick = std::ceil(bbox.first.x / smallStep) * smallStep;
tick < bbox.second.x;
tick += smallStep)
{
if (std::lround(tick) % step != 0) {
engine_->drawSegment(tick, bbox.first.y, tick, bbox.second.y);
}
}
for (
double tick = std::ceil(bbox.first.y / smallStep) * smallStep;
tick < bbox.second.y;
tick += smallStep)
{
if (std::lround(tick) % step != 0) {
engine_->drawSegment(bbox.first.x, tick, bbox.second.x, tick);
}
}
engine_->setWidth(0.75);
engine_->setStroke("grey");
for (
double tick = std::ceil(bbox.first.x / step) * step;
tick < bbox.second.x;
tick += step)
{
engine_->drawSegment(tick, bbox.first.y, tick, bbox.second.y);
}
for (
double tick = std::ceil(bbox.first.y / step) * step;
tick < bbox.second.y;
tick += step)
{
engine_->drawSegment(bbox.first.x, tick, bbox.second.x, tick);
}
const double textOffsetX =
(bbox.second.x - bbox.first.x) * TEXT_OFFSET_RATIO;
const double textOffsetY =
(bbox.second.y - bbox.first.y) * TEXT_OFFSET_RATIO;
auto format = [](double x) {
static char buf[10];
std::sprintf(buf, "%d", int(std::lround(x)));
return std::string(buf);
};
if (spread < MAX_SPREAD_TO_DRAW_ALL_TICKS) {
step = 1;
}
for (
double tick = std::ceil(bbox.first.y / step) * step;
tick < bbox.second.y;
tick += step)
{
engine_->drawText(
bbox.first.x + textOffsetX, tick + textOffsetX, format(tick));
}
for (
double tick = std::ceil(bbox.first.x / step) * step;
tick < bbox.second.x;
tick += step)
{
engine_->drawText(
tick + textOffsetY, bbox.first.y + textOffsetY, format(tick));
}
if (spread <= MAX_SPREAD_TO_DRAW_ALL_TICKS) {
step = 1;
}
engine_->restoreState(savedState);
}
void Drawer::dumpSvg(const std::string& filename) {
if (requests_.empty()) {
return;
}
auto bbox = bbox_;
auto viewport = viewportByBbox(bbox);
std::unique_ptr<SvgEngine> svgEngine(new SvgEngine(
viewport.first.x, viewport.first.y,
viewport.second.x, viewport.second.y));
engine_ = svgEngine.get();
if (gridEnabled_) {
drawGrid(viewport);
}
drawAll();
std::string svg = svgEngine->serialize();
std::ofstream out(filename);
out << svg;
out.close();
}
#endif // JNGEN_DECLARE_ONLY
}} // namespace jngen::drawing
using jngen::drawing::Drawer;
using jngen::drawing::Color;
#include <algorithm>
#include <vector>
namespace jngen {
class Dsu {
public:
int getRoot(int x);
bool unite(int x, int y);
bool isConnected() const { return components <= 1; }
int numComponents() const { return components; }
void extend(size_t size);
private:
std::vector<int> parent;
std::vector<int> rank;
int components = 0;
};
#ifndef JNGEN_DECLARE_ONLY
int Dsu::getRoot(int x) {
extend(x);
return parent[x] == x ? x : (parent[x] = getRoot(parent[x]));
}
bool Dsu::unite(int x, int y) {
extend(std::max(x, y) + 1);
x = getRoot(x);
y = getRoot(y);
if (x == y) {
return false;
}
if (rank[x] > rank[y]) {
std::swap(x, y);
}
if (rank[y] == rank[x]) {
++rank[y];
}
parent[x] = y;
--components;
return true;
}
void Dsu::extend(size_t x) {
size_t last = parent.size() - 1;
while (parent.size() < x) {
++components;
parent.push_back(++last);
rank.push_back(0);
}
}
#endif // JNGEN_DECLARE_ONLY
} // namespace jngen
namespace jngen {
namespace impl {
// Generated with util/generate_prime_rehash_policy.cpp
// Necessary for anti-unordered_set test.
extern const unsigned int primeList[]
#ifndef JNGEN_DECLARE_ONLY
= {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
71, 73, 79, 83, 89, 97, 103, 109, 113, 127, 137, 139, 149, 157, 167, 179,
193, 199, 211, 227, 241, 257, 277, 293, 313, 337, 359, 383, 409, 439,
467, 503, 541, 577, 619, 661, 709, 761, 823, 887, 953, 1031, 1109, 1193,
1289, 1381, 1493, 1613, 1741, 1879, 2029, 2179, 2357, 2549, 2753, 2971,
3209, 3469, 3739, 4027, 4349, 4703, 5087, 5503, 5953, 6427, 6949, 7517,
8123, 8783, 9497, 10273, 11113, 12011, 12983, 14033, 15173, 16411, 17749,
19183, 20753, 22447, 24281, 26267, 28411, 30727, 33223, 35933, 38873,
42043, 45481, 49201, 53201, 57557, 62233, 67307, 72817, 78779, 85229,
92203, 99733, 107897, 116731, 126271, 136607, 147793, 159871, 172933,
187091, 202409, 218971, 236897, 256279, 277261, 299951, 324503, 351061,
379787, 410857, 444487, 480881, 520241, 562841, 608903, 658753, 712697,
771049, 834181, 902483, 976369, 1056323, 1142821, 1236397, 1337629, 1447153,
1565659, 1693859, 1832561, 1982627, 2144977, 2320627, 2510653, 2716249,
2938679, 3179303, 3439651, 3721303, 4026031, 4355707, 4712381, 5098259,
5515729, 5967347, 6456007, 6984629, 7556579, 8175383, 8844859, 9569143,
10352717, 11200489
}
#endif // JNGEN_DECLARE_ONLY
;
} // namespace impl
} // namespace jngen
#include <functional>
namespace jngen {
class Graph;
namespace graph_detail {
class GraphRandom;
struct Traits {
int n;
int m;
bool directed = false;
bool acyclic = false;
bool allowLoops = false;
bool allowMulti = false;
bool allowAntiparallel = false;
bool connected = false;
Traits() {}
explicit Traits(int n) : n(n) {}
Traits(int n, int m) : n(n), m(m) {}
};
class BuilderProxy {
public:
BuilderProxy(
Traits traits,
std::function<Graph(Traits)> builder) :
traits_(traits),
builder_(builder)
{ }
Graph g() const;
operator Graph() const;
BuilderProxy& allowLoops(bool value = true) {
traits_.allowLoops = value;
return *this;
}
BuilderProxy& allowMulti(bool value = true) {
traits_.allowMulti = value;
return *this;
}
BuilderProxy& allowAntiparallel(bool value = true) {
traits_.allowAntiparallel = value;
return *this;
}
BuilderProxy& connected(bool value = true) {
traits_.connected = value;
return *this;
}
BuilderProxy& directed(bool value = true) {
traits_.directed = value;
return *this;
}
BuilderProxy& acyclic(bool value = true) {
traits_.acyclic = value;
return *this;
}
private:
Traits traits_;
std::function<Graph(Traits)> builder_;
};
} // namespace graph_detail
} // namespace jngen
#include <iterator>
#include <unordered_set>
#include <vector>
#include <type_traits>
#include <utility>
namespace jngen {
template<typename T, typename Enable = void>
struct Hash;
namespace impl {
inline void hashCombine(uint64_t& h, uint64_t k) {
const uint64_t m = 0xc6a4a7935bd1e995;
const int r = 47;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
h += 0xe6546b64;
}
template<typename Iterator>
void hashCombine(uint64_t& h, Iterator begin, Iterator end) {
Hash<typename std::iterator_traits<Iterator>::value_type> hash;
while (begin != end) {
hashCombine(h, hash(*begin++));
}
}
} // namespace impl
template<typename T>
struct Hash<
T,
enable_if_t<std::is_integral<T>::value>>
{
uint64_t operator()(const T& t) const {
uint64_t h = 0;
impl::hashCombine(h, t);
return h;
}
};
#define JNGEN_DEFINE_STD_HASH(Type)\
namespace std {\
template<>\
struct hash<Type> {\
size_t operator()(const Type& value) const {\
return jngen::Hash<Type>{}(value);\
}\
};\
}
#define JNGEN_DEFINE_STD_HASH_TEMPLATE(T, Type)\
namespace std {\
template<typename T>\
struct hash<Type> {\
size_t operator()(const Type& value) const {\
return jngen::Hash<Type>{}(value);\
}\
};\
}
template<typename T>
struct Hash<std::vector<T>> {
uint64_t operator()(const std::vector<T>& elements) const {
uint64_t h = 0;
impl::hashCombine(h, elements.size());
impl::hashCombine(h, elements.begin(), elements.end());
return h;
}
};
template<typename T, typename U>
struct Hash<std::pair<T, U>> {
uint64_t operator()(const std::pair<T, U>& value) const {
uint64_t h = 0;
impl::hashCombine(h, Hash<T>{}(value.first));
impl::hashCombine(h, Hash<U>{}(value.second));
return h;
}
};
} // namespace jngen
JNGEN_DEFINE_STD_HASH_TEMPLATE(T, std::vector<T>);
namespace std {
template<typename T, typename U>
struct hash<std::pair<T, U>> {
size_t operator()(const std::pair<T, U>& value) const {
return jngen::Hash<std::pair<T, U>>{}(value);
}
};
} // namespace std
#include <algorithm>
#include <cctype>
#include <functional>
#include <set>
#include <string>
#include <utility>
#include <vector>
// TODO: adequate error messages
namespace jngen {
class Pattern {
friend class Parser;
public:
Pattern() : isOrPattern(false), min(1), max(1) {}
Pattern(const std::string& s);
std::string next(std::function<int(int)>&& rnd) const;
private:
Pattern(Pattern p, std::pair<int, int> quantity);
Pattern(std::vector<char> chars, std::pair<int, int> quantity);
std::vector<char> chars;
std::vector<Pattern> children;
bool isOrPattern;
int min;
int max;
static std::map<std::string, Pattern> cachedPatterns_;
};
class Parser {
public:
Pattern parse(const std::string& s) {
this->s = s;
pos = 0;
return parsePattern();
}
private:
static bool isControl(char c);
static int control(int c);
int next();
int peek() const;
int peekAndMove(size_t& newPos) const;
// TODO: catch overflows
int readInt();
std::pair<int, int> parseRange();
std::pair<int, int> tryParseQuantity();
std::vector<char> parseBlock();
Pattern parsePattern();
std::string s;
size_t pos;
};
} // namespace jngen
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_PATTERN_INL_H
#ifndef JNGEN_INCLUDE_PATTERN_INL_H
#error File "pattern_inl.h" must not be included directly.
#endif
namespace jngen {
Pattern::Pattern(const std::string& s) {
auto iter = cachedPatterns_.find(s);
if (iter != cachedPatterns_.end()) {
*this = iter->second;
return;
}
*this = Parser().parse(s);
cachedPatterns_[s] = *this;
}
Pattern::Pattern(Pattern p, std::pair<int, int> quantity) :
isOrPattern(false),
min(quantity.first),
max(quantity.second)
{
children.push_back(std::move(p));
}
Pattern::Pattern(std::vector<char> chars, std::pair<int, int> quantity) :
chars(std::move(chars)),
isOrPattern(false),
min(quantity.first),
max(quantity.second)
{ }
std::map<std::string, Pattern> Pattern::cachedPatterns_;
std::string Pattern::next(std::function<int(int)>&& rnd) const {
if (isOrPattern) {
ENSURE(!children.empty());
return children[rnd(children.size())].next(std::move(rnd));
}
ENSURE( (!!chars.empty()) ^ (!!children.empty()) );
int count;
if (min == max) {
count = min;
} else {
count = min + rnd(max - min + 1);
}
std::string result;
for (int i = 0; i < count; ++i) {
if (!children.empty()) {
for (const Pattern& p: children) {
result += p.next(std::move(rnd));
}
} else {
result += chars[rnd(chars.size())];
}
}
return result;
}
bool Parser::isControl(char c) {
static const std::string CONTROL_CHARS = "()[]{}|?";
return CONTROL_CHARS.find(c) != std::string::npos;
}
int Parser::control(int c) {
return c >> 8;
}
int Parser::next() {
size_t newPos;
int result = peekAndMove(newPos);
pos = newPos;
return result;
}
int Parser::peek() const {
size_t dummy;
return peekAndMove(dummy);
}
int Parser::peekAndMove(size_t& newPos) const {
newPos = pos;
if (pos == s.size()) {
return -1;
}
if (s[pos] == '\\') {
ensure(
pos+1 < s.size(),
"Backslash at the end of the pattern is illegal");
newPos += 2;
return s[pos+1];
}
++newPos;
int ret = s[pos];
return isControl(ret) ? (ret << 8) : ret;
}
// TODO: catch overflows
int Parser::readInt() {
ENSURE(std::isdigit(peek()));
int res = 0;
while (std::isdigit(peek())) {
res = res * 10 + next() - '0';
}
return res;
}
std::pair<int, int> Parser::parseRange() {
ENSURE(control(next()) == '{');
int from = readInt();
int nxt = next();
if (control(nxt) == '}') {
return {from, from};
} else if (nxt == ',' || nxt == '-') {
int to = readInt();
ENSURE(control(next()) == '}');
return {from, to};
} else {
ensure(false, "cannot parse character range");
}
}
std::pair<int, int> Parser::tryParseQuantity() {
std::pair<int, int> quantity = {1, 1};
int qchar = peek();
if (control(qchar) == '?') {
quantity = {0, 1};
next();
} else if (control(qchar) == '{') {
quantity = parseRange();
}
return quantity;
}
std::vector<char> Parser::parseBlock() {
std::vector<char> allowed;
char last = -1;
bool inRange = false;
while (control(peek()) != ']') {
char c = next(); // buggy on cases like [a-}]
ENSURE(c != -1);
if (c == '-') {
ensure(!inRange, "invalid pattern");
inRange = true;
} else if (inRange) {
ensure(c >= last, "invalid pattern");
for (char i = last; i <= c; ++i) {
allowed.push_back(i);
}
inRange = false;
last = -1;
} else {
if (last != -1) {
allowed.push_back(last);
}
last = c;
}
}
ENSURE(control(next()) == ']');
ENSURE(!inRange);
if (last != -1) {
allowed.push_back(last);
}
std::sort(allowed.begin(), allowed.end());
return allowed;
}
Pattern Parser::parsePattern() {
std::vector<Pattern> orPatterns;
Pattern cur;
while (true) {
int nxt = next();
if (nxt == -1 || control(nxt) == ')') {
break;
} else if (control(nxt) == '(') {
Pattern p = parsePattern();
cur.children.push_back(Pattern(p, tryParseQuantity()));
} else if (control(nxt) == '|') {
orPatterns.emplace_back();
std::swap(orPatterns.back(), cur);
} else {
std::vector<char> chars;
if (control(nxt) == '[') {
chars = parseBlock();
} else {
ENSURE(!control(nxt));
chars = {static_cast<char>(nxt)};
}
cur.children.push_back(Pattern(chars, tryParseQuantity()));
}
}
if (orPatterns.empty()) {
return cur;
} else {
orPatterns.emplace_back();
std::swap(orPatterns.back(), cur);
Pattern p;
p.isOrPattern = true;
p.children = orPatterns;
return p;
}
}
} // namespace jngen
#undef JNGEN_INCLUDE_PATTERN_INL_H
#endif // JNGEN_DECLARE_ONLY
using jngen::Pattern;
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <random>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace jngen {
void assertRandomEngineConsistency();
void assertIntegerSizes();
void registerGen(int argc, char *argv[], int version = 1);
class Random;
class BaseTypedRandom {
public:
BaseTypedRandom(Random& random) : random(random) {}
protected:
Random& random;
};
template<typename T>
struct TypedRandom;
uint64_t maskForBound(uint64_t bound);
template<typename Result, typename Source>
Result uniformRandom(Result bound, Random& random, Source (Random::*method)()) {
static_assert(sizeof(Result) <= sizeof(Source),
"uniformRandom: Source type must be at least as large as Result type");
#ifdef JNGEN_FAST_RANDOM
return (random.*method)() % bound;
#else
Source mask = maskForBound(bound);
while (true) {
Source outcome = (random.*method)() & mask;
if (outcome < static_cast<Source>(bound)) {
return outcome;
}
}
#endif
}
class Random {
public:
Random() {
assertRandomEngineConsistency();
assertIntegerSizes();
std::vector<uint32_t> seedSeq;
// 4 random_device calls is enough for everyone
std::random_device rd;
for (size_t i = 0; i < 4; ++i) {
seedSeq.push_back(rd());
}
seed(seedSeq);
}
void seed(uint32_t val);
void seed(const std::vector<uint32_t>& seed);
uint32_t next();
uint64_t next64();
double nextf();
int next(int n);
long long next(long long n);
size_t next(size_t n);
double next(double n);
int next(int l, int r);
long long next(long long l, long long r);
size_t next(size_t l, size_t r);
double next(double l, double r);
int wnext(int n, int w);
long long wnext(long long n, int w);
size_t wnext(size_t n, int w);
double wnext(double n, int w);
int wnext(int l, int r, int w);
long long wnext(long long l, long long r, int w);
size_t wnext(size_t l, size_t r, int w);
double wnext(double l, double r, int w);
std::string next(const std::string& pattern);
template<typename ... Args>
std::string next(const std::string& pattern, Args... args) {
return next(format(pattern, args...));
}
template<typename T, typename ... Args>
T tnext(Args... args) {
return TypedRandom<T>{*this}.next(args...);
}
template<typename ... Args>
std::pair<int, int> nextp(Args... args) {
return tnext<std::pair<int, int>>(args...);
}
template<typename Iterator>
auto choice(Iterator begin, Iterator end)
-> typename std::iterator_traits<Iterator>::value_type
{
auto length = std::distance(begin, end);
ensure(length > 0, "Cannot select from a range of negative length");
size_t index = tnext<size_t>(length);
std::advance(begin, index);
return *begin;
}
template<typename Container>
typename Container::value_type choice(const Container& container) {
ensure(!container.empty(), "Cannot select from an empty container");
return choice(container.begin(), container.end());
}
template<typename T>
T choice(const std::initializer_list<T>& ilist) {
return choice(ilist.begin(), ilist.end());
}
template<typename Numeric>
size_t nextByDistribution(const std::vector<Numeric>& distribution) {
ensure(!distribution.empty(), "Cannot sample by empty distribution");
Numeric sum = std::accumulate(
distribution.begin(), distribution.end(), Numeric(0));
auto x = next(sum);
for (size_t i = 0; i < distribution.size(); ++i) {
if (x < distribution[i]) {
return i;
}
x -= distribution[i];
}
return distribution.size() - 1;
}
template<typename Numeric>
size_t nextByDistribution(const std::initializer_list<Numeric>& ilist) {
// TODO: looks suboptimal
return nextByDistribution(std::vector<Numeric>(ilist));
}
private:
template<typename T, typename ...Args>
T smallWnext(int w, Args... args) {
ENSURE(std::abs(w) <= WNEXT_LIMIT);
T result = next(args...);
while (w > 0) {
result = std::max(result, next(args...));
--w;
}
while (w < 0) {
result = std::min(result, next(args...));
++w;
}
return result;
}
double realWnext(int w) {
if (w == 0) {
return nextf();
} else if (w > 0) {
return std::pow(nextf(), 1.0 / (w + 1));
} else {
return 1.0 - std::pow(nextf(), 1.0 / (-w + 1));
}
}
std::mt19937 randomEngine_;
constexpr static int WNEXT_LIMIT = 8;
};
JNGEN_EXTERN Random rnd;
template<>
struct TypedRandom<int> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
int next(int n) { return random.next(n); }
int next(int l, int r) { return random.next(l, r); }
};
template<>
struct TypedRandom<double> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
double next(double n) { return random.next(n); }
double next(double l, double r) { return random.next(l, r); }
};
template<>
struct TypedRandom<long double> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
double next(double n) { return random.next(n); }
double next(double l, double r) { return random.next(l, r); }
};
template<>
struct TypedRandom<long long> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
long long next(long long n) { return random.next(n); }
long long next(long long l, long long r) { return random.next(l, r); }
};
template<>
struct TypedRandom<size_t> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
size_t next(size_t n) { return random.next(n); }
size_t next(size_t l, size_t r) { return random.next(l, r); }
};
template<>
struct TypedRandom<char> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
char next(char n) { return random.next(n); }
char next(char l, char r) { return random.next(l, r); }
};
template<typename T>
struct TypedRandom : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
template<typename ... Args>
T next(Args... args) { return random.next(args...); }
};
struct RandomPairTraits {
const bool ordered;
const bool distinct;
};
#ifdef JNGEN_DECLARE_ONLY
extern RandomPairTraits opair, dpair, odpair, dopair;
#else
RandomPairTraits opair{true, false};
RandomPairTraits dpair{false, true};
RandomPairTraits odpair{true, true};
RandomPairTraits dopair{true, true};
#endif
template<>
struct TypedRandom<std::pair<int, int>> : public BaseTypedRandom {
using BaseTypedRandom::BaseTypedRandom;
std::pair<int, int> next(int n) {
return next(n, {false, false});
}
std::pair<int, int> next(int l, int r) {
return next(l, r, {false, false});
}
std::pair<int, int> next(int n, RandomPairTraits traits) {
int first = rnd.next(n);
int second;
do {
second = rnd.next(n);
} while (traits.distinct && first == second);
if (traits.ordered && first > second) {
std::swap(first, second);
}
return {first, second};
}
std::pair<int, int> next(int l, int r, RandomPairTraits traits) {
auto res = next(r-l+1, traits);
res.first += l;
res.second += l;
return res;
}
private:
std::pair<int, int> ordered(std::pair<int, int> pair) const {
if (pair.first > pair.second) {
std::swap(pair.first, pair.second);
}
return pair;
}
};
} // namespace jngen
using jngen::Random;
using jngen::rnd;
using jngen::opair;
using jngen::dpair;
using jngen::dopair;
using jngen::odpair;
using jngen::registerGen;
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_RANDOM_INL_H
#ifndef JNGEN_INCLUDE_RANDOM_INL_H
#error File "random_inl.h" must not be included directly.
#endif
namespace jngen {
void assertRandomEngineConsistency() {
std::mt19937 engine(1234);
ENSURE(engine() == 822569775,
"std::mt19937 doesn't conform to the C++ standard");
ENSURE(engine() == 2137449171,
"std::mt19937 doesn't conform to the C++ standard");
ENSURE(engine() == 2671936806,
"std::mt19937 doesn't conform to the C++ standard");
}
void assertIntegerSizes() {
static_assert(
std::numeric_limits<unsigned char>::max() == 255,
"max(unsigned char) != 255");
static_assert(sizeof(int) == 4, "sizeof(int) != 4");
static_assert(sizeof(long long) == 8, "sizeof(long long) != 8");
static_assert(
sizeof(size_t) == 4 || sizeof(size_t) == 8,
"sizeof(size_t) is neither 4 nor 8");
static_assert(
sizeof(std::size_t) == sizeof(size_t),
"sizeof(size_t) != sizeof(std::size_t)");
}
void registerGen(int argc, char *argv[], int version) {
(void)version; // unused, only for testlib.h compatibility
std::vector<uint32_t> seed;
for (int i = 1; i < argc; ++i) {
int startPosition = seed.size();
seed.emplace_back();
for (char *s = argv[i]; *s; ++s) {
++seed[startPosition];
seed.push_back(*s);
}
}
rnd.seed(seed);
}
uint64_t maskForBound(uint64_t bound) {
--bound;
uint64_t mask = ~0;
if ((mask >> 32) >= bound) mask >>= 32;
if ((mask >> 16) >= bound) mask >>= 16;
if ((mask >> 8 ) >= bound) mask >>= 8 ;
if ((mask >> 4 ) >= bound) mask >>= 4 ;
if ((mask >> 2 ) >= bound) mask >>= 2 ;
if ((mask >> 1 ) >= bound) mask >>= 1 ;
return mask;
}
void Random::seed(uint32_t val) {
randomEngine_.seed(val);
}
void Random::seed(const std::vector<uint32_t>& seed) {
std::seed_seq seq(seed.begin(), seed.end());
randomEngine_.seed(seq);
}
uint32_t Random::next() {
return randomEngine_();
}
uint64_t Random::next64() {
uint64_t a = next();
uint64_t b = next();
return (a << 32) ^ b;
}
double Random::nextf() {
return (double)randomEngine_() / randomEngine_.max();
}
int Random::next(int n) {
ensure(n > 0);
return uniformRandom(n, *this, (uint32_t (Random::*)())&Random::next);
}
long long Random::next(long long n) {
ensure(n > 0);
return uniformRandom(n, *this, &Random::next64);
}
size_t Random::next(size_t n) {
ensure(n > 0);
return uniformRandom(n, *this, &Random::next64);
}
double Random::next(double n) {
ensure(n >= 0);
return nextf() * n;
}
int Random::next(int l, int r) {
ensure(l <= r);
uint32_t n = static_cast<uint32_t>(r) - l + 1;
return l + uniformRandom(
n, *this, (uint32_t (Random::*)())&Random::next);
}
long long Random::next(long long l, long long r) {
ensure(l <= r);
uint64_t n = static_cast<uint64_t>(r) - l + 1;
return l + uniformRandom(n, *this, &Random::next64);
}
size_t Random::next(size_t l, size_t r) {
ensure(l <= r);
uint64_t n = static_cast<uint64_t>(r) - l + 1;
return l + uniformRandom(n, *this, &Random::next64);
}
double Random::next(double l, double r) {
ensure(l <= r);
return l + next(r-l);
}
int Random::wnext(int n, int w) {
ensure(n > 0);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<int>(w, n);
} else {
double t = realWnext(w);
return n * t;
}
}
long long Random::wnext(long long n, int w) {
ensure(n > 0);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<long long>(w, n);
} else {
return n * realWnext(w);
}
}
size_t Random::wnext(size_t n, int w) {
ensure(n > 0);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<size_t>(w, n);
} else {
return n * realWnext(w);
}
}
double Random::wnext(double n, int w) {
ensure(n >= 0);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<double>(w, n);
} else {
return realWnext(w) * n;
}
}
int Random::wnext(int l, int r, int w) {
ensure(l <= r);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<int>(w, l, r);
} else {
uint32_t n = static_cast<uint32_t>(r) - l + 1;
return l + static_cast<uint32_t>(n * realWnext(w));
}
}
long long Random::wnext(long long l, long long r, int w) {
ensure(l <= r);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<long long>(w, l, r);
} else {
uint64_t n = static_cast<uint64_t>(r) - l + 1;
return l + static_cast<uint64_t>(n * realWnext(w));
}
}
size_t Random::wnext(size_t l, size_t r, int w) {
ensure(l <= r);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<size_t>(w, l, r);
} else {
uint64_t n = static_cast<uint64_t>(r) - l + 1;
return l + static_cast<uint64_t>(n * realWnext(w));
}
}
double Random::wnext(double l, double r, int w) {
ensure(l <= r);
if (std::abs(w) <= WNEXT_LIMIT) {
return smallWnext<double>(w, l, r);
} else {
return realWnext(w) * (r - l) + l;
}
}
std::string Random::next(const std::string& pattern) {
return Pattern(pattern).next([this](int n) { return next(n); });
}
} // namespace jngen
#undef JNGEN_INCLUDE_RANDOM_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <iterator>
#include <sstream>
#include <string>
namespace jngen {
namespace options {
struct Range {
int first;
int last;
int step;
Range(int first, int last, int step = 1) :
first(first),
last(last),
step(step)
{
ensure(first <= last);
ensure(step > 0);
}
class iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using reference = const int&;
using pointer = const int*;
using difference_type = int;
iterator(int value, int step, int last) :
value_(value),
step_(step),
last_(last)
{ }
iterator& operator++() {
ensure(value_ <= last_, "Cannot increment past-the-end iterator");
value_ += step_;
return *this;
}
iterator operator++(int) {
iterator copy = *this;
++*this;
return copy;
}
int operator*() const {
ensure(value_ <= last_, "Cannot dereference past-the-end iterator");
return value_;
}
bool operator==(const iterator& other) const {
if (value_ <= last_) {
return value_ == other.value_;
}
return other.value_ > last_;
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
private:
int value_;
int step_;
int last_;
};
iterator begin() const {
return { first, step, last };
}
iterator end() const {
return { last + 1, step, last };
}
static Range fromString(std::string s) {
for (char& c: s) {
ensure(c != ' ', "No spaces allowed in range description");
if (c == ':') {
c = ' ';
}
}
std::istringstream ss(s);
int first = 1;
int last = std::numeric_limits<int>::max() - 1;
int step = 1;
if (!(ss >> first)) {
ensure(false, "Failed to parse range");
}
if (!(ss >> last)) {
return Range(first, last, step);
}
if (!(ss >> step)) {
return Range(first, last, step);
}
return Range(first, last, step);
}
};
} // namespace options
} // namespace jngen
#include <map>
#include <sstream>
#include <string>
#include <vector>
namespace jngen {
namespace detail {
template<typename T>
constexpr bool isValidOptionType() {
return std::is_same<T, std::string>::value ||
(std::is_arithmetic<T>::value && !std::is_same<T, char>::value);
}
template<typename T>
using StringIfCharPtrElseT = typename std::conditional<
std::is_same<typename std::decay<T>::type, const char*>::value ||
std::is_same<typename std::decay<T>::type, char*>::value,
std::string,
T>::type;
} // namespace detail
struct Index {
size_t index;
std::string name;
Index(size_t index) : index(index) {
if (!config.largeOptionIndices) {
ensure(
index < 32,
"Looks like you called getOpt('c'). Consider using "
"getOpt(\"c\") or set 'config.largeOptionIndices = true' "
"if you indeed have more than 32 options.");
}
}
Index(const std::string& name) : name(name) {
ensure(!name.empty(), "Variable name cannot be empty");
}
bool isNamed() const {
return !name.empty();
}
};
struct VariableMap {
std::vector<std::string> positional;
std::map<std::string, std::string> named;
int count(size_t pos) const {
return pos < positional.size();
}
int count(const std::string& name) const {
return named.count(name);
}
std::string operator[](size_t pos) const {
if (!count(pos)) {
return "";
}
return positional.at(pos);
}
std::string operator[](const std::string& name) const {
if (!count(name)) {
return "";
}
return named.at(name);
}
int count(const Index& index) const {
if (index.isNamed()) {
return count(index.name);
} else {
return count(index.index);
}
}
std::string operator[](const Index& index) const {
if (index.isNamed()) {
return (*this)[index.name];
} else {
return (*this)[index.index];
}
}
void assertExistence(const Index& index) const {
if (count(index)) {
return;
}
if (index.isNamed()) {
ensure(false, format(
"There is no variable with name '%s'", index.name.c_str()));
} else {
ensure(false, format(
"There is no variable with index %d", index.index));
}
}
bool initialized = false;
};
template<typename T>
class PendingVariable {
public:
explicit PendingVariable(std::string value) :
value_(std::move(value))
{ }
PendingVariable(std::string value, T defaultValue) :
value_(std::move(value)),
default_(std::move(defaultValue))
{ }
explicit PendingVariable(std::nullptr_t, T defaultValue) :
valid_(false),
default_(std::move(defaultValue))
{ }
// We need this check in order to make the following work:
// string s = getOpt(0);
// s = getOpt(0);
// Weird things happen if we allow all kind of casts. See
// https://stackoverflow.com/questions/46740341
template<
typename U,
typename std::enable_if<
detail::isValidOptionType<U>()>::type* = nullptr>
operator U() const
{
if (!valid_) {
return static_cast<U>(default_);
}
std::istringstream ss(value_);
U t;
if (ss >> t) {
return t;
} else {
ensure(
false,
format(
"Cannot parse option. Raw value: '%s'",
value_.c_str()));
}
}
// TODO: getOpt operators, like getOpt("n") == 100
private:
bool valid_ = true;
std::string value_;
T default_;
};
template<>
class PendingVariable<void> {
public:
explicit PendingVariable(std::string value) :
value_(std::move(value))
{ }
// We need this check in order to make the following work:
// string s = getOpt(0);
// s = getOpt(0);
// Weird things happen if we allow all kind of casts. See
// https://stackoverflow.com/questions/46740341
template<
typename U,
typename std::enable_if<
detail::isValidOptionType<U>()>::type* = nullptr>
operator U() const
{
std::istringstream ss(value_);
U t;
if (ss >> t) {
return t;
} else {
ensure(
false,
format(
"Cannot parse option. Raw value: '%s'",
value_.c_str()));
}
}
private:
std::string value_;
};
// TODO: think about seed as a last argument
inline VariableMap parseArguments(const std::vector<std::string>& args) {
VariableMap result;
auto setNamedVar = [&result](
const std::string& name,
const std::string& value)
{
ensure(
!result.count(value),
"Named arguments must have distinct names");
result.named[name] = value;
};
std::string pendingVarName;
for (const std::string& s: args) {
if (s == "-") {
continue;
}
if (s == "--") {
break;
}
if (s[0] != '-') {
if (!pendingVarName.empty()) {
setNamedVar(pendingVarName, s);
pendingVarName = "";
} else {
result.positional.push_back(s);
}
continue;
}
if (!pendingVarName.empty()) {
result.named[pendingVarName] = "1";
pendingVarName = "";
}
std::string name;
std::string value;
bool foundEq = false;
for (char c: s.substr(1)) {
if (!foundEq && c == '=') {
foundEq = true;
} else {
if (foundEq) {
value += c;
} else {
name += c;
}
}
}
if (foundEq) {
setNamedVar(name, value);
} else {
pendingVarName = name;
}
setNamedVar(name, value);
}
if (!pendingVarName.empty()) {
result.named[pendingVarName] = "1";
}
result.initialized = true;
return result;
}
JNGEN_EXTERN VariableMap vmap;
namespace detail {
inline PendingVariable<void> getOpt(const Index& index) {
ensure(
vmap.initialized,
"parseArgs(args, argv) must be called before getOpt(...)");
vmap.assertExistence(index);
return PendingVariable<void>(vmap[index]);
}
template<typename T, typename U = detail::StringIfCharPtrElseT<T>>
PendingVariable<U> getOpt(const Index& index, const T& defaultValue) {
ensure(
vmap.initialized,
"parseArgs(args, argv) must be called before getOpt(...)");
if (vmap.count(index)) {
return PendingVariable<U>(vmap[index], U{defaultValue});
} else {
return PendingVariable<U>(nullptr, U{defaultValue});
}
}
inline bool hasOpt(const Index& index) {
return vmap.count(index);
}
template<typename T>
bool readVariable(const std::string& value, T& var) {
std::istringstream ss(value);
T t;
if (ss >> t) {
var = t;
return true;
}
return false;
}
inline int getNamedImpl(std::vector<std::string>::const_iterator) { return 0; }
template<typename T, typename ... Args>
int getNamedImpl(
std::vector<std::string>::const_iterator it, T& var, Args&... args)
{
T value;
int res = 0;
if (readVariable(vmap[*it], value)) {
var = value;
++res;
}
res += getNamedImpl(++it, args...);
return res;
}
inline int getPositionalImpl(size_t) { return 0; }
template<typename T, typename ... Args>
int getPositionalImpl(size_t index, T& var, Args&... args) {
T value;
int res = 0;
if (readVariable(vmap[index], value)) {
var = value;
++res;
}
res += getPositionalImpl(index + 1, args...);
return res;
}
} // namespace detail
template<typename ... Args>
int doGetNamed(const std::string& names, Args&... args) {
ensure(
vmap.initialized,
"parseArgs(args, argv) must be called before getNamed(...)");
auto namesSplit = util::split(names, ',');
ENSURE(
namesSplit.size() == sizeof...(args),
"Number of names is not equal to number of variables");
return detail::getNamedImpl(namesSplit.begin(), args...);
}
template<typename ... Args>
int getPositional(Args&... args) {
ensure(
vmap.initialized,
"parseArgs(args, argv) must be called before getPositional(...)");
return detail::getPositionalImpl(0, args...);
}
inline void parseArgs(int argc, char *argv[]) {
vmap = parseArguments(std::vector<std::string>(argv + 1, argv + argc));
}
inline PendingVariable<void> getOpt(size_t index) {
return detail::getOpt(Index(index));
}
inline PendingVariable<void> getOpt(const std::string& name) {
return detail::getOpt(Index(name));
}
template<typename T, typename U = detail::StringIfCharPtrElseT<T>>
PendingVariable<U> getOpt(size_t index, const T& defaultValue) {
return detail::getOpt(Index(index), defaultValue);
}
template<typename T, typename U = detail::StringIfCharPtrElseT<T>>
PendingVariable<U> getOpt(const std::string& name, const T& defaultValue) {
return detail::getOpt(Index(name), defaultValue);
}
inline bool hasOpt(size_t index) {
return vmap.count(index);
}
inline bool hasOpt(const std::string& name) {
return vmap.count(name);
}
inline options::Range parseRange(const std::string& value) {
return options::Range::fromString(value);
}
} // namespace jngen
using jngen::parseArgs;
using jngen::getOpt;
using jngen::hasOpt;
using jngen::parseRange;
using jngen::getPositional;
#define getNamed(...) ::jngen::doGetNamed(#__VA_ARGS__, __VA_ARGS__)
#include <iostream>
#include <type_traits>
namespace jngen {
template<int N> struct PTag : PTag<N-1> {};
template<> struct PTag<0> {};
struct PTagMax : PTag<20> {};
struct OutputModifier {
int addition = 0;
bool printN = false;
bool printM = false;
int printParents;
bool printEdges = true;
bool printWeights = true;
char sep = ' ';
};
JNGEN_EXTERN OutputModifier defaultMod;
template<typename T>
class Repr {
friend std::ostream& operator<<(std::ostream& out, const Repr& repr) {
repr.print(out);
return out;
}
template<typename P>
friend Repr<P> repr(const P& t);
template<typename P>
friend class ReprProxy;
protected:
Repr() = delete;
Repr(const Repr<T>&) = default;
Repr<T>& operator=(const Repr<T>&) = default;
Repr(Repr<T>&&) = default;
Repr<T>& operator=(Repr<T>&&) = default;
public:
Repr(const T& object) :
object_(object),
mod_(defaultMod)
{ }
Repr<T>& add1(bool value = true) {
mod_.addition = value;
return *this;
}
Repr<T>& printN(bool value = true) {
mod_.printN = value;
return *this;
}
Repr<T>& printM(bool value = true) {
mod_.printM = value;
return *this;
}
Repr<T>& printParents(int value = -1) {
mod_.printParents = value;
mod_.printEdges = false;
return *this;
}
Repr<T>& printEdges(bool value = true) {
mod_.printEdges = value;
if (!value) {
mod_.printParents = -1;
}
return *this;
}
Repr<T>& printWeights(bool value = true) {
mod_.printWeights = value;
return *this;
}
Repr<T>& endl(bool value = true) {
mod_.sep = value ? '\n' : ' ';
return *this;
}
private:
void print(std::ostream& out) const {
printValue(out, object_, mod_, PTagMax{});
}
const T& object_;
protected:
OutputModifier mod_;
};
class BaseReprProxy {};
template<typename T>
class ReprProxy : public BaseReprProxy {
friend std::ostream& operator<<(std::ostream& out, const ReprProxy& proxy) {
Repr<T> repr(static_cast<const T&>(proxy));
return out << repr;
}
public:
Repr<T> add1(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.add1(value);
return repr;
}
Repr<T> printN(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.printN(value);
return repr;
}
Repr<T> printM(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.printM(value);
return repr;
}
Repr<T> printParents(int value = -1) {
Repr<T> repr(static_cast<const T&>(*this));
repr.printParents(value);
return repr;
}
Repr<T> printEdges(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.printEdges(value);
return repr;
}
Repr<T> printWeights(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.printWeights(value);
return repr;
}
Repr<T> endl(bool value = true) {
Repr<T> repr(static_cast<const T&>(*this));
repr.endl(value);
return repr;
}
protected:
ReprProxy() {
static_assert(
std::is_base_of<ReprProxy<T>, T>::value,
"ReprProxy<T> must be inherited by T");
}
};
template<typename T>
Repr<T> repr(const T& t) {
return Repr<T>(t);
}
class DefaultModSetter : public Repr<int> {
friend DefaultModSetter setMod();
private:
DefaultModSetter(int val) :
Repr<int>(val)
{ }
public:
~DefaultModSetter() {
defaultMod = mod_;
}
Repr<int>& reset() {
mod_ = OutputModifier();
return *this;
}
};
inline DefaultModSetter setMod() {
static int dummy = 0;
return DefaultModSetter(dummy);
}
} // namespace jngen
using jngen::repr;
using jngen::setMod;
#include <iostream>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
namespace jngen {
namespace detail {
// TODO: maybe make it more clear SFINAE, like boost::has_left_shift<X,Y>?
// TODO: make these defines namespace independent
#define JNGEN_DEFINE_FUNCTION_CHECKER(name, expr)\
template<typename T, typename Enable = void>\
class Has ## name ## Helper: public std::false_type {};\
\
template<typename T>\
class Has ## name ## Helper<T,\
decltype(void(\
expr\
))\
> : public std::true_type {};\
#define JNGEN_HAS_FUNCTION(name)\
::jngen::detail::Has ## name ## Helper<T>::value
JNGEN_DEFINE_FUNCTION_CHECKER(
OstreamMethod,
std::declval<std::ostream&>().operator<< (std::declval<T>())
)
JNGEN_DEFINE_FUNCTION_CHECKER(
OstreamFreeFunction,
std::operator<<(std::declval<std::ostream&>(), std::declval<T>())
)
JNGEN_DEFINE_FUNCTION_CHECKER(
Plus,
T(std::declval<T>() + 1)
)
JNGEN_DEFINE_FUNCTION_CHECKER(
Container,
std::distance(std::declval<T>().begin(), std::declval<T>().end())
)
#define JNGEN_HAS_OSTREAM()\
(JNGEN_HAS_FUNCTION(OstreamMethod) ||\
JNGEN_HAS_FUNCTION(OstreamFreeFunction))
template<typename T>
struct VectorDepth {
constexpr static int value = 0;
};
template<typename T, template <typename...> class C>
struct VectorDepth<C<T>> {
constexpr static int value =
std::is_base_of<
std::vector<T>,
C<T>
>::value ? VectorDepth<T>::value + 1 : 0;
};
} // namespace detail
#define JNGEN_DECLARE_PRINTER(constraint, priority)\
template<typename T>\
auto printValue(\
std::ostream& out, const T& t, const OutputModifier& mod, PTag<priority>)\
-> enable_if_t<constraint, void>
#define JNGEN_DECLARE_SIMPLE_PRINTER(type, priority)\
inline void printValue(std::ostream& out, const type& t,\
const OutputModifier& mod, PTag<priority>)
#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)
{
static bool locked = false;
ensure(
!locked,
std::string{} + "You are trying to print a type for which "
"operator<< is not defined: " + typeid(T).name());
locked = true;
(void)mod;
out << t;
locked = false;
}
JNGEN_DECLARE_PRINTER(JNGEN_HAS_OSTREAM(), 10)
{
(void)mod;
out << t;
}
JNGEN_DECLARE_PRINTER(
JNGEN_HAS_OSTREAM() && JNGEN_HAS_FUNCTION(Plus), 11)
{
if (std::is_integral<T>::value) {
out << T(t + mod.addition);
} else {
out << t;
}
}
JNGEN_DECLARE_PRINTER(detail::VectorDepth<T>::value == 1, 3)
{
if (mod.printN) {
out << t.size() << "\n";
}
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << mod.sep;
}
JNGEN_PRINT(x);
}
}
JNGEN_DECLARE_PRINTER(detail::VectorDepth<T>::value == 1 &&
std::tuple_size<typename T::value_type>::value == 2, 4)
{
if (mod.printN) {
out << t.size() << "\n";
}
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << "\n";
}
JNGEN_PRINT(x);
}
}
JNGEN_DECLARE_PRINTER(detail::VectorDepth<T>::value == 2, 4)
{
if (mod.printN) {
out << t.size() << (mod.printM ? " " : "");
}
if (mod.printM) {
if (t.empty()) {
out << 0;
} else {
auto size = t[0].size();
out << size;
for (const auto& vec: t) {
ensure(size == vec.size(), "Size of all matrix elements must "
"be equal if printM is specified");
}
}
}
if ((mod.printN || mod.printM) && !t.empty()) {
out << "\n";
}
auto tmp = mod;
{
auto mod = tmp;
mod.printN = mod.printM = false;
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << '\n';
}
JNGEN_PRINT(x);
}
}
}
JNGEN_DECLARE_PRINTER(JNGEN_HAS_FUNCTION(Container), 2)
{
if (mod.printN) {
out << t.size() << "\n";
}
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << " ";
}
JNGEN_PRINT(x);
}
}
JNGEN_DECLARE_PRINTER(JNGEN_HAS_FUNCTION(Container)
&& std::tuple_size<typename T::value_type>::value == 2, 3)
{
if (mod.printN) {
out << t.size() << "\n";
}
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << "\n";
}
JNGEN_PRINT(x);
}
}
// http://stackoverflow.com/a/19841470/2159939
#define JNGEN_COMMA ,
template<typename Lhs, typename Rhs>
JNGEN_DECLARE_SIMPLE_PRINTER(std::pair<Lhs JNGEN_COMMA Rhs>, 3)
{
JNGEN_PRINT(t.first);
out << " ";
JNGEN_PRINT(t.second);
}
#undef JNGEN_COMMA
// Following snippet allows writing
// cout << pair<int, int>(1, 2) << endl;
// in user code. I have to put it into separate namespace because
// 1) I don't want to 'use' all operator<< from jngen
// 2) I cannot do it in global namespace because JNGEN_HAS_OSTREAM relies
// on that it is in jngen.
namespace namespace_for_fake_operator_ltlt {
template<typename T>
auto operator<<(std::ostream& out, const T& t)
-> enable_if_t<
!JNGEN_HAS_OSTREAM() && !std::is_base_of<BaseReprProxy, T>::value,
std::ostream&
>
{
// not jngen::printValue, because relying on ADL here for printers declared
// later (see, e.g., http://stackoverflow.com/questions/42833134)
printValue(out, t, jngen::defaultMod, jngen::PTagMax{});
return out;
}
} // namespace namespace_for_fake_operator_ltlt
// Calling this operator inside jngen namespace doesn't work without this line.
using namespace jngen::namespace_for_fake_operator_ltlt;
} // namespace jngen
using namespace jngen::namespace_for_fake_operator_ltlt;
#include <algorithm>
#include <iterator>
#include <numeric>
namespace jngen {
// TODO: deprecate random_shuffle as done in testlib.h
template<typename Iterator>
void shuffle(Iterator begin, Iterator end) {
ensure(end >= begin, "Cannot shuffle range of negative length");
size_t size = end - begin;
for (size_t i = 1; i < size; ++i) {
std::swap(*(begin + i), *(begin + rnd.next(i + 1)));
}
}
template<typename Iterator>
auto choice(Iterator begin, Iterator end)
-> typename std::iterator_traits<Iterator>::value_type
{
return rnd.choice(begin, end);
}
template<typename Container>
typename Container::value_type choice(const Container& container) {
return rnd.choice(container);
}
template<typename T>
T choice(std::initializer_list<T> ilist) {
return choice(ilist.begin(), ilist.end());
}
namespace detail {
template<typename Collection2D>
typename Collection2D::value_type interleave(const Collection2D& collection) {
std::vector<size_t> sizes;
for (const auto& c: collection) {
sizes.push_back(c.size());
}
size_t size = std::accumulate(sizes.begin(), sizes.end(), 0u);
typename Collection2D::value_type result;
while (size > 0) {
size_t id = rnd.nextByDistribution(sizes);
result.emplace_back(collection[id][collection[id].size() - sizes[id]]);
--sizes[id];
--size;
}
return result;
}
} // namespace detail
template<typename Collection2D>
typename Collection2D::value_type interleave(const Collection2D& collection) {
return detail::interleave(collection);
}
template<typename Collection>
Collection interleave(const std::initializer_list<Collection>& ilist) {
return detail::interleave<std::vector<Collection>>(ilist);
}
} // namespace jngen
using jngen::shuffle;
using jngen::choice;
using jngen::interleave;
#include <algorithm>
#include <initializer_list>
#include <numeric>
#include <set>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <unordered_map>
#include <utility>
#include <vector>
namespace jngen {
template<typename T>
class GenericArray : public ReprProxy<GenericArray<T>>, public std::vector<T> {
public:
typedef std::vector<T> Base;
using Base::Base;
GenericArray() {}
GenericArray(const GenericArray<T>&) = default;
GenericArray& operator=(const GenericArray<T>&) = default;
GenericArray(GenericArray<T>&&) = default;
GenericArray& operator=(GenericArray<T>&&) = default;
~GenericArray() {}
/* implicit */ GenericArray(const Base& base) :
Base(base)
{ }
using Base::at;
using Base::size;
using Base::resize;
using Base::begin;
using Base::end;
using Base::insert;
using Base::clear;
using Base::erase;
void extend(size_t requiredSize) {
checkLargeParameter(requiredSize);
if (requiredSize > size()) {
resize(requiredSize);
}
}
template<typename F, typename ...Args>
static GenericArray<T> randomf(size_t size, F func, const Args& ... args);
template<typename F, typename ...Args>
static GenericArray<T> randomfUnique(
size_t size, F func, const Args& ... args);
template<typename F, typename ...Args>
static GenericArray<T> randomfAll(F func, const Args& ... args);
template<typename ...Args>
static GenericArray<T> random(size_t size, const Args& ... args);
template<typename ...Args>
static GenericArray<T> randomUnique(size_t size, const Args& ... args);
template<typename ...Args>
static GenericArray<T> randomAll(const Args& ... args);
static GenericArray<T> id(size_t size, T start = T{});
GenericArray<T>& shuffle();
GenericArray<T> shuffled() const;
GenericArray<T>& reverse();
GenericArray<T> reversed() const;
GenericArray<T>& sort();
GenericArray<T> sorted() const;
template<typename Comp>
GenericArray<T>& sort(Comp&& comp);
template<typename Comp>
GenericArray<T> sorted(Comp&& comp) const;
GenericArray<T>& unique();
GenericArray<T> uniqued() const;
GenericArray<T> inverse() const;
template<typename Integer>
GenericArray<T> subseq(const std::vector<Integer>& indices) const;
template<typename Integer>
GenericArray<T> subseq(
const std::initializer_list<Integer>& indices) const;
T choice() const;
GenericArray<T> choice(size_t count) const;
GenericArray<T> choiceWithRepetition(size_t count) const;
GenericArray<T>& operator+=(const GenericArray<T>& other);
GenericArray<T> operator+(const GenericArray<T>& other) const;
GenericArray<T>& operator*=(int k);
GenericArray<T> operator*(int k) const;
operator std::string() const;
};
template<typename T>
template<typename ...Args>
GenericArray<T> GenericArray<T>::random(size_t size, const Args& ... args) {
checkLargeParameter(size);
GenericArray<T> result(size);
for (T& x: result) {
x = rnd.tnext<T>(args...);
}
return result;
}
template<typename T>
template<typename F, typename ...Args>
GenericArray<T> GenericArray<T>::randomf(
size_t size,
F func,
const Args& ... args)
{
checkLargeParameter(size);
GenericArray<T> result(size);
for (T& x: result) {
x = func(args...);
}
return result;
}
namespace detail {
template<typename T, typename Enable = std::size_t>
struct DictContainer {
typedef std::set<T> type;
};
template<typename T>
struct DictContainer<T, typename std::hash<T>::result_type>
{
typedef std::unordered_set<T> type;
};
} // namespace detail
template<typename T>
template<typename F, typename ...Args>
GenericArray<T> GenericArray<T>::randomfUnique(
size_t size,
F func,
const Args& ... args)
{
typename detail::DictContainer<T>::type set;
checkLargeParameter(size);
GenericArray<T> result;
result.reserve(size);
size_t retries = (size + 10) * log(size + 10) * 2;
while (result.size() != size) {
T t = func(args...);
if (!set.count(t)) {
set.insert(t);
result.push_back(t);
}
if (--retries == 0) {
ensure(false, "There are not enough unique elements");
}
}
return result;
}
template<typename T>
template<typename ...Args>
GenericArray<T> GenericArray<T>::randomUnique(
size_t size, const Args& ... args)
{
return GenericArray<T>::randomfUnique(
size,
[](Args... args) { return rnd.tnext<T>(args...); },
args...);
}
template<typename T>
template<typename F, typename ...Args>
GenericArray<T> GenericArray<T>::randomfAll(
F func,
const Args& ... args)
{
typename detail::DictContainer<T>::type set;
GenericArray<T> result;
size_t timeAfterLastHit = 0;
while (true) {
T t = func(args...);
if (!set.count(t)) {
set.insert(t);
result.push_back(t);
timeAfterLastHit = 0;
}
++timeAfterLastHit;
// Probability of finding not all elements is about e^{-20} ~= 1e-9
if (timeAfterLastHit > (result.size() + 10) * 20) {
return result;
}
}
}
template<typename T>
template<typename ...Args>
GenericArray<T> GenericArray<T>::randomAll(const Args& ... args)
{
return GenericArray<T>::randomfAll(
[](Args... args) { return rnd.tnext<T>(args...); },
args...);
}
template<typename T>
GenericArray<T> GenericArray<T>::id(size_t size, T start) {
constexpr bool enable = std::is_integral<T>::value;
static_assert(enable, "Cannot call Array<T>::id with non-integral T");
checkLargeParameter(size);
if (enable) {
GenericArray<T> result(size);
std::iota(result.begin(), result.end(), start);
return result;
} else {
return {};
}
}
template<typename T>
GenericArray<T>& GenericArray<T>::shuffle() {
jngen::shuffle(begin(), end());
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::shuffled() const {
auto res = *this;
res.shuffle();
return res;
}
template<typename T>
GenericArray<T>& GenericArray<T>::reverse() {
std::reverse(begin(), end());
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::reversed() const {
auto res = *this;
res.reverse();
return res;
}
template<typename T>
GenericArray<T>& GenericArray<T>::sort() {
std::sort(begin(), end());
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::sorted() const {
auto res = *this;
res.sort();
return res;
}
template<typename T>
template<typename Comp>
GenericArray<T>& GenericArray<T>::sort(Comp&& comp) {
std::sort(begin(), end(), comp);
return *this;
}
template<typename T>
template<typename Comp>
GenericArray<T> GenericArray<T>::sorted(Comp&& comp) const {
auto res = *this;
res.sort(comp);
return res;
}
template<typename T>
GenericArray<T>& GenericArray<T>::unique() {
erase(std::unique(begin(), end()), end());
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::uniqued() const {
auto res = *this;
res.unique();
return res;
}
template<typename T>
GenericArray<T> GenericArray<T>::inverse() const {
static_assert(
std::is_integral<T>::value,
"Can only take inverse permutation of integral array");
int n = size();
if (n == 0) {
return *this;
}
// sanity check
ensure(*max_element(begin(), end()) == n-1 &&
*min_element(begin(), end()) == 0,
"Trying to take inverse of the array which is not a permutation");
const static T NONE = static_cast<T>(-1);
GenericArray<T> result(n, NONE);
for (int i = 0; i < n; ++i) {
ensure(result[at(i)] == NONE,
"Trying to take inverse of the array which is not a permutation");
result[at(i)] = i;
}
return result;
}
template<typename T>
template<typename Integer>
GenericArray<T> GenericArray<T>::subseq(
const std::vector<Integer>& indices) const
{
GenericArray<T> result;
result.reserve(indices.size());
for (Integer idx: indices) {
result.push_back(at(idx));
}
return result;
}
// TODO(ifsmirnov): ever need to make it faster?
template<typename T>
template<typename Integer>
GenericArray<T> GenericArray<T>::subseq(
const std::initializer_list<Integer>& indices) const
{
return subseq(std::vector<T>(indices));
}
template<typename T>
T GenericArray<T>::choice() const {
return jngen::choice(begin(), end());
}
template<typename T>
GenericArray<T> GenericArray<T>::choice(size_t count) const {
ensure(
count <= size(),
"Use Array::choiceWithRepetition to select more than size() elements");
size_t n = size();
std::unordered_map<size_t, size_t> used;
std::vector<size_t> res;
for (size_t i = 0; i < count; ++i) {
size_t oldValue = used.count(n-i-1) ? used[n-i-1] : n-i-1;
size_t index = rnd.tnext<size_t>(n-i);
res.push_back(used.count(index) ? used[index] : index);
used[index] = oldValue;
}
return subseq(res);
}
template<typename T>
GenericArray<T> GenericArray<T>::choiceWithRepetition(size_t count) const {
checkLargeParameter(count);
GenericArray<T> res(count);
for (T& t: res) {
t = choice();
}
return res;
}
template<typename T>
GenericArray<T>& GenericArray<T>::operator+=(const GenericArray<T>& other) {
if (&other == this) {
return *this *= 2;
}
insert(end(), other.begin(), other.end());
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::operator+(const GenericArray<T>& other) const {
GenericArray<T> copy(*this);
return copy += other;
}
template<typename T>
GenericArray<T>& GenericArray<T>::operator*=(int k) {
if (k == 0) {
clear();
return *this;
}
this->reserve(size() * k);
std::copy_n(begin(), size() * (k - 1), std::back_inserter(*this));
return *this;
}
template<typename T>
GenericArray<T> GenericArray<T>::operator*(int k) const {
GenericArray<T> copy(*this);
return copy *= k;
}
template<typename T>
GenericArray<T>::operator std::string() const {
static_assert(std::is_same<T, char>::value, "Must not cast"
" TArray<T> to std::string with 'T' != 'char'");
return std::string(begin(), end());
}
// JNGEN_EXTERN template class GenericArray<int>;
template<typename T>
using TArray = GenericArray<T>;
using Array = GenericArray<int>;
using Array2d = GenericArray<jngen::GenericArray<int>>;
using Array64 = GenericArray<long long>;
using Arrayf = GenericArray<double>;
using Arrayp = GenericArray<std::pair<int, int>>;
template<typename T>
jngen::GenericArray<T> makeArray(const std::vector<T>& values) {
return jngen::GenericArray<T>(values);
}
template<typename T>
jngen::GenericArray<T> makeArray(const std::initializer_list<T>& values) {
return jngen::GenericArray<T>(values);
}
template<typename T, typename U>
TArray<std::pair<T, U>> zip(const TArray<T>& lhs, const TArray<U>& rhs) {
ensure(
lhs.size() == rhs.size(),
"In zip(a, b), a and b must have the same size");
TArray<std::pair<T, U>> result;
for (size_t i = 0; i < lhs.size(); ++i) {
result.emplace_back(lhs[i], rhs[i]);
}
return result;
}
template<typename T, typename U>
TArray<T> arrayCast(const TArray<U>& array) {
return TArray<T>(array.begin(), array.end());
}
template<typename T>
struct Hash<TArray<T>> {
uint64_t operator()(const TArray<T>& elements) const {
return Hash<std::vector<T>>{}(elements);
}
};
} // namespace jngen
JNGEN_DEFINE_STD_HASH_TEMPLATE(T, jngen::TArray<T>);
using jngen::makeArray;
using jngen::zip;
using jngen::arrayCast;
using jngen::TArray;
using jngen::Array;
using jngen::Array2d;
using jngen::Array64;
using jngen::Arrayf;
using jngen::Arrayp;
// This header is named 'math_jngen.h' and not 'math.h' because in the latter
// case it will replace the standard 'math.h' if you set jngen folder as the
// include path.
#include <algorithm>
#include <cmath>
#include <iterator>
#include <limits>
#include <type_traits>
#include <unordered_set>
#include <vector>
namespace jngen {
namespace detail {
inline int multiply(int x, int y, int mod) {
return static_cast<long long>(x) * y % mod;
}
inline long long multiply(long long x, long long y, long long mod) {
#if defined(__SIZEOF_INT128__)
return static_cast<__int128>(x) * y % mod;
#else
long long res = 0;
while (y) {
if (y&1) {
res = (static_cast<unsigned long long>(res) + x) % mod;
}
x = (static_cast<unsigned long long>(x) + x) % mod;
y >>= 1;
}
return res;
#endif
}
inline int power(int x, int k, int mod) {
int res = 1;
while (k) {
if (k&1) {
res = multiply(res, x, mod);
}
x = multiply(x, x, mod);
k >>= 1;
}
return res;
}
inline long long power(long long x, long long k, long long mod) {
long long res = 1;
while (k) {
if (k&1) {
res = multiply(res, x, mod);
}
x = multiply(x, x, mod);
k >>= 1;
}
return res;
}
template<typename I>
bool millerRabinTest(I n, const std::vector<I>& witnesses) {
static_assert(
std::is_same<I, int>::value || std::is_same<I, long long>::value,
"millerRabinTest<int/long long> only is supported");
if (n == 1) {
return false;
}
constexpr int LIMIT = 10000;
if (n <= LIMIT) {
for (int i = 2; i*i <= n; ++i) {
if (n%i == 0) {
return false;
}
}
return true;
}
int r = 0;
I d = n - 1;
while (d % 2 == 0) {
++r;
d /= 2;
}
for (I a: witnesses) {
I x = power(a, d, n);
if (x == 1 || x == n - 1) {
continue;
}
bool composite = true;
for (int i = 0; i < r - 1; ++i) {
x = multiply(x, x, n);
if (x == 1) {
return false;
}
if (x == n - 1) {
i = r;
composite = false;
continue;
}
}
if (composite) {
return false;
}
}
return true;
}
} // namespace detail
inline bool isPrime(long long n) {
const static std::vector<int> INT_WITNESSES{2, 7, 61};
const static std::vector<long long> LONG_LONG_WITNESSES
{2, 3, 5, 7, 11, 13, 17, 19, 23};
// todo: experiment with base
// 2, 325, 9375, 28178, 450775, 9780504, and 1795265022
// (guaranteed for all integers < 2^64)
// first strong pseudoprime to i64 bases is 3825123056546413051 ~= 3.8e18
ensure(n > 0, "isPrime() is undefined for negative numbers");
ensure(
n <= static_cast<long long>(3.8e18),
"isPrime() supports only numbers not greater than 3.8 * 10^18");
if (n < std::numeric_limits<int>::max()) {
return detail::millerRabinTest<int>(n, INT_WITNESSES);
} else {
return detail::millerRabinTest<long long>(n, LONG_LONG_WITNESSES);
}
}
class MathRandom {
public:
MathRandom() {
static bool created = false;
ensure(!created, "jngen::MathRandom should be created only once");
created = true;
}
static long long randomPrime(long long n) {
ensure(n > 2, format("There are no primes below %lld", n));
return randomPrime(2, n - 1);
}
static long long randomPrime(long long l, long long r) {
ensure(l <= r);
std::unordered_set<long long> used;
while (static_cast<long long>(used.size()) < r - l + 1) {
long long x = rnd.next(l, r);
if (used.count(x)) {
continue;
}
used.insert(x);
if (isPrime(x)) {
return x;
}
}
ensure(
false,
format(
"There are no primes between %lld and %lld",
l, r)
);
}
static long long nextPrime(long long n) {
while (!isPrime(n)) {
++n;
}
return n;
}
static long long previousPrime(long long n) {
ensure(n >= 2, format("There are no primes less or equal to %lld", n));
while (!isPrime(n)) {
--n;
}
return n;
}
static Array partition(
int n,
int numParts,
int minSize = 0,
int maxSize = -1)
{
auto res = partition(
static_cast<long long>(n),
numParts,
static_cast<long long>(minSize),
static_cast<long long>(maxSize));
return Array(res.begin(), res.end());
}
static Array64 partition(
long long n,
int numParts,
long long minSize = 0,
long long maxSize = -1)
{
if (maxSize == -1) {
maxSize = n;
}
ensure(n >= 0);
ensure(numParts >= 0);
ensure(numParts * minSize <= n, "minSize is too large");
ensure(numParts * maxSize >= n, "maxSize is too small");
ensure(minSize <= maxSize);
n -= minSize * numParts;
auto delimiters = Array64::random(
numParts - 1, 0, n).sorted();
delimiters.insert(delimiters.begin(), 0);
delimiters.push_back(n);
Array64 partition(numParts);
for (long long i = 0; i < numParts; ++i) {
partition[i] = delimiters[i + 1] - delimiters[i];
}
partition.sort().reverse();
long long remaining = 0;
long long localMax = maxSize - minSize;
for (auto& x: partition) {
if (x > localMax) {
remaining += x - localMax;
x = localMax;
}
x += minSize;
}
// Here we try to distribute the remaining part in some even manner
// between remaining slots. Looks like crap anyway, need a smarter way.
for (int divisor: { 2, 1 }) {
partition.shuffle();
for (auto& x: partition) {
if (x < maxSize) {
long long add = std::min(
remaining, (maxSize - x) / divisor);
x += add;
remaining -= add;
}
}
}
ensure(remaining == 0, "maxSize is too small");
return partition;
}
template<typename T>
TArray<TArray<T>> partition(
TArray<T> elements,
int numParts,
int minSize = 0,
int maxSize = -1)
{
return partition(
std::move(elements),
partition(
static_cast<int>(elements.size()),
numParts,
minSize,
maxSize));
}
template<typename T>
TArray<TArray<T>> partition(TArray<T> elements, const Array& sizes) {
size_t total = std::accumulate(sizes.begin(), sizes.end(), size_t(0));
ensure(total == elements.size(), "sum(sizes) != elements.size()");
elements.shuffle();
TArray<TArray<T>> res;
auto it = elements.begin();
for (int size: sizes) {
res.emplace_back();
std::copy(it, it + size, std::back_inserter(res.back()));
it += size;
}
return res;
}
};
JNGEN_EXTERN MathRandom rndm;
} // namespace jngen
using jngen::isPrime;
using jngen::rndm;
#include <algorithm>
#include <cstdlib>
namespace jngen {
enum class UnorderedSetCompiler {
Gcc4,
Gcc5or6,
Clang
};
class ArrayRandom {
public:
ArrayRandom() {
static bool created = false;
ENSURE(!created, "jngen::ArrayRandom should be created only once");
created = true;
}
template<typename F, typename ...Args>
static auto randomf(
size_t size,
F func,
Args... args) -> GenericArray<decltype(func(args...))>
{
typedef decltype(func(args...)) T;
return GenericArray<T>::randomf(size, func, args...);
}
template<typename F, typename ...Args>
static auto randomfUnique(
size_t size,
F func,
Args... args) -> GenericArray<decltype(func(args...))>
{
typedef decltype(func(args...)) T;
return GenericArray<T>::randomfUnique(size, func, args...);
}
template<typename F, typename ...Args>
static auto randomfAll(
F func,
Args... args) -> GenericArray<decltype(func(args...))>
{
typedef decltype(func(args...)) T;
return GenericArray<T>::randomfAll(func, args...);
}
static Array64 antiUnorderedSet(
int n,
double maxLoadFactor = 1.0,
bool reserve = false,
UnorderedSetCompiler compiler = UnorderedSetCompiler::Gcc4);
private:
static Array64 numbersDividingPrime(int n, long long p);
static long long nextPrime(
unsigned long long x,
UnorderedSetCompiler compiler);
};
JNGEN_EXTERN ArrayRandom rnda;
#ifndef JNGEN_DECLARE_ONLY
Array64 ArrayRandom::antiUnorderedSet(
int n,
double maxLoadFactor,
bool reserve,
UnorderedSetCompiler compiler)
{
ensure(
compiler == UnorderedSetCompiler::Gcc4,
"unordered set antitest supported only for gcc-4.x yet");
ensure(
n <= 1000000,
"unordered set antitest supported only for n <= 1e7");
int buckets;
if (reserve) {
buckets = nextPrime(std::ceil(n / maxLoadFactor), compiler);
} else {
buckets = 2;
for (int size = 1; size <= n; ++size) {
if (size + 1 > buckets * maxLoadFactor) {
buckets = nextPrime(buckets * 2, compiler);
}
}
}
return numbersDividingPrime(n, buckets);
}
Array64 ArrayRandom::numbersDividingPrime(int n, long long p) {
auto a = Array64::id(n);
for (auto& x: a) {
x *= p;
}
return a;
}
long long ArrayRandom::nextPrime(
unsigned long long x,
UnorderedSetCompiler compiler)
{
ENSURE(compiler == UnorderedSetCompiler::Gcc4);
const static size_t SIZE =
sizeof(impl::primeList) / sizeof(impl::primeList[0]);
return *std::lower_bound(impl::primeList, impl::primeList + SIZE, x);
}
#endif // JNGEN_DECLARE_ONLY
} // namespace jngen
using jngen::rnda;
using jngen::UnorderedSetCompiler;
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_set>
namespace jngen {
#ifdef JNGEN_DECLARE_ONLY
extern long double eps;
#else
long double eps = 1e-9;
#endif
inline void setEps(long double value) {
eps = value;
}
template<typename T, typename U, typename Enable = void>
struct Comparator {
static bool eq(T a, U b) { return a == b; }
static bool lt(T a, U b) { return a < b; }
};
template<typename T, typename U>
struct Comparator<T, U, enable_if_t<
std::is_floating_point<T>::value || std::is_floating_point<U>::value,
void>>
{
static bool eq(T a, U b) { return std::abs(b - a) < eps; }
static bool lt(T a, U b) { return a < b - eps; }
};
template<typename T, typename U>
bool eq(T t, U u) {
return Comparator<T, U>().eq(t, u);
}
template<typename T, typename U>
bool lt(T t, U u) {
return Comparator<T, U>().lt(t, u);
}
template<typename T, typename U> bool ne(T t, U u) { return !eq(t, u); }
template<typename T, typename U> bool le(T t, U u) { return !lt(u, t); }
template<typename T, typename U> bool gt(T t, U u) { return lt(u, t); }
template<typename T, typename U> bool ge(T t, U u) { return !lt(t, u); }
template<typename T>
struct TPoint : public ReprProxy<TPoint<T>> {
T x, y;
TPoint() : x(0), y(0) {}
TPoint(T x, T y) : x(x), y(y) {}
template<typename U>
TPoint(const TPoint<U>& other) : x(other.x), y(other.y) {}
TPoint<T> operator+(const TPoint<T>& other) const {
return TPoint<T>(x + other.x, y + other.y);
}
TPoint<T>& operator+=(const TPoint<T>& other) {
x += other.x;
y += other.y;
return *this;
}
TPoint<T> operator-(const TPoint<T>& other) const {
return TPoint<T>(x - other.x, y - other.y);
}
TPoint<T>& operator-=(const TPoint<T>& other) {
x -= other.x;
y -= other.y;
return *this;
}
TPoint<T> operator-() const {
return TPoint<T>(-x, -y);
}
TPoint<T> operator*(T factor) const {
return TPoint<T>(x * factor, y * factor);
}
TPoint<T>& operator*=(T factor) {
x *= factor;
y *= factor;
return *this;
}
T operator*(const TPoint<T>& other) const {
return x * other.x + y * other.y;
}
T operator%(const TPoint<T>& other) const {
return x * other.y - y * other.x;
}
bool operator==(const TPoint<T>& other) const {
return eq(x, other.x) && eq(y, other.y);
}
bool operator!=(const TPoint<T>& other) const {
return !(*this == other);
}
bool operator<(const TPoint<T>& other) const {
if (eq(x, other.x)) {
return lt(y, other.y);
}
return lt(x, other.x);
}
};
using Point = TPoint<long long>;
using Pointf = TPoint<long double>;
template<>
struct Hash<Point> {
uint64_t operator()(const Point& point) const {
uint64_t h = 0;
impl::hashCombine(h, Hash<long long>{}(point.x));
impl::hashCombine(h, Hash<long long>{}(point.y));
return h;
}
};
template<typename T>
JNGEN_DECLARE_SIMPLE_PRINTER(TPoint<T>, 3) {
(void)mod;
out << t.x << " " << t.y;
}
template<typename T>
class TPolygon : public GenericArray<TPoint<T>> {
public:
using Base = GenericArray<TPoint<T>>;
using Base::Base;
TPolygon<T>& shift(const TPoint<T>& vector) {
for (auto &pt: *this) {
pt += vector;
}
return *this;
}
TPolygon<T> shifted(const TPoint<T>& vector) const {
auto res = *this;
res.shift(vector);
return res;
}
TPolygon<T>& reflect() {
for (auto& pt: *this) {
pt = -pt;
}
return *this;
}
TPolygon<T> reflected() const {
auto res = *this;
res.reflect();
return res;
}
};
using Polygon = TPolygon<long long>;
using Polygonf = TPolygon<long double>;
template<>
struct Hash<Polygon> {
uint64_t operator()(const Polygon& p) const {
return Hash<TArray<Point>>{}(p);
}
};
template<typename T>
JNGEN_DECLARE_SIMPLE_PRINTER(TArray<TPoint<T>>, 5) {
// I should avoid copy-paste from array printer here but need to output
// points with '\n' separator. Maybe 'mod' should be made non-const?
if (mod.printN) {
out << t.size() << "\n";
}
bool first = true;
for (const auto& x: t) {
if (first) {
first = false;
} else {
out << '\n';
}
JNGEN_PRINT(x);
}
}
namespace detail {
template<typename T>
TPolygon<T> convexHull(TArray<TPoint<T>> points) {
points.sort().unique();
if (points.size() <= 2u) {
return points;
}
TArray<TPoint<T>> upper(points.begin(), points.begin() + 2);
upper.reserve(points.size());
int top = 1;
for (size_t i = 2; i < points.size(); ++i) {
while (top >= 1 && ge(
(upper[top] - upper[top-1]) % (points[i] - upper[top]), 0ll))
{
upper.pop_back();
--top;
}
upper.push_back(points[i]);
++top;
}
TArray<TPoint<T>> lower(points.begin(), points.begin() + 2);
lower.reserve(points.size());
top = 1;
for (size_t i = 2; i < points.size(); ++i) {
while (top >= 1 && le(
(lower[top] - lower[top-1]) % (points[i] - lower[top]), 0ll))
{
lower.pop_back();
--top;
}
lower.push_back(points[i]);
++top;
}
upper.pop_back();
upper.erase(upper.begin());
return lower + upper.reversed();
}
template<typename T>
TPolygon<T> convexPolygonByEllipse(
int n, Pointf center, Pointf xAxis, Pointf yAxis)
{
return convexHull(rnda.randomf(
n,
[center, xAxis, yAxis] () -> TPoint<T> {
static const long double PI = acosl(-1.0);
long double angle = rnd.next(0., PI*2);
long double sina = sinl(angle);
long double cosa = cosl(angle);
return center + xAxis * cosa + yAxis * sina;
}
));
}
} // namespace detail
class GeometryRandom {
public:
GeometryRandom() {
static bool created = false;
ensure(!created, "jngen::GeometryRandom should be created only once");
created = true;
}
// point in [0, C] x [0, C]
static Point point(long long C);
// point in [min, max] x [min, max]
static Point point(long long min, long long max);
// point in [X1, Y1] x [X2, Y2]
static Point point(
long long X1, long long Y1,
long long X2, long long Y2);
// point in [0, C] x [0, C]
static Point pointf(long double C);
// point in [min, max] x [min, max]
static Point pointf(long double min, long double max);
// point in [X1, Y1] x [X2, Y2]
static Point pointf(
long double X1, long double Y1,
long double X2, long double Y2);
static Polygon convexPolygon(int n, long long C);
static Polygon convexPolygon(int n, long long min, long long max);
static Polygon convexPolygon(
int n,
long long X1, long long Y1,
long long X2, long long Y2);
static TArray<Point> pointsInGeneralPosition(int n, long long C);
static TArray<Point> pointsInGeneralPosition(
int n, long long min, long long max);
static TArray<Point> pointsInGeneralPosition(
int n,
long long X1, long long Y1,
long long X2, long long Y2);
};
JNGEN_EXTERN GeometryRandom rndg;
JNGEN_EXTERN template struct jngen::TPoint<long long>;
JNGEN_EXTERN template struct jngen::TPoint<long double>;
JNGEN_EXTERN template class jngen::TPolygon<long long>;
JNGEN_EXTERN template class jngen::TPolygon<long double>;
JNGEN_EXTERN template TPolygon<long long> detail::convexHull<long long>(
TArray<TPoint<long long>> points);
JNGEN_EXTERN template TPolygon<long double> detail::convexHull<long double>(
TArray<TPoint<long double>> points);
} // namespace jngen
JNGEN_DEFINE_STD_HASH(jngen::Point);
JNGEN_DEFINE_STD_HASH(jngen::Polygon);
using jngen::Point;
using jngen::Pointf;
using jngen::Polygon;
using jngen::Polygonf;
using jngen::rndg;
using jngen::setEps;
// workaround for g++-7
namespace std {
JNGEN_EXTERN template class std::allocator<Point>;
JNGEN_EXTERN template class std::allocator<Pointf>;
} // namespace std
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_GEOMETRY_INL_H
#ifndef JNGEN_INCLUDE_GEOMETRY_INL_H
#error File "geometry_inl.h" must not be included directly.
#endif
namespace jngen {
Point GeometryRandom::point(long long C) {
return point(0, 0, C, C);
}
Point GeometryRandom::point(long long min, long long max) {
return point(min, min, max, max);
}
Point GeometryRandom::point(
long long X1, long long Y1,
long long X2, long long Y2) {
long long x = rnd.tnext<long long>(X1, X2);
long long y = rnd.tnext<long long>(Y1, Y2);
return Point(x, y);
}
Point GeometryRandom::pointf(long double C) {
return pointf(0, 0, C, C);
}
Point GeometryRandom::pointf(long double min, long double max) {
return pointf(min, min, max, max);
}
Point GeometryRandom::pointf(
long double X1, long double Y1,
long double X2, long double Y2)
{
long double x = rnd.tnext<long double>(X1, X2);
long double y = rnd.tnext<long double>(Y1, Y2);
return Pointf(x, y);
}
Polygon GeometryRandom::convexPolygon(int n, long long C) {
return convexPolygon(n, 0, 0, C, C);
}
Polygon GeometryRandom::convexPolygon(int n, long long min, long long max) {
return convexPolygon(n, min, min, max, max);
}
Polygon GeometryRandom::convexPolygon(
int n,
long long X1, long long Y1,
long long X2, long long Y2)
{
// todo: off-by-one error?
auto dx = X2 - X1;
auto dy = Y2 - Y1;
ensure(n >= 0);
Polygon res = detail::convexPolygonByEllipse<long long>(
n * 10, // BUBEN!
Point(dx / 2, dy / 2),
Point(dx / 2, 0),
Point(0, dy / 2)
);
res.shift(Point(X1, Y1));
for (auto& x: res) {
ENSURE(x.x >= X1);
ENSURE(x.x <= X2);
ENSURE(x.y >= Y1);
ENSURE(x.y <= Y2);
}
ensure(
static_cast<int>(res.size()) >= n,
"Cannot generate a convex polygon with so many vertices");
return res.subseq(Array::id(res.size()).choice(n).sort());
}
TArray<Point> GeometryRandom::pointsInGeneralPosition(int n, long long C) {
return pointsInGeneralPosition(n, 0, 0, C, C);
}
TArray<Point> GeometryRandom::pointsInGeneralPosition(
int n, long long min, long long max)
{
return pointsInGeneralPosition(n, min, min, max, max);
}
TArray<Point> GeometryRandom::pointsInGeneralPosition(
int n,
long long X1, long long Y1,
long long X2, long long Y2)
{
struct Line {
long long A, B, C; // Ax + By + C = 0
Line() {}
Line(const Point& p1, const Point& p2) {
A = p1.y - p2.y;
B = p2.x - p1.x;
C = -(p1.x * A + p1.y * B);
ENSURE(A != 0 || B != 0);
long long g = util::gcd(A, util::gcd(B, C));
A /= g;
B /= g;
C /= g;
if (A < 0 || (A == 0 && B < 0)) {
A = -A;
B = -B;
C = -C;
}
}
bool operator<(const Line& other) const {
return std::tie(A, B, C) < std::tie(other.A, other.B, other.C);
}
};
const long long LIMIT = 2e9;
ensure(
std::abs(X2 - X1) <= LIMIT && X1 <= LIMIT && X2 <= LIMIT &&
std::abs(Y2 - Y1) <= LIMIT && Y1 <= LIMIT && Y2 <= LIMIT,
"rndg.pointsInGeneralPosition must not be called with coordinates "
"larger than 2e9");
std::set<Line> lines;
std::unordered_set<Point> points;
TArray<Point> res;
while (static_cast<int>(res.size()) != n) {
Point p = point(X1, Y1, X2, Y2);
if (points.count(p)) {
continue;
}
if (std::none_of(
res.begin(),
res.end(),
[&lines, &p] (const Point& q) {
return lines.count(Line(p, q));
}))
{
points.insert(p);
for (const auto& q: res) {
lines.emplace(p, q);
}
res.push_back(p);
}
}
return res;
}
} // namespace jngen
#undef JNGEN_INCLUDE_GEOMETRY_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <algorithm>
#include <utility>
namespace jngen {
class QueryBuilder {
public:
QueryBuilder() {}
QueryBuilder(int n);
QueryBuilder(int l, int r);
QueryBuilder& minLen(int value);
QueryBuilder& maxLen(int value);
QueryBuilder& range(int n);
QueryBuilder& range(int l, int r);
QueryBuilder& small();
QueryBuilder& large();
QueryBuilder& ordered(bool value);
std::pair<int, int> next();
Arrayp next(int m);
private:
enum class QueryType {
Default, Large, Small
};
QueryType queryType_ = QueryType::Default;
std::pair<int, int> range_; // half-interval
std::pair<int, int> lenRange_; // segment
bool ordered_;
};
#ifndef JNGEN_DECLARE_ONLY
QueryBuilder::QueryBuilder(int n) :
QueryBuilder(0, n - 1)
{ }
QueryBuilder::QueryBuilder(int l, int r) :
range_(l, r + 1),
lenRange_(1, r - l + 1),
ordered_(true)
{
ensure(l <= r);
}
QueryBuilder& QueryBuilder::minLen(int value) {
lenRange_.first = value;
return *this;
}
QueryBuilder& QueryBuilder::maxLen(int value) {
lenRange_.second = value;
return *this;
}
QueryBuilder& QueryBuilder::range(int n) {
ensure(n > 0);
return this->range(0, n - 1);
}
QueryBuilder& QueryBuilder::range(int l, int r) {
ensure(l <= r);
range_ = {l, r+1};
lenRange_.second = std::min(lenRange_.second, r - l + 1);
lenRange_.first = std::min(lenRange_.first, lenRange_.second);
return *this;
}
QueryBuilder& QueryBuilder::small() {
queryType_ = QueryType::Small;
return *this;
}
QueryBuilder& QueryBuilder::large() {
queryType_ = QueryType::Large;
return *this;
}
QueryBuilder& QueryBuilder::ordered(bool value) {
ordered_ = value;
return *this;
}
std::pair<int, int> QueryBuilder::next() {
switch (queryType_) {
case QueryType::Default: {
// This is inaccurate to say the least. I don't know how to
// generate a random segment with length from l to r without
// calling sqrt.
int len = rnd.wnext(lenRange_.first, lenRange_.second, -1);
int l = rnd.next(range_.first, range_.second - len);
if (ordered_ || rnd.next(2)) {
return {l, l + len - 1};
} else {
return {l + len - 1, l};
}
}
case QueryType::Large: {
ENSURE(false, "not implemented");
break;
}
case QueryType::Small: {
ENSURE(false, "not implemented");
break;
}
default: ENSURE(false, "Nonexistent option");
}
}
Arrayp QueryBuilder::next(int m) {
return rnda.randomf(m, [this]() { return next(); });
}
#endif
template<typename ... Args>
QueryBuilder rndq(Args... args) {
return QueryBuilder(args...);
}
} // namespace jngen
using jngen::rndq;
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
namespace jngen {
typedef std::pair<long long, long long> HashBase; // (mod, base)
typedef std::pair<std::string, std::string> StringPair;
class StringRandom {
public:
StringRandom() {
static bool created = false;
ENSURE(!created, "jngen::StringRandom should be created only once");
created = true;
}
static std::string random(int len, const std::string& alphabet = "a-z");
template<typename ... Args>
static std::string random(const std::string& pattern, Args... args) {
return rnd.next(pattern, std::forward(args)...);
}
static std::string thueMorse(int len, char first = 'a', char second = 'b');
static std::string abacaba(int len, char first = 'a');
static StringPair antiHash(
const std::vector<HashBase>& bases,
const std::string& alphabet = "a-z",
int length = -1);
};
JNGEN_EXTERN StringRandom rnds;
} // namespace jngen
using jngen::rnds;
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_RNDS_INL_H
#ifndef JNGEN_INCLUDE_RNDS_INL_H
#error File "rnds_inl.h" must not be included directly.
#endif
namespace jngen {
namespace detail {
int popcount(long long x) {
int res = 0;
while (x) {
++res;
x &= x-1;
}
return res;
}
int trailingZeroes(long long x) {
int res = 0;
ENSURE(x != 0);
while (!(x&1)) {
++res;
x >>= 1;
}
return res;
}
std::string parseAllowedChars(std::string pattern) {
std::string result;
pattern += "\0\0";
for (size_t i = 0; i < pattern.length(); ++i) {
if (pattern[i] == '-') {
result += '-';
} else if (pattern[i+1] == '-' && pattern[i+2] != '\0') {
for (char c = pattern[i]; c <= pattern[i+2]; ++c) {
result += c;
}
i += 2;
} else {
result += pattern[i];
}
}
std::sort(result.begin(), result.end());
return result;
}
std::vector<std::string> extendAntiHash(
const std::vector<std::string>& chars,
HashBase base,
int count)
{
ENSURE(count == 2, "Count != 2 is not supported (yet)");
size_t baseLength = chars[0].size();
for (const auto& s: chars) {
ensure(s.size() == baseLength);
}
long long mod = base.first;
long long p = base.second;
long long pPower = 1;
for (size_t i = 0; i != baseLength; ++i) {
pPower = (pPower * p) % mod;
}
std::vector<long long> charHashes;
for (const auto& s: chars) {
long long hash = 0;
for (char c: s) {
hash = (hash * p + c) % mod;
}
charHashes.push_back(hash);
}
auto computeHash = [&charHashes, mod, pPower](const std::vector<int>& a) {
long long hash = 0;
for (int x: a) {
hash = (hash * pPower + charHashes[x]) % mod;
}
return hash;
};
// This bounds were achieved empirically and should be justified.
int needForMatch;
if (count == 2) {
needForMatch = 5 * pow(double(mod), 0.5);
} else {
ENSURE(false, "Only count = 2 is supported yet");
}
int length = 2;
double wordCount = pow(double(chars.size()), double(length));
while (true) {
++length;
wordCount *= chars.size();
if (wordCount < needForMatch) {
continue;
}
std::vector<std::pair<long long, Array>> words;
std::map<long long, int> hashCount;
std::set<Array> used;
for (int i = 0; i < needForMatch; ++i) {
Array w = Array::random(length, chars.size());
if (used.count(w)) {
--i;
continue;
}
used.insert(w);
long long hash = computeHash(w);
words.emplace_back(hash, w);
if (++hashCount[hash] == count) {
std::vector<std::string> result;
for (const auto& kv: words) {
if (kv.first == hash) {
std::string word;
for (int c: kv.second) {
word += chars[c];
}
result.push_back(word);
}
}
return result;
}
}
}
}
StringPair minimalAntiHashTest(
std::vector<HashBase> bases,
const std::string allowedChars)
{
for (auto base: bases) {
ensure(base.first >= 0, "0 < MOD must hold");
ensure(
base.first <= (long long)(2e9),
"Modules larger than 2'000'000'000 are not supported yet");
ensure(
0 < base.second && base.second < base.first,
"0 <= P < MOD must hold");
}
std::vector<int> counts;
if (bases.size() == 1) {
counts = {2};
} else if (bases.size() == 2) {
counts = {2, 2};
} else {
counts.assign(bases.size(), 2);
}
std::vector<std::string> cur;
for (char c: allowedChars) {
cur.emplace_back(1, c);
}
for (size_t i = 0; i != bases.size(); ++i) {
cur = extendAntiHash(cur, bases[i], counts[i]);
ensure(static_cast<int>(cur.size()) == counts[i],
"Cannot generate long enough pair with same hash");
}
return {cur[0], cur[1]};
}
} // namespace detail
std::string StringRandom::random(int len, const std::string& alphabet) {
checkLargeParameter(len);
std::string chars = detail::parseAllowedChars(alphabet);
std::string res;
res.reserve(len);
for (int i = 0; i < len; ++i) {
res += choice(chars);
}
return res;
}
std::string StringRandom::thueMorse(int len, char first, char second) {
ensure(len >= 0);
checkLargeParameter(len);
std::string res(len, ' ');
for (int i = 0; i < len; ++i) {
res[i] = detail::popcount(i)%2 == 0 ? first : second;
}
return res;
}
std::string StringRandom::abacaba(int len, char first) {
ensure(len >= 0);
checkLargeParameter(len);
std::string res(len, ' ');
for (int i = 0; i < len; ++i) {
res[i] = first + detail::trailingZeroes(~i);
}
return res;
}
StringPair StringRandom::antiHash(
const std::vector<HashBase>& bases,
const std::string& alphabet,
int length)
{
checkLargeParameter(length);
std::string allowedChars = detail::parseAllowedChars(alphabet);
StringPair result = detail::minimalAntiHashTest(bases, allowedChars);
if (length == -1) {
return result;
}
ensure(
static_cast<int>(result.first.length()) <= length,
"Cannot generate enough long anti-hash test");
int extraLength = length - result.first.length();
int leftSize = rnd.next(0, extraLength);
std::string left = rnd.next(format("[%s]{%d}", alphabet.c_str(), leftSize));
std::string right =
rnd.next(format("[%s]{%d}", alphabet.c_str(), extraLength - leftSize));
return {
left + result.first + right,
left + result.second + right
};
}
} // namespace jngen
#undef JNGEN_INCLUDE_RNDS_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_set>
#include <vector>
namespace jngen {
namespace suites {
#define JNGEN_ADD_PRODUCER(...)\
{\
std::string name = #__VA_ARGS__;\
if (name.empty()) {\
name = format("noname%d", (int)names_.size());\
}\
if (std::find(names_.begin(), names_.end(), name) != names_.end()) {\
ENSURE(false, format("Duplicated test name: '%s'", name.c_str()));\
}\
names_.emplace_back(name);\
}\
*std::back_inserter(producers_) = [this] (JNGEN_PRODUCER_ARGS) -> value_type
template<typename T, typename Traits, typename ... Args>
class BaseTestSuite {
public:
explicit BaseTestSuite(const std::string& name) : name_(name) { }
BaseTestSuite(const BaseTestSuite&) = delete;
BaseTestSuite& operator=(const BaseTestSuite&) = delete;
size_t size() const {
return producers_.size();
}
TArray<std::string> names() const {
return names_;
}
T gen(size_t id, Args... args) const {
ensure(
id < producers_.size(),
format("Cannot generate test #%d in suite '%s', there are only "
"%d", (int)id, name_.c_str(), (int)producers_.size()));
return producers_[id](args...);
}
T gen(const std::string& name, Args... args) const {
size_t pos = std::find(names_.begin(), names_.end(), name)
- names_.begin();
ensure(
pos < names_.size(),
format("There is no test '%s' in suite '%s'",
name.c_str(), name_.c_str()));
return gen(pos, args...);
}
TArray<T> genMany(size_t count, Args... args) const {
ensure(
count <= producers_.size(),
format("Cannot generate %d tests in suite '%s', there are only "
"%d", (int)count, name_.c_str(), (int)producers_.size()));
TArray<T> result;
result.reserve(count);
for (size_t id = 0; id < count; ++id) {
try {
result.push_back(gen(id, args...));
} catch (...) {
std::cerr << "Cannot generate test #" << id << " of suite "
<< name_ << "\n";
}
}
return result;
}
Traits& conf() { return conf_; }
protected:
using Producer = std::function<T(Args...)>;
using value_type = T;
std::vector<Producer> producers_;
std::vector<std::string> names_;
Traits conf_;
private:
std::string name_;
};
}} // namespace jngen::suites
#include <algorithm>
#include <cstdio>
#include <cstdlib>
namespace jngen {
inline int getInitialTestNo() {
char *envvar = std::getenv("TESTNO");
int testno;
if (!envvar || 1 != std::sscanf(envvar, "%d", &testno)) {
return 1;
}
return testno;
}
#ifdef JNGEN_DECLARE_ONLY
extern int nextTestNo;
#else
int nextTestNo = -1;
#endif // JNGEN_DECLARE_ONLY
void startTest(int testNo);
void startTest();
void setNextTestNumber(int testNo);
Array64 randomTestSizes(
long long totalSize,
int count,
long long minSize,
long long maxSize,
const Array64& predefined);
Array randomTestSizes(
int totalSize,
int count,
int minSize,
int maxSize,
const Array& predefined);
#ifndef JNGEN_DECLARE_ONLY
void startTest(int testNo) {
nextTestNo = testNo + 1;
char filename[10];
std::sprintf(filename, "%d", testNo);
if (!std::freopen(filename, "w", stdout)) {
ensure(false, format("Cannot open the file `%s'", filename));
}
}
void startTest() {
if (nextTestNo == -1) {
nextTestNo = getInitialTestNo();
}
startTest(nextTestNo);
}
void setNextTestNumber(int testNo) {
nextTestNo = testNo;
}
Array64 randomTestSizes(
long long totalSize,
int count,
long long minSize,
long long maxSize,
const Array64& predefined)
{
for (auto x: predefined) {
totalSize -= x;
}
ensure(totalSize >= 0, "Sum of predefined test sizes exceeds total size");
ensure(count * minSize <= totalSize, "minSize is too large");
ensure(count * maxSize >= totalSize, "maxSize is too small");
ensure(minSize <= maxSize);
return (rndm.partition(totalSize, count, minSize, maxSize) +
predefined).shuffle();
}
Array randomTestSizes(
int totalSize,
int count,
int minSize,
int maxSize,
const Array& predefined)
{
return arrayCast<int>(randomTestSizes(
static_cast<long long>(totalSize),
count,
static_cast<long long>(minSize),
static_cast<long long>(maxSize),
arrayCast<long long>(predefined)
));
}
#endif // JNGEN_DECLARE_ONLY
} // namespace jngen
using jngen::startTest;
using jngen::setNextTestNumber;
using jngen::randomTestSizes;
#include <iostream>
#include <stdexcept>
#include <type_traits>
namespace jngen {
namespace variant_detail {
constexpr static int NO_TYPE = -1;
template<size_t Size, size_t Align, typename ... Args>
class VariantImpl;
template<size_t Size, size_t Align>
class VariantImpl<Size, Align> {
public:
VariantImpl() {
type_ = NO_TYPE;
}
private:
alignas(Align) char data_[Size];
int type_;
protected:
int& type() { return type_; }
int type() const { return type_; }
char* data() { return data_; }
const char* data() const { return data_; }
void doDestroy() {
throw;
}
template<typename P>
constexpr static int typeId() {
return NO_TYPE;
}
void copy(char*) const {
throw;
}
void move(char*) const {
throw;
}
void setType(int) {
throw;
}
template<typename V>
typename V::return_type applyVisitor(V&&) const {
throw;
}
void assign() {}
};
template<size_t Size, size_t Align, typename T, typename ... Args>
class VariantImpl<Size, Align, T, Args...> : public VariantImpl<
(sizeof(T) > Size ? sizeof(T) : Size),
(alignof(T) > Align ? alignof(T) : Align),
Args...
>
{
using Base = VariantImpl<
(sizeof(T) > Size ? sizeof(T) : Size),
(alignof(T) > Align ? alignof(T) : Align),
Args...
>;
constexpr static int MY_ID = sizeof...(Args);
protected:
void doDestroy() {
if (this->type() == MY_ID) {
this->type() = NO_TYPE;
reinterpret_cast<T*>(this->data())->~T();
} else {
Base::doDestroy();
}
}
template<typename P>
constexpr static int typeId() {
return std::is_same<P, T>::value ?
MY_ID :
Base::template typeId<P>();
}
void copy(char* dst) const {
if (this->type() == MY_ID) {
new(dst) T(*reinterpret_cast<const T*>(this->data()));
} else {
Base::copy(dst);
}
}
void move(char* dst) const {
if (this->type() == MY_ID) {
new(dst) T(std::move(*reinterpret_cast<const T*>(this->data())));
} else {
Base::copy(dst);
}
}
void setType(int typeIndex) {
if (typeIndex == MY_ID) {
if (this->type() != NO_TYPE) {
throw;
}
assign(T{});
} else {
Base::setType(typeIndex);
}
}
template<typename V>
typename V::return_type applyVisitor(V&& v) const {
if (this->type() == MY_ID) {
return v(*reinterpret_cast<const T*>(this->data()));
} else {
return Base::applyVisitor(std::forward<V>(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<T*>(this->data()); }
public:
operator T() const {
if (this->type() == MY_ID) {
return *reinterpret_cast<const T*>(this->data());
} else {
return T();
}
}
};
template<typename ... Args>
class Variant : public VariantImpl<0, 1, Args...> {
using Base = VariantImpl<0, 1, Args...>;
public:
Variant() { }
Variant(const Variant<Args...>& other) {
if (other.type() != NO_TYPE) {
other.copy(this->data());
unsafeType() = other.type();
}
}
Variant& operator=(const Variant<Args...>& other) {
if (&other == this) {
return *this;
}
if (this->type() != NO_TYPE) {
this->doDestroy();
}
if (other.type() != NO_TYPE) {
other.copy(this->data());
unsafeType() = other.type();
}
return *this;
}
Variant(Variant<Args...>&& other) {
if (other.type() != NO_TYPE) {
other.move(this->data());
unsafeType() = other.type();
} else {
unsafeType() = other.type();
}
}
Variant& operator=(Variant<Args...>&& other) {
if (&other == this) {
return *this;
}
if (this->type() != NO_TYPE) {
this->doDestroy();
}
if (other.type() != NO_TYPE) {
other.move(this->data());
unsafeType() = other.type();
}
return *this;
}
~Variant() {
if (type() != NO_TYPE) {
this->doDestroy();
}
}
template<typename T>
Variant(const T& t) : Variant() {
this->assign(t);
}
template<typename T>
T& ref() {
return *ptr<T>();
}
template<typename T>
const T& cref() const {
auto ptr = cptr<T>();
if (ptr == 0) {
throw std::logic_error("jngen::Variant: taking a reference for"
" a type which is not active now");
}
return *ptr;
}
bool operator==(const Variant& v) const { return compareTo(v) == 0; }
bool operator!=(const Variant& v) const { return compareTo(v) != 0; }
bool operator< (const Variant& v) const { return compareTo(v) < 0; }
bool operator> (const Variant& v) const { return compareTo(v) > 0; }
bool operator<=(const Variant& v) const { return compareTo(v) <= 0; }
bool operator>=(const Variant& v) const { return compareTo(v) >= 0; }
template<typename V>
typename V::return_type applyVisitor(V&& v) const {
return Base::applyVisitor(std::forward<V>(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<typename T>
constexpr static bool hasType() {
return Base::template typeId<T>() != NO_TYPE;
}
private:
template<typename T_>
decay_t<T_>* ptr() {
using T = decay_t<T_>;
if (type() != this->template typeId<T>()) {
if (type() != NO_TYPE) {
this->doDestroy();
}
::new(this->data()) T;
unsafeType() = this->template typeId<T>();
}
return reinterpret_cast<T*>(this->data());
}
template<typename T_>
const decay_t<T_>* cptr() const {
using T = decay_t<T_>;
if (type() != this->template typeId<T>()) {
return nullptr;
}
return reinterpret_cast<const T*>(this->data());
}
int& unsafeType() {
return Base::type();
}
int compareTo(const Variant& other) const;
};
struct OstreamVisitor {
using return_type = void;
template<typename T>
void operator()(const T& t) {
JNGEN_PRINT(t);
}
std::ostream& out;
const OutputModifier& mod;
};
template<typename V>
struct CompareToVisitor {
using return_type = int;
template<typename T>
int operator()(const T& t) {
if (t == variant.template cref<T>()) {
return 0;
}
return t < variant.template cref<T>() ? -1 : 1;
}
const V& variant;
};
template<typename ... Args>
int Variant<Args...>::compareTo(const Variant& other) const {
if (empty()) {
return other.empty() ? 0 : -1;
}
if (other.empty()) {
return 1;
}
if (type() != other.type()) {
return type() > other.type() ? -1 : 1;
}
return applyVisitor(CompareToVisitor<Variant>{other});
}
} // namespace variant_detail
using variant_detail::Variant;
template<typename ... Args>
JNGEN_DECLARE_SIMPLE_PRINTER(Variant<Args...>, 5) {
if (t.type() == jngen::variant_detail::NO_TYPE) {
out << "{empty variant}";
} else {
t.applyVisitor(jngen::variant_detail::OstreamVisitor{out, mod});
}
}
} // namespace jngen
#include <iterator>
#include <vector>
#include <type_traits>
namespace jngen {
template<typename ... Args>
class VariantArray : public GenericArray<Variant<Args...>> {
public:
using Base = GenericArray<Variant<Args...>>;
using BaseVariant = Variant<Args...>;
using Base::Base;
VariantArray() {}
/* implicit */ VariantArray(const Base& base) :
Base(base)
{ }
template<typename T, typename = typename std::enable_if<
BaseVariant::template hasType<T>()>::type>
VariantArray(const std::vector<T>& other) {
std::copy(other.begin(), other.end(), std::back_inserter(*this));
}
template<typename T, typename = typename std::enable_if<
BaseVariant::template hasType<T>()>::type>
VariantArray(std::vector<T>&& other) {
std::move(other.begin(), other.end(), std::back_inserter(*this));
GenericArray<T>().swap(other);
}
template<typename T, typename = typename std::enable_if<
BaseVariant::template hasType<T>()>::type>
operator GenericArray<T>() const
{
return GenericArray<T>(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 <string>
#include <utility>
namespace jngen {
#define JNGEN_DEFAULT_WEIGHT_TYPES int, double, std::string, char, std::pair<int, int>
#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<JNGEN_WEIGHT_TYPES>;
using WeightArray = VariantArray<JNGEN_WEIGHT_TYPES>;
} // namespace jngen
using jngen::Weight;
using jngen::WeightArray;
#include <algorithm>
#include <iostream>
#include <iterator>
#include <set>
#include <utility>
#include <vector>
namespace jngen {
class GenericGraph {
public:
virtual ~GenericGraph() {}
virtual int n() const { return adjList_.size(); }
virtual int m() const { return numEdges_; }
bool directed() const { return directed_; }
// 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_.at(v); }
virtual int vertexByLabel(int v) const { return vertexByLabel_.at(v); }
// v: label
// return: array<label>
virtual Array edges(int v) const;
// return: array<label, label>
virtual Arrayp edges() const;
// order: by labels
// TODO: think about ordering here
virtual void setVertexWeights(const WeightArray& weights);
// v: label
virtual void setVertexWeight(int v, const Weight& weight);
virtual void setEdgeWeights(const WeightArray& weights);
virtual void setEdgeWeight(size_t index, const Weight& weight);
// v: label
virtual Weight vertexWeight(int v) const;
virtual Weight edgeWeight(size_t index) const;
// TODO: should it really be public?
virtual void doPrintEdges(
std::ostream& out, const OutputModifier& mod) const;
virtual bool operator==(const GenericGraph& other) const;
virtual bool operator!=(const GenericGraph& other) const;
virtual bool operator< (const GenericGraph& other) const;
virtual bool operator> (const GenericGraph& other) const;
virtual bool operator<=(const GenericGraph& other) const;
virtual bool operator>=(const GenericGraph& other) const;
void initWithEdges(int n, const Arrayp& edges);
protected:
static WeightArray prepareWeightArray(WeightArray a, int requiredSize);
void doShuffle();
void doShuffleAllBut(const Array& except);
void doShuffleEdges();
void extend(size_t size);
// v: vertex number
// returns: array<number>
Array internalEdges(int v) const;
// u, v: vertex numbers
void addEdgeUnsafe(int u, int v);
// v: vertex number
// returns: vertex number
int edgeOtherEnd(int v, int edgeId) const;
void permuteEdges(const Array& order);
void normalizeEdges();
int compareTo(const GenericGraph& other) const;
int numEdges_ = 0;
bool directed_ = false;
Dsu dsu_;
std::vector<Array> adjList_;
Array vertexLabel_;
Array vertexByLabel_;
Arrayp edges_;
WeightArray vertexWeights_;
WeightArray edgeWeights_;
};
template<>
struct Hash<GenericGraph> {
uint64_t operator()(const GenericGraph& graph) const {
uint64_t h = 0;
for (int i = 0; i < graph.n(); ++i) {
impl::hashCombine(h, Hash<Array>{}(graph.edges(i)));
}
return h;
}
};
} // namespace jngen
JNGEN_DEFINE_STD_HASH(jngen::GenericGraph);
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_GENERIC_GRAPH_INL_H
#ifndef JNGEN_INCLUDE_GENERIC_GRAPH_INL_H
#error File "generic_graph_inl.h" must not be included directly.
#endif
namespace jngen {
void GenericGraph::setVertexWeights(const WeightArray& weights) {
ensure(
static_cast<int>(weights.size()) == n(),
"The argument of setVertexWeights must have exactly n elements");
vertexWeights_.resize(n());
for (int i = 0; i < n(); ++i) {
vertexWeights_[i] = weights[vertexByLabel(i)];
}
}
void GenericGraph::setVertexWeight(int v, const Weight& weight) {
ensure(v < n(), "setVertexWeight");
v = vertexByLabel(v);
vertexWeights_.extend(v + 1);
vertexWeights_[v] = weight;
}
void GenericGraph::setEdgeWeights(const WeightArray& weights) {
ensure(
static_cast<int>(weights.size()) == m(),
"The argument of setEdgeWeights must have exactly m elements");
edgeWeights_ = weights;
}
void GenericGraph::setEdgeWeight(size_t index, const Weight& weight) {
ensure(static_cast<int>(index) < m(), "setEdgeWeight");
edgeWeights_.extend(index + 1);
edgeWeights_[index] = weight;
}
Weight GenericGraph::vertexWeight(int v) const {
ensure(v < n(), "vertexWeight");
size_t index = vertexByLabel(v);
if (index >= vertexWeights_.size()) {
return Weight{};
}
return vertexWeights_[index];
}
Weight GenericGraph::edgeWeight(size_t index) const {
ensure(static_cast<int>(index) < m(), "edgeWeight");
if (index >= edgeWeights_.size()) {
return Weight{};
}
return edgeWeights_[index];
}
Array GenericGraph::edges(int v) const {
ensure(v < n(), "Graph::edges(v)");
v = vertexByLabel(v);
Array result = internalEdges(v);
for (auto& x: result) {
x = vertexLabel(x);
}
return result;
}
Arrayp GenericGraph::edges() const {
auto edges = edges_;
for (auto& e: edges) {
e.first = vertexLabel(e.first);
e.second = vertexLabel(e.second);
}
return edges;
}
WeightArray GenericGraph::prepareWeightArray(WeightArray a, int requiredSize) {
ENSURE(a.hasNonEmpty(), "Attempt to print empty weight array");
a.extend(requiredSize);
int type = a.anyType();
for (auto& x: a) {
if (x.empty()) {
x.setType(type);
}
}
return a;
}
void GenericGraph::doShuffle() {
// this if is to be removed after all checks pass
if (vertexLabel_.size() < static_cast<size_t>(n())) {
ENSURE(false, "GenericGraph::doShuffle");
vertexLabel_ = Array::id(n());
}
vertexLabel_.shuffle();
vertexByLabel_ = vertexLabel_.inverse();
doShuffleEdges();
}
void GenericGraph::doShuffleAllBut(const Array& except) {
Array index = except.sorted();
Array needed = Array::id(n());
needed.erase(std::set_difference(
needed.begin(), needed.end(),
index.begin(), index.end(),
needed.begin()), needed.end());
Array neededShuffled = needed.shuffled();
Array perm = Array::id(n());
for (size_t i = 0; i < needed.size(); ++i) {
perm[needed[i]] = neededShuffled[i];
}
vertexLabel_ = vertexLabel_.subseq(perm);
vertexByLabel_ = vertexLabel_.inverse();
doShuffleEdges();
}
void GenericGraph::doShuffleEdges() {
if (!directed_) {
for (auto& edge: edges_) {
if (rnd.next(2)) {
std::swap(edge.first, edge.second);
}
}
}
permuteEdges(Array::id(numEdges_).shuffled());
}
void GenericGraph::extend(size_t size) {
checkLargeParameter(size);
size_t oldSize = n();
if (size > oldSize) {
adjList_.resize(size);
vertexLabel_ += Array::id(size - oldSize, oldSize);
vertexByLabel_ += Array::id(size - oldSize, oldSize);
dsu_.extend(size);
}
}
Array GenericGraph::internalEdges(int v) const {
Array result;
std::transform(
adjList_[v].begin(),
adjList_[v].end(),
std::back_inserter(result),
[this, v](int x) { return edgeOtherEnd(v, x); }
);
return result;
}
void GenericGraph::addEdgeUnsafe(int u, int v) {
int id = numEdges_++;
edges_.emplace_back(u, v);
ENSURE(u < n() && v < n(), "GenericGraph::addEdgeUnsafe");
adjList_[u].push_back(id);
if (!directed_ && u != v) {
adjList_[v].push_back(id);
}
}
int GenericGraph::edgeOtherEnd(int v, int edgeId) const {
ENSURE(edgeId < numEdges_);
const auto& edge = edges_[edgeId];
if (edge.first == v) {
return edge.second;
}
ENSURE(!directed_);
ENSURE(edge.second == v);
return edge.first;
}
void GenericGraph::permuteEdges(const Array& order) {
ENSURE(static_cast<int>(order.size()) == m(), "GenericGraph::permuteEdges");
edges_ = edges_.subseq(order);
auto newByOld = order.inverse();
for (int v = 0; v < n(); ++v) {
for (auto& x: adjList_[v]) {
x = newByOld[x];
}
}
if (edgeWeights_.hasNonEmpty()) {
edgeWeights_.extend(m());
edgeWeights_ = edgeWeights_.subseq(order);
}
}
void GenericGraph::addEdge(int u, int v, const Weight& w) {
extend(std::max(u, v) + 1);
u = vertexByLabel(u);
v = vertexByLabel(v);
dsu_.unite(u, v);
addEdgeUnsafe(u, v);
if (!w.empty()) {
setEdgeWeight(m() - 1, w);
}
}
void GenericGraph::doPrintEdges(
std::ostream& out, const OutputModifier& mod) const
{
bool pendingEndline = false;
if (mod.printN) {
out << n();
if (mod.printM) {
out << " " << m();
}
pendingEndline = true;
} else if (mod.printM) {
out << m();
pendingEndline = true;
}
if (n() == 0) {
return;
}
if (vertexWeights_.hasNonEmpty()) {
auto vertexWeights = prepareWeightArray(vertexWeights_, n());
if (pendingEndline) {
out << "\n";
}
for (int i = 0; i < n(); ++i) {
if (i > 0) {
out << " ";
}
JNGEN_PRINT_NO_MOD(vertexWeights[vertexByLabel(i)]);
}
pendingEndline = true;
}
if (m() == 0) {
return;
}
if (pendingEndline) {
out << "\n";
}
auto t(mod);
{
auto mod(t);
Arrayp edges = this->edges();
mod.printN = false;
if (edgeWeights_.hasNonEmpty()) {
auto edgeWeights = prepareWeightArray(edgeWeights_, m());
for (int i = 0; i < m(); ++i) {
if (i > 0) {
out << "\n";
}
JNGEN_PRINT(edges[i]);
out << " ";
JNGEN_PRINT_NO_MOD(edgeWeights[i]);
}
} else {
JNGEN_PRINT(edges);
}
}
}
bool GenericGraph::operator==(const GenericGraph& other) const {
return compareTo(other) == 0;
}
bool GenericGraph::operator!=(const GenericGraph& other) const {
return compareTo(other) != 0;
}
bool GenericGraph::operator<(const GenericGraph& other) const {
return compareTo(other) == -1;
}
bool GenericGraph::operator>(const GenericGraph& other) const {
return compareTo(other) == 1;
}
bool GenericGraph::operator<=(const GenericGraph& other) const {
return compareTo(other) != 1;
}
bool GenericGraph::operator>=(const GenericGraph& other) const {
return compareTo(other) != -1;
}
void GenericGraph::normalizeEdges() {
if (!config.normalizeEdges) {
return;
}
ENSURE(
vertexLabel_ == Array::id(n()),
"Can call normalizeEdges() only on newly created graph");
if (!directed_) {
for (auto& edge: edges_) {
if (edge.first > edge.second) {
std::swap(edge.first, edge.second);
}
}
}
auto order = Array::id(numEdges_).sorted(
[this](int i, int j) {
return edges_[i] < edges_[j];
});
permuteEdges(order);
}
int GenericGraph::compareTo(const GenericGraph& other) const {
if (n() != other.n()) {
return n() < other.n() ? -1 : 1;
}
for (int i = 0; i < n(); ++i) {
auto e1 = edges(i).sorted();
auto e2 = other.edges(i).sorted();
if (e1 != e2) {
return e1 < e2 ? -1 : 1;
}
}
return 0;
}
void GenericGraph::initWithEdges(int n, const Arrayp& edges) {
ENSURE(this->n() == 0, "Can call initWithEdges only on empty graph");
extend(n);
edges_ = edges;
numEdges_ = edges.size();
Array degree(n);
for (const auto& edge: edges) {
++degree[edge.first];
if (!directed_ && edge.first != edge.second) {
++degree[edge.second];
}
dsu_.unite(edge.first, edge.second);
}
for (int i = 0; i < n; ++i) {
adjList_[i].reserve(degree[i]);
}
for (size_t id = 0; id != edges.size(); ++id) {
const auto& edge = edges[id];
adjList_[edge.first].push_back(id);
if (!directed_ && edge.first != edge.second) {
adjList_[edge.second].push_back(id);
}
}
normalizeEdges();
}
} // namespace jngen
#undef JNGEN_INCLUDE_GENERIC_GRAPH_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <algorithm>
#include <vector>
namespace jngen {
class Tree : public ReprProxy<Tree>, public GenericGraph {
public:
Tree() {
extend(1);
}
Tree(const GenericGraph& gg) : GenericGraph(gg) {
extend(1);
ensure(
dsu_.numComponents() == n() - m(),
"Cannot create a tree from a graph with cycles");
}
void addEdge(int u, int v, const Weight& w = Weight{}) override;
bool canAddEdge(int u, int v);
Array parents(int root) const;
Tree& shuffle();
Tree shuffled() const;
Tree& shuffleAllBut(const Array& except);
Tree shuffledAllBut(const Array& except) const;
Tree link(int vInThis, const Tree& other, int vInOther);
Tree glue(int vInThis, const Tree& other, int vInOther);
static Tree bamboo(int size);
static Tree random(int size);
static Tree randomPrim(int size, int elongation = 0);
static Tree randomKruskal(int size);
static Tree star(int size);
static Tree caterpillar(int size, int length);
static Tree binary(int size);
static Tree kary(int size, int k);
static Tree fromPruferSequence(const Array& code);
void doPrintParents(std::ostream& out, const OutputModifier& mod) const;
};
JNGEN_DECLARE_SIMPLE_PRINTER(Tree, 2) {
ensure(t.isConnected(), "Cannot print a tree: it is not connected");
if (mod.printEdges) {
t.doPrintEdges(out, mod);
} else {
t.doPrintParents(out, mod);
}
}
template<>
struct Hash<Tree> {
uint64_t operator()(const Tree& t) const {
return Hash<GenericGraph>{}(t);
}
};
} // namespace jngen
JNGEN_DEFINE_STD_HASH(jngen::Tree);
using jngen::Tree;
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_TREE_INL_H
#ifndef JNGEN_INCLUDE_TREE_INL_H
#error File "tree_inl.h" must not be included directly.
#endif
void Tree::addEdge(int u, int v, const Weight& w) {
extend(std::max(u, v) + 1);
u = vertexByLabel(u);
v = vertexByLabel(v);
int ret = dsu_.unite(u, v);
ensure(ret, "A cycle appeared in the tree");
addEdgeUnsafe(u, v);
if (!w.empty()) {
setEdgeWeight(m() - 1, w);
}
}
bool Tree::canAddEdge(int u, int v) {
u = vertexByLabel(u);
v = vertexByLabel(v);
return dsu_.getRoot(u) != dsu_.getRoot(v);
}
Array Tree::parents(int root) const {
ensure(isConnected(), "Tree::parents(int): Tree is not connected");
root = vertexByLabel(root);
Array parents(n());
parents[root] = -1;
std::vector<int> used(n());
std::vector<int> queue{root};
for (size_t i = 0; i < queue.size(); ++i) {
int v = queue[i];
used[v] = true;
for (auto to: internalEdges(v)) {
if (!used[to]) {
parents[to] = v;
queue.push_back(to);
}
}
}
for (auto& x: parents) {
if (x != -1) {
x = vertexLabel(x);
}
}
return parents;
}
Tree& Tree::shuffle() {
doShuffle();
return *this;
}
Tree Tree::shuffled() const {
Tree t = *this;
return t.shuffle();
}
Tree& Tree::shuffleAllBut(const Array& except) {
doShuffleAllBut(except);
return *this;
}
Tree Tree::shuffledAllBut(const Array& except) const {
Tree g(*this);
return g.shuffleAllBut(except);
}
Tree Tree::link(int vInThis, const Tree& other, int vInOther) {
ensure(vInThis < n(), "Cannot link a nonexistent vertex");
ensure(vInOther < other.n(), "Cannot link to a nonexistent vertex");
Tree t(*this);
for (const auto& e: other.edges()) {
t.addEdge(e.first + n(), e.second + n());
}
t.addEdge(vInThis, vInOther + n());
return t;
}
Tree Tree::glue(int vInThis, const Tree& other, int vInOther) {
ensure(vInThis < n(), "Cannot glue a nonexistent vertex");
ensure(vInOther < other.n(), "Cannot glue to a nonexistent vertex");
auto newLabel = [vInThis, vInOther, &other, this] (int v) {
if (v < vInOther) {
return n() + v;
} else if (v == vInOther) {
return vInThis;
} else {
return n() + v - 1;
}
};
Tree t(*this);
for (const auto& e: other.edges()) {
t.addEdge(newLabel(e.first), newLabel(e.second));
}
ensure(t.n() == n() + other.n() - 1);
return t;
}
// Tree generators go here
Tree Tree::bamboo(int size) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
Tree t;
for (int i = 0; i + 1 < size; ++i) {
t.addEdge(i, i+1);
}
t.normalizeEdges();
return t;
}
Tree Tree::random(int size) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
if (size == 1) {
return Tree();
}
return fromPruferSequence(Array::random(size - 2, size));
}
Tree Tree::randomPrim(int size, int elongation) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
Tree t;
for (int v = 1; v < size; ++v) {
int parent = rnd.wnext(v, elongation);
t.addEdge(parent, v);
}
t.normalizeEdges();
return t;
}
Tree Tree::randomKruskal(int size) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
Tree t;
t.extend(size);
while (!t.isConnected()) {
auto e = rnd.nextp(size, dpair);
if (t.canAddEdge(e.first, e.second)) {
t.addEdge(e.first, e.second);
}
}
return t;
}
Tree Tree::star(int size) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
Tree t;
for (int i = 1; i < size; ++i) {
t.addEdge(0, i);
}
t.normalizeEdges();
return t;
}
Tree Tree::caterpillar(int size, int length) {
ensure(size > 0, "Number of vertices in the tree must be positive");
ensure(length > 0, "Length of the caterpillar must be positive");
checkLargeParameter(size);
ensure(length <= size);
Tree t = Tree::bamboo(length);
for (int i = length; i < size; ++i) {
t.addEdge(rnd.next(length), i);
}
t.normalizeEdges();
return t;
}
Tree Tree::binary(int size) {
return kary(size, 2);
}
Tree Tree::kary(int size, int k) {
ensure(size > 0, "Number of vertices in the tree must be positive");
checkLargeParameter(size);
Tree t;
for (int i = 1; i < size; ++i) {
t.addEdge((i - 1) / k, i);
}
t.normalizeEdges();
return t;
}
Tree Tree::fromPruferSequence(const Array& code) {
std::vector<int> degree(code.size() + 2, 1);
for (int v: code) {
++degree[v];
}
std::set<int> leaves;
for (size_t v = 0; v != degree.size(); ++v) {
if (degree[v] == 1) {
leaves.insert(v);
}
}
Tree t;
for (int v: code) {
ENSURE(!leaves.empty());
int to = *leaves.begin();
leaves.erase(leaves.begin());
if (--degree[v] == 1) {
leaves.insert(v);
}
t.addEdge(v, to);
}
ENSURE(leaves.size() == 2u);
t.addEdge(*leaves.begin(), *leaves.rbegin());
t.normalizeEdges();
return t;
}
void Tree::doPrintParents(std::ostream& out, const OutputModifier& mod) const {
int root = mod.printParents;
if (root == -1) {
root = 0;
}
auto parents = this->parents(root);
if (mod.printParents == -1) {
parents.erase(parents.begin());
}
if (mod.printN) {
out << n() << "\n";
}
// TODO: avoid copy-paste from doPrintEdges
if (mod.printWeights && vertexWeights_.hasNonEmpty()) {
auto vertexWeights = prepareWeightArray(vertexWeights_, n());
for (int i = 0; i < n(); ++i) {
if (i > 0) {
out << " ";
}
JNGEN_PRINT_NO_MOD(vertexWeights[vertexByLabel(i)]);
}
out << "\n";
}
auto t(mod);
{
auto mod(t);
mod.printN = false;
if (mod.printWeights && edgeWeights_.hasNonEmpty()) {
ensure(false, "Printing parents and edge weights is not supported");
ensure(
mod.printParents == -1,
"Root must not be set to any exact value when printing a tree "
"with edge weights. To fix it, either set printParents() "
"or printWeights(false)");
ENSURE(root == 0);
// TODO: some code to be here
} else {
JNGEN_PRINT(parents);
}
}
}
#undef JNGEN_INCLUDE_TREE_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <memory>
#include <set>
#include <utility>
#include <vector>
namespace jngen {
class Graph : public ReprProxy<Graph>, public GenericGraph {
using BuilderProxy = graph_detail::BuilderProxy;
using Traits = graph_detail::Traits;
friend class graph_detail::GraphRandom;
friend class graph_detail::BuilderProxy;
public:
virtual ~Graph() {}
Graph() {}
Graph(int n) {
extend(n);
}
Graph(const GenericGraph& gg) : GenericGraph(gg) {}
void setN(int n);
Graph& shuffle();
Graph shuffled() const;
Graph& shuffleAllBut(const Array& except);
Graph shuffledAllBut(const Array& except) const;
static BuilderProxy random(int n, int m);
static BuilderProxy complete(int n);
static BuilderProxy empty(int n);
static BuilderProxy cycle(int n);
static BuilderProxy randomStretched(
int n, int m, int elongation, int spread);
static BuilderProxy randomBipartite(int n1, int n2, int m);
static BuilderProxy completeBipartite(int n1, int n2);
};
inline void Graph::setN(int n) {
ensure(n >= this->n(), "Cannot lessen number of vertices in the graph");
extend(n);
}
inline Graph& Graph::shuffle() {
doShuffle();
return *this;
}
inline Graph Graph::shuffled() const {
Graph g(*this);
return g.shuffle();
}
inline Graph& Graph::shuffleAllBut(const Array& except) {
doShuffleAllBut(except);
return *this;
}
inline Graph Graph::shuffledAllBut(const Array& except) const {
Graph g(*this);
return g.shuffleAllBut(except);
}
JNGEN_DECLARE_SIMPLE_PRINTER(Graph, 2) {
t.doPrintEdges(out, mod);
}
JNGEN_DECLARE_SIMPLE_PRINTER(graph_detail::BuilderProxy, 2) {
JNGEN_PRINT(t.g());
}
template<>
struct Hash<Graph> {
uint64_t operator()(const Graph& g) const {
return Hash<GenericGraph>{}(g);
};
};
} // namespace jngen
using jngen::Graph;
JNGEN_DEFINE_STD_HASH(jngen::Graph);
#ifndef JNGEN_DECLARE_ONLY
#define JNGEN_INCLUDE_GRAPH_INL_H
#ifndef JNGEN_INCLUDE_GRAPH_INL_H
#error File "graph_inl.h" must not be included directly.
#endif
namespace jngen {
namespace graph_detail {
Graph BuilderProxy::g() const {
return builder_(traits_);
}
BuilderProxy::operator Graph() const {
return g();
}
class GraphRandom {
using BuilderProxy = graph_detail::BuilderProxy;
using Traits = graph_detail::Traits;
public:
GraphRandom() {
static bool created = false;
ensure(!created, "jngen::GraphRandom should be created only once");
created = true;
}
static BuilderProxy random(int n, int m) {
ensure(
n >= 0 && m >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
checkLargeParameter(m);
return BuilderProxy(Traits(n, m), &doRandom);
}
static BuilderProxy complete(int n) {
ensure(
n >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n * n);
return BuilderProxy(Traits(n), [](Traits t) {
Graph g;
g.setN(t.n);
if (t.directed) {
g.directed_ = true;
}
for (int i = 0; i < t.n; ++i) {
for (int j = 0; j <= i; ++j) {
if (i == j) {
if (t.allowLoops) {
g.addEdge(i, j);
}
continue;
}
if (t.directed) {
if (t.acyclic) {
g.addEdge(i, j);
} else if (t.allowAntiparallel) {
g.addEdge(i, j);
g.addEdge(j, i);
} else {
if (rnd.next(2)) {
g.addEdge(i, j);
} else {
g.addEdge(j, i);
}
}
} else {
g.addEdge(i, j);
}
}
}
g.normalizeEdges();
return g;
});
}
static BuilderProxy empty(int n) {
ensure(
n >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
return BuilderProxy(Traits(n), [](Traits t) {
ensure(
t.n <= 1 || !t.connected,
"Empty graph on >1 vertices cannot be connected");
Graph g;
if (t.directed) {
g.directed_ = true;
}
g.setN(t.n);
return g;
});
}
static BuilderProxy cycle(int n) {
ensure(
n >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
return BuilderProxy(Traits(n), [](Traits t) {
Graph g;
if (t.directed) {
g.directed_ = true;
ensure(!t.acyclic, "Cannot generate acyclic cycle");
}
for (int i = 0; i < t.n; ++i) {
g.addEdge(i, (i+1)%t.n);
}
g.normalizeEdges();
return g;
});
}
static BuilderProxy randomStretched(
int n, int m, int elongation, int spread)
{
ensure(
n >= 0 && m >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
checkLargeParameter(m);
return BuilderProxy(Traits(n, m), [elongation, spread](Traits t) {
return doRandomStretched(t, elongation, spread);
});
}
static BuilderProxy randomBipartite(int n1, int n2, int m) {
ensure(
n1 >= 0 && n2 >= 0 && m >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n1 + n2);
checkLargeParameter(m);
return BuilderProxy(Traits(0, m), [n1, n2](Traits t) {
return doRandomBipartite(t, n1, n2);
});
}
static BuilderProxy completeBipartite(int n1, int n2) {
ensure(
n1 >= 0 && n2 >= 0,
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n1 * n2);
return BuilderProxy(Traits(0, 0), [n1, n2](Traits t) {
ensure(!t.directed, "Directed bipartite graphs are not supported");
Arrayp edges;
edges.reserve(n1 * n2);
for (int u = 0; u < n1; ++u) {
for (int v = 0; v < n2; ++v) {
edges.emplace_back(u, v + n1);
}
}
Graph g;
g.initWithEdges(n1 + n2, edges);
return g;
});
}
private:
static Graph doRandom(Traits t) {
int n = t.n;
int m = t.m;
if (!t.allowMulti) {
ensure(m <= maxEdges(n, t), "Too many edges in the graph");
}
std::unordered_set<std::pair<int, int>> usedEdges;
if (t.connected) {
ensure(m >= n - 1, "Not enough edges for a connected graph");
auto treeEdges = Tree::random(n).edges();
if (t.directed) {
for (auto& edge: treeEdges) {
if (rnd.next(2)) {
std::swap(edge.first, edge.second);
}
}
}
usedEdges.insert(treeEdges.begin(), treeEdges.end());
ENSURE(usedEdges.size() == static_cast<size_t>(n - 1));
}
auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
if (t.directed && !t.allowAntiparallel &&
usedEdges.count({edge.second, edge.first}))
{
return false;
}
return true;
};
Arrayp result(usedEdges.begin(), usedEdges.end());
result.reserve(m);
while (result.size() < static_cast<size_t>(m)) {
auto edge = randomEdge(n, t);
if (edgeIsGood(edge)) {
usedEdges.insert(edge);
result.push_back(edge);
}
}
Graph graph;
if (t.directed && t.acyclic) {
makeAcyclic(result);
}
if (t.directed) {
graph.directed_ = true;
}
graph.initWithEdges(n, result);
return graph;
}
static Graph doRandomStretched(Traits t, int elongation, int spread) {
Tree tree = Tree::randomPrim(t.n, elongation);
Array parents = tree.parents(0);
parents[0] = 0;
auto treeEdges = tree.edges();
if (t.directed && !t.acyclic) {
for (auto& edge: treeEdges) {
if (rnd.next(2)) {
std::swap(edge.first, edge.second);
}
}
}
Arrayp edges = treeEdges;
edges.reserve(t.m);
std::unordered_set<std::pair<int, int>> usedEdges(
treeEdges.begin(), treeEdges.end());
auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
if (t.directed && !t.allowAntiparallel &&
usedEdges.count({edge.second, edge.first}))
{
return false;
}
return true;
};
constexpr size_t MAX_ATTEMPTS = 1000;
size_t attemptsToFail = MAX_ATTEMPTS;
while (static_cast<int>(edges.size()) != t.m) {
if (--attemptsToFail == 0) {
ensure(false, format("Cannot generate random stretched graph "
"with parameters %d, %d, %d, %d",
t.n, t.m, elongation, spread));
}
int u = rnd.next(t.n);
int up = rnd.next(0, spread);
int v = u;
for (int iter = 0; iter < up; ++iter) {
v = parents[v];
}
ENSURE(v <= u);
if (!t.allowLoops && u == v) {
continue;
}
if (!edgeIsGood({v, u})) {
continue;
}
if (t.directed && !t.acyclic && rnd.next(2)) {
std::swap(u, v);
}
edges.emplace_back(v, u);
usedEdges.emplace(v, u);
attemptsToFail = MAX_ATTEMPTS;
}
Graph graph;
if (t.directed) {
graph.directed_ = true;
}
graph.initWithEdges(t.n, edges);
return graph;
}
static Graph doRandomBipartite(Traits t, int n1, int n2) {
int m = t.m;
if (!t.allowMulti) {
ensure(m <= static_cast<long long>(n1) * n2,
"Too many edges in the graph");
}
ensure(!t.directed, "Directed bipartite graphs are not supported");
std::unordered_set<std::pair<int, int>> usedEdges;
if (t.connected) {
ensure(m >= n1 + n2 - 1, "Not enough edges for a connected graph");
auto pruferCode = Array::random(n2 - 1, 0, n1 - 1) +
Array::random(n1 - 1, n1, n1 + n2 - 1);
pruferCode.shuffle();
auto treeEdges = Tree::fromPruferSequence(pruferCode).edges();
usedEdges.insert(treeEdges.begin(), treeEdges.end());
ENSURE(usedEdges.size() == static_cast<size_t>(n1 + n2 - 1));
}
auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
if (t.directed && !t.allowAntiparallel &&
usedEdges.count({edge.second, edge.first}))
{
return false;
}
return true;
};
Arrayp result(usedEdges.begin(), usedEdges.end());
result.reserve(m);
while (result.size() < static_cast<size_t>(m)) {
int u = rnd.next(0, n1 - 1);
int v = rnd.next(n1, n1 + n2 - 1);
std::pair<int, int> edge(u, v);
if (edgeIsGood(edge)) {
usedEdges.insert(edge);
result.push_back(edge);
}
}
Graph graph;
graph.initWithEdges(n1 + n2, result);
return graph;
}
static std::pair<int, int> randomEdge(int n, const Traits& t) {
return rnd.nextp(n, RandomPairTraits{!t.directed, !t.allowLoops});
}
static long long maxEdges(int n, const Traits& t) {
ENSURE(!t.allowMulti);
long long res = static_cast<long long>(n) * (n-1);
if (!(t.directed && t.allowAntiparallel)) {
res /= 2;
}
if (t.allowLoops) {
res += n;
}
return res;
}
static void makeAcyclic(Arrayp& edges) {
auto numbering = Array::id(edges.size()).shuffle();
for (auto& edge: edges) {
if (numbering[edge.first] > numbering[edge.second]) {
std::swap(edge.first, edge.second);
}
}
}
};
} // namespace graph_detail
Graph::BuilderProxy Graph::random(int n, int m) {
return graph_detail::GraphRandom::random(n, m);
}
Graph::BuilderProxy Graph::complete(int n) {
return graph_detail::GraphRandom::complete(n);
}
Graph::BuilderProxy Graph::empty(int n) {
return graph_detail::GraphRandom::empty(n);
}
Graph::BuilderProxy Graph::cycle(int n) {
return graph_detail::GraphRandom::cycle(n);
}
Graph::BuilderProxy Graph::randomStretched(
int n, int m, int elongation, int spread) {
return graph_detail::GraphRandom::randomStretched(n, m, elongation, spread);
}
Graph::BuilderProxy Graph::randomBipartite(int n1, int n2, int m) {
return graph_detail::GraphRandom::randomBipartite(n1, n2, m);
}
Graph::BuilderProxy Graph::completeBipartite( int n1, int n2) {
return graph_detail::GraphRandom::completeBipartite(n1, n2);
}
} // namespace jngen
#undef JNGEN_INCLUDE_GRAPH_INL_H
#endif // JNGEN_DECLARE_ONLY
#include <algorithm>
#include <cstdlib>
namespace jngen {
namespace suites {
JNGEN_CHAINING_TRAITS(GeneralGraphSuiteTraits, allowLoops, allowMulti, connected)
class GeneralGraphSuite : public BaseTestSuite<Graph, GeneralGraphSuiteTraits, int, int> {
graph_detail::BuilderProxy&& apply(graph_detail::BuilderProxy&& builder) const {
builder.allowLoops(conf_._allowLoops);
builder.allowMulti(conf_._allowMulti);
builder.connected(conf_._connected);
return std::move(builder);
}
int selectMForRandom(int n, int m, double ratio) const {
if (conf_._connected) {
return std::min(m, n + static_cast<int>((m - n + 1) * ratio));
} else {
return std::max(1, static_cast<int>(m * ratio));
}
}
Graph randomWithRatio(int n, int m, double ratio) const {
return apply(Graph::random(n, selectMForRandom(n, m, ratio))).g();
}
public:
GeneralGraphSuite() : BaseTestSuite("GeneralGraphSuite") {
#define JNGEN_PRODUCER_ARGS int n, int m
JNGEN_ADD_PRODUCER(random0.001) {
return randomWithRatio(n, m, 0.001);
};
JNGEN_ADD_PRODUCER(random0.005) {
return randomWithRatio(n, m, 0.005);
};
JNGEN_ADD_PRODUCER(random0.01) {
return randomWithRatio(n, m, 0.01);
};
JNGEN_ADD_PRODUCER(random0.02) {
return randomWithRatio(n, m, 0.02);
};
JNGEN_ADD_PRODUCER(random0.05) {
return randomWithRatio(n, m, 0.05);
};
JNGEN_ADD_PRODUCER(random0.1) {
return randomWithRatio(n, m, 0.1);
};
JNGEN_ADD_PRODUCER(random0.2) {
return randomWithRatio(n, m, 0.2);
};
JNGEN_ADD_PRODUCER(random0.3) {
return randomWithRatio(n, m, 0.3);
};
JNGEN_ADD_PRODUCER(random0.4) {
return randomWithRatio(n, m, 0.4);
};
JNGEN_ADD_PRODUCER(random0.6) {
return randomWithRatio(n, m, 0.6);
};
JNGEN_ADD_PRODUCER(random0.7) {
return randomWithRatio(n, m, 0.7);
};
JNGEN_ADD_PRODUCER(random0.9) {
return randomWithRatio(n, m, 0.9);
};
JNGEN_ADD_PRODUCER(random1) {
return randomWithRatio(n, m, 1);
};
JNGEN_ADD_PRODUCER(cycle) {
(void)m;
return Graph::cycle(n);
};
JNGEN_ADD_PRODUCER(complete) {
int size = 1;
while (size <= n && size * (size - 1) / 2 +
(conf_._allowLoops ? size : 0) <= m) {
++size;
}
return apply(Graph::complete(size - 1));
};
JNGEN_ADD_PRODUCER(bamboo) {
(void)m;
return Graph(Tree::bamboo(n));
};
JNGEN_ADD_PRODUCER(star) {
(void)m;
return Graph(Tree::star(n));
};
JNGEN_ADD_PRODUCER(wheel) {
Graph g = Tree::star(n);
int edges = m - (n - 1);
for (int i = 1; i < n && edges > 0; ++i) {
g.addEdge(i, i == n-1 ? 1 : i+1);
}
return g;
};
#undef JNGEN_PRODUCER_ARGS
}
};
} // namespace test_suites
} // namespace jngen
#include <cstdlib>
namespace jngen {
namespace suites {
JNGEN_CHAINING_TRAITS(GeneralTreeSuiteTraits, _)
class GeneralTreeSuite : public BaseTestSuite<Tree, GeneralTreeSuiteTraits, int> {
public:
GeneralTreeSuite() : BaseTestSuite("GeneralTreeSuite") {
#define JNGEN_PRODUCER_ARGS int n
JNGEN_ADD_PRODUCER(random1) {
return Tree::random(n);
};
JNGEN_ADD_PRODUCER(random2) {
return Tree::random(n);
};
JNGEN_ADD_PRODUCER(random3) {
return Tree::random(n);
};
JNGEN_ADD_PRODUCER(bamboo) {
return Tree::bamboo(n);
};
JNGEN_ADD_PRODUCER(shuffled_bamboo) {
return Tree::bamboo(n).shuffled();
};
JNGEN_ADD_PRODUCER(3branches) {
int k = (n - 1) / 3 + 1;
Tree t = Tree::bamboo(k);
t = t.glue(0, Tree::bamboo(k), 0);
t = t.glue(0, Tree::bamboo(k), 0);
ENSURE(t.n() <= n);
return t;
};
JNGEN_ADD_PRODUCER(sqrt_branches) {
int k = std::sqrt(n) + 1;
Tree t = Tree::bamboo(k);
while (t.n() + k - 1 <= n) {
t = t.glue(0, Tree::bamboo(k), 0);
}
ENSURE(t.n() <= n);
return t;
};
JNGEN_ADD_PRODUCER(branches_123) {
Tree t = Tree::bamboo(2);
for (int i = 2; t.n() + i <= n; ++i) {
t = t.link(0, Tree::bamboo(i), 0);
}
ENSURE(t.n() < n);
return t;
};
JNGEN_ADD_PRODUCER(binary) {
return Tree::binary(n);
};
JNGEN_ADD_PRODUCER(3ary) {
return Tree::kary(n, 3);
};
JNGEN_ADD_PRODUCER(4ary) {
return Tree::kary(n, 4);
};
JNGEN_ADD_PRODUCER(50ary) {
return Tree::kary(n, 50);
};
JNGEN_ADD_PRODUCER(500ary) {
return Tree::kary(n, 500);
};
JNGEN_ADD_PRODUCER(star) {
return Tree::star(n);
};
JNGEN_ADD_PRODUCER(shuffled_star) {
return Tree::star(n).shuffled();
};
JNGEN_ADD_PRODUCER(caterpillar_len90) {
return Tree::caterpillar(n, n * 0.9);
};
JNGEN_ADD_PRODUCER(caterpillar_len50) {
return Tree::caterpillar(n, n * 0.5);
};
JNGEN_ADD_PRODUCER(caterpillar_len10) {
return Tree::caterpillar(n, n * 0.1);
};
JNGEN_ADD_PRODUCER(broom_n/2) {
auto t1 = Tree::bamboo(n/2);
auto t2 = Tree::star(n - n/2);
return t1.link(n/2 - 1, t2, 0);
};
JNGEN_ADD_PRODUCER(random_w-100) {
return Tree::randomPrim(n, -100);
};
JNGEN_ADD_PRODUCER(random_w-50) {
return Tree::randomPrim(n, -50);
};
JNGEN_ADD_PRODUCER(random_w-10) {
return Tree::randomPrim(n, -10);
};
JNGEN_ADD_PRODUCER(random_w-5) {
return Tree::randomPrim(n, -5);
};
JNGEN_ADD_PRODUCER(random_w0) {
return Tree::randomPrim(n, 0);
};
JNGEN_ADD_PRODUCER(random_w5) {
return Tree::randomPrim(n, 5);
};
JNGEN_ADD_PRODUCER(random_w10) {
return Tree::randomPrim(n, 10);
};
JNGEN_ADD_PRODUCER(random_w50) {
return Tree::randomPrim(n, 50);
};
JNGEN_ADD_PRODUCER(random_w100) {
return Tree::randomPrim(n, 100);
};
#undef JNGEN_PRODUCER_ARGS
}
};
} // namespace test_suites
} // namespace jngen
namespace jngen {
struct TestSuites {
suites::GeneralGraphSuite graph;
suites::GeneralTreeSuite tree;
};
JNGEN_EXTERN TestSuites testSuites;
} // namespace jngen
using jngen::testSuites;
#pragma GCC diagnostic pop // -Wconversion
#if __clang__major >= 5
#pragma GCC diagnostic pop // -Wunused-lambda-capture
#endif