#pragma once

#include<string>
#include<iostream>
#include<fstream>
#include<vector>
#include<iomanip>
#include<sstream>
#include<cmath>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <functional>
#include <set>
#include <queue>
#include <memory>
#include <map>
#include <cassert>
#include <utility>

using std::exchange;
using std::move;
using std::size_t;

#include "Test.hpp"

namespace smart_pointer {
class exception : std::exception {
    using base_class = std::exception;
    using base_class::base_class;
};

template<
    typename T,
    typename Allocator>
class SmartPointer {
ENABLE_CLASS_TESTS;

 public:
    using value_type = T;

    explicit SmartPointer(value_type* x = nullptr) :
        core((x == nullptr ? nullptr : new Core(x))) {
    }

    SmartPointer(const SmartPointer& oth) :
        core(oth.core != nullptr ? oth.core : nullptr) {
        if (core != nullptr) {
            this->core->c++;
        }
    }

    SmartPointer(SmartPointer&& oth) :
        core(exchange(oth.core, nullptr)) { }

    SmartPointer& operator=(const SmartPointer& oth) {
        this->~SmartPointer();
        this->core = oth.core;
        if (this->core != nullptr) {
            this->core->c++;
        }
        return *this;
    }

    SmartPointer& operator=(SmartPointer&& oth) {
        this->~SmartPointer();
        this->core = move(oth.core);
        oth.core = nullptr;
        return *this;
    }

    SmartPointer& operator=(value_type* p) {
        this->~SmartPointer();
        if (p == nullptr) {
            this->core = nullptr;
        } else {
            this->core = new Core(p);
        }
        return *this;
    }

    ~SmartPointer() {
        if (this->core != nullptr) {
            if (this->core->c <= 1) {
                delete this->core;
            } else {
                this->core->c--;
            }
        }
    }

    value_type& operator*() {
        if (this->core == nullptr || this->core->p == nullptr) {
            throw smart_pointer::exception();
        } else {
            return *(this->core->p);
        }
    }
    const value_type& operator*() const {
        if (this->core == nullptr || this->core->p == nullptr) {
            throw smart_pointer::exception();
        }
        return *(this->core->p);
    }

    value_type* operator->() const {
        return this->get();
    }

    value_type* get() const {
        if (this->core == nullptr) {
            return nullptr;
        } else {
            return this->core->p;
        }
    }

    operator bool() const {
        if (this->core == nullptr || this->core->p == nullptr) {
            return false;
        } else {
            return true;
        }
    }

    decltype(auto) getCore() const {
        return static_cast<void*>(this->core);
    }

    template<typename U, typename AnotherAllocator>
    bool operator==(const SmartPointer<U, AnotherAllocator>& oth)
        const {
        return this->getCore() == oth.getCore();
    }

    template<typename U, typename AnotherAllocator>
    bool operator!=(const SmartPointer<U, AnotherAllocator>& oth) const {
        return !(SmartPointer::operator==(oth));
    }

    size_t count_owners() const {
        if (this->core == nullptr) {
            return 0;
        } else {
            return this->core->c;
        }
    }

 private:
    class Core {
     public:
        explicit Core(value_type* x) :
 p(x),
 c(1) {}
 ~Core() {
                if (p != nullptr) delete p;
            }
 size_t c;
 value_type* p;
            };
 Core* core;
};
};  // namespace smart_pointer