diff --git a/libraries/stdlib/test/unsigned/UNumbersTest.kt b/libraries/stdlib/test/unsigned/UNumbersTest.kt new file mode 100644 index 00000000000..82baa832ba0 --- /dev/null +++ b/libraries/stdlib/test/unsigned/UNumbersTest.kt @@ -0,0 +1,204 @@ +/* + * Copyright 2010-2019 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 test.unsigned + +import kotlin.random.* +import kotlin.test.* + + +class NumbersTest { + + @Test + fun ubyteBits() { + fun test(value: UByte, oneBits: Int, leadingZeroes: Int, trailingZeroes: Int) { + assertEquals(oneBits, value.countOneBits()) + assertEquals(leadingZeroes, value.countLeadingZeroBits()) + assertEquals(trailingZeroes, value.countTrailingZeroBits()) + val highestBit = if (leadingZeroes < UByte.SIZE_BITS) 1u.shl(UByte.SIZE_BITS - leadingZeroes - 1).toUByte() else 0u + val lowestBit = if (trailingZeroes < UByte.SIZE_BITS) 1u.shl(trailingZeroes).toUByte() else 0u + assertEquals(highestBit, value.takeHighestOneBit()) + assertEquals(lowestBit, value.takeLowestOneBit()) + } + + test(0u, 0, 8, 8) + test(1u, 1, 7, 0) + test(2u, 1, 6, 1) + test(0x44u, 2, 1, 2) + test(0x80u, 1, 0, 7) + test(0xF0u, 4, 0, 4) + } + + @Test + fun ushortBits() { + fun test(value: UShort, oneBits: Int, leadingZeroes: Int, trailingZeroes: Int) { + assertEquals(oneBits, value.countOneBits()) + assertEquals(leadingZeroes, value.countLeadingZeroBits()) + assertEquals(trailingZeroes, value.countTrailingZeroBits()) + val highestBit = if (leadingZeroes < UShort.SIZE_BITS) 1u.shl(UShort.SIZE_BITS - leadingZeroes - 1).toUShort() else 0u + val lowestBit = if (trailingZeroes < UShort.SIZE_BITS) 1u.shl(trailingZeroes).toUShort() else 0u + assertEquals(highestBit, value.takeHighestOneBit()) + assertEquals(lowestBit, value.takeLowestOneBit()) + } + + test(0u, 0, 16, 16) + test(1u, 1, 15, 0) + test(2u, 1, 14, 1) + test(0xF2u, 5, 8, 1) + test(0x8000u, 1, 0, 15) + test(0xF200u, 5, 0, 9) + } + + @Test + fun uintBits() { + fun test(value: UInt, oneBits: Int, leadingZeroes: Int, trailingZeroes: Int) { + assertEquals(oneBits, value.countOneBits()) + assertEquals(leadingZeroes, value.countLeadingZeroBits()) + assertEquals(trailingZeroes, value.countTrailingZeroBits()) + val highestBit = if (leadingZeroes < UInt.SIZE_BITS) 1u.shl(UInt.SIZE_BITS - leadingZeroes - 1) else 0u + val lowestBit = if (trailingZeroes < UInt.SIZE_BITS) 1u.shl(trailingZeroes) else 0u + assertEquals(highestBit, value.takeHighestOneBit()) + assertEquals(lowestBit, value.takeLowestOneBit()) + } + + test(0u, 0, 32, 32) + test(1u, 1, 31, 0) + test(2u, 1, 30, 1) + test(0xF002u, 5, 16, 1) + test(0xF00F0000u, 8, 0, 16) + } + + @Test + fun ulongBits() { + fun test(value: ULong, oneBits: Int, leadingZeroes: Int, trailingZeroes: Int) { + assertEquals(oneBits, value.countOneBits()) + assertEquals(leadingZeroes, value.countLeadingZeroBits()) + assertEquals(trailingZeroes, value.countTrailingZeroBits()) + val highestBit = if (leadingZeroes < ULong.SIZE_BITS) 1uL.shl(ULong.SIZE_BITS - leadingZeroes - 1).toULong() else 0u + val lowestBit = if (trailingZeroes < ULong.SIZE_BITS) 1uL.shl(trailingZeroes).toULong() else 0u + assertEquals(highestBit, value.takeHighestOneBit()) + assertEquals(lowestBit, value.takeLowestOneBit()) + } + + test(0uL, 0, 64, 64) + test(1uL, 1, 63, 0) + test(2uL, 1, 62, 1) + test(0xF002uL, 5, 48, 1) + test(0xF00F0000uL, 8, 32, 16) + test(0x1111_3333_EEEE_0000uL, 4 + 8 + 12, 3, 17) + } + + + @Test + fun uintRotate() { + fun test(value: UInt, n: Int, expected: UInt) { + assertEquals(expected, value.rotateLeft(n)) + assertEquals(expected, value.rotateRight(-n)) + } + + fun testCyclic(value: UInt) { + for (n in -2 * UInt.SIZE_BITS..2 * UInt.SIZE_BITS) { + val rl = value.rotateLeft(n) + val rr = value.rotateRight(-n) + assertEquals(rl, rr) + assertEquals(rl, value.rotateLeft(n % UInt.SIZE_BITS)) + assertEquals(rr, value.rotateRight((-n) % UInt.SIZE_BITS)) + assertEquals(value, value.rotateLeft(n).rotateLeft(-n)) + assertEquals(value, value.rotateRight(n).rotateRight(-n)) + } + } + + test(0x7_3422345u, 4, 0x3422345_7u) + test(0x7342234_5u, -4, 0x5_7342234u) + test(0x73422345u, 1, 0xE684468Au) + repeat(100) { + testCyclic(Random.nextUInt()) + } + } + + @Test + fun ubyteRotate() { + fun test(value: UByte, n: Int, expected: UByte) { + assertEquals(expected, value.rotateLeft(n)) + assertEquals(expected, value.rotateRight(-n)) + } + + fun testCyclic(value: UByte) { + for (n in -2 * UByte.SIZE_BITS..2 * UByte.SIZE_BITS) { + val rl = value.rotateLeft(n) + val rr = value.rotateRight(-n) + assertEquals(rl, rr) + assertEquals(rl, value.rotateLeft(n % UByte.SIZE_BITS)) + assertEquals(rr, value.rotateRight((-n) % UByte.SIZE_BITS)) + assertEquals(value, value.rotateLeft(n).rotateLeft(-n)) + assertEquals(value, value.rotateRight(n).rotateRight(-n)) + } + } + + test(0x73u, 4, 0x37u) + test(0x73u, -3, 0x6Eu) + test(0x73u, 1, 0xE6u) + test(0xE6u, 1, 0xCDu) + repeat(100) { + testCyclic(Random.nextInt().toUByte()) + } + } + + @Test + fun ulongRotate() { + fun test(value: ULong, n: Int, expected: ULong) { + assertEquals(expected, value.rotateLeft(n)) + assertEquals(expected, value.rotateRight(-n)) + } + + fun testCyclic(value: ULong) { + for (n in -2 * ULong.SIZE_BITS..2 * ULong.SIZE_BITS) { + val rl = value.rotateLeft(n) + val rr = value.rotateRight(-n) + assertEquals(rl, rr) + assertEquals(rl, value.rotateLeft(n % ULong.SIZE_BITS)) + assertEquals(rr, value.rotateRight((-n) % ULong.SIZE_BITS)) + assertEquals(value, value.rotateLeft(n).rotateLeft(-n)) + assertEquals(value, value.rotateRight(n).rotateRight(-n)) + } + } + + test(0x7372ABAC_DEEF0123uL, 4, 0x372ABAC_DEEF01237uL) + test(0x88888888_44444444uL, -3, 0x91111111_08888888uL) + test(0x88888888_44444444uL, 1, 0x11111110_88888889uL) + repeat(100) { + testCyclic(Random.nextULong()) + } + } + + @Test + fun ushortRotate() { + fun test(value: UShort, n: Int, expected: UShort) { + assertEquals(expected, value.rotateLeft(n)) + assertEquals(expected, value.rotateRight(-n)) + } + + fun testCyclic(value: UShort) { + for (n in -2 * UShort.SIZE_BITS..2 * UShort.SIZE_BITS) { + val rl = value.rotateLeft(n) + val rr = value.rotateRight(-n) + assertEquals(rl, rr) + assertEquals(rl, value.rotateLeft(n % UShort.SIZE_BITS)) + assertEquals(rr, value.rotateRight((-n) % UShort.SIZE_BITS)) + assertEquals(value, value.rotateLeft(n).rotateLeft(-n)) + assertEquals(value, value.rotateRight(n).rotateRight(-n)) + } + } + + test(0x7361u, 4, 0x3617u) + test(0x7361u, -3, 0b001_0111_0011_0110_0u) + test(0x7361u, 1, 0b111_0011_0110_0001_0u) + test(0xE6C2u, 1, 0b11_0011_0110_0001_01u) + repeat(100) { + testCyclic(Random.nextInt().toUShort()) + } + } + +} \ No newline at end of file diff --git a/libraries/stdlib/unsigned/src/kotlin/UNumbers.kt b/libraries/stdlib/unsigned/src/kotlin/UNumbers.kt new file mode 100644 index 00000000000..4e857e86eea --- /dev/null +++ b/libraries/stdlib/unsigned/src/kotlin/UNumbers.kt @@ -0,0 +1,327 @@ +/* + * Copyright 2010-2019 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. + */ + +@file:kotlin.jvm.JvmName("UNumbersKt") +package kotlin + +/** + * Counts the number of set bits in the binary representation of this [UInt] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.countOneBits(): Int = toInt().countOneBits() + +/** + * Counts the number of consecutive most significant bits that are zero in the binary representation of this [UInt] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.countLeadingZeroBits(): Int = toInt().countLeadingZeroBits() + +/** + * Counts the number of consecutive least significant bits that are zero in the binary representation of this [UInt] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.countTrailingZeroBits(): Int = toInt().countTrailingZeroBits() + +/** + * Returns a number having a single bit set in the position of the most significant set bit of this [UInt] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.takeHighestOneBit(): UInt = toInt().takeHighestOneBit().toUInt() + +/** + * Returns a number having a single bit set in the position of the least significant set bit of this [UInt] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.takeLowestOneBit(): UInt = toInt().takeLowestOneBit().toUInt() + +/** + * Rotates the binary representation of this [UInt] number left by the specified [bitCount] number of bits. + * The most significant bits pushed out from the left side reenter the number as the least significant bits on the right side. + * + * Rotating the number left by a negative bit count is the same as rotating it right by the negated bit count: + * `number.rotateLeft(-n) == number.rotateRight(n)` + * + * Rotating by a multiple of [UInt.SIZE_BITS] (32) returns the same number, or more generally + * `number.rotateLeft(n) == number.rotateLeft(n % 32)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.rotateLeft(bitCount: Int): UInt = toInt().rotateLeft(bitCount).toUInt() + + +/** + * Rotates the binary representation of this [UInt] number right by the specified [bitCount] number of bits. + * The least significant bits pushed out from the right side reenter the number as the most significant bits on the left side. + * + * Rotating the number right by a negative bit count is the same as rotating it left by the negated bit count: + * `number.rotateRight(-n) == number.rotateLeft(n)` + * + * Rotating by a multiple of [UInt.SIZE_BITS] (32) returns the same number, or more generally + * `number.rotateRight(n) == number.rotateRight(n % 32)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UInt.rotateRight(bitCount: Int): UInt = toInt().rotateRight(bitCount).toUInt() + + +/** + * Counts the number of set bits in the binary representation of this [ULong] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.countOneBits(): Int = toLong().countOneBits() + +/** + * Counts the number of consecutive most significant bits that are zero in the binary representation of this [ULong] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.countLeadingZeroBits(): Int = toLong().countLeadingZeroBits() + +/** + * Counts the number of consecutive least significant bits that are zero in the binary representation of this [ULong] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.countTrailingZeroBits(): Int = toLong().countTrailingZeroBits() + +/** + * Returns a number having a single bit set in the position of the most significant set bit of this [ULong] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.takeHighestOneBit(): ULong = toLong().takeHighestOneBit().toULong() + +/** + * Returns a number having a single bit set in the position of the least significant set bit of this [ULong] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.takeLowestOneBit(): ULong = toLong().takeLowestOneBit().toULong() + +/** + * Rotates the binary representation of this [ULong] number left by the specified [bitCount] number of bits. + * The most significant bits pushed out from the left side reenter the number as the least significant bits on the right side. + * + * Rotating the number left by a negative bit count is the same as rotating it right by the negated bit count: + * `number.rotateLeft(-n) == number.rotateRight(n)` + * + * Rotating by a multiple of [ULong.SIZE_BITS] (64) returns the same number, or more generally + * `number.rotateLeft(n) == number.rotateLeft(n % 64)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.rotateLeft(bitCount: Int): ULong = toLong().rotateLeft(bitCount).toULong() + +/** + * Rotates the binary representation of this [ULong] number right by the specified [bitCount] number of bits. + * The least significant bits pushed out from the right side reenter the number as the most significant bits on the left side. + * + * Rotating the number right by a negative bit count is the same as rotating it left by the negated bit count: + * `number.rotateRight(-n) == number.rotateLeft(n)` + * + * Rotating by a multiple of [ULong.SIZE_BITS] (64) returns the same number, or more generally + * `number.rotateRight(n) == number.rotateRight(n % 64)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULong.rotateRight(bitCount: Int): ULong = toLong().rotateRight(bitCount).toULong() + +/** + * Counts the number of set bits in the binary representation of this [UByte] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.countOneBits(): Int = toUInt().countOneBits() + +/** + * Counts the number of consecutive most significant bits that are zero in the binary representation of this [UByte] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.countLeadingZeroBits(): Int = toByte().countLeadingZeroBits() + +/** + * Counts the number of consecutive least significant bits that are zero in the binary representation of this [UByte] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.countTrailingZeroBits(): Int = toByte().countTrailingZeroBits() + +/** + * Returns a number having a single bit set in the position of the most significant set bit of this [UByte] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.takeHighestOneBit(): UByte = toInt().takeHighestOneBit().toUByte() + +/** + * Returns a number having a single bit set in the position of the least significant set bit of this [UByte] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.takeLowestOneBit(): UByte = toInt().takeLowestOneBit().toUByte() + + +/** + * Rotates the binary representation of this [UByte] number left by the specified [bitCount] number of bits. + * The most significant bits pushed out from the left side reenter the number as the least significant bits on the right side. + * + * Rotating the number left by a negative bit count is the same as rotating it right by the negated bit count: + * `number.rotateLeft(-n) == number.rotateRight(n)` + * + * Rotating by a multiple of [UByte.SIZE_BITS] (8) returns the same number, or more generally + * `number.rotateLeft(n) == number.rotateLeft(n % 8)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.rotateLeft(bitCount: Int): UByte = toByte().rotateLeft(bitCount).toUByte() + +/** + * Rotates the binary representation of this [UByte] number right by the specified [bitCount] number of bits. + * The least significant bits pushed out from the right side reenter the number as the most significant bits on the left side. + * + * Rotating the number right by a negative bit count is the same as rotating it left by the negated bit count: + * `number.rotateRight(-n) == number.rotateLeft(n)` + * + * Rotating by a multiple of [UByte.SIZE_BITS] (8) returns the same number, or more generally + * `number.rotateRight(n) == number.rotateRight(n % 8)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByte.rotateRight(bitCount: Int): UByte = toByte().rotateRight(bitCount).toUByte() + +/** + * Counts the number of set bits in the binary representation of this [UShort] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.countOneBits(): Int = toUInt().countOneBits() + +/** + * Counts the number of consecutive most significant bits that are zero in the binary representation of this [UShort] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.countLeadingZeroBits(): Int = toShort().countLeadingZeroBits() + +/** + * Counts the number of consecutive least significant bits that are zero in the binary representation of this [UShort] number. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.countTrailingZeroBits(): Int = toShort().countTrailingZeroBits() + +/** + * Returns a number having a single bit set in the position of the most significant set bit of this [UShort] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.takeHighestOneBit(): UShort = toInt().takeHighestOneBit().toUShort() + +/** + * Returns a number having a single bit set in the position of the least significant set bit of this [UShort] number, + * or zero, if this number is zero. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.takeLowestOneBit(): UShort = toInt().takeLowestOneBit().toUShort() + + +/** + * Rotates the binary representation of this [UShort] number left by the specified [bitCount] number of bits. + * The most significant bits pushed out from the left side reenter the number as the least significant bits on the right side. + * + * Rotating the number left by a negative bit count is the same as rotating it right by the negated bit count: + * `number.rotateLeft(-n) == number.rotateRight(n)` + * + * Rotating by a multiple of [UShort.SIZE_BITS] (16) returns the same number, or more generally + * `number.rotateLeft(n) == number.rotateLeft(n % 16)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.rotateLeft(bitCount: Int): UShort = toShort().rotateLeft(bitCount).toUShort() + +/** + * Rotates the binary representation of this [UShort] number right by the specified [bitCount] number of bits. + * The least significant bits pushed out from the right side reenter the number as the most significant bits on the left side. + * + * Rotating the number right by a negative bit count is the same as rotating it left by the negated bit count: + * `number.rotateRight(-n) == number.rotateLeft(n)` + * + * Rotating by a multiple of [UShort.SIZE_BITS] (16) returns the same number, or more generally + * `number.rotateRight(n) == number.rotateRight(n % 16)` + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShort.rotateRight(bitCount: Int): UShort = toShort().rotateRight(bitCount).toUShort()