From f2c01a9d9b404138f9eb94716415394a590d6ea5 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 3 May 2018 21:37:48 +0300 Subject: [PATCH] Generate ranges, progressions and progressioniterators for UInt and ULong --- generators/builtins/unsignedTypes.kt | 136 +++++++++++++++++- libraries/stdlib/unsigned/src/kotlin/UByte.kt | 3 + libraries/stdlib/unsigned/src/kotlin/UInt.kt | 3 + .../stdlib/unsigned/src/kotlin/UIntRange.kt | 117 +++++++++++++++ libraries/stdlib/unsigned/src/kotlin/ULong.kt | 3 + .../stdlib/unsigned/src/kotlin/ULongRange.kt | 117 +++++++++++++++ .../unsigned/src/kotlin/UProgressionUtil.kt | 84 +++++++++++ .../stdlib/unsigned/src/kotlin/UShort.kt | 3 + 8 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 libraries/stdlib/unsigned/src/kotlin/UIntRange.kt create mode 100644 libraries/stdlib/unsigned/src/kotlin/ULongRange.kt create mode 100644 libraries/stdlib/unsigned/src/kotlin/UProgressionUtil.kt diff --git a/generators/builtins/unsignedTypes.kt b/generators/builtins/unsignedTypes.kt index b5cbbd57009..d1fe7e6ee39 100644 --- a/generators/builtins/unsignedTypes.kt +++ b/generators/builtins/unsignedTypes.kt @@ -22,6 +22,10 @@ fun generateUnsignedTypes( generate(File(targetDir, "kotlin/${type.capitalized}Array.kt")) { UnsignedArrayGenerator(type, it) } } + for (type in listOf(UnsignedType.UINT, UnsignedType.ULONG)) { + generate(File(targetDir, "kotlin/${type.capitalized}Range.kt")) { UnsignedRangeGenerator(type, it) } + } + generate(File(targetDir, "kotlin/UIterators.kt"), ::UnsignedIteratorsGenerator) @@ -116,7 +120,12 @@ class UnsignedTypeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltIns } private fun generateRangeTo() { - // TODO("not implemented") + val rangeElementType = maxByDomainCapacity(type, UnsignedType.UINT) + val rangeType = rangeElementType.capitalized + "Range" + fun convert(name: String) = if (rangeElementType == type) name else "$name.to${rangeElementType.capitalized}()" + out.println(" /** Creates a range from this value to the specified [other] value. */") + out.println(" public operator fun rangeTo(other: $className): $rangeType = $rangeType(${convert("this")}, ${convert("other")})") + out.println() } @@ -245,3 +254,128 @@ public fun $arrayTypeOf(vararg elements: $elementType): $arrayType { ) } } + +class UnsignedRangeGenerator(val type: UnsignedType, out: PrintWriter) : BuiltInsSourceGenerator(out) { + val elementType = type.capitalized + val signedType = type.asSigned.capitalized + val stepType = signedType + + override fun getPackage(): String = "kotlin.ranges" + + override fun generateBody() { + fun hashCodeConversion(name: String, isSigned: Boolean = false) = + if (type == UnsignedType.ULONG) "($name xor ($name ${if (isSigned) "u" else ""}shr 32))" else name + + out.println( + """ + +import kotlin.internal.* + +/** + * A range of values of type `$elementType`. + */ +public class ${elementType}Range(start: $elementType, endInclusive: $elementType) : ${elementType}Progression(start, endInclusive, 1), ClosedRange<${elementType}> { + override val start: $elementType get() = first + override val endInclusive: $elementType get() = last + + override fun contains(value: $elementType): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is ${elementType}Range && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * ${hashCodeConversion("first")}.toInt() + ${hashCodeConversion("last")}.toInt()) + + override fun toString(): String = "${'$'}first..${'$'}last" + + companion object { + /** An empty range of values of type $elementType. */ + public val EMPTY: ${elementType}Range = ${elementType}Range($elementType.MAX_VALUE, $elementType.MIN_VALUE) + } +} + +/** + * A progression of values of type `$elementType`. + */ +public open class ${elementType}Progression +internal constructor( + start: $elementType, + endInclusive: $elementType, + step: $stepType +) : Iterable<$elementType> { + init { + if (step == 0.to$stepType()) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: $elementType = start + + /** + * The last element in the progression. + */ + public val last: $elementType = getProgressionLastElement(start, endInclusive, step) + + /** + * The step of the progression. + */ + public val step: $stepType = step + + override fun iterator(): ${elementType}Iterator = ${elementType}ProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is ${elementType}Progression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * ${hashCodeConversion("first")}.toInt() + ${hashCodeConversion("last")}.toInt()) + ${hashCodeConversion("step", isSigned = true)}.toInt()) + + override fun toString(): String = if (step > 0) "${'$'}first..${'$'}last step ${'$'}step" else "${'$'}first downTo ${'$'}last step ${'$'}{-step}" + + companion object { + /** + * Creates ${elementType}Progression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: $elementType, rangeEnd: $elementType, step: $stepType): ${elementType}Progression = ${elementType}Progression(rangeStart, rangeEnd, step) + } +} + + +/** + * An iterator over a progression of values of type `$elementType`. + * @property step the number by which the value is incremented on each step. + */ +private class ${elementType}ProgressionIterator(first: $elementType, last: $elementType, step: $stepType) : ${elementType}Iterator() { + private val finalElement = last + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + private val step = step.to$elementType() // use 2-complement math for negative steps + private var next = if (hasNext) first else finalElement + + override fun hasNext(): Boolean = hasNext + + override fun next$elementType(): $elementType { + val value = next + if (value == finalElement) { + if (!hasNext) throw kotlin.NoSuchElementException() + hasNext = false + } else { + next += step + } + return value + } +} +""" + ) + } + +} diff --git a/libraries/stdlib/unsigned/src/kotlin/UByte.kt b/libraries/stdlib/unsigned/src/kotlin/UByte.kt index eddb12ccd75..2c2debaf303 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UByte.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UByte.kt @@ -101,6 +101,9 @@ public inline class UByte internal constructor(private val data: Byte) : Compara /** Decrements this value. */ public operator fun dec(): UByte = TODO() + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: UByte): UIntRange = UIntRange(this.toUInt(), other.toUInt()) + /** Performs a bitwise AND operation between the two values. */ public infix fun and(other: UByte): UByte = UByte(this.data and other.data) /** Performs a bitwise OR operation between the two values. */ diff --git a/libraries/stdlib/unsigned/src/kotlin/UInt.kt b/libraries/stdlib/unsigned/src/kotlin/UInt.kt index ef11e5c55ca..bb099268e2c 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UInt.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UInt.kt @@ -101,6 +101,9 @@ public inline class UInt internal constructor(private val data: Int) : Comparabl /** Decrements this value. */ public operator fun dec(): UInt = TODO() + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: UInt): UIntRange = UIntRange(this, other) + /** Shifts this value left by the [bitCount] number of bits. */ public infix fun shl(bitCount: Int): UInt = UInt(data shl bitCount) /** Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. */ diff --git a/libraries/stdlib/unsigned/src/kotlin/UIntRange.kt b/libraries/stdlib/unsigned/src/kotlin/UIntRange.kt new file mode 100644 index 00000000000..a2c81cd36e0 --- /dev/null +++ b/libraries/stdlib/unsigned/src/kotlin/UIntRange.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +// Auto-generated file. DO NOT EDIT! + +package kotlin.ranges + + + +import kotlin.internal.* + +/** + * A range of values of type `UInt`. + */ +public class UIntRange(start: UInt, endInclusive: UInt) : UIntProgression(start, endInclusive, 1), ClosedRange { + override val start: UInt get() = first + override val endInclusive: UInt get() = last + + override fun contains(value: UInt): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is UIntRange && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * first.toInt() + last.toInt()) + + override fun toString(): String = "$first..$last" + + companion object { + /** An empty range of values of type UInt. */ + public val EMPTY: UIntRange = UIntRange(UInt.MAX_VALUE, UInt.MIN_VALUE) + } +} + +/** + * A progression of values of type `UInt`. + */ +public open class UIntProgression +internal constructor( + start: UInt, + endInclusive: UInt, + step: Int +) : Iterable { + init { + if (step == 0.toInt()) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: UInt = start + + /** + * The last element in the progression. + */ + public val last: UInt = getProgressionLastElement(start, endInclusive, step) + + /** + * The step of the progression. + */ + public val step: Int = step + + override fun iterator(): UIntIterator = UIntProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is UIntProgression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * first.toInt() + last.toInt()) + step.toInt()) + + override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}" + + companion object { + /** + * Creates UIntProgression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: UInt, rangeEnd: UInt, step: Int): UIntProgression = UIntProgression(rangeStart, rangeEnd, step) + } +} + + +/** + * An iterator over a progression of values of type `UInt`. + * @property step the number by which the value is incremented on each step. + */ +private class UIntProgressionIterator(first: UInt, last: UInt, step: Int) : UIntIterator() { + private val finalElement = last + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + private val step = step.toUInt() // use 2-complement math for negative steps + private var next = if (hasNext) first else finalElement + + override fun hasNext(): Boolean = hasNext + + override fun nextUInt(): UInt { + val value = next + if (value == finalElement) { + if (!hasNext) throw kotlin.NoSuchElementException() + hasNext = false + } else { + next += step + } + return value + } +} + diff --git a/libraries/stdlib/unsigned/src/kotlin/ULong.kt b/libraries/stdlib/unsigned/src/kotlin/ULong.kt index aeb8d472bdc..122c5cac3f6 100644 --- a/libraries/stdlib/unsigned/src/kotlin/ULong.kt +++ b/libraries/stdlib/unsigned/src/kotlin/ULong.kt @@ -101,6 +101,9 @@ public inline class ULong internal constructor(private val data: Long) : Compara /** Decrements this value. */ public operator fun dec(): ULong = TODO() + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: ULong): ULongRange = ULongRange(this, other) + /** Shifts this value left by the [bitCount] number of bits. */ public infix fun shl(bitCount: Int): ULong = ULong(data shl bitCount) /** Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. */ diff --git a/libraries/stdlib/unsigned/src/kotlin/ULongRange.kt b/libraries/stdlib/unsigned/src/kotlin/ULongRange.kt new file mode 100644 index 00000000000..d727edc3b8a --- /dev/null +++ b/libraries/stdlib/unsigned/src/kotlin/ULongRange.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +// Auto-generated file. DO NOT EDIT! + +package kotlin.ranges + + + +import kotlin.internal.* + +/** + * A range of values of type `ULong`. + */ +public class ULongRange(start: ULong, endInclusive: ULong) : ULongProgression(start, endInclusive, 1), ClosedRange { + override val start: ULong get() = first + override val endInclusive: ULong get() = last + + override fun contains(value: ULong): Boolean = first <= value && value <= last + + override fun isEmpty(): Boolean = first > last + + override fun equals(other: Any?): Boolean = + other is ULongRange && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (first xor (first shr 32)).toInt() + (last xor (last shr 32)).toInt()) + + override fun toString(): String = "$first..$last" + + companion object { + /** An empty range of values of type ULong. */ + public val EMPTY: ULongRange = ULongRange(ULong.MAX_VALUE, ULong.MIN_VALUE) + } +} + +/** + * A progression of values of type `ULong`. + */ +public open class ULongProgression +internal constructor( + start: ULong, + endInclusive: ULong, + step: Long +) : Iterable { + init { + if (step == 0.toLong()) throw kotlin.IllegalArgumentException("Step must be non-zero") + } + + /** + * The first element in the progression. + */ + public val first: ULong = start + + /** + * The last element in the progression. + */ + public val last: ULong = getProgressionLastElement(start, endInclusive, step) + + /** + * The step of the progression. + */ + public val step: Long = step + + override fun iterator(): ULongIterator = ULongProgressionIterator(first, last, step) + + /** Checks if the progression is empty. */ + public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last + + override fun equals(other: Any?): Boolean = + other is ULongProgression && (isEmpty() && other.isEmpty() || + first == other.first && last == other.last && step == other.step) + + override fun hashCode(): Int = + if (isEmpty()) -1 else (31 * (31 * (first xor (first shr 32)).toInt() + (last xor (last shr 32)).toInt()) + (step xor (step ushr 32)).toInt()) + + override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}" + + companion object { + /** + * Creates ULongProgression within the specified bounds of a closed range. + + * The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step]. + * In order to go backwards the [step] must be negative. + */ + public fun fromClosedRange(rangeStart: ULong, rangeEnd: ULong, step: Long): ULongProgression = ULongProgression(rangeStart, rangeEnd, step) + } +} + + +/** + * An iterator over a progression of values of type `ULong`. + * @property step the number by which the value is incremented on each step. + */ +private class ULongProgressionIterator(first: ULong, last: ULong, step: Long) : ULongIterator() { + private val finalElement = last + private var hasNext: Boolean = if (step > 0) first <= last else first >= last + private val step = step.toULong() // use 2-complement math for negative steps + private var next = if (hasNext) first else finalElement + + override fun hasNext(): Boolean = hasNext + + override fun nextULong(): ULong { + val value = next + if (value == finalElement) { + if (!hasNext) throw kotlin.NoSuchElementException() + hasNext = false + } else { + next += step + } + return value + } +} + diff --git a/libraries/stdlib/unsigned/src/kotlin/UProgressionUtil.kt b/libraries/stdlib/unsigned/src/kotlin/UProgressionUtil.kt new file mode 100644 index 00000000000..5de50c35b08 --- /dev/null +++ b/libraries/stdlib/unsigned/src/kotlin/UProgressionUtil.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.internal + +import kotlin.jvm.JvmName + + +// a mod b (in arithmetical sense) + +@JvmName("umod") +private fun mod(a: UInt, b: Int): Int = (a % b.toUInt()).toInt() + +private fun mod(a: Int, b: Int): Int { + val mod = a % b + return if (mod >= 0) mod else mod + b +} + +@JvmName("umod") +private fun mod(a: ULong, b: Long): Long = (a % b.toULong()).toLong() + +private fun mod(a: Long, b: Long): Long { + val mod = a % b + return if (mod >= 0) mod else mod + b +} + + + +// (a - b) mod c +private fun differenceModulo(a: UInt, b: UInt, c: Int): UInt { + return mod(mod(a, c) - mod(b, c), c).toUInt() +} + +private fun differenceModulo(a: ULong, b: ULong, c: Long): ULong { + return mod(mod(a, c) - mod(b, c), c).toULong() +} + +/** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from [start] to [end] in case of a positive [step], or from [end] to [start] in case of a negative + * [step]. + * + * No validation on passed parameters is performed. The given parameters should satisfy the condition: + * + * - either `step > 0` and `start <= end`, + * - or `step < 0` and `start >= end`. + * + * @param start first element of the progression + * @param end ending bound for the progression + * @param step increment, or difference of successive elements in the progression + * @return the final element of the progression + * @suppress + */ +@PublishedApi +internal fun getProgressionLastElement(start: UInt, end: UInt, step: Int): UInt = when { + step > 0 -> if (start >= end) end else end - differenceModulo(end, start, step) + step < 0 -> if (start <= end) end else end + differenceModulo(start, end, -step) + else -> throw kotlin.IllegalArgumentException("Step is zero.") +} + +/** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from [start] to [end] in case of a positive [step], or from [end] to [start] in case of a negative + * [step]. + * + * No validation on passed parameters is performed. The given parameters should satisfy the condition: + * + * - either `step > 0` and `start <= end`, + * - or `step < 0` and `start >= end`. + * + * @param start first element of the progression + * @param end ending bound for the progression + * @param step increment, or difference of successive elements in the progression + * @return the final element of the progression + * @suppress + */ +@PublishedApi +internal fun getProgressionLastElement(start: ULong, end: ULong, step: Long): ULong = when { + step > 0 -> if (start >= end) end else end - differenceModulo(end, start, step) + step < 0 -> if (start <= end) end else end + differenceModulo(start, end, -step) + else -> throw kotlin.IllegalArgumentException("Step is zero.") +} diff --git a/libraries/stdlib/unsigned/src/kotlin/UShort.kt b/libraries/stdlib/unsigned/src/kotlin/UShort.kt index 4c19cf0188c..167e30f02f5 100644 --- a/libraries/stdlib/unsigned/src/kotlin/UShort.kt +++ b/libraries/stdlib/unsigned/src/kotlin/UShort.kt @@ -101,6 +101,9 @@ public inline class UShort internal constructor(private val data: Short) : Compa /** Decrements this value. */ public operator fun dec(): UShort = TODO() + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: UShort): UIntRange = UIntRange(this.toUInt(), other.toUInt()) + /** Performs a bitwise AND operation between the two values. */ public infix fun and(other: UShort): UShort = UShort(this.data and other.data) /** Performs a bitwise OR operation between the two values. */