From e988ea5a1cdc63895a78729fc7b125526487b3b7 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 4 May 2018 23:52:37 +0300 Subject: [PATCH] Implement unsigned arithmetic operations including ulong division and remainder --- generators/builtins/unsignedTypes.kt | 17 +++++-- libraries/stdlib/unsigned/src/kotlin/UByte.kt | 44 ++++++++--------- libraries/stdlib/unsigned/src/kotlin/UInt.kt | 44 ++++++++--------- libraries/stdlib/unsigned/src/kotlin/ULong.kt | 44 ++++++++--------- .../stdlib/unsigned/src/kotlin/UShort.kt | 44 ++++++++--------- .../unsigned/src/kotlin/UnsignedUtils.kt | 47 ++++++++++++++++++- 6 files changed, 148 insertions(+), 92 deletions(-) diff --git a/generators/builtins/unsignedTypes.kt b/generators/builtins/unsignedTypes.kt index 43342dec271..42f3a4ead02 100644 --- a/generators/builtins/unsignedTypes.kt +++ b/generators/builtins/unsignedTypes.kt @@ -111,7 +111,17 @@ class UnsignedTypeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltIns val returnType = getOperatorReturnType(type, otherType) out.println(" /** $doc */") - out.println(" public operator fun $name(other: ${otherType.capitalized}): ${returnType.capitalized} = TODO()") + out.print(" public operator fun $name(other: ${otherType.capitalized}): ${returnType.capitalized} = ") + if (type == otherType && type == returnType) { + when (name) { + "plus", "minus", "times" -> out.println("$className(this.data.$name(other.data))") + "div" -> out.println("${type.capitalized.toLowerCase()}Divide(this, other)") + "rem" -> out.println("${type.capitalized.toLowerCase()}Remainder(this, other)") + else -> error(name) + } + } else { + out.println("${convert("this", type, returnType)}.$name(${convert("other", otherType, returnType)})") + } } out.println() } @@ -119,9 +129,10 @@ class UnsignedTypeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltIns private fun generateUnaryOperators() { for ((name, doc) in GeneratePrimitives.unaryOperators) { - if (name in listOf("unaryPlus", "unaryMinus")) continue // TODO: Decide if unaryPlus and unaryMinus are needed + if (name in listOf("unaryPlus", "unaryMinus")) continue out.println(" /** $doc */") - out.println(" public operator fun $name(): $className = TODO()") + out.println(" public operator fun $name(): $className = $className(data.$name())") + } out.println() } diff --git a/libraries/stdlib/unsigned/src/kotlin/UByte.kt b/libraries/stdlib/unsigned/src/kotlin/UByte.kt index 31a0797a16c..237d4f1deef 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UByte.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UByte.kt @@ -52,54 +52,54 @@ public inline class UByte internal constructor(private val data: Byte) : Compara public operator fun compareTo(other: ULong): Int = this.toULong().compareTo(other) /** Adds the other value to this value. */ - public operator fun plus(other: UByte): UInt = TODO() + public operator fun plus(other: UByte): UInt = this.toUInt().plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UShort): UInt = TODO() + public operator fun plus(other: UShort): UInt = this.toUInt().plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UInt): UInt = TODO() + public operator fun plus(other: UInt): UInt = this.toUInt().plus(other) /** Adds the other value to this value. */ - public operator fun plus(other: ULong): ULong = TODO() + public operator fun plus(other: ULong): ULong = this.toULong().plus(other) /** Subtracts the other value from this value. */ - public operator fun minus(other: UByte): UInt = TODO() + public operator fun minus(other: UByte): UInt = this.toUInt().minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UShort): UInt = TODO() + public operator fun minus(other: UShort): UInt = this.toUInt().minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UInt): UInt = TODO() + public operator fun minus(other: UInt): UInt = this.toUInt().minus(other) /** Subtracts the other value from this value. */ - public operator fun minus(other: ULong): ULong = TODO() + public operator fun minus(other: ULong): ULong = this.toULong().minus(other) /** Multiplies this value by the other value. */ - public operator fun times(other: UByte): UInt = TODO() + public operator fun times(other: UByte): UInt = this.toUInt().times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UShort): UInt = TODO() + public operator fun times(other: UShort): UInt = this.toUInt().times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UInt): UInt = TODO() + public operator fun times(other: UInt): UInt = this.toUInt().times(other) /** Multiplies this value by the other value. */ - public operator fun times(other: ULong): ULong = TODO() + public operator fun times(other: ULong): ULong = this.toULong().times(other) /** Divides this value by the other value. */ - public operator fun div(other: UByte): UInt = TODO() + public operator fun div(other: UByte): UInt = this.toUInt().div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UShort): UInt = TODO() + public operator fun div(other: UShort): UInt = this.toUInt().div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UInt): UInt = TODO() + public operator fun div(other: UInt): UInt = this.toUInt().div(other) /** Divides this value by the other value. */ - public operator fun div(other: ULong): ULong = TODO() + public operator fun div(other: ULong): ULong = this.toULong().div(other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UByte): UInt = TODO() + public operator fun rem(other: UByte): UInt = this.toUInt().rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UShort): UInt = TODO() + public operator fun rem(other: UShort): UInt = this.toUInt().rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UInt): UInt = TODO() + public operator fun rem(other: UInt): UInt = this.toUInt().rem(other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: ULong): ULong = TODO() + public operator fun rem(other: ULong): ULong = this.toULong().rem(other) /** Increments this value. */ - public operator fun inc(): UByte = TODO() + public operator fun inc(): UByte = UByte(data.inc()) /** Decrements this value. */ - public operator fun dec(): UByte = TODO() + public operator fun dec(): UByte = UByte(data.dec()) /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: UByte): UIntRange = UIntRange(this.toUInt(), other.toUInt()) diff --git a/libraries/stdlib/unsigned/src/kotlin/UInt.kt b/libraries/stdlib/unsigned/src/kotlin/UInt.kt index 3cb5e5d1cfa..0cbf400b3c9 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UInt.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UInt.kt @@ -52,54 +52,54 @@ public inline class UInt internal constructor(private val data: Int) : Comparabl public operator fun compareTo(other: ULong): Int = this.toULong().compareTo(other) /** Adds the other value to this value. */ - public operator fun plus(other: UByte): UInt = TODO() + public operator fun plus(other: UByte): UInt = this.plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UShort): UInt = TODO() + public operator fun plus(other: UShort): UInt = this.plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UInt): UInt = TODO() + public operator fun plus(other: UInt): UInt = UInt(this.data.plus(other.data)) /** Adds the other value to this value. */ - public operator fun plus(other: ULong): ULong = TODO() + public operator fun plus(other: ULong): ULong = this.toULong().plus(other) /** Subtracts the other value from this value. */ - public operator fun minus(other: UByte): UInt = TODO() + public operator fun minus(other: UByte): UInt = this.minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UShort): UInt = TODO() + public operator fun minus(other: UShort): UInt = this.minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UInt): UInt = TODO() + public operator fun minus(other: UInt): UInt = UInt(this.data.minus(other.data)) /** Subtracts the other value from this value. */ - public operator fun minus(other: ULong): ULong = TODO() + public operator fun minus(other: ULong): ULong = this.toULong().minus(other) /** Multiplies this value by the other value. */ - public operator fun times(other: UByte): UInt = TODO() + public operator fun times(other: UByte): UInt = this.times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UShort): UInt = TODO() + public operator fun times(other: UShort): UInt = this.times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UInt): UInt = TODO() + public operator fun times(other: UInt): UInt = UInt(this.data.times(other.data)) /** Multiplies this value by the other value. */ - public operator fun times(other: ULong): ULong = TODO() + public operator fun times(other: ULong): ULong = this.toULong().times(other) /** Divides this value by the other value. */ - public operator fun div(other: UByte): UInt = TODO() + public operator fun div(other: UByte): UInt = this.div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UShort): UInt = TODO() + public operator fun div(other: UShort): UInt = this.div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UInt): UInt = TODO() + public operator fun div(other: UInt): UInt = uintDivide(this, other) /** Divides this value by the other value. */ - public operator fun div(other: ULong): ULong = TODO() + public operator fun div(other: ULong): ULong = this.toULong().div(other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UByte): UInt = TODO() + public operator fun rem(other: UByte): UInt = this.rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UShort): UInt = TODO() + public operator fun rem(other: UShort): UInt = this.rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UInt): UInt = TODO() + public operator fun rem(other: UInt): UInt = uintRemainder(this, other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: ULong): ULong = TODO() + public operator fun rem(other: ULong): ULong = this.toULong().rem(other) /** Increments this value. */ - public operator fun inc(): UInt = TODO() + public operator fun inc(): UInt = UInt(data.inc()) /** Decrements this value. */ - public operator fun dec(): UInt = TODO() + public operator fun dec(): UInt = UInt(data.dec()) /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: UInt): UIntRange = UIntRange(this, other) diff --git a/libraries/stdlib/unsigned/src/kotlin/ULong.kt b/libraries/stdlib/unsigned/src/kotlin/ULong.kt index 81628fead6e..612b46a56bc 100644 --- a/libraries/stdlib/unsigned/src/kotlin/ULong.kt +++ b/libraries/stdlib/unsigned/src/kotlin/ULong.kt @@ -52,54 +52,54 @@ public inline class ULong internal constructor(private val data: Long) : Compara public override operator fun compareTo(other: ULong): Int = ulongCompare(this.data, other.data) /** Adds the other value to this value. */ - public operator fun plus(other: UByte): ULong = TODO() + public operator fun plus(other: UByte): ULong = this.plus(other.toULong()) /** Adds the other value to this value. */ - public operator fun plus(other: UShort): ULong = TODO() + public operator fun plus(other: UShort): ULong = this.plus(other.toULong()) /** Adds the other value to this value. */ - public operator fun plus(other: UInt): ULong = TODO() + public operator fun plus(other: UInt): ULong = this.plus(other.toULong()) /** Adds the other value to this value. */ - public operator fun plus(other: ULong): ULong = TODO() + public operator fun plus(other: ULong): ULong = ULong(this.data.plus(other.data)) /** Subtracts the other value from this value. */ - public operator fun minus(other: UByte): ULong = TODO() + public operator fun minus(other: UByte): ULong = this.minus(other.toULong()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UShort): ULong = TODO() + public operator fun minus(other: UShort): ULong = this.minus(other.toULong()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UInt): ULong = TODO() + public operator fun minus(other: UInt): ULong = this.minus(other.toULong()) /** Subtracts the other value from this value. */ - public operator fun minus(other: ULong): ULong = TODO() + public operator fun minus(other: ULong): ULong = ULong(this.data.minus(other.data)) /** Multiplies this value by the other value. */ - public operator fun times(other: UByte): ULong = TODO() + public operator fun times(other: UByte): ULong = this.times(other.toULong()) /** Multiplies this value by the other value. */ - public operator fun times(other: UShort): ULong = TODO() + public operator fun times(other: UShort): ULong = this.times(other.toULong()) /** Multiplies this value by the other value. */ - public operator fun times(other: UInt): ULong = TODO() + public operator fun times(other: UInt): ULong = this.times(other.toULong()) /** Multiplies this value by the other value. */ - public operator fun times(other: ULong): ULong = TODO() + public operator fun times(other: ULong): ULong = ULong(this.data.times(other.data)) /** Divides this value by the other value. */ - public operator fun div(other: UByte): ULong = TODO() + public operator fun div(other: UByte): ULong = this.div(other.toULong()) /** Divides this value by the other value. */ - public operator fun div(other: UShort): ULong = TODO() + public operator fun div(other: UShort): ULong = this.div(other.toULong()) /** Divides this value by the other value. */ - public operator fun div(other: UInt): ULong = TODO() + public operator fun div(other: UInt): ULong = this.div(other.toULong()) /** Divides this value by the other value. */ - public operator fun div(other: ULong): ULong = TODO() + public operator fun div(other: ULong): ULong = ulongDivide(this, other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UByte): ULong = TODO() + public operator fun rem(other: UByte): ULong = this.rem(other.toULong()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UShort): ULong = TODO() + public operator fun rem(other: UShort): ULong = this.rem(other.toULong()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UInt): ULong = TODO() + public operator fun rem(other: UInt): ULong = this.rem(other.toULong()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: ULong): ULong = TODO() + public operator fun rem(other: ULong): ULong = ulongRemainder(this, other) /** Increments this value. */ - public operator fun inc(): ULong = TODO() + public operator fun inc(): ULong = ULong(data.inc()) /** Decrements this value. */ - public operator fun dec(): ULong = TODO() + public operator fun dec(): ULong = ULong(data.dec()) /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: ULong): ULongRange = ULongRange(this, other) diff --git a/libraries/stdlib/unsigned/src/kotlin/UShort.kt b/libraries/stdlib/unsigned/src/kotlin/UShort.kt index e881611285c..59819cfd327 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UShort.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UShort.kt @@ -52,54 +52,54 @@ public inline class UShort internal constructor(private val data: Short) : Compa public operator fun compareTo(other: ULong): Int = this.toULong().compareTo(other) /** Adds the other value to this value. */ - public operator fun plus(other: UByte): UInt = TODO() + public operator fun plus(other: UByte): UInt = this.toUInt().plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UShort): UInt = TODO() + public operator fun plus(other: UShort): UInt = this.toUInt().plus(other.toUInt()) /** Adds the other value to this value. */ - public operator fun plus(other: UInt): UInt = TODO() + public operator fun plus(other: UInt): UInt = this.toUInt().plus(other) /** Adds the other value to this value. */ - public operator fun plus(other: ULong): ULong = TODO() + public operator fun plus(other: ULong): ULong = this.toULong().plus(other) /** Subtracts the other value from this value. */ - public operator fun minus(other: UByte): UInt = TODO() + public operator fun minus(other: UByte): UInt = this.toUInt().minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UShort): UInt = TODO() + public operator fun minus(other: UShort): UInt = this.toUInt().minus(other.toUInt()) /** Subtracts the other value from this value. */ - public operator fun minus(other: UInt): UInt = TODO() + public operator fun minus(other: UInt): UInt = this.toUInt().minus(other) /** Subtracts the other value from this value. */ - public operator fun minus(other: ULong): ULong = TODO() + public operator fun minus(other: ULong): ULong = this.toULong().minus(other) /** Multiplies this value by the other value. */ - public operator fun times(other: UByte): UInt = TODO() + public operator fun times(other: UByte): UInt = this.toUInt().times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UShort): UInt = TODO() + public operator fun times(other: UShort): UInt = this.toUInt().times(other.toUInt()) /** Multiplies this value by the other value. */ - public operator fun times(other: UInt): UInt = TODO() + public operator fun times(other: UInt): UInt = this.toUInt().times(other) /** Multiplies this value by the other value. */ - public operator fun times(other: ULong): ULong = TODO() + public operator fun times(other: ULong): ULong = this.toULong().times(other) /** Divides this value by the other value. */ - public operator fun div(other: UByte): UInt = TODO() + public operator fun div(other: UByte): UInt = this.toUInt().div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UShort): UInt = TODO() + public operator fun div(other: UShort): UInt = this.toUInt().div(other.toUInt()) /** Divides this value by the other value. */ - public operator fun div(other: UInt): UInt = TODO() + public operator fun div(other: UInt): UInt = this.toUInt().div(other) /** Divides this value by the other value. */ - public operator fun div(other: ULong): ULong = TODO() + public operator fun div(other: ULong): ULong = this.toULong().div(other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UByte): UInt = TODO() + public operator fun rem(other: UByte): UInt = this.toUInt().rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UShort): UInt = TODO() + public operator fun rem(other: UShort): UInt = this.toUInt().rem(other.toUInt()) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: UInt): UInt = TODO() + public operator fun rem(other: UInt): UInt = this.toUInt().rem(other) /** Calculates the remainder of dividing this value by the other value. */ - public operator fun rem(other: ULong): ULong = TODO() + public operator fun rem(other: ULong): ULong = this.toULong().rem(other) /** Increments this value. */ - public operator fun inc(): UShort = TODO() + public operator fun inc(): UShort = UShort(data.inc()) /** Decrements this value. */ - public operator fun dec(): UShort = TODO() + public operator fun dec(): UShort = UShort(data.dec()) /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: UShort): UIntRange = UIntRange(this.toUInt(), other.toUInt()) diff --git a/libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt b/libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt index 6d9743ea651..46a152016ce 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt @@ -6,4 +6,49 @@ package kotlin internal fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) -internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) \ No newline at end of file +internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) + +internal fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() +internal fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() + +// TODO: Add reference to Guava implementation source +internal fun ulongDivide(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) ULong(0) else ULong(1) + } + + // Optimization - use signed division if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend / divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(quotient + if (ULong(rem) >= ULong(divisor)) 1 else 0) + +} + +internal fun ulongRemainder(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) { + v1 // dividend < divisor + } else { + v1 - v2 // dividend >= divisor + } + } + + // Optimization - use signed modulus if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend % divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(rem - if (ULong(rem) >= ULong(divisor)) divisor else 0) +} \ No newline at end of file