/**
 * \file dnn/include/megdnn/thin/small_vector.h
 * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
 *
 * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */
//===- llvm/ADT/SmallVector.h - 'Normally small' vectors --------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the SmallVector class.
//
//===----------------------------------------------------------------------===//
/**
 * \file include/megdnn/thin/small_vector.h
 *
 * This file is part of MegDNN, a deep neural network run-time library
 * developed by Megvii.
 *
 * \brief thin megdnn function
 *
 * \copyright Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
 */
#pragma once

#include "megdnn/arch.h"

#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>

#include "megdnn/internal/visibility_prologue.h"

namespace megdnn {

class SmallVectorBase {
protected:
    void *m_begin_ptr, *m_end_ptr, *m_capacity_ptr;

    MEGDNN_NORETURN static void on_invalid_at(size_t idx, size_t size);

protected:
    SmallVectorBase(void* first_elm, size_t size)
            : m_begin_ptr(first_elm),
              m_end_ptr(first_elm),
              m_capacity_ptr(static_cast<char*>(first_elm) + size) {}

    void grow_pod(void* first_elm_ptr, size_t min_sz_in_bytes,
                  size_t type_size);

public:
    size_t size_in_bytes() const {
        return size_t(static_cast<char*>(m_end_ptr) -
                      static_cast<char*>(m_begin_ptr));
    }

    size_t capacity_in_bytes() const {
        return size_t(static_cast<char*>(m_capacity_ptr) -
                      static_cast<char*>(m_begin_ptr));
    }

    bool empty() const { return m_begin_ptr == m_end_ptr; }
};
template <typename T, typename = void>
class SmallVectorTemplateCommon : public SmallVectorBase {
private:
    template <typename, unsigned>
    friend struct SmallVectorStorage;

    using U = typename std::aligned_storage<sizeof(T), alignof(T)>::type;

    U m_first_elm;

protected:
    SmallVectorTemplateCommon(size_t size)
            : SmallVectorBase(&m_first_elm, size) {}

    void grow_pod(size_t min_sz_in_bytes, size_t type_size) {
        SmallVectorBase::grow_pod(&m_first_elm, min_sz_in_bytes, type_size);
    }

    bool is_small() {
        return m_begin_ptr == static_cast<const void*>(&m_first_elm);
    }

    void reset_to_small() {
        m_begin_ptr = m_end_ptr = m_capacity_ptr = &m_first_elm;
    }

    void set_end(T* p) { m_end_ptr = p; }

public:
    using size_type = size_t;
    using difference_type = std::ptrdiff_t;
    using value_type = T;
    using iterator = T*;
    using const_iterator = const T*;

    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using const_pointer = const T*;

    size_t capacity() const { return capacity_ptr() - begin(); }

protected:
    iterator capacity_ptr() { return static_cast<iterator>(m_capacity_ptr); }
    const_iterator capacity_ptr() const {
        return static_cast<const_iterator>(m_capacity_ptr);
    }

public:
    // forwarding iterator creation
    iterator begin() { return static_cast<iterator>(m_begin_ptr); }
    const_iterator begin() const {
        return static_cast<const_iterator>(m_begin_ptr);
    }
    const_iterator cbegin() const {
        return static_cast<const_iterator>(m_begin_ptr);
    }

    iterator end() { return static_cast<iterator>(m_end_ptr); }
    const_iterator end() const {
        return static_cast<const_iterator>(m_end_ptr);
    }
    const_iterator cend() const {
        return static_cast<const_iterator>(m_end_ptr);
    }

    reference at(size_type idx) {
        if (idx >= size()) {
            on_invalid_at(idx, size());
        }
        return begin()[idx];
    }
    const_reference at(size_type idx) const {
        if (idx >= size()) {
            on_invalid_at(idx, size());
        }
        return begin()[idx];
    }

    reference operator[](size_type idx) { return begin()[idx]; }
    const_reference operator[](size_type idx) const { return begin()[idx]; }

    reference front() { return begin()[0]; }
    const_reference front() const { return begin()[0]; }

