46 lines
1.4 KiB
Scala
46 lines
1.4 KiB
Scala
package lila.common
|
|
|
|
import scala.Numeric.Implicits._
|
|
import scala.reflect.ClassTag
|
|
import scala.util.Sorting
|
|
|
|
object Maths {
|
|
|
|
def mean[T](a: Iterable[T])(implicit n: Numeric[T]): Option[Double] =
|
|
a.nonEmpty option (n.toDouble(a.sum) / a.size)
|
|
|
|
def median[T: ClassTag](a: Iterable[T])(implicit n: Numeric[T]) =
|
|
a.nonEmpty option {
|
|
val arr = a.toArray
|
|
Sorting.stableSort(arr)
|
|
val size = arr.length
|
|
val mid = size / 2
|
|
if (size % 2 == 0) n.toDouble(arr(mid) + arr(mid - 1)) / 2
|
|
else n.toDouble(arr(mid))
|
|
}
|
|
|
|
def roundAt(n: Double, p: Int): BigDecimal = {
|
|
BigDecimal(n).setScale(p, BigDecimal.RoundingMode.HALF_UP)
|
|
}
|
|
|
|
def roundDownAt(n: Double, p: Int): BigDecimal = {
|
|
BigDecimal(n).setScale(p, BigDecimal.RoundingMode.DOWN)
|
|
}
|
|
|
|
def closestMultipleOf(mult: Int, v: Int): Int =
|
|
((2 * v + mult) / (2 * mult)) * mult
|
|
|
|
/* Moderates distribution with a factor,
|
|
* and retries when value is outside the mean+deviation box.
|
|
* Factor is at most 1 to prevent too many retries
|
|
* Factor=1 => 30% retry
|
|
* Factor=0.3 => 0.1% retry
|
|
*/
|
|
@scala.annotation.tailrec
|
|
def boxedNormalDistribution(mean: Int, deviation: Int, factor: Double): Int = {
|
|
val normal = mean + deviation * ThreadLocalRandom.nextGaussian() * factor.atMost(1)
|
|
if (normal > mean - deviation && normal < mean + deviation) normal.toInt
|
|
else boxedNormalDistribution(mean, deviation, factor)
|
|
}
|
|
}
|