Introduce new overloads of max and min to fix NaN propagation behavior on JVM.

This commit is contained in:
Ilya Gorbunov
2016-12-28 04:08:05 +03:00
parent 74331f8eaa
commit 0bf2ba8701
11 changed files with 557 additions and 61 deletions
@@ -827,6 +827,8 @@ public final class kotlin/collections/ArraysKt {
public static final fun max ([I)Ljava/lang/Integer;
public static final fun max ([J)Ljava/lang/Long;
public static final fun max ([Ljava/lang/Comparable;)Ljava/lang/Comparable;
public static final fun max ([Ljava/lang/Double;)Ljava/lang/Double;
public static final fun max ([Ljava/lang/Float;)Ljava/lang/Float;
public static final fun max ([S)Ljava/lang/Short;
public static final fun maxBy ([BLkotlin/jvm/functions/Function1;)Ljava/lang/Byte;
public static final fun maxBy ([CLkotlin/jvm/functions/Function1;)Ljava/lang/Character;
@@ -853,6 +855,8 @@ public final class kotlin/collections/ArraysKt {
public static final fun min ([I)Ljava/lang/Integer;
public static final fun min ([J)Ljava/lang/Long;
public static final fun min ([Ljava/lang/Comparable;)Ljava/lang/Comparable;
public static final fun min ([Ljava/lang/Double;)Ljava/lang/Double;
public static final fun min ([Ljava/lang/Float;)Ljava/lang/Float;
public static final fun min ([S)Ljava/lang/Short;
public static final fun minBy ([BLkotlin/jvm/functions/Function1;)Ljava/lang/Byte;
public static final fun minBy ([CLkotlin/jvm/functions/Function1;)Ljava/lang/Character;
@@ -1501,9 +1505,13 @@ public final class kotlin/collections/CollectionsKt {
public static final fun mapNotNullTo (Ljava/lang/Iterable;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection;
public static final fun mapTo (Ljava/lang/Iterable;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection;
public static final fun max (Ljava/lang/Iterable;)Ljava/lang/Comparable;
public static final fun max (Ljava/lang/Iterable;)Ljava/lang/Double;
public static final fun max (Ljava/lang/Iterable;)Ljava/lang/Float;
public static final fun maxBy (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun maxWith (Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/lang/Object;
public static final fun min (Ljava/lang/Iterable;)Ljava/lang/Comparable;
public static final fun min (Ljava/lang/Iterable;)Ljava/lang/Double;
public static final fun min (Ljava/lang/Iterable;)Ljava/lang/Float;
public static final fun minBy (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun minWith (Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/lang/Object;
public static final fun minus (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List;
@@ -2098,9 +2106,13 @@ public final class kotlin/sequences/SequencesKt {
public static final fun mapNotNullTo (Lkotlin/sequences/Sequence;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection;
public static final fun mapTo (Lkotlin/sequences/Sequence;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/util/Collection;
public static final fun max (Lkotlin/sequences/Sequence;)Ljava/lang/Comparable;
public static final fun max (Lkotlin/sequences/Sequence;)Ljava/lang/Double;
public static final fun max (Lkotlin/sequences/Sequence;)Ljava/lang/Float;
public static final fun maxBy (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun maxWith (Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Ljava/lang/Object;
public static final fun min (Lkotlin/sequences/Sequence;)Ljava/lang/Comparable;
public static final fun min (Lkotlin/sequences/Sequence;)Ljava/lang/Double;
public static final fun min (Lkotlin/sequences/Sequence;)Ljava/lang/Float;
public static final fun minBy (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun minWith (Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Ljava/lang/Object;
public static final fun minus (Lkotlin/sequences/Sequence;Ljava/lang/Iterable;)Lkotlin/sequences/Sequence;
@@ -137,36 +137,65 @@ fun aggregates(): List<GenericFunction> {
}
}
templates add f("min()") {
doc { f -> "Returns the smallest ${f.element} or `null` if there are no ${f.element.pluralize()}." }
returns("T?")
exclude(PrimitiveType.Boolean)
typeParam("T : Comparable<T>")
body {
"""
val iterator = iterator()
if (!iterator.hasNext()) return null
var min = iterator.next()
while (iterator.hasNext()) {
val e = iterator.next()
if (min > e) min = e
templates addAll listOf("min", "max").flatMap { op ->
val genericSpecializations = PrimitiveType.numericPrimitives.filterNot { it.isIntegral() } + listOf(null)
listOf(
Iterables to genericSpecializations,
Sequences to genericSpecializations,
ArraysOfObjects to genericSpecializations,
ArraysOfPrimitives to (PrimitiveType.defaultPrimitives - PrimitiveType.Boolean),
CharSequences to setOf(null)
).map { (f, primitives) -> primitives.map { primitive ->
f("$op()") {
val isFloat = primitive?.isIntegral() == false
val isGeneric = f in listOf(Iterables, Sequences, ArraysOfObjects)
only(f)
typeParam("T : Comparable<T>")
if (primitive != null) {
onlyPrimitives(f, primitive)
if (isFloat && isGeneric)
since("1.1")
}
doc { f -> "Returns the ${if (op == "max") "largest" else "smallest"} ${f.element} or `null` if there are no ${f.element.pluralize()}." }
returns("T?")
body {
if (f == ArraysOfObjects || f == ArraysOfPrimitives || f == CharSequences) {
"""
if (isEmpty()) return null
var $op = this[0]
${if (isFloat) "if ($op.isNaN()) return $op" else "\\"}
for (i in 1..lastIndex) {
val e = this[i]
${if (isFloat) "if (e.isNaN()) return e" else "\\"}
if ($op ${if (op == "max") "<" else ">"} e) $op = e
}
return $op
"""
}
else {
"""
val iterator = iterator()
if (!iterator.hasNext()) return null
var $op = iterator.next()
${if (isFloat) "if ($op.isNaN()) return $op" else "\\"}
while (iterator.hasNext()) {
val e = iterator.next()
${if (isFloat) "if (e.isNaN()) return e" else "\\"}
if ($op ${if (op == "max") "<" else ">"} e) $op = e
}
return $op
"""
}.replace(Regex("""^\s+\\\n""", RegexOption.MULTILINE), "") // trim lines ending with \
}
}
return min
"""
}
body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) {
"""
if (isEmpty()) return null
var min = this[0]
for (i in 1..lastIndex) {
val e = this[i]
if (min > e) min = e
}
return min
"""
}
}
}}
}.flatten()
templates add f("minBy(selector: (T) -> R)") {
inline(true)
@@ -242,39 +271,6 @@ fun aggregates(): List<GenericFunction> {
body(Maps) { "return entries.minWith(comparator)" }
}
templates add f("max()") {
doc { f -> "Returns the largest ${f.element} or `null` if there are no ${f.element.pluralize()}." }
returns("T?")
exclude(PrimitiveType.Boolean)
typeParam("T : Comparable<T>")
body {
"""
val iterator = iterator()
if (!iterator.hasNext()) return null
var max = iterator.next()
while (iterator.hasNext()) {
val e = iterator.next()
if (max < e) max = e
}
return max
"""
}
body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) {
"""
if (isEmpty()) return null
var max = this[0]
for (i in 1..lastIndex) {
val e = this[i]
if (max < e) max = e
}
return max
"""
}
}
templates add f("maxBy(selector: (T) -> R)") {
inline(true)