Index: voting/pom.xml
===================================================================
--- voting/pom.xml	(revision 29)
+++ voting/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>voting</artifactId>
-	<version>1.5.8</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.9</version> <!-- must equal ${geniusweb.version} -->
 	<packaging>jar</packaging>
 
@@ -17,5 +17,5 @@
 		<passwd>${env.ARTIFACTORY_PASS}</passwd>
 		<jackson-2-version>2.9.10</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -35,7 +35,12 @@
 		</dependency>
 		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>ip</artifactId>
+			<version>${geniusweb.version}</version>
+		</dependency>
+		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 		<!-- the core, which includes Streaming API, shared low-level abstractions 
@@ -84,5 +89,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.2</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -112,14 +117,17 @@
 			</snapshots>
 		</repository>
-
 	</repositories>
 
 	<pluginRepositories>
 		<pluginRepository>
-			<id>jcenter</id>
-			<url>https://jcenter.bintray.com</url>
+			<id>central</id>
+			<url>https://repo1.maven.org/</url>
+		</pluginRepository>
+		<pluginRepository>
+			<id>jfrog-plugins-release</id>
+			<name>plugins-release</name>
+			<url>https://oss.jfrog.org/artifactory/plugins-release</url>
 		</pluginRepository>
 	</pluginRepositories>
-
 
 	<build>
