| /* |
| * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. |
| * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. |
| */ |
| |
| package templates |
| |
| import templates.Family.* |
| |
| object ComparableOps : TemplateGroupBase() { |
| |
| init { |
| defaultBuilder { |
| specialFor(Unsigned) { |
| since("1.3") |
| annotation("@ExperimentalUnsignedTypes") |
| } |
| } |
| } |
| |
| private val Family.sourceFileRanges: SourceFile |
| get() = when (this) { |
| Generic, Primitives -> SourceFile.Ranges |
| Unsigned -> SourceFile.URanges |
| else -> error(this) |
| } |
| private val Family.sourceFileComparisons: SourceFile |
| get() = when (this) { |
| Generic, Primitives -> SourceFile.Comparisons |
| Unsigned -> SourceFile.UComparisons |
| else -> error(this) |
| } |
| |
| private val Family.sampleSuffix: String |
| get() = when (this) { |
| Primitives -> "" |
| Unsigned -> "Unsigned" |
| Generic -> "Comparable" |
| else -> error(this) |
| } |
| |
| private val numericPrimitives = PrimitiveType.numericPrimitives.sortedBy { it.capacity }.toSet() |
| private val intPrimitives = setOf(PrimitiveType.Int, PrimitiveType.Long) |
| private val shortIntPrimitives = setOf(PrimitiveType.Byte, PrimitiveType.Short) |
| private val uintPrimitives = setOf(PrimitiveType.UInt, PrimitiveType.ULong) |
| |
| val f_coerceAtLeast = fn("coerceAtLeast(minimumValue: SELF)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileRanges) |
| returns("SELF") |
| typeParam("T : Comparable<T>") |
| doc { |
| """ |
| Ensures that this value is not less than the specified [minimumValue]. |
| |
| @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. |
| """ |
| } |
| sample("samples.comparisons.ComparableOps.coerceAtLeast${f.sampleSuffix}") |
| body { |
| """ |
| return if (this < minimumValue) minimumValue else this |
| """ |
| } |
| } |
| |
| val f_coerceAtMost = fn("coerceAtMost(maximumValue: SELF)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileRanges) |
| returns("SELF") |
| typeParam("T : Comparable<T>") |
| doc { |
| """ |
| Ensures that this value is not greater than the specified [maximumValue]. |
| |
| @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. |
| """ |
| } |
| sample("samples.comparisons.ComparableOps.coerceAtMost${f.sampleSuffix}") |
| body { |
| """ |
| return if (this > maximumValue) maximumValue else this |
| """ |
| } |
| } |
| |
| val f_coerceIn_range_primitive = fn("coerceIn(range: ClosedRange<T>)") { |
| include(Generic) |
| include(Primitives, intPrimitives) |
| include(Unsigned, uintPrimitives) |
| } builder { |
| sourceFile(f.sourceFileRanges) |
| returns("SELF") |
| typeParam("T : Comparable<T>") |
| doc { |
| """ |
| Ensures that this value lies in the specified [range]. |
| |
| @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. |
| """ |
| } |
| sample("samples.comparisons.ComparableOps.coerceIn${f.sampleSuffix}") |
| body { |
| """ |
| if (range is ClosedFloatingPointRange) { |
| return this.coerceIn<T>(range) |
| } |
| if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") |
| return when { |
| this < range.start -> range.start |
| this > range.endInclusive -> range.endInclusive |
| else -> this |
| } |
| """ |
| } |
| } |
| |
| val f_coerceIn_fpRange = fn("coerceIn(range: ClosedFloatingPointRange<T>)") { |
| include(Generic) |
| } builder { |
| sourceFile(f.sourceFileRanges) |
| since("1.1") |
| returns("SELF") |
| typeParam("T : Comparable<T>") |
| doc { |
| """ |
| Ensures that this value lies in the specified [range]. |
| |
| @return this value if it's in the [range], or `range.start` if this value is less than `range.start`, or `range.endInclusive` if this value is greater than `range.endInclusive`. |
| """ |
| } |
| sample("samples.comparisons.ComparableOps.coerceInFloatingPointRange") |
| body(Generic) { |
| """ |
| if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") |
| return when { |
| // this < start equiv to this <= start && !(this >= start) |
| range.lessThanOrEquals(this, range.start) && !range.lessThanOrEquals(range.start, this) -> range.start |
| // this > end equiv to this >= end && !(this <= end) |
| range.lessThanOrEquals(range.endInclusive, this) && !range.lessThanOrEquals(this, range.endInclusive) -> range.endInclusive |
| else -> this |
| } |
| """ |
| } |
| } |
| |
| |
| val f_minOf_2 = fn("minOf(a: T, b: T)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| typeParam("T : Comparable<T>") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the smaller of two values. |
| If values are equal, returns the first one. |
| """ |
| } |
| val defaultImpl = "if (a <= b) a else b" |
| body { "return $defaultImpl" } |
| |
| specialFor(Primitives, Unsigned) { |
| doc { "Returns the smaller of two values." } |
| } |
| // TODO: Add a note about NaN propagation for floats. |
| specialFor(Primitives) { |
| inlineOnly() |
| var convertBack = "to$primitive()" |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| convertBack = "unsafeCast<$primitive>()" |
| } |
| on(Platform.JVM) { |
| body { "return Math.min(a, b)" } |
| } |
| on(Platform.JS) { |
| body { "return Math.min(a, b)" } |
| if (primitive == PrimitiveType.Long) { |
| inline(suppressWarning = true) |
| body { "return $defaultImpl" } |
| } |
| } |
| if (primitive in shortIntPrimitives) { |
| body { "return Math.min(a.toInt(), b.toInt()).$convertBack" } |
| on(Platform.Native) { |
| body { "return minOf(a.toInt(), b.toInt()).$convertBack" } |
| } |
| } |
| if (primitive?.isFloatingPoint() == true) { |
| on(Platform.Native) { |
| body { |
| """ |
| return when { |
| a.isNaN() -> a |
| b.isNaN() -> b |
| else -> if (a.compareTo(b) <= 0) a else b |
| } |
| """ |
| } |
| } |
| } |
| } |
| specialFor(Generic) { |
| on(Platform.JS) { /* just to make expect, KT-22520 */ } |
| } |
| } |
| |
| val f_minOf_3 = fn("minOf(a: T, b: T, c: T)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| typeParam("T : Comparable<T>") |
| returns("T") |
| receiver("") |
| specialFor(Primitives, Unsigned) { inlineOnly() } |
| // TODO: Add a note about NaN propagation for floats. |
| doc { |
| """ |
| Returns the smaller of three values. |
| """ |
| } |
| body { |
| "return minOf(a, minOf(b, c))" |
| } |
| specialFor(Primitives, Generic) { |
| on(Platform.JS) { /* just to make expect, KT-22520 */ } |
| } |
| specialFor(Primitives) { |
| if (primitive in shortIntPrimitives) { |
| body { "return minOf(a.toInt(), minOf(b.toInt(), c.toInt())).to$primitive()" } |
| on(Platform.JVM) { |
| body { "return Math.min(a.toInt(), Math.min(b.toInt(), c.toInt())).to$primitive()" } |
| } |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| body { "return Math.min(a.toInt(), b.toInt(), c.toInt()).unsafeCast<$primitive>()" } |
| } |
| } |
| else if (primitive != PrimitiveType.Long) { |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| body { "return Math.min(a, b, c)" } |
| } |
| } |
| } |
| } |
| |
| val f_minOf_2_comparator = fn("minOf(a: T, b: T, comparator: Comparator<in T>)") { |
| include(Generic) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the smaller of two values according to the order specified by the given [comparator]. |
| If values are equal, returns the first one. |
| """ |
| } |
| body { |
| "return if (comparator.compare(a, b) <= 0) a else b" |
| } |
| } |
| |
| val f_minOf_3_comparator = fn("minOf(a: T, b: T, c: T, comparator: Comparator<in T>)") { |
| include(Generic) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the smaller of three values according to the order specified by the given [comparator]. |
| """ |
| } |
| body { |
| "return minOf(a, minOf(b, c, comparator), comparator)" |
| } |
| } |
| |
| val f_maxOf_2 = fn("maxOf(a: T, b: T)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| typeParam("T : Comparable<T>") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the greater of two values. |
| If values are equal, returns the first one. |
| """ |
| } |
| val defaultImpl = "if (a >= b) a else b" |
| body { "return $defaultImpl" } |
| |
| specialFor(Primitives, Unsigned) { |
| doc { "Returns the greater of two values." } |
| } |
| // TODO: Add a note about NaN propagation for floats. |
| specialFor(Primitives) { |
| inlineOnly() |
| var convertBack = "to$primitive()" |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| convertBack = "unsafeCast<$primitive>()" |
| } |
| on(Platform.JVM) { |
| body { "return Math.max(a, b)" } |
| } |
| on(Platform.JS) { |
| body { "return Math.max(a, b)" } |
| if (primitive == PrimitiveType.Long) { |
| inline(suppressWarning = true) |
| body { "return $defaultImpl" } |
| } |
| } |
| if (primitive in shortIntPrimitives) { |
| body { "return Math.max(a.toInt(), b.toInt()).$convertBack" } |
| on(Platform.Native) { |
| body { "return maxOf(a.toInt(), b.toInt()).$convertBack" } |
| } |
| } |
| if (primitive?.isFloatingPoint() == true) { |
| on(Platform.Native) { |
| body { |
| """ |
| return if (a.compareTo(b) >= 0) a else b |
| """ |
| } |
| } |
| } |
| } |
| specialFor(Generic) { |
| on(Platform.JS) { /* just to make expect, KT-22520 */ } |
| } |
| } |
| |
| val f_maxOf_3 = fn("maxOf(a: T, b: T, c: T)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| typeParam("T : Comparable<T>") |
| returns("T") |
| receiver("") |
| specialFor(Primitives, Unsigned) { inlineOnly() } |
| // TODO: Add a note about NaN propagation for floats. |
| doc { |
| """ |
| Returns the greater of three values. |
| """ |
| } |
| body { |
| "return maxOf(a, maxOf(b, c))" |
| } |
| specialFor(Primitives, Generic) { |
| on(Platform.JS) { /* just to make expect, KT-22520 */ } |
| } |
| specialFor(Primitives) { |
| if (primitive in shortIntPrimitives) { |
| body { "return maxOf(a.toInt(), maxOf(b.toInt(), c.toInt())).to$primitive()" } |
| on(Platform.JVM) { |
| body { "return Math.max(a.toInt(), Math.max(b.toInt(), c.toInt())).to$primitive()" } |
| } |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| body { "return Math.max(a.toInt(), b.toInt(), c.toInt()).unsafeCast<$primitive>()" } |
| } |
| } |
| else if (primitive != PrimitiveType.Long) { |
| on(Platform.JS) { |
| suppress("DEPRECATION_ERROR") |
| body { "return Math.max(a, b, c)" } |
| } |
| } |
| } |
| } |
| |
| val f_maxOf_2_comparator = fn("maxOf(a: T, b: T, comparator: Comparator<in T>)") { |
| include(Generic) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the greater of two values according to the order specified by the given [comparator]. |
| If values are equal, returns the first one. |
| """ |
| } |
| body { |
| "return if (comparator.compare(a, b) >= 0) a else b" |
| } |
| } |
| |
| val f_maxOf_3_comparator = fn("maxOf(a: T, b: T, c: T, comparator: Comparator<in T>)") { |
| include(Generic) |
| } builder { |
| sourceFile(f.sourceFileComparisons) |
| since("1.1") |
| returns("T") |
| receiver("") |
| doc { |
| """ |
| Returns the greater of three values according to the order specified by the given [comparator]. |
| """ |
| } |
| body { |
| "return maxOf(a, maxOf(b, c, comparator), comparator)" |
| } |
| } |
| |
| |
| val f_coerceIn_min_max = fn("coerceIn(minimumValue: SELF, maximumValue: SELF)") { |
| include(Generic) |
| include(Primitives, numericPrimitives) |
| include(Unsigned) |
| } builder { |
| sourceFile(f.sourceFileRanges) |
| |
| specialFor(Generic) { signature("coerceIn(minimumValue: SELF?, maximumValue: SELF?)", notForSorting = true) } |
| typeParam("T : Comparable<T>") |
| returns("SELF") |
| doc { |
| """ |
| Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. |
| |
| @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. |
| """ |
| } |
| sample("samples.comparisons.ComparableOps.coerceIn${f.sampleSuffix}") |
| body(Primitives, Unsigned) { |
| """ |
| if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum ${'$'}maximumValue is less than minimum ${'$'}minimumValue.") |
| if (this < minimumValue) return minimumValue |
| if (this > maximumValue) return maximumValue |
| return this |
| """ |
| } |
| body(Generic) { |
| """ |
| if (minimumValue !== null && maximumValue !== null) { |
| if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum ${'$'}maximumValue is less than minimum ${'$'}minimumValue.") |
| if (this < minimumValue) return minimumValue |
| if (this > maximumValue) return maximumValue |
| } |
| else { |
| if (minimumValue !== null && this < minimumValue) return minimumValue |
| if (maximumValue !== null && this > maximumValue) return maximumValue |
| } |
| return this |
| """ |
| } |
| } |
| } |