@snapshot-labs/snapshot.js 0.12.50 → 0.12.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1090,6 +1090,7 @@ var definitions$1 = {
1090
1090
  "approval",
1091
1091
  "ranked-choice",
1092
1092
  "quadratic",
1093
+ "copeland",
1093
1094
  "weighted",
1094
1095
  "custom",
1095
1096
  "basic"
@@ -3944,6 +3945,121 @@ class RankedChoiceVoting {
3944
3945
  }
3945
3946
  }
3946
3947
 
3948
+ // CopelandVoting implements ranked choice voting using Copeland's method
3949
+ // This method compares each pair of choices and awards points based on pairwise victories
3950
+ class CopelandVoting {
3951
+ constructor(proposal, votes, strategies, selected) {
3952
+ this.proposal = proposal;
3953
+ this.votes = votes;
3954
+ this.strategies = strategies;
3955
+ this.selected = selected;
3956
+ }
3957
+ // Validates if a vote choice is valid for the given proposal
3958
+ // Allows partial ranking (not all choices need to be ranked)
3959
+ static isValidChoice(voteChoice, proposalChoices) {
3960
+ if (!Array.isArray(voteChoice) ||
3961
+ voteChoice.length === 0 ||
3962
+ voteChoice.length > proposalChoices.length ||
3963
+ new Set(voteChoice).size !== voteChoice.length) {
3964
+ return false;
3965
+ }
3966
+ return voteChoice.every((choice) => Number.isInteger(choice) &&
3967
+ choice >= 1 &&
3968
+ choice <= proposalChoices.length);
3969
+ }
3970
+ // Returns only the valid votes
3971
+ getValidVotes() {
3972
+ return this.votes.filter((vote) => CopelandVoting.isValidChoice(vote.choice, this.proposal.choices));
3973
+ }
3974
+ // Calculates the Copeland scores for each choice
3975
+ getScores() {
3976
+ const validVotes = this.getValidVotes();
3977
+ const choicesCount = this.proposal.choices.length;
3978
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array(choicesCount).fill(0));
3979
+ // Calculate pairwise comparisons
3980
+ for (const vote of validVotes) {
3981
+ for (let i = 0; i < vote.choice.length; i++) {
3982
+ for (let j = i + 1; j < vote.choice.length; j++) {
3983
+ const winner = vote.choice[i] - 1;
3984
+ const loser = vote.choice[j] - 1;
3985
+ pairwiseComparisons[winner][loser] += vote.balance;
3986
+ pairwiseComparisons[loser][winner] -= vote.balance;
3987
+ }
3988
+ }
3989
+ }
3990
+ // Calculate Copeland scores
3991
+ const scores = Array(choicesCount).fill(0);
3992
+ for (let i = 0; i < choicesCount; i++) {
3993
+ for (let j = 0; j < choicesCount; j++) {
3994
+ if (i !== j) {
3995
+ if (pairwiseComparisons[i][j] > 0) {
3996
+ scores[i]++;
3997
+ }
3998
+ else if (pairwiseComparisons[i][j] < 0) {
3999
+ scores[j]++;
4000
+ }
4001
+ else {
4002
+ scores[i] += 0.5;
4003
+ scores[j] += 0.5;
4004
+ }
4005
+ }
4006
+ }
4007
+ }
4008
+ return scores;
4009
+ }
4010
+ // Calculates the Copeland scores for each choice, broken down by strategy
4011
+ getScoresByStrategy() {
4012
+ const validVotes = this.getValidVotes();
4013
+ const choicesCount = this.proposal.choices.length;
4014
+ const strategiesCount = this.strategies.length;
4015
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0)));
4016
+ // Calculate pairwise comparisons for each strategy
4017
+ for (const vote of validVotes) {
4018
+ for (let i = 0; i < vote.choice.length; i++) {
4019
+ for (let j = i + 1; j < vote.choice.length; j++) {
4020
+ const winner = vote.choice[i] - 1;
4021
+ const loser = vote.choice[j] - 1;
4022
+ for (let s = 0; s < strategiesCount; s++) {
4023
+ pairwiseComparisons[winner][loser][s] += vote.scores[s];
4024
+ pairwiseComparisons[loser][winner][s] -= vote.scores[s];
4025
+ }
4026
+ }
4027
+ }
4028
+ }
4029
+ // Calculate Copeland scores for each strategy
4030
+ const scores = Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0));
4031
+ for (let i = 0; i < choicesCount; i++) {
4032
+ for (let j = 0; j < choicesCount; j++) {
4033
+ if (i !== j) {
4034
+ for (let s = 0; s < strategiesCount; s++) {
4035
+ if (pairwiseComparisons[i][j][s] > 0) {
4036
+ scores[i][s]++;
4037
+ }
4038
+ else if (pairwiseComparisons[i][j][s] < 0) {
4039
+ scores[j][s]++;
4040
+ }
4041
+ else {
4042
+ scores[i][s] += 0.5;
4043
+ scores[j][s] += 0.5;
4044
+ }
4045
+ }
4046
+ }
4047
+ }
4048
+ }
4049
+ return scores;
4050
+ }
4051
+ // Calculates the total score (sum of all valid vote balances)
4052
+ getScoresTotal() {
4053
+ return this.getValidVotes().reduce((total, vote) => total + vote.balance, 0);
4054
+ }
4055
+ // Returns a string representation of the selected choices
4056
+ getChoiceString() {
4057
+ return this.selected
4058
+ .map((choice) => this.proposal.choices[choice - 1])
4059
+ .join(', ');
4060
+ }
4061
+ }
4062
+
3947
4063
  function percentageOfTotal(i, values, total) {
3948
4064
  const reducedTotal = total.reduce((a, b) => a + b, 0);
3949
4065
  const percent = (values[i] / reducedTotal) * 100;
@@ -4016,6 +4132,7 @@ var voting = {
4016
4132
  approval: ApprovalVoting,
4017
4133
  quadratic: QuadraticVoting,
4018
4134
  'ranked-choice': RankedChoiceVoting,
4135
+ copeland: CopelandVoting,
4019
4136
  weighted: WeightedVoting,
4020
4137
  basic: SingleChoiceVoting
4021
4138
  };
@@ -1080,6 +1080,7 @@ var definitions$1 = {
1080
1080
  "approval",
1081
1081
  "ranked-choice",
1082
1082
  "quadratic",
1083
+ "copeland",
1083
1084
  "weighted",
1084
1085
  "custom",
1085
1086
  "basic"
@@ -3934,6 +3935,121 @@ class RankedChoiceVoting {
3934
3935
  }
3935
3936
  }
3936
3937
 
3938
+ // CopelandVoting implements ranked choice voting using Copeland's method
3939
+ // This method compares each pair of choices and awards points based on pairwise victories
3940
+ class CopelandVoting {
3941
+ constructor(proposal, votes, strategies, selected) {
3942
+ this.proposal = proposal;
3943
+ this.votes = votes;
3944
+ this.strategies = strategies;
3945
+ this.selected = selected;
3946
+ }
3947
+ // Validates if a vote choice is valid for the given proposal
3948
+ // Allows partial ranking (not all choices need to be ranked)
3949
+ static isValidChoice(voteChoice, proposalChoices) {
3950
+ if (!Array.isArray(voteChoice) ||
3951
+ voteChoice.length === 0 ||
3952
+ voteChoice.length > proposalChoices.length ||
3953
+ new Set(voteChoice).size !== voteChoice.length) {
3954
+ return false;
3955
+ }
3956
+ return voteChoice.every((choice) => Number.isInteger(choice) &&
3957
+ choice >= 1 &&
3958
+ choice <= proposalChoices.length);
3959
+ }
3960
+ // Returns only the valid votes
3961
+ getValidVotes() {
3962
+ return this.votes.filter((vote) => CopelandVoting.isValidChoice(vote.choice, this.proposal.choices));
3963
+ }
3964
+ // Calculates the Copeland scores for each choice
3965
+ getScores() {
3966
+ const validVotes = this.getValidVotes();
3967
+ const choicesCount = this.proposal.choices.length;
3968
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array(choicesCount).fill(0));
3969
+ // Calculate pairwise comparisons
3970
+ for (const vote of validVotes) {
3971
+ for (let i = 0; i < vote.choice.length; i++) {
3972
+ for (let j = i + 1; j < vote.choice.length; j++) {
3973
+ const winner = vote.choice[i] - 1;
3974
+ const loser = vote.choice[j] - 1;
3975
+ pairwiseComparisons[winner][loser] += vote.balance;
3976
+ pairwiseComparisons[loser][winner] -= vote.balance;
3977
+ }
3978
+ }
3979
+ }
3980
+ // Calculate Copeland scores
3981
+ const scores = Array(choicesCount).fill(0);
3982
+ for (let i = 0; i < choicesCount; i++) {
3983
+ for (let j = 0; j < choicesCount; j++) {
3984
+ if (i !== j) {
3985
+ if (pairwiseComparisons[i][j] > 0) {
3986
+ scores[i]++;
3987
+ }
3988
+ else if (pairwiseComparisons[i][j] < 0) {
3989
+ scores[j]++;
3990
+ }
3991
+ else {
3992
+ scores[i] += 0.5;
3993
+ scores[j] += 0.5;
3994
+ }
3995
+ }
3996
+ }
3997
+ }
3998
+ return scores;
3999
+ }
4000
+ // Calculates the Copeland scores for each choice, broken down by strategy
4001
+ getScoresByStrategy() {
4002
+ const validVotes = this.getValidVotes();
4003
+ const choicesCount = this.proposal.choices.length;
4004
+ const strategiesCount = this.strategies.length;
4005
+ const pairwiseComparisons = Array.from({ length: choicesCount }, () => Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0)));
4006
+ // Calculate pairwise comparisons for each strategy
4007
+ for (const vote of validVotes) {
4008
+ for (let i = 0; i < vote.choice.length; i++) {
4009
+ for (let j = i + 1; j < vote.choice.length; j++) {
4010
+ const winner = vote.choice[i] - 1;
4011
+ const loser = vote.choice[j] - 1;
4012
+ for (let s = 0; s < strategiesCount; s++) {
4013
+ pairwiseComparisons[winner][loser][s] += vote.scores[s];
4014
+ pairwiseComparisons[loser][winner][s] -= vote.scores[s];
4015
+ }
4016
+ }
4017
+ }
4018
+ }
4019
+ // Calculate Copeland scores for each strategy
4020
+ const scores = Array.from({ length: choicesCount }, () => Array(strategiesCount).fill(0));
4021
+ for (let i = 0; i < choicesCount; i++) {
4022
+ for (let j = 0; j < choicesCount; j++) {
4023
+ if (i !== j) {
4024
+ for (let s = 0; s < strategiesCount; s++) {
4025
+ if (pairwiseComparisons[i][j][s] > 0) {
4026
+ scores[i][s]++;
4027
+ }
4028
+ else if (pairwiseComparisons[i][j][s] < 0) {
4029
+ scores[j][s]++;
4030
+ }
4031
+ else {
4032
+ scores[i][s] += 0.5;
4033
+ scores[j][s] += 0.5;
4034
+ }
4035
+ }
4036
+ }
4037
+ }
4038
+ }
4039
+ return scores;
4040
+ }
4041
+ // Calculates the total score (sum of all valid vote balances)
4042
+ getScoresTotal() {
4043
+ return this.getValidVotes().reduce((total, vote) => total + vote.balance, 0);
4044
+ }
4045
+ // Returns a string representation of the selected choices
4046
+ getChoiceString() {
4047
+ return this.selected
4048
+ .map((choice) => this.proposal.choices[choice - 1])
4049
+ .join(', ');
4050
+ }
4051
+ }
4052
+
3937
4053
  function percentageOfTotal(i, values, total) {
3938
4054
  const reducedTotal = total.reduce((a, b) => a + b, 0);
3939
4055
  const percent = (values[i] / reducedTotal) * 100;
@@ -4006,6 +4122,7 @@ var voting = {
4006
4122
  approval: ApprovalVoting,
4007
4123
  quadratic: QuadraticVoting,
4008
4124
  'ranked-choice': RankedChoiceVoting,
4125
+ copeland: CopelandVoting,
4009
4126
  weighted: WeightedVoting,
4010
4127
  basic: SingleChoiceVoting
4011
4128
  };