#ifndef __AMSMATHUTIL25_AMSARRAY_HPP__
#define __AMSMATHUTIL25_AMSARRAY_HPP__

namespace ams
{
    typedef int64_t amsarray_size_t;
    static const int amsarray_success = amsmathutil25_success;
    static const int amsarray_failure = amsmathutil25_failure;

    static const int amsarray_sortthreadpsize = 5000;

    template<typename T> class amsarray
    {
        public:
        amsarray_size_t length;
        amsarray_size_t reserved;
        double growfactor;

        T *data;

        //Rule of 5 boilerplate
        amsarray();
        ~amsarray();
        amsarray(const amsarray<T>& other);
        amsarray(amsarray<T>&& other) noexcept;
        amsarray<T>& operator=(const amsarray<T> &other);
        amsarray<T>& operator=(amsarray<T> &&other) noexcept;

        //other initializers
        amsarray<T>& operator=(const std::vector<T>& other);
        amsarray(const std::initializer_list<T> initlist);
        amsarray<T>& operator=(const std::initializer_list<T> initlist);

        //casting datatypes
        template<typename T2> operator amsarray<T2>() const; //casting datatypes

        //resizing operator
        // returns: 
        //      amsarray_success on successfully allocating new memory, else
        //      amsarray_failure.
        int resize(amsarray_size_t _newlen);

        //resizing routine for insertions/erasures, 
        //adjusts reserved capacity according to the growth factor
        int resize_insert(amsarray_size_t _newlen);

        int reserve(amsarray_size_t _newcap);

        int shrink_to_fit();

        //returns the array length for routines that expect size() to be present
        const amsarray_size_t size() const; //returns length
        const amsarray_size_t capacity() const; //returns reserved

        T& operator[](amsarray_size_t ind);
        const T& operator[](amsarray_size_t ind) const;
        T& at(amsarray_size_t ind);
        const T& at(amsarray_size_t ind) const;

        int setall(const T &val);

        //Comparators
        bool operator==(const amsarray<T>& other) const;
        bool operator!=(const amsarray<T>& other) const;

        //inserts a value at (before) index ind
        //insert(0,val) would give {val, oldval0, oldval1, ...}
        //insert(N,val)
        int insert(amsarray_size_t ind, const T& val);
        
        //removes the value at index ind, and reduces array size by 1
        int erase(amsarray_size_t ind);

        //aliases to insert/erase operations
        int append(const T& val);
        int prepend(const T& val);
        int push_back(const T& val);
        int push_front(const T& val);
        T pop_back();
        T pop_front();

        //finds the first instance of val in the array
        amsarray_size_t find(const T& val);

        //finds the next instance of val, starting consideration at indstart
        amsarray_size_t findnext(amsarray_size_t indstart, const T& val);

        //finds where to insert a particular value such that the list remains ordered
        amsarray_size_t find_insert_ordered(const T& val);

        //subarray operations
        amsarray<T> subarray(amsarray_size_t ind1, amsarray_size_t ind2) const;

        //returns an array that is {thisarray[inds[0]],thisarray[inds[1]],...}
        amsarray<T> select(const amsarray<amsarray_size_t> &inds);
        
        //returns an array of indices that is a permutation which will sort
        //this array in ascending order
        amsarray<amsarray_size_t> sort_permutation();

        //sorts this array
        int sort();

        //returns an array that is this array in reverse order
        amsarray<T> reverse();


        //C++11 iterators
        // (magic boilerplate I don't really understand yet)
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator begin() {return data;}
        iterator end() {return data + length;}
        const_iterator begin() const {return data;}
        const_iterator end() const {return data + length;}
        const_iterator cbegin() const { return data; }
        const_iterator cend() const { return data+length;}

        //template specialized for particular array types
        void print(bool newline=0, int printstyle=0);
    };

// {0, 1, 2, .... N}
amsarray<amsarray_size_t> permutation_identity(amsarray_size_t _length);

void _debug_amsarray_print(amsarray<double> *array, bool newline=1,int printstyle=0);
void _debug_amsarray_print(amsarray<long> *array, bool newline=1,int printstyle=0);


}; //end namespace ams

#include <amsmathutil25/util/amsmathutil25_amsarray_impl.hpp>
#include <amsmathutil25/util/amsmathutil25_amsarray_sortimpl.hpp>

#endif

