private static (int ix1, int ix2, int[] ranked) attemptRanking(RankRanking ranking, RankSet set) { var ix1 = -1; var ix2 = -1; var indexesToRank = Enumerable.Range(0, set.Items.Length).Except(ranking.Ignore).ToArray(); var ranked = indexesToRank.OrderBy((i1, i2) => { if (i1 == i2) { return(0); } if (ranking.Comparisons.Contains(new RankComparison(i1, i2))) { return(1); } if (ranking.Comparisons.Contains(new RankComparison(i2, i1))) { return(-1); } if (ix1 == -1) { ix1 = i1; ix2 = i2; } return(0); }).ToArray(); return(ix1, ix2, ranked); }
private HttpResponse processPost(HttpRequest req) { switch (req.Post["fnc"].Value) { case "create": var items = req.Post["items"].Value.Replace("\r", "").Trim().Split('\n').Select(line => line.Trim()).ToArray(); var hash = MD5.Create().ComputeHash(items.JoinString("\n").ToUtf8()).ToHex(); var newSet = new RankSet { Hash = hash, Items = items, Name = req.Post["title"].Value }; if (!_setsDic.ContainsKey(hash)) { lock (this) if (!_setsDic.ContainsKey(hash)) { var newSetSlim = newSet.ToSlim(); _setsDic[hash] = newSetSlim; _setsList.Add(newSetSlim); ClassifyJson.SerializeToFile(newSet, setPath(hash)); } } return(HttpResponse.Redirect(req.Url.WithQuery("set", hash))); case "start": lock (this) { var currentSetHash = req.Post["set"].Value; var setFilePath = setPath(currentSetHash); if (!File.Exists(setFilePath)) { return(HttpResponse.PlainText("That set does not exist.", HttpStatusCode._404_NotFound)); } var currentSet = ClassifyJson.DeserializeFile <RankSet>(setFilePath); retry: var publicToken = Rnd.GenerateString(32); var privateToken = Rnd.GenerateString(32); var path = rankingPath(publicToken); if (File.Exists(path) || currentSet.DicRankings.ContainsKey(publicToken)) { goto retry; } var newRanking = new RankRanking { Finished = false, PublicToken = publicToken, PrivateToken = privateToken, SetHash = currentSetHash, Title = req.Post["title"].Value, Question = req.Post["question"].Value }; var newRankingSlim = newRanking.ToSlim(); currentSet.DicRankings[publicToken] = newRankingSlim; currentSet.ListRankings.Add(newRankingSlim); ClassifyJson.SerializeToFile(newRanking, path); ClassifyJson.SerializeToFile(currentSet, setFilePath); return(HttpResponse.Redirect(req.Url.WithoutQuery().WithQuery("ranking", publicToken).WithQuery("secret", privateToken))); } case "rank": lock (this) { var publicToken = req.Post["ranking"].Value; var privateToken = req.Post["secret"].Value; var rankingFilePath = rankingPath(publicToken); if (!File.Exists(rankingFilePath)) { return(HttpResponse.PlainText("That ranking does not exist.", HttpStatusCode._404_NotFound)); } var currentRanking = ClassifyJson.DeserializeFile <RankRanking>(rankingFilePath); if (privateToken != currentRanking.PrivateToken) { return(HttpResponse.PlainText("You cannot vote in this ranking.", HttpStatusCode._404_NotFound)); } var setFilePath = setPath(currentRanking.SetHash); if (!File.Exists(setFilePath)) { return(HttpResponse.PlainText("That set does not exist.", HttpStatusCode._404_NotFound)); } var currentSet = ClassifyJson.DeserializeFile <RankSet>(setFilePath); if (!int.TryParse(req.Post["ix1"].Value, out var ix1) || !int.TryParse(req.Post["ix2"].Value, out var ix2) || !int.TryParse(req.Post["more"].Value, out var more) || (more != ix1 && more != ix2)) { return(HttpResponse.PlainText("Invalid integers.", HttpStatusCode._404_NotFound)); } var newComparison = new RankComparison(more == ix1 ? ix2 : ix1, more == ix1 ? ix1 : ix2); // Transitive closure var ancestorLesses = currentRanking.Comparisons.Where(c => c.More == newComparison.Less).Select(c => c.Less).ToList(); ancestorLesses.Add(newComparison.Less); var descendantMores = currentRanking.Comparisons.Where(c => c.Less == newComparison.More).Select(c => c.More).ToList(); descendantMores.Add(newComparison.More); for (int i = 0; i < ancestorLesses.Count; i++) { for (int j = 0; j < descendantMores.Count; j++) { currentRanking.Comparisons.Add(new RankComparison(ancestorLesses[i], descendantMores[j])); } } var result = attemptRanking(currentRanking, currentSet); if (result.ix1 == -1) { currentRanking.Finished = true; // This relies on reference equality, i.e. that currentSet.ListRankings contains the same object currentSet.DicRankings[publicToken].Finished = true; ClassifyJson.SerializeToFile(currentSet, setFilePath); } ClassifyJson.SerializeToFile(currentRanking, rankingFilePath); return(HttpResponse.Redirect(req.Url.WithoutQuery().WithQuery("ranking", publicToken).WithQuery("secret", privateToken))); } } return(HttpResponse.PlainText("What?", HttpStatusCode._500_InternalServerError)); }