use Heapsort
parent
3911d62158
commit
f38a1a51ac
|
@ -3,10 +3,12 @@ package lila.activity
|
|||
import org.joda.time.{ DateTime, Interval }
|
||||
import reactivemongo.api.ReadPreference
|
||||
|
||||
import lila.common.Heapsort
|
||||
import lila.db.dsl._
|
||||
import lila.game.LightPov
|
||||
import lila.practice.PracticeStructure
|
||||
import lila.user.User
|
||||
import lila.tournament.LeaderboardApi
|
||||
|
||||
final class ActivityReadApi(
|
||||
coll: Coll,
|
||||
|
@ -95,7 +97,11 @@ final class ActivityReadApi(
|
|||
.dmap { entries =>
|
||||
entries.nonEmpty option ActivityView.Tours(
|
||||
nb = entries.size,
|
||||
best = entries.sortBy(_.rankRatio.value).take(activities.maxSubEntries)
|
||||
best = Heapsort.topN(
|
||||
entries,
|
||||
activities.maxSubEntries,
|
||||
Ordering.by[LeaderboardApi.Entry, Double](-_.rankRatio.value)
|
||||
)
|
||||
)
|
||||
}
|
||||
.mon(_.user segment "activity.tours")
|
||||
|
|
|
@ -2,8 +2,14 @@ package lila.common
|
|||
|
||||
import scala.collection.BuildFrom
|
||||
import scala.collection.mutable.{ Growable, PriorityQueue }
|
||||
import scala.math.Ordering
|
||||
|
||||
/*
|
||||
* Sorts elements in priority order: higher first, lower last.
|
||||
* It's the opposite of what scala .sort does.
|
||||
*/
|
||||
object Heapsort {
|
||||
|
||||
private[this] def moveN[T](p: PriorityQueue[T], g: Growable[T], n: Int): Unit = {
|
||||
//Only the dequeue and dequeueAll methods will return elements in priority order (while removing elements from the heap).
|
||||
var k = n
|
||||
|
@ -12,11 +18,12 @@ object Heapsort {
|
|||
k -= 1
|
||||
}
|
||||
}
|
||||
|
||||
/* selects maximum nb elements from n size collection
|
||||
* should be used for small nb and large n
|
||||
* Complexity: O(n + nb * log(n))
|
||||
*/
|
||||
def topN[T, C](xs: IterableOnce[T], nb: Int, ord: scala.math.Ordering[T])(implicit
|
||||
def topN[T, C](xs: IterableOnce[T], nb: Int, ord: Ordering[T])(implicit
|
||||
bf: BuildFrom[xs.type, T, C]
|
||||
): C = {
|
||||
val p = PriorityQueue.from(xs)(ord)
|
||||
|
@ -25,15 +32,23 @@ object Heapsort {
|
|||
moveN(p, b, nb)
|
||||
b.result()
|
||||
}
|
||||
def topNToList[T, C](xs: IterableOnce[T], nb: Int, ord: scala.math.Ordering[T]): List[T] = {
|
||||
|
||||
def topNToList[T, C](xs: IterableOnce[T], nb: Int, ord: Ordering[T]): List[T] = {
|
||||
val p = PriorityQueue.from(xs)(ord)
|
||||
val b = List.newBuilder[T]
|
||||
moveN(p, b, nb)
|
||||
b.result()
|
||||
}
|
||||
class ListOps[A](private val l: List[A]) extends AnyVal {
|
||||
def topN(nb: Int)(implicit ord: scala.math.Ordering[A]): List[A] =
|
||||
Heapsort.topNToList(l, nb, ord)
|
||||
|
||||
object implicits {
|
||||
|
||||
implicit final class ListOps[A](private val l: List[A]) extends AnyVal {
|
||||
|
||||
def topN(nb: Int)(implicit ord: Ordering[A]): List[A] =
|
||||
Heapsort.topNToList(l, nb, ord)
|
||||
|
||||
def botN(nb: Int)(implicit ord: Ordering[A]): List[A] =
|
||||
Heapsort.topNToList(l, nb, ord.reverse)
|
||||
}
|
||||
}
|
||||
implicit def toListOps[A](l: List[A]) = new ListOps(l)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.lobby
|
|||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import lila.common.Heapsort
|
||||
import lila.socket.Socket.Sri
|
||||
|
||||
private object HookRepo {
|
||||
|
@ -10,6 +11,8 @@ private object HookRepo {
|
|||
|
||||
private val hardLimit = 200
|
||||
|
||||
implicit private val creationOrdering = Ordering.by[Hook, Long](_.createdAt.getMillis)
|
||||
|
||||
def size = hooks.size
|
||||
|
||||
def findCompatible(hook: Hook): Vector[Hook] = hooks.filter(_ compatibleWith hook)
|
||||
|
@ -17,7 +20,7 @@ private object HookRepo {
|
|||
def truncateIfNeeded() =
|
||||
if (size >= hardLimit) {
|
||||
logger.warn(s"Found $size hooks, cleaning up!")
|
||||
hooks = hooks.sortBy(-_.createdAt.getMillis).take(hardLimit * 2 / 3)
|
||||
hooks = Heapsort.topN(hooks, hardLimit * 2 / 3, creationOrdering)
|
||||
logger.warn(s"Kept ${hooks.size} hooks")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package lila.perfStat
|
||||
|
||||
import org.joda.time.{ DateTime, Period }
|
||||
|
||||
import lila.common.Heapsort
|
||||
import lila.game.Pov
|
||||
import lila.rating.PerfType
|
||||
|
||||
import org.joda.time.{ DateTime, Period }
|
||||
|
||||
case class PerfStat(
|
||||
_id: String, // userId/perfId
|
||||
userId: UserId,
|
||||
|
@ -27,8 +28,8 @@ case class PerfStat(
|
|||
copy(
|
||||
highest = RatingAt.agg(highest, pov, 1),
|
||||
lowest = if (thisYear) RatingAt.agg(lowest, pov, -1) else lowest,
|
||||
bestWins = if (~pov.win) bestWins.agg(pov, -1) else bestWins,
|
||||
worstLosses = if (thisYear && ~pov.loss) worstLosses.agg(pov, 1) else worstLosses,
|
||||
bestWins = if (~pov.win) bestWins.agg(pov, 1) else bestWins,
|
||||
worstLosses = if (thisYear && ~pov.loss) worstLosses.agg(pov, -1) else worstLosses,
|
||||
count = count(pov),
|
||||
resultStreak = resultStreak agg pov,
|
||||
playStreak = playStreak agg pov
|
||||
|
@ -194,12 +195,16 @@ case class Results(results: List[Result]) extends AnyVal {
|
|||
.ifTrue(pov.game.bothPlayersHaveMoved)
|
||||
.fold(this) { opInt =>
|
||||
Results(
|
||||
(Result(
|
||||
opInt,
|
||||
UserId(~pov.opponent.userId),
|
||||
pov.game.movedAt,
|
||||
pov.gameId
|
||||
) :: results).sortBy(_.opInt * comp) take Results.nb
|
||||
Heapsort.topN(
|
||||
Result(
|
||||
opInt,
|
||||
UserId(~pov.opponent.userId),
|
||||
pov.game.movedAt,
|
||||
pov.gameId
|
||||
) :: results,
|
||||
Results.nb,
|
||||
Ordering.by[Result, Int](_.opInt * comp)
|
||||
)
|
||||
)
|
||||
}
|
||||
def userIds = results.map(_.opId)
|
||||
|
|
|
@ -5,13 +5,14 @@ import reactivemongo.api._
|
|||
import reactivemongo.api.bson._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import BSONHandlers._
|
||||
import lila.common.Bus
|
||||
import lila.common.Heapsort.implicits._
|
||||
import lila.db.dsl._
|
||||
import lila.db.paginator._
|
||||
import lila.hub.actorApi.timeline.{ Propagate, Follow => FollowUser }
|
||||
import lila.hub.actors
|
||||
import lila.memo.CacheApi._
|
||||
import lila.relation.BSONHandlers._
|
||||
import lila.user.User
|
||||
|
||||
final class RelationApi(
|
||||
|
@ -229,5 +230,5 @@ final class RelationApi(
|
|||
}
|
||||
|
||||
def searchFollowedBy(u: User, term: String, max: Int): Fu[List[User.ID]] =
|
||||
repo.followingLike(u.id, term) map { _.sorted take max }
|
||||
repo.followingLike(u.id, term) map { _ botN max } // alphabetical order
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.slack
|
|||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import lila.common.{ ApiVersion, EmailAddress, IpAddress, LightUser }
|
||||
import lila.common.{ ApiVersion, EmailAddress, Heapsort, IpAddress, LightUser }
|
||||
import lila.hub.actorApi.slack._
|
||||
import lila.user.User
|
||||
|
||||
|
@ -20,6 +20,8 @@ final class SlackApi(
|
|||
|
||||
private var buffer: Vector[ChargeEvent] = Vector.empty
|
||||
|
||||
implicit private val amountOrdering = Ordering.by[ChargeEvent, Int](_.amount)
|
||||
|
||||
def apply(event: ChargeEvent): Funit =
|
||||
if (event.amount < 10000) addToBuffer(event)
|
||||
else
|
||||
|
@ -30,7 +32,7 @@ final class SlackApi(
|
|||
private def addToBuffer(event: ChargeEvent): Funit = {
|
||||
buffer = buffer :+ event
|
||||
(buffer.head.date isBefore DateTime.now.minusHours(12)) ?? {
|
||||
val firsts = buffer.sortBy(-_.amount).take(10).map(_.username).map(userAt).mkString(", ")
|
||||
val firsts = Heapsort.topN(buffer, 10, amountOrdering).map(_.username).map(userAt).mkString(", ")
|
||||
val amountSum = buffer.map(_.amount).sum
|
||||
val patrons =
|
||||
if (firsts.lengthIs > 10) s"$firsts and, like, ${firsts.lengthIs - 10} others,"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package lila.tournament
|
||||
|
||||
import lila.common.Heapsort.implicits._
|
||||
import lila.user.User
|
||||
|
||||
case class Spotlight(
|
||||
|
@ -14,16 +15,13 @@ object Spotlight {
|
|||
|
||||
import Schedule.Freq._
|
||||
|
||||
implicit private val importanceOrdering = Ordering.by[Tournament, Int](_.schedule.??(_.freq.importance))
|
||||
|
||||
def select(tours: List[Tournament], user: Option[User], max: Int): List[Tournament] =
|
||||
user.fold(sort(tours) take max) { select(tours, _, max) }
|
||||
user.fold(tours topN max) { select(tours, _, max) }
|
||||
|
||||
def select(tours: List[Tournament], user: User, max: Int): List[Tournament] =
|
||||
sort(tours.filter { select(_, user) }) take max
|
||||
|
||||
private def sort(tours: List[Tournament]) =
|
||||
tours.sortBy { t =>
|
||||
-t.schedule.??(_.freq.importance)
|
||||
}
|
||||
tours.filter { select(_, user) } topN max
|
||||
|
||||
private def select(tour: Tournament, user: User): Boolean =
|
||||
!tour.isFinished &&
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package lila.user
|
||||
|
||||
import chess.Speed
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import chess.Speed
|
||||
import lila.common.Heapsort.implicits._
|
||||
import lila.db.BSON
|
||||
import lila.rating.{ Glicko, Perf, PerfType }
|
||||
|
||||
|
@ -59,12 +60,14 @@ case class Perfs(
|
|||
}
|
||||
}
|
||||
|
||||
implicit private val ratingOrdering = Ordering.by[(PerfType, Perf), Int](_._2.intRating)
|
||||
|
||||
def bestPerfs(nb: Int): List[(PerfType, Perf)] = {
|
||||
val ps = PerfType.nonPuzzle map { pt =>
|
||||
pt -> apply(pt)
|
||||
}
|
||||
val minNb = math.max(1, ps.foldLeft(0)(_ + _._2.nb) / 15)
|
||||
ps.filter(p => p._2.nb >= minNb).sortBy(-_._2.intRating) take nb
|
||||
ps.filter(p => p._2.nb >= minNb).topN(nb)
|
||||
}
|
||||
|
||||
def bestPerfType: Option[PerfType] = bestPerf.map(_._1)
|
||||
|
|
Loading…
Reference in New Issue