Index: voting/src/main/java/geniusweb/voting/CollectedVotesWithValue.java
===================================================================
--- voting/src/main/java/geniusweb/voting/CollectedVotesWithValue.java	(revision 30)
+++ voting/src/main/java/geniusweb/voting/CollectedVotesWithValue.java	(revision 30)
@@ -0,0 +1,305 @@
+package geniusweb.voting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.Votes;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.Output;
+import geniusweb.ip.ipSolver.IPSolver;
+import geniusweb.ip.mainSolver.Result;
+import geniusweb.issuevalue.Bid;
+import tudelft.utilities.immutablelist.Tuple;
+
+/**
+ * Utility class. Typically contains all the votes collected from 1 round. Can
+ * find the best combinations of votes, eg to get the maximum consensus group.
+ * Basically a copy of {@link CollectedVotesWithValue} but for
+ * {@link VotesWithValue}.
+ * <p>
+ * immutable.
+ */
+public class CollectedVotesWithValue {
+	private final Map<PartyId, VotesWithValue> allvotes;
+	private final Map<PartyId, Integer> powers;
+
+	// caching
+	private transient Map<Bid, Set<VoteWithValue>> allBids = null;
+	private transient Map<PartyId, Bid> maxAgreements = null;
+	// parties must have fixed order.
+	private transient List<PartyId> parties;
+
+	/**
+	 * 
+	 * @param votesMap the {@link Votes} done for each party
+	 * @param power    the power of each party. The power is an integer number.
+	 *                 In the voting process, the party powers add up to form
+	 *                 the total voting power. This set may contain parties that
+	 *                 are not in newmap. The maximum number of parties is 32
+	 *                 (memory constraints)
+	 */
+	public CollectedVotesWithValue(Map<PartyId, VotesWithValue> votesMap,
+			Map<PartyId, Integer> power) {
+		this.allvotes = new HashMap<>(votesMap);
+		this.powers = new HashMap<>(power);
+		// respect insertion order of votesMap, useful
+		// for testing in combination with LinkedHashMap
+		parties = new ArrayList<PartyId>(votesMap.keySet());
+
+		if (!powers.keySet().containsAll(allvotes.keySet())) {
+			throw new IllegalArgumentException(
+					"All voting parties must have a power");
+		}
+		if (parties.size() > 32)
+			throw new IllegalArgumentException(
+					"Exceeded max number of 32 parties- won't fit in memory");
+	}
+
+	/**
+	 * @return all the votes placed so far
+	 */
+	public Map<PartyId, VotesWithValue> getVotes() {
+		return Collections.unmodifiableMap(allvotes);
+	}
+
+	/**
+	 * @return the powers of all parties
+	 */
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	/**
+	 * @return a map containing for each {@link Bid}s a subset of
+	 *         {@link PartyId}s that placed a satisfied vote for that bid, such
+	 *         that the subset has maximum power. Maximum power means that there
+	 *         is no other subset of satisfied votes for that bid with more
+	 *         group power. There may be other subsets of equal power for the
+	 *         bid. If there are no concensus possibilities at all for a bid,
+	 *         the bid is not placed in the map.
+	 */
+	public Map<PartyId, Bid> getMaxAgreements() {
+		if (maxAgreements == null)
+			maxAgreements = getMaxAgreements1();
+		return maxAgreements;
+	}
+
+	/**
+	 * @return all bids that were voted on and the votes for that bid. This
+	 *         basically reverses keys and values in {@link #allvotes}
+	 */
+	public Map<Bid, Set<VoteWithValue>> getAllBids() {
+		if (allBids == null)
+			allBids = getAllBids1();
+		return allBids;
+	}
+
+	/**
+	 * @return the list of all parties, in the exact order as used internally
+	 */
+	public List<PartyId> getParties() {
+		return Collections.unmodifiableList(parties);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((allvotes == null) ? 0 : allvotes.hashCode());
+		result = prime * result + ((powers == null) ? 0 : powers.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		CollectedVotesWithValue other = (CollectedVotesWithValue) obj;
+		if (allvotes == null) {
+			if (other.allvotes != null)
+				return false;
+		} else if (!allvotes.equals(other.allvotes))
+			return false;
+		if (powers == null) {
+			if (other.powers != null)
+				return false;
+		} else if (!powers.equals(other.powers))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "CollectedVotesWithValue[" + allvotes + "," + powers + "]";
+	}
+
+	/**
+	 * @see #getMaxAgreements()
+	 */
+	private Map<PartyId, Bid> getMaxAgreements1() {
+		int numOfAgents = parties.size();
+		if (numOfAgents == 0)
+			return Collections.emptyMap();
+
+		double[] coalitionValues = getCoalitionValues();
+		double acceptableRatio = 100;
+		Input input = new Input(numOfAgents, coalitionValues, acceptableRatio);
+		Result result = new Result(input);
+		Output output = new Output(input);
+
+		IPSolver ipSolver = new IPSolver();
+		ipSolver.solve(input, output, result);
+		int[][] coalitions = result.get_ipBestCSFound();
+
+		// coalitions now contains array like [[3, 4], [1, 2]]
+		// indicating which parties are in coalitions.
+		Map<PartyId, Bid> agreements = new HashMap<>();
+		for (int n = 0; n < coalitions.length; n++) {
+			Set<PartyId> active = getParties(coalitions[n]);
+			Tuple<Bid, Integer> bestcoalition = getBestCoalition(active);
+			if (bestcoalition != null) {
+				Bid bestbid = bestcoalition.get1();
+				for (PartyId party : active) {
+					agreements.put(party, bestbid);
+				}
+			}
+		}
+		return agreements;
+	}
+
+	/**
+	 * @return array of the maximum goalition value for each possible coalition,
+	 *         structured as required for the IP algorithm: arrey element k
+	 *         contains the best coalition with members
+	 *         {@link #getActiveParties(int)}
+	 */
+	protected double[] getCoalitionValues() {
+		int numOfAgents = parties.size();
+		double[] coalitionValues = new double[1 << numOfAgents];
+
+		for (int n = 0; n < 1 << numOfAgents; n++) {
+			Tuple<Bid, Integer> best = getBestCoalition(getActiveParties(n));
+			coalitionValues[n] = best == null ? 0 : best.get2();
+		}
+		return coalitionValues;
+	}
+
+	/**
+	 * @param active the set of active parties to consider for a coalition.
+	 * @return the Bid and the coalition value of the bid. This searches the bid
+	 *         that all parties can accept and has the highest sum of values.
+	 *         Returns null if there is no coalition possible
+	 */
+	protected Tuple<Bid, Integer> getBestCoalition(Set<PartyId> active) {
+		if (active.isEmpty())
+			return null;
+		PartyId firstparty = active.iterator().next();
+		if (!allvotes.containsKey(firstparty))
+			return null;
+		Integer power = getPower(active);
+		Bid bestBid = null;
+		Integer bestValue = 0;
+		for (VoteWithValue vote : allvotes.get(firstparty).getVotes()) {
+			Integer val = getBidValue(active, vote.getBid(), power);
+			if (val > bestValue) {
+				bestBid = vote.getBid();
+				bestValue = val;
+			}
+		}
+		if (bestBid == null)
+			return null;
+		return new Tuple<Bid, Integer>(bestBid, bestValue);
+	}
+
+	/***
+	 * 
+	 * @param active the active parties
+	 * @param bid    the bid to evaluate
+	 * @param power  the power of the set of active parties
+	 * @return the sum of the values for this bid, or 0 if this bid is
+	 *         unacceptable: a party did not vote for that bid, or if the actual
+	 *         power of the active parties is outside the acceptable range for a
+	 *         vote.
+	 */
+	private Integer getBidValue(Set<PartyId> active, Bid bid, int power) {
+		int value = 0;
+		for (PartyId party : active) {
+			VoteWithValue vote = allvotes.get(party).getVote(bid);
+			if (vote == null || power < vote.getMinPower()
+					|| power > vote.getMaxPower())
+				return 0;
+			value = value + vote.getValue();
+		}
+		return value;
+	}
+
+	/**
+	 * @param n a coalition number, where 1 at bit position k indicates party k
+	 *          is active.
+	 * @return the active parties in the n bit-indicator
+	 */
+	protected Set<PartyId> getActiveParties(int n) {
+		Set<PartyId> activeparties = new HashSet<>();
+		for (PartyId party : parties) {
+			if ((n & 1) == 1) {
+				activeparties.add(party);
+			}
+			n = n >> 1;
+		}
+		return activeparties;
+	}
+
+	/**
+	 * 
+	 * @param partynrs the party numbers in the coalition. WARNING the first
+	 *                 party number is assumed to be 1, instead of the usual 0.
+	 *                 This to match the working of the IP library that starts
+	 *                 counting at 1.
+	 * @return list of {@link PartyId}s
+	 */
+	protected Set<PartyId> getParties(int[] partynrs) {
+		Set<PartyId> active = new HashSet<PartyId>(partynrs.length);
+		for (int n = 0; n < partynrs.length; n++) {
+			active.add(parties.get(partynrs[n] - 1));
+		}
+		return active;
+	}
+
+	private Map<Bid, Set<VoteWithValue>> getAllBids1() {
+		Map<Bid, Set<VoteWithValue>> bids = new HashMap<>();
+		for (VotesWithValue votes : allvotes.values()) {
+			for (VoteWithValue vote : votes.getVotes()) {
+				Bid bid = vote.getBid();
+				Set<VoteWithValue> list = bids.get(bid);
+				if (list == null)
+					list = new HashSet<VoteWithValue>();
+				list.add(vote);
+				bids.put(bid, list);
+			}
+		}
+		return bids;
+	}
+
+	/**
+	 * 
+	 * @param activeparties a list of parties
+	 * @return total voting power of the parties
+	 */
+	protected Integer getPower(Set<PartyId> activeparties) {
+		return activeparties.stream().map(p -> powers.get(p)).reduce(0,
+				Integer::sum);
+	}
+}
Index: voting/src/main/java/geniusweb/voting/VotingEvaluator.java
===================================================================
--- voting/src/main/java/geniusweb/voting/VotingEvaluator.java	(revision 29)
+++ voting/src/main/java/geniusweb/voting/VotingEvaluator.java	(revision 30)
@@ -34,7 +34,8 @@
 	 * </ul>
 	 * 
-	 * @param votes the Votes made by parties. All active parties in the
-	 *              negotiation must be available, even if they did not vote, to
-	 *              ensure that {@link #isFinished()} can work properly.
+	 * @param votes the Votes made by parties in the round to be evaluated.. All
+	 *              active parties in the negotiation must be available, even if
+	 *              they did not vote, to ensure that {@link #isFinished()} can
+	 *              work properly.
 	 * 
 	 * @return new VotingEvaluation object containing the given votes.
Index: voting/src/main/java/geniusweb/voting/VotingEvaluatorWithValue.java
===================================================================
--- voting/src/main/java/geniusweb/voting/VotingEvaluatorWithValue.java	(revision 30)
+++ voting/src/main/java/geniusweb/voting/VotingEvaluatorWithValue.java	(revision 30)
@@ -0,0 +1,56 @@
+package geniusweb.voting;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+
+import geniusweb.inform.Agreements;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+
+/**
+ * Evaluates the {@link CollectedVotes}, determining the agreements and if the
+ * negotiation should continue.
+ * 
+ * Implementations should be immutable and not serialize internal variables (eg
+ * &#64;JsonIgnore them).
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT)
+@JsonSubTypes({ @JsonSubTypes.Type(value = LargestAgreementWithValue.class) })
+public interface VotingEvaluatorWithValue {
+	/**
+	 * This function is the effective constructor. This mechanism serves several
+	 * purposes:
+	 * <ul>
+	 * <li>It defines the constructor at interface level, so that we can ensure
+	 * it's available and can call it given an instance,
+	 * <li>We can use a (typically blank) instance of this to make more
+	 * instances
+	 * <li>You can cache intermediate results in the object for caching
+	 * </ul>
+	 * 
+	 * @param votes the Votes made by parties in the round to be evaluated.. All
+	 *              active parties in the negotiation must be available, even if
+	 *              they did not vote, to ensure that {@link #isFinished()} can
+	 *              work properly.
+	 * 
+	 * @return new VotingEvaluation object containing the given votes.
+	 */
+	VotingEvaluatorWithValue create(CollectedVotesWithValue votes);
+
+	/**
+	 * 
+	 * @return the agreements that is contained in the current available votes.
+	 *         The exact procedure varies with the implementation.
+	 */
+	Agreements getAgreements();
+
+	/**
+	 * 
+	 * @return true iff the negotiation is finished after taking the agreement
+	 *         returned by {@link #getAgreements()}
+	 */
+	boolean isFinished();
+}
Index: voting/src/main/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValue.java
===================================================================
--- voting/src/main/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValue.java	(revision 30)
+++ voting/src/main/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValue.java	(revision 30)
@@ -0,0 +1,81 @@
+package geniusweb.voting.evaluatorwithvalue;
+
+import java.util.Collections;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import geniusweb.inform.Agreements;
+import geniusweb.voting.CollectedVotesWithValue;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+/**
+ * Take the first possible agreement, and take the largest possible agreement.
+ * Stop after first agreement.
+ */
+public class LargestAgreementWithValue implements VotingEvaluatorWithValue {
+	@JsonIgnore
+	private final CollectedVotesWithValue allVotes;
+
+	// caching
+	@JsonIgnore
+	private Agreements maxAgreements = null;
+
+	@JsonCreator
+	public LargestAgreementWithValue() {
+		this(new CollectedVotesWithValue(Collections.emptyMap(),
+				Collections.emptyMap()));
+	}
+
+	protected LargestAgreementWithValue(CollectedVotesWithValue votes) {
+		this.allVotes = votes;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		if (maxAgreements == null)
+			maxAgreements = new Agreements(allVotes.getMaxAgreements());
+		return maxAgreements;
+	}
+
+	@Override
+	public boolean isFinished() {
+		return getAgreements().getMap().isEmpty();
+	}
+
+	@Override
+	public VotingEvaluatorWithValue create(CollectedVotesWithValue votes) {
+		return new LargestAgreementWithValue(votes);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((allVotes == null) ? 0 : allVotes.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		LargestAgreementWithValue other = (LargestAgreementWithValue) obj;
+		if (allVotes == null) {
+			if (other.allVotes != null)
+				return false;
+		} else if (!allVotes.equals(other.allVotes))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "LargestAgreement";
+	}
+}
Index: voting/src/test/java/geniusweb/voting/CollectedVotesWithValueTest.java
===================================================================
--- voting/src/test/java/geniusweb/voting/CollectedVotesWithValueTest.java	(revision 30)
+++ voting/src/test/java/geniusweb/voting/CollectedVotesWithValueTest.java	(revision 30)
@@ -0,0 +1,372 @@
+package geniusweb.voting;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.issuevalue.Bid;
+import tudelft.utilities.immutablelist.Tuple;
+import tudelft.utilities.junit.GeneralTests;
+
+public class CollectedVotesWithValueTest
+		extends GeneralTests<CollectedVotesWithValue> {
+
+	private final Bid bidA = mock(Bid.class), bidB = mock(Bid.class);
+	private final Bid a = mock(Bid.class), b = mock(Bid.class),
+			c = mock(Bid.class), d = mock(Bid.class);
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+	private final PartyId party4 = new PartyId("party4");
+
+	private final PartyId partyP = new PartyId("partyP");
+	private final PartyId partyQ = new PartyId("partyQ");
+	private final PartyId partyR = new PartyId("partyR");
+	private final PartyId partyS = new PartyId("partyS");
+
+	private final VoteWithValue votePA2 = new VoteWithValue(partyP, bidA, 2, 9,
+			100);
+	private final VoteWithValue voteQA2 = new VoteWithValue(partyQ, bidA, 2, 9,
+			50);
+	private final VoteWithValue voteQB3 = new VoteWithValue(partyQ, bidB, 3, 9,
+			50);
+	private final VoteWithValue voteRA4 = new VoteWithValue(partyR, bidA, 4, 9,
+			50);
+
+	private Map<PartyId, Integer> allpowers = new HashMap<>();
+
+	private CollectedVotesWithValue cv1 = new CollectedVotesWithValue(
+			Collections.emptyMap(), Collections.emptyMap());
+	private CollectedVotesWithValue cv1a = new CollectedVotesWithValue(
+			Collections.emptyMap(), Collections.emptyMap());
+
+	private VotesWithValue votes = new VotesWithValue(partyP,
+			Collections.singleton(votePA2));
+	private CollectedVotesWithValue cv2 = new CollectedVotesWithValue(
+			Collections.singletonMap(partyP, votes),
+			Collections.singletonMap(partyP, 1));
+
+	@Override
+	public List<List<CollectedVotesWithValue>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(cv1, cv1a), Arrays.asList(cv2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("CollectedVotesWithValue.\\{\\}.*\\{\\}.*",
+				"CollectedVotesWithValue.\\{partyP=VotesWithValue.*partyP,.VoteWithValue.partyP,bidA.*,2.*\\}.*\\{partyP=1\\}.*");
+	}
+
+	@Before
+	public void before() {
+		allpowers.put(partyP, 1);
+		allpowers.put(partyQ, 1);
+		allpowers.put(partyR, 1);
+		allpowers.put(partyS, 1);
+
+		when(bidA.toString()).thenReturn("bidA");
+		when(bidB.toString()).thenReturn("bidB");
+
+	}
+
+	@Test
+	public void testGetAllBids() {
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP, Collections.singleton(votePA2)));
+		allvotes.put(partyQ, new VotesWithValue(partyQ,
+				new HashSet<>(Arrays.asList(voteQA2, voteQB3))));
+
+		Map<Bid, Set<VoteWithValue>> allbids = new CollectedVotesWithValue(
+				allvotes, allpowers).getAllBids();
+		assertEquals(2, allbids.size());
+		assertEquals(new HashSet<>(Arrays.asList(votePA2, voteQA2)),
+				allbids.get(bidA));
+		assertEquals(new HashSet<>(Arrays.asList(voteQB3)), allbids.get(bidB));
+	}
+
+	@Test
+	public void getBestCoalitionTest() {
+		// P votes for A, Q votes 50% on A 50% on B.
+		// Only deal is A, 150%
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP, Collections.singleton(votePA2)));
+		allvotes.put(partyQ, new VotesWithValue(partyQ,
+				new HashSet<>(Arrays.asList(voteQA2, voteQB3))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Tuple<Bid, Integer> best = cv
+				.getBestCoalition(new HashSet<>(Arrays.asList(partyP, partyQ)));
+		assertEquals(bidA, best.get1());
+		assertEquals((Integer) 150, best.get2());
+	}
+
+	@Test
+	public void getBestCoalitionTest2() {
+		// P votes 40% A 60$ B, Q votes 50% on A 50% on B.
+		// Only deal is B, 110%
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 9, 40),
+								new VoteWithValue(partyP, bidB, 2, 9, 60)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 50),
+								new VoteWithValue(partyQ, bidB, 2, 9, 50)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Tuple<Bid, Integer> best = cv
+				.getBestCoalition(new HashSet<>(Arrays.asList(partyP, partyQ)));
+		assertEquals(bidB, best.get1());
+		assertEquals((Integer) 110, best.get2());
+	}
+
+	@Test
+	public void getBestCoalitionTest3() {
+		// P votes 50% A 50$ B, Q votes 50% on A 50% on B.
+		// Both deals A and B are now equivalent so both A and B are good
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 9, 50),
+								new VoteWithValue(partyP, bidB, 2, 9, 50)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 50),
+								new VoteWithValue(partyQ, bidB, 2, 9, 50)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Tuple<Bid, Integer> best = cv
+				.getBestCoalition(new HashSet<>(Arrays.asList(partyP, partyQ)));
+		assertEquals((Integer) 100, best.get2());
+	}
+
+	@Test
+	public void getBestCoalitionTest4() {
+		// P votes 60% A 40% B, Q votes 50% on A 50% on B.
+		// R votes 70% A 30% B.
+		// But P does not want to be in a power-3 team.
+		// therefore there is no 3-team coalition.
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 2, 60),
+								new VoteWithValue(partyP, bidB, 2, 2, 40)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 50),
+								new VoteWithValue(partyQ, bidB, 2, 9, 50)))));
+		allvotes.put(partyR,
+				new VotesWithValue(partyR,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyR, bidA, 2, 9, 70),
+								new VoteWithValue(partyR, bidB, 2, 9, 30)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Tuple<Bid, Integer> best = cv.getBestCoalition(
+				new HashSet<>(Arrays.asList(partyP, partyQ, partyR)));
+		assertNull(best);
+	}
+
+	@Test
+	public void getActiveParties() {
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP, new VotesWithValue(partyP, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyP, bidB, 2, 2, 100)))));
+		allvotes.put(partyQ, new VotesWithValue(partyQ, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyQ, bidB, 2, 9, 100)))));
+		allvotes.put(partyR, new VotesWithValue(partyR, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyR, bidB, 2, 9, 100)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+
+		List<PartyId> parties = cv.getParties();
+		assertEquals(Collections.emptySet(), cv.getActiveParties(0));
+		assertEquals(new HashSet<>(Arrays.asList(parties.get(0))),
+				cv.getActiveParties(1));
+		assertEquals(new HashSet<>(Arrays.asList(parties.get(1))),
+				cv.getActiveParties(2));
+		assertEquals(new HashSet<>(Arrays.asList(parties.get(2))),
+				cv.getActiveParties(4));
+		assertEquals(
+				new HashSet<>(Arrays.asList(parties.get(2), parties.get(0))),
+				cv.getActiveParties(5));
+	}
+
+	@Test
+	public void getPartiesTest() {
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP, new VotesWithValue(partyP, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyP, bidB, 2, 2, 100)))));
+		allvotes.put(partyQ, new VotesWithValue(partyQ, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyQ, bidB, 2, 9, 100)))));
+		allvotes.put(partyR, new VotesWithValue(partyR, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyR, bidB, 2, 9, 100)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+
+		List<PartyId> parties = cv.getParties();
+		assertEquals(
+				new HashSet<>(Arrays.asList(parties.get(2), parties.get(0))),
+				cv.getParties(new int[] { 1, 3 }));
+
+	}
+
+	@Test
+	public void getCoalitionValuesTest() {
+		// P votes 60% A 40% B, Q votes 50% on A 50% on B.
+		// R votes 70% A 30% B.
+		// But P does not want to be in a power-3 team.
+		// therefore there is no 3-team coalition.
+		// the max 2-team coalition is with P anyway: P+R with bid A, 130.
+
+		// use LinkedHashMap to ensure order of parties
+		// remains P,Q,R in the result.
+		Map<PartyId, VotesWithValue> allvotes = new LinkedHashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 2, 60),
+								new VoteWithValue(partyP, bidB, 2, 2, 40)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 50),
+								new VoteWithValue(partyQ, bidB, 2, 9, 50)))));
+		allvotes.put(partyR,
+				new VotesWithValue(partyR,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyR, bidA, 2, 9, 70),
+								new VoteWithValue(partyR, bidB, 2, 9, 30)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		assertArrayEquals(new double[] { 0d, 0d, 0d, 110d, 0d, 130, 120, 0d },
+				cv.getCoalitionValues(), .0001);
+
+	}
+
+	@Test
+	public void getMaxAgreementsTest() {
+		// P votes 60% A 40% B, Q votes 50% on A 50% on B.
+		// R votes 70% A 30% B.
+		// But P does not want to be in a power-3 team.
+		// therefore there is no 3-team coalition.
+		// the max 2-team coalition is with P anyway: P+R with bid A, 130.
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 2, 60),
+								new VoteWithValue(partyP, bidB, 2, 2, 40)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 50),
+								new VoteWithValue(partyQ, bidB, 2, 9, 50)))));
+		allvotes.put(partyR,
+				new VotesWithValue(partyR,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyR, bidA, 2, 9, 70),
+								new VoteWithValue(partyR, bidB, 2, 9, 30)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Map<PartyId, Bid> best = cv.getMaxAgreements();
+
+		assertEquals(new HashSet<>(Arrays.asList(partyP, partyR)),
+				best.keySet());
+		assertEquals(bidA, best.get(partyP));
+		assertEquals(bidA, best.get(partyR));
+	}
+
+	@Test
+	public void getMaxAgreementsTest2() {
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP,
+				new VotesWithValue(partyP,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyP, bidA, 2, 2, 30),
+								new VoteWithValue(partyP, bidB, 2, 3, 70)))));
+		allvotes.put(partyQ,
+				new VotesWithValue(partyQ,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyQ, bidA, 2, 9, 30),
+								new VoteWithValue(partyQ, bidB, 2, 9, 70)))));
+		allvotes.put(partyR,
+				new VotesWithValue(partyR,
+						new HashSet<>(Arrays.asList(
+								new VoteWithValue(partyR, bidA, 2, 9, 30),
+								new VoteWithValue(partyR, bidB, 2, 9, 70)))));
+
+		allvotes.put(partyS, new VotesWithValue(partyS, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyS, bidA, 2, 9, 100)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Map<PartyId, Bid> best = cv.getMaxAgreements();
+		assertEquals(
+				new HashSet<>(Arrays.asList(partyP, partyQ, partyR, partyS)),
+				best.keySet());
+		assertEquals(bidB, best.get(partyP));
+		assertEquals(bidB, best.get(partyQ));
+		assertEquals(bidA, best.get(partyR));
+		assertEquals(bidA, best.get(partyS));
+	}
+
+	@Test
+	public void getMaxAgreementsTest3() {
+		// all vote on bid A, but partyP wants no more than 2 in the coalition.
+		// this deal can still be reached, bu placing partyP,Q in one coalition
+		// and R and S in another coalition.
+		Map<PartyId, VotesWithValue> allvotes = new HashMap<>();
+		allvotes.put(partyP, new VotesWithValue(partyP, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyP, bidA, 2, 2, 100)))));
+		allvotes.put(partyQ, new VotesWithValue(partyQ, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyQ, bidA, 2, 9, 100)))));
+		allvotes.put(partyR, new VotesWithValue(partyR, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyR, bidA, 2, 3, 100)))));
+
+		allvotes.put(partyS, new VotesWithValue(partyS, new HashSet<>(
+				Arrays.asList(new VoteWithValue(partyS, bidA, 2, 9, 100)))));
+
+		CollectedVotesWithValue cv = new CollectedVotesWithValue(allvotes,
+				allpowers);
+		Map<PartyId, Bid> best = cv.getMaxAgreements();
+		assertEquals(
+				new HashSet<>(Arrays.asList(partyP, partyQ, partyR, partyS)),
+				best.keySet());
+	}
+
+}
Index: voting/src/test/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValueTest.java
===================================================================
--- voting/src/test/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValueTest.java	(revision 30)
+++ voting/src/test/java/geniusweb/voting/evaluatorwithvalue/LargestAgreementWithValueTest.java	(revision 30)
@@ -0,0 +1,119 @@
+package geniusweb.voting.evaluatorwithvalue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.voting.CollectedVotesWithValue;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class LargestAgreementWithValueTest
+		extends GeneralTests<LargestAgreementWithValue> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private LargestAgreementWithValue lal1 = new LargestAgreementWithValue();
+	private LargestAgreementWithValue lal1a = new LargestAgreementWithValue();
+	private PartyId party1 = new PartyId("party1");
+	private PartyId party2 = new PartyId("party2");
+	private PartyId party3 = new PartyId("party3");
+	private PartyId party4 = new PartyId("party4");
+	private VotesWithValue votes1 = mock(VotesWithValue.class);
+	private LargestAgreementWithValue lal2 = new LargestAgreementWithValue(
+			new CollectedVotesWithValue(
+					Collections.singletonMap(party1, votes1),
+					Collections.singletonMap(party1, 1)));
+
+	private String lalstring = "{\"LargestAgreementWithValue\":{}}";
+
+	@Override
+	public List<List<LargestAgreementWithValue>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(lal1, lal1a), Arrays.asList(lal2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("LargestAgreement", "LargestAgreement");
+	}
+
+	@Test
+	public void serializeTest() throws JsonProcessingException {
+		// we use lal2 to also check we don't serialize the contents
+		System.out.println(jackson.writeValueAsString(lal2));
+		assertEquals(lalstring, jackson.writeValueAsString(lal2));
+	}
+
+	@Test
+	public void deserializeTest() throws IOException {
+		VotingEvaluatorWithValue eval = jackson.readValue(lalstring,
+				VotingEvaluatorWithValue.class);
+		// it should deserialize as the empty lal1 because we ignore the fields.
+		assertEquals(lal1, eval);
+	}
+
+	@Test
+	public void testCollectVotes() {
+		Bid bid1 = mock(Bid.class);
+		when(bid1.toString()).thenReturn("bid1");
+		Bid bid2 = mock(Bid.class);
+		when(bid2.toString()).thenReturn("bid2");
+
+		VotesWithValue votes1 = new VotesWithValue(party1,
+				new HashSet<>(Arrays.asList(
+						new VoteWithValue(party1, bid1, 2, 99, 40),
+						new VoteWithValue(party1, bid2, 2, 99, 60))));
+		VotesWithValue votes2 = new VotesWithValue(party2, new HashSet<>(
+				Arrays.asList(new VoteWithValue(party2, bid1, 2, 99, 100))));
+		VotesWithValue votes3 = new VotesWithValue(party3, new HashSet<>(
+				Arrays.asList(new VoteWithValue(party3, bid1, 2, 99, 100))));
+		VotesWithValue votes4 = new VotesWithValue(party4, new HashSet<>(
+				Arrays.asList(new VoteWithValue(party4, bid1, 2, 99, 100))));
+		HashMap<PartyId, VotesWithValue> votesmap = new HashMap<>();
+		votesmap.put(party1, votes1);
+		votesmap.put(party2, votes2);
+		votesmap.put(party3, votes3);
+		votesmap.put(party4, votes4);
+		HashMap<PartyId, Integer> powermap = new HashMap<>();
+		powermap.put(party1, 1);
+		powermap.put(party2, 1);
+		powermap.put(party3, 1);
+		powermap.put(party4, 4);
+
+		CollectedVotesWithValue votes = new CollectedVotesWithValue(votesmap,
+				powermap);
+
+		LargestAgreementWithValue la = new LargestAgreementWithValue(votes);
+		Agreements agreement = la.getAgreements();
+		assertEquals(
+				new HashSet<PartyId>(
+						Arrays.asList(party1, party2, party3, party4)),
+				agreement.getMap().keySet());
+	}
+
+	@Test
+	public void testEmptyCollectVotes() {
+
+		CollectedVotesWithValue votes = new CollectedVotesWithValue(
+				Collections.emptyMap(), Collections.emptyMap());
+
+		LargestAgreementWithValue la = new LargestAgreementWithValue(votes);
+		Agreements agreement = la.getAgreements();
+	}
+}
