// Copyright 2018 The Abseil Authors.
//
// 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
//
//      https://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.
//
// -----------------------------------------------------------------------------
// compare.h
// -----------------------------------------------------------------------------
//
// This header file defines the `absl::partial_ordering`, `absl::weak_ordering`,
// and `absl::strong_ordering` types for storing the results of three way
// comparisons.
//
// Example:
//   absl::weak_ordering compare(const std::string& a, const std::string& b);
//
// These are C++11 compatible versions of the C++20 corresponding types
// (`std::partial_ordering`, etc.) and are designed to be drop-in replacements
// for code compliant with C++20.

#ifndef ABSL_TYPES_COMPARE_H_
#define ABSL_TYPES_COMPARE_H_

#include "third_party/abseil-cpp/absl/base/config.h"

#ifdef ABSL_USES_STD_ORDERING

#include <compare>  // IWYU pragma: export
#include <type_traits>

#include "third_party/abseil-cpp/absl/meta/type_traits.h"

#else

#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <type_traits>

#include "third_party/abseil-cpp/absl/base/attributes.h"
#include "third_party/abseil-cpp/absl/base/macros.h"
#include "third_party/abseil-cpp/absl/meta/type_traits.h"

#endif

namespace absl {
    ABSL_NAMESPACE_BEGIN

#ifdef ABSL_USES_STD_ORDERING

    using std::partial_ordering;
using std::strong_ordering;
using std::weak_ordering;

#else

    namespace compare_internal {

        using value_type = int8_t;

        class OnlyLiteralZero {
        public:
#if ABSL_HAVE_ATTRIBUTE(enable_if)
            // On clang, we can avoid triggering modernize-use-nullptr by only enabling
  // this overload when the value is a compile time integer constant equal to 0.
  //
  // In c++20, this could be a static_assert in a consteval function.
  constexpr OnlyLiteralZero(int n)  // NOLINT
      __attribute__((enable_if(n == 0, "Only literal `0` is allowed."))) {}
#else  // ABSL_HAVE_ATTRIBUTE(enable_if)
            // Accept only literal zero since it can be implicitly converted to a pointer
            // to member type. nullptr constants will be caught by the other constructor
            // which accepts a nullptr_t.
            //
            // This constructor is not used for clang since it triggers
            // modernize-use-nullptr.
            constexpr OnlyLiteralZero(int OnlyLiteralZero::*) noexcept {}  // NOLINT
#endif

            // Fails compilation when `nullptr` or integral type arguments other than
            // `int` are passed. This constructor doesn't accept `int` because literal `0`
            // has type `int`. Literal `0` arguments will be implicitly converted to
            // `std::nullptr_t` and accepted by the above constructor, while other `int`
            // arguments will fail to be converted and cause compilation failure.
            template <typename T, typename = typename std::enable_if<
                    std::is_same<T, std::nullptr_t>::value ||
                    (std::is_integral<T>::value &&
                     !std::is_same<T, int>::value)>::type>
            OnlyLiteralZero(T) {  // NOLINT
                static_assert(sizeof(T) < 0, "Only literal `0` is allowed.");
            }
        };

        enum class eq : value_type {
            equal = 0,
            equivalent = equal,
            nonequal = 1,
            nonequivalent = nonequal,
        };

        enum class ord : value_type { less = -1, greater = 1 };

        enum class ncmp : value_type { unordered = -127 };

// Define macros to allow for creation or emulation of C++17 inline variables
// based on whether the feature is supported. Note: we can't use
// ABSL_INTERNAL_INLINE_CONSTEXPR here because the variables here are of
// incomplete types so they need to be defined after the types are complete.
#ifdef __cpp_inline_variables

        // A no-op expansion that can be followed by a semicolon at class level.
#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) static_assert(true, "")

#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \
  static const type name

#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
  inline constexpr type type::name(init)

// absl:google3-begin(disable warning for google3 C++17 migration)
#ifdef __clang__
#undef ABSL_COMPARE_INLINE_INIT
#define ABSL_COMPARE_INLINE_INIT(type, name, init)                   \
  _Pragma("clang diagnostic push")                                   \
  _Pragma("clang diagnostic ignored \"-Wc++98-c++11-c++14-compat\"") \
  inline constexpr type type::name(init);                            \
  _Pragma("clang diagnostic pop")
#endif  // __clang__
// absl:google3-end

#else  // __cpp_inline_variables

#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \
  ABSL_CONST_INIT static const T name

// A no-op expansion that can be followed by a semicolon at class level.
#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) static_assert(true, "")

#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
  template <typename T>                            \
  const T compare_internal::type##_base<T>::name(init)

#endif  // __cpp_inline_variables

// These template base classes allow for defining the values of the constants
// in the header file (for performance) without using inline variables (which
// aren't available in C++11).
        template <typename T>
        struct partial_ordering_base {
            ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(unordered);
        };

        template <typename T>
        struct weak_ordering_base {
            ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
        };

