lila/modules/common/src/main/Maths.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)
}
}