#ifndef __AMSCPPNARRAY_LLBOPSIMPL_HPP__
#define __AMSCPPNARRAY_LLBOPSIMPL_HPP__

namespace ams
{
namespace narray
{

template<typename T1, typename T2> void buffer_copycast_tf(
    T1 *bufferto,
    const T2 *bufferfrom,
    const narray_size_t N,
    int threadnum, int nthreads
)
{
    int I0,I1,Is,I;
    
    Is = N/nthreads;
    I0 = Is*threadnum;
    I1 = (threadnum>=(nthreads-1))? N: Is*(threadnum+1);
    for(I=I0;I<I1;I++)
    {
        bufferto[I] = (T1) bufferfrom[I];
    }

    return;
}

template<typename T1, typename T2> void buffer_copycast(T1 *bufferto, const T2 *bufferfrom, const narray_size_t N)
{
    narray_size_t I;
    int J;
    int nthreads;
    std::vector<std::thread*> threads;

    if(N<=0) return;

    if(N<narray_thread_sz)
    {
        for(I=0;I<N;I++)
        {
            bufferto[I] = (T1) bufferfrom[I];
        }
    }
    else
    {
        //threaded operation
        
        nthreads = std::thread::hardware_concurrency();
        nthreads = (nthreads<1) ? 1 : nthreads;
        nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
        threads.resize(nthreads);

        for(J=0;J<nthreads;J++)
        {
            threads[J] = new(std::nothrow) std::thread(
                &buffer_copycast_tf<T1,T2>,
                bufferto,
                bufferfrom,
                N,
                J,nthreads
            );
            if(threads[J]==NULL)
            {
                printf("buffer_copy_cast: warning, thread %d failed to launch.\n",J);
            }
        }
        for(J=0;J<nthreads;J++)
        {
            if(threads[J]!=NULL)
            { 
                threads[J]->join(); 
                delete threads[J]; 
                threads[J]= NULL;
            }
        }
    }

    return;
}

template<typename T> void buffer_set_tf(
    T *bufferto,
    const T val,
    const narray_size_t ind1,
    const narray_size_t ind2,
    const narray_size_t N,
    int threadnum, int nthreads
)
{
    int I0,I1,Is,I;
    
    Is = N/nthreads;
    I0 = Is*threadnum;
    I1 = (threadnum>=(nthreads-1))? N: Is*(threadnum+1);

    I0 = I0 + ind1;
    I1 = I1 + ind1;

    for(I=I0;I<I1;I++)
    {
        bufferto[I] = val;
    }

    return;
}

template<typename T> void buffer_set(T *buffer, const T val, const narray_size_t ind1, const narray_size_t ind2)
{
    narray_size_t I;
    int J;
    int nthreads;
    std::vector<std::thread*> threads;

    narray_size_t N = ind2-ind1;
    if(N<=0) return;

    if(N<narray_thread_sz)
    {
        for(I=ind1;I<ind2;I++)
        {
            buffer[I] = val;
        }
    }
    else
    {
        //threaded operation
        
        nthreads = std::thread::hardware_concurrency();
        nthreads = (nthreads<1) ? 1 : nthreads;
        nthreads = (nthreads>narray_max_threads) ? narray_max_threads : nthreads;
        threads.resize(nthreads);

        for(J=0;J<nthreads;J++)
        {
            threads[J] = new(std::nothrow) std::thread(
                &buffer_set_tf<T>,
                buffer,
                val,
                ind1,ind2,N,
                J,nthreads
            );
            if(threads[J]==NULL)
            {
                printf("buffer_copy_cast: warning, thread %d failed to launch.\n",J);
            }
        }
        for(J=0;J<nthreads;J++)
        {
            if(threads[J]!=NULL)
            { 
                threads[J]->join(); 
                delete threads[J]; 
                threads[J]= NULL;
            }
        }
    }

    return;
}


};
};

#endif