/*
 * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
 *
 * This source code and/or documentation ("Licensed Deliverables") are
 * subject to NVIDIA intellectual property rights under U.S. and
 * international Copyright laws.
 */

#pragma once

#include <utils/cudart.hpp>

#include <catch2/catch.hpp>

#include <sstream>
#include <string>
#include <vector>

namespace cugraph::ops::catch2 {

/**
 * @brief Custom matcher class for comparing device arrays to be in a range
 *
 * @tparam DataT data type of the arrays
 */
template <typename DataT>
class device_range_t : public Catch::MatcherBase<DataT*> {
  const DataT start_, end_;
  const size_t len_;

 public:
  explicit device_range_t(DataT _start, DataT _end, size_t _len)
    : start_(_start), end_(_end), len_(_len)
  {
  }

  bool match(DataT* const& d_computed) const override
  {
    std::vector<DataT> h_computed(len_);
    utils::copy(h_computed.data(), d_computed, len_, cudaMemcpyDeviceToHost);
    for (size_t i = 0; i < len_; ++i) {
      if (!(start_ <= h_computed[i] && h_computed[i] < end_)) {
        UNSCOPED_INFO("@i=" << i << " val=" << h_computed[i] << " range=[" << start_ << ", " << end_
                            << "[");
        return false;
      }
    }
    return true;
  }

  [[nodiscard]] std::string describe() const override
  {
    std::stringstream ss;
    ss << "device range matcher [" << start_ << ", " << end_ << ")";
    return ss.str();
  }
};  // class device_range_t

/**
 * @brief Builder function for the custom device array range matcher
 *
 * @tparam DataT data type of the arrays
 *
 * @param[in] start range start
 * @param[in] end   range end
 * @param[in] len   length of the array
 *
 * @return the matcher object
 */
template <typename DataT>
device_range_t<DataT> device_range(DataT start, DataT end, size_t len)
{
  return device_range_t<DataT>(start, end, len);
}

}  // namespace cugraph::ops::catch2