        template <typename T>
        struct strong_ordering_base {
            ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
            ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
        };

    }  // namespace compare_internal

    class partial_ordering
            : public compare_internal::partial_ordering_base<partial_ordering> {
        explicit constexpr partial_ordering(compare_internal::eq v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        explicit constexpr partial_ordering(compare_internal::ord v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        friend struct compare_internal::partial_ordering_base<partial_ordering>;

        constexpr bool is_ordered() const noexcept {
            return value_ !=
                   compare_internal::value_type(compare_internal::ncmp::unordered);
        }

    public:
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered);

        // Comparisons
        friend constexpr bool operator==(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.is_ordered() && v.value_ == 0;
        }
        friend constexpr bool operator!=(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return !v.is_ordered() || v.value_ != 0;
        }
        friend constexpr bool operator<(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.is_ordered() && v.value_ < 0;
        }
        friend constexpr bool operator<=(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.is_ordered() && v.value_ <= 0;
        }
        friend constexpr bool operator>(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.is_ordered() && v.value_ > 0;
        }
        friend constexpr bool operator>=(
                partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.is_ordered() && v.value_ >= 0;
        }
        friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                         partial_ordering v) noexcept {
            return v.is_ordered() && 0 == v.value_;
        }
        friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                         partial_ordering v) noexcept {
            return !v.is_ordered() || 0 != v.value_;
        }
        friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                        partial_ordering v) noexcept {
            return v.is_ordered() && 0 < v.value_;
        }
        friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                         partial_ordering v) noexcept {
            return v.is_ordered() && 0 <= v.value_;
        }
        friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                        partial_ordering v) noexcept {
            return v.is_ordered() && 0 > v.value_;
        }
        friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                         partial_ordering v) noexcept {
            return v.is_ordered() && 0 >= v.value_;
        }
        friend constexpr bool operator==(partial_ordering v1,
                                         partial_ordering v2) noexcept {
            return v1.value_ == v2.value_;
        }
        friend constexpr bool operator!=(partial_ordering v1,
                                         partial_ordering v2) noexcept {
            return v1.value_ != v2.value_;
        }

    private:
        compare_internal::value_type value_;
    };
    ABSL_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less);
    ABSL_COMPARE_INLINE_INIT(partial_ordering, equivalent,
                             compare_internal::eq::equivalent);
    ABSL_COMPARE_INLINE_INIT(partial_ordering, greater,
                             compare_internal::ord::greater);
    ABSL_COMPARE_INLINE_INIT(partial_ordering, unordered,
                             compare_internal::ncmp::unordered);

    class weak_ordering
            : public compare_internal::weak_ordering_base<weak_ordering> {
        explicit constexpr weak_ordering(compare_internal::eq v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        explicit constexpr weak_ordering(compare_internal::ord v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        friend struct compare_internal::weak_ordering_base<weak_ordering>;

    public:
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater);

        // Conversions
        constexpr operator partial_ordering() const noexcept {  // NOLINT
            return value_ == 0 ? partial_ordering::equivalent
                               : (value_ < 0 ? partial_ordering::less
                                             : partial_ordering::greater);
        }
        // Comparisons
        friend constexpr bool operator==(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ == 0;
        }
        friend constexpr bool operator!=(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ != 0;
        }
        friend constexpr bool operator<(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ < 0;
        }
        friend constexpr bool operator<=(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ <= 0;
        }
        friend constexpr bool operator>(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ > 0;
        }
        friend constexpr bool operator>=(
                weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ >= 0;
        }
        friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                         weak_ordering v) noexcept {
            return 0 == v.value_;
        }
        friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                         weak_ordering v) noexcept {
            return 0 != v.value_;
        }
        friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                        weak_ordering v) noexcept {
            return 0 < v.value_;
        }
        friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                         weak_ordering v) noexcept {
            return 0 <= v.value_;
        }
        friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                        weak_ordering v) noexcept {
            return 0 > v.value_;
        }
        friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                         weak_ordering v) noexcept {
            return 0 >= v.value_;
        }
        friend constexpr bool operator==(weak_ordering v1,
                                         weak_ordering v2) noexcept {
            return v1.value_ == v2.value_;
        }
        friend constexpr bool operator!=(weak_ordering v1,
                                         weak_ordering v2) noexcept {
            return v1.value_ != v2.value_;
        }

    private:
        compare_internal::value_type value_;
    };
    ABSL_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less);
    ABSL_COMPARE_INLINE_INIT(weak_ordering, equivalent,
                             compare_internal::eq::equivalent);
    ABSL_COMPARE_INLINE_INIT(weak_ordering, greater,
                             compare_internal::ord::greater);

    class strong_ordering
            : public compare_internal::strong_ordering_base<strong_ordering> {
        explicit constexpr strong_ordering(compare_internal::eq v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        explicit constexpr strong_ordering(compare_internal::ord v) noexcept
                : value_(static_cast<compare_internal::value_type>(v)) {}
        friend struct compare_internal::strong_ordering_base<strong_ordering>;

    public:
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent);
        ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater);

        // Conversions
        constexpr operator partial_ordering() const noexcept {  // NOLINT
            return value_ == 0 ? partial_ordering::equivalent
                               : (value_ < 0 ? partial_ordering::less
                                             : partial_ordering::greater);
        }
        constexpr operator weak_ordering() const noexcept {  // NOLINT
            return value_ == 0
                   ? weak_ordering::equivalent
                   : (value_ < 0 ? weak_ordering::less : weak_ordering::greater);
        }
        // Comparisons
        friend constexpr bool operator==(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ == 0;
        }
        friend constexpr bool operator!=(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ != 0;
        }
        friend constexpr bool operator<(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ < 0;
        }
        friend constexpr bool operator<=(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ <= 0;
        }
        friend constexpr bool operator>(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ > 0;
        }
        friend constexpr bool operator>=(
                strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
            return v.value_ >= 0;
        }
        friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                         strong_ordering v) noexcept {
            return 0 == v.value_;
        }
        friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                         strong_ordering v) noexcept {
            return 0 != v.value_;
        }
        friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                        strong_ordering v) noexcept {
            return 0 < v.value_;
        }
        friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                         strong_ordering v) noexcept {
            return 0 <= v.value_;
        }
        friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                        strong_ordering v) noexcept {
            return 0 > v.value_;
        }
        friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                         strong_ordering v) noexcept {
            return 0 >= v.value_;
        }
        friend constexpr bool operator==(strong_ordering v1,
                                         strong_ordering v2) noexcept {
            return v1.value_ == v2.value_;
        }
        friend constexpr bool operator!=(strong_ordering v1,
                                         strong_ordering v2) noexcept {
            return v1.value_ != v2.value_;
        }

    private:
        compare_internal::value_type value_;
    };
    ABSL_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less);
    ABSL_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal);
    ABSL_COMPARE_INLINE_INIT(strong_ordering, equivalent,
                             compare_internal::eq::equivalent);
    ABSL_COMPARE_INLINE_INIT(strong_ordering, greater,
                             compare_internal::ord::greater);

