From 5773594412660af11357ac9adc6ffcb45138b840 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 29 Nov 2016 20:39:54 +0300 Subject: [PATCH] Reimplement `ClosedRange.contains` extensions to call interface `contains` method. --- libraries/stdlib/src/generated/_Ranges.kt | 116 +++++++++++++----- libraries/stdlib/test/ranges/RangeTest.kt | 13 ++ .../kotlin-stdlib-gen/src/templates/Ranges.kt | 21 +++- 3 files changed, 119 insertions(+), 31 deletions(-) diff --git a/libraries/stdlib/src/generated/_Ranges.kt b/libraries/stdlib/src/generated/_Ranges.kt index 9f856f13760..e9c1731290b 100644 --- a/libraries/stdlib/src/generated/_Ranges.kt +++ b/libraries/stdlib/src/generated/_Ranges.kt @@ -15,7 +15,7 @@ import kotlin.comparisons.* */ @kotlin.jvm.JvmName("intRangeContains") public operator fun ClosedRange.contains(value: Byte): Boolean { - return start <= value && value <= endInclusive + return contains(value.toInt()) } /** @@ -23,7 +23,7 @@ public operator fun ClosedRange.contains(value: Byte): Boolean { */ @kotlin.jvm.JvmName("longRangeContains") public operator fun ClosedRange.contains(value: Byte): Boolean { - return start <= value && value <= endInclusive + return contains(value.toLong()) } /** @@ -31,7 +31,7 @@ public operator fun ClosedRange.contains(value: Byte): Boolean { */ @kotlin.jvm.JvmName("shortRangeContains") public operator fun ClosedRange.contains(value: Byte): Boolean { - return start <= value && value <= endInclusive + return contains(value.toShort()) } /** @@ -39,7 +39,7 @@ public operator fun ClosedRange.contains(value: Byte): Boolean { */ @kotlin.jvm.JvmName("doubleRangeContains") public operator fun ClosedRange.contains(value: Byte): Boolean { - return start <= value && value <= endInclusive + return contains(value.toDouble()) } /** @@ -47,7 +47,7 @@ public operator fun ClosedRange.contains(value: Byte): Boolean { */ @kotlin.jvm.JvmName("floatRangeContains") public operator fun ClosedRange.contains(value: Byte): Boolean { - return start <= value && value <= endInclusive + return contains(value.toFloat()) } /** @@ -55,7 +55,7 @@ public operator fun ClosedRange.contains(value: Byte): Boolean { */ @kotlin.jvm.JvmName("intRangeContains") public operator fun ClosedRange.contains(value: Double): Boolean { - return start <= value && value <= endInclusive + return value.toIntExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -63,7 +63,7 @@ public operator fun ClosedRange.contains(value: Double): Boolean { */ @kotlin.jvm.JvmName("longRangeContains") public operator fun ClosedRange.contains(value: Double): Boolean { - return start <= value && value <= endInclusive + return value.toLongExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -71,7 +71,7 @@ public operator fun ClosedRange.contains(value: Double): Boolean { */ @kotlin.jvm.JvmName("byteRangeContains") public operator fun ClosedRange.contains(value: Double): Boolean { - return start <= value && value <= endInclusive + return value.toByteExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -79,7 +79,7 @@ public operator fun ClosedRange.contains(value: Double): Boolean { */ @kotlin.jvm.JvmName("shortRangeContains") public operator fun ClosedRange.contains(value: Double): Boolean { - return start <= value && value <= endInclusive + return value.toShortExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -87,7 +87,7 @@ public operator fun ClosedRange.contains(value: Double): Boolean { */ @kotlin.jvm.JvmName("floatRangeContains") public operator fun ClosedRange.contains(value: Double): Boolean { - return start <= value && value <= endInclusive + return contains(value.toFloat()) } /** @@ -95,7 +95,7 @@ public operator fun ClosedRange.contains(value: Double): Boolean { */ @kotlin.jvm.JvmName("intRangeContains") public operator fun ClosedRange.contains(value: Float): Boolean { - return start <= value && value <= endInclusive + return value.toIntExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -103,7 +103,7 @@ public operator fun ClosedRange.contains(value: Float): Boolean { */ @kotlin.jvm.JvmName("longRangeContains") public operator fun ClosedRange.contains(value: Float): Boolean { - return start <= value && value <= endInclusive + return value.toLongExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -111,7 +111,7 @@ public operator fun ClosedRange.contains(value: Float): Boolean { */ @kotlin.jvm.JvmName("byteRangeContains") public operator fun ClosedRange.contains(value: Float): Boolean { - return start <= value && value <= endInclusive + return value.toByteExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -119,7 +119,7 @@ public operator fun ClosedRange.contains(value: Float): Boolean { */ @kotlin.jvm.JvmName("shortRangeContains") public operator fun ClosedRange.contains(value: Float): Boolean { - return start <= value && value <= endInclusive + return value.toShortExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -127,7 +127,7 @@ public operator fun ClosedRange.contains(value: Float): Boolean { */ @kotlin.jvm.JvmName("doubleRangeContains") public operator fun ClosedRange.contains(value: Float): Boolean { - return start <= value && value <= endInclusive + return contains(value.toDouble()) } /** @@ -135,7 +135,7 @@ public operator fun ClosedRange.contains(value: Float): Boolean { */ @kotlin.jvm.JvmName("longRangeContains") public operator fun ClosedRange.contains(value: Int): Boolean { - return start <= value && value <= endInclusive + return contains(value.toLong()) } /** @@ -143,7 +143,7 @@ public operator fun ClosedRange.contains(value: Int): Boolean { */ @kotlin.jvm.JvmName("byteRangeContains") public operator fun ClosedRange.contains(value: Int): Boolean { - return start <= value && value <= endInclusive + return value.toByteExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -151,7 +151,7 @@ public operator fun ClosedRange.contains(value: Int): Boolean { */ @kotlin.jvm.JvmName("shortRangeContains") public operator fun ClosedRange.contains(value: Int): Boolean { - return start <= value && value <= endInclusive + return value.toShortExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -159,7 +159,7 @@ public operator fun ClosedRange.contains(value: Int): Boolean { */ @kotlin.jvm.JvmName("doubleRangeContains") public operator fun ClosedRange.contains(value: Int): Boolean { - return start <= value && value <= endInclusive + return contains(value.toDouble()) } /** @@ -167,7 +167,7 @@ public operator fun ClosedRange.contains(value: Int): Boolean { */ @kotlin.jvm.JvmName("floatRangeContains") public operator fun ClosedRange.contains(value: Int): Boolean { - return start <= value && value <= endInclusive + return contains(value.toFloat()) } /** @@ -175,7 +175,7 @@ public operator fun ClosedRange.contains(value: Int): Boolean { */ @kotlin.jvm.JvmName("intRangeContains") public operator fun ClosedRange.contains(value: Long): Boolean { - return start <= value && value <= endInclusive + return value.toIntExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -183,7 +183,7 @@ public operator fun ClosedRange.contains(value: Long): Boolean { */ @kotlin.jvm.JvmName("byteRangeContains") public operator fun ClosedRange.contains(value: Long): Boolean { - return start <= value && value <= endInclusive + return value.toByteExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -191,7 +191,7 @@ public operator fun ClosedRange.contains(value: Long): Boolean { */ @kotlin.jvm.JvmName("shortRangeContains") public operator fun ClosedRange.contains(value: Long): Boolean { - return start <= value && value <= endInclusive + return value.toShortExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -199,7 +199,7 @@ public operator fun ClosedRange.contains(value: Long): Boolean { */ @kotlin.jvm.JvmName("doubleRangeContains") public operator fun ClosedRange.contains(value: Long): Boolean { - return start <= value && value <= endInclusive + return contains(value.toDouble()) } /** @@ -207,7 +207,7 @@ public operator fun ClosedRange.contains(value: Long): Boolean { */ @kotlin.jvm.JvmName("floatRangeContains") public operator fun ClosedRange.contains(value: Long): Boolean { - return start <= value && value <= endInclusive + return contains(value.toFloat()) } /** @@ -215,7 +215,7 @@ public operator fun ClosedRange.contains(value: Long): Boolean { */ @kotlin.jvm.JvmName("intRangeContains") public operator fun ClosedRange.contains(value: Short): Boolean { - return start <= value && value <= endInclusive + return contains(value.toInt()) } /** @@ -223,7 +223,7 @@ public operator fun ClosedRange.contains(value: Short): Boolean { */ @kotlin.jvm.JvmName("longRangeContains") public operator fun ClosedRange.contains(value: Short): Boolean { - return start <= value && value <= endInclusive + return contains(value.toLong()) } /** @@ -231,7 +231,7 @@ public operator fun ClosedRange.contains(value: Short): Boolean { */ @kotlin.jvm.JvmName("byteRangeContains") public operator fun ClosedRange.contains(value: Short): Boolean { - return start <= value && value <= endInclusive + return value.toByteExactOrNull().let { if (it != null) contains(it) else false } } /** @@ -239,7 +239,7 @@ public operator fun ClosedRange.contains(value: Short): Boolean { */ @kotlin.jvm.JvmName("doubleRangeContains") public operator fun ClosedRange.contains(value: Short): Boolean { - return start <= value && value <= endInclusive + return contains(value.toDouble()) } /** @@ -247,7 +247,7 @@ public operator fun ClosedRange.contains(value: Short): Boolean { */ @kotlin.jvm.JvmName("floatRangeContains") public operator fun ClosedRange.contains(value: Short): Boolean { - return start <= value && value <= endInclusive + return contains(value.toFloat()) } /** @@ -448,6 +448,62 @@ public infix fun CharProgression.step(step: Int): CharProgression { return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step) } +internal fun Int.toByteExactOrNull(): Byte? { + return if (this in Byte.MIN_VALUE.toInt()..Byte.MAX_VALUE.toInt()) this.toByte() else null +} + +internal fun Long.toByteExactOrNull(): Byte? { + return if (this in Byte.MIN_VALUE.toLong()..Byte.MAX_VALUE.toLong()) this.toByte() else null +} + +internal fun Short.toByteExactOrNull(): Byte? { + return if (this in Byte.MIN_VALUE.toShort()..Byte.MAX_VALUE.toShort()) this.toByte() else null +} + +internal fun Double.toByteExactOrNull(): Byte? { + return if (this in Byte.MIN_VALUE.toDouble()..Byte.MAX_VALUE.toDouble()) this.toByte() else null +} + +internal fun Float.toByteExactOrNull(): Byte? { + return if (this in Byte.MIN_VALUE.toFloat()..Byte.MAX_VALUE.toFloat()) this.toByte() else null +} + +internal fun Long.toIntExactOrNull(): Int? { + return if (this in Int.MIN_VALUE.toLong()..Int.MAX_VALUE.toLong()) this.toInt() else null +} + +internal fun Double.toIntExactOrNull(): Int? { + return if (this in Int.MIN_VALUE.toDouble()..Int.MAX_VALUE.toDouble()) this.toInt() else null +} + +internal fun Float.toIntExactOrNull(): Int? { + return if (this in Int.MIN_VALUE.toFloat()..Int.MAX_VALUE.toFloat()) this.toInt() else null +} + +internal fun Double.toLongExactOrNull(): Long? { + return if (this in Long.MIN_VALUE.toDouble()..Long.MAX_VALUE.toDouble()) this.toLong() else null +} + +internal fun Float.toLongExactOrNull(): Long? { + return if (this in Long.MIN_VALUE.toFloat()..Long.MAX_VALUE.toFloat()) this.toLong() else null +} + +internal fun Int.toShortExactOrNull(): Short? { + return if (this in Short.MIN_VALUE.toInt()..Short.MAX_VALUE.toInt()) this.toShort() else null +} + +internal fun Long.toShortExactOrNull(): Short? { + return if (this in Short.MIN_VALUE.toLong()..Short.MAX_VALUE.toLong()) this.toShort() else null +} + +internal fun Double.toShortExactOrNull(): Short? { + return if (this in Short.MIN_VALUE.toDouble()..Short.MAX_VALUE.toDouble()) this.toShort() else null +} + +internal fun Float.toShortExactOrNull(): Short? { + return if (this in Short.MIN_VALUE.toFloat()..Short.MAX_VALUE.toFloat()) this.toShort() else null +} + /** * Returns a range from this value up to but excluding the specified [to] value. */ diff --git a/libraries/stdlib/test/ranges/RangeTest.kt b/libraries/stdlib/test/ranges/RangeTest.kt index 8e4612f7a0b..fdb2dcd4b78 100644 --- a/libraries/stdlib/test/ranges/RangeTest.kt +++ b/libraries/stdlib/test/ranges/RangeTest.kt @@ -206,6 +206,12 @@ public class RangeTest { val nanRange = 0.0..Double.NaN assertFalse(Double.NaN in nanRange) assertTrue(nanRange.isEmpty()) + + val halfInfRange = 0.0..Double.POSITIVE_INFINITY + assertTrue(Double.POSITIVE_INFINITY in halfInfRange) + assertFalse(Double.NEGATIVE_INFINITY in halfInfRange) + assertFalse(Double.NaN in halfInfRange) + assertTrue(Float.POSITIVE_INFINITY in halfInfRange) } @Test fun floatRange() { @@ -244,6 +250,13 @@ public class RangeTest { val nanRange = 0.0F..Float.NaN assertFalse(Float.NaN in nanRange) assertTrue(nanRange.isEmpty()) + + val halfInfRange = 0.0F..Float.POSITIVE_INFINITY + assertTrue(Float.POSITIVE_INFINITY in halfInfRange) + assertFalse(Float.NEGATIVE_INFINITY in halfInfRange) + assertFalse(Float.NaN in halfInfRange) + assertTrue(Double.POSITIVE_INFINITY in halfInfRange) + assertTrue(Double.MAX_VALUE in halfInfRange) } @Test fun isEmpty() { diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt index 96dbff0a344..482a807aba5 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt @@ -127,12 +127,31 @@ fun ranges(): List { platformName("${rangeType.name.decapitalize()}RangeContains") returns("Boolean") doc { "Checks if the specified [value] belongs to this range." } - body { "return start <= value && value <= endInclusive" } + body { + if (rangeType.capacity > itemType.capacity || !rangeType.isIntegral()) + "return contains(value.to$rangeType())" + else + "return value.to${rangeType}ExactOrNull().let { if (it != null) contains(it) else false }" + } } templates addAll numericPermutations.filter { it.first != it.second }.map { contains(it.first, it.second) } + fun narrowingExactOrNull(fromType: PrimitiveType, toType: PrimitiveType) = f("to${toType}ExactOrNull()") { + visibility("internal") + sourceFile(SourceFile.Ranges) + check(toType.isIntegral()) + + returns("$toType?") + only(Primitives) + only(fromType) + body { + "return if (this in $toType.MIN_VALUE.to$fromType()..$toType.MAX_VALUE.to$fromType()) this.to$toType() else null" + } + } + + templates addAll numericPermutations.filter { it.first.capacity > it.second.capacity && it.second.isIntegral() }.map { narrowingExactOrNull(it.first, it.second) } return templates } \ No newline at end of file