#ifndef __AMSCPPNARRAY_IMPLOPER_HPP__
#define __AMSCPPNARRAY_IMPLOPER_HPP__

namespace ams
{
namespace narray
{

    template<typename T> void narray_operator_comp_tf( 
        const narray<T> *a,
        const narray<T> *b, 
        narray<bool> *cmp,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        cmp->data[threadnum] = 1;
        for(I=I0;I<I1;I++)
        {
            if(a->data[I]!=b->data[I])
            {
                cmp->data[threadnum] = 0;
                break;
            }
        }
        return;
    }

    template<typename T> bool narray<T>::operator==(const narray<T>& other) const
    {
        bool ret = 1;
        narray<bool> cmp;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;

        if(this->length != other.length)
        {
            ret = 0;
            return ret;
        }
        if(this->length == 0)
        {
            ret = 1;
            return ret;
        }
        if(this->length < narray_thread_sz)
        {
            for(I=0;I<this->length;I++)
            {
                if(this->data[I]!=other.data[I])
                {
                    ret = 0;
                    break;
                }
            }
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            cmp.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                //printf("debug: launching thread %d\n",J);
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_comp_tf<T>,
                    this,
                    &other,
                    &cmp,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }

            ret = 1;
            for(J=0;J<nthreads;J++)
            {
                if(cmp[J]==0)
                {
                    ret = 0;
                    break;
                }
            }

            cmp.resize(0);
        }

        return ret;
    }

    template<typename T> bool narray<T>::operator!=(const narray<T>& other) const
    {
        return !(*this==other);
    }