    reference back() { return rbegin()[0]; }
    const_reference back() const { return rbegin()[0]; }

    // reverse iterator creation method.
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator(end());
    }
    reverse_iterator rend() { return reverse_iterator(begin()); }
    const_reverse_iterator rend() const {
        return const_reverse_iterator(begin());
    }

    pointer data() { return pointer(begin()); }
    const_pointer data() const { return const_pointer(begin()); }

    size_type size() const { return end() - begin(); }
    size_type max_size() const {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    template <typename in_iter>
    in_iter find(in_iter first, in_iter last, const T& value) const {
        while (first != last) {
            if (*first == value)
                return first;
            ++first;
        }
        return last;
    }
};
template <typename T, bool is_pod>
class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> {
protected:
    SmallVectorTemplateBase(size_t size) : SmallVectorTemplateCommon<T>(size) {}

    static void destroy_range(T* start, T* end) {
        while (start != end) {
            --end;
            end->~T();
        }
    }

    template <typename It1, typename It2>
    static void uninitialized_move(It1 first, It1 last, It2 dest) {
        std::uninitialized_copy(std::make_move_iterator(first),
                                std::make_move_iterator(last), dest);
    }

    template <typename It1, typename It2>
    static void uninitialized_copy(It1 first, It1 last, It2 dest) {
        std::uninitialized_copy(first, last, dest);
    }

    void grow(size_t min_sz = 0);

public:
    void push_back(const T& _elm) {
        if (megdnn_unlikely(this->m_end_ptr >= this->m_capacity_ptr)) {
            T elm = _elm;
            this->grow();
            new (static_cast<void*>(this->end())) T(std::move(elm));
        } else {
            new (static_cast<void*>(this->end())) T(_elm);
        }
        this->set_end(this->end() + 1);
    }

    void push_back(T&& elm) {
        if (megdnn_unlikely(this->m_end_ptr >= this->m_capacity_ptr)) {
            this->grow();
        }
        new (static_cast<void*>(this->end())) T(std::move(elm));
        this->set_end(this->end() + 1);
    }

    void pop_back() {
        this->set_end(this->end() - 1);
        this->end()->~T();
    }
};
template <typename T, bool is_pod>
void SmallVectorTemplateBase<T, is_pod>::grow(size_t min_sz) {
    size_t cur_capacity = this->capacity();
    size_t cur_sz = this->size();
    size_t new_capacity = (cur_capacity + 2) * 2;
    if (new_capacity < min_sz) {
        new_capacity = min_sz;
    }
    T* elms = static_cast<T*>(malloc(new_capacity * sizeof(T)));

    this->uninitialized_move(this->begin(), this->end(), elms);

    this->destroy_range(this->begin(), this->end());

    if (!this->is_small()) {
        free(this->begin());
    }

    this->m_begin_ptr = elms;
    this->set_end(elms + cur_sz);
    this->m_capacity_ptr = this->begin() + new_capacity;
}

template <typename T>
class SmallVectorTemplateBase<T, true> : public SmallVectorTemplateCommon<T> {
protected:
    SmallVectorTemplateBase(size_t size) : SmallVectorTemplateCommon<T>(size) {}

    static void destroy_range(T*, T*) {}

    template <typename It1, typename It2>
    static void uninitialized_move(It1 first, It1 last, It2 dest) {
        uninitialized_copy(first, last, dest);
    }

    template <typename It1, typename It2>
    static void uninitialized_copy(It1 first, It1 last, It2 dest) {
        std::uninitialized_copy(first, last, dest);
    }

    template <typename T1, typename T2>
    static void uninitialized_copy(
            T1* first, T1* last, T2* dest,
            typename std::enable_if<std::is_same<
                    typename std::remove_const<T1>::type, T2>::value>::type* =
                    nullptr) {
        if (first != last)
            memcpy(dest, first, (last - first) * sizeof(T));
    }

