blob: a3d465c43b7425ec2f4efbb4ed7273ed1b77c37d [file] [log] [blame]
/*
* Copyright 2010-2023 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 org.jetbrains.kotlin.generators.builtins.numbers
import org.jetbrains.kotlin.generators.builtins.PrimitiveType
import org.jetbrains.kotlin.generators.builtins.convert
import org.jetbrains.kotlin.generators.builtins.generateBuiltIns.BuiltInsSourceGenerator
import org.jetbrains.kotlin.generators.builtins.numbers.primitives.BasePrimitivesGenerator
import org.jetbrains.kotlin.generators.builtins.printDoc
import java.io.PrintWriter
class GenerateFloorDivMod(out: PrintWriter) : BuiltInsSourceGenerator(out) {
override fun getMultifileClassName() = "NumbersKt"
override fun generateBody() {
out.println("import kotlin.math.sign")
out.println()
val integerTypes = PrimitiveType.integral intersect PrimitiveType.onlyNumeric
for (thisType in integerTypes) {
for (otherType in integerTypes) {
generateFloorDiv(thisType, otherType)
generateMod(thisType, otherType)
}
}
val fpTypes = PrimitiveType.floatingPoint
for (thisType in fpTypes) {
for (otherType in fpTypes) {
generateFpMod(thisType, otherType)
}
}
}
private fun generateFloorDiv(thisKind: PrimitiveType, otherKind: PrimitiveType) {
val returnType = getOperatorReturnType(thisKind, otherKind)
val returnTypeName = returnType.capitalized
out.printDoc(BasePrimitivesGenerator.binaryOperatorDoc("floorDiv", thisKind, otherKind), "")
out.println("""@SinceKotlin("1.5")""")
out.println("@kotlin.internal.InlineOnly")
out.println("@kotlin.internal.IntrinsicConstEvaluation")
val declaration = "public inline fun ${thisKind.capitalized}.floorDiv(other: ${otherKind.capitalized}): $returnTypeName"
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
out.println(
"""
$declaration {
var q = this / other
if (this xor other < 0 && q * other != this) q--
return q
}
""".trimIndent()
)
} else {
out.println("$declaration = ")
out.println(" ${
convert("this", thisKind, returnType)}.floorDiv(${convert("other", otherKind, returnType)})")
}
out.println()
}
private fun generateMod(thisKind: PrimitiveType, otherKind: PrimitiveType) {
val operationType = getOperatorReturnType(thisKind, otherKind)
val returnType = otherKind
out.printDoc(BasePrimitivesGenerator.binaryOperatorDoc("mod", thisKind, otherKind),"")
out.println("""@SinceKotlin("1.5")""")
out.println("@kotlin.internal.InlineOnly")
out.println("@kotlin.internal.IntrinsicConstEvaluation")
val declaration = "public inline fun ${thisKind.capitalized}.mod(other: ${otherKind.capitalized}): ${returnType.capitalized}"
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
out.println(
"""
$declaration {
val r = this % other
return r + (other and (((r xor other) and (r or -r)) shr ${operationType.bitSize - 1}))
}
""".trimIndent()
)
} else {
out.println("$declaration = ")
out.println(" " + convert(
"${convert("this", thisKind, operationType)}.mod(${convert("other", otherKind, operationType)})",
operationType, returnType
))
}
out.println()
}
private fun generateFpMod(thisKind: PrimitiveType, otherKind: PrimitiveType) {
val operationType = getOperatorReturnType(thisKind, otherKind)
out.printDoc(BasePrimitivesGenerator.binaryOperatorDoc("mod", thisKind, otherKind), "")
out.println("""@SinceKotlin("1.5")""")
out.println("@kotlin.internal.InlineOnly")
out.println("@kotlin.internal.IntrinsicConstEvaluation")
val declaration = "public inline fun ${thisKind.capitalized}.mod(other: ${otherKind.capitalized}): ${operationType.capitalized}"
if (thisKind == otherKind && thisKind >= PrimitiveType.INT) {
out.println(
"""
$declaration {
val r = this % other
return if (r != ${convert("0.0", PrimitiveType.DOUBLE, operationType)} && r.sign != other.sign) r + other else r
}
""".trimIndent()
)
} else {
out.println("$declaration = ")
out.println(" ${convert("this", thisKind, operationType)}.mod(${convert("other", otherKind, operationType)})")
}
out.println()
}
}
private fun maxByDomainCapacity(type1: PrimitiveType, type2: PrimitiveType): PrimitiveType
= if (type1.ordinal > type2.ordinal) type1 else type2
private fun getOperatorReturnType(kind1: PrimitiveType, kind2: PrimitiveType): PrimitiveType {
require(kind1 != PrimitiveType.BOOLEAN) { "kind1 must not be BOOLEAN" }
require(kind2 != PrimitiveType.BOOLEAN) { "kind2 must not be BOOLEAN" }
return maxByDomainCapacity(maxByDomainCapacity(kind1, kind2), PrimitiveType.INT)
}