All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.scalajs.runtime.RuntimeLong.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-M3
Show newest version
package scala.scalajs.runtime

import scala.annotation.tailrec

import scala.scalajs.js
import js.|
import js.JSNumberOps._
import js.JSStringOps._

/* IMPORTANT NOTICE about this file
 *
 * The code of RuntimeLong is code-size- and performance critical. The methods
 * of this class are used for every single primitive operation on Longs, and
 * must therefore be as fast as they can.
 *
 * This means that this implementation is oriented for performance over
 * readability and idiomatic code.
 *
 * DRY is applied as much as possible but is bounded by the performance and
 * code size requirements. We use a lot of inline_xyz helpers meant to be used
 * when we already have the parameters on stack, but they are sometimes
 * duplicated in entry points to avoid the explicit extraction of heap fields
 * into temporary variables when they are used only once.
 *
 * Otherwise, we typically extract the lo and hi fields from the heap into
 * local variables once, whether explicitly in vals or implicitly when passed
 * as arguments to inlineable methods. This reduces heap/record accesses, and
 * allows both our optimizer and the JIT to know that we indeed always have the
 * same value (the JIT does not even know that fields are immutable, but even
 * our optimizer does not make use of that information).
 */

/** Emulates a Long on the JavaScript platform. */
@inline
final class RuntimeLong(val lo: Int, val hi: Int)
    extends java.lang.Number with java.io.Serializable
    with java.lang.Comparable[java.lang.Long] { a =>

  import RuntimeLong._
  import Utils._

  /** Constructs a Long from an Int. */
  def this(value: Int) = this(value, value >> 31)

  // Binary compatibility for the old (l, m, h) encoding

  @deprecated("Use the constructor with (lo, hi) instead.", "0.6.6")
  def this(l: Int, m: Int, h: Int) =
    this(l | (m << 22), (m >> 10) | (h << 12))

  @deprecated("Use lo and hi instead.", "0.6.6")
  def l: Int = lo & ((1 << 22) - 1)

  @deprecated("Use lo and hi instead.", "0.6.6")
  def m: Int = (lo >>> 22) & ((hi & ((1 << 12) - 1)) << 10)

  @deprecated("Use lo and hi instead.", "0.6.6")
  def h: Int = hi >>> 12

  // Universal equality

  @inline
  override def equals(that: Any): Boolean = that match {
    case b: RuntimeLong => inline_equals(b)
    case _              => false
  }

  @inline override def hashCode(): Int = lo ^ hi

  // String operations

  @inline override def toString(): String =
    RuntimeLong.toString(lo, hi)

  // Conversions

  @inline def toByte: Byte = lo.toByte
  @inline def toShort: Short = lo.toShort
  @inline def toChar: Char = lo.toChar
  @inline def toInt: Int = lo
  @inline def toLong: Long = this.asInstanceOf[Long]
  @inline def toFloat: Float = toDouble.toFloat
  @inline def toDouble: Double = RuntimeLong.toDouble(lo, hi)

  // java.lang.Number

  @inline override def byteValue(): Byte = toByte
  @inline override def shortValue(): Short = toShort
  @inline def intValue(): Int = toInt
  @inline def longValue(): Long = toLong
  @inline def floatValue(): Float = toFloat
  @inline def doubleValue(): Double = toDouble

  // Comparisons and java.lang.Comparable interface

  @inline
  def compareTo(b: RuntimeLong): Int =
    RuntimeLong.compare(a.lo, a.hi, b.lo, b.hi)

  @inline
  def compareTo(that: java.lang.Long): Int =
    compareTo(that.asInstanceOf[RuntimeLong])

  @inline
  private def inline_equals(b: RuntimeLong): Boolean =
    a.lo == b.lo && a.hi == b.hi

  @inline
  def equals(b: RuntimeLong): Boolean =
    inline_equals(b)

  @inline
  def notEquals(b: RuntimeLong): Boolean =
    !inline_equals(b)

  @inline
  def <(b: RuntimeLong): Boolean = {
    /* We should use `inlineUnsignedInt_<(a.lo, b.lo)`, but that first extracts
     * a.lo and b.lo into local variables, which cause the if/else not to be
     * a valid JavaScript expression anymore. This causes useless explosion of
     * JavaScript code at call site, when inlined. So we manually inline
     * `inlineUnsignedInt_<(a.lo, b.lo)` to avoid that problem.
     */
    val ahi = a.hi
    val bhi = b.hi
    if (ahi == bhi) (a.lo ^ 0x80000000) < (b.lo ^ 0x80000000)
    else ahi < bhi
  }

  @inline
  def <=(b: RuntimeLong): Boolean = {
    /* Manually inline `inlineUnsignedInt_<=(a.lo, b.lo)`.
     * See the comment in `<` for the rationale.
     */
    val ahi = a.hi
    val bhi = b.hi
    if (ahi == bhi) (a.lo ^ 0x80000000) <= (b.lo ^ 0x80000000)
    else ahi < bhi
  }

  @inline
  def >(b: RuntimeLong): Boolean = {
    /* Manually inline `inlineUnsignedInt_>a.lo, b.lo)`.
     * See the comment in `<` for the rationale.
     */
    val ahi = a.hi
    val bhi = b.hi
    if (ahi == bhi) (a.lo ^ 0x80000000) > (b.lo ^ 0x80000000)
    else ahi > bhi
  }

  @inline
  def >=(b: RuntimeLong): Boolean = {
    /* Manually inline `inlineUnsignedInt_>=(a.lo, b.lo)`.
     * See the comment in `<` for the rationale.
     */
    val ahi = a.hi
    val bhi = b.hi
    if (ahi == bhi) (a.lo ^ 0x80000000) >= (b.lo ^ 0x80000000)
    else ahi > bhi
  }

  // Bitwise operations

  @inline
  def unary_~ : RuntimeLong = // scalastyle:ignore
    new RuntimeLong(~lo, ~hi)

  @inline
  def |(b: RuntimeLong): RuntimeLong =
    new RuntimeLong(a.lo | b.lo, a.hi | b.hi)

  @inline
  def &(b: RuntimeLong): RuntimeLong =
    new RuntimeLong(a.lo & b.lo, a.hi & b.hi)

  @inline
  def ^(b: RuntimeLong): RuntimeLong =
    new RuntimeLong(a.lo ^ b.lo, a.hi ^ b.hi)

  // Shifts

  /** Shift left */
  @inline
  def <<(n: Int): RuntimeLong = {
    /* This should *reasonably* be:
     *   val n1 = n & 63
     *   if (n1 < 32)
     *     new RuntimeLong(lo << n1, if (n1 == 0) hi else (lo >>> 32-n1) | (hi << n1))
     *   else
     *     new RuntimeLong(0, lo << n1)
     *
     * Replacing n1 by its definition, we have:
     *   if (n & 63 < 32)
     *     new RuntimeLong(lo << (n & 63),
     *         if ((n & 63) == 0) hi else (lo >>> 32-(n & 63)) | (hi << (n & 63)))
     *   else
     *     new RuntimeLong(0, lo << (n & 63))
     *
     * Since the values on the rhs of shifts are always in arithmetic mod 32,
     * we can get:
     *   if (n & 63 < 32)
     *     new RuntimeLong(lo << n, if ((n & 63) == 0) hi else (lo >>> -n) | (hi << n))
     *   else
     *     new RuntimeLong(0, lo << n)
     *
     * The condition `n & 63 < 32` is equivalent to
     *   (n & 63) & 32 == 0
     *   n & (63 & 32) == 0
     *   n & 32 == 0
     *
     * In the then part, we have `n & 32 == 0` hence `n & 63 == n & 31`:
     *   new RuntimeLong(lo << n, if ((n & 31) == 0) hi else (lo >>> -n) | (hi << n))
     *
     * Consider the following portion:
     *   if ((n & 31) == 0) hi else (lo >>> -n) | (hi << n)
     * When (n & 31) == 0, `hi == (hi << n)` and therefore we have
     *   (if ((n & 31) == 0) 0 else (lo >>> -n)) | (hi << n)
     *
     * The left part of the |
     *   if ((n & 31) == 0) 0 else (lo >>> -n)
     * has the following branchless version:
     *   lo >>> 1 >>> (31-n)
     * Indeed, when `n & 31 == 0, we have
     *   lo >>> 1 >>> 31 == 0
     * and when `n & 31 != 0`, we know that ((31-n) & 31) < 31, and hence we have
     *   lo >>> 1 >>> (31-n) == lo >>> (1+31-n) == lo >>> (32-n) == lo >>> -n
     *
     * Was it good? We have traded
     *   if ((n & 31) == 0) hi else (lo >>> -n) | (hi << n)
     * for
     *   (lo >>> 1 >>> (31-n)) | (hi << n)
     * When (n & 31) != 0, which is the common case, we have traded a test
     * `if ((n & 31) == 0)` for one additional constant shift `>>> 1`. That's
     * probably worth it performance-wise. The code is also shorter.
     *
     * Summarizing, so far we have
     *   if (n & 32 == 0)
     *     new RuntimeLong(lo << n, (lo >>> 1 >>> (31-n)) | (hi << n))
     *   else
     *     new RuntimeLong(0, lo << n)
     *
     * If we distribute the condition in the lo and hi arguments of the
     * constructors, we get a version with only one RuntimeLong output, which
     * avoids reification as records by the optimizer, yielding shorter code.
     * It is potentially slightly less efficient, except when `n` is constant,
     * which is often the case anyway.
     *
     * Finally we have:
     */
    new RuntimeLong(
        if ((n & 32) == 0) lo << n else 0,
        if ((n & 32) == 0) (lo >>> 1 >>> (31-n)) | (hi << n) else lo << n)
  }

  /** Logical shift right */
  @inline
  def >>>(n: Int): RuntimeLong = {
    // This derives in a similar way as in <<
    new RuntimeLong(
        if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >>> n,
        if ((n & 32) == 0) hi >>> n else 0)
  }

  /** Arithmetic shift right */
  @inline
  def >>(n: Int): RuntimeLong = {
    // This derives in a similar way as in <<
    new RuntimeLong(
        if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >> n,
        if ((n & 32) == 0) hi >> n else hi >> 31)
  }

  // Arithmetic operations

  @inline
  def unary_- : RuntimeLong = { // scalastyle:ignore
    val lo = this.lo
    val hi = this.hi
    new RuntimeLong(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi))
  }

  @inline
  def +(b: RuntimeLong): RuntimeLong = {
    val alo = a.lo
    val ahi = a.hi
    val bhi = b.hi
    val lo = alo + b.lo
    new RuntimeLong(lo,
        if (inlineUnsignedInt_<(lo, alo)) ahi + bhi + 1 else ahi + bhi)
  }

  @inline
  def -(b: RuntimeLong): RuntimeLong = {
    val alo = a.lo
    val ahi = a.hi
    val bhi = b.hi
    val lo = alo - b.lo
    new RuntimeLong(lo,
        if (inlineUnsignedInt_>(lo, alo)) ahi - bhi - 1 else ahi - bhi)
  }

  @inline
  def *(b: RuntimeLong): RuntimeLong = {
    val alo = a.lo
    val blo = b.lo
    new RuntimeLong(alo * blo, RuntimeLong.timesHi(alo, a.hi, blo, b.hi))
  }

  @inline
  def /(b: RuntimeLong): RuntimeLong =
    RuntimeLong.divide(a, b)

  /** `java.lang.Long.divideUnsigned(a, b)` */
  @inline
  def divideUnsigned(b: RuntimeLong): RuntimeLong =
    RuntimeLong.divideUnsigned(a, b)

  @inline
  def %(b: RuntimeLong): RuntimeLong =
    RuntimeLong.remainder(a, b)

  /** `java.lang.Long.remainderUnsigned(a, b)` */
  @inline
  def remainderUnsigned(b: RuntimeLong): RuntimeLong =
    RuntimeLong.remainderUnsigned(a, b)

  // TODO Remove these. They were support for intrinsics before 0.6.6.

  @deprecated("Use java.lang.Long.toBinaryString instead.", "0.6.6")
  def toBinaryString: String = {
    val zeros = "00000000000000000000000000000000" // 32 zeros
    @inline def padBinary32(i: Int) = {
      val s = Integer.toBinaryString(i)
      zeros.substring(s.length) + s
    }

    val lo = this.lo
    val hi = this.hi

    if (hi != 0) Integer.toBinaryString(hi) + padBinary32(lo)
    else Integer.toBinaryString(lo)
  }

  @deprecated("Use java.lang.Long.toHexString instead.", "0.6.6")
  def toHexString: String = {
    val zeros = "00000000" // 8 zeros
    @inline def padHex8(i: Int) = {
      val s = Integer.toHexString(i)
      zeros.substring(s.length) + s
    }

    val lo = this.lo
    val hi = this.hi

    if (hi != 0) Integer.toHexString(hi) + padHex8(lo)
    else Integer.toHexString(lo)
  }

  @deprecated("Use java.lang.Long.toOctalString instead.", "0.6.6")
  def toOctalString: String = {
    val zeros = "0000000000" // 10 zeros
    @inline def padOctal10(i: Int) = {
      val s = Integer.toOctalString(i)
      zeros.substring(s.length) + s
    }

    val lo = this.lo
    val hi = this.hi

    val lp = lo & 0x3fffffff
    val mp = ((lo >>> 30) + (hi << 2)) & 0x3fffffff
    val hp = hi >>> 28

    if (hp != 0) Integer.toOctalString(hp) + padOctal10(mp) + padOctal10(lp)
    else if (mp != 0) Integer.toOctalString(mp) + padOctal10(lp)
    else Integer.toOctalString(lp)
  }

  @deprecated("Use java.lang.Long.bitCount instead.", "0.6.6")
  def bitCount: Int =
    Integer.bitCount(lo) + Integer.bitCount(hi)

  @deprecated("Use java.lang.Long.signum instead.", "0.6.6")
  def signum: RuntimeLong = {
    val hi = this.hi
    if (hi < 0) MinusOne
    else if (isZero(lo, hi)) Zero
    else One
  }

  @deprecated("Use java.lang.Long.numberOfLeadingZeros instead.", "0.6.6")
  def numberOfLeadingZeros: Int = {
    val hi = this.hi
    if (hi != 0) Integer.numberOfLeadingZeros(hi)
    else Integer.numberOfLeadingZeros(lo) + 32
  }

  @deprecated("Use java.lang.Long.numberOfTrailingZeros instead.", "0.6.6")
  def numberOfTrailingZeros: Int = {
    val lo = this.lo
    if (lo != 0) Integer.numberOfTrailingZeros(lo)
    else Integer.numberOfTrailingZeros(hi) + 32
  }

  // TODO Remove those. There are remnant of before we had LongReflectiveCall

  @deprecated("Just use `this` instead.", "0.6.6")
  def unary_+ : RuntimeLong = this // scalastyle:ignore

  @deprecated("Use `this.toString + y` instead.", "0.6.6")
  def +(y: String): String = this.toString + y

}

