text: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  // Copyright (c) 2017-19, Lawrence Livermore National Security, LLC
  // and RAJA Performance Suite project contributors.
  // See the RAJAPerf/COPYRIGHT file for details.
  //
  // SPDX-License-Identifier: (BSD-3-Clause)
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  
  #include "LTIMES.hpp"
  
  #include "RAJA/RAJA.hpp"
  
  #include "common/DataUtils.hpp"
  
  #include <iostream>
  
  namespace rajaperf 
  {
  namespace apps
  {
  
  
  #define LTIMES_DATA_SETUP_CPU \
    ResReal_ptr phidat = m_phidat; \
    ResReal_ptr elldat = m_elldat; \
    ResReal_ptr psidat = m_psidat; \
  \
    Index_type num_d = m_num_d; \
    Index_type num_z = m_num_z; \
    Index_type num_g = m_num_g; \
    Index_type num_m = m_num_m;
  
  
  LTIMES::LTIMES(const RunParams& params)
    : KernelBase(rajaperf::Apps_LTIMES, params)
  {
    m_num_d_default = 64;
    m_num_z_default = 500;
    m_num_g_default = 32;
    m_num_m_default = 25;
  
    setDefaultSize(m_num_d_default * m_num_m_default * 
                   m_num_g_default * m_num_z_default);
    setDefaultReps(50);
  }
  
  LTIMES::~LTIMES() 
  {
  }
  
  void LTIMES::setUp(VariantID vid)
  {
    m_num_z = run_params.getSizeFactor() * m_num_z_default;
    m_num_g = m_num_g_default;  
    m_num_m = m_num_m_default;  
    m_num_d = m_num_d_default;  
  
    m_philen = m_num_m * m_num_g * m_num_z;
    m_elllen = m_num_d * m_num_m;
    m_psilen = m_num_d * m_num_g * m_num_z;
  
    allocAndInitDataConst(m_phidat, int(m_philen), Real_type(0.0), vid);
    allocAndInitData(m_elldat, int(m_elllen), vid);
    allocAndInitData(m_psidat, int(m_psilen), vid);
  }
  
  void LTIMES::runKernel(VariantID vid)
  {
    const Index_type run_reps = getRunReps();
  
    switch ( vid ) {
  
      case Base_Seq : {
  
        LTIMES_DATA_SETUP_CPU;
    
        startTimer();
        for (RepIndex_type irep = 0; irep < run_reps; ++irep) {
  
          for (Index_type z = 0; z < num_z; ++z ) {
            for (Index_type g = 0; g < num_g; ++g ) {
              for (Index_type m = 0; m < num_m; ++m ) {
                for (Index_type d = 0; d < num_d; ++d ) {
                  LTIMES_BODY;
                }
              }
            }
          }
  
        }
        stopTimer();
  
        break;
      } 
  
  #if defined(RUN_RAJA_SEQ)     
      case RAJA_Seq : {
  
        LTIMES_DATA_SETUP_CPU;
  
        LTIMES_VIEWS_RANGES_RAJA;
  
        using EXEC_POL = 
          RAJA::KernelPolicy<
            RAJA::statement::For<1, RAJA::loop_exec,       // z
              RAJA::statement::For<2, RAJA::loop_exec,     // g
                RAJA::statement::For<3, RAJA::loop_exec,   // m
                  RAJA::statement::For<0, RAJA::loop_exec, // d
                    RAJA::statement::Lambda<0>
                  >
                >
              >
            >
          >;  
  
        startTimer();
        for (RepIndex_type irep = 0; irep < run_reps; ++irep) {
       
          RAJA::kernel<EXEC_POL>( RAJA::make_tuple(IDRange(0, num_d),
                                                   IZRange(0, num_z),
                                                   IGRange(0, num_g),
                                                   IMRange(0, num_m)), 
            [=](ID d, IZ z, IG g, IM m) {
            LTIMES_BODY_RAJA;
          });
  
        }
        stopTimer(); 
  
        break;
      }
  #endif // RUN_RAJA_SEQ
  
  #if defined(RAJA_ENABLE_OPENMP) && defined(RUN_OPENMP)
      case Base_OpenMP : {
  
        LTIMES_DATA_SETUP_CPU;
  
        startTimer();
        for (RepIndex_type irep = 0; irep < run_reps; ++irep) {
  
          #pragma omp parallel for
          for (Index_type z = 0; z < num_z; ++z ) {
            for (Index_type g = 0; g < num_g; ++g ) {
              for (Index_type m = 0; m < num_m; ++m ) {
                for (Index_type d = 0; d < num_d; ++d ) {
                  LTIMES_BODY;
                }
              }
            }
          }  
  
        }
        stopTimer();
  
        break;
      }
  
      case RAJA_OpenMP : {
  
        LTIMES_DATA_SETUP_CPU;
  
        LTIMES_VIEWS_RANGES_RAJA;
  
        using EXEC_POL = 
          RAJA::KernelPolicy<
            RAJA::statement::For<1, RAJA::omp_parallel_for_exec, // z
              RAJA::statement::For<2, RAJA::loop_exec,           // g
                RAJA::statement::For<3, RAJA::loop_exec,         // m
                  RAJA::statement::For<0, RAJA::loop_exec,       // d
                    RAJA::statement::Lambda<0>
                  >
                > 
              >
            >
          >;
  
        startTimer();
        for (RepIndex_type irep = 0; irep < run_reps; ++irep) {
  
          RAJA::kernel<EXEC_POL>( RAJA::make_tuple(IDRange(0, num_d),
                                                   IZRange(0, num_z),
                                                   IGRange(0, num_g),
                                                   IMRange(0, num_m)), 
            [=](ID d, IZ z, IG g, IM m) {
            LTIMES_BODY_RAJA;
          });
  
        }
        stopTimer();
  
        break;
      }
  #endif
  
  #if defined(RAJA_ENABLE_TARGET_OPENMP)
      case Base_OpenMPTarget :
      case RAJA_OpenMPTarget :
      {
        runOpenMPTargetVariant(vid);
        break;
      }
  #endif
  
  #if defined(RAJA_ENABLE_CUDA)
      case Base_CUDA :
      case RAJA_CUDA :
      {
        runCudaVariant(vid);
        break;
      }
  #endif
  
      default : {
        std::cout << "\n LTIMES : Unknown variant id = " << vid << std::endl;
      }
  
    }
  }
  
  void LTIMES::updateChecksum(VariantID vid)
  {
    checksum[vid] += calcChecksum(m_phidat, m_philen);
  }
  
  void LTIMES::tearDown(VariantID vid)
  {
    (void) vid;
   
    deallocData(m_phidat);
    deallocData(m_elldat);
    deallocData(m_psidat);
  }
  
  } // end namespace apps
  } // end namespace rajaperf
  