/*
 *  Copyright 2008-2013 NVIDIA Corporation
 *
 *  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.
 */

#pragma once

#include <thrust/detail/config.h>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header
#include <thrust/detail/functional/actor.h>
#include <thrust/detail/functional/composite.h>
#include <thrust/detail/functional/operators/operator_adaptors.h>
#include <thrust/functional.h>

THRUST_NAMESPACE_BEGIN
namespace detail
{
namespace functional
{

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<thrust::negate<>>, actor<Eval>>> _CCCL_HOST_DEVICE
operator-(const actor<Eval>& _1)
{
  return compose(transparent_unary_operator<thrust::negate<>>(), _1);
} // end operator-()

// there's no standard unary_plus functional, so roll an ad hoc one here
struct unary_plus
{
  using is_transparent = void;

  _CCCL_EXEC_CHECK_DISABLE
  template <typename T1>
  _CCCL_HOST_DEVICE constexpr auto operator()(T1&& t1) const noexcept(noexcept(+THRUST_FWD(t1)))
    THRUST_TRAILING_RETURN(decltype(+THRUST_FWD(t1)))
  {
    return +THRUST_FWD(t1);
  }
};

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<unary_plus>, actor<Eval>>> operator+(const actor<Eval>& _1)
{
  return compose(transparent_unary_operator<unary_plus>(), _1);
} // end operator+()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::plus<>>, actor<T1>, typename as_actor<T2>::type>>
operator+(const actor<T1>& _1, const T2& _2)
{
  return compose(transparent_binary_operator<thrust::plus<>>(), make_actor(_1), make_actor(_2));
} // end operator+()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::plus<>>, typename as_actor<T1>::type, actor<T2>>>
operator+(const T1& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::plus<>>(), make_actor(_1), make_actor(_2));
} // end operator+()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::plus<>>, actor<T1>, actor<T2>>>
operator+(const actor<T1>& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::plus<>>(), make_actor(_1), make_actor(_2));
} // end operator+()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::minus<>>, typename as_actor<T1>::type, actor<T2>>>
operator-(const T1& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::minus<>>(), make_actor(_1), make_actor(_2));
} // end operator-()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::minus<>>, actor<T1>, typename as_actor<T2>::type>>
operator-(const actor<T1>& _1, const T2& _2)
{
  return compose(transparent_binary_operator<thrust::minus<>>(), make_actor(_1), make_actor(_2));
} // end operator-()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::minus<>>, actor<T1>, actor<T2>>>
operator-(const actor<T1>& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::minus<>>(), make_actor(_1), make_actor(_2));
} // end operator-()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::multiplies<>>, typename as_actor<T1>::type, actor<T2>>>
  operator*(const T1& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::multiplies<>>(), make_actor(_1), make_actor(_2));
} // end operator*()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::multiplies<>>, actor<T1>, typename as_actor<T2>::type>>
  operator*(const actor<T1>& _1, const T2& _2)
{
  return compose(transparent_binary_operator<thrust::multiplies<>>(), make_actor(_1), make_actor(_2));
} // end operator*()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::multiplies<>>, actor<T1>, actor<T2>>>
operator*(const actor<T1>& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::multiplies<>>(), make_actor(_1), make_actor(_2));
} // end operator*()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::divides<>>, actor<T1>, typename as_actor<T2>::type>>
  operator/(const actor<T1>& _1, const T2& _2)
{
  return compose(transparent_binary_operator<thrust::divides<>>(), make_actor(_1), make_actor(_2));
} // end operator/()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::divides<>>, typename as_actor<T1>::type, actor<T2>>>
  operator/(const T1& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::divides<>>(), make_actor(_1), make_actor(_2));
} // end operator/()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::divides<>>, actor<T1>, actor<T2>>>
operator/(const actor<T1>& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::divides<>>(), make_actor(_1), make_actor(_2));
} // end operator/()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::modulus<>>, actor<T1>, typename as_actor<T2>::type>>
  operator%(const actor<T1>& _1, const T2& _2)
{
  return compose(transparent_binary_operator<thrust::modulus<>>(), make_actor(_1), make_actor(_2));
} // end operator%()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE
  actor<composite<transparent_binary_operator<thrust::modulus<>>, typename as_actor<T1>::type, actor<T2>>>
  operator%(const T1& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::modulus<void>>(), make_actor(_1), make_actor(_2));
} // end operator%()

