// A simple count of who has the most votes.
        /// <inheritdoc/>
        protected override void CountBallot(CountedBallot ballot)
        {
            // Only counts ballots for hopeful and elected candidates
            Dictionary <Candidate, CandidateState> candidates
                = candidateStates
                  .Where(x => new[] { CandidateState.States.hopeful, CandidateState.States.elected }
                         .Contains(x.Value.State))
                  .ToDictionary(x => x.Key, x => x.Value);

            Vote vote = null;

            foreach (Vote v in ballot.Votes)
            {
                // Skip candidates not included in this count.
                if (!candidates.Keys.Contains(v.Candidate))
                {
                    continue;
                }
                // First vote examined or it beats current
                if (vote is null || v.Beats(vote))
                {
                    vote = v;
                }
            }
            if (!(vote is null))
            {
                candidateStates[vote.Candidate].VoteCount += ballot.Count;
            }
예제 #2
0
        /// <inheritdoc/>
        protected override void CountBallot(CountedBallot ballot)
        {
            decimal     weight = 1.0m;
            List <Vote> votes  = ballot.Votes.ToList();
            // Ensure any newly-seen candidates are counted
            List <Candidate> c = ballot.Votes.Where(x => !candidateStates.Keys.Contains(x.Candidate))
                                 .Select(x => x.Candidate).ToList();

            InitializeCandidateStates(c);

            votes.Sort();
            foreach (Vote v in votes)
            {
                MeekCandidateState cs = candidateStates[v.Candidate] as MeekCandidateState;
                if (cs is null)
                {
                    throw new InvalidOperationException("candidateState contains objects that aren't of type MeekCandidateState");
                }
                // Get value to transfer to this candidate, restricted to
                // the specified precision
                decimal value = weight * cs.KeepFactor;
                weight = RoundUp(weight);

                // Add this to the candidate's vote and remove from ballot's
                // weight.  CountedBallot shows multiple identical ballots, so
                // we add that many ballots to the vote and decrease the weight
                // of all identical ballots by the value kept.
                cs.VoteCount += value * ballot.Count;
                weight       -= value;

                // Do this until weight hits zero, or we run out of rankings.
                // Note:  Already-elected candidates have keep factors between
                // 0 and 1; hopefuls all have 1; defeated will have 0.  The
                // remaining voting power is either all transfered to the first
                // hopeful candidate or exhausted as the ballot runs out of
                // non-defeated candidates.
                //
                // We only hit 0 if a non-elected hopeful is on the ballot.
                if (weight <= 0.0m)
                {
                    break;
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Condenses an enumerable of Ballots into a BallotSet. Combines duplicate
        /// Ballots and CountedBallots into single CountedBallots.
        /// </summary>
        /// <param name="ballots">The Ballots to return as a set.</param>
        /// <returns>A BallotSet with any duplicate Ballots combined into CountedBallots.</returns>
        public BallotSet CreateBallotSet(IEnumerable <Ballot> ballots)
        {
            HashSet <Ballot>          outBallots   = new HashSet <Ballot>();
            Dictionary <Ballot, long> ballotCounts = new Dictionary <Ballot, long>();

            int threadCount = Environment.ProcessorCount;

            // We need to create and count each single, identical ballot,
            // and count the number of such ballots in any CountedBallot we
            // encounter.  To do this, we create uncounted, single ballots
            // and specifically avoid looking up CountedBallot.
            void CountBallots()
            {
                List <Ballot> bList = ballots.ToList();

                Dictionary <Ballot, long>[] subsets = new Dictionary <Ballot, long> [threadCount];

                // Thread safety:  only writes to function-local objects;
                // reads from an index in bList.
                Dictionary <Ballot, long> CountSubsets(int start, int end)
                {
                    Dictionary <Ballot, long> bC = new Dictionary <Ballot, long>();

                    for (int i = start; i <= end; i++)
                    {
                        Ballot oneBallot = CreateBallot(bList[i].Votes);
                        long   count     = (bList[i] is CountedBallot) ? (bList[i] as CountedBallot).Count : 1;

                        if (!bC.ContainsKey(oneBallot))
                        {
                            bC[oneBallot] = 0;
                        }
                        bC[oneBallot] += count;
                    }

                    return(bC);
                }

                // First divide all the processes up for background run
                // Thread safety:  subsets is an array not accessed outside this loop;
                // each parallel thread accesses a specific unique index in the array.
                Parallel.For(0, threadCount, i =>
                {
                    subsets[i] = CountSubsets(bList.Count() * i / threadCount, (bList.Count() * (i + 1) / threadCount) - 1);
                });

                // Now merge them
                for (int i = 0; i < threadCount; i++)
                {
                    foreach (Ballot b in subsets[i].Keys)
                    {
                        // Count this in the full ballot counts
                        if (!ballotCounts.ContainsKey(b))
                        {
                            ballotCounts[b] = 0;
                        }
                        ballotCounts[b] += subsets[i][b];

                        // Check for identical ballots found in each further thread
                        for (int j = i + 1; j < threadCount; j++)
                        {
                            if (subsets[j].ContainsKey(b))
                            {
                                ballotCounts[b] += subsets[j][b];
                                // It's been counted, so remove it
                                subsets[j].Remove(b);
                            }
                        }
                    }
                    // We've counted all these, so clear them.
                    subsets[i].Clear();
                }
            }

            // Count in a threaded model
            CountBallots();

            // Generate CountedBallots from the counts made
            foreach (Ballot b in ballotCounts.Keys)
            {
                Ballot newBallot;
                if (ballotCounts[b] == 1)
                {
                    newBallot = b;
                }
                else
                {
                    newBallot = new CountedBallot(b, ballotCounts[b]);
                }
                // Look itself up to store or deduplicate
                newBallot = Ballots[newBallot];
                outBallots.Add(newBallot);
            }

            return(new BallotSet(outBallots));
        }
예제 #4
0
 /// <summary>
 /// Count ballot and update candidateState.
 /// </summary>
 /// <param name="ballot">The ballot to count.</param>
 protected abstract void CountBallot(CountedBallot ballot);
예제 #5
0
        /// <summary>
        /// Converts a set of candidates and ballots to a graph of wins and ties.
        /// </summary>
        /// <param name="ballots">Ranked ballots in the election.</param>
        public PairwiseGraph(BallotSet ballots)
        {
            // Initialize candidate graph
            HashSet <Candidate> candidates = (ballots as IEnumerable <CountedBallot>).SelectMany(x => x.Votes.Select(y => y.Candidate)).ToHashSet();

            foreach (Candidate c in candidates)
            {
                nodes[c] = new Dictionary <Candidate, decimal>();
                foreach (Candidate d in candidates.Except(new[] { c }))
                {
                    nodes[c][d] = 0.0m;
                }
            }

            void BuildGraph()
            {
                List <CountedBallot> bList = ballots.ToList();

                int threadCount = Environment.ProcessorCount;

                PairwiseGraph[] subsets = new PairwiseGraph[threadCount];

                PairwiseGraph CountSubsets(int start, int end)
                {
                    PairwiseGraph g = new PairwiseGraph();

                    foreach (Candidate c in candidates)
                    {
                        g.nodes[c] = new Dictionary <Candidate, decimal>();
                        foreach (Candidate d in candidates.Except(new[] { c }))
                        {
                            g.nodes[c][d] = 0.0m;
                        }
                    }

                    // Iterate each ballot and count who wins and who ties.
                    // This can support tied ranks and each ballot is O(SUM(1..n)) and o(n).
                    //foreach (CountedBallot b in ballots)
                    for (int i = start; i <= end; i++)
                    {
                        CountedBallot       b        = bList[i];
                        HashSet <Candidate> ranked   = b.Votes.Select(x => x.Candidate).ToHashSet();
                        HashSet <Candidate> unranked = candidates.Except(ranked).ToHashSet();

                        // Iterate to compare each pair.
                        Stack <Vote> votes = new Stack <Vote>(b.Votes);
                        while (votes.Count > 0)
                        {
                            Vote v = votes.Pop();
                            foreach (Vote u in votes)
                            {
                                // Who is ranked first?  No action if a tie.
                                if (v.Beats(u))
                                {
                                    g.nodes[v.Candidate][u.Candidate] += b.Count;
                                }
                                else if (u.Beats(v))
                                {
                                    g.nodes[u.Candidate][v.Candidate] += b.Count;
                                }
                            }
                            // Defeat all unranked candidates
                            foreach (Candidate c in unranked)
                            {
                                g.nodes[v.Candidate][c] += b.Count;
                            }
                        }
                    }
                    return(g);
                }

                // First divide all the processes up for background run
                Parallel.For(0, threadCount, (i, state) =>
                             subsets[i] = CountSubsets(bList.Count() * i / threadCount, (bList.Count() * (i + 1) / threadCount) - 1));
                // Add them all together
                foreach (PairwiseGraph g in subsets)
                {
                    AddGraph(g);
                }
            }

            BuildGraph();
        }