/*
 * Copyright (c) 2012, Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_

#include <climits>
#include <iosfwd>
#include <limits>
#include <optional>
#include <type_traits>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/logging.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/vector_traits.h"

namespace blink {

#if DCHECK_IS_ON()
#define REPORT_OVERFLOW(doesOverflow)                                          \
  DLOG_IF(ERROR, !(doesOverflow)) << "LayoutUnit overflow !(" << #doesOverflow \
                                  << ") in " << PRETTY_FUNCTION
#else
#define REPORT_OVERFLOW(doesOverflow) ((void)0)
#endif

//
// `FixedPoint` is a fixed-point math class template, with the number of bits
// for the fractional part as a template parameter.
//
// `LayoutUnit` is an instantiated class of the `FixedPoint`, storing multiples
// of 1/64 of a pixel in an `int32_t` storage.
// See: https://trac.webkit.org/wiki/LayoutUnit
//
// `TextRunLayoutUnit` stores multiples of 1/65536 of a pixel in the same
// storage, so it has less integral part than `LayoutUnit` (16/16 bits vs 26/6
// bits). Suitable for a run of text, but not for the whole layout space.
//
// `InlineLayoutUnit` stores the same precision as `TextRunLayoutUnit` using an
// `int64_t` storage. It can provide the text precision, and represent the whole
// layout space (and more, 48 bits vs 26 bits), but it's double-sized.
//
// Note, non-member functions and operators for `TextRunLayoutUnit` and
// `InlineLayoutUnit` are implemented as needed.
//
template <unsigned fractional_bits, typename Storage>
class PLATFORM_EXPORT FixedPoint {
  DISALLOW_NEW();

 public:
  using StorageType = Storage;
  using UnsignedStorageType = std::make_unsigned<Storage>::type;
  static constexpr unsigned kFractionalBits = fractional_bits;
  static constexpr unsigned kIntegralBits =
      sizeof(Storage) * 8 - kFractionalBits;
  static constexpr int kFixedPointDenominator = 1 << kFractionalBits;
  static constexpr Storage kRawValueMax = std::numeric_limits<Storage>::max();
  static constexpr Storage kRawValueMin = std::numeric_limits<Storage>::min();
  static constexpr Storage kIntMax = kRawValueMax / kFixedPointDenominator;
  static constexpr Storage kIntMin = kRawValueMin / kFixedPointDenominator;

  template <typename T>
  static constexpr Storage ClampRawValue(T raw_value) {
    return base::saturated_cast<Storage>(raw_value);
  }

  constexpr FixedPoint() : value_(0) {}

  // Creates a `FixedPoint` with the specified integer value.
  // If the specified value is smaller than `FixedPoint::Min()`, the new
  // `FixedPoint` is equivalent to `FixedPoint::Min()`.
  // If the specified value is greater than the maximum integer value which
  // `FixedPoint` can represent, the new `FixedPoint` is equivalent to
  // `FixedPoint(FixedPoint::kIntMax)` in 32-bit Arm, or is equivalent to
  // `FixedPoint::Max()` otherwise.
  constexpr explicit FixedPoint(std::signed_integral auto value)
    requires(sizeof(value) <= sizeof(int))
      : value_(0) {
    SaturatedSet(static_cast<int>(value));
  }
  constexpr explicit FixedPoint(std::unsigned_integral auto value)
    requires(sizeof(value) <= sizeof(int))
      : value_(0) {
    SaturatedSet(static_cast<unsigned>(value));
  }
  constexpr explicit FixedPoint(std::integral auto value)
    requires(sizeof(value) > sizeof(int))
      : value_(ClampRawValue(value * kFixedPointDenominator)) {}

  // The specified `value` is truncated to a multiple of `Epsilon()` near 0, and
  // is clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
  constexpr explicit FixedPoint(float value)
      : value_(ClampRawValue(value * kFixedPointDenominator)) {}
  constexpr explicit FixedPoint(double value)
      : value_(ClampRawValue(value * kFixedPointDenominator)) {}

  // The specified `value` is rounded up to a multiple of `Epsilon()`, and is
  // clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
  static FixedPoint FromFloatCeil(float value) {
    return FromRawValueWithClamp(ceilf(value * kFixedPointDenominator));
  }

  // The specified `value` is truncated to a multiple of `Epsilon()`, and is
  // clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
  static FixedPoint FromFloatFloor(float value) {
    return FromRawValueWithClamp(floorf(value * kFixedPointDenominator));
  }

  // The specified `value` is rounded to a multiple of `Epsilon()`, and is
  // clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
  static FixedPoint FromFloatRound(float value) {
    return FromRawValueWithClamp(roundf(value * kFixedPointDenominator));
  }

  static FixedPoint FromDoubleRound(double value) {
    return FromRawValueWithClamp(round(value * kFixedPointDenominator));
  }

  static constexpr FixedPoint FromRawValue(Storage raw_value) {
    FixedPoint v;
    v.value_ = raw_value;
    return v;
  }
  template <typename T>
  static constexpr FixedPoint FromRawValueWithClamp(T raw_value) {
    return FromRawValue(ClampRawValue(raw_value));
  }

  // Given unrounded `start_value` and `end_value`, return a pair of rounded
  // FixedPoints, where the final rounded values encompass the original passed
  // in floats. However, if `start_value` and `end_value` are equal, the
  // returned FixedPoints will also be equal, with both results being floored,
  // ensuring that the pairs are also equivalent after rounding.
  static std::pair<FixedPoint, FixedPoint> FromFloatEncompassRound(
      float start_value,
      float end_value) {
    FixedPoint start_position;
    FixedPoint end_position;
    if (start_value < end_value) [[likely]] {
      start_position = FromFloatFloor(start_value);
      end_position = FromFloatCeil(end_value);
    } else if (start_value > end_value) [[unlikely]] {
      start_position = FromFloatCeil(start_value);
      end_position = FromFloatFloor(end_value);
    } else {
      start_position = end_position = FromFloatFloor(start_value);
    }
    return {start_position, end_position};
  }

  // Construct from a `FixedPoint` with different template parameters. Implicit
  // because it's lossless. For lossy conversions, use `To<>()` below instead.
  template <unsigned source_fractional_bits, typename SourceStorage>
    requires(
        sizeof(Storage) > sizeof(SourceStorage) &&
        kFractionalBits >= source_fractional_bits &&
        kIntegralBits >=
            FixedPoint<source_fractional_bits, SourceStorage>::kIntegralBits)
  FixedPoint(FixedPoint<source_fractional_bits, SourceStorage> source)
      : value_(static_cast<Storage>(source.RawValue())
               << (kFractionalBits - source_fractional_bits)) {}

  // Convert from a fixed point integer.
  template <unsigned source_fractional_bits>
    requires(source_fractional_bits == kFractionalBits)
  static constexpr FixedPoint FromFixed(Storage value) {
    return FromRawValue(value);
  }
  template <unsigned source_fractional_bits>
    requires(source_fractional_bits >= kFractionalBits)
  static constexpr FixedPoint FromFixed(std::integral auto value) {
    constexpr unsigned kBitsDiff = source_fractional_bits - kFractionalBits;
    return FromRawValueWithClamp(value >> kBitsDiff);
  }
  template <unsigned source_fractional_bits>
    requires(kFractionalBits > source_fractional_bits)
  static constexpr FixedPoint FromFixed(std::integral auto value) {
    constexpr unsigned kBitsDiff = kFractionalBits - source_fractional_bits;
    if (value >= kRawValueMax >> kBitsDiff) [[unlikely]] {
      return Max();
    }
    if (value <= kRawValueMin >> kBitsDiff) [[unlikely]] {
      return Min();
    }
    return FromRawValue(value << kBitsDiff);
  }

  // Convert to a `FixedPoint` with a different storage and/or precision.
  template <typename Target>
  constexpr Target To() const {
    return Target::template FromFixed<kFractionalBits>(RawValue());
  }

  // Convert to a different `FixedPoint` by ceiling the lost precisions (e.g.,
  // `InlineLayoutUnit` to `LayoutUnit`).
  template <typename Target>
    requires(Target::kFractionalBits < kFractionalBits &&
             sizeof(typename Target::StorageType) < sizeof(Storage))
  constexpr Target ToCeil() const {
    constexpr unsigned kBitsDiff = kFractionalBits - Target::kFractionalBits;
    Storage raw_value = RawValue() >> kBitsDiff;
    if (RawValue() & ((1 << kBitsDiff) - 1)) {
      ++raw_value;
    }
    return Target::FromRawValueWithClamp(raw_value);
  }

  constexpr Storage ToInt() const { return value_ / kFixedPointDenominator; }
  constexpr float ToFloat() const {
    return static_cast<float>(value_) / kFixedPointDenominator;
  }
  constexpr double ToDouble() const {
    return static_cast<double>(value_) / kFixedPointDenominator;
  }
  UnsignedStorageType ToUnsigned() const {
    REPORT_OVERFLOW(value_ >= 0);
    return ToInt();
  }

  // Conversion to int or unsigned is lossy. 'explicit' on these operators won't
  // work because there are also other implicit conversion paths (e.g. operator
  // bool then to int which would generate wrong result). Use toInt() and
  // toUnsigned() instead.
  operator int() const = delete;
  operator unsigned() const = delete;

  constexpr operator double() const { return ToDouble(); }
  constexpr operator float() const { return ToFloat(); }
  constexpr operator bool() const { return value_; }

  std::strong_ordering operator<=>(const FixedPoint&) const = default;
  std::partial_ordering operator<=>(double d) const { return ToDouble() <=> d; }
  std::partial_ordering operator<=>(float f) const { return ToFloat() <=> f; }

  FixedPoint operator++(int) {
    value_ = base::ClampAdd(value_, kFixedPointDenominator);
    return *this;
  }

  constexpr Storage RawValue() const { return value_; }
  inline void SetRawValue(int value) { value_ = value; }
  void SetRawValue(int64_t value) {
    if constexpr (sizeof(Storage) < sizeof(int64_t)) {
      REPORT_OVERFLOW(value > kRawValueMin && value < kRawValueMax);
    }
    value_ = static_cast<Storage>(value);
  }

  FixedPoint Abs() const { return FromRawValue(::abs(value_)); }
  Storage Ceil() const {
    if (value_ >= kRawValueMax - kFixedPointDenominator + 1) [[unlikely]] {
      return kIntMax;
    }

    if (value_ >= 0)
      return (value_ + kFixedPointDenominator - 1) / kFixedPointDenominator;
    return ToInt();
  }
  ALWAYS_INLINE Storage Round() const {
    return ToInt() + ((Fraction().RawValue() + (kFixedPointDenominator / 2)) >>
                      kFractionalBits);
  }

  Storage Floor() const {
    if (value_ <= kRawValueMin + kFixedPointDenominator - 1) [[unlikely]] {
      return kIntMin;
    }

    return value_ >> kFractionalBits;
  }

  FixedPoint ClampNegativeToZero() const {
    return value_ < 0 ? FixedPoint() : *this;
  }

  FixedPoint ClampPositiveToZero() const {
    return value_ > 0 ? FixedPoint() : *this;
  }

  FixedPoint ClampIndefiniteToZero() const {
    // We compare to |kFixedPointDenominator| here instead of |kIndefiniteSize|
    // as the operator== for LayoutUnit is inlined below.
    if (value_ == -kFixedPointDenominator)
      return FixedPoint();
    DCHECK_GE(value_, 0);
    return *this;
  }

  constexpr bool HasFraction() const {
    return RawValue() % kFixedPointDenominator;
  }
  constexpr bool IsInteger() const { return !HasFraction(); }

  FixedPoint Fraction() const {
    // Compute fraction using the mod operator to preserve the sign of the value
    // as it may affect rounding.
    return FromRawValue(RawValue() % kFixedPointDenominator);
  }

  bool MightBeSaturated() const {
    return RawValue() == kRawValueMax || RawValue() == kRawValueMin;
  }

  static constexpr float Epsilon() { return 1.0f / kFixedPointDenominator; }

  FixedPoint AddEpsilon() const {
    return FromRawValue(value_ < kRawValueMax ? value_ + 1 : value_);
  }

  static constexpr FixedPoint Max() { return FromRawValue(kRawValueMax); }
  static constexpr FixedPoint Min() { return FromRawValue(kRawValueMin); }

  // Versions of max/min that are slightly smaller/larger than max/min() to
  // allow for rounding without overflowing.
  static constexpr FixedPoint NearlyMax() {
    return FromRawValue(kRawValueMax - kFixedPointDenominator / 2);
  }
  static constexpr FixedPoint NearlyMin() {
    return FromRawValue(kRawValueMin + kFixedPointDenominator / 2);
  }

  static FixedPoint Clamp(double value) { return FromFloatFloor(value); }

  // Multiply by |m| and divide by |d| as a single ("fused") operation, avoiding
  // any saturation of the intermediate result. Rounding matches that of the
  // regular operations (i.e the result of the divide is rounded towards zero).
  FixedPoint MulDiv(FixedPoint m, FixedPoint d) const;

  // Return `std::nullopt` if `this` is the specified value.
  std::optional<FixedPoint> NullOptIf(FixedPoint null_value) const;
  std::optional<FixedPoint> NullOptIfMin() const { return NullOptIf(Min()); }

  String ToString() const;

 private:
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) && \
    defined(COMPILER_GCC) && __OPTIMIZE__
  // If we're building ARM 32-bit on GCC we replace the C++ versions with some
  // native ARM assembly for speed.
  constexpr inline void SaturatedSet(int value) {
    if (std::is_constant_evaluated() || sizeof(Storage) > sizeof(int)) {
      SaturatedSetNonAsm(value);
    } else {
      SaturatedSetAsm(value);
    }
  }

  inline void SaturatedSetAsm(int value) {
    // Figure out how many bits are left for storing the integer part of
    // the fixed point number, and saturate our input to that
    enum { Saturate = 32 - kFractionalBits };

    int result;

    // The following ARM code will Saturate the passed value to the number of
    // bits used for the whole part of the fixed point representation, then
    // shift it up into place. This will result in the low
    // <kFractionalBits> bits all being 0's. When the value saturates
    // this gives a different result to from the C++ case; in the C++ code a
    // saturated value has all the low bits set to 1 (for a +ve number at
    // least). This cannot be done rapidly in ARM ... we live with the
    // difference, for the sake of speed.

    asm("ssat %[output],%[saturate],%[value]\n\t"
        "lsl  %[output],%[shift]"
        : [output] "=r"(result)
        : [value] "r"(value), [saturate] "n"(Saturate),
          [shift] "n"(kFractionalBits));

    value_ = result;
  }

  constexpr inline void SaturatedSet(unsigned value) {
    if (std::is_constant_evaluated() || sizeof(Storage) > sizeof(int)) {
      SaturatedSetNonAsm(value);
    } else {
      SaturatedSetAsm(value);
    }
  }

  inline void SaturatedSetAsm(unsigned value) {
    // Here we are being passed an unsigned value to saturate,
    // even though the result is returned as a signed integer. The ARM
    // instruction for unsigned saturation therefore needs to be given one
    // less bit (i.e. the sign bit) for the saturation to work correctly; hence
    // the '31' below.
    enum { Saturate = 31 - kFractionalBits };

    // The following ARM code will Saturate the passed value to the number of
    // bits used for the whole part of the fixed point representation, then
    // shift it up into place. This will result in the low
    // <kFractionalBits> bits all being 0's. When the value saturates
    // this gives a different result to from the C++ case; in the C++ code a
    // saturated value has all the low bits set to 1. This cannot be done
    // rapidly in ARM, so we live with the difference, for the sake of speed.

    int result;

    asm("usat %[output],%[saturate],%[value]\n\t"
        "lsl  %[output],%[shift]"
        : [output] "=r"(result)
        : [value] "r"(value), [saturate] "n"(Saturate),
          [shift] "n"(kFractionalBits));

    value_ = result;
  }
#else  // end of 32-bit ARM GCC
  ALWAYS_INLINE constexpr void SaturatedSet(int value) {
    SaturatedSetNonAsm(value);
  }

  ALWAYS_INLINE constexpr void SaturatedSet(unsigned value) {
    SaturatedSetNonAsm(value);
  }
#endif

  ALWAYS_INLINE constexpr void SaturatedSetNonAsm(int value) {
    if (value > kIntMax) {
      value_ = kRawValueMax;
    } else if (value < kIntMin) {
      value_ = kRawValueMin;
    } else {
      value_ = static_cast<UnsignedStorageType>(value) << kFractionalBits;
    }
  }

  ALWAYS_INLINE constexpr void SaturatedSetNonAsm(unsigned value) {
    if (value >= static_cast<UnsignedStorageType>(kIntMax)) {
      value_ = kRawValueMax;
    } else {
      value_ = static_cast<UnsignedStorageType>(value) << kFractionalBits;
    }
  }

  Storage value_;
};

using LayoutUnit = FixedPoint<6, int32_t>;
using TextRunLayoutUnit = FixedPoint<16, int32_t>;
using InlineLayoutUnit = FixedPoint<16, int64_t>;

// kIndefiniteSize is a special value used within layout code. It is typical
// within layout to have sizes which are only allowed to be non-negative or
// "indefinite". We use the value of "-1" to represent these indefinite values.
//
// It is common to clamp these indefinite values to zero.
// |LayoutUnit::ClampIndefiniteToZero| provides this functionality, and
// additionally DCHECKs that it isn't some other negative value.
inline constexpr LayoutUnit kIndefiniteSize(-1);

// TODO(kojii): Using three-way comparison (spaceship) operator for `int` makes
// too many cases ambiguous.
inline bool operator<=(const LayoutUnit& a, int b) {
  return a <= LayoutUnit(b);
}

inline bool operator<=(const int a, const LayoutUnit& b) {
  return LayoutUnit(a) <= b;
}

inline bool operator>=(const LayoutUnit& a, int b) {
  return a >= LayoutUnit(b);
}

inline bool operator>=(const int a, const LayoutUnit& b) {
  return LayoutUnit(a) >= b;
}

inline bool operator<(const LayoutUnit& a, int b) {
  return a < LayoutUnit(b);
}

inline bool operator<(const int a, const LayoutUnit& b) {
  return LayoutUnit(a) < b;
}

inline bool operator>(const LayoutUnit& a, int b) {
  return a > LayoutUnit(b);
}

inline bool operator>(const int a, const LayoutUnit& b) {
  return LayoutUnit(a) > b;
}

inline bool operator==(const LayoutUnit& a, int b) {
  return a == LayoutUnit(b);
}

inline bool operator==(const int a, const LayoutUnit& b) {
  return LayoutUnit(a) == b;
}

// For multiplication that's prone to overflow, this bounds it to
// `FixedPoint::Max()` and `FixedPoint::Min()`.
template <unsigned fractional_bits, typename RawValue>
  requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> BoundedMultiply(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  int64_t result =
      static_cast<int64_t>(a.RawValue()) * static_cast<int64_t>(b.RawValue()) /
      FixedPoint<fractional_bits, RawValue>::kFixedPointDenominator;
  int32_t high = static_cast<int32_t>(result >> 32);
  int32_t low = static_cast<int32_t>(result);
  uint32_t saturated =
      (static_cast<uint32_t>(a.RawValue() ^ b.RawValue()) >> 31) +
      FixedPoint<fractional_bits, RawValue>::kRawValueMax;
  // If the higher 32 bits does not match the lower 32 with sign extension the
  // operation overflowed.
  if (high != low >> 31)
    result = saturated;

  return FixedPoint<fractional_bits, RawValue>::FromRawValue(
      static_cast<RawValue>(result));
}

template <unsigned fractional_bits, typename RawValue>
  requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator*(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  return BoundedMultiply(a, b);
}

inline double operator*(const LayoutUnit& a, double b) {
  return a.ToDouble() * b;
}

inline float operator*(const LayoutUnit& a, float b) {
  return a.ToFloat() * b;
}

template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator*(
    const FixedPoint<fractional_bits, RawValue> a,
    std::integral auto b) {
  return FixedPoint<fractional_bits, RawValue>::FromRawValue(
      base::ClampMul(a.RawValue(), b));
}

inline LayoutUnit operator*(std::integral auto a, const LayoutUnit& b) {
  return b * a;
}

constexpr float operator*(const float a, const LayoutUnit& b) {
  return a * b.ToFloat();
}

constexpr double operator*(const double a, const LayoutUnit& b) {
  return a * b.ToDouble();
}

template <unsigned fractional_bits, typename RawValue>
  requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator/(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  int64_t raw_val =
      static_cast<int64_t>(
          FixedPoint<fractional_bits, RawValue>::kFixedPointDenominator) *
      a.RawValue() / b.RawValue();
  return FixedPoint<fractional_bits, RawValue>::FromRawValueWithClamp(raw_val);
}

template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue>
FixedPoint<fractional_bits, RawValue>::MulDiv(FixedPoint m,
                                              FixedPoint d) const {
  int64_t n = static_cast<int64_t>(RawValue()) * m.RawValue();
  int64_t q = n / d.RawValue();
  return FromRawValueWithClamp(q);
}

constexpr float operator/(const LayoutUnit& a, float b) {
  return a.ToFloat() / b;
}

constexpr double operator/(const LayoutUnit& a, double b) {
  return a.ToDouble() / b;
}

template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator/(
    const FixedPoint<fractional_bits, RawValue>& a,
    std::integral auto b) {
  return FixedPoint<fractional_bits, RawValue>::FromRawValue(a.RawValue() / b);
}

constexpr float operator/(const float a, const LayoutUnit& b) {
  return a / b.ToFloat();
}

constexpr double operator/(const double a, const LayoutUnit& b) {
  return a / b.ToDouble();
}

inline LayoutUnit operator/(std::integral auto a, const LayoutUnit& b) {
  return LayoutUnit(a) / b;
}

template <unsigned fractional_bits, typename RawValue>
  requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator%(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  int64_t raw_val = a.RawValue() % b.RawValue();
  return FixedPoint<fractional_bits, RawValue>::FromRawValueWithClamp(raw_val);
}

template <unsigned fractional_bits, typename RawValue>
ALWAYS_INLINE FixedPoint<fractional_bits, RawValue> operator+(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  return FixedPoint<fractional_bits, RawValue>::FromRawValue(
      base::ClampAdd(a.RawValue(), b.RawValue()).RawValue());
}

inline LayoutUnit operator+(const LayoutUnit& a, std::integral auto b) {
  return a + LayoutUnit(b);
}

template <unsigned fractional_bits, typename RawValue>
inline float operator+(const FixedPoint<fractional_bits, RawValue>& a,
                       float b) {
  return a.ToFloat() + b;
}

inline double operator+(const LayoutUnit& a, double b) {
  return a.ToDouble() + b;
}

inline LayoutUnit operator+(std::integral auto a, const LayoutUnit& b) {
  return LayoutUnit(a) + b;
}

constexpr float operator+(const float a, const LayoutUnit& b) {
  return a + b.ToFloat();
}

constexpr double operator+(const double a, const LayoutUnit& b) {
  return a + b.ToDouble();
}

ALWAYS_INLINE LayoutUnit operator-(const LayoutUnit& a, const LayoutUnit& b) {
  return LayoutUnit::FromRawValue(
      base::ClampSub(a.RawValue(), b.RawValue()).RawValue());
}

inline LayoutUnit operator-(const LayoutUnit& a, std::integral auto b) {
  return a - LayoutUnit(b);
}

constexpr float operator-(const LayoutUnit& a, float b) {
  return a.ToFloat() - b;
}

constexpr double operator-(const LayoutUnit& a, double b) {
  return a.ToDouble() - b;
}

inline LayoutUnit operator-(std::integral auto a, const LayoutUnit& b) {
  return LayoutUnit(a) - b;
}

constexpr float operator-(const float a, const LayoutUnit& b) {
  return a - b.ToFloat();
}

template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator-(
    const FixedPoint<fractional_bits, RawValue>& a) {
  return FixedPoint<fractional_bits, RawValue>::FromRawValue(
      (-base::ClampedNumeric(a.RawValue())).RawValue());
}

// Returns the remainder after a division with integer results.
// This calculates the modulo so that:
//   a = static_cast<int>(a / b) * b + IntMod(a, b).
inline LayoutUnit IntMod(const LayoutUnit& a, const LayoutUnit& b) {
  return LayoutUnit::FromRawValue(a.RawValue() % b.RawValue());
}

template <unsigned fractional_bits, typename RawValue, typename SourceStorage>
  requires(sizeof(SourceStorage) <= sizeof(RawValue))
inline FixedPoint<fractional_bits, RawValue>& operator+=(
    FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, SourceStorage>& b) {
  a.SetRawValue(base::ClampAdd(a.RawValue(), b.RawValue()).RawValue());
  return a;
}

template <unsigned fractional_bits, typename RawValue, typename SourceStorage>
  requires(sizeof(SourceStorage) <= sizeof(RawValue))
inline FixedPoint<fractional_bits, RawValue> operator+(
    const FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, SourceStorage>& b) {
  FixedPoint<fractional_bits, RawValue> r = a;
  r += b;
  return r;
}

inline LayoutUnit& operator+=(LayoutUnit& a, std::integral auto b) {
  a = a + LayoutUnit(b);
  return a;
}

inline LayoutUnit& operator+=(LayoutUnit& a, float b) {
  a = LayoutUnit(a + b);
  return a;
}

inline float& operator+=(float& a, const LayoutUnit& b) {
  a = a + b;
  return a;
}

inline LayoutUnit& operator-=(LayoutUnit& a, std::integral auto b) {
  a = a - LayoutUnit(b);
  return a;
}

template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue>& operator-=(
    FixedPoint<fractional_bits, RawValue>& a,
    const FixedPoint<fractional_bits, RawValue>& b) {
  a.SetRawValue(base::ClampSub(a.RawValue(), b.RawValue()).RawValue());
  return a;
}

inline LayoutUnit& operator-=(LayoutUnit& a, float b) {
  a = LayoutUnit(a - b);
  return a;
}

inline float& operator-=(float& a, const LayoutUnit& b) {
  a = a - b;
  return a;
}

inline LayoutUnit& operator*=(LayoutUnit& a, const LayoutUnit& b) {
  a = a * b;
  return a;
}

inline LayoutUnit& operator*=(LayoutUnit& a, float b) {
  a = LayoutUnit(a * b);
  return a;
}

inline float& operator*=(float& a, const LayoutUnit& b) {
  a = a * b;
  return a;
}

inline LayoutUnit& operator/=(LayoutUnit& a, const LayoutUnit& b) {
  a = a / b;
  return a;
}

inline LayoutUnit& operator/=(LayoutUnit& a, float b) {
  a = LayoutUnit(a / b);
  return a;
}

inline float& operator/=(float& a, const LayoutUnit& b) {
  a = a / b;
  return a;
}

inline int SnapSizeToPixel(LayoutUnit size, LayoutUnit location) {
  LayoutUnit fraction = location.Fraction();
  int result = (fraction + size).Round() - fraction.Round();
  if (result == 0 && (size.RawValue() > 4 || size.RawValue() < -4))
      [[unlikely]] {
    return size > 0 ? 1 : -1;
  }
  return result;
}

inline int SnapSizeToPixelAllowingZero(LayoutUnit size, LayoutUnit location) {
  LayoutUnit fraction = location.Fraction();
  return (fraction + size).Round() - fraction.Round();
}

inline int RoundToInt(LayoutUnit value) {
  return value.Round();
}

inline int FloorToInt(LayoutUnit value) {
  return value.Floor();
}

inline int CeilToInt(LayoutUnit value) {
  return value.Ceil();
}

inline LayoutUnit AbsoluteValue(const LayoutUnit& value) {
  return value.Abs();
}

template <unsigned fractional_bits, typename RawValue>
inline std::optional<FixedPoint<fractional_bits, RawValue>>
FixedPoint<fractional_bits, RawValue>::NullOptIf(FixedPoint null_value) const {
  if (*this == null_value) {
    return std::nullopt;
  }
  return *this;
}

#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) && \
    defined(COMPILER_GCC) && __OPTIMIZE__
inline int GetMaxSaturatedSetResultForTesting() {
  // For ARM Asm version the set function maxes out to the biggest
  // possible integer part with the fractional part zero'd out.
  // e.g. 0x7fffffc0.
  return LayoutUnit::kRawValueMax & ~(LayoutUnit::kFixedPointDenominator - 1);
}

inline int GetMinSaturatedSetResultForTesting() {
  return LayoutUnit::kRawValueMin;
}
#else
ALWAYS_INLINE int GetMaxSaturatedSetResultForTesting() {
  // For C version the set function maxes out to max int, this differs from
  // the ARM asm version.
  return LayoutUnit::kRawValueMax;
}

ALWAYS_INLINE int GetMinSaturatedSetResultForTesting() {
  return LayoutUnit::kRawValueMin;
}
#endif  // CPU(ARM) && COMPILER(GCC)

template <unsigned fractional_bits, typename RawValue>
PLATFORM_EXPORT std::ostream& operator<<(
    std::ostream&,
    const FixedPoint<fractional_bits, RawValue>&);

}  // namespace blink

WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::LayoutUnit)

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_
