Skip to content

Commit d2e5542

Browse files
committed
Work around KT-77150
1 parent dc35615 commit d2e5542

File tree

3 files changed

+40
-66
lines changed

3 files changed

+40
-66
lines changed

core/common/src/LocalDateRange.kt

+6-33
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55

66
package kotlinx.datetime
77

8-
import kotlinx.datetime.internal.clampToInt
9-
import kotlinx.datetime.internal.safeAdd
10-
import kotlinx.datetime.internal.safeMultiplyOrClamp
8+
import kotlinx.datetime.internal.*
119
import kotlin.random.Random
12-
import kotlin.random.nextLong
1310

1411
private class LocalDateProgressionIterator(private val iterator: LongIterator) : Iterator<LocalDate> {
1512
override fun hasNext(): Boolean = iterator.hasNext()
@@ -67,7 +64,7 @@ internal constructor(internal val longProgression: LongProgression) : Collection
6764
* Returns [Int.MAX_VALUE] if the number of dates overflows [Int]
6865
*/
6966
override val size: Int
70-
get() = longProgression.size
67+
get() = longProgression.sizeUnsafe
7168

7269
/**
7370
* Returns true iff every element in [elements] is a member of the progression.
@@ -82,7 +79,7 @@ internal constructor(internal val longProgression: LongProgression) : Collection
8279
@Suppress("USELESS_CAST")
8380
if ((value as Any?) !is LocalDate) return false
8481

85-
return longProgression.contains(value.toEpochDays())
82+
return longProgression.containsUnsafe(value.toEpochDays())
8683
}
8784

8885
override fun equals(other: Any?): Boolean =
@@ -267,7 +264,7 @@ public infix fun LocalDate.downTo(that: LocalDate): LocalDateProgression =
267264
*/
268265
public fun LocalDateProgression.random(random: Random = Random): LocalDate =
269266
if (isEmpty()) throw NoSuchElementException("Cannot get random in empty range: $this")
270-
else longProgression.random(random).let(LocalDate.Companion::fromEpochDays)
267+
else longProgression.randomUnsafe(random).let(LocalDate.Companion::fromEpochDays)
271268

272269
/**
273270
* Returns a random [LocalDate] within the bounds of the [LocalDateProgression] or null if the progression is empty.
@@ -277,29 +274,5 @@ public fun LocalDateProgression.random(random: Random = Random): LocalDate =
277274
*
278275
* @sample kotlinx.datetime.test.samples.LocalDateRangeSamples.random
279276
*/
280-
public fun LocalDateProgression.randomOrNull(random: Random = Random): LocalDate? = longProgression.randomOrNull(random)
281-
?.let(LocalDate.Companion::fromEpochDays)
282-
283-
// this implementation is incorrect in general
284-
// (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).random()` throws an exception),
285-
// but for the range of epoch days in LocalDate it's good enough
286-
private fun LongProgression.random(random: Random = Random): Long =
287-
random.nextLong(0L..(last - first) / step) * step + first
288-
289-
// incorrect in general; see `random` just above
290-
private fun LongProgression.randomOrNull(random: Random = Random): Long? = if (isEmpty()) null else random(random)
291-
292-
// this implementation is incorrect in general (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).step(5).contains(2)`
293-
// returns `false` incorrectly https://www.wolframalpha.com/input?i=-2%5E63+%2B+1844674407370955162+*+5),
294-
// but for the range of epoch days in LocalDate it's good enough
295-
private fun LongProgression.contains(value: Long): Boolean =
296-
value in (if (step > 0) first..last else last..first) && (value - first) % step == 0L
297-
298-
// this implementation is incorrect in general (for example, `Long.MIN_VALUE..Long.MAX_VALUE` has size == 0),
299-
// but for the range of epoch days in LocalDate it's good enough
300-
private val LongProgression.size: Int
301-
get() = if (isEmpty()) 0 else try {
302-
(safeAdd(last, -first) / step + 1).clampToInt()
303-
} catch (e: ArithmeticException) {
304-
Int.MAX_VALUE
305-
}
277+
public fun LocalDateProgression.randomOrNull(random: Random = Random): LocalDate? = longProgression.randomUnsafeOrNull(random)
278+
?.let(LocalDate.Companion::fromEpochDays)

core/common/src/YearMonthRange.kt

+6-33
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55

66
package kotlinx.datetime
77

8-
import kotlinx.datetime.internal.clampToInt
9-
import kotlinx.datetime.internal.safeAdd
10-
import kotlinx.datetime.internal.safeMultiplyOrClamp
8+
import kotlinx.datetime.internal.*
119
import kotlin.random.Random
12-
import kotlin.random.nextLong
1310

1411
private class YearMonthProgressionIterator(private val iterator: LongIterator) : Iterator<YearMonth> {
1512
override fun hasNext(): Boolean = iterator.hasNext()
@@ -67,7 +64,7 @@ internal constructor(internal val longProgression: LongProgression) : Collection
6764
* Returns [Int.MAX_VALUE] if the number of months overflows [Int]
6865
*/
6966
override val size: Int
70-
get() = longProgression.size
67+
get() = longProgression.sizeUnsafe
7168

7269
/**
7370
* Returns true iff every element in [elements] is a member of the progression.
@@ -82,7 +79,7 @@ internal constructor(internal val longProgression: LongProgression) : Collection
8279
@Suppress("USELESS_CAST")
8380
if ((value as Any?) !is YearMonth) return false
8481

85-
return longProgression.contains(value.prolepticMonth)
82+
return longProgression.containsUnsafe(value.prolepticMonth)
8683
}
8784

8885
override fun equals(other: Any?): Boolean =
@@ -267,7 +264,7 @@ public infix fun YearMonth.downTo(that: YearMonth): YearMonthProgression =
267264
*/
268265
public fun YearMonthProgression.random(random: Random = Random): YearMonth =
269266
if (isEmpty()) throw NoSuchElementException("Cannot get random in empty range: $this")
270-
else longProgression.random(random).let(YearMonth.Companion::fromProlepticMonth)
267+
else longProgression.randomUnsafe(random).let(YearMonth.Companion::fromProlepticMonth)
271268

272269
/**
273270
* Returns a random [YearMonth] within the bounds of the [YearMonthProgression] or null if the progression is empty.
@@ -277,29 +274,5 @@ public fun YearMonthProgression.random(random: Random = Random): YearMonth =
277274
*
278275
* @sample kotlinx.datetime.test.samples.YearMonthRangeSamples.random
279276
*/
280-
public fun YearMonthProgression.randomOrNull(random: Random = Random): YearMonth? = longProgression.randomOrNull(random)
281-
?.let(YearMonth.Companion::fromProlepticMonth)
282-
283-
// this implementation is incorrect in general
284-
// (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).random()` throws an exception),
285-
// but for the range of epoch days in YearMonth it's good enough
286-
private fun LongProgression.random(random: Random = Random): Long =
287-
random.nextLong(0L..(last - first) / step) * step + first
288-
289-
// incorrect in general; see `random` just above
290-
private fun LongProgression.randomOrNull(random: Random = Random): Long? = if (isEmpty()) null else random(random)
291-
292-
// this implementation is incorrect in general (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).step(5).contains(2)`
293-
// returns `false` incorrectly https://www.wolframalpha.com/input?i=-2%5E63+%2B+1844674407370955162+*+5),
294-
// but for the range of epoch days in YearMonth it's good enough
295-
private fun LongProgression.contains(value: Long): Boolean =
296-
value in (if (step > 0) first..last else last..first) && (value - first) % step == 0L
297-
298-
// this implementation is incorrect in general (for example, `Long.MIN_VALUE..Long.MAX_VALUE` has size == 0),
299-
// but for the range of epoch days in YearMonth it's good enough
300-
private val LongProgression.size: Int
301-
get() = if (isEmpty()) 0 else try {
302-
(safeAdd(last, -first) / step + 1).clampToInt()
303-
} catch (e: ArithmeticException) {
304-
Int.MAX_VALUE
305-
}
277+
public fun YearMonthProgression.randomOrNull(random: Random = Random): YearMonth? = longProgression.randomUnsafeOrNull(random)
278+
?.let(YearMonth.Companion::fromProlepticMonth)

core/common/src/internal/math.kt

+28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
package kotlinx.datetime.internal
77

8+
import kotlin.random.Random
9+
import kotlin.random.nextLong
10+
811
internal fun Long.clampToInt(): Int =
912
when {
1013
this > Int.MAX_VALUE -> Int.MAX_VALUE
@@ -257,3 +260,28 @@ internal class DecimalFraction(
257260
throw UnsupportedOperationException("DecimalFraction is not supposed to be used as a hash key")
258261
}
259262
}
263+
264+
// this implementation is incorrect in general
265+
// (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).random()` throws an exception),
266+
// but for the range of epoch days in LocalDate it's good enough
267+
internal fun LongProgression.randomUnsafe(random: Random = Random): Long =
268+
random.nextLong(0L..(last - first) / step) * step + first
269+
270+
// incorrect in general; see `randomUnsafe` just above
271+
internal fun LongProgression.randomUnsafeOrNull(random: Random = Random): Long? =
272+
if (isEmpty()) null else randomUnsafe(random)
273+
274+
// this implementation is incorrect in general (for example, `(Long.MIN_VALUE..Long.MAX_VALUE).step(5).contains(2)`
275+
// returns `false` incorrectly https://www.wolframalpha.com/input?i=-2%5E63+%2B+1844674407370955162+*+5),
276+
// but for the range of epoch days in LocalDate it's good enough
277+
internal fun LongProgression.containsUnsafe(value: Long): Boolean =
278+
value in (if (step > 0) first..last else last..first) && (value - first) % step == 0L
279+
280+
// this implementation is incorrect in general (for example, `Long.MIN_VALUE..Long.MAX_VALUE` has size == 0),
281+
// but for the range of epoch days in LocalDate it's good enough
282+
internal val LongProgression.sizeUnsafe: Int
283+
get() = if (isEmpty()) 0 else try {
284+
(safeAdd(last, -first) / step + 1).clampToInt()
285+
} catch (e: ArithmeticException) {
286+
Int.MAX_VALUE
287+
}

0 commit comments

Comments
 (0)