From 29817c501095465d65a21d36cf4e7bbd040b91cb Mon Sep 17 00:00:00 2001 From: anga4 Date: Mon, 12 Aug 2024 15:43:27 +0100 Subject: [PATCH 1/4] Cross building setup for scala 2.12 and 2.13 [WIP] --- .bsp/sbt.json | 2 +- build.sbt | 61 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/.bsp/sbt.json b/.bsp/sbt.json index 641a3ec..302fe41 100644 --- a/.bsp/sbt.json +++ b/.bsp/sbt.json @@ -1 +1 @@ -{"name":"sbt","version":"1.7.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["C:\\Program Files\\Java\\jdk-17/bin/java","-Xms100m","-Xmx100m","-classpath","C:\\\\Program Files (x86)\\\\sbt\\\\\\\\bin\\\\sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=C:\\\\Program%20Files%20(x86)\\\\sbt\\\\\\\\bin\\\\sbt-launch.jar"]} \ No newline at end of file +{"name":"sbt","version":"1.7.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["C:\\Program Files\\Java\\jdk-17/bin/java","-Xms100m","-Xmx100m","-classpath","C:\\Program Files (x86)\\sbt\\\\bin\\sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=C:\\Program%20Files%20(x86)\\sbt\\\\bin\\sbt-launch.jar"]} \ No newline at end of file diff --git a/build.sbt b/build.sbt index 77cc010..3bf1854 100644 --- a/build.sbt +++ b/build.sbt @@ -1,12 +1,13 @@ import language.postfixOps -name := "countvotes" +lazy val scala212 = "2.12.19" +lazy val scala213 = "2.13.14" +lazy val supportedScalaVersions = List(scala212, scala213) -organization := "AOSSIE" - -version := "1.2" - -scalaVersion := "2.12.19" +ThisBuild / name := "countvotes" +ThisBuild / organization := "AOSSIE" +ThisBuild / version := "1.2" +ThisBuild / scalaVersion := scala213 resolvers += Resolver.sonatypeRepo("public") resolvers += "Sonatype OSS Snapshots" at @@ -31,13 +32,14 @@ lazy val root = Project("agora", file(".")) .aggregate( core, cli - ).settings(commonSettings) + ).settings(commonSettings, crossScalaVersions := Nil) lazy val core = (project in file("modules/core")) .configs(Testing.configs: _*) .settings(Testing.settings: _*) .settings( commonSettings, + crossScalaVersions := supportedScalaVersions, name := "core" ) @@ -49,6 +51,7 @@ lazy val cli = (project in file("modules/cli")) ) .settings( commonSettings, + crossScalaVersions := supportedScalaVersions, name := "cli" ) @@ -56,24 +59,44 @@ lazy val commonSettings = Seq( scalafmtOnCompile := true, semanticdbEnabled := true, semanticdbVersion := scalafixSemanticdb.revision, + scalacOptions += "-P:semanticdb:synthetics:on", scalacOptions += { if (scalaVersion.value.startsWith("2.12")) "-Ywarn-unused-import" else "-Wunused:imports" }, - libraryDependencies ++= Seq( - "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0", - "com.github.scopt" %% "scopt" % "4.1.0", - "org.specs2" %% "specs2-core" % "4.20.8" % "test,verification-test,bench", - "com.lihaoyi" %% "ammonite-ops" % "2.4.1", - "ch.qos.logback" % "logback-classic" % "1.2.11", - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", - "com.storm-enroute" %% "scalameter" % "0.19", - "com.storm-enroute" %% "scalameter-core" % "0.19", - "com.typesafe.play" %% "play-json" % "2.9.4", - "org.typelevel" %% "spire" % "0.17.0" - ), + libraryDependencies ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 12)) => Seq( + "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0", + "com.github.scopt" %% "scopt" % "4.1.0", + "org.specs2" %% "specs2-core" % "4.20.8" % "test,verification-test,bench", + "com.lihaoyi" %% "ammonite-ops" % "2.4.1", + "ch.qos.logback" % "logback-classic" % "1.2.11", + "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", + "com.storm-enroute" %% "scalameter" % "0.19", + "com.storm-enroute" %% "scalameter-core" % "0.19", + "com.typesafe.play" %% "play-json" % "2.9.4", + "org.typelevel" %% "spire" % "0.17.0", + "org.scala-lang.modules" % "scala-collection-compat_2.12" % "2.12.0" + ) + case Some((2, 13)) => Seq( + "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0", + "com.github.scopt" %% "scopt" % "4.1.0", + "org.specs2" %% "specs2-core" % "4.20.6" % "test,verification-test,bench", + "com.lihaoyi" %% "ammonite-ops" % "2.4.1", + "ch.qos.logback" % "logback-classic" % "1.2.11", + "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", + "com.storm-enroute" %% "scalameter" % "0.21", + "com.storm-enroute" %% "scalameter-core" % "0.21", + "com.typesafe.play" %% "play-json" % "2.9.4", + "org.typelevel" %% "spire" % "0.18.0", + "org.scala-lang.modules" % "scala-collection-compat_2.13" % "2.12.0" + ) + case _ => Nil + } + }, dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0" ) From 92c7f6288b65cc51c4cf2c4b6a03d5c188b0975d Mon Sep 17 00:00:00 2001 From: icemc Date: Wed, 14 Aug 2024 17:01:15 +0100 Subject: [PATCH 2/4] Removed src folder --- src/main/scala/agora/Main.scala | 417 ------------ src/main/scala/agora/PreferenceAnalyser.scala | 109 --- src/main/scala/agora/SetComparator.scala | 125 ---- src/main/scala/agora/StabilityAnalyser.scala | 136 ---- .../analyzer/PreferenceAnalysisMethod.scala | 12 - .../agora/analyzer/SinglePeakAnalyser.scala | 201 ------ .../analyzer/ValueRestrictedAnalyser.scala | 96 --- .../agora/comparator/FishburnsExtension.scala | 73 -- .../agora/comparator/KellyExtension.scala | 80 --- .../comparator/SetExtensionMethods.scala | 45 -- src/main/scala/agora/model/Ballot.scala | 56 -- src/main/scala/agora/model/Candidate.scala | 16 - src/main/scala/agora/model/Count.scala | 112 --- src/main/scala/agora/model/Election.scala | 60 -- src/main/scala/agora/model/Parameters.scala | 66 -- src/main/scala/agora/model/Report.scala | 298 -------- src/main/scala/agora/model/Result.scala | 89 --- .../scala/agora/parser/CandidatesParser.scala | 34 - .../scala/agora/parser/ElectionParser.scala | 37 - .../scala/agora/parser/ParameterParser.scala | 20 - .../agora/parser/PreferencesParser.scala | 121 ---- .../scala/agora/util/matrix/BaseMatrix.scala | 18 - .../scala/agora/util/matrix/package.scala | 52 -- src/main/scala/agora/votecounter/ACT.scala | 385 ----------- .../agora/votecounter/ApprovalRule.scala | 22 - .../agora/votecounter/AustralianSenate.scala | 635 ------------------ .../agora/votecounter/BaldwinMethod.scala | 51 -- .../agora/votecounter/BipartisanSet.scala | 122 ---- src/main/scala/agora/votecounter/Borda.scala | 46 -- .../scala/agora/votecounter/Bucklin.scala | 55 -- .../scala/agora/votecounter/Contingent.scala | 40 -- src/main/scala/agora/votecounter/Coomb.scala | 67 -- .../scala/agora/votecounter/Copeland.scala | 67 -- .../scala/agora/votecounter/Dodgson.scala | 134 ---- src/main/scala/agora/votecounter/EVACS.scala | 11 - .../agora/votecounter/EVACSDelayedWD.scala | 37 - .../scala/agora/votecounter/EVACSnoLP.scala | 21 - .../scala/agora/votecounter/Egalitarian.scala | 29 - .../agora/votecounter/EgalitarianBrute.scala | 22 - .../agora/votecounter/EgalitarianDP.scala | 40 -- ...bridPluralityPreferentialBlockVoting.scala | 64 -- .../votecounter/InstantExhaustiveBallot.scala | 32 - .../InstantExhaustiveDropOffRule.scala | 72 -- .../votecounter/InstantRunoff2Round.scala | 61 -- .../scala/agora/votecounter/KemenyYoung.scala | 88 --- .../scala/agora/votecounter/Majority.scala | 29 - .../scala/agora/votecounter/Maximin.scala | 62 -- .../scala/agora/votecounter/MeekSTV.scala | 157 ----- .../agora/votecounter/MinimaxCondorcet.scala | 55 -- src/main/scala/agora/votecounter/Nanson.scala | 29 - .../scala/agora/votecounter/Oklahoma.scala | 63 -- .../votecounter/PreferentialBlockVoting.scala | 46 -- .../ProportionalApprovalVoting.scala | 76 --- .../agora/votecounter/RandomBallot.scala | 45 -- .../scala/agora/votecounter/RangeVoting.scala | 33 - .../scala/agora/votecounter/RankedPairs.scala | 23 - src/main/scala/agora/votecounter/SMC.scala | 78 --- src/main/scala/agora/votecounter/STV.scala | 119 ---- .../agora/votecounter/STVAustralia.scala | 101 --- .../SatisfactionApprovalVoting.scala | 29 - .../scala/agora/votecounter/Schulze.scala | 75 --- ...SequentialProportionalApprovalVoting.scala | 53 -- .../agora/votecounter/SimpleApproval.scala | 21 - .../scala/agora/votecounter/SimpleSTV.scala | 163 ----- .../scala/agora/votecounter/SmithSet.scala | 111 --- .../agora/votecounter/SuperMajority.scala | 54 -- .../agora/votecounter/UncoveredSet.scala | 67 -- src/main/scala/agora/votecounter/Veto.scala | 34 - .../scala/agora/votecounter/VoteCounter.scala | 39 -- .../common/PreferencePairwiseComparison.scala | 30 - .../common/RankPairwiseComparison.scala | 30 - .../agora/votecounter/stv/ACTBallot.scala | 47 -- .../agora/votecounter/stv/ACTCandidate.scala | 12 - .../scala/agora/votecounter/stv/Action.scala | 17 - .../votecounter/stv/ExactWinnerRemoval.scala | 68 -- .../agora/votecounter/stv/Exclusion.scala | 184 ----- .../stv/ExclusionTieResolution.scala | 117 ---- .../agora/votecounter/stv/FractionLoss.scala | 45 -- .../agora/votecounter/stv/NewWinners.scala | 32 - .../stv/NewWinnersDuringExclusion.scala | 117 ---- ...ewWinnersDuringSurplusesDistribution.scala | 54 -- .../scala/agora/votecounter/stv/Quota.scala | 74 -- .../votecounter/stv/SurplusDistribution.scala | 293 -------- .../SurplusDistributionTieResolution.scala | 176 ----- .../stv/TotalsDuringExclusion.scala | 69 -- .../agora/votecounter/stv/TransferValue.scala | 185 ----- .../agora/analyzer/SinglePeakednessTest.scala | 40 -- .../ValueRestrictedAnalyserTest.scala | 36 - .../comparator/FishburnsExtensionTest.scala | 45 -- .../comparator/KellysExtensionTest.scala | 68 -- .../scala/agora/votecounter/BaldwinTest.scala | 25 - .../agora/votecounter/BipartisanSetTest.scala | 46 -- .../agora/votecounter/BordaRuleTest.scala | 33 - .../scala/agora/votecounter/BucklinTest.scala | 29 - .../agora/votecounter/ContingentTest.scala | 29 - .../scala/agora/votecounter/CoombTest.scala | 39 -- .../agora/votecounter/CopelandTest.scala | 38 -- .../scala/agora/votecounter/DodgsonTest.scala | 33 - ...PluralityPreferentialBlockVotingTest.scala | 51 -- .../InstantExhaustiveBallotTest.scala | 33 - .../InstantExhaustiveDropOffRuleTest.scala | 33 - .../votecounter/InstantRunoff2RoundTest.scala | 34 - .../agora/votecounter/KemenyYoungTest.scala | 35 - .../agora/votecounter/MajorityRuleTest.scala | 42 -- .../agora/votecounter/MaximinMethodTest.scala | 30 - .../scala/agora/votecounter/MeekSTVTest.scala | 33 - .../votecounter/MinimaxCondorcetTest.scala | 34 - .../votecounter/OklahomaMethodTest.scala | 35 - .../PreferentialBlockVotingTest.scala | 33 - .../ProportionalApprovalVotingTest.scala | 36 - .../agora/votecounter/RandomBallotTest.scala | 84 --- .../agora/votecounter/RangeVotingTest.scala | 33 - .../agora/votecounter/RunAllMethods.scala | 75 --- .../agora/votecounter/SMCMethodTest.scala | 46 -- .../SatisfactionApprovalVotingTest.scala | 35 - .../scala/agora/votecounter/SchulzeTest.scala | 34 - ...entialProportionalApprovalVotingTest.scala | 39 -- .../agora/votecounter/SimpleSTVRuleTest.scala | 36 - .../agora/votecounter/SmithSetTest.scala | 28 - .../agora/votecounter/SuperMajorityTest.scala | 52 -- .../agora/votecounter/UncoveredSetTest.scala | 34 - .../scala/agora/votecounter/VetoTest.scala | 33 - .../scala/performance/AgoraBenchmark.scala | 90 --- .../scala/performance/BaldwinRegression.scala | 46 -- .../scala/performance/BordaRegression.scala | 48 -- .../scala/performance/CoombRegression.scala | 45 -- .../performance/CopelandRegression.scala | 51 -- .../scala/performance/FullRegression.scala | 44 -- .../MinimaxCondorcetRegression.scala | 44 -- .../performance/RandomBallotRegression.scala | 46 -- .../performance/UncoveredSetRegression.scala | 47 -- 131 files changed, 9366 deletions(-) delete mode 100644 src/main/scala/agora/Main.scala delete mode 100644 src/main/scala/agora/PreferenceAnalyser.scala delete mode 100644 src/main/scala/agora/SetComparator.scala delete mode 100644 src/main/scala/agora/StabilityAnalyser.scala delete mode 100644 src/main/scala/agora/analyzer/PreferenceAnalysisMethod.scala delete mode 100644 src/main/scala/agora/analyzer/SinglePeakAnalyser.scala delete mode 100644 src/main/scala/agora/analyzer/ValueRestrictedAnalyser.scala delete mode 100644 src/main/scala/agora/comparator/FishburnsExtension.scala delete mode 100644 src/main/scala/agora/comparator/KellyExtension.scala delete mode 100644 src/main/scala/agora/comparator/SetExtensionMethods.scala delete mode 100644 src/main/scala/agora/model/Ballot.scala delete mode 100644 src/main/scala/agora/model/Candidate.scala delete mode 100644 src/main/scala/agora/model/Count.scala delete mode 100644 src/main/scala/agora/model/Election.scala delete mode 100644 src/main/scala/agora/model/Parameters.scala delete mode 100644 src/main/scala/agora/model/Report.scala delete mode 100644 src/main/scala/agora/model/Result.scala delete mode 100644 src/main/scala/agora/parser/CandidatesParser.scala delete mode 100644 src/main/scala/agora/parser/ElectionParser.scala delete mode 100644 src/main/scala/agora/parser/ParameterParser.scala delete mode 100644 src/main/scala/agora/parser/PreferencesParser.scala delete mode 100644 src/main/scala/agora/util/matrix/BaseMatrix.scala delete mode 100644 src/main/scala/agora/util/matrix/package.scala delete mode 100644 src/main/scala/agora/votecounter/ACT.scala delete mode 100644 src/main/scala/agora/votecounter/ApprovalRule.scala delete mode 100644 src/main/scala/agora/votecounter/AustralianSenate.scala delete mode 100644 src/main/scala/agora/votecounter/BaldwinMethod.scala delete mode 100644 src/main/scala/agora/votecounter/BipartisanSet.scala delete mode 100644 src/main/scala/agora/votecounter/Borda.scala delete mode 100644 src/main/scala/agora/votecounter/Bucklin.scala delete mode 100644 src/main/scala/agora/votecounter/Contingent.scala delete mode 100644 src/main/scala/agora/votecounter/Coomb.scala delete mode 100644 src/main/scala/agora/votecounter/Copeland.scala delete mode 100644 src/main/scala/agora/votecounter/Dodgson.scala delete mode 100644 src/main/scala/agora/votecounter/EVACS.scala delete mode 100644 src/main/scala/agora/votecounter/EVACSDelayedWD.scala delete mode 100644 src/main/scala/agora/votecounter/EVACSnoLP.scala delete mode 100644 src/main/scala/agora/votecounter/Egalitarian.scala delete mode 100644 src/main/scala/agora/votecounter/EgalitarianBrute.scala delete mode 100644 src/main/scala/agora/votecounter/EgalitarianDP.scala delete mode 100644 src/main/scala/agora/votecounter/HybridPluralityPreferentialBlockVoting.scala delete mode 100644 src/main/scala/agora/votecounter/InstantExhaustiveBallot.scala delete mode 100644 src/main/scala/agora/votecounter/InstantExhaustiveDropOffRule.scala delete mode 100644 src/main/scala/agora/votecounter/InstantRunoff2Round.scala delete mode 100644 src/main/scala/agora/votecounter/KemenyYoung.scala delete mode 100644 src/main/scala/agora/votecounter/Majority.scala delete mode 100644 src/main/scala/agora/votecounter/Maximin.scala delete mode 100644 src/main/scala/agora/votecounter/MeekSTV.scala delete mode 100644 src/main/scala/agora/votecounter/MinimaxCondorcet.scala delete mode 100644 src/main/scala/agora/votecounter/Nanson.scala delete mode 100644 src/main/scala/agora/votecounter/Oklahoma.scala delete mode 100644 src/main/scala/agora/votecounter/PreferentialBlockVoting.scala delete mode 100644 src/main/scala/agora/votecounter/ProportionalApprovalVoting.scala delete mode 100644 src/main/scala/agora/votecounter/RandomBallot.scala delete mode 100644 src/main/scala/agora/votecounter/RangeVoting.scala delete mode 100644 src/main/scala/agora/votecounter/RankedPairs.scala delete mode 100644 src/main/scala/agora/votecounter/SMC.scala delete mode 100644 src/main/scala/agora/votecounter/STV.scala delete mode 100644 src/main/scala/agora/votecounter/STVAustralia.scala delete mode 100644 src/main/scala/agora/votecounter/SatisfactionApprovalVoting.scala delete mode 100644 src/main/scala/agora/votecounter/Schulze.scala delete mode 100644 src/main/scala/agora/votecounter/SequentialProportionalApprovalVoting.scala delete mode 100644 src/main/scala/agora/votecounter/SimpleApproval.scala delete mode 100644 src/main/scala/agora/votecounter/SimpleSTV.scala delete mode 100644 src/main/scala/agora/votecounter/SmithSet.scala delete mode 100644 src/main/scala/agora/votecounter/SuperMajority.scala delete mode 100644 src/main/scala/agora/votecounter/UncoveredSet.scala delete mode 100644 src/main/scala/agora/votecounter/Veto.scala delete mode 100644 src/main/scala/agora/votecounter/VoteCounter.scala delete mode 100644 src/main/scala/agora/votecounter/common/PreferencePairwiseComparison.scala delete mode 100644 src/main/scala/agora/votecounter/common/RankPairwiseComparison.scala delete mode 100644 src/main/scala/agora/votecounter/stv/ACTBallot.scala delete mode 100644 src/main/scala/agora/votecounter/stv/ACTCandidate.scala delete mode 100644 src/main/scala/agora/votecounter/stv/Action.scala delete mode 100644 src/main/scala/agora/votecounter/stv/ExactWinnerRemoval.scala delete mode 100644 src/main/scala/agora/votecounter/stv/Exclusion.scala delete mode 100644 src/main/scala/agora/votecounter/stv/ExclusionTieResolution.scala delete mode 100644 src/main/scala/agora/votecounter/stv/FractionLoss.scala delete mode 100644 src/main/scala/agora/votecounter/stv/NewWinners.scala delete mode 100644 src/main/scala/agora/votecounter/stv/NewWinnersDuringExclusion.scala delete mode 100644 src/main/scala/agora/votecounter/stv/NewWinnersDuringSurplusesDistribution.scala delete mode 100644 src/main/scala/agora/votecounter/stv/Quota.scala delete mode 100644 src/main/scala/agora/votecounter/stv/SurplusDistribution.scala delete mode 100644 src/main/scala/agora/votecounter/stv/SurplusDistributionTieResolution.scala delete mode 100644 src/main/scala/agora/votecounter/stv/TotalsDuringExclusion.scala delete mode 100644 src/main/scala/agora/votecounter/stv/TransferValue.scala delete mode 100644 src/test/scala/agora/analyzer/SinglePeakednessTest.scala delete mode 100644 src/test/scala/agora/analyzer/ValueRestrictedAnalyserTest.scala delete mode 100644 src/test/scala/agora/comparator/FishburnsExtensionTest.scala delete mode 100644 src/test/scala/agora/comparator/KellysExtensionTest.scala delete mode 100644 src/test/scala/agora/votecounter/BaldwinTest.scala delete mode 100644 src/test/scala/agora/votecounter/BipartisanSetTest.scala delete mode 100644 src/test/scala/agora/votecounter/BordaRuleTest.scala delete mode 100644 src/test/scala/agora/votecounter/BucklinTest.scala delete mode 100644 src/test/scala/agora/votecounter/ContingentTest.scala delete mode 100644 src/test/scala/agora/votecounter/CoombTest.scala delete mode 100644 src/test/scala/agora/votecounter/CopelandTest.scala delete mode 100644 src/test/scala/agora/votecounter/DodgsonTest.scala delete mode 100644 src/test/scala/agora/votecounter/HybridPluralityPreferentialBlockVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/InstantExhaustiveBallotTest.scala delete mode 100644 src/test/scala/agora/votecounter/InstantExhaustiveDropOffRuleTest.scala delete mode 100644 src/test/scala/agora/votecounter/InstantRunoff2RoundTest.scala delete mode 100644 src/test/scala/agora/votecounter/KemenyYoungTest.scala delete mode 100644 src/test/scala/agora/votecounter/MajorityRuleTest.scala delete mode 100644 src/test/scala/agora/votecounter/MaximinMethodTest.scala delete mode 100644 src/test/scala/agora/votecounter/MeekSTVTest.scala delete mode 100644 src/test/scala/agora/votecounter/MinimaxCondorcetTest.scala delete mode 100644 src/test/scala/agora/votecounter/OklahomaMethodTest.scala delete mode 100644 src/test/scala/agora/votecounter/PreferentialBlockVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/ProportionalApprovalVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/RandomBallotTest.scala delete mode 100644 src/test/scala/agora/votecounter/RangeVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/RunAllMethods.scala delete mode 100644 src/test/scala/agora/votecounter/SMCMethodTest.scala delete mode 100644 src/test/scala/agora/votecounter/SatisfactionApprovalVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/SchulzeTest.scala delete mode 100644 src/test/scala/agora/votecounter/SequentialProportionalApprovalVotingTest.scala delete mode 100644 src/test/scala/agora/votecounter/SimpleSTVRuleTest.scala delete mode 100644 src/test/scala/agora/votecounter/SmithSetTest.scala delete mode 100644 src/test/scala/agora/votecounter/SuperMajorityTest.scala delete mode 100644 src/test/scala/agora/votecounter/UncoveredSetTest.scala delete mode 100644 src/test/scala/agora/votecounter/VetoTest.scala delete mode 100644 src/test/scala/performance/AgoraBenchmark.scala delete mode 100644 src/test/scala/performance/BaldwinRegression.scala delete mode 100644 src/test/scala/performance/BordaRegression.scala delete mode 100644 src/test/scala/performance/CoombRegression.scala delete mode 100644 src/test/scala/performance/CopelandRegression.scala delete mode 100644 src/test/scala/performance/FullRegression.scala delete mode 100644 src/test/scala/performance/MinimaxCondorcetRegression.scala delete mode 100644 src/test/scala/performance/RandomBallotRegression.scala delete mode 100644 src/test/scala/performance/UncoveredSetRegression.scala diff --git a/src/main/scala/agora/Main.scala b/src/main/scala/agora/Main.scala deleted file mode 100644 index 1905af1..0000000 --- a/src/main/scala/agora/Main.scala +++ /dev/null @@ -1,417 +0,0 @@ -package agora - -import agora.votecounter._ -import agora.parser._ -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} -import scala.languageFeature.implicitConversions -import scala.util.parsing.combinator._ - -import spire.math.Rational - -sealed abstract class VoteCounterTableFormats - -case object ACT extends VoteCounterTableFormats - -case object Concise extends VoteCounterTableFormats - -object Main extends RegexParsers { - - case class Config( - directory: String = "", - ballotsfile: Option[String] = None, - method: String = "", - parameters: Option[Parameters] = None, - nvacancies: String = "", - nkandidates: Option[String] = None, - candidatesfile: String = "", - table: VoteCounterTableFormats = Concise - ) - - val parser = new scopt.OptionParser[Config]("compress") { - head("\nCommand Line Interface for Electronic Vote Counting\n\n ") - - note( - """The arguments are as follows:""" + "\n" + - """ -d [-b] -c -m [-p] -v [-k] [-t]""" + "\n \n" - ) - - opt[String]('d', "directory") - .required() - .unbounded() - .action { (v, c) => - c.copy(directory = v) - } - .text("set working directory to \n") - .valueName("") - - opt[String]('b', "ballotsfile").action { (v, c) => - c.copy(ballotsfile = Some(v)) - }.text("use preferences listed in \n").valueName("") - - opt[String]('c', "candidatesfile") - .required() - .action { (v, c) => - c.copy(candidatesfile = v) - } - .text("use preferences listed in \n") - .valueName("") - - opt[String]('m', "method") - .required() - .action { (v, c) => - c.copy(method = v) - } - .text("use vote counting method \n") - .valueName("") - - opt[String]('p', "parameterFile").action { (v, c) => - c.copy(parameters = Some(ParameterParser.parse(c.directory + v))) - }.text("set paramfile to \n").valueName("") - - opt[String]('v', "nvacancies") - .required() - .action { (v, c) => - c.copy(nvacancies = v) - } - .text("set number of vacancies \n") - .valueName("") - - opt[String]('k', "nkandidates").action { (v, c) => - c.copy(nkandidates = Some(v)) - }.text("set number of candidates \n").valueName("") - - opt[String]('t', "table").action { (v, c) => - val tableFormat = v match { - case "ACT" => ACT - case _ => Concise - } - c.copy(table = tableFormat) - - }.text("set format of the output table \n").valueName("") - - note( - """Possible values are as follows:""" + "\n" + - - """for -m: EVACS, EVACSnoLP, EVACSDWD, Simple, Majority, Borda, Approval, Baldwin, Nanson, Kemeny-Young, Contingent,| Runoff2Round, Copeland, UncoveredSet, InstantExhaustiveBallot, PreferentialBlockVoting, HybridPluralityPreferentialBlockVoting, InstantExhaustiveDropOff, SAV, PAV, SPAV, Oklahoma, Meek""".stripMargin + "\n" + - - """for -t: Concise, ACT""" + "\n \n" - ) - - help("help").text("prints this usage text") - } - - // scalastyle:off cyclomatic.complexity - // scalastyle:off method.length - def main(args: Array[String]): Unit = { - - def callMethod( - c: Config, - electionFile: String, - winnersfile: String, - reportfile: String, - candidates_in_order: List[Candidate], - parameters: Option[Parameters] - ) = { - c.method match { - case "EVACS" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = - (new EVACS).runVoteCounterGeneral(election, candidates_in_order, c.nvacancies.toInt) - c.table match { - case ACT => r.writeDistributionOfPreferencesACT(reportfile, Some(candidates_in_order)) - case _ => r.writeDistributionOfPreferences(reportfile, Some(candidates_in_order)) - } - println("The scrutiny was recorded to " + reportfile) - r.writeWinners(winnersfile) - println("The winners were recorded to " + winnersfile) - case "EVACSnoLP" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = - (new EVACSnoLP).runVoteCounterGeneral(election, candidates_in_order, c.nvacancies.toInt) - r.writeDistributionOfPreferences(reportfile, Some(candidates_in_order)) - r.writeWinners(winnersfile) - case "EVACSDWD" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = (new EVACSDelayedWD).runVoteCounterGeneral( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeDistributionOfPreferences(reportfile, Some(candidates_in_order)) - r.writeWinners(winnersfile) - case "Senate" => - val election = PreferencesParser.read(c.directory + electionFile) - val electionwithIds = - for (b <- election) yield Ballot(b.preferences, election.indexOf(b) + 1, Rational(1, 1)) - var r = (new AustralianSenate).runVoteCounterGeneral( - electionwithIds, - candidates_in_order, - c.nvacancies.toInt - ) - c.table match { - case ACT => r.writeDistributionOfPreferencesACT(reportfile, Some(candidates_in_order)) - case _ => r.writeDistributionOfPreferences(reportfile, Some(candidates_in_order)) - } - println("The scrutiny was recorded to " + reportfile) - r.writeWinners(winnersfile) - println("The winners were recorded to " + winnersfile) - case "Simple" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = (new SimpleSTV).runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - case "Egalitarian" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = - (new EgalitarianBrute).runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - case "Majority" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Majority.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Approval" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = ApprovalRule.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Borda" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Borda.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Kemeny-Young" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = KemenyYoung.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Baldwin" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = BaldwinMethod.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Nanson" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Nanson.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "InstantRunoff2Round" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = - InstantRunoff2Round.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - case "Coomb" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Coomb.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "InstantExhaustiveBallot" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = InstantExhaustiveBallot.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "Contingent" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Contingent.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "RandomBallot" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = RandomBallot.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "MinimaxCondorcet" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = MinimaxCondorcet.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Copeland" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Copeland.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - case "Dodgson" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Dodgson.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "UncoveredSet" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = UncoveredSet.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "SmithSet" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = SmithSet.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "InstantExhaustiveDropOff" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = InstantExhaustiveDropOffRule.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "PreferentialBlockVoting" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = PreferentialBlockVoting.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "HybridPluralityPreferentialBlockVoting" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = HybridPluralityPreferentialBlockVoting.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "Oklahoma" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Oklahoma.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "SPAV" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = SequentialProportionalApprovalVoting.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "PAV" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = ProportionalApprovalVoting.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - - case "SAV" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = SatisfactionApprovalVoting.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt - ) - r.writeWinners(winnersfile) - case "SMC" => - val election = PreferencesParser.read(c.directory + electionFile) - parameters match { - case Some(param) => - var r = SMC.runVoteCounter(election, candidates_in_order, param, c.nvacancies.toInt) - r.writeWinners(winnersfile) - case None => - println("\n\nPlease provide the comparison order to execute this voting method\n\n") - } - - case "RankedPairs" => - val election = PreferencesParserWithIndifference.read(c.directory + electionFile) - var r = RankedPairs.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Meek" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = MeekSTV.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Schulze" => - val election = PreferencesParserWithRank.read(c.directory + electionFile) - var r = Schulze.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Maximin" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Maximin.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - println(" VoteCounter table for method Maximin is not implemented yet.") - r.writeWinners(winnersfile) - - case "RangeVoting" => - val election = PreferencesParserWithScore.read(c.directory + electionFile) - var r = RangeVoting.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "BipartisanSet" => - val election = PreferencesParser.read(c.directory + electionFile) - parameters match { - case Some(param) => - var r = BipartisanSet.runVoteCounter(election, candidates_in_order, param) - r.writeWinners(winnersfile) - case None => - println("Please provide probability distribution to compute bipartisan set") - } - - case "SuperMajority" => - val election = PreferencesParser.read(c.directory + electionFile) - parameters match { - case Some(param) => - var r = SuperMajority.runVoteCounter( - election, - candidates_in_order, - c.nvacancies.toInt, - param - ) - r.writeWinners(winnersfile) - case None => - println( - "Please provide a .json file containing the majority percentage required to elect winner." - ) - } - - case "Veto" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Veto.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "Bucklin" => - val election = PreferencesParser.read(c.directory + electionFile) - var r = Bucklin.runVoteCounter(election, candidates_in_order, c.nvacancies.toInt) - r.writeWinners(winnersfile) - - case "" => println("Please specify which algorithm should be used.") - } - } - - parser.parse(args, Config()).map { c => - c.ballotsfile match { - case Some(filename) => // ONLY ONE FILE IS ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - println("Candidates: " + candidates) - val winnersfile = - c.directory + "winners/" + "Winners_" + c.method + "_InputFile_" + filename - val reportfile = - c.directory + "reports/" + "Report_" + c.method + "_InputFile_" + filename - callMethod(c, filename, winnersfile, reportfile, candidates, c.parameters) - case None => // ALL FILES IN THE DIRECTORY ARE ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - val files = new java.io.File(c.directory).listFiles.filter(_.getName.endsWith(".kat")) - for (file <- files) { - val filename = file.getName - println("------------------------------------------------") - println("\n" + " NEW ELECTION: " + file.getName + "\n") - println("------------------------------------------------") - // val election = PreferencesParser.read(c.directory + filename) - val winnersfile = - c.directory + "winners/" + "Winners_" + c.method + "_InputFile_" + filename - val reportfile = - c.directory + "reports/" + "Report_" + c.method + "_InputFile_" + filename - callMethod(c, filename, winnersfile, reportfile, candidates, c.parameters) - } - } - } - } - -} diff --git a/src/main/scala/agora/PreferenceAnalyser.scala b/src/main/scala/agora/PreferenceAnalyser.scala deleted file mode 100644 index 165e08c..0000000 --- a/src/main/scala/agora/PreferenceAnalyser.scala +++ /dev/null @@ -1,109 +0,0 @@ -package agora - -import agora.analyzer.SinglePeakAnalyser -import agora.analyzer.ValueRestrictedAnalyser -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import agora.model.{PreferenceBallot => Ballot} -import agora.model.Election - -object PreferenceAnalyser { - - case class PreferenceConfig( - directory: String = "", - ballotsfile: Option[String] = None, - method: String = "", - candidatesfile: String = "", - table: VoteCounterTableFormats = Concise - ) - - val parser = new scopt.OptionParser[PreferenceConfig]("compress") { - head("\nCommand Line Interface for Electronic Votes preference analysis\n\n ") - - note( - """The arguments are as follows:""" + "\n" + - """ -d [-b] -c -m """ + "\n \n" - ) - - opt[String]('d', "directory") - .required() - .unbounded() - .action { (v, c) => - c.copy(directory = v) - } - .text("set working directory to \n") - .valueName("") - - opt[String]('b', "ballotsfile").action { (v, c) => - c.copy(ballotsfile = Some(v)) - }.text("use preferences listed in \n").valueName("") - - opt[String]('c', "candidatesfile") - .required() - .action { (v, c) => - c.copy(candidatesfile = v) - } - .text("use preferences listed in \n") - .valueName("") - - opt[String]('m', "method") - .required() - .action { (v, c) => - c.copy(method = v) - } - .text("use preference analysis method method \n") - .valueName("") - - note( - """Possible values are as follows:""" + "\n" + - - """for -m: single-peak, single-caved, value-restricted""" + "\n" - ) - - help("help").text("prints this usage text") - } - - def main(args: Array[String]): Unit = { - - def callMethod( - c: PreferenceConfig, - election: Election[Ballot], - candidates_in_order: List[Candidate] - ) = { - - c.method match { - case "single-peak" => - SinglePeakAnalyser.analyse(election, candidates_in_order) - - case "single-caved" => - println("perform single-caved preference analysis") - - case "value-restricted" => - ValueRestrictedAnalyser.analyse(election, candidates_in_order) - } - } - - parser.parse(args, PreferenceConfig()).map { c => - c.ballotsfile match { - case Some(filename) => // ONLY ONE FILE IS ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - val election = PreferencesParser.read(c.directory + filename) - callMethod(c, election, candidates) - case None => // ALL FILES IN THE DIRECTORY ARE ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - val files = new java.io.File(c.directory).listFiles.filter(_.getName.endsWith(".kat")) - for (file <- files) { - val filename = file.getName - println("------------------------------------------------") - println("\n" + " NEW ELECTION: " + file.getName + "\n") - println("------------------------------------------------") - val election = PreferencesParser.read(c.directory + filename) - callMethod(c, election, candidates) - } - } - } - - } - -} diff --git a/src/main/scala/agora/SetComparator.scala b/src/main/scala/agora/SetComparator.scala deleted file mode 100644 index 28c934b..0000000 --- a/src/main/scala/agora/SetComparator.scala +++ /dev/null @@ -1,125 +0,0 @@ -package agora - -import agora.comparator.FishburnsExtension -import agora.comparator.KellyExtension -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import agora.model.{PreferenceBallot => Ballot} -import agora.model.Election - -object SetComparator { - - case class CompareConfig( - directory: String = "", - ballotsfile: Option[String] = None, - method: String = "", - parameterFile: String = "", - candidatesfile: String = "", - table: VoteCounterTableFormats = Concise - ) - - val parser = new scopt.OptionParser[CompareConfig]("compress") { - head("\nCommand Line Interface for candidates set comparisons\n\n ") - - note( - """The arguments are as follows:""" + "\n" + - """ -d [-b] -c -m -p """ + "\n \n" - ) - - opt[String]('d', "directory") - .required() - .unbounded() - .action { (v, c) => - c.copy(directory = v) - } - .text("set working directory to \n") - .valueName("") - - opt[String]('b', "ballotsfile").action { (v, c) => - c.copy(ballotsfile = Some(v)) - }.text("use preferences listed in \n").valueName("") - - opt[String]('c', "candidatesfile") - .required() - .action { (v, c) => - c.copy(candidatesfile = v) - } - .text("use preferences listed in \n") - .valueName("") - - opt[String]('m', "method") - .required() - .action { (v, c) => - c.copy(method = v) - } - .text("use preference analysis method method \n") - .valueName("") - - opt[String]('p', "parameterFile") - .required() - .action { (v, c) => - c.copy(parameterFile = v) - } - .text("set paramfile to \n") - .valueName("") - - note( - """Possible values are as follows:""" + "\n" + - - """for -m: Kelly, Fishburns""" + "\n" - ) - - help("help").text("prints this usage text") - } - - def main(args: Array[String]): Unit = { - - def callMethod( - c: CompareConfig, - election: Election[Ballot], - candidates_in_order: List[Candidate] - ) = { - - c.method match { - case "Kelly" => - KellyExtension.compare( - election, - candidates_in_order, - ParameterParser.parse(c.directory + c.parameterFile) - ) - - case "Fishburns" => - FishburnsExtension.compare( - election, - candidates_in_order, - ParameterParser.parse(c.directory + c.parameterFile) - ) - - } - } - - parser.parse(args, CompareConfig()).map { c => - c.ballotsfile match { - case Some(filename) => // ONLY ONE FILE IS ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - val election = PreferencesParser.read(c.directory + filename) - callMethod(c, election, candidates) - case None => // ALL FILES IN THE DIRECTORY ARE ANALYSED - val candidates = CandidatesParser.read(c.directory + c.candidatesfile) - val files = new java.io.File(c.directory).listFiles.filter(_.getName.endsWith(".kat")) - for (file <- files) { - val filename = file.getName - println("------------------------------------------------") - println("\n" + " NEW COMPARISON: " + file.getName + "\n") - println("------------------------------------------------") - val election = PreferencesParser.read(c.directory + filename) - callMethod(c, election, candidates) - } - } - } - - } - -} diff --git a/src/main/scala/agora/StabilityAnalyser.scala b/src/main/scala/agora/StabilityAnalyser.scala deleted file mode 100644 index 22edae9..0000000 --- a/src/main/scala/agora/StabilityAnalyser.scala +++ /dev/null @@ -1,136 +0,0 @@ -package agora - -import agora.votecounter._ -import agora.model.{PreferenceBallot => Ballot, _} -import scala.collection.mutable.ArrayBuffer -import scala.language.postfixOps -import scala.util.Random -import spire.math.Rational -import spire.math.Rational.apply - -object StabilityAnalyser { - - def main(args: Array[String]): Unit = { - - val report: Report[Ballot] = new Report[Ballot] - - val (candidates, elections) = generateElections(20, 100, 5) - - val electionsPairs = elections.combinations(2).filter(kendallTauDistance(_) != 0) toList - - val analysisArray = new ArrayBuffer[(String, Double, Double)] - - analysisArray.append(analyseStability(Borda, electionsPairs, candidates)) - // analyseStability(MajorityRuleMethod, electionsPairs, candidates) /*some cases there might be no majority winner*/ - analysisArray.append(analyseStability(ApprovalRule, electionsPairs, candidates)) - analysisArray.append(analyseStability(KemenyYoung, electionsPairs, candidates)) - analysisArray.append(analyseStability(BaldwinMethod, electionsPairs, candidates)) - analysisArray.append(analyseStability(Nanson, electionsPairs, candidates)) - analysisArray.append(analyseStability(InstantRunoff2Round, electionsPairs, candidates)) - analysisArray.append(analyseStability(Coomb, electionsPairs, candidates)) - analysisArray.append(analyseStability(InstantExhaustiveBallot, electionsPairs, candidates)) - analysisArray.append(analyseStability(Contingent, electionsPairs, candidates)) - analysisArray.append(analyseStability(MinimaxCondorcet, electionsPairs, candidates)) - analysisArray.append(analyseStability(Copeland, electionsPairs, candidates)) - // analyseStability(UncoveredSetMethod, electionsPairs, candidates) /* in some cases the uncovered set could be empty*/ - analysisArray.append(analyseStability(SmithSet, electionsPairs, candidates)) - analysisArray.append(analyseStability(InstantExhaustiveDropOffRule, electionsPairs, candidates)) - analysisArray.append(analyseStability(PreferentialBlockVoting, electionsPairs, candidates)) - analysisArray.append( - analyseStability(HybridPluralityPreferentialBlockVoting, electionsPairs, candidates) - ) - // analyseStability(OklahomaMethod, electionsPairs, candidates) /* going into infinite loop for some random election*/ - analysisArray.append( - analyseStability(SequentialProportionalApprovalVoting, electionsPairs, candidates) - ) - analysisArray.append(analyseStability(ProportionalApprovalVoting, electionsPairs, candidates)) - analysisArray.append(analyseStability(SatisfactionApprovalVoting, electionsPairs, candidates)) - - report.setStabilityAnalysis(analysisArray.toList) - - report.writeStabilityAnalysis("../Agora/files/Examples/analysis/" + "stabilityanalysis.txt") - - } - - def analyseStability( - vcm: VoteCounter[Ballot], - electionsPair: List[List[Election[Ballot]]], - candidates: List[Candidate] - ): (String, Double, Double) = { - - val electionResults = electionsPair.map(stability(vcm, _, candidates)) - - val averageRatio = electionResults.sum / electionResults.length - - val varianceRatio = - electionResults.map(c => Math.pow(c - averageRatio, 2)).sum / electionResults.length - - (vcm.getClass.getSimpleName, averageRatio, varianceRatio) - - } - - def stability( - vcm: VoteCounter[Ballot], - elections: List[Election[Ballot]], - candidates: List[Candidate] - ): Double = { - - val winnerEA = vcm.winners(elections(0), candidates, 1) - val winnerEB = vcm.winners(elections(1), candidates, 1) - val winnersDistance = winnerSetComparison(winnerEA.map(_._1), winnerEB.map(_._1)) - val ktDistance = kendallTauDistance(elections) - - winnersDistance.toDouble / ktDistance.toDouble - } - - // generate n random election of m voters and c candidates - def generateElections(n: Int, m: Int, c: Int): (List[Candidate], List[Election[Ballot]]) = { - - require(c < 26) - - val candidates = ('A' to 'Z').take(c).map(name => new Candidate(name.toString)) toList - - val elections = for { - i <- List.range(1, n) - } yield { - Election(for { - j <- List.range(1, m) - } yield Ballot(Random.shuffle(candidates), i, 1)) - } - - (candidates, elections) - } - - // calculate the kendall tau distance between two profiles - def kendallTauDistance(profile: List[Election[Ballot]]): Int = { - - var kTDistance = 0 - - profile(0).zip(profile(1)).foreach { case (we1, we2) => - we1.preferences.foreach(c1 => { - we1.preferences.foreach(c2 => { - if ( - (we1.preferences.indexOf(c1) < we1.preferences.indexOf(c2)) - && (we2.preferences.indexOf(c1) > we2.preferences.indexOf(c2)) - ) { - kTDistance += 1 - } - }) - }) - } - - kTDistance - } - - // http://www.nature.com/nature/journal/v234/n5323/abs/234034a0.html?foxtrotcallback=true - def winnerSetComparison(winnerEA: List[Candidate], winnerEB: List[Candidate]): Rational = { - - if (winnerEA.union(winnerEB).distinct.nonEmpty) { - (winnerEA.intersect(winnerEB).distinct length) / winnerEA.union(winnerEB).distinct.length - - } else { - Rational(0, 1) - } - } - -} diff --git a/src/main/scala/agora/analyzer/PreferenceAnalysisMethod.scala b/src/main/scala/agora/analyzer/PreferenceAnalysisMethod.scala deleted file mode 100644 index 041de96..0000000 --- a/src/main/scala/agora/analyzer/PreferenceAnalysisMethod.scala +++ /dev/null @@ -1,12 +0,0 @@ -package agora.analyzer - -import agora.model.{PreferenceBallot => Ballot} -import agora.model.Candidate -import agora.model.Election - -/** Created by deepeshpandey on 18/06/17. */ -abstract class PreferenceAnalysisMethod[B <: Ballot] { - - def analyse(e: agora.model.Election[B], ccandidates: List[Candidate]): Boolean - -} diff --git a/src/main/scala/agora/analyzer/SinglePeakAnalyser.scala b/src/main/scala/agora/analyzer/SinglePeakAnalyser.scala deleted file mode 100644 index de68af9..0000000 --- a/src/main/scala/agora/analyzer/SinglePeakAnalyser.scala +++ /dev/null @@ -1,201 +0,0 @@ -package agora.analyzer - -import agora.model.Candidate -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.ListBuffer - -/** Please check the last page of the pdf - * https://drive.google.com/file/d/0B4uPp6wWiMpSZ2FaTFFneGtJSDg/view when preferences are single - * peaked there is always a unique condorcet winner - */ -object SinglePeakAnalyser extends PreferenceAnalysisMethod[Ballot] { - - def analyse(election: agora.model.Election[Ballot], candidates: List[Candidate]): Boolean = { - - require(election.forall(b => b.preferences.size == candidates.size)) - - getSinglePeakAxis(election, candidates) match { - case Some(axis) => - if (isCompatibleAxis(axis, election)) { - println("Single Peaked with respect to ", axis.mkString(" > ")) - true - } else { - println("\n\nNot Single Peaked!\n\n") - false - } - case None => - println("\n\nNot Single Peaked!\n\n") - false - } - } - - /** returns a single peak linear ordering if it exists otherwise None - * @param election - * @param candidates - * @return - */ - def getSinglePeakAxis( - election: Election[Ballot], - candidates: List[Candidate] - ): Option[List[Candidate]] = { - - val B = election.map(_.preferences.last).distinct - - // size of B cannot be zero as ensured by the required condition - if (B.size > 2) { - None - } else { - val left = new ListBuffer[Candidate] - val right = new ListBuffer[Candidate] - - if (B.size == 2) { - left.insert(0, B.head) - right.insert(0, B.last) - } else { - left.insert(0, B.head) - } - singlePeakAxisAux(left, right, election, candidates.filter(!B.contains(_))) - } - } - - /** recursive utility which finds the last ranked candidates and set L and set R and places those - * candidates in left/right lists and then recurse - * @param left - * @param right - * @param election - * @param candidates - * @return - */ - def singlePeakAxisAux( - left: ListBuffer[Candidate], - right: ListBuffer[Candidate], - election: Election[Ballot], - candidates: List[Candidate] - ): Option[List[Candidate]] = { - - if (candidates.isEmpty) { - - Option((left ++ right).toList) - - } else if (candidates.size == 1) { - - left.insert(left.size, candidates.head) - Option((left ++ right).toList) - - } else { - // process only when there are atleast 2 candidates that needs to be placed - val B = election - .filter(b => - b.preferences.nonEmpty && b.preferences.exists(c => !(left ++ right).contains(c)) - ) - .flatMap(_.preferences.reverseIterator.filter(c => !(left ++ right).contains(c)).take(1)) - .toSet - - val L = B.filter(x => existNrxl(election, x, right.headOption, left.lastOption, candidates)) - val R = B.filter(x => existNrxl(election, x, left.lastOption, right.headOption, candidates)) - - // the algorithm condition - if (B.size <= 2 && L.size <= 1 && R.size <= 1 && L.intersect(R).isEmpty) { - - val leftPlaceCandidate = (B -- R).union(L).toList.headOption - val rightPlaceCandidate = (B -- Set(leftPlaceCandidate.get)).union(R).toList.headOption - - // left place candidate will always exist after the above expression - left.insert(left.size, leftPlaceCandidate.get) - - if (rightPlaceCandidate.nonEmpty) { - right.insert(0, rightPlaceCandidate.get) - } - singlePeakAxisAux(left, right, election, candidates.filter(!B.contains(_))) - - } else { - None - } - } - - } - - /** returns true if there exists a voter who has x as last ranked candidate and also prefer - * candidate l > x > r (algorithm reference : Set Nlxr) - * @param election - * @param x - * @param l - * @param r - * @param candidates - * @return - */ - def existNrxl( - election: Election[Ballot], - x: Candidate, - l: Option[Candidate], - r: Option[Candidate], - candidates: List[Candidate] - ): Boolean = { - - // get all the preferences where candidate x is last ranked - val xLastRankedList = election - .map(_.preferences) - .filter(_.reverseIterator.filter(c => candidates.contains(c)).toList.headOption == Option(x)) - - // only 3 possible conditions - if (l.nonEmpty && r.nonEmpty) { - xLastRankedList.exists(prefs => - prefs.indexOf(l.get) < prefs.indexOf(x) && prefs.indexOf(x) < prefs.indexOf(l.get) - ) - } else if (l.isEmpty && r.nonEmpty) { - xLastRankedList.exists(prefs => prefs.indexOf(x) < prefs.indexOf(r.get)) - } else { - xLastRankedList.exists(prefs => prefs.indexOf(l.get) < prefs.indexOf(x)) - } - } - - /** this checks if calculated axis is compatible with all the ballots as per definition 2 of - * http://www.lamsade.dauphine.fr/~lang/papers/elo-ecai08.pdf - * @param axis - * \- single peak ordering - * @param election - * \- election - * @return - */ - def isCompatibleAxis(axis: List[Candidate], election: Election[Ballot]): Boolean = { - - election.forall { b => - val prefs = b.preferences - val peak = b.preferences.head - - prefs.tail.zipWithIndex.forall(c1 => { - prefs.tail.zipWithIndex - .filter(c => - c._2 > c1._2 && { - // check if c1 and c2 are on the same side of the axis - if ( - axis.indexOf(prefs.head) < axis.indexOf(c1._1) && axis.indexOf(prefs.head) < axis - .indexOf(c._1) - || axis.indexOf(prefs.head) > axis.indexOf(c1._1) && axis.indexOf(prefs.head) > axis - .indexOf(c._1) - ) { - true - } else { - false - } - } - ) - .forall(c2 => { - if ( - (axis.indexOf(peak) > axis.indexOf(c1._1) && axis.indexOf(c1._1) > axis - .indexOf(c2._1)) || - (axis.indexOf(c2._1) > axis.indexOf(c1._1) && axis.indexOf(c1._1) > axis - .indexOf(peak)) - ) { - true - } else { - false - } - }) - }) - } - } - -} diff --git a/src/main/scala/agora/analyzer/ValueRestrictedAnalyser.scala b/src/main/scala/agora/analyzer/ValueRestrictedAnalyser.scala deleted file mode 100644 index 8bd81d9..0000000 --- a/src/main/scala/agora/analyzer/ValueRestrictedAnalyser.scala +++ /dev/null @@ -1,96 +0,0 @@ -package agora.analyzer - -import agora.model.Candidate -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} - -/** This analyser analyses for Sen's Value restricted preferences source link : - * https://www.youtube.com/watch?v=F51U9Sv9QNo&t=26s - */ -object ValueRestrictedAnalyser extends PreferenceAnalysisMethod[Ballot] { - - /** assumption : voters preference relations are complete over set of candidates. for every - * triplet of candidates check if any of value restricted conditions are true. For any triplet(A, - * B, C) check if (a(ABC) = 0 or a(BCA) = 0 or a(CAB) = 0) and (a(CBA) = 0 or a(ACB) = 0 or - * a(BAC) = 0) where a(CAB) is the total number of voters having preferences in order of C > A > - * B. - * - * @param election - * \=> election file assuming preferences with ordering ">" - * @param ccandidates - * \=> candidates list - * @return - */ - override def analyse( - election: agora.model.Election[Ballot], - ccandidates: List[Candidate] - ): Boolean = { - - require(election.forall(b => b.preferences.length == ccandidates.length)) - - // lazily generate triplet of candidates - val tripletList = - for { - i <- ccandidates.indices.view - j <- (i + 1 until ccandidates.length).view - k <- (j + 1 until ccandidates.length).view - } yield List(ccandidates(i), ccandidates(j), ccandidates(k)) - - // try to find the failing triplet and print it out - val failingTriplet = tripletList.find(triplet => - !triplet.exists(cand => { - !election.exists(b => b.preferences.filter(p => triplet.contains(p)).indexOf(cand) == 0) || - !election.exists(b => b.preferences.filter(p => triplet.contains(p)).indexOf(cand) == 1) || - !election.exists(b => b.preferences.filter(p => triplet.contains(p)).indexOf(cand) == 2) - }) - ) - - failingTriplet match { - case Some(list) => - println( - "\n\nPreference profile is not value restricted for the triplet " + list.mkString(" , ") - ) - false - case None => - println("\n\nPreference profile is value restricted.\n\n") - true - } - - // will compare the efficiency once a bigger preference profile is found - /*val failingtriplet = tripletlist.find(triplet => !valueRestrictedTriplet(triplet, election)) - - if (failingtriplet.isEmpty) { - println("\n\nGiven election data satisfies value-restricted preferences.\n\n") - } else { - println("\n\nGiven election data does not satisfies value-restricted preferences for the following triplet:\n") - println(failingtriplet.flatten.mkString("\n")) - }*/ - } - - /** marks the rankings for each candidate of the triplet and check's for the value restricted - * condition Assumption: triplet = List[A, B, C] as per the value restricted notation from the - * link - * - * @param triplet - * a triplet of candidate against which we want to check value restructed preferences - * @param election - * election file - */ - def valueRestrictedTriplet(triplet: List[Candidate], election: Election[Ballot]): Boolean = { - - val tripletRankings = Array.ofDim[Int](3, 3) - - for (b <- election) { - b.preferences - .filter(triplet.contains(_)) - .zipWithIndex - .foreach { c => - tripletRankings(triplet.indexOf(c._1))(c._2) = 1 - } - } - - tripletRankings.exists(p => p.contains(0)) - - } - -} diff --git a/src/main/scala/agora/comparator/FishburnsExtension.scala b/src/main/scala/agora/comparator/FishburnsExtension.scala deleted file mode 100644 index ba057e4..0000000 --- a/src/main/scala/agora/comparator/FishburnsExtension.scala +++ /dev/null @@ -1,73 +0,0 @@ -package agora.comparator - -import agora.model.{PreferenceBallot => Ballot, _} - -import spire.math.Rational - -/* - Link : https://drive.google.com/file/d/0B4uPp6wWiMpSbWh2NGNfLXdiTTA/view?usp=sharing - */ -object FishburnsExtension extends SetExtensionMethods[Ballot] { - - override def compare( - election: agora.model.Election[Ballot], - candidates: List[Candidate], - parameters: Parameters - ): Set[Candidate] = { - - // candidates in comparison sets should be consistent with the actual candidates - require( - parameters.comparisonSets.isDefined && parameters.comparisonSets.get.set1.forall(c => - candidates.exists(cand => cand.name == c) - ) && - parameters.comparisonSets.get.set2.forall(c => candidates.exists(cand => cand.name == c)) - ) - - val setX = parameters.comparisonSets.get.set1 - .map(name => candidates.find(cand => cand.name == name).get) - .toSet - val setY = parameters.comparisonSets.get.set2 - .map(name => candidates.find(cand => cand.name == name).get) - .toSet - val matrix = getPairwiseComparisons(election, candidates) - - if (fishburnComparison(matrix, setX, setY, candidates)) { - println("\n\nFishburn Preferred Set is " + setX.mkString(" , ")) - setX - } else if (fishburnComparison(matrix, setY, setX, candidates)) { - println("\n\nFishburn Preferred Set is " + setY.mkString(" , ")) - setY - } else { - Set() - } - } - - /** X RF Y ⇔ (∀x∈X\Y, y∈Y: x R y) ∧ (∀x∈X, y∈Y\X: x R y) - * @param matrix - * @param setX - * @param setY - * @return - */ - def fishburnComparison( - matrix: Array[Array[Rational]], - setX: Set[Candidate], - setY: Set[Candidate], - candidates: List[Candidate] - ): Boolean = { - - (setX -- setY).forall(x => - setY.forall(y => { - matrix(candidates.indexOf(x))(candidates.indexOf(y)) >= matrix(candidates.indexOf(y))( - candidates.indexOf(x) - ) - }) - ) && setX.forall(x => - (setY -- setX).forall(y => { - matrix(candidates.indexOf(x))(candidates.indexOf(y)) >= matrix(candidates.indexOf(y))( - candidates.indexOf(x) - ) - }) - ) - } - -} diff --git a/src/main/scala/agora/comparator/KellyExtension.scala b/src/main/scala/agora/comparator/KellyExtension.scala deleted file mode 100644 index 15180cc..0000000 --- a/src/main/scala/agora/comparator/KellyExtension.scala +++ /dev/null @@ -1,80 +0,0 @@ -package agora.comparator - -import com.typesafe.scalalogging.LazyLogging -import agora.model.{PreferenceBallot => Ballot, _} - -import spire.math.Rational - -/* - Link : https://drive.google.com/file/d/0B4uPp6wWiMpScEM2Q21kT2x1N3M/view?usp=sharing - */ -object KellyExtension extends SetExtensionMethods[Ballot] with LazyLogging { - - override def compare( - election: agora.model.Election[Ballot], - candidates: List[Candidate], - parameters: Parameters - ): Set[Candidate] = { - // require that the sets are consistent with the candidates list - require( - parameters.comparisonSets.isDefined && parameters.comparisonSets.get.set1.forall(c => - candidates.exists(cand => cand.name == c) - ) && - parameters.comparisonSets.get.set2.forall(c => candidates.exists(cand => cand.name == c)) - ) - - logger.info("Computing Kelly preferred set") - - val majorityMatrix = getPairwiseComparisons(election, candidates) - val setX = parameters.comparisonSets.get.set1 - .map(name => candidates.find(cand => cand.name == name).get) - .toSet - val setY = parameters.comparisonSets.get.set2 - .map(name => candidates.find(cand => cand.name == name).get) - .toSet - - if (kellyComparison(majorityMatrix, setX, setY, candidates)) { - println("\n\nKelly Preferred Set is " + setX.mkString(" , ")) - setX - } else if (kellyComparison(majorityMatrix, setY, setX, candidates)) { - println("\n\nKelly Preferred Set is " + setY.mkString(" , ")) - setY - } else { - Set() - } - - } - - /** X PK Y ⇔ ∀x∈X, y∈Y: (x R y) ∧ ∃x∈X, y∈Y: (x P y) => set X is Kelly preferred to set Y - * @param matrix - * matrix of pairwise votes between candidates - * @param setX - * @param setY - * @return - */ - def kellyComparison( - matrix: Array[Array[Rational]], - setX: Set[Candidate], - setY: Set[Candidate], - candidates: List[Candidate] - ): Boolean = { - - val xRy = setX.forall(cand1 => - setY.forall(cand2 => { - matrix(candidates.indexOf(cand1))(candidates.indexOf(cand2)) >= matrix( - candidates.indexOf(cand2) - )(candidates.indexOf(cand1)) - }) - ) - - xRy && setX.exists(x => - setY.exists(y => - matrix(candidates.indexOf(x))(candidates.indexOf(y)) > matrix(candidates.indexOf(y))( - candidates.indexOf(x) - ) - ) - ) - - } - -} diff --git a/src/main/scala/agora/comparator/SetExtensionMethods.scala b/src/main/scala/agora/comparator/SetExtensionMethods.scala deleted file mode 100644 index eaa2b7c..0000000 --- a/src/main/scala/agora/comparator/SetExtensionMethods.scala +++ /dev/null @@ -1,45 +0,0 @@ -package agora.comparator - -import agora.model.{PreferenceBallot => Ballot, _} - -import spire.math.Rational - -/** A proper definition of strategyproofness for irresolute social choice functions requires the - * specification of preferences over sets of alternatives. One way to obtain such preferences is to - * extend the preferences that voters have over individual alternatives to (not necessarily - * complete) preference relations over sets. A function that yields a preference relation over - * subsets of alternatives when given a preference relation over single alternatives is called a - * set extension. This implementation includes two of the natural and well studied set extension - * methods - Kellys and Fishburn's extension methods. - */ -abstract class SetExtensionMethods[B <: Ballot] { - - // will return the set that is preferred over another as given in the json parameters file - def compare( - election: agora.model.Election[Ballot], - candidates: List[Candidate], - parameters: Parameters - ): Set[Candidate] - - // utility method for matrix where a[i][j] = x means candidate i has got #x votes against candidate j - def getPairwiseComparisons( - election: Election[Ballot], - candidates: List[Candidate] - ): Array[Array[Rational]] = { - - val zeroRational = Rational(0, 1) - val responseMatrix = Array.fill(candidates.size, candidates.size)(Rational(0, 1)) - - for (b <- election if b.preferences.nonEmpty) { - b.preferences.zipWithIndex.foreach { case (c1, i1) => - b.preferences.zipWithIndex.foreach { case (c2, i2) => - if (i1 < i2) { - responseMatrix(candidates.indexOf(c1))(candidates.indexOf(c2)) += b.weight - } - } - } - } - responseMatrix - } - -} diff --git a/src/main/scala/agora/model/Ballot.scala b/src/main/scala/agora/model/Ballot.scala deleted file mode 100644 index 573b32f..0000000 --- a/src/main/scala/agora/model/Ballot.scala +++ /dev/null @@ -1,56 +0,0 @@ -package agora.model - -import scala.language.implicitConversions -import spire.math.Rational - -abstract class Ballot(val id: Int, val weight: Rational) { - - def firstVotes: Map[Candidate, Rational] - -} - -case class PreferenceBallot( - val preferences: List[Candidate], - override val id: Int, - override val weight: Rational -) extends Ballot(id, weight) { - - lazy val firstVotes = preferences.headOption match { - case Some(c) => Map(c -> Rational(1, 1)) - case None => Map() - } - -} - -case class ScoreBallot(scores: List[(Candidate, Rational)], override val id: Int, w: Rational) - extends Ballot(id, w) { - - lazy val sortedScores = scores.sortWith((cs1, cs2) => cs1._2 > cs2._2) - - lazy val firstVotes = sortedScores.headOption match { - case Some((c, s)) => Map(c -> Rational(1, 1)) - case None => Map() - } - -} - -case class RankBallot(val ranks: List[(Candidate, Int)], override val id: Int, w: Rational) - extends Ballot(id, w) { - - lazy val sortedRanks = ranks.sortWith((cs1, cs2) => cs1._2 < cs2._2) - - lazy val firstVotes = sortedRanks.headOption match { - case Some((c, s)) => Map(c -> Rational(1, 1)) - case None => Map() - } - -} - -case class ApprovalBallot(val approvals: Set[Candidate], override val id: Int, w: Rational) - extends Ballot(id, w) { - - lazy val firstVotes = ??? - -} - -// TODO: use ApprovalBallot in Approval voting diff --git a/src/main/scala/agora/model/Candidate.scala b/src/main/scala/agora/model/Candidate.scala deleted file mode 100644 index 1a1dfca..0000000 --- a/src/main/scala/agora/model/Candidate.scala +++ /dev/null @@ -1,16 +0,0 @@ -package agora.model - -case class Candidate( - val name: String, - val id: Option[Int] = None, - val party: Option[String] = None -) { - - override def toString: String = Seq(id, Some(name), party) - .filter(_.isDefined) - .map { - _.get - } - .mkString("[", ",", "]") - -} diff --git a/src/main/scala/agora/model/Count.scala b/src/main/scala/agora/model/Count.scala deleted file mode 100644 index 7f84f0f..0000000 --- a/src/main/scala/agora/model/Count.scala +++ /dev/null @@ -1,112 +0,0 @@ -package agora.model - -import collection.mutable.{HashMap => MMap} -import spire.math.Rational -import agora.votecounter.stv.Action - -import scala.collection.Map - -class Count[B <: Ballot] { - - private var action: Option[Action] = None - - private var initiator: Option[Candidate] = None // initiator of the action - - private var election: Option[Election[B]] = None // election resulting from performing the action - - // private var modifiedelection: Option[Election[B]] = None - // outcome election resulting from performing the action and modifications, as for example lbf or random removal of ballots - - private var totals: Option[Map[Candidate, Rational]] = None // outcome of the count - - // private var numVotesReceived: Option[Map[Candidate, Rational] ] = None // for each candidate, number of votes she/he received in the current count - - private var winners: List[(Candidate, Rational)] = Nil // outcome of the count - - private var exhaustedBallots: Option[Set[B]] = None // outcome of the count - - private var lossByFraction: Option[Rational] = None // outcome of the count - - private var ignoredBallots: Option[Election[B]] = None // outcome of the count - - // private var tv: Option[Rational] = None - - // private var quota: Option[Rational] = None - - def setExhaustedBallots(exhballots: Set[B]): Unit = - exhaustedBallots = Some(exhballots) - - def getExhaustedBallots: Option[Set[B]] = - exhaustedBallots - - def setIgnoredBallots(iballots: Election[B]): Unit = - ignoredBallots = Some(iballots) - - def getIgnoredBallots: Option[Election[B]] = - ignoredBallots - - def setLossByFraction(lbf: Rational): Unit = - lossByFraction = Some(lbf) - - def getLossByFraction: Rational = { - lossByFraction match { - case Some(lbf) => lbf - case None => throw new Exception("Loss by Fraction is not set.") - } - } - - def addWinners(w: List[(Candidate, Rational)]): Unit = - winners = w.sortBy(x => x._2) ++ winners - - def getWinners: List[(Candidate, Rational)] = - winners - - def setAction(a: Action): Unit = - action = Some(a) - - def getAction: Action = { - action match { - case Some(a) => a - case None => throw new Exception("Action is not set.") - } - } - - def setInitiator(c: Candidate): Unit = - initiator = Some(c) - - def getInitiator: Candidate = { - initiator match { - case Some(c) => c - case None => new Candidate("no initiator") - } - - } - - def setElection(e: Election[B]): Unit = - election = Some(e) - - def setTotals(s: Map[Candidate, Rational]): Unit = - totals = Some(s) - - def getTotals: Map[Candidate, Rational] = { - totals match { - case Some(pt) => pt - case None => Map() // throw new Exception("Progressive totals are not set in Report yet.") - } - } - - /* - def setNumVotesReceived(m: Map[Candidate, Rational]) ={ - numVotesReceived = Some(m) - } - - def getNumVotesReceived: Map[Candidate, Rational] = { - numVotesReceived match { - case Some(m) => m - case None => Map() //throw new Exception("Progressive totals are not set in Report yet.") - } - } - * - */ - -} diff --git a/src/main/scala/agora/model/Election.scala b/src/main/scala/agora/model/Election.scala deleted file mode 100644 index 47f05b9..0000000 --- a/src/main/scala/agora/model/Election.scala +++ /dev/null @@ -1,60 +0,0 @@ -package agora.model - -import scala.collection._ -import scala.collection.generic._ -import scala.collection.mutable.Builder -import scala.collection.mutable.ListBuffer -import scala.collection.mutable.{HashMap => MMap} -import scala.collection.mutable.{HashSet => MSet} - -import scala.language.implicitConversions -import spire.math.Rational -import agora.votecounter.stv.ACTBallot - -class Election[+B <: Ballot](val ballots: Seq[B]) extends Seq[B] with SeqLike[B, Election[B]] { - - override def companion = ballots.companion - - def iterator = ballots.iterator - - def apply(i: Int) = ballots(i) - - def length = ballots.length - - override protected[this] def newBuilder = Election.newBuilder - - override def toString = ballots.map(_.toString).mkString("\n") - - def firstVotes(candidates: List[Candidate]): Map[Candidate, Rational] = { - val m = new MMap[Candidate, Rational] - - for { - b <- ballots - (c, t) <- b.firstVotes - } m(c) = t * b.weight + m.getOrElse(c, 0) - - m - } - - lazy val weight = ((ballots.map(_.weight)) :\ Rational(0, 1))(_ + _) - -} - -object Election { - - def newBuilder[B <: Ballot] = new mutable.Builder[B, Election[B]] { - private[this] val base = Seq().genericBuilder[B] - override def +=(e: B) = { base += e; this } - override def clear() = base.clear() - override def result() = new Election[B](base.result()) - } - - implicit def canBuildFrom[B <: Ballot] = new CanBuildFrom[Election[_], B, Election[B]] { - def apply(from: Election[_]): Builder[B, Election[B]] = newBuilder - def apply(): Builder[B, Election[B]] = newBuilder - } - - // def apply[B <: Ballot](ballots: B*) = new Election(ballots) - def apply[B <: Ballot](ballots: Seq[B]) = new Election(ballots) - -} diff --git a/src/main/scala/agora/model/Parameters.scala b/src/main/scala/agora/model/Parameters.scala deleted file mode 100644 index cc9c3c7..0000000 --- a/src/main/scala/agora/model/Parameters.scala +++ /dev/null @@ -1,66 +0,0 @@ -package agora.model - -import play.api.libs.functional.syntax._ -import play.api.libs.json._ - -case class MajorityBonus(jackpot: Double, bonus: Double) - -object MajorityBonus { - - implicit val majorityBonusReader: Reads[MajorityBonus] = - (__ \ "jackpot").read[Double].and((__ \ "bonus").read[Double])(MajorityBonus.apply _) - - implicit val majorityBonusWriter: Writes[MajorityBonus] = - (__ \ "jackpot").write[Double].and((__ \ "bonus").write[Double])(unlift(MajorityBonus.unapply)) - -} - -case class ComparisonSets(set1: Array[String], set2: Array[String]) - -object ComparisonSets { - - implicit val comparisonSetsReader: Reads[ComparisonSets] = - (__ \ "set1").read[Array[String]].and((__ \ "set2").read[Array[String]])(ComparisonSets.apply _) - - implicit val comparisonSetsWriter: Writes[ComparisonSets] = - (__ \ "set1") - .write[Array[String]] - .and((__ \ "set2").write[Array[String]])(unlift(ComparisonSets.unapply)) - -} - -case class Parameters( - comparisonOrder: Option[Array[String]] = None, - allowedVote: Option[Int] = None, - cutOffQuota: Option[Double] = None, - proportionalRatio: Option[Double] = None, - majorityBonus: Option[MajorityBonus] = None, - probabilityDistribution: Option[Array[Map[String, Double]]] = None, - comparisonSets: Option[ComparisonSets] = None, - majorityPercentage: Option[Double] = None -) - -object Parameters { - - /*implicit val methodParamWriter: Writes[Parameters] = ( - (__ \ "comparison_order").write[Array[String]] and - (__ \ "allowed_vote").write[Int] and - (__ \ "cut_off_quota").write[Double] and - (__ \ "proportional_ratio").write[Double] and - (__ \ "majority_bonus").write[MajorityBonus] and - (__ \ "probability_distribution").write[Array[Double]] and - (__ \ "comparison_sets").write[ComparisonSets] - ) (unlift(Parameters.unapply))*/ - - implicit val methodParameterReader: Reads[Parameters] = - (__ \ "comparison_order") - .readNullable[Array[String]] - .and((__ \ "allowed_vote").readNullable[Int]) - .and((__ \ "cut_off_quota").readNullable[Double]) - .and((__ \ "proportional_ratio").readNullable[Double]) - .and((__ \ "majority_bonus").readNullable[MajorityBonus]) - .and((__ \ "probability_distribution").readNullable[Array[Map[String, Double]]]) - .and((__ \ "comparison_sets").readNullable[ComparisonSets]) - .and((__ \ "majorityPercentage").readNullable[Double])(Parameters.apply _) - -} diff --git a/src/main/scala/agora/model/Report.scala b/src/main/scala/agora/model/Report.scala deleted file mode 100644 index 36be910..0000000 --- a/src/main/scala/agora/model/Report.scala +++ /dev/null @@ -1,298 +0,0 @@ -package agora.model - -import java.io._ -import scala.collection.mutable.{HashMap => MMap} -import spire.math.Rational -import agora.votecounter.stv.Action - -import scala.collection.Map - -class Report[B <: Ballot] { - - private var countHistory: List[Count[B]] = Nil - // private var totals: Map[Candidate, Rational] = Map() - - private var candidates: List[Candidate] = Nil - - private var quota: Option[Rational] = None - - private var numVacancies: Option[Int] = None - - private var winners: List[(Candidate, Rational)] = Nil - - private var stabilityAnalysis: List[(String, Double, Double)] = Nil - - def clear: Unit = { - countHistory = Nil - candidates = Nil - quota = None - numVacancies = None - winners = Nil - } - - def setLossByFractionToZero: Unit = - countHistory.head.setLossByFraction(Rational(0, 1)) - - def setLossByFraction( - oldtotals: Map[Candidate, Rational], - newtotals: Map[Candidate, Rational] - ): Unit = { - - def sumTotals(totals: Map[Candidate, Rational]): Rational = { - var sum: Rational = 0 - for (t <- totals) sum += t._2 - sum - } - - var sumoldtotals = sumTotals(oldtotals) - var sumnewtotals = sumTotals(newtotals) - - // println("sumoldtotals " + sumoldtotals) - // println("sumnewtotals " + sumnewtotals) - - countHistory.head.setLossByFraction(sumoldtotals - sumnewtotals) - } - - def setIgnoredBallots(ignoredBallots: Election[B]): Unit = - countHistory.head.setIgnoredBallots(ignoredBallots) - - def setNumVacancies(n: Int): Unit = - numVacancies = Some(n) - - def getNumVacancies: Option[Int] = - numVacancies - - def setCandidates(cands: List[Candidate]): Unit = - candidates = cands - - def getCandidates: List[Candidate] = - candidates - - def setQuota(q: Rational): Unit = - quota = Some(q) - - def getQuota: Rational = { - quota match { - case Some(q) => q - case None => throw new Exception("quota is not set yet.") - } - } - - def newCount( - action: Action, - initiator: Option[Candidate], - relection: Option[Election[B]], - totals: Option[Map[Candidate, Rational]], - winners: Option[List[(Candidate, Rational)]], - exhaustedBallots: Option[Set[B]] - ): Unit = { - - val count = new Count[B] - - count.setAction(action) - - initiator match { - case Some(i) => count.setInitiator(i) - case None => - } - - relection match { // election resulting from the action - case Some(e) => // count.setElection(e) - // TODO: commented because was taking much memory. Find a better solution (make a hash table for marked ballots). - case None => - } - - totals match { - case Some(pt) => count.setTotals(pt) - case None => - } - - winners match { - case Some(w) => count.addWinners(w) - case None => - } - - exhaustedBallots match { - case Some(eb) => count.setExhaustedBallots(eb) - case None => - } - - countHistory = count :: countHistory - } - - def getCountHistory: List[Count[B]] = - countHistory - - def setWinners(ws: List[(Candidate, Rational)]): Unit = - winners = ws - - def getWinners: List[(Candidate, Rational)] = - winners - - def writeWinners(file: String): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))) - // writer.write(result.getWinners.toString()) - var sw = "" - println("\n WINNERS \n") - for (w <- winners) { - println(w._1 + ": " + w._2.numerator.toFloat / w._2.denominator.toFloat + "\n") - sw = sw + w._1 + ": " + w._2.numerator.toFloat / w._2.denominator.toFloat + "\n" - } - writer.write(sw) - writer.close() - } - - def setStabilityAnalysis(sa: List[(String, Double, Double)]): Unit = - stabilityAnalysis = sa - - def writeStabilityAnalysis(file: String): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))) - // writer.write(result.getWinners.toString()) - var sw = "" - for (analysis <- stabilityAnalysis) - sw = - sw + analysis._1 + "\n" + "Average Ratio: " + analysis._2 + "\n" + "Variance Ratio: " + analysis._3 + "\n\n\n"; - writer.write(sw) - writer.close() - } - - def writeDistributionOfPreferencesACT(file: String, order: Option[List[Candidate]]): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))) - - val separator = "," - - var tableorder: List[Candidate] = Nil - order match { - case Some(o) => tableorder = o - case None => tableorder = candidates - } - - var countnum = 0 - - for (count <- countHistory.reverse) { - // println(countr.getNumVotesReceived) - countnum += 1 - var line: String = countnum + separator - - if (countnum == 1) { - for (c <- tableorder) { - if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator - } else { - line += separator - } - } - writer.write(line + "\n") - countnum += 1 - line = countnum + separator - } - - for (c <- tableorder) { - if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator - } else { - line += separator - } - } - - // val exhaustedBallots = count.getExhaustedBallots - // val ignoredBallots = count.getIgnoredBallots - - writer.write(line + "\n") - } - // rwriter.write("Candidates: " + report.getCandidates.toString()+"\n") - // rwriter.write("Number of seats: " + report.getNumVacancies.toString()+"\n") - // rwriter.write("Quota: " + report.getQuota.toString()) - writer.close - } - - // scalastyle:off cyclomatic.complexity - // scalastyle:off method.length - def writeDistributionOfPreferences(file: String, order: Option[List[Candidate]]): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))) - - val separator = "," - - var tableorder: List[Candidate] = Nil - order match { - case Some(o) => tableorder = o - case None => tableorder = candidates - } - - writer.write("Count" + separator) - var countnum = -1 - tableorder.foreach(c => writer.write(c + separator)) - writer.write( - "Initiator" + separator + - "Action" + separator + - "Winners" + separator + - "~ Loss by Fraction" + separator + - "N Exhausted Ballots" + separator + - "~ Exhausted Votes" + separator + - "N Ignored Ballots" + separator + - "~ Ignored Votes" - + "\n" - ) - - for (count <- countHistory.reverse) { - - countnum += 1 - var line: String = countnum + separator - - for (c <- tableorder) { - if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator - } else { - line += separator - } - } - - var winners = "" - for (w <- count.getWinners if count.getWinners.nonEmpty) - winners += w._1 + " (" + w._2 + "); " - - line += count.getInitiator + separator + count.getAction + separator + winners + separator + count.getLossByFraction.toInt + separator - - val exhaustedBallots = count.getExhaustedBallots - val ignoredBallots = count.getIgnoredBallots - - var exhhaustedandignored: String = "" - exhaustedBallots match { - case Some(eB) => - var totalweighteB: Rational = 0 - for (b <- eB) - totalweighteB += b.weight - exhhaustedandignored += eB.size + separator + totalweighteB.toInt + separator - case None => - exhhaustedandignored += " " + separator - } - ignoredBallots match { - case Some(iB) => - val writer = new BufferedWriter( - new OutputStreamWriter( - new FileOutputStream(file + "IgnoredBallots_Count" + countnum + ".txt") - ) - ) - - var s = "" - - for (b <- iB) - s += b.toString + "\n" - - writer.write(s) - writer.close() - - var totalweightiB: Rational = 0 - for (b <- iB) - totalweightiB += b.weight - exhhaustedandignored += iB.size + separator + totalweightiB.toInt - case None => - } - - line += exhhaustedandignored - writer.write(line + "\n") - } - writer.close - } - -} diff --git a/src/main/scala/agora/model/Result.scala b/src/main/scala/agora/model/Result.scala deleted file mode 100644 index feb65ff..0000000 --- a/src/main/scala/agora/model/Result.scala +++ /dev/null @@ -1,89 +0,0 @@ -package agora.model - -import collection.mutable.{HashMap => MMap} -import spire.math.Rational -import scala.collection.Map - -class Result { - - private var quota: Option[Rational] = None - - private var excludedCandidates: List[(Candidate, Rational)] = Nil - - private var pendingWinners: List[(Candidate, Rational, Option[Set[Int]])] = Nil - - private var totalsHistory: List[Map[Candidate, Rational]] = - Nil // required for ACT's ties resolutions - - private var winners: List[(Candidate, Rational)] = Nil - - def clear: Unit = { - quota = None - excludedCandidates = Nil - pendingWinners = Nil - totalsHistory = Nil - winners = Nil - } - - def setQuota(q: Rational): Unit = - quota = Some(q) - - def getQuota: Rational = { - quota match { - case Some(q) => q - case None => throw new Exception("quota is not set yet.") - } - } - - def addPendingWinners( - pendWinners: List[(Candidate, Rational)], - markings: Option[Set[Int]] - ): Unit = { - if (pendWinners.nonEmpty) { - for (w <- pendWinners) - if (!pendingWinners.contains(w)) { - // println("Adding yet undistributed winner " + w + " with markings " + markings.toList.sorted) - addPendingWinner(w._1, w._2, markings) - } - } - } - - def addPendingWinner(candidate: Candidate, total: Rational, markings: Option[Set[Int]]): Unit = - // markings match { - // case Some(mrks) => pendingWinners = pendingWinners :+ (candidate, total, mrks) // !!! is it adding at the end of the list? - // case None => pendingWinners = pendingWinners :+ (candidate, total, None) // !!! is it adding at the end of the list? - // } - pendingWinners = pendingWinners :+ (candidate, total, markings) - - def getPendingWinners: List[(Candidate, Rational, Option[Set[Int]])] = - pendingWinners - - def takeAndRemoveFirstPendingWinner: (Candidate, Rational, Option[Set[Int]]) = { - val h = pendingWinners.head - pendingWinners = pendingWinners.tail - h - } - - def takeButRetainFirstPendingWinner: (Candidate, Rational, Option[Set[Int]]) = - pendingWinners.head - - def removePendingWinner(c: Candidate): Unit = - pendingWinners = pendingWinners.filterNot(p => p._1.name == c.name) - - def addExcludedCandidate(candidate: Candidate, total: Rational): Unit = - excludedCandidates = (candidate, total) :: excludedCandidates - // continuingCandidates = continuingCandidates diff List(candidate) - - def addTotalsToHistory(totals: Map[Candidate, Rational]): Unit = - totalsHistory = totals :: totalsHistory - - def getTotalsHistoryClone: List[Map[Candidate, Rational]] = - totalsHistory - - def setWinners(ws: List[(Candidate, Rational)]): Unit = - winners = ws - - def getWinners: List[(Candidate, Rational)] = - winners - -} diff --git a/src/main/scala/agora/parser/CandidatesParser.scala b/src/main/scala/agora/parser/CandidatesParser.scala deleted file mode 100644 index 91f4736..0000000 --- a/src/main/scala/agora/parser/CandidatesParser.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.parser - -import agora._ -import agora.model._ - -import scala.util.parsing.combinator._ - -object CandidatesParser extends LineParser[Candidate] { - - def read(filename: String) = readLines(filename) - - // the method line returns a Parser of type ACTBallotPapersDataStructure - def line: Parser[Candidate] = name ~ opt(id) ~ opt(party) ^^ { - case ~(~(name, id), party) => Candidate(name, id, party) - case _ => throw new Exception - } - - def string: Parser[String] = """[0-9A-Za-z\-\,\.\ \']+""".r - - def name = string ^^ { _.toString } - - // obligatory semi-colon only if party present - def party = ";" ~ string ^^ { - case ~(";", string) => string - case _ => throw new Exception - } - - // obligatory semi-colon only if id exists - def id: Parser[Int] = ";" ~ """[0-9]+""".r ^^ { - case ~(";", number) => number.toInt - case _ => throw new Exception - } - -} diff --git a/src/main/scala/agora/parser/ElectionParser.scala b/src/main/scala/agora/parser/ElectionParser.scala deleted file mode 100644 index 084fcbd..0000000 --- a/src/main/scala/agora/parser/ElectionParser.scala +++ /dev/null @@ -1,37 +0,0 @@ -package agora.parser - -import agora.model._ - -import scala.io.Source -import java.io.FileReader -import java.io.FileNotFoundException -import java.io.IOException - -import scala.util.parsing.combinator._ - -abstract class ElectionParser[T <: Ballot] extends LineParser[T] { - - def line: Parser[T] - - def read(filename: String): Election[T] = Election(readLines(filename)) - -} - -abstract class LineParser[T] extends RegexParsers { - - def line: Parser[T] - - def readLines(filename: String): List[T] = { - val bufferedSource = io.Source.fromFile(filename) - val lines = bufferedSource.getLines.toList - val output = for (l <- lines) yield { - parse(line, l) match { - case Success(sucLine, _) => sucLine - case _ => throw new Exception("Parsing Error") - } - } - bufferedSource.close() - output - } - -} diff --git a/src/main/scala/agora/parser/ParameterParser.scala b/src/main/scala/agora/parser/ParameterParser.scala deleted file mode 100644 index f50879f..0000000 --- a/src/main/scala/agora/parser/ParameterParser.scala +++ /dev/null @@ -1,20 +0,0 @@ -package agora.parser - -import agora.model.Parameters -import play.api.libs.json.Json - -import scala.io.Source - -/** Created by deepeshpandey on 06/08/17. */ -object ParameterParser { - - def parse(fileName: String): Parameters = { - - val src = Source.fromFile(fileName) - val parameters = - Json.parse(src.getLines.mkString).as[Parameters](Parameters.methodParameterReader) - src.close() - parameters - } - -} diff --git a/src/main/scala/agora/parser/PreferencesParser.scala b/src/main/scala/agora/parser/PreferencesParser.scala deleted file mode 100644 index 13ab7d4..0000000 --- a/src/main/scala/agora/parser/PreferencesParser.scala +++ /dev/null @@ -1,121 +0,0 @@ -package agora.parser - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational - -import scala.util.parsing.combinator._ - -trait ElectionParsers extends RegexParsers { - - def candidate: Parser[Candidate] = """[0-9A-Za-z\-\,\.\ \']*""".r ^^ { s => Candidate(s) } - - def numerator: Parser[BigInt] = """[0-9]*""".r ^^ { s => BigInt(s) } - - def denominator: Parser[BigInt] = """[0-9]*""".r ^^ { s => BigInt(s) } - - def weight: Parser[Rational] = numerator ~ "/" ~ denominator ^^ { case ~(~(n, _), d) => - Rational(n, d) - } - - def id: Parser[Int] = """[0-9]+""".r ^^ { _.toInt } - - def rank: Parser[Int] = - """[0-9]+""".r ^^ { - _.toInt - } - - def score: Parser[Rational] = """[0-9\.]+""".r ^^ { case value => Rational(value) } - -} - -object PreferencesParser extends ElectionParser[Ballot] with RegexParsers with ElectionParsers { - - def preferences: Parser[List[Candidate]] = repsep(candidate, ">") - - def line: Parser[Ballot] = id ~ weight ~ preferences ^^ { case ~(~(i, w), prefs) => - new Ballot(prefs, i, w) - } - -} - -object PreferencesParserWithIndifference - extends ElectionParser[RankBallot] - with RegexParsers - with ElectionParsers { - - def line: Parser[RankBallot] = id ~ weight ~ preferences ^^ { case ~(~(i, w), prefs) => - RankBallot(prefs, i, w) - } - - def preferences: Parser[List[(Candidate, Int)]] = { - - var rank = 1 - ((candidate ^^ { cand => List((cand, rank)) }) ~ rep((">" ~ candidate) ^^ { - case ~(">", cand) => - rank = rank + 1 - (cand, rank) - case _ => throw new Exception - } | ("=" ~ candidate) ^^ { - case ~("=", cand) => (cand, rank) - case _ => throw new Exception - })) ^^ { case ~(list1, list2) => list1 ++ list2 } - } ^^ { case prefs => - prefs.sortWith { - case ((_, r1), (_, r2)) => r1 < r2 - case (_, _) => true - } - } - -} - -object PreferencesParserWithScore - extends ElectionParser[ScoreBallot] - with RegexParsers - with ElectionParsers { - - def line: Parser[ScoreBallot] = id ~ weight ~ opt("(") ~ preferences ~ opt(")") ^^ { - case ~(~(~(~(i, w), _), prefs), _) => - ScoreBallot(prefs, i, w) - } - - def preferences: Parser[List[(Candidate, Rational)]] = repsep(candidateWithScore, ")(") ^^ { - case prefs => - prefs.sortWith { - case ((_, s1), (_, s2)) => s1 > s2 - case (_, _) => true - } - } - - def candidateWithScore: Parser[(Candidate, Rational)] = candidate ~ ";" ~ score ^^ { - case ~(~(candidate, ";"), score) => - (candidate, score) - } - -} - -object PreferencesParserWithRank - extends ElectionParser[RankBallot] - with RegexParsers - with ElectionParsers { - - def line: Parser[RankBallot] = id ~ weight ~ opt("(") ~ preferences ~ opt(")") ^^ { - case ~(~(~(~(i, w), _), prefs), _) => - RankBallot(prefs, i, w) - } - - def preferences: Parser[List[(Candidate, Int)]] = repsep(candidateWithRank, ")(") ^^ { - case prefs => - prefs.sortWith { - case ((_, r1), (_, r2)) => r1 < r2 - case (_, _) => true - } - } - - def candidateWithRank: Parser[(Candidate, Int)] = candidate ~ ";" ~ rank ^^ { - case (~(~(candidate, ";"), rank)) => - (candidate, rank) - } - -} diff --git a/src/main/scala/agora/util/matrix/BaseMatrix.scala b/src/main/scala/agora/util/matrix/BaseMatrix.scala deleted file mode 100644 index bd068af..0000000 --- a/src/main/scala/agora/util/matrix/BaseMatrix.scala +++ /dev/null @@ -1,18 +0,0 @@ -package agora.util.matrix - -// TODO: Use Breeze - -/** 2 Dimensional matrix */ -object BaseMatrix { - - def apply[T: Manifest](rows: Int, cols: Int)(f: (Int, Int) => T): Array[Array[T]] = { - - val matrix = Array.ofDim[T](rows, cols) - for (i <- 0 until rows) - for (j <- 0 until cols) - matrix(i)(j) = f(i, j) - - matrix - } - -} diff --git a/src/main/scala/agora/util/matrix/package.scala b/src/main/scala/agora/util/matrix/package.scala deleted file mode 100644 index 1cd17ab..0000000 --- a/src/main/scala/agora/util/matrix/package.scala +++ /dev/null @@ -1,52 +0,0 @@ -package agora.util - -import agora.util.matrix.BaseMatrix - -import spire.math.Rational - -/** Created by deepeshpandey on 30/07/17. */ -package object matrix { - - def addMatrix(m1: Array[Array[Rational]], m2: Array[Array[Rational]]): Array[Array[Rational]] = { - - m1.zip(m2).map { rows: (Array[Rational], Array[Rational]) => - { - rows._1.zip(rows._2).map { items: (Rational, Rational) => - items._1 + items._2 - } - } - } - } - - def square(m: Array[Array[Rational]], size: Int): Array[Array[Rational]] = { - val squaredMatrix = for (m1 <- m) yield for (m2 <- transpose(m, size)) yield dotProduct(m1, m2) - - squaredMatrix - } - - def identityMatrix(size: Int): Array[Array[Rational]] = BaseMatrix[Rational](size, size) { - (i: Int, j: Int) => - { - if (i == j) { - Rational(1, 1) - } else { - Rational(0, 1) - } - } - } - - def dotProduct(row1: Array[Rational], row2: Array[Rational]): Rational = - row1.zip(row2).map { t: (Rational, Rational) => t._1 * t._2 }.reduce(_ + _) - - def transpose(matrix: Array[Array[Rational]], size: Int): Array[Array[Rational]] = { - for (i <- 0 until size) - for (j <- 0 until size) - if (i < j) { - val temp = matrix(i)(j) - matrix(i)(j) = matrix(j)(i) - matrix(i)(j) = temp - } - matrix - } - -} diff --git a/src/main/scala/agora/votecounter/ACT.scala b/src/main/scala/agora/votecounter/ACT.scala deleted file mode 100644 index ae05fe3..0000000 --- a/src/main/scala/agora/votecounter/ACT.scala +++ /dev/null @@ -1,385 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.votecounter.stv._ - -import spire.math.Rational - -import scala.collection.immutable.ListMap -//import collection.mutable.{HashMap => MMap} -import scala.collection.SortedMap -import collection.mutable.HashSet -import collection.breakOut -import scala.util.Random -import scala.util.Sorting -import java.io._ -import agora.votecounter.stv.VictoryWithoutQuota -import agora.votecounter.stv.SurplusDistribution -import agora.votecounter.stv.ACTBallot - -import scala.collection.Map - -abstract class ACT - extends STVAustralia - with DroopQuota - with NoFractionInQuota - with NewWinnersOrderedByTotals[ACTBallot] - with ACTSurplusDistributionTieResolution - with ACTFractionLoss - with ACTExclusion - with ACTExclusionTieResolution - with ACTExactWinnerRemoval { - -// val result: Result = new Result -// val report: Report[ACTBallot] = new Report[ACTBallot] - - def declareNewWinnersWhileExcluding( - candidate: Candidate, - exhaustedBallots: Set[ACTBallot], - newtotals: Map[Candidate, Rational], - totalsWithoutNewWinners: Map[Candidate, Rational], - newElectionWithoutFractionInTotals: Election[ACTBallot] - ): List[(Candidate, Rational)] - - def declareNewWinnersWhileDistributingSurpluses( - totals: Map[Candidate, Rational], - election: Election[ACTBallot] - ): List[(Candidate, Rational)] - - def rewriteTotalOfCandidate( - totals: Map[Candidate, Rational], - candidate: Candidate, - newTotal: Option[Int] - ): Map[Candidate, Rational] - - def computeIncorrectTotalofEVACS( - step: (Candidate, Rational), - newElectionWithoutFractionInTotals: Election[ACTBallot] - ): Option[Int] - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def filterBallotsWithFirstPreferences( - election: Election[ACTBallot], - preferences: List[Candidate] - ): Election[ACTBallot] = { - var ballots: List[ACTBallot] = List() - for (b <- election) - if (b.preferences.take(preferences.length) == preferences) ballots = b :: ballots - Election(ballots) - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def winners( - election: Election[ACTBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - println(" \n NEW RECURSIVE CALL \n") - - // println("Election: " + election) - - if (election.isEmpty) { - Nil - } // If all ballots are removed by the candidate who reached the quota exactly, the election will be empty. - // For example (3 seats, quota=2): - // 1 1/1 2 - // 2 1/1 2 - // 3 1/1 2 - // 4 1/1 5>6>1 - // 5 1/1 5>3>6 - else { - - // val ccands = getCandidates(election) - println("Continuing candidates: " + ccandidates) - - val tls = election.firstVotes(ccandidates) - println("Totals: " + tls) - - // result.addTotalsToHistory(totals) - - // Notice: There may be more new winners than available vacancies!!! - // Apparently EVACS does not check this condition. See step 8. Or in count.c - // while (for_each_candidate(e->candidates, &check_status, - // (void *)(CAND_PENDING|CAND_ELECTED)) - // != e->electorate->num_seats) { - // That is why we also check only equality here - if (ccandidates.length == numVacancies) { - var ws: List[(Candidate, Rational)] = List() - for (c <- ccandidates) ws = (c, tls.getOrElse(c, Rational(0, 1))) :: ws - report.newCount(VictoryWithoutQuota, None, None, None, Some(ws), None) - report.setLossByFractionToZero - for (c <- ccandidates) yield (c, tls.getOrElse(c, Rational(0, 1))) - } else { - quotaReached(tls, result.getQuota) match { - case true => - val ws: List[(Candidate, Rational)] = - returnNewWinners(tls, result.getQuota) // sorted! tie resolved! - println("New winners: " + ws) - result.addPendingWinners(ws.toList, Some(extractMarkings(election))) - - val vacanciesFilled = ws.length >= numVacancies - - vacanciesFilled match { - case false => - println("Vacancies: not yet filled.") - val res = surplusesDistribution(election, ccandidates, numVacancies - ws.length) - val newElection: Election[ACTBallot] = res._1 - val newWinners: List[(Candidate, Rational)] = res._2 - - val nws = ws.length + newWinners.length - println("Number of winners in this recursive call: " + nws) - val allWinners = ws ::: newWinners - if (nws == numVacancies) { allWinners } - else { - val setAllWinners = allWinners.map(_._1).toSet - winners( - newElection, - ccandidates.filterNot(setAllWinners.contains(_)), - numVacancies - nws - ) ::: allWinners - // TODO: care should be taken that newElection is not empty?! - } - case true => ws - } - case false => - val leastVotedCandidate = chooseCandidateForExclusion(tls) - println("Candidate to be excluded: " + leastVotedCandidate) - result.addExcludedCandidate(leastVotedCandidate._1, leastVotedCandidate._2) - - val res = exclusion(election, ccandidates, leastVotedCandidate, numVacancies) - val newElection: Election[ACTBallot] = res._1 - val newWinners: List[(Candidate, Rational)] = res._2 - - println("New winners: " + newWinners) - println("Number of winners in this recursive call: " + newWinners.length) - if (newWinners.length == numVacancies) { - // Notice: There may be more new winners than available vacancies!!! - // Apparently EVACS does not check this condition. See step 42. Or in count.c - // if (for_each_candidate(candidates, &check_status,(void *)(CAND_ELECTED|CAND_PENDING)) == num_seats) return true; - newWinners - } else { - winners( - newElection, - ccandidates.filterNot(x => x == leastVotedCandidate._1), - numVacancies - newWinners.length - ) ::: newWinners - } - } - - } - } - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def extractMarkings(election: Election[ACTBallot]): Set[Int] = { - var markings: Set[Int] = Set() - for (b <- election) { - if (b.marking) { - markings += b.id - } - } - markings - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def surplusesDistribution( - election: Election[ACTBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - println("Distribution of surpluses.") - var newws: List[(Candidate, Rational)] = List() - var newElection = election - - while (result.getPendingWinners.nonEmpty && newws.length != numVacancies) { - val (cand, ctotal, markings) = result.takeAndRemoveFirstPendingWinner - val res = tryToDistributeSurplusVotes(newElection, ccandidates, cand, ctotal, markings) - newElection = res._1 - newws = newws ::: res._2 - println("Are there pending candidates? " + result.getPendingWinners.nonEmpty) - } - (newElection, newws) - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def tryToDistributeSurplusVotes( - election: Election[ACTBallot], - ccandidates: List[Candidate], - winner: Candidate, - ctotal: Rational, - markings: Option[Set[Int]] - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - - val pendingWinners = result.getPendingWinners.map(x => x._1) - - if (ctotal == result.getQuota) { - val newElection = removeWinnerWithoutSurplusFromElection(election, winner) - result.removePendingWinner(winner) - (newElection, List()) - } else - // NOTE THAT WHEN (!ballotsAreContinuing(winner, election, pendingWinners)) THE ELECTION DOES NOT CHANGE - // - // if (!ballotsAreContinuing(winner, election, pendingWinners) ) { - // val newElection = ??? - // result.removePendingWinner(winner) - // (newElection, List()) - // } - // else - { - println("Distributing the surplus of " + winner) - - val surplus = ctotal - result.getQuota - - val tv = computeTransferValue(surplus, election, pendingWinners, winner, markings) - println("tv = " + tv) - - val (newElection, exhaustedBallots, ignoredBallots) = - distributeSurplusVotes(election, winner, ctotal, markings, pendingWinners, tv) - val newElectionWithoutFractionInTotals = loseFraction(newElection, ccandidates) - - val newtotalsWithoutFraction = newElectionWithoutFractionInTotals.firstVotes(ccandidates) - val newtotalsWithoutFractionWithoutpendingwinners = - newtotalsWithoutFraction.filterKeys(!pendingWinners.contains(_)) - - result.removePendingWinner(winner) - - result.addTotalsToHistory(newtotalsWithoutFractionWithoutpendingwinners) - var ws = declareNewWinnersWhileDistributingSurpluses( - newtotalsWithoutFractionWithoutpendingwinners, - newElection - ) - - // ------------ Reporting ------------------------------------------ - if (ws.nonEmpty) { - report.newCount( - SurplusDistribution, - Some(winner), - Some(newElectionWithoutFractionInTotals), - Some(newtotalsWithoutFraction), - Some(ws), - Some(exhaustedBallots) - ) - } else { - report.newCount( - SurplusDistribution, - Some(winner), - Some(newElectionWithoutFractionInTotals), - Some(newtotalsWithoutFraction), - None, - Some(exhaustedBallots) - ) - } - report.setLossByFraction(newElection.firstVotes(ccandidates), newtotalsWithoutFraction) - ignoredBallots match { // ballots ignored because they don't belong to the last parcel of the winner - case Some(ib) => report.setIgnoredBallots(ib) - case None => - } - // ------------------------------------------------------------------ - - (newElectionWithoutFractionInTotals, ws) - } - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def exclusion( - election: Election[ACTBallot], - ccandidates: List[Candidate], - candidate: (Candidate, Rational), - numVacancies: Int - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - println("Vacancies left: " + numVacancies) - - var ws: List[(Candidate, Rational)] = List() - var newws: List[(Candidate, Rational)] = List() - var newElection = election - var newElectionWithoutFractionInTotals = election - var exhaustedBallots: Set[ACTBallot] = Set() - - if (candidate._2 == Rational(0, 1)) { - println("Excluding candidate with zero votes: " + candidate) - - val ex = excludeZero(election, candidate._1) - - (ex._1, ws) - } else { - var steps = determineStepsOfExclusion(election, candidate._1) - - while (ws.length != numVacancies && !steps.isEmpty) { - val step = steps.head - println("Step of exclusion: " + step) - steps = steps.tail // any better way to do this? - - val newTotal = computeIncorrectTotalofEVACS( - step, - newElectionWithoutFractionInTotals - ) // simulating EVACS's incorrect total as a result of partial exclusion - - val ex = exclude( - newElectionWithoutFractionInTotals, - step._1, - Some(step._2), - Some(newws.map(x => x._1)) - ) - - newElection = ex._1 - - exhaustedBallots = ex._2 - - val totalsBeforeFractionLoss = newElection.firstVotes(ccandidates) // for computing LbF - - newElectionWithoutFractionInTotals = loseFraction( - newElection, - ccandidates - ) // perhaps it is better to get rid of newws in a separate function - - val totalsAfterFractionLoss = newElectionWithoutFractionInTotals.firstVotes(ccandidates) - - val totalsWithIncorrectValueForCandidate = - rewriteTotalOfCandidate(totalsAfterFractionLoss, candidate._1, newTotal) - // simulating EVACS's incorrect total as a result of partial exclusion - - val totalsWithoutNewWinners = - totalsWithIncorrectValueForCandidate.filterKeys(k => !ws.map(_._1).contains(k)) - // excluding winners that are already identified in the while-loop - - result.addTotalsToHistory(totalsWithIncorrectValueForCandidate) - println("Totals: " + totalsWithIncorrectValueForCandidate) - - newws = declareNewWinnersWhileExcluding( - candidate._1, - exhaustedBallots, - totalsWithIncorrectValueForCandidate, - totalsWithoutNewWinners, - newElectionWithoutFractionInTotals - ) - - ws = ws ::: newws - - report.setLossByFraction(totalsBeforeFractionLoss, totalsWithIncorrectValueForCandidate) - // report.setIgnoredBallots(List()) - } - // TODO distribute remaining votes - // if (vacanciesFilled(ws.length, numVacancies)) { - // } - var dws: List[(Candidate, Rational)] = List() - if (ws.nonEmpty) { - val res = surplusesDistribution( - newElectionWithoutFractionInTotals, - ccandidates.filterNot(x => x == candidate._1), - numVacancies - ws.length - ) - newElectionWithoutFractionInTotals = res._1 - dws = res._2 - } - - (newElectionWithoutFractionInTotals, ws ::: dws) - } - } - -} diff --git a/src/main/scala/agora/votecounter/ApprovalRule.scala b/src/main/scala/agora/votecounter/ApprovalRule.scala deleted file mode 100644 index 5ab04cb..0000000 --- a/src/main/scala/agora/votecounter/ApprovalRule.scala +++ /dev/null @@ -1,22 +0,0 @@ -package agora.votecounter - -import agora.model.{PreferenceBallot => Ballot} -import agora.model._ - -import spire.math.Rational - -import scala.collection.mutable.{HashMap => MMap} - -object ApprovalRule extends VoteCounter[Ballot] with SimpleApproval { - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val tls = countApprovals(election, ccandidates) - tls.toList.sortWith(_._2 > _._2).take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/AustralianSenate.scala b/src/main/scala/agora/votecounter/AustralianSenate.scala deleted file mode 100644 index 91e9e24..0000000 --- a/src/main/scala/agora/votecounter/AustralianSenate.scala +++ /dev/null @@ -1,635 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.votecounter.stv._ - -import spire.math.Rational - -import scala.collection.immutable.ListMap -import collection.mutable.{HashMap => MMap} -import collection.Map -import scala.collection.SortedMap -import collection.mutable.HashSet -import collection.breakOut -import scala.util.Random -import scala.util.Sorting -import java.io._ -import agora.votecounter.stv.VictoryWithoutQuota -import agora.votecounter.stv.TwoLastCandidatesForOneVacancy -import agora.votecounter.stv.SurplusDistribution -import agora.votecounter.stv.ACTBallot - -sealed abstract class BulkExclusionType - -case object ExclusionBulk extends BulkExclusionType - -case object SurplusDistributionBulk extends BulkExclusionType - -class AustralianSenate - extends STVAustralia - with DroopQuota // Section 273 (8) - with NoFractionInQuota // Section 273 (8) - with NewWinnersOrderedByTotals[ACTBallot] // TODO - with SenateSurplusDistributionTieResolution // Section 273 (22) - with ACTFractionLoss // - with SenateExclusion // exactly like ACTExclusion - with SenateExactWinnerRemoval // exactly like ACTExactWinnerRemoval - with TransferValueWithDenominatorWithNumOfBallots // Section 273 (9)(a) - with SenateSurplusDistribution // Section 273 (9)(b) - with SenateNewWinnersDuringSurplusesDistribution - with SenateNewWinnersDuringExclusion - with UnfairExclusionTieResolution // TODO - { - - // def declareNewWinnersWhileDistributingSurpluses(totals: Map[Candidate, Rational], election:Election[ACTBallot]): List[(Candidate,Rational)] - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Functions for Bulk Exclusion - Section 273 (13A), (13B), (13C) -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Section 273 (29) - def computeNotionalVotes(candidate: Candidate, totals: Map[Candidate, Rational]): Rational = - totals.filter(p => p._2 < totals(candidate)).foldLeft(Rational(0, 1))(_ + _._2) - - def computeAdjustedNotionalVotes( - candidate: Candidate, - totals: Map[Candidate, Rational], - surplus: Option[Rational] - ): Rational = { - surplus match { - case Some(s) => - computeNotionalVotes(candidate: Candidate, totals: Map[Candidate, Rational]) + s - case None => throw new Exception("Surplus is None in computeAdjustedNotionalVotes.") - } - } - - def computeShortfall( - candidate: Candidate, - totals: Map[Candidate, Rational], - quota: Rational - ): Rational = - quota - totals(candidate) - - def returnLeadingShortfall(totals: Map[Candidate, Rational], quota: Rational): Rational = - quota - totals.valuesIterator.max - - def computeVacancyShortfall( - totals: Map[Candidate, Rational], - numRemainingVacancies: Int, - quota: Rational - ): Rational = { - val orderedTotals = totals.toList.sortBy(_._2).reverse - // it does not matter how tie of equal values is resolved here, because we care only about values, hence - simple sort - // orderedTotals.take(numRemainingVacancies).foldLeft(Rational(0,1))(_+(quota-totals(_._2))) - var aggregate = Rational(0, 1) - for (candidate <- orderedTotals.take(numRemainingVacancies)) - aggregate += (quota - totals(candidate._1)) - aggregate - } - - def returnCandidateA( - totals: Map[Candidate, Rational], - vacancyShortfall: Rational, - bulktype: BulkExclusionType, - surplus: Option[Rational] - ): Option[Candidate] = { - - var pickedTotals: Map[Candidate, Rational] = Map() - bulktype match { - case ExclusionBulk => - pickedTotals = totals.filter(p => computeNotionalVotes(p._1, totals) >= vacancyShortfall) // Section 273 (13A)(a) - case SurplusDistributionBulk => - pickedTotals = totals.filter(p => - computeAdjustedNotionalVotes(p._1, totals, surplus) >= vacancyShortfall - ) // Section 273 (13A)(a) - } - if (pickedTotals.nonEmpty) { - pickedTotals.filter(p => p._2 == pickedTotals.valuesIterator.min) - if (pickedTotals.size != 1) { - // not specified in the legislation how this case should be addressed. - // Section 273 (13A)(a) says "stands lower or lowest in the poll" - println( - "More than one candidate satisfy conditions of Candidate A: Section 273 (13A)(a). One of them is picked: " + pickedTotals.head._1 - ) - Some(pickedTotals.head._1) - } else { - Some(pickedTotals.head._1) - } - } else { - None // CandidateA is unidentified - } - } - - def returnCandidateB( - totals: Map[Candidate, Rational], - candidateA: Option[Candidate], - vacancyShortfall: Rational, - bulktype: BulkExclusionType, - surplus: Option[Rational] - ): Option[Candidate] = { - - var totalsOfCandidatesPotentiallyB: Map[Candidate, Rational] = Map() - candidateA match { - case Some(cA) => - totalsOfCandidatesPotentiallyB = totals.filter(p => p._2 < totals(cA)) - // val candidateB = totals.clone().filter(p => p._2 == valueOfCandidateB).head._1 - case None => - bulktype match { - case ExclusionBulk => - totalsOfCandidatesPotentiallyB = - totals.filter(p => computeNotionalVotes(p._1, totals) < vacancyShortfall) - case SurplusDistributionBulk => - totalsOfCandidatesPotentiallyB = totals.filter(p => - computeAdjustedNotionalVotes(p._1, totals, surplus) < vacancyShortfall - ) - } - // val candidateB = totals.clone().filter(p => p._2 == valueOfCandidateB).head._1 - } - if (totalsOfCandidatesPotentiallyB.nonEmpty) { - val orderedTotalsOfCandidatesPotentiallyB = - totalsOfCandidatesPotentiallyB.toList.sortBy(_._2) // TODO: sort appropriately - val candidatesB = - for ( - (left, right) <- - orderedTotalsOfCandidatesPotentiallyB.zip(orderedTotalsOfCandidatesPotentiallyB.tail) - if computeNotionalVotes(left._1, totals) < right._2 - ) yield left - if (candidatesB.nonEmpty) Some(candidatesB.head._1) else None // TODO: tail? - } else { - None - } - - } - - def returnCandidateC( - totals: Map[Candidate, Rational], - leadingShortFall: Rational, - bulktype: BulkExclusionType, - surplus: Option[Rational] - ): Option[Candidate] = { - - var potentialCandidatesC: Map[Candidate, Rational] = Map() - bulktype match { - case ExclusionBulk => - potentialCandidatesC = - totals.filter(p => computeNotionalVotes(p._1, totals) < leadingShortFall) - case SurplusDistributionBulk => - potentialCandidatesC = - totals.filter(p => computeAdjustedNotionalVotes(p._1, totals, surplus) < leadingShortFall) - } - println("potentialCandidatesC " + potentialCandidatesC) - if (potentialCandidatesC.nonEmpty) { - Some(potentialCandidatesC.toList.sortBy(_._2).head._1) // TODO: sort appropriately - } else { - None - } - } - - def selectCandidatesForBulkExclusion( - totals: Map[Candidate, Rational], - numRemainingVacancies: Int, - quota: Rational, - bulktype: BulkExclusionType, - surplus: Option[Rational] - ): List[(Candidate, Rational)] = { - - val orderedCandidates = totals.toList.sortBy(_._2) // TODO: sort appropriately - println("orderedCandidates: " + orderedCandidates) - val vacancyShortfall = computeVacancyShortfall(totals, numRemainingVacancies, quota) - println("vacancyShortfall: " + vacancyShortfall) - var candidateB: Option[Candidate] = None - bulktype match { - case ExclusionBulk => - val candidateA = returnCandidateA(totals, vacancyShortfall, ExclusionBulk, None) - println("candidateA: " + candidateA) - candidateB = returnCandidateB(totals, candidateA, vacancyShortfall, ExclusionBulk, None) - println("candidateB: " + candidateB) - case SurplusDistributionBulk => - val candidateA = - returnCandidateA(totals, vacancyShortfall, SurplusDistributionBulk, surplus) - println("candidateA: " + candidateA) - candidateB = - returnCandidateB(totals, candidateA, vacancyShortfall, SurplusDistributionBulk, surplus) - println("candidateB: " + candidateB) - } - candidateB match { - case Some(cB) => // "in a case where Candidate B has been identified" - var notionalVotesOfB: Rational = Rational(0, 1) - bulktype match { - case ExclusionBulk => notionalVotesOfB = computeNotionalVotes(cB, totals) - case SurplusDistributionBulk => - notionalVotesOfB = computeAdjustedNotionalVotes(cB, totals, surplus) - } - val leadingShortfall = returnLeadingShortfall(totals, quota) - if (notionalVotesOfB < leadingShortfall) { // Section273 (13A)(c) - orderedCandidates.take(orderedCandidates.indexOf(cB) + 1) - } else { // Section273 (13A)(d) - var candidateC: Option[Candidate] = None - bulktype match { - case ExclusionBulk => - candidateC = returnCandidateC(totals, leadingShortfall, ExclusionBulk, None) - case SurplusDistributionBulk => - candidateC = - returnCandidateC(totals, leadingShortfall, SurplusDistributionBulk, surplus) - } - candidateC match { - case Some(cC) => orderedCandidates.take(orderedCandidates.indexOf(candidateC) + 1) - case None => List() - // throw new Exception( - // "CandidateC is None in selectCandidatesForBulkExclusion. - // Is this case allowed/possible meaning that we can continue the scrutiny? - // This is unclear from 13(A)(d). But can be analogously with 13(A)(b),(c) and (d) for CandidateB.") - } - } - case None => List() // Candidate B has not been identified - } - } -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// End of functions for Bulk Exclusion -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def filterBallotsWithFirstPreferences( - election: Election[ACTBallot], - preferences: List[Candidate] - ): Election[ACTBallot] = { - var ballots: List[ACTBallot] = List() - for (b <- election) - if (b.preferences.take(preferences.length) == preferences) ballots = b :: ballots - Election(ballots) - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Differs from ACTMethod as follows: -// - absence of ``Last Parcel'', hence None in place of markings -// - existence of ``Bulk exclusion'' -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def winners( - election: Election[ACTBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - println(" \n NEW RECURSIVE CALL \n") - - //// printElection(election) - - if (election.isEmpty) { - Nil - } // If all ballots are removed by the candidate who reached the quota exactly, the election will be empty. - // For example (3 seats, quota=2): - // 1 1/1 2 - // 2 1/1 2 - // 3 1/1 2 - // 4 1/1 5>6>1 - // 5 1/1 5>3>6 - else { - - // val ccands = getCandidates(election) - println("Continuing candidates: " + ccandidates) - - val tls = election.firstVotes(ccandidates) - - // result.addTotalsToHistory(tls) - - // TODO: Section 273(17) (when only two continuing candidates remain for a single seat) - // Notice: There may be more new winners than available vacancies!!! - if (ccandidates.length == numVacancies) { // Section 273(18) - println("HERE") - val ws = for (c <- ccandidates) yield (c, tls.getOrElse(c, Rational(0, 1))) - report.newCount(VictoryWithoutQuota, None, None, None, Some(ws), None) - report.setLossByFractionToZero - for (c <- ccandidates) yield (c, tls.getOrElse(c, Rational(0, 1))) - } else { - if (numVacancies == 1 && ccandidates.length == 2) { - println(TwoLastCandidatesForOneVacancy) - var ws: List[(Candidate, Rational)] = List() - val c1 = ccandidates(0) - val c2 = ccandidates(1) - if (tls(c1) > tls(c2)) { - ws = (c1, tls.getOrElse(c1, Rational(0, 1))) :: ws - } else { - if (tls(c1) < tls(c2)) { - ws = (c2, tls.getOrElse(c2, Rational(0, 1))) :: ws - } else { - println("Tie has to be resolved!") - ws = (c1, tls.getOrElse(c1, Rational(0, 1))) :: ws - } - } - report.newCount(TwoLastCandidatesForOneVacancy, None, None, None, Some(ws), None) - report.setLossByFractionToZero - ws - } else { - quotaReached(tls, result.getQuota) match { - case true => - val ws: List[(Candidate, Rational)] = - returnNewWinners(tls, result.getQuota) // sorted! tie resolved! - println("New winners: " + ws) - result.addPendingWinners(ws.toList, None) - - val vacanciesFilled = ws.length >= numVacancies - - vacanciesFilled match { - case false => - println("Vacancies: not yet filled.") - val res = surplusesDistribution(election, ccandidates, numVacancies - ws.length) - val newElection: Election[ACTBallot] = res._1 - val newWinners: List[(Candidate, Rational)] = res._2 - - val nws = ws.length + newWinners.length - println("Number of winners in this recursive call: " + nws) - val allWinners = ws ::: newWinners - if (nws == numVacancies) { allWinners } - else { - winners( - newElection, - ccandidates.filterNot(allWinners.map(_._1).toSet.contains(_)), - numVacancies - nws - ) ::: allWinners - // TODO: care should be taken that newElection is not empty?! - } - case true => ws - } - case false => - // Section 273 (13)(b) => (13A) and (13)(a) => (13AA) - val candidatesToExclude = - getCandidatesToExclude(tls, numVacancies, result.getQuota, ExclusionBulk, None) - val res = exclusion(election, ccandidates, candidatesToExclude, numVacancies) - val newElection: Election[ACTBallot] = res._1 - val newWinners: List[(Candidate, Rational)] = res._2 - println("New winners: " + newWinners) - println("Number of winners in this recursive call: " + newWinners.length) - if (newWinners.length == numVacancies) { - // Notice: There may be more new winners than available vacancies!!! - // if (for_each_candidate(candidates, &check_status,(void *)(CAND_ELECTED|CAND_PENDING)) == num_seats) return true; - newWinners - } else { - winners( - newElection, - ccandidates.filterNot(x => candidatesToExclude.map(_._1).contains(x)), - numVacancies - newWinners.length - ) ::: newWinners - } - } - - } - } - } - } - - def getCandidatesToExclude( - totals: Map[Candidate, Rational], - numRemainingVacancies: Int, - quota: Rational, - bulktype: BulkExclusionType, - surplus: Option[Rational] - ): List[(Candidate, Rational)] = { - - var candidatesToExclude: List[(Candidate, Rational)] = List() - val candidatesForBulkExclusion = - selectCandidatesForBulkExclusion(totals, numRemainingVacancies, quota, bulktype, surplus) - if (candidatesForBulkExclusion.nonEmpty) { // DO BULK EXCLUSION - Section 273 (13)(b) => (13A) - candidatesToExclude = candidatesForBulkExclusion - } else { // Exclude the least voted candidate - Section 273 (13)(a) => (13AA) - bulktype match { - case ExclusionBulk => - val leastVotedCandidate = chooseCandidateForExclusion(totals) - println("Candidate to be excluded: " + leastVotedCandidate) - result.addExcludedCandidate(leastVotedCandidate._1, leastVotedCandidate._2) - candidatesToExclude = leastVotedCandidate :: candidatesToExclude - case SurplusDistributionBulk => List() - } - } - candidatesToExclude - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def surplusesDistribution( - election: Election[ACTBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - println(" \n Distribution of surpluses. \n ") - var newws: List[(Candidate, Rational)] = List() - var newElection = election - - while (result.getPendingWinners.nonEmpty && newws.length != numVacancies) { - val (cand, ctotal, markings) = - result.takeButRetainFirstPendingWinner // IT IS NOT REMOVED FROM PENDING YET - - val tls = newElection.firstVotes(ccandidates) - val candidatesToExclude = getCandidatesToExclude( - tls, - numVacancies, - result.getQuota, - SurplusDistributionBulk, - Some(ctotal - result.getQuota) - ) - - if (candidatesToExclude.nonEmpty) { - // Section 273, (13C) - HERE I DO ONLY ONE BULK EXCLUSION - // IF IT IS POSSIBLE. CAN THERE BE ANOTHER BULK EXCLUSION AFTER THIS ONE - is unclear from (13C) - - println("\n Bulk exclusion: type 2. \n ") - val res = exclusion(election, ccandidates, candidatesToExclude, numVacancies): ( - Election[ACTBallot], - List[(Candidate, Rational)] - ) - newElection = res._1 - newws = newws ::: res._2 - println("Are there pending candidates? " + result.getPendingWinners.nonEmpty) - } - - result.removePendingWinner(cand) - val res = tryToDistributeSurplusVotes(newElection, ccandidates, cand, ctotal, markings) - newElection = res._1 - //// printElection(newElection) - newws = newws ::: res._2 - println("Are there pending candidates? " + result.getPendingWinners.nonEmpty) - } - (newElection, newws) - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// like ACT, but no fraction loss - def tryToDistributeSurplusVotes( - election: Election[ACTBallot], - ccandidates: List[Candidate], - winner: Candidate, - ctotal: Rational, - markings: Option[Set[Int]] - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - - val pendingWinners = result.getPendingWinners.map(x => x._1) - - if (ctotal == result.getQuota) { - val newElection = removeWinnerWithoutSurplusFromElection(election, winner) - result.removePendingWinner(winner) - println("Candidate with exact total is eliminated: " + winner) - //// printElection(newElection) - (newElection, List()) - } else - // NOTE THAT WHEN (!ballotsAreContinuing(winner, election, pendingWinners)) THE ELECTION DOES NOT CHANGE - // - // if (!ballotsAreContinuing(winner, election, pendingWinners) ) { - // val newElection = ??? - // result.removePendingWinner(winner) - // (newElection, List()) - // } - // else - { - - //// printElection(election) - - println("Distributing the surplus of " + winner) - - val surplus = ctotal - result.getQuota - - val tv = computeTransferValue(surplus, election, pendingWinners, winner, None) - println("tv = " + tv) - - val (newElection, exhaustedBallots, ignoredBallots) = - distributeSurplusVotes(election, winner, ctotal, None, pendingWinners, tv) - - val newElectionWithoutFractionInTotals = loseFraction(newElection, ccandidates) - - val newtotalsWithoutFraction = newElectionWithoutFractionInTotals.firstVotes(ccandidates) - - val newtotalsWithoutFractionWithoutpendingwinners = - newtotalsWithoutFraction.filterKeys(k => !pendingWinners.contains(k)) - - println("winner " + winner) - - result.removePendingWinner(winner) - - println("result.getPendingWinners " + result.getPendingWinners) - - result.addTotalsToHistory(newtotalsWithoutFractionWithoutpendingwinners) - var ws = declareNewWinnersWhileDistributingSurpluses( - newtotalsWithoutFractionWithoutpendingwinners, - newElection - ) - - //// printElection(newElection) - - // ------------ Reporting ------------------------------------------ - if (ws.nonEmpty) { - report.newCount( - SurplusDistribution, - Some(winner), - Some(newElectionWithoutFractionInTotals), - Some(newtotalsWithoutFraction), - Some(ws), - Some(exhaustedBallots) - ) - } else { - report.newCount( - SurplusDistribution, - Some(winner), - Some(newElectionWithoutFractionInTotals), - Some(newtotalsWithoutFraction), - None, - Some(exhaustedBallots) - ) - } - report.setLossByFraction(newElection.firstVotes(ccandidates), newtotalsWithoutFraction) - ignoredBallots match { // ballots ignored because they don't belong to the last parcel of the winner - case Some(ib) => report.setIgnoredBallots(ib) - case None => - } - // ------------------------------------------------------------------ - - (newElectionWithoutFractionInTotals, ws) - - } - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// similar to ACT's exclusion. -// but it also implements bulk exclusion - def exclusion( - election: Election[ACTBallot], - ccandidates: List[Candidate], - candidatesForExclusion: List[(Candidate, Rational)], - numVacancies: Int - ): (Election[ACTBallot], List[(Candidate, Rational)]) = { - - println("Vacancies left: " + numVacancies) - - var ws: List[(Candidate, Rational)] = List() - var newws: List[(Candidate, Rational)] = List() - var newElection = election - var newElectionWithoutFractionInTotals = election - var exhaustedBallots: Set[ACTBallot] = Set() - - for (candidate <- candidatesForExclusion) { - - if (candidate._2 == Rational(0, 1)) { - println("Excluding candidate with zero votes: " + candidate) - val ex = excludeZero(election, candidate._1) - newElection = ex._1 - } else { - var steps = determineStepsOfExclusion(election, candidate._1) - - while (ws.length != numVacancies && !steps.isEmpty) { - val step = steps.head - println("Step of exclusion: " + step) - steps = steps.tail // any better way to do this? - - val ex = exclude( - newElectionWithoutFractionInTotals, - step._1, - Some(step._2), - Some(newws.map(x => x._1)) - ) - - newElection = ex._1 - exhaustedBallots = ex._2 - - val totalsBeforeFractionLoss = newElection.firstVotes(ccandidates) // for computing LbF - - newElectionWithoutFractionInTotals = loseFraction( - newElection, - ccandidates - ) // perhaps it is better to get rid of newws in a separate function - - val totalsAfterFractionLoss = newElectionWithoutFractionInTotals.firstVotes(ccandidates) - - val totalsWithoutNewWinners = - totalsAfterFractionLoss.filterKeys(k => !ws.map(_._1).contains(k)) - // excluding winners that are already identified in the while-loop - - result.addTotalsToHistory(totalsWithoutNewWinners) - - newws = declareNewWinnersWhileExcluding( - candidate._1, - exhaustedBallots, - totalsWithoutNewWinners, - totalsWithoutNewWinners, - newElectionWithoutFractionInTotals - ) - - ws = ws ::: newws - report.setLossByFraction(totalsBeforeFractionLoss, totalsWithoutNewWinners) - } - } - } - - var dws: List[(Candidate, Rational)] = List() - if (ws.nonEmpty) { - val res = surplusesDistribution( - newElection, - ccandidates.filterNot(x => candidatesForExclusion.map(_._1).contains(x)), - numVacancies - ws.length - ) - newElection = res._1 - dws = res._2 - } - (newElection, ws ::: dws) - } - - override def chooseCandidateForExclusion( - totals: Map[Candidate, Rational] - ): (Candidate, Rational) = ??? - -} diff --git a/src/main/scala/agora/votecounter/BaldwinMethod.scala b/src/main/scala/agora/votecounter/BaldwinMethod.scala deleted file mode 100644 index d8a2dd1..0000000 --- a/src/main/scala/agora/votecounter/BaldwinMethod.scala +++ /dev/null @@ -1,51 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import spire.math.Rational - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} - -/** Created by deepeshpandey on 09/03/17. */ -object BaldwinMethod extends VoteCounter[Ballot] with LazyLogging { - - def bordaScores( - election: Election[Ballot], - candidates: List[Candidate] - ): Map[Candidate, Rational] = { - val m = new Map[Candidate, Rational] - - for (b <- election if !b.preferences.isEmpty) { - // need to take the size of the list first and then calculate the borda scores - var bordaCounter = candidates.length - b.preferences - .filter(candidate => candidates.contains(candidate)) - .map { candidate => - m(candidate) = - m.getOrElse(candidate, Rational(0, 1)) + ((bordaCounter - 1) * b.weight.numerator.toInt) - bordaCounter -= 1 - } - } - m - } - - def winners( - election: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Baldwin rule method called") - - if (candidates.length == 1) { - bordaScores(election, candidates).toList - } else { - // removing the lowest borda score candidate from the candidate list - var lowestBordaCandidate = bordaScores(election, candidates).minBy(_._2) - winners(election, candidates.filter(_ != lowestBordaCandidate._1), numVacancies) - } - } - -} diff --git a/src/main/scala/agora/votecounter/BipartisanSet.scala b/src/main/scala/agora/votecounter/BipartisanSet.scala deleted file mode 100644 index a021e6f..0000000 --- a/src/main/scala/agora/votecounter/BipartisanSet.scala +++ /dev/null @@ -1,122 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison - -import spire.math.Rational - -/** Link: https://drive.google.com/file/d/0B4uPp6wWiMpSMHZqaWVva0RZZjA/view?usp=sharing Important: - * Dominion of a candidate a is D(a) = { b ∈ A : a >M b }, Dominators of candidate a is D'(a) = { b - * ∈ A : b >M a } Every tournament is zero-sum multiplayer game and admits a unique probability - * distribution - due to Von Neumann's Minimax theorem(https://www.youtube.com/watch?v=toP9XPT7Bv4) - * Bipartisan Set is defined as : BP(A,PM) = {x∈A : p(x)>0, p balanced for (A,PM)} where PM is - * majority graph - */ -object BipartisanSet extends VoteCounter[Ballot] with PreferencePairwiseComparison { - - def runVoteCounter( - election: Election[Ballot], - candidates: List[Candidate], - param: Parameters - ): Report[Ballot] = { - - // print("\n INPUT ELECTION: \n") - // //printElection(election) - - val result: Result = new Result - val report: Report[Ballot] = new Report[Ballot] - - report.setCandidates(candidates) - - report.setWinners(bipartisanSet(election, candidates, param)) - - report - } - - // scalastyle:off method.length - def bipartisanSet( - election: Election[Ballot], - candidates: List[Candidate], - parameters: Parameters - ): List[(Candidate, Rational)] = { - - // check if the probability distribution is given for each candidate - require( - parameters.probabilityDistribution.isDefined && parameters.probabilityDistribution.get.length == candidates.length, - "inconsistency in candidates and probability distribution" - ) - - val distribution = parameters.probabilityDistribution.get.reduce(_ ++ _) - - // check if the name given in the distribution is consistent with the candidates names - require( - distribution.forall { case (cand, prob) => - candidates.exists(candidate => candidate.name == cand) - }, - "inconsistencies in candidates names" - ) - - val candidatesProbabilities = candidates.map(cand => (cand, distribution(cand.name))) - - val majorityMatrix = pairwiseComparison(election, candidates) - - // Dominion of a candidate a is D(a) = { b ∈ A : a >M b } - def dominions(candidate: Candidate): List[(Candidate, Double)] = { - candidatesProbabilities.filter { case (cand, prob) => - cand != candidate && - majorityMatrix(candidates.indexOf(candidate))(candidates.indexOf(cand)) > majorityMatrix( - candidates.indexOf(cand) - )(candidates.indexOf(candidate)) - } - } - - // Dominators of candidate a is D'(a) = { b ∈ A : b >M a } - def dominators(candidate: Candidate): List[(Candidate, Double)] = { - candidatesProbabilities.filter { case (cand, prob) => - cand != candidate && - majorityMatrix(candidates.indexOf(cand))(candidates.indexOf(candidate)) > majorityMatrix( - candidates.indexOf(candidate) - )(candidates.indexOf(cand)) - } - } - - def probabilityMargin( - dominions: List[(Candidate, Double)], - dominators: List[(Candidate, Double)] - ): Double = { - dominions.map { case (cand, prob) => prob }.sum - dominators.map { case (cand, prob) => - prob - }.sum - } - - // p is balanced if (p(x)>0 ⇔ mp(x)=0) and (p(x)=0 ⇔ mp(x)<0) - def balancedProbabilityDistribution(): Boolean = { - candidatesProbabilities.forall { case (cand, prob) => - if (prob > 0) { - probabilityMargin(dominions(cand), dominators(cand)) == 0 - } else if (prob == 0) { - probabilityMargin(dominions(cand), dominators(cand)) < 0 - } else { - false - } - } - } - - // check if p is a balanced probability distribution => p is balanced if (p(x)>0 ⇔ mp(x)=0) and (p(x)=0 ⇔ mp(x)<0) - require(balancedProbabilityDistribution, "probability distribution is not balanced!") - - // BP(A,PM) = {x∈A : p(x)>0, p balanced for (A,PM)} where PM is majority graph - candidatesProbabilities.filter { case (cand, prob) => - prob > 0 && - probabilityMargin(dominions(cand), dominators(cand)) == 0 - }.map { case (cand, prob) => (cand, Rational(prob)) } - } - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = ??? - -} diff --git a/src/main/scala/agora/votecounter/Borda.scala b/src/main/scala/agora/votecounter/Borda.scala deleted file mode 100644 index d62cfde..0000000 --- a/src/main/scala/agora/votecounter/Borda.scala +++ /dev/null @@ -1,46 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model.Candidate -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} -import spire.math.Rational - -/** Created by deepeshpandey on 07/03/17. */ -object Borda extends VoteCounter[Ballot] with LazyLogging { - - def totals(election: Election[Ballot], candidates: List[Candidate]): Map[Candidate, Rational] = { - val m = new Map[Candidate, Rational] - val numCandidates = candidates.length - - for (b <- election if !b.preferences.isEmpty) { - - // need to take the size of the list first and then calculate the borda scores - b.preferences.zipWithIndex.foreach(preference => { - m(preference._1) = m.getOrElse( - preference._1, - Rational(0, 1) - ) + ((numCandidates - 1 - preference._2) * b.weight.numerator.toInt) - }) - - } - m - } - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Borda rule computation called") - - val tls = totals(election, ccandidates) - - tls.toList.sortWith(_._2 > _._2).take(numVacancies) - - } - -} diff --git a/src/main/scala/agora/votecounter/Bucklin.scala b/src/main/scala/agora/votecounter/Bucklin.scala deleted file mode 100644 index b63ade4..0000000 --- a/src/main/scala/agora/votecounter/Bucklin.scala +++ /dev/null @@ -1,55 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** * https://en.wikipedia.org/wiki/Bucklin_voting we are not enforcing a strict number of - * preferences per ballot, unlike in the original Bucklin method so ballots with fewer preferences - * are not voided - */ - -object Bucklin extends VoteCounter[Ballot] { - - // The following recursive function calculates totals and if total of any candidate exceeds half of election length - // candidate wins, else next preferences are added, with weight 1/1 as previous preferences - def bucklinTotals( - election: Election[Ballot], - ccandidates: List[Candidate], - ccandScoreMap: MMap[Candidate, Rational] - ): List[(Candidate, Rational)] = { - val candidateScoreMap = ccandScoreMap - val candidateTotalScores = election.firstVotes(ccandidates) - for (c <- ccandidates) - candidateScoreMap(c) = candidateScoreMap.getOrElse(c, Rational(0, 1)) + candidateTotalScores - .getOrElse(c, Rational(0, 1)) * Rational(1, 1) - val sortedCandidateScoreMap = candidateScoreMap.toList.sortWith(_._2 > _._2) - if (sortedCandidateScoreMap.head._2 > (Rational(election.length, 2))) { - sortedCandidateScoreMap.head :: List() - } else { - var ballots: List[Ballot] = Nil - for (b <- election) { - ballots = new Ballot( - if (b.preferences != Nil) b.preferences.tail else Nil, - b.id, - b.weight - ) :: ballots - } - bucklinTotals(Election(ballots), ccandidates, candidateScoreMap) - } - } - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - var ccandidateScoreMap = new MMap[Candidate, Rational] - var winner = bucklinTotals(election, ccandidates, ccandidateScoreMap) - winner - } - -} diff --git a/src/main/scala/agora/votecounter/Contingent.scala b/src/main/scala/agora/votecounter/Contingent.scala deleted file mode 100644 index 85bca79..0000000 --- a/src/main/scala/agora/votecounter/Contingent.scala +++ /dev/null @@ -1,40 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Contingent_vote */ -object Contingent extends VoteCounter[Ballot] { - - val majorityThreshold = Rational(1, 2) - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val tls = election.firstVotes(ccandidates) - val ctSorted: List[(Candidate, Rational)] = tls.toList.sortWith(_._2 > _._2) - if (ctSorted.head._2 > majorityThreshold * election.length) { - List(ctSorted.head) - } else { - val tlsSecondRound = ctSorted.take(2) - val ccands: List[Candidate] = - ccandidates.filterNot(m => m != tlsSecondRound.head._1 && m != tlsSecondRound.tail.head._1) - val secondRoundScores = new MMap[Candidate, Rational] - for (b <- election if !b.preferences.isEmpty) { - val preferredCandidate = b.preferences.find(ccands.contains(_)) - preferredCandidate match { - case Some(c) => secondRoundScores(c) = b.weight + (secondRoundScores.getOrElse(c, 0)) - case _ => - } - } - List(secondRoundScores.toList.sortWith(_._2 > _._2).head) - } - } - -} diff --git a/src/main/scala/agora/votecounter/Coomb.scala b/src/main/scala/agora/votecounter/Coomb.scala deleted file mode 100644 index 7aea1fb..0000000 --- a/src/main/scala/agora/votecounter/Coomb.scala +++ /dev/null @@ -1,67 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import spire.math.Rational - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -/** Algorithm : https://en.wikipedia.org/wiki/Coombs%27_method Note: This voting method requires - * voters to rank all the candidates - */ -object Coomb extends VoteCounter[Ballot] with LazyLogging { - - private val majorityThreshold = Rational(1, 2) - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("computing coomb winner") - - val firstRankedMap = new MMap[Candidate, Rational] - - // check if there is a majority winner - - for (b <- election if b.preferences.nonEmpty) { - - b.preferences.find(c => ccandidates.contains(c)) match { - case Some(candidate) => - firstRankedMap(candidate) = firstRankedMap.getOrElse(candidate, Rational(0, 1)) + b.weight - case None => - } - } - - if (firstRankedMap.maxBy(_._2)._2 > Rational(1, 2) * election.weight) { - - List(firstRankedMap.maxBy(_._2)) - - } else { - // winner not found create the last ranked map and filter the highest last ranked candidate - - val lastRankedMap = new MMap[Candidate, Rational] - for (b <- election if b.preferences.nonEmpty) { - - assert( - b.preferences.find(c => ccandidates.contains(c)) != b.preferences.reverseIterator.find( - c => ccandidates.contains(c) - ) - ) - b.preferences.reverseIterator.find(c => ccandidates.contains(c)) match { - case Some(candidate) => - lastRankedMap(candidate) = lastRankedMap.getOrElse(candidate, Rational(0, 1)) + b.weight - case None => - } - - } - - winners(election, ccandidates.filter(_ != lastRankedMap.maxBy(_._2)._1), numVacancies) - } - - } - -} diff --git a/src/main/scala/agora/votecounter/Copeland.scala b/src/main/scala/agora/votecounter/Copeland.scala deleted file mode 100644 index c6fcae9..0000000 --- a/src/main/scala/agora/votecounter/Copeland.scala +++ /dev/null @@ -1,67 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import spire.math.Rational - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -/** Algorithm : https://en.wikipedia.org/wiki/Copeland%27s_method */ -object Copeland extends VoteCounter[Ballot] with LazyLogging { - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Computing Copeland winner") - - val candidatesNetScores = new MMap[Candidate, Rational] - val majorityRational = Rational(1, 2) - val totalVoters = election.weight - - // calculate pariwise comparison according to the ranked voting methods - // non - ranked candidates are worse than ranked candidates - - val pairwiseComp = Array.fill(ccandidates.size, ccandidates.size)(Rational(0, 1)) - - for (b <- election if b.preferences.nonEmpty) { - for { - c1 <- b.preferences - c2 <- ccandidates - } { - if (!b.preferences.contains(c2) || b.preferences.indexOf(c1) < b.preferences.indexOf(c2)) { - pairwiseComp(ccandidates.indexOf(c1))(ccandidates.indexOf(c2)) += b.weight - } - } - } - - // calculate the net victory for each candidate and find the copeland winner - for { - c1 <- ccandidates.indices - c2 <- ccandidates.indices if c1 < c2 - } { - - if (pairwiseComp(c1)(c2) > majorityRational * totalVoters) { - - candidatesNetScores(ccandidates(c1)) = - candidatesNetScores.getOrElse(ccandidates(c1), Rational(0, 1)) + 1 - candidatesNetScores(ccandidates(c2)) = - candidatesNetScores.getOrElse(ccandidates(c2), Rational(0, 1)) - 1 - - } else if (pairwiseComp(c2)(c1) > majorityRational * totalVoters) { - - candidatesNetScores(ccandidates(c2)) = - candidatesNetScores.getOrElse(ccandidates(c2), Rational(0, 1)) + 1 - candidatesNetScores(ccandidates(c1)) = - candidatesNetScores.getOrElse(ccandidates(c1), Rational(0, 1)) - 1 - - } - } - candidatesNetScores.toList.sortWith(_._2 > _._2).take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/Dodgson.scala b/src/main/scala/agora/votecounter/Dodgson.scala deleted file mode 100644 index 4d845fe..0000000 --- a/src/main/scala/agora/votecounter/Dodgson.scala +++ /dev/null @@ -1,134 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational - -import collection.mutable.{HashMap => MMap} - -/** Wiki : https://en.wikipedia.org/wiki/Dodgson%27s_method Implementation : - * http://infosyncratic.nl/talks/2008-votingprocedures.pdf - */ -object Dodgson extends VoteCounter[Ballot] { - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - // find flip vector from min sum to max sum which satisfies condorcet condition - val minDodgsonFlipList = List - .fill(e.weight.toInt)(0 to ccandidates.length) - .flatten - .view - .combinations(e.weight.toInt) - .flatMap(_.permutations) - .toList - .sortBy(_.sum) - .view - - val minDodgsonFlip = minDodgsonFlipList.find(list => - getCondorcetWinnerIfExist(list.force, ccandidates, e).nonEmpty - ) - - // find the dodgson winner based on this flip vector - minDodgsonFlip match { - case Some(list) => - getCondorcetWinnerIfExist(list.force, ccandidates, e) match { - case Some(candidate) => List((candidate, Rational(list.sum, 1))) - case None => List() - } - case None => List() - } - } - - def getCondorcetWinnerIfExist( - list: List[Int], - candidates: List[Candidate], - election: Election[Ballot] - ): Option[Candidate] = { - - val dodgsonWinner = candidates.find(c => - getCandidateMajorityArray(election, c, list, candidates) match { - case Some(matrix) => isCondorcetWinner(c, candidates, matrix, election.weight.toInt) - case None => false - } - ) - - dodgsonWinner - } - - // returns an array where the value at index i represents total votes to param candidate against candidates(i) - // this is all required to calculate if the param candidate is condorcet winner or not - def getCandidateMajorityArray( - election: Election[Ballot], - candidate: Candidate, - flipVector: List[Int], - candidates: List[Candidate] - ): Option[Array[Int]] = { - - val dispersedElection = dispersed(election) - - assert(dispersedElection.length == flipVector.length) - - val candElectionResponse = Array.ofDim[Int](candidates.size) - - val succesful = dispersedElection.zip(flipVector).forall { ballotsWithFlip => - { - - if (isFlippable(candidate, ballotsWithFlip._2, ballotsWithFlip._1.preferences)) { - - // no need to generate new list with updated positions just compare using indices - val prefs = ballotsWithFlip._1.preferences - for (cand <- prefs) { - if ( - (prefs.indexOf(cand) >= prefs.indexOf( - candidate - ) - ballotsWithFlip._2) && candidate != cand - ) { - candElectionResponse(candidates.indexOf(cand)) += 1 - } - } - true - } else { - false - } - } - } - - if (succesful) { - Option(candElectionResponse) - } else { - None - } - } - - private val cache = new MMap[Election[Ballot], Election[Ballot]]() - - def dispersed(election: Election[Ballot]): Election[Ballot] = { - - def disperseUtil(election: Election[Ballot]) = { - for { - b <- election - i <- 1 to b.weight.toInt - } yield b - } - cache.getOrElseUpdate(election, disperseUtil(election)) - } - - def isCondorcetWinner( - candidate: Candidate, - candidates: List[Candidate], - matrix: Array[Int], - totalVoters: Int - ): Boolean = - matrix.zip(candidates).forall { case (score, cand) => - score > Rational(1, 2) * totalVoters || (cand == candidate) - } - - def isFlippable(candidate: Candidate, rank: Int, ballot: List[Candidate]): Boolean = - if (ballot.indexOf(candidate) - rank >= 0) true else false - -} diff --git a/src/main/scala/agora/votecounter/EVACS.scala b/src/main/scala/agora/votecounter/EVACS.scala deleted file mode 100644 index 49eee9e..0000000 --- a/src/main/scala/agora/votecounter/EVACS.scala +++ /dev/null @@ -1,11 +0,0 @@ -package agora.votecounter - -import agora.votecounter.stv._ - -class EVACS - extends ACT - with TransferValueWithDenominatorWithNumOfMarkedContinuingBallotsOrOne - with ACTSurplusDistribution - with ACTNewWinnersDuringSurplusesDistribution - with ACTNewWinnersDuringExclusion - with ACTTotalsDuringExclusion diff --git a/src/main/scala/agora/votecounter/EVACSDelayedWD.scala b/src/main/scala/agora/votecounter/EVACSDelayedWD.scala deleted file mode 100644 index da862da..0000000 --- a/src/main/scala/agora/votecounter/EVACSDelayedWD.scala +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2011-2012 the original author or authors. -// See the LICENCE.txt file distributed with this work for additional -// information regarding copyright ownership. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package agora.votecounter - -import agora.model._ -import agora.votecounter.stv._ - -import scala.collection.immutable.ListMap -import collection.mutable.{HashMap => Map} -import scala.collection.SortedMap -import collection.mutable.HashSet -import collection.breakOut -import scala.util.Random -import scala.util.Sorting -import java.io._ - -class EVACSDelayedWD - extends ACT - with TransferValueWithDenominatorWithNumOfMarkedContinuingBallotsOrOne - with ACTSurplusDistribution - with NoNewWinnersDuringSurplusesDistribution - with NoNewWinnersDuringExclusion - with ACTTotalsDuringExclusion {} diff --git a/src/main/scala/agora/votecounter/EVACSnoLP.scala b/src/main/scala/agora/votecounter/EVACSnoLP.scala deleted file mode 100644 index efe174a..0000000 --- a/src/main/scala/agora/votecounter/EVACSnoLP.scala +++ /dev/null @@ -1,21 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.votecounter.stv._ - -import scala.collection.immutable.ListMap -import collection.mutable.{HashMap => Map} -import scala.collection.SortedMap -import collection.mutable.HashSet -import collection.breakOut -import scala.util.Random -import scala.util.Sorting -import java.io._ - -class EVACSnoLP - extends ACT - with TransferValueWithDenominatorWithNumOfAllContinuingBallotsOrOne // instead of TransferValueWithDenominatorWithNumOfMarkedContinuingBallotsOrOne - with ACTVoteCounterWithAllContinuingBallotsInSurplusDistribution // instead ACTSurplusDistribution - with ACTNewWinnersDuringSurplusesDistribution - with ACTNewWinnersDuringExclusion - with ACTTotalsDuringExclusion {} diff --git a/src/main/scala/agora/votecounter/Egalitarian.scala b/src/main/scala/agora/votecounter/Egalitarian.scala deleted file mode 100644 index 75ea113..0000000 --- a/src/main/scala/agora/votecounter/Egalitarian.scala +++ /dev/null @@ -1,29 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.PreferenceBallot - -import spire.math._ - -abstract class Egalitarian[B <: PreferenceBallot] extends VoteCounter[B] { - - val fairness: Double - - def rank(b: PreferenceBallot, c: Candidate, numCandidates: Int): Int = { - val r = b.preferences.indexOf(c) - if (r != -1) r else numCandidates - } - - def utilityIndividual(b: PreferenceBallot, c: Candidate, numCandidates: Int): Int = - numCandidates - rank(b, c, numCandidates) - - def utilitySet(b: PreferenceBallot, cs: Traversable[Candidate]): Int = - cs.map(c => utilityIndividual(b, c, cs.size)).reduce(_ + _) - - def socialWelfare(e: Election[B], cs: Traversable[Candidate]): Rational = { - (Rational(0, 1) /: e.ballots) { (acc, b) => - acc + (b.weight) * pow(utilitySet(b, cs), 1 / fairness) - } - } - -} diff --git a/src/main/scala/agora/votecounter/EgalitarianBrute.scala b/src/main/scala/agora/votecounter/EgalitarianBrute.scala deleted file mode 100644 index 8f436fe..0000000 --- a/src/main/scala/agora/votecounter/EgalitarianBrute.scala +++ /dev/null @@ -1,22 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational - -import scala.language.postfixOps - -class EgalitarianBrute(val fairness: Double = 2) extends Egalitarian[Ballot] { - - def winners( - e: Election[Ballot], - cs: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val candidateSubsets = cs.toSet.subsets(numVacancies) - val bestCandidateSubset = candidateSubsets.maxBy(socialWelfare(e, _)) - bestCandidateSubset.map((_, Rational(1, 1))) toList - } - -} diff --git a/src/main/scala/agora/votecounter/EgalitarianDP.scala b/src/main/scala/agora/votecounter/EgalitarianDP.scala deleted file mode 100644 index 7d741d1..0000000 --- a/src/main/scala/agora/votecounter/EgalitarianDP.scala +++ /dev/null @@ -1,40 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import spire.math.Rational - -import scala.collection.mutable.{HashMap => MMap} - -class EgalitarianDP(val fairness: Double = 2) extends Egalitarian[Ballot] { - - val idealCandidates = new MMap[(Int, Set[Candidate]), List[Candidate]]() - - def winners( - e: Election[Ballot], - cs: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val winningCandidates: List[Candidate] = recursiveWinnersComputation(e, cs, numVacancies) - winningCandidates.map((_, Rational(1, 1))) - } - - def recursiveWinnersComputation( - e: Election[Ballot], - cs: List[Candidate], - numVacancies: Int - ): List[Candidate] = numVacancies match { - case 0 => List.empty - case n => - if (idealCandidates.contains((numVacancies, cs.toSet))) { - idealCandidates((numVacancies, cs.toSet)) - } - val candidateSets: List[List[Candidate]] = cs.map(x => - recursiveWinnersComputation(e, cs.filterNot(elem => elem == x), numVacancies - 1) :+ x - ) - val result = candidateSets.maxBy(socialWelfare(e, _)) - idealCandidates += (((numVacancies, cs.toSet), result)) - result - } - -} diff --git a/src/main/scala/agora/votecounter/HybridPluralityPreferentialBlockVoting.scala b/src/main/scala/agora/votecounter/HybridPluralityPreferentialBlockVoting.scala deleted file mode 100644 index 8cbd388..0000000 --- a/src/main/scala/agora/votecounter/HybridPluralityPreferentialBlockVoting.scala +++ /dev/null @@ -1,64 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Preferential_block_voting */ - -object HybridPluralityPreferentialBlockVoting extends VoteCounter[Ballot] { - - def totalsForFirstNVacancies( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): MMap[Candidate, Rational] = { - // calculates the totals for first n candidates where n is equal to number of vacancies - var m = new MMap[Candidate, Rational] - for (b <- election if !b.preferences.isEmpty) - for (c <- b.preferences.take(numVacancies)) - m(c) = m.getOrElse(c, Rational(0, 1)) + b.weight - m - } - - def exclude(election: Election[Ballot], ccandidate: Candidate): Election[Ballot] = { - var list: List[Ballot] = Nil - for (b <- election if !b.preferences.isEmpty) - list = new Ballot(b.preferences.filter(_ != ccandidate), b.id, b.weight) :: list - Election(list) - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - var winnerlist: List[(Candidate, Rational)] = Nil - var election1 = election - var ccandidates1 = ccandidates - var vacancies = numVacancies - while (vacancies != 0) { - var tls = totalsForFirstNVacancies(election1, ccandidates1, vacancies) - val sortedCandList = tls.toList.sortWith(_._2 > _._2) - if (sortedCandList.head._2 > (election1.size / 2)) { - winnerlist = sortedCandList.head :: winnerlist - election1 = exclude(election1, sortedCandList.head._1) - ccandidates1 = ccandidates1.filter(_ != sortedCandList.head._1) - vacancies = vacancies - 1 - } else { - election1 = exclude( - election1, - tls.filter(x => ccandidates1.contains(x._1)).toList.sortWith(_._2 < _._2).head._1 - ) - ccandidates1 = ccandidates1.filter(_ != sortedCandList.last._1) - } - } - winnerlist - } - -} diff --git a/src/main/scala/agora/votecounter/InstantExhaustiveBallot.scala b/src/main/scala/agora/votecounter/InstantExhaustiveBallot.scala deleted file mode 100644 index 21f69b2..0000000 --- a/src/main/scala/agora/votecounter/InstantExhaustiveBallot.scala +++ /dev/null @@ -1,32 +0,0 @@ -package agora.votecounter - -import agora.votecounter.stv._ -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Exhaustive_ballot */ - -object InstantExhaustiveBallot - extends VoteCounter[Ballot] - with SimpleExclusionWithFixedElectionSize { - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val ct = election.firstVotes(ccandidates) - val sortedCandList = ct.toList.sortWith(_._2 < _._2) - if (ct.size > 2) { - val losingCand = sortedCandList.head - val newElection = exclude(election, losingCand._1) - winners(newElection, ccandidates.filter(_ != losingCand._1), numVacancies) - } else { - sortedCandList.last :: List() - } - } - -} diff --git a/src/main/scala/agora/votecounter/InstantExhaustiveDropOffRule.scala b/src/main/scala/agora/votecounter/InstantExhaustiveDropOffRule.scala deleted file mode 100644 index 0a567a4..0000000 --- a/src/main/scala/agora/votecounter/InstantExhaustiveDropOffRule.scala +++ /dev/null @@ -1,72 +0,0 @@ -package agora.votecounter - -import agora.votecounter.stv._ -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Exhaustive_ballot#Notes */ - -object InstantExhaustiveDropOffRule - extends VoteCounter[Ballot] - with SimpleExclusionWithFixedElectionSize { - - var dropOffPercentage = Rational(0, 100) - - val cutoffPercentage = - 25 // drop off of candidates in steps of 5% till 25% after which lowest scoring candidate is eliminated - - def loser( - candidate: (Candidate, Rational), - total: Int, - dropOffPercentage: Rational - ): Option[(Candidate, Rational)] = { - if (dropOffPercentage.numerator * (100 / dropOffPercentage.denominator) > cutoffPercentage) { - // println(" 25% dropoff rule crossed, eliminating the last candidate " + candidate._1) - Some(candidate) - } else { - val ccandPercentage = Rational(candidate._2.numerator, total) - if (ccandPercentage < dropOffPercentage) { - // println(" Dropping off candidate " + candidate._1 + " for being below " + dropOffPercentage.numerator*100/dropOffPercentage.denominator + "%") - Some(candidate) - } else { - None - } - } - } - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val majorityRational = Rational(1, 2) - val incrememtSize = - 5 // drop off percentage increases by 5% in each round of elimination till 25% - var tls = election.firstVotes(ccandidates).toList.sortWith(_._2 > _._2) - if (tls.size > 2) { - if (tls.head._2 > majorityRational * election.size) { - tls.head :: List() - } else { - dropOffPercentage = Rational( - dropOffPercentage.numerator * (100 / dropOffPercentage.denominator) + incrememtSize, - 100 - ) - val losingCand: Option[(Candidate, Rational)] = - loser(tls.last, election.size, dropOffPercentage) - losingCand match { - case Some((c, _)) => - val newElection = exclude(election, c) - winners(newElection, ccandidates.filterNot(x => x == c), numVacancies) - case None => - winners(election, ccandidates, numVacancies) - } - } - } else { - tls.head :: List() - } - } - -} diff --git a/src/main/scala/agora/votecounter/InstantRunoff2Round.scala b/src/main/scala/agora/votecounter/InstantRunoff2Round.scala deleted file mode 100644 index f97a491..0000000 --- a/src/main/scala/agora/votecounter/InstantRunoff2Round.scala +++ /dev/null @@ -1,61 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} - -import spire.math.Rational - -/** Created by deepeshpandey on 03/06/17. Algorithm : https://en.wikipedia.org/wiki/Two-round_system - * Comments : It's a hybrid algorithm combining Instant Run-Off and 2-round voting. Two round - * voting cannot be handled exclusively becuase of its nature of having two rounds at different - * times Assumptions : voters vote only once with preferential ballots Rounds : only 2 as per - * 2-round voting - */ -object InstantRunoff2Round extends VoteCounter[Ballot] { - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val majorityRational = Rational(1, 2) - val rnd1Winners = election.firstVotes(ccandidates).toList.sortWith(_._2 > _._2).take(2) - val totalVoters = election.weight - - if (rnd1Winners.head._2 > majorityRational * totalVoters) - List(rnd1Winners.head) - else - getSecondRoundWinner( - election, - ccandidates, - rnd1Winners.map(c => c._1), - totalVoters.toInt, - numVacancies - ) - - } - - def getSecondRoundWinner( - election: Election[Ballot], - ccandidates: List[Candidate], - rnd1Winners: List[Candidate], - totalVoters: Int, - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val m = new Map[Candidate, Rational] - - for (c <- ccandidates) m(c) = 0 - - for (b <- election if b.preferences.nonEmpty) { - val candidate = b.preferences.filter(c => rnd1Winners.contains(c)).take(1) - m(candidate.head) = b.weight + m.getOrElse(candidate.head, 0) - } - - m.toList.sortWith(_._2 > _._2).take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/KemenyYoung.scala b/src/main/scala/agora/votecounter/KemenyYoung.scala deleted file mode 100644 index 5876f0f..0000000 --- a/src/main/scala/agora/votecounter/KemenyYoung.scala +++ /dev/null @@ -1,88 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} - -import spire.math.Rational - -/** Created by deepeshpandey on 10/03/17. */ -object KemenyYoung extends VoteCounter[Ballot] { - - private val rationalZero = Rational(0, 1) - - // scalastyle:off cyclomatic.complexity - // scalastyle:off method.length - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - var tallyTable = new Map[Map[Candidate, Candidate], Integer] - var candidatePairKey = new Map[Candidate, Candidate] - val candidates = ccandidates.zipWithIndex - - // initialise the tally table - candidates.foreach { case (c1, i1) => - candidates.foreach { case (c2, i2) => - if (i2 != i1) { - - var candidatePair = new Map[Candidate, Candidate] - candidatePair(c1) = c2 - tallyTable(candidatePair) = 0 - } - } - } - - // update the tally table using election - for (b <- election if !b.preferences.isEmpty) { - val voterPreference = b.preferences - b.preferences.zipWithIndex.foreach(preference => { - - voterPreference.zipWithIndex.foreach(candidate => { - - if (preference._2 < candidate._2) { - candidatePairKey.put(preference._1, candidate._1) - tallyTable.put( - candidatePairKey, - tallyTable.get(candidatePairKey).get + b.weight.numerator.toInt - ) - candidatePairKey.clear() - } - - }) - }) - } - - // permute the list and check for the maximum kemeny ranking - - var maxRankingScore = 0 - var maxRanking = new Array[Candidate](ccandidates.length) - - ccandidates.permutations.toList.foreach { ranking => - var currentRankingScore = 0 - val currentRanking = ranking.zipWithIndex - - currentRanking.foreach { case (c1, i1) => - currentRanking.foreach { case (c2, i2) => - if (i1 < i2) { - var candidatePairKey = new Map[Candidate, Candidate] - candidatePairKey.put(c1, c2) - currentRankingScore = currentRankingScore + tallyTable.get(candidatePairKey).get - } - } - } - // keep track of the maximum score and rankings - - if (currentRankingScore > maxRankingScore) { - maxRankingScore = currentRankingScore - maxRanking = ranking.toArray - } - } - - maxRanking.map(candidate => (candidate, rationalZero)).toList.take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/Majority.scala b/src/main/scala/agora/votecounter/Majority.scala deleted file mode 100644 index ddcdecb..0000000 --- a/src/main/scala/agora/votecounter/Majority.scala +++ /dev/null @@ -1,29 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -object Majority extends VoteCounter[Ballot] { - - import spire.math.Rational - - // TODO: There is an implicit assumption here that all votes have weight 1. - // Should this be checked? - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - election - .firstVotes(ccandidates) - .toList - .sortWith { (ct1, ct2) => - ct1._2 > ct2._2 - } - .take(numVacancies) - .filter { case (c, t) => - t > (election.length / 2) - } // only select the alternative that has more than half the votes - } - -} diff --git a/src/main/scala/agora/votecounter/Maximin.scala b/src/main/scala/agora/votecounter/Maximin.scala deleted file mode 100644 index b495aa2..0000000 --- a/src/main/scala/agora/votecounter/Maximin.scala +++ /dev/null @@ -1,62 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison - -import collection.mutable.ListBuffer -import collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** Algorithm : https://www.cs.cmu.edu/~arielpro/mfai_papers/lecture6.pdf page-4 Variant : winning - * votes => W = \arg \min_X ( \max_Y score(Y, X)) - */ -object Maximin extends VoteCounter[Ballot] with PreferencePairwiseComparison with LazyLogging { - - private val rational0 = Rational(0, 1) - - private val majorityThreshold = Rational(1, 2) - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Computing maximin Condorcet Winner") - - val pairwiseComparisons = pairwiseComparison(election, ccandidates) - val mcScores = - getMaximinScores(pairwiseComparisons, ccandidates, election).toList.sortWith(_._2 > _._2) - - mcScores.head :: List() - - } - - def getMaximinScores( - pairwiseComparisons: Array[Array[Rational]], - ccandidates: List[Candidate], - election: Election[Ballot] - ): MMap[Candidate, Rational] = { - - val maximinScores = new MMap[Candidate, Rational] - - for (c <- ccandidates) - maximinScores(c) = Rational(election.size, 1) - - for (i <- ccandidates) - for (j <- ccandidates) - if ( - maximinScores(i) > pairwiseComparisons(ccandidates.indexOf(i))( - ccandidates.indexOf(j) - ) & i != j - ) { - maximinScores(i) = pairwiseComparisons(ccandidates.indexOf(i))(ccandidates.indexOf(j)) - } - - maximinScores - } - -} diff --git a/src/main/scala/agora/votecounter/MeekSTV.scala b/src/main/scala/agora/votecounter/MeekSTV.scala deleted file mode 100644 index 9e8a132..0000000 --- a/src/main/scala/agora/votecounter/MeekSTV.scala +++ /dev/null @@ -1,157 +0,0 @@ -package agora.votecounter - -/** http://blog.opavote.com/2017/04/meek-stv-explained.html - * https://en.wikipedia.org/wiki/Counting_single_transferable_votes#Meek - */ - -import agora.model._ -import agora.votecounter.stv._ -import collection.mutable.{HashMap => MMap} -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational -import agora.votecounter.stv.Input - -object MeekSTV - extends STV[Ballot] - with DroopQuota // Imp - with NoFractionInQuota // Imp - with NewWinnersNotOrdered[Ballot] - with SimpleSurplusDistributionTieResolution // not necessary because of NewWinnersNotOrdered - with SimpleExclusion - with UnfairExclusionTieResolution - with TransferValueWithDenominatorEqualToTotal - with VoteCounterWithAllBallotsInSurplusDistribution - with ExactWinnerRemoval { - - val result: Result = new Result - - val report: Report[Ballot] = new Report[Ballot] - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - override def runVoteCounter( - election: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int - ): Report[Ballot] = { - - print("\n INPUT ELECTION: \n") - // printElection(election) - - val tls = election.firstVotes( - candidates - ) // Here are totals of candidates also not OCCURING in the ballots - result.addTotalsToHistory(tls) - - // report.setCandidates(getCandidates(election)) // Here are candidates OCCURING in the election - report.setCandidates(candidates) // Here are candidates also not OCCURING in the election - - report.newCount(Input, None, Some(election), Some(tls), None, None) - - report.setWinners(winners(election, candidates, numVacancies)) - - report - } - - def totalsMeek( - election: Election[Ballot], - ccandidates: List[Candidate], - keepFactor: MMap[Candidate, Rational] - ): MMap[Candidate, Rational] = { - val scoreMap = new MMap[Candidate, Rational] - - for (b <- election if !b.preferences.isEmpty) { - var multiplier = Rational(1, 1) - for (c <- b.preferences) { - scoreMap(c) = scoreMap.getOrElse(c, Rational(0, 1)) + b.weight * multiplier * keepFactor(c) - multiplier = multiplier * (Rational(1, 1) - keepFactor(c)) - } - } - scoreMap - } - - def surplusCandidates(totals: MMap[Candidate, Rational], quota: Rational): Int = { - val surplusCandidatesNumber = totals.filter(x => x._2 >= quota).size - surplusCandidatesNumber - } - - def surplusQuantity(totals: MMap[Candidate, Rational], quota: Rational): Rational = { - val surplusAmount: Rational = - (Rational(0, 1) /: (totals.filter(_._2 >= quota).map(_._2))) { (surplus, t) => - surplus + t - quota - } - surplusAmount - } - - def winnersList( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int, - keepFactor: MMap[Candidate, Rational] - ): List[(Candidate, Rational)] = { - - println(" \n NEW RECURSIVE CALL \n") - - val tls = totalsMeek(election, ccandidates, keepFactor) - if (ccandidates.length <= numVacancies) { - for (c <- ccandidates) yield (c, tls(c)) - } else { - if (surplusCandidates(tls, result.getQuota) >= numVacancies) { - tls.toList.sortWith(_._2 > _._2).take(numVacancies) - } else { - // Find surplus and check if surplus + last candidate's number of votes < quota - // If so, then KV = 0 for them - // Else find surplus ones and reduce their KV - val surplusAmount = surplusQuantity(tls, result.getQuota) - val sortedScoreList = - tls.toList.filter(x => ccandidates.contains(x._1)).sortWith(_._2 < _._2) - if ( - sortedScoreList.head._2 + surplusAmount < tls.toList - .filter(x => ccandidates.contains(x._1)) - .sortWith(_._2 < _._2) - .tail - .head - ._2 - ) { - keepFactor(sortedScoreList.head._1) = Rational(0, 1) - winnersList( - exclude(election, sortedScoreList.head._1, None, None)._1, - ccandidates.filterNot(_ == sortedScoreList.head._1), - numVacancies, - keepFactor - ) - } else { - val winnerList = - tls.filter(x => ccandidates.contains(x._1)).filter(_._2 > result.getQuota) - for (w <- winnerList) { - keepFactor(w._1) = keepFactor(w._1) * Rational( - w._2.denominator.toInt * result.getQuota.numerator.toInt, - w._2.numerator.toInt - ) - } - winnersList(election, ccandidates, numVacancies, keepFactor) - } - } - } - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val quota = cutQuotaFraction(computeQuota(election.length, numVacancies)) - // println("Quota = " + quota) - result.setQuota(quota) - - val keepFactor = new MMap[Candidate, Rational] - for (c <- ccandidates) - keepFactor(c) = Rational(1, 1) - - winnersList(election, ccandidates, numVacancies, keepFactor) - } - -} diff --git a/src/main/scala/agora/votecounter/MinimaxCondorcet.scala b/src/main/scala/agora/votecounter/MinimaxCondorcet.scala deleted file mode 100644 index a20e612..0000000 --- a/src/main/scala/agora/votecounter/MinimaxCondorcet.scala +++ /dev/null @@ -1,55 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** Algorithm : https://en.wikipedia.org/wiki/Minimax_Condorcet Variant : winning votes => W = \arg - * \min_X ( \max_Y score(Y, X)) - */ -object MinimaxCondorcet - extends VoteCounter[Ballot] - with PreferencePairwiseComparison - with LazyLogging { - - private val rational0 = Rational(0, 1) - - private val majorityThreshold = Rational(1, 2) - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Computing minimax Condorcet Winner") - - val pairwiseComparisons = pairwiseComparison(election, ccandidates) - val mcScores = getMinimaxCondorcetScores(pairwiseComparisons, ccandidates) - - List(ccandidates.map(c => (c, mcScores.map(_(ccandidates.indexOf(c))).max)).minBy(_._2)) - - } - - def getMinimaxCondorcetScores( - pairwiseComparisons: Array[Array[Rational]], - ccandidates: List[Candidate] - ): Array[Array[Rational]] = { - - val minimaxCondorcetScores = Array.fill(ccandidates.size, ccandidates.size)(Rational(0, 1)) - - for (i <- ccandidates.indices) - for (j <- ccandidates.indices) - if (pairwiseComparisons(i)(j) > pairwiseComparisons(j)(i)) { - minimaxCondorcetScores(i)(j) = pairwiseComparisons(i)(j) - } - - minimaxCondorcetScores - } - -} diff --git a/src/main/scala/agora/votecounter/Nanson.scala b/src/main/scala/agora/votecounter/Nanson.scala deleted file mode 100644 index 599f740..0000000 --- a/src/main/scala/agora/votecounter/Nanson.scala +++ /dev/null @@ -1,29 +0,0 @@ -package agora.votecounter - -import agora.votecounter.BaldwinMethod.bordaScores -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => Map} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Nanson%27s_method */ -object Nanson extends VoteCounter[Ballot] { - - def winners( - election: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - if (candidates.length == 1) { - bordaScores(election, candidates).toList - } else { - var cbs = bordaScores(election, candidates) // borda scores of candidates - val average = (Rational(0, 1) /: candidates)(_ + cbs(_)) / Rational(candidates.length) - winners(election, candidates.filter(x => cbs(x) > average), numVacancies) - } - } - -} diff --git a/src/main/scala/agora/votecounter/Oklahoma.scala b/src/main/scala/agora/votecounter/Oklahoma.scala deleted file mode 100644 index 4301044..0000000 --- a/src/main/scala/agora/votecounter/Oklahoma.scala +++ /dev/null @@ -1,63 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** * https://en.wikipedia.org/wiki/Oklahoma_primary_electoral_system we are not enforcing a strict - * number of preferences per ballot, unlike in the original Oklahoma method so ballots with fewer - * prefererences are not voided - */ - -object Oklahoma extends VoteCounter[Ballot] { - - // following recursive function calculates totals and if total of any candidate exceeds half of election length - // candidate wins, else next preferences are added, reducing their weights by 1/N, - // where N denotes Nth preference on the ballot - // that is, 1st preference has weight 1, 2nd preference has weight 1/2. 3rd preference has weight 1/3 and so on - def oklahomaTotals( - election: Election[Ballot], - ccandidates: List[Candidate], - ccandScoreMap: MMap[Candidate, Rational], - multiplier: Rational - ): List[(Candidate, Rational)] = { - val candidateScoreMap = ccandScoreMap - val candidateTotalScores = election.firstVotes(ccandidates) - for (c <- ccandidates) - candidateScoreMap(c) = candidateScoreMap.getOrElse(c, Rational(0, 1)) + candidateTotalScores - .getOrElse(c, Rational(0, 1)) * multiplier - val sortedCandidateScoreMap = candidateScoreMap.toList.sortWith(_._2 > _._2) - if (sortedCandidateScoreMap.head._2 > (Rational(election.length, 2))) { - sortedCandidateScoreMap.head :: List() - } else { - var ballots: List[Ballot] = Nil - for (b <- election) { - ballots = new Ballot( - if (b.preferences != Nil) b.preferences.tail else Nil, - b.id, - b.weight - ) :: ballots - } - oklahomaTotals( - Election(ballots), - ccandidates, - candidateScoreMap, - Rational(multiplier.numerator, multiplier.denominator + 1) - ) - } - } - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - var ccandidateScoreMap = new MMap[Candidate, Rational] - var winner = oklahomaTotals(election, ccandidates, ccandidateScoreMap, Rational(1, 1)) - winner - } - -} diff --git a/src/main/scala/agora/votecounter/PreferentialBlockVoting.scala b/src/main/scala/agora/votecounter/PreferentialBlockVoting.scala deleted file mode 100644 index ca064a4..0000000 --- a/src/main/scala/agora/votecounter/PreferentialBlockVoting.scala +++ /dev/null @@ -1,46 +0,0 @@ -package agora.votecounter - -import agora.votecounter.HybridPluralityPreferentialBlockVoting.exclude -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** https://en.wikipedia.org/wiki/Preferential_block_voting */ - -object PreferentialBlockVoting extends VoteCounter[Ballot] { - - val majorityThreshold = Rational(1, 2) - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - var winnerlist: List[(Candidate, Rational)] = Nil - var vacancies = numVacancies - var ccandidates1 = ccandidates - var election1 = election - while (vacancies != 0) { - val sortedCandList = election1.firstVotes(ccandidates1).toList.sortWith(_._2 > _._2) - if ( - sortedCandList.head._2 > majorityThreshold * election1.length && ccandidates1.length > vacancies - ) { - winnerlist = sortedCandList.head :: winnerlist - vacancies -= 1 - ccandidates1 = ccandidates1.filter(_ != sortedCandList.head._1) - election1 = exclude(election1, sortedCandList.head._1) - } else if (ccandidates1.length == vacancies) { - winnerlist = sortedCandList ::: winnerlist - vacancies = 0 - } else { - ccandidates1 = ccandidates1.filter(_ != sortedCandList.last._1) - election1 = exclude(election1, sortedCandList.last._1) - } - } - winnerlist - } - -} diff --git a/src/main/scala/agora/votecounter/ProportionalApprovalVoting.scala b/src/main/scala/agora/votecounter/ProportionalApprovalVoting.scala deleted file mode 100644 index e6be4b9..0000000 --- a/src/main/scala/agora/votecounter/ProportionalApprovalVoting.scala +++ /dev/null @@ -1,76 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.immutable.{Map => IMap} -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** * https://en.wikipedia.org/wiki/Proportional_approval_voting - */ -object ProportionalApprovalVoting extends VoteCounter[Ballot] { - - // following function calculates score, i.e., given N, it calculates summation 1 to 1/N - def proportionalApprovalScore(nmatches: Int): Rational = { - var score = Rational(0, 1) - for (i <- 1 to nmatches) - score = score + Rational(1, i) - score - } - - // following function calculates totals for each candidate subsets in the follwoing manner - // if N of the candidate preferences matches with any one candidate subset, - // then score for that subset is summation 1 to 1/N - def candidateSubsetTotals( - election: Election[Ballot], - candidates: List[Candidate], - ccandSubsetList: List[List[Candidate]] - ): List[(Candidate, Rational)] = { - val scoredCandidateSubsetMap = new MMap[List[Candidate], Rational] - for (a <- ccandSubsetList) { - for (b <- election) { - scoredCandidateSubsetMap(a) = scoredCandidateSubsetMap.getOrElse(a, Rational(0, 1)) + - proportionalApprovalScore( - b.preferences.length - b.preferences.toSet[Candidate].diff(a.toSet[Candidate]).size - ) - } - } - val sortedCandidateSubsetList = scoredCandidateSubsetMap.toList.sortWith(_._2 > _._2) - val winnerList = sortedCandidateSubsetList.head._1 - val winnerScore = sortedCandidateSubsetList.head._2 - val finalList = winnerList.map { - (_, winnerScore) - } - finalList - } - - // generates subsets of length k of list of candidates in recursive manner - def candidateSubsetListGenerator(k: Int, candidates: List[Candidate]): List[List[Candidate]] = { - candidates match { - case Nil => Nil - case head :: tail => - if (k <= 0 || k > candidates.length) { - Nil - } else if (k == 1) { - candidates.map(List(_)) - } else { - candidateSubsetListGenerator(k - 1, tail).map(head :: _) ::: candidateSubsetListGenerator( - k, - tail - ) - } - } - } - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val ccandSubsetList = candidateSubsetListGenerator(numVacancies, ccandidates) - candidateSubsetTotals(election, ccandidates, ccandSubsetList) - } - -} diff --git a/src/main/scala/agora/votecounter/RandomBallot.scala b/src/main/scala/agora/votecounter/RandomBallot.scala deleted file mode 100644 index 5e26f51..0000000 --- a/src/main/scala/agora/votecounter/RandomBallot.scala +++ /dev/null @@ -1,45 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.util.Random - -import spire.math.Rational - -/** Algorithm : https://en.wikipedia.org/wiki/Random_ballot */ -object RandomBallot extends VoteCounter[Ballot] with LazyLogging { - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = - randomBallotWinner(election, ccandidates, numVacancies, None) - - def randomBallotWinner( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int, - seed: Option[Int] - ): List[(Candidate, Rational)] = { - - logger.info("computing random ballot winner") - - val totalVoters = election.weight - - val random = seed match { - case Some(seed) => new Random(seed).nextDouble() - case None => new Random().nextDouble() - } - - val cumulative = election.map(_.weight).scanLeft(Rational(0, 1))(_ + _) - - val dictator = cumulative.lastIndexWhere(_.toDouble <= random * totalVoters.toDouble) - - election(dictator).preferences.take(numVacancies).map(c => (c, Rational(0, 1))) - - } - -} diff --git a/src/main/scala/agora/votecounter/RangeVoting.scala b/src/main/scala/agora/votecounter/RangeVoting.scala deleted file mode 100644 index 255b95a..0000000 --- a/src/main/scala/agora/votecounter/RangeVoting.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.model._ - -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** Algorithm : https://en.wikipedia.org/wiki/Range_voting Note: This variant sums the score of - * candidates over all voters TODO: Variant where average score of a candidate is used to compute - * the winner - */ -object RangeVoting extends VoteCounter[ScoreBallot] { - - def winners( - election: Election[ScoreBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val candidateScores = new MMap[Candidate, Rational] - - for (b <- election) { - b.scores.foreach { case (candidate, score) => - candidateScores(candidate) = - candidateScores.getOrElse(candidate, Rational(0, 1)) + score * b.weight - } - } - // because range voting is a single seat election - numVacancies are always 1 - List(candidateScores.toList.maxBy(_._2)) - } - -} diff --git a/src/main/scala/agora/votecounter/RankedPairs.scala b/src/main/scala/agora/votecounter/RankedPairs.scala deleted file mode 100644 index 85991f4..0000000 --- a/src/main/scala/agora/votecounter/RankedPairs.scala +++ /dev/null @@ -1,23 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ - -import spire.math.Rational - -object RankedPairs extends VoteCounter[RankBallot] with LazyLogging { - - def winners( - e: Election[RankBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info( - "computing scored pairs winners: checking if the ballot contains the indtended data" - ) - - List((ccandidates.head, Rational(0, 1))) - } - -} diff --git a/src/main/scala/agora/votecounter/SMC.scala b/src/main/scala/agora/votecounter/SMC.scala deleted file mode 100644 index b1a1e2d..0000000 --- a/src/main/scala/agora/votecounter/SMC.scala +++ /dev/null @@ -1,78 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison - -import spire.math.Rational - -/** Created by deepeshpandey on 03/06/17. About : Sequential Majority Comparison (SMC): Fix some - * enumeration {x1, x2, . . . , xm} of the alternatives. The winner of round 1 is x1; the winner of - * round i + 1 is the winner w of round i, if w >(majority) xi+1, and is xi+1, if xi+1 >(majority) - * w; and the ultimate winner is the winner of round m. - */ -object SMC extends VoteCounter[Ballot] with PreferencePairwiseComparison with LazyLogging { - - def runVoteCounter( - election: Election[Ballot], - candidates: List[Candidate], - param: Parameters, - numVacancies: Int - ): Report[Ballot] = { - - val result: Result = new Result - val report: Report[Ballot] = new Report[Ballot] - - print("\n INPUT ELECTION: \n") - // printElection(election) - - report.setCandidates(candidates) - - report.setWinners(smcWinner(election, candidates, param, numVacancies)) - - report - } - - def smcWinner( - election: Election[Ballot], - ccandidates: List[Candidate], - param: Parameters, - numVacancies: Int - ): List[(Candidate, Rational)] = { - - // it may be possible that param candidates and actual candidates are inconsistent - require( - param.comparisonOrder.isDefined && param.comparisonOrder.get.forall(c => - ccandidates.exists(cand => cand.name == c) - ) - ) - - val zeroRational = Rational(0, 1) - val majorityRational = Rational(1, 2) - - val totalVoters = election.weight - val electionResponse = pairwiseComparison(election, ccandidates) - - // generate the ordered list of candidates - val candOrderList = - param.comparisonOrder.get.map(name => ccandidates.find(cand => cand.name == name).get) - - List((candOrderList.head /: candOrderList.tail)((cA, cB) => { - if ( - electionResponse(ccandidates.indexOf(cA))( - ccandidates.indexOf(cB) - ) > majorityRational * totalVoters - ) cA - else cB - })).map(c => (c, zeroRational)) - - } - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = ??? - -} diff --git a/src/main/scala/agora/votecounter/STV.scala b/src/main/scala/agora/votecounter/STV.scala deleted file mode 100644 index 5a5b99d..0000000 --- a/src/main/scala/agora/votecounter/STV.scala +++ /dev/null @@ -1,119 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -//import scala.collection.mutable.{HashMap => MMap} - -import scala.collection.Map - -import spire.math.Rational - -abstract class STV[B <: Ballot] extends VoteCounter[B] { - - def computeQuota(numVotes: Int, numVacancies: Int): Rational - - def cutQuotaFraction(num: Rational): Rational - - def returnNewWinners( - totals: Map[Candidate, Rational], - quota: Rational - ): List[(Candidate, Rational)] - - def computeTransferValue( - surplus: Rational, - election: Election[B], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational - - def distributeSurplusVotes( - election: Election[B], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[B], Set[B], Option[Election[B]]) - - def resolveSurpluseDistributionTie( - equaltotals: Map[Candidate, Rational] - ): List[(Candidate, Rational)] - - def chooseCandidateForExclusion(totals: Map[Candidate, Rational]): (Candidate, Rational) - - def exclude( - election: Election[B], - candidate: Candidate, - value: Option[Rational], - newWinners: Option[List[Candidate]] - ): (Election[B], Set[B]) - - def removeWinnerWithoutSurplusFromElection(election: Election[B], winner: Candidate): Election[B] - - // def run(e: Election[B], numVacancies: Int): Report[B] = { - // val output = runVoteCounter(e: Election[B], numVacancies: Int) - // result.clear - // report.clear - // output - // } - - def sumTotals(totals: Map[Candidate, Rational]): Rational = { - var sum: Rational = 0 - for (t <- totals) - sum += t._2 - sum - } - - def computeTotal(election: Election[B], candidate: Candidate): Rational = { - var r: Rational = 0 - for (b <- election if !b.preferences.isEmpty && b.preferences.head == candidate) - r = r + b.weight - r - } - - def quotaReached(totals: Map[Candidate, Rational], quota: Rational): Boolean = { - if (totals.exists(_._2 >= quota)) { - println("\nQuota: reached") - true - } else { - println("\nQuota: not reached") - false - } - } - - def ballotsAreContinuing( - c: Candidate, - election: Election[B], - pendingWinners: List[Candidate] - ): Boolean = { - var el = election - var ballotsC = false - while (ballotsC == false && el.nonEmpty) { - val ballot = el.head - if (ballot.preferences.head == c && !ballot.preferences.tail.diff(pendingWinners).isEmpty) { - ballotsC = true - } - el = el.tail - } - println("Has continuing candidates?: " + ballotsC) - ballotsC - } - - // TODO: Optimize: as soon as we found continuing candidate, we can simply attach the rest of the list - def filterPreferences( - preferences: List[Candidate], - candidates: List[Candidate] - ): List[Candidate] = { - var newpreferences: List[Candidate] = Nil - for (c <- preferences) { - candidates.exists(x => x == c) match { - case true => - case false => newpreferences = c :: newpreferences - } - } - newpreferences.reverse - } - -} diff --git a/src/main/scala/agora/votecounter/STVAustralia.scala b/src/main/scala/agora/votecounter/STVAustralia.scala deleted file mode 100644 index 5aab0a2..0000000 --- a/src/main/scala/agora/votecounter/STVAustralia.scala +++ /dev/null @@ -1,101 +0,0 @@ -package agora.votecounter - -import agora.model._ - -import scala.collection.mutable.{HashMap => Map} - -import spire.math.Rational -import agora.votecounter.stv.Input -import agora.votecounter.stv.ACTBallot - -abstract class STVAustralia extends STV[ACTBallot] { - - val result: Result = new Result - - val report = new Report[ACTBallot] - - def tryToDistributeSurplusVotes( - election: Election[ACTBallot], - ccandidates: List[Candidate], - winner: Candidate, - ctotal: Rational, - markings: Option[Set[Int]] - ): (Election[ACTBallot], List[(Candidate, Rational)]) - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // FIXME: This is an ugly hack that should be removed after we get rid of ACTBallot - def runVoteCounterGeneral( - election: Election[PreferenceBallot], - candidates: List[Candidate], - numVacancies: Int - ): Report[PreferenceBallot] = { - - val r = runVoteCounter(convertBallots(election), candidates, numVacancies) - val r1 = new Report[PreferenceBallot] - r1.setWinners(r.getWinners) - r1 - } - - // FIXME: This is an ugly hack that should be removed after we get rid of ACTBallot - def convertBallots(we: Election[PreferenceBallot]): Election[ACTBallot] = - new Election(for (b <- we) yield ACTBallot.fromBallot(b)) // b // ACTBallot.fromBallot(b) - - override def runVoteCounter( - election: Election[ACTBallot], - candidates: List[Candidate], - numVacancies: Int - ): Report[ACTBallot] = { // all ballots of e are marked when the function is called - val quota = cutQuotaFraction(computeQuota(election.length, numVacancies)) - println("Number of ballots:" + election.length) - println("Quota: " + quota) - result.setQuota(quota) - report.setQuota(quota) - - val tls = election.firstVotes(candidates) // Here are totals also of those candidates - // that are NOT OCCURING in the ballots (i.e. when nobody mentioned them in preferences) - result.addTotalsToHistory(tls) - - // report.setCandidates(getCandidates(election)) // Here are candidates OCCURING in the election - report.setCandidates( - candidates - ) // Here are totals also of those candidates that are NOT OCCURING in the ballots - - report.newCount(Input, None, Some(election), Some(tls), None, None) - report.setLossByFractionToZero - - report.setWinners(winners(election, candidates, numVacancies)) - - report - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// ACT Legislation: -// 9(1): If a candidate is excluded in accordance with clause 8, the ballot papers counted for the candidate -// shall be sorted into groups according to their transfer values when counted for him or her. -// - // like ACT -// Senate Legislation: -// (13AA)(a) and (13AA)(b) -// - - def determineStepsOfExclusion( - election: Election[ACTBallot], - candidate: Candidate - ): List[(Candidate, Rational)] = { - var s: Set[(Candidate, Rational)] = Set() - - for (b <- election) { - if ( - b.preferences.nonEmpty && b.preferences.head == candidate && !s.contains( - (candidate, b.value) - ) - ) { - s += ((candidate, b.value)) - } - } - s.toList.sortBy(x => x._2).reverse // > - } - -} diff --git a/src/main/scala/agora/votecounter/SatisfactionApprovalVoting.scala b/src/main/scala/agora/votecounter/SatisfactionApprovalVoting.scala deleted file mode 100644 index 7e943a2..0000000 --- a/src/main/scala/agora/votecounter/SatisfactionApprovalVoting.scala +++ /dev/null @@ -1,29 +0,0 @@ -package agora.votecounter - -import spire.math.Rational - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.immutable.{Map => IMap} -import scala.collection.mutable.{HashMap => MMap} - -/** * https://en.wikipedia.org/wiki/Satisfaction_approval_voting - */ - -object SatisfactionApprovalVoting extends VoteCounter[Ballot] { - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - // following code makes use of additive satisfcation property of Satisfaction Approval Voting - val candidateScoreMap = new MMap[Candidate, Rational] - for (b <- election if !b.preferences.isEmpty) - for (c <- b.preferences) - candidateScoreMap(c) = Rational(1, b.preferences.size) + candidateScoreMap.getOrElse(c, 0) - candidateScoreMap.toList.sortWith(_._2 > _._2).take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/Schulze.scala b/src/main/scala/agora/votecounter/Schulze.scala deleted file mode 100644 index ced0db8..0000000 --- a/src/main/scala/agora/votecounter/Schulze.scala +++ /dev/null @@ -1,75 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.votecounter.common.RankPairwiseComparison - -import spire.math.Rational - -/** Algorithm : https://en.wikipedia.org/wiki/Schulze_method */ -object Schulze extends VoteCounter[RankBallot] with RankPairwiseComparison { - - override def winners( - election: Election[RankBallot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - // FIXME: when ranked ballot is converted to ballot, ties are arbitrarily broken, and this affects the result of Schulze's algorithm. - val electionResponse = pairwiseComparison(election, ccandidates) - - schulzeWinnerRanking(getSchulzeStrongestPathMatrix(electionResponse, ccandidates), ccandidates) - .take(numVacancies) - } - - // calculating the schulze response matrix using algorithm on https://en.m.wikipedia.org/wiki/Schulze_method - // scalastyle:off cyclomatic.complexity - // scalastyle:off method.length - def getSchulzeStrongestPathMatrix( - electionResponse: Array[Array[Rational]], - candidates: List[Candidate] - ): Array[Array[Rational]] = { - - val schulzeMatrix = Array.fill(candidates.size, candidates.size)(Rational(0, 1)) - val csize = candidates.size - - // initialize the schulze matrix - for (i <- 0 until csize) - for (j <- 0 until csize) - if (i != j) { - if (electionResponse(i)(j) > electionResponse(j)(i)) { - schulzeMatrix(i)(j) = electionResponse(i)(j) - } else { - schulzeMatrix(i)(j) = 0 - } - } - - // updating schulze matrix - for (i <- 0 until csize) - for (j <- 0 until csize) - if (i != j) - for (k <- 0 until csize) - if (i != k && j != k) { - schulzeMatrix(j)(k) = Math.max( - schulzeMatrix(j)(k).toInt, - Math.min(schulzeMatrix(j)(i).toInt, schulzeMatrix(i)(k).toInt) - ) - } - - schulzeMatrix - } - - def schulzeWinnerRanking( - schulzeMatrix: Array[Array[Rational]], - candidates: List[Candidate] - ): List[(Candidate, Rational)] = { - - def better(candA: Candidate, candB: Candidate) = - schulzeMatrix(candidates.indexOf(candA))(candidates.indexOf(candB)) > schulzeMatrix( - candidates.indexOf(candB) - )(candidates.indexOf(candA)) - - candidates.sortWith { case (c1, c2) => better(c1, c2) }.map(cand => (cand, Rational(0, 1))) - - } - -} diff --git a/src/main/scala/agora/votecounter/SequentialProportionalApprovalVoting.scala b/src/main/scala/agora/votecounter/SequentialProportionalApprovalVoting.scala deleted file mode 100644 index f5d8b43..0000000 --- a/src/main/scala/agora/votecounter/SequentialProportionalApprovalVoting.scala +++ /dev/null @@ -1,53 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import scala.collection.immutable.{Map => IMap} -import scala.collection.mutable.{HashMap => MMap} - -import spire.math.Rational - -/** * https://en.wikipedia.org/wiki/Sequential_proportional_approval_voting - */ - -object SequentialProportionalApprovalVoting extends VoteCounter[Ballot] with SimpleApproval { - - // following function removes winner and reduces weight on ballot to 1/(N+1) - // where N is the number of winners in one single ballot choice list - def excludeWinner(election: Election[Ballot], winner: (Candidate, Rational)): Election[Ballot] = { - var ballots: List[Ballot] = Nil - for (b <- election) { - if (b.preferences.contains(winner._1)) { - ballots = new Ballot( - b.preferences.filter(_ != winner._1), - b.id, - Rational(b.weight.numerator, b.weight.denominator + 1) - ) :: ballots - } else { - ballots = new Ballot(b.preferences, b.id, b.weight) :: ballots - } - } - Election(ballots) - } - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - var winnerList: List[(Candidate, Rational)] = Nil - var election1 = election - var ccandidates1 = ccandidates - var vacancies = numVacancies - while (vacancies != 0) { - val winner = countApprovals(election1, ccandidates1).toList.sortWith(_._2 > _._2).head - winnerList = winner :: winnerList - election1 = excludeWinner(election1, winner) - ccandidates1 = ccandidates1.filter(_ != winner._1) - vacancies = vacancies - 1 - } - winnerList - } - -} diff --git a/src/main/scala/agora/votecounter/SimpleApproval.scala b/src/main/scala/agora/votecounter/SimpleApproval.scala deleted file mode 100644 index 9237b9f..0000000 --- a/src/main/scala/agora/votecounter/SimpleApproval.scala +++ /dev/null @@ -1,21 +0,0 @@ -package agora.votecounter - -import agora.model.{PreferenceBallot => Ballot, _} -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} -import spire.math.Rational - -trait SimpleApproval { - - def countApprovals( - election: Election[Ballot], - candidates: List[Candidate] - ): MMap[Candidate, Rational] = { - val m = new MMap[Candidate, Rational] - for (b <- election) - for (d <- b.preferences) - m(d) = b.weight + (m.getOrElse(d, 0)) - m - } - -} diff --git a/src/main/scala/agora/votecounter/SimpleSTV.scala b/src/main/scala/agora/votecounter/SimpleSTV.scala deleted file mode 100644 index 689268c..0000000 --- a/src/main/scala/agora/votecounter/SimpleSTV.scala +++ /dev/null @@ -1,163 +0,0 @@ -package agora.votecounter - -import agora.votecounter.stv._ -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} - -import spire.math.Rational -import agora.votecounter.stv.Input - -class SimpleSTV - extends STV[Ballot] - with DroopQuota - with NoFractionInQuota - with NewWinnersNotOrdered[Ballot] - with SimpleSurplusDistributionTieResolution // not necessary because of NewWinnersNotOrdered - with SimpleExclusion - with UnfairExclusionTieResolution - with TransferValueWithDenominatorEqualToTotal - with VoteCounterWithAllBallotsInSurplusDistribution - with ExactWinnerRemoval { - - val result: Result = new Result - - val report: Report[Ballot] = new Report[Ballot] - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - override def runVoteCounter( - election: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int - ): Report[Ballot] = { - val quota = cutQuotaFraction(computeQuota(election.length, numVacancies)) - println("Quota = " + quota) - result.setQuota(quota) - - print("\n INPUT ELECTION: \n") - // printElection(election) - - val tls = election.firstVotes( - candidates - ) // Here are totals of candidates also not OCCURING in the ballots - result.addTotalsToHistory(tls) - - // report.setCandidates(getCandidates(election)) // Here are candidates OCCURING in the election - report.setCandidates(candidates) // Here are candidates also not OCCURING in the election - - report.newCount(Input, None, Some(election), Some(tls), None, None) - - report.setWinners(winners(election, candidates, numVacancies)) - - report - } -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - override def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - println(" \n NEW RECURSIVE CALL \n") - - def mentionedCandidates[B <: PreferenceBallot](election: Election[B]): List[Candidate] = { - val set = new collection.mutable.HashSet[Candidate]() - for (b <- election) - for (c <- b.preferences) - if (!set.exists(n => n == c)) set += c - set.toList - } - - val ccands = mentionedCandidates(election) - - val tls = election.firstVotes(ccandidates) - - println("Totals: " + tls) - - if (ccands.length <= numVacancies) { - for (c <- ccands) yield (c, tls(c)) - } else { - quotaReached(tls, result.getQuota) match { - case true => - println("The quota is reached.") - val ws: List[(Candidate, Rational)] = returnNewWinners(tls, result.getQuota) - println("New winners: " + ws) - result.addPendingWinners(ws.toList, None) - - val vacanciesFilled = ws.length >= numVacancies - - vacanciesFilled match { - case false => - println("Vacancies are not yet filled.") - val newElection = surplusesDistribution(election, numVacancies - ws.length) - // printElection(newElection) - winners( - newElection, - ccandidates.filterNot(ws.contains(_)), - numVacancies - ws.length - ) ::: ws - // TODO: care should be taken that newElection is not empty?! - case true => ws - } - case false => - val leastVotedCandidate = chooseCandidateForExclusion(tls) - println("Excluding " + leastVotedCandidate) - result.addExcludedCandidate(leastVotedCandidate._1, leastVotedCandidate._2) - val newElection = exclusion(election, leastVotedCandidate._1, numVacancies) - // printElection(newElection) - winners( - newElection, - ccandidates.filterNot(x => x == leastVotedCandidate._1), - numVacancies - ) - } - } - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def surplusesDistribution(election: Election[Ballot], numVacancies: Int): Election[Ballot] = { - println("Distribution of surpluses.") - var newElection = election - while (result.getPendingWinners.nonEmpty) { - val (cand, ctotal, markings) = result.takeAndRemoveFirstPendingWinner - newElection = tryToDistributeSurplusVotes(newElection, cand, ctotal) - } - newElection - } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def tryToDistributeSurplusVotes( - election: Election[Ballot], - winner: Candidate, - ctotal: Rational - ): Election[Ballot] = { - - val pendingWinners = result.getPendingWinners.map(x => x._1) - - if (ctotal == result.getQuota || !ballotsAreContinuing(winner, election, pendingWinners)) { - removeWinnerWithoutSurplusFromElection(election, winner) - } else { - println("Distributing the surplus of " + winner) - val surplus = ctotal - result.getQuota - - val tv = computeTransferValue(surplus, election, pendingWinners, winner, None) - println("tv = " + tv) - val res = distributeSurplusVotes(election, winner, ctotal, None, pendingWinners, tv) - res._1 - } - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def exclusion( - election: Election[Ballot], - candidate: Candidate, - numVacancies: Int - ): Election[Ballot] = { - println("Exclusion of " + candidate) - val ex = exclude(election, candidate, None, None) - ex._1 - } - -} diff --git a/src/main/scala/agora/votecounter/SmithSet.scala b/src/main/scala/agora/votecounter/SmithSet.scala deleted file mode 100644 index d9dbe3e..0000000 --- a/src/main/scala/agora/votecounter/SmithSet.scala +++ /dev/null @@ -1,111 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison - -import scala.language.postfixOps - -import spire.math.Rational -import agora.util.matrix.BaseMatrix - -/** Algorithm : http://wiki.electorama.com/wiki/Maximal_elements_algorithms#Floyd-Warshall_algorithm - */ -object SmithSet extends VoteCounter[Ballot] with PreferencePairwiseComparison { - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - val pairWiseComp = pairwiseComparison(e, ccandidates) - - val relationMatrix = getRelationMatrix(e, ccandidates, pairWiseComp) - - val maximalArray = floydWarshallMaximal(relationMatrix, ccandidates) - - maximalArray - .zip(ccandidates) - .filter { case (inMaximal, candidate) => - inMaximal - } - .map { case (inMaximal, candidate) => - (candidate, Rational(0, 1)) - } toList - - } - - /** get the relation matrix for the algorithm as described in - * http://wiki.electorama.com/wiki/Maximal_elements_algorithms#Background - * @param election - * @param ccandidates - * @param pairWiseComp - * @return - */ - def getRelationMatrix( - election: Election[Ballot], - ccandidates: List[Candidate], - pairWiseComp: Array[Array[Rational]] - ): Array[Array[Boolean]] = { - - val relationMatrix = BaseMatrix[Boolean](ccandidates.size, ccandidates.size) { - (i: Int, j: Int) => false - } - - ccandidates.zipWithIndex.foreach(c1 => { - ccandidates.zipWithIndex.foreach(c2 => { - if (c1._2 != c2._2) { - relationMatrix(c1._2)(c2._2) = pairWiseComp(c1._2)(c2._2) >= pairWiseComp(c2._2)(c1._2) - } - }) - }) - relationMatrix - } - - // scalastyle:off cyclomatic.complexity - def floydWarshallMaximal( - relations: Array[Array[Boolean]], - ccandidates: List[Candidate] - ): Array[Boolean] = { - - val isInMaximal = Array.ofDim[Boolean](ccandidates.size) - for (i <- ccandidates.indices) - isInMaximal(i) = true - - // eventually, hasPath[i][j] == true iff there is a path from i to j - val hasPath = Array.ofDim[Boolean](ccandidates.size, ccandidates.size) - for (i <- ccandidates.indices) - for (j <- ccandidates.indices) - if (i != j) { - hasPath(i)(j) = relations(i)(j) - } - - // expand consideration to paths that have intermediate nodes from 1 to k - for (k <- ccandidates.indices) - for (i <- ccandidates.indices) - if (k != i) { - for (j <- ccandidates.indices) - if (k != j && i != j) { - if (hasPath(i)(k) && hasPath(k)(j)) { - hasPath(i)(j) = true - } - } - } - - // disqualify as maximal any candidates that have paths to them - // but no path back to complete a cycle - - for (i <- ccandidates.indices) - for (j <- ccandidates.indices) - if (i != j) { - if (hasPath(j)(i) && !hasPath(i)(j)) { - isInMaximal(i) = false - } - } - - isInMaximal - - } - -} diff --git a/src/main/scala/agora/votecounter/SuperMajority.scala b/src/main/scala/agora/votecounter/SuperMajority.scala deleted file mode 100644 index 1870ca9..0000000 --- a/src/main/scala/agora/votecounter/SuperMajority.scala +++ /dev/null @@ -1,54 +0,0 @@ -package agora.votecounter - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.BipartisanSet.bipartisanSet -import spire.math.Rational - -object SuperMajority extends VoteCounter[Ballot] { - - def runVoteCounter( - election: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int, - param: Parameters - ): Report[Ballot] = { - // print("\n INPUT ELECTION: \n") - // //printElection(election) - - val result: Result = new Result - val report: Report[Ballot] = new Report[Ballot] - - report.setCandidates(candidates) - - report.setWinners(superMajority(election, candidates, numVacancies, param)) - - report - } - - def superMajority( - e: Election[Ballot], - candidates: List[Candidate], - numVacancies: Int, - param: Parameters - ): List[(Candidate, Rational)] = { - val sortedList = e.firstVotes(candidates).toList.sortWith { (ct1, ct2) => - ct1._2 > ct2._2 - } - // Get majority percentage or default to 50% - val percentage = param.majorityPercentage.getOrElse(0.5) - if (percentage >= 0.5 && percentage <= 1.0) { - sortedList.take(numVacancies).filter { case (c, r) => r > (e.length * percentage) } - } else { - // Default to 50% if specified percentage is less than 50% or greater than 100% - sortedList.take(numVacancies).filter { case (c, r) => r > (e.length * 0.5) } - } - } - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = ??? - -} diff --git a/src/main/scala/agora/votecounter/UncoveredSet.scala b/src/main/scala/agora/votecounter/UncoveredSet.scala deleted file mode 100644 index ea752a0..0000000 --- a/src/main/scala/agora/votecounter/UncoveredSet.scala +++ /dev/null @@ -1,67 +0,0 @@ -package agora.votecounter - -import com.typesafe.scalalogging.LazyLogging -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter.common.PreferencePairwiseComparison -import agora.util.matrix._ - -import scala.language.postfixOps - -import spire.math.Rational -import agora.util.matrix.BaseMatrix - -/** Algorithm via multiplication - * http://www.alg.ewi.tudelft.nl/mates2010/media/matesslides/BrandtTournament%20Solutions%20(MATES).pdf - * The Uncovered Set(UC) consists of all uncovered alternatives x covers y (x C y) if D(y) is a - * subset of D(x) where D(x) = { y ⍷ A | x >(majority) y} - */ -object UncoveredSet extends VoteCounter[Ballot] with PreferencePairwiseComparison with LazyLogging { - - override def winners( - e: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - - logger.info("Computing Uncovered Set") - val zeroRational = Rational(0, 1) - val majorityRational = Rational(1, 2) - val electionResponse = pairwiseComparison(e, ccandidates) - val ucMatrix = BaseMatrix[Rational](ccandidates.size, ccandidates.size) { (i: Int, j: Int) => - zeroRational - } - val totalVoters = e.weight - - // mark all majority winners as rational 1 - ccandidates.foreach(c1 => { - ccandidates.foreach(c2 => { - if (ccandidates.indexOf(c1) < ccandidates.indexOf(c2)) { - val pairscore = electionResponse(ccandidates.indexOf(c1))(ccandidates.indexOf(c2)) - if (pairscore > majorityRational * totalVoters) { - ucMatrix(ccandidates.indexOf(c1))(ccandidates.indexOf(c2)) = Rational(1, 1) - } else { - ucMatrix(ccandidates.indexOf(c1))(ccandidates.indexOf(c2)) = Rational(0, 1) - } - } - }) - }) - - // matrix calculation step from algorithm - val uncoveredMatrix = addMatrix( - addMatrix(square(ucMatrix, ccandidates.size), ucMatrix), - identityMatrix(ccandidates.size) - ) - - uncoveredMatrix - .zip(ccandidates) - .filter { case (row, candidate) => - !row.contains(Rational(0, 1)) - } - .map { case (row, candidate) => - (candidate, Rational(0, 1)) - } toList - - } - -} diff --git a/src/main/scala/agora/votecounter/Veto.scala b/src/main/scala/agora/votecounter/Veto.scala deleted file mode 100644 index 1e9625b..0000000 --- a/src/main/scala/agora/votecounter/Veto.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.votecounter - -import agora.model.{PreferenceBallot => Ballot} -import agora.model.Candidate -import agora.model.Election -import scala.collection.mutable.{HashMap => MMap} - -object Veto extends VoteCounter[Ballot] { - - import spire.math.Rational - - def winners( - election: Election[Ballot], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] = { - val candidateScoreMap = new MMap[Candidate, Rational] - for (c <- ccandidates) candidateScoreMap(c) = Rational(0, 1) - - for (ballot <- election) { - for (preference <- ballot.preferences) { - if (!(preference == ballot.preferences.last && ballot.preferences.length > 1)) { - // Automatically assign a weight of 1 to all candidates in the ballot except the last candidate. - // All ballot weights specified in the election file are ignored. - candidateScoreMap(preference) = - candidateScoreMap.getOrElse(preference, Rational(0, 1)) + Rational(1, 1) - } - } - } - - candidateScoreMap.toList.sortWith(_._2 > _._2).take(numVacancies) - } - -} diff --git a/src/main/scala/agora/votecounter/VoteCounter.scala b/src/main/scala/agora/votecounter/VoteCounter.scala deleted file mode 100644 index 0c24022..0000000 --- a/src/main/scala/agora/votecounter/VoteCounter.scala +++ /dev/null @@ -1,39 +0,0 @@ -package agora.votecounter - -import agora.model._ - -import scala.collection.mutable.HashSet -import scala.collection.mutable.{HashMap => Map} - -import spire.math.Rational -import agora.votecounter.stv.Input - -abstract class VoteCounter[B <: Ballot] { - - def winners( - e: Election[B], - ccandidates: List[Candidate], - numVacancies: Int - ): List[(Candidate, Rational)] - - def runVoteCounter( - election: Election[B], - candidates: List[Candidate], - numVacancies: Int - ): Report[B] = { - - val result: Result = new Result - val report: Report[B] = new Report[B] - - var tls = election.firstVotes(candidates) - result.addTotalsToHistory(tls) - report.newCount(Input, None, None, Some(tls), None, None) - - report.setCandidates(candidates) - - report.setWinners(winners(election, candidates, numVacancies)) - - report - } - -} diff --git a/src/main/scala/agora/votecounter/common/PreferencePairwiseComparison.scala b/src/main/scala/agora/votecounter/common/PreferencePairwiseComparison.scala deleted file mode 100644 index f0c7aa5..0000000 --- a/src/main/scala/agora/votecounter/common/PreferencePairwiseComparison.scala +++ /dev/null @@ -1,30 +0,0 @@ -package agora.votecounter.common - -import agora.model.Candidate -import agora.model.Election -import agora.model.PreferenceBallot - -import spire.math.Rational - -trait PreferencePairwiseComparison { - - // utility method for matrix where a[i][j] = x means candidate i has got #x votes against candidate j - def pairwiseComparison( - election: Election[PreferenceBallot], - candidates: List[Candidate] - ): Array[Array[Rational]] = { - val responseMatrix = Array.fill(candidates.size, candidates.size)(Rational(0, 1)) - - for (b <- election) { - val pi = b.preferences.zipWithIndex - for { - (c1, i1) <- pi - (c2, i2) <- pi.take(i1) - } - responseMatrix(candidates.indexOf(c2))(candidates.indexOf(c1)) += b.weight - } - - responseMatrix - } - -} diff --git a/src/main/scala/agora/votecounter/common/RankPairwiseComparison.scala b/src/main/scala/agora/votecounter/common/RankPairwiseComparison.scala deleted file mode 100644 index 3d3ef80..0000000 --- a/src/main/scala/agora/votecounter/common/RankPairwiseComparison.scala +++ /dev/null @@ -1,30 +0,0 @@ -package agora.votecounter.common - -import agora.model.Candidate -import agora.model.Election -import agora.model.RankBallot - -import spire.math.Rational - -trait RankPairwiseComparison { - - // utility method for matrix where a[i][j] = x means candidate i has got #x votes against candidate j - def pairwiseComparison( - election: Election[RankBallot], - candidates: List[Candidate] - ): Array[Array[Rational]] = { - val responseMatrix = Array.fill(candidates.size, candidates.size)(Rational(0, 1)) - - for (b <- election) { - val pi = b.sortedRanks.zipWithIndex - for { - ((c1, s1), i1) <- pi - ((c2, s2), i2) <- pi.take(i1) if s1 != s2 - } - responseMatrix(candidates.indexOf(c2))(candidates.indexOf(c1)) += b.weight - } - - responseMatrix - } - -} diff --git a/src/main/scala/agora/votecounter/stv/ACTBallot.scala b/src/main/scala/agora/votecounter/stv/ACTBallot.scala deleted file mode 100644 index ee2549c..0000000 --- a/src/main/scala/agora/votecounter/stv/ACTBallot.scala +++ /dev/null @@ -1,47 +0,0 @@ -package agora.votecounter.stv - -import scala.language.implicitConversions -import agora.model.PreferenceBallot -import spire.math.Rational -import agora.model.Candidate - -class ACTBallot(p: List[Candidate], override val id: Int, m: Boolean, w: Rational, v: Rational) -//extends MarkedBallot(p, id, m, w) with Value { - extends PreferenceBallot(p, id, w) - with Value - with Marking { - - val value = v - - val marking = m - -} - -object ACTBallot { - - def apply(p: List[Candidate], id: Int, m: Boolean, w: Rational, v: Rational): ACTBallot = - new ACTBallot(p, id, m, w, v) - - implicit def fromBallot(b: PreferenceBallot): ACTBallot = { - new ACTBallot( - b.preferences, - b.id, - true, - b.weight, - b.weight - ) // note that the marking is assigned true here - } - -} - -trait Value extends PreferenceBallot { - - val value: Rational - -} - -trait Marking extends PreferenceBallot { - - val marking: Boolean - -} diff --git a/src/main/scala/agora/votecounter/stv/ACTCandidate.scala b/src/main/scala/agora/votecounter/stv/ACTCandidate.scala deleted file mode 100644 index f7e8479..0000000 --- a/src/main/scala/agora/votecounter/stv/ACTCandidate.scala +++ /dev/null @@ -1,12 +0,0 @@ -package agora.votecounter.stv - -import agora.model.Candidate - -class ACTCandidate( - val ecode: Int, - val pcode: Int, - val ccode: Int, - override val name: String, - override val id: Option[Int] = None, - override val party: Option[String] = None -) extends Candidate(name, id, party) diff --git a/src/main/scala/agora/votecounter/stv/Action.scala b/src/main/scala/agora/votecounter/stv/Action.scala deleted file mode 100644 index 7fcb013..0000000 --- a/src/main/scala/agora/votecounter/stv/Action.scala +++ /dev/null @@ -1,17 +0,0 @@ -package agora.votecounter.stv - -import scala.language.implicitConversions - -sealed abstract class Action - -case object Exclusion extends Action - -case object SurplusDistribution extends Action - -case object ExactWinner extends Action - -case object Input extends Action - -case object VictoryWithoutQuota extends Action - -case object TwoLastCandidatesForOneVacancy extends Action diff --git a/src/main/scala/agora/votecounter/stv/ExactWinnerRemoval.scala b/src/main/scala/agora/votecounter/stv/ExactWinnerRemoval.scala deleted file mode 100644 index c6adb4c..0000000 --- a/src/main/scala/agora/votecounter/stv/ExactWinnerRemoval.scala +++ /dev/null @@ -1,68 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => Map} -import java.io._ - -trait ACTExactWinnerRemoval extends STV[ACTBallot] { - - def removeWinnerWithoutSurplusFromElection( - election: Election[ACTBallot], - winner: Candidate - ): Election[ACTBallot] = { - var list: List[ACTBallot] = Nil - for (b <- election if !b.preferences.isEmpty) - if (b.preferences.head.name != winner.name) { - list = ACTBallot( - filterPreferences(b.preferences, winner :: List()), - b.id, - b.marking, - b.weight, - b.value - ) :: list - } - Election(list) - } - -} - -// exactly like ACTExactWinnerRemoval -trait SenateExactWinnerRemoval extends STV[ACTBallot] { - - def removeWinnerWithoutSurplusFromElection( - election: Election[ACTBallot], - winner: Candidate - ): Election[ACTBallot] = { - var list: List[ACTBallot] = Nil - for (b <- election if !b.preferences.isEmpty) - if (b.preferences.head.name != winner.name) { - list = ACTBallot( - filterPreferences(b.preferences, winner :: List()), - b.id, - b.marking, - b.weight, - b.value - ) :: list - } - Election(list) - } - -} - -trait ExactWinnerRemoval extends STV[Ballot] { - - def removeWinnerWithoutSurplusFromElection( - election: Election[Ballot], - winner: Candidate - ): Election[Ballot] = { - var list: List[Ballot] = Nil - for (b <- election if !b.preferences.isEmpty) - if (b.preferences.head.name != winner.name) { - list = Ballot(filterPreferences(b.preferences, winner :: List()), b.id, b.weight) :: list - } - Election(list) - } - -} diff --git a/src/main/scala/agora/votecounter/stv/Exclusion.scala b/src/main/scala/agora/votecounter/stv/Exclusion.scala deleted file mode 100644 index e7a7fa1..0000000 --- a/src/main/scala/agora/votecounter/stv/Exclusion.scala +++ /dev/null @@ -1,184 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => Map} - -import spire.math.Rational - -trait ACTExclusion extends STV[ACTBallot] { - - def excludeZero( - election: Election[ACTBallot], - candidate: Candidate - ): (Election[ACTBallot], Set[ACTBallot]) = { - var list: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - for (b <- election if b.preferences.nonEmpty) - list = ACTBallot( - (b.preferences.head :: b.preferences.tail).filter(_ != candidate), - b.id, - false, - b.weight, - b.value - ) :: list - (Election(list), setExhausted) - } - - def exclude( - election: Election[ACTBallot], - candidate: Candidate, - value: Option[Rational], - newWinners: Option[List[Candidate]] - ): (Election[ACTBallot], Set[ACTBallot]) = { - var list: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - value match { - case None => throw new Exception("Argument value are missing in trait ACTExclusion") - case Some(v) => - newWinners match { - case None => throw new Exception("Argument newWinners are missing in trait ACTExclusion") - case Some(nW) => - for (b <- election if b.preferences.nonEmpty) { - if (b.preferences.head == candidate && b.value == v) { - if (b.preferences.tail.nonEmpty) { - val restOfPreferences = filterPreferences(b.preferences.tail, candidate :: nW) - if (restOfPreferences.nonEmpty) { - list = ACTBallot(restOfPreferences, b.id, true, b.value, b.value) :: list - } else { - setExhausted += b - } - } - } else { - list = ACTBallot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter { - _ != candidate - }, - nW - ), - b.id, - false, - b.weight, - b.value - ) :: list - } - } - } - } - (Election(list), setExhausted) - } - -} - -// exactly like ACTExclusion -trait SenateExclusion extends STV[ACTBallot] { - - def excludeZero( - election: Election[ACTBallot], - candidate: Candidate - ): (Election[ACTBallot], Set[ACTBallot]) = { - var list: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - for (b <- election if b.preferences.nonEmpty) - list = ACTBallot( - (b.preferences.head :: b.preferences.tail).filter(_ != candidate), - b.id, - false, - b.weight, - b.value - ) :: list - (Election(list), setExhausted) - } - - def exclude( - election: Election[ACTBallot], - candidate: Candidate, - value: Option[Rational], - newWinners: Option[List[Candidate]] - ): (Election[ACTBallot], Set[ACTBallot]) = { - var list: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - value match { - case None => throw new Exception("Argument value are missing in trait ACTExclusion") - case Some(v) => - newWinners match { - case None => throw new Exception("Argument newWinners are missing in trait ACTExclusion") - case Some(nW) => - for (b <- election if b.preferences.nonEmpty) { - if (b.preferences.head == candidate && b.value == v) { - if (b.preferences.tail.nonEmpty) { - val restOfPreferences = filterPreferences(b.preferences.tail, candidate :: nW) - if (restOfPreferences.nonEmpty) { - list = ACTBallot(restOfPreferences, b.id, true, b.value, b.value) :: list - } else { - setExhausted += b - } - } - } else { - list = ACTBallot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter { - _ != candidate - }, - nW - ), - b.id, - false, - b.weight, - b.value - ) :: list - } - } - } - } - (Election(list), setExhausted) - } - -} - -trait SimpleExclusion extends STV[Ballot] { - - def exclude( - election: Election[Ballot], - candidate: Candidate, - value: Option[Rational], - newWinners: Option[List[Candidate]] - ): (Election[Ballot], Set[Ballot]) = { - var list: List[Ballot] = Nil - var setExhausted: Set[Ballot] = Set() - for (b <- election if !b.preferences.isEmpty) { - if (b.preferences.head == candidate) { - if (b.preferences.tail.nonEmpty) { - list = new Ballot(b.preferences.tail, b.id, b.weight) :: list - } else { - setExhausted += b - } - } else { - list = new Ballot( - (b.preferences.head :: b.preferences.tail).filter { - _ != candidate - }, - b.id, - b.weight - ) :: list - } - } - - (Election(list), setExhausted) - } - -} - -trait SimpleExclusionWithFixedElectionSize { - - // Removes the candidate from the ballot but does not reduce the election size by removing empty ballots - def exclude(election: Election[Ballot], candidate: Candidate): Election[Ballot] = { - election.map { b => - val newPrefs = b.preferences.filter(_ != candidate) - new Ballot(newPrefs, b.id, b.weight) - } - } - -} diff --git a/src/main/scala/agora/votecounter/stv/ExclusionTieResolution.scala b/src/main/scala/agora/votecounter/stv/ExclusionTieResolution.scala deleted file mode 100644 index 45fca52..0000000 --- a/src/main/scala/agora/votecounter/stv/ExclusionTieResolution.scala +++ /dev/null @@ -1,117 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} -import collection.Map -import scala.util.Random - -import scala.language.postfixOps - -import spire.math.Rational - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait ExclusionTieResolution { - - def chooseCandidateForExclusion(totals: Map[Candidate, Rational]): (Candidate, Rational) - -} - -trait UnfairExclusionTieResolution { - - def chooseCandidateForExclusion(totals: Map[Candidate, Rational]): (Candidate, Rational) = { - var min = Rational(Int.MaxValue, 1) - for (kv <- totals) if (kv._2 < min) min = kv._2 - val equaltotals = totals.filter(_._2 == min) - equaltotals head - } - -} - -trait PriorRoundExclusionTieResolution { - - def chooseCandidateForExclusion( - equalTotals: Map[Candidate, Rational], - priorRoundTotals: Map[Candidate, Rational] - ): (Candidate, Rational) = { - if (equalTotals.size > 1 && priorRoundTotals.nonEmpty) { - val equalCandidatesList = equalTotals.toList.map(x => x._1) - var smallestCandidate: Candidate = equalCandidatesList.head - for (c <- equalCandidatesList.tail) { - if ( - (priorRoundTotals.getOrElse(c, Rational(0, 1))) < priorRoundTotals.getOrElse( - smallestCandidate, - Rational(0, 1) - ) - ) { - smallestCandidate = c - } - } - // In case there are several candidates with prior totals equal to the minimum candidate total, a random selection is made. - Random - .shuffle(equalTotals.filter { p => - priorRoundTotals.getOrElse(p._1, Rational(0, 1)) == priorRoundTotals.getOrElse( - smallestCandidate, - Rational(0, 1) - ) - }) head - } else { - equalTotals head - } - } - -} - -trait ACTExclusionTieResolution extends STV[ACTBallot] with ExclusionTieResolution { - - val result: Result - - def recFindSmallest( - equaltotals: Map[Candidate, Rational], - totalshistory: List[Map[Candidate, Rational]] - ): Map[Candidate, Rational] = { - if (equaltotals.size > 1 && totalshistory.nonEmpty) { - val listequalcandidates = equaltotals.toList.map(x => x._1) - var smallestcandidate: Candidate = listequalcandidates.head - for (c <- listequalcandidates.tail) { - if ( - (totalshistory.head.getOrElse(c, Rational(0, 1))) < totalshistory.head.getOrElse( - smallestcandidate, - Rational(0, 1) - ) - ) { - smallestcandidate = c - } - } - recFindSmallest( - equaltotals.filter { p => - totalshistory.head.getOrElse(p._1, Rational(0, 1)) == totalshistory.head - .getOrElse(smallestcandidate, Rational(0, 1)) - }, - totalshistory.tail - ) // it may be not unique!!! - } else { - equaltotals - } - } - - def chooseCandidateForExclusion(totals: Map[Candidate, Rational]): (Candidate, Rational) = { - - var min = Rational(Int.MaxValue, 1) - for (kv <- totals) if (kv._2 < min) min = kv._2 - val equaltotals = totals.filter(_._2 == min) - // println("Equal smallest totals: " + equaltotals) - val smallestCandidate = recFindSmallest(equaltotals, result.getTotalsHistoryClone.tail) - if (smallestCandidate.size > 1) { - // If did not manage to resolve tie, take a random candidate (the commissioner decided according to the ACT Electorate act) - // Random.shuffle(equaltotals.toList).head - // If did not manage to resolve tie, the candidate with the "smallest name" (the commissioner decided according to the ACT Electorate act) - equaltotals.minBy(_._1.name) - } else { - smallestCandidate.toList.head - // equaltotals.toList.head - } - } - -} diff --git a/src/main/scala/agora/votecounter/stv/FractionLoss.scala b/src/main/scala/agora/votecounter/stv/FractionLoss.scala deleted file mode 100644 index b58233f..0000000 --- a/src/main/scala/agora/votecounter/stv/FractionLoss.scala +++ /dev/null @@ -1,45 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.votecounter._ -import collection.mutable.{HashMap => Map} - -import spire.math.Rational - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// ACT -// This function takes a lot of time. The running time is large because of it. - -trait ACTFractionLoss extends STV[ACTBallot] { - - def loseFraction(e: Election[ACTBallot], ccandidates: List[Candidate]): Election[ACTBallot] = { - val pt = e.firstVotes(ccandidates) - var newe = e - for ((k, v) <- pt) { - val n = v.toBigDecimal(0, java.math.RoundingMode.DOWN).toInt - // println("k: " + k + "; v: " + v + "; n: " + n) - val neweste = for (b <- newe if !b.preferences.isEmpty) yield { - if (b.preferences.head == k) { - if (v.numerator != 0) { - ACTBallot(b.preferences, b.id, b.marking, b.weight * (n / v), b.value) - } else { - ACTBallot(b.preferences, b.id, b.marking, Rational(0, 1), Rational(0, 1)) - } - } else { - b - } - } - newe = neweste - } - newe - } - -} - -trait NoFractionLoss extends STV[ACTBallot] { - - def loseFraction(e: Election[ACTBallot], ccandidates: List[Candidate]): Election[ACTBallot] = - e - -} diff --git a/src/main/scala/agora/votecounter/stv/NewWinners.scala b/src/main/scala/agora/votecounter/stv/NewWinners.scala deleted file mode 100644 index 0bc1c33..0000000 --- a/src/main/scala/agora/votecounter/stv/NewWinners.scala +++ /dev/null @@ -1,32 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} -import collection.Map - -import spire.math.Rational - -trait NewWinnersOrderedByTotals[B <: Ballot] extends STV[B] with SurplusDistributionTieResolution { - - def returnNewWinners( - totals: Map[Candidate, Rational], - quota: Rational - ): List[(Candidate, Rational)] = { - val ws = totals.filter(_._2 >= quota) - // val lws = ws.toSeq.sortWith(_._2 < _._2).toList - resolveSurpluseDistributionTie(ws) - } - -} - -trait NewWinnersNotOrdered[B <: Ballot] extends STV[B] { - - def returnNewWinners( - totals: Map[Candidate, Rational], - quota: Rational - ): List[(Candidate, Rational)] = - totals.filter(_._2 >= quota).toList - -} diff --git a/src/main/scala/agora/votecounter/stv/NewWinnersDuringExclusion.scala b/src/main/scala/agora/votecounter/stv/NewWinnersDuringExclusion.scala deleted file mode 100644 index 6c7f7d9..0000000 --- a/src/main/scala/agora/votecounter/stv/NewWinnersDuringExclusion.scala +++ /dev/null @@ -1,117 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} - -import collection.Map - -import spire.math.Rational - -// for ACT newElection is newElectionWithoutFractionInTotals -trait ACTNewWinnersDuringExclusion extends ACT { - - def declareNewWinnersWhileExcluding( - candidate: Candidate, - exhaustedBallots: Set[ACTBallot], - newtotals: Map[Candidate, Rational], - totalsWithoutNewWinners: Map[Candidate, Rational], - newElection: Election[ACTBallot] - ): List[(Candidate, Rational)] = { - var newws: List[(Candidate, Rational)] = List() - if (quotaReached(totalsWithoutNewWinners, result.getQuota)) { - newws = returnNewWinners(totalsWithoutNewWinners, result.getQuota) // sorted! - println("New winners as a result of the current partial exclusion: " + newws) - result.addPendingWinners(newws.toList, Some(extractMarkings(newElection))) - // ------------ Reporting ------------------------------------------ - report.newCount( - Exclusion, - Some(candidate), - Some(newElection), - Some(newtotals), - Some(newws), - Some(exhaustedBallots) - ) - } - // ------------ Reporting ------------------------------------------ - else { - report.newCount( - Exclusion, - Some(candidate), - Some(newElection), - Some(totalsWithoutNewWinners), - None, - Some(exhaustedBallots) - ) - } - newws - } - -} - -// Like ACT, but no markings -trait SenateNewWinnersDuringExclusion extends STV[ACTBallot] { - - val result: Result - - val report: Report[ACTBallot] - - def declareNewWinnersWhileExcluding( - candidate: Candidate, - exhaustedBallots: Set[ACTBallot], - newtotals: Map[Candidate, Rational], - totalsWithoutNewWinners: Map[Candidate, Rational], - newElection: Election[ACTBallot] - ): List[(Candidate, Rational)] = { - var newws: List[(Candidate, Rational)] = List() - if (quotaReached(totalsWithoutNewWinners, result.getQuota)) { - newws = returnNewWinners(totalsWithoutNewWinners, result.getQuota) // sorted! - println("New winners as a result of the current partial exclusion: " + newws) - result.addPendingWinners(newws.toList, None) - // ------------ Reporting ------------------------------------------ - report.newCount( - Exclusion, - Some(candidate), - Some(newElection), - Some(newtotals), - Some(newws), - Some(exhaustedBallots) - ) - } - // ------------ Reporting ------------------------------------------ - else { - report.newCount( - Exclusion, - Some(candidate), - Some(newElection), - Some(totalsWithoutNewWinners), - None, - Some(exhaustedBallots) - ) - } - newws - } - -} - -trait NoNewWinnersDuringExclusion extends ACT { - - def declareNewWinnersWhileExcluding( - candidate: Candidate, - exhaustedBallots: Set[ACTBallot], - newtotals: Map[Candidate, Rational], - totalsWithoutNewWinners: Map[Candidate, Rational], - newElectionWithoutFractionInTotals: Election[ACTBallot] - ): List[(Candidate, Rational)] = { - report.newCount( - Exclusion, - Some(candidate), - Some(newElectionWithoutFractionInTotals), - Some(totalsWithoutNewWinners), - None, - Some(exhaustedBallots) - ) - Nil - } - -} diff --git a/src/main/scala/agora/votecounter/stv/NewWinnersDuringSurplusesDistribution.scala b/src/main/scala/agora/votecounter/stv/NewWinnersDuringSurplusesDistribution.scala deleted file mode 100644 index 5ac4398..0000000 --- a/src/main/scala/agora/votecounter/stv/NewWinnersDuringSurplusesDistribution.scala +++ /dev/null @@ -1,54 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} - -import collection.Map - -import spire.math.Rational - -trait ACTNewWinnersDuringSurplusesDistribution extends ACT { - - def declareNewWinnersWhileDistributingSurpluses( - totals: Map[Candidate, Rational], - election: Election[ACTBallot] - ): List[(Candidate, Rational)] = { - var ws: List[(Candidate, Rational)] = List() - if (quotaReached(totals, result.getQuota)) { - ws = returnNewWinners(totals, result.getQuota) // sorted for further surplus distribution! - result.addPendingWinners(ws.toList, Some(extractMarkings(election))) - } - ws - } - -} - -// Like ACTNewWinnersDuringSurplusesDistribution, but None instead of markings -trait SenateNewWinnersDuringSurplusesDistribution extends STV[ACTBallot] { - - val result: Result - - def declareNewWinnersWhileDistributingSurpluses( - totals: Map[Candidate, Rational], - election: Election[ACTBallot] - ): List[(Candidate, Rational)] = { - var ws: List[(Candidate, Rational)] = List() - if (quotaReached(totals, result.getQuota)) { - ws = returnNewWinners(totals, result.getQuota) // sorted for further surplus distribution! - result.addPendingWinners(ws.toList, None) - } - ws - } - -} - -trait NoNewWinnersDuringSurplusesDistribution { - - def declareNewWinnersWhileDistributingSurpluses( - totals: Map[Candidate, Rational], - election: Election[ACTBallot] - ): List[(Candidate, Rational)] = - List() - -} diff --git a/src/main/scala/agora/votecounter/stv/Quota.scala b/src/main/scala/agora/votecounter/stv/Quota.scala deleted file mode 100644 index 00c8274..0000000 --- a/src/main/scala/agora/votecounter/stv/Quota.scala +++ /dev/null @@ -1,74 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ - -import spire.math.Rational - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait DhondtQuotientQuota { - - def computeQuota(numPartyVotes: Int, numSeatsAllocated: Int): Rational = - numPartyVotes / (numSeatsAllocated + 1) - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait DroopQuota { - - def computeQuota(numVotes: Int, numVacancies: Int): Rational = (numVotes / (numVacancies + 1)) + 1 - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait HagenbachBischoffQuota { - - def computeQuota(numVotes: Int, numVacancies: Int): Rational = numVotes / (numVacancies + 1) - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait HareQuota { - - def computeQuota(numVotes: Int, numVacancies: Int): Rational = numVotes / numVacancies - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait HuntingtonHillQuota { - - def computeQuota(numPartyVotes: Int, numSeatsAllocated: Int): Rational = - numPartyVotes / scala.math.sqrt(numSeatsAllocated * (numSeatsAllocated + 1)) - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait ImperialiQuota { - - def computeQuota(numVotes: Int, numVacancies: Int): Rational = numVotes / (numVacancies + 2) - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// ACT, TAS, NSW LC, NSW LG, SENATE -trait NoFractionInQuota { - - def cutQuotaFraction(num: Rational): Rational = - num.toBigDecimal(0, java.math.RoundingMode.DOWN).toInt - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait WebsterSainteLagueQuota { - - def computeQuota(numPartyVotes: Int, numSeatsAllocated: Int): Rational = - numPartyVotes / (2 * numSeatsAllocated + 1) - -} diff --git a/src/main/scala/agora/votecounter/stv/SurplusDistribution.scala b/src/main/scala/agora/votecounter/stv/SurplusDistribution.scala deleted file mode 100644 index 2dcb6be..0000000 --- a/src/main/scala/agora/votecounter/stv/SurplusDistribution.scala +++ /dev/null @@ -1,293 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => Map} -import java.io._ - -import spire.math.Rational - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// * ACT Electoral act 1992: -// -// 1A + 6: -// 1A(1): For this schedule, "count votes", in relation to a candidate, means the number of votes worked out as follows: BPxTV -// 1A(2): However, any fraction is to be disregarded. -// 1A(3): In this clause: -// "BP" means the number of ballot papers to be dealt with at a count that record the next available preference for the candidate. -// "TV" means the transfer value of those ballot papers. -// 6(2) EACH ballot paper COUNTED for the purpose of allotting votes to the successful candidate at the count at which the candidate became successful shall be dealt with as follows: -// 6(2)(a) if it does not specify a next available preference—it shall be set aside as finally dealt with for this part; -// 6(2)(b) if it specifies a next available preference—it shall be grouped according to the candidate for whom that preference is recorded. -// 6(3): The count votes for each continuing candidate shall be determined and allotted to him or her. -// 6(4) After the allotment under subclause 6(3), the continuing candidates' total votes shall be calculated and, if the total votes of a candidate equal or exceed the quota, the candidate is successful. - -// * ACT Electoral act 1992: 1C(2) + 1C(4): -// 1C(4): However, if the transfer value of a ballot paper worked out in accordance with subclause (2) would be greater than the transfer value of the ballot paper when counted for the successful candidate, -// the transfer value of that ballot paper is the transfer value of the ballot paper when counted for the successful candidate. -// i.e.: -// if (tv > weight_i) then weight_(i+1) = weight_i else weight_(i+1) = tv -// -trait ACTSurplusDistribution extends STV[ACTBallot] { - - def distributeSurplusVotes( - election: Election[ACTBallot], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[ACTBallot], Set[ACTBallot], Option[Election[ACTBallot]]) = { - var list: List[ACTBallot] = Nil - var listIgnored: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - markings match { - case None => throw new Exception("Last parcel is undetermined.") - case Some(mrks) => - for (b <- election if !b.preferences.isEmpty) { - - if (b.preferences.head == candidate) { - - val continuingPreferences = - filterPreferences(b.preferences.tail, candidate :: pendingWinners) - if (continuingPreferences.nonEmpty) { - // NOTE: HERE WE IGNORE BALLOTS THAT HAVE candidate AS FP BUT ARE NOT MARKED. THESE BALLOTS BECOME OUT OF SCRUTINY: - if (mrks.contains(b.id)) { - if (transferValue > b.value) { // 1C(4) of the ACT Electoral act 1992 Schedule 4 - list = ACTBallot( - continuingPreferences, - b.id, - true, - b.value, - b.value - ) :: list // take care of b.weight (4th argument) here - } else { - list = ACTBallot( - continuingPreferences, - b.id, - true, - transferValue, - transferValue - ) :: list // take care of b.weight (4th argument) here - } - } else { - listIgnored = b :: listIgnored // this ballot is lost because it does not belong to the last parcel - } - } else { - setExhausted += b // this ballot is exhausted - } - } else { - list = ACTBallot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter(_ != candidate), - pendingWinners - ), - b.id, - false, - b.weight, - b.value - ) :: list - } - } - } - // println("setExhausted " + setExhausted) - // println("listIgnored " + listIgnored) - (Election(list), setExhausted, Some(Election(listIgnored))) - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Section 273 (9) -trait SenateSurplusDistribution extends STV[ACTBallot] { - - def distributeSurplusVotes( - election: Election[ACTBallot], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[ACTBallot], Set[ACTBallot], Option[Election[ACTBallot]]) = { - var list: List[ACTBallot] = Nil - var listIgnored: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - - for (b <- election if !b.preferences.isEmpty) { - if (b.preferences.head == candidate) { - val continuingPreferences = - filterPreferences(b.preferences.tail, candidate :: pendingWinners) - if (continuingPreferences.nonEmpty) { - list = ACTBallot(continuingPreferences, b.id, true, transferValue, transferValue) :: list - } else { - setExhausted += b // this ballot is exhausted - } - } else { - list = ACTBallot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter { - _ != candidate - }, - pendingWinners - ), - b.id, - false, - b.weight, - b.value - ) :: list - } - } - // println("setExhausted " + setExhausted) - // println("listIgnored " + listIgnored) - (Election(list), setExhausted, Some(Election(listIgnored))) - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait ACTVoteCounterWithAllContinuingBallotsInSurplusDistribution extends STV[ACTBallot] { - - def distributeSurplusVotes( - election: Election[ACTBallot], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[ACTBallot], Set[ACTBallot], Option[Election[ACTBallot]]) = { - - var list: List[ACTBallot] = Nil - var setExhausted: Set[ACTBallot] = Set() - - for (b <- election if !b.preferences.isEmpty) { - - if (b.preferences.head == candidate) { - val continuingPreferences = - filterPreferences(b.preferences.tail, candidate :: pendingWinners) - if (continuingPreferences.nonEmpty) { - if (transferValue > b.value) { // 1C(4) of the ACT Electoral act 1992 Schedule 4 - list = ACTBallot( - continuingPreferences, - b.id, - true, - b.value, - b.value - ) :: list // take care of b.weight (4th argument) here - } else { - list = ACTBallot( - continuingPreferences, - b.id, - true, - transferValue, - transferValue - ) :: list // take care of b.weight (4th argument) here - } - } else { - setExhausted += b // this ballot is exhausted - } - } else { - list = ACTBallot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter { - _ != candidate - }, - pendingWinners - ), - b.id, - false, - b.weight, - b.value - ) :: list - } - } - (Election(list), setExhausted, None) - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait VoteCounterWithAllContinuingBallotsInSurplusDistribution extends STV[Ballot] { - - def distributeSurplusVotes( - election: Election[Ballot], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[Ballot], Set[Ballot], Option[Election[Ballot]]) = { - - var list: List[Ballot] = Nil - var setExhausted: Set[Ballot] = Set() - - for (b <- election if !b.preferences.isEmpty) { - - if (b.preferences.head == candidate) { - val continuingPreferences = - filterPreferences(b.preferences.tail, candidate :: pendingWinners) - if (continuingPreferences.nonEmpty) { - list = new Ballot(continuingPreferences, b.id, b.weight * transferValue) :: list - } else { - setExhausted += b // this ballot is exhausted - } - } else { - list = new Ballot( - b.preferences.head :: filterPreferences( - b.preferences.tail.filter { - _ != candidate - }, - pendingWinners - ), - b.id, - b.weight - ) :: list - } - } - (Election(list), setExhausted, None) - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait VoteCounterWithAllBallotsInSurplusDistribution extends STV[Ballot] { - - def distributeSurplusVotes( - election: Election[Ballot], - candidate: Candidate, - total: Rational, - markings: Option[Set[Int]], - pendingWinners: List[Candidate], - transferValue: Rational - ): (Election[Ballot], Set[Ballot], Option[Election[Ballot]]) = { - - var list: List[Ballot] = Nil - var setExhausted: Set[Ballot] = Set() - - for (b <- election if !b.preferences.isEmpty) { - - if (b.preferences.head == candidate) { - if (b.preferences.tail.nonEmpty) { - list = new Ballot(b.preferences.tail, b.id, b.weight * transferValue) :: list - } else { - setExhausted += b // this ballot is exhausted - } - } else { - list = new Ballot( - (b.preferences.head :: b.preferences.tail).filter { - _ != candidate - }, - b.id, - b.weight - ) :: list - } - } - (Election(list), setExhausted, None) - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/main/scala/agora/votecounter/stv/SurplusDistributionTieResolution.scala b/src/main/scala/agora/votecounter/stv/SurplusDistributionTieResolution.scala deleted file mode 100644 index 98623b1..0000000 --- a/src/main/scala/agora/votecounter/stv/SurplusDistributionTieResolution.scala +++ /dev/null @@ -1,176 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} -import collection.Map -import scala.util.Random - -import spire.math.Rational - -trait SurplusDistributionTieResolution { - - def resolveSurpluseDistributionTie( - equaltotals: Map[Candidate, Rational] - ): List[(Candidate, Rational)] - -} - -// Section 273 (22) -trait SenateSurplusDistributionTieResolution - extends STV[ACTBallot] - with SurplusDistributionTieResolution { - - val result: Result - - def resolveSurpluseDistributionTie( - totalsOfWinners: Map[Candidate, Rational] - ): List[(Candidate, Rational)] = { - val candidates = totalsOfWinners.map(_._1).toSet - val listwithtieresolved = recOrder(candidates, result.getTotalsHistoryClone) - val totals = result.getTotalsHistoryClone.head - for (l <- listwithtieresolved) yield (l, totals(l)) - } - - def recOrder( - candidates: Set[Candidate], - totalshistory: List[Map[Candidate, Rational]] - ): List[Candidate] = { - if (candidates.nonEmpty) { - if (totalshistory.nonEmpty) { - val totals = totalshistory.head - println(" totals: " + totals) - var setOfValues: Set[Rational] = Set() - for (candidate <- totals.filterKeys(candidates).map(_._1)) { - if (setOfValues.contains(totals(candidate))) { - recOrder(candidates, totalshistory.tail) - } else { - setOfValues = setOfValues + totals(candidate) - } - } - // they are pairwise different, we just need to sort them by their totals - totals.filter(p => candidates.contains(p._1)).toList.sortBy(x => x._2).reverse.map(_._1) - } else { // the Australian Electoral Officer shall determine the order - Random.shuffle( - candidates.toList - ) // If did not manage to resolve the tie, shuffle them randomly - } - } else { - throw new Exception("Empty set of winners with equal surplus.") - } - } - -} - -trait ACTSurplusDistributionTieResolution - extends STV[ACTBallot] - with SurplusDistributionTieResolution { - - val result: Result - - def recOrderIdentical( - equaltotals: List[Candidate], - totalshistory: List[Map[Candidate, Rational]] - ): List[Candidate] = { - - if (totalshistory.nonEmpty) { - var biggestcandidate: Candidate = equaltotals.head - for (c <- equaltotals) { - if ( - totalshistory.head.getOrElse(c, Rational(0, 1)) > totalshistory.head.getOrElse( - biggestcandidate, - Rational(0, 1) - ) - ) biggestcandidate = c - } - val biggestcandidates = totalshistory.head.filter { p => - p._2 == totalshistory.head(biggestcandidate) && equaltotals.toSet.contains(p._1) == true - } - val lbiggestcandidates = biggestcandidates.toList.map(x => x._1) - val totalsofremainingcandidates = totalshistory.head.filterKeys(k => - lbiggestcandidates.toSet.contains(k) == false && equaltotals.toSet.contains(k) == true - ) - val listoftotalsofremainingcandidates = - totalsofremainingcandidates.toList.sortBy(x => x._2).reverse - if (biggestcandidates.size > 1) { - recOrderIdentical(lbiggestcandidates, totalshistory.tail) ::: - recOrderDifferent( - totalsofremainingcandidates, - listoftotalsofremainingcandidates, - totalshistory - ) - } else { - lbiggestcandidates.head :: recOrderDifferent( - totalsofremainingcandidates, - listoftotalsofremainingcandidates, - totalshistory - ) - } - } else { - // If did not manage to resolve tie, shuffle them randomly (the commissioner decided according to the ACT Electorate act) - // Random.shuffle(equaltotals.toList) - equaltotals.sortBy( - _.name - ) // If did not manage to resolve tie, sort them by name (the commissioner decided according to the ACT Electorate act) - } - } - - def recOrderDifferent( - totalsOfWinners: Map[Candidate, Rational], - sortedlist: List[(Candidate, Rational)], - totalshistory: List[Map[Candidate, Rational]] - ): List[Candidate] = { - if (sortedlist.nonEmpty) { - var c = sortedlist.head - var equaltoc = totalsOfWinners.filter(_._2 == c._2) - if (equaltoc.size > 1) { - var twf = totalsOfWinners.filter(_._2 != c._2) - if (twf.nonEmpty) { - recOrderIdentical( - equaltoc.toList.map(x => x._1), - totalshistory.tail - ) ::: recOrderDifferent(twf, sortedlist.filter(p => p._2 != c._2), totalshistory) - } else { - recOrderIdentical(equaltoc.toList.map(x => x._1), totalshistory.tail) - } - } else { - if (sortedlist.tail.nonEmpty) { - c._1 :: recOrderDifferent( - totalsOfWinners.filter { - _ != c - }, - sortedlist.tail, - totalshistory - ) - } else { - c._1 :: List() - } - } - } else { - List() - } - } - - def resolveSurpluseDistributionTie( - totalsOfWinners: Map[Candidate, Rational] - ): List[(Candidate, Rational)] = { - val sortedList = totalsOfWinners.toList.sortBy(x => x._2).reverse // > - // println("sortedList: " + sortedList) - val listwithtieresolved = - recOrderDifferent(totalsOfWinners, sortedList, result.getTotalsHistoryClone) - for (l <- listwithtieresolved) yield (l, totalsOfWinners(l)) - } - -} - -trait SimpleSurplusDistributionTieResolution - extends STV[Ballot] - with SurplusDistributionTieResolution { - - def resolveSurpluseDistributionTie( - equaltotals: Map[Candidate, Rational] - ): List[(Candidate, Rational)] = - equaltotals.toList.sortBy(x => x._2).reverse // > - -} diff --git a/src/main/scala/agora/votecounter/stv/TotalsDuringExclusion.scala b/src/main/scala/agora/votecounter/stv/TotalsDuringExclusion.scala deleted file mode 100644 index c406d81..0000000 --- a/src/main/scala/agora/votecounter/stv/TotalsDuringExclusion.scala +++ /dev/null @@ -1,69 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.votecounter._ -import collection.mutable.{HashMap => MMap} -import collection.Map -import scala.util.Random - -import spire.math.Rational - -trait ACTTotalsDuringExclusion extends ACT { - - def computeIncorrectTotalofEVACS( - step: (Candidate, Rational), - newElectionWithoutFractionInTotals: Election[ACTBallot] - ): Option[Int] = { - val roundedExcludedTotal = computeRoundedExcludedTotal(step, newElectionWithoutFractionInTotals) - val previousTotalOfTheCandidate = - result.getTotalsHistoryClone - .head(step._1) - .toInt // TODO: take care here, check that it is correct - val newTotal = previousTotalOfTheCandidate - roundedExcludedTotal - println("ACT's total of candidate being excluded: " + newTotal) - Some(newTotal) - } - - def computeRoundedExcludedTotal( - step: (Candidate, Rational), - election: Election[ACTBallot] - ): Int = { - var numOccurences = 0 - for (b <- election) - if (b.preferences.head == step._1 && b.value == step._2) { - numOccurences += 1 - } - val total = numOccurences * step._2 - val roundedtotal = total.toBigDecimal(0, java.math.RoundingMode.DOWN).toInt - roundedtotal - } - - def rewriteTotalOfCandidate( - totals: Map[Candidate, Rational], - candidate: Candidate, - newTotal: Option[Int] - ): Map[Candidate, Rational] = { - newTotal match { - case Some(t) => totals + (candidate -> t) - case None => totals - } - } - -} - -// Totals as the sum of weights of ballots in partial exclusion (in contrast to how it is done in EVACS) -trait RegularTotalsDuringExclusion { - - def rewriteTotalOfCandidate( - totals: Map[Candidate, Rational], - candidate: Candidate, - newTotal: Option[Int] - ): Map[Candidate, Rational] = - totals - - def computeIncorrectTotalofEVACS( - step: (Candidate, Rational), - newElectionWithoutFractionInTotals: Election[ACTBallot] - ): Option[Int] = None - -} diff --git a/src/main/scala/agora/votecounter/stv/TransferValue.scala b/src/main/scala/agora/votecounter/stv/TransferValue.scala deleted file mode 100644 index 576be2d..0000000 --- a/src/main/scala/agora/votecounter/stv/TransferValue.scala +++ /dev/null @@ -1,185 +0,0 @@ -package agora.votecounter.stv - -import agora.model._ -import agora.model.{PreferenceBallot => Ballot} -import agora.votecounter._ -import collection.mutable.{HashMap => Map} -import java.io._ - -import spire.math.Rational - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait TransferValueWithDenominatorWithNumOfMarkedContinuingBallots extends STV[ACTBallot] { - - def computeTransferValue( - surplus: Rational, - election: Election[ACTBallot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - // println("Pending winners: " + pendingWinners) - var num = 0 - markings match { - case None => throw new Exception("Last parcel is undetermined.") - case Some(mrks) => - for (b <- election if !b.preferences.isEmpty) { - - if ( - b.preferences.head == candidate && !b.preferences.tail - .diff(pendingWinners) - .isEmpty && mrks.contains(b.id) - ) { num = num + 1 } - } - println("Surplus: " + surplus) - println("Denominator: " + num) - surplus / num - } - } - -} - -trait TransferValueWithDenominatorWithNumOfMarkedContinuingBallotsOrOne { - - def computeTransferValue( - surplus: Rational, - election: Election[ACTBallot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - // println("TV with denominator with the cardinality of marked non-exhausted ballots") - var num = 0 - markings match { - case None => throw new Exception("Last parcel is undetermined.") - case Some(mrks) => - for (b <- election if !b.preferences.isEmpty) { - if ( - b.preferences.head == candidate && !b.preferences.tail - .diff(pendingWinners) - .isEmpty && mrks.contains(b.id) - ) { num = num + 1 } - } - } - if (num == 0) { - println("Denominator is equal to 0 !!!!!!!!!!!!!!!!!!!!") - } - - var tv: Rational = 1 - if (num != 0) tv = surplus / num - if (tv > 1) 1 else tv - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait TransferValueWithDenominatorWithNumOfBallots extends STV[ACTBallot] { - - def computeTransferValue( - surplus: Rational, - election: Election[ACTBallot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - var num = 0 - for (b <- election if !b.preferences.isEmpty) - if (b.preferences.head == candidate) { num = num + 1 } - println("Denominator: " + num) - println("Surplus: " + surplus) - surplus / num - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -trait TransferValueWithDenominatorWithNumOfAllContinuingBallots extends STV[ACTBallot] { - - def computeTransferValue( - surplus: Rational, - election: Election[ACTBallot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - var num = 0 - for (b <- election if !b.preferences.isEmpty) { - if (b.preferences.head == candidate && !b.preferences.tail.diff(pendingWinners).isEmpty) { - num = num + 1 - } - } - // println("Denominator: " + num) - surplus / num - } - -} - -trait TransferValueWithDenominatorWithNumOfAllContinuingBallotsOrOne extends STV[ACTBallot] { - - def computeTransferValue( - surplus: Rational, - election: Election[ACTBallot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - var num = 0 - for (b <- election if !b.preferences.isEmpty) { - if (b.preferences.head == candidate && !b.preferences.tail.diff(pendingWinners).isEmpty) { - num = num + 1 - } - } - if (num == 0) { - println("Denominator is equal to 0 !!!!!!!!!!!!!!!!!!!!") - } - - var tv: Rational = 1 - if (num != 0) tv = surplus / num - if (tv > 1) 1 else tv - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// TV = surplus / total of continuing ballot papers (i.e. with further continuing preferences) -trait TransferValueWithDenominatorWithTotalOfContinuingBallots extends STV[Ballot] { - - def computeTransferValue( - surplus: Rational, - election: Election[Ballot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = { - var num: Rational = 0 - for (b <- election if !b.preferences.isEmpty) { - if (b.preferences.head == candidate && !b.preferences.tail.diff(pendingWinners).isEmpty) { - num = num + b.weight - } - } - // println("Denominator: " + num) - surplus / num - } - -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -/* - * TV = surplus / total - * */ -trait TransferValueWithDenominatorEqualToTotal extends STV[Ballot] { - - def computeTransferValue( - surplus: Rational, - election: Election[Ballot], - pendingWinners: List[Candidate], - candidate: Candidate, - markings: Option[Set[Int]] - ): Rational = - surplus / computeTotal(election, candidate) - -} diff --git a/src/test/scala/agora/analyzer/SinglePeakednessTest.scala b/src/test/scala/agora/analyzer/SinglePeakednessTest.scala deleted file mode 100644 index f5530e7..0000000 --- a/src/test/scala/agora/analyzer/SinglePeakednessTest.scala +++ /dev/null @@ -1,40 +0,0 @@ -package agora.analyzer - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import org.specs2.mutable.Specification - -/** Examples 1,2,3,4 from http://www.lamsade.dauphine.fr/~lang/papers/elo-ecai08.pdf */ -class SinglePeakednessTest extends Specification { - - val singlePeaked = true - val notSinglePeaked = false - - "Single Peakedness Test " should { - - "verify result" in { - singlePeakednessVerification("24-example.e", "24-candidates.txt") shouldEqual singlePeaked - } - "verify result" in { - singlePeakednessVerification("25-example.e", "24-candidates.txt") shouldEqual notSinglePeaked - } - "verify result" in { - singlePeakednessVerification("26-example.e", "24-candidates.txt") shouldEqual singlePeaked - } - "verify result" in { - singlePeakednessVerification("27-example.e", "27-candidates.txt") shouldEqual notSinglePeaked - } - "verify result" in { - singlePeakednessVerification("28-example.e", "27-candidates.txt") shouldEqual notSinglePeaked - } - } - - def singlePeakednessVerification(electionFile: String, candidatesFile: String): Boolean = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - SinglePeakAnalyser.analyse(election, candidates) - } - -} diff --git a/src/test/scala/agora/analyzer/ValueRestrictedAnalyserTest.scala b/src/test/scala/agora/analyzer/ValueRestrictedAnalyserTest.scala deleted file mode 100644 index e42f9ba..0000000 --- a/src/test/scala/agora/analyzer/ValueRestrictedAnalyserTest.scala +++ /dev/null @@ -1,36 +0,0 @@ -package agora.analyzer - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import org.specs2.mutable.Specification - -class ValueRestrictedAnalyserTest extends Specification { - - val expectedAnalysis1 = true - val expectedAnalysis2 = false - - "Value Restricted Analyser " should { - - "verify result" in { - valueRestrictedAnalyserVerification( - "22-example.e", - "13-candidates.txt" - ) shouldEqual expectedAnalysis1 - } - "verify result" in { - valueRestrictedAnalyserVerification( - "31-example.e", - "13-candidates.txt" - ) shouldEqual expectedAnalysis2 - } - } - - def valueRestrictedAnalyserVerification(electionFile: String, candidateFile: String): Boolean = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidateFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - ValueRestrictedAnalyser.analyse(election, candidates) - } - -} diff --git a/src/test/scala/agora/comparator/FishburnsExtensionTest.scala b/src/test/scala/agora/comparator/FishburnsExtensionTest.scala deleted file mode 100644 index 73434d6..0000000 --- a/src/test/scala/agora/comparator/FishburnsExtensionTest.scala +++ /dev/null @@ -1,45 +0,0 @@ -package agora.comparator - -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class FishburnsExtensionTest extends Specification { - - val expectedFishburnSet1 = Set(Candidate("A"), Candidate("B")) - val expectedFishburnSet2 = Set(Candidate("A"), Candidate("B"), Candidate("C")) - - "UnconveredSet Test " should { - - "verify result" in { - fisburnsExtensionVerification( - "46-example.e", - "46-candidates.txt", - "fishburns-param.json" - ) shouldEqual expectedFishburnSet1 - } - "verify result" in { - fisburnsExtensionVerification( - "46-example.e", - "46-candidates.txt", - "fishburns-param1.json" - ) shouldEqual expectedFishburnSet2 - } - } - - def fisburnsExtensionVerification( - electionFile: String, - candidatesFile: String, - parameterFile: String - ): Set[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - val parameters = ParameterParser.parse("../Agora/files/Examples/" + parameterFile) - - FishburnsExtension.compare(election, candidates, parameters) - } - -} diff --git a/src/test/scala/agora/comparator/KellysExtensionTest.scala b/src/test/scala/agora/comparator/KellysExtensionTest.scala deleted file mode 100644 index 8085127..0000000 --- a/src/test/scala/agora/comparator/KellysExtensionTest.scala +++ /dev/null @@ -1,68 +0,0 @@ -package agora.comparator - -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class KellysExtensionTest extends Specification { - - val expectedKellySet1 = Set(Candidate("A")) - val expectedKellySet2 = Set(Candidate("A"), Candidate("B")) - val expectedKellySet3 = Set(Candidate("B")) - val expectedKellySet4 = Set(Candidate("B"), Candidate("C")) - - "UnconveredSet Test " should { - - "verify result" in { - kellysExtensionVerification( - "45-example.e", - "45-candidates.txt", - "kellys-sets.json" - ) shouldEqual expectedKellySet1 - } - "verify result" in { - kellysExtensionVerification( - "45-example.e", - "45-candidates.txt", - "kellys-sets1.json" - ) shouldEqual expectedKellySet2 - } - "verify result" in { - kellysExtensionVerification( - "45-example.e", - "45-candidates.txt", - "kellys-sets2.json" - ) shouldEqual expectedKellySet3 - } - "verify result" in { - kellysExtensionVerification( - "45-example.e", - "45-candidates.txt", - "kellys-sets3.json" - ) shouldEqual expectedKellySet4 - } - "verify result" in { - kellysExtensionVerification( - "45-example.e", - "45-candidates.txt", - "kellys-sets4.json" - ) shouldEqual expectedKellySet2 - } - } - - def kellysExtensionVerification( - electionFile: String, - candidatesFile: String, - parameterFile: String - ): Set[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - val parameters = ParameterParser.parse("../Agora/files/Examples/" + parameterFile) - - KellyExtension.compare(election, candidates, parameters) - } - -} diff --git a/src/test/scala/agora/votecounter/BaldwinTest.scala b/src/test/scala/agora/votecounter/BaldwinTest.scala deleted file mode 100644 index 14d282f..0000000 --- a/src/test/scala/agora/votecounter/BaldwinTest.scala +++ /dev/null @@ -1,25 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class BaldwinTest extends Specification { - - val expectedBaldwinWinnerList = List(Candidate("A")) - - "Baldwin Test " should { - - "verify result" in { baldwinVerification("13-example.e") shouldEqual expectedBaldwinWinnerList } - } - - def baldwinVerification(electionFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/13-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - BaldwinMethod.winners(election, candidates, candidates.length).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/BipartisanSetTest.scala b/src/test/scala/agora/votecounter/BipartisanSetTest.scala deleted file mode 100644 index 5fb1519..0000000 --- a/src/test/scala/agora/votecounter/BipartisanSetTest.scala +++ /dev/null @@ -1,46 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class BipartisanSetTest extends Specification { - - val expectedBipartisanSet = Set(Candidate("A"), Candidate("B"), Candidate("C")) - val expectedBipartisanSet1 = - Set(Candidate("A"), Candidate("B"), Candidate("C"), Candidate("D"), Candidate("E")) - - "BipartisanSet Test " should { - - "verify result" in { - bipartisanSetVerification( - "41-example.e", - "37-candidates.txt", - "bipartisan-param.json" - ) shouldEqual expectedBipartisanSet - } - "verify result" in { - bipartisanSetVerification( - "38-example.e", - "37-candidates.txt", - "bipartisan-param1.json" - ) shouldEqual expectedBipartisanSet1 - } - } - - def bipartisanSetVerification( - electionFile: String, - candidatesFile: String, - paramFile: String - ): Set[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - val param = ParameterParser.parse("../Agora/files/Examples/" + paramFile) - - BipartisanSet.bipartisanSet(election, candidates, param).map(_._1).toSet - } - -} diff --git a/src/test/scala/agora/votecounter/BordaRuleTest.scala b/src/test/scala/agora/votecounter/BordaRuleTest.scala deleted file mode 100644 index cfb9683..0000000 --- a/src/test/scala/agora/votecounter/BordaRuleTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 19/06/17. */ -class BordaRuleTest extends Specification { - - val expectedBordaWinnerList = List( - Candidate("Nashville"), - Candidate("Chattanooga"), - Candidate("Memphis"), - Candidate("Knoxville") - ) - - "Borda Rule Test " should { - - "verify result" in { - bordaRuleMethodVerification("14-example.e") shouldEqual expectedBordaWinnerList - } - } - - def bordaRuleMethodVerification(electionFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/14-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Borda.winners(election, candidates, candidates.length).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/BucklinTest.scala b/src/test/scala/agora/votecounter/BucklinTest.scala deleted file mode 100644 index 34968cb..0000000 --- a/src/test/scala/agora/votecounter/BucklinTest.scala +++ /dev/null @@ -1,29 +0,0 @@ -package votecounter - -import agora.model.Candidate -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.votecounter.Bucklin -import org.specs2.mutable.Specification - -class BucklinTest extends Specification { - - // Test data obtained from https://en.wikipedia.org/wiki/Bucklin_voting#Example_application - val expectedBucklinWinnerList = List(Candidate("Nashville")) - - "Bucklin Vote Test " should { - - "verify result" in { - bucklinVerification("48-example.e", "48-candidates.txt") shouldEqual expectedBucklinWinnerList - } - } - - def bucklinVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Bucklin.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/ContingentTest.scala b/src/test/scala/agora/votecounter/ContingentTest.scala deleted file mode 100644 index 4a8a780..0000000 --- a/src/test/scala/agora/votecounter/ContingentTest.scala +++ /dev/null @@ -1,29 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -//To verify tests using example from Wikipedia - https://en.wikipedia.org/wiki/Contingent_vote#Example_I - -class ContingentTest extends Specification { - - val expectedContingentWinner = List(Candidate("Catherine")) - - "Contingent Test " should { - - "verify result" in { - contingentMethodVerification("15-example.e") shouldEqual expectedContingentWinner - } - } - - def contingentMethodVerification(electionFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/15-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Contingent.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/CoombTest.scala b/src/test/scala/agora/votecounter/CoombTest.scala deleted file mode 100644 index d03f966..0000000 --- a/src/test/scala/agora/votecounter/CoombTest.scala +++ /dev/null @@ -1,39 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import scala.collection.mutable.ListBuffer - -class CoombTest extends Specification { - - val expectedCoombWinnerList = List(Candidate("Nashville")) - val expectedCoombWinnerList1 = List( - Candidate("B") - ) // tie resolution test case both A/B could be a winner here - - "Coomb Test" should { - "verify result" in { - coombMethodVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedCoombWinnerList - coombMethodVerification( - "23-example.e", - "23-candidates.txt" - ) shouldEqual expectedCoombWinnerList1 - } - } - - def coombMethodVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Coomb.winners(election, candidates, 1).map(_._1) - - } - -} diff --git a/src/test/scala/agora/votecounter/CopelandTest.scala b/src/test/scala/agora/votecounter/CopelandTest.scala deleted file mode 100644 index 2811fa7..0000000 --- a/src/test/scala/agora/votecounter/CopelandTest.scala +++ /dev/null @@ -1,38 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 27/06/17. */ -class CopelandTest extends Specification { - - val expectedCopelandWinnerList = List(Candidate("Nashville")) - val expectedCopelandWinnerList2 = List(Candidate("A")) - - "Copeland Test " should { - - "verify result" in { - copelandMethodVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedCopelandWinnerList - } - "verify result" in { - copelandMethodVerification( - "29-example.e", - "28-candidates.txt" - ) shouldEqual expectedCopelandWinnerList2 - } - } - - def copelandMethodVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Copeland.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/DodgsonTest.scala b/src/test/scala/agora/votecounter/DodgsonTest.scala deleted file mode 100644 index f56dd49..0000000 --- a/src/test/scala/agora/votecounter/DodgsonTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Example : Page 2 - * https://www.maa.org/sites/default/files/pdf/cmj_ftp/CMJ/September%202010/3%20Articles/6%2009-229%20Ratliff/Dodgson_CMJ_Final.pdf - */ -class DodgsonTest extends Specification { - - val expectedDodgsonWinnerList = List(Candidate("B")) - - "Dodgson Rule Test " should { - - "verify result" in { - dodgsonMethodVerification( - "39-example.e", - "30-candidates.txt" - ) shouldEqual expectedDodgsonWinnerList - } - } - - def dodgsonMethodVerification(electionFile: String, candidateFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidateFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Dodgson.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/HybridPluralityPreferentialBlockVotingTest.scala b/src/test/scala/agora/votecounter/HybridPluralityPreferentialBlockVotingTest.scala deleted file mode 100644 index b9a1abe..0000000 --- a/src/test/scala/agora/votecounter/HybridPluralityPreferentialBlockVotingTest.scala +++ /dev/null @@ -1,51 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class HybridPluralityPreferentialBlockVotingTest extends Specification { - - val expectedHybridPluralityPreferentialBlockVotingWinnerList = List(Candidate("Sue")) - val expectedHybridPluralityPreferentialBlockVotingWinnerList1 = - List(Candidate("Bill"), Candidate("Bob")) - - "HybridPluralityPreferentialBlockVoting Test " should { - "verify result" in { - HybridPluralityPreferentialBlockVotingVerification( - "32-example.e", - "32-candidates.txt" - ) shouldEqual expectedHybridPluralityPreferentialBlockVotingWinnerList - } - "verify result" in { - HybridPluralityPreferentialBlockVotingVerification1( - "32-example.e", - "32-candidates.txt" - ) shouldEqual expectedHybridPluralityPreferentialBlockVotingWinnerList1 - } - } - - def HybridPluralityPreferentialBlockVotingVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - HybridPluralityPreferentialBlockVoting.winners(election, candidates, 1).map(_._1) - } - - def HybridPluralityPreferentialBlockVotingVerification1( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - HybridPluralityPreferentialBlockVoting.winners(election, candidates, 2).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/InstantExhaustiveBallotTest.scala b/src/test/scala/agora/votecounter/InstantExhaustiveBallotTest.scala deleted file mode 100644 index 703daa1..0000000 --- a/src/test/scala/agora/votecounter/InstantExhaustiveBallotTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class InstantExhaustiveBallotTest extends Specification { - - val expectedInstantExhaustiveBallotWinnerList = List(Candidate("Knoxville")) - - "InstantExhaustiveBallot Test " should { - - "verify result" in { - instantExhaustiveBallotVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedInstantExhaustiveBallotWinnerList - } - } - - def instantExhaustiveBallotVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - InstantExhaustiveBallot.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/InstantExhaustiveDropOffRuleTest.scala b/src/test/scala/agora/votecounter/InstantExhaustiveDropOffRuleTest.scala deleted file mode 100644 index cae4d2b..0000000 --- a/src/test/scala/agora/votecounter/InstantExhaustiveDropOffRuleTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class InstantExhaustiveDropOffRuleTest extends Specification { - - val expectedInstantExhaustiveDropOffRuleWinnerList = List(Candidate("Knoxville")) - - "InstantExhaustiveDropOffRule Test " should { - - "verify result" in { - instantExhaustiveDropOffRuleVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedInstantExhaustiveDropOffRuleWinnerList - } - } - - def instantExhaustiveDropOffRuleVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - InstantExhaustiveDropOffRule.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/InstantRunoff2RoundTest.scala b/src/test/scala/agora/votecounter/InstantRunoff2RoundTest.scala deleted file mode 100644 index 3809367..0000000 --- a/src/test/scala/agora/votecounter/InstantRunoff2RoundTest.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 03/06/17. */ -class InstantRunoff2RoundTest extends Specification { - - val expectedRunoff2RoundMethodWinnerList = List(Candidate("icecream")) - - "Runoff2Round Test " should { - - "verify result" in { - runoff2RoundMethodVerification( - "17-example.e", - "17-candidates.txt" - ) shouldEqual expectedRunoff2RoundMethodWinnerList - } - } - - def runoff2RoundMethodVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - InstantRunoff2Round.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/KemenyYoungTest.scala b/src/test/scala/agora/votecounter/KemenyYoungTest.scala deleted file mode 100644 index 88bef74..0000000 --- a/src/test/scala/agora/votecounter/KemenyYoungTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** This test is to verify the election results with the wikipedia example - * https://en.wikipedia.org/wiki/Kemeny–Young_method - */ -class KemenyYoungTest extends Specification { - - val expectedKemenyYoungWinnerList = List( - Candidate("Nashville"), - Candidate("Chattanooga"), - Candidate("Knoxville"), - Candidate("Memphis") - ) - - "KemenyYoung Test " should { - - "verify result" in { - kemenyYoungMethodVerification("14-example.e") shouldEqual expectedKemenyYoungWinnerList - } - } - - def kemenyYoungMethodVerification(electionFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/14-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - KemenyYoung.winners(election, candidates, candidates.length).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/MajorityRuleTest.scala b/src/test/scala/agora/votecounter/MajorityRuleTest.scala deleted file mode 100644 index e95b457..0000000 --- a/src/test/scala/agora/votecounter/MajorityRuleTest.scala +++ /dev/null @@ -1,42 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by lebedka on 12/5/17. */ -class MajorityRuleTest extends Specification { - - val expectedMajorityRuleWinnerList1 = List() - val expectedMajorityRuleWinnerList2 = List(Candidate("A")) - - "MajorityRule Test " should { - - "verify result" in { - majorityRuleMethodVerification( - "01-example.e", - "01-candidates.txt" - ) shouldEqual expectedMajorityRuleWinnerList1 - } - "verify result" in { - majorityRuleMethodVerification( - "02-example.e", - "02-candidates.txt" - ) shouldEqual expectedMajorityRuleWinnerList2 - } - - } - - def majorityRuleMethodVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Majority.winners(election, candidates, candidates.length).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/MaximinMethodTest.scala b/src/test/scala/agora/votecounter/MaximinMethodTest.scala deleted file mode 100644 index 89a4d5f..0000000 --- a/src/test/scala/agora/votecounter/MaximinMethodTest.scala +++ /dev/null @@ -1,30 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class MaximinMethodTest extends Specification { - - val expectedMaximinMethodWinnerList = List(Candidate("Nashville")) - - "Maximin Method Test " should { - - "verify result" in { - maximinMethodVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedMaximinMethodWinnerList - } - } - - def maximinMethodVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/14-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Maximin.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/MeekSTVTest.scala b/src/test/scala/agora/votecounter/MeekSTVTest.scala deleted file mode 100644 index 2b45a9d..0000000 --- a/src/test/scala/agora/votecounter/MeekSTVTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import spire.math.Rational - -class MeekSTVTest extends Specification { - - val expectedMeekSTVWinnerList = List( - Candidate("C"), - Candidate("A"), - Candidate("B") - ) // result is from OpaVote's page - http://blog.opavote.com/2017/04/meek-stv-explained.html - - "MeekSTV Test " should { - - "verify result" in { - meekSTVVerification("43-example.e", "43-candidates.txt") shouldEqual expectedMeekSTVWinnerList - } - } - - def meekSTVVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - MeekSTV.winners(election, candidates, 3).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/MinimaxCondorcetTest.scala b/src/test/scala/agora/votecounter/MinimaxCondorcetTest.scala deleted file mode 100644 index 79af65f..0000000 --- a/src/test/scala/agora/votecounter/MinimaxCondorcetTest.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 21/03/17. */ -class MinimaxCondorcetTest extends Specification { - - val expectedKemenyYoungWinnerList = List(Candidate("Nashville")) - - "MinimaxCondorcet Test " should { - - "verify result" in { - minimaxCondorcetMethodVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedKemenyYoungWinnerList - } - } - - def minimaxCondorcetMethodVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/14-candidates.txt") - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - MinimaxCondorcet.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/OklahomaMethodTest.scala b/src/test/scala/agora/votecounter/OklahomaMethodTest.scala deleted file mode 100644 index 58d3cb3..0000000 --- a/src/test/scala/agora/votecounter/OklahomaMethodTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import spire.math.Rational - -class OklahomaMethodTest extends Specification { - - val expectedOklahomaMethodWinnerList = List((Candidate("Nashville"), Rational(173, 3))) - - "OklahomaMethod Test " should { - - "verify result" in { - OklahomaMethodVerification( - "14-example.e", - "14-candidates.txt" - ) shouldEqual expectedOklahomaMethodWinnerList - } - } - - def OklahomaMethodVerification( - electionFile: String, - candidatesFile: String - ): List[(Candidate, Rational)] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Oklahoma.winners(election, candidates, 1) - } - -} diff --git a/src/test/scala/agora/votecounter/PreferentialBlockVotingTest.scala b/src/test/scala/agora/votecounter/PreferentialBlockVotingTest.scala deleted file mode 100644 index a5c45fb..0000000 --- a/src/test/scala/agora/votecounter/PreferentialBlockVotingTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class PreferentialBlockVotingTest extends Specification { - - val expectedPreferentialBlockVotingWinnerList = List(Candidate("Sue")) - - "PreferentialBlockVoting Test " should { - - "verify result" in { - PreferentialBlockVotingVerification( - "32-example.e", - "32-candidates.txt" - ) shouldEqual expectedPreferentialBlockVotingWinnerList - } - } - - def PreferentialBlockVotingVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - PreferentialBlockVoting.winners(election, candidates, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/ProportionalApprovalVotingTest.scala b/src/test/scala/agora/votecounter/ProportionalApprovalVotingTest.scala deleted file mode 100644 index 36a14f1..0000000 --- a/src/test/scala/agora/votecounter/ProportionalApprovalVotingTest.scala +++ /dev/null @@ -1,36 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import spire.math.Rational - -class ProportionalApprovalVotingTest extends Specification { - - val expectedProportionalApprovalWinnerList = - List((Candidate("A"), Rational(61, 2)), (Candidate("C"), Rational(61, 2))) - - "ProportionalApprovalVoting Test " should { - - "verify result" in { - proportionalApprovalVotingVerification( - "35-example.e", - "35-candidates.txt" - ) shouldEqual expectedProportionalApprovalWinnerList - } - } - - def proportionalApprovalVotingVerification( - electionFile: String, - candidatesFile: String - ): List[(Candidate, Rational)] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - ProportionalApprovalVoting.winners(election, candidates, 2) - } - -} diff --git a/src/test/scala/agora/votecounter/RandomBallotTest.scala b/src/test/scala/agora/votecounter/RandomBallotTest.scala deleted file mode 100644 index 6f6c1d9..0000000 --- a/src/test/scala/agora/votecounter/RandomBallotTest.scala +++ /dev/null @@ -1,84 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -class RandomBallotTest extends Specification { - - val expectedRandomBallotWinnerList1 = List(Candidate("E"), Candidate("C")) - val expectedRandomBallotWinnerList2 = List(Candidate("E")) - val expectedRandomBallotWinnerList3 = List(Candidate("D"), Candidate("E")) - val expectedRandomBallotWinnerList4 = List(Candidate("A"), Candidate("B"), Candidate("C")) - val expectedRandomBallotWinnerList5 = List(Candidate("D"), Candidate("E")) - val expectedRandomBallotWinnerList6 = - List(Candidate("E"), Candidate("C"), Candidate("B"), Candidate("D")) - - "RandomBallot Test " should { - - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(6142), - 2 - ) shouldEqual expectedRandomBallotWinnerList1 - } - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(2416), - 1 - ) shouldEqual expectedRandomBallotWinnerList2 - } - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(1426), - 2 - ) shouldEqual expectedRandomBallotWinnerList3 - } - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(4216), - 3 - ) shouldEqual expectedRandomBallotWinnerList4 - } - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(1264), - 2 - ) shouldEqual expectedRandomBallotWinnerList5 - } - "verify result" in { - randomBallotMethodVerification( - "21-example.e", - "21-candidates.txt", - Option(2614), - 4 - ) shouldEqual expectedRandomBallotWinnerList6 - } - - } - - def randomBallotMethodVerification( - electionFile: String, - candidatesFile: String, - seed: Option[Int], - vacancies: Int - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - RandomBallot.randomBallotWinner(election, candidates, vacancies, seed).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/RangeVotingTest.scala b/src/test/scala/agora/votecounter/RangeVotingTest.scala deleted file mode 100644 index d78959d..0000000 --- a/src/test/scala/agora/votecounter/RangeVotingTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParserWithScore -import agora.model.Candidate -import org.specs2.mutable.Specification - -class RangeVotingTest extends Specification { - - val expectedRangeVotingWinnerList = List(Candidate("Nashville")) - - "RangeVoting Test " should { - - "verify result" in { - rangeVotingMethod( - "42-example.es", - "42-candidates.txt" - ) shouldEqual expectedRangeVotingWinnerList - } - } - - def rangeVotingMethod(electionFile: String, candidatesFile: String): List[Candidate] = { - - val dir = "../Agora/files/Examples/" - val candidates = CandidatesParser.read(dir + candidatesFile) - val election = PreferencesParserWithScore.read(dir + electionFile) - - RangeVoting.winners(election, candidates, 1).map { - _._1 - } - } - -} diff --git a/src/test/scala/agora/votecounter/RunAllMethods.scala b/src/test/scala/agora/votecounter/RunAllMethods.scala deleted file mode 100644 index bcb2491..0000000 --- a/src/test/scala/agora/votecounter/RunAllMethods.scala +++ /dev/null @@ -1,75 +0,0 @@ -package agora.votecounter - -import ammonite.ops._ -import org.specs2.mutable.Specification - -import agora.Main; -import scala.collection.mutable - -/** This class executes all vote counting methods. Its purpose is merely to check that Agora - * compiles and does not throw any obvious run-time exceptions. - */ -class RunAllMethods extends Specification { - - def test(method: String, electionFile: String, candidatesFile: String): Boolean = { - Main.main( - Seq( - "-d", "files/Examples/", "-b", electionFile, "-c", candidatesFile, "-v", "2", "-m", method - ).toArray - ) - return true - } - - def str(i: Int) = if (i < 10) "0" + i else i.toString - - def run(method: String): Boolean = { - (1 to 13) - .map(i => str(i)) - .map { i: String => - test(method, s"$i-example.txt", s"$i-candidates.txt") - } - .reduce((b1, b2) => b1 && b2) - } - - "Agora" should { - "execute EVACS" in { test("EVACS", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute EVACSnoLP" in { - test("EVACSnoLP", "02-example.e", "02-candidates.txt") shouldEqual true - } - "execute EVACSDWD" in { test("EVACSDWD", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute Senate" in { test("Senate", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute Simple" in { test("Simple", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute Egalitarian" in { - test("Egalitarian", "02-example.e", "02-candidates.txt") shouldEqual true - } - "execute Majority" in { test("Majority", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute Approval" in { test("Approval", "02-example.e", "02-candidates.txt") shouldEqual true } - "execute Borda" in { test("Borda", "13-example.e", "13-candidates.txt") shouldEqual true } - "execute Baldwin" in { test("Baldwin", "13-example.e", "13-candidates.txt") shouldEqual true } - "execute Nanson" in { test("Nanson", "13-example.e", "13-candidates.txt") shouldEqual true } - "execute Kemeny-Young" in { - test("Kemeny-Young", "14-example.e", "14-candidates.txt") shouldEqual true - } - "execute Coomb" in { test("Coomb", "14-example.e", "14-candidates.txt") shouldEqual true } - "execute Contingent" in { - test("Contingent", "14-example.e", "14-candidates.txt") shouldEqual true - } - "execute InstantExhaustiveBallot" in { - test("InstantExhaustiveBallot", "14-example.e", "14-candidates.txt") shouldEqual true - } - "execute RandomBallot" in { - test("RandomBallot", "21-example.e", "21-candidates.txt") shouldEqual true - } - "execute PreferentialBlockVoting" in { - test("PreferentialBlockVoting", "32-example.e", "32-candidates.txt") shouldEqual true - } - "execute HybridPluralityPreferentialBlockVoting" in { - test( - "HybridPluralityPreferentialBlockVoting", - "32-example.e", - "32-candidates.txt" - ) shouldEqual true - } - } - -} diff --git a/src/test/scala/agora/votecounter/SMCMethodTest.scala b/src/test/scala/agora/votecounter/SMCMethodTest.scala deleted file mode 100644 index ae7a1cf..0000000 --- a/src/test/scala/agora/votecounter/SMCMethodTest.scala +++ /dev/null @@ -1,46 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 06/08/17. */ -class SMCMethodTest extends Specification { - - val smcWinnerList = List(Candidate("C")) - - "SMC Test " should { - - "verify result" in { - smcMethodVerification( - "37-example.e", - "37-candidates.txt", - "method-param1.json" - ) shouldEqual smcWinnerList - } - "verify result" in { - smcMethodVerification( - "37-example.e", - "37-candidates.txt", - "method-param.json" - ) shouldEqual smcWinnerList - } - } - - def smcMethodVerification( - electionFile: String, - candidateFile: String, - paramFile: String - ): List[Candidate] = { - - val dir = "../Agora/files/Examples/" - val candidates = CandidatesParser.read(dir + candidateFile) - val election = PreferencesParser.read(dir + electionFile) - val param = ParameterParser.parse(dir + paramFile) - - SMC.smcWinner(election, candidates, param, 1).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/SatisfactionApprovalVotingTest.scala b/src/test/scala/agora/votecounter/SatisfactionApprovalVotingTest.scala deleted file mode 100644 index 6da1778..0000000 --- a/src/test/scala/agora/votecounter/SatisfactionApprovalVotingTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package agora.votecounter; - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import spire.math.Rational - -class SatisfactionApprovalVotingTest extends Specification { - - val expectedSatisfactionApprovalWinnerList = List(Candidate("D"), Candidate("C")) - - "SatisfactionApprovalVoting Test " should { - - "verify result" in { - SatisfactionApprovalVotingVerification( - "34-example.e", - "34-candidates.txt" - ) shouldEqual expectedSatisfactionApprovalWinnerList - } - } - - def SatisfactionApprovalVotingVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - SatisfactionApprovalVoting.winners(election, candidates, 2).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/SchulzeTest.scala b/src/test/scala/agora/votecounter/SchulzeTest.scala deleted file mode 100644 index 09a8416..0000000 --- a/src/test/scala/agora/votecounter/SchulzeTest.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParserWithRank -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** this class tests the example on https://en.wikipedia.org/wiki/Schulze_method on two preference - * profiles 15-example.txt and 16-example.txt given in different format - */ -class SchulzeTest extends Specification { - - val expectedSchulzeWinnerList = - List(Candidate("E"), Candidate("A"), Candidate("C"), Candidate("B"), Candidate("D")) - - "Schulze Test" should { - "verify result" in { - schulzeMethodVerification( - "44-example.er", - "44-candidates.txt" - ) shouldEqual expectedSchulzeWinnerList - } - } - - def schulzeMethodVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val dir = "../Agora/files/Examples/" - val candidates = CandidatesParser.read(dir + candidatesFile) - val election = PreferencesParserWithRank.read(dir + electionFile) - - Schulze.winners(election, candidates, candidates.length).map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/SequentialProportionalApprovalVotingTest.scala b/src/test/scala/agora/votecounter/SequentialProportionalApprovalVotingTest.scala deleted file mode 100644 index ae2fcbb..0000000 --- a/src/test/scala/agora/votecounter/SequentialProportionalApprovalVotingTest.scala +++ /dev/null @@ -1,39 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -import spire.math.Rational - -class SequentialProportionalApprovalVotingTest extends Specification { - - val expectedSequentialProportionalApprovalWinnerList = List( - (Candidate("D"), Rational(3, 1)), - (Candidate("B"), Rational(4, 1)), - (Candidate("A"), Rational(8, 1)) - ) - - "SequentialProportionalApprovalVoting Test " should { - - "verify result" in { - SequentialProportionalApprovalVotingVerification( - "36-example.e", - "36-candidates.txt" - ) shouldEqual expectedSequentialProportionalApprovalWinnerList - } - } - - def SequentialProportionalApprovalVotingVerification( - electionFile: String, - candidatesFile: String - ): List[(Candidate, Rational)] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - SequentialProportionalApprovalVoting.winners(election, candidates, 3) - } - -} diff --git a/src/test/scala/agora/votecounter/SimpleSTVRuleTest.scala b/src/test/scala/agora/votecounter/SimpleSTVRuleTest.scala deleted file mode 100644 index 30509ce..0000000 --- a/src/test/scala/agora/votecounter/SimpleSTVRuleTest.scala +++ /dev/null @@ -1,36 +0,0 @@ -package votecounter - -import agora.model.Candidate -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.votecounter.SimpleSTV -import org.specs2.mutable.Specification - -class SimpleSTVRuleTest extends Specification { - - // Winners in list in order of merit as expected from election results at https://en.wikipedia.org/wiki/Single_transferable_vote - val expectedSimpleRuleWinnerList = - List(Candidate("Chocolate"), Candidate("Oranges"), Candidate("Strawberries")) - - "SimpleSTVRule Test " should { - // Election data taken from https://en.wikipedia.org/wiki/Single_transferable_vote - "verify result" in { - simpleRuleMethodVerification( - "47-example.e", - "47-candidates.txt" - ) shouldEqual expectedSimpleRuleWinnerList.reverse - } - } - - def simpleRuleMethodVerification( - electionFile: String, - candidatesFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - (new SimpleSTV).runVoteCounter(election, candidates, 3).getWinners.map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/SmithSetTest.scala b/src/test/scala/agora/votecounter/SmithSetTest.scala deleted file mode 100644 index 1de9fa2..0000000 --- a/src/test/scala/agora/votecounter/SmithSetTest.scala +++ /dev/null @@ -1,28 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Created by deepeshpandey on 16/07/17. */ -class SmithSetTest extends Specification { - - val expectedSmithSet = Set(Candidate("A"), Candidate("B"), Candidate("C")) - - "SmithSet Test " should { - - "verify result" in { - topCycleSetVerification("30-example.e", "30-candidates.txt") shouldEqual expectedSmithSet - } - } - - def topCycleSetVerification(electionFile: String, candidatesFile: String): Set[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - SmithSet.winners(election, candidates, 1).map(_._1).toSet - } - -} diff --git a/src/test/scala/agora/votecounter/SuperMajorityTest.scala b/src/test/scala/agora/votecounter/SuperMajorityTest.scala deleted file mode 100644 index d4d6465..0000000 --- a/src/test/scala/agora/votecounter/SuperMajorityTest.scala +++ /dev/null @@ -1,52 +0,0 @@ -package votecounter - -import agora.model.Candidate -import agora.model.Parameters -import agora.parser.CandidatesParser -import agora.parser.ParameterParser -import agora.parser.PreferencesParser -import agora.votecounter.SuperMajority -import org.specs2.mutable.Specification -import spire.math.Rational - -class SuperMajorityTest extends Specification { - - // From the file provided it can be seen that candidate A wins with 60% of the votes - val expectedSuperMajorityRuleWinnerList1 = List(Candidate("A")) - val expectedSuperMajorityRuleWinnerList2 = List() - - "SuperMajorityRule Test " should { - - "verify result" in { - superMajorityRuleMethodVerification( - "02-example.e", - "02-candidates.txt", - "supermajority-param.json" - ) shouldEqual expectedSuperMajorityRuleWinnerList1 - } - - "verify result" in { - superMajorityRuleMethodVerification( - "02-example.e", - "02-candidates.txt", - "supermajority-param2.json" - ) shouldEqual expectedSuperMajorityRuleWinnerList2 - } - } - - def superMajorityRuleMethodVerification( - electionFile: String, - candidatesFile: String, - parametersFile: String - ): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - val parameters = ParameterParser.parse("../Agora/files/Examples/" + parametersFile) - SuperMajority - .runVoteCounter(election, candidates, candidates.length, parameters) - .getWinners - .map(_._1) - } - -} diff --git a/src/test/scala/agora/votecounter/UncoveredSetTest.scala b/src/test/scala/agora/votecounter/UncoveredSetTest.scala deleted file mode 100644 index f6aa4f5..0000000 --- a/src/test/scala/agora/votecounter/UncoveredSetTest.scala +++ /dev/null @@ -1,34 +0,0 @@ -package agora.votecounter - -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.model.Candidate -import org.specs2.mutable.Specification - -/** Test class for unconvered set for the preference profile in 21-example.e */ -class UncoveredSetTest extends Specification { - - val expectedUncoveredSet = Set(Candidate("A"), Candidate("B"), Candidate("C")) - - "UnconveredSet Test " should { - - "verify result" in { - unconveredSetMethodVerification( - "30-example.e", - "29-candidates.txt" - ) shouldEqual expectedUncoveredSet - } - } - - def unconveredSetMethodVerification( - electionFile: String, - candidatesFile: String - ): Set[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - UncoveredSet.winners(election, candidates, candidates.length).map(_._1).toSet - } - -} diff --git a/src/test/scala/agora/votecounter/VetoTest.scala b/src/test/scala/agora/votecounter/VetoTest.scala deleted file mode 100644 index 7e1b436..0000000 --- a/src/test/scala/agora/votecounter/VetoTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package votecounter - -import agora.model.Candidate -import agora.parser.CandidatesParser -import agora.parser.PreferencesParser -import agora.votecounter.Veto -import org.specs2.mutable.Specification - -class VetoTest extends Specification { - - // Test data as provided by http://democratix.dbai.tuwien.ac.at/examples/veto.php - val expectedVetoRuleWinnerList = List(Candidate("Banana"), Candidate("Cherry")) - - "VetoRule Test " should { - - "verify result" in { - vetoRuleMethodVerification( - "49-example.e", - "49-candidates.txt" - ) shouldEqual expectedVetoRuleWinnerList - } - - } - - def vetoRuleMethodVerification(electionFile: String, candidatesFile: String): List[Candidate] = { - - val candidates = CandidatesParser.read("../Agora/files/Examples/" + candidatesFile) - val election = PreferencesParser.read("../Agora/files/Examples/" + electionFile) - - Veto.winners(election, candidates, 2).map(_._1) - } - -} diff --git a/src/test/scala/performance/AgoraBenchmark.scala b/src/test/scala/performance/AgoraBenchmark.scala deleted file mode 100644 index d53d3eb..0000000 --- a/src/test/scala/performance/AgoraBenchmark.scala +++ /dev/null @@ -1,90 +0,0 @@ -package performance - -import agora.votecounter.Borda -import agora.model.Candidate -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.Bench -import org.scalameter.api._ -import org.scalameter.persistence.GZIPJSONSerializationPersistor - -import scala.collection.immutable.ListSet -import scala.util.Random - -//TODO: Get rid of code duplication in the performance regression tests - -class AgoraBenchmark extends Bench.OfflineRegressionReport { - - val preferenceSet: ListSet[Candidate] = - ListSet(Candidate("A"), Candidate("B"), Candidate("C"), Candidate("D"), Candidate("E")) - val electionSizes: Gen[Int] = Gen.range("electionSize")(10000, 20000, 5000) - - val election = for { - size <- electionSizes - } yield { - Election(for { - i <- List.range(1, size) - } yield Ballot(randomPreference(), i, 1)) - } - - def randomPreference(): List[Candidate] = - // generate random permutations uniformly for 5 candidates - Random.shuffle(preferenceSet).toList - - def votingMethodName(): String = - "" - - def votingMethod(election: Election[Ballot]) = {} - -} - -trait RuntimeRegression extends AgoraBenchmark { - - override def persistor: Persistor = new GZIPJSONSerializationPersistor( - "target/benchmarks/borda/time" - ) - - performance.of("VotingMethod") in { - measure - .method(votingMethodName()) - .config( - exec.benchRuns -> 15 - ) in { - using(election) in { preferences => - votingMethod(preferences) - } - } - } - - def votingMethodName(): String - - def votingMethod(election: Election[Ballot]): Unit - -} - -trait MemoryRegression extends AgoraBenchmark { - - override def measurer = new Measurer.MemoryFootprint - - override def persistor: Persistor = new GZIPJSONSerializationPersistor( - "target/benchmarks/borda/memory" - ) - - performance.of("MemoryFootprint") in { - performance.of(votingMethodName()) in { - using(election).config( - exec.minWarmupRuns -> 2, - exec.maxWarmupRuns -> 5, - exec.benchRuns -> 5, - exec.independentSamples -> 1 - ) in { preferences => - votingMethod(preferences) - } - } - } - - def votingMethodName(): String - - def votingMethod(election: Election[Ballot]): Unit - -} diff --git a/src/test/scala/performance/BaldwinRegression.scala b/src/test/scala/performance/BaldwinRegression.scala deleted file mode 100644 index a7019f2..0000000 --- a/src/test/scala/performance/BaldwinRegression.scala +++ /dev/null @@ -1,46 +0,0 @@ -package performance - -import agora.votecounter.BaldwinMethod -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api._ - -/** Created by deepeshpandey on 13/05/17. */ -trait BaldwinRegression extends RuntimeRegression { - - override def votingMethodName(): String = "Baldwin" - - override def votingMethod(election: Election[Ballot]): Unit = - BaldwinMethod.winners(election, randomPreference(), 1) - -} - -trait BaldwinMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "Baldwin" - - override def votingMethod(election: Election[Ballot]): Unit = - BaldwinMethod.winners(election, randomPreference(), 1) - -} - -object BaldwinRegressionTest extends Bench.Group { - - // perform regression for Baldwin method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/baldwin/memory" - ) in { - include(new BaldwinMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/baldwin/time" - ) in { - include(new BaldwinRegression {}) - } - -} diff --git a/src/test/scala/performance/BordaRegression.scala b/src/test/scala/performance/BordaRegression.scala deleted file mode 100644 index 0dec716..0000000 --- a/src/test/scala/performance/BordaRegression.scala +++ /dev/null @@ -1,48 +0,0 @@ -package performance - -import agora.votecounter.Borda -import agora.votecounter.BaldwinMethod -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api._ -import org.scalameter.persistence.GZIPJSONSerializationPersistor - -trait BordaRegression extends RuntimeRegression { - - override def votingMethodName(): String = "Borda" - - override def votingMethod(election: Election[Ballot]): Unit = - Borda.winners(election, randomPreference(), 1) - -} - -trait BordaMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "Borda" - - override def votingMethod(election: Election[Ballot]): Unit = - Borda.winners(election, randomPreference(), 1) - -} - -// existing issue : not overriding reports -object BordaRegressionTest extends Bench.Group { - - // perform regression for borda method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/borda/memory" - ) in { - include(new BordaMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/borda/time" - ) in { - include(new BordaRegression {}) - } - -} diff --git a/src/test/scala/performance/CoombRegression.scala b/src/test/scala/performance/CoombRegression.scala deleted file mode 100644 index 5ea4890..0000000 --- a/src/test/scala/performance/CoombRegression.scala +++ /dev/null @@ -1,45 +0,0 @@ -package performance - -import agora.votecounter.Coomb -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api._ - -trait CoombRegression extends RuntimeRegression { - - override def votingMethodName(): String = "Coomb" - - override def votingMethod(election: Election[Ballot]): Unit = - Coomb.winners(election, randomPreference(), 1) - -} - -trait CoombMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "Coomb" - - override def votingMethod(election: Election[Ballot]): Unit = - Coomb.winners(election, randomPreference(), 1) - -} - -object CoombRegressionTest extends Bench.Group { - - // perform regression for Coomb method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/coomb/memory" - ) in { - include(new CoombMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/coomb/time" - ) in { - include(new CoombRegression {}) - } - -} diff --git a/src/test/scala/performance/CopelandRegression.scala b/src/test/scala/performance/CopelandRegression.scala deleted file mode 100644 index beccc6e..0000000 --- a/src/test/scala/performance/CopelandRegression.scala +++ /dev/null @@ -1,51 +0,0 @@ -import agora.votecounter.Copeland -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api.Bench -import org.scalameter.api._ -import performance.BordaRegressionTest.include -import performance.BordaMemoryRegression -import performance.BordaRegression -import performance.MemoryRegression -import performance.RuntimeRegression - -/** Created by deepeshpandey on 27/06/17. */ -trait CopelandRegression extends RuntimeRegression { - - override def votingMethodName(): String = "Copeland" - - override def votingMethod(election: Election[Ballot]): Unit = - Copeland.winners(election, randomPreference(), 1) - -} - -trait CopelandMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "Copeland" - - override def votingMethod(election: Election[Ballot]): Unit = - Copeland.winners(election, randomPreference(), 1) - -} - -// existing issue : not overriding reports -object CopelandRegressionTest extends Bench.Group { - - // perform regression for copeland method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/copeland/memory" - ) in { - include(new CopelandMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/copeland/time" - ) in { - include(new CopelandRegression {}) - } - -} diff --git a/src/test/scala/performance/FullRegression.scala b/src/test/scala/performance/FullRegression.scala deleted file mode 100644 index c02e15a..0000000 --- a/src/test/scala/performance/FullRegression.scala +++ /dev/null @@ -1,44 +0,0 @@ -package performance - -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.Bench -import org.scalameter.api._ - -/** Created by deepeshpandey on 13/05/17. */ -object FullRegression extends Bench.Group { - - // perform regression for Baldwin method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/baldwin/memory" - ) in { - include(new BaldwinMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/baldwin/time" - ) in { - include(new BaldwinRegression {}) - } - - // perform regression for borda method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/borda/memory" - ) in { - include(new BordaMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/borda/time" - ) in { - include(new BordaRegression {}) - } - -} diff --git a/src/test/scala/performance/MinimaxCondorcetRegression.scala b/src/test/scala/performance/MinimaxCondorcetRegression.scala deleted file mode 100644 index 490a2e3..0000000 --- a/src/test/scala/performance/MinimaxCondorcetRegression.scala +++ /dev/null @@ -1,44 +0,0 @@ -package performance - -import agora.votecounter.MinimaxCondorcet -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api._ - -/** Created by deepeshpandey on 14/07/17. */ -trait MinimaxCondorcetRegression extends RuntimeRegression { - - override def votingMethodName(): String = "MinimaxCondorcet" - - override def votingMethod(election: Election[Ballot]): Unit = - MinimaxCondorcet.winners(election, randomPreference(), 1) - -} - -trait MinimaxCondorcetMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "MinimaxCondorcet" - - override def votingMethod(election: Election[Ballot]): Unit = - MinimaxCondorcet.winners(election, randomPreference(), 1) - -} - -object MinimaxCondorcetTest extends Bench.Group { - - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/minimaxCondorcet/memory" - ) in { - include(new MinimaxCondorcetMemoryRegression {}) - } - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/minimaxCondorcet/time" - ) in { - include(new MinimaxCondorcetRegression {}) - } - -} diff --git a/src/test/scala/performance/RandomBallotRegression.scala b/src/test/scala/performance/RandomBallotRegression.scala deleted file mode 100644 index fe9ef9f..0000000 --- a/src/test/scala/performance/RandomBallotRegression.scala +++ /dev/null @@ -1,46 +0,0 @@ -package performance - -import agora.votecounter.RandomBallot -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api._ - -trait RandomBallotRegression extends RuntimeRegression { - - override def votingMethodName(): String = "RandomBallot" - - override def votingMethod(election: Election[Ballot]): Unit = - RandomBallot.winners(election, randomPreference(), 1) - -} - -trait RandomBallotMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "RandomBallot" - - override def votingMethod(election: Election[Ballot]): Unit = - RandomBallot.winners(election, randomPreference(), 1) - -} - -// existing issue : not overriding reports -object RandomBallotRegressionTest extends Bench.Group { - - // perform regression for Random Ballot method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/randomballot/memory" - ) in { - include(new RandomBallotMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/randomballot/time" - ) in { - include(new RandomBallotRegression {}) - } - -} diff --git a/src/test/scala/performance/UncoveredSetRegression.scala b/src/test/scala/performance/UncoveredSetRegression.scala deleted file mode 100644 index c1429de..0000000 --- a/src/test/scala/performance/UncoveredSetRegression.scala +++ /dev/null @@ -1,47 +0,0 @@ -import agora.votecounter.UncoveredSet -import agora.model.Election -import agora.model.{PreferenceBallot => Ballot} -import org.scalameter.api.Bench -import org.scalameter.api._ -import performance.MemoryRegression -import performance.RuntimeRegression - -/** Regression test for computing uncovered set */ -trait UncoveredSetRegression extends RuntimeRegression { - - override def votingMethodName(): String = "UncoveredSet" - - override def votingMethod(election: Election[Ballot]): Unit = - UncoveredSet.winners(election, randomPreference(), 1) - -} - -trait UncoveredSetMemoryRegression extends MemoryRegression { - - override def votingMethodName(): String = "UncoveredSet" - - override def votingMethod(election: Election[Ballot]): Unit = - UncoveredSet.winners(election, randomPreference(), 1) - -} - -object UncoveredSetRegressionTest extends Bench.Group { - - // perform regression for Uncovered method - performance - .of("memory") - .config( - reports.resultDir -> "target/benchmarks/uncoveredset/memory" - ) in { - include(new UncoveredSetMemoryRegression {}) - } - - performance - .of("running time") - .config( - reports.resultDir -> "target/benchmarks/uncoveredset/time" - ) in { - include(new UncoveredSetRegression {}) - } - -} From 4f9d36ad6a0cd11ca0f390b7bca5e196d0f23f5e Mon Sep 17 00:00:00 2001 From: icemc Date: Thu, 15 Aug 2024 00:57:16 +0100 Subject: [PATCH 3/4] Removed support for scala 2.12.x Added support for scala 3.4.x Cross compilation supported for scala 2.13.14 and 3.4.2 --- .bsp/sbt.json | 2 +- .scalafix.conf | 1 - build.sbt | 43 +++++++--------- .../agora/votecounter/BucklinTest.scala | 1 - .../aossie/agora/votecounter/CoombTest.scala | 2 - .../agora/votecounter/MeekSTVTest.scala | 2 - .../agora/votecounter/RunAllMethods.scala | 4 +- .../SatisfactionApprovalVotingTest.scala | 2 - .../agora/votecounter/SimpleSTVRuleTest.scala | 1 - .../agora/votecounter/SuperMajorityTest.scala | 3 -- .../aossie/agora/votecounter/VetoTest.scala | 1 - .../agora/analyzer/SinglePeakAnalyser.scala | 2 +- .../org/aossie/agora/model/Election.scala | 51 +++++++++++-------- .../org/aossie/agora/model/Parameters.scala | 15 +++--- .../scala/org/aossie/agora/model/Report.scala | 19 ++++--- .../aossie/agora/util/matrix/package.scala | 6 +-- .../org/aossie/agora/votecounter/ACT.scala | 4 +- .../agora/votecounter/AustralianSenate.scala | 4 +- .../agora/votecounter/BipartisanSet.scala | 2 +- .../aossie/agora/votecounter/Dodgson.scala | 30 +++++------ .../agora/votecounter/Egalitarian.scala | 6 +-- .../aossie/agora/votecounter/MeekSTV.scala | 2 +- .../org/aossie/agora/votecounter/Nanson.scala | 2 +- .../org/aossie/agora/votecounter/SMC.scala | 2 +- .../SurplusDistributionTieResolution.scala | 8 +-- project/Testing.scala | 4 +- 26 files changed, 105 insertions(+), 114 deletions(-) diff --git a/.bsp/sbt.json b/.bsp/sbt.json index 302fe41..641a3ec 100644 --- a/.bsp/sbt.json +++ b/.bsp/sbt.json @@ -1 +1 @@ -{"name":"sbt","version":"1.7.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["C:\\Program Files\\Java\\jdk-17/bin/java","-Xms100m","-Xmx100m","-classpath","C:\\Program Files (x86)\\sbt\\\\bin\\sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=C:\\Program%20Files%20(x86)\\sbt\\\\bin\\sbt-launch.jar"]} \ No newline at end of file +{"name":"sbt","version":"1.7.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["C:\\Program Files\\Java\\jdk-17/bin/java","-Xms100m","-Xmx100m","-classpath","C:\\\\Program Files (x86)\\\\sbt\\\\\\\\bin\\\\sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=C:\\\\Program%20Files%20(x86)\\\\sbt\\\\\\\\bin\\\\sbt-launch.jar"]} \ No newline at end of file diff --git a/.scalafix.conf b/.scalafix.conf index 04b66f1..e8bde19 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -1,6 +1,5 @@ rules = [ RemoveUnused, DisableSyntax, - ProcedureSyntax, NoValInForComprehension ] \ No newline at end of file diff --git a/build.sbt b/build.sbt index 3bf1854..b9305d3 100644 --- a/build.sbt +++ b/build.sbt @@ -1,26 +1,22 @@ import language.postfixOps -lazy val scala212 = "2.12.19" lazy val scala213 = "2.13.14" -lazy val supportedScalaVersions = List(scala212, scala213) +lazy val scala304 = "3.4.2" +lazy val supportedScalaVersions = List(scala213, scala304) ThisBuild / name := "countvotes" ThisBuild / organization := "AOSSIE" ThisBuild / version := "1.2" -ThisBuild / scalaVersion := scala213 +ThisBuild / scalaVersion := scala304 resolvers += Resolver.sonatypeRepo("public") resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/releases" -testFrameworks += new TestFramework("org.scalameter.ScalaMeterFramework") - logBuffered := false parallelExecution in Test := false -scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature") - scalacOptions in (Compile, doc) ++= Seq("-diagrams","-implicits") scalacOptions in Test ++= Seq("-Yrangepos") @@ -35,8 +31,8 @@ lazy val root = Project("agora", file(".")) ).settings(commonSettings, crossScalaVersions := Nil) lazy val core = (project in file("modules/core")) - .configs(Testing.configs: _*) - .settings(Testing.settings: _*) + .configs(Testing.configs *) + .settings(Testing.settings *) .settings( commonSettings, crossScalaVersions := supportedScalaVersions, @@ -44,8 +40,8 @@ lazy val core = (project in file("modules/core")) ) lazy val cli = (project in file("modules/cli")) - .configs(Testing.configs: _*) - .settings(Testing.settings: _*) + .configs(Testing.configs *) + .settings(Testing.settings *) .dependsOn( core ) @@ -59,7 +55,8 @@ lazy val commonSettings = Seq( scalafmtOnCompile := true, semanticdbEnabled := true, semanticdbVersion := scalafixSemanticdb.revision, - scalacOptions += "-P:semanticdb:synthetics:on", + scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"), +// scalacOptions += "-P:semanticdb:synthetics:on", scalacOptions += { if (scalaVersion.value.startsWith("2.12")) "-Ywarn-unused-import" else "-Wunused:imports" @@ -68,31 +65,27 @@ lazy val commonSettings = Seq( libraryDependencies ++= { CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 12)) => Seq( + case Some((2, 13)) => Seq( "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0", "com.github.scopt" %% "scopt" % "4.1.0", - "org.specs2" %% "specs2-core" % "4.20.8" % "test,verification-test,bench", + "org.specs2" %% "specs2-core" % "4.20.6" % "test,verification-test,bench", "com.lihaoyi" %% "ammonite-ops" % "2.4.1", "ch.qos.logback" % "logback-classic" % "1.2.11", "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", - "com.storm-enroute" %% "scalameter" % "0.19", - "com.storm-enroute" %% "scalameter-core" % "0.19", + "org.scala-lang.modules" %% "scala-parser-combinators" % "2.4.0", "com.typesafe.play" %% "play-json" % "2.9.4", - "org.typelevel" %% "spire" % "0.17.0", - "org.scala-lang.modules" % "scala-collection-compat_2.12" % "2.12.0" + "org.typelevel" %% "spire" % "0.18.0" ) - case Some((2, 13)) => Seq( + case Some((3, 4)) => Seq( "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.0", "com.github.scopt" %% "scopt" % "4.1.0", "org.specs2" %% "specs2-core" % "4.20.6" % "test,verification-test,bench", - "com.lihaoyi" %% "ammonite-ops" % "2.4.1", + ("com.lihaoyi" %% "ammonite-ops" % "2.4.1").cross(CrossVersion.for3Use2_13), "ch.qos.logback" % "logback-classic" % "1.2.11", "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", - "com.storm-enroute" %% "scalameter" % "0.21", - "com.storm-enroute" %% "scalameter-core" % "0.21", - "com.typesafe.play" %% "play-json" % "2.9.4", - "org.typelevel" %% "spire" % "0.18.0", - "org.scala-lang.modules" % "scala-collection-compat_2.13" % "2.12.0" + "org.scala-lang.modules" %% "scala-parser-combinators" % "2.4.0", + "com.typesafe.play" %% "play-json" % "2.10.6", + "org.typelevel" %% "spire" % "0.18.0" ) case _ => Nil } diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/BucklinTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/BucklinTest.scala index 7ff1d69..b30bb5d 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/BucklinTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/BucklinTest.scala @@ -3,7 +3,6 @@ package org.aossie.agora.votecounter import org.aossie.agora.model.Candidate import org.aossie.agora.parser.CandidatesParser import org.aossie.agora.parser.PreferencesParser -import org.aossie.agora.votecounter.Bucklin import org.specs2.mutable.Specification class BucklinTest extends Specification { diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/CoombTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/CoombTest.scala index 1bf8960..48863f7 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/CoombTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/CoombTest.scala @@ -5,8 +5,6 @@ import org.aossie.agora.parser.PreferencesParser import org.aossie.agora.model.Candidate import org.specs2.mutable.Specification -import scala.collection.mutable.ListBuffer - class CoombTest extends Specification { val expectedCoombWinnerList = List(new Candidate("Nashville")) diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/MeekSTVTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/MeekSTVTest.scala index f9be603..af808e3 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/MeekSTVTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/MeekSTVTest.scala @@ -5,8 +5,6 @@ import org.aossie.agora.parser.PreferencesParser import org.aossie.agora.model.Candidate import org.specs2.mutable.Specification -import spire.math.Rational - class MeekSTVTest extends Specification { val expectedMeekSTVWinnerList = List( diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/RunAllMethods.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/RunAllMethods.scala index 8a313f3..e1444b6 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/RunAllMethods.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/RunAllMethods.scala @@ -1,10 +1,8 @@ package org.aossie.agora.votecounter -import ammonite.ops._ import org.specs2.mutable.Specification import org.aossie.agora.Main; -import scala.collection.mutable /** This class executes all vote counting methods. Its purpose is merely to check that Agora * compiles and does not throw any obvious run-time exceptions. @@ -25,7 +23,7 @@ class RunAllMethods extends Specification { def run(method: String): Boolean = { (1 to 13) .map(i => str(i)) - .map { i: String => + .map { i => test(method, s"$i-example.txt", s"$i-candidates.txt") } .reduce((b1, b2) => b1 && b2) diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SatisfactionApprovalVotingTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SatisfactionApprovalVotingTest.scala index 9542e67..43e0d08 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SatisfactionApprovalVotingTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SatisfactionApprovalVotingTest.scala @@ -5,8 +5,6 @@ import org.aossie.agora.parser.PreferencesParser import org.aossie.agora.model.Candidate import org.specs2.mutable.Specification -import spire.math.Rational - class SatisfactionApprovalVotingTest extends Specification { val expectedSatisfactionApprovalWinnerList = List(new Candidate("D"), new Candidate("C")) diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SimpleSTVRuleTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SimpleSTVRuleTest.scala index 42da483..f30b3d6 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SimpleSTVRuleTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SimpleSTVRuleTest.scala @@ -3,7 +3,6 @@ package org.aossie.agora.votecounter import org.aossie.agora.model.Candidate import org.aossie.agora.parser.CandidatesParser import org.aossie.agora.parser.PreferencesParser -import org.aossie.agora.votecounter.SimpleSTV import org.specs2.mutable.Specification class SimpleSTVRuleTest extends Specification { diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SuperMajorityTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SuperMajorityTest.scala index 876330a..6693ea1 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/SuperMajorityTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/SuperMajorityTest.scala @@ -1,13 +1,10 @@ package org.aossie.agora.votecounter import org.aossie.agora.model.Candidate -import org.aossie.agora.model.Parameters import org.aossie.agora.parser.CandidatesParser import org.aossie.agora.parser.ParameterParser import org.aossie.agora.parser.PreferencesParser -import org.aossie.agora.votecounter.SuperMajority import org.specs2.mutable.Specification -import spire.math.Rational class SuperMajorityTest extends Specification { diff --git a/modules/cli/src/test/scala/org/aossie/agora/votecounter/VetoTest.scala b/modules/cli/src/test/scala/org/aossie/agora/votecounter/VetoTest.scala index f562ed8..9d8d607 100644 --- a/modules/cli/src/test/scala/org/aossie/agora/votecounter/VetoTest.scala +++ b/modules/cli/src/test/scala/org/aossie/agora/votecounter/VetoTest.scala @@ -3,7 +3,6 @@ package org.aossie.agora.votecounter import org.aossie.agora.model.Candidate import org.aossie.agora.parser.CandidatesParser import org.aossie.agora.parser.PreferencesParser -import org.aossie.agora.votecounter.Veto import org.specs2.mutable.Specification class VetoTest extends Specification { diff --git a/modules/core/src/main/scala/org/aossie/agora/analyzer/SinglePeakAnalyser.scala b/modules/core/src/main/scala/org/aossie/agora/analyzer/SinglePeakAnalyser.scala index 3950bb2..0f3ad3a 100644 --- a/modules/core/src/main/scala/org/aossie/agora/analyzer/SinglePeakAnalyser.scala +++ b/modules/core/src/main/scala/org/aossie/agora/analyzer/SinglePeakAnalyser.scala @@ -22,7 +22,7 @@ object SinglePeakAnalyser extends PreferenceAnalysisMethod[Candidate, Ballot] { getSinglePeakAxis(election, candidates) match { case Some(axis) => if (isCompatibleAxis(axis, election)) { - println("Single Peaked with respect to ", axis.mkString(" > ")) + println("Single Peaked with respect to " + axis.mkString(" > ")) true } else { println("\n\nNot Single Peaked!\n\n") diff --git a/modules/core/src/main/scala/org/aossie/agora/model/Election.scala b/modules/core/src/main/scala/org/aossie/agora/model/Election.scala index e989a42..2c21c53 100644 --- a/modules/core/src/main/scala/org/aossie/agora/model/Election.scala +++ b/modules/core/src/main/scala/org/aossie/agora/model/Election.scala @@ -1,18 +1,25 @@ package org.aossie.agora.model import scala.collection._ -import scala.collection.generic._ -import scala.collection.mutable.Builder import scala.collection.mutable.{HashMap => MMap} import spire.math.Rational import scala.language.higherKinds +import scala.collection.compat.IterableOnce class Election[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]](val ballots: Seq[B[C]]) extends Seq[B[C]] - with SeqLike[B[C], Election[C, B]] { + with SeqOps[B[C], Seq, Election[C, B]] { - override def companion = ballots.companion + // Mandatory overrides of `fromSpecific`, `newSpecificBuilder`, + // and `empty`, from `IterableOps` + + override protected def fromSpecific(coll: IterableOnce[B[C]]): Election[C, B] = + Election.fromSpecific(coll) + + override protected def newSpecificBuilder: mutable.Builder[B[C], Election[C, B]] = + Election.newBuilder + override def empty: Election[C, B] = Election(Nil) def iterator = ballots.iterator @@ -20,8 +27,6 @@ class Election[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]](val ballot def length = ballots.length - override protected[this] def newBuilder = Election.newBuilder - override def toString = ballots.map(_.toString).mkString("\n") def firstVotes(candidates: List[C]): Map[C, Rational] = { @@ -35,29 +40,31 @@ class Election[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]](val ballot m } - lazy val weight = ((ballots.map(_.weight)) :\ Rational(0, 1))(_ + _) + lazy val weight = ballots.map(_.weight).foldRight(Rational(0, 1))(_ + _) } object Election { + // Mandatory factory methods: `empty`, `newBuilder` + // and `fromSpecific` - def newBuilder[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] = - new mutable.Builder[B[C], Election[C, B]] { - private[this] val base = Seq().genericBuilder[B[C]] - override def +=(e: B[C]) = { base += e; this } - override def clear() = base.clear() - override def result() = new Election[C, B](base.result()) - } - - implicit def canBuildFrom[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] - : CanBuildFrom[Election[C, B], B[C], Election[C, B]] = - new CanBuildFrom[Election[C, B], B[C], Election[C, B]] { - def apply(from: Election[C, B]): Builder[B[C], Election[C, B]] = newBuilder[C, B] - def apply(): Builder[B[C], Election[C, B]] = newBuilder[C, B] - } + def empty[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]]: Election[C, B] = apply(Nil) + + def newBuilder[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] + : mutable.Builder[B[C], Election[C, B]] = + mutable.Seq.newBuilder[B[C]].mapResult(apply) + + def fromSpecific[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]]( + it: IterableOnce[B[C]] + ): Election[C, B] = it match { + case seq: collection.Seq[B[C]] => apply(seq) + case _ => apply(mutable.Seq.from(it)) + } // def apply[B <: Ballot](ballots: B*) = new Election(ballots) - def apply[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]](ballots: Seq[B[C]]) = + def apply[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]]( + ballots: Seq[B[C]] + ): Election[C, B] = new Election(ballots) } diff --git a/modules/core/src/main/scala/org/aossie/agora/model/Parameters.scala b/modules/core/src/main/scala/org/aossie/agora/model/Parameters.scala index 43aa8b7..0582773 100644 --- a/modules/core/src/main/scala/org/aossie/agora/model/Parameters.scala +++ b/modules/core/src/main/scala/org/aossie/agora/model/Parameters.scala @@ -8,10 +8,10 @@ case class MajorityBonus(jackpot: Double, bonus: Double) object MajorityBonus { implicit val majorityBonusReader: Reads[MajorityBonus] = - (__ \ "jackpot").read[Double].and((__ \ "bonus").read[Double])(MajorityBonus.apply _) + (__ \ "jackpot").read[Double].and((__ \ "bonus").read[Double])(MajorityBonus.apply) implicit val majorityBonusWriter: Writes[MajorityBonus] = - (__ \ "jackpot").write[Double].and((__ \ "bonus").write[Double])(unlift(MajorityBonus.unapply)) + (__ \ "jackpot").write[Double].and((__ \ "bonus").write[Double])(m => (m.jackpot, m.bonus)) } @@ -20,12 +20,11 @@ case class ComparisonSets(set1: Array[String], set2: Array[String]) object ComparisonSets { implicit val comparisonSetsReader: Reads[ComparisonSets] = - (__ \ "set1").read[Array[String]].and((__ \ "set2").read[Array[String]])(ComparisonSets.apply _) + (__ \ "set1").read[Array[String]].and((__ \ "set2").read[Array[String]])(ComparisonSets.apply) - implicit val comparisonSetsWriter: Writes[ComparisonSets] = - (__ \ "set1") - .write[Array[String]] - .and((__ \ "set2").write[Array[String]])(unlift(ComparisonSets.unapply)) + implicit val comparisonSetsWriter: Writes[ComparisonSets] = (__ \ "set1") + .write[Array[String]] + .and((__ \ "set2").write[Array[String]])(s => (s.set1, s.set2)) } @@ -61,6 +60,6 @@ object Parameters { .and((__ \ "majority_bonus").readNullable[MajorityBonus]) .and((__ \ "probability_distribution").readNullable[Array[Map[String, Double]]]) .and((__ \ "comparison_sets").readNullable[ComparisonSets]) - .and((__ \ "majorityPercentage").readNullable[Double])(Parameters.apply _) + .and((__ \ "majorityPercentage").readNullable[Double])(Parameters.apply) } diff --git a/modules/core/src/main/scala/org/aossie/agora/model/Report.scala b/modules/core/src/main/scala/org/aossie/agora/model/Report.scala index 2970742..e095fda 100644 --- a/modules/core/src/main/scala/org/aossie/agora/model/Report.scala +++ b/modules/core/src/main/scala/org/aossie/agora/model/Report.scala @@ -135,7 +135,9 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { var sw = "" println("\n WINNERS \n") for (w <- winners) { - println(w._1 + ": " + w._2.numerator.toFloat / w._2.denominator.toFloat + "\n") + println( + w._1.toString + ": " + (w._2.numerator.toFloat / w._2.denominator.toFloat).toString + "\n" + ) sw = sw + w._1 + ": " + w._2.numerator.toFloat / w._2.denominator.toFloat + "\n" } writer.write(sw) @@ -177,7 +179,8 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { if (countnum == 1) { for (c <- tableorder) { if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator + line += (count.getTotals(c).numerator / count.getTotals(c).denominator) + .toString() + separator } else { line += separator } @@ -189,7 +192,8 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { for (c <- tableorder) { if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator + line += (count.getTotals(c).numerator / count.getTotals(c).denominator) + .toString() + separator } else { line += separator } @@ -221,7 +225,7 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { writer.write("Count" + separator) var countnum = -1 - tableorder.foreach(c => writer.write(c + separator)) + tableorder.foreach(c => writer.write(c.toString + separator)) writer.write( "Initiator" + separator + "Action" + separator + @@ -241,7 +245,8 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { for (c <- tableorder) { if (count.getTotals.exists(_._1 == c)) { - line += count.getTotals(c).numerator / count.getTotals(c).denominator + separator + line += (count.getTotals(c).numerator / count.getTotals(c).denominator) + .toString() + separator } else { line += separator } @@ -249,9 +254,9 @@ class Report[C <: Candidate, B[CC >: C <: Candidate] <: Ballot[CC]] { var winners = "" for (w <- count.getWinners if count.getWinners.nonEmpty) - winners += w._1 + " (" + w._2 + "); " + winners += w._1.toString + " (" + w._2 + "); " - line += count.getInitiator + separator + count.getAction + separator + winners + separator + count.getLossByFraction.toInt + separator + line += count.getInitiator.toString + separator + count.getAction + separator + winners + separator + count.getLossByFraction.toInt + separator val exhaustedBallots = count.getExhaustedBallots val ignoredBallots = count.getIgnoredBallots diff --git a/modules/core/src/main/scala/org/aossie/agora/util/matrix/package.scala b/modules/core/src/main/scala/org/aossie/agora/util/matrix/package.scala index 6ae9885..bace260 100644 --- a/modules/core/src/main/scala/org/aossie/agora/util/matrix/package.scala +++ b/modules/core/src/main/scala/org/aossie/agora/util/matrix/package.scala @@ -7,9 +7,9 @@ package object matrix { def addMatrix(m1: Array[Array[Rational]], m2: Array[Array[Rational]]): Array[Array[Rational]] = { - m1.zip(m2).map { rows: (Array[Rational], Array[Rational]) => + m1.zip(m2).map { rows => { - rows._1.zip(rows._2).map { items: (Rational, Rational) => + rows._1.zip(rows._2).map { items => items._1 + items._2 } } @@ -34,7 +34,7 @@ package object matrix { } def dotProduct(row1: Array[Rational], row2: Array[Rational]): Rational = - row1.zip(row2).map { t: (Rational, Rational) => t._1 * t._2 }.reduce(_ + _) + row1.zip(row2).map((t: (Rational, Rational)) => t._1 * t._2).reduce(_ + _) def transpose(matrix: Array[Array[Rational]], size: Int): Array[Array[Rational]] = { for (i <- 0 until size) diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/ACT.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/ACT.scala index 2fd9490..67092be 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/ACT.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/ACT.scala @@ -236,7 +236,7 @@ abstract class ACT[C <: Candidate] val newtotalsWithoutFraction = newElectionWithoutFractionInTotals.firstVotes(ccandidates) val newtotalsWithoutFractionWithoutpendingwinners = - newtotalsWithoutFraction.filterKeys(!pendingWinners.contains(_)) + newtotalsWithoutFraction.filterKeys(!pendingWinners.contains(_)).toMap result.removePendingWinner(winner) @@ -337,7 +337,7 @@ abstract class ACT[C <: Candidate] // simulating EVACS's incorrect total as a result of partial exclusion val totalsWithoutNewWinners = - totalsWithIncorrectValueForCandidate.filterKeys(k => !ws.map(_._1).contains(k)) + totalsWithIncorrectValueForCandidate.filterKeys(k => !ws.map(_._1).contains(k)).toMap // excluding winners that are already identified in the while-loop result.addTotalsToHistory(totalsWithIncorrectValueForCandidate) diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/AustralianSenate.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/AustralianSenate.scala index bf3c2ee..4b66da0 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/AustralianSenate.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/AustralianSenate.scala @@ -488,7 +488,7 @@ class AustralianSenate[C <: Candidate] val newtotalsWithoutFraction = newElectionWithoutFractionInTotals.firstVotes(ccandidates) val newtotalsWithoutFractionWithoutpendingwinners = - newtotalsWithoutFraction.filterKeys(k => !pendingWinners.contains(k)) + newtotalsWithoutFraction.filterKeys(k => !pendingWinners.contains(k)).toMap println("winner " + winner) @@ -588,7 +588,7 @@ class AustralianSenate[C <: Candidate] val totalsAfterFractionLoss = newElectionWithoutFractionInTotals.firstVotes(ccandidates) val totalsWithoutNewWinners = - totalsAfterFractionLoss.filterKeys(k => !ws.map(_._1).contains(k)) + totalsAfterFractionLoss.filterKeys(k => !ws.map(_._1).contains(k)).toMap // excluding winners that are already identified in the while-loop result.addTotalsToHistory(totalsWithoutNewWinners) diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/BipartisanSet.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/BipartisanSet.scala index b7794f8..7a7e916 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/BipartisanSet.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/BipartisanSet.scala @@ -105,7 +105,7 @@ object BipartisanSet } // check if p is a balanced probability distribution => p is balanced if (p(x)>0 ⇔ mp(x)=0) and (p(x)=0 ⇔ mp(x)<0) - require(balancedProbabilityDistribution, "probability distribution is not balanced!") + require(balancedProbabilityDistribution(), "probability distribution is not balanced!") // BP(A,PM) = {x∈A : p(x)>0, p balanced for (A,PM)} where PM is majority graph candidatesProbabilities.filter { case (cand, prob) => diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/Dodgson.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/Dodgson.scala index 40c9b8c..e6428a6 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/Dodgson.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/Dodgson.scala @@ -1,10 +1,10 @@ package org.aossie.agora.votecounter import org.aossie.agora.model._ - import spire.math.Rational import collection.mutable.{HashMap => MMap} +import scala.collection.SeqView /** Wiki : https://en.wikipedia.org/wiki/Dodgson%27s_method Implementation : * http://infosyncratic.nl/talks/2008-votingprocedures.pdf @@ -18,24 +18,24 @@ object Dodgson extends VoteCounter[PreferenceBallot] { ): List[(C, Rational)] = { // find flip vector from min sum to max sum which satisfies condorcet condition - val minDodgsonFlipList = List - .fill(e.weight.toInt)(0 to ccandidates.length) - .flatten - .view - .combinations(e.weight.toInt) - .flatMap(_.permutations) - .toList - .sortBy(_.sum) - .view - - val minDodgsonFlip = minDodgsonFlipList.find(list => - getCondorcetWinnerIfExist(list.force, ccandidates, e).nonEmpty - ) + val minDodgsonFlipList: SeqView[List[Int]] = + List + .fill(e.weight.toInt)(0 to ccandidates.length) + .flatten + .view + .combinations(e.weight.toInt) + .flatMap(_.toList.permutations) + .toList + .sortBy(_.sum) + .view + + val minDodgsonFlip = + minDodgsonFlipList.find(list => getCondorcetWinnerIfExist(list, ccandidates, e).nonEmpty) // find the dodgson winner based on this flip vector minDodgsonFlip match { case Some(list) => - getCondorcetWinnerIfExist(list.force, ccandidates, e) match { + getCondorcetWinnerIfExist(list, ccandidates, e) match { case Some(candidate) => List((candidate, Rational(list.sum, 1))) case None => List() } diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/Egalitarian.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/Egalitarian.scala index adf334d..242a05d 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/Egalitarian.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/Egalitarian.scala @@ -15,11 +15,11 @@ abstract class Egalitarian[C <: Candidate] extends VoteCounterWithCandidate[C, P def utilityIndividual(b: PreferenceBallot[C], c: C, numCandidates: Int): Int = numCandidates - rank(b, c, numCandidates) - def utilitySet(b: PreferenceBallot[C], cs: Traversable[C]): Int = + def utilitySet(b: PreferenceBallot[C], cs: Iterable[C]): Int = cs.map(c => utilityIndividual(b, c, cs.size)).reduce(_ + _) - def socialWelfare(e: Election[C, PreferenceBallot], cs: Traversable[C]): Rational = { - (Rational(0, 1) /: e.ballots) { (acc, b) => + def socialWelfare(e: Election[C, PreferenceBallot], cs: Iterable[C]): Rational = { + e.ballots.foldLeft(Rational(0, 1)) { (acc, b) => acc + (b.weight) * pow(utilitySet(b, cs), 1 / fairness) } } diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/MeekSTV.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/MeekSTV.scala index b228ca9..18767c3 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/MeekSTV.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/MeekSTV.scala @@ -77,7 +77,7 @@ class MeekSTV[C <: Candidate] def surplusQuantity[C <: Candidate](totals: MMap[C, Rational], quota: Rational): Rational = { val surplusAmount: Rational = - (Rational(0, 1) /: (totals.filter(_._2 >= quota).map(_._2))) { (surplus, t) => + totals.filter(_._2 >= quota).map(_._2).foldLeft(Rational(0, 1)) { (surplus, t) => surplus + t - quota } surplusAmount diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/Nanson.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/Nanson.scala index f597f79..869f482 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/Nanson.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/Nanson.scala @@ -18,7 +18,7 @@ object Nanson extends VoteCounter[PreferenceBallot] { bordaScores(election, candidates).toList } else { var cbs = bordaScores(election, candidates) // borda scores of candidates - val average = (Rational(0, 1) /: candidates)(_ + cbs(_)) / Rational(candidates.length) + val average = candidates.foldLeft(Rational(0, 1))(_ + cbs(_)) / Rational(candidates.length) winners(election, candidates.filter(x => cbs(x) > average), numVacancies) } } diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/SMC.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/SMC.scala index 7908bd9..4670e09 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/SMC.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/SMC.scala @@ -60,7 +60,7 @@ object SMC val candOrderList = param.comparisonOrder.get.map(name => ccandidates.find(cand => cand.name == name).get) - List((candOrderList.head /: candOrderList.tail)((cA, cB) => { + List(candOrderList.tail.foldLeft(candOrderList.head)((cA, cB) => { if ( electionResponse(ccandidates.indexOf(cA))( ccandidates.indexOf(cB) diff --git a/modules/core/src/main/scala/org/aossie/agora/votecounter/stv/SurplusDistributionTieResolution.scala b/modules/core/src/main/scala/org/aossie/agora/votecounter/stv/SurplusDistributionTieResolution.scala index 3b68e91..b7bd74d 100644 --- a/modules/core/src/main/scala/org/aossie/agora/votecounter/stv/SurplusDistributionTieResolution.scala +++ b/modules/core/src/main/scala/org/aossie/agora/votecounter/stv/SurplusDistributionTieResolution.scala @@ -87,9 +87,11 @@ trait ACTSurplusDistributionTieResolution[C <: Candidate] p._2 == totalshistory.head(biggestcandidate) && equaltotals.toSet.contains(p._1) == true } val lbiggestcandidates = biggestcandidates.toList.map(x => x._1) - val totalsofremainingcandidates = totalshistory.head.filterKeys(k => - lbiggestcandidates.toSet.contains(k) == false && equaltotals.toSet.contains(k) == true - ) + val totalsofremainingcandidates = totalshistory.head + .filterKeys(k => + lbiggestcandidates.toSet.contains(k) == false && equaltotals.toSet.contains(k) == true + ) + .toMap val listoftotalsofremainingcandidates = totalsofremainingcandidates.toList.sortBy(x => x._2).reverse if (biggestcandidates.size > 1) { diff --git a/project/Testing.scala b/project/Testing.scala index 15683f3..087a4cb 100644 --- a/project/Testing.scala +++ b/project/Testing.scala @@ -13,14 +13,14 @@ object Testing { Seq( fork in VerificationTest := false, parallelExecution in VerificationTest := false, - scalaSource in VerificationTest := baseDirectory.value / "src/test/scala/agora") + scalaSource in VerificationTest := baseDirectory.value / "modules/cli/src/test/scala/org/aossie/agora") private lazy val performanceTestSettings = inConfig(PerformanceTest)(Defaults.testSettings) ++ Seq( fork in PerformanceTest := false, parallelExecution in PerformanceTest := false, - scalaSource in PerformanceTest := baseDirectory.value / "src/test/scala/performance") + scalaSource in PerformanceTest := baseDirectory.value / "modules/cli/src/test/scala/org/aossie/agora/performance") lazy val testAllQuick = TaskKey[Unit]("test-all-quick") From 04014920b2839e1699c0bbd02a10f85bab3dd1fd Mon Sep 17 00:00:00 2001 From: icemc Date: Thu, 15 Aug 2024 01:53:34 +0100 Subject: [PATCH 4/4] updated scalafmt code --- .scalafmt.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 54decd3..387f979 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,5 +1,5 @@ version = 3.8.1 -runner.dialect = scala212 +runner.dialect = scala213 # Number of maximum characters in a column maxColumn = 100 @@ -16,7 +16,7 @@ assumeStandardLibraryStripMargin = true # Align everything that can be aligned align.preset = most -align.multiline = false +align.multiline = true align.tokens."+" = [ { code = ":=", owner = "Term.ApplyInfix"