    template<typename T> void narray_operator_add_tf( 
        const narray<T> *a,
        const narray<T> *b, 
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] + b->data[I];
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator+(const narray<T>& other) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length!=other.length)
        {
            ret.resize(0);
            printf("narray<T>::operator+:: error - array sizes %ld and %ld are not the same.\n",(long)this->length,(long)other.length);
        }
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] + other.data[I];
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                //printf("debug: launching thread %d\n",J);
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_add_tf<T>,
                    this,
                    &other,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_sub_tf( 
        const narray<T> *a,
        const narray<T> *b, 
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] - b->data[I];
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator-(const narray<T>& other) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length!=other.length)
        {
            ret.resize(0);
            printf("narray<T>::operator-:: error - array sizes %ld and %ld are not the same.\n",(long)this->length,(long)other.length);
        }
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] - other.data[I];
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_sub_tf<T>,
                    this,
                    &other,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_mult_tf( 
        const narray<T> *a,
        const narray<T> *b, 
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] * b->data[I];
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator*(const narray<T>& other) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length!=other.length)
        {
            ret.resize(0);
            printf("narray<T>::operator*:: error - array sizes %ld and %ld are not the same.\n",(long)this->length,(long)other.length);
        }
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] * other.data[I];
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_mult_tf<T>,
                    this,
                    &other,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_div_tf( 
        const narray<T> *a,
        const narray<T> *b, 
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] / b->data[I];
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator/(const narray<T>& other) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length!=other.length)
        {
            ret.resize(0);
            printf("narray<T>::operator/:: error - array sizes %ld and %ld are not the same.\n",(long)this->length,(long)other.length);
        }
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] / other.data[I];
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_div_tf<T>,
                    this,
                    &other,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }
    
    template<typename T> void narray_operator_add2_tf( 
        const narray<T> *a,
        const T val,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] + val;
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator+(const T val) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] + val;
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_add2_tf<T>,
                    this,
                    val,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_sub2_tf( 
        const narray<T> *a,
        const T val,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] - val;
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator-(const T val) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] - val;
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_sub2_tf<T>,
                    this,
                    val,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_mult2_tf( 
        const narray<T> *a,
        const T val,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] * val;
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator*(const T val) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] * val;
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_mult2_tf<T>,
                    this,
                    val,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_operator_div2_tf( 
        const narray<T> *a,
        const T val,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = a->data[I] / val;
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::operator/(const T val) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = this->data[I] / val;
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_div2_tf<T>,
                    this,
                    val,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }


    template<typename T> void narray_operator_negative_tf( 
        const narray<T> *a,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = -a->data[I];
        }
    
        return;
    }

    template<class T> narray<T> operator-(const narray<T> &n)
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(n.length);
    
        if(n.length<narray_thread_sz)
        {
            for(I=0;I<n.length;I++) ret.data[I] = -n.data[I];
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_operator_negative_tf<T>,
                    &n,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_applyfn_tf( 
        const narray<T> *a,
        T (*fnptr)(T),
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = a->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? a->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            out->data[I] = fnptr(a->data[I]);
        }
    
        return;
    }

    template<typename T> narray<T> narray<T>::applyfn(T (*fnptr)(T)) const
    {
        narray<T> ret;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        ret.resize(this->length);
    
        if(this->length<narray_thread_sz)
        {
            for(I=0;I<this->length;I++) ret.data[I] = fnptr(this->data[I]);
        }
        else
        {
            //threaded operation
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_applyfn_tf<T>,
                    this,
                    fnptr,
                    &ret,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_min_tf( 
        const narray<T> *in,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = in->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? in->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            if(in->data[I]<out->data[threadnum])
                out->data[threadnum] = in->data[I];
        }
    
        return;
    }

    template<typename T> T narray<T>::min() const
    {
        T ret = T();
        narray<T> minarr;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length<=0)
        {
            return ret;
        }
    
        if(this->length<narray_thread_sz)
        {
            ret = this->data[0];
            for(I=0;I<this->length;I++)
            {
                if(this->data[I]<ret)
                {
                    ret = this->data[I];
                }
            }
        }
        else
        {
            //threaded operation
            
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            minarr.resize(nthreads);
            minarr.setall(this->data[0]);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_min_tf<T>,
                    this,
                    &minarr,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
            ret = this->data[0];
            for(J=0;J<nthreads;J++)
            {
                if(minarr[J]<ret)
                {
                    ret = minarr[J];
                }
            }
        }
        return ret;
    }

    template<typename T> void narray_max_tf( 
        const narray<T> *in,
        narray<T> *out,
        int threadnum, int nthreads
    )
    {
        int I0,I1,Is,I;
    
        Is = in->length/nthreads;
        I0 = Is*threadnum;
        I1 = (threadnum>=(nthreads-1))? in->length: Is*(threadnum+1);
        for(I=I0;I<I1;I++)
        {
            if(in->data[I]>out->data[threadnum])
                out->data[threadnum] = in->data[I];
        }
    
        return;
    }

    template<typename T> T narray<T>::max() const
    {
        T ret = T();
        narray<T> maxarr;
        narray_size_t I;
        int J;
        int nthreads;
        std::vector<std::thread*> threads;
    
        if(this->length<=0)
        {
            return ret;
        }
    
        if(this->length<narray_thread_sz)
        {
            ret = this->data[0];
            for(I=0;I<this->length;I++)
            {
                if(this->data[I]>ret)
                {
                    ret = this->data[I];
                }
            }
        }
        else
        {
            //threaded operation
            
            nthreads = std::thread::hardware_concurrency();
            nthreads = (nthreads<1) ? 1 : nthreads;
            nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
            //if(nthreads<1) nthreads=1;
            //if(nthreads>narray_max_threads) nthreads = narray_max_threads;
            threads.resize(nthreads);
            maxarr.resize(nthreads);
            maxarr.setall(this->data[0]);
            for(J=0;J<nthreads;J++)
            {
                threads[J] = new(std::nothrow) std::thread(
                    &narray_max_tf<T>,
                    this,
                    &maxarr,
                    J,nthreads
                );
            }
            for(J=0;J<nthreads;J++)
            {
                if(threads[J]!=NULL)
                { 
                    threads[J]->join(); 
                    delete threads[J]; 
                    threads[J]= NULL;
                }
            }
            ret = this->data[0];
            for(J=0;J<nthreads;J++)
            {
                if(maxarr[J]>ret)
                {
                    ret = maxarr[J];
                }
            }
        }
        return ret;
    }

}; //end namespace narray
}; //end namespace ams

#endif