diff --git a/js/js.libraries/src/core/ranges.kt b/js/js.libraries/src/core/ranges.kt new file mode 100644 index 00000000000..a73e137a5d8 --- /dev/null +++ b/js/js.libraries/src/core/ranges.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.ranges + +@SinceKotlin("1.1") +public inline operator fun Float.rangeTo(that: Float): ClosedRange = + this.toDouble().rangeTo(that.toDouble()) as ClosedRange diff --git a/libraries/stdlib/src/kotlin/ranges/Ranges.kt b/libraries/stdlib/src/kotlin/ranges/Ranges.kt index de5232de0ca..b6f2fb122bf 100644 --- a/libraries/stdlib/src/kotlin/ranges/Ranges.kt +++ b/libraries/stdlib/src/kotlin/ranges/Ranges.kt @@ -25,30 +25,69 @@ private open class ComparableRange> ( private class DoubleRange ( start: Double, endInclusive: Double -): ComparableRange(start, endInclusive) { - override fun contains(value: Double): Boolean = value >= start && value <= endInclusive - override fun isEmpty(): Boolean = start > endInclusive +): ClosedRange { + private val _start = start + private val _endInclusive = endInclusive + override val start: Double get() = _start + override val endInclusive: Double get() = _endInclusive + + override fun contains(value: Double): Boolean = value >= _start && value <= _endInclusive + override fun isEmpty(): Boolean = !(_start <= _endInclusive) + + override fun equals(other: Any?): Boolean { + return other is DoubleRange && (isEmpty() && other.isEmpty() || + _start == other._start && _endInclusive == other._endInclusive) + } + + override fun hashCode(): Int { + return if (isEmpty()) -1 else 31 * _start.hashCode() + _endInclusive.hashCode() + } + override fun toString(): String = "$_start..$_endInclusive" } +@JvmVersion private class FloatRange ( start: Float, endInclusive: Float -): ComparableRange(start, endInclusive) { - override fun contains(value: Float): Boolean = value >= start && value <= endInclusive - override fun isEmpty(): Boolean = start > endInclusive +): ClosedRange { + private val _start = start + private val _endInclusive = endInclusive + override val start: Float get() = _start + override val endInclusive: Float get() = _endInclusive + + override fun contains(value: Float): Boolean = value >= _start && value <= _endInclusive + override fun isEmpty(): Boolean = !(_start <= _endInclusive) + + override fun equals(other: Any?): Boolean { + return other is FloatRange && (isEmpty() && other.isEmpty() || + _start == other._start && _endInclusive == other._endInclusive) + } + + override fun hashCode(): Int { + return if (isEmpty()) -1 else 31 * _start.hashCode() + _endInclusive.hashCode() + } + override fun toString(): String = "$_start..$_endInclusive" } /** - * Creates a range from this [Comparable] value to the specified [that] value. This value - * needs to be smaller than [that] value, otherwise the returned range will be empty. + * Creates a range from this [Comparable] value to the specified [that] value. + * + * This value needs to be smaller than [that] value, otherwise the returned range will be empty. */ -public operator fun > T.rangeTo(that: T): ClosedRange = - @Suppress("UNCHECKED_CAST") - when { - this is Double && that is Double -> DoubleRange(this, that) as ClosedRange - this is Float && that is Float -> FloatRange(this, that) as ClosedRange - else -> ComparableRange(this, that) - } +public operator fun > T.rangeTo(that: T): ClosedRange = ComparableRange(this, that) + +/** + * Creates a range from this [Double] value to the specified [other] value. + */ +@SinceKotlin("1.1") +public operator fun Double.rangeTo(that: Double): ClosedRange = DoubleRange(this, that) + +/** + * Creates a range from this [Float] value to the specified [other] value. + */ +@JvmVersion +@SinceKotlin("1.1") +public operator fun Float.rangeTo(that: Float): ClosedRange = FloatRange(this, that) internal fun checkStepIsPositive(isPositive: Boolean, step: Number) { diff --git a/libraries/stdlib/test/ranges/RangeTest.kt b/libraries/stdlib/test/ranges/RangeTest.kt index 298cf3a8afe..8e4612f7a0b 100644 --- a/libraries/stdlib/test/ranges/RangeTest.kt +++ b/libraries/stdlib/test/ranges/RangeTest.kt @@ -200,10 +200,12 @@ public class RangeTest { val specialRange = 0.0..-0.0 assertFalse(specialRange.isEmpty()) assertTrue(-0.0 in specialRange) - // should it be or not: assertEquals(-0.0..0.0, specialRange) + val normalSpecialRange = -0.0..0.0 + assertEquals(specialRange, normalSpecialRange) + assertEquals(specialRange.hashCode(), normalSpecialRange.hashCode()) val nanRange = 0.0..Double.NaN assertFalse(Double.NaN in nanRange) - + assertTrue(nanRange.isEmpty()) } @Test fun floatRange() { @@ -236,9 +238,12 @@ public class RangeTest { val specialRange = 0.0F..-0.0F assertFalse(specialRange.isEmpty()) assertTrue(-0.0F in specialRange) - // should it be or not: assertEquals(-0.0F..0.0F, specialRange) + val normalSpecialRange = -0.0F..0.0F + assertEquals(specialRange, normalSpecialRange) + assertEquals(specialRange.hashCode(), normalSpecialRange.hashCode()) val nanRange = 0.0F..Float.NaN assertFalse(Float.NaN in nanRange) + assertTrue(nanRange.isEmpty()) } @Test fun isEmpty() { diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt index 8577d3194dd..a2eb77856d4 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt @@ -1932,6 +1932,8 @@ public final class kotlin/ranges/RangesKt { public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;F)Z public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;I)Z public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;S)Z + public static final fun rangeTo (DD)Lkotlin/ranges/ClosedRange; + public static final fun rangeTo (FF)Lkotlin/ranges/ClosedRange; public static final fun rangeTo (Ljava/lang/Comparable;Ljava/lang/Comparable;)Lkotlin/ranges/ClosedRange; public static final fun reversed (Lkotlin/ranges/CharProgression;)Lkotlin/ranges/CharProgression; public static final fun reversed (Lkotlin/ranges/IntProgression;)Lkotlin/ranges/IntProgression;