/*
 * This file is part of hipSYCL, a SYCL implementation based on CUDA/HIP
 *
 * Copyright (c) 2018 Aksel Alpay
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#ifndef HIPSYCL_DEVICE_HPP
#define HIPSYCL_DEVICE_HPP

#include <exception>
#include <limits>
#include <type_traits>

#include "types.hpp"
#include "aspect.hpp"
#include "info/info.hpp"
#include "backend.hpp"
#include "exception.hpp"
#include "version.hpp"
#include "hipSYCL/sycl/libkernel/id.hpp"

#include "hipSYCL/runtime/device_id.hpp"
#include "hipSYCL/runtime/application.hpp"
#include "hipSYCL/runtime/runtime.hpp"
#include "hipSYCL/runtime/backend.hpp"
#include "hipSYCL/runtime/hardware.hpp"

namespace hipsycl {
namespace sycl {

class device;

namespace detail {

inline rt::device_id get_host_device() {
  return rt::device_id{rt::backend_descriptor(rt::hardware_platform::cpu,
                                              rt::api_platform::omp),
                       0};
}

rt::device_id extract_rt_device(const device&);

}

class device_selector;
class platform;

class device {
  friend class queue;
  friend class context;
  friend class platform;
  friend rt::device_id detail::extract_rt_device(const device&);
public:
  device(rt::device_id id)
      : _device_id{id} {}
 
  device()
      : _device_id(detail::get_host_device()) {}

  // Implemented in device_selector.hpp
  template <class DeviceSelector>
  explicit device(const DeviceSelector &deviceSelector);

  bool is_host() const 
  {
    return is_cpu();
  }

  bool is_cpu() const
  {
    return get_rt_device()->is_cpu();
  }

  bool is_gpu() const
  {
    return get_rt_device()->is_gpu();
  }

  bool is_accelerator() const { return !is_cpu(); }

  bool has(aspect asp) const {
    if(asp == aspect::cpu) {
      return is_cpu();
    } else if(asp == aspect::gpu) {
      return is_gpu();
    } else if(asp == aspect::accelerator) {
      return is_accelerator();
    } else if(asp == aspect::custom) {
      return false;
    } else if(asp == aspect::emulated) {
      return false;
    } else if(asp == aspect::host_debuggable) {
      return _device_id.get_full_backend_descriptor().hw_platform ==
           rt::hardware_platform::cpu;
    } else if(asp == aspect::fp16) {
      // fp16 is only partially supported in hipSYCL
      return false;
    } else if(asp == aspect::fp64) {
      return true;
    } else if(asp == aspect::atomic64) {
      return true;
    } else if(asp == aspect::image) {
      return false;
    } else if(asp == aspect::online_compiler) {
      return false;
    } else if(asp == aspect::online_linker) {
      return false;
    } else if(asp == aspect::queue_profiling) {
      return get_rt_device()->has(
          rt::device_support_aspect::execution_timestamps);
    } else if(asp == aspect::usm_device_allocations) {
      return get_rt_device()->has(
          rt::device_support_aspect::usm_device_allocations);
    } else if(asp == aspect::usm_host_allocations) {
      return get_rt_device()->has(
          rt::device_support_aspect::usm_host_allocations);
    } else if(asp == aspect::usm_atomic_host_allocations) {
      return get_rt_device()->has(
          rt::device_support_aspect::usm_atomic_host_allocations);
    } else if(asp == aspect::usm_shared_allocations) {
      return get_rt_device()->has(
          rt::device_support_aspect::usm_shared_allocations);
    } else if(asp == aspect::usm_system_allocations) {
      return get_rt_device()->has(
          rt::device_support_aspect::usm_system_allocations);
    }

    return false;
  }

  bool hipSYCL_has_compiled_kernels() const {
#if defined(__HIPSYCL_ENABLE_OMPHOST_TARGET__)
    if (is_cpu())
      return true;
#endif
    
#if defined(__HIPSYCL_ENABLE_CUDA_TARGET__)
    if(_device_id.get_backend() == rt::backend_id::cuda)
      return true;
#endif
    
#if defined(__HIPSYCL_ENABLE_HIP_TARGET__)
    if(_device_id.get_backend() == rt::backend_id::hip)
      return true;
#endif

#if defined(__HIPSYCL_ENABLE_SPIRV_TARGET__)
    if(_device_id.get_backend() == rt::backend_id::level_zero)
      return true;
#endif
    
    return false;
  }

  // Implemented in platform.hpp
  platform get_platform() const;

  template <info::device param>
  typename info::param_traits<info::device, param>::return_type
  get_info() const;

  bool has_extension(const string_class &extension) const
  {
    return false;
  }



  // Available only when prop == info::partition_property::partition_equally
  template <info::partition_property prop,
            std::enable_if_t<prop == info::partition_property::partition_equally>*
              = nullptr>
  std::vector<device> create_sub_devices(size_t nbSubDev) const
  {
    throw feature_not_supported{"subdevices are unsupported."};
  }

  // Available only when prop == info::partition_property::partition_by_counts
  template <info::partition_property prop,
            std::enable_if_t<prop == info::partition_property::partition_by_counts>*
              = nullptr>
  std::vector<device> create_sub_devices(const std::vector<size_t> &counts) const
  {
    throw feature_not_supported{"subdevices are unsupported."};
  }

  // Available only when prop == info::partition_property::partition_by_affinity_domain
  template <info::partition_property prop,
            std::enable_if_t<prop == info::partition_property::partition_by_affinity_domain>*
              = nullptr>
  std::vector<device> create_sub_devices(info::partition_affinity_domain
                                          affinityDomain) const
  {
    throw feature_not_supported{"subdevices are unsupported."};
  }

  static std::vector<device>
  get_devices(info::device_type deviceType = info::device_type::all) {

    std::vector<device> result;
    rt::runtime_keep_alive_token requires_runtime;

    requires_runtime.get()->backends().for_each_backend(
        [&](rt::backend *b) {
          rt::backend_descriptor bd = b->get_backend_descriptor();
          std::size_t num_devices =
              b->get_hardware_manager()->get_num_devices();

          for (std::size_t dev = 0; dev < num_devices; ++dev) {
            rt::device_id d_id{bd, static_cast<int>(dev)};

            device d;
            d._device_id = d_id;

            if (deviceType == info::device_type::all ||
                (deviceType == info::device_type::accelerator &&
                 d.is_accelerator()) ||
                (deviceType == info::device_type::cpu && d.is_cpu()) ||
                (deviceType == info::device_type::host && d.is_cpu()) ||
                (deviceType == info::device_type::gpu && d.is_gpu())) {

              result.push_back(d);
            }
          }
        });

    return result;
  }

  static int get_num_devices() {
    return get_devices(info::device_type::all).size();
  }

  friend bool operator ==(const device& lhs, const device& rhs)
  { return lhs._device_id == rhs._device_id; }

  friend bool operator!=(const device& lhs, const device &rhs)
  { return !(lhs == rhs); }
  
  backend get_backend() const noexcept {
    return _device_id.get_backend();
  }

  std::size_t hipSYCL_hash_code() const {
    return std::hash<hipsycl::rt::device_id>{}(_device_id);
  }

  rt::runtime* hipSYCL_runtime() const {
    return _requires_runtime.get();
  }
private:
  rt::device_id _device_id;
  rt::runtime_keep_alive_token _requires_runtime;

  rt::hardware_context *get_rt_device() const {
    auto ptr = _requires_runtime.get()->backends().get(_device_id.get_backend())
                   ->get_hardware_manager()
                   ->get_device(_device_id.get_id());
    if (!ptr) {
      throw runtime_error{"Could not access device"};
    }
    return ptr;
  }
};

HIPSYCL_SPECIALIZE_GET_INFO(device, device_type) {
  if (this->is_cpu())
    return info::device_type::cpu;
  else if (this->is_gpu())
    return info::device_type::gpu;
  else
    return info::device_type::custom;
}

/// \todo Return different id for amd and nvidia
HIPSYCL_SPECIALIZE_GET_INFO(device, vendor_id)
{ 
  return get_rt_device()->get_property(
      rt::device_uint_property::vendor_id); 
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_compute_units)
{
  return get_rt_device()->get_property(
      rt::device_uint_property::max_compute_units);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_work_item_dimensions)
{ return 3; }

HIPSYCL_SPECIALIZE_GET_INFO(device, max_work_item_sizes)
{
  std::size_t size0 = static_cast<std::size_t>(get_rt_device()->get_property(
      rt::device_uint_property::max_global_size0));
  std::size_t size1 = static_cast<std::size_t>(get_rt_device()->get_property(
      rt::device_uint_property::max_global_size1));
  std::size_t size2 = static_cast<std::size_t>(get_rt_device()->get_property(
      rt::device_uint_property::max_global_size2));
  return id<3>{size0, size1, size2};
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_work_group_size)
{
  return static_cast<size_t>(
      get_rt_device()->get_property(rt::device_uint_property::max_group_size));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_num_sub_groups)
{
  return static_cast<unsigned int>(
      get_rt_device()->get_property(rt::device_uint_property::max_num_sub_groups));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, sub_group_independent_forward_progress)
{
  return get_rt_device()->has(
      rt::device_support_aspect::sub_group_independent_forward_progress);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, sub_group_sizes)
{
  return get_rt_device()->get_property(
      rt::device_uint_list_property::sub_group_sizes);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_char) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_char));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_double){
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_double));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_float) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_float));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_half) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_half));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_int) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_int));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_long) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_long));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_vector_width_short) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::preferred_vector_width_short));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_char) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_char));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_double) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_double));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_float) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_float));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_half) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_half));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_int) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_int));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_long) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_long));
}
HIPSYCL_SPECIALIZE_GET_INFO(device, native_vector_width_short) {
  return static_cast<int>(get_rt_device()->get_property(
      rt::device_uint_property::native_vector_width_short));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_clock_frequency)
{
  return static_cast<detail::u_int>(
      get_rt_device()->get_property(rt::device_uint_property::max_clock_speed));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, address_bits) {
  return get_rt_device()->get_property(rt::device_uint_property::address_bits);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_mem_alloc_size)
{
  return static_cast<detail::u_long>(
      get_rt_device()->get_property(rt::device_uint_property::max_malloc_size));
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image_support) {
  return get_rt_device()->has(rt::device_support_aspect::images);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_read_image_args) {
  return get_rt_device()->get_property(
      rt::device_uint_property::max_read_image_args);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_write_image_args) {
  return get_rt_device()->get_property(
      rt::device_uint_property::max_write_image_args);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image2d_max_width) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image2d_max_width);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image2d_max_height) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image2d_max_height);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image3d_max_width) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image3d_max_width);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image3d_max_height) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image3d_max_height);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image3d_max_depth) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image3d_max_depth);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image_max_buffer_size) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image_max_buffer_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, image_max_array_size) {
  return get_rt_device()->get_property(
      rt::device_uint_property::image_max_array_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_samplers) {
  return get_rt_device()->get_property(rt::device_uint_property::max_samplers);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_parameter_size) {
  return get_rt_device()->get_property(
      rt::device_uint_property::max_parameter_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, mem_base_addr_align) {
  return get_rt_device()->get_property(
      rt::device_uint_property::mem_base_addr_align);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, half_fp_config)
{
  return std::vector<info::fp_config>{
    info::fp_config::denorm,
    info::fp_config::inf_nan,
    info::fp_config::round_to_nearest,
    info::fp_config::round_to_zero,
    info::fp_config::round_to_inf,
    info::fp_config::fma,
    info::fp_config::correctly_rounded_divide_sqrt
  };
}

HIPSYCL_SPECIALIZE_GET_INFO(device, single_fp_config)
{
  return std::vector<info::fp_config>{
    info::fp_config::denorm,
    info::fp_config::inf_nan,
    info::fp_config::round_to_nearest,
    info::fp_config::round_to_zero,
    info::fp_config::round_to_inf,
    info::fp_config::fma,
    info::fp_config::correctly_rounded_divide_sqrt
  };
}

HIPSYCL_SPECIALIZE_GET_INFO(device, double_fp_config)
{
  return std::vector<info::fp_config>{
    info::fp_config::denorm,
    info::fp_config::inf_nan,
    info::fp_config::round_to_nearest,
    info::fp_config::round_to_zero,
    info::fp_config::round_to_inf,
    info::fp_config::fma,
    info::fp_config::correctly_rounded_divide_sqrt
  };
}


HIPSYCL_SPECIALIZE_GET_INFO(device, name)
{
  return get_rt_device()->get_device_name();
}

HIPSYCL_SPECIALIZE_GET_INFO(device, global_mem_cache_type)
{
  if (!get_rt_device()->has(rt::device_support_aspect::global_mem_cache))
    return info::global_mem_cache_type::none;
  if (get_rt_device()->has(
          rt::device_support_aspect::global_mem_cache_read_only))
    return info::global_mem_cache_type::read_only;
  else if (get_rt_device()->has(
          rt::device_support_aspect::global_mem_cache_write_only))
    return info::global_mem_cache_type::write_only;

  return info::global_mem_cache_type::none;
}

HIPSYCL_SPECIALIZE_GET_INFO(device, global_mem_cache_line_size) {
  return get_rt_device()->get_property(
      rt::device_uint_property::global_mem_cache_line_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, global_mem_cache_size)
{
  return get_rt_device()->get_property(
      rt::device_uint_property::global_mem_cache_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, global_mem_size)
{
  return get_rt_device()->get_property(
      rt::device_uint_property::global_mem_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_constant_buffer_size)
{
  return get_rt_device()->get_property(
      rt::device_uint_property::max_constant_buffer_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, max_constant_args) {
  return get_rt_device()->get_property(
      rt::device_uint_property::max_constant_args);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, local_mem_type) {
  if (get_rt_device()->has(rt::device_support_aspect::emulated_local_memory)) {
    return info::local_mem_type::global;
  } else {
    return info::local_mem_type::local;
  }
}

HIPSYCL_SPECIALIZE_GET_INFO(device, local_mem_size)
{
  return get_rt_device()->get_property(
      rt::device_uint_property::local_mem_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, error_correction_support) {
  return get_rt_device()->has(rt::device_support_aspect::error_correction);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, host_unified_memory) {
  return get_rt_device()->has(rt::device_support_aspect::host_unified_memory);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, profiling_timer_resolution)
{ return 1; }

HIPSYCL_SPECIALIZE_GET_INFO(device, is_endian_little)
{ return get_rt_device()->has(rt::device_support_aspect::little_endian); }

HIPSYCL_SPECIALIZE_GET_INFO(device, is_available)
{ return true; }

HIPSYCL_SPECIALIZE_GET_INFO(device, is_compiler_available)
{ return true; }

HIPSYCL_SPECIALIZE_GET_INFO(device, is_linker_available)
{ return true; }

HIPSYCL_SPECIALIZE_GET_INFO(device, execution_capabilities)
{
  return std::vector<info::execution_capability>{
    info::execution_capability::exec_kernel
  };
}

HIPSYCL_SPECIALIZE_GET_INFO(device, queue_profiling) {
  return get_rt_device()->has(rt::device_support_aspect::execution_timestamps);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, built_in_kernels)
{ return std::vector<string_class>{}; }


HIPSYCL_SPECIALIZE_GET_INFO(device, vendor) {
  return get_rt_device()->get_vendor_name();
}

HIPSYCL_SPECIALIZE_GET_INFO(device, driver_version)
{
  return get_rt_device()->get_driver_version();
}

HIPSYCL_SPECIALIZE_GET_INFO(device, profile)
{ return get_rt_device()->get_profile(); }

HIPSYCL_SPECIALIZE_GET_INFO(device, version) {
  return "1.2 "+detail::version_string();
}

HIPSYCL_SPECIALIZE_GET_INFO(device, opencl_c_version)
{ return "1.2 HIPSYCL"; }

HIPSYCL_SPECIALIZE_GET_INFO(device, aspects)
{
  std::array aspects = {aspect::cpu,
                        aspect::gpu,
                        aspect::accelerator,
                        aspect::custom,
                        aspect::emulated,
                        aspect::host_debuggable,
                        aspect::fp16,
                        aspect::fp64,
                        aspect::atomic64,
                        aspect::image,
                        aspect::online_compiler,
                        aspect::online_linker,
                        aspect::queue_profiling,
                        aspect::usm_device_allocations,
                        aspect::usm_host_allocations,
                        aspect::usm_atomic_host_allocations,
                        aspect::usm_shared_allocations,
                        aspect::usm_atomic_shared_allocations,
                        aspect::usm_system_allocations};

  std::vector<aspect> result;
  
  for(auto asp : aspects) {
    if(this->has(asp)){
      result.push_back(asp);
    }
  }

  return result;
}

HIPSYCL_SPECIALIZE_GET_INFO(device, extensions)
{
  return std::vector<string_class>{};
}

HIPSYCL_SPECIALIZE_GET_INFO(device, printf_buffer_size) {
  return get_rt_device()->get_property(
      rt::device_uint_property::printf_buffer_size);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, preferred_interop_user_sync)
{ return true; }

HIPSYCL_SPECIALIZE_GET_INFO(device, parent_device)
{ throw invalid_object_error{"Device is not a subdevice"}; }

HIPSYCL_SPECIALIZE_GET_INFO(device, partition_max_sub_devices) {
  return get_rt_device()->get_property(
      rt::device_uint_property::partition_max_sub_devices);
}

HIPSYCL_SPECIALIZE_GET_INFO(device, partition_properties)
{ return std::vector<info::partition_property>{}; }

HIPSYCL_SPECIALIZE_GET_INFO(device, partition_affinity_domains)
{
  return std::vector<info::partition_affinity_domain>{
    info::partition_affinity_domain::not_applicable
  };
}

HIPSYCL_SPECIALIZE_GET_INFO(device, partition_type_property)
{ return info::partition_property::no_partition; }

HIPSYCL_SPECIALIZE_GET_INFO(device, partition_type_affinity_domain)
{ return info::partition_affinity_domain::not_applicable; }


HIPSYCL_SPECIALIZE_GET_INFO(device, reference_count)
{
  // hipSYCL device classes do not need any resources, and hence
  // no reference counting is required.
  return 1;
}

namespace detail {

inline rt::device_id extract_rt_device(const device &d) {
  return d._device_id;
}

}

} // namespace sycl
} // namespace hipsycl

namespace std {

template <>
struct hash<hipsycl::sycl::device>
{
  std::size_t operator()(const hipsycl::sycl::device& d) const
  {
    return d.hipSYCL_hash_code();
  }
};

}

#endif
