Fix equals and hashCode for empty ranges and progressions
Empty range is equal to any other empty range
This commit is contained in:
@@ -32,9 +32,11 @@ public class ByteProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is ByteProgression && start == other.start && end == other.end && increment == other.increment
|
||||
other is ByteProgression && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end && increment == other.increment)
|
||||
|
||||
override fun hashCode(): Int = 31 * (31 * start.toInt() + end) + increment
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * start.toInt() + end) + increment)
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -53,9 +55,11 @@ public class CharProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is CharProgression && start == other.start && end == other.end && increment == other.increment
|
||||
other is CharProgression && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end && increment == other.increment)
|
||||
|
||||
override fun hashCode(): Int = 31 * (31 * start.toInt() + end) + increment
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * start.toInt() + end) + increment)
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -74,9 +78,11 @@ public class ShortProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is ShortProgression && start == other.start && end == other.end && increment == other.increment
|
||||
other is ShortProgression && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end && increment == other.increment)
|
||||
|
||||
override fun hashCode(): Int = 31 * (31 * start.toInt() + end) + increment
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * start.toInt() + end) + increment)
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -95,9 +101,11 @@ public class IntProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is IntProgression && start == other.start && end == other.end && increment == other.increment
|
||||
other is IntProgression && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end && increment == other.increment)
|
||||
|
||||
override fun hashCode(): Int = 31 * (31 * start + end) + increment
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * start + end) + increment)
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -116,9 +124,11 @@ public class LongProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is LongProgression && start == other.start && end == other.end && increment == other.increment
|
||||
other is LongProgression && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end && increment == other.increment)
|
||||
|
||||
override fun hashCode(): Int = (31 * (31 * (start xor (start ushr 32)) + (end xor (end ushr 32))) + (increment xor (increment ushr 32))).toInt()
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * (start xor (start ushr 32)) + (end xor (end ushr 32))) + (increment xor (increment ushr 32))).toInt()
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -138,9 +148,11 @@ public class FloatProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is FloatProgression && java.lang.Float.compare(start, other.start) == 0 && java.lang.Float.compare(end, other.end) == 0 && java.lang.Float.compare(increment, other.increment) == 0
|
||||
other is FloatProgression && (isEmpty() && other.isEmpty() ||
|
||||
java.lang.Float.compare(start, other.start) == 0 && java.lang.Float.compare(end, other.end) == 0 && java.lang.Float.compare(increment, other.increment) == 0)
|
||||
|
||||
override fun hashCode(): Int = 31 * (31 * java.lang.Float.floatToIntBits(start) + java.lang.Float.floatToIntBits(end)) + java.lang.Float.floatToIntBits(increment)
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (31 * java.lang.Float.floatToIntBits(start) + java.lang.Float.floatToIntBits(end)) + java.lang.Float.floatToIntBits(increment))
|
||||
|
||||
override fun toString(): String = if (increment > 0) "$start..$end step $increment" else "$start downTo $end step ${-increment}"
|
||||
}
|
||||
@@ -160,9 +172,11 @@ public class DoubleProgression(
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is DoubleProgression && java.lang.Double.compare(start, other.start) == 0 && java.lang.Double.compare(end, other.end) == 0 && java.lang.Double.compare(increment, other.increment) == 0
|
||||
other is DoubleProgression && (isEmpty() && other.isEmpty() ||
|
||||
java.lang.Double.compare(start, other.start) == 0 && java.lang.Double.compare(end, other.end) == 0 && java.lang.Double.compare(increment, other.increment) == 0)
|
||||
|
||||
override fun hashCode(): Int {
|
||||
if (isEmpty()) return -1
|
||||
var temp = java.lang.Double.doubleToLongBits(start)
|
||||
var result = (temp xor (temp ushr 32))
|
||||
temp = java.lang.Double.doubleToLongBits(end)
|
||||
|
||||
@@ -29,9 +29,11 @@ public class ByteRange(public override val start: Byte, public override val end:
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is ByteRange && start == other.start && end == other.end
|
||||
other is ByteRange && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
|
||||
override fun hashCode(): Int = 31 * start.toInt() + end
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * start.toInt() + end)
|
||||
|
||||
class object {
|
||||
public val EMPTY: ByteRange = ByteRange(1, 0)
|
||||
@@ -49,9 +51,11 @@ public class CharRange(public override val start: Char, public override val end:
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is CharRange && start == other.start && end == other.end
|
||||
other is CharRange && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
|
||||
override fun hashCode(): Int = 31 * start.toInt() + end
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * start.toInt() + end)
|
||||
|
||||
class object {
|
||||
public val EMPTY: CharRange = CharRange(1.toChar(), 0.toChar())
|
||||
@@ -69,9 +73,11 @@ public class ShortRange(public override val start: Short, public override val en
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is ShortRange && start == other.start && end == other.end
|
||||
other is ShortRange && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
|
||||
override fun hashCode(): Int = 31 * start.toInt() + end
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * start.toInt() + end)
|
||||
|
||||
class object {
|
||||
public val EMPTY: ShortRange = ShortRange(1, 0)
|
||||
@@ -89,9 +95,11 @@ public class IntRange(public override val start: Int, public override val end: I
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is IntRange && start == other.start && end == other.end
|
||||
other is IntRange && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
|
||||
override fun hashCode(): Int = 31 * start + end
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * start + end)
|
||||
|
||||
class object {
|
||||
public val EMPTY: IntRange = IntRange(1, 0)
|
||||
@@ -109,9 +117,11 @@ public class LongRange(public override val start: Long, public override val end:
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is LongRange && start == other.start && end == other.end
|
||||
other is LongRange && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
|
||||
override fun hashCode(): Int = (31 * (start xor (start ushr 32)) + (end xor (end ushr 32))).toInt()
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * (start xor (start ushr 32)) + (end xor (end ushr 32))).toInt()
|
||||
|
||||
class object {
|
||||
public val EMPTY: LongRange = LongRange(1, 0)
|
||||
@@ -129,9 +139,11 @@ public class FloatRange(public override val start: Float, public override val en
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is FloatRange && java.lang.Float.compare(start, other.start) == 0 && java.lang.Float.compare(end, other.end) == 0
|
||||
other is FloatRange && (isEmpty() && other.isEmpty() ||
|
||||
java.lang.Float.compare(start, other.start) == 0 && java.lang.Float.compare(end, other.end) == 0)
|
||||
|
||||
override fun hashCode(): Int = 31 * java.lang.Float.floatToIntBits(start) + java.lang.Float.floatToIntBits(end)
|
||||
override fun hashCode(): Int =
|
||||
if (isEmpty()) -1 else (31 * java.lang.Float.floatToIntBits(start) + java.lang.Float.floatToIntBits(end))
|
||||
|
||||
class object {
|
||||
public val EMPTY: FloatRange = FloatRange(1.0f, 0.0f)
|
||||
@@ -149,9 +161,11 @@ public class DoubleRange(public override val start: Double, public override val
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is DoubleRange && java.lang.Double.compare(start, other.start) == 0 && java.lang.Double.compare(end, other.end) == 0
|
||||
other is DoubleRange && (isEmpty() && other.isEmpty() ||
|
||||
java.lang.Double.compare(start, other.start) == 0 && java.lang.Double.compare(end, other.end) == 0)
|
||||
|
||||
override fun hashCode(): Int {
|
||||
if (isEmpty()) return -1
|
||||
var temp = java.lang.Double.doubleToLongBits(start)
|
||||
val result = (temp xor (temp ushr 32))
|
||||
temp = java.lang.Double.doubleToLongBits(end)
|
||||
|
||||
@@ -45,11 +45,16 @@ class GenerateProgressions(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
val constructor = checkNaN + checkZero
|
||||
|
||||
val hashCode = when (kind) {
|
||||
BYTE, CHAR, SHORT -> "= 31 * (31 * start.toInt() + end) + increment"
|
||||
INT -> "= 31 * (31 * start + end) + increment"
|
||||
LONG -> "= (31 * (31 * ${hashLong("start")} + ${hashLong("end")}) + ${hashLong("increment")}).toInt()"
|
||||
FLOAT -> "= 31 * (31 * ${floatToIntBits("start")} + ${floatToIntBits("end")}) + ${floatToIntBits("increment")}"
|
||||
BYTE, CHAR, SHORT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * (31 * start.toInt() + end) + increment)"
|
||||
INT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * (31 * start + end) + increment)"
|
||||
LONG -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * (31 * ${hashLong("start")} + ${hashLong("end")}) + ${hashLong("increment")}).toInt()"
|
||||
FLOAT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * (31 * ${floatToIntBits("start")} + ${floatToIntBits("end")}) + ${floatToIntBits("increment")})"
|
||||
DOUBLE -> "{\n" +
|
||||
" if (isEmpty()) return -1\n" +
|
||||
" var temp = ${doubleToLongBits("start")}\n" +
|
||||
" var result = ${hashLong("temp")}\n" +
|
||||
" temp = ${doubleToLongBits("end")}\n" +
|
||||
@@ -74,7 +79,8 @@ class GenerateProgressions(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
public fun isEmpty(): Boolean = if (increment > 0) start > end else start < end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is $progression && ${compare("start")} && ${compare("end")} && ${compare("increment")}
|
||||
other is $progression && (isEmpty() && other.isEmpty() ||
|
||||
${compare("start")} && ${compare("end")} && ${compare("increment")})
|
||||
|
||||
override fun hashCode(): Int $hashCode
|
||||
|
||||
|
||||
@@ -45,11 +45,16 @@ class GenerateRanges(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
fun compare(v: String) = areEqualNumbers(kind, v)
|
||||
|
||||
val hashCode = when (kind) {
|
||||
BYTE, CHAR, SHORT -> "= 31 * start.toInt() + end"
|
||||
INT -> "= 31 * start + end"
|
||||
LONG -> "= (31 * ${hashLong("start")} + ${hashLong("end")}).toInt()"
|
||||
FLOAT -> "= 31 * ${floatToIntBits("start")} + ${floatToIntBits("end")}"
|
||||
BYTE, CHAR, SHORT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * start.toInt() + end)"
|
||||
INT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * start + end)"
|
||||
LONG -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * ${hashLong("start")} + ${hashLong("end")}).toInt()"
|
||||
FLOAT -> "=\n" +
|
||||
" if (isEmpty()) -1 else (31 * ${floatToIntBits("start")} + ${floatToIntBits("end")})"
|
||||
DOUBLE -> "{\n" +
|
||||
" if (isEmpty()) return -1\n" +
|
||||
" var temp = ${doubleToLongBits("start")}\n" +
|
||||
" val result = ${hashLong("temp")}\n" +
|
||||
" temp = ${doubleToLongBits("end")}\n" +
|
||||
@@ -69,7 +74,8 @@ class GenerateRanges(out: PrintWriter) : BuiltInsSourceGenerator(out) {
|
||||
override fun isEmpty(): Boolean = start > end
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is $range && ${compare("start")} && ${compare("end")}
|
||||
other is $range && (isEmpty() && other.isEmpty() ||
|
||||
${compare("start")} && ${compare("end")})
|
||||
|
||||
override fun hashCode(): Int $hashCode
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
package kotlin
|
||||
|
||||
public data class ComparableRange<T: Comparable<T>> (
|
||||
public class ComparableRange<T: Comparable<T>> (
|
||||
public override val start: T,
|
||||
public override val end: T
|
||||
): Range<T> {
|
||||
public override fun contains(item: T): Boolean {
|
||||
override fun contains(item: T): Boolean {
|
||||
return start <= item && item <= end
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is ComparableRange<*> && (isEmpty() && other.isEmpty() ||
|
||||
start == other.start && end == other.end)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return if (isEmpty()) -1 else 31 * start.hashCode() + end.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T: Comparable<T>> T.rangeTo(that: T): ComparableRange<T> {
|
||||
|
||||
@@ -153,6 +153,53 @@ public class RangeTest {
|
||||
assertTrue(("range".."progression").isEmpty())
|
||||
}
|
||||
|
||||
test fun emptyEquals() {
|
||||
assertTrue(IntRange.EMPTY == IntRange.EMPTY)
|
||||
assertEquals(IntRange.EMPTY, IntRange.EMPTY)
|
||||
assertEquals(0L..42L, 0L..42L)
|
||||
assertEquals(3 downTo 0, 3 downTo 0)
|
||||
|
||||
assertEquals(2..1, 1..0)
|
||||
assertEquals(2L..1L, 1L..0L)
|
||||
assertEquals(2.toShort()..1.toShort(), 1.toShort()..0.toShort())
|
||||
assertEquals(2.toByte()..1.toByte(), 1.toByte()..0.toByte())
|
||||
assertEquals(0f..-3.14f, 3.14f..0f)
|
||||
assertEquals(-2.0..-3.0, 3.0..2.0)
|
||||
assertEquals('b'..'a', 'c'..'b')
|
||||
|
||||
assertTrue(1 downTo 2 == 2 downTo 3)
|
||||
assertTrue(-1L downTo 0L == -2L downTo -1L)
|
||||
assertEquals(-1f downTo 1f, -2f downTo 2f)
|
||||
assertEquals(-4.0 downTo -3.0, -2.0 downTo -1.0)
|
||||
assertEquals('j'..'a' step 4, 'u'..'q' step 2)
|
||||
|
||||
assertFalse(0..1 == IntRange.EMPTY)
|
||||
assertFalse(1f downTo 2f == 2f downTo 1f)
|
||||
|
||||
assertEquals("range".."progression", "hashcode".."equals")
|
||||
assertFalse(("aa".."bb") == ("aaa".."bbb"))
|
||||
}
|
||||
|
||||
test fun emptyHashCode() {
|
||||
assertEquals((0..42).hashCode(), (0..42).hashCode())
|
||||
assertEquals((1.23..4.56).hashCode(), (1.23..4.56).hashCode())
|
||||
|
||||
assertEquals((0..-1).hashCode(), IntRange.EMPTY.hashCode())
|
||||
assertEquals((2L..1L).hashCode(), (1L..0L).hashCode())
|
||||
assertEquals((0.toShort()..-1.toShort()).hashCode(), (42.toShort()..0.toShort()).hashCode())
|
||||
assertEquals((0.toByte()..-1.toByte()).hashCode(), (42.toByte()..0.toByte()).hashCode())
|
||||
assertEquals((0f..-3.14f).hashCode(), (2.39f..1.41f).hashCode())
|
||||
assertEquals((0.0..-10.0).hashCode(), (10.0..0.0).hashCode())
|
||||
assertEquals(('z'..'x').hashCode(), ('l'..'k').hashCode())
|
||||
|
||||
assertEquals((1 downTo 2).hashCode(), (2 downTo 3).hashCode())
|
||||
assertEquals((1L downTo 2L).hashCode(), (2L downTo 3L).hashCode())
|
||||
assertEquals((1.0 downTo 2.0).hashCode(), (2.0 downTo 3.0).hashCode())
|
||||
assertEquals(('a' downTo 'b').hashCode(), ('c' downTo 'd').hashCode())
|
||||
|
||||
assertEquals(("range".."progression").hashCode(), ("hashcode".."equals").hashCode())
|
||||
}
|
||||
|
||||
test fun comparableRange() {
|
||||
val range = "island".."isle"
|
||||
assertFalse("apple" in range)
|
||||
|
||||
Reference in New Issue
Block a user