template <typename T1, typename T2>
_CCCL_HOST_DEVICE actor<composite<transparent_binary_operator<thrust::modulus<>>, actor<T1>, actor<T2>>>
operator%(const actor<T1>& _1, const actor<T2>& _2)
{
  return compose(transparent_binary_operator<thrust::modulus<>>(), make_actor(_1), make_actor(_2));
} // end operator%()

// there's no standard prefix_increment functional, so roll an ad hoc one here
struct prefix_increment
{
  using is_transparent = void;

  _CCCL_EXEC_CHECK_DISABLE
  template <typename T1>
  _CCCL_HOST_DEVICE constexpr auto operator()(T1&& t1) const noexcept(noexcept(++THRUST_FWD(t1)))
    THRUST_TRAILING_RETURN(decltype(++THRUST_FWD(t1)))
  {
    return ++THRUST_FWD(t1);
  }
}; // end prefix_increment

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<prefix_increment>, actor<Eval>>>
operator++(const actor<Eval>& _1)
{
  return compose(transparent_unary_operator<prefix_increment>(), _1);
} // end operator++()

// there's no standard postfix_increment functional, so roll an ad hoc one here
struct postfix_increment
{
  using is_transparent = void;

  _CCCL_EXEC_CHECK_DISABLE
  template <typename T1>
  _CCCL_HOST_DEVICE constexpr auto operator()(T1&& t1) const noexcept(noexcept(THRUST_FWD(t1)++))
    THRUST_TRAILING_RETURN(decltype(THRUST_FWD(t1)++))
  {
    return THRUST_FWD(t1)++;
  }
}; // end postfix_increment

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<postfix_increment>, actor<Eval>>>
operator++(const actor<Eval>& _1, int)
{
  return compose(transparent_unary_operator<postfix_increment>(), _1);
} // end operator++()

// there's no standard prefix_decrement functional, so roll an ad hoc one here
struct prefix_decrement
{
  using is_transparent = void;

  _CCCL_EXEC_CHECK_DISABLE
  template <typename T1>
  _CCCL_HOST_DEVICE constexpr auto operator()(T1&& t1) const noexcept(noexcept(--THRUST_FWD(t1)))
    THRUST_TRAILING_RETURN(decltype(--THRUST_FWD(t1)))
  {
    return --THRUST_FWD(t1);
  }
}; // end prefix_decrement

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<prefix_decrement>, actor<Eval>>>
operator--(const actor<Eval>& _1)
{
  return compose(transparent_unary_operator<prefix_decrement>(), _1);
} // end operator--()

// there's no standard postfix_decrement functional, so roll an ad hoc one here
struct postfix_decrement
{
  using is_transparent = void;

  _CCCL_EXEC_CHECK_DISABLE
  template <typename T1>
  _CCCL_HOST_DEVICE constexpr auto operator()(T1&& t1) const noexcept(noexcept(THRUST_FWD(t1)--))
    THRUST_TRAILING_RETURN(decltype(THRUST_FWD(t1)--))
  {
    return THRUST_FWD(t1)--;
  }
}; // end prefix_increment

template <typename Eval>
_CCCL_HOST_DEVICE actor<composite<transparent_unary_operator<postfix_decrement>, actor<Eval>>>
operator--(const actor<Eval>& _1, int)
{
  return compose(transparent_unary_operator<postfix_decrement>(), _1);
} // end operator--()

} // namespace functional
} // namespace detail
THRUST_NAMESPACE_END
