+-- Efficient data structure for normalizing arbitrary scores attached to objects
+-- e.g. {{"a", 3.14}, {"b", 3.14}, {"c", 20}, {"d", 0}}
+-- -> {["d"] = 0, ["a"] = 0.5, ["b"] = 0.5, ["c"] = 1}
+local Normalizer = {}
+
+function Normalizer:new()
+ local t = {
+ map = {}
+ }
+ setmetatable(t, self)
+ self.__index = self
+ return t
+end
+
+function Normalizer:push(obj, score)
+ if not self.map[score] then
+ self.map[score] = {}
+ end
+ local t = self.map[score]
+ t[#t + 1] = obj
+end
+
+function Normalizer:calc()
+ local list = {}
+ for k, _ in pairs(self.map) do
+ list[#list + 1] = k
+ end
+ table.sort(list)
+ local ret = {}
+ for i, k in ipairs(list) do
+ local score = #list == 1 and 1 or ( (i - 1) / (#list - 1) )
+ for _, obj in ipairs(self.map[k]) do
+ ret[obj] = score
+ end
+ end
+ return ret
+end
+
+--------------------------------------------------------------------------------
+-- how much the pre-sorted server list contributes to the final ranking
+local WEIGHT_SORT = 2
+-- how much the estimated latency contributes to the final ranking
+local WEIGHT_LATENCY = 1
+