#include <memory>
#include <algorithm>
#include <cassert>
#include <iostream>

template <typename T, std::size_t N>
class tiny_buffer {
public:

    typedef T value_type;
    typedef value_type * iterator;
    typedef const value_type * const_iterator;

    tiny_buffer()
        : size_(0)
    {}

    tiny_buffer(std::size_t n, const value_type & v)
        : size_(n)
    {
        std::uninitialized_fill(begin(), end(), v);
    }
    
    tiny_buffer(std::size_t n)
        : size_(n)
    {
        std::uninitialized_fill(begin(), end(), (value_type()));
    }

    ~tiny_buffer()
    { shrink(0); }

    template <typename S, std::size_t M>
    tiny_buffer(const tiny_buffer<S, M> & rhs)
        : size_(rhs.size())
    {
        check_size(size_);
        std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
    }

    template <typename S, std::size_t M>
    tiny_buffer & operator=(const tiny_buffer<S, M> & rhs)
    {
        const std::size_t new_size = rhs.size();

        if (new_size < size_) {
            shrink(new_size);
            std::copy(rhs.begin(), rhs.end(), begin());
        } else {
            check_size(new_size);
            std::copy(rhs.begin(), rhs.begin() + size_, begin());
            std::uninitialized_copy(rhs.begin() + size_, rhs.end(), end());
            size_ = new_size;
        }
    }

    value_type & operator[](std::size_t i)
    { return data()[i]; }

    const value_type & operator[](std::size_t i) const
    { return data()[i]; }

    value_type & at(std::size_t i)
    {
        check_index(i);
        return (*this)[i];
    }

    const value_type & at(std::size_t i) const
    {
        check_index(i);
        return (*this)[i];
    }

    iterator begin()
    { return data(); }

    iterator end()
    { return data() + size_; }

    const_iterator begin() const
    { return data(); }

    const_iterator end() const
    { return data() + size_; }

    std::size_t size() const
    { return size_; }

    void push_back(const value_type & v)
    {
        check_size(size_ + 1);
        new(data() + size_) value_type(v);
        ++size_;
    }

    void shrink(std::size_t new_size)
    {
        assert(new_size <= size_);
        for (std::size_t i = new_size; i < size_; ++i) {
            data()[i].~value_type();
        }
        size_ = new_size;
    }

private:
    char raw_data_[sizeof(T) * N];
    std::size_t size_;

    T * data()
    { return reinterpret_cast<T*>(raw_data_); }

    const T * data() const
    { return reinterpret_cast<const T*>(raw_data_); }

    void check_index(std::size_t i) const
    { assert(i < size() && "Buffer index out of bounds"); }

    void check_size(std::size_t s) const
    { assert(s <= N && "Buffer size exceeds capacity"); }
};

// Testing

static int cnt;

struct tracer
{
    tracer() { cnt++; std::cout << "ctor\n"; }
    tracer(const tracer &) { cnt++; std::cout << "copy ctor\n"; };
    ~tracer() { cnt--; std::cout << "dtor\n"; }
};

int main()
{
    tiny_buffer<int, 5> a;
    assert(a.size() == 0);
    a.push_back(3);
    assert(a[0] == 3);
    assert(a.size() == 1);

    tiny_buffer<short, 3> b(3, 1);
    a = b;

    assert(b.size() == 3);
    assert(a.size() == b.size());
    assert(a[0] == 1 && a[1] == 1 && a[2] == 1);

    tiny_buffer<int, 5> c(b);
    assert(c.size() == b.size());
    assert(c[0] == 1 && c[1] == 1 && c[2] == 1);

    tiny_buffer<tracer, 3> ts(2);
    assert(ts.size() == 2);
    assert(cnt == ts.size());

    ts.shrink(1);
    assert(ts.size() == 1);
    assert(cnt == 1);

    ts.push_back((tracer()));

    return 0;
}