#undef ABSL_COMPARE_INLINE_BASECLASS_DECL
#undef ABSL_COMPARE_INLINE_SUBCLASS_DECL
#undef ABSL_COMPARE_INLINE_INIT

#endif  // ABSL_USES_STD_ORDERING

    namespace compare_internal {
// We also provide these comparator adapter functions for internal absl use.

// absl:google3-begin(GTL/internal first, might make public later)
// Google3 users can use the public versions in google3/util/gtl/compare.h.
// absl:google3-end

// Helper functions to do a boolean comparison of two keys given a boolean
// or three-way comparator.
// SFINAE prevents implicit conversions to bool (such as from int).
        template <typename BoolT,
                absl::enable_if_t<std::is_same<bool, BoolT>::value, int> = 0>
        constexpr bool compare_result_as_less_than(const BoolT r) { return r; }
        constexpr bool compare_result_as_less_than(const absl::weak_ordering r) {
            return r < 0;
        }

        template <typename Compare, typename K, typename LK>
        constexpr bool do_less_than_comparison(const Compare &compare, const K &x,
                                               const LK &y) {
            return compare_result_as_less_than(compare(x, y));
        }

// Helper functions to do a three-way comparison of two keys given a boolean or
// three-way comparator.
// SFINAE prevents implicit conversions to int (such as from bool).
        template <typename Int,
                absl::enable_if_t<std::is_same<int, Int>::value, int> = 0>
        constexpr absl::weak_ordering compare_result_as_ordering(const Int c) {
            return c < 0 ? absl::weak_ordering::less
                         : c == 0 ? absl::weak_ordering::equivalent
                                  : absl::weak_ordering::greater;
        }
        constexpr absl::weak_ordering compare_result_as_ordering(
                const absl::weak_ordering c) {
            return c;
        }

        template <
                typename Compare, typename K, typename LK,
                absl::enable_if_t<!std::is_same<bool, absl::result_of_t<Compare(
                        const K &, const LK &)>>::value,
                        int> = 0>
        constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
                                                              const K &x, const LK &y) {
            return compare_result_as_ordering(compare(x, y));
        }
        template <
                typename Compare, typename K, typename LK,
                absl::enable_if_t<std::is_same<bool, absl::result_of_t<Compare(
                        const K &, const LK &)>>::value,
                        int> = 0>
        constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
                                                              const K &x, const LK &y) {
            return compare(x, y) ? absl::weak_ordering::less
                                 : compare(y, x) ? absl::weak_ordering::greater
                                                 : absl::weak_ordering::equivalent;
        }

    }  // namespace compare_internal
    ABSL_NAMESPACE_END
}  // namespace absl

#endif  // THIRD_PARTY_ABSL_TYPES_COMPARE_H_