object RuntimeLong {
  import Utils._

  private final val TwoPow32 = 4294967296.0
  private final val TwoPow63 = 9223372036854775808.0

  /** The magical mask that allows to test whether an unsigned long is a safe
   *  double.
   *  @see Utils.isUnsignedSafeDouble
   */
  private final val UnsignedSafeDoubleHiMask = 0xffe00000

  private final val AskQuotient = 0
  private final val AskRemainder = 1
  private final val AskBoth = 2

  /** The hi part of a (lo, hi) return value. */
  private[this] var hiReturn: Int = _

  /** The instance of 0L, which is used by the `Emitter` in various places. */
  val Zero = new RuntimeLong(0, 0)

  @deprecated("Use new RuntimeLong(1, 0) instead.", "0.6.11")
  def One: RuntimeLong = new RuntimeLong(1, 0)

  @deprecated("Use new RuntimeLong(-1, -1) instead.", "0.6.11")
  def MinusOne: RuntimeLong = new RuntimeLong(-1, -1)

  @deprecated("Use new RuntimeLong(0, 0x80000000) instead.", "0.6.11")
  def MinValue: RuntimeLong = new RuntimeLong(0, 0x80000000)

  @deprecated("Use new RuntimeLong(0xffffffff, 0x7fffffff) instead.", "0.6.11")
  def MaxValue: RuntimeLong = new RuntimeLong(0xffffffff, 0x7fffffff)

