Introduce OpenEndRange and make primitive ranges implement it
#KT-52932
This commit is contained in:
@@ -10,9 +10,18 @@ package kotlin.ranges
|
||||
/**
|
||||
* A range of values of type `Char`.
|
||||
*/
|
||||
public class CharRange(start: Char, endInclusive: Char) : CharProgression(start, endInclusive, 1), ClosedRange<Char> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
public class CharRange(start: Char, endInclusive: Char) : CharProgression(start, endInclusive, 1), ClosedRange<Char>, OpenEndRange<Char> {
|
||||
override val start: Char get() = first
|
||||
override val endInclusive: Char get() = last
|
||||
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
@Deprecated("Can throw an exception when it's impossible to represent the value with Char type, for example, when the range includes MAX_VALUE. It's recommended to use 'endInclusive' property that doesn't throw.")
|
||||
override val endExclusive: Char get() {
|
||||
if (last == Char.MAX_VALUE) error("Cannot return the exclusive upper bound of a range that includes MAX_VALUE.")
|
||||
return last + 1
|
||||
}
|
||||
|
||||
override fun contains(value: Char): Boolean = first <= value && value <= last
|
||||
|
||||
@@ -41,9 +50,18 @@ public class CharRange(start: Char, endInclusive: Char) : CharProgression(start,
|
||||
/**
|
||||
* A range of values of type `Int`.
|
||||
*/
|
||||
public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange<Int> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange<Int>, OpenEndRange<Int> {
|
||||
override val start: Int get() = first
|
||||
override val endInclusive: Int get() = last
|
||||
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
@Deprecated("Can throw an exception when it's impossible to represent the value with Int type, for example, when the range includes MAX_VALUE. It's recommended to use 'endInclusive' property that doesn't throw.")
|
||||
override val endExclusive: Int get() {
|
||||
if (last == Int.MAX_VALUE) error("Cannot return the exclusive upper bound of a range that includes MAX_VALUE.")
|
||||
return last + 1
|
||||
}
|
||||
|
||||
override fun contains(value: Int): Boolean = first <= value && value <= last
|
||||
|
||||
@@ -72,9 +90,18 @@ public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, end
|
||||
/**
|
||||
* A range of values of type `Long`.
|
||||
*/
|
||||
public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, endInclusive, 1), ClosedRange<Long> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, endInclusive, 1), ClosedRange<Long>, OpenEndRange<Long> {
|
||||
override val start: Long get() = first
|
||||
override val endInclusive: Long get() = last
|
||||
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
@Deprecated("Can throw an exception when it's impossible to represent the value with Long type, for example, when the range includes MAX_VALUE. It's recommended to use 'endInclusive' property that doesn't throw.")
|
||||
override val endExclusive: Long get() {
|
||||
if (last == Long.MAX_VALUE) error("Cannot return the exclusive upper bound of a range that includes MAX_VALUE.")
|
||||
return last + 1
|
||||
}
|
||||
|
||||
override fun contains(value: Long): Boolean = first <= value && value <= last
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
package kotlin.ranges
|
||||
|
||||
/**
|
||||
* Represents a range of values (for example, numbers or characters).
|
||||
* Represents a range of values (for example, numbers or characters) where both the lower and upper bounds are included in the range.
|
||||
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/ranges.html) for more information.
|
||||
*/
|
||||
public interface ClosedRange<T: Comparable<T>> {
|
||||
public interface ClosedRange<T : Comparable<T>> {
|
||||
/**
|
||||
* The minimum value in the range.
|
||||
*/
|
||||
@@ -22,6 +22,8 @@ public interface ClosedRange<T: Comparable<T>> {
|
||||
|
||||
/**
|
||||
* Checks whether the specified [value] belongs to the range.
|
||||
*
|
||||
* A value belongs to the closed range if it is greater than or equal to the [start] bound and less than or equal to the [endInclusive] bound.
|
||||
*/
|
||||
public operator fun contains(value: T): Boolean = value >= start && value <= endInclusive
|
||||
|
||||
@@ -32,3 +34,38 @@ public interface ClosedRange<T: Comparable<T>> {
|
||||
*/
|
||||
public fun isEmpty(): Boolean = start > endInclusive
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a range of values (for example, numbers or characters) where the upper bound is not included in the range.
|
||||
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/ranges.html) for more information.
|
||||
*/
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
public interface OpenEndRange<T : Comparable<T>> {
|
||||
/**
|
||||
* The minimum value in the range.
|
||||
*/
|
||||
public val start: T
|
||||
|
||||
/**
|
||||
* The maximum value in the range (exclusive).
|
||||
*
|
||||
* @throws IllegalStateException can be thrown if the exclusive end bound cannot be represented
|
||||
* with a value of type [T].
|
||||
*/
|
||||
public val endExclusive: T
|
||||
|
||||
/**
|
||||
* Checks whether the specified [value] belongs to the range.
|
||||
*
|
||||
* A value belongs to the open-ended range if it is greater than or equal to the [start] bound and strictly less than the [endExclusive] bound.
|
||||
*/
|
||||
public operator fun contains(value: T): Boolean = value >= start && value < endExclusive
|
||||
|
||||
/**
|
||||
* Checks whether the range is empty.
|
||||
*
|
||||
* The open-ended range is empty if its start value is greater than or equal to the end value.
|
||||
*/
|
||||
public fun isEmpty(): Boolean = start >= endExclusive
|
||||
}
|
||||
@@ -36,6 +36,37 @@ private open class ComparableRange<T : Comparable<T>>(
|
||||
*/
|
||||
public operator fun <T : Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
|
||||
|
||||
/**
|
||||
* Represents a range of [Comparable] values.
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private open class ComparableOpenEndRange<T : Comparable<T>>(
|
||||
override val start: T,
|
||||
override val endExclusive: T
|
||||
) : OpenEndRange<T> {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is ComparableOpenEndRange<*> && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && endExclusive == other.endExclusive)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return if (isEmpty()) -1 else 31 * start.hashCode() + endExclusive.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = "$start..<$endExclusive"
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an open-ended 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.
|
||||
* @sample samples.ranges.Ranges.rangeFromComparable
|
||||
*/
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
public operator fun <T : Comparable<T>> T.rangeUntil(that: T): OpenEndRange<T> = ComparableOpenEndRange(this, that)
|
||||
|
||||
|
||||
/**
|
||||
* Represents a range of floating point numbers.
|
||||
@@ -96,6 +127,47 @@ private class ClosedDoubleRange(
|
||||
@SinceKotlin("1.1")
|
||||
public operator fun Double.rangeTo(that: Double): ClosedFloatingPointRange<Double> = ClosedDoubleRange(this, that)
|
||||
|
||||
/**
|
||||
* An open-ended range of values of type `Double`.
|
||||
*
|
||||
* Numbers are compared with the ends of this range according to IEEE-754.
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private class OpenEndDoubleRange(
|
||||
start: Double,
|
||||
endExclusive: Double
|
||||
) : OpenEndRange<Double> {
|
||||
private val _start = start
|
||||
private val _endExclusive = endExclusive
|
||||
override val start: Double get() = _start
|
||||
override val endExclusive: Double get() = _endExclusive
|
||||
|
||||
private fun lessThanOrEquals(a: Double, b: Double): Boolean = a <= b
|
||||
|
||||
override fun contains(value: Double): Boolean = value >= _start && value < _endExclusive
|
||||
override fun isEmpty(): Boolean = !(_start < _endExclusive)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OpenEndDoubleRange && (isEmpty() && other.isEmpty() ||
|
||||
_start == other._start && _endExclusive == other._endExclusive)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return if (isEmpty()) -1 else 31 * _start.hashCode() + _endExclusive.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = "$_start..<$_endExclusive"
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an open-ended range from this [Double] value to the specified [that] value.
|
||||
*
|
||||
* Numbers are compared with the ends of this range according to IEEE-754.
|
||||
*/
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
public operator fun Double.rangeUntil(that: Double): OpenEndRange<Double> = OpenEndDoubleRange(this, that)
|
||||
|
||||
|
||||
/**
|
||||
* A closed range of values of type `Float`.
|
||||
@@ -138,6 +210,48 @@ private class ClosedFloatRange(
|
||||
public operator fun Float.rangeTo(that: Float): ClosedFloatingPointRange<Float> = ClosedFloatRange(this, that)
|
||||
|
||||
|
||||
/**
|
||||
* An open-ended range of values of type `Float`.
|
||||
*
|
||||
* Numbers are compared with the ends of this range according to IEEE-754.
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private class OpenEndFloatRange(
|
||||
start: Float,
|
||||
endExclusive: Float
|
||||
) : OpenEndRange<Float> {
|
||||
private val _start = start
|
||||
private val _endExclusive = endExclusive
|
||||
override val start: Float get() = _start
|
||||
override val endExclusive: Float get() = _endExclusive
|
||||
|
||||
private fun lessThanOrEquals(a: Float, b: Float): Boolean = a <= b
|
||||
|
||||
override fun contains(value: Float): Boolean = value >= _start && value < _endExclusive
|
||||
override fun isEmpty(): Boolean = !(_start < _endExclusive)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OpenEndFloatRange && (isEmpty() && other.isEmpty() ||
|
||||
_start == other._start && _endExclusive == other._endExclusive)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return if (isEmpty()) -1 else 31 * _start.hashCode() + _endExclusive.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = "$_start..<$_endExclusive"
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an open-ended range from this [Float] value to the specified [that] value.
|
||||
*
|
||||
* Numbers are compared with the ends of this range according to IEEE-754.
|
||||
*/
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
public operator fun Float.rangeUntil(that: Float): OpenEndRange<Float> = OpenEndFloatRange(this, that)
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if this iterable range contains the specified [element].
|
||||
*
|
||||
@@ -145,9 +259,19 @@ public operator fun Float.rangeTo(that: Float): ClosedFloatingPointRange<Float>
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline operator fun <T, R> R.contains(element: T?): Boolean where T : Any, R : Iterable<T>, R : ClosedRange<T> =
|
||||
public inline operator fun <T, R> R.contains(element: T?): Boolean where T : Any, R : ClosedRange<T>, R : Iterable<T> =
|
||||
element != null && contains(element)
|
||||
|
||||
/**
|
||||
* Returns `true` if this iterable range contains the specified [element].
|
||||
*
|
||||
* Always returns `false` if the [element] is `null`.
|
||||
*/
|
||||
@SinceKotlin("1.7")
|
||||
@ExperimentalStdlibApi
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline operator fun <T, R> R.contains(element: T?): Boolean where T : Any, R : OpenEndRange<T>, R : Iterable<T> =
|
||||
element != null && contains(element)
|
||||
|
||||
internal fun checkStepIsPositive(isPositive: Boolean, step: Number) {
|
||||
if (!isPositive) throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||
|
||||
Reference in New Issue
Block a user