    void grow(size_t min_sz = 0) {
        this->grow_pod(min_sz * sizeof(T), sizeof(T));
    }

public:
    void push_back(const T& _elm) {
        if (megdnn_unlikely(this->m_end_ptr >= this->m_capacity_ptr)) {
            T elm = _elm;
            this->grow();
            memcpy(this->end(), &elm, sizeof(T));
        } else {
            memcpy(this->end(), &_elm, sizeof(T));
        }
        this->set_end(this->end() + 1);
    }

    void pop_back() { this->set_end(this->end() - 1); }
};

/*!
 * \brief the implementation class of SmallVector
 *
 * SmallVector<T, N> can be converted to SmallVectorImpl<T> to erase N
 */
template <typename T>
class SmallVectorImpl
        : public SmallVectorTemplateBase<T, std::is_pod<T>::value> {
    using SuperClass = SmallVectorTemplateBase<T, std::is_pod<T>::value>;

public:
    using iterator = typename SuperClass::iterator;
    using const_iterator = typename SuperClass::const_iterator;
    using size_type = typename SuperClass::size_type;

protected:
    explicit SmallVectorImpl(unsigned n)
            : SmallVectorTemplateBase<T, std::is_pod<T>::value>(n * sizeof(T)) {
    }

public:
    SmallVectorImpl(const SmallVectorImpl&) = delete;

    ~SmallVectorImpl() {
        this->destroy_range(this->begin(), this->end());

        if (!this->is_small())
            free(this->begin());
    }

    void clear() {
        this->destroy_range(this->begin(), this->end());
        this->m_end_ptr = this->m_begin_ptr;
    }

    void resize(size_type n) {
        if (n < this->size()) {
            this->destroy_range(this->begin() + n, this->end());
            this->set_end(this->begin() + n);
        } else if (n > this->size()) {
            if (this->capacity() < n)
                this->grow(n);
            for (auto it = this->end(), end = this->begin() + n; it != end;
                 ++it)
                new (&*it) T();
            this->set_end(this->begin() + n);
        }
    }

    void resize(size_type n, const T& _nv) {
        T nv = _nv;
        if (n < this->size()) {
            this->destroy_range(this->begin() + n, this->end());
            this->set_end(this->begin() + n);
        } else if (n > this->size()) {
            if (this->capacity() < n)
                this->grow(n);
            std::uninitialized_fill(this->end(), this->begin() + n, nv);
            this->set_end(this->begin() + n);
        }
    }

    void reserve(size_type n) {
        if (this->capacity() < n) {
            this->grow(n);
        }
    }

    T pop_back_val() {
        T result = std::move(this->back());
        this->pop_back();
        return result;
    }

    void swap(SmallVectorImpl<T>& rhs);

    /// Add the specified range to the end of the SmallVector.
    template <typename in_iter,
              typename = typename std::enable_if<std::is_convertible<
                      typename std::iterator_traits<in_iter>::iterator_category,
                      std::input_iterator_tag>::value>::type>
    void append(in_iter in_start, in_iter in_end) {
        size_type num_inputs = std::distance(in_start, in_end);
        // Grow allocated space if needed.
        if (num_inputs > size_type(this->capacity_ptr() - this->end()))
            this->grow(this->size() + num_inputs);

        // Copy the new elements over.
        this->uninitialized_copy(in_start, in_end, this->end());
        this->set_end(this->end() + num_inputs);
    }

    /// Add the specified range to the end of the SmallVector.
    void append(size_type num_inputs, const T& _elm) {
        T elm = _elm;
        // Grow allocated space if needed.
        if (num_inputs > size_type(this->capacity_ptr() - this->end()))
            this->grow(this->size() + num_inputs);

        // Copy the new elements over.
        std::uninitialized_fill_n(this->end(), num_inputs, elm);
        this->set_end(this->end() + num_inputs);
    }

    void append(std::initializer_list<T> init_list) {
        append(init_list.begin(), init_list.end());
    }

    // FIXME: Consider assigning over existing elements, rather than clearing &
    // re-initializing them - for all assign(...) variants.

    void assign(size_type num_elms, const T& _elm) {
        T elm = _elm;
        clear();
        if (this->capacity() < num_elms)
            this->grow(num_elms);
        this->set_end(this->begin() + num_elms);
        std::uninitialized_fill(this->begin(), this->end(), elm);
    }

    template <typename in_iter,
              typename = typename std::enable_if<std::is_convertible<
                      typename std::iterator_traits<in_iter>::iterator_category,
                      std::input_iterator_tag>::value>::type>
    void assign(in_iter in_start, in_iter in_end) {
        clear();
        append(in_start, in_end);
    }

    void assign(std::initializer_list<T> init_list) {
        clear();
        append(init_list);
    }

    iterator erase(const_iterator cit) {
        // Just cast away constness because this is a non-const member function.
        iterator it = const_cast<iterator>(cit);
        iterator n = it;
        // Shift all elms down one.
        std::move(it + 1, this->end(), it);
        // Drop the last elm.
        this->pop_back();
        return (n);
    }

    iterator erase(const_iterator c_first, const_iterator c_last) {
        // Just cast away constness because this is a non-const member function.
        iterator first = const_cast<iterator>(c_first);
        iterator last = const_cast<iterator>(c_last);
        iterator n = first;
        // Shift all elms down.
        iterator it = std::move(last, this->end(), first);
        // Drop the last elms.
        this->destroy_range(it, this->end());
        this->set_end(it);
        return (n);
    }

    iterator insert(iterator it, T&& elm) {
        if (it == this->end()) {  // Important special case for empty vector.
            this->push_back(std::move(elm));
            return this->end() - 1;
        }

        if (this->m_end_ptr >= this->m_capacity_ptr) {
            size_t elm_idx = it - this->begin();
            this->grow();
            it = this->begin() + elm_idx;
        }

        new (static_cast<void*>(this->end())) T(std::move(this->back()));
        // Push everything else over.
        std::move_backward(it, this->end() - 1, this->end());
        this->set_end(this->end() + 1);

        // If we just moved the element we're inserting, be sure to update
        // the reference.
        T* elm_ptr = &elm;
        if (it <= elm_ptr && elm_ptr < this->m_end_ptr)
            ++elm_ptr;

        *it = std::move(*elm_ptr);
        return it;
    }

    iterator insert(iterator it, const T& _elm) {
        if (it == this->end()) {  // Important special case for empty vector.
            this->push_back(_elm);
            return this->end() - 1;
        }
        T elm = _elm;
        if (this->m_end_ptr >= this->m_capacity_ptr) {
            size_t elm_idx = it - this->begin();
            this->grow();
            it = this->begin() + elm_idx;
        }
        new (static_cast<void*>(this->end())) T(std::move(this->back()));
        // Push everything else over.
        std::move_backward(it, this->end() - 1, this->end());
        this->set_end(this->end() + 1);

        // If we just moved the element we're inserting, be sure to update
        // the reference.
        const T* elm_ptr = &elm;
        if (it <= elm_ptr && elm_ptr < this->m_end_ptr)
            ++elm_ptr;

        *it = *elm_ptr;
        return it;
    }

    iterator insert(iterator it, size_type num_to_insert, const T& _elm) {
        // Convert iterator to elm# to avoid invalidating iterator
        // when we reserve()
        size_t elm_idx = it - this->begin();

        if (it == this->end()) {  // Important special case for empty vector.
            append(num_to_insert, _elm);
            return this->begin() + elm_idx;
        }

        T elm = _elm;

        // Ensure there is enough space.
        reserve(this->size() + num_to_insert);

        // Uninvalidate the iterator.
        it = this->begin() + elm_idx;

        // If there are more elements between the insertion point and
        // the end of the range than there are being inserted,
        // we can use a simple approach to insertion.
        // Since we already reserved space, we know that this won't
        // reallocate the vector.
        if (size_t(this->end() - it) >= num_to_insert) {
            T* old_end = this->end();
            append(std::move_iterator<iterator>(this->end() - num_to_insert),
                   std::move_iterator<iterator>(this->end()));

            // Copy the existing elements that get replaced.
            std::move_backward(it, old_end - num_to_insert, old_end);

            std::fill_n(it, num_to_insert, elm);
            return it;
        }

        // Otherwise, we're inserting more elements than exist already,
        // and we're not inserting at the end.

        // Move over the elements that we're about to overwrite.
        T* old_end = this->end();
        this->set_end(this->end() + num_to_insert);
        size_t num_overwritten = old_end - it;
        this->uninitialized_move(it, old_end, this->end() - num_overwritten);

        // Replace the overwritten part.
        std::fill_n(it, num_overwritten, elm);

        // Insert the non-overwritten middle part.
        std::uninitialized_fill_n(old_end, num_to_insert - num_overwritten,
                                  elm);
        return it;
    }

    template <
            typename IterType,
            typename = typename std::enable_if<std::is_convertible<
                    typename std::iterator_traits<IterType>::iterator_category,
                    std::input_iterator_tag>::value>::type>
    iterator insert(iterator it, IterType from, IterType to) {
        // Convert iterator to elm# to avoid invalidating iterator
        // when we reserve()
        size_t elm_idx = it - this->begin();

        if (it == this->end()) {  // Important special case for empty vector.
            append(from, to);
            return this->begin() + elm_idx;
        }

        size_t num_to_insert = std::distance(from, to);

        // Ensure there is enough space.
        reserve(this->size() + num_to_insert);

        // Uninvalidate the iterator.
        it = this->begin() + elm_idx;

        // If there are more elements between the insertion point and
        // the end of the range than there are being inserted,
        // we can use a simple approach to insertion.
        // Since we already reserved space, we know that this won't
        // reallocate the vector.
        if (size_t(this->end() - it) >= num_to_insert) {
            T* old_end = this->end();
            append(std::move_iterator<iterator>(this->end() - num_to_insert),
                   std::move_iterator<iterator>(this->end()));

            // Copy the existing elements that get replaced.
            std::move_backward(it, old_end - num_to_insert, old_end);

            std::copy(from, to, it);
            return it;
        }

        // Otherwise, we're inserting more elements than exist already,
        // and we're not inserting at the end.

        // Move over the elements that we're about to overwrite.
        T* old_end = this->end();
        this->set_end(this->end() + num_to_insert);
        size_t num_overwritten = old_end - it;
        this->uninitialized_move(it, old_end, this->end() - num_overwritten);

        // Replace the overwritten part.
        for (T* iter = it; num_overwritten > 0; --num_overwritten) {
            *iter = *from;
            ++iter;
            ++from;
        }

        // Insert the non-overwritten middle part.
        this->uninitialized_copy(from, to, old_end);
        return it;
    }

    void insert(iterator it, std::initializer_list<T> init_list) {
        insert(it, init_list.begin(), init_list.end());
    }

    template <typename... ArgTypes>
    void emplace_back(ArgTypes&&... args) {
        if (megdnn_unlikely(this->m_end_ptr >= this->m_capacity_ptr)) {
            this->grow();
        }
        new (static_cast<void*>(this->end()))
                T(std::forward<ArgTypes>(args)...);
        this->set_end(this->end() + 1);
    }

    SmallVectorImpl& operator=(const SmallVectorImpl& rhs);

    SmallVectorImpl& operator=(SmallVectorImpl&& rhs);

    bool operator==(const SmallVectorImpl<T>& rhs) const {
        if (this->size() != rhs.size())
            return false;
        return std::equal(this->begin(), this->end(), rhs.begin());
    }

    bool operator!=(const SmallVectorImpl<T>& rhs) const {
        return !(*this == rhs);
    }

    bool operator<(const SmallVectorImpl<T>& rhs) const {
        return std::lexicographical_compare(this->begin(), this->end(),
                                            rhs.begin(), rhs.end());
    }
};

template <typename T>
void SmallVectorImpl<T>::swap(SmallVectorImpl<T>& rhs) {
    if (this == &rhs)
        return;

    // We can only avoid copying elements if neither vector is small.
    if (!this->is_small() && !rhs.is_small()) {
        std::swap(this->m_begin_ptr, rhs.m_begin_ptr);
        std::swap(this->m_end_ptr, rhs.m_end_ptr);
        std::swap(this->m_capacity_ptr, rhs.m_capacity_ptr);
        return;
    }
    if (rhs.size() > this->capacity())
        this->grow(rhs.size());
    if (this->size() > rhs.capacity())
        rhs.grow(this->size());

    // Swap the shared elements.
    size_t num_shared = this->size();
    if (num_shared > rhs.size())
        num_shared = rhs.size();
    for (size_type i = 0; i != num_shared; ++i)
        std::swap((*this)[i], rhs[i]);

    // Copy over the extra elms.
    if (this->size() > rhs.size()) {
        size_t elm_diff = this->size() - rhs.size();
        this->uninitialized_move(this->begin() + num_shared, this->end(),
                                 rhs.end());
        rhs.set_end(rhs.end() + elm_diff);
        this->destroy_range(this->begin() + num_shared, this->end());
        this->set_end(this->begin() + num_shared);
    } else if (rhs.size() > this->size()) {
        size_t elm_diff = rhs.size() - this->size();
        this->uninitialized_move(rhs.begin() + num_shared, rhs.end(),
                                 this->end());
        this->set_end(this->end() + elm_diff);
        this->destroy_range(rhs.begin() + num_shared, rhs.end());
        rhs.set_end(rhs.begin() + num_shared);
    }
}

template <typename T>
SmallVectorImpl<T>& SmallVectorImpl<T>::operator=(
        const SmallVectorImpl<T>& rhs) {
    if (this == &rhs)
        return *this;
    size_t rhs_sz = rhs.size();
    size_t cur_sz = this->size();
    if (cur_sz >= rhs_sz) {
        iterator new_end;
        if (rhs_sz) {
            new_end = std::copy(rhs.begin(), rhs.end(), this->begin());
        } else {
            new_end = this->begin();
        }
        this->destroy_range(new_end, this->end());
        this->set_end(new_end);
        return *this;
    }
    if (this->capacity() < rhs_sz) {
        // save time for no copy when growing
        this->destroy_range(this->begin(), this->end());
        this->set_end(this->begin());
        cur_sz = 0;
        this->grow(rhs_sz);
    } else if (cur_sz) {
        std::copy(rhs.begin(), rhs.begin() + cur_sz, this->begin());
    }
    std::uninitialized_copy(rhs.begin() + cur_sz, rhs.end(),
                            this->begin() + cur_sz);
    this->set_end(this->begin() + rhs_sz);
    return *this;
}

template <typename T>
SmallVectorImpl<T>& SmallVectorImpl<T>::operator=(SmallVectorImpl<T>&& rhs) {
    // avoid self assignment
    if (this == &rhs)
        return *this;

    // copy ptr when rhs is small
    if (!rhs.is_small()) {
        this->destroy_range(this->begin(), this->end());
        if (!this->is_small())
            free(this->begin());
        this->m_begin_ptr = rhs.m_begin_ptr;
        this->m_end_ptr = rhs.m_end_ptr;
        this->m_capacity_ptr = rhs.m_capacity_ptr;
        rhs.reset_to_small();
        return *this;
    }

    size_t rhs_sz = rhs.size();
    size_t cur_sz = this->size();
    if (cur_sz >= rhs_sz) {
        iterator new_end = this->begin();
        if (rhs_sz) {
            new_end = std::move(rhs.begin(), rhs.end(), new_end);
        }
        this->destroy_range(new_end, this->end());
        this->set_end(new_end);
        rhs.clear();
        return *this;
    }
    if (this->capacity() < rhs_sz) {
        this->destroy_range(this->begin(), this->end());
        this->set_end(this->begin());
        cur_sz = 0;
        this->grow(rhs_sz);
    } else if (cur_sz) {
        std::move(rhs.begin(), rhs.begin() + cur_sz, this->begin());
    }

    this->uninitialized_move(rhs.begin() + cur_sz, rhs.end(),
                             this->begin() + cur_sz);

    this->set_end(this->begin() + rhs_sz);

    rhs.clear();
    return *this;
}
template <typename T, unsigned N>
struct SmallVectorStorage {
    typename SmallVectorTemplateCommon<T>::U inline_elms[N - 1];
};
template <typename T>
struct SmallVectorStorage<T, 1> {};
template <typename T>
struct SmallVectorStorage<T, 0> {};

/*!
 * \brief This is a 'vector' (really, a variable-sized array), optimized for the
 *      case when the array is small.
 *
 * It contains some number of elements in-place,
 * which allows it to avoid heap allocation when the actual number of elements
 * is below that threshold. This allows normal "small" cases to be fast without
 * losing generality for large inputs.
 *
 * Note that this does not attempt to be exception safe.
 *
 * SmallVector<T, N>& can be converted to SmallVectorImpl<T>& to erase the
 * template param \p N; this is useful for function params.
 *
 * \tparam T emelment type
 * \tparam N number of elements to be stored in the class object
 */
template <typename T, unsigned N = 4>
class SmallVector : public SmallVectorImpl<T> {
    SmallVectorStorage<T, N> m_storage;

public:
    SmallVector() : SmallVectorImpl<T>(N) {}

    explicit SmallVector(size_t size, const T& value = T())
            : SmallVectorImpl<T>(N) {
        this->assign(size, value);
    }

    template <
            typename IterType,
            typename = typename std::enable_if<std::is_convertible<
                    typename std::iterator_traits<IterType>::iterator_category,
                    std::input_iterator_tag>::value>::type>
    SmallVector(IterType first, IterType last) : SmallVectorImpl<T>(N) {
        this->append(first, last);
    }

    SmallVector(std::initializer_list<T> init_list) : SmallVectorImpl<T>(N) {
        this->assign(init_list);
    }

    SmallVector(const SmallVector& rhs) : SmallVectorImpl<T>(N) {
        if (!rhs.empty())
            SmallVectorImpl<T>::operator=(rhs);
    }

    ~SmallVector() {}

    const SmallVector& operator=(const SmallVector& rhs) {
        SmallVectorImpl<T>::operator=(rhs);
        return *this;
    }

    SmallVector(SmallVector&& rhs) : SmallVectorImpl<T>(N) {
        if (!rhs.empty())
            SmallVectorImpl<T>::operator=(std::move(rhs));
    }

    SmallVector(SmallVectorImpl<T>&& rhs) : SmallVectorImpl<T>(N) {
        if (!rhs.empty())
            SmallVectorImpl<T>::operator=(std::move(rhs));
    }

