#ifndef __AMSMATHUTIL25_UTILIMPL_HPP__
#define __AMSMATHUTIL25_UTILIMPL_HPP__

namespace ams
{

//A template function that takes as input a function pointer and a series of arguments
//The function is executed with fptr(threadnum, nthreads, otherargs...) with a certain number of threads
//psize must be supplied to determine whether to execute in threaded mode or not.
template<typename callable, typename ... argst> int threaded_execute(callable &&fptr, int64_t psize, argst&&... args)
{
    int ret = amsmathutil25_success;
    int nthreads;
    int I;
    std::vector<std::thread*> threads;

    if(psize<amsmathutil25_threadpsz)
    {
        nthreads = 1;
        I = 0;
        // std::invoke(
        //     std::forward<callable>(fptr),
        //     I,
        //     nthreads,
        //     std::forward<argst>(args)...
        // );

        //std::invoke is a C++17 feature, and mingw8 complains even so.
        // Can I get away with just calling the functions?
        fptr(I,nthreads,std::forward<argst>(args)...);
    }
    else
    {
        nthreads = std::thread::hardware_concurrency(); //number of cpu cores the system thinks you have
        nthreads = (nthreads<=0) ? 1 : nthreads;
        nthreads = (nthreads>amsmathutil25_maxthreads) ? amsmathutil25_maxthreads : nthreads;
        threads.resize(nthreads);
        for(I=0;I<nthreads;I++) threads[I] = NULL;
        for(I=0;I<nthreads;I++)
        {
            threads[I] = new(std::nothrow) std::thread
            (
                std::forward<callable>(fptr),
                I,
                nthreads,
                std::forward<argst>(args)...
            );
        }
        for(I=0;I<nthreads;I++)
        {
            if(threads[I]==NULL)
            { //null thread creation failure check
                //printf("debug check!\n");
                ret = amsmathutil25_failure;
            }
        }
        for(I=0;I<nthreads;I++)
        {
            if(threads[I]!=NULL)
            {
                threads[I]->join();
                delete threads[I];
                threads[I] = NULL;
            }
        }
    }

    return ret;
}

}; //end namespace ams


#endif

