And now, proper random decimals implementation
This commit is contained in:
parent
c965bcdc82
commit
1087e755ff
@ -4,9 +4,10 @@ import it.unimi.dsi.fastutil.ints.IntList
|
|||||||
import net.minecraft.Util
|
import net.minecraft.Util
|
||||||
import net.minecraft.util.RandomSource
|
import net.minecraft.util.RandomSource
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.NoSuchElementException
|
import kotlin.experimental.and
|
||||||
import kotlin.math.ln
|
import kotlin.math.ln
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@ -103,14 +104,116 @@ fun <T> List<T>.random(random: RandomSource): T {
|
|||||||
return get(random.nextInt(size))
|
return get(random.nextInt(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RandomSource.nextDecimal(min: Decimal, max: Decimal, round: Boolean = false): Decimal {
|
class RandomByteSource(private val source: RandomSource) {
|
||||||
val value = nextDouble()
|
private val bytes = ByteArray(8)
|
||||||
|
private var i = 7
|
||||||
|
|
||||||
return if (round) {
|
fun next(): Byte {
|
||||||
Decimal((min + (max - min) * value).whole)
|
if (++i == 8) {
|
||||||
} else {
|
i = 0
|
||||||
min + (max - min) * value
|
|
||||||
|
var generate = source.nextLong()
|
||||||
|
|
||||||
|
for (i in 0 .. 7) {
|
||||||
|
bytes[i] = generate.toByte()
|
||||||
|
generate = generate ushr 8
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun next(bound: Int): Byte {
|
||||||
|
require(bound > 0) { "Bound must be positive" }
|
||||||
|
val m = bound - 1
|
||||||
|
var r = next().toInt().and(0xFF)
|
||||||
|
|
||||||
|
if (bound and m == 0) {
|
||||||
|
r = r and m
|
||||||
|
} else {
|
||||||
|
var u = r ushr 1
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
r = u % bound
|
||||||
|
|
||||||
|
if (u + m - r < 0) {
|
||||||
|
u = next().toInt().and(0xFF).ushr(1)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun next(bytes: ByteArray) {
|
||||||
|
for (i in bytes.indices) {
|
||||||
|
bytes[i] = next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uniformely distributed [Decimal] value on [0,[bound]) range
|
||||||
|
*
|
||||||
|
* If [round] is `true`, will only return integers
|
||||||
|
*/
|
||||||
|
fun RandomSource.nextDecimal(bound: Decimal, round: Boolean = false): Decimal {
|
||||||
|
if (round)
|
||||||
|
require(bound > Decimal.ZERO) { "Bound must be positive, $bound given" }
|
||||||
|
else
|
||||||
|
require(bound >= Decimal.ONE) { "Bound must be 1 or bigger, $bound given" }
|
||||||
|
|
||||||
|
require(bound.isFinite) { "Bound must be finite" }
|
||||||
|
|
||||||
|
val bytes = RandomByteSource(this)
|
||||||
|
|
||||||
|
if (round) {
|
||||||
|
val thisBytes = bound.whole.toByteArray()
|
||||||
|
val generateBytes = ByteArray(thisBytes.size)
|
||||||
|
bytes.next(generateBytes)
|
||||||
|
|
||||||
|
generateBytes[0] = generateBytes[0].and(127)
|
||||||
|
|
||||||
|
if (generateBytes[0] >= thisBytes[0]) {
|
||||||
|
generateBytes[0] = bytes.next(thisBytes[0].toInt().and(127))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decimal(BigInteger(generateBytes))
|
||||||
|
} else {
|
||||||
|
val thisBytes = bound.mag.toByteArray()
|
||||||
|
val generateBytes = ByteArray(thisBytes.size)
|
||||||
|
bytes.next(generateBytes)
|
||||||
|
|
||||||
|
generateBytes[0] = generateBytes[0].and(127)
|
||||||
|
|
||||||
|
if (generateBytes[0] >= thisBytes[0]) {
|
||||||
|
generateBytes[0] = bytes.next(thisBytes[0].toInt().and(127))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decimal.raw(BigInteger(generateBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uniformely distributed [Decimal] value on [[origin],[bound]) range
|
||||||
|
*
|
||||||
|
* If [round] is `true`, will only return integers
|
||||||
|
*/
|
||||||
|
fun RandomSource.nextDecimal(origin: Decimal, bound: Decimal, round: Boolean = false): Decimal {
|
||||||
|
require(origin < bound) { "Origin must be less than bound: $origin < $bound" }
|
||||||
|
require(origin.isFinite) { "Origin must be finite" }
|
||||||
|
require(bound.isFinite) { "Bound must be finite" }
|
||||||
|
|
||||||
|
return origin + nextDecimal(bound - origin, round)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uniformely distributed [Decimal] value on [0,1) range
|
||||||
|
*/
|
||||||
|
fun RandomSource.nextDecimal(): Decimal {
|
||||||
|
return nextDecimal(Decimal.ZERO, Decimal.ONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RandomSource.nextVariance(value: Decimal, round: Boolean = false): Decimal {
|
fun RandomSource.nextVariance(value: Decimal, round: Boolean = false): Decimal {
|
||||||
|
@ -39,6 +39,8 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
|||||||
*/
|
*/
|
||||||
abstract val fractional: BigInteger
|
abstract val fractional: BigInteger
|
||||||
|
|
||||||
|
abstract internal val mag: BigInteger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* *Signed* normalized (-1,1) fractional part of this Decimal, as [Float]
|
* *Signed* normalized (-1,1) fractional part of this Decimal, as [Float]
|
||||||
*/
|
*/
|
||||||
@ -181,7 +183,7 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
|||||||
return toBigDecmial().divide(divisor.toBigDecmial(), PERCENTAGE_CONTEXT).toFloat()
|
return toBigDecmial().divide(divisor.toBigDecmial(), PERCENTAGE_CONTEXT).toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Regular(val mag: BigInteger, marker: Nothing?) : Decimal() {
|
private class Regular(override val mag: BigInteger, marker: Nothing?) : Decimal() {
|
||||||
constructor(value: BigInteger) : this(value * PRECISION_POW_BI, null)
|
constructor(value: BigInteger) : this(value * PRECISION_POW_BI, null)
|
||||||
constructor(value: BigDecimal) : this(value.setScale(PRECISION, RoundingMode.HALF_UP).unscaledValue(), null)
|
constructor(value: BigDecimal) : this(value.setScale(PRECISION, RoundingMode.HALF_UP).unscaledValue(), null)
|
||||||
constructor(value: Float) : this(BigDecimal.valueOf(value.toDouble()))
|
constructor(value: Float) : this(BigDecimal.valueOf(value.toDouble()))
|
||||||
@ -703,6 +705,9 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object PositiveInfinity : Decimal() {
|
private object PositiveInfinity : Decimal() {
|
||||||
|
override val mag: BigInteger
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
private fun readResolve(): Any = PositiveInfinity
|
private fun readResolve(): Any = PositiveInfinity
|
||||||
|
|
||||||
override val isInfinite: Boolean
|
override val isInfinite: Boolean
|
||||||
@ -952,6 +957,9 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object NegativeInfinity : Decimal() {
|
private object NegativeInfinity : Decimal() {
|
||||||
|
override val mag: BigInteger
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
private fun readResolve(): Any = NegativeInfinity
|
private fun readResolve(): Any = NegativeInfinity
|
||||||
|
|
||||||
override val isInfinite: Boolean
|
override val isInfinite: Boolean
|
||||||
@ -1195,6 +1203,9 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object Zero : Decimal() {
|
private object Zero : Decimal() {
|
||||||
|
override val mag: BigInteger
|
||||||
|
get() = BigInteger.ZERO
|
||||||
|
|
||||||
private fun readResolve(): Any = Zero
|
private fun readResolve(): Any = Zero
|
||||||
|
|
||||||
override val isInfinite: Boolean
|
override val isInfinite: Boolean
|
||||||
|
Loading…
Reference in New Issue
Block a user