//#####################################################################
// Copyright 2020 Craig Schroeder
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class ACCUMULATE_HELPER
//#####################################################################
#ifndef __ACCUMULATE_HELPER__
#define __ACCUMULATE_HELPER__

#include <Core/Arrays/ARRAY.h>
#ifdef USE_OPENMP
#include <omp.h>
#endif
namespace PhysBAM{

template<class T>
class ACCUMULATE_HELPER
{
public:
    struct SPACER
    {
        ARRAY<T> array;
        char spacer[128-sizeof(ARRAY<T>)];
    };
    ARRAY<T> array;
    ARRAY<SPACER> thread_array;
    int threads;

    ACCUMULATE_HELPER()
    {
    }

    // Run from within #pragma omp parallel
    // Returns a thread-local array that can be safely written to independently
    // by this thread.
    ARRAY<T>& Init(int num,T init)
    {
#ifdef USE_OPENMP
#pragma omp single
        {
            threads=omp_get_num_threads();
            thread_array.Resize(threads);
            array.Resize(num,init_all,init);
        }
        auto& a=thread_array(omp_get_thread_num()).array;
        a.Resize(num,init_all,init);
        return a;
#else
        threads=1;
        array.Resize(num,init_all,init);
        return array;
#endif
    }

    // Run from within #pragma omp parallel
    // inside a #pragma omp single
    // merge(T& t0,const T& t1), updates t0 in place.
    // Combines the thread-local versions of the array into this->array.
    template<class FUNC>
    void Combine(FUNC merge)
    {
#ifdef USE_OPENMP
        int n=array.m;
#pragma omp for
        for(int i=0;i<n;i++)
        {
            T x=array(i);
            for(int t=0;t<threads;t++)
                merge(x,thread_array(t).array(i));
            array(i)=x;
        }
#endif
    }
};
}
#endif