  private def toString(lo: Int, hi: Int): String = {
    if (isInt32(lo, hi)) {
      lo.toString()
    } else if (hi < 0) {
      "-" + toUnsignedString(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi))
    } else {
      toUnsignedString(lo, hi)
    }
  }

  private def toUnsignedString(lo: Int, hi: Int): String = {
    // This is called only if (lo, hi) is not an Int32

    if (isUnsignedSafeDouble(hi)) {
      // (lo, hi) is small enough to be a Double, use that directly
      asUnsignedSafeDouble(lo, hi).toString
    } else {
      /* At this point, (lo, hi) >= 2^53.
       * We divide (lo, hi) once by 10^9 and keep the remainder.
       *
       * The remainder must then be < 10^9, and is therefore an int32.
       *
       * The quotient must be <= ULong.MaxValue / 10^9, which is < 2^53, and
       * is therefore a valid double. It must also be non-zero, since
       * (lo, hi) >= 2^53 > 10^9.
       */
      val TenPow9Lo = 1000000000L.toInt
      val TenPow9Hi = (1000000000L >>> 32).toInt

      val quotRem = unsignedDivModHelper(lo, hi, TenPow9Lo, TenPow9Hi,
          AskBoth).asInstanceOf[js.Tuple4[Int, Int, Int, Int]]
      val quotLo = quotRem._1
      val quotHi = quotRem._2
      val rem = quotRem._3 // remHi must be 0 by construction

      val quot = asUnsignedSafeDouble(quotLo, quotHi)

      val remStr = rem.toString
      quot.toString + "000000000".jsSubstring(remStr.length) + remStr
    }
  }

  private def toDouble(lo: Int, hi: Int): Double = {
    if (hi < 0) {
      // We do .toUint on the hi part specifically for MinValue
      -(inline_hi_unary_-(lo, hi).toUint * TwoPow32 + inline_lo_unary_-(lo).toUint)
    } else {
      hi * TwoPow32 + lo.toUint
    }
  }

  @inline
  def fromDouble(value: Double): RuntimeLong = {
    val lo = fromDoubleImpl(value)
    new RuntimeLong(lo, hiReturn)
  }

  private def fromDoubleImpl(value: Double): Int = {
    /* When value is NaN, the conditions of the 3 `if`s are false, and we end
     * up returning (NaN | 0, (NaN / TwoPow32) | 0), which is correctly (0, 0).
     */

    if (value < -TwoPow63) {
      hiReturn = 0x80000000
      0
    } else if (value >= TwoPow63) {
      hiReturn = 0x7fffffff
      0xffffffff
    } else {
      val rawLo = rawToInt(value)
      val rawHi = rawToInt(value / TwoPow32)

      /* Magic!
       *
       * When value < 0, this should *reasonably* be:
       *   val absValue = -value
       *   val absLo = rawToInt(absValue)
       *   val absHi = rawToInt(absValue / TwoPow32)
       *   val lo = -absLo
       *   hiReturn = if (absLo != 0) ~absHi else -absHi
       *   return lo
       *
       * Using the fact that rawToInt(-x) == -rawToInt(x), we can rewrite
       * absLo and absHi without absValue as:
       *   val absLo = -rawToInt(value)
       *             = -rawLo
       *   val absHi = -rawToInt(value / TwoPow32)
       *             = -rawHi
       *
       * Now, we can replace absLo in the definition of lo and get:
       *   val lo = -(-rawLo)
       *          = rawLo
       *
       * The `hiReturn` definition can be rewritten as
       *   hiReturn = if (lo != 0) -absHi - 1 else -absHi
       *            = if (rawLo != 0) -(-rawHi) - 1 else -(-rawHi)
       *            = if (rawLo != 0) rawHi - 1 else rawHi
       *
       * Now that we do not need absValue, absLo nor absHi anymore, we end
       * end up with:
       *   hiReturn = if (rawLo != 0) rawHi - 1 else rawHi
       *   return rawLo
       *
       * When value >= 0, the definitions are simply
       *   hiReturn = rawToInt(value / TwoPow32) = rawHi
       *   lo = rawToInt(value) = rawLo
       *
       * Combining the negative and positive cases, we get:
       */
      hiReturn = if (value < 0 && rawLo != 0) rawHi - 1 else rawHi
      rawLo
    }
  }

  private def compare(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    if (ahi == bhi) {
      if (alo == blo) 0
      else if (inlineUnsignedInt_<(alo, blo)) -1
      else 1
    } else {
      if (ahi < bhi) -1
      else 1
    }
  }

  private def timesHi(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    val a0 = alo & 0xffff
    val a1 = alo >>> 16
    val a2 = ahi & 0xffff
    val a3 = ahi >>> 16
    val b0 = blo & 0xffff
    val b1 = blo >>> 16
    val b2 = bhi & 0xffff
    val b3 = bhi >>> 16

    val c1part = ((a0 * b0) >>> 16) + (a1 * b0)
    var c2 = (c1part >>> 16) + (((c1part & 0xffff) + (a0 * b1)) >>> 16)
    var c3 = c2 >>> 16
    c2 = (c2 & 0xffff) + a2 * b0
    c3 = c3 + (c2 >>> 16)
    c2 = (c2 & 0xffff) + a1 * b1
    c3 = c3 + (c2 >>> 16)
    c2 = (c2 & 0xffff) + a0 * b2
    c3 = c3 + (c2 >>> 16)
    c3 = c3 + a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3

    (c2 & 0xffff) | (c3 << 16)
  }

  @inline
  def divide(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
    val lo = divideImpl(a.lo, a.hi, b.lo, b.hi)
    new RuntimeLong(lo, hiReturn)
  }

  def divideImpl(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    if (isZero(blo, bhi))
      throw new ArithmeticException("/ by zero")

    if (isInt32(alo, ahi)) {
      if (isInt32(blo, bhi)) {
        if (alo == Int.MinValue && blo == -1) {
          hiReturn = 0
          Int.MinValue
        } else {
          val lo = alo / blo
          hiReturn = lo >> 31
          lo
        }
      } else {
        // Either a == Int.MinValue && b == (Int.MaxValue + 1), or (abs(b) > abs(a))
        if (alo == Int.MinValue && (blo == 0x80000000 && bhi == 0)) {
          hiReturn = -1
          -1
        } else {
          // 0L, because abs(b) > abs(a)
          hiReturn = 0
          0
        }
      }
    } else {
      val (aNeg, aAbs) = inline_abs(alo, ahi)
      val (bNeg, bAbs) = inline_abs(blo, bhi)
      val absRLo = unsigned_/(aAbs.lo, aAbs.hi, bAbs.lo, bAbs.hi)
      if (aNeg == bNeg) absRLo
      else inline_hiReturn_unary_-(absRLo, hiReturn)
    }
  }

  @inline
  def divideUnsigned(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
    val lo = divideUnsignedImpl(a.lo, a.hi, b.lo, b.hi)
    new RuntimeLong(lo, hiReturn)
  }

  def divideUnsignedImpl(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    if (isZero(blo, bhi))
      throw new ArithmeticException("/ by zero")

    if (isUInt32(ahi)) {
      if (isUInt32(bhi)) {
        hiReturn = 0
        // Integer.divideUnsigned(alo, blo), inaccessible when compiling on JDK < 8
        rawToInt(alo.toUint / blo.toUint)
      } else {
        // a < b
        hiReturn = 0
        0
      }
    } else {
      unsigned_/(alo, ahi, blo, bhi)
    }
  }

  private def unsigned_/(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    // This method is not called if isInt32(alo, ahi) nor if isZero(blo, bhi)
    if (isUnsignedSafeDouble(ahi)) {
      if (isUnsignedSafeDouble(bhi)) {
        val aDouble = asUnsignedSafeDouble(alo, ahi)
        val bDouble = asUnsignedSafeDouble(blo, bhi)
        val rDouble = aDouble / bDouble
        hiReturn = unsignedSafeDoubleHi(rDouble)
        unsignedSafeDoubleLo(rDouble)
      } else {
        // 0L, because b > a
        hiReturn = 0
        0
      }
    } else {
      if (bhi == 0 && isPowerOfTwo_IKnowItsNot0(blo)) {
        val pow = log2OfPowerOfTwo(blo)
        hiReturn = ahi >>> pow
        (alo >>> pow) | (ahi << 1 << (31-pow))
      } else if (blo == 0 && isPowerOfTwo_IKnowItsNot0(bhi)) {
        val pow = log2OfPowerOfTwo(bhi)
        hiReturn = 0
        ahi >>> pow
      } else {
        unsignedDivModHelper(alo, ahi, blo, bhi, AskQuotient).asInstanceOf[Int]
      }
    }
  }

  @inline
  def remainder(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
    val lo = remainderImpl(a.lo, a.hi, b.lo, b.hi)
    new RuntimeLong(lo, hiReturn)
  }

  def remainderImpl(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    if (isZero(blo, bhi))
      throw new ArithmeticException("/ by zero")

    if (isInt32(alo, ahi)) {
      if (isInt32(blo, bhi)) {
        if (blo != -1) {
          val lo = alo % blo
          hiReturn = lo >> 31
          lo
        } else {
          // Work around https://github.com/ariya/phantomjs/issues/12198
          hiReturn = 0
          0
        }
      } else {
        // Either a == Int.MinValue && b == (Int.MaxValue + 1), or (abs(b) > abs(a))
        if (alo == Int.MinValue && (blo == 0x80000000 && bhi == 0)) {
          hiReturn = 0
          0
        } else {
          // a, because abs(b) > abs(a)
          hiReturn = ahi
          alo
        }
      }
    } else {
      val (aNeg, aAbs) = inline_abs(alo, ahi)
      val (_, bAbs) = inline_abs(blo, bhi)
      val absRLo = unsigned_%(aAbs.lo, aAbs.hi, bAbs.lo, bAbs.hi)
      if (aNeg) inline_hiReturn_unary_-(absRLo, hiReturn)
      else absRLo
    }
  }

  @inline
  def remainderUnsigned(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
    val lo = remainderUnsignedImpl(a.lo, a.hi, b.lo, b.hi)
    new RuntimeLong(lo, hiReturn)
  }

  def remainderUnsignedImpl(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    if (isZero(blo, bhi))
      throw new ArithmeticException("/ by zero")

    if (isUInt32(ahi)) {
      if (isUInt32(bhi)) {
        hiReturn = 0
        // Integer.remainderUnsigned(alo, blo), inaccessible when compiling on JDK < 8
        rawToInt(alo.toUint % blo.toUint)
      } else {
        // a < b
        hiReturn = ahi
        alo
      }
    } else {
      unsigned_%(alo, ahi, blo, bhi)
    }
  }

  private def unsigned_%(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = {
    // This method is not called if isInt32(alo, ahi) nor if isZero(blo, bhi)
    if (isUnsignedSafeDouble(ahi)) {
      if (isUnsignedSafeDouble(bhi)) {
        val aDouble = asUnsignedSafeDouble(alo, ahi)
        val bDouble = asUnsignedSafeDouble(blo, bhi)
        val rDouble = aDouble % bDouble
        hiReturn = unsignedSafeDoubleHi(rDouble)
        unsignedSafeDoubleLo(rDouble)
      } else {
        // a, because b > a
        hiReturn = ahi
        alo
      }
    } else {
      if (bhi == 0 && isPowerOfTwo_IKnowItsNot0(blo)) {
        hiReturn = 0
        alo & (blo - 1)
      } else if (blo == 0 && isPowerOfTwo_IKnowItsNot0(bhi)) {
        hiReturn = ahi & (bhi - 1)
        alo
      } else {
        unsignedDivModHelper(alo, ahi, blo, bhi, AskRemainder).asInstanceOf[Int]
      }
    }
  }

  private def unsignedDivModHelper(alo: Int, ahi: Int, blo: Int, bhi: Int,
      ask: Int): Int | js.Tuple4[Int, Int, Int, Int] = {

    var shift =
      inlineNumberOfLeadingZeros(blo, bhi) - inlineNumberOfLeadingZeros(alo, ahi)
    val initialBShift = new RuntimeLong(blo, bhi) << shift
    var bShiftLo = initialBShift.lo
    var bShiftHi = initialBShift.hi
    var remLo = alo
    var remHi = ahi
    var quotLo = 0
    var quotHi = 0

    /* Invariants:
     *   bShift == b << shift == b * 2^shift
     *   quot >= 0
     *   0 <= rem < 2 * bShift
     *   quot * b + rem == a
     *
     * The loop condition should be
     *   while (shift >= 0 && !isUnsignedSafeDouble(remHi))
     * but we manually inline isUnsignedSafeDouble because remHi is a var. If
     * we let the optimizer inline it, it will first store remHi in a temporary
     * val, which will explose the while condition as a while(true) + if +
     * break, and we don't want that.
     */
    while (shift >= 0 && (remHi & UnsignedSafeDoubleHiMask) != 0) {
      if (inlineUnsigned_>=(remLo, remHi, bShiftLo, bShiftHi)) {
        val newRem =
          new RuntimeLong(remLo, remHi) - new RuntimeLong(bShiftLo, bShiftHi)
        remLo = newRem.lo
        remHi = newRem.hi
        if (shift < 32)
          quotLo |= (1 << shift)
        else
          quotHi |= (1 << shift) // == (1 << (shift - 32))
      }
      shift -= 1
      val newBShift = new RuntimeLong(bShiftLo, bShiftHi) >>> 1
      bShiftLo = newBShift.lo
      bShiftHi = newBShift.hi
    }

    // Now rem < 2^53, we can finish with a double division
    if (inlineUnsigned_>=(remLo, remHi, blo, bhi)) {
      val remDouble = asUnsignedSafeDouble(remLo, remHi)
      val bDouble = asUnsignedSafeDouble(blo, bhi)

      if (ask != AskRemainder) {
        val rem_div_bDouble = fromUnsignedSafeDouble(remDouble / bDouble)
        val newQuot = new RuntimeLong(quotLo, quotHi) + rem_div_bDouble
        quotLo = newQuot.lo
        quotHi = newQuot.hi
      }

      if (ask != AskQuotient) {
        val rem_mod_bDouble = remDouble % bDouble
        remLo = unsignedSafeDoubleLo(rem_mod_bDouble)
        remHi = unsignedSafeDoubleHi(rem_mod_bDouble)
      }
    }

    if (ask == AskQuotient) {
      hiReturn = quotHi
      quotLo
    } else if (ask == AskRemainder) {
      hiReturn = remHi
      remLo
    } else {
      js.Tuple4(quotLo, quotHi, remLo, remHi)
    }
  }

  @inline
  private def inline_hiReturn_unary_-(lo: Int, hi: Int): Int = {
    hiReturn = inline_hi_unary_-(lo, hi)
    inline_lo_unary_-(lo)
  }

  // In a different object so they can be inlined without cost
  private object Utils {
    /** Tests whether the long (lo, hi) is 0. */
    @inline def isZero(lo: Int, hi: Int): Boolean =
      (lo | hi) == 0

    /** Tests whether the long (lo, hi)'s mathematic value fits in a signed Int. */
    @inline def isInt32(lo: Int, hi: Int): Boolean =
      hi == (lo >> 31)

    /** Tests whether the long (_, hi)'s mathematic value fits in an unsigned Int. */
    @inline def isUInt32(hi: Int): Boolean =
      hi == 0

    /** Tests whether an unsigned long (lo, hi) is a safe Double.
     *  This test is in fact slightly stricter than necessary, as it tests
     *  whether `x < 2^53`, although x == 2^53 would be a perfectly safe
     *  Double. The reason we do this is that testing `x <= 2^53` is much
     *  slower, as `x == 2^53` basically has to be treated specially.
     *  Since there is virtually no gain to treating 2^53 itself as a safe
     *  Double, compared to all numbers smaller than it, we don't bother, and
     *  stay on the fast side.
     */
    @inline def isUnsignedSafeDouble(hi: Int): Boolean =
      (hi & UnsignedSafeDoubleHiMask) == 0

    /** Converts an unsigned safe double into its Double representation. */
    @inline def asUnsignedSafeDouble(lo: Int, hi: Int): Double =
      hi * TwoPow32 + lo.toUint

    /** Converts an unsigned safe double into its RuntimeLong representation. */
    @inline def fromUnsignedSafeDouble(x: Double): RuntimeLong =
      new RuntimeLong(unsignedSafeDoubleLo(x), unsignedSafeDoubleHi(x))

    /** Computes the lo part of a long from an unsigned safe double. */
    @inline def unsignedSafeDoubleLo(x: Double): Int =
      rawToInt(x)

    /** Computes the hi part of a long from an unsigned safe double. */
    @inline def unsignedSafeDoubleHi(x: Double): Int =
      rawToInt(x / TwoPow32)

    /** Performs the JavaScript operation `(x | 0)`. */
    @inline def rawToInt(x: Double): Int =
      (x.asInstanceOf[js.Dynamic] | 0.asInstanceOf[js.Dynamic]).asInstanceOf[Int]

    /** Tests whether the given non-zero unsigned Int is an exact power of 2. */
    @inline def isPowerOfTwo_IKnowItsNot0(i: Int): Boolean =
      (i & (i - 1)) == 0

    /** Returns the log2 of the given unsigned Int assuming it is an exact power of 2. */
    @inline def log2OfPowerOfTwo(i: Int): Int =
      31 - Integer.numberOfLeadingZeros(i)

    /** Returns the number of leading zeros in the given long (lo, hi). */
    @inline def inlineNumberOfLeadingZeros(lo: Int, hi: Int): Int =
      if (hi != 0) Integer.numberOfLeadingZeros(hi)
      else Integer.numberOfLeadingZeros(lo) + 32

    /** Tests whether the unsigned long (alo, ahi) is >= (blo, bhi). */
    @inline
    def inlineUnsigned_>=(alo: Int, ahi: Int, blo: Int, bhi: Int): Boolean =
      if (ahi == bhi) inlineUnsignedInt_>=(alo, blo)
      else inlineUnsignedInt_>=(ahi, bhi)

    @inline
    def inlineUnsignedInt_<(a: Int, b: Int): Boolean =
      (a ^ 0x80000000) < (b ^ 0x80000000)

    @inline
    def inlineUnsignedInt_>(a: Int, b: Int): Boolean =
      (a ^ 0x80000000) > (b ^ 0x80000000)

    @inline
    def inlineUnsignedInt_>=(a: Int, b: Int): Boolean =
      (a ^ 0x80000000) >= (b ^ 0x80000000)

    @inline
    def inline_lo_unary_-(lo: Int): Int =
      -lo

    @inline
    def inline_hi_unary_-(lo: Int, hi: Int): Int =
      if (lo != 0) ~hi else -hi

    @inline
    def inline_abs(lo: Int, hi: Int): (Boolean, RuntimeLong) = {
      val neg = hi < 0
      val abs =
        if (neg) new RuntimeLong(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi))
        else new RuntimeLong(lo, hi)
      (neg, abs)
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy