diff --git a/agt/ballotagent/new_ballot.go b/agt/ballotagent/new_ballot.go
index e0121b2a7c47f00407789e8298af578fa39b32b5..ecaa4f0d0587e0ad535407236489cbdf9cd94e34 100644
--- a/agt/ballotagent/new_ballot.go
+++ b/agt/ballotagent/new_ballot.go
@@ -77,6 +77,10 @@ func (rsa *BallotServerAgent) createBallot(w http.ResponseWriter, r *http.Reques
 		go rsa.handleBallot(resp, cs.BordaSWF, cs.BordaSCF, tb, deadline)
 	case "approval":
 		go rsa.handleBallotWithSingleOption(resp, cs.ApprovalSWF, cs.ApprovalSCF, tb, deadline)
+	case "copeland":
+		go rsa.handleBallot(resp, cs.CopelandSWF, cs.CopelandSCF, tb, deadline)
+	case "stv":
+		go rsa.handleBallot(resp, cs.STV_SWF, cs.STV_SCF, tb, deadline)
 	default:
 		w.WriteHeader(http.StatusNotImplemented)
 		msg := fmt.Sprintf("Unkonwn rule '%s'", req.Rule)
diff --git a/comsoc/Copeland.go b/comsoc/Copeland.go
index 7076a9b4de20578f2bfe7749ab88012274203de2..b99c415fc71cc8b77a72829f580366024ce58b9d 100644
--- a/comsoc/Copeland.go
+++ b/comsoc/Copeland.go
@@ -1 +1,41 @@
-package comsoc
\ No newline at end of file
+package comsoc
+
+func CopelandSWF(p Profile) (count Count, err error) {
+	count = make(Count)
+	alts := make([]Alternative, 0)
+	for i := 1; i <= len(p[0]); i++ {
+		alts = append(alts, Alternative(i))
+	}
+	err = checkProfileAlternative(p, alts)
+	if err != nil {
+		return nil, err
+	}
+	for alt1 := 1; alt1 <= len(alts); alt1++ {
+		for alt2 := alt1 + 1; alt2 <= len(alts); alt2++ {
+			score1, score2 := 0, 0
+			for _, pref := range p {
+				if isPref(Alternative(alt1), Alternative(alt2), pref) {
+					score1++
+				} else {
+					score2++
+				}
+			}
+			if score1 > score2 {
+				count[Alternative(alt1)]++
+				count[Alternative(alt2)]--
+			} else if score2 > score1 {
+				count[Alternative(alt1)]--
+				count[Alternative(alt2)]++
+			}
+		}
+	}
+	return
+}
+
+func CopelandSCF(p Profile) (bestAlts []Alternative, err error) {
+	count, err := CopelandSWF(p)
+	if err != nil {
+		return nil, err
+	}
+	return maxCount(count), err
+}
diff --git a/comsoc/STV.go b/comsoc/STV.go
index 7076a9b4de20578f2bfe7749ab88012274203de2..0416ec0a962ed6876ee16f554d1fb084379c04c1 100644
--- a/comsoc/STV.go
+++ b/comsoc/STV.go
@@ -1 +1,77 @@
-package comsoc
\ No newline at end of file
+package comsoc
+
+func getRandomKey(count Count) Alternative {
+	for k := range count {
+		return k
+	}
+	return 0 // should never happen
+}
+
+func countContains(count Count, alt Alternative) bool {
+	for k := range count {
+		if alt == k {
+			return true
+		}
+	}
+	return false
+}
+
+// renvoie une des pires alternatives pour un décompte donné
+func minCount(count Count, alts []Alternative) (worstAlt Alternative) {
+	for _, alt := range alts {
+		if !countContains(count, alt) {
+			return alt
+		}
+	}
+	worstAlt = getRandomKey(count)
+	minPoints := count[worstAlt]
+	for i, points := range count {
+		if points < minPoints {
+			worstAlt = i
+			minPoints = points
+		}
+	}
+	return worstAlt
+}
+
+func STV_SWF(p Profile) (count Count, err error) {
+	count = make(Count)
+	alts := make([]Alternative, 0)
+	for i := 1; i <= len(p[0]); i++ {
+		alts = append(alts, Alternative(i))
+	}
+	err = checkProfileAlternative(p, alts)
+	if err != nil {
+		return nil, err
+	}
+
+	nbRounds := len(alts)
+	for round := 0; round < nbRounds; round++ {
+		majorityRes := make(Count)
+		for _, pref := range p {
+			majorityRes[pref[0]]++
+		}
+		worstAlt := minCount(majorityRes, alts)
+		count[worstAlt] = round
+
+		// On enlève la pire alt des alts
+		alts[rank(worstAlt, alts)] = alts[len(alts)-1]
+		alts = alts[:len(alts)-1]
+
+		// on enlève la pire alt de chacunes des preferences
+		for i, pref := range p {
+			pref[rank(worstAlt, pref)] = pref[len(pref)-1]
+			pref = pref[:len(pref)-1]
+			p[i] = pref
+		}
+	}
+	return
+}
+
+func STV_SCF(p Profile) (bestAlts []Alternative, err error) {
+	count, err := STV_SWF(p)
+	if err != nil {
+		return nil, err
+	}
+	return maxCount(count), err
+}