競技プログラミング用の C++ ライブラリ
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
// repr.hpp の文字列化を検証する
// CHECK_REPR(値, 式) は repr(値) が「式」の文字列と一致し、さらにその式を評価すると値と等しくなることを確かめる
// (式はこのファイルで C++ としてコンパイルされるので、repr の出力が C++ の式として正しいことも確かめられる)
// 問題の入力は使わず、すべて一致したら Hello World を出力する
#include <array>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "../../src/base/range.hpp"
#include "../../src/base/repr.hpp"
bool ok = true;
void check(bool cond, const std::string& what) {
if (!cond) {
if (ok) std::cerr << "failed: " << what << std::endl;
ok = false;
}
}
#define CHECK_REPR(value, ...) \
do { \
const auto& value_ = (value); \
check(repr(value_) == #__VA_ARGS__, "repr(" #value ") == " #__VA_ARGS__ " (got " + repr(value_) + ")"); \
check(value_ == (__VA_ARGS__), "evaluating " #__VA_ARGS__); \
} while (0)
// repr などのメンバ関数を持たない型
struct Plain {};
// initializer_str だけを持つ型
struct OnlyInit {
int x;
std::string initializer_str() const { return "{" + std::to_string(x) + "}"; }
};
// char の期待値 (愚直に作る)
std::string naive_char_repr(char c) {
unsigned char u = static_cast<unsigned char>(c);
switch (c) {
case '\0': return "'\\0'";
case '\a': return "'\\a'";
case '\b': return "'\\b'";
case '\f': return "'\\f'";
case '\r': return "'\\r'";
case '\n': return "'\\n'";
case '\v': return "'\\v'";
case '\'': return "'\\''";
case '\\': return "'\\\\'";
}
if (32 <= u && u <= 126) return std::string("'") + c + "'";
return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}
int main() {
std::mt19937 rng(314);
// 整数・浮動小数点数
CHECK_REPR(5, 5);
CHECK_REPR(-3, -3);
CHECK_REPR(5L, 5l);
CHECK_REPR(-7LL, -7ll);
CHECK_REPR(5U, 5u);
CHECK_REPR(5UL, 5ul);
CHECK_REPR(18446744073709551615ULL, 18446744073709551615ull);
CHECK_REPR(1.5f, 1.500000f);
CHECK_REPR(-0.25, -0.250000);
CHECK_REPR(2.5L, 2.500000l);
check(type_str(5) == "int" && type_str(5L) == "long" && type_str(5LL) == "long long", "type_str (signed)");
check(type_str(5U) == "unsigned int" && type_str(5UL) == "unsigned long" && type_str(5ULL) == "unsigned long long", "type_str (unsigned)");
check(type_str(1.0f) == "float" && type_str(1.0) == "double" && type_str(1.0L) == "long double", "type_str (floating)");
check(initializer_str(5LL) == "5ll" && initializer_str(1.5f) == "1.500000f", "initializer_str (number)");
// char
CHECK_REPR('a', 'a');
CHECK_REPR('\n', '\n');
CHECK_REPR('\0', '\0');
CHECK_REPR('\'', '\'');
CHECK_REPR('\\', '\\');
CHECK_REPR(char(1), '\001');
CHECK_REPR(char(127), '\177');
CHECK_REPR(char(-61), '\303'); // UTF-8 の文字のバイトなど、負の値の char
check(type_str('a') == "char" && initializer_str('a') == "'a'", "type_str / initializer_str (char)");
for (int i = -128; i <= 127; i++) {
char c = static_cast<char>(i);
check(repr(c) == naive_char_repr(c), "repr(char) for all values");
}
// 文字列
{
const char* s = "abc";
check(repr(s) == "\"abc\"" && type_str(s) == "char*" && initializer_str(s) == "\"abc\"", "const char*");
}
CHECK_REPR(std::string("abc"), std::string{"abc"});
CHECK_REPR(std::string(), std::string{""});
check(type_str(std::string()) == "std::string" && initializer_str(std::string("x")) == "\"x\"", "type_str / initializer_str (string)");
// コンテナ
CHECK_REPR((std::vector<int>{1, 2, 3}), std::vector<int>{1, 2, 3});
CHECK_REPR(std::vector<int>(), std::vector<int>{});
CHECK_REPR((std::vector<std::vector<long long>>{{1, 2}, {}}), std::vector<std::vector<long long>>{{1ll, 2ll}, {}});
CHECK_REPR((std::set<int>{3, 1, 2}), std::set<int>{1, 2, 3});
CHECK_REPR((std::pair<int, std::string>{1, "a"}), std::pair<int, std::string>{1, "a"});
CHECK_REPR((std::pair<char, double>{'x', 0.5}), std::pair<char, double>{'x', 0.500000});
CHECK_REPR((std::map<int, char>{{2, 'b'}, {1, 'a'}}), std::map<int, char>{{1, 'a'}, {2, 'b'}});
CHECK_REPR((std::map<std::string, std::vector<int>>{{"k", {1}}}), std::map<std::string, std::vector<int>>{{"k", {1}}});
CHECK_REPR((std::array<int, 3>{1, 2, 3}), std::array<int, 3>{1, 2, 3});
CHECK_REPR((std::vector<std::pair<int, unsigned>>{{1, 2u}}), std::vector<std::pair<int, unsigned int>>{{1, 2u}});
check(type_str(std::vector<std::set<long long>>()) == "std::vector<std::set<long long>>", "type_str (nested)");
check(initializer_str(std::vector<int>{1, 2}) == "{1, 2}", "initializer_str (vector)");
// repr / type_str / initializer_str をメンバ関数に持つ型 (Range)
CHECK_REPR(Range(0, 10, 2), Range(0, 10, 2));
CHECK_REPR((std::vector<Range>{Range(0, 3, 1)}), std::vector<Range>{{0, 3, 1}});
check(type_str(Range(5)) == "Range" && initializer_str(Range(1, 5, 2)) == "{1, 5, 2}", "Range");
// メンバ関数を持たない型は "T" と "{}"
check(type_str(Plain{}) == "T" && initializer_str(Plain{}) == "{}" && repr(Plain{}) == "T{}", "Plain");
// initializer_str だけを持つ型
check(repr(OnlyInit{7}) == "T{7}" && initializer_str(std::vector<OnlyInit>{{1}, {2}}) == "{{1}, {2}}", "OnlyInit");
// ランダム: 整数の vector
for (int rep = 0; rep < 1000 && ok; rep++) {
int n = rng() % 5;
std::vector<long long> v(n);
std::string expect = "std::vector<long long>{";
for (int i = 0; i < n; i++) {
v[i] = (long long)(rng() % 2001) - 1000;
expect += (i ? ", " : "") + std::to_string(v[i]) + "ll";
}
expect += "}";
check(repr(v) == expect, "repr(vector<long long>) (random)");
}
// DEBUG(x) は std::cerr に "x = repr(x)" を出力する
{
std::ostringstream buf;
std::streambuf* old = std::cerr.rdbuf(buf.rdbuf());
std::vector<int> xs{1, 2};
DEBUG(xs);
std::cerr.rdbuf(old);
check(buf.str() == "xs = std::vector<int>{1, 2}\n", "DEBUG");
}
if (ok) std::cout << "Hello World" << std::endl;
}#line 1 "test/unittest/repr.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
// repr.hpp の文字列化を検証する
// CHECK_REPR(値, 式) は repr(値) が「式」の文字列と一致し、さらにその式を評価すると値と等しくなることを確かめる
// (式はこのファイルで C++ としてコンパイルされるので、repr の出力が C++ の式として正しいことも確かめられる)
// 問題の入力は使わず、すべて一致したら Hello World を出力する
#include <array>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#line 2 "src/base/range.hpp"
/**
* @file range.hpp
* @brief Python の range のような整数列
*/
#include <cassert>
#include <cstddef>
#include <iterator>
#line 12 "src/base/range.hpp"
#include <type_traits>
#line 15 "src/base/range.hpp"
struct Range {
public:
using value_type = long long;
using const_reference = const value_type&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
const value_type start;
const value_type stop;
const value_type step;
private:
const value_type norm_stop;
constexpr static value_type normalize(value_type _start, value_type _stop, value_type _step) {
assert(_step != 0);
if (_step > 0) {
if (_start >= _stop) {
return _start;
} else {
return _start + (_stop - _start + _step - 1) / _step * _step;
}
} else {
if (_start <= _stop) {
return _start;
} else {
return _start - (_start - _stop - _step - 1) / _step * _step;
}
}
}
constexpr Range(value_type _start, value_type _stop, value_type _step, value_type _norm_stop)
: start(_start), stop(_stop), step(_step), norm_stop(_norm_stop) {
}
public:
/**
* @brief start から stop の手前まで step ずつ進む整数列
* @pre step != 0
*/
constexpr Range(value_type _start, value_type _stop, value_type _step)
: start(_start), stop(_stop), step(_step), norm_stop(normalize(_start, _stop, _step)) {
}
constexpr Range(value_type _start, value_type _stop)
: start(_start), stop(_stop), step(1), norm_stop(normalize(_start, _stop, 1)) {}
constexpr Range(value_type _stop)
: start(0), stop(_stop), step(1), norm_stop(normalize(0, _stop, 1)) {}
constexpr Range()
: start(0), stop(0), step(1), norm_stop(0) {
}
constexpr Range(const Range& o) = default;
Range& operator=(const Range& o) = delete;
struct const_iterator {
public:
using difference_type = Range::difference_type;
using value_type = Range::value_type;
using pointer = const Range::value_type*;
using reference = const Range::value_type&;
using iterator_category = std::random_access_iterator_tag;
private:
const Range& range;
value_type value;
constexpr bool same_range(const const_iterator& o) const noexcept {
return range.start == o.range.start and range.stop == o.range.stop and range.step == o.range.step;
}
public:
constexpr const_iterator(const Range& _range, value_type _value) noexcept
: range(_range), value(_value) {}
constexpr const_iterator(const const_iterator& o) noexcept
: range(o.range), value(o.value) {}
constexpr reference base() const noexcept {
return value;
}
constexpr const_iterator& operator=(const const_iterator& o) {
assert(same_range(o));
value = o.value;
return *this;
};
constexpr const_iterator& operator++() noexcept {
value += range.step;
return *this;
}
constexpr const_iterator operator++(int) noexcept {
const_iterator ret(*this);
value += range.step;
return ret;
}
constexpr const_iterator& operator+=(difference_type _n) noexcept {
value += _n * range.step;
return *this;
}
constexpr friend const_iterator operator+(const const_iterator& i, difference_type _n) noexcept {
const_iterator ret(i);
ret += _n;
return ret;
}
constexpr friend const_iterator operator+(difference_type _n, const const_iterator& i) noexcept {
const_iterator ret(i);
ret += _n;
return ret;
}
constexpr const_iterator& operator--() noexcept {
value -= range.step;
return *this;
}
constexpr const_iterator operator--(int) noexcept {
const_iterator ret(*this);
value -= range.step;
return ret;
}
constexpr const_iterator& operator-=(difference_type _n) noexcept {
value -= _n * range.step;
return *this;
}
constexpr friend const_iterator operator-(const const_iterator& i, difference_type _n) noexcept {
const_iterator ret(i);
ret -= _n;
return ret;
}
constexpr reference operator*() const noexcept {
return value;
}
constexpr friend bool operator!=(const const_iterator& a, const const_iterator& b) noexcept {
return !a.same_range(b) or a.value != b.value;
}
constexpr friend bool operator==(const const_iterator& a, const const_iterator& b) noexcept {
return !(a != b);
}
constexpr friend difference_type operator-(const const_iterator& a, const const_iterator& b) {
assert(a.same_range(b));
if (a.range.step > 0) {
return (a.value - b.value) / a.range.step;
} else {
return (b.value - a.value) / (-a.range.step);
}
}
};
constexpr const_iterator begin() const noexcept {
return {*this, start};
}
constexpr const_iterator end() const noexcept {
return {*this, norm_stop};
}
/**
* @brief x の個数 (0 か 1)
*/
constexpr difference_type count(value_type x) const noexcept {
if (step > 0) {
if (start <= x and x < stop) {
if ((x - start) % step == 0) {
return 1;
}
}
} else {
if (stop < x and x <= start) {
if ((start - x) % (-step) == 0) {
return 1;
}
}
}
return 0;
}
/**
* @brief x が含まれるか
*/
constexpr bool contains(value_type x) const noexcept {
return count(x) != 0;
}
/**
* @brief x のインデックス
* x が含まれないとエラー
*/
constexpr size_type index(value_type x) const {
assert(contains(x));
return (x - start) / step;
}
/**
* @brief 要素が x のイテレータ
* x が含まれないと end() を返す
*/
constexpr const_iterator find(value_type x) const noexcept {
if (contains(x)) {
return {*this, x};
} else {
return {*this, norm_stop};
}
}
/**
* @brief 要素が x 以上の最小のイテレータ
* step > 0 じゃないとエラー
*/
constexpr const_iterator lower_bound(value_type x) const {
assert(step > 0);
if (norm_stop <= x) {
return end();
} else if (x < start) {
return begin();
} else {
return {*this, start + (x - start + step - 1) / step * step};
}
}
/**
* @brief 要素が x より大きい最小のイテレータ
* step > 0 じゃないとエラー
*/
constexpr const_iterator upper_bound(value_type x) const {
assert(step > 0);
if (norm_stop <= x) {
return end();
} else if (x < start) {
return begin();
} else {
return {*this, start + (x - start + step) / step * step};
}
}
/**
* @brief コンテナの要素数
*/
constexpr size_type size() const noexcept {
if (step > 0) {
return (norm_stop - start) / step;
} else {
return (start - norm_stop) / (-step);
}
}
constexpr value_type at(size_type i) const {
assert(i < size());
return start + i * step;
}
constexpr value_type operator[](size_type i) const {
return at(i);
}
constexpr bool empty() const noexcept {
return start == norm_stop;
}
constexpr value_type sum() const noexcept {
return static_cast<Range::value_type>(size()) * (norm_stop - step + start) / 2;
}
/**
* @brief 最小値
* 空だとエラー
*/
constexpr value_type min() const {
assert(not empty());
if (step > 0) {
return start;
} else {
return norm_stop - step;
}
}
/**
* @brief 最大値
* 空だとエラー
*/
constexpr value_type max() const {
assert(not empty());
if (step > 0) {
return norm_stop - step;
} else {
return start;
}
}
/**
* @brief 総 xor
* 負を含むとエラー
*/
constexpr value_type product_xor() const noexcept {
if (empty()) return 0;
assert(min() >= 0);
Range sorted_range(sorted());
value_type res = 0;
std::make_unsigned_t<value_type> t = 1;
while (t <= static_cast<std::make_unsigned_t<value_type>>(max())) {
std::make_unsigned_t<value_type> tmp = 0, n = size(), m = t, a = sorted_range.step, b = sorted_range.start;
while (true) {
if (a >= m) {
tmp += n * (n - 1) / 2 * (a / m);
a %= m;
}
if (b >= m) {
tmp += n * (b / m);
b %= m;
}
std::make_unsigned_t<value_type> y_max = a * n + b;
if (y_max < m) break;
n = y_max / m;
b = y_max % m;
std::swap(m, a);
}
if (tmp % 2 == 1) {
res += t;
}
t <<= 1;
}
return res;
}
/**
* @brief 逆順にしたもの
*/
constexpr Range reversed() const noexcept {
return {norm_stop - step, start - step, -step, start - step};
}
/**
* @brief step > 0 だとそのまま step < 0 だと逆順にしたもの
*/
constexpr Range sorted() const noexcept {
if (step > 0) {
return {start, norm_stop, step, norm_stop};
} else {
return {norm_stop - step, start - step, -step, start - step};
}
}
/**
* @brief 要素が [l,r] である範囲を返す
*/
constexpr Range clipped(value_type l, value_type r) const {
if (l > r) return {};
if (step > 0) {
value_type _start = [&] {
if (norm_stop <= l) {
return norm_stop;
} else if (l < start) {
return start;
} else {
return start + (l - start + step - 1) / step * step;
}
}();
value_type _stop = [&] {
if (norm_stop <= r) {
return norm_stop;
} else if (r < start) {
return start;
} else {
return start + (r - start + step) / step * step;
}
}();
return {_start, _stop, step, _stop};
} else {
value_type _start = [&] {
if (start - step <= r) {
return start;
} else if (r < norm_stop - step) {
return norm_stop;
} else {
return norm_stop + (r - norm_stop) / step * step;
}
}();
value_type _stop = [&] {
if (start - step <= l) {
return start;
} else if (l < norm_stop - step) {
return norm_stop;
} else {
return norm_stop + (l - norm_stop - 1) / step * step;
}
}();
return {_start, _stop, step, _stop};
}
}
/**
* @brief 要素を Range で指定したコンテナ
*/
constexpr Range slice(const Range& r) const {
if (r.empty()) return {};
if (r.max() < 0) return {};
if (r.min() >= static_cast<Range::value_type>(size())) return {};
Range trimmed_range(r.clipped(0, static_cast<Range::value_type>(size()) - 1));
if (trimmed_range.empty()) return {};
value_type _step = step * trimmed_range.step;
value_type _start = at(trimmed_range.start);
value_type _stop = _start + _step * static_cast<Range::value_type>(trimmed_range.size());
return {_start, _stop, _step, _stop};
}
static std::string type_str() {
return "Range";
}
std::string initializer_str() const {
return "{" + std::to_string(start) + ", " + std::to_string(stop) + ", " + std::to_string(step) + "}";
}
std::string repr() const {
return "Range(" + std::to_string(start) + ", " + std::to_string(stop) + ", " + std::to_string(step) + ")";
}
std::vector<value_type> to_vector() const {
std::vector<value_type> ret;
ret.reserve(size());
if (step > 0) {
for (value_type x = start; x < stop; x += step) {
ret.push_back(x);
}
} else {
for (value_type x = start; x > stop; x += step) {
ret.push_back(x);
}
}
return ret;
}
constexpr friend bool operator==(const Range& a, const Range& b) noexcept {
if (a.empty() and b.empty()) return true;
if (a.size() != b.size()) return false;
if (a.start != b.start) return false;
if (a.size() == 1) return true;
if (a.step != b.step) return false;
return true;
}
constexpr friend bool operator!=(const Range& a, const Range& b) noexcept {
return !(a == b);
}
};
#line 2 "src/base/repr.hpp"
/**
* @file repr.hpp
* @brief 出力をそのままコンストラクタに使える文字列化 (repr)
*/
#line 2 "src/base/traits.hpp"
/**
* @file traits.hpp
* @brief 型特性
*/
#line 13 "src/base/traits.hpp"
namespace detail {
using std::begin, std::end;
template <class T, class = void>
struct is_range_impl : std::false_type {};
template <class T>
struct is_range_impl<T, std::void_t<decltype(begin(std::declval<T&>()), end(std::declval<T&>()))>> : std::true_type {};
} // namespace detail
template <class T>
struct is_range : detail::is_range_impl<T>::type {};
template <class T>
inline constexpr bool is_range_v = is_range<T>::value;
#define HAS_METHOD(func_name) \
namespace detail { \
template <class T, class = void> \
struct has_##func_name##_impl : std::false_type {}; \
template <class T> \
struct has_##func_name##_impl<T, std::void_t<decltype(std::declval<T>().func_name())>> \
: std::true_type {}; \
} \
template <class T> \
struct has_##func_name : detail::has_##func_name##_impl<T>::type {}; \
template <class T> \
inline constexpr bool has_##func_name##_v = has_##func_name<T>::value;
#define HAS_METHOD_ARG(func_name) \
namespace detail { \
template <class T, typename U, class = void> \
struct has_##func_name##_impl : std::false_type {}; \
template <class T, typename U> \
struct has_##func_name##_impl<T, U, std::void_t<decltype(std::declval<T>().func_name(std::declval<U>()))>> \
: std::true_type {}; \
} \
template <class T, typename U> \
struct has_##func_name : detail::has_##func_name##_impl<T, U>::type {}; \
template <class T, typename U> \
inline constexpr bool has_##func_name##_v = has_##func_name<T, U>::value;
HAS_METHOD(repr)
HAS_METHOD(type_str)
HAS_METHOD(initializer_str)
HAS_METHOD(max)
HAS_METHOD(min)
HAS_METHOD(reversed)
HAS_METHOD(sorted)
HAS_METHOD(sum)
HAS_METHOD(product)
HAS_METHOD(product_xor)
HAS_METHOD_ARG(count)
HAS_METHOD_ARG(find)
HAS_METHOD_ARG(lower_bound)
HAS_METHOD_ARG(upper_bound)
#define ENABLE_IF_T_IMPL(expr) std::enable_if_t<expr, std::nullptr_t> = nullptr
#define ENABLE_IF_T(...) ENABLE_IF_T_IMPL((__VA_ARGS__))
template <class C>
using mem_value_type = typename C::value_type;
template <class C>
using mem_difference_type = typename C::difference_type;
namespace detail {
template <class T, class = void>
struct is_sorted_container_impl : std::false_type {};
template <class T>
struct is_sorted_container_impl<std::set<T>> : std::true_type {};
template <class T>
struct is_sorted_container_impl<std::multiset<T>> : std::true_type {};
} // namespace detail
template <class T>
struct is_sorted_container : detail::is_sorted_container_impl<T>::type {};
template <class T>
inline constexpr bool is_sorted_container_v = is_sorted_container<T>::value;
#line 17 "src/base/repr.hpp"
namespace detail {
///////////////////////////////////////////////////////////////////
/////////////////////////// declaration ///////////////////////////
///////////////////////////////////////////////////////////////////
inline std::string type_str(const char*);
inline std::string initializer_str(const char*);
inline std::string repr(const char*);
inline std::string type_str(const char&);
inline std::string initializer_str(const char&);
inline std::string repr(const char&);
inline std::string type_str(const int&);
inline std::string initializer_str(const int&);
inline std::string repr(const int&);
inline std::string type_str(const long&);
inline std::string initializer_str(const long&);
inline std::string repr(const long&);
inline std::string type_str(const long long&);
inline std::string initializer_str(const long long&);
inline std::string repr(const long long&);
inline std::string type_str(const unsigned int&);
inline std::string initializer_str(const unsigned int&);
inline std::string repr(const unsigned int&);
inline std::string type_str(const unsigned long&);
inline std::string initializer_str(const unsigned long&);
inline std::string repr(const unsigned long&);
inline std::string type_str(const unsigned long long&);
inline std::string initializer_str(const unsigned long long&);
inline std::string repr(const unsigned long long&);
inline std::string type_str(const std::string&);
inline std::string initializer_str(const std::string&);
inline std::string repr(const std::string&);
inline std::string type_str(const float&);
inline std::string initializer_str(const float&);
inline std::string repr(const float&);
inline std::string type_str(const double&);
inline std::string initializer_str(const double&);
inline std::string repr(const double&);
inline std::string type_str(const long double&);
inline std::string initializer_str(const long double&);
inline std::string repr(const long double&);
template <typename T>
inline std::string type_str(const std::vector<T>&);
template <typename T>
inline std::string initializer_str(const std::vector<T>&);
template <typename T>
inline std::string repr(const std::vector<T>&);
template <typename T>
inline std::string type_str(const std::set<T>&);
template <typename T>
inline std::string initializer_str(const std::set<T>&);
template <typename T>
inline std::string repr(const std::set<T>&);
template <typename T1, typename T2>
inline std::string type_str(const std::pair<T1, T2>&);
template <typename T1, typename T2>
inline std::string initializer_str(const std::pair<T1, T2>&);
template <typename T1, typename T2>
inline std::string repr(const std::pair<T1, T2>&);
template <typename T1, typename T2>
inline std::string type_str(const std::map<T1, T2>&);
template <typename T1, typename T2>
inline std::string initializer_str(const std::map<T1, T2>&);
template <typename T1, typename T2>
inline std::string repr(const std::map<T1, T2>&);
template <typename T, size_t N>
inline std::string type_str(const std::array<T, N>&);
template <typename T, size_t N>
inline std::string initializer_str(const std::array<T, N>&);
template <typename T, size_t N>
inline std::string repr(const std::array<T, N>&);
///////////////////////////////////////////////////////////////////
/////////////////////////// definition ////////////////////////////
///////////////////////////////////////////////////////////////////
template <typename T, std::enable_if_t<not has_type_str_v<T>, std::nullptr_t> = nullptr>
inline std::string type_str(const T& t) {
return "T";
}
template <typename T, std::enable_if_t<has_type_str_v<T>, std::nullptr_t> = nullptr>
inline std::string type_str(const T& t) {
return t.type_str();
}
template <typename T, std::enable_if_t<not has_initializer_str_v<T>, std::nullptr_t> = nullptr>
inline std::string initializer_str(const T& t) {
return "{}";
}
template <typename T, std::enable_if_t<has_initializer_str_v<T>, std::nullptr_t> = nullptr>
inline std::string initializer_str(const T& t) {
return t.initializer_str();
}
template <typename T, std::enable_if_t<not has_repr_v<T>, std::nullptr_t> = nullptr>
inline std::string repr(const T& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T, std::enable_if_t<has_repr_v<T>, std::nullptr_t> = nullptr>
inline std::string repr(const T& t) {
return t.repr();
}
inline std::string type_str(const char*) {
return "char*";
}
inline std::string initializer_str(const char* t) {
return "\"" + std::string(t) + "\"";
}
inline std::string repr(const char* t) {
return detail::initializer_str(t);
}
inline std::string type_str(const char&) {
return "char";
}
inline std::string initializer_str(const char& t) {
// char が符号付きでも添字が負にならないように unsigned char で扱う (UTF-8 のバイトなど)
const unsigned char u = static_cast<unsigned char>(t);
std::vector<std::string> v(128, "");
v['\0'] = "\\0";
v['\a'] = "\\a";
v['\b'] = "\\b";
v['\f'] = "\\f";
v['\r'] = "\\r";
v['\n'] = "\\n";
v['\v'] = "\\v";
v['\''] = "\\'";
v['\\'] = "\\\\";
if (u < 128 and !v[u].empty())
return "'" + v[u] + "'";
if (32 <= u and u <= 126)
return "'" + std::string{t} + "'";
return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}
inline std::string repr(const char& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const int&) {
return "int";
}
inline std::string initializer_str(const int& t) {
return std::to_string(t);
}
inline std::string repr(const int& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const long&) {
return "long";
}
inline std::string initializer_str(const long& t) {
return std::to_string(t) + "l";
}
inline std::string repr(const long& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const long long&) {
return "long long";
}
inline std::string initializer_str(const long long& t) {
return std::to_string(t) + "ll";
}
inline std::string repr(const long long& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const unsigned int&) {
return "unsigned int";
}
inline std::string initializer_str(const unsigned int& t) {
return std::to_string(t) + "u";
}
inline std::string repr(const unsigned int& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const unsigned long&) {
return "unsigned long";
}
inline std::string initializer_str(const unsigned long& t) {
return std::to_string(t) + "ul";
}
inline std::string repr(const unsigned long& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const unsigned long long&) {
return "unsigned long long";
}
inline std::string initializer_str(const unsigned long long& t) {
return std::to_string(t) + "ull";
}
inline std::string repr(const unsigned long long& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const std::string&) {
return "std::string";
}
inline std::string initializer_str(const std::string& t) {
return "\"" + t + "\"";
}
inline std::string repr(const std::string& t) {
return detail::type_str(t) + "{" + detail::initializer_str(t) + "}";
}
inline std::string type_str(const float&) {
return "float";
}
inline std::string initializer_str(const float& t) {
return std::to_string(t) + "f";
}
inline std::string repr(const float& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const double&) {
return "double";
}
inline std::string initializer_str(const double& t) {
return std::to_string(t);
}
inline std::string repr(const double& t) {
return detail::initializer_str(t);
}
inline std::string type_str(const long double&) {
return "long double";
}
inline std::string initializer_str(const long double& t) {
return std::to_string(t) + "l";
}
inline std::string repr(const long double& t) {
return detail::initializer_str(t);
}
template <typename T>
inline std::string type_str(const std::vector<T>&) {
T t;
return "std::vector<" + detail::type_str(t) + ">";
}
template <typename T>
inline std::string initializer_str(const std::vector<T>& t) {
std::string ret;
ret += "{";
for (auto itr = t.begin(); itr != t.end(); itr++) {
if (itr != t.begin()) ret += ", ";
ret += detail::initializer_str(*itr);
}
ret += "}";
return ret;
}
template <typename T>
inline std::string repr(const std::vector<T>& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T>
inline std::string type_str(const std::set<T>&) {
T t;
return "std::set<" + detail::type_str(t) + ">";
}
template <typename T>
inline std::string initializer_str(const std::set<T>& t) {
std::string ret;
ret += "{";
for (auto itr = t.begin(); itr != t.end(); itr++) {
if (itr != t.begin()) ret += ", ";
ret += detail::initializer_str(*itr);
}
ret += "}";
return ret;
}
template <typename T>
inline std::string repr(const std::set<T>& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T1, typename T2>
inline std::string type_str(const std::pair<T1, T2>&) {
T1 t1;
T2 t2;
return "std::pair<" + detail::type_str(t1) + ", " + detail::type_str(t2) + ">";
}
template <typename T1, typename T2>
inline std::string initializer_str(const std::pair<T1, T2>& t) {
return "{" + detail::initializer_str(t.first) + ", " + detail::initializer_str(t.second) + "}";
}
template <typename T1, typename T2>
inline std::string repr(const std::pair<T1, T2>& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T1, typename T2>
inline std::string type_str(const std::map<T1, T2>&) {
T1 t1;
T2 t2;
return "std::map<" + detail::type_str(t1) + ", " + detail::type_str(t2) + ">";
}
template <typename T1, typename T2>
inline std::string initializer_str(const std::map<T1, T2>& t) {
std::string ret;
ret += "{";
for (auto itr = t.begin(); itr != t.end(); itr++) {
if (itr != t.begin()) ret += ", ";
ret += "{";
ret += detail::initializer_str(itr->first);
ret += ", ";
ret += detail::initializer_str(itr->second);
ret += "}";
}
ret += "}";
return ret;
}
template <typename T1, typename T2>
inline std::string repr(const std::map<T1, T2>& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
template <typename T, size_t N>
inline std::string type_str(const std::array<T, N>&) {
T t;
return "std::array<" + detail::type_str(t) + ", " + std::to_string(N) + ">";
}
template <typename T, size_t N>
inline std::string initializer_str(const std::array<T, N>& t) {
std::string ret;
ret += "{";
for (auto itr = t.begin(); itr != t.end(); itr++) {
if (itr != t.begin()) ret += ", ";
ret += detail::initializer_str(*itr);
}
ret += "}";
return ret;
}
template <typename T, size_t N>
inline std::string repr(const std::array<T, N>& t) {
return detail::type_str(t) + detail::initializer_str(t);
}
} // namespace detail
template <typename T>
inline std::string type_str(const T& t) {
return detail::type_str(t);
}
template <typename T>
inline std::string initializer_str(const T& t) {
return detail::initializer_str(t);
}
template <typename T>
inline std::string repr(const T& t) {
return detail::repr(t);
}
#define DEBUG(x) { std::cerr << #x << " = " << repr(x) << std::endl; }
#line 20 "test/unittest/repr.test.cpp"
bool ok = true;
void check(bool cond, const std::string& what) {
if (!cond) {
if (ok) std::cerr << "failed: " << what << std::endl;
ok = false;
}
}
#define CHECK_REPR(value, ...) \
do { \
const auto& value_ = (value); \
check(repr(value_) == #__VA_ARGS__, "repr(" #value ") == " #__VA_ARGS__ " (got " + repr(value_) + ")"); \
check(value_ == (__VA_ARGS__), "evaluating " #__VA_ARGS__); \
} while (0)
// repr などのメンバ関数を持たない型
struct Plain {};
// initializer_str だけを持つ型
struct OnlyInit {
int x;
std::string initializer_str() const { return "{" + std::to_string(x) + "}"; }
};
// char の期待値 (愚直に作る)
std::string naive_char_repr(char c) {
unsigned char u = static_cast<unsigned char>(c);
switch (c) {
case '\0': return "'\\0'";
case '\a': return "'\\a'";
case '\b': return "'\\b'";
case '\f': return "'\\f'";
case '\r': return "'\\r'";
case '\n': return "'\\n'";
case '\v': return "'\\v'";
case '\'': return "'\\''";
case '\\': return "'\\\\'";
}
if (32 <= u && u <= 126) return std::string("'") + c + "'";
return "'\\" + std::to_string(u / 64) + std::to_string(u / 8 % 8) + std::to_string(u % 8) + "'";
}
int main() {
std::mt19937 rng(314);
// 整数・浮動小数点数
CHECK_REPR(5, 5);
CHECK_REPR(-3, -3);
CHECK_REPR(5L, 5l);
CHECK_REPR(-7LL, -7ll);
CHECK_REPR(5U, 5u);
CHECK_REPR(5UL, 5ul);
CHECK_REPR(18446744073709551615ULL, 18446744073709551615ull);
CHECK_REPR(1.5f, 1.500000f);
CHECK_REPR(-0.25, -0.250000);
CHECK_REPR(2.5L, 2.500000l);
check(type_str(5) == "int" && type_str(5L) == "long" && type_str(5LL) == "long long", "type_str (signed)");
check(type_str(5U) == "unsigned int" && type_str(5UL) == "unsigned long" && type_str(5ULL) == "unsigned long long", "type_str (unsigned)");
check(type_str(1.0f) == "float" && type_str(1.0) == "double" && type_str(1.0L) == "long double", "type_str (floating)");
check(initializer_str(5LL) == "5ll" && initializer_str(1.5f) == "1.500000f", "initializer_str (number)");
// char
CHECK_REPR('a', 'a');
CHECK_REPR('\n', '\n');
CHECK_REPR('\0', '\0');
CHECK_REPR('\'', '\'');
CHECK_REPR('\\', '\\');
CHECK_REPR(char(1), '\001');
CHECK_REPR(char(127), '\177');
CHECK_REPR(char(-61), '\303'); // UTF-8 の文字のバイトなど、負の値の char
check(type_str('a') == "char" && initializer_str('a') == "'a'", "type_str / initializer_str (char)");
for (int i = -128; i <= 127; i++) {
char c = static_cast<char>(i);
check(repr(c) == naive_char_repr(c), "repr(char) for all values");
}
// 文字列
{
const char* s = "abc";
check(repr(s) == "\"abc\"" && type_str(s) == "char*" && initializer_str(s) == "\"abc\"", "const char*");
}
CHECK_REPR(std::string("abc"), std::string{"abc"});
CHECK_REPR(std::string(), std::string{""});
check(type_str(std::string()) == "std::string" && initializer_str(std::string("x")) == "\"x\"", "type_str / initializer_str (string)");
// コンテナ
CHECK_REPR((std::vector<int>{1, 2, 3}), std::vector<int>{1, 2, 3});
CHECK_REPR(std::vector<int>(), std::vector<int>{});
CHECK_REPR((std::vector<std::vector<long long>>{{1, 2}, {}}), std::vector<std::vector<long long>>{{1ll, 2ll}, {}});
CHECK_REPR((std::set<int>{3, 1, 2}), std::set<int>{1, 2, 3});
CHECK_REPR((std::pair<int, std::string>{1, "a"}), std::pair<int, std::string>{1, "a"});
CHECK_REPR((std::pair<char, double>{'x', 0.5}), std::pair<char, double>{'x', 0.500000});
CHECK_REPR((std::map<int, char>{{2, 'b'}, {1, 'a'}}), std::map<int, char>{{1, 'a'}, {2, 'b'}});
CHECK_REPR((std::map<std::string, std::vector<int>>{{"k", {1}}}), std::map<std::string, std::vector<int>>{{"k", {1}}});
CHECK_REPR((std::array<int, 3>{1, 2, 3}), std::array<int, 3>{1, 2, 3});
CHECK_REPR((std::vector<std::pair<int, unsigned>>{{1, 2u}}), std::vector<std::pair<int, unsigned int>>{{1, 2u}});
check(type_str(std::vector<std::set<long long>>()) == "std::vector<std::set<long long>>", "type_str (nested)");
check(initializer_str(std::vector<int>{1, 2}) == "{1, 2}", "initializer_str (vector)");
// repr / type_str / initializer_str をメンバ関数に持つ型 (Range)
CHECK_REPR(Range(0, 10, 2), Range(0, 10, 2));
CHECK_REPR((std::vector<Range>{Range(0, 3, 1)}), std::vector<Range>{{0, 3, 1}});
check(type_str(Range(5)) == "Range" && initializer_str(Range(1, 5, 2)) == "{1, 5, 2}", "Range");
// メンバ関数を持たない型は "T" と "{}"
check(type_str(Plain{}) == "T" && initializer_str(Plain{}) == "{}" && repr(Plain{}) == "T{}", "Plain");
// initializer_str だけを持つ型
check(repr(OnlyInit{7}) == "T{7}" && initializer_str(std::vector<OnlyInit>{{1}, {2}}) == "{{1}, {2}}", "OnlyInit");
// ランダム: 整数の vector
for (int rep = 0; rep < 1000 && ok; rep++) {
int n = rng() % 5;
std::vector<long long> v(n);
std::string expect = "std::vector<long long>{";
for (int i = 0; i < n; i++) {
v[i] = (long long)(rng() % 2001) - 1000;
expect += (i ? ", " : "") + std::to_string(v[i]) + "ll";
}
expect += "}";
check(repr(v) == expect, "repr(vector<long long>) (random)");
}
// DEBUG(x) は std::cerr に "x = repr(x)" を出力する
{
std::ostringstream buf;
std::streambuf* old = std::cerr.rdbuf(buf.rdbuf());
std::vector<int> xs{1, 2};
DEBUG(xs);
std::cerr.rdbuf(old);
check(buf.str() == "xs = std::vector<int>{1, 2}\n", "DEBUG");
}
if (ok) std::cout << "Hello World" << std::endl;
}