init
This commit is contained in:
commit
46288f0f07
121
avl/main.cpp
Normal file
121
avl/main.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
template<typename T>
|
||||
class AVLTree {
|
||||
private:
|
||||
struct Node {
|
||||
T content;
|
||||
Node* left = nullptr;
|
||||
Node* right = nullptr;
|
||||
size_t height = 0;
|
||||
|
||||
Node(const Node&) = default;
|
||||
Node(const T& content) : content(content) {}
|
||||
|
||||
void Update() {
|
||||
height = std::min((left ? left->height : 0), (right ? right->height : 0)) + 1;
|
||||
}
|
||||
|
||||
int GetDifference() {
|
||||
return (left ? left->height : 0) - (right ? right->height : 0);
|
||||
}
|
||||
};
|
||||
|
||||
Node* root_;
|
||||
|
||||
static Node* LeftRotate(Node* v) {
|
||||
Node* tmp = v->right;
|
||||
v->right = tmp->left;
|
||||
tmp->left = v;
|
||||
v->Update();
|
||||
tmp->Update();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static Node* RightRotate(Node* v) {
|
||||
Node* tmp = v->left;
|
||||
v->left = tmp->right;
|
||||
tmp->right = v;
|
||||
v->Update();
|
||||
tmp->Update();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static Node* BigLeftRotate(Node* v) {
|
||||
v->right = RightRotate(v->right);
|
||||
return LeftRotate(v);
|
||||
}
|
||||
|
||||
static Node* BigRightRotate(Node* v) {
|
||||
v->left = LeftRotate(v->left);
|
||||
return RightRotate(v);
|
||||
}
|
||||
|
||||
void stabilization(Node*& v) {
|
||||
v->Update();
|
||||
|
||||
if (v->GetDifference() == -2) {
|
||||
if (v->right->GetDifference() != 1) {
|
||||
v = LeftRotate(v);
|
||||
} else {
|
||||
v = BigLeftRotate(v);
|
||||
}
|
||||
} else if (v->GetDifference() == 2) {
|
||||
if (v->left->GetDifference() != -1) {
|
||||
v = RightRotate(v);
|
||||
} else {
|
||||
v = BigRightRotate(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void insert(Node* v, const T& element) {
|
||||
if (v->content == element) {
|
||||
// ТУТ НАДО ЧТО-ТО ИЗМЕНИТЬ, ЕСЛИ ХОТИМ МУЛЬТИМНОЖЕСТВО
|
||||
return;
|
||||
} else if (v->content > element) {
|
||||
if (v->left) {
|
||||
insert(v->left, element);
|
||||
stabilization(v);
|
||||
} else {
|
||||
v->left = new Node(element);
|
||||
}
|
||||
} else if (v->content < element) {
|
||||
if (v->right) {
|
||||
insert(v->right, element);
|
||||
stabilisztion(v);
|
||||
} else {
|
||||
v->right = new Node(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T eraseMinimum(Node* v) {
|
||||
if (v->left) {
|
||||
T tmp = eraseMinimum(v->left);
|
||||
stabilisztion(v);
|
||||
return tmp;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void erase(Node* v, const T& element) {
|
||||
if ()
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void insert(const T& element) {
|
||||
insert(root_, element);
|
||||
}
|
||||
|
||||
void erase(const T& element) {
|
||||
erase(root_, element);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
AVLTree<int> a;
|
||||
a.insert(10);
|
||||
}
|
638
biginteger/biginteger.h
Normal file
638
biginteger/biginteger.h
Normal file
|
@ -0,0 +1,638 @@
|
|||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace FastFourierTransform {
|
||||
typedef double ld;
|
||||
typedef std::complex<ld> cld;
|
||||
typedef std::vector<cld> ComplexPolynom;
|
||||
|
||||
const ld PI = acosl(-1);
|
||||
std::vector<int16_t> Muliplication(const std::vector<int16_t>& a,
|
||||
const std::vector<int16_t>& b,
|
||||
std::vector<int16_t>& result);
|
||||
ComplexPolynom ToComplex(const std::vector<int16_t>& a);
|
||||
void FastFourierTransform_(ComplexPolynom& a, bool invert);
|
||||
|
||||
std::vector<int16_t> Muliplication(const std::vector<int16_t>& a,
|
||||
const std::vector<int16_t>& b,
|
||||
std::vector<int16_t>& result) {
|
||||
ComplexPolynom ca = ToComplex(a);
|
||||
ComplexPolynom cb = ToComplex(b);
|
||||
|
||||
size_t n = std::max(a.size(), b.size());
|
||||
size_t m = 1;
|
||||
while (m < n) m <<= 1;
|
||||
m <<= 1;
|
||||
|
||||
ca.resize(m);
|
||||
cb.resize(m);
|
||||
|
||||
FastFourierTransform_(ca, false);
|
||||
FastFourierTransform_(cb, false);
|
||||
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
ca[i] *= cb[i];
|
||||
}
|
||||
|
||||
FastFourierTransform_(ca, true);
|
||||
std::vector<int64_t> v(m);
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
v[i] = ca[i].real() + 0.5;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
v[i + 1] += v[i] / 10000;
|
||||
v[i] %= 10000;
|
||||
while (v[i] < 0) v[i] += 10000, v[i + 1] -= 1;
|
||||
}
|
||||
result.resize(v.size());
|
||||
std::copy(v.begin(), v.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
ComplexPolynom ToComplex(const std::vector<int16_t>& a) {
|
||||
ComplexPolynom res(a.begin(), a.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t rev(size_t num, size_t lg_n) {
|
||||
int res = 0;
|
||||
for (size_t i = 0; i < lg_n; ++i)
|
||||
if (num & (1 << i)) res |= 1 << (lg_n - 1 - i);
|
||||
return res;
|
||||
}
|
||||
|
||||
void FastFourierTransform_(ComplexPolynom& a, bool invert) {
|
||||
size_t n = a.size();
|
||||
size_t lg_n = 0;
|
||||
while ((1u << lg_n) < n) ++lg_n;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (i < rev(i, lg_n)) swap(a[i], a[rev(i, lg_n)]);
|
||||
|
||||
for (size_t len = 2; len <= n; len <<= 1) {
|
||||
double ang = 2 * PI / len * (invert ? -1 : 1);
|
||||
cld wlen(cos(ang), sin(ang));
|
||||
for (size_t i = 0; i < n; i += len) {
|
||||
cld w(1);
|
||||
for (size_t j = 0; j < len / 2; ++j) {
|
||||
cld u = a[i + j], v = a[i + j + len / 2] * w;
|
||||
a[i + j] = u + v;
|
||||
a[i + j + len / 2] = u - v;
|
||||
w *= wlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invert)
|
||||
for (size_t i = 0; i < n; ++i) a[i] /= n;
|
||||
}
|
||||
}; // namespace FastFourierTransform
|
||||
|
||||
class BigInteger;
|
||||
|
||||
BigInteger operator-(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator*(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator/(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator+(const BigInteger& a, const BigInteger& b);
|
||||
bool operator>(const BigInteger& a, const BigInteger& b);
|
||||
bool operator<=(const BigInteger& a, const BigInteger& b);
|
||||
bool operator>=(const BigInteger& a, const BigInteger& b);
|
||||
bool operator==(const BigInteger& a, const BigInteger& b);
|
||||
bool operator!=(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator%(const BigInteger& a, const BigInteger& b);
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
BigInteger() {}
|
||||
|
||||
BigInteger(int64_t x) {
|
||||
if (x < 0) {
|
||||
is_negative_ = true, x *= -1;
|
||||
}
|
||||
t_[0] = x % BASE;
|
||||
x /= BASE;
|
||||
while (x > 0) {
|
||||
t_.push_back(x % BASE);
|
||||
x /= BASE;
|
||||
}
|
||||
Normalize();
|
||||
}
|
||||
|
||||
BigInteger(const BigInteger& x) : t_(x.t_), is_negative_(x.is_negative_) {}
|
||||
|
||||
void swap(BigInteger& x) {
|
||||
std::swap(t_, x.t_);
|
||||
std::swap(is_negative_, x.is_negative_);
|
||||
}
|
||||
|
||||
BigInteger& operator=(const BigInteger& x) {
|
||||
BigInteger tmp = x;
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator<(const BigInteger& a, const BigInteger& b) {
|
||||
if (a.is_negative_ ^ b.is_negative_) {
|
||||
return a.is_negative_;
|
||||
}
|
||||
if (a.t_.size() != b.t_.size()) {
|
||||
return (a.t_.size() < b.t_.size()) ^ a.is_negative_;
|
||||
}
|
||||
for (size_t i = a.t_.size(); i > 0; --i) {
|
||||
if (a.t_[i - 1] != b.t_[i - 1]) {
|
||||
return (a.t_[i - 1] < b.t_[i - 1]) ^ a.is_negative_;
|
||||
}
|
||||
}
|
||||
return a.is_negative_;
|
||||
}
|
||||
|
||||
BigInteger& operator++() {
|
||||
*this += 1;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger operator++(int) {
|
||||
BigInteger tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void addingDifferentSigns(const BigInteger& x) {
|
||||
int16_t carry[2] = {0, 0};
|
||||
// Вычитаем, если *this < x, то последнее число будет отрицательным и
|
||||
// потом это пофиксим
|
||||
for (size_t i = 0; i < x.t_.size() || (carry[i & 1] && i < t_.size());
|
||||
++i) {
|
||||
if (i < x.t_.size()) {
|
||||
t_[i] -= x.t_[i];
|
||||
}
|
||||
t_[i] += carry[i & 1];
|
||||
if (t_[i] < 0 && i + 1 < t_.size()) {
|
||||
t_[i] += BASE;
|
||||
carry[(i & 1) ^ 1] = -1;
|
||||
} else {
|
||||
carry[(i & 1) ^ 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Убераем нули в начале и начинаем фиксить отрицательное число
|
||||
Normalize();
|
||||
if (t_.back() < 0) {
|
||||
t_.pop_back();
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
t_[i] = BASE - 1 - t_[i];
|
||||
}
|
||||
++t_[0];
|
||||
while (t_.size() > 1 && t_.back() == 0) {
|
||||
t_.pop_back();
|
||||
}
|
||||
is_negative_ ^= true;
|
||||
}
|
||||
|
||||
// Когда мы пофиксили отрицательные числа, у нас в ячейках могли
|
||||
// образоваться числа >= BASE, фиксим это
|
||||
carry[0] = 0;
|
||||
carry[1] = 0;
|
||||
|
||||
if (t_[0] >= BASE) {
|
||||
carry[1] = 1;
|
||||
t_[0] -= BASE;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < t_.size() && carry[i & 1]; ++i) {
|
||||
t_[i] += carry[i & 1];
|
||||
carry[i & 1] = 0;
|
||||
if (t_[i] >= BASE) {
|
||||
carry[(i & 1) ^ 1] = 1;
|
||||
t_[i] -= BASE;
|
||||
}
|
||||
}
|
||||
|
||||
// У нас может произойти перенос в ячейку, которой не существует, добавим
|
||||
// ее)
|
||||
if (carry[t_.size() & 1]) {
|
||||
t_.push_back(1);
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger& operator+=(const BigInteger& x) {
|
||||
t_.resize(std::max(t_.size(), x.t_.size()) + 1);
|
||||
// Если числа одинаковых знаков - просто сложение
|
||||
if (!is_negative_ ^ x.is_negative_) {
|
||||
int16_t carry[2] = {0, 0};
|
||||
for (size_t i = 0; i < x.t_.size() || carry[i & 1]; ++i) {
|
||||
if (i < x.t_.size()) {
|
||||
t_[i] += x.t_[i];
|
||||
}
|
||||
t_[i] += carry[i & 1];
|
||||
|
||||
if (t_[i] >= BASE) {
|
||||
t_[i] -= BASE;
|
||||
carry[(i & 1) ^ 1] = 1;
|
||||
} else {
|
||||
carry[(i & 1) ^ 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Normalize();
|
||||
} else {
|
||||
addingDifferentSigns(x);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator-=(const BigInteger& x) {
|
||||
// a - b = -(-a + b)
|
||||
is_negative_ ^= 1;
|
||||
*this += x;
|
||||
is_negative_ ^= 1;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(int16_t x) {
|
||||
// Это умножение на маленькое число, работает за O(n) и используется в
|
||||
// делении
|
||||
if (x < 0) {
|
||||
is_negative_ = !is_negative_;
|
||||
x *= -1;
|
||||
}
|
||||
if (x >= BASE) {
|
||||
return *this *= BigInteger(x);
|
||||
}
|
||||
|
||||
t_.resize(t_.size() + 3);
|
||||
int16_t carry[2] = {0, 0};
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
carry[!(i & 1)] = (int(t_[i]) * x + carry[i & 1]) / BASE;
|
||||
t_[i] = (int(t_[i]) * x + carry[i & 1]) % BASE;
|
||||
}
|
||||
|
||||
while (t_.size() > 1 && t_.back() == 0) t_.pop_back();
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(const BigInteger& x) {
|
||||
// Если число на которое нам надо умножить небольшое, выгоднее умножать за
|
||||
// квадрат
|
||||
if (x.t_.size() < 100) {
|
||||
return SquareMultiplication(x);
|
||||
}
|
||||
FastFourierTransform::Muliplication(t_, x.t_, t_);
|
||||
is_negative_ ^= x.is_negative_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Деление работает за O(N^2), мы вычисляем все значащие биты в делении,
|
||||
// которых O(n), с помощью бинпоиска(константа, так как log 10000), и
|
||||
// проверкой в бинпоиске за O(n), потому что используется умножение за O(n), и
|
||||
// в итоге O(N^2)
|
||||
BigInteger& operator/=(const BigInteger& x) {
|
||||
bool is_negative = is_negative_ ^ x.is_negative_;
|
||||
is_negative_ = false;
|
||||
std::vector<int16_t> res(t_.size() - std::min(x.t_.size(), t_.size()) + 4);
|
||||
int16_t d = 1;
|
||||
if (x < 0) {
|
||||
d = -1;
|
||||
}
|
||||
|
||||
for (int i = res.size(); i > 0; --i) {
|
||||
int l = 0, r = BASE - 1, m;
|
||||
while (l != r) {
|
||||
m = (l + r + 1) / 2;
|
||||
if ((x * d * m).MuliplicationDegree10(i - 1) <= *this) {
|
||||
l = m;
|
||||
} else {
|
||||
r = m - 1;
|
||||
}
|
||||
}
|
||||
res[i - 1] = l;
|
||||
*this -= (x * d * res[i - 1]).MuliplicationDegree10(i - 1);
|
||||
}
|
||||
|
||||
t_ = res;
|
||||
is_negative_ = is_negative;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator%=(const BigInteger& x) {
|
||||
*this -= (*this / x) * x;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream string;
|
||||
if (is_negative_) {
|
||||
string << "-";
|
||||
}
|
||||
string << t_.back();
|
||||
for (auto it = ++t_.rbegin(); it != t_.rend(); ++it) {
|
||||
string << std::setw(4) << std::setfill('0') << *it;
|
||||
}
|
||||
return string.str();
|
||||
}
|
||||
|
||||
explicit operator bool() const { return t_.size() > 1 || t_[0]; }
|
||||
|
||||
friend std::istream& operator>>(std::istream& in, BigInteger& x) {
|
||||
std::string s;
|
||||
in >> s;
|
||||
std::reverse(s.begin(), s.end());
|
||||
if (s.back() == '-') {
|
||||
x.is_negative_ = true;
|
||||
s.pop_back();
|
||||
} else {
|
||||
x.is_negative_ = false;
|
||||
}
|
||||
while (s.size() % 4) {
|
||||
s.push_back('0');
|
||||
}
|
||||
x.t_.resize(s.size() / 4);
|
||||
for (size_t i = 0; i < s.size(); i += 4) {
|
||||
x.t_[i / 4] = s[i] - '0' + 10 * (s[i + 1] - '0') +
|
||||
100 * (s[i + 2] - '0') + 1000 * (s[i + 3] - '0');
|
||||
}
|
||||
x.Normalize();
|
||||
return in;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const BigInteger& x) {
|
||||
out << x.toString();
|
||||
return out;
|
||||
}
|
||||
|
||||
BigInteger operator-() {
|
||||
BigInteger tmp(*this);
|
||||
tmp.is_negative_ ^= true;
|
||||
tmp.Normalize();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
void Normalize() {
|
||||
while (t_.size() > 1 && t_.back() == 0) {
|
||||
t_.pop_back();
|
||||
}
|
||||
if (t_.size() == 1 && t_[0] == 0) {
|
||||
is_negative_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Это умножение на 10^degree
|
||||
BigInteger& MuliplicationDegree10(int degree) {
|
||||
size_t n = t_.size();
|
||||
t_.resize(t_.size() + degree);
|
||||
std::rotate(t_.begin(), t_.begin() + n, t_.end());
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Это используется, если число на которое мы умножаем не очень большое,
|
||||
// потому что FFT работает достаточно медленно
|
||||
BigInteger& SquareMultiplication(const BigInteger& x) {
|
||||
std::vector<uint64_t> t(t_.size() + x.t_.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
for (size_t j = 0; j < x.t_.size(); ++j) {
|
||||
t[i + j] += static_cast<uint64_t>(t_[i]) * x.t_[j];
|
||||
}
|
||||
}
|
||||
|
||||
t_.resize(t_.size() + x.t_.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < t.size(); ++i) {
|
||||
t[i + 1] += t[i] / BASE;
|
||||
t_[i] = t[i] % BASE;
|
||||
}
|
||||
|
||||
while (t_.size() > 1 && t_.back() == 0) t_.pop_back();
|
||||
|
||||
is_negative_ ^= x.is_negative_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
static constexpr uint16_t BASE = 10000;
|
||||
std::vector<int16_t> t_ = {0};
|
||||
bool is_negative_ = false;
|
||||
};
|
||||
|
||||
BigInteger operator-(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) -= b;
|
||||
}
|
||||
|
||||
BigInteger operator*(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) *= b;
|
||||
}
|
||||
|
||||
BigInteger operator/(const BigInteger& a, const BigInteger& b) {
|
||||
BigInteger tmp(a);
|
||||
tmp /= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
BigInteger operator%(const BigInteger& a, const BigInteger& b) {
|
||||
BigInteger tmp(a);
|
||||
tmp %= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
BigInteger operator+(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) += b;
|
||||
}
|
||||
|
||||
bool operator>(const BigInteger& a, const BigInteger& b) { return b < a; }
|
||||
|
||||
bool operator<=(const BigInteger& a, const BigInteger& b) { return !(a > b); }
|
||||
|
||||
bool operator>=(const BigInteger& a, const BigInteger& b) { return b <= a; }
|
||||
|
||||
bool operator==(const BigInteger& a, const BigInteger& b) {
|
||||
return !(a < b) && !(b < a);
|
||||
}
|
||||
|
||||
bool operator!=(const BigInteger& a, const BigInteger& b) { return !(a == b); }
|
||||
|
||||
class Rational;
|
||||
|
||||
Rational operator+(const Rational& a, const Rational& b);
|
||||
Rational operator-(const Rational& a, const Rational& b);
|
||||
Rational operator*(const Rational& a, const Rational& b);
|
||||
Rational operator/(const Rational& a, const Rational& b);
|
||||
bool operator>(const Rational& a, const Rational& b);
|
||||
bool operator<=(const Rational& a, const Rational& b);
|
||||
bool operator>=(const Rational& a, const Rational& b);
|
||||
bool operator==(const Rational& a, const Rational& b);
|
||||
bool operator!=(const Rational& a, const Rational& b);
|
||||
|
||||
class Rational {
|
||||
public:
|
||||
Rational() = default;
|
||||
|
||||
Rational(const BigInteger& a) { numerator_ = a; }
|
||||
|
||||
Rational(int a) { numerator_ = a; }
|
||||
|
||||
Rational(const Rational& x)
|
||||
: numerator_(x.numerator_), denominator_(x.denominator_) {}
|
||||
|
||||
void swap(Rational& x) {
|
||||
std::swap(numerator_, x.numerator_);
|
||||
std::swap(denominator_, x.denominator_);
|
||||
}
|
||||
|
||||
Rational& operator=(const Rational& x) {
|
||||
Rational tmp(x);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator+=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this += Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
numerator_ += a.numerator_ * denominator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator-=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this -= Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
numerator_ -= a.numerator_ * denominator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator*=(const Rational& a) {
|
||||
numerator_ *= a.numerator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator/=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this /= Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
denominator_ *= a.numerator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator-() const {
|
||||
Rational tmp(*this);
|
||||
tmp.numerator_ *= -1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator<(const Rational& a) const {
|
||||
return numerator_ * a.denominator_ < denominator_ * a.numerator_;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream str;
|
||||
str << numerator_;
|
||||
if (denominator_ != 1) {
|
||||
str << "/" << denominator_;
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string asDecimal(size_t precision) {
|
||||
BigInteger t = 1;
|
||||
for (size_t i = 0; i < precision; ++i) {
|
||||
t *= 10;
|
||||
}
|
||||
std::ostringstream str;
|
||||
BigInteger result = (numerator_ * t) / denominator_;
|
||||
|
||||
if (result < 0) {
|
||||
str << "-", result *= -1;
|
||||
}
|
||||
str << result / t << "." << std::setw(precision) << std::setfill('0')
|
||||
<< result % t;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
explicit operator double() { return 1.0; }
|
||||
|
||||
BigInteger gcd(const BigInteger& a, const BigInteger& b) {
|
||||
return b ? gcd(b, a % b) : a;
|
||||
}
|
||||
|
||||
void Normalize() {
|
||||
if (numerator_ == 0) {
|
||||
denominator_ = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
BigInteger d = gcd(numerator_, denominator_);
|
||||
numerator_ /= d;
|
||||
denominator_ /= d;
|
||||
|
||||
if (denominator_ < 0) {
|
||||
numerator_ *= -1;
|
||||
denominator_ *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger numerator_ = 0;
|
||||
BigInteger denominator_ = 1;
|
||||
};
|
||||
|
||||
Rational operator+(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator-(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp -= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator*(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp *= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator/(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp /= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator>(const Rational& a, const Rational& b) { return b < a; }
|
||||
|
||||
bool operator<=(const Rational& a, const Rational& b) {
|
||||
return a < b || a == b;
|
||||
}
|
||||
|
||||
bool operator>=(const Rational& a, const Rational& b) {
|
||||
return a > b || a == b;
|
||||
}
|
||||
|
||||
bool operator==(const Rational& a, const Rational& b) {
|
||||
return !(b < a) && !(a < b);
|
||||
}
|
||||
|
||||
bool operator!=(const Rational& a, const Rational& b) { return !(a == b); }
|
35
biginteger/main.cpp
Normal file
35
biginteger/main.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "biginteger.h"
|
||||
|
||||
int f() {
|
||||
|
||||
for () {
|
||||
|
||||
for () {
|
||||
|
||||
for () {
|
||||
for () {
|
||||
for () {
|
||||
|
||||
mk
|
||||
for () {
|
||||
|
||||
for () {
|
||||
|
||||
for () {
|
||||
for () {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
BigInteger a = 10, b = 1;
|
||||
std::cout << a * b << " " << (a /= a) << std::endl;
|
||||
}
|
5
biginteger/makefile
Normal file
5
biginteger/makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -Wextra -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
633
biginteger/save1.h
Normal file
633
biginteger/save1.h
Normal file
|
@ -0,0 +1,633 @@
|
|||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace FastFourierTransform {
|
||||
typedef double ld;
|
||||
typedef std::complex<ld> cld;
|
||||
typedef std::vector<cld> ComplexPolynom;
|
||||
|
||||
const ld PI = acosl(-1);
|
||||
std::vector<int16_t> Muliplication(const std::vector<int16_t>& a, const std::vector<int16_t>& b,
|
||||
std::vector<int16_t>& result);
|
||||
ComplexPolynom ToComplex(const std::vector<int16_t>& a);
|
||||
void FastFourierTransform_(ComplexPolynom& a, bool invert);
|
||||
|
||||
std::vector<int16_t> Muliplication(const std::vector<int16_t>& a,
|
||||
const std::vector<int16_t>& b,
|
||||
std::vector<int16_t>& result) {
|
||||
ComplexPolynom ca = ToComplex(a);
|
||||
ComplexPolynom cb = ToComplex(b);
|
||||
|
||||
size_t n = std::max(a.size(), b.size());
|
||||
size_t m = 1;
|
||||
while (m < n) m <<= 1;
|
||||
m <<= 1;
|
||||
|
||||
ca.resize(m);
|
||||
cb.resize(m);
|
||||
|
||||
FastFourierTransform_(ca, false);
|
||||
FastFourierTransform_(cb, false);
|
||||
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
ca[i] *= cb[i];
|
||||
}
|
||||
|
||||
FastFourierTransform_(ca, true);
|
||||
std::vector<int64_t> v(m);
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
v[i] = ca[i].real() + 0.5;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
v[i + 1] += v[i] / 10000;
|
||||
v[i] %= 10000;
|
||||
while (v[i] < 0) v[i] += 10000, v[i + 1] -= 1;
|
||||
}
|
||||
result.resize(v.size());
|
||||
std::copy(v.begin(), v.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
ComplexPolynom ToComplex(const std::vector<int16_t>& a) {
|
||||
ComplexPolynom res(a.begin(), a.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t rev(size_t num, size_t lg_n) {
|
||||
int res = 0;
|
||||
for (size_t i = 0; i < lg_n; ++i)
|
||||
if (num & (1 << i)) res |= 1 << (lg_n - 1 - i);
|
||||
return res;
|
||||
}
|
||||
|
||||
void FastFourierTransform_(ComplexPolynom& a, bool invert) {
|
||||
size_t n = a.size();
|
||||
size_t lg_n = 0;
|
||||
while ((1u << lg_n) < n) ++lg_n;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (i < rev(i, lg_n)) swap(a[i], a[rev(i, lg_n)]);
|
||||
|
||||
for (size_t len = 2; len <= n; len <<= 1) {
|
||||
double ang = 2 * PI / len * (invert ? -1 : 1);
|
||||
cld wlen(cos(ang), sin(ang));
|
||||
for (size_t i = 0; i < n; i += len) {
|
||||
cld w(1);
|
||||
for (size_t j = 0; j < len / 2; ++j) {
|
||||
cld u = a[i + j], v = a[i + j + len / 2] * w;
|
||||
a[i + j] = u + v;
|
||||
a[i + j + len / 2] = u - v;
|
||||
w *= wlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invert)
|
||||
for (size_t i = 0; i < n; ++i) a[i] /= n;
|
||||
}
|
||||
};
|
||||
|
||||
class BigInteger;
|
||||
|
||||
BigInteger operator-(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator*(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator/(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator+(const BigInteger& a, const BigInteger& b);
|
||||
bool operator>(const BigInteger& a, const BigInteger& b);
|
||||
bool operator<=(const BigInteger& a, const BigInteger& b);
|
||||
bool operator>=(const BigInteger& a, const BigInteger& b);
|
||||
bool operator==(const BigInteger& a, const BigInteger& b);
|
||||
bool operator!=(const BigInteger& a, const BigInteger& b);
|
||||
BigInteger operator%(const BigInteger& a, const BigInteger& b);
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
BigInteger() {}
|
||||
|
||||
BigInteger(int64_t x) {
|
||||
if (x < 0) {
|
||||
is_negative_ = true, x *= -1;
|
||||
}
|
||||
t_[0] = x % BASE;
|
||||
x /= BASE;
|
||||
while (x > 0) {
|
||||
t_.push_back(x % BASE);
|
||||
x /= BASE;
|
||||
}
|
||||
Normalize();
|
||||
}
|
||||
|
||||
BigInteger(const BigInteger& x) : t_(x.t_), is_negative_(x.is_negative_) {}
|
||||
|
||||
void swap(BigInteger& x) {
|
||||
std::swap(t_, x.t_);
|
||||
std::swap(is_negative_, x.is_negative_);
|
||||
}
|
||||
|
||||
BigInteger& operator=(const BigInteger& x) {
|
||||
BigInteger tmp = x;
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator<(const BigInteger&a, const BigInteger& b) {
|
||||
if (a.is_negative_ ^ b.is_negative_) {
|
||||
return a.is_negative_;
|
||||
}
|
||||
if (a.t_.size() != b.t_.size()) {
|
||||
return (a.t_.size() < b.t_.size()) ^ a.is_negative_;
|
||||
}
|
||||
for (size_t i = a.t_.size(); i > 0; --i) {
|
||||
if (a.t_[i - 1] != b.t_[i - 1]) {
|
||||
return (a.t_[i - 1] < b.t_[i - 1]) ^ a.is_negative_;
|
||||
}
|
||||
}
|
||||
return a.is_negative_;
|
||||
}
|
||||
|
||||
BigInteger& operator++() {
|
||||
*this += 1;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger operator++(int) {
|
||||
BigInteger tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void addingDifferentSigns(const BigInteger& x) {
|
||||
int16_t carry[2] = {0, 0};
|
||||
// Вычитаем, если *this < x, то последнее число будет отрицательным и
|
||||
// потом это пофиксим
|
||||
for (size_t i = 0; i < x.t_.size() || (carry[i & 1] && i < t_.size());
|
||||
++i) {
|
||||
if (i < x.t_.size()) {
|
||||
t_[i] -= x.t_[i];
|
||||
}
|
||||
t_[i] += carry[i & 1];
|
||||
if (t_[i] < 0 && i + 1 < t_.size()) {
|
||||
t_[i] += BASE;
|
||||
carry[(i & 1) ^ 1] = -1;
|
||||
} else {
|
||||
carry[(i & 1) ^ 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Убераем нули в начале и начинаем фиксить отрицательное число
|
||||
Normalize();
|
||||
if (t_.back() < 0) {
|
||||
t_.pop_back();
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
t_[i] = BASE - 1 - t_[i];
|
||||
}
|
||||
++t_[0];
|
||||
while (t_.size() > 1 && t_.back() == 0) {
|
||||
t_.pop_back();
|
||||
}
|
||||
is_negative_ ^= true;
|
||||
}
|
||||
|
||||
// Когда мы пофиксили отрицательные числа, у нас в ячейках могли
|
||||
// образоваться числа >= BASE, фиксим это
|
||||
carry[0] = 0;
|
||||
carry[1] = 0;
|
||||
|
||||
if (t_[0] >= BASE) {
|
||||
carry[1] = 1;
|
||||
t_[0] -= BASE;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < t_.size() && carry[i & 1]; ++i) {
|
||||
t_[i] += carry[i & 1];
|
||||
carry[i & 1] = 0;
|
||||
if (t_[i] >= BASE) {
|
||||
carry[(i & 1) ^ 1] = 1;
|
||||
t_[i] -= BASE;
|
||||
}
|
||||
}
|
||||
|
||||
// У нас может произойти перенос в ячейку, которой не существует, добавим
|
||||
// ее)
|
||||
if (carry[t_.size() & 1]) {
|
||||
t_.push_back(1);
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger& operator+=(const BigInteger& x) {
|
||||
t_.resize(std::max(t_.size(), x.t_.size()) + 1);
|
||||
// Если числа одинаковых знаков - просто сложение
|
||||
if (!is_negative_ ^ x.is_negative_) {
|
||||
int16_t carry[2] = {0, 0};
|
||||
for (size_t i = 0; i < x.t_.size() || carry[i & 1]; ++i) {
|
||||
if (i < x.t_.size()) {
|
||||
t_[i] += x.t_[i];
|
||||
}
|
||||
t_[i] += carry[i & 1];
|
||||
|
||||
if (t_[i] >= BASE) {
|
||||
t_[i] -= BASE;
|
||||
carry[(i & 1) ^ 1] = 1;
|
||||
} else {
|
||||
carry[(i & 1) ^ 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Normalize();
|
||||
} else {
|
||||
addingDifferentSigns(x);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator-=(const BigInteger& x) {
|
||||
// a - b = -(-a + b)
|
||||
is_negative_ ^= 1;
|
||||
*this += x;
|
||||
is_negative_ ^= 1;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(int16_t x) {
|
||||
// Это умножение на маленькое число, работает за O(n) и используется в
|
||||
// делении
|
||||
if (x < 0) {
|
||||
is_negative_ = !is_negative_;
|
||||
x *= -1;
|
||||
}
|
||||
if (x >= BASE) {
|
||||
return *this *= BigInteger(x);
|
||||
}
|
||||
|
||||
t_.resize(t_.size() + 3);
|
||||
int16_t carry[2] = {0, 0};
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
carry[!(i & 1)] = (int(t_[i]) * x + carry[i & 1]) / BASE;
|
||||
t_[i] = (int(t_[i]) * x + carry[i & 1]) % BASE;
|
||||
}
|
||||
|
||||
while (t_.size() > 1 && t_.back() == 0) t_.pop_back();
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(const BigInteger& x) {
|
||||
// Если число на которое нам надо умножить небольшое, выгоднее умножать за
|
||||
// квадрат
|
||||
if (x.t_.size() < 100) {
|
||||
return SquareMultiplication(x);
|
||||
}
|
||||
FastFourierTransform::Muliplication(t_, x.t_, t_);
|
||||
is_negative_ ^= x.is_negative_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Деление работает за O(N^2), мы вычисляем все значащие биты в делении,
|
||||
// которых O(n), с помощью бинпоиска(константа, так как log 10000), и
|
||||
// проверкой в бинпоиске за O(n), потому что используется умножение за O(n), и
|
||||
// в итоге O(N^2)
|
||||
BigInteger& operator/=(const BigInteger& x) {
|
||||
bool is_negative = is_negative_ ^ x.is_negative_;
|
||||
is_negative_ = false;
|
||||
std::vector<int16_t> res(t_.size() - std::min(x.t_.size(), t_.size()) + 4);
|
||||
int16_t d = 1;
|
||||
if (x < 0) {
|
||||
d = -1;
|
||||
}
|
||||
|
||||
for (int i = res.size(); i > 0; --i) {
|
||||
int l = 0, r = BASE - 1, m;
|
||||
while (l != r) {
|
||||
m = (l + r + 1) / 2;
|
||||
if ((x * d * m).MuliplicationDegree10(i - 1) <= *this) {
|
||||
l = m;
|
||||
} else {
|
||||
r = m - 1;
|
||||
}
|
||||
}
|
||||
res[i - 1] = l;
|
||||
*this -= (x * d * res[i - 1]).MuliplicationDegree10(i - 1);
|
||||
}
|
||||
|
||||
t_ = res;
|
||||
is_negative_ = is_negative;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator%=(const BigInteger& x) {
|
||||
*this -= (*this / x) * x;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream string;
|
||||
if (is_negative_) {
|
||||
string << "-";
|
||||
}
|
||||
string << t_.back();
|
||||
for (auto it = ++t_.rbegin(); it != t_.rend(); ++it) {
|
||||
string << std::setw(4) << std::setfill('0') << *it;
|
||||
}
|
||||
return string.str();
|
||||
}
|
||||
|
||||
explicit operator bool() const { return t_.size() > 1 || t_[0]; }
|
||||
|
||||
friend std::istream& operator>>(std::istream& in, BigInteger& x) {
|
||||
std::string s;
|
||||
in >> s;
|
||||
std::reverse(s.begin(), s.end());
|
||||
if (s.back() == '-') {
|
||||
x.is_negative_ = true;
|
||||
s.pop_back();
|
||||
} else {
|
||||
x.is_negative_ = false;
|
||||
}
|
||||
while (s.size() % 4) {
|
||||
s.push_back('0');
|
||||
}
|
||||
x.t_.resize(s.size() / 4);
|
||||
for (size_t i = 0; i < s.size(); i += 4) {
|
||||
x.t_[i / 4] = s[i] - '0' + 10 * (s[i + 1] - '0') +
|
||||
100 * (s[i + 2] - '0') + 1000 * (s[i + 3] - '0');
|
||||
}
|
||||
x.Normalize();
|
||||
return in;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const BigInteger& x) {
|
||||
out << x.toString();
|
||||
return out;
|
||||
}
|
||||
|
||||
BigInteger operator-() {
|
||||
BigInteger tmp(*this);
|
||||
tmp.is_negative_ ^= true;
|
||||
tmp.Normalize();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
void Normalize() {
|
||||
while (t_.size() > 1 && t_.back() == 0) {
|
||||
t_.pop_back();
|
||||
}
|
||||
if (t_.size() == 1 && t_[0] == 0) {
|
||||
is_negative_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Это умножение на 10^degree
|
||||
BigInteger& MuliplicationDegree10(int degree) {
|
||||
size_t n = t_.size();
|
||||
t_.resize(t_.size() + degree);
|
||||
std::rotate(t_.begin(), t_.begin() + n, t_.end());
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Это используется, если число на которое мы умножаем не очень большое, потому что
|
||||
// FFT работает достаточно медленно
|
||||
BigInteger& SquareMultiplication(const BigInteger& x) {
|
||||
std::vector<uint64_t> t(t_.size() + x.t_.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < t_.size(); ++i) {
|
||||
for (size_t j = 0; j < x.t_.size(); ++j) {
|
||||
t[i + j] += static_cast<uint64_t>(t_[i]) * x.t_[j];
|
||||
}
|
||||
}
|
||||
|
||||
t_.resize(t_.size() + x.t_.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < t.size(); ++i) {
|
||||
t[i + 1] += t[i] / BASE;
|
||||
t_[i] = t[i] % BASE;
|
||||
}
|
||||
|
||||
while (t_.size() > 1 && t_.back() == 0) t_.pop_back();
|
||||
|
||||
is_negative_ ^= x.is_negative_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
static constexpr uint16_t BASE = 10000;
|
||||
std::vector<int16_t> t_ = {0};
|
||||
bool is_negative_ = false;
|
||||
};
|
||||
|
||||
BigInteger operator-(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) -= b;
|
||||
}
|
||||
|
||||
BigInteger operator*(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) *= b;
|
||||
}
|
||||
|
||||
BigInteger operator/(const BigInteger& a, const BigInteger& b) {
|
||||
BigInteger tmp(a);
|
||||
tmp /= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
BigInteger operator%(const BigInteger& a, const BigInteger& b) {
|
||||
BigInteger tmp(a);
|
||||
tmp %= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
BigInteger operator+(const BigInteger& a, const BigInteger& b) {
|
||||
return BigInteger(a) += b;
|
||||
}
|
||||
|
||||
bool operator>(const BigInteger& a, const BigInteger& b) { return b < a; }
|
||||
|
||||
bool operator<=(const BigInteger& a, const BigInteger& b) { return !(a > b); }
|
||||
|
||||
bool operator>=(const BigInteger& a, const BigInteger& b) { return b <= a; }
|
||||
|
||||
bool operator==(const BigInteger& a, const BigInteger& b) {
|
||||
return !(a < b) && !(b < a);
|
||||
}
|
||||
|
||||
bool operator!=(const BigInteger& a, const BigInteger& b) { return !(a == b); }
|
||||
|
||||
class Rational;
|
||||
|
||||
Rational operator+(const Rational& a, const Rational& b);
|
||||
Rational operator-(const Rational& a, const Rational& b);
|
||||
Rational operator*(const Rational& a, const Rational& b);
|
||||
Rational operator/(const Rational& a, const Rational& b);
|
||||
bool operator>(const Rational& a, const Rational& b);
|
||||
bool operator<=(const Rational& a, const Rational& b);
|
||||
bool operator>=(const Rational& a, const Rational& b);
|
||||
bool operator==(const Rational& a, const Rational& b);
|
||||
bool operator!=(const Rational& a, const Rational& b);
|
||||
|
||||
class Rational {
|
||||
public:
|
||||
Rational() = default;
|
||||
|
||||
Rational(const BigInteger& a) { numerator_ = a; }
|
||||
|
||||
Rational(int a) { numerator_ = a; }
|
||||
|
||||
Rational(const Rational& x)
|
||||
: numerator_(x.numerator_), denominator_(x.denominator_) {}
|
||||
|
||||
void swap(Rational& x) {
|
||||
std::swap(numerator_, x.numerator_);
|
||||
std::swap(denominator_, x.denominator_);
|
||||
}
|
||||
|
||||
Rational& operator=(const Rational& x) {
|
||||
Rational tmp(x);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator+=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this += Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
numerator_ += a.numerator_ * denominator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator-=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this -= Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
numerator_ -= a.numerator_ * denominator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator*=(const Rational& a) {
|
||||
numerator_ *= a.numerator_;
|
||||
denominator_ *= a.denominator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& operator/=(const Rational& a) {
|
||||
if (this == &a) {
|
||||
return *this /= Rational(a);
|
||||
}
|
||||
numerator_ *= a.denominator_;
|
||||
denominator_ *= a.numerator_;
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator-() const {
|
||||
Rational tmp(*this);
|
||||
tmp.numerator_ *= -1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator<(const Rational& a) const {
|
||||
return numerator_ * a.denominator_ < denominator_ * a.numerator_;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream str;
|
||||
str << numerator_;
|
||||
if (denominator_ != 1) {
|
||||
str << "/" << denominator_;
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string asDecimal(size_t precision) {
|
||||
BigInteger t = 1;
|
||||
for (size_t i = 0; i < precision; ++i) {
|
||||
t *= 10;
|
||||
}
|
||||
std::ostringstream str;
|
||||
BigInteger result = (numerator_ * t) / denominator_;
|
||||
|
||||
if (result < 0) {
|
||||
str << "-", result *= -1;
|
||||
}
|
||||
str << result / t << "." << std::setw(precision) << std::setfill('0')
|
||||
<< result % t;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
explicit operator double() { return 1.0; }
|
||||
|
||||
BigInteger gcd(const BigInteger& a, const BigInteger& b) {
|
||||
return b ? gcd(b, a % b) : a;
|
||||
}
|
||||
|
||||
void Normalize() {
|
||||
if (numerator_ == 0) {
|
||||
denominator_ = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
BigInteger d = gcd(numerator_, denominator_);
|
||||
numerator_ /= d;
|
||||
denominator_ /= d;
|
||||
|
||||
if (denominator_ < 0) {
|
||||
numerator_ *= -1;
|
||||
denominator_ *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger numerator_ = 0;
|
||||
BigInteger denominator_ = 1;
|
||||
};
|
||||
|
||||
Rational operator+(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator-(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp -= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator*(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp *= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Rational operator/(const Rational& a, const Rational& b) {
|
||||
Rational tmp(a);
|
||||
tmp /= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator>(const Rational& a, const Rational& b) { return b < a; }
|
||||
|
||||
bool operator<=(const Rational& a, const Rational& b) { return a < b || a == b; }
|
||||
|
||||
bool operator>=(const Rational& a, const Rational& b) { return a > b || a == b; }
|
||||
|
||||
bool operator==(const Rational& a, const Rational& b) {
|
||||
return !(b < a) && !(a < b);
|
||||
}
|
||||
|
||||
bool operator!=(const Rational& a, const Rational& b) { return !(a == b); }
|
21
biginteger/test.py
Normal file
21
biginteger/test.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
f = open("input.txt", "r")
|
||||
x = f.read()
|
||||
|
||||
for i in x.split('\n'):
|
||||
t = i.split()
|
||||
if (len(t) < 3):
|
||||
break
|
||||
if (t[0] == '+'):
|
||||
print(int(t[1]) + int(t[2]))
|
||||
elif (t[0] == '-'):
|
||||
print(int(t[1]) - int(t[2]))
|
||||
elif (t[0] == '%'):
|
||||
print(int(t[1]) % int(t[2]))
|
||||
elif (t[0] == '*'):
|
||||
print(int(t[1]) * int(t[2]))
|
||||
elif (t[0] == '/'):
|
||||
x = int(t[1]) // int(t[2])
|
||||
if (x < 0):
|
||||
if (int(t[1]) % int(t[2]) != 0):
|
||||
x += 1
|
||||
print(x)
|
32
deque/main.cpp
Normal file
32
deque/main.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "deque.h"
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
Deque<int> a;
|
||||
|
||||
a.push_back(243);
|
||||
a.push_back(34);
|
||||
a.push_back(45);
|
||||
a.push_back(456);
|
||||
a.push_front(3240);
|
||||
|
||||
cout << endl;
|
||||
cout << endl;
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
cout << a[i] << " ";
|
||||
}
|
||||
cout << endl << endl;
|
||||
|
||||
auto it = a.begin();
|
||||
it += 3;
|
||||
|
||||
a.erase(a.end());
|
||||
|
||||
cout << endl;
|
||||
cout << endl;
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
cout << a[i] << " ";
|
||||
}
|
||||
}
|
5
deque/makefile
Normal file
5
deque/makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -Wextra -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
14
geometry/main.cpp
Normal file
14
geometry/main.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <bits/stdc++.h>
|
||||
#include "geometry.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
|
||||
|
||||
int main() {
|
||||
Polygon p(Point(0, 0), Point(0, 1), Point(1, 2), Point(2, 0));
|
||||
Polygon q(Point(0, 0), Point(2, 0), Point(1, 2), Point(0, 1));
|
||||
|
||||
cout << p.isCongruentTo(q) << endl;
|
||||
return 0;
|
||||
}
|
2
geometry/makefile
Normal file
2
geometry/makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
g++ main.cpp -DLOCAL -Wextra -Wall -o a
|
9
list/CMakeLists.txt
Normal file
9
list/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project("List")
|
||||
|
||||
set(SOURCE_FILES main.cpp fastallocator.h)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(list ${SOURCE_FILES})
|
308
list/fastallocator.h
Normal file
308
list/fastallocator.h
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
template<size_t chunkSize>
|
||||
class FixedAllocator {
|
||||
private:
|
||||
static const int count_chunks = 16;
|
||||
static inline std::vector<void*> free;
|
||||
public:
|
||||
[[nodiscard]] static void* allocate() {
|
||||
if (free.empty()) {
|
||||
char* tmp = new char[chunkSize * count_chunks];
|
||||
for (int i = 0; i < count_chunks; ++i) free.push_back(reinterpret_cast<void*>(tmp + i * chunkSize));
|
||||
}
|
||||
void* tmp = free.back();
|
||||
free.pop_back();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void deallocate(void* p) {
|
||||
free.push_back(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class FastAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
FastAllocator() = default;
|
||||
|
||||
template<typename U>
|
||||
FastAllocator(const FastAllocator<U>&) {}
|
||||
|
||||
template<typename U>
|
||||
FastAllocator& operator=(const FastAllocator<U>&) const { return *this; }
|
||||
|
||||
[[nodiscard]] T* allocate(size_t n) {
|
||||
if (n * sizeof(T) > kSizeBigBlock)
|
||||
return reinterpret_cast<T*>(new char[n * sizeof(T)]);
|
||||
else {
|
||||
return reinterpret_cast<T*>(allocate_template_for<sizeof(T), kSizeBigBlock>(sizeof(T) * n));
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n) {
|
||||
if (n * sizeof(T) > 48)
|
||||
delete[] reinterpret_cast<char*>(p);
|
||||
else
|
||||
deallocate_template_for<sizeof(T), kSizeBigBlock>(p, n * sizeof(T));
|
||||
}
|
||||
private:
|
||||
static const size_t kSizeBigBlock = 48;
|
||||
|
||||
template<size_t x = 1, size_t end = kSizeBigBlock>
|
||||
[[nodiscard]] void* allocate_template_for(size_t n) {
|
||||
if (x == n) {
|
||||
return FixedAllocator<x>::allocate();
|
||||
} else {
|
||||
if constexpr (x < end) {
|
||||
return allocate_template_for<x + 1, end>(n);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<size_t x = 1, size_t end = kSizeBigBlock>
|
||||
void deallocate_template_for(void* ptr, size_t n) {
|
||||
if (x == n) {
|
||||
FixedAllocator<x>::deallocate(ptr);
|
||||
} else {
|
||||
if constexpr (x < end) {
|
||||
deallocate_template_for<x + 1, end>(ptr, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<class T, class U>
|
||||
bool operator==(const FastAllocator<T>& a, const FastAllocator<U>& b) {
|
||||
return &a == &b;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
bool operator!=(const FastAllocator<T>& a, const FastAllocator<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template<typename T, typename Allocator=std::allocator<T>>
|
||||
class List {
|
||||
private:
|
||||
class Node;
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator;
|
||||
public:
|
||||
using iterator = base_iterator<false>;
|
||||
using const_iterator = base_iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
explicit List(const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEndNode();
|
||||
}
|
||||
|
||||
List(size_t count, const T& value, const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEndNode();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp, value);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(size_t count, const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEndNode();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(const List& another) {
|
||||
alloc_ = alloc_traits_::select_on_container_copy_construction(another.alloc_);
|
||||
AllocateEndNode();
|
||||
for (const T& i: another) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp, i);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = another.size();
|
||||
}
|
||||
|
||||
List& operator=(const List& another) {
|
||||
if (this == &another) return *this;
|
||||
if (alloc_traits_::propagate_on_container_copy_assignment::value && alloc_ != another.alloc_) {
|
||||
alloc_ = another.alloc_;
|
||||
}
|
||||
|
||||
auto it1 = begin();
|
||||
auto it2 = another.begin();
|
||||
for (size_t i = 0; i < std::min(another.size(), size()); ++i) {
|
||||
*it1 = *it2;
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
if (another.size() > size()) {
|
||||
while (size() != another.size()) {
|
||||
push_back(*it2);
|
||||
++it2;
|
||||
}
|
||||
} else {
|
||||
while (size() != another.size()) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~List() {
|
||||
while (end_ != end_->r) {
|
||||
erase(iterator(end_->r));
|
||||
}
|
||||
alloc_.deallocate(end_, 1);
|
||||
}
|
||||
|
||||
Allocator get_allocator() {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
void push_back(const T& value) {
|
||||
insert(end(), value);
|
||||
}
|
||||
|
||||
void push_front(const T& value) {
|
||||
insert(begin(), value);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
erase(std::prev(end()));
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
erase(begin());
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
iterator begin() { return iterator(end_->r); }
|
||||
iterator end() { return iterator(end_); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(end_->r); }
|
||||
const_iterator end() const { return const_iterator(end_); }
|
||||
|
||||
const_iterator cbegin() const { return const_iterator(end_->r); }
|
||||
const_iterator cend() const { return const_iterator(end_); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
void insert(const_iterator it, const T& value) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
std::allocator_traits<decltype(alloc_)>::construct(alloc_, tmp, value);
|
||||
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||||
++size_;
|
||||
}
|
||||
|
||||
void erase(const_iterator it) {
|
||||
Node* tmp = const_cast<Node*>(it.it);
|
||||
tmp->l->r = tmp->r;
|
||||
tmp->r->l = tmp->l;
|
||||
std::allocator_traits<decltype(alloc_)>::destroy(alloc_, tmp);
|
||||
alloc_.deallocate(tmp, 1);
|
||||
--size_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AllocateEndNode() {
|
||||
end_ = alloc_.allocate(1);
|
||||
end_->l = end_;
|
||||
end_->r = end_;
|
||||
}
|
||||
|
||||
void Link(Node* l, Node* x) {
|
||||
Node* r = l->r;
|
||||
x->l = l;
|
||||
x->r = r;
|
||||
l->r = x;
|
||||
r->l = x;
|
||||
}
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator {
|
||||
friend List;
|
||||
std::conditional_t<is_const, const Node*, Node*> it;
|
||||
using U = std::conditional_t<is_const, const T, T>;
|
||||
public:
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = U;
|
||||
using pointer = U*;
|
||||
using reference = U&;
|
||||
using difference_type = int;
|
||||
|
||||
base_iterator() : it(nullptr) {}
|
||||
base_iterator(Node* v) { it = v; }
|
||||
|
||||
base_iterator(const base_iterator&) = default;
|
||||
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||||
|
||||
U& operator*() const { return it->value; }
|
||||
U* operator->() const { return &it->value; }
|
||||
base_iterator& operator++()& {
|
||||
it = it->r;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator++(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->r;
|
||||
return tmp;
|
||||
}
|
||||
base_iterator& operator--()& {
|
||||
it = it->l;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator--(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->l;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator==(base_iterator first, base_iterator second) { return first.it == second.it; }
|
||||
|
||||
friend bool operator!=(base_iterator first, base_iterator second) { return !(first == second); }
|
||||
|
||||
base_iterator& operator=(const base_iterator<false>& another) {
|
||||
it = another.it;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Node* l = nullptr;
|
||||
Node* r = nullptr;
|
||||
T value;
|
||||
Node() = default;
|
||||
Node(const T& value) : value(value) {}
|
||||
Node& operator=(const Node& another) { value = another.value; }
|
||||
};
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<Node> alloc_;
|
||||
using alloc_traits_ = std::allocator_traits<decltype(alloc_)>;
|
||||
Node* end_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
136
list/main.cpp
Normal file
136
list/main.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include <bits/stdc++.h>
|
||||
#include "fastallocator.h"
|
||||
|
||||
using namespace std;
|
||||
template<size_t number>
|
||||
struct C1 {
|
||||
static inline vector<int> v = {0, 0, 0, 0, 0};
|
||||
C1() {
|
||||
v[0]++;
|
||||
}
|
||||
C1(int) {
|
||||
v[1]++;
|
||||
}
|
||||
C1(const C1&) {
|
||||
v[2]++;
|
||||
}
|
||||
C1& operator=(const C1&) {
|
||||
v[3]++;
|
||||
return *this;
|
||||
}
|
||||
~C1() {
|
||||
v[4]++;
|
||||
}
|
||||
static void print() {
|
||||
for (int i: v) {
|
||||
cout << i << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
};
|
||||
|
||||
void test1() {
|
||||
|
||||
{
|
||||
const int N = 10000;
|
||||
|
||||
List<C1<0>> l(N);
|
||||
list<C1<1>> t(N);
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
List<C1<0>> ll(N, C1<0>(10));
|
||||
list<C1<1>> tt(N, C1<1>(10));
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
l.push_back(C1<0>());
|
||||
t.push_back(C1<1>());
|
||||
l.push_front(C1<0>());
|
||||
t.push_front(C1<1>());
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
l.insert(l.begin(), C1<0>());
|
||||
t.insert(t.begin(), C1<1>());
|
||||
l.insert(next(l.begin()), C1<0>(10));
|
||||
t.insert(next(t.begin()), C1<1>(10));
|
||||
l.insert(prev(l.end()), C1<0>());
|
||||
t.insert(prev(t.end()), C1<1>());
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N / 4; ++i) {
|
||||
l.pop_back();
|
||||
t.pop_back();
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N / 8; ++i) {
|
||||
l.erase(l.begin());
|
||||
t.erase(t.begin());
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N / 8; ++i) {
|
||||
l.erase(prev(l.end()));
|
||||
t.erase(prev(t.end()));
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N / 8; ++i) {
|
||||
auto a = C1<0>();
|
||||
l.push_back(a);
|
||||
auto b = C1<1>();
|
||||
t.push_back(b);
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
for (size_t i = 0; i < N / 8; ++i) {
|
||||
(*next(l.begin())) = C1<0>();
|
||||
(*next(t.begin())) = C1<1>();
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
C1<0>::print();
|
||||
C1<1>::print();
|
||||
List<C1<0>> lll = l;
|
||||
list<C1<1>> ttt = t;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
l.push_back(10);
|
||||
t.push_back(10);
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
C1<0>::print();
|
||||
C1<1>::print();
|
||||
lll = l;
|
||||
ttt = t;
|
||||
C1<0>::print();
|
||||
C1<1>::print();
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
}
|
||||
|
||||
assert(C1<0>::v == C1<1>::v);
|
||||
|
||||
std::cout << "TEST 1 PASSED" << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test1();
|
||||
|
||||
std::list<char, FastAllocator<char>> x;
|
||||
x.push_back(10);
|
||||
}
|
11
map/CMakeLists.txt
Normal file
11
map/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project("List")
|
||||
|
||||
set(SOURCE_FILES main.cpp)
|
||||
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(map ${SOURCE_FILES})
|
302
map/fastallocator.h
Normal file
302
map/fastallocator.h
Normal file
|
@ -0,0 +1,302 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
template<size_t chunkSize>
|
||||
class FixedAllocator {
|
||||
private:
|
||||
static const int count_chunks = 16;
|
||||
static inline std::vector<void*> free;
|
||||
public:
|
||||
[[nodiscard]] static void* allocate() {
|
||||
if (free.empty()) {
|
||||
char* tmp = static_cast<char*>(malloc(chunkSize * count_chunks));
|
||||
for (int i = 0; i < count_chunks; ++i) free.push_back(reinterpret_cast<void*>(tmp + i * chunkSize));
|
||||
}
|
||||
void* tmp = free.back();
|
||||
free.pop_back();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void deallocate(void* p) noexcept {
|
||||
free.push_back(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t x, size_t end>
|
||||
[[nodiscard]] void* genius_allocate_fixed_allocator(size_t n) {
|
||||
if (x == n) {
|
||||
return FixedAllocator<x>::allocate();
|
||||
} else {
|
||||
if constexpr (x < end) {
|
||||
return genius_allocate_fixed_allocator<x + 1, end>(n);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class FastAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
FastAllocator() = default;
|
||||
|
||||
template<typename U>
|
||||
FastAllocator(const FastAllocator<U>&) {}
|
||||
|
||||
template<typename U>
|
||||
FastAllocator& operator=(const FastAllocator<U>&) const { return *this; }
|
||||
|
||||
[[nodiscard]] T* allocate(size_t n) {
|
||||
if (n * sizeof(T) > big_block_ || n > 1)
|
||||
return static_cast<T*>(std::malloc(n * sizeof(T)));
|
||||
else {
|
||||
return static_cast<T*>(genius_allocate_fixed_allocator<sizeof(T), big_block_>(sizeof(T) * n));
|
||||
// return static_cast<T*>(FixedAllocator<sizeof(T)>().allocate());
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n) noexcept {
|
||||
if (n * sizeof(T) > 48 || n > 1)
|
||||
free(p);
|
||||
else
|
||||
return FixedAllocator<sizeof(T)>().deallocate(p);
|
||||
}
|
||||
private:
|
||||
static const size_t big_block_ = 48;
|
||||
};
|
||||
|
||||
template<class T, class U>
|
||||
bool operator==(const FastAllocator<T>& a, const FastAllocator<U>& b) {
|
||||
return &a == &b;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
bool operator!=(const FastAllocator<T>& a, const FastAllocator<U>& b) {
|
||||
return &a != &b;
|
||||
}
|
||||
|
||||
template<typename T, typename Allocator=std::allocator<T>>
|
||||
class List {
|
||||
private:
|
||||
class Node;
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator;
|
||||
public:
|
||||
using iterator = base_iterator<false>;
|
||||
using const_iterator = base_iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
explicit List(const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEnd();
|
||||
}
|
||||
|
||||
List(size_t count, const T& value, const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEnd();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp, value);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(size_t count, const Allocator& alloc = Allocator()) : alloc_(alloc) {
|
||||
AllocateEnd();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(const List& another) {
|
||||
alloc_ = alloc_traits_::select_on_container_copy_construction(another.alloc_);
|
||||
AllocateEnd();
|
||||
for (const T& i: another) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
alloc_traits_::construct(alloc_, tmp, i);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = another.size();
|
||||
}
|
||||
|
||||
List(List&&) = default;
|
||||
|
||||
List& operator=(const List& another) {
|
||||
if (this == &another) return *this;
|
||||
if (alloc_traits_::propagate_on_container_copy_assignment::value && alloc_ != another.alloc_) {
|
||||
alloc_ = another.alloc_;
|
||||
}
|
||||
|
||||
auto it1 = begin();
|
||||
auto it2 = another.begin();
|
||||
for (size_t i = 0; i < std::min(another.size(), size()); ++i) {
|
||||
*it1 = *it2;
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
if (another.size() > size()) {
|
||||
while (size() != another.size()) {
|
||||
push_back(*it2);
|
||||
++it2;
|
||||
}
|
||||
} else {
|
||||
while (size() != another.size()) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
List& operator= (List&&) = default;
|
||||
|
||||
~List() {
|
||||
while (end_ != end_->r) {
|
||||
erase(iterator(end_->r));
|
||||
}
|
||||
alloc_.deallocate(end_, 1);
|
||||
}
|
||||
|
||||
Allocator get_allocator() {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
void push_back(const T& value) {
|
||||
insert(end(), value);
|
||||
}
|
||||
|
||||
void push_front(const T& value) {
|
||||
insert(begin(), value);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
erase(std::prev(end()));
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
erase(begin());
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
iterator begin() { return iterator(end_->r); }
|
||||
iterator end() { return iterator(end_); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(end_->r); }
|
||||
const_iterator end() const { return const_iterator(end_); }
|
||||
|
||||
const_iterator cbegin() const { return const_iterator(end_->r); }
|
||||
const_iterator cend() const { return const_iterator(end_); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
void insert(const_iterator it, const T& value) {
|
||||
Node* tmp = alloc_.allocate(1);
|
||||
std::allocator_traits<decltype(alloc_)>::construct(alloc_, tmp, value);
|
||||
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||||
++size_;
|
||||
}
|
||||
|
||||
void erase(const_iterator it) {
|
||||
Node* tmp = const_cast<Node*>(it.it);
|
||||
tmp->l->r = tmp->r;
|
||||
tmp->r->l = tmp->l;
|
||||
std::allocator_traits<decltype(alloc_)>::destroy(alloc_, tmp);
|
||||
alloc_.deallocate(tmp, 1);
|
||||
--size_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AllocateEnd() {
|
||||
end_ = alloc_.allocate(1);
|
||||
end_->l = end_;
|
||||
end_->r = end_;
|
||||
}
|
||||
|
||||
void Link(Node* l, Node* x) {
|
||||
Node* r = l->r;
|
||||
x->l = l;
|
||||
x->r = r;
|
||||
l->r = x;
|
||||
r->l = x;
|
||||
}
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator {
|
||||
friend List;
|
||||
std::conditional_t<is_const, const Node*, Node*> it;
|
||||
using U = std::conditional_t<is_const, const T, T>;
|
||||
public:
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = U;
|
||||
using pointer = U*;
|
||||
using reference = U&;
|
||||
using difference_type = int;
|
||||
|
||||
base_iterator() : it(nullptr) {}
|
||||
base_iterator(Node* v) { it = v; }
|
||||
|
||||
base_iterator(const base_iterator&) = default;
|
||||
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||||
|
||||
U& operator*() const { return it->value; }
|
||||
U* operator->() const { return &it->value; }
|
||||
base_iterator& operator++()& {
|
||||
it = it->r;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator++(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->r;
|
||||
return tmp;
|
||||
}
|
||||
base_iterator& operator--()& {
|
||||
it = it->l;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator--(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->l;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator==(base_iterator first, base_iterator second) { return first.it == second.it; }
|
||||
|
||||
friend bool operator!=(base_iterator first, base_iterator second) { return first.it != second.it; }
|
||||
|
||||
base_iterator& operator=(const base_iterator<false>& another) {
|
||||
it = another.it;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Node* l = nullptr;
|
||||
Node* r = nullptr;
|
||||
T value;
|
||||
Node() = default;
|
||||
Node(const T& value) : value(value) {}
|
||||
Node& operator=(const Node& another) { value = another.value; }
|
||||
};
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<Node> alloc_;
|
||||
using alloc_traits_ = std::allocator_traits<decltype(alloc_)>;
|
||||
Node* end_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
385
map/main.cpp
Normal file
385
map/main.cpp
Normal file
|
@ -0,0 +1,385 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
#include "unordered_map.h"
|
||||
|
||||
template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Equal = std::equal_to<Key>, typename Alloc = std::allocator<std::pair<Key, Value>>>
|
||||
using unordered_map = UnorderedMap<Key, Value, Hash, Equal, Alloc>;
|
||||
//
|
||||
//using std::unordered_map;
|
||||
|
||||
void SimpleTest() {
|
||||
unordered_map<std::string, int> m;
|
||||
|
||||
m["aaaaa"] = 5;
|
||||
m["bbb"] = 6;
|
||||
m.at("bbb") = 7;
|
||||
|
||||
assert(m.size() == 2);
|
||||
|
||||
std::cout << m["aaaaa"] << std::endl;
|
||||
assert(m["aaaaa"] == 5);
|
||||
assert(m["bbb"] == 7);
|
||||
assert(m["ccc"] == 0);
|
||||
|
||||
assert(m.size() == 3);
|
||||
|
||||
try {
|
||||
m.at("xxxxxxxx");
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
auto it = m.find("dddd");
|
||||
assert(it == m.end());
|
||||
|
||||
it = m.find("bbb");
|
||||
assert(it->second == 7);
|
||||
++it->second;
|
||||
assert(it->second == 8);
|
||||
|
||||
for (auto& item : m) {
|
||||
--item.second;
|
||||
}
|
||||
|
||||
assert(m.at("aaaaa") == 4);
|
||||
|
||||
{
|
||||
auto mm = m;
|
||||
m = std::move(mm);
|
||||
}
|
||||
|
||||
auto res = m.emplace("abcde", 2);
|
||||
assert(res.second);
|
||||
}
|
||||
|
||||
void TestIterators() {
|
||||
unordered_map<double, std::string> m;
|
||||
|
||||
std::vector<double> keys = {0.4, 0.3, -8.32, 7.5, 10.0, 0.0};
|
||||
std::vector<std::string> values = {
|
||||
"Summer has come and passed",
|
||||
"The innocent can never last",
|
||||
"Wake me up when September ends",
|
||||
"Like my fathers come to pass",
|
||||
"Seven years has gone so fast",
|
||||
"Wake me up when September ends",
|
||||
};
|
||||
|
||||
m.reserve(1'000'000);
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
m.insert({keys[i], values[i]});
|
||||
}
|
||||
|
||||
auto beg = m.cbegin();
|
||||
std::string s = beg->second;
|
||||
auto it = m.begin();
|
||||
++it;
|
||||
m.erase(it++);
|
||||
it = m.begin();
|
||||
m.erase(++it);
|
||||
|
||||
assert(beg->second == s);
|
||||
assert(m.size() == 4);
|
||||
|
||||
unordered_map<double, std::string> mm;
|
||||
std::vector<std::pair<const double, std::string>> elements = {
|
||||
{3.0, values[0]},
|
||||
{5.0, values[1]},
|
||||
{-10.0, values[2]},
|
||||
{35.7, values[3]}
|
||||
};
|
||||
mm.insert(elements.cbegin(), elements.cend());
|
||||
s = mm.begin()->second;
|
||||
|
||||
m.insert(mm.begin(), mm.end());
|
||||
assert(mm.size() == 4);
|
||||
assert(mm.begin()->second == s);
|
||||
|
||||
|
||||
// Test traverse efficiency
|
||||
m.reserve(1'000'000); // once again, nothing really should happen
|
||||
assert(m.size() == 8);
|
||||
// Actions below must be quick (~ 1000 * 8 operations) despite reserving space for 1M elements
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
long long h = 0;
|
||||
for (auto it = m.cbegin(); it != m.cend(); ++it) {
|
||||
// just some senseless action
|
||||
h += int(it->first) + int((it->second)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
it = m.begin();
|
||||
++it;
|
||||
s = it->second;
|
||||
// I asked to reserve space for 1M elements so actions below adding 100'000 elements mustn't cause reallocation
|
||||
for (double d = 100.0; d < 10100.0; d += 0.1) {
|
||||
m.emplace(d, "a");
|
||||
}
|
||||
// And my iterator must point to the same object as before
|
||||
assert(it->second == s);
|
||||
|
||||
int dist = std::distance(it, m.end());
|
||||
int sz = m.size();
|
||||
m.erase(it, m.end());
|
||||
assert(sz - dist == m.size());
|
||||
|
||||
// Must be also fast
|
||||
for (double d = 200.0; d < 10200.0; d += 0.35) {
|
||||
auto it = m.find(d);
|
||||
if (it != m.end()) m.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Just a simple SFINAE trick to check CE presence when it's necessary
|
||||
// Stay tuned, we'll discuss this kind of tricks in our next lectures ;)
|
||||
template<typename T>
|
||||
decltype(unordered_map<T, T>().cbegin()->second = 0, int()) TestConstIteratorDoesntAllowModification(T) {
|
||||
assert(false);
|
||||
}
|
||||
template<typename... FakeArgs>
|
||||
void TestConstIteratorDoesntAllowModification(FakeArgs...) {}
|
||||
|
||||
|
||||
struct VerySpecialType {
|
||||
int x = 0;
|
||||
explicit VerySpecialType(int x): x(x) {}
|
||||
VerySpecialType(const VerySpecialType&) = delete;
|
||||
VerySpecialType& operator=(const VerySpecialType&) = delete;
|
||||
|
||||
VerySpecialType(VerySpecialType&&) = default;
|
||||
VerySpecialType& operator=(VerySpecialType&&) = default;
|
||||
};
|
||||
|
||||
struct NeitherDefaultNorCopyConstructible {
|
||||
VerySpecialType x;
|
||||
|
||||
NeitherDefaultNorCopyConstructible() = delete;
|
||||
NeitherDefaultNorCopyConstructible(const NeitherDefaultNorCopyConstructible&) = delete;
|
||||
NeitherDefaultNorCopyConstructible& operator=(const NeitherDefaultNorCopyConstructible&) = delete;
|
||||
|
||||
NeitherDefaultNorCopyConstructible(VerySpecialType&& x): x(std::move(x)) {}
|
||||
NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default;
|
||||
NeitherDefaultNorCopyConstructible& operator=(NeitherDefaultNorCopyConstructible&&) = default;
|
||||
|
||||
bool operator==(const NeitherDefaultNorCopyConstructible& other) const {
|
||||
return x.x == other.x.x;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<NeitherDefaultNorCopyConstructible> {
|
||||
size_t operator()(const NeitherDefaultNorCopyConstructible& x) const {
|
||||
return std::hash<int>()(x.x.x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void TestNoRedundantCopies() {
|
||||
unordered_map<NeitherDefaultNorCopyConstructible, NeitherDefaultNorCopyConstructible> m;
|
||||
|
||||
m.reserve(10);
|
||||
|
||||
m.emplace(VerySpecialType(0), VerySpecialType(0));
|
||||
|
||||
m.reserve(1'000'000);
|
||||
|
||||
std::pair<NeitherDefaultNorCopyConstructible, NeitherDefaultNorCopyConstructible> p{VerySpecialType(1), VerySpecialType(1)};
|
||||
|
||||
m.insert(std::move(p));
|
||||
|
||||
assert(m.size() == 2);
|
||||
|
||||
// this shouldn't compile
|
||||
// m[VerySpecialType(0)] = VerySpecialType(1);
|
||||
|
||||
// but this should
|
||||
m.at(VerySpecialType(1)) = VerySpecialType(0);
|
||||
|
||||
{
|
||||
auto mm = std::move(m);
|
||||
m = std::move(mm);
|
||||
}
|
||||
|
||||
m.erase(m.begin());
|
||||
m.erase(m.begin());
|
||||
assert(m.size() == 0);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct MyHash {
|
||||
size_t operator()(const T& p) const {
|
||||
return std::hash<int>()(p.second / p.first);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct MyEqual {
|
||||
bool operator()(const T& x, const T& y) const {
|
||||
return y.second / y.first == x.second / x.first;
|
||||
}
|
||||
};
|
||||
|
||||
struct OneMoreStrangeStruct {
|
||||
int first;
|
||||
int second;
|
||||
};
|
||||
|
||||
bool operator==(const OneMoreStrangeStruct&, const OneMoreStrangeStruct&) = delete;
|
||||
|
||||
|
||||
void TestCustomHashAndCompare() {
|
||||
unordered_map<std::pair<int, int>, char, MyHash<std::pair<int, int>>,
|
||||
MyEqual<std::pair<int, int>>> m;
|
||||
|
||||
m.insert({{1, 2}, 0});
|
||||
m.insert({{2, 4}, 1});
|
||||
assert(m.size() == 1);
|
||||
|
||||
m[{3, 6}] = 3;
|
||||
assert(m.at({4, 8}) == 3);
|
||||
|
||||
unordered_map<OneMoreStrangeStruct, int, MyHash<OneMoreStrangeStruct>, MyEqual<OneMoreStrangeStruct>> mm;
|
||||
mm[{1, 2}] = 3;
|
||||
assert(mm.at({5, 10}) == 3);
|
||||
|
||||
mm.emplace(OneMoreStrangeStruct{3, 9}, 2);
|
||||
assert(mm.size() == 2);
|
||||
mm.reserve(1'000);
|
||||
mm.erase(mm.begin());
|
||||
mm.erase(mm.begin());
|
||||
for (int k = 1; k < 100; ++k) {
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
mm.insert({{i, k*i}, 0});
|
||||
}
|
||||
}
|
||||
std::string ans;
|
||||
std::string myans;
|
||||
for (auto it = mm.cbegin(); it != mm.cend(); ++it) {
|
||||
ans += std::to_string(it->second);
|
||||
myans += '0';
|
||||
}
|
||||
assert(ans == myans);
|
||||
}
|
||||
|
||||
|
||||
// Finally, some tricky fixtures to test custom allocator.
|
||||
// Done by professional, don't try to repeat
|
||||
class Chaste {
|
||||
private:
|
||||
int x = 0;
|
||||
Chaste() = default;
|
||||
Chaste(int x): x(x) {}
|
||||
|
||||
// Nobody can construct me except this guy
|
||||
template<typename T>
|
||||
friend class TheChosenOne;
|
||||
|
||||
public:
|
||||
Chaste(const Chaste&) = default;
|
||||
Chaste(Chaste&&) = default;
|
||||
|
||||
bool operator==(const Chaste& other) const {
|
||||
return x == other.x;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Chaste> {
|
||||
size_t operator()(const Chaste& x) const {
|
||||
return std::hash<int>()(reinterpret_cast<const int&>(x));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct TheChosenOne: public std::allocator<T> {
|
||||
TheChosenOne() {}
|
||||
|
||||
template<typename U>
|
||||
TheChosenOne(const TheChosenOne<U>&) {}
|
||||
|
||||
template<typename... Args>
|
||||
void construct(T* p, Args&&... args) const {
|
||||
new(p) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy(T* p) const {
|
||||
p->~T();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = TheChosenOne<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TheChosenOne<std::pair<const Chaste, Chaste>>
|
||||
: public std::allocator<std::pair<const Chaste, Chaste>> {
|
||||
using PairType = std::pair<const Chaste, Chaste>;
|
||||
|
||||
TheChosenOne() {}
|
||||
|
||||
template<typename U>
|
||||
TheChosenOne(const TheChosenOne<U>&) {}
|
||||
|
||||
void construct(PairType* p, int a, int b) const {
|
||||
new(p) PairType(Chaste(a), Chaste(b));
|
||||
}
|
||||
|
||||
void destroy(PairType* p) const {
|
||||
p->~PairType();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = TheChosenOne<U>;
|
||||
};
|
||||
};
|
||||
|
||||
void TestCustomAlloc() {
|
||||
// This container mustn't construct or destroy any objects without using TheChosenOne allocator
|
||||
unordered_map<Chaste, Chaste, std::hash<Chaste>, std::equal_to<Chaste>,
|
||||
TheChosenOne<std::pair<const Chaste, Chaste>>> m;
|
||||
|
||||
m.emplace(0, 0);
|
||||
|
||||
{
|
||||
auto mm = m;
|
||||
mm.reserve(1'000);
|
||||
mm.erase(mm.begin());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1'000'000; ++i) {
|
||||
m.emplace(i, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 500'000; ++i) {
|
||||
auto it = m.begin();
|
||||
++it, ++it;
|
||||
m.erase(m.begin(), it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
// List<NeitherDefaultNorCopyConstructible> lst;
|
||||
// NeitherDefaultNorCopyConstructible p = {VerySpecialType(1)};
|
||||
// lst.insert(lst.end(), std::move(p));
|
||||
// return 0;
|
||||
|
||||
SimpleTest();
|
||||
TestIterators();
|
||||
TestConstIteratorDoesntAllowModification(0);
|
||||
TestNoRedundantCopies();
|
||||
TestCustomHashAndCompare();
|
||||
TestCustomAlloc();
|
||||
|
||||
}
|
5
map/makefile
Executable file
5
map/makefile
Executable file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
571
map/unordered_map.h
Normal file
571
map/unordered_map.h
Normal file
|
@ -0,0 +1,571 @@
|
|||
#include <utility>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using std::prev;
|
||||
|
||||
template<typename T, typename Allocator=std::allocator<T>>
|
||||
class List {
|
||||
private:
|
||||
class Node;
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator;
|
||||
public:
|
||||
using iterator = base_iterator<false>;
|
||||
using const_iterator = base_iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
explicit List(const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||||
AllocateEnd();
|
||||
}
|
||||
|
||||
List(size_t count, const T& value, const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||||
AllocateEnd();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
alloc_traits_::construct(allocator_node_, tmp, value);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(size_t count, const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||||
AllocateEnd();
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
alloc_traits_::construct(allocator_node_, tmp);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = count;
|
||||
}
|
||||
|
||||
List(const List& another) {
|
||||
allocator_node_ = alloc_traits_::select_on_container_copy_construction(another.allocator_node_);
|
||||
AllocateEnd();
|
||||
for (const T& i: another) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
alloc_traits_::construct(allocator_node_, tmp, i);
|
||||
Link(end_->l, tmp);
|
||||
}
|
||||
size_ = another.size();
|
||||
}
|
||||
|
||||
List(List&& another) : end_(std::move(another.end_)), size_(std::move(another.size_)) {
|
||||
another.end_ = nullptr;
|
||||
}
|
||||
|
||||
List& operator=(const List& another) {
|
||||
if (this == &another) return *this;
|
||||
if (alloc_traits_::propagate_on_container_copy_assignment::value && allocator_node_ != another.allocator_node_) {
|
||||
allocator_node_ = another.allocator_node_;
|
||||
}
|
||||
|
||||
auto it1 = begin();
|
||||
auto it2 = another.begin();
|
||||
for (size_t i = 0; i < std::min(another.size(), size()); ++i) {
|
||||
*it1 = *it2;
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
if (another.size() > size()) {
|
||||
while (size() != another.size()) {
|
||||
push_back(*it2);
|
||||
++it2;
|
||||
}
|
||||
} else {
|
||||
while (size() != another.size()) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
List& operator=(List&& another) {
|
||||
auto tmp(std::move(another));
|
||||
std::swap(end_, tmp.end_);
|
||||
std::swap(size_, tmp.size_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~List() {
|
||||
if (end_) {
|
||||
while (end_ != end_->r) {
|
||||
erase(iterator(end_->r));
|
||||
}
|
||||
allocator_node_.deallocate(end_, 1); // TODO
|
||||
}
|
||||
}
|
||||
|
||||
Allocator get_allocator() {
|
||||
return allocator_node_;
|
||||
}
|
||||
|
||||
void push_back(const T& value) {
|
||||
insert(end(), value);
|
||||
}
|
||||
|
||||
void push_back(T&& value) {
|
||||
insert(end(), std::move(value));
|
||||
}
|
||||
|
||||
void push_front(const T& value) {
|
||||
insert(begin(), value);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
erase(std::prev(end()));
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
erase(begin());
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator emplace_back(Args&&... args) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
tmp->l = nullptr;
|
||||
tmp->r = nullptr;
|
||||
|
||||
Allocator tmp1 = allocator_node_;
|
||||
std::allocator_traits<Allocator>::construct(tmp1, &tmp->value, std::forward<Args>(args)...);
|
||||
|
||||
Link(std::prev(end()).it, tmp);
|
||||
++size_;
|
||||
return iterator(tmp);
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
iterator begin() { return iterator(end_->r); }
|
||||
iterator end() { return iterator(end_); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(end_->r); }
|
||||
const_iterator end() const { return const_iterator(end_); }
|
||||
|
||||
const_iterator cbegin() const { return const_iterator(end_->r); }
|
||||
const_iterator cend() const { return const_iterator(end_); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
iterator insert(const_iterator it, const T& value) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
tmp->l = nullptr;
|
||||
tmp->r = nullptr;
|
||||
std::allocator_traits<decltype(allocator_node_)>::construct(allocator_node_, tmp, value);
|
||||
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||||
++size_;
|
||||
return iterator(tmp);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator it, T&& value) {
|
||||
Node* tmp = allocator_node_.allocate(1);
|
||||
tmp->l = nullptr;
|
||||
tmp->r = nullptr;
|
||||
|
||||
alloc_traits_::construct(allocator_node_, tmp, std::move(value));
|
||||
|
||||
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||||
++size_;
|
||||
return iterator(tmp);
|
||||
}
|
||||
|
||||
void erase(const_iterator it) {
|
||||
Node* tmp = const_cast<Node*>(it.it);
|
||||
tmp->l->r = tmp->r;
|
||||
tmp->r->l = tmp->l;
|
||||
std::allocator_traits<decltype(allocator_node_)>::destroy(allocator_node_, tmp);
|
||||
allocator_node_.deallocate(tmp, 1);
|
||||
--size_;
|
||||
}
|
||||
|
||||
void relink(iterator a, iterator b) {
|
||||
Link(a.it, b.it);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AllocateEnd() {
|
||||
end_ = allocator_node_.allocate(1);
|
||||
end_->l = end_;
|
||||
end_->r = end_;
|
||||
}
|
||||
|
||||
void Link(Node* l, Node* x) {
|
||||
Node* r = l->r;
|
||||
if (r == x)
|
||||
return;
|
||||
auto xl = x->l, xr = x->r;
|
||||
if (xl)
|
||||
xl->r = xr;
|
||||
if (xr)
|
||||
xr->l = xl;
|
||||
x->l = l;
|
||||
x->r = r;
|
||||
l->r = x;
|
||||
r->l = x;
|
||||
}
|
||||
|
||||
template<bool is_const>
|
||||
class base_iterator {
|
||||
friend List;
|
||||
std::conditional_t<is_const, const Node*, Node*> it;
|
||||
using U = std::conditional_t<is_const, const T, T>;
|
||||
public:
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = U;
|
||||
using pointer = U*;
|
||||
using reference = U&;
|
||||
using difference_type = int;
|
||||
|
||||
base_iterator() : it(nullptr) {}
|
||||
base_iterator(Node* v) { it = v; }
|
||||
|
||||
base_iterator(const base_iterator&) = default;
|
||||
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||||
|
||||
U& operator*() const { return it->value; }
|
||||
U* operator->() const { return &it->value; }
|
||||
base_iterator& operator++()& {
|
||||
it = it->r;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator++(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->r;
|
||||
return tmp;
|
||||
}
|
||||
base_iterator& operator--()& {
|
||||
it = it->l;
|
||||
return *this;
|
||||
}
|
||||
base_iterator operator--(int)& {
|
||||
base_iterator tmp = *this;
|
||||
it = it->l;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator==(base_iterator first, base_iterator second) { return first.it == second.it; }
|
||||
|
||||
friend bool operator!=(base_iterator first, base_iterator second) { return first.it != second.it; }
|
||||
|
||||
base_iterator& operator=(const base_iterator<false>& another) {
|
||||
it = another.it;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Node* l = nullptr;
|
||||
Node* r = nullptr;
|
||||
T value;
|
||||
Node() = default;
|
||||
Node(const T& value) : value(value) {}
|
||||
Node(T&& value) : value(std::move(value)) {}
|
||||
Node& operator=(const Node& another) { value = another.value; }
|
||||
};
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<Node> allocator_node_;
|
||||
using alloc_traits_ = std::allocator_traits<decltype(allocator_node_)>;
|
||||
Node* end_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Equal = std::equal_to<Key>, typename Alloc = std::allocator<std::pair<const Key, Value>>>
|
||||
class UnorderedMap {
|
||||
private:
|
||||
using NonConstNodeType = std::pair<Key, Value>;
|
||||
using ListNonConstNodeType = List<NonConstNodeType, typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<Key, Value>>>;
|
||||
|
||||
struct Node {
|
||||
std::pair<Key, Value> pair;
|
||||
size_t hash;
|
||||
Node() {}
|
||||
Node(const Node& another) : pair(another.pair), hash(another.hash) {}
|
||||
Node(Node&& another) : pair(std::move(another.pair)), hash(another.hash) { another.hash = 0; }
|
||||
Node(Key&& key, Value&& value, const Hash& hash_) : pair(std::make_pair(std::move(key), std::move(value))) {
|
||||
hash = hash_(key);
|
||||
}
|
||||
Node(const Key& key, const Value& value, const Hash& hash_) : pair(std::make_pair(key, value)){
|
||||
hash = hash_(key);
|
||||
}
|
||||
Node(std::pair<Key, Value>&& pair_, const Hash& hash_) : pair(std::move(pair_)) {
|
||||
hash = hash_(pair.first);
|
||||
}
|
||||
Node(const std::pair<Key, Value>& pair_, const Hash& hash_) : pair(pair_) {
|
||||
hash = hash_(pair.first);
|
||||
}
|
||||
};
|
||||
|
||||
using NewList = List<Node, typename std::allocator_traits<Alloc>::template rebind_alloc<Node>>;
|
||||
|
||||
NewList new_all_pairs;
|
||||
std::vector<typename NewList::iterator> new_hash_table;
|
||||
|
||||
size_t GetBucketIdx(const Key& key) {
|
||||
return hash_(key) % new_hash_table.size();
|
||||
}
|
||||
size_t GetBucketIdx(const Node& node) {
|
||||
return node.hash % new_hash_table.size();
|
||||
}
|
||||
public:
|
||||
using new_iterator = typename NewList::iterator;
|
||||
|
||||
template<bool is_const>
|
||||
struct base_iterator {
|
||||
using value_type = std::conditional_t<is_const, const std::pair<const Key, Value>, std::pair<const Key, Value>>;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
using difference_type = int;
|
||||
|
||||
new_iterator it;
|
||||
base_iterator() {}
|
||||
base_iterator(const base_iterator&) = default;
|
||||
base_iterator(const new_iterator& another) : it(another) {}
|
||||
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||||
base_iterator& operator=(const base_iterator&) = default;
|
||||
reference operator*() { return *reinterpret_cast<std::pair<const Key, Value>*>(&it->pair); }
|
||||
pointer operator->() { return reinterpret_cast<std::pair<const Key, Value>*>(&it->pair); }
|
||||
base_iterator& operator++()& { ++it; return *this; }
|
||||
base_iterator operator++(int)& { base_iterator tmp(*this); ++it; return tmp; }
|
||||
base_iterator& operator--()& { --it; return *this; }
|
||||
base_iterator operator--(int)& { base_iterator tmp(*this); --it; return tmp; }
|
||||
bool operator==(const base_iterator& another) { return it == another.it; }
|
||||
bool operator!=(const base_iterator& another) { return it != another.it; }
|
||||
};
|
||||
using iterator = base_iterator<false>;
|
||||
using const_iterator = base_iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using NodeType = std::pair<const Key, Value>;
|
||||
|
||||
UnorderedMap() {
|
||||
new_hash_table.resize(1000, new_all_pairs.end());
|
||||
}
|
||||
|
||||
UnorderedMap(const UnorderedMap& another) : new_all_pairs(another.new_all_pairs), hash_(another.hash_), equal_(another.equal_) {
|
||||
new_hash_table.resize(another.new_hash_table.size(), new_all_pairs.end());
|
||||
new_hash_table[GetBucketIdx(*new_all_pairs.begin())] = new_all_pairs.begin();
|
||||
|
||||
for (auto it = std::next(new_all_pairs.begin()); it != new_all_pairs.end(); ++it) {
|
||||
if (GetBucketIdx(*it) != GetBucketIdx(*prev(it)))
|
||||
new_hash_table[GetBucketIdx(*it)] = it;
|
||||
}
|
||||
}
|
||||
|
||||
UnorderedMap(UnorderedMap&&) = default;
|
||||
|
||||
UnorderedMap& operator=(const UnorderedMap& another) {
|
||||
this->all_pairs = another.all_pairs;
|
||||
this->hash_table_ = another.hash_table_;
|
||||
this->hash_ = another.hash_;
|
||||
this->equal_ = another.equal_;
|
||||
this->max_load_factor_ = another.max_load_factor_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnorderedMap& operator=(UnorderedMap&&) = default;
|
||||
|
||||
size_t size() const {
|
||||
return new_all_pairs.size();
|
||||
}
|
||||
|
||||
Value& at(const Key& key) {
|
||||
auto it = find(key);
|
||||
|
||||
if (it == end()) {
|
||||
throw std::out_of_range("uhaha");
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
Value& operator[](const Key& key) {
|
||||
return insert(std::make_pair(key, Value())).first->second;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
if (new_hash_table.size() < new_all_pairs.size())
|
||||
rehash(new_hash_table.size() * 1.8);
|
||||
|
||||
new_all_pairs.emplace_back(std::forward<Args>(args)..., hash_);
|
||||
auto& node = *std::prev(new_all_pairs.end());
|
||||
auto& key = node.pair.first;
|
||||
size_t hash = GetBucketIdx(node);
|
||||
|
||||
if (new_hash_table[hash] != new_all_pairs.end()) {
|
||||
auto i = new_hash_table[hash];
|
||||
while (i != prev(new_all_pairs.end()) && GetBucketIdx(*i) == hash) {
|
||||
if (equal_(i->pair.first, key)) break;
|
||||
++i;
|
||||
}
|
||||
if (i != std::prev(new_all_pairs.end()) && equal_(i->pair.first, key)) {
|
||||
new_all_pairs.pop_back();
|
||||
return {i, false};
|
||||
} else {
|
||||
auto it = std::prev(new_all_pairs.end());
|
||||
new_all_pairs.relink(std::prev(i), it);
|
||||
return {it, true};
|
||||
}
|
||||
} else {
|
||||
new_hash_table[hash] = prev(new_all_pairs.end());
|
||||
return {new_hash_table[hash], true};
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::pair<Key, Value>& tmp) {
|
||||
return emplace(tmp);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(std::pair<Key, Value>&& tmp) {
|
||||
return emplace(std::move(tmp));
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end) {
|
||||
for (; begin != end; ++begin) {
|
||||
insert(*begin);
|
||||
}
|
||||
}
|
||||
|
||||
iterator erase(const Key& key) {
|
||||
return erase(find(key));
|
||||
}
|
||||
|
||||
iterator erase(iterator it1) {
|
||||
auto it = it1.it;
|
||||
size_t hash = GetBucketIdx(*it);
|
||||
|
||||
if (new_hash_table[hash] != it) {
|
||||
auto i = std::next(it);
|
||||
new_all_pairs.erase(it);
|
||||
return i;
|
||||
} else {
|
||||
if (GetBucketIdx(*std::next(it)) == hash) {
|
||||
auto i = std::next(it);
|
||||
new_all_pairs.erase(it);
|
||||
new_hash_table[hash] = i;
|
||||
return i;
|
||||
} else {
|
||||
auto i = std::next(it);
|
||||
new_all_pairs.erase(it);
|
||||
new_hash_table[hash] = new_all_pairs.end();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterator erase(iterator begin, iterator end) {
|
||||
for (; begin != end;) {
|
||||
begin = erase(begin);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
const_iterator erase(const_iterator begin, const_iterator end) {
|
||||
for (; begin != end; ++begin) {
|
||||
erase(begin);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
iterator find(const Key& key) {
|
||||
size_t hash = GetBucketIdx(key);
|
||||
if (new_hash_table[hash] != new_all_pairs.end()) {
|
||||
auto i = new_hash_table[hash];
|
||||
while (i != new_all_pairs.end() && GetBucketIdx(*i) == hash) {
|
||||
if (equal_(i->pair.first, key)) break;
|
||||
++i;
|
||||
}
|
||||
if (i != new_all_pairs.end() && equal_(i->pair.first, key)) {
|
||||
return i;
|
||||
} else {
|
||||
return end();
|
||||
}
|
||||
} else {
|
||||
return end();
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator find(const Key& key) const {
|
||||
return const_cast<UnorderedMap&>(*this).find(key);
|
||||
}
|
||||
|
||||
void rehash(size_t count) {
|
||||
if (count <= new_hash_table.size())
|
||||
return;
|
||||
std::vector<new_iterator> v(count, new_all_pairs.end());
|
||||
|
||||
for (auto it = begin_(); it != end_(); ++it) {
|
||||
auto& i = *it;
|
||||
size_t hash = hash_(i.pair.first);
|
||||
if (v[hash % v.size()] != new_all_pairs.end()) {
|
||||
new_all_pairs.relink(v[hash % v.size()], it);
|
||||
} else {
|
||||
v[hash % v.size()] = it;
|
||||
}
|
||||
}
|
||||
std::swap(new_hash_table, v);
|
||||
}
|
||||
|
||||
void reserve(size_t count) {
|
||||
rehash(count);
|
||||
}
|
||||
|
||||
float load_factor() const {
|
||||
return static_cast<float>(size()) / new_hash_table.size();
|
||||
}
|
||||
|
||||
float max_load_factor() const {
|
||||
return max_load_factor_;
|
||||
}
|
||||
|
||||
void max_load_factor(float ml) {
|
||||
max_load_factor_ = ml;
|
||||
}
|
||||
|
||||
iterator begin() { return new_all_pairs.begin(); }
|
||||
iterator end() { return new_all_pairs.end(); }
|
||||
|
||||
new_iterator begin_() { return new_all_pairs.begin(); }
|
||||
new_iterator end_() { return new_all_pairs.end(); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(const_cast<NewList&>(new_all_pairs).begin()); }
|
||||
const_iterator end() const { return const_iterator(const_cast<NewList&>(new_all_pairs).end()); }
|
||||
|
||||
const_iterator cbegin() const { return const_iterator(const_cast<NewList&>(new_all_pairs).begin()); }
|
||||
const_iterator cend() const { return const_iterator(const_cast<NewList&>(new_all_pairs).end()); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||||
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||||
|
||||
private:
|
||||
Hash hash_;
|
||||
Equal equal_;
|
||||
|
||||
float max_load_factor_;
|
||||
};
|
49
matrix/main.cpp
Normal file
49
matrix/main.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include <bits/stdc++.h>
|
||||
#include "matrix.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
BigInteger d = 1000000;
|
||||
d *= d;
|
||||
d /= 132;
|
||||
cout << d << endl;
|
||||
|
||||
Matrix<20, 20, Rational> a;
|
||||
|
||||
for (size_t i = 0; i < a.getRow(0).size(); ++i) {
|
||||
for (size_t j = 0; j < a.getColumn(0).size(); ++j) {
|
||||
cin >> a[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < a.getRow(0).size(); ++i) {
|
||||
for (size_t j = 0; j < a.getColumn(0).size(); ++j) {
|
||||
cout << setprecision(20) << (double)a[i][j] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
cout << "START INVERTED" << endl;
|
||||
auto s = a.inverted();
|
||||
cout << "END INVERTED" << endl;
|
||||
|
||||
Matrix<20, 20, double> result;
|
||||
|
||||
for (size_t i = 0; i < a.getRow(0).size(); ++i) {
|
||||
for (size_t j = 0; j < a.getColumn(0).size(); ++j) {
|
||||
cin >> result[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < s.getRow(0).size(); ++i) {
|
||||
for (size_t j = 0; j < s.getColumn(0).size(); ++j) {
|
||||
if (abs((double)s[i][j] - result[i][j]) < 1e-6) continue;
|
||||
|
||||
cout << setprecision(8) << (double)s[i][j] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
5
matrix/makefile
Executable file
5
matrix/makefile
Executable file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
1286
matrix/matrix.h
Normal file
1286
matrix/matrix.h
Normal file
File diff suppressed because it is too large
Load diff
968
matrix/matrix2.h
Normal file
968
matrix/matrix2.h
Normal file
|
@ -0,0 +1,968 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
/*
|
||||
* TODO: assert and invert
|
||||
* порядок методов
|
||||
* проверка на N != M
|
||||
*/
|
||||
|
||||
class BigInteger{
|
||||
private:
|
||||
static const int BASE = 10;
|
||||
bool isPositive; //Zero is positive
|
||||
std::vector<short> digits;
|
||||
void normilize();
|
||||
void popBackZeros();
|
||||
void fixMinusZero();
|
||||
void reverseDigits();
|
||||
void addDigit(short);
|
||||
friend std::istream& operator>>(std::istream&, BigInteger&);
|
||||
public:
|
||||
BigInteger(int);
|
||||
BigInteger();
|
||||
BigInteger& operator+=(const BigInteger&);
|
||||
BigInteger operator+() const;
|
||||
BigInteger& operator++();
|
||||
BigInteger operator++(int);
|
||||
BigInteger& operator-=(const BigInteger&);
|
||||
BigInteger operator-() const;
|
||||
BigInteger& operator--();
|
||||
BigInteger operator--(int);
|
||||
BigInteger& operator*=(const BigInteger&);
|
||||
BigInteger& operator/=(const BigInteger&);
|
||||
BigInteger& operator%=(const BigInteger&);
|
||||
bool operator<(const BigInteger&) const;
|
||||
bool operator>(const BigInteger&) const;
|
||||
bool operator==(const BigInteger&) const;
|
||||
bool operator!=(const BigInteger&) const;
|
||||
bool operator<=(const BigInteger&) const;
|
||||
bool operator>=(const BigInteger&) const;
|
||||
explicit operator int() const;
|
||||
explicit operator bool() const;
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
|
||||
// if something weird in istream => return interpreted, istream is on the first weird symbol
|
||||
// also interpret number = sign as 0
|
||||
std::istream& operator>>(std::istream& in, BigInteger& num) {
|
||||
char c;
|
||||
bool signDefined = false;
|
||||
num.isPositive = true;
|
||||
num.digits.clear();
|
||||
while(in.get(c)) {
|
||||
if (c == '-' && !signDefined) {
|
||||
num.isPositive = false;
|
||||
signDefined = true;
|
||||
} else if (c == '+' && !signDefined) {
|
||||
num.isPositive = true;
|
||||
signDefined = true;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
num.digits.push_back(c - '0');
|
||||
signDefined = true;
|
||||
} else {
|
||||
if (c != ' ' && c != '\n')
|
||||
in.putback(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num.digits.size() == 0)
|
||||
num.digits.push_back(0);
|
||||
|
||||
num.reverseDigits();
|
||||
num.normilize();
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const BigInteger& num) {
|
||||
out << num.toString();
|
||||
return out;
|
||||
}
|
||||
|
||||
void BigInteger::normilize() {
|
||||
popBackZeros();
|
||||
fixMinusZero();
|
||||
}
|
||||
|
||||
void BigInteger::popBackZeros() {
|
||||
while(digits.size() > 1 && digits.back() == 0) {
|
||||
digits.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void BigInteger::fixMinusZero() {
|
||||
isPositive = !isPositive;
|
||||
if (*this == BigInteger(0))
|
||||
isPositive = !isPositive;
|
||||
isPositive = !isPositive;
|
||||
}
|
||||
|
||||
void BigInteger::reverseDigits() {
|
||||
int size = digits.size();
|
||||
for (int i = 0; i < size / 2; ++i) {
|
||||
std::swap(digits[i], digits[size-1-i]);
|
||||
}
|
||||
}
|
||||
|
||||
void BigInteger::addDigit(short digit) {
|
||||
reverseDigits();
|
||||
digits.push_back(digit);
|
||||
reverseDigits();
|
||||
}
|
||||
|
||||
BigInteger::BigInteger(int value) {
|
||||
isPositive = (value >= 0);
|
||||
if (!isPositive)
|
||||
value = -value;
|
||||
if (!value) digits.push_back(0);
|
||||
while (value) {
|
||||
digits.push_back(value % BASE);
|
||||
value /= BASE;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger::BigInteger() : BigInteger(0) {
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator+=(const BigInteger& num) {
|
||||
if (this == &num) {
|
||||
return *this += BigInteger(num);
|
||||
}
|
||||
if (num.isPositive == isPositive) {
|
||||
for (unsigned int i = 0; i < std::max(num.digits.size(), digits.size()); ++i) {
|
||||
short add = (i < num.digits.size() ? num.digits[i] : 0);
|
||||
if (digits.size() == i)
|
||||
digits.push_back(0);
|
||||
if (digits[i] + add >= BASE) {
|
||||
if (digits.size() == i + 1)
|
||||
digits.push_back(0);
|
||||
++digits[i + 1];
|
||||
} else if (i >= num.digits.size()) {
|
||||
digits[i] = (digits[i] + add) % BASE;
|
||||
break;
|
||||
}
|
||||
digits[i] = (digits[i] + add) % BASE;
|
||||
}
|
||||
} else {
|
||||
isPositive = !isPositive;
|
||||
*this -= num;
|
||||
isPositive = !isPositive;
|
||||
}
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger operator+(const BigInteger& num1, const BigInteger& num2) {
|
||||
BigInteger copy = num1;
|
||||
return copy += num2;
|
||||
}
|
||||
|
||||
BigInteger BigInteger::operator+() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator++() {
|
||||
return (*this += 1);
|
||||
}
|
||||
|
||||
BigInteger BigInteger::operator++(int) {
|
||||
BigInteger copy = *this;
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator-=(const BigInteger& num) {
|
||||
if (this == &num) {
|
||||
return *this -= BigInteger(num);
|
||||
}
|
||||
if (num.isPositive == isPositive) {
|
||||
bool needChange = isPositive ^ (num <= *this);
|
||||
const BigInteger& a = needChange ? num : *this;
|
||||
const BigInteger& b = needChange ? *this : num;
|
||||
bool need = false;
|
||||
for (unsigned int i = 0; i < a.digits.size(); ++i) {
|
||||
bool needNext = (a.digits[i] - need - (i < b.digits.size() ? b.digits[i] : 0) < 0);
|
||||
if(i == digits.size()) digits.push_back(0);
|
||||
digits[i] = (a.digits[i] - need - (i < b.digits.size() ? b.digits[i] : 0) + BASE) % BASE;
|
||||
need = needNext;
|
||||
}
|
||||
isPositive ^= needChange;
|
||||
} else {
|
||||
isPositive = !isPositive;
|
||||
*this += num;
|
||||
isPositive = !isPositive;
|
||||
}
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger operator-(const BigInteger& num1, const BigInteger& num2) {
|
||||
BigInteger copy = num1;
|
||||
return copy -= num2;
|
||||
}
|
||||
|
||||
BigInteger BigInteger::operator-() const {
|
||||
return BigInteger(0) -= *this;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator--() {
|
||||
return (*this -= 1);
|
||||
}
|
||||
|
||||
BigInteger BigInteger::operator--(int) {
|
||||
BigInteger copy = *this;
|
||||
--(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator*=(const BigInteger& num) {
|
||||
BigInteger ans;
|
||||
ans.digits.resize(digits.size() + num.digits.size());
|
||||
for(unsigned int i = 0; i < digits.size(); ++i) {
|
||||
int add = 0;
|
||||
for (unsigned int j = 0; j <= num.digits.size(); ++j) {
|
||||
short numDigit = (j == num.digits.size() ? 0 : num.digits[j]);
|
||||
short addNext = (ans.digits[i + j] + digits[i] * numDigit + add) / BASE;
|
||||
ans.digits[i + j] = (ans.digits[i + j] + digits[i] * numDigit + add) % BASE;
|
||||
add = addNext;
|
||||
}
|
||||
}
|
||||
ans.isPositive = !(isPositive ^ num.isPositive);
|
||||
ans.normilize();
|
||||
return *this = ans;
|
||||
}
|
||||
|
||||
BigInteger operator*(const BigInteger& num1, const BigInteger& num2) {
|
||||
BigInteger copy = num1;
|
||||
return copy *= num2;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator/=(const BigInteger& num) {
|
||||
if(num == 0)
|
||||
return *this;
|
||||
BigInteger ans, val(digits.back());
|
||||
ans.isPositive = !(isPositive ^ num.isPositive);
|
||||
int ptr = digits.size();
|
||||
ptr -= 2;
|
||||
val.isPositive = num.isPositive;
|
||||
while (ptr >= 0 && (num.isPositive ? val < num : num < val)) {
|
||||
val.addDigit(digits[ptr]);
|
||||
--ptr;
|
||||
}
|
||||
val.isPositive = true;
|
||||
for (; ptr >= -1; --ptr) {
|
||||
int number = 0;
|
||||
while (val >= 0) {
|
||||
num.isPositive ? val -= num : val += num;
|
||||
++number;
|
||||
}
|
||||
num.isPositive ? val += num : val -= num;
|
||||
--number;
|
||||
|
||||
ans.digits.push_back(number);
|
||||
if(ptr != -1)
|
||||
val.addDigit(digits[ptr]);
|
||||
}
|
||||
ans.reverseDigits();
|
||||
ans.normilize();
|
||||
return *this = ans;
|
||||
}
|
||||
|
||||
BigInteger operator/(const BigInteger& num1, const BigInteger& num2) {
|
||||
BigInteger copy = num1;
|
||||
return copy /= num2;
|
||||
}
|
||||
|
||||
BigInteger& BigInteger::operator%=(const BigInteger& num) {
|
||||
if(num == 0)
|
||||
return *this;
|
||||
BigInteger copy = (*this) / num;
|
||||
return *this -= (copy *= num);
|
||||
}
|
||||
|
||||
BigInteger operator%(const BigInteger& num1, const BigInteger& num2) {
|
||||
BigInteger copy = num1;
|
||||
return copy %= num2;
|
||||
}
|
||||
|
||||
bool BigInteger::operator<(const BigInteger& num) const {
|
||||
if (isPositive != num.isPositive)
|
||||
return num.isPositive;
|
||||
for (int j = std::max(static_cast<int>(digits.size()), static_cast<int>(num.digits.size())) - 1; j >= 0; --j) {
|
||||
unsigned int i = static_cast<unsigned int>(j);
|
||||
if ((i < digits.size() ? digits[i] : 0) < (i < num.digits.size() ? num.digits[i] : 0)) return isPositive;
|
||||
if ((i < digits.size() ? digits[i] : 0) > (i < num.digits.size() ? num.digits[i] : 0)) return !isPositive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BigInteger::operator>(const BigInteger& num) const {
|
||||
return num < *this;
|
||||
}
|
||||
|
||||
bool BigInteger::operator==(const BigInteger& num) const {
|
||||
return !(num < *this || *this < num);
|
||||
}
|
||||
|
||||
bool BigInteger::operator!=(const BigInteger& num) const {
|
||||
return num < *this || *this < num;
|
||||
}
|
||||
|
||||
bool BigInteger::operator<=(const BigInteger& num) const {
|
||||
return !(num < *this);
|
||||
}
|
||||
|
||||
bool BigInteger::operator>=(const BigInteger& num) const {
|
||||
return !(*this < num);
|
||||
}
|
||||
|
||||
std::string BigInteger::toString() const {
|
||||
std::string num = "";
|
||||
if (!isPositive) num += '-';
|
||||
for (int i = digits.size() - 1; i >= 0; --i) {
|
||||
num += ('0' + digits[i]);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
BigInteger::operator int() const {
|
||||
int value = 0;
|
||||
for (int i = digits.size() - 1; i >= 0; --i)
|
||||
value = value * 10 + digits[i];
|
||||
if(!isPositive)
|
||||
value = -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
BigInteger::operator bool() const {
|
||||
return int(*this);
|
||||
}
|
||||
|
||||
|
||||
class Rational{
|
||||
private:
|
||||
BigInteger numerator, denominator;
|
||||
void normilize();
|
||||
static BigInteger gcd(const BigInteger&, const BigInteger&);
|
||||
static void reverse(std::string&);
|
||||
public:
|
||||
Rational();
|
||||
Rational(const BigInteger&);
|
||||
Rational(const BigInteger& numerator, const BigInteger& denominator): numerator(numerator), denominator(denominator) {}
|
||||
Rational(int);
|
||||
Rational& operator+=(const Rational&);
|
||||
Rational operator+() const;
|
||||
Rational& operator-=(const Rational&);
|
||||
Rational operator-() const;
|
||||
Rational& operator*=(const Rational&);
|
||||
Rational& operator/=(const Rational&);
|
||||
bool operator<(const Rational&) const;
|
||||
bool operator>(const Rational&) const;
|
||||
bool operator==(const Rational&) const;
|
||||
bool operator!=(const Rational&) const;
|
||||
bool operator<=(const Rational&) const;
|
||||
bool operator>=(const Rational&) const;
|
||||
explicit operator double() const;
|
||||
std::string asDecimal(size_t) const;
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
BigInteger Rational::gcd(const BigInteger& a, const BigInteger& b) {
|
||||
if (b == 0)
|
||||
return a;
|
||||
return gcd(b, a % b);
|
||||
}
|
||||
|
||||
void Rational::normilize() {
|
||||
BigInteger _gcd = gcd(numerator, denominator);
|
||||
numerator /= _gcd;
|
||||
denominator /= _gcd;
|
||||
if (denominator < 0) {
|
||||
numerator *= -1;
|
||||
denominator *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Rational::reverse(std::string& s) {
|
||||
for(size_t i = 0; i < s.size()/2; ++i) {
|
||||
std::swap(s[i], s[s.size() - 1 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
Rational::Rational() : Rational(0) {
|
||||
}
|
||||
|
||||
Rational::Rational(const BigInteger& val) {
|
||||
numerator = val;
|
||||
denominator = 1;
|
||||
}
|
||||
|
||||
Rational::Rational(int val) {
|
||||
numerator = val;
|
||||
denominator = 1;
|
||||
}
|
||||
|
||||
Rational& Rational::operator+=(const Rational& frac) {
|
||||
if (this == &frac)
|
||||
return *this += Rational(frac);
|
||||
numerator *= frac.denominator;
|
||||
numerator += denominator * frac.numerator;
|
||||
denominator *= frac.denominator;
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator+(const Rational& frac1, const Rational& frac2) {
|
||||
Rational copy = frac1;
|
||||
return copy += frac2;
|
||||
}
|
||||
|
||||
Rational Rational::operator+() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational& Rational::operator-=(const Rational& frac) {
|
||||
if (this == &frac)
|
||||
return *this -= Rational(frac);
|
||||
numerator *= frac.denominator;
|
||||
numerator -= denominator * frac.numerator;
|
||||
denominator *= frac.denominator;
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator-(const Rational& frac1, const Rational& frac2) {
|
||||
Rational copy = frac1;
|
||||
return copy -= frac2;
|
||||
}
|
||||
|
||||
Rational Rational::operator-() const {
|
||||
return Rational(0) -= *this;
|
||||
}
|
||||
|
||||
Rational& Rational::operator*=(const Rational& frac) {
|
||||
if (this == &frac)
|
||||
return *this *= Rational(frac);
|
||||
numerator *= frac.numerator;
|
||||
denominator *= frac.denominator;
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator*(const Rational& frac1, const Rational& frac2) {
|
||||
Rational copy = frac1;
|
||||
return copy *= frac2;
|
||||
}
|
||||
|
||||
Rational& Rational::operator/=(const Rational& frac) {
|
||||
if (frac.numerator == 0)
|
||||
return *this;
|
||||
if (this == &frac)
|
||||
return *this /= Rational(frac);
|
||||
numerator *= frac.denominator;
|
||||
denominator *= frac.numerator;
|
||||
normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rational operator/(const Rational& frac1, const Rational& frac2) {
|
||||
Rational copy = frac1;
|
||||
return copy /= frac2;
|
||||
}
|
||||
|
||||
bool Rational::operator<(const Rational& frac) const {
|
||||
return numerator * frac.denominator < frac.numerator * denominator;
|
||||
}
|
||||
|
||||
bool Rational::operator>(const Rational& frac) const {
|
||||
return frac < *this;
|
||||
}
|
||||
|
||||
bool Rational::operator==(const Rational& frac) const {
|
||||
return !(frac < *this || *this < frac);
|
||||
}
|
||||
|
||||
bool Rational::operator!=(const Rational& frac) const {
|
||||
return frac < *this || *this < frac;
|
||||
}
|
||||
|
||||
bool Rational::operator<=(const Rational& frac) const {
|
||||
return !(frac < *this);
|
||||
}
|
||||
|
||||
bool Rational::operator>=(const Rational& frac) const {
|
||||
return !(*this < frac);
|
||||
}
|
||||
|
||||
Rational::operator double() const {
|
||||
return std::stod(asDecimal(200));
|
||||
}
|
||||
|
||||
std::string Rational::asDecimal(size_t precision = 0u) const {
|
||||
BigInteger ten = 1;
|
||||
for (size_t i = 0; i < precision; ++i) {
|
||||
ten *= 10;
|
||||
}
|
||||
BigInteger val = (numerator < 0 ? -1 : 1) * numerator * ten / denominator;
|
||||
std::string integer = (val / ten).toString();
|
||||
std::string frac = (val % ten).toString();
|
||||
reverse(frac);
|
||||
while(frac.size() < precision) frac += '0';
|
||||
frac += '.';
|
||||
reverse(frac);
|
||||
if(precision == 0u) frac = "";
|
||||
integer = (numerator < 0 ? "-" : "") + integer;
|
||||
return integer + frac;
|
||||
}
|
||||
|
||||
std::string Rational::toString() const {
|
||||
std::string num = numerator.toString();
|
||||
if (denominator != 1) {
|
||||
num += "/" + denominator.toString();
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Rational& value) {
|
||||
out << value.asDecimal(5);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, Rational& value) {
|
||||
BigInteger numerator(0), denominator(0);
|
||||
bool was = false;
|
||||
char c;
|
||||
while (in.get(c)) {
|
||||
if (c == ' ' || c == '\n')
|
||||
break;
|
||||
if (c == '/') {
|
||||
was = true;
|
||||
} else {
|
||||
if (!was) {
|
||||
numerator *= 10;
|
||||
numerator += (c - '0');
|
||||
} else {
|
||||
denominator *= 10;
|
||||
denominator += (c - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!was) {
|
||||
denominator = BigInteger(1);
|
||||
}
|
||||
value = Rational(numerator, denominator);
|
||||
return in;
|
||||
}
|
||||
|
||||
bool isPrime(int N) {
|
||||
for (long long i = 2; i * i <= N; ++i) {
|
||||
if (N % i == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int fastPow(int a, int b, int module) {
|
||||
if (b == 0)
|
||||
return 1;
|
||||
if (b % 2 == 0) {
|
||||
long long val = fastPow(a, b / 2, module);
|
||||
return (val * val) % module;
|
||||
} else {
|
||||
return (1ll * fastPow(a, b - 1, module) * a) % module;
|
||||
}
|
||||
}
|
||||
|
||||
int inverseValue(int value, int module) {
|
||||
return fastPow(value, module - 2, module);
|
||||
}
|
||||
|
||||
template<int N>
|
||||
class Finite {
|
||||
public:
|
||||
Finite<N>(int value = 0): value((value % N + N) % N), isPrimeN(isPrime(N)) {}
|
||||
Finite<N>& operator+=(const Finite<N>& number) {
|
||||
value = (value + number.value) % N;
|
||||
return *this;
|
||||
}
|
||||
Finite<N> operator+(const Finite<N>& number) const {
|
||||
Finite<N> copy = *this;
|
||||
return copy += number;
|
||||
}
|
||||
Finite<N>& operator-=(const Finite<N>& number) {
|
||||
value = (value - number.value + N) % N;
|
||||
return *this;
|
||||
}
|
||||
Finite<N> operator-(const Finite<N>& number) const {
|
||||
Finite<N> copy = *this;
|
||||
return copy -= number;
|
||||
}
|
||||
Finite<N> operator-() const {
|
||||
return Finite<N>(0 - value);
|
||||
}
|
||||
Finite<N>& operator*=(const Finite<N>& number) {
|
||||
value = (1ll * value * number.value) % N;
|
||||
return *this;
|
||||
}
|
||||
Finite<N> operator*(const Finite<N>& number) const {
|
||||
Finite<N> copy = *this;
|
||||
return copy *= number;
|
||||
}
|
||||
Finite<N>& operator/=(const Finite<N>& number) {
|
||||
assert(isPrimeN);
|
||||
value = (1ll * value * inverseValue(number.value, N)) % N;
|
||||
return *this;
|
||||
}
|
||||
Finite<N> operator/(const Finite<N>& number) const {
|
||||
Finite<N> copy = *this;
|
||||
return copy /= number;
|
||||
}
|
||||
bool operator==(const Finite<N>& number) const {
|
||||
return value == number.value;
|
||||
}
|
||||
bool operator!=(const Finite<N>& number) const {
|
||||
return value != number.value;
|
||||
}
|
||||
Finite<N>& operator++() {
|
||||
return (*this += 1);
|
||||
}
|
||||
Finite<N> operator++(int) {
|
||||
Finite<N> copy;
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
template<int U>
|
||||
friend std::ostream& operator<<(std::ostream&, const Finite<U>&);
|
||||
private:
|
||||
int value;
|
||||
bool isPrimeN;
|
||||
};
|
||||
|
||||
template<int N>
|
||||
std::ostream& operator<<(std::ostream& out, const Finite<N>& value){
|
||||
out << value.value;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<unsigned M, unsigned N, typename Field = Rational>
|
||||
class Matrix {
|
||||
public:
|
||||
Matrix<M, N, Field>();
|
||||
|
||||
template<typename T>
|
||||
Matrix(std::initializer_list<std::initializer_list<T>> a) {
|
||||
for (auto i: a) {
|
||||
table.push_back({});
|
||||
for (auto j: i) {
|
||||
table.back().push_back(Field(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
Matrix<M, N, Field>(std::vector<std::vector<Field>>&);
|
||||
Matrix<M, N, Field>(std::vector<std::vector<int>>&);
|
||||
template<unsigned M1, unsigned N1, typename Field1>
|
||||
bool operator==(const Matrix<M1, N1, Field1>&) const;
|
||||
template<unsigned M1, unsigned N1, typename Field1>
|
||||
bool operator!=(const Matrix<M1, N1, Field1>&) const;
|
||||
Matrix<M, N, Field>& operator+=(const Matrix<M, N, Field>&);
|
||||
Matrix<M, N, Field> operator+(const Matrix<M, N, Field>&) const;
|
||||
Matrix<M, N, Field>& operator-=(const Matrix<M, N, Field>&);
|
||||
Matrix<M, N, Field> operator-(const Matrix<M, N, Field>&) const;
|
||||
Matrix<M, N, Field> operator*(const Field&) const;
|
||||
Matrix<M, N, Field>& operator*=(const Matrix<M, N, Field>&);
|
||||
Field det() const;
|
||||
Matrix<N, M, Field> transposed() const;
|
||||
unsigned rank() const;
|
||||
Field trace() const;
|
||||
void invert();
|
||||
Matrix<N, M, Field> inverted() const;
|
||||
std::vector<Field> getRow(unsigned) const;
|
||||
std::vector<Field> getColumn(unsigned) const;
|
||||
std::vector<Field>& operator[](size_t index) {
|
||||
return table[index];
|
||||
}
|
||||
const std::vector<Field>& operator[](size_t index) const {
|
||||
return table[index];
|
||||
}
|
||||
template<unsigned N1, unsigned M1, typename Field1>
|
||||
friend class Matrix;
|
||||
private:
|
||||
std::vector<std::vector<Field>> table;
|
||||
void swapStrings(unsigned, unsigned);
|
||||
void divideString(unsigned, const Field);
|
||||
void subtractStrings(unsigned, unsigned, const Field);
|
||||
Field makeSimple();
|
||||
};
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>::Matrix() {
|
||||
if (M != N)
|
||||
assert(0);
|
||||
table.resize(M, std::vector<Field>(N));
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
table[i][i] = Field(1);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>::Matrix(std::vector<std::vector<Field>>& matrix): table(matrix) {}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>::Matrix(std::vector<std::vector<int>>& matrix) {
|
||||
table.resize(M, std::vector<Field>(N));
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j){
|
||||
table[i][j] = Field(matrix[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
template<unsigned int M1, unsigned int N1, typename Field1>
|
||||
bool Matrix<M, N, Field>::operator==(const Matrix<M1, N1, Field1>& matrix) const {
|
||||
if (!std::is_same<Matrix<M, N, Field>, Matrix<M1, N1, Field1>>::value)
|
||||
return false;
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
if (table[i][j] != matrix[i][j])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
template<unsigned int M1, unsigned int N1, typename Field1>
|
||||
bool Matrix<M, N, Field>::operator!=(const Matrix<M1, N1, Field1>& matrix) const {
|
||||
return !(*this == matrix);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>& Matrix<M, N, Field>::operator+=(const Matrix<M, N, Field>& matrix) {
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
table[i][j] += matrix[i][j];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field> Matrix<M, N, Field>::operator+(const Matrix<M, N, Field>& matrix) const {
|
||||
Matrix<M, N, Field> copy = *this;
|
||||
return copy += matrix;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>& Matrix<M, N, Field>::operator-=(const Matrix<M, N, Field>& matrix) {
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
table[i][j] -= matrix[i][j];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field> Matrix<M, N, Field>::operator-(const Matrix<M, N, Field>& matrix) const {
|
||||
Matrix<M, N, Field> copy = *this;
|
||||
return copy -= matrix;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field> Matrix<M, N, Field>::operator*(const Field& multiplier) const {
|
||||
Matrix<M, N, Field> copy = *this;
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
copy[i][j] *= multiplier;
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field> operator*(const Field& multiplier, const Matrix<M, N, Field> matrix) {
|
||||
return matrix * multiplier;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, unsigned int K, typename Field>
|
||||
Matrix<M, K, Field> operator*(const Matrix<M, N, Field>& matrix1, const Matrix<N, K, Field>& matrix2) {
|
||||
std::vector<std::vector<Field>> resultTable(M, std::vector<Field>(K));
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < K; ++j) {
|
||||
for (unsigned k = 0; k < N; ++k) {
|
||||
resultTable[i][j] += matrix1[i][k] * matrix2[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return Matrix<M, K, Field>(resultTable);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<M, N, Field>& Matrix<M, N, Field>::operator*=(const Matrix<M, N, Field>& matrix) {
|
||||
if (M != N)
|
||||
assert(0);
|
||||
return (*this = (*this) * matrix);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Field Matrix<M, N, Field>::det() const {
|
||||
if (N != M)
|
||||
assert(0);
|
||||
Matrix<M, N, Field> copy = *this;
|
||||
return copy.makeSimple();
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<N, M, Field> Matrix<M, N, Field>::transposed() const {
|
||||
std::vector<std::vector<Field>> result(N, std::vector<Field>(M));
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
for (unsigned j = 0; j < M; ++j) {
|
||||
result[i][j] = table[j][i];
|
||||
}
|
||||
}
|
||||
return Matrix<N, M, Field>(result);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
unsigned Matrix<M, N, Field>::rank() const {
|
||||
Matrix<M, N, Field> copy = *this;
|
||||
copy.makeSimple();
|
||||
unsigned rank = 0;
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
bool empty = true;
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
if (copy[i][j] != 0)
|
||||
empty = false;
|
||||
}
|
||||
rank += !empty;
|
||||
}
|
||||
return rank;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Field Matrix<M, N, Field>::trace() const {
|
||||
if (N != M)
|
||||
assert(0);
|
||||
Field result(0);
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
result += table[i][i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
void Matrix<M, N, Field>::invert() {
|
||||
if (M != N)
|
||||
assert(0);
|
||||
if (N > 5){
|
||||
std::cerr << "was:\n";
|
||||
for(unsigned i = 0; i < 10; ++i) {
|
||||
for(unsigned j = 0; j < 20; ++j){
|
||||
std::cerr << table[i][j] << " ";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
std::vector<std::vector<Field>> complexTable(N, std::vector<Field>(2 * N));
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
complexTable[i][j] = table[i][j];
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < N; ++i)
|
||||
complexTable[i][i + N] = Field(1);
|
||||
Matrix<N, 2 * N, Field> complexMatrix(complexTable);
|
||||
complexMatrix.makeSimple();
|
||||
std::vector<std::vector<Field>> result(N, std::vector<Field>(N));
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
for (unsigned j = 0; j < N; ++j) {
|
||||
result[i][j] = complexMatrix[i][j + N];
|
||||
}
|
||||
}
|
||||
if (N > 100){
|
||||
std::cerr << "now:\n";
|
||||
for(unsigned i = 0; i < M; ++i) {
|
||||
for(unsigned j = 0; j < N; ++j){
|
||||
std::cerr << result[i][j] << " ";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
*this = Matrix<N, M, Field>(result);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Matrix<N, M, Field> Matrix<M, N, Field>::inverted() const {
|
||||
Matrix<N, M, Field> matrix = *this;
|
||||
matrix.invert();
|
||||
return matrix;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
std::vector<Field> Matrix<M, N, Field>::getRow(unsigned int index) const {
|
||||
return table[index];
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
std::vector<Field> Matrix<M, N, Field>::getColumn(unsigned int index) const {
|
||||
std::vector<Field> result(M);
|
||||
for (unsigned i = 0; i < M; ++i) {
|
||||
result[i] = table[i][index];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
void Matrix<M, N, Field>::swapStrings(unsigned index1, unsigned index2) {
|
||||
std::swap(table[index1], table[index2]);
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
void Matrix<M, N, Field>::divideString(unsigned index, const Field divisor) {
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
table[index][i] /= divisor;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
void Matrix<M, N, Field>::subtractStrings(unsigned indexMinuend, unsigned indexSubtrahend, const Field multiplier) {
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
table[indexMinuend][i] -= table[indexSubtrahend][i] * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int M, unsigned int N, typename Field>
|
||||
Field Matrix<M, N, Field>::makeSimple() {
|
||||
Field determinant(1);
|
||||
unsigned string = 0;
|
||||
for (unsigned i = 0; i < N; ++i) {
|
||||
unsigned found = M + 1;
|
||||
for (unsigned j = string; j < M; ++j) {
|
||||
if (table[j][i] != Field(0)) {
|
||||
found = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == M + 1){
|
||||
determinant = 0;
|
||||
continue;
|
||||
}
|
||||
swapStrings(string, found);
|
||||
determinant *= table[string][i];
|
||||
divideString(string, table[string][i]);
|
||||
for (unsigned j = 0; j < M; ++j) {
|
||||
if (j != string)
|
||||
subtractStrings(j, string, table[j][i]);
|
||||
}
|
||||
++string;
|
||||
}
|
||||
return determinant;
|
||||
}
|
||||
|
||||
template <unsigned N, typename Field = Rational>
|
||||
using SquareMatrix = Matrix<N, N, Field>;
|
2
residue/Makefile
Normal file
2
residue/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
g++ main.cpp -std=c++14 -Wall -Wextra -o a
|
9
residue/main.cpp
Normal file
9
residue/main.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "residue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
Residue<2147483647> a(2147483644);
|
||||
Residue<2147483647> b(2147483645);
|
||||
cout << int(a * b) << endl;
|
||||
}
|
244
residue/residue.h
Normal file
244
residue/residue.h
Normal file
|
@ -0,0 +1,244 @@
|
|||
#include <vector>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
template<unsigned N, long long i>
|
||||
struct is_prime_ {
|
||||
static const bool value = !(N % i == 0) && is_prime_<N, (i + 1) * (i + 1) <= N ? (N % i == 0 ? 1 : i + 1) : -1>::value;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
struct is_prime_<N, 1LL> {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
struct is_prime_<N, -1LL> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
struct is_prime {
|
||||
static const bool value = (is_prime_<N, 2>::value && (N != 1)) || (N == 2);
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
static const bool is_prime_v = is_prime<N>::value;
|
||||
|
||||
template<unsigned N, unsigned long long i, bool find_divider>
|
||||
struct is_power_of_prime_ {
|
||||
static const bool value = is_power_of_prime_<N % i == 0 ? N / i : (find_divider ? 0 : (i * i >= N ? 1 : N)), N % i == 0 ? i : i + 2, N % i == 0>::value;
|
||||
};
|
||||
|
||||
template<unsigned long long i, bool find_divider>
|
||||
struct is_power_of_prime_<1, i, find_divider> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template<unsigned long long i, bool find_divider>
|
||||
struct is_power_of_prime_<0, i, find_divider> {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
struct is_power_of_prime {
|
||||
static const bool value = is_power_of_prime_<N, 3, false>::value;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
struct has_primitive_root {
|
||||
static const bool value = is_power_of_prime<(N == 4 || N == 2 ? 1 : (N % 4 == 0 ? 0 : (N % 2 == 0 ? N / 2 : N)))>::value;
|
||||
};
|
||||
|
||||
template<unsigned N>
|
||||
static const bool has_primitive_root_v = has_primitive_root<N>::value;
|
||||
|
||||
template<bool T>
|
||||
void my_static_assert() {
|
||||
int* a = new int [T ? 1 : -1];
|
||||
delete[] a;
|
||||
}
|
||||
|
||||
unsigned phi(unsigned n) {
|
||||
unsigned result = n;
|
||||
for (int i = 2; i * i <= n; ++i) {
|
||||
if (n % i == 0) {
|
||||
while (n % i == 0) {
|
||||
n /= i;
|
||||
}
|
||||
result -= result / i;
|
||||
}
|
||||
}
|
||||
if (n > 1)
|
||||
result -= result / n;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
class Residue {
|
||||
public:
|
||||
Residue() = default;
|
||||
Residue(const Residue<Modulus>& another) = default;
|
||||
explicit Residue(long long x) : x(x) { Normilize(); }
|
||||
explicit operator int() const { return x; }
|
||||
|
||||
bool operator==(const Residue<Modulus>& another) {
|
||||
return x == another.x;
|
||||
}
|
||||
|
||||
bool operator!=(const Residue<Modulus>& another) {
|
||||
return !(*this == another);
|
||||
}
|
||||
|
||||
Residue& operator+=(const Residue<Modulus>& another);
|
||||
Residue& operator-=(const Residue<Modulus>& another);
|
||||
Residue& operator*=(const Residue<Modulus>& another);
|
||||
Residue& operator/=(const Residue<Modulus>& another);
|
||||
|
||||
Residue getInverse() const {
|
||||
cerr << "getInverse " << x << " " << Modulus << endl;
|
||||
my_static_assert<is_prime_v<Modulus>>();
|
||||
return Residue(pow(x, Modulus - 2));
|
||||
}
|
||||
|
||||
unsigned order() const {
|
||||
cerr << "order " << x << " " << Modulus << endl;
|
||||
|
||||
unsigned phi = ::phi(Modulus);
|
||||
unsigned copy_phi = phi;
|
||||
std::vector<unsigned> first = {1}, second = {phi};
|
||||
for (unsigned i = 2; i * i <= phi; ++i) {
|
||||
if (copy_phi % i == 0) {
|
||||
first.push_back(i);
|
||||
second.push_back(phi / i);
|
||||
}
|
||||
}
|
||||
std::reverse(second.begin(), second.end());
|
||||
first.insert(first.end(), second.begin(), second.end());
|
||||
|
||||
for (unsigned i: first) {
|
||||
if (pow(i) == Residue(1)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Residue getPrimitiveRoot() {
|
||||
cerr << "getPrimitiveRoot " << Modulus << endl;
|
||||
my_static_assert<has_primitive_root_v<Modulus>>();
|
||||
if (Modulus == 2) {
|
||||
return Residue(1);
|
||||
} else if (Modulus == 4) {
|
||||
return Residue(3);
|
||||
}
|
||||
unsigned phi = ::phi(Modulus);
|
||||
unsigned copy_phi = phi;
|
||||
std::vector<unsigned> deviders;
|
||||
for (unsigned i = 2; i * i <= phi; ++i) {
|
||||
if (copy_phi % i == 0) {
|
||||
deviders.push_back(i);
|
||||
deviders.push_back(phi / i);
|
||||
while (copy_phi % i == 0) copy_phi /= i;
|
||||
}
|
||||
}
|
||||
if (copy_phi > 1)
|
||||
deviders.push_back(copy_phi);
|
||||
|
||||
for (unsigned res = 2; res < phi; ++res) {
|
||||
bool ok = pow(res, phi) == 1;
|
||||
for (unsigned i: deviders) {
|
||||
ok &= pow(res, i) != 1;
|
||||
}
|
||||
if (ok) {
|
||||
return Residue(res);
|
||||
}
|
||||
}
|
||||
return Residue(phi);
|
||||
}
|
||||
|
||||
Residue<Modulus> pow(unsigned degree) const;
|
||||
private:
|
||||
static int pow(int x, unsigned degree);
|
||||
|
||||
void Normilize() {
|
||||
x %= static_cast<long long>(Modulus);
|
||||
if (x < 0)
|
||||
x += Modulus;
|
||||
}
|
||||
long long x;
|
||||
};
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus>& Residue<Modulus>::operator+=(const Residue<Modulus>& another) {
|
||||
x += another.x;
|
||||
Normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus>& Residue<Modulus>::operator-=(const Residue<Modulus>& another) {
|
||||
x -= another.x;
|
||||
Normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus>& Residue<Modulus>::operator*=(const Residue<Modulus>& another) {
|
||||
x *= another.x;
|
||||
Normilize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus>& Residue<Modulus>::operator/=(const Residue<Modulus>& another) {
|
||||
*this *= another.getInverse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus> operator+(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||||
Residue<Modulus> tmp(a);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus> operator-(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||||
Residue<Modulus> tmp(a);
|
||||
tmp -= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus> operator*(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||||
Residue<Modulus> tmp(a);
|
||||
tmp *= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus> operator/(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||||
Residue<Modulus> tmp(a);
|
||||
tmp /= b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
Residue<Modulus> Residue<Modulus>::pow(unsigned degree) const {
|
||||
|
||||
return Residue(pow(x, degree));
|
||||
}
|
||||
|
||||
template<unsigned Modulus>
|
||||
int Residue<Modulus>::pow(int x, unsigned degree) {
|
||||
|
||||
if (degree == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
long long t = pow(x, degree / 2);
|
||||
return (((t * t) % static_cast<long long>(Modulus)) * (degree % 2 == 0 ? 1 : x)) % static_cast<long long>(Modulus);
|
||||
}
|
||||
}
|
11
shared_pointer/CMakeLists.txt
Normal file
11
shared_pointer/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project("List")
|
||||
|
||||
set(SOURCE_FILES main.cpp)
|
||||
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(map ${SOURCE_FILES})
|
600
shared_pointer/main.cpp
Normal file
600
shared_pointer/main.cpp
Normal file
|
@ -0,0 +1,600 @@
|
|||
#include <set>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "smart_pointers.h"
|
||||
|
||||
|
||||
|
||||
//template<typename T>
|
||||
//using SharedPtr = std::shared_ptr<T>;
|
||||
//
|
||||
//template<typename T>
|
||||
//using WeakPtr = std::weak_ptr<T>;
|
||||
//
|
||||
//#define makeShared std::make_shared
|
||||
//#define allocateShared std::allocate_shared
|
||||
//
|
||||
//template<typename T>
|
||||
//using EnableSharedFromThis = std::enable_shared_from_this<T>;
|
||||
|
||||
|
||||
struct Base {
|
||||
virtual ~Base() {}
|
||||
};
|
||||
|
||||
struct Derived: public Base {
|
||||
static int constructed;
|
||||
static int destructerd;
|
||||
|
||||
Derived() {
|
||||
++constructed;
|
||||
}
|
||||
~Derived() {
|
||||
++destructerd;
|
||||
}
|
||||
};
|
||||
int Derived::constructed = 0;
|
||||
int Derived::destructerd = 0;
|
||||
|
||||
void test_shared_ptr() {
|
||||
|
||||
using std::vector;
|
||||
|
||||
auto first_ptr = SharedPtr<vector<int>>(new vector<int>(1'000'000));
|
||||
auto second_ptr = SharedPtr<vector<int>>(new vector<int>(10));
|
||||
|
||||
first_ptr.reset(new vector<int>());
|
||||
second_ptr.reset();
|
||||
SharedPtr<vector<int>>().swap(first_ptr);
|
||||
|
||||
assert(second_ptr.get() == nullptr);
|
||||
assert(second_ptr.get() == nullptr);
|
||||
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
vector<SharedPtr<int>> ptrs;
|
||||
for (int i = 0; i < 100'000; ++i) {
|
||||
int* p = new int(rand() % 99'999);
|
||||
ptrs.push_back(SharedPtr<int>(p));
|
||||
}
|
||||
std::sort(ptrs.begin(), ptrs.end(), [](auto&& x, auto&& y){return *x < *y;});
|
||||
for (int i = 0; i + 1 < 100'000; ++i) {
|
||||
assert(*(ptrs[i]) <= *(ptrs[i+1]));
|
||||
}
|
||||
while (!ptrs.empty()) {
|
||||
ptrs.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// test const
|
||||
{
|
||||
const SharedPtr<int> sp(new int(42));
|
||||
assert(sp.use_count() == 1);
|
||||
assert(*sp.get() == 42);
|
||||
assert(*sp == 42);
|
||||
}
|
||||
}
|
||||
|
||||
struct Node;
|
||||
|
||||
struct Next {
|
||||
SharedPtr<Node> shared;
|
||||
WeakPtr<Node> weak;
|
||||
Next(const SharedPtr<Node>& shared): shared(shared) {}
|
||||
Next(const WeakPtr<Node>& weak): weak(weak) {}
|
||||
};
|
||||
|
||||
struct Node {
|
||||
static int constructed;
|
||||
static int destructed;
|
||||
|
||||
int value;
|
||||
Next next;
|
||||
Node(int value): value(value), next(SharedPtr<Node>()) {
|
||||
++constructed;
|
||||
}
|
||||
Node(int value, const SharedPtr<Node>& next): value(value), next(next) {
|
||||
++constructed;
|
||||
}
|
||||
Node(int value, const WeakPtr<Node>& next): value(value), next(next) {
|
||||
++constructed;
|
||||
}
|
||||
~Node() {
|
||||
++destructed;
|
||||
}
|
||||
};
|
||||
|
||||
int Node::constructed = 0;
|
||||
int Node::destructed = 0;
|
||||
|
||||
SharedPtr<Node> getCyclePtr(int cycleSize) {
|
||||
SharedPtr<Node> head(new Node(0));
|
||||
SharedPtr<Node> prev(head);
|
||||
for (int i = 1; i < cycleSize; ++i) {
|
||||
SharedPtr<Node> current(new Node(i));
|
||||
prev->next.shared = current;
|
||||
prev = current;
|
||||
// std::cout << prev.use_count() << '\n';
|
||||
}
|
||||
//prev->next.shared.~SharedPtr<Node>();
|
||||
//new (&prev->next.weak) WeakPtr<Node>(head);
|
||||
prev->next.weak = head;
|
||||
//prev->next.isLast = true;
|
||||
return head;
|
||||
}
|
||||
|
||||
int allocated = 0;
|
||||
int deallocated = 0;
|
||||
|
||||
int allocate_called = 0;
|
||||
int deallocate_called = 0;
|
||||
|
||||
int new_called = 0;
|
||||
int delete_called = 0;
|
||||
|
||||
void test_weak_ptr() {
|
||||
auto sp = SharedPtr<int>(new int(23));
|
||||
WeakPtr<int> weak = sp;
|
||||
{
|
||||
auto shared = SharedPtr<int>(new int(42));
|
||||
weak = shared;
|
||||
assert(weak.use_count() == 1);
|
||||
assert(!weak.expired());
|
||||
}
|
||||
assert(weak.use_count() == 0);
|
||||
assert(weak.expired());
|
||||
|
||||
weak = sp;
|
||||
auto wp = weak;
|
||||
assert(weak.use_count() == 1);
|
||||
assert(wp.use_count() == 1);
|
||||
auto wwp = std::move(weak);
|
||||
//assert(weak.use_count() == 0);
|
||||
assert(wwp.use_count() == 1);
|
||||
|
||||
auto ssp = wwp.lock();
|
||||
assert(sp.use_count() == 2);
|
||||
|
||||
sp = ssp;
|
||||
ssp = sp;
|
||||
assert(ssp.use_count() == 2);
|
||||
|
||||
sp = std::move(ssp);
|
||||
assert(sp.use_count() == 1);
|
||||
|
||||
ssp.reset(); // nothing should happen
|
||||
sp.reset();
|
||||
|
||||
unsigned int useless_value = 0;
|
||||
for (int i = 0; i < 100'000; ++i) {
|
||||
SharedPtr<Node> head = getCyclePtr(8);
|
||||
SharedPtr<Node> nextHead = head->next.shared;
|
||||
assert(nextHead.use_count() == 2);
|
||||
useless_value += 19'937 * i * nextHead.use_count();
|
||||
|
||||
head.reset();
|
||||
assert(nextHead.use_count() == 1);
|
||||
}
|
||||
|
||||
assert(Node::constructed == 800'000);
|
||||
assert(Node::destructed == 800'000);
|
||||
|
||||
// test inheritance
|
||||
{
|
||||
SharedPtr<Derived> dsp(new Derived());
|
||||
|
||||
SharedPtr<Base> bsp = dsp;
|
||||
|
||||
WeakPtr<Derived> wdsp = dsp;
|
||||
WeakPtr<Base> wbsp = dsp;
|
||||
WeakPtr<Base> wwbsp = wdsp;
|
||||
|
||||
assert(dsp.use_count() == 2);
|
||||
|
||||
bsp = std::move(dsp);
|
||||
assert(bsp.use_count() == 1);
|
||||
|
||||
bsp.reset();
|
||||
assert(wdsp.expired());
|
||||
assert(wbsp.expired());
|
||||
assert(wwbsp.expired());
|
||||
}
|
||||
assert(Derived::constructed == Derived::destructerd);
|
||||
|
||||
// test const
|
||||
new_called = 0;
|
||||
delete_called = 0;
|
||||
{
|
||||
SharedPtr<int> sp(new int(42));
|
||||
const WeakPtr<int> wp(sp);
|
||||
assert(!wp.expired());
|
||||
auto ssp = wp.lock();
|
||||
}
|
||||
assert(new_called == 2);
|
||||
assert(delete_called == 2);
|
||||
}
|
||||
|
||||
struct NeitherDefaultNorCopyConstructible {
|
||||
NeitherDefaultNorCopyConstructible() = delete;
|
||||
NeitherDefaultNorCopyConstructible(const NeitherDefaultNorCopyConstructible&) = delete;
|
||||
NeitherDefaultNorCopyConstructible& operator=(const NeitherDefaultNorCopyConstructible&) = delete;
|
||||
|
||||
NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default;
|
||||
NeitherDefaultNorCopyConstructible& operator=(NeitherDefaultNorCopyConstructible&&) = default;
|
||||
|
||||
explicit NeitherDefaultNorCopyConstructible(int x): x(x) {}
|
||||
int x;
|
||||
};
|
||||
|
||||
struct Accountant {
|
||||
static int constructed;
|
||||
static int destructed;
|
||||
|
||||
Accountant() {
|
||||
++constructed;
|
||||
}
|
||||
Accountant(const Accountant&) {
|
||||
++constructed;
|
||||
}
|
||||
~Accountant() {
|
||||
++destructed;
|
||||
}
|
||||
};
|
||||
|
||||
int Accountant::constructed = 0;
|
||||
int Accountant::destructed = 0;
|
||||
|
||||
void* operator new(size_t n) {
|
||||
++new_called;
|
||||
auto t = std::malloc(n);
|
||||
return t;
|
||||
}
|
||||
void operator delete(void* ptr) {
|
||||
++delete_called;
|
||||
std::free(ptr);
|
||||
}
|
||||
|
||||
struct VerySpecialType {};
|
||||
|
||||
void* operator new(size_t n, VerySpecialType) {
|
||||
return std::malloc(n);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, VerySpecialType) {
|
||||
std::free(ptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct MyAllocator {
|
||||
using value_type = T;
|
||||
|
||||
MyAllocator() = default;
|
||||
|
||||
template<typename U>
|
||||
MyAllocator(const MyAllocator<U>&) {}
|
||||
|
||||
T* allocate(size_t n) {
|
||||
++allocate_called;
|
||||
allocated += n * sizeof(T);;
|
||||
return (T*) ::operator new(n * sizeof(T), VerySpecialType());
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n) {
|
||||
++deallocate_called;
|
||||
deallocated += n * sizeof(T);
|
||||
::operator delete((void*)p, VerySpecialType());
|
||||
}
|
||||
};
|
||||
|
||||
void test_make_allocate_shared() {
|
||||
|
||||
new_called = 0;
|
||||
delete_called = 0;
|
||||
{
|
||||
auto sp = makeShared<NeitherDefaultNorCopyConstructible>(
|
||||
NeitherDefaultNorCopyConstructible(0));
|
||||
WeakPtr<NeitherDefaultNorCopyConstructible> wp = sp;
|
||||
auto ssp = sp;
|
||||
sp.reset();
|
||||
assert(!wp.expired());
|
||||
ssp.reset();
|
||||
assert(wp.expired());
|
||||
}
|
||||
|
||||
new_called = 0;
|
||||
delete_called = 0;
|
||||
|
||||
{
|
||||
auto sp = makeShared<Accountant>();
|
||||
assert(Accountant::constructed == 1);
|
||||
|
||||
WeakPtr<Accountant> wp = sp;
|
||||
auto ssp = sp;
|
||||
sp.reset();
|
||||
assert(Accountant::constructed == 1);
|
||||
assert(Accountant::destructed == 0);
|
||||
|
||||
assert(!wp.expired());
|
||||
ssp.reset();
|
||||
assert(Accountant::destructed == 1);
|
||||
|
||||
Accountant::constructed = 0;
|
||||
Accountant::destructed = 0;
|
||||
}
|
||||
|
||||
new_called = 0;
|
||||
delete_called = 0;
|
||||
|
||||
{
|
||||
MyAllocator<NeitherDefaultNorCopyConstructible> alloc;
|
||||
auto sp = allocateShared<NeitherDefaultNorCopyConstructible>(
|
||||
alloc, NeitherDefaultNorCopyConstructible(0));
|
||||
int count = allocated;
|
||||
assert(allocated > 0);
|
||||
assert(allocate_called == 1);
|
||||
|
||||
WeakPtr<NeitherDefaultNorCopyConstructible> wp = sp;
|
||||
auto ssp = sp;
|
||||
sp.reset();
|
||||
assert(count == allocated);
|
||||
assert(deallocated == 0);
|
||||
|
||||
assert(!wp.expired());
|
||||
ssp.reset();
|
||||
assert(count == allocated);
|
||||
}
|
||||
assert(allocated == deallocated);
|
||||
|
||||
assert(allocate_called == 1);
|
||||
assert(deallocate_called == 1);
|
||||
|
||||
allocated = 0;
|
||||
deallocated = 0;
|
||||
allocate_called = 0;
|
||||
deallocate_called = 0;
|
||||
|
||||
{
|
||||
MyAllocator<Accountant> alloc;
|
||||
auto sp = allocateShared<Accountant>(alloc);
|
||||
int count = allocated;
|
||||
assert(allocated > 0);
|
||||
assert(allocate_called == 1);
|
||||
assert(Accountant::constructed == 1);
|
||||
|
||||
WeakPtr<Accountant> wp = sp;
|
||||
auto ssp = sp;
|
||||
sp.reset();
|
||||
assert(count == allocated);
|
||||
assert(deallocated == 0);
|
||||
assert(Accountant::constructed == 1);
|
||||
assert(Accountant::destructed == 0);
|
||||
|
||||
assert(!wp.expired());
|
||||
ssp.reset();
|
||||
assert(count == allocated);
|
||||
}
|
||||
|
||||
assert(allocated == deallocated);
|
||||
|
||||
assert(Accountant::constructed == 1);
|
||||
assert(Accountant::destructed == 1);
|
||||
|
||||
assert(allocate_called == 1);
|
||||
assert(deallocate_called == 1);
|
||||
|
||||
assert(new_called == 0);
|
||||
assert(delete_called == 0);
|
||||
}
|
||||
|
||||
//struct Enabled: public EnableSharedFromThis<Enabled> {
|
||||
// SharedPtr<Enabled> get_shared() {
|
||||
// return shared_from_this();
|
||||
// }
|
||||
//};
|
||||
|
||||
//void test_enable_shared_from_this() {
|
||||
// {
|
||||
// Enabled e;
|
||||
// bool caught = false;
|
||||
// try {
|
||||
// e.get_shared();
|
||||
// } catch (...) {
|
||||
// caught = true;
|
||||
// }
|
||||
// assert(caught);
|
||||
// }
|
||||
//
|
||||
// auto esp = makeShared<Enabled>();
|
||||
//
|
||||
// auto& e = *esp;
|
||||
// auto sp = e.get_shared();
|
||||
//
|
||||
// assert(sp.use_count() == 2);
|
||||
//
|
||||
// esp.reset();
|
||||
// assert(sp.use_count() == 1);
|
||||
//
|
||||
// sp.reset();
|
||||
//}
|
||||
|
||||
|
||||
struct A {
|
||||
static int deleted;
|
||||
virtual ~A() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A::deleted = 0;
|
||||
struct A1: public A {
|
||||
static int deleted;
|
||||
virtual ~A1() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A1::deleted = 0;
|
||||
struct A2: public A {
|
||||
static int deleted;
|
||||
virtual ~A2() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A2::deleted = 0;
|
||||
struct A11: public A1 {
|
||||
static int deleted;
|
||||
virtual ~A11() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A11::deleted = 0;
|
||||
struct A12: public A1 {
|
||||
static int deleted;
|
||||
virtual ~A12() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A12::deleted = 0;
|
||||
struct A21: public A2 {
|
||||
static int deleted;
|
||||
virtual ~A21() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A21::deleted = 0;
|
||||
struct A22: public A2 {
|
||||
static int deleted;
|
||||
virtual ~A22() {
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
int A22::deleted = 0;
|
||||
|
||||
|
||||
int destroy_called;
|
||||
template<typename T>
|
||||
struct MyAlloc {
|
||||
using value_type = T;
|
||||
|
||||
MyAlloc() = default;
|
||||
|
||||
template<typename U>
|
||||
MyAlloc(const MyAlloc<U>&) {}
|
||||
|
||||
void destroy(T* p) {
|
||||
++destroy_called;
|
||||
p->~T();
|
||||
}
|
||||
|
||||
T* allocate(size_t n) {
|
||||
++allocate_called;
|
||||
allocated += n * sizeof(T);;
|
||||
return (T*) ::operator new(n * sizeof(T), VerySpecialType());
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n) {
|
||||
++deallocate_called;
|
||||
deallocated += n * sizeof(T);
|
||||
::operator delete((void*)p, VerySpecialType());
|
||||
}
|
||||
};
|
||||
|
||||
void my_test_inheritance_destroy() {
|
||||
{
|
||||
SharedPtr<A22> a22 = allocateShared<A22, MyAlloc<A22>>(MyAlloc<A22>());
|
||||
SharedPtr<A2> a2 = a22;
|
||||
SharedPtr<A> a = a2;
|
||||
assert(a.use_count() == 3);
|
||||
a22.reset();
|
||||
a.reset();
|
||||
a = allocateShared<A21, MyAlloc<A21>>(MyAlloc<A21>());
|
||||
WeakPtr<A> wa = a;
|
||||
WeakPtr<A> wwa = wa;
|
||||
a.reset(new A2());
|
||||
}
|
||||
assert(A22::deleted == 1);
|
||||
assert(A21::deleted == 1);
|
||||
}
|
||||
|
||||
void my_test_custom_deleter();
|
||||
|
||||
struct T {
|
||||
~T() {
|
||||
std::cout << "~" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
using namespace std;
|
||||
std::allocator<T> all;
|
||||
SharedPtr<T> p(all.allocate(1));
|
||||
// p.reset();
|
||||
// SharedPtr<int> p;
|
||||
p.reset(new T);
|
||||
p.reset(new T);
|
||||
p.reset(new T);
|
||||
p.reset(new T);
|
||||
p.reset(new T);
|
||||
p.reset();
|
||||
std::cout << "alsdkfj" << endl;
|
||||
|
||||
// return 0;
|
||||
|
||||
static_assert(!std::is_base_of_v<std::shared_ptr<VerySpecialType>, SharedPtr<VerySpecialType>>,
|
||||
"don't try to use std smart pointers");
|
||||
|
||||
// static_assert(!std::is_base_of_v<WeakPtr<VerySpecialType>, WeakPtr<VerySpecialType>>,
|
||||
// "don't try to use std smart pointers");
|
||||
//
|
||||
test_shared_ptr();
|
||||
std::cout << "1/5 passed" << std::endl;
|
||||
test_weak_ptr();
|
||||
std::cout << "2/5 passed" << std::endl;
|
||||
test_make_allocate_shared();
|
||||
std::cout << "3/5 passed" << std::endl;
|
||||
std::cout << "starting my tests" << std::endl;
|
||||
my_test_inheritance_destroy();
|
||||
std::cout << "4/5 passed" << std::endl;
|
||||
my_test_custom_deleter();
|
||||
std::cout << "5/5 passed" << std::endl;
|
||||
std::cout << "now it should work" << std::endl;
|
||||
|
||||
// test_enable_shared_from_this();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct MyDeletor {
|
||||
static int deleted;
|
||||
void operator()(T* x) {
|
||||
::delete x;
|
||||
++deleted;
|
||||
}
|
||||
};
|
||||
template<typename T>
|
||||
int MyDeletor<T>::deleted = 0;
|
||||
|
||||
void my_test_custom_deleter() {
|
||||
{
|
||||
SharedPtr<Accountant> p(new Accountant(), MyDeletor<Accountant>());
|
||||
SharedPtr<Accountant> pp = std::move(p);
|
||||
std::cout << pp.use_count() << std::endl;
|
||||
SharedPtr<Accountant> h = pp;
|
||||
std::cout << h.use_count() << std::endl;
|
||||
// p = allocateShared<Accountant, MyAlloc<Accountant>>(MyAlloc<Accountant>());
|
||||
}
|
||||
std::cout << delete_called << std::endl;
|
||||
assert(delete_called == 4);
|
||||
assert(destroy_called == 3);
|
||||
destroy_called = 0;
|
||||
{
|
||||
auto* smth = new Accountant();
|
||||
SharedPtr<Accountant> p(smth, MyDeletor<Accountant>(), MyAlloc<Accountant>());
|
||||
SharedPtr<Accountant> pp = std::move(p);
|
||||
SharedPtr<Accountant> h = pp;
|
||||
p = allocateShared<Accountant, MyAlloc<Accountant>>(MyAlloc<Accountant>());
|
||||
}
|
||||
assert(delete_called == 5);
|
||||
assert(destroy_called == 1);
|
||||
}
|
5
shared_pointer/makefile
Executable file
5
shared_pointer/makefile
Executable file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
350
shared_pointer/smart_pointers.h
Normal file
350
shared_pointer/smart_pointers.h
Normal file
|
@ -0,0 +1,350 @@
|
|||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
struct StandardDelete {
|
||||
template<typename T>
|
||||
void operator()(T* ptr) {
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Deleter {
|
||||
virtual void destroy(void*) = 0;
|
||||
virtual void deallocate(void*) = 0;
|
||||
virtual ~Deleter() {}
|
||||
};
|
||||
|
||||
template<class T, class Allocator>
|
||||
struct ObjectManagerBlock : Deleter {
|
||||
Allocator allocator_;
|
||||
ObjectManagerBlock(const Allocator& allocator) : allocator_(allocator) {}
|
||||
|
||||
void destroy(void* ptr) override {
|
||||
using ConstructAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||
using ConstructAllocatorTraits = typename std::allocator_traits<ConstructAllocator>;
|
||||
ConstructAllocator construct_allocator = allocator_;
|
||||
|
||||
ConstructAllocatorTraits::destroy(construct_allocator, static_cast<T*>(ptr));
|
||||
}
|
||||
void deallocate(void* ptr) override {
|
||||
using MemoryAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>;
|
||||
using MemoryAllocatorTraits = typename std::allocator_traits<MemoryAllocator>;
|
||||
MemoryAllocator memory_allocator = allocator_;
|
||||
|
||||
MemoryAllocatorTraits::deallocate(memory_allocator,
|
||||
reinterpret_cast<uint8_t*>(ptr),
|
||||
sizeof(T) + 2 * sizeof(size_t) + sizeof(Deleter));
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class TDeleter, class Allocator>
|
||||
struct DeleterWithSpecialDeleter : Deleter {
|
||||
Allocator allocator_;
|
||||
TDeleter t_deleter_;
|
||||
DeleterWithSpecialDeleter(const Allocator& allocator, const TDeleter& t_deleter)
|
||||
: allocator_(allocator), t_deleter_(t_deleter) {}
|
||||
void destroy(void* ptr) override {
|
||||
t_deleter_(reinterpret_cast<T*>(ptr));
|
||||
}
|
||||
void deallocate(void* ptr) override {
|
||||
using MemoryAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>;
|
||||
using MemoryAllocatorTraits = typename std::allocator_traits<MemoryAllocator>;
|
||||
MemoryAllocator memory_allocator = allocator_;
|
||||
MemoryAllocatorTraits::deallocate(memory_allocator,
|
||||
reinterpret_cast<uint8_t*>(ptr),
|
||||
2 * sizeof(size_t) + sizeof(Deleter));
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class WeakPtr;
|
||||
|
||||
template<class T>
|
||||
class SharedPtr {
|
||||
private:
|
||||
template<class U>
|
||||
friend
|
||||
class SharedPtr;
|
||||
template<class U>
|
||||
friend
|
||||
class WeakPtr;
|
||||
struct TemplateConstructorTag {};
|
||||
|
||||
void decrease_counter() {
|
||||
if (counter_) {
|
||||
--*counter_;
|
||||
if (*counter_ == 0) {
|
||||
deleter_->destroy(value_);
|
||||
if (*weak_counter_ == 0) {
|
||||
if (!is_block_) {
|
||||
deleter_->deallocate(counter_);
|
||||
} else {
|
||||
deleter_->deallocate(value_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void increase_counter() {
|
||||
if (counter_) {
|
||||
++*counter_;
|
||||
}
|
||||
}
|
||||
|
||||
void reset_without_decrease() {
|
||||
value_ = nullptr;
|
||||
counter_ = nullptr;
|
||||
weak_counter_ = nullptr;
|
||||
deleter_ = nullptr;
|
||||
is_block_ = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
~SharedPtr() {
|
||||
decrease_counter();
|
||||
}
|
||||
SharedPtr() {}
|
||||
SharedPtr(T* ptr) : SharedPtr(ptr, StandardDelete(), std::allocator<size_t>()) {}
|
||||
template<class TDeleter, class Allocator = std::allocator<size_t>>
|
||||
SharedPtr(T* ptr, const TDeleter& deleter, Allocator allocator = Allocator()) : SharedPtr() {
|
||||
reset<Allocator, TDeleter>(ptr, allocator, deleter);
|
||||
}
|
||||
|
||||
template<class Allocator = std::allocator<size_t>, class TDeleter = StandardDelete>
|
||||
void reset(T* ptr, Allocator allocator = Allocator(), const TDeleter& t_deleter = TDeleter()) {
|
||||
decrease_counter();
|
||||
value_ = ptr;
|
||||
is_block_ = false;
|
||||
|
||||
using MemoryAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>;
|
||||
using MemoryAllocatorTraits = typename std::allocator_traits<MemoryAllocator>;
|
||||
MemoryAllocator memory_allocator = allocator;
|
||||
auto mem = MemoryAllocatorTraits::allocate(memory_allocator, 2 * sizeof(size_t) + sizeof(Deleter));
|
||||
|
||||
counter_ = reinterpret_cast<size_t*>(mem);
|
||||
*counter_ = 1;
|
||||
weak_counter_ = reinterpret_cast<size_t*>(mem + sizeof(size_t));
|
||||
*weak_counter_ = 0;
|
||||
deleter_ = reinterpret_cast<Deleter*>(mem + 2 * sizeof(size_t));
|
||||
new(deleter_) DeleterWithSpecialDeleter<T, TDeleter, Allocator>(allocator, t_deleter);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
decrease_counter();
|
||||
reset_without_decrease();
|
||||
}
|
||||
|
||||
SharedPtr(SharedPtr&& another) : SharedPtr(std::move(another), TemplateConstructorTag()) {}
|
||||
|
||||
template<class U>
|
||||
SharedPtr(SharedPtr<U>&& another, TemplateConstructorTag = TemplateConstructorTag()) {
|
||||
value_ = std::move(another.value_);
|
||||
counter_ = std::move(another.counter_);
|
||||
weak_counter_ = std::move(another.weak_counter_);
|
||||
deleter_ = std::move(another.deleter_);
|
||||
is_block_ = another.is_block_;
|
||||
another.reset_without_decrease();
|
||||
}
|
||||
|
||||
SharedPtr(const SharedPtr& another) : SharedPtr(another, TemplateConstructorTag()) {}
|
||||
|
||||
template<class U>
|
||||
SharedPtr(const SharedPtr<U>& another, TemplateConstructorTag = TemplateConstructorTag()) {
|
||||
value_ = another.value_;
|
||||
counter_ = another.counter_;
|
||||
weak_counter_ = another.weak_counter_;
|
||||
deleter_ = another.deleter_;
|
||||
is_block_ = another.is_block_;
|
||||
increase_counter();
|
||||
}
|
||||
|
||||
SharedPtr& operator=(const SharedPtr& another) {
|
||||
return operator=<T>(another);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
SharedPtr& operator=(const SharedPtr<U>& another) {
|
||||
SharedPtr tmp(another);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SharedPtr& operator=(SharedPtr&& another) {
|
||||
return operator=<T>(std::move(another));
|
||||
}
|
||||
|
||||
template<class U>
|
||||
SharedPtr& operator=(SharedPtr<U>&& another) {
|
||||
SharedPtr tmp(std::move(another));
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t use_count() const {
|
||||
return *counter_;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return value_;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return *value_;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return *value_;
|
||||
}
|
||||
|
||||
T* get() {
|
||||
return value_;
|
||||
}
|
||||
|
||||
const T* get() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void swap(SharedPtr<T>& another) {
|
||||
std::swap(value_, another.value_);
|
||||
std::swap(counter_, another.counter_);
|
||||
std::swap(weak_counter_, another.weak_counter_);
|
||||
std::swap(deleter_, another.deleter_);
|
||||
std::swap(is_block_, another.is_block_);
|
||||
}
|
||||
private:
|
||||
template<class U, class... Args>
|
||||
friend SharedPtr<U> makeShared(Args&& ... args);
|
||||
template<class U, class Alloc, class... Args>
|
||||
friend SharedPtr<U> allocateShared(const Alloc& alloc, Args&& ... args);
|
||||
|
||||
friend WeakPtr<T>;
|
||||
|
||||
T* value_ = nullptr;
|
||||
size_t* counter_ = nullptr;
|
||||
size_t* weak_counter_ = nullptr;
|
||||
Deleter* deleter_ = nullptr;
|
||||
bool is_block_ = false;
|
||||
template<class Alloc = std::allocator<size_t>>
|
||||
SharedPtr(T* ptr, size_t* counter, size_t* weak_counter, Deleter* deleter, const Alloc& alloc) :
|
||||
value_(ptr), counter_(counter), weak_counter_(weak_counter), deleter_(deleter), is_block_(true) {
|
||||
increase_counter();
|
||||
new(deleter) ObjectManagerBlock<T, Alloc>(alloc);
|
||||
}
|
||||
SharedPtr(T* ptr, size_t* counter, size_t* weak_counter, Deleter* deleter, bool is_block) :
|
||||
value_(ptr), counter_(counter), weak_counter_(weak_counter), deleter_(deleter), is_block_(is_block) {
|
||||
increase_counter();
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class WeakPtr {
|
||||
template<class U>
|
||||
friend
|
||||
class WeakPtr;
|
||||
struct TemplateConstructorTag {};
|
||||
public:
|
||||
~WeakPtr() {
|
||||
decrease_counter();
|
||||
}
|
||||
WeakPtr() {}
|
||||
WeakPtr(const SharedPtr<T>& another) : WeakPtr(another, TemplateConstructorTag()) {}
|
||||
template<class U>
|
||||
WeakPtr(const SharedPtr<U>& another, TemplateConstructorTag = TemplateConstructorTag()) {
|
||||
value_ = another.value_;
|
||||
counter_ = another.counter_;
|
||||
weak_counter_ = another.weak_counter_;
|
||||
deleter_ = another.deleter_;
|
||||
is_block_ = another.is_block_;
|
||||
increase_counter();
|
||||
}
|
||||
WeakPtr(const WeakPtr& another) : WeakPtr(another, TemplateConstructorTag()) {}
|
||||
template<class U>
|
||||
WeakPtr(const WeakPtr<U>& another, TemplateConstructorTag = TemplateConstructorTag()) {
|
||||
value_ = another.value_;
|
||||
counter_ = another.counter_;
|
||||
weak_counter_ = another.weak_counter_;
|
||||
deleter_ = another.deleter_;
|
||||
is_block_ = another.is_block_;
|
||||
increase_counter();
|
||||
}
|
||||
size_t use_count() const {
|
||||
return *counter_;
|
||||
}
|
||||
bool expired() const {
|
||||
return *counter_ == 0;
|
||||
}
|
||||
SharedPtr<T> lock() const {
|
||||
return SharedPtr<T>(value_, counter_, weak_counter_, deleter_, is_block_);
|
||||
}
|
||||
WeakPtr& operator=(const WeakPtr& another) {
|
||||
WeakPtr tmp(another);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
WeakPtr& operator=(WeakPtr&& another) {
|
||||
WeakPtr tmp(another);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
WeakPtr& operator=(const SharedPtr<T>& another) {
|
||||
WeakPtr tmp(another);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
void decrease_counter() {
|
||||
if (weak_counter_) {
|
||||
--*weak_counter_;
|
||||
if (*counter_ == 0 && *weak_counter_ == 0) {
|
||||
if (!is_block_) {
|
||||
deleter_->deallocate(counter_);
|
||||
} else {
|
||||
deleter_->deallocate(value_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void increase_counter() {
|
||||
if (weak_counter_) {
|
||||
++*weak_counter_;
|
||||
}
|
||||
}
|
||||
void swap(WeakPtr& another) {
|
||||
std::swap(value_, another.value_);
|
||||
std::swap(counter_, another.counter_);
|
||||
std::swap(weak_counter_, another.weak_counter_);
|
||||
std::swap(deleter_, another.deleter_);
|
||||
}
|
||||
T* value_ = nullptr;
|
||||
size_t* counter_ = nullptr;
|
||||
size_t* weak_counter_ = nullptr;
|
||||
Deleter* deleter_ = nullptr;
|
||||
bool is_block_ = false;
|
||||
};
|
||||
|
||||
template<typename T, typename Alloc, typename... Args>
|
||||
SharedPtr<T> allocateShared(const Alloc& alloc, Args&& ... args) {
|
||||
using MemoryAllocator = typename std::allocator_traits<Alloc>::template rebind_alloc<uint8_t>;
|
||||
using MemoryAllocatorTraits = typename std::allocator_traits<MemoryAllocator>;
|
||||
MemoryAllocator memory_allocator = alloc;
|
||||
auto ptr = MemoryAllocatorTraits::allocate(memory_allocator, sizeof(T) + 2 * sizeof(size_t) + sizeof(Deleter));
|
||||
|
||||
using ConstructAllocator = typename std::allocator_traits<Alloc>::template rebind_alloc<T>;
|
||||
using ConstructAllocatorTraits = typename std::allocator_traits<ConstructAllocator>;
|
||||
ConstructAllocator construct_allocator = alloc;
|
||||
|
||||
ConstructAllocatorTraits::construct(construct_allocator, (T*) ptr, std::forward<Args>(args)...);
|
||||
|
||||
*(size_t*) (ptr + sizeof(T)) = 0;
|
||||
*(size_t*) (ptr + sizeof(T) + sizeof(size_t)) = 0;
|
||||
return SharedPtr<T>((T*) ptr, (size_t*) (ptr + sizeof(T)), (size_t*) (ptr + sizeof(T) + sizeof(size_t)),
|
||||
(Deleter*) (ptr + sizeof(T) + 2 * sizeof(size_t)), alloc);
|
||||
}
|
||||
|
||||
template<class T, typename... Args>
|
||||
SharedPtr<T> makeShared(Args&& ... args) {
|
||||
return allocateShared<T>(std::allocator<T>(), std::forward<Args>(args)...);
|
||||
}
|
10
string/main.cpp
Normal file
10
string/main.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <iostream>
|
||||
#include "string.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
String s = "$$#$##$#$####$#$$$#";
|
||||
|
||||
return 0;
|
||||
}
|
2
string/makefile
Normal file
2
string/makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
g++ main.cpp -Wall -Wextra -o a
|
184
string/string.h
Normal file
184
string/string.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
class String {
|
||||
private:
|
||||
size_t capacity_ = 0;
|
||||
size_t size_ = 0;
|
||||
char* t_ = nullptr;
|
||||
|
||||
void realloc() {
|
||||
if (4 * size_ <= capacity_) {
|
||||
reserve(size_ * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void realloc(size_t capacity) {
|
||||
if (capacity > capacity_) {
|
||||
reserve(capacity * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
~String() { delete[] t_; }
|
||||
|
||||
String() = default;
|
||||
|
||||
String(char x) {
|
||||
size_ = 0;
|
||||
capacity_ = 1;
|
||||
t_ = new char[capacity_];
|
||||
t_[size_++] = x;
|
||||
}
|
||||
|
||||
String(const char* t) {
|
||||
size_ = strlen(t);
|
||||
capacity_ = size_;
|
||||
t_ = new char[capacity_];
|
||||
memcpy(t_, t, size_ * sizeof(char));
|
||||
}
|
||||
|
||||
String(size_t n, char c) : capacity_(n), size_(n), t_(new char[capacity_]) {
|
||||
memset(t_, c, size_ * sizeof(char));
|
||||
}
|
||||
|
||||
String(const String& s)
|
||||
: capacity_(s.size_), size_(s.size_), t_(new char[capacity_]) {
|
||||
memcpy(t_, s.t_, size_ * sizeof(char));
|
||||
}
|
||||
|
||||
void swap(String& s) {
|
||||
std::swap(capacity_, s.capacity_);
|
||||
std::swap(size_, s.size_);
|
||||
std::swap(t_, s.t_);
|
||||
}
|
||||
|
||||
String& operator=(const String& s) {
|
||||
String copy(s);
|
||||
swap(copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const String& s) const {
|
||||
if (size_ != s.size_) return false;
|
||||
|
||||
for (size_t i = 0; i < size_; ++i) {
|
||||
if (t_[i] != s.t_[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char& operator[](size_t x) { return t_[x]; }
|
||||
|
||||
char operator[](size_t x) const { return t_[x]; }
|
||||
|
||||
size_t length() const { return size_; }
|
||||
|
||||
char& front() { return t_[0]; }
|
||||
|
||||
const char& front() const { return t_[0]; }
|
||||
|
||||
char& back() { return t_[size_ - 1]; }
|
||||
|
||||
const char& back() const { return t_[size_ - 1]; }
|
||||
|
||||
void reserve(size_t capacity) {
|
||||
char* p = new char[capacity];
|
||||
capacity_ = capacity;
|
||||
|
||||
if (t_) {
|
||||
size_ = std::min(size_, capacity);
|
||||
memcpy(p, t_, size_ * sizeof(char));
|
||||
delete[] t_;
|
||||
}
|
||||
t_ = p;
|
||||
}
|
||||
|
||||
void push_back(char x) {
|
||||
realloc(size_ + 1);
|
||||
t_[size_++] = x;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
size_--;
|
||||
realloc();
|
||||
}
|
||||
|
||||
String& operator+=(const String& s) {
|
||||
realloc(size_ + s.size_);
|
||||
memcpy(t_ + size_, s.t_, s.size_ * sizeof(char));
|
||||
size_ += s.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t find(const String& s) const {
|
||||
for (size_t i = 0; i < size_ - s.size_ + 1; ++i) {
|
||||
bool is_equal = true;
|
||||
for (size_t j = 0; j < s.size_; ++j) {
|
||||
if (t_[i + j] != s.t_[j]) is_equal = false;
|
||||
}
|
||||
if (is_equal) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size_;
|
||||
}
|
||||
|
||||
size_t rfind(const String& s) const {
|
||||
for (size_t i = size_ - 1; i >= s.size_; --i) {
|
||||
bool is_equal = true;
|
||||
for (int j = s.size_; j--;) {
|
||||
if (t_[i - s.size_ + j + 1] != s.t_[j]) is_equal = false;
|
||||
}
|
||||
if (is_equal) {
|
||||
return i - s.size_ + 1;
|
||||
}
|
||||
}
|
||||
return size_;
|
||||
}
|
||||
|
||||
String substr(size_t i, size_t count) const {
|
||||
String s;
|
||||
s.reserve(count);
|
||||
memcpy(s.t_, t_ + i, count * sizeof(char));
|
||||
s.size_ = count;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool empty() { return size_ == 0; }
|
||||
|
||||
void clear() {
|
||||
size_ = 0;
|
||||
delete[] t_;
|
||||
t_ = nullptr;
|
||||
capacity_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
String operator+(const String& s, const String& g) {
|
||||
String res = s;
|
||||
res += g;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, String& s) {
|
||||
s.clear();
|
||||
char x;
|
||||
in >> x;
|
||||
|
||||
while (!in.eof()) {
|
||||
if (std::isspace(x)) break;
|
||||
s.push_back(x);
|
||||
x = in.get();
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const String& s) {
|
||||
for (size_t i = 0; i < s.length(); ++i) {
|
||||
out << s[i];
|
||||
}
|
||||
return out;
|
||||
}
|
13
variant/CMakeLists.txt
Normal file
13
variant/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
#project("Variant")
|
||||
|
||||
set(SOURCE_FILES main.cpp)
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "-Ofast -Wall -Wextra")
|
||||
|
||||
set(CMAKE_CXX_COMPILER g++)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(map ${SOURCE_FILES})
|
259
variant/main.cpp
Normal file
259
variant/main.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include "variant.h"
|
||||
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
/*
|
||||
using std::get;
|
||||
using std::holds_alternative;
|
||||
template<typename... Types>
|
||||
using Variant = std::variant<Types...>;
|
||||
*/
|
||||
|
||||
struct A {
|
||||
A() {
|
||||
cout << "construct ()" << endl;
|
||||
}
|
||||
A(const A&) {
|
||||
cout << "construct (const A&)" << endl;
|
||||
}
|
||||
A(A&& a) {
|
||||
cout << "A(&&)" << endl;
|
||||
}
|
||||
A& operator=(const A&) {
|
||||
cout << "operator= (const A&)" << endl;
|
||||
return *this;
|
||||
}
|
||||
A& operator=(A&& another) {
|
||||
cout << "=&&" << endl;
|
||||
return *this;
|
||||
}
|
||||
~A() {
|
||||
cout << "~A()" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
void compile_test() {
|
||||
int x;
|
||||
{
|
||||
Variant<int, double> v(1.0);
|
||||
const Variant<int, double> cv(1.0);
|
||||
Variant<std::vector<int>> vv;
|
||||
holds_alternative<double>(v);
|
||||
get<double>(v);
|
||||
get<double>(cv);
|
||||
static_assert(!std::is_assignable_v<decltype(v), std::vector<int>>);
|
||||
// static_assert(!std::is_assignable_v<decltype(vv), int>);
|
||||
static_assert(!std::is_assignable_v<Variant < float, int>, double > );
|
||||
static_assert(!std::is_assignable_v<decltype(get<double>(cv)), double>);
|
||||
static_assert(std::is_rvalue_reference_v<decltype(get<double>(std::move(v)))>);
|
||||
static_assert(std::is_lvalue_reference_v<decltype(get<double>(v))>);
|
||||
}
|
||||
{
|
||||
Variant<std::string> v = "abcdefgh";
|
||||
v = "lakjdflksd";
|
||||
v = std::string("alksdjfasf");
|
||||
Variant<std::string> vv("abcdefgh");
|
||||
assert(get<std::string>(vv).size() == 8);
|
||||
Variant<std::string, char, std::vector<int>> vvv = "abcdefgh";
|
||||
Variant<std::string, char, std::vector<int>> vvvv("abcdefgh");
|
||||
Variant<std::string> vvvvv(std::string("alskdjflaksf"));
|
||||
Variant<std::string> vvvvvv = std::string("alskdjflaksf");
|
||||
}
|
||||
{
|
||||
// У тебя должен быть
|
||||
// template<typename T, typename U, typename... Args>
|
||||
// void emplace(std::initializer_list<U>, Args&&...);
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/utility/variant/emplace
|
||||
using vec = std::vector<int>;
|
||||
Variant<vec> v;
|
||||
vec& a = v.emplace<std::vector<int>>({1, 2, 3});
|
||||
// Также долежн быть emplace по индексу
|
||||
vec& b = v.emplace<0>(std::vector<int>(2));
|
||||
// Также долежн быть emplace по индексу и std::initializer_list :)
|
||||
vec& c = v.emplace<0>({1, 2, 3});
|
||||
}
|
||||
{
|
||||
Variant<const int> v;
|
||||
// Пока хз как это решить
|
||||
// static_assert(!std::is_assignable_v<decltype(v), int>);
|
||||
// v = 10;
|
||||
}
|
||||
{
|
||||
Variant<const int, std::string, const std::string, double> v = 1;
|
||||
Variant<int, double> vvv = 1.0f;
|
||||
std::variant<int, double> vv = 1.0f;
|
||||
std::cout << std::is_constructible_v<int, float> << std::endl;
|
||||
}
|
||||
cout << "compile test passed" << endl;
|
||||
}
|
||||
|
||||
void test1() {
|
||||
int x;
|
||||
{
|
||||
const Variant<A, int> v(10);
|
||||
std::cout << get<int>(v) << std::endl;
|
||||
assert(get<int>(v) == 10);
|
||||
{
|
||||
int cnt_try = 0;
|
||||
try {
|
||||
get<A>(v);
|
||||
} catch (...) {
|
||||
cnt_try++;
|
||||
}
|
||||
try {
|
||||
get<0>(v);
|
||||
} catch (...) {
|
||||
cnt_try++;
|
||||
}
|
||||
assert(cnt_try == 2);
|
||||
}
|
||||
}
|
||||
{
|
||||
Variant<A, int> v(10);
|
||||
assert(get<int>(v) == 10);
|
||||
v = 11;
|
||||
assert(get<int>(v) == 11);
|
||||
get<int>(v)++;
|
||||
assert(get<int>(v) == 12);
|
||||
}
|
||||
{
|
||||
Variant<std::vector<int>> v;
|
||||
// assert(v.valueless_by_exception());
|
||||
}
|
||||
|
||||
cout << "test1 passed" << endl;
|
||||
}
|
||||
|
||||
void string_test() {
|
||||
Variant<std::string> v = "abcdefgh";
|
||||
assert(v.index() == 0);
|
||||
v = "lakjdflksd";
|
||||
assert(get<std::string>(v).size() == 10);
|
||||
assert(v.index() == 0);
|
||||
v = std::string("a");
|
||||
assert(get<std::string>(v).size() == 1);
|
||||
assert(v.index() == 0);
|
||||
Variant<std::string> vv("ab");
|
||||
assert(get<std::string>(vv).size() == 2);
|
||||
assert(vv.index() == 0);
|
||||
vv = "als";
|
||||
assert(get<std::string>(vv).size() == 3);
|
||||
assert(vv.index() == 0);
|
||||
vv = std::string("alsd");
|
||||
assert(get<std::string>(vv).size() == 4);
|
||||
assert(vv.index() == 0);
|
||||
Variant<std::string, char, std::vector<int>> vvv = "abcdefgh";
|
||||
assert(vvv.index() == 0);
|
||||
vvv = "alksjdgasg";
|
||||
assert(vvv.index() == 0);
|
||||
vvv = "alskdjglkagj";
|
||||
assert(vvv.index() == 0);
|
||||
Variant<std::string, char, std::vector<int>> vvvv("abcdefgh");
|
||||
assert(vvvv.index() == 0);
|
||||
vvvv = "asljgs;lgdk";
|
||||
assert(vvvv.index() == 0);
|
||||
vvvv = "al;kdja;k";
|
||||
assert(vvvv.index() == 0);
|
||||
Variant<std::string> vvvvv(std::string("alskdjflaksf"));
|
||||
assert(vvvvv.index() == 0);
|
||||
vvvvv = "a;kjga;g";
|
||||
assert(vvvvv.index() == 0);
|
||||
vvvvv = "asdjg;alkjg;alkjg";
|
||||
assert(vvvvv.index() == 0);
|
||||
Variant<std::string> vvvvvv = std::string("alskdjflaksf");
|
||||
assert(vvvvvv.index() == 0);
|
||||
vvvvvv = "askjga;j";
|
||||
assert(vvvvvv.index() == 0);
|
||||
vvvvvv = "a;lkjdg;aldjg";
|
||||
assert(vvvvvv.index() == 0);
|
||||
cout << "string test passed" << endl;
|
||||
}
|
||||
|
||||
|
||||
int testConstr = 0;
|
||||
int testDestroy = 0;
|
||||
|
||||
template<size_t N>
|
||||
struct TestDelete {
|
||||
TestDelete() { testConstr++; }
|
||||
~TestDelete() { ++testDestroy; }
|
||||
};
|
||||
|
||||
int countDelete = 0;
|
||||
void operator delete[](void* ptr) {
|
||||
countDelete++;
|
||||
}
|
||||
void operator delete(void* ptr) {
|
||||
countDelete++;
|
||||
}
|
||||
|
||||
void test2() {
|
||||
countDelete = 0;
|
||||
{
|
||||
Variant<TestDelete<0>, TestDelete<1>, TestDelete<2>> v;
|
||||
}
|
||||
// assert(testDestroy == 1);
|
||||
assert(testConstr == 1);
|
||||
// assert(countDelete == 1);
|
||||
{
|
||||
std::variant<TestDelete<0>, TestDelete<1>, TestDelete<2>> v;
|
||||
v = TestDelete<1>();
|
||||
v = TestDelete<2>();
|
||||
}
|
||||
// assert(testDestroy == 6);
|
||||
assert(testConstr == 4);
|
||||
std::cout << countDelete << std::endl;
|
||||
// assert(countDelete == 2);
|
||||
std::cout << "test2 passed" << std::endl;
|
||||
}
|
||||
|
||||
void test3() {
|
||||
using VAR = Variant<std::string, int>;
|
||||
VAR x = "ddd";
|
||||
{
|
||||
VAR v("als");
|
||||
x = std::move(v);
|
||||
}
|
||||
std::cout << get<0>(x) << std::endl;
|
||||
assert(get<0>(x) == "als");
|
||||
std::cout << "test3 passed";
|
||||
}
|
||||
|
||||
void test4() {
|
||||
{
|
||||
Variant<const int, const double> v = 1.0;
|
||||
assert(v.index() == 1);
|
||||
}
|
||||
{
|
||||
Variant<const int, const double> v = 1.0f;
|
||||
assert(v.index() == 1);
|
||||
}
|
||||
{
|
||||
Variant<const int, const double> v = 1;
|
||||
assert(v.index() == 0);
|
||||
}
|
||||
{
|
||||
Variant<const int, const double> v = 'a';
|
||||
assert(v.index() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
compile_test();
|
||||
test1();
|
||||
string_test();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
|
||||
// std::variant<int, int, double> v;
|
||||
// get<int>(v) = 10;
|
||||
}
|
5
variant/makefile
Executable file
5
variant/makefile
Executable file
|
@ -0,0 +1,5 @@
|
|||
all: main.cpp
|
||||
g++ -g -Wall -std=c++17 -D LOCAL main.cpp -o a
|
||||
|
||||
debug: main.cpp
|
||||
g++ -g -std=c++17 main.cpp -D LOCAL -o a -Wall -Wextra -pedantic -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector
|
343
variant/variant.h
Normal file
343
variant/variant.h
Normal file
|
@ -0,0 +1,343 @@
|
|||
#include <cstring>
|
||||
|
||||
template <typename T, typename ...Types>
|
||||
struct get_index_by_type {
|
||||
static const size_t value = -1;
|
||||
};
|
||||
|
||||
template <typename T, typename Head, typename ...Tail>
|
||||
struct get_index_by_type<T, Head, Tail...> {
|
||||
static const size_t value = std::is_same_v<T, Head> ? 0 : get_index_by_type<T, Tail...>::value + 1;
|
||||
};
|
||||
|
||||
template<typename T, typename ...Tail>
|
||||
static const size_t get_index_by_type_v = get_index_by_type<T, Tail...>::value;
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct t_is_on_of_types {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T, typename Head, typename... Tail>
|
||||
struct t_is_on_of_types<T, Head, Tail...> {
|
||||
static const bool value = std::is_same_v<T, Head> || t_is_on_of_types<T, Tail...>::value;
|
||||
};
|
||||
|
||||
template<typename T, typename... Types>
|
||||
static const bool t_is_on_of_types_v = t_is_on_of_types<T, Types...>::value;
|
||||
|
||||
template<size_t N, typename... Types>
|
||||
struct get_type_by_index {};
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct get_type_by_index<0, T, Types...> {
|
||||
using value = T;
|
||||
};
|
||||
|
||||
template<size_t N, typename T, typename... Types>
|
||||
struct get_type_by_index<N, T, Types...> {
|
||||
using value = typename get_type_by_index<N - 1, Types...>::value;
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct count_types_in_package {};
|
||||
|
||||
template<typename T, typename Head, typename... Tail>
|
||||
struct count_types_in_package<T, Head, Tail...> {
|
||||
static const size_t value = std::is_same_v<T, Head> + count_types_in_package<T, Tail...>::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct count_types_in_package<T> {
|
||||
static const size_t value = 0;
|
||||
};
|
||||
|
||||
|
||||
template<size_t N, typename... Types>
|
||||
using get_type_by_index_v = typename get_type_by_index<N, Types...>::value;
|
||||
|
||||
template<typename... Types>
|
||||
struct get_max_size {
|
||||
static const size_t value = 0;
|
||||
};
|
||||
|
||||
template<typename Head, typename... Tail>
|
||||
struct get_max_size<Head, Tail...> {
|
||||
static const size_t value = std::max(sizeof(Head), get_max_size<Tail...>::value);
|
||||
};
|
||||
|
||||
template<typename... Types>
|
||||
static const size_t get_max_size_v = get_max_size<Types...>::value;
|
||||
|
||||
template <typename ...Types>
|
||||
class Variant;
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct VariantAlternative;
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct VariantAlternative {
|
||||
public:
|
||||
VariantAlternative() {}
|
||||
VariantAlternative(const VariantAlternative&) {}
|
||||
|
||||
VariantAlternative(const T& value) {
|
||||
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(value);
|
||||
}
|
||||
VariantAlternative(T&& value) {
|
||||
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(std::move(value));
|
||||
}
|
||||
VariantAlternative& operator=(const T& value) {
|
||||
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
||||
me->destroy();
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
VariantAlternative& operator=(T&& value) {
|
||||
auto* me = static_cast<Variant<Types...>*>(this);
|
||||
me->destroy();
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(std::move(value));
|
||||
return *this;
|
||||
}
|
||||
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_constructible_v<U, K>, bool> = true>
|
||||
VariantAlternative(const K& value) {
|
||||
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(value);
|
||||
}
|
||||
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_constructible_v<U, K>, bool> = true>
|
||||
VariantAlternative(K&& value) {
|
||||
auto* me = std::launder(static_cast<Variant<Types...>*>(this));
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(std::forward<K>(value));
|
||||
}
|
||||
|
||||
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_assignable_v<U, K>, bool> = true>
|
||||
VariantAlternative& operator=(const K& value) {
|
||||
auto* me = static_cast<Variant<Types...>*>(this);
|
||||
me->destroy();
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(value);
|
||||
return *this;
|
||||
}
|
||||
template<typename K, typename U = T, std::enable_if_t<!std::is_convertible_v<U, K> && std::is_assignable_v<U, K>, bool> = true>
|
||||
VariantAlternative& operator=(K&& value) {
|
||||
auto* me = static_cast<Variant<Types...>*>(this);
|
||||
me->destroy();
|
||||
me->index_ = get_index_by_type_v<T, Types...>;
|
||||
new(me->object_) T(std::forward<K>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~VariantAlternative() {
|
||||
auto* me = static_cast<Variant<Types...>*>(this);
|
||||
if (get_index_by_type_v<T, Types...> == me->index_) {
|
||||
me->destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... TTypes>
|
||||
bool holds_alternative(const Variant<TTypes...>& another) {
|
||||
return another.template holds_alternative<T>();
|
||||
}
|
||||
|
||||
template<typename T, typename... TTypes>
|
||||
const T& get(const Variant<TTypes...>& another) {
|
||||
static_assert(t_is_on_of_types_v<T, TTypes...>);
|
||||
static_assert(count_types_in_package<T, TTypes...>::value == 1);
|
||||
|
||||
if (another.template holds_alternative<T>()) {
|
||||
return *reinterpret_cast<T*>(std::launder(const_cast<Variant<TTypes...>&>(another).object_));
|
||||
} else {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename... TTypes>
|
||||
T& get(Variant<TTypes...>& another) {
|
||||
return const_cast<T&>(get<T>(const_cast<const Variant<TTypes...>&>(another)));
|
||||
}
|
||||
|
||||
template<typename T, typename... TTypes>
|
||||
T&& get(Variant<TTypes...>&& another) {
|
||||
return std::move(const_cast<T>(get<T>(const_cast<const Variant<TTypes...>&>(another))));
|
||||
}
|
||||
|
||||
template<size_t N, typename... TTypes>
|
||||
const get_type_by_index_v<N, TTypes...>& get(const Variant<TTypes...>& another) {
|
||||
return get<get_type_by_index_v<N, TTypes...>>(another);
|
||||
}
|
||||
|
||||
template<size_t N, typename... TTypes>
|
||||
get_type_by_index_v<N, TTypes...>& get(Variant<TTypes...>& another) {
|
||||
using R = get_type_by_index_v<N, TTypes...>;
|
||||
return const_cast<R&>(get<R>(const_cast<const Variant<TTypes...>&>(another)));
|
||||
}
|
||||
|
||||
template<size_t N, typename... TTypes>
|
||||
get_type_by_index_v<N, TTypes...>&& get(Variant<TTypes...>&& another) {
|
||||
using R = get_type_by_index_v<N, TTypes...>;
|
||||
return std::move(const_cast<R>(get<R>(const_cast<const Variant<TTypes...>&>(another))));
|
||||
}
|
||||
|
||||
template <typename ...Types>
|
||||
class Variant : public VariantAlternative<Types, Types...>... {
|
||||
template<typename TT, typename... TTypes>
|
||||
friend class VariantAlternative;
|
||||
|
||||
|
||||
private:
|
||||
static const size_t size_variant = get_max_size_v<Types...>;
|
||||
alignas(Types...) int8_t object_[size_variant];
|
||||
size_t index_;
|
||||
template<class T>
|
||||
bool holds_alternative() const {
|
||||
if (get_index_by_type_v<T, Types...> == -1)
|
||||
throw std::bad_cast();
|
||||
else
|
||||
return get_index_by_type_v<T, Types...> == index_;
|
||||
}
|
||||
template<size_t N = 0>
|
||||
void destroy() {
|
||||
if (index_ == N) {
|
||||
index_ = -1;
|
||||
using R = get_type_by_index_v<N, Types...>;
|
||||
reinterpret_cast<R*>(object_)->~R();
|
||||
} else {
|
||||
if constexpr (N + 1 < sizeof...(Types)) {
|
||||
destroy<N + 1>();
|
||||
}
|
||||
}
|
||||
}
|
||||
template<size_t N = 0>
|
||||
void copy_constructor(const Variant& another) {
|
||||
if (another.index_ == N) {
|
||||
using R = get_type_by_index_v<N, Types...>;
|
||||
new(object_) R(*reinterpret_cast<R const*>(std::launder(another.object_)));
|
||||
index_ = another.index_;
|
||||
} else {
|
||||
if constexpr (N + 1 < sizeof...(Types)) {
|
||||
copy_constructor<N + 1>(another);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<size_t N = 0>
|
||||
void move_constructor(Variant&& another) {
|
||||
if (this == &another) return;
|
||||
if (another.index_ == N) {
|
||||
using R = get_type_by_index_v<N, Types...>;
|
||||
|
||||
new(object_) R(std::move(*reinterpret_cast<R*>(std::launder(another.object_))));
|
||||
index_ = another.index_;
|
||||
} else {
|
||||
if constexpr (N + 1 < sizeof...(Types)) {
|
||||
move_constructor<N + 1>(std::move(another));
|
||||
}
|
||||
}
|
||||
}
|
||||
template<size_t N = 0, size_t M = 0>
|
||||
void swap(Variant& another) {
|
||||
if (index_ == N && another.index_ == M) {
|
||||
using R = get_type_by_index_v<N, Types...>;
|
||||
using S = get_type_by_index_v<M, Types...>;
|
||||
S tmp(std::move(*reinterpret_cast<S*>(another.object_)));
|
||||
new(another.object_) R(std::move(*reinterpret_cast<R*>(object_)));
|
||||
new(object_) S(std::move(tmp));
|
||||
std::swap(index_, another.index_);
|
||||
} else if (index_ != N) {
|
||||
if constexpr(N + 1 < sizeof...(Types))
|
||||
swap<N + 1, M>(another);
|
||||
} else {
|
||||
if constexpr(M + 1 < sizeof...(Types))
|
||||
swap<N, M + 1>(another);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
using VariantAlternative<Types, Types...>::operator=...;
|
||||
using VariantAlternative<Types, Types...>::VariantAlternative...;
|
||||
|
||||
Variant() : index_(0) {
|
||||
new(object_) get_type_by_index_v<0, Types...>();
|
||||
}
|
||||
Variant(Variant&& another) {
|
||||
move_constructor(std::move(another));
|
||||
}
|
||||
Variant(const Variant& another) : Variant() {
|
||||
copy_constructor(another);
|
||||
}
|
||||
Variant& operator=(const Variant& another) {
|
||||
Variant tmp(another);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
Variant& operator=(Variant&& another) noexcept {
|
||||
if (&another == this) return *this;
|
||||
Variant tmp(std::move(another));
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
template<typename T, typename... Args>
|
||||
T& emplace(Args&&... args) {
|
||||
destroy();
|
||||
index_ = get_index_by_type_v<T, Types...>;
|
||||
new(object_) T(std::forward<Args...>(args)...);
|
||||
return *reinterpret_cast<T*>(object_);
|
||||
}
|
||||
template<size_t I, typename... Args>
|
||||
get_type_by_index_v<I, Types...>& emplace(Args&&... args) {
|
||||
using T = get_type_by_index_v<I, Types...>;
|
||||
destroy();
|
||||
index_ = get_index_by_type_v<T, Types...>;
|
||||
new(object_) T(std::forward<Args...>(args)...);
|
||||
return *reinterpret_cast<T*>(object_);
|
||||
}
|
||||
template<typename T, typename U, typename... Args>
|
||||
T& emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
destroy();
|
||||
index_ = get_index_by_type_v<T, Types...>;
|
||||
new(object_) T(il, std::forward<Args...>(args)...);
|
||||
return *reinterpret_cast<T*>(object_);
|
||||
}
|
||||
template<size_t I, typename U, typename... Args>
|
||||
get_type_by_index_v<I, Types...>& emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
using T = get_type_by_index_v<I, Types...>;
|
||||
destroy();
|
||||
index_ = get_index_by_type_v<T, Types...>;
|
||||
new(object_) T(il, std::forward<Args...>(args)...);
|
||||
return *reinterpret_cast<T*>(object_);
|
||||
}
|
||||
size_t index() const {
|
||||
return index_;
|
||||
}
|
||||
bool valueless_by_exception() const {
|
||||
return index_ == -1;
|
||||
}
|
||||
template<typename T, typename... TTypes>
|
||||
friend bool holds_alternative(const Variant<TTypes...>& another);
|
||||
template<typename T, typename... TTypes>
|
||||
friend const T& get(const Variant<TTypes...>& another);
|
||||
template<typename T, typename... TTypes>
|
||||
friend T& get(Variant<TTypes...>& another);
|
||||
template<typename T, typename... TTypes>
|
||||
friend T&& get(Variant<TTypes...>&& another);
|
||||
template<size_t N, typename... TTypes>
|
||||
friend const get_type_by_index_v<N, TTypes...>& get(const Variant<TTypes...>& another);
|
||||
template<size_t N, typename... TTypes>
|
||||
friend get_type_by_index_v<N, TTypes...>& get(Variant<TTypes...>& another);
|
||||
template<size_t N, typename... TTypes>
|
||||
friend get_type_by_index_v<N, TTypes...>&& get(Variant<TTypes...>&& another);
|
||||
};
|
Loading…
Reference in a new issue