Introduce ClosedComparableRange interface which inherits ClosedRange to provide special comparison operation for double and float.

This commit is contained in:
Ilya Gorbunov
2016-11-25 07:14:14 +03:00
parent 5773594412
commit d60fc7d9a8
6 changed files with 71 additions and 25 deletions
@@ -12,7 +12,12 @@ class ComparablePair<T : Comparable<T>>(val first: T, val second: T) : Comparabl
fun <T : Comparable<T>> genericRangeTo(start: T, endInclusive: T) = start..endInclusive
operator fun Double.rangeTo(other: Double) = genericRangeTo(this, other)
operator fun Float.rangeTo(other: Float) = ClosedFloatRange(this, other)
// some weird inverted range
operator fun Float.rangeTo(other: Float) = object : ClosedComparableRange<Float> {
override val endInclusive: Float = this@rangeTo
override val start: Float = other
override fun lessThanOrEquals(a: Float, b: Float) = a >= b
}
fun check(x: Double, left: Double, right: Double): Boolean {
val result = x in left..right
+2 -2
View File
@@ -17,5 +17,5 @@
package kotlin.ranges
@SinceKotlin("1.1")
public inline operator fun Float.rangeTo(that: Float): ClosedRange<Float> =
this.toDouble().rangeTo(that.toDouble()) as ClosedRange<Float>
public inline operator fun Float.rangeTo(that: Float): ClosedComparableRange<Float> =
this.toDouble().rangeTo(that.toDouble()).unsafeCast<ClosedComparableRange<Float>>()
+30 -8
View File
@@ -2,6 +2,13 @@
@file:kotlin.jvm.JvmName("RangesKt")
package kotlin.ranges
public interface ClosedComparableRange<T: Comparable<T>> : ClosedRange<T> {
override fun contains(value: T): Boolean = lessThanOrEquals(value, start) && lessThanOrEquals(value, endInclusive)
override fun isEmpty(): Boolean = !lessThanOrEquals(start, endInclusive)
fun lessThanOrEquals(a: T, b: T): Boolean
}
/**
* Represents a range of [Comparable] values.
*/
@@ -22,20 +29,27 @@ private open class ComparableRange<T: Comparable<T>> (
override fun toString(): String = "$start..$endInclusive"
}
private class DoubleRange (
/**
* A closed range of values of type `Double`.
*
* Numbers are compared with the ends of this range according to IEEE-754.
*/
private class ClosedDoubleRange (
start: Double,
endInclusive: Double
): ClosedRange<Double> {
) : ClosedComparableRange<Double> {
private val _start = start
private val _endInclusive = endInclusive
override val start: Double get() = _start
override val endInclusive: Double get() = _endInclusive
override fun lessThanOrEquals(a: Double, b: Double): Boolean = a <= b
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() ||
return other is ClosedDoubleRange && (isEmpty() && other.isEmpty() ||
_start == other._start && _endInclusive == other._endInclusive)
}
@@ -45,21 +59,29 @@ private class DoubleRange (
override fun toString(): String = "$_start..$_endInclusive"
}
/**
* A closed range of values of type `Float`.
*
* Numbers are compared with the ends of this range according to IEEE-754.
*/
@JvmVersion
private class FloatRange (
private class ClosedFloatRange (
start: Float,
endInclusive: Float
): ClosedRange<Float> {
): ClosedComparableRange<Float> {
private val _start = start
private val _endInclusive = endInclusive
override val start: Float get() = _start
override val endInclusive: Float get() = _endInclusive
override fun lessThanOrEquals(a: Float, b: Float): Boolean = a <= b
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() ||
return other is ClosedFloatRange && (isEmpty() && other.isEmpty() ||
_start == other._start && _endInclusive == other._endInclusive)
}
@@ -80,14 +102,14 @@ public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = Comp
* Creates a range from this [Double] value to the specified [other] value.
*/
@SinceKotlin("1.1")
public operator fun Double.rangeTo(that: Double): ClosedRange<Double> = DoubleRange(this, that)
public operator fun Double.rangeTo(that: Double): ClosedComparableRange<Double> = ClosedDoubleRange(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<Float> = FloatRange(this, that)
public operator fun Float.rangeTo(that: Float): ClosedComparableRange<Float> = ClosedFloatRange(this, that)
internal fun checkStepIsPositive(isPositive: Boolean, step: Number) {
@@ -84,6 +84,8 @@ class CoercionTest {
assertFails { 1.0.coerceIn(1.0, 0.0) }
assertFails { 1.0.coerceIn(1.0..0.0) }
assertTrue(0.0 == 0.0.coerceIn(0.0, -0.0))
assertTrue(0.0 == 0.0.coerceIn(0.0..-0.0))
}
@Test
+18 -12
View File
@@ -197,14 +197,18 @@ public class RangeTest {
assertTrue(1.toLong() in range)
assertTrue(1.toFloat() in range)
val specialRange = 0.0..-0.0
assertFalse(specialRange.isEmpty())
assertTrue(-0.0 in specialRange)
val normalSpecialRange = -0.0..0.0
assertEquals(specialRange, normalSpecialRange)
assertEquals(specialRange.hashCode(), normalSpecialRange.hashCode())
val zeroRange = 0.0..-0.0
assertFalse(zeroRange.isEmpty())
assertTrue(-0.0 in zeroRange)
assertTrue(-0.0F in zeroRange)
val normalZeroRange = -0.0..0.0
assertEquals(zeroRange, normalZeroRange)
assertEquals(zeroRange.hashCode(), normalZeroRange.hashCode())
val nanRange = 0.0..Double.NaN
assertFalse(1.0 in nanRange)
assertFalse(Double.NaN in nanRange)
assertFalse(Float.NaN in nanRange)
assertTrue(nanRange.isEmpty())
val halfInfRange = 0.0..Double.POSITIVE_INFINITY
@@ -241,13 +245,15 @@ public class RangeTest {
assertFalse(Double.MAX_VALUE in range)
val specialRange = 0.0F..-0.0F
assertFalse(specialRange.isEmpty())
assertTrue(-0.0F in specialRange)
val normalSpecialRange = -0.0F..0.0F
assertEquals(specialRange, normalSpecialRange)
assertEquals(specialRange.hashCode(), normalSpecialRange.hashCode())
val zeroRange = 0.0F..-0.0F
assertFalse(zeroRange.isEmpty())
assertTrue(-0.0F in zeroRange)
val normalZeroRange = -0.0F..0.0F
assertEquals(zeroRange, normalZeroRange)
assertEquals(zeroRange.hashCode(), normalZeroRange.hashCode())
val nanRange = 0.0F..Float.NaN
assertFalse(1.0F in nanRange)
assertFalse(Float.NaN in nanRange)
assertTrue(nanRange.isEmpty())
@@ -1865,6 +1865,17 @@ public abstract interface class kotlin/properties/ReadWriteProperty {
public abstract fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
}
public abstract interface class kotlin/ranges/ClosedComparableRange : kotlin/ranges/ClosedRange {
public abstract fun contains (Ljava/lang/Comparable;)Z
public abstract fun isEmpty ()Z
public abstract fun lessThanOrEquals (Ljava/lang/Comparable;Ljava/lang/Comparable;)Z
}
public final class kotlin/ranges/ClosedComparableRange$DefaultImpls {
public static fun contains (Lkotlin/ranges/ClosedComparableRange;Ljava/lang/Comparable;)Z
public static fun isEmpty (Lkotlin/ranges/ClosedComparableRange;)Z
}
public final class kotlin/ranges/RangesKt {
public static final fun byteRangeContains (Lkotlin/ranges/ClosedRange;D)Z
public static final fun byteRangeContains (Lkotlin/ranges/ClosedRange;F)Z
@@ -1932,8 +1943,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 (DD)Lkotlin/ranges/ClosedComparableRange;
public static final fun rangeTo (FF)Lkotlin/ranges/ClosedComparableRange;
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;