/*
 * Copyright 2017 MapD Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file    ProfileTest.h
 * @author  Minggang Yu <miyu@mapd.com>
 * @brief   Unit tests for microbenchmark.
 *
 * Copyright (c) 2016 MapD Technologies, Inc.  All rights reserved.
 */

#ifndef PROFILETEST_H
#define PROFILETEST_H

#include "../QueryEngine/GpuRtConstants.h"

#include "../Logger/Logger.h"

#ifdef HAVE_CUDA
#include <cuda_runtime_api.h>
#endif

#if defined(HAVE_CUDA) && CUDA_VERSION >= 8000
#include <cuda.h>

#define TRY_COLUMNAR
#define TRY_MASH
#define TRY_MASH_COLUMNAR
#if defined(TRY_MASH) || defined(TRY_MASH_COLUMNAR)
#define SAVE_MASH_BUF
#endif
#endif

#include <vector>

enum DEV_KIND { CPU, GPU };

enum DIST_KIND { INVALID, NRM, EXP1, EXP2, UNI, POI };

enum OP_KIND { OP_COUNT, OP_SUM, OP_MIN, OP_MAX };

#if defined(HAVE_CUDA) && CUDA_VERSION >= 8000
void init_groups_on_device(int8_t* groups,
                           const size_t group_count,
                           const size_t col_count,
                           const std::vector<size_t>& col_widths,
                           const std::vector<size_t>& init_vals,
                           const bool is_columnar);

void run_query_on_device(int8_t* groups_buffer,
                         const size_t group_count,
                         const int8_t* row_buffer,
                         const size_t row_count,
                         const size_t key_count,
                         const size_t val_count,
                         const std::vector<size_t>& col_widths,
                         const std::vector<OP_KIND>& agg_ops,
                         const bool is_columnar);
#if defined(TRY_MASH) || defined(TRY_MASH_COLUMNAR)
void mash_run_query_on_device(int8_t* groups_buffer,
                              const size_t group_count,
                              const int8_t* row_buffer,
                              const size_t row_count,
                              const size_t key_count,
                              const size_t val_count,
                              const std::vector<size_t>& col_widths,
                              const std::vector<OP_KIND>& agg_ops,
                              const bool is_columnar);
#endif
bool generate_columns_on_device(int8_t* buffers,
                                const size_t row_count,
                                const size_t col_count,
                                const std::vector<size_t>& col_widths,
                                const std::vector<std::pair<int64_t, int64_t>>& ranges,
                                const bool is_columnar,
                                const std::vector<DIST_KIND>& dists);

void columnarize_groups_on_device(int8_t* columnar_buffer,
                                  const int8_t* rowwise_buffer,
                                  const size_t group_count,
                                  const std::vector<size_t>& col_widths);

size_t deduplicate_rows_on_device(int8_t* row_buffer,
                                  const size_t row_count,
                                  const size_t key_count,
                                  const std::vector<size_t>& col_widths,
                                  const bool is_columnar);

int8_t* get_hashed_copy(int8_t* dev_buffer,
                        const size_t entry_count,
                        const size_t new_entry_count,
                        const std::vector<size_t>& col_widths,
                        const std::vector<OP_KIND>& agg_ops,
                        const std::vector<size_t>& init_vals,
                        const bool is_columnar);

size_t drop_rows(int8_t* row_buffer,
                 const size_t entry_count,
                 const size_t entry_size,
                 const size_t row_count,
                 const float fill_rate,
                 const bool is_columnar);

void reduce_on_device(int8_t*& this_dev_buffer,
                      const size_t this_dev_id,
                      size_t& this_entry_count,
                      int8_t* that_dev_buffer,
                      const size_t that_dev_id,
                      const size_t that_entry_count,
                      const size_t that_actual_row_count,
                      const std::vector<size_t>& col_widths,
                      const std::vector<OP_KIND>& agg_ops,
                      const std::vector<size_t>& init_vals,
                      const bool is_columnar);

int8_t* fetch_segs_from_others(std::vector<int8_t*>& dev_reduced_buffers,
                               const size_t entry_count,
                               const size_t dev_id,
                               const size_t dev_count,
                               const std::vector<size_t>& col_widths,
                               const bool is_columnar,
                               const size_t start,
                               const size_t end);

std::pair<int8_t*, size_t> get_perfect_hashed_copy(
    int8_t* dev_buffer,
    const size_t entry_count,
    const std::vector<size_t>& col_widths,
    const std::vector<std::pair<int64_t, int64_t>>& ranges,
    const std::vector<OP_KIND>& agg_ops,
    const std::vector<size_t>& init_vals,
    const bool is_columnar);

void reduce_segment_on_device(int8_t* dev_seg_buf,
                              const int8_t* dev_other_segs,
                              const size_t entry_count,
                              const size_t seg_count,
                              const std::vector<size_t>& col_widths,
                              const std::vector<OP_KIND>& agg_ops,
                              const bool is_columnar,
                              const size_t start,
                              const size_t end);
#endif

#endif /* PROFILETEST_H */
