From d035c2690a74639799b342f84ae39490b6bd5231 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 23 Aug 2018 17:18:19 +0300 Subject: [PATCH] Random extensions for unsigned: fixes after review #KT-25570 --- libraries/stdlib/src/kotlin/random/Random.kt | 65 ++++++++++++-------- libraries/stdlib/test/random/RandomTest.kt | 27 ++++---- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/libraries/stdlib/src/kotlin/random/Random.kt b/libraries/stdlib/src/kotlin/random/Random.kt index 343576d9146..4788b5625db 100644 --- a/libraries/stdlib/src/kotlin/random/Random.kt +++ b/libraries/stdlib/src/kotlin/random/Random.kt @@ -324,29 +324,32 @@ public fun Random(seed: Long): Random = XorWowRandom(seed.toInt(), seed.shr(32). /** * Gets the next random [UInt] from the random number generator. * - * Generates a Unsigned Int random value uniformly distributed between [UInt.MIN_VALUE] and [UInt.MAX_VALUE] (inclusive) + * Generates a [UInt] random value uniformly distributed between [UInt.MIN_VALUE] and [UInt.MAX_VALUE] (inclusive). */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextUInt(): UInt = nextInt().toUInt() /** * Gets the next random [UInt] from the random number generator not greater than the specified [bound]. * - * Generated an Unsigned Int random value uniformly distributed between `0`(inclusive) and the specified [bound] (exclusive). + * Generates a [UInt] random value uniformly distributed between `0` (inclusive) and the specified [bound] (exclusive). * - * @throws IllegalArgumentException if [bound] is zero + * @throws IllegalArgumentException if [bound] is zero. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextUInt(bound: UInt): UInt = nextUInt(0u, bound) /** * Gets the next random [UInt] from the random number generator in the specified range. * - * Generated an Unsigned Integer random value uniformly distributed between the specified [origin] (inclusive) and the specified [bound] (exclusive). + * Generates a [UInt] random value uniformly distributed between the specified [origin] (inclusive) and the specified [bound] (exclusive). * - * @throws IllegalArgumentException if [origin] is greater than or equal to [bound] + * @throws IllegalArgumentException if [origin] is greater than or equal to [bound]. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextUInt(origin: UInt, bound: UInt): UInt { checkUIntRangeBounds(origin, bound) @@ -361,45 +364,49 @@ public fun Random.nextUInt(origin: UInt, bound: UInt): UInt { /** * Gets the next random [UInt] from the random number generator in the specified [range]. * - * Generates an Unsigned Integer random value uniformly distributed in the specified [range]: + * Generates a [UInt] random value uniformly distributed in the specified [range]: * from `range.start` inclusive to `range.endInclusive` inclusive. * * @throws IllegalArgumentException if [range] is empty. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextUInt(range: UIntRange): UInt = when { range.isEmpty() -> throw IllegalArgumentException("Cannot get random in empty range: $range") - range.last < UInt.MAX_VALUE -> nextUInt(range.first, range.last + 1.toUInt()) - range.first > UInt.MIN_VALUE -> nextUInt(range.first, range.last + 1.toUInt()) + range.last < UInt.MAX_VALUE -> nextUInt(range.first, range.last + 1u) + range.first > UInt.MIN_VALUE -> nextUInt(range.first, range.last + 1u) else -> nextUInt() } /** * Gets the next random [ULong] from the random number generator. * - * Generated a Unsigned Long random value uniformly distributed between [ULong.MIN_VALUE] and [ULong.MAX_VALUE] (inclusive) + * Generates a [ULong] random value uniformly distributed between [ULong.MIN_VALUE] and [ULong.MAX_VALUE] (inclusive). */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextULong(): ULong = nextLong().toULong() /** * Gets the next random [ULong] from the random number generator not greater than the specified [bound]. * - * Generated an Unsigned Long random value uniformly distributed between `0`(inclusive) and the specified [bound] (exclusive). + * Generates a [ULong] random value uniformly distributed between `0` (inclusive) and the specified [bound] (exclusive). * - * @throws IllegalArgumentException if [bound] is zero + * @throws IllegalArgumentException if [bound] is zero. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextULong(bound: ULong): ULong = nextULong(0uL, bound) /** - * Gets the next random [ULong] from the random number generator in the specified range + * Gets the next random [ULong] from the random number generator in the specified range. * - * Generated an Unsigned Long random value uniformly distributed between the specified [origin] (inclusive) and the specified [bound] (exclusive). + * Generates a [ULong] random value uniformly distributed between the specified [origin] (inclusive) and the specified [bound] (exclusive). * - * @throws IllegalArgumentException if [origin] is greater than or equal to [bound] + * @throws IllegalArgumentException if [origin] is greater than or equal to [bound]. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextULong(origin: ULong, bound: ULong): ULong { checkULongRangeBounds(origin, bound) @@ -413,41 +420,49 @@ public fun Random.nextULong(origin: ULong, bound: ULong): ULong { /** * Gets the next random [ULong] from the random number generator in the specified [range]. * - * Generates an Unsigned Long random value uniformly distributed in the specified [range]: + * Generates a [ULong] random value uniformly distributed in the specified [range]: * from `range.start` inclusive to `range.endInclusive` inclusive. * - * @throws IllegalArgumentException if [range] is empty + * @throws IllegalArgumentException if [range] is empty. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextULong(range: ULongRange): ULong = when { range.isEmpty() -> throw IllegalArgumentException("Cannot get random in empty range: $range") - range.last < ULong.MAX_VALUE -> nextULong(range.first, range.last + 1.toULong()) - range.first > ULong.MIN_VALUE -> nextULong(range.first, range.last + 1.toULong()) + range.last < ULong.MAX_VALUE -> nextULong(range.first, range.last + 1u) + range.first > ULong.MIN_VALUE -> nextULong(range.first, range.last + 1u) else -> nextULong() } /** - * Fills the specified byte [array] with random bytes and returns it. + * Fills the specified unsigned byte [array] with random bytes and returns it. * * @return [array] filled with random bytes. */ @SinceKotlin("1.3") -public fun Random.nextUBytes(array: UByteArray) = nextUBytes(array, 0, array.size) +@ExperimentalUnsignedTypes +public fun Random.nextUBytes(array: UByteArray): UByteArray { + nextBytes(array.asByteArray()) + return array +} /** - * Creates a byte array of the specified [size], filled with random bytes. + * Creates an unsigned byte array of the specified [size], filled with random bytes. */ @SinceKotlin("1.3") -public fun Random.nextUBytes(size: Int): UByteArray = UByteArray(nextBytes(size)) +@ExperimentalUnsignedTypes +public fun Random.nextUBytes(size: Int): UByteArray = nextBytes(size).asUByteArray() /** - * Fills a subrange of the specified UByte [array] starting from [fromIndex] inclusive and ending [toIndex] exclusive with random UBytes + * Fills a subrange of the specified `UByte` [array] starting from [fromIndex] inclusive and ending [toIndex] exclusive with random UBytes. * - * @returns [array] with the subrange filled with random bytes. + * @return [array] with the subrange filled with random bytes. */ @SinceKotlin("1.3") +@ExperimentalUnsignedTypes public fun Random.nextUBytes(array: UByteArray, fromIndex: Int = 0, toIndex: Int = array.size): UByteArray { - return UByteArray(nextBytes(array.storage, fromIndex, toIndex)) + nextBytes(array.asByteArray(), fromIndex, toIndex) + return array } internal expect fun defaultPlatformRandom(): Random diff --git a/libraries/stdlib/test/random/RandomTest.kt b/libraries/stdlib/test/random/RandomTest.kt index 9975e40e770..00345cac0ae 100644 --- a/libraries/stdlib/test/random/RandomTest.kt +++ b/libraries/stdlib/test/random/RandomTest.kt @@ -51,7 +51,7 @@ abstract class RandomSmokeTest { @Test fun nextUInt() { - var result1 = UInt.MIN_VALUE + var result1 = 0u var result2 = UInt.MAX_VALUE repeat(1000) { val r = subject.nextUInt() @@ -59,7 +59,7 @@ abstract class RandomSmokeTest { result2 = result2 and r } assertEquals(UInt.MAX_VALUE, result1, "All one bits should be present") - assertEquals(UInt.MIN_VALUE, result2, "All zero bits should present") + assertEquals(0u, result2, "All zero bits should present") } @@ -93,7 +93,7 @@ abstract class RandomSmokeTest { for (bound in listOf(2u, 3u, 7u, 16u, 32u, 0x4000_0000u, UInt.MAX_VALUE)) { repeat(1000) { val x = subject.nextUInt(bound) - if (x !in 0u..(bound - 1u)) + if (x !in 0u until bound) fail("Value $x must be in range [0, $bound)") } } @@ -137,7 +137,7 @@ abstract class RandomSmokeTest { for ((origin, bound) in listOf((0u to 2u), (1u to 6u), (0u to 32u), (0u to UInt.MAX_VALUE), (1u to (Int.MAX_VALUE.toUInt() + 1u)), (UInt.MIN_VALUE to UInt.MAX_VALUE))) { repeat(1000) { val x = subject.nextUInt(origin, bound) - if (x !in origin..bound - 1u) + if (x !in origin until bound) fail("Value $x must be in range [$origin, $bound)") } } @@ -167,7 +167,7 @@ abstract class RandomSmokeTest { @Test fun nextUIntInUIntRange() { assertFailsWith { subject.nextUInt(1u..0u) } - assertFailsWith { subject.nextUInt((-1).toUInt()..UInt.MIN_VALUE) } + assertFailsWith { subject.nextUInt(UInt.MAX_VALUE..UInt.MIN_VALUE) } assertFailsWith { subject.nextUInt(UInt.MAX_VALUE..(UInt.MAX_VALUE - 1u)) } repeat(1000) { it -> @@ -205,7 +205,7 @@ abstract class RandomSmokeTest { @Test fun nextULong() { - var result1 = ULong.MIN_VALUE + var result1 = 0uL var result2 = ULong.MAX_VALUE repeat(1000) { val r = subject.nextULong() @@ -214,7 +214,7 @@ abstract class RandomSmokeTest { } assertEquals(ULong.MAX_VALUE, result1, "All one bits should be present") - assertEquals(ULong.MIN_VALUE, result2, "All zero bits should be present") + assertEquals(0uL, result2, "All zero bits should be present") } @Test @@ -244,10 +244,10 @@ abstract class RandomSmokeTest { assertEquals(0uL, subject.nextULong(1uL)) } - for (bound in listOf(2uL, 23uL, 32uL, (0x1_0000_0000).toULong(), 0x4000_0000_0000_000.toULong(), ULong.MAX_VALUE)) { + for (bound in listOf(2uL, 23uL, 32uL, 0x1_0000_0000uL, 0x8000_0000_0000_000uL, ULong.MAX_VALUE)) { repeat(1000) { val x = subject.nextULong(bound) - if (x !in 0uL..(bound - 1uL)) { + if (x !in 0uL until bound) { fail("Value $x must be in range [0, $bound)") } } @@ -280,8 +280,8 @@ abstract class RandomSmokeTest { assertFailsWith { subject.nextULong(0uL, 0uL) } assertFailsWith { subject.nextULong((-1).toULong(), (-2).toULong()) } - for (i in 500uL..1500uL) { - val n = (0x1_0000_0000).toULong() + i + for (i in 0u..1000u) { + val n = 0x1_0000_0000uL - 500u + i assertEquals(n, subject.nextULong(n, n + 1uL)) } @@ -293,7 +293,7 @@ abstract class RandomSmokeTest { )) { repeat(1000) { val x = subject.nextULong(origin, bound) - if (x !in origin..(bound - 1uL)) { + if (x !in origin until bound) { fail("Value $x must be in range [$origin, $bound)") } } @@ -331,7 +331,6 @@ abstract class RandomSmokeTest { repeat(1000) { i -> val n = (0x1_0000_0000).toULong() - 500uL + i.toULong() assertEquals(n, subject.nextULong(n..n)) - } for (range in listOf( @@ -466,6 +465,8 @@ abstract class RandomSmokeTest { val ubytes2 = subject.nextUBytes(UByteArray(size)) assertEquals(size, ubytes2.size) assertTrue(ubytes2.any { it != 0.toUByte() }) + + assertFalse(ubytes1 contentEquals ubytes2) } @Test