// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ARRAY_H_
#define CTL_ARRAY_H_
#include "initializer_list.h"
#include "out_of_range.h"
#include "reverse_iterator.h"

namespace ctl {

template<typename T, size_t N>
struct array
{
    using value_type = T;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    using reference = value_type&;
    using const_reference = const value_type&;
    using pointer = value_type*;
    using const_pointer = const value_type*;
    using iterator = value_type*;
    using const_iterator = const value_type*;
    using reverse_iterator = ctl::reverse_iterator<iterator>;
    using const_reverse_iterator = ctl::reverse_iterator<const_iterator>;

    T elems[N];

    constexpr array() = default;

    constexpr array(std::initializer_list<T> init)
    {
        auto it = init.begin();
        for (size_t i = 0; i < N && it != init.end(); ++i, ++it)
            elems[i] = *it;
    }

    constexpr reference at(size_type pos)
    {
        if (pos >= N)
            throw ctl::out_of_range();
        return elems[pos];
    }

    constexpr const_reference at(size_type pos) const
    {
        if (pos >= N)
            throw ctl::out_of_range();
        return elems[pos];
    }

    constexpr reference operator[](size_type pos)
    {
        if (pos >= N)
            __builtin_trap();
        return elems[pos];
    }

    constexpr const_reference operator[](size_type pos) const
    {
        if (pos >= N)
            __builtin_trap();
        return elems[pos];
    }

    constexpr reference front()
    {
        return elems[0];
    }

    constexpr const_reference front() const
    {
        return elems[0];
    }

    constexpr reference back()
    {
        return elems[N - 1];
    }

    constexpr const_reference back() const
    {
        return elems[N - 1];
    }

    constexpr T* data() noexcept
    {
        return elems;
    }

    constexpr const T* data() const noexcept
    {
        return elems;
    }

    constexpr iterator begin() noexcept
    {
        return elems;
    }

    constexpr const_iterator begin() const noexcept
    {
        return elems;
    }

    constexpr iterator end() noexcept
    {
        return elems + N;
    }

    constexpr const_iterator end() const noexcept
    {
        return elems + N;
    }

    constexpr reverse_iterator rbegin() noexcept
    {
        return reverse_iterator(end());
    }

    constexpr const_reverse_iterator rbegin() const noexcept
    {
        return const_reverse_iterator(end());
    }

    constexpr reverse_iterator rend() noexcept
    {
        return reverse_iterator(begin());
    }

    constexpr const_reverse_iterator rend() const noexcept
    {
        return const_reverse_iterator(begin());
    }

    constexpr const_iterator cbegin() const noexcept
    {
        return begin();
    }

    constexpr const_iterator cend() const noexcept
    {
        return end();
    }

    constexpr const_reverse_iterator crbegin() const noexcept
    {
        return rbegin();
    }

    constexpr const_reverse_iterator crend() const noexcept
    {
        return rend();
    }

    constexpr bool empty() const noexcept
    {
        return N == 0;
    }

    constexpr size_type size() const noexcept
    {
        return N;
    }

    constexpr size_type max_size() const noexcept
    {
        return N;
    }

    void fill(const T& value)
    {
        for (size_type i = 0; i < N; ++i) {
            elems[i] = value;
        }
    }

    void swap(array& other) noexcept
    {
        for (size_type i = 0; i < N; ++i) {
            T temp = elems[i];
            elems[i] = other.elems[i];
            other.elems[i] = temp;
        }
    }
};

template<typename T, size_t N>
bool
operator==(const array<T, N>& lhs, const array<T, N>& rhs)
{
    for (size_t i = 0; i < N; ++i) {
        if (!(lhs[i] == rhs[i]))
            return false;
    }
    return true;
}

template<typename T, size_t N>
bool
operator!=(const array<T, N>& lhs, const array<T, N>& rhs)
{
    return !(lhs == rhs);
}

template<typename T, size_t N>
bool
operator<(const array<T, N>& lhs, const array<T, N>& rhs)
{
    for (size_t i = 0; i < N; ++i) {
        if (lhs[i] < rhs[i])
            return true;
        if (rhs[i] < lhs[i])
            return false;
    }
    return false;
}

template<typename T, size_t N>
bool
operator<=(const array<T, N>& lhs, const array<T, N>& rhs)
{
    return !(rhs < lhs);
}

template<typename T, size_t N>
bool
operator>(const array<T, N>& lhs, const array<T, N>& rhs)
{
    return rhs < lhs;
}

template<typename T, size_t N>
bool
operator>=(const array<T, N>& lhs, const array<T, N>& rhs)
{
    return !(lhs < rhs);
}

template<typename T, size_t N>
void
swap(array<T, N>& lhs, array<T, N>& rhs) noexcept
{
    lhs.swap(rhs);
}

} // namespace ctl

#endif // CTL_ARRAY_H_