text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
  #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
  
  #include <stdint.h>
  
  #include <limits>
  #include <type_traits>
  
  #if defined(__GNUC__) || defined(__clang__)
  #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
  #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
  #else
  #define BASE_NUMERICS_LIKELY(x) (x)
  #define BASE_NUMERICS_UNLIKELY(x) (x)
  #endif
  
  namespace base {
  namespace internal {
  
  // The std library doesn't provide a binary max_exponent for integers, however
  // we can compute an analog using std::numeric_limits<>::digits.
  template <typename NumericType>
  struct MaxExponent {
    static const int value = std::is_floating_point<NumericType>::value
                                 ? std::numeric_limits<NumericType>::max_exponent
                                 : std::numeric_limits<NumericType>::digits + 1;
  };
  
  // The number of bits (including the sign) in an integer. Eliminates sizeof
  // hacks.
  template <typename NumericType>
  struct IntegerBitsPlusSign {
    static const int value = std::numeric_limits<NumericType>::digits +
                             std::is_signed<NumericType>::value;
  };
  
  // Helper templates for integer manipulations.
  
  template <typename Integer>
  struct PositionOfSignBit {
    static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
  };
  
  // Determines if a numeric value is negative without throwing compiler
  // warnings on: unsigned(value) < 0.
  template <typename T,
            typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
  constexpr bool IsValueNegative(T value) {
    static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
    return value < 0;
  }
  
  template <typename T,
            typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
  constexpr bool IsValueNegative(T) {
    static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
    return false;
  }
  
  // This performs a fast negation, returning a signed value. It works on unsigned
  // arguments, but probably doesn't do what you want for any unsigned value
  // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
  template <typename T>
  constexpr typename std::make_signed<T>::type ConditionalNegate(
      T x,
      bool is_negative) {
    static_assert(std::is_integral<T>::value, "Type must be integral");
    using SignedT = typename std::make_signed<T>::type;
    using UnsignedT = typename std::make_unsigned<T>::type;
    return static_cast<SignedT>(
        (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
  }
  
  // This performs a safe, absolute value via unsigned overflow.
  template <typename T>
  constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
    static_assert(std::is_integral<T>::value, "Type must be integral");
    using UnsignedT = typename std::make_unsigned<T>::type;
    return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
                                  : static_cast<UnsignedT>(value);
  }
  
  // This allows us to switch paths on known compile-time constants.
  #if defined(__clang__) || defined(__GNUC__)
  constexpr bool CanDetectCompileTimeConstant() {
    return true;
  }
  template <typename T>
  constexpr bool IsCompileTimeConstant(const T v) {
    return __builtin_constant_p(v);
  }
  #else
  constexpr bool CanDetectCompileTimeConstant() {
    return false;
  }
  template <typename T>
  constexpr bool IsCompileTimeConstant(const T) {
    return false;
  }
  #endif
  template <typename T>
  constexpr bool MustTreatAsConstexpr(const T v) {
    // Either we can't detect a compile-time constant, and must always use the
    // constexpr path, or we know we have a compile-time constant.
    return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
  }
  
  // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
  // Also used in a constexpr template to trigger a compilation failure on
  // an error condition.
  struct CheckOnFailure {
    template <typename T>
    static T HandleFailure() {
  #if defined(__GNUC__) || defined(__clang__)
      __builtin_trap();
  #else
      ((void)(*(volatile char*)0 = 0));
  #endif
      return T();
    }
  };
  
  enum IntegerRepresentation {
    INTEGER_REPRESENTATION_UNSIGNED,
    INTEGER_REPRESENTATION_SIGNED
  };
  
  // A range for a given nunmeric Src type is contained for a given numeric Dst
  // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
  // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
  // We implement this as template specializations rather than simple static
  // comparisons to ensure type correctness in our comparisons.
  enum NumericRangeRepresentation {
    NUMERIC_RANGE_NOT_CONTAINED,
    NUMERIC_RANGE_CONTAINED
  };
  
  // Helper templates to statically determine if our destination type can contain
  // maximum and minimum values represented by the source type.
  
  template <typename Dst,
            typename Src,
            IntegerRepresentation DstSign = std::is_signed<Dst>::value
                                                ? INTEGER_REPRESENTATION_SIGNED
                                                : INTEGER_REPRESENTATION_UNSIGNED,
            IntegerRepresentation SrcSign = std::is_signed<Src>::value
                                                ? INTEGER_REPRESENTATION_SIGNED
                                                : INTEGER_REPRESENTATION_UNSIGNED>
  struct StaticDstRangeRelationToSrcRange;
  
  // Same sign: Dst is guaranteed to contain Src only if its range is equal or
  // larger.
  template <typename Dst, typename Src, IntegerRepresentation Sign>
  struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
    static const NumericRangeRepresentation value =
        MaxExponent<Dst>::value >= MaxExponent<Src>::value
            ? NUMERIC_RANGE_CONTAINED
            : NUMERIC_RANGE_NOT_CONTAINED;
  };
  
  // Unsigned to signed: Dst is guaranteed to contain source only if its range is
  // larger.
  template <typename Dst, typename Src>
  struct StaticDstRangeRelationToSrcRange<Dst,
                                          Src,
                                          INTEGER_REPRESENTATION_SIGNED,
                                          INTEGER_REPRESENTATION_UNSIGNED> {
    static const NumericRangeRepresentation value =
        MaxExponent<Dst>::value > MaxExponent<Src>::value
            ? NUMERIC_RANGE_CONTAINED
            : NUMERIC_RANGE_NOT_CONTAINED;
  };
  
  // Signed to unsigned: Dst cannot be statically determined to contain Src.
  template <typename Dst, typename Src>
  struct StaticDstRangeRelationToSrcRange<Dst,
                                          Src,
                                          INTEGER_REPRESENTATION_UNSIGNED,
                                          INTEGER_REPRESENTATION_SIGNED> {
    static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
  };
  
  // This class wraps the range constraints as separate booleans so the compiler
  // can identify constants and eliminate unused code paths.
  class RangeCheck {
   public:
    constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
        : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
    constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
    constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
    constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
    constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
    constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
    constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
    constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
    constexpr bool operator==(const RangeCheck rhs) const {
      return is_underflow_ == rhs.is_underflow_ &&
             is_overflow_ == rhs.is_overflow_;
    }
    constexpr bool operator!=(const RangeCheck rhs) const {
      return !(*this == rhs);
    }
  
   private:
    // Do not change the order of these member variables. The integral conversion
    // optimization depends on this exact order.
    const bool is_underflow_;
    const bool is_overflow_;
  };
  
  // The following helper template addresses a corner case in range checks for
  // conversion from a floating-point type to an integral type of smaller range
  // but larger precision (e.g. float -> unsigned). The problem is as follows:
  //   1. Integral maximum is always one less than a power of two, so it must be
  //      truncated to fit the mantissa of the floating point. The direction of
  //      rounding is implementation defined, but by default it's always IEEE
  //      floats, which round to nearest and thus result in a value of larger
  //      magnitude than the integral value.
  //      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
  //                                   // is 4294967295u.
  //   2. If the floating point value is equal to the promoted integral maximum
  //      value, a range check will erroneously pass.
  //      Example: (4294967296f <= 4294967295u) // This is true due to a precision
  //                                            // loss in rounding up to float.
  //   3. When the floating point value is then converted to an integral, the
  //      resulting value is out of range for the target integral type and
  //      thus is implementation defined.
  //      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
  // To fix this bug we manually truncate the maximum value when the destination
  // type is an integral of larger precision than the source floating-point type,
  // such that the resulting maximum is represented exactly as a floating point.
  template <typename Dst, typename Src, template <typename> class Bounds>
  struct NarrowingRange {
    using SrcLimits = std::numeric_limits<Src>;
    using DstLimits = typename std::numeric_limits<Dst>;
  
    // Computes the mask required to make an accurate comparison between types.
    static const int kShift =
        (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
         SrcLimits::digits < DstLimits::digits)
            ? (DstLimits::digits - SrcLimits::digits)
            : 0;
    template <
        typename T,
        typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
  
    // Masks out the integer bits that are beyond the precision of the
    // intermediate type used for comparison.
    static constexpr T Adjust(T value) {
      static_assert(std::is_same<T, Dst>::value, "");
      static_assert(kShift < DstLimits::digits, "");
      return static_cast<T>(
          ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
                            IsValueNegative(value)));
    }
  
    template <typename T,
              typename std::enable_if<std::is_floating_point<T>::value>::type* =
                  nullptr>
    static constexpr T Adjust(T value) {
      static_assert(std::is_same<T, Dst>::value, "");
      static_assert(kShift == 0, "");
      return value;
    }
  
    static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
    static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
  };
  
  template <typename Dst,
            typename Src,
            template <typename> class Bounds,
            IntegerRepresentation DstSign = std::is_signed<Dst>::value
                                                ? INTEGER_REPRESENTATION_SIGNED
                                                : INTEGER_REPRESENTATION_UNSIGNED,
            IntegerRepresentation SrcSign = std::is_signed<Src>::value
                                                ? INTEGER_REPRESENTATION_SIGNED
                                                : INTEGER_REPRESENTATION_UNSIGNED,
            NumericRangeRepresentation DstRange =
                StaticDstRangeRelationToSrcRange<Dst, Src>::value>
  struct DstRangeRelationToSrcRangeImpl;
  
  // The following templates are for ranges that must be verified at runtime. We
  // split it into checks based on signedness to avoid confusing casts and
  // compiler warnings on signed an unsigned comparisons.
  
  // Same sign narrowing: The range is contained for normal limits.
  template <typename Dst,
            typename Src,
            template <typename> class Bounds,
            IntegerRepresentation DstSign,
            IntegerRepresentation SrcSign>
  struct DstRangeRelationToSrcRangeImpl<Dst,
                                        Src,
                                        Bounds,
                                        DstSign,
                                        SrcSign,
                                        NUMERIC_RANGE_CONTAINED> {
    static constexpr RangeCheck Check(Src value) {
      using SrcLimits = std::numeric_limits<Src>;
      using DstLimits = NarrowingRange<Dst, Src, Bounds>;
      return RangeCheck(
          static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
              static_cast<Dst>(value) >= DstLimits::lowest(),
          static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
              static_cast<Dst>(value) <= DstLimits::max());
    }
  };
  
  // Signed to signed narrowing: Both the upper and lower boundaries may be
  // exceeded for standard limits.
  template <typename Dst, typename Src, template <typename> class Bounds>
  struct DstRangeRelationToSrcRangeImpl<Dst,
                                        Src,
                                        Bounds,
                                        INTEGER_REPRESENTATION_SIGNED,
                                        INTEGER_REPRESENTATION_SIGNED,
                                        NUMERIC_RANGE_NOT_CONTAINED> {
    static constexpr RangeCheck Check(Src value) {
      using DstLimits = NarrowingRange<Dst, Src, Bounds>;
      return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
    }
  };
  
  // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
  // standard limits.
  template <typename Dst, typename Src, template <typename> class Bounds>
  struct DstRangeRelationToSrcRangeImpl<Dst,
                                        Src,
                                        Bounds,
                                        INTEGER_REPRESENTATION_UNSIGNED,
                                        INTEGER_REPRESENTATION_UNSIGNED,
                                        NUMERIC_RANGE_NOT_CONTAINED> {
    static constexpr RangeCheck Check(Src value) {
      using DstLimits = NarrowingRange<Dst, Src, Bounds>;
      return RangeCheck(
          DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
          value <= DstLimits::max());
    }
  };
  
  // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
  template <typename Dst, typename Src, template <typename> class Bounds>
  struct DstRangeRelationToSrcRangeImpl<Dst,
                                        Src,
                                        Bounds,
                                        INTEGER_REPRESENTATION_SIGNED,
                                        INTEGER_REPRESENTATION_UNSIGNED,
                                        NUMERIC_RANGE_NOT_CONTAINED> {
    static constexpr RangeCheck Check(Src value) {
      using DstLimits = NarrowingRange<Dst, Src, Bounds>;
      using Promotion = decltype(Src() + Dst());
      return RangeCheck(DstLimits::lowest() <= Dst(0) ||
                            static_cast<Promotion>(value) >=
                                static_cast<Promotion>(DstLimits::lowest()),
                        static_cast<Promotion>(value) <=
                            static_cast<Promotion>(DstLimits::max()));
    }
  };
  
  // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
  // and any negative value exceeds the lower boundary for standard limits.
  template <typename Dst, typename Src, template <typename> class Bounds>
  struct DstRangeRelationToSrcRangeImpl<Dst,
                                        Src,
                                        Bounds,
                                        INTEGER_REPRESENTATION_UNSIGNED,
                                        INTEGER_REPRESENTATION_SIGNED,
                                        NUMERIC_RANGE_NOT_CONTAINED> {
    static constexpr RangeCheck Check(Src value) {
      using SrcLimits = std::numeric_limits<Src>;
      using DstLimits = NarrowingRange<Dst, Src, Bounds>;
      using Promotion = decltype(Src() + Dst());
      return RangeCheck(
          value >= Src(0) && (DstLimits::lowest() == 0 ||
                              static_cast<Dst>(value) >= DstLimits::lowest()),
          static_cast<Promotion>(SrcLimits::max()) <=
                  static_cast<Promotion>(DstLimits::max()) ||
              static_cast<Promotion>(value) <=
                  static_cast<Promotion>(DstLimits::max()));
    }
  };
  
  // Simple wrapper for statically checking if a type's range is contained.
  template <typename Dst, typename Src>
  struct IsTypeInRangeForNumericType {
    static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
                              NUMERIC_RANGE_CONTAINED;
  };
  
  template <typename Dst,
            template <typename> class Bounds = std::numeric_limits,
            typename Src>
  constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
    static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
    static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
    return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
  }
  
  // Integer promotion templates used by the portable checked integer arithmetic.
  template <size_t Size, bool IsSigned>
  struct IntegerForDigitsAndSign;
  
  #define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
    template <>                                                   \
    struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
                                   std::is_signed<I>::value> {    \
      using type = I;                                             \
    }
  
  INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
  INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
  INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
  INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
  INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
  INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
  INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
  INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
  #undef INTEGER_FOR_DIGITS_AND_SIGN
  
  // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
  // support 128-bit math, then the ArithmeticPromotion template below will need
  // to be updated (or more likely replaced with a decltype expression).
  static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
                "Max integer size not supported for this toolchain.");
  
  template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
  struct TwiceWiderInteger {
    using type =
        typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
                                         IsSigned>::type;
  };
  
  enum ArithmeticPromotionCategory {
    LEFT_PROMOTION,  // Use the type of the left-hand argument.
    RIGHT_PROMOTION  // Use the type of the right-hand argument.
  };
  
  // Determines the type that can represent the largest positive value.
  template <typename Lhs,
            typename Rhs,
            ArithmeticPromotionCategory Promotion =
                (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
                    ? LEFT_PROMOTION
                    : RIGHT_PROMOTION>
  struct MaxExponentPromotion;
  
  template <typename Lhs, typename Rhs>
  struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
    using type = Lhs;
  };
  
  template <typename Lhs, typename Rhs>
  struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
    using type = Rhs;
  };
  
  // Determines the type that can represent the lowest arithmetic value.
  template <typename Lhs,
            typename Rhs,
            ArithmeticPromotionCategory Promotion =
                std::is_signed<Lhs>::value
                    ? (std::is_signed<Rhs>::value
                           ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
                                  ? LEFT_PROMOTION
                                  : RIGHT_PROMOTION)
                           : LEFT_PROMOTION)
                    : (std::is_signed<Rhs>::value
                           ? RIGHT_PROMOTION
                           : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
                                  ? LEFT_PROMOTION
                                  : RIGHT_PROMOTION))>
  struct LowestValuePromotion;
  
  template <typename Lhs, typename Rhs>
  struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
    using type = Lhs;
  };
  
  template <typename Lhs, typename Rhs>
  struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
    using type = Rhs;
  };
  
  // Determines the type that is best able to represent an arithmetic result.
  template <
      typename Lhs,
      typename Rhs = Lhs,
      bool is_intmax_type =
          std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
              IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
                  value == IntegerBitsPlusSign<intmax_t>::value,
      bool is_max_exponent =
          StaticDstRangeRelationToSrcRange<
              typename MaxExponentPromotion<Lhs, Rhs>::type,
              Lhs>::value ==
          NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
              typename MaxExponentPromotion<Lhs, Rhs>::type,
              Rhs>::value == NUMERIC_RANGE_CONTAINED>
  struct BigEnoughPromotion;
  
  // The side with the max exponent is big enough.
  template <typename Lhs, typename Rhs, bool is_intmax_type>
  struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
    using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
    static const bool is_contained = true;
  };
  
  // We can use a twice wider type to fit.
  template <typename Lhs, typename Rhs>
  struct BigEnoughPromotion<Lhs, Rhs, false, false> {
    using type =
        typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
                                   std::is_signed<Lhs>::value ||
                                       std::is_signed<Rhs>::value>::type;
    static const bool is_contained = true;
  };
  
  // No type is large enough.
  template <typename Lhs, typename Rhs>
  struct BigEnoughPromotion<Lhs, Rhs, true, false> {
    using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
    static const bool is_contained = false;
  };
  
  // We can statically check if operations on the provided types can wrap, so we
  // can skip the checked operations if they're not needed. So, for an integer we
  // care if the destination type preserves the sign and is twice the width of
  // the source.
  template <typename T, typename Lhs, typename Rhs = Lhs>
  struct IsIntegerArithmeticSafe {
    static const bool value =
        !std::is_floating_point<T>::value &&
        !std::is_floating_point<Lhs>::value &&
        !std::is_floating_point<Rhs>::value &&
        std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
        IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
        std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
        IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
  };
  
  // Promotes to a type that can represent any possible result of a binary
  // arithmetic operation with the source types.
  template <typename Lhs,
            typename Rhs,
            bool is_promotion_possible = IsIntegerArithmeticSafe<
                typename std::conditional<std::is_signed<Lhs>::value ||
                                              std::is_signed<Rhs>::value,
                                          intmax_t,
                                          uintmax_t>::type,
                typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
  struct FastIntegerArithmeticPromotion;
  
  template <typename Lhs, typename Rhs>
  struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
    using type =
        typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
                                   std::is_signed<Lhs>::value ||
                                       std::is_signed<Rhs>::value>::type;
    static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
    static const bool is_contained = true;
  };
  
  template <typename Lhs, typename Rhs>
  struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
    using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
    static const bool is_contained = false;
  };
  
  // Extracts the underlying type from an enum.
  template <typename T, bool is_enum = std::is_enum<T>::value>
  struct ArithmeticOrUnderlyingEnum;
  
  template <typename T>
  struct ArithmeticOrUnderlyingEnum<T, true> {
    using type = typename std::underlying_type<T>::type;
    static const bool value = std::is_arithmetic<type>::value;
  };
  
  template <typename T>
  struct ArithmeticOrUnderlyingEnum<T, false> {
    using type = T;
    static const bool value = std::is_arithmetic<type>::value;
  };
  
  // The following are helper templates used in the CheckedNumeric class.
  template <typename T>
  class CheckedNumeric;
  
  template <typename T>
  class ClampedNumeric;
  
  template <typename T>
  class StrictNumeric;
  
  // Used to treat CheckedNumeric and arithmetic underlying types the same.
  template <typename T>
  struct UnderlyingType {
    using type = typename ArithmeticOrUnderlyingEnum<T>::type;
    static const bool is_numeric = std::is_arithmetic<type>::value;
    static const bool is_checked = false;
    static const bool is_clamped = false;
    static const bool is_strict = false;
  };
  
  template <typename T>
  struct UnderlyingType<CheckedNumeric<T>> {
    using type = T;
    static const bool is_numeric = true;
    static const bool is_checked = true;
    static const bool is_clamped = false;
    static const bool is_strict = false;
  };
  
  template <typename T>
  struct UnderlyingType<ClampedNumeric<T>> {
    using type = T;
    static const bool is_numeric = true;
    static const bool is_checked = false;
    static const bool is_clamped = true;
    static const bool is_strict = false;
  };
  
  template <typename T>
  struct UnderlyingType<StrictNumeric<T>> {
    using type = T;
    static const bool is_numeric = true;
    static const bool is_checked = false;
    static const bool is_clamped = false;
    static const bool is_strict = true;
  };
  
  template <typename L, typename R>
  struct IsCheckedOp {
    static const bool value =
        UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
        (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
  };
  
  template <typename L, typename R>
  struct IsClampedOp {
    static const bool value =
        UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
        (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
        !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
  };
  
  template <typename L, typename R>
  struct IsStrictOp {
    static const bool value =
        UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
        (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
        !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
        !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
  };
  
  // as_signed<> returns the supplied integral value (or integral castable
  // Numeric template) cast as a signed integral of equivalent precision.
  // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
  template <typename Src>
  constexpr typename std::make_signed<
      typename base::internal::UnderlyingType<Src>::type>::type
  as_signed(const Src value) {
    static_assert(std::is_integral<decltype(as_signed(value))>::value,
                  "Argument must be a signed or unsigned integer type.");
    return static_cast<decltype(as_signed(value))>(value);
  }
  
  // as_unsigned<> returns the supplied integral value (or integral castable
  // Numeric template) cast as an unsigned integral of equivalent precision.
  // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
  template <typename Src>
  constexpr typename std::make_unsigned<
      typename base::internal::UnderlyingType<Src>::type>::type
  as_unsigned(const Src value) {
    static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
                  "Argument must be a signed or unsigned integer type.");
    return static_cast<decltype(as_unsigned(value))>(value);
  }
  
  template <typename L, typename R>
  constexpr bool IsLessImpl(const L lhs,
                            const R rhs,
                            const RangeCheck l_range,
                            const RangeCheck r_range) {
    return l_range.IsUnderflow() || r_range.IsOverflow() ||
           (l_range == r_range &&
            static_cast<decltype(lhs + rhs)>(lhs) <
                static_cast<decltype(lhs + rhs)>(rhs));
  }
  
  template <typename L, typename R>
  struct IsLess {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
                        DstRangeRelationToSrcRange<L>(rhs));
    }
  };
  
  template <typename L, typename R>
  constexpr bool IsLessOrEqualImpl(const L lhs,
                                   const R rhs,
                                   const RangeCheck l_range,
                                   const RangeCheck r_range) {
    return l_range.IsUnderflow() || r_range.IsOverflow() ||
           (l_range == r_range &&
            static_cast<decltype(lhs + rhs)>(lhs) <=
                static_cast<decltype(lhs + rhs)>(rhs));
  }
  
  template <typename L, typename R>
  struct IsLessOrEqual {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
                               DstRangeRelationToSrcRange<L>(rhs));
    }
  };
  
  template <typename L, typename R>
  constexpr bool IsGreaterImpl(const L lhs,
                               const R rhs,
                               const RangeCheck l_range,
                               const RangeCheck r_range) {
    return l_range.IsOverflow() || r_range.IsUnderflow() ||
           (l_range == r_range &&
            static_cast<decltype(lhs + rhs)>(lhs) >
                static_cast<decltype(lhs + rhs)>(rhs));
  }
  
  template <typename L, typename R>
  struct IsGreater {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
                           DstRangeRelationToSrcRange<L>(rhs));
    }
  };
  
  template <typename L, typename R>
  constexpr bool IsGreaterOrEqualImpl(const L lhs,
                                      const R rhs,
                                      const RangeCheck l_range,
                                      const RangeCheck r_range) {
    return l_range.IsOverflow() || r_range.IsUnderflow() ||
           (l_range == r_range &&
            static_cast<decltype(lhs + rhs)>(lhs) >=
                static_cast<decltype(lhs + rhs)>(rhs));
  }
  
  template <typename L, typename R>
  struct IsGreaterOrEqual {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
                                  DstRangeRelationToSrcRange<L>(rhs));
    }
  };
  
  template <typename L, typename R>
  struct IsEqual {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return DstRangeRelationToSrcRange<R>(lhs) ==
                 DstRangeRelationToSrcRange<L>(rhs) &&
             static_cast<decltype(lhs + rhs)>(lhs) ==
                 static_cast<decltype(lhs + rhs)>(rhs);
    }
  };
  
  template <typename L, typename R>
  struct IsNotEqual {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    static constexpr bool Test(const L lhs, const R rhs) {
      return DstRangeRelationToSrcRange<R>(lhs) !=
                 DstRangeRelationToSrcRange<L>(rhs) ||
             static_cast<decltype(lhs + rhs)>(lhs) !=
                 static_cast<decltype(lhs + rhs)>(rhs);
    }
  };
  
  // These perform the actual math operations on the CheckedNumerics.
  // Binary arithmetic operations.
  template <template <typename, typename> class C, typename L, typename R>
  constexpr bool SafeCompare(const L lhs, const R rhs) {
    static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
                  "Types must be numeric.");
    using Promotion = BigEnoughPromotion<L, R>;
    using BigType = typename Promotion::type;
    return Promotion::is_contained
               // Force to a larger type for speed if both are contained.
               ? C<BigType, BigType>::Test(
                     static_cast<BigType>(static_cast<L>(lhs)),
                     static_cast<BigType>(static_cast<R>(rhs)))
               // Let the template functions figure it out for mixed types.
               : C<L, R>::Test(lhs, rhs);
  }
  
  template <typename Dst, typename Src>
  constexpr bool IsMaxInRangeForNumericType() {
    return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
                                            std::numeric_limits<Src>::max());
  }
  
  template <typename Dst, typename Src>
  constexpr bool IsMinInRangeForNumericType() {
    return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
                                         std::numeric_limits<Src>::lowest());
  }
  
  template <typename Dst, typename Src>
  constexpr Dst CommonMax() {
    return !IsMaxInRangeForNumericType<Dst, Src>()
               ? Dst(std::numeric_limits<Dst>::max())
               : Dst(std::numeric_limits<Src>::max());
  }
  
  template <typename Dst, typename Src>
  constexpr Dst CommonMin() {
    return !IsMinInRangeForNumericType<Dst, Src>()
               ? Dst(std::numeric_limits<Dst>::lowest())
               : Dst(std::numeric_limits<Src>::lowest());
  }
  
  // This is a wrapper to generate return the max or min for a supplied type.
  // If the argument is false, the returned value is the maximum. If true the
  // returned value is the minimum.
  template <typename Dst, typename Src = Dst>
  constexpr Dst CommonMaxOrMin(bool is_min) {
    return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
  }
  
  }  // namespace internal
  }  // namespace base
  
  #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
  