// Copyright 2022 the V8 project 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 V8_NUMBERS_INTEGER_LITERAL_H_
#define V8_NUMBERS_INTEGER_LITERAL_H_

#include "src/base/optional.h"
#include "src/common/globals.h"

namespace v8 {
namespace internal {

class IntegerLiteral {
 public:
  IntegerLiteral(bool negative, uint64_t absolute_value)
      : negative_(negative), absolute_value_(absolute_value) {
    if (absolute_value == 0) negative_ = false;
  }

  template <typename T>
  explicit IntegerLiteral(T value) : IntegerLiteral(value, true) {}

  bool is_negative() const { return negative_; }
  uint64_t absolute_value() const { return absolute_value_; }

  template <typename T>
  bool IsRepresentableAs() const {
    static_assert(std::is_integral<T>::value, "Integral type required");
    static_assert(sizeof(T) <= sizeof(uint64_t),
                  "Types with more than 64 bits are not supported");
    return Compare(IntegerLiteral(std::numeric_limits<T>::min(), false)) >= 0 &&
           Compare(IntegerLiteral(std::numeric_limits<T>::max(), false)) <= 0;
  }

  template <typename T>
  T To() const {
    static_assert(std::is_integral<T>::value, "Integral type required");
    DCHECK(IsRepresentableAs<T>());
    uint64_t v = absolute_value_;
    if (negative_) v = ~v + 1;
    return static_cast<T>(v);
  }

  template <typename T>
  base::Optional<T> TryTo() const {
    static_assert(std::is_integral<T>::value, "Integral type required");
    if (!IsRepresentableAs<T>()) return base::nullopt;
    return To<T>();
  }

  int Compare(const IntegerLiteral& other) const {
    if (absolute_value_ == other.absolute_value_) {
      if (absolute_value_ == 0 || negative_ == other.negative_) return 0;
      return negative_ ? -1 : 1;
    } else if (absolute_value_ < other.absolute_value_) {
      return other.negative_ ? 1 : -1;
    } else {
      return negative_ ? -1 : 1;
    }
  }

  std::string ToString() const;

 private:
  template <typename T>
  explicit IntegerLiteral(T value, bool perform_dcheck) : negative_(false) {
    static_assert(std::is_integral<T>::value, "Integral type required");
    absolute_value_ = static_cast<uint64_t>(value);
    if (value < T(0)) {
      negative_ = true;
      absolute_value_ = ~absolute_value_ + 1;
    }
    if (perform_dcheck) DCHECK_EQ(To<T>(), value);
  }

  bool negative_;
  uint64_t absolute_value_;
};

inline bool operator==(const IntegerLiteral& x, const IntegerLiteral& y) {
  return x.Compare(y) == 0;
}

inline bool operator!=(const IntegerLiteral& x, const IntegerLiteral& y) {
  return x.Compare(y) != 0;
}

inline std::ostream& operator<<(std::ostream& stream,
                                const IntegerLiteral& literal) {
  return stream << literal.ToString();
}

inline IntegerLiteral operator|(const IntegerLiteral& x,
                                const IntegerLiteral& y) {
  DCHECK(!x.is_negative());
  DCHECK(!y.is_negative());
  return IntegerLiteral(false, x.absolute_value() | y.absolute_value());
}

IntegerLiteral operator<<(const IntegerLiteral& x, const IntegerLiteral& y);
IntegerLiteral operator+(const IntegerLiteral& x, const IntegerLiteral& y);

}  // namespace internal
}  // namespace v8
#endif  // V8_NUMBERS_INTEGER_LITERAL_H_
