From ea2c33a5326e576a766d7cb3664609f09bbfff3b Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 24 Aug 2018 06:12:50 +0300 Subject: [PATCH] Extension random() to select random element from a collection Fixes #KT-15539 --- .../stdlib/common/src/generated/_Arrays.kt | 208 ++++++++++++++++++ .../common/src/generated/_Collections.kt | 24 ++ .../common/src/generated/_Comparisons.kt | 1 + .../stdlib/common/src/generated/_Maps.kt | 1 + .../stdlib/common/src/generated/_Ranges.kt | 64 ++++++ .../stdlib/common/src/generated/_Sequences.kt | 1 + .../stdlib/common/src/generated/_Sets.kt | 1 + .../stdlib/common/src/generated/_Strings.kt | 24 ++ .../stdlib/common/src/generated/_UArrays.kt | 101 +++++++++ .../stdlib/common/src/generated/_URanges.kt | 47 ++++ .../stdlib/test/collections/ArraysTest.kt | 26 +++ .../stdlib/test/collections/CollectionTest.kt | 27 +++ libraries/stdlib/test/text/StringTest.kt | 24 ++ .../kotlin-stdlib-runtime-merged.txt | 20 ++ .../src/templates/Elements.kt | 74 +++++++ .../kotlin-stdlib-gen/src/templates/Ranges.kt | 2 +- .../src/templates/dsl/CommonTypes.kt | 1 + .../src/templates/dsl/FamilyProperties.kt | 5 +- .../src/templates/dsl/Templates.kt | 1 + .../src/templates/dsl/Writers.kt | 3 + 20 files changed, 652 insertions(+), 3 deletions(-) diff --git a/libraries/stdlib/common/src/generated/_Arrays.kt b/libraries/stdlib/common/src/generated/_Arrays.kt index 3a0614e585e..92c20b6c996 100644 --- a/libraries/stdlib/common/src/generated/_Arrays.kt +++ b/libraries/stdlib/common/src/generated/_Arrays.kt @@ -16,6 +16,7 @@ package kotlin.collections import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns 1st *element* from the collection. @@ -2049,6 +2050,213 @@ public inline fun CharArray.lastOrNull(predicate: (Char) -> Boolean): Char? { return null } +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun Array.random(): T { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun ByteArray.random(): Byte { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun ShortArray.random(): Short { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun IntArray.random(): Int { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun LongArray.random(): Long { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun FloatArray.random(): Float { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun DoubleArray.random(): Double { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun BooleanArray.random(): Boolean { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun CharArray.random(): Char { + return random(Random) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun Array.random(random: Random): T { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun ByteArray.random(random: Random): Byte { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun ShortArray.random(random: Random): Short { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun IntArray.random(random: Random): Int { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun LongArray.random(random: Random): Long { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun FloatArray.random(random: Random): Float { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun DoubleArray.random(random: Random): Double { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun BooleanArray.random(random: Random): Boolean { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +public fun CharArray.random(random: Random): Char { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + /** * Returns the single element, or throws an exception if the array is empty or has more than one element. */ diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index 8ef7760b5d6..984914bad1e 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -16,6 +16,7 @@ package kotlin.collections import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns 1st *element* from the collection. @@ -456,6 +457,29 @@ public inline fun List.lastOrNull(predicate: (T) -> Boolean): T? { return null } +/** + * Returns a random element from this collection. + * + * @throws NoSuchElementException if this collection is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun Collection.random(): T { + return random(Random) +} + +/** + * Returns a random element from this collection using the specified source of randomness. + * + * @throws NoSuchElementException if this collection is empty. + */ +@SinceKotlin("1.3") +public fun Collection.random(random: Random): T { + if (isEmpty()) + throw NoSuchElementException("Collection is empty.") + return elementAt(random.nextInt(size)) +} + /** * Returns the single element, or throws an exception if the collection is empty or has more than one element. */ diff --git a/libraries/stdlib/common/src/generated/_Comparisons.kt b/libraries/stdlib/common/src/generated/_Comparisons.kt index 4f667094100..6ba7aa83967 100644 --- a/libraries/stdlib/common/src/generated/_Comparisons.kt +++ b/libraries/stdlib/common/src/generated/_Comparisons.kt @@ -16,6 +16,7 @@ package kotlin.comparisons import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns the greater of two values. diff --git a/libraries/stdlib/common/src/generated/_Maps.kt b/libraries/stdlib/common/src/generated/_Maps.kt index 1009ba3ba0c..849fc60f607 100644 --- a/libraries/stdlib/common/src/generated/_Maps.kt +++ b/libraries/stdlib/common/src/generated/_Maps.kt @@ -16,6 +16,7 @@ package kotlin.collections import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns a [List] containing all key-value pairs. diff --git a/libraries/stdlib/common/src/generated/_Ranges.kt b/libraries/stdlib/common/src/generated/_Ranges.kt index ecacb0021e7..190de27f7d5 100644 --- a/libraries/stdlib/common/src/generated/_Ranges.kt +++ b/libraries/stdlib/common/src/generated/_Ranges.kt @@ -16,6 +16,70 @@ package kotlin.ranges import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* + +/** + * Returns a random element from this range. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun IntRange.random(): Int { + return random(Random) +} + +/** + * Returns a random element from this range. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun LongRange.random(): Long { + return random(Random) +} + +/** + * Returns a random element from this range. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun CharRange.random(): Char { + return random(Random) +} + +/** + * Returns a random element from this range using the specified source of randomness. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +public fun IntRange.random(random: Random): Int { + return random.nextInt(this) +} + +/** + * Returns a random element from this range using the specified source of randomness. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +public fun LongRange.random(random: Random): Long { + return random.nextLong(this) +} + +/** + * Returns a random element from this range using the specified source of randomness. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +public fun CharRange.random(random: Random): Char { + return random.nextInt(first.toInt(), last.toInt() + 1).toChar() +} /** * Checks if the specified [value] belongs to this range. diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index 96d536d1f43..736337824c1 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -16,6 +16,7 @@ package kotlin.sequences import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* import kotlin.coroutines.experimental.* /** diff --git a/libraries/stdlib/common/src/generated/_Sets.kt b/libraries/stdlib/common/src/generated/_Sets.kt index 6ca289ed7ae..93ab64994b9 100644 --- a/libraries/stdlib/common/src/generated/_Sets.kt +++ b/libraries/stdlib/common/src/generated/_Sets.kt @@ -16,6 +16,7 @@ package kotlin.collections import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns a set containing all elements of the original set except the given [element]. diff --git a/libraries/stdlib/common/src/generated/_Strings.kt b/libraries/stdlib/common/src/generated/_Strings.kt index 8aa32fb2f1b..1cc56c9f7dd 100644 --- a/libraries/stdlib/common/src/generated/_Strings.kt +++ b/libraries/stdlib/common/src/generated/_Strings.kt @@ -16,6 +16,7 @@ package kotlin.text import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* /** * Returns a character at the given [index] or throws an [IndexOutOfBoundsException] if the [index] is out of bounds of this char sequence. @@ -170,6 +171,29 @@ public inline fun CharSequence.lastOrNull(predicate: (Char) -> Boolean): Char? { return null } +/** + * Returns a random character from this char sequence. + * + * @throws NoSuchElementException if this char sequence is empty. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun CharSequence.random(): Char { + return random(Random) +} + +/** + * Returns a random character from this char sequence using the specified source of randomness. + * + * @throws NoSuchElementException if this char sequence is empty. + */ +@SinceKotlin("1.3") +public fun CharSequence.random(random: Random): Char { + if (isEmpty()) + throw NoSuchElementException("Char sequence is empty.") + return get(random.nextInt(length)) +} + /** * Returns the single character, or throws an exception if the char sequence is empty or has more than one character. */ diff --git a/libraries/stdlib/common/src/generated/_UArrays.kt b/libraries/stdlib/common/src/generated/_UArrays.kt index 3afc5cfa879..c032443c2b7 100644 --- a/libraries/stdlib/common/src/generated/_UArrays.kt +++ b/libraries/stdlib/common/src/generated/_UArrays.kt @@ -16,6 +16,107 @@ package kotlin.collections import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UIntArray.random(): UInt { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULongArray.random(): ULong { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UByteArray.random(): UByte { + return random(Random) +} + +/** + * Returns a random element from this array. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UShortArray.random(): UShort { + return random(Random) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UIntArray.random(random: Random): UInt { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun ULongArray.random(random: Random): ULong { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UByteArray.random(random: Random): UByte { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} + +/** + * Returns a random element from this array using the specified source of randomness. + * + * @throws NoSuchElementException if this array is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UShortArray.random(random: Random): UShort { + if (isEmpty()) + throw NoSuchElementException("Array is empty.") + return get(random.nextInt(size)) +} /** * Returns an array of type [ByteArray], which is a view of this array where each element is a signed reinterpretation diff --git a/libraries/stdlib/common/src/generated/_URanges.kt b/libraries/stdlib/common/src/generated/_URanges.kt index 5f98ae80d9b..148c2ce5876 100644 --- a/libraries/stdlib/common/src/generated/_URanges.kt +++ b/libraries/stdlib/common/src/generated/_URanges.kt @@ -16,6 +16,53 @@ package kotlin.ranges import kotlin.* import kotlin.text.* import kotlin.comparisons.* +import kotlin.random.* + +/** + * Returns a random element from this range. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun UIntRange.random(): UInt { + return random(Random) +} + +/** + * Returns a random element from this range. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +@kotlin.internal.InlineOnly +public inline fun ULongRange.random(): ULong { + return random(Random) +} + +/** + * Returns a random element from this range using the specified source of randomness. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UIntRange.random(random: Random): UInt { + return random.nextUInt(this) +} + +/** + * Returns a random element from this range using the specified source of randomness. + * + * @throws IllegalArgumentException if this range is empty. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun ULongRange.random(random: Random): ULong { + return random.nextULong(this) +} /** * Returns a progression from this value down to the specified [to] value with the step -1. diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index aaf6484521a..aa86d657ea4 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -11,6 +11,7 @@ import test.collections.behaviors.* import test.comparisons.STRING_CASE_INSENSITIVE_ORDER import kotlin.test.* import kotlin.comparisons.* +import kotlin.random.Random fun assertArrayNotSameButEquals(expected: Array, actual: Array, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } fun assertArrayNotSameButEquals(expected: IntArray, actual: IntArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } @@ -592,6 +593,31 @@ class ArraysTest { expect(2) { arrayOf(1, 2, 3).last { it % 2 == 0 } } } + + @Test fun random() { + Array(100) { it }.let { array -> + val tosses = List(10) { array.random() } + assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses") + + val seed = Random.nextInt() + val random1 = Random(seed) + val random2 = Random(seed) + + val tosses1 = List(10) { array.random(random1) } + val tosses2 = List(10) { array.random(random2) } + + assertEquals(tosses1, tosses2) + } + + arrayOf("x").let { singletonArray -> + val tosses = List(10) { singletonArray.random() } + assertEquals(singletonArray.toList(), tosses.distinct()) + } + + assertFailsWith { emptyArray().random() } + } + + @Test fun contains() { assertTrue(arrayOf("1", "2", "3", "4").contains("2")) assertTrue("3" in arrayOf("1", "2", "3", "4")) diff --git a/libraries/stdlib/test/collections/CollectionTest.kt b/libraries/stdlib/test/collections/CollectionTest.kt index 748d7e876c5..abe91f09a99 100644 --- a/libraries/stdlib/test/collections/CollectionTest.kt +++ b/libraries/stdlib/test/collections/CollectionTest.kt @@ -10,6 +10,8 @@ import kotlin.test.* import test.collections.behaviors.* import test.comparisons.STRING_CASE_INSENSITIVE_ORDER import kotlin.comparisons.* +import kotlin.math.sin +import kotlin.random.Random class CollectionTest { @@ -620,6 +622,31 @@ class CollectionTest { assertFails { arrayListOf().last() } } + @Test fun random() { + val list = List(100) { it } + val set = list.toSet() + listOf(list, set).forEach { collection: Collection -> + val tosses = List(10) { collection.random() } + assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses") + + val seed = Random.nextInt() + val random1 = Random(seed) + val random2 = Random(seed) + + val tosses1 = List(10) { collection.random(random1) } + val tosses2 = List(10) { collection.random(random2) } + + assertEquals(tosses1, tosses2) + } + + listOf("x").let { singletonList -> + val tosses = List(10) { singletonList.random() } + assertEquals(singletonList, tosses.distinct()) + } + + assertFailsWith { emptyList().random() } + } + @Test fun subscript() { val list = arrayListOf("foo", "bar") assertEquals("foo", list[0]) diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index 861bf9c9b66..e4c467cf0f3 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -10,6 +10,7 @@ import test.* import test.collections.behaviors.iteratorBehavior import test.collections.compare import kotlin.math.sign +import kotlin.random.Random fun createString(content: String): CharSequence = content @@ -936,6 +937,29 @@ class StringTest { assertNull(data.filterNot { it.isAsciiLetter() || it.isAsciiDigit() }.firstOrNull()) } + @Test fun random() = withOneCharSequenceArg { data -> + data("abcdefg").let { charSeq -> + val tosses = List(10) { charSeq.random() } + assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses") + + val seed = Random.nextInt() + val random1 = Random(seed) + val random2 = Random(seed) + + val tosses1 = List(10) { charSeq.random(random1) } + val tosses2 = List(10) { charSeq.random(random2) } + + assertEquals(tosses1, tosses2) + } + + data("x").let { singletonCharSeq -> + val tosses = List(10) { singletonCharSeq.random() } + assertEquals(singletonCharSeq.toList(), tosses.distinct()) + } + + assertFailsWith { data("").random() } + } + @Test fun partition() { val data = "a1b2c3" val pair = data.partition { it.isAsciiDigit() } diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index cadbaf2901b..bc3cf160c61 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -1604,6 +1604,15 @@ public final class kotlin/collections/ArraysKt { public static final fun plus ([ZLjava/util/Collection;)[Z public static final fun plus ([ZZ)[Z public static final fun plus ([Z[Z)[Z + public static final fun random ([BLkotlin/random/Random;)B + public static final fun random ([CLkotlin/random/Random;)C + public static final fun random ([DLkotlin/random/Random;)D + public static final fun random ([FLkotlin/random/Random;)F + public static final fun random ([ILkotlin/random/Random;)I + public static final fun random ([JLkotlin/random/Random;)J + public static final fun random ([Ljava/lang/Object;Lkotlin/random/Random;)Ljava/lang/Object; + public static final fun random ([SLkotlin/random/Random;)S + public static final fun random ([ZLkotlin/random/Random;)Z public static final fun reduce ([BLkotlin/jvm/functions/Function2;)B public static final fun reduce ([CLkotlin/jvm/functions/Function2;)C public static final fun reduce ([DLkotlin/jvm/functions/Function2;)D @@ -2234,6 +2243,7 @@ public final class kotlin/collections/CollectionsKt { public static final fun plus (Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/List; public static final fun plus (Ljava/util/Collection;Lkotlin/sequences/Sequence;)Ljava/util/List; public static final fun plus (Ljava/util/Collection;[Ljava/lang/Object;)Ljava/util/List; + public static final fun random (Ljava/util/Collection;Lkotlin/random/Random;)Ljava/lang/Object; public static final fun reduce (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun reduceIndexed (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object; public static final fun reduceRight (Ljava/util/List;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; @@ -2493,6 +2503,10 @@ public final class kotlin/collections/UArraysKt { public static final fun contentToString-3o8tnvbt ([J)Ljava/lang/String; public static final fun contentToString-9gu3s6ig ([S)Ljava/lang/String; public static final fun contentToString-doljm8k0 ([I)Ljava/lang/String; + public static final fun random-25eqzsg0 ([JLkotlin/random/Random;)J + public static final fun random-8tx9e8n3 ([BLkotlin/random/Random;)B + public static final fun random-9uc5g3in ([SLkotlin/random/Random;)S + public static final fun random-bunzpqo3 ([ILkotlin/random/Random;)I public static final fun toTypedArray-1biuymyp ([B)[Lkotlin/UByte; public static final fun toTypedArray-3o8tnvbt ([J)[Lkotlin/ULong; public static final fun toTypedArray-9gu3s6ig ([S)[Lkotlin/UShort; @@ -4125,6 +4139,9 @@ public final class kotlin/ranges/RangesKt { public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;F)Z public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;I)Z public static final fun longRangeContains (Lkotlin/ranges/ClosedRange;S)Z + public static final fun random (Lkotlin/ranges/CharRange;Lkotlin/random/Random;)C + public static final fun random (Lkotlin/ranges/IntRange;Lkotlin/random/Random;)I + public static final fun random (Lkotlin/ranges/LongRange;Lkotlin/random/Random;)J public static final fun rangeTo (DD)Lkotlin/ranges/ClosedFloatingPointRange; public static final fun rangeTo (FF)Lkotlin/ranges/ClosedFloatingPointRange; public static final fun rangeTo (Ljava/lang/Comparable;Ljava/lang/Comparable;)Lkotlin/ranges/ClosedRange; @@ -4235,6 +4252,8 @@ public final class kotlin/ranges/URangesKt { public static final fun downTo-2ccbonl2 (BB)Lkotlin/ranges/UIntProgression; public static final fun downTo-6o7e3yo2 (JJ)Lkotlin/ranges/ULongProgression; public static final fun downTo-cjsx449s (SS)Lkotlin/ranges/UIntProgression; + public static final fun random (Lkotlin/ranges/UIntRange;Lkotlin/random/Random;)I + public static final fun random (Lkotlin/ranges/ULongRange;Lkotlin/random/Random;)J public static final fun reversed (Lkotlin/ranges/UIntProgression;)Lkotlin/ranges/UIntProgression; public static final fun reversed (Lkotlin/ranges/ULongProgression;)Lkotlin/ranges/ULongProgression; public static final fun step (Lkotlin/ranges/UIntProgression;I)Lkotlin/ranges/UIntProgression; @@ -4934,6 +4953,7 @@ public final class kotlin/text/StringsKt { public static final fun partition (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair; public static final fun prependIndent (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; public static synthetic fun prependIndent$default (Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String; + public static final fun random (Ljava/lang/CharSequence;Lkotlin/random/Random;)C public static final fun reduce (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)C public static final fun reduceIndexed (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function3;)C public static final fun reduceRight (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)C diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt index 7df89e5301f..ec2da831a38 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt @@ -13,6 +13,17 @@ object Elements : TemplateGroupBase() { init { defaultBuilder { sequenceClassification(terminal) + specialFor(ArraysOfUnsigned) { + since("1.3") + annotation("@ExperimentalUnsignedTypes") + } + specialFor(RangesOfPrimitives) { + if (primitive in PrimitiveType.unsignedPrimitives) { + since("1.3") + annotation("@ExperimentalUnsignedTypes") + sourceFile(SourceFile.URanges) + } + } } } @@ -808,6 +819,69 @@ object Elements : TemplateGroupBase() { } } + val f_random = fn("random()") { + include(Collections, ArraysOfObjects, ArraysOfPrimitives, ArraysOfUnsigned, CharSequences, RangesOfPrimitives) + } builder { + since("1.3") + inlineOnly() + returns("T") + doc { + """ + Returns a random ${f.element} from this ${f.collection}. + + @throws ${if (f == RangesOfPrimitives) "IllegalArgumentException" else "NoSuchElementException"} if this ${f.collection} is empty. + """ + } + body { + """return random(Random)""" + } + } + + val f_random_random = fn("random(random: Random)") { + include(Collections, ArraysOfObjects, ArraysOfPrimitives, ArraysOfUnsigned, CharSequences, RangesOfPrimitives) + } builder { + since("1.3") + returns("T") + doc { + """ + Returns a random ${f.element} from this ${f.collection} using the specified source of randomness. + + @throws ${if (f == RangesOfPrimitives) "IllegalArgumentException" else "NoSuchElementException"} if this ${f.collection} is empty. + """ + } + body { + """ + if (isEmpty()) + throw NoSuchElementException("${f.doc.collection.capitalize()} is empty.") + return elementAt(random.nextInt(size)) + """ + } + specialFor(ArraysOfObjects, ArraysOfPrimitives, ArraysOfUnsigned, CharSequences) { + body { + val size = if (family == CharSequences) "length" else "size" + """ + if (isEmpty()) + throw NoSuchElementException("${f.doc.collection.capitalize()} is empty.") + return get(random.nextInt($size)) + """ + } + } + specialFor(Maps) { + body { + """return entries.random(random)""" + } + } + specialFor(RangesOfPrimitives) { + body { + val expr = when (primitive) { + PrimitiveType.Char -> "nextInt(first.toInt(), last.toInt() + 1).toChar()" + else -> "next$primitive(this)" + } + """return random.$expr""" + } + } + } + val f_components = (1..5).map { n -> fn("component$n()") { include(Lists, ArraysOfObjects, ArraysOfPrimitives) diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt index 80c8816d3a5..36465f9b84c 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Ranges.kt @@ -10,7 +10,7 @@ import templates.PrimitiveType.Companion.maxByCapacity object RangeOps : TemplateGroupBase() { - private val rangePrimitives = setOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Char, PrimitiveType.UInt, PrimitiveType.ULong) + private val rangePrimitives = PrimitiveType.rangePrimitives private fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType) = maxByCapacity(fromType, toType).let { when { diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/CommonTypes.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/CommonTypes.kt index 94342fe7308..014f70e16e2 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/CommonTypes.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/CommonTypes.kt @@ -60,6 +60,7 @@ enum class PrimitiveType { val defaultPrimitives = PrimitiveType.values().toSet() - unsignedPrimitives val numericPrimitives = setOf(Int, Long, Byte, Short, Double, Float) val integralPrimitives = setOf(Int, Long, Byte, Short, Char) + val rangePrimitives = setOf(Int, Long, Char, UInt, ULong) val descendingByDomainCapacity = listOf(Double, Float, Long, Int, Short, Char, Byte) val descendingByDomainCapacityUnsigned = listOf(ULong, UInt, UShort, UByte) diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/FamilyProperties.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/FamilyProperties.kt index d19b8abecfd..4bdb0053ab8 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/FamilyProperties.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/FamilyProperties.kt @@ -32,8 +32,9 @@ object DocExtensions { val Family.collection: String get() = when (this) { CharSequences -> "char sequence" - ArraysOfObjects, ArraysOfPrimitives, InvariantArraysOfObjects -> "array" - Strings, Sequences, Maps, Lists, Sets, Ranges -> name.singularize().decapitalize() + ArraysOfObjects, ArraysOfPrimitives, InvariantArraysOfObjects, ArraysOfUnsigned -> "array" + Ranges, RangesOfPrimitives -> "range" + Strings, Sequences, Maps, Lists, Sets -> name.singularize().decapitalize() else -> "collection" } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt index 1cbb5d36c25..1042dbc449f 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt @@ -149,6 +149,7 @@ abstract class MemberTemplateDefinition : MemberTemplate { private fun defaultPrimitives(f: Family): Set = when { f == Family.Unsigned || f == Family.ArraysOfUnsigned -> PrimitiveType.unsignedPrimitives + f == Family.RangesOfPrimitives -> PrimitiveType.rangePrimitives f.isPrimitiveSpecialization -> PrimitiveType.defaultPrimitives else -> emptySet() } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Writers.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Writers.kt index 65c092d9041..c77e00a9333 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Writers.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Writers.kt @@ -92,6 +92,9 @@ fun List.writeTo(file: File, platformSource: PlatformSourceFile) writer.appendln("import kotlin.*") writer.appendln("import kotlin.text.*") writer.appendln("import kotlin.comparisons.*") + if (platform == Platform.Common) { + writer.appendln("import kotlin.random.*") + } if (sourceFile == SourceFile.Sequences) { writer.appendln("import kotlin.coroutines.experimental.*")