// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_FIXEDSIZEVECTOR_H
#define EIGEN_FIXEDSIZEVECTOR_H

namespace Eigen {

/** \class MaxSizeVector
  * \ingroup Core
  *
  * \brief The MaxSizeVector class.
  *
  * The %MaxSizeVector provides a subset of std::vector functionality.
  *
  * The goal is to provide basic std::vector operations when using
  * std::vector is not an option (e.g. on GPU or when compiling using
  * FMA/AVX, as this can cause either compilation failures or illegal
  * instruction failures).
  *
  * Beware: The constructors are not API compatible with these of
  * std::vector.
  */
template <typename T>
class MaxSizeVector
{
  public:
    // Construct a new MaxSizeVector, reserve n elements.
    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    explicit
    MaxSizeVector(size_t n)
        : reserve_(n), size_(0),
          data_(static_cast<T*>(internal::aligned_malloc(n * sizeof(T))))
    {
        for (size_t i = 0; i < n; ++i)
        {
            new (&data_[i]) T;
        }
    }

    // Construct a new MaxSizeVector, reserve and resize to n.
    // Copy the init value to all elements.
    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    MaxSizeVector(size_t n, const T& init)
        : reserve_(n), size_(n),
          data_(static_cast<T*>(internal::aligned_malloc(n * sizeof(T))))
    {
        for (size_t i = 0; i < n; ++i)
        {
            new (&data_[i]) T(init);
        }
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    ~MaxSizeVector()
    {
        for (size_t i = 0; i < size_; ++i)
        {
            data_[i].~T();
        }

        internal::aligned_free(data_);
    }

    void
    resize(size_t n)
    {
        eigen_assert(n <= reserve_);

        for (size_t i = size_; i < n; ++i)
        {
            new (&data_[i]) T;
        }

        for (size_t i = n; i < size_; ++i)
        {
            data_[i].~T();
        }

        size_ = n;
    }

    // Append new elements (up to reserved size).
    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    void
    push_back(const T& t)
    {
        eigen_assert(size_ < reserve_);
        data_[size_++] = t;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    const T&
    operator[] (size_t i) const
    {
        eigen_assert(i < size_);
        return data_[i];
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    T&
    operator[] (size_t i)
    {
        eigen_assert(i < size_);
        return data_[i];
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    T&
    back()
    {
        eigen_assert(size_ > 0);
        return data_[size_ - 1];
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    const T&
    back() const
    {
        eigen_assert(size_ > 0);
        return data_[size_ - 1];
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    void
    pop_back()
    {
        // NOTE: This does not destroy the value at the end the way
        // std::vector's version of pop_back() does.  That happens when
        // the Vector is destroyed.
        eigen_assert(size_ > 0);
        size_--;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    size_t
    size() const
    {
        return size_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    bool
    empty() const
    {
        return size_ == 0;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    T*
    data()
    {
        return data_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    const T*
    data() const
    {
        return data_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    T*
    begin()
    {
        return data_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    T*
    end()
    {
        return data_ + size_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    const T*
    begin() const
    {
        return data_;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
    const T*
    end() const
    {
        return data_ + size_;
    }

  private:
    size_t reserve_;
    size_t size_;
    T* data_;
};

}  // namespace Eigen

#endif  // EIGEN_FIXEDSIZEVECTOR_H