    const SmallVector& operator=(SmallVector&& rhs) {
        SmallVectorImpl<T>::operator=(std::move(rhs));
        return *this;
    }

    const SmallVector& operator=(SmallVectorImpl<T>&& rhs) {
        SmallVectorImpl<T>::operator=(std::move(rhs));
        return *this;
    }

    const SmallVector& operator=(std::initializer_list<T> init_list) {
        this->assign(init_list);
        return *this;
    }
};

template <typename T, unsigned n>
static inline size_t capacity_in_bytes(const SmallVector<T, n>& vec) {
    return vec.capacity_in_bytes();
}

template <typename T>
inline typename SmallVectorImpl<T>::const_iterator find(
        const SmallVectorImpl<T>& vec, const T& value) {
    return vec.find(vec.begin(), vec.end(), value);
}

}  // end namespace megdnn

#include "megdnn/internal/visibility_epilogue.h"

namespace std {

/// Implement std::swap in terms of SmallVector swap.
template <typename T>
inline void swap(megdnn::SmallVectorImpl<T>& lhs,
                 megdnn::SmallVectorImpl<T>& rhs) {
    lhs.swap(rhs);
}

/// Implement std::swap in terms of SmallVector swap.
template <typename T, unsigned N>
inline void swap(megdnn::SmallVector<T, N>& lhs,
                 megdnn::SmallVector<T, N>& rhs) {
    lhs.swap(rhs);
}
}  // end namespace std

// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}
