Index: /bidspace/pom.xml
===================================================================
--- /bidspace/pom.xml	(revision 29)
+++ /bidspace/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>bidspace</artifactId>
-	<version>1.5.8</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.9</version> <!-- must equal ${geniusweb.version} -->
 	<packaging>jar</packaging>
 
@@ -16,5 +16,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>
 
@@ -43,5 +43,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -104,5 +104,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -119,13 +119,18 @@
 			</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>
+
 
 
Index: /boa/pom.xml
===================================================================
--- /boa/pom.xml	(revision 29)
+++ /boa/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>boa</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>
 
@@ -68,5 +68,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -100,8 +100,14 @@
 	<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>
+
 
 
Index: /boa/src/test/resources/testprofile.json
===================================================================
--- /boa/src/test/resources/testprofile.json	(revision 29)
+++ /boa/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /collectparties.sh
===================================================================
--- /collectparties.sh	(revision 29)
+++ /collectparties.sh	(revision 30)
@@ -7,5 +7,5 @@
 rm -rf collectedparties
 mkdir collectedparties
-VERSION=1.5.8
+VERSION=1.5.9
 
 cp "exampleparties/anac2019/agentgg/target/agentgg-${VERSION}-jar-with-dependencies.jar" collectedparties
Index: /events/pom.xml
===================================================================
--- /events/pom.xml	(revision 29)
+++ /events/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>events</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>
 
@@ -113,8 +113,7 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
-
 	</dependencies>
 
@@ -122,8 +121,14 @@
 	<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>
+
 
 
Index: /events/src/main/java/geniusweb/actions/Action.java
===================================================================
--- /events/src/main/java/geniusweb/actions/Action.java	(revision 29)
+++ /events/src/main/java/geniusweb/actions/Action.java	(revision 30)
@@ -18,5 +18,7 @@
 		@JsonSubTypes.Type(value = ElicitComparison.class),
 		@JsonSubTypes.Type(value = Vote.class),
-		@JsonSubTypes.Type(value = Votes.class) })
+		@JsonSubTypes.Type(value = Votes.class),
+		@JsonSubTypes.Type(value = VoteWithValue.class),
+		@JsonSubTypes.Type(value = VotesWithValue.class) })
 public interface Action {
 	/**
Index: /events/src/main/java/geniusweb/actions/VoteWithValue.java
===================================================================
--- /events/src/main/java/geniusweb/actions/VoteWithValue.java	(revision 30)
+++ /events/src/main/java/geniusweb/actions/VoteWithValue.java	(revision 30)
@@ -0,0 +1,127 @@
+package geniusweb.actions;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+
+import geniusweb.issuevalue.Bid;
+
+/**
+ * A vote is an indication by a party that it conditionally accepts a bid. It's
+ * then up to the protocol to determine the outcome.
+ */
+@JsonTypeName("votewithvalue")
+public class VoteWithValue extends ActionWithBid {
+	private final Integer minPower;
+	private final Integer maxPower;
+	private final Integer value;
+
+	/**
+	 * @param id       the {@link PartyId} that does the action
+	 * @param bid      the bid that is voted on
+	 * @param minPower the minimum power this bid must get in order for the vote
+	 *                 to be valid. Power is the sum of the powers of the
+	 *                 parties that are in the deal. If power=1 for all
+	 *                 participants (usually the default) power can be
+	 *                 interpreted as number of votes
+	 * @param maxPower the maximum power this bid must get in order for the vote
+	 *                 to be valid. See {@link #minPower}
+	 * @param value    the value for this vote. An integer in [1,100]. Higher
+	 *                 means that the actor likes this bid better. Note: value 0
+	 *                 is reserved for "unacceptable" and therefore can not be
+	 *                 used as a valid value.
+	 */
+	@JsonCreator
+	public VoteWithValue(@JsonProperty("actor") PartyId id,
+			@JsonProperty("bid") Bid bid,
+			@JsonProperty("minPower") Integer minPower,
+			@JsonProperty("maxPower") Integer maxPower,
+			@JsonProperty("value") Integer value) {
+		super(id, bid);
+		this.minPower = minPower;
+		this.maxPower = maxPower;
+		this.value = value;
+		if (value < 1 || value > 100)
+			throw new IllegalArgumentException(
+					"value must be in [1,100] but got " + value);
+		if (bid == null || minPower == null || minPower < 1 || maxPower == null
+				|| maxPower < minPower) {
+			throw new IllegalArgumentException(
+					"Vote must have non-null bid and minVotes, and minPower must be >=1 and maxPower must be >=minPower");
+		}
+	}
+
+	/**
+	 * 
+	 * @return the minimum power this bid must get in order for the vote to be
+	 *         valid.
+	 */
+	public Integer getMinPower() {
+		return minPower;
+	}
+
+	/**
+	 * 
+	 * @return the max power this bid must get in order for the vote to be
+	 *         valid.
+	 */
+	public Integer getMaxPower() {
+		return maxPower;
+	}
+
+	/**
+	 * 
+	 * @return the value for this vote. An integer in [1,100]. Higher means that
+	 *         the actor likes this bid better.
+	 * 
+	 */
+	public Integer getValue() {
+		return value;
+	}
+
+	@Override
+	public String toString() {
+		return "VoteWithValue[" + getActor() + "," + getBid() + "," + minPower
+				+ "," + maxPower + "," + value + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result
+				+ ((maxPower == null) ? 0 : maxPower.hashCode());
+		result = prime * result
+				+ ((minPower == null) ? 0 : minPower.hashCode());
+		result = prime * result + ((value == null) ? 0 : value.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		VoteWithValue other = (VoteWithValue) obj;
+		if (maxPower == null) {
+			if (other.maxPower != null)
+				return false;
+		} else if (!maxPower.equals(other.maxPower))
+			return false;
+		if (minPower == null) {
+			if (other.minPower != null)
+				return false;
+		} else if (!minPower.equals(other.minPower))
+			return false;
+		if (value == null) {
+			if (other.value != null)
+				return false;
+		} else if (!value.equals(other.value))
+			return false;
+		return true;
+	}
+
+}
Index: /events/src/main/java/geniusweb/actions/VotesWithValue.java
===================================================================
--- /events/src/main/java/geniusweb/actions/VotesWithValue.java	(revision 30)
+++ /events/src/main/java/geniusweb/actions/VotesWithValue.java	(revision 30)
@@ -0,0 +1,125 @@
+package geniusweb.actions;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.issuevalue.Bid;
+
+/**
+ * Indicates that a party conditionally agrees with any of the
+ * {@link VoteWithValue}s provided.
+ */
+public class VotesWithValue extends AbstractAction {
+	private final Set<VoteWithValue> votes;
+
+	/**
+	 * 
+	 * @param id    party id
+	 * @param votes the {@link Vote}s that the party can agree on, if the
+	 *              condition of the Vote holds. Every {@link Vote#getActor()}
+	 *              should equal the id. The sum of the values must =100.
+	 */
+	@JsonCreator
+	public VotesWithValue(@JsonProperty("actor") PartyId id,
+			@JsonProperty("votes") Set<VoteWithValue> votes) {
+		super(id);
+		this.votes = votes;
+		if (votes == null)
+			throw new NullPointerException("votes must be not null");
+		for (VoteWithValue vote : votes) {
+			if (!vote.getActor().equals(id)) {
+				throw new IllegalArgumentException("All votes must come from "
+						+ id + " but found " + vote);
+			}
+		}
+		if (votes.stream().map(v -> v.getValue()).reduce(0,
+				Integer::sum) != 100)
+			throw new IllegalArgumentException(
+					"Sum of the placed votes must be 100");
+		// check for duplicate Vote's, possibly with different powers
+		Map<Bid, Long> counts = votes.stream().map(vote -> vote.getBid())
+				.collect(Collectors.groupingBy(bid -> bid,
+						Collectors.counting()));
+		Optional<Entry<Bid, Long>> nonunique = counts.entrySet().stream()
+				.filter(entry -> entry.getValue() > 1).findAny();
+		if (nonunique.isPresent())
+			throw new IllegalArgumentException(
+					"Votes contains multiple Vote's for "
+							+ nonunique.get().getKey());
+	}
+
+	/**
+	 * Test if Votes extends other votes. Extending means that for each vote on
+	 * bid B with power P in othervotes, this contains also a vote for bid B
+	 * with power at most P.
+	 * 
+	 * @param otherVotes the {@link VotesWithValue}, usually from a previous
+	 *                   round, that this should extend.
+	 * @return true iff this extends the otherVotes.
+	 */
+	public boolean isExtending(VotesWithValue otherVotes) {
+		if (!otherVotes.getActor().equals(getActor()))
+			return false;
+		for (VoteWithValue vote : otherVotes.getVotes()) {
+			VoteWithValue myvote = getVote(vote.getBid());
+			if (myvote == null || myvote.getMinPower() > vote.getMinPower()
+					|| myvote.getMaxPower() < vote.getMaxPower())
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * 
+	 * @param bid the bid that we may have a vote for
+	 * @return myvote for bid, or null if no vote for that bid;
+	 */
+	public VoteWithValue getVote(Bid bid) {
+		for (VoteWithValue vote : votes) {
+			if (vote.getBid().equals(bid))
+				return vote;
+		}
+		return null;
+	}
+
+	public Set<VoteWithValue> getVotes() {
+		return Collections.unmodifiableSet(votes);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + ((votes == null) ? 0 : votes.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		VotesWithValue other = (VotesWithValue) obj;
+		if (votes == null) {
+			if (other.votes != null)
+				return false;
+		} else if (!votes.equals(other.votes))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "VotesWithValue[" + getActor() + "," + votes + "]";
+	}
+}
Index: /events/src/main/java/geniusweb/inform/Agreements.java
===================================================================
--- /events/src/main/java/geniusweb/inform/Agreements.java	(revision 29)
+++ /events/src/main/java/geniusweb/inform/Agreements.java	(revision 30)
@@ -30,5 +30,5 @@
 
 	@JsonCreator
-	protected Agreements(Map<PartyId, Bid> agrees) {
+	public Agreements(Map<PartyId, Bid> agrees) {
 		this.agreements = agrees;
 	}
Index: /events/src/main/java/geniusweb/inform/Inform.java
===================================================================
--- /events/src/main/java/geniusweb/inform/Inform.java	(revision 29)
+++ /events/src/main/java/geniusweb/inform/Inform.java	(revision 30)
@@ -16,5 +16,6 @@
 		@JsonSubTypes.Type(value = YourTurn.class),
 		@JsonSubTypes.Type(value = Voting.class),
-		@JsonSubTypes.Type(value = OptIn.class) })
+		@JsonSubTypes.Type(value = OptIn.class),
+		@JsonSubTypes.Type(value = OptInWithValue.class) })
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
 public interface Inform {
Index: /events/src/main/java/geniusweb/inform/OptInWithValue.java
===================================================================
--- /events/src/main/java/geniusweb/inform/OptInWithValue.java	(revision 30)
+++ /events/src/main/java/geniusweb/inform/OptInWithValue.java	(revision 30)
@@ -0,0 +1,79 @@
+package geniusweb.inform;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VotesWithValue;
+
+/**
+ * Informs party that it's time to Opt-in.
+ */
+public class OptInWithValue implements Inform {
+
+	private final List<VotesWithValue> votes;
+
+	/**
+	 * @param votes a list of votes. There may be only one votes per party.
+	 */
+	@JsonCreator
+	public OptInWithValue(@JsonProperty("votes") List<VotesWithValue> votes) {
+		this.votes = votes;
+
+		Map<PartyId, Long> counts = votes.stream().map(vote -> vote.getActor())
+				.collect(Collectors.groupingBy(actor -> actor,
+						Collectors.counting()));
+		Optional<Entry<PartyId, Long>> nonunique = counts.entrySet().stream()
+				.filter(entry -> entry.getValue() > 1).findAny();
+		if (nonunique.isPresent())
+			throw new IllegalArgumentException(
+					"OptIn contains multiple Votes for party "
+							+ nonunique.get().getKey());
+
+	}
+
+	/**
+	 * @return list of votes that can be opted in to
+	 */
+	public List<VotesWithValue> getVotes() {
+		return Collections.unmodifiableList(votes);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((votes == null) ? 0 : votes.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;
+		OptInWithValue other = (OptInWithValue) obj;
+		if (votes == null) {
+			if (other.votes != null)
+				return false;
+		} else if (!votes.equals(other.votes))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "OptInWithValue[" + votes + "]";
+	}
+
+}
Index: /events/src/test/java/geniusweb/actions/VoteWithValueTest.java
===================================================================
--- /events/src/test/java/geniusweb/actions/VoteWithValueTest.java	(revision 30)
+++ /events/src/test/java/geniusweb/actions/VoteWithValueTest.java	(revision 30)
@@ -0,0 +1,99 @@
+package geniusweb.actions;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.NumberValue;
+import geniusweb.issuevalue.Value;
+import tudelft.utilities.junit.GeneralTests;
+
+public class VoteWithValueTest extends GeneralTests<VoteWithValue> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final PartyId id = new PartyId("party1");
+	private static final PartyId id2 = new PartyId("party2");
+	private final static Map<String, Value> issuevalues = new HashMap<String, Value>();
+	private final static Map<String, Value> issuevaluesb = new HashMap<String, Value>();
+	private static Bid bid;
+	private static Bid bidb;
+	private final static String ISSUE1 = "issue1";
+	private final static Value VALUE1 = new DiscreteValue("value1");
+	private final static String ISSUE2 = "issue2";
+	private final static Value VALUE2 = new NumberValue("10");
+	// issue 2 is NUMBER and thus serializes with leading '='
+	private final String votestring = "{\"votewithvalue\":{\"actor\":\"party1\",\"bid\":{\"issuevalues\":{\"issue2\":10,\"issue1\":\"value1\"}},\"minPower\":1,\"maxPower\":2,\"value\":10}}";
+
+	private static VoteWithValue vote1, vote1a, vote2, vote3, vote4, vote5,
+			vote6;
+
+	static {
+		issuevalues.put(ISSUE1, VALUE1);
+		issuevalues.put(ISSUE2, VALUE2);
+		bid = new Bid(issuevalues);
+		vote1 = new VoteWithValue(id, bid, 1, 2, 10);
+		vote1a = new VoteWithValue(id, bid, 1, 2, 10);
+
+		vote2 = new VoteWithValue(id2, bid, 1, 2, 10);
+
+		// values swapped, so different issuevalues.
+		issuevaluesb.put(ISSUE1, VALUE2);
+		issuevaluesb.put(ISSUE2, VALUE2);
+		bidb = new Bid(issuevaluesb);
+		vote3 = new VoteWithValue(id, bidb, 1, 2, 10);
+
+		vote4 = new VoteWithValue(id, bid, 2, 2, 10);
+		vote5 = new VoteWithValue(id, bid, 1, 3, 10);
+
+		vote6 = new VoteWithValue(id, bid, 1, 2, 20);
+
+	}
+
+	@Override
+	public List<List<VoteWithValue>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(vote1, vote1a), Arrays.asList(vote2),
+				Arrays.asList(vote3), Arrays.asList(vote4),
+				Arrays.asList(vote5), Arrays.asList(vote6));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"VoteWithValue.*party1.*issue2=10.*issue1=.value1.*1.*2.*10.*",
+				"VoteWithValue.*party2.*issue2=10.*issue1=.value1.*1.*2.*10.*",
+				"VoteWithValue.*party1.*issue2=10.*issue1=10.*1.*2.*10.*",
+				"VoteWithValue.*party1.*issue2=10.*issue1=.value1.*2.*2.*10.*",
+				"VoteWithValue.*party1.*issue2=10.*issue1=.value1.*1.*3.*10.*",
+				"VoteWithValue.*party1.*issue2=10.*issue1=.value1.*1.*2.*20.*");
+	}
+
+	@Before
+	public void before() {
+
+	}
+
+	@Test
+	public void serializeTest() throws JsonProcessingException {
+		System.out.println(jackson.writeValueAsString(vote1));
+		assertEquals(votestring, jackson.writeValueAsString(vote1));
+	}
+
+	@Test
+	public void deserializeAcceptTest() throws IOException {
+		Action act = jackson.readValue(votestring, Action.class);
+		assertEquals(vote1, act);
+	}
+
+}
Index: /events/src/test/java/geniusweb/actions/VotesWithValueTest.java
===================================================================
--- /events/src/test/java/geniusweb/actions/VotesWithValueTest.java	(revision 30)
+++ /events/src/test/java/geniusweb/actions/VotesWithValueTest.java	(revision 30)
@@ -0,0 +1,149 @@
+package geniusweb.actions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+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.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class VotesWithValueTest extends GeneralTests<VotesWithValue> {
+	private final ObjectMapper jackson = new ObjectMapper();
+	private final String votestring = "{\"VotesWithValue\":{\"actor\":\"partyA\",\"votes\":[{\"votewithvalue\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"is1\":\"val1\"}},\"minPower\":2,\"maxPower\":9,\"value\":30}},{\"votewithvalue\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"is1\":\"val2\"}},\"minPower\":2,\"maxPower\":9,\"value\":70}}]}}";
+
+	private PartyId partyA = new PartyId("partyA");
+	private PartyId partyB = new PartyId("partyB");
+	private Bid bid1 = new Bid("is1", new DiscreteValue("val1")),
+			bid2 = new Bid("is1", new DiscreteValue("val2"));
+	private VoteWithValue voteA1 = new VoteWithValue(partyA, bid1, 2, 9, 30);
+	private VoteWithValue voteA2 = new VoteWithValue(partyA, bid2, 2, 9, 70);
+	private VoteWithValue voteA1_100 = new VoteWithValue(partyA, bid1, 2, 9,
+			100);
+	private VoteWithValue voteA2_100 = new VoteWithValue(partyA, bid2, 2, 9,
+			100);
+	private VoteWithValue voteB1 = new VoteWithValue(partyB, bid1, 2, 9, 40);
+	private VoteWithValue voteB2 = new VoteWithValue(partyB, bid2, 2, 9, 60);
+
+	private VotesWithValue votes1 = new VotesWithValue(partyA,
+			new HashSet<>(Arrays.asList(voteA1, voteA2)));
+	private VotesWithValue votes1a = new VotesWithValue(partyA,
+			new HashSet<>(Arrays.asList(voteA1, voteA2)));
+	private VotesWithValue votes2 = new VotesWithValue(partyB,
+			new HashSet<>(Arrays.asList(voteB1, voteB2)));
+
+	@Override
+	public List<List<VotesWithValue>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(votes1, votes1a),
+				Arrays.asList(votes2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"Votes.*Vote.*partyA,Bid.*is1.*val1.*2.*Vote.*partyA,Bid.*is1.*val2.*2.*",
+				"Votes.*Vote.*partyB,Bid.*is1.*val2.*2.*Vote.*partyB,Bid.*is1.*val1.*2.*");
+	}
+
+	@SuppressWarnings("unused")
+	@Test(expected = IllegalArgumentException.class)
+	public void testRejectVoteWithIncorrectParty() {
+		new VotesWithValue(partyA, Collections.singleton(voteB1));
+	}
+
+	@Test
+	public void serializeTest() throws JsonProcessingException {
+		System.out.println(jackson.writeValueAsString(votes1));
+		assertEquals(votestring, jackson.writeValueAsString(votes1));
+	}
+
+	@Test
+	public void deserializeTest() throws IOException {
+		Action act = jackson.readValue(votestring, Action.class);
+		assertEquals(votes1, act);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWrongPartyName() {
+		new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(voteB1, voteB2)));
+	}
+
+	@Test
+	public void isExtendingTestExtraVote() {
+		VotesWithValue otherVotes = new VotesWithValue(partyA,
+				Collections.singleton(voteA1_100));
+		assertTrue(votes1.isExtending(otherVotes));
+	}
+
+	@Test
+	public void isExtendingTestMissing() {
+		VotesWithValue otherVotes = new VotesWithValue(partyA,
+				Collections.singleton(voteA1_100));
+		assertFalse(otherVotes.isExtending(votes1));
+	}
+
+	@Test
+	public void isExtendingIdenticalVotes() {
+		assertTrue(votes1.isExtending(votes1));
+	}
+
+	@Test
+	public void isReallyExtendingVotesMin() {
+		VoteWithValue powervoteA1 = new VoteWithValue(partyA, bid1, 1, 9, 30);
+		VotesWithValue powerVotes = new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(powervoteA1, voteA2)));
+		assertTrue(powerVotes.isExtending(votes1));
+	}
+
+	@Test
+	public void isReallyExtendingVotesMax() {
+		VoteWithValue powervoteA1 = new VoteWithValue(partyA, bid1, 2, 10, 30);
+		VotesWithValue powerVotes = new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(powervoteA1, voteA2)));
+		assertTrue(powerVotes.isExtending(votes1));
+	}
+
+	@Test
+	public void IsNarrowingOneVoteRemoved() {
+		VotesWithValue narrowVotes = new VotesWithValue(partyA,
+				Collections.singleton(voteA2_100));
+		assertFalse(narrowVotes.isExtending(votes1));
+	}
+
+	@Test
+	public void isNarrowingTestMoreMinPower() {
+		VoteWithValue narrowVoteA1 = new VoteWithValue(partyA, bid1, 3, 9, 30);
+		VotesWithValue narrowVotes = new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(narrowVoteA1, voteA2)));
+		assertFalse(narrowVotes.isExtending(votes1));
+		assertTrue(votes1.isExtending(narrowVotes));
+	}
+
+	@Test
+	public void isNarrowingLessMaxPower() {
+		VoteWithValue powervoteA1 = new VoteWithValue(partyA, bid1, 2, 8, 30);
+		VotesWithValue narrowVotes = new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(powervoteA1, voteA2)));
+		assertFalse(narrowVotes.isExtending(votes1));
+		assertTrue(votes1.isExtending(narrowVotes));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testDuplicateBidsNotAllowed() {
+		VoteWithValue voteA1similar = new VoteWithValue(partyA, bid1, 3, 9, 30);
+		VotesWithValue votes = new VotesWithValue(partyA,
+				new HashSet<>(Arrays.asList(voteA1, voteA1similar)));
+	}
+}
Index: /events/src/test/java/geniusweb/inform/OptInWithValueTest.java
===================================================================
--- /events/src/test/java/geniusweb/inform/OptInWithValueTest.java	(revision 30)
+++ /events/src/test/java/geniusweb/inform/OptInWithValueTest.java	(revision 30)
@@ -0,0 +1,84 @@
+package geniusweb.inform;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class OptInWithValueTest extends GeneralTests<OptInWithValue> {
+
+	private Bid bid1 = new Bid("iss", new DiscreteValue("val1"));
+	private Bid bid2 = new Bid("iss", new DiscreteValue("val2"));
+	private PartyId partyA = new PartyId("partyA");
+	private PartyId partyB = new PartyId("partyB");
+
+	private final VoteWithValue voteA1 = new VoteWithValue(partyA, bid1, 2, 9,
+			100);
+	private final VoteWithValue voteB1 = new VoteWithValue(partyB, bid1, 2, 9,
+			60);
+	private final VoteWithValue voteB2 = new VoteWithValue(partyB, bid2, 2, 9,
+			40);
+
+	private VotesWithValue votesA = new VotesWithValue(partyA,
+			Collections.singleton(voteA1));
+	private VotesWithValue votesB = new VotesWithValue(partyB,
+			new HashSet<>(Arrays.asList(voteB1, voteB2)));
+
+	private final OptInWithValue optIn1 = new OptInWithValue(
+			Arrays.asList(votesA, votesB));
+	private final OptInWithValue optIn1a = new OptInWithValue(
+			Arrays.asList(votesA, votesB));
+	private final OptInWithValue optIn2 = new OptInWithValue(
+			Arrays.asList(votesA));
+
+	private String asJson = "{\"OptInWithValue\":{\"votes\":[{\"VotesWithValue\":{\"actor\":\"partyA\",\"votes\":[{\"votewithvalue\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9,\"value\":100}}]}},{\"VotesWithValue\":{\"actor\":\"partyB\",\"votes\":[{\"votewithvalue\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9,\"value\":60}},{\"votewithvalue\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val2\"}},\"minPower\":2,\"maxPower\":9,\"value\":40}}]}}]}}";
+
+	@Override
+	public List<List<OptInWithValue>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(optIn1, optIn1a),
+				Arrays.asList(optIn2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"OptInWithValue.*VotesWithValue.*partyA.*VoteWithValue.*partyA.*iss.*val1.*2.*VotesWithValue.*partyB.*VoteWithValue.*partyB.*iss.*val1.*.*2.*VoteWithValue.*partyB.*iss.*val2.*2.*",
+				"OptIn.*Votes.*Vote.*partyA.*iss.*val.*2.*");
+	}
+
+	@Test
+	public void testSerialize() throws JsonProcessingException {
+		ObjectMapper jackson = new ObjectMapper();
+
+		String json = jackson.writeValueAsString(optIn1);
+		System.out.println(json);
+		assertEquals(asJson, json);
+	}
+
+	@Test
+	public void testDeserialize()
+			throws JsonParseException, JsonMappingException, IOException {
+		ObjectMapper jackson = new ObjectMapper();
+		Inform p = jackson.readValue(asJson, Inform.class);
+		System.out.println(p);
+		assertEquals(optIn1, p);
+	}
+
+}
Index: /exampleparties/anac2019/agentgg/pom.xml
===================================================================
--- /exampleparties/anac2019/agentgg/pom.xml	(revision 29)
+++ /exampleparties/anac2019/agentgg/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties.anac2019</groupId>
 	<artifactId>agentgg</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -55,10 +55,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -125,5 +125,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -145,9 +145,13 @@
 	<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: /exampleparties/anac2019/agentgg/src/test/resources/jobs1partial20.json
===================================================================
--- /exampleparties/anac2019/agentgg/src/test/resources/jobs1partial20.json	(revision 29)
+++ /exampleparties/anac2019/agentgg/src/test/resources/jobs1partial20.json	(revision 30)
@@ -55,5 +55,5 @@
 				"career development opportunities": "medium",
 				"fte": "0.8",
-				"salary": "300",
+				"salary": "3000",
 				"work from home": "0"
 			}
Index: /exampleparties/anac2019/winkyagent/pom.xml
===================================================================
--- /exampleparties/anac2019/winkyagent/pom.xml	(revision 29)
+++ /exampleparties/anac2019/winkyagent/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties.anac2019</groupId>
 	<artifactId>winkyagent</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -55,10 +55,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -125,5 +125,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -140,14 +140,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: /exampleparties/anac2019/winkyagent/src/test/resources/jobs1partial20.json
===================================================================
--- /exampleparties/anac2019/winkyagent/src/test/resources/jobs1partial20.json	(revision 29)
+++ /exampleparties/anac2019/winkyagent/src/test/resources/jobs1partial20.json	(revision 30)
@@ -55,5 +55,5 @@
 				"career development opportunities": "medium",
 				"fte": "0.8",
-				"salary": "300",
+				"salary": "3000",
 				"work from home": "0"
 			}
Index: /exampleparties/boulware/pom.xml
===================================================================
--- /exampleparties/boulware/pom.xml	(revision 29)
+++ /exampleparties/boulware/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>boulware</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>
 
@@ -39,5 +39,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -53,13 +53,18 @@
 			</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>
+
 
 
Index: /exampleparties/comparebids/pom.xml
===================================================================
--- /exampleparties/comparebids/pom.xml	(revision 29)
+++ /exampleparties/comparebids/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>comparebids</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -50,10 +50,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -120,5 +120,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -135,13 +135,18 @@
 			</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>
+
 
 
Index: /exampleparties/comparebids/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/comparebids/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/comparebids/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /exampleparties/conceder/pom.xml
===================================================================
--- /exampleparties/conceder/pom.xml	(revision 29)
+++ /exampleparties/conceder/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>conceder</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>
 
@@ -39,5 +39,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -53,13 +53,18 @@
 			</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>
+
 
 
Index: /exampleparties/hardliner/pom.xml
===================================================================
--- /exampleparties/hardliner/pom.xml	(revision 29)
+++ /exampleparties/hardliner/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>hardliner</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>
 
@@ -39,5 +39,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -53,13 +53,18 @@
 			</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>
+
 
 
Index: /exampleparties/humangui/pom.xml
===================================================================
--- /exampleparties/humangui/pom.xml	(revision 29)
+++ /exampleparties/humangui/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>humangui</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -50,5 +50,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -115,8 +115,7 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
-			<scope>test</scope>
-		</dependency>
-
+			<version>1.0.5</version>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 
@@ -130,11 +129,15 @@
 			</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>
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 29)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 30)
@@ -173,3 +173,7 @@
 	}
 
+	@Override
+	public void close() {
+	}
+
 }
Index: /exampleparties/humangui/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/humangui/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/humangui/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /exampleparties/humangui/src/test/resources/testprofile2.json
===================================================================
--- /exampleparties/humangui/src/test/resources/testprofile2.json	(revision 29)
+++ /exampleparties/humangui/src/test/resources/testprofile2.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue3": {
-					"values": ["issue3value1", "issue3value2"]
+					"values": [
+						"issue3value1",
+						"issue3value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue2": "12",
-				"issue3": "issuevalue1"
+				"issue2": 12,
+				"issue3": "issue3value1"
 			}
 		}
-		
 	}
 }
Index: /exampleparties/linear/pom.xml
===================================================================
--- /exampleparties/linear/pom.xml	(revision 29)
+++ /exampleparties/linear/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>linear</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>
 
@@ -39,5 +39,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -53,14 +53,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: /exampleparties/randomparty/pom.xml
===================================================================
--- /exampleparties/randomparty/pom.xml	(revision 29)
+++ /exampleparties/randomparty/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>randomparty</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -50,10 +50,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -120,5 +120,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -135,11 +135,15 @@
 			</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>
Index: /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java
===================================================================
--- /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 29)
+++ /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 30)
@@ -6,4 +6,5 @@
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Random;
 import java.util.Set;
@@ -16,5 +17,7 @@
 import geniusweb.actions.PartyId;
 import geniusweb.actions.Vote;
+import geniusweb.actions.VoteWithValue;
 import geniusweb.actions.Votes;
+import geniusweb.actions.VotesWithValue;
 import geniusweb.bidspace.AllPartialBidsList;
 import geniusweb.inform.ActionDone;
@@ -22,4 +25,5 @@
 import geniusweb.inform.Inform;
 import geniusweb.inform.OptIn;
+import geniusweb.inform.OptInWithValue;
 import geniusweb.inform.Settings;
 import geniusweb.inform.Voting;
@@ -38,6 +42,8 @@
 
 /**
- * A simple party that places random bids and accepts when it receives an offer
- * with sufficient utility.
+ * A party that places random bids and accepts when it receives an offer with
+ * sufficient utility. This party is also a demo on how to support the various
+ * protocols, which causes this party to look a bit complex.
+ * 
  * <h2>parameters</h2>
  * <table >
@@ -60,8 +66,9 @@
 	private PartyId me;
 	private final Random random = new Random();
-	protected ProfileInterface profileint;
+	protected ProfileInterface profileint = null;
 	private Progress progress;
 	private Settings settings;
 	private Votes lastvotes;
+	private VotesWithValue lastvoteswithvalue;
 	private String protocol;
 
@@ -95,9 +102,18 @@
 				getReporter().log(Level.INFO, "Final ourcome:" + info);
 			} else if (info instanceof Voting) {
-				lastvotes = vote((Voting) info);
-				getConnection().send(lastvotes);
+				switch (protocol) {
+				case "MOPAC":
+					lastvotes = vote((Voting) info);
+					getConnection().send(lastvotes);
+					break;
+				case "MOPAC2":
+					lastvoteswithvalue = voteWithValue((Voting) info);
+					getConnection().send(lastvoteswithvalue);
+				}
 			} else if (info instanceof OptIn) {
 				// just repeat our last vote.
 				getConnection().send(lastvotes);
+			} else if (info instanceof OptInWithValue) {
+				getConnection().send(lastvoteswithvalue);
 			}
 		} catch (Exception e) {
@@ -110,5 +126,5 @@
 	public Capabilities getCapabilities() {
 		return new Capabilities(
-				new HashSet<>(Arrays.asList("SAOP", "AMOP", "MOPAC")),
+				new HashSet<>(Arrays.asList("SAOP", "AMOP", "MOPAC", "MOPAC2")),
 				Collections.singleton(Profile.class));
 	}
@@ -119,4 +135,15 @@
 				+ "Parameters minPower and maxPower can be used to control voting behaviour.";
 	}
+
+	@Override
+	public void terminate() {
+		super.terminate();
+		if (this.profileint != null) {
+			this.profileint.close();
+			this.profileint = null;
+		}
+	}
+
+	/******************* private support funcs ************************/
 
 	/**
@@ -137,4 +164,8 @@
 		case "MOPAC":
 			if (!(info instanceof OptIn))
+				return;
+			break;
+		case "MOPAC2":
+			if (!(info instanceof OptInWithValue))
 				return;
 			break;
@@ -213,3 +244,34 @@
 	}
 
+	/**
+	 * @param voting the {@link Voting} object containing the options
+	 * 
+	 * @return our next Votes. Returns only votes on good bids and tries to
+	 *         distribute vote values evenly over all good bids.
+	 */
+	private VotesWithValue voteWithValue(Voting voting) throws IOException {
+		Object val = settings.getParameters().get("minPower");
+		Integer minpower = (val instanceof Integer) ? (Integer) val : 2;
+		val = settings.getParameters().get("maxPower");
+		Integer maxpower = (val instanceof Integer) ? (Integer) val
+				: Integer.MAX_VALUE;
+
+		List<Bid> goodbids = voting.getBids().stream().distinct()
+				.filter(offer -> isGood(offer.getBid()))
+				.map(offer -> offer.getBid()).collect(Collectors.toList());
+
+		if (goodbids.isEmpty()) {
+			return new VotesWithValue(me, Collections.emptySet());
+		}
+		// extra difficulty now is to have the utility sum to exactly 100
+		int mostvalues = 100 / goodbids.size();
+		int value = 100 - mostvalues * (goodbids.size() - 1);
+		Set<VoteWithValue> votes = new HashSet<>();
+		for (Bid bid : goodbids) {
+			votes.add(new VoteWithValue(me, bid, minpower, maxpower, value));
+			value = mostvalues;
+		}
+		return new VotesWithValue(me, votes);
+	}
+
 }
Index: /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java
===================================================================
--- /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 29)
+++ /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 30)
@@ -37,4 +37,5 @@
 import geniusweb.actions.PartyId;
 import geniusweb.actions.Votes;
+import geniusweb.actions.VotesWithValue;
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
@@ -71,6 +72,7 @@
 	private final ProtocolRef protocol = new ProtocolRef(SAOP);
 	private final ProtocolRef mopacProtocol = new ProtocolRef("MOPAC");
+	private final ProtocolRef mopac2Protocol = new ProtocolRef("MOPAC2");
 	private final ProgressRounds progress = mock(ProgressRounds.class);
-	private Settings settings, mopacSettings;
+	private Settings settings, mopacSettings, mopac2Settings;
 	private LinearAdditive profile;
 	private final Parameters parameters = new Parameters();
@@ -86,4 +88,7 @@
 				new ProfileRef(new URI("file:" + PROFILE)), mopacProtocol,
 				progress, parameters);
+		mopac2Settings = new Settings(PARTY1,
+				new ProfileRef(new URI("file:" + PROFILE)), mopac2Protocol,
+				progress, parameters);
 
 		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
@@ -235,4 +240,20 @@
 	}
 
+	@Test
+	public void testVotingWithValue() throws URISyntaxException {
+		party.connect(connection);
+		party.notifyChange(mopac2Settings);
+
+		Bid bid = findGoodBid();
+		Offer offer = new Offer(PARTY1, bid);
+		party.notifyChange(new Voting(Arrays.asList(offer),
+				Collections.singletonMap(PARTY1, 1)));
+		assertEquals(1, connection.getActions().size());
+		Action action = connection.getActions().get(0);
+		assertTrue(action instanceof VotesWithValue);
+		assertEquals(1, ((VotesWithValue) action).getVotes().size());
+		assertEquals(bid, ((VotesWithValue) action).getVotes().iterator().next()
+				.getBid());
+	}
 }
 
Index: /exampleparties/randomparty/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/randomparty/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/randomparty/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /exampleparties/randompartypy/pom.xml
===================================================================
--- /exampleparties/randompartypy/pom.xml	(revision 29)
+++ /exampleparties/randompartypy/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>randompyparty</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>
 
@@ -48,5 +48,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -112,5 +112,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -126,13 +126,18 @@
 			</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>
+
 
 
Index: /exampleparties/randompartypy/src/main/resources/RandomParty.py
===================================================================
--- /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 29)
+++ /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 30)
@@ -72,4 +72,12 @@
 		return "places random bids until it can accept an offer with utility >0.6. Python version"
 
+	# Override
+	def terminate(self):
+		super(RandomParty,self).terminate()
+		if self.profile != None:
+			self.profile.close()
+			self.profile = None
+
+
 	def _myTurn(self):
 		if self.lastReceivedBid != None and self.profile.getProfile().getUtility(self.lastReceivedBid).doubleValue() > 0.6:
@@ -99,2 +107,3 @@
 			return profile.isPreferredOrEqual(bid, profile.getReservationBid())
 		raise Exception("Can not handle this type of profile")
+
Index: /exampleparties/randompartypy/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/randompartypy/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/randompartypy/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /exampleparties/simpleboa/pom.xml
===================================================================
--- /exampleparties/simpleboa/pom.xml	(revision 29)
+++ /exampleparties/simpleboa/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>simpleboaparty</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -58,5 +58,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -73,14 +73,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: /exampleparties/simpleboa/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/simpleboa/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/simpleboa/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /exampleparties/simpleshaop/pom.xml
===================================================================
--- /exampleparties/simpleshaop/pom.xml	(revision 29)
+++ /exampleparties/simpleshaop/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>simpleshaop</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.6</jackson-2-version>
-		<geniusweb.version>1.5.8</geniusweb.version>
+		<geniusweb.version>1.5.9</geniusweb.version>
 	</properties>
 
@@ -50,10 +50,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -120,5 +120,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -135,14 +135,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: /exampleparties/timedependentparty/pom.xml
===================================================================
--- /exampleparties/timedependentparty/pom.xml	(revision 29)
+++ /exampleparties/timedependentparty/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>timedependentparty</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>
 
@@ -50,10 +50,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -120,5 +120,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -135,11 +135,15 @@
 			</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>
Index: /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java
===================================================================
--- /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 29)
+++ /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 30)
@@ -73,5 +73,5 @@
 public class TimeDependentParty extends DefaultParty {
 
-	private ProfileInterface profileint;
+	private ProfileInterface profileint = null;
 	private LinearAdditive utilspace = null; // last received space
 	private PartyId me;
@@ -165,4 +165,13 @@
 	}
 
+	@Override
+	public void terminate() {
+		super.terminate();
+		if (this.profileint != null) {
+			this.profileint.close();
+			this.profileint = null;
+		}
+	}
+
 	/******************* private support funcs ************************/
 
Index: /exampleparties/timedependentparty/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/timedependentparty/src/test/resources/testprofile.json	(revision 29)
+++ /exampleparties/timedependentparty/src/test/resources/testprofile.json	(revision 30)
@@ -27,19 +27,25 @@
 			"issuesValues": {
 				"issue2": {
-					"range": ["12", "18", "1"]
+					"range": [
+						"12",
+						"18",
+						"1"
+					]
 				},
 				"issue1": {
-					"values": ["issue1value1", "issue1value2"]
+					"values": [
+						"issue1value1",
+						"issue1value2"
+					]
 				}
 			}
 		},
 		"name": "testprofile",
-				"reservationBid": {
+		"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
-		
 	}
 }
Index: /ip/README.TXT
===================================================================
--- /ip/README.TXT	(revision 30)
+++ /ip/README.TXT	(revision 30)
@@ -0,0 +1,2 @@
+IP is an algorithm. which is used as a way to determine termination of a MOPAC negotiation.
+Code is mostly copied from https://github.com/trahwan/ODP-IP and cleaned up and fixed for our use.
Index: /ip/pom.xml
===================================================================
--- /ip/pom.xml	(revision 30)
+++ /ip/pom.xml	(revision 30)
@@ -0,0 +1,184 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>geniusweb</groupId>
+	<artifactId>ip</artifactId>
+	<version>1.5.9</version> <!-- must equal ${geniusweb.version} -->
+	<packaging>jar</packaging>
+
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.compiler.target>1.8</maven.compiler.target>
+		<maven.compiler.source>1.8</maven.compiler.source>
+		<basedir>.</basedir>
+		<passwd>${env.ARTIFACTORY_PASS}</passwd>
+		<jackson-2-version>2.9.10</jackson-2-version>
+		<geniusweb.version>1.5.9</geniusweb.version>
+	</properties>
+
+	<distributionManagement>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+		</repository>
+	</distributionManagement>
+
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-math</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+
+	</dependencies>
+
+
+	<repositories>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+
+	<pluginRepositories>
+		<pluginRepository>
+			<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>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.8.0</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>3.1.0</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+			<!-- The javadoc is disabled because this basically is a clone of 3rd 
+				party code that has no proper documentation. <plugin> <groupId>org.apache.maven.plugins</groupId> 
+				<artifactId>maven-javadoc-plugin</artifactId> <version>3.2.0</version> <executions> 
+				<execution> <id>attach-javadocs</id> <goals> <goal>jar</goal> </goals> </execution> 
+				</executions> </plugin> -->
+			<plugin>
+				<groupId>org.jfrog.buildinfo</groupId>
+				<artifactId>artifactory-maven-plugin</artifactId>
+				<version>2.6.1</version>
+				<executions>
+					<execution>
+						<id>build-info</id>
+						<goals>
+							<goal>publish</goal>
+						</goals>
+						<configuration>
+							<publisher>
+								<contextUrl>http://artifactory.ewi.tudelft.nl/artifactory</contextUrl>
+								<repoKey>libs-release</repoKey>
+								<username>wouter</username>
+								<password>${passwd}</password>
+							</publisher>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.4.1</version>
+				<configuration>
+					<!-- get all project dependencies -->
+					<descriptorRefs>
+						<descriptorRef>jar-with-dependencies</descriptorRef>
+					</descriptorRefs>
+					<archive>
+						<manifest>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+						</manifest>
+					</archive>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<!-- bind to the packaging phase -->
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+
+		</plugins>
+
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.jfrog.buildinfo</groupId>
+										<artifactId>artifactory-maven-plugin</artifactId>
+										<versionRange>[1.0.0,)</versionRange>
+										<goals>
+											<goal>publish</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore />
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+
+
+	</build>
+</project>
Index: /ip/src/main/java/geniusweb/ip/dpSolver/DPSolver.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/dpSolver/DPSolver.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/dpSolver/DPSolver.java	(revision 30)
@@ -0,0 +1,850 @@
+/**
+ * The dynamic programming solvers. This code is used for either DP, IDP, ODP, or the IDP part of ODP-IP
+ * @author Talal Rahwan
+ */
+package geniusweb.ip.dpSolver;
+
+import java.util.Arrays;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.SolverNames;
+import geniusweb.ip.inputOutput.ValueDistribution;
+import geniusweb.ip.mainSolver.Result;
+
+public class DPSolver {
+	int[] t; // t[i] contains the first half of the best split of coalition i
+				// (where the coalition is represented in bit format)
+	// this is only used by DP, i.e., it is not used by IDP, ODP, or the IDP
+	// part of ODP-IP
+
+	private double[] f;
+
+	private boolean stop = false;
+
+	private Input input;
+
+	private Result result;
+
+	public DPSolver(Input input, Result result) {
+		this.input = input;
+		this.result = result;
+	}
+
+	// *********************************************************************************************************
+
+	public void set_f(int index, double value) {
+		if (input.solverName == SolverNames.ODPIP)
+			result.idpSolver_whenRunning_ODPIP
+					.updateValueOfBestPartitionFound(index, value);
+		else
+			f[index] = value;
+	}
+
+	public double get_f(int index) {
+		if (input.solverName == SolverNames.ODPIP)
+			return result.idpSolver_whenRunning_ODPIP
+					.getValueOfBestPartitionFound(index);
+		else
+			return f[index];
+	}
+
+	// *********************************************************************************************************
+
+	public void setStop(boolean value) {
+		stop = value;
+	}
+
+	public boolean getStop() {
+		return stop;
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Run DP or IDP, or the IDP part of ODP-IP. To specify which algorithm to
+	 * run, set input.solverName to either SolverNames.DP or SolverNames.IDP, or
+	 * SolverNames.ODPIP
+	 */
+	public void runDPorIDP() {
+		if (input.solverName != SolverNames.ODPIP) {
+			f = new double[input.coalitionValues.length];
+			for (int i = 0; i < f.length; i++)
+				set_f(i, input.coalitionValues[i]);
+		}
+		int numOfAgents = input.numOfAgents;
+		set_f(0, 0); // just in case, set the value of the empty set to 0
+
+		// initialization
+		long[] requiredTimeForEachSize = new long[numOfAgents + 1];
+		requiredTimeForEachSize[1] = 0;
+		long[] startTimeForEachSize = new long[numOfAgents + 1];
+		int grandCoalition = (1 << numOfAgents) - 1;
+		int numOfCoalitions = 1 << numOfAgents;
+		int bestHalfOfGrandCoalition = -1;
+		long startTime = System.currentTimeMillis();
+		result.set_dpMaxSizeThatWasComputedSoFar(1);
+
+		// Step 1 (initialization for step 2). Basically, initialize the t
+		// table.
+		if (input.solverName == SolverNames.DP) { // Initialize the best half of
+													// every coalition to be the
+													// coalition itself.
+			t = new int[numOfCoalitions];
+			for (int coalition = 0; coalition < numOfCoalitions; coalition++)
+				t[coalition] = coalition;
+		}
+		// Step 2 (evaluate the possible splits of every coalition of size 2, 3,
+		// 4, ...)
+		for (int curSize = 2; curSize <= numOfAgents; curSize++)// For every
+																// size
+		{
+			if ((input.solverName == SolverNames.IDP)
+					|| (input.solverName == SolverNames.ODPIP))
+				if (((int) (Math
+						.floor((2 * numOfAgents) / (double) 3)) < curSize)
+						&& (curSize < numOfAgents))
+					continue;
+
+			// check if all splits of this size will be evaluated
+			boolean allSplitsOfCurSizeWillBeEvaluated = true;
+			for (int sizeOfFirstHalf = (int) Math.ceil(curSize
+					/ (double) 2); sizeOfFirstHalf < curSize; sizeOfFirstHalf++)
+				if ((input.solverName == SolverNames.IDP)
+						|| (input.solverName == SolverNames.ODPIP))
+					if ((sizeOfFirstHalf > numOfAgents - curSize)
+							&& (curSize != numOfAgents)) {
+						allSplitsOfCurSizeWillBeEvaluated = false;
+						break;
+					}
+			startTimeForEachSize[curSize] = System.currentTimeMillis();
+
+			// If the coalition happens to be the grand coalition, then deal
+			// with this case saperately
+			if (curSize < numOfAgents) {
+				int numOfCoalitionsOfCurSize = (int) Combinations
+						.binomialCoefficient(numOfAgents, curSize);
+
+				if (allSplitsOfCurSizeWillBeEvaluated) // in this case, the
+														// evaluation of the
+														// splits can be done
+														// more efficiently
+				{
+					// Compute the number of possible splits of any coalition of
+					// size = "curSize"
+					int numOfPossibleSplits = 1 << curSize;
+					// Evaluate the possible splits of the current coalition
+					int[] curCoalition = Combinations
+							.getCombinationAtGivenIndex(curSize,
+									numOfCoalitionsOfCurSize - 1, numOfAgents);
+					evaluateSplitsEfficiently(curCoalition, curSize,
+							numOfPossibleSplits);
+					for (int i = 1; i < numOfCoalitionsOfCurSize; i++) {
+						Combinations.getPreviousCombination(numOfAgents,
+								curSize, curCoalition);
+						evaluateSplitsEfficiently(curCoalition, curSize,
+								numOfPossibleSplits);
+						if (getStop())
+							break;
+					}
+				} else {
+					// Compute the number of possible splits of any coalition of
+					// size = "curSize"
+					int numOfPossibleSplits = 1 << curSize;
+					// Compute the number of possible splits for every partition
+					// of "curSize" into "sizeOfFirstHalf" + "sizeOfSecondHalf"
+					long[] numOfPossibleSplitsBasedOnSizeOfFirstHalf = computeNumOfPossibleSplitsBasedOnSizeOfFirstHalf(
+							curSize);
+					// Evaluate the possible splits of the current coalition
+					int[] curCoalition = Combinations
+							.getCombinationAtGivenIndex(curSize,
+									numOfCoalitionsOfCurSize - 1, numOfAgents);
+					for (int i = 1; i < numOfCoalitionsOfCurSize; i++) {
+						Combinations.getPreviousCombination(numOfAgents,
+								curSize, curCoalition);
+						if ((input.solverName == SolverNames.DP)
+								|| (input.useEfficientImplementationOfIDP))
+							evaluateSplitsEfficiently(curCoalition, curSize,
+									numOfPossibleSplits);
+						else
+							evaluateSplits(curCoalition, curSize,
+									numOfPossibleSplitsBasedOnSizeOfFirstHalf);
+
+						if (getStop())
+							break;
+					}
+				}
+				if (getStop())
+					break;
+			} else
+				bestHalfOfGrandCoalition = evaluateSplitsOfGrandCoalition();
+
+			// Update the value of the best CS found so far by IDP
+			if (curSize < numOfAgents) {
+				bestHalfOfGrandCoalition = evaluateSplitsOfGrandCoalition();
+				int[] bestCSFoundSoFar = getOptimalSplit(grandCoalition,
+						bestHalfOfGrandCoalition);
+				int[][] bestCSFoundSoFar_byteFormat = Combinations
+						.convertSetOfCombinationsFromBitToByteFormat(
+								bestCSFoundSoFar, numOfAgents);
+				result.updateDPSolution(bestCSFoundSoFar_byteFormat,
+						+input.getCoalitionStructureValue(
+								bestCSFoundSoFar_byteFormat));
+				if (input.solverName == SolverNames.ODPIP)
+					result.updateIPSolution(bestCSFoundSoFar_byteFormat,
+							input.getCoalitionStructureValue(
+									bestCSFoundSoFar_byteFormat));
+			}
+			// Print the time that was taken to perform this step
+			requiredTimeForEachSize[curSize] = System.currentTimeMillis()
+					- startTimeForEachSize[curSize];
+			if (input.solverName == SolverNames.DP) {
+				System.out.println(
+						"   The time for DP to finish evaluating the splittings of coalitions of size "
+								+ curSize + " is: "
+								+ requiredTimeForEachSize[curSize]);
+			} else { // i.e., if it is IDP or ODP-IP
+				System.out.print(
+						"   The time for IDP to finish evaluating the splittings of coalitions of size "
+								+ curSize + " is: "
+								+ requiredTimeForEachSize[curSize]);
+				System.out.println(".  The best CS found so far by IDP is : "
+						+ Arrays.deepToString(result.get_dpBestCSFound())
+						+ " , its value is: "
+						+ result.get_dpValueOfBestCSFound());
+			}
+			// Update the maximum size that DP has finished searching
+			result.set_dpMaxSizeThatWasComputedSoFar(curSize);
+		}
+		if (getStop())
+			return;
+
+		// Initialize the result
+		result.dpTimeForEachSize = requiredTimeForEachSize;
+
+		// Step 3 (divide the grand coalition in the optimal way using the f
+		// table)
+		int[] bestCSFound = getOptimalSplit(grandCoalition,
+				bestHalfOfGrandCoalition);
+
+		// Set the final result
+		result.dpTime = System.currentTimeMillis() - startTime;
+		int[][] dpBestCSInByteFormat = Combinations
+				.convertSetOfCombinationsFromBitToByteFormat(bestCSFound,
+						numOfAgents);
+		result.updateDPSolution(dpBestCSInByteFormat,
+				input.getCoalitionStructureValue(dpBestCSInByteFormat));
+		if (input.solverName == SolverNames.ODPIP)
+			result.updateIPSolution(dpBestCSInByteFormat,
+					input.getCoalitionStructureValue(dpBestCSInByteFormat));
+
+		// Print the distribution of f if required
+		if (input.printDistributionOfThefTable)
+			printDistributionOfThefTable();
+
+		if (input.printPercentageOf_v_equals_f)
+			printPercentageOf_v_equals_f();
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a size, we can split it into [size-1,1], and [size-2,2],and so on.
+	 * Now suppose that there are 1000 possible splits that match [size-1,1],
+	 * and 8700 splits that match [size-2,2], and so on. This method computes
+	 * these numbers of possible splits.
+	 * 
+	 * numOfPossibleSplitsBasedOnSizeOfFirstHalf[ 3 ] is the number of possible
+	 * splits, where the first half is equals 3
+	 */
+	private long[] computeNumOfPossibleSplitsBasedOnSizeOfFirstHalf(int size) {
+		long[] numOfPossibleSplitsBasedOnSizeOfFirstHalf = new long[size];
+		for (int sizeOfFirstHalf = (int) Math.ceil(
+				size / (double) 2); sizeOfFirstHalf < size; sizeOfFirstHalf++) {
+			int sizeOfSecondHalf = size - sizeOfFirstHalf;
+			if (((size % 2) == 0) && (sizeOfFirstHalf == sizeOfSecondHalf))
+				numOfPossibleSplitsBasedOnSizeOfFirstHalf[sizeOfFirstHalf] = Combinations
+						.binomialCoefficient(size, sizeOfFirstHalf) / 2;
+			else
+				numOfPossibleSplitsBasedOnSizeOfFirstHalf[sizeOfFirstHalf] = Combinations
+						.binomialCoefficient(size, sizeOfFirstHalf);
+		}
+		return (numOfPossibleSplitsBasedOnSizeOfFirstHalf);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Splits the given coalition in an optimal way using the f table (i.e., it
+	 * makes the optimal movements in the coalition structure graph). Here,
+	 * coalitions are represented IN BIT FORMAT.
+	 */
+	private int[] getOptimalSplit(int coalitionInBitFormat,
+			int bestHalfOfCoalition) {
+		int[] optimalSplit;
+		if (bestHalfOfCoalition == coalitionInBitFormat) {
+			optimalSplit = new int[1];
+			optimalSplit[0] = coalitionInBitFormat;
+		} else {
+			// Initialization
+			int[] arrayOfBestHalf = new int[2];
+			int[][] arrayOfOptimalSplit = new int[2][];
+			int[] arrayOfCoalitionInBitFormat = new int[2];
+
+			// Set the two halves
+			arrayOfCoalitionInBitFormat[0] = bestHalfOfCoalition;
+			arrayOfCoalitionInBitFormat[1] = coalitionInBitFormat
+					- bestHalfOfCoalition;
+
+			// For each one of the two halves
+			for (int i = 0; i < 2; i++) {
+				if ((input.solverName == SolverNames.DP))
+					arrayOfBestHalf[i] = t[arrayOfCoalitionInBitFormat[i]];
+				else
+					arrayOfBestHalf[i] = getBestHalf(
+							arrayOfCoalitionInBitFormat[i]); // We need to
+																// recompute the
+																// best half
+
+				arrayOfOptimalSplit[i] = getOptimalSplit(
+						arrayOfCoalitionInBitFormat[i], arrayOfBestHalf[i]);
+			}
+			// Set "optimalSplit" by combining "arrayOfOptimalSplit[0]" with
+			// "arrayOfOptimalSplit[1]"
+			optimalSplit = new int[arrayOfOptimalSplit[0].length
+					+ arrayOfOptimalSplit[1].length];
+			int k = 0;
+			for (int i = 0; i < 2; i++)
+				for (int j = 0; j < arrayOfOptimalSplit[i].length; j++) {
+					optimalSplit[k] = arrayOfOptimalSplit[i][j];
+					k++;
+				}
+		}
+		return (optimalSplit);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * @param CS an array of coalition structures.
+	 * @return Split each coalition in the given structure in an optimal way
+	 *         using the f table (i.e., it makes the optimal movements in the
+	 *         coalition structure graph). Here, coalitions are represented IN
+	 *         BYTE FORMAT.
+	 */
+	public int[][] getOptimalSplit(int[][] CS) {
+		// Initialization
+		int[][] optimalSplit = new int[CS.length][];
+		int numOfCoalitionsInFinalResult = 0;
+
+		// For every coalition CS[i] in CS, find the optimal split of CS[i], and
+		// store it in "optimalSplit[i]"
+		for (int i = 0; i < CS.length; i++) {
+			int coalitionInBitFormat = Combinations
+					.convertCombinationFromByteToBitFormat(CS[i]);
+			int bestHalfOfCoalitionInBitFormat = getBestHalf(
+					coalitionInBitFormat);
+			optimalSplit[i] = getOptimalSplit(coalitionInBitFormat,
+					bestHalfOfCoalitionInBitFormat);
+			numOfCoalitionsInFinalResult += optimalSplit[i].length;
+		}
+		// Group the optimal splits of different coalitions into "finalResult"
+		int[][] finalResult = new int[numOfCoalitionsInFinalResult][];
+		int k = 0;
+		for (int i = 0; i < CS.length; i++) // For every coalition CS[i] in CS
+		{
+			for (int j = 0; j < optimalSplit[i].length; j++) // For every
+																// coalition in
+																// the optimal
+																// split of
+																// CS[i]
+			{
+				finalResult[k] = Combinations
+						.convertCombinationFromBitToByteFormat(
+								optimalSplit[i][j], input.numOfAgents);
+				k++;
+			}
+		}
+		return (finalResult);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Evaluate the possible splittings of the grand coalition. The way this is
+	 * done is similar to the way IP searches the second layer while scanning
+	 * the input.
+	 */
+	private int evaluateSplitsOfGrandCoalition() {
+		// Initialization
+		double curValue = -1;
+		double bestValue = -1;
+		int bestHalfOfGrandCoalitionInBitFormat = -1;
+		int numOfCoalitions = 1 << input.numOfAgents;
+		int grandCoalition = (1 << input.numOfAgents) - 1;
+
+		// Check the possible ways of splitting the grand coalition into two
+		// (non-empty) coalitions
+		for (int firstHalfOfGrandCoalition = (numOfCoalitions / 2)
+				- 1; firstHalfOfGrandCoalition < numOfCoalitions; firstHalfOfGrandCoalition++) {
+			int secondHalfOfGrandCoalition = numOfCoalitions - 1
+					- firstHalfOfGrandCoalition;
+			curValue = get_f(firstHalfOfGrandCoalition)
+					+ get_f(secondHalfOfGrandCoalition);
+			if (curValue > bestValue) {
+				bestValue = curValue;
+				bestHalfOfGrandCoalitionInBitFormat = firstHalfOfGrandCoalition;
+			}
+		}
+		// Deal with the case where the first half is the grand coalition itself
+		int firstHalfOfGrandCoalition = grandCoalition;
+		curValue = get_f(firstHalfOfGrandCoalition);
+		if (curValue > bestValue) {
+			bestValue = curValue;
+			bestHalfOfGrandCoalitionInBitFormat = firstHalfOfGrandCoalition;
+		}
+		// Set t and f
+		set_f(grandCoalition, bestValue);
+		if (input.solverName == SolverNames.DP) // Then use the t table
+			t[grandCoalition] = bestHalfOfGrandCoalitionInBitFormat;
+
+		return (bestHalfOfGrandCoalitionInBitFormat);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition of a particular size, this method evaluates the
+	 * possible splits of this coalition into two. Here, the evaluation is based
+	 * on f (not v) of each of the two halves.
+	 * 
+	 * NOTE: this will be called when running DP or IDP, but not when running
+	 * ODP!!
+	 */
+	private void evaluateSplits(int[] coalitionInByteFormat, int coalitionSize,
+			long[] numOfPossibleSplitsBasedOnSizeOfFirstHalf) {
+		double curValue = -1;
+		double bestValue = -1;
+		int bestHalfOfCoalitionInBitFormat = -1;
+		int numOfAgents = input.numOfAgents;
+		int coalitionInBitFormat = Combinations
+				.convertCombinationFromByteToBitFormat(coalitionInByteFormat);
+
+		// bit[i] is the bit representing agent a_i (e.g. given 4 agents,
+		// bit[2]=2=0010, bit[3]=4=0100, etc.)
+		int[] bit = new int[numOfAgents + 1];
+		for (int i = 0; i < numOfAgents; i++)
+			bit[i + 1] = 1 << i;
+
+		// Check the possible ways of splitting the coalition into two
+		// (non-empty) coalitions
+		for (int sizeOfFirstHalf = (int) Math.ceil(coalitionSize
+				/ (double) 2); sizeOfFirstHalf < coalitionSize; sizeOfFirstHalf++) {
+			// int sizeOfSecondHalf = (int)(coalitionSize - sizeOfFirstHalf);
+
+			// If we want to evaluate only the useful splits, and the size of
+			// the biggest half (which, here, happens to
+			// be the first half) is greater than the number of agents minus the
+			// size of the coalition, then continue.
+			if (((input.solverName == SolverNames.IDP)
+					|| (input.solverName == SolverNames.ODPIP))
+					&& (sizeOfFirstHalf > numOfAgents - coalitionSize)
+					&& (coalitionSize != numOfAgents))
+				continue;
+
+			// Set the initial indices of the members of the first half
+			int[] indicesOfMembersOfFirstHalf = new int[sizeOfFirstHalf];
+			for (int i = 0; i < sizeOfFirstHalf; i++)
+				indicesOfMembersOfFirstHalf[i] = i + 1;
+
+			// Compute the first half (in bit format)
+			int firstHalfInBitFormat = 0;
+			for (int i = 0; i < sizeOfFirstHalf; i++)
+				firstHalfInBitFormat += bit[coalitionInByteFormat[indicesOfMembersOfFirstHalf[i]
+						- 1]];
+
+			// Compute the second half (in bit format)
+			int secondHalfInBitFormat = (coalitionInBitFormat
+					- firstHalfInBitFormat);
+
+			// Update the functions t and f
+			curValue = get_f(firstHalfInBitFormat)
+					+ get_f(secondHalfInBitFormat);
+			if (bestValue < curValue) {
+				bestValue = curValue;
+				if (input.solverName == SolverNames.DP)
+					bestHalfOfCoalitionInBitFormat = firstHalfInBitFormat;
+			}
+			// Do the same for the remaining possibilities of the first and
+			// second halves
+			for (int j = 1; j < numOfPossibleSplitsBasedOnSizeOfFirstHalf[sizeOfFirstHalf]; j++) {
+				/**/
+				Combinations.getPreviousCombination(coalitionSize,
+						sizeOfFirstHalf, indicesOfMembersOfFirstHalf);
+
+				// Compute the first half (in bit format)
+				firstHalfInBitFormat = 0;
+				for (int i = 0; i < sizeOfFirstHalf; i++)
+					firstHalfInBitFormat += bit[coalitionInByteFormat[indicesOfMembersOfFirstHalf[i]
+							- 1]];
+				/**/
+				// Combinations.getPreviousCombinationInBitFormat2(numOfAgents,
+				// sizeOfFirstHalf, firstHalfInBitFormat);
+
+				// Compute the second half (in bit format)
+				secondHalfInBitFormat = (coalitionInBitFormat
+						- firstHalfInBitFormat);
+
+				// Update the functions t and f
+				curValue = get_f(firstHalfInBitFormat)
+						+ get_f(secondHalfInBitFormat);
+				if (bestValue < curValue) {
+					bestValue = curValue;
+					if (input.solverName == SolverNames.DP)
+						bestHalfOfCoalitionInBitFormat = firstHalfInBitFormat;
+				}
+			}
+		}
+		// Deal with the case where the first half is the coalition itself
+		int firstHalfInBitFormat = coalitionInBitFormat;
+		curValue = get_f(firstHalfInBitFormat);
+		if (bestValue < curValue) {
+			bestValue = curValue;
+			if (input.solverName == SolverNames.DP)
+				bestHalfOfCoalitionInBitFormat = firstHalfInBitFormat;
+		}
+
+		// Update the maximum value of f for the size that is equal to
+		// "coalitionSize"
+		if (input.solverName == SolverNames.ODPIP)
+			if (result.get_max_f(coalitionSize - 1) < bestValue)
+				result.set_max_f(coalitionSize - 1, bestValue);
+
+		// Finalizing
+		set_f(coalitionInBitFormat, bestValue);
+		if (input.solverName == SolverNames.DP)
+			t[coalitionInBitFormat] = bestHalfOfCoalitionInBitFormat;
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition of a particular size, this method evaluates the
+	 * possible splits of this coalition into two. Here, the evaluation is based
+	 * on f (not v) of each of the two halves. NOTE: here, the iteration over
+	 * all splits is done efficiently
+	 */
+	private void evaluateSplitsEfficiently(int[] coalitionInByteFormat,
+			int coalitionSize, int numOfPossibilities) {
+		double curValue = -1;
+		double bestValue = -1;
+		int bestHalfOfCoalitionInBitFormat = -1;
+		int coalitionInBitFormat = Combinations
+				.convertCombinationFromByteToBitFormat(coalitionInByteFormat);
+
+		bestValue = input.getCoalitionValue(coalitionInBitFormat);
+		bestHalfOfCoalitionInBitFormat = coalitionInBitFormat;
+
+		// Check the possible ways of splitting the grand coalition into two
+		// (non-empty) coalitions
+		for (int firstHalfInBitFormat = coalitionInBitFormat - 1
+				& coalitionInBitFormat; /* firstHalfInBitFormat > 0 */; firstHalfInBitFormat = firstHalfInBitFormat
+						- 1 & coalitionInBitFormat) {
+			int secondHalfInBitFormat = coalitionInBitFormat
+					^ firstHalfInBitFormat;
+
+			// Update the functions t and f
+			curValue = get_f(firstHalfInBitFormat)
+					+ get_f(secondHalfInBitFormat);
+
+			if (bestValue <= curValue) {
+				bestValue = curValue;
+				if (input.solverName == SolverNames.DP) { // i.e., if we are
+															// using the t table
+					if (Integer.bitCount(firstHalfInBitFormat) > Integer
+							.bitCount(secondHalfInBitFormat))
+						bestHalfOfCoalitionInBitFormat = firstHalfInBitFormat;
+					else
+						bestHalfOfCoalitionInBitFormat = secondHalfInBitFormat;
+				}
+			}
+			if ((firstHalfInBitFormat & (firstHalfInBitFormat - 1)) == 0)
+				break;
+		}
+		// Update the maximum value of f for the size that is equal to
+		// "coalitionSize"
+		if (input.solverName == SolverNames.ODPIP)
+			if (result.get_max_f(coalitionSize - 1) < bestValue)
+				result.set_max_f(coalitionSize - 1, bestValue);
+
+		// Finalizing
+		set_f(coalitionInBitFormat, bestValue);
+		if (input.solverName == SolverNames.DP)
+			t[coalitionInBitFormat] = bestHalfOfCoalitionInBitFormat;
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a non-empty coalition C \subset A\{1,2}, this method evaluates the
+	 * possible splits of this coalition into two coalitions, C1 and C2
+	 * (including the split {\emptyset,C}). For every such split, compute: f(C1
+	 * U {1}) + f(C2 U {2}) f(C1 U {2}) + f(C2 U {1}) if( min agents in C1 < min
+	 * agent in A \ (C U {1,2}) ) f(C2 U {1,2}) + f(C1) if( min agents in C2 <
+	 * min agent in A \ (C U {1,2}) ) f(C1 U {1,2}) + f(C2)
+	 */
+	private void evaluateSplitsOptimally(int C, int sizeOfC, int grandCoalition,
+			int numOfAgents) {
+		double bestValue = input.getCoalitionValue(C + 3); // curValue = v( C U
+															// {1,2} )
+
+		// Compute the minimum agent in: "remainingAgents", which is: A \ ( C U
+		// {1,2} )
+		int remainingAgents = grandCoalition - C - 3;
+		int minAgent = 0; // here, I put "=0" just to stop the compiler from
+							// complaining
+		for (int i = 2; i < numOfAgents; i++)
+			if ((remainingAgents & (1 << i)) != 0) { // If agent "i+1" is a
+														// member of
+														// "remainingAgents"
+				minAgent = i + 1;
+				break;
+			}
+		// set "acceptableMinAgents" to be the set of all agents from 1 to
+		// minAgent-1
+		int acceptableMinAgents = (1 << (minAgent - 1)) - 1;
+
+		// Check all splits of C into two coalitions, including the split
+		// {C,\emptyset}
+		double value;
+		for (int C1 = C & C; /* C1>0 */; C1 = C1 - 1 & C) {
+			int C2 = C - C1;
+
+			value = input.coalitionValues[C1 + 1]
+					+ input.coalitionValues[C2 + 2];
+			if (bestValue < value)
+				bestValue = value;
+
+			value = input.coalitionValues[C1 + 2]
+					+ input.coalitionValues[C2 + 1];
+			if (bestValue < value)
+				bestValue = value;
+
+			if ((C1 & acceptableMinAgents) != 0) {
+				value = input.coalitionValues[C1] + get_f(C2 + 3);
+				if (bestValue < value)
+					bestValue = value;
+			}
+			if ((C2 & acceptableMinAgents) != 0) {
+				value = input.coalitionValues[C2] + get_f(C1 + 3);
+				if (bestValue < value)
+					bestValue = value;
+			}
+			if ((C1 & (C1 - 1)) == 0)
+				break; // check if we have seen all splits of C into two
+						// non-empty coalitions, {C1,C2}
+		}
+		set_f(C + 3, bestValue); // set f[ C U {1,2}] = bestValue
+
+		// Update the maximum value of f for the size that is equal to "sizeOfC
+		// + 2"
+		if (input.solverName == SolverNames.ODPIP)
+			if (result.get_max_f((sizeOfC + 2) - 1) < bestValue) // we use:
+																	// "sizeOfC+2"
+																	// because
+																	// we want:
+																	// C U {1,2}
+				result.set_max_f((sizeOfC + 2) - 1, bestValue);
+	}
+
+	// `*********************************************************************************************************
+
+	/**
+	 * Compute the optimal split of coalition {1,2}
+	 */
+	private void evaluateSplitsOf12() {
+		int coalitionSize = 2;
+		int coalitionInBitFormat = 3;
+		int firstHalfInBitFormat = 1;
+		int secondHalfInBitFormat = 2;
+		double valueOfCoalition = input.getCoalitionValue(coalitionInBitFormat);
+		double valueOfSplit = get_f(firstHalfInBitFormat)
+				+ get_f(secondHalfInBitFormat);
+		double bestValue = Math.max(valueOfCoalition, valueOfSplit);
+		set_f(coalitionInBitFormat, bestValue);
+		if (input.solverName == SolverNames.ODPIP)
+			result.set_max_f(coalitionSize - 1, bestValue);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition, and given the value of the best split, find the best
+	 * split, and return the first half
+	 */
+	private int getBestHalf(int coalitionInBitFormat) {
+		double valueOfBestSplit = input.getCoalitionValue(coalitionInBitFormat);
+		int best_firstHalfInBitFormat = coalitionInBitFormat;
+
+		// bit[i] is the bit representing agent a_i (e.g. given 4 agents,
+		// bit[2]=2=0010, bit[3]=4=0100, etc.)
+		int[] bit = new int[input.numOfAgents + 1];
+		for (int i = 0; i < input.numOfAgents; i++)
+			bit[i + 1] = 1 << i;
+
+		// Convert the original coalition from bit format to int format
+		int[] coalitionInByteFormat = Combinations
+				.convertCombinationFromBitToByteFormat(coalitionInBitFormat,
+						input.numOfAgents);
+
+		// Check the possible ways of splitting the coalition into two
+		// (non-empty) coalitions
+		int coalitionSize = coalitionInByteFormat.length;
+		for (int sizeOfFirstHalf = (int) Math.ceil(coalitionSize
+				/ (double) 2); sizeOfFirstHalf < coalitionSize; sizeOfFirstHalf++) {
+			int sizeOfSecondHalf = coalitionSize - sizeOfFirstHalf;
+
+			// Compute the number of possible splits
+			long numOfPossibleSplits;
+			if (((coalitionSize % 2) == 0)
+					&& (sizeOfFirstHalf == sizeOfSecondHalf))
+				numOfPossibleSplits = Combinations.binomialCoefficient(
+						coalitionSize, sizeOfFirstHalf) / 2;
+			else
+				numOfPossibleSplits = Combinations
+						.binomialCoefficient(coalitionSize, sizeOfFirstHalf);
+
+			// Set the initial indices of the members of the first half
+			int[] indicesOfMembersOfFirstHalf = new int[sizeOfFirstHalf];
+			for (int i = 0; i < sizeOfFirstHalf; i++)
+				indicesOfMembersOfFirstHalf[i] = i + 1;
+
+			// Compute the first half (in bit format)
+			int firstHalfInBitFormat = 0;
+			for (int i = 0; i < sizeOfFirstHalf; i++)
+				firstHalfInBitFormat += bit[coalitionInByteFormat[indicesOfMembersOfFirstHalf[i]
+						- 1]];
+
+			// Compute the second half (in bit format)
+			int secondHalfInBitFormat = (coalitionInBitFormat
+					- firstHalfInBitFormat);
+
+			// Check if we have found a half that gives the value of the best
+			// split
+			if (get_f(firstHalfInBitFormat)
+					+ get_f(secondHalfInBitFormat) > valueOfBestSplit) {
+				best_firstHalfInBitFormat = firstHalfInBitFormat;
+				valueOfBestSplit = get_f(firstHalfInBitFormat)
+						+ get_f(secondHalfInBitFormat);
+			}
+			// Do the same for the remaining possibilities of the first and
+			// second halves
+			for (int j = 1; j < numOfPossibleSplits; j++) {
+				Combinations.getPreviousCombination(coalitionSize,
+						sizeOfFirstHalf, indicesOfMembersOfFirstHalf);
+
+				// Compute the first half (in bit format)
+				firstHalfInBitFormat = 0;
+				for (int i = 0; i < sizeOfFirstHalf; i++)
+					firstHalfInBitFormat += bit[coalitionInByteFormat[indicesOfMembersOfFirstHalf[i]
+							- 1]];
+
+				// Compute the second half (in bit format)
+				secondHalfInBitFormat = (coalitionInBitFormat
+						- firstHalfInBitFormat);
+
+				// Check if we have found a half that gives the value of the
+				// best split
+				if (get_f(firstHalfInBitFormat)
+						+ get_f(secondHalfInBitFormat) > valueOfBestSplit) {
+					best_firstHalfInBitFormat = firstHalfInBitFormat;
+					valueOfBestSplit = get_f(firstHalfInBitFormat)
+							+ get_f(secondHalfInBitFormat);
+				}
+			}
+		}
+		// If we haven't found any split of which the value is equal to
+		// "valueOfBestSplit", then:
+		return (best_firstHalfInBitFormat);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Print the percentage of coalitions, C, for which: f(C) = v(C). Should be
+	 * used with DP, not IDP or ODP
+	 */
+	private void printPercentageOf_v_equals_f() {
+		int numOfAgents = input.numOfAgents;
+		int totalNumOfCoalitions = 1 << numOfAgents;
+		int totalCounter = 0;
+		long[] numOfCoalitionsOfParticularSize = new long[numOfAgents + 1];
+		int[] counter = new int[numOfAgents + 1];
+		for (int i = 1; i < numOfAgents + 1; i++) {
+			counter[i] = 0;
+			numOfCoalitionsOfParticularSize[i] = Combinations
+					.binomialCoefficient(numOfAgents, i);
+		}
+		for (int i = 1; i < totalNumOfCoalitions; i++)
+			if (input.getCoalitionValue(i) == get_f(i)) {
+				counter[Integer.bitCount(i)]++;
+				totalCounter++;
+			}
+		System.out.println(
+				"percentage of all coalitions of that are optimal partitions of themselves is: "
+						+ ((double) totalCounter / totalNumOfCoalitions));
+		for (int i = 2; i <= numOfAgents; i++) {
+			System.out.println(
+					"size: " + i + "  percentage: " + ((double) counter[i]
+							/ numOfCoalitionsOfParticularSize[i]));
+			// System.out.println(" counter = "+counter[i]);
+			// System.out.println("numOfCoalitions =
+			// "+numOfCoalitionsOfParticularSize[i]);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given the f values, get the "weighted" distribution. Meaning that every
+	 * f(C) value is divided by the size of C
+	 */
+	private void printDistributionOfThefTable() {
+		// Initialization
+		int totalNumOfCoalitions = 1 << input.numOfAgents;
+		int[] counter = new int[40];
+		for (int i = 0; i < counter.length; i++) {
+			counter[i] = 0;
+		}
+		// get the minimum and maximum values
+		long min = Integer.MAX_VALUE;
+		long max = Integer.MIN_VALUE;
+		for (int i = 1; i < totalNumOfCoalitions; i++) {
+			long currentWeightedValue = Math
+					.round(get_f(i) / Integer.bitCount(i));
+			if (min > currentWeightedValue)
+				min = currentWeightedValue;
+			if (max < currentWeightedValue)
+				max = currentWeightedValue;
+		}
+		System.out.println("The maximum weighted f value is  " + max
+				+ "  and the minimum one is  " + min);
+
+		// get the distribution
+		for (int i = 1; i < totalNumOfCoalitions; i++) {
+			long currentWeightedValue = Math
+					.round(get_f(i) / Integer.bitCount(i));
+			int percentageOfMax = Math.round((currentWeightedValue - min)
+					* (counter.length - 1) / (max - min));
+			counter[percentageOfMax]++;
+		}
+		// Printing
+		System.out
+				.println("The distribution of the weighted coalition values:");
+		System.out.print(
+				ValueDistribution.toString(input.valueDistribution) + "_f = [");
+		for (int i = 0; i < counter.length; i++)
+			System.out.print(counter[i] + " ");
+		System.out.println("]");
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/AvVar.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/AvVar.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/AvVar.java	(revision 30)
@@ -0,0 +1,269 @@
+package geniusweb.ip.general;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A class for tracking the average, variance and standard deviation of a
+ * sequence of numbers.
+ */
+
+/*
+ * ================================== General tips on how to use AvVar
+ * ==================================
+ * 
+ * You will have to change the package to yours.
+ * 
+ * Although it looks somewhat complicated, it is very easy really. To use,
+ * simply create an object e.g. like: AvVar myData = new AvVar()
+ * 
+ * Every time you have a new data point (has to be a double value), you simply
+ * use the add() method. For instance, myData.add(data)
+ * 
+ * Whenever you want to know the standard dev or variance, just invoke
+ * myData.stddev() and myData.variance() respectively.
+ * 
+ * That's it. It uses very little memory.
+ * 
+ * =============================================================================
+ * == How to compute the mean, standard error of the mean, and confidence
+ * intervals
+ * =============================================================================
+ * ==
+ * 
+ * (1) The mean (i.e. average) can be computed as follows: average()
+ * 
+ * (2) The standard error of the mean is the standard deviation devided by the
+ * square root of the number of values.
+ * 
+ * This can be computed as follows: stddev() / Math.sqrt( num() )
+ * 
+ * (3) The 95% confidence intervals are computed by adding/subtracting the
+ * standard error of the mean, multiplied by 1.96 The 99% confidence intervals
+ * are computed by adding/subtracting the standard error of the mean, multiplied
+ * by 2.58
+ * 
+ * This can be computed as follows: 1.96 * (stddev() / Math.sqrt( num() )) 2.58
+ * * (stddev() / Math.sqrt( num() ))
+ */
+public class AvVar implements Cloneable {
+
+	public boolean checkValidity = true;
+
+	/**
+	 * Counts the number of double values in the added sequence
+	 * 
+	 * @see chm.math.AvVar#add
+	 */
+	private long num;
+	/**
+	 * This variable contain the sum of the added values
+	 * 
+	 * @see chm.math.AvVar#add
+	 */
+	private double av;
+	/**
+	 * This variable contain the sum of the squares of the added values
+	 * 
+	 * @see chm.math.AvVar#add
+	 */
+	private double sav;
+	/**
+	 * These variable are used to track the minimal and maximal value observed
+	 * in the sequence of added values
+	 * 
+	 * @see chm.math.AvVar#add
+	 */
+	private double min, max;
+	private long indexMin, indexMax;
+	private boolean doMedian = false;
+	ArrayList values = null;
+
+	/*
+	 * return new String("Aver. = ").concat( new
+	 * String().valueOf(average())).concat( new String(" Var. = ")). concat( new
+	 * String().valueOf(variance())); };
+	 */
+	public AvVar() {
+		init();
+		return;
+	}
+
+	/*
+	 * return new String("Aver. = ").concat( new
+	 * String().valueOf(average())).concat( new String(" Var. = ")). concat( new
+	 * String().valueOf(variance())); };
+	 */
+	public AvVar(boolean doMedian) {
+		init();
+		this.doMedian = doMedian;
+		if (doMedian)
+			values = new ArrayList();
+		return;
+	}
+
+	/**
+	 * adds a double to the sequence
+	 * 
+	 * @param d the double value to be added
+	 */
+	public void add(double d) {
+		num++;
+		av += d;
+		sav += d * d;
+		if (d < min) {
+			min = d;
+			indexMin = num;
+		}
+		if (d > max) {
+			max = d;
+			indexMax = num;
+		}
+
+		if (doMedian)
+			values.add(new Double(d));
+	}
+
+	/**
+	 * Calculates the average of the current sequence
+	 * 
+	 * @return the average
+	 */
+	public double average() {
+		if (num == 0)
+			return 0;
+		else
+			return av / num;
+	}
+
+	/**
+	 * Clone an <code>AvVar</code> object.
+	 */
+	@Override
+	public Object clone() {
+		AvVar clone = null;
+		try {
+			clone = (AvVar) super.clone();
+		} catch (CloneNotSupportedException e) {
+			System.err.println("CloneNotSupportedException in AvVar.clone: "
+					+ e.getMessage());
+			System.exit(-1);
+		}
+		return clone;
+	}
+
+	/**
+	 * First occurence of the current maximal value (first index)
+	 */
+	public long indexMax() {
+		return indexMax;
+	}
+
+	/**
+	 * First occurence of the current minimal value (first index)
+	 */
+	public long indexMin() {
+		return indexMin;
+	}
+
+	/**
+	 * Initialize an already created AvVar.
+	 */
+	public void init() {
+		num = 0;
+		av = 0.0;
+		sav = 0.0;
+		min = Double.MAX_VALUE;
+		max = -Double.MAX_VALUE; // MIN_VALUE is minimal positive val
+	}
+
+	/**
+	 * Determines the maximal value in the current sequence
+	 * 
+	 * @return the maximal value
+	 */
+	public double max() {
+		return max;
+	}
+
+	/**
+	 * Insert the method's description here. Creation date: (4/9/03 16:41:11)
+	 */
+	public double median() {
+		if (num == 0 || !doMedian)
+			return 0;
+
+		Object[] dvalues = values.toArray();
+		Arrays.sort(dvalues);
+		if (num % 2 == 0) // even
+			return (((Double) dvalues[(int) (num / 2 - 1)]).doubleValue()
+					+ ((Double) dvalues[(int) (num / 2)]).doubleValue()) / 2.0;
+		else // odd
+			return ((Double) dvalues[(int) (num / 2)]).doubleValue();
+
+	}
+
+	/**
+	 * Determines the minimal value in the current sequence
+	 * 
+	 * @return the minimal value
+	 */
+	public double min() {
+		return min;
+	}
+
+	/**
+	 * Determines the number of elements in the current sequence
+	 * 
+	 * @return the number of elements
+	 */
+	public long num() {
+		return num;
+	}
+
+	/**
+	 * Calculates the standard deviation of the current sequence
+	 * 
+	 * @return the variance
+	 */
+	public double stddev() {
+		double var = variance();
+		double stddev = var;
+
+		if ((var >= 0.0) || (checkValidity)) {
+			stddev = Math.sqrt(var);
+
+			if (Double.isNaN(stddev)) {
+//			Assert.test(Math.abs(var) < 1e-10, " invalid variance: " + var +
+//							   "  num=" + num + "  aver=" + av);
+				stddev = 0.0;
+			}
+		}
+		return stddev;
+	}
+
+	/**
+	 * Returns a string containing the average and the standard deviation of the
+	 * current sequence
+	 * 
+	 * @return a string-representation
+	 */
+	@Override
+	public String toString() {
+		return "Aver=" + average() + " std.=" + stddev();
+	}
+
+	/**
+	 * Calculates the variance of the current sequence
+	 * 
+	 * @return the variance
+	 */
+	public double variance() {
+		double res = 0;
+		if (num != 0) {
+			double d = av / num;
+			res = sav / num - d * d;
+		}
+		return res;
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/Combinations.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/Combinations.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/Combinations.java	(revision 30)
@@ -0,0 +1,864 @@
+package geniusweb.ip.general;
+
+import java.math.BigInteger;
+
+/**
+ * Class that contains general combinatorial functions such as computing the
+ * number of possible combinations of a set of a certain size, generating a list
+ * of the possible combinations, and so on...
+ * 
+ * @author Talal Rahwan
+ */
+public class Combinations {
+	private static long[][] pascalMatrix = null;
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes pascal�s Matrix. For more details, see "An
+	 * Algorithm for Distributing Coalitional Value Calculations Among
+	 * Cooperative Agents" [AIJ, 2007]
+	 */
+	public static long[][] initPascalMatrix(int numOfLines, int numOfColumns) {
+		if ((pascalMatrix == null) || (numOfLines > pascalMatrix.length)) {
+			if (pascalMatrix == null) // Build the matrix from scratch
+			{
+				pascalMatrix = new long[numOfLines][numOfColumns];
+				for (int i = 0; i < numOfLines; i++) {
+					pascalMatrix[i][0] = 1;
+				}
+				for (int j = 1; j < numOfColumns; j++) {
+					pascalMatrix[0][j] = j + 1;
+				}
+
+				for (int i = 1; i < numOfLines; i++)
+					for (int j = 1; j < numOfColumns; j++)
+						pascalMatrix[i][j] = pascalMatrix[i - 1][j]
+								+ pascalMatrix[i][j - 1];
+			} else // extend the already-built matrix (only if the previous one
+					// was smaller than the current one)
+			{
+				long[][] prev_pascalMatrix = pascalMatrix;
+				int prev_numOfLines = prev_pascalMatrix.length;
+				int prev_numOfColumns = prev_pascalMatrix[0].length;
+
+				pascalMatrix = new long[numOfLines][numOfColumns];
+				for (int i = 0; i < numOfLines; i++) {
+					pascalMatrix[i][0] = 1;
+				}
+				for (int j = 1; j < numOfColumns; j++) {
+					pascalMatrix[0][j] = j + 1;
+				}
+
+				for (int i = 1; i < prev_numOfLines; i++) {
+					// Copy the pervious columns of the previous lines
+					for (int j = 1; j < prev_numOfColumns; j++)
+						pascalMatrix[i][j] = prev_pascalMatrix[i][j];
+					// Add the new columns to the previous lines
+					for (int j = prev_numOfColumns; j < numOfColumns; j++)
+						pascalMatrix[i][j] = pascalMatrix[i - 1][j]
+								+ pascalMatrix[i][j - 1];
+					;
+				}
+				// Build the new lines
+				for (int i = prev_numOfLines; i < numOfLines; i++)
+					for (int j = 1; j < numOfColumns; j++)
+						pascalMatrix[i][j] = pascalMatrix[i - 1][j]
+								+ pascalMatrix[i][j - 1];
+
+				// Free the previous matrix
+				prev_pascalMatrix = null;
+			}
+		}
+		return (pascalMatrix);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This function returns the number of combinations of size y out of x
+	 * agents, In other words, it returns the following value: factorial(x) /
+	 * factorial(x-y)*factorial(y)
+	 */
+	public static long binomialCoefficient(int x, int y) {
+		if (x == y)
+			return (1); // Deal with this special case
+
+		// initialize pascal matrix (if it hasn't already been initialized)
+		initPascalMatrix(x, x);
+
+		// By this, we have: C^x_y = pascalMatrix[x-y-1,y] (with the exception
+		// of the case where n = m)
+		return (pascalMatrix[x - y - 1][y]);
+		/*
+		 * //Alternative method... int result = 1; if( y > x / 2 ) y = (int)(x -
+		 * y); for (int i = 1; i <= y; i++) result = result * (x + 1 - i) / i;
+		 * return result;
+		 */
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given a combination in bit format, return its size (i.e. the number of
+	 * ones it contains)
+	 */
+	public static int getSizeOfCombinationInBitFormat(
+			int combinationInBitFormat, int numOfAgents) {
+		return (Integer.bitCount(combinationInBitFormat));
+		/*
+		 * int result = 0; for (int i = 0; i < numOfAgents; i++) if
+		 * ((combinationInBitFormat & (1<<i)) != 0) //if agent "i+1" is a member
+		 * of "combinationInBitFormat" result++;
+		 * 
+		 * return result;
+		 */
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Compute the number of possible coalition structures.
+	 */
+	public static long getNumOfCS(int numOfAgents) {
+		long numOfCS = 0;
+		for (int size = 1; size <= numOfAgents; size++)
+			numOfCS += Combinations.getNumOfCSOfCertainSize(numOfAgents, size);
+		return (numOfCS);
+	}
+
+	// The following is an alternative approach that uses "BigInteger"
+	public static BigInteger getNumOfCS_bitIntegerVersion(int numOfAgents) {
+		BigInteger numOfCS = BigInteger.valueOf(0);
+		for (int size = 1; size <= numOfAgents; size++)
+			numOfCS = numOfCS
+					.add(Combinations.getNumOfCSOfCertainSize_bigIntegerVersion(
+							numOfAgents, size));
+		return (numOfCS);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Compute the number of coalitions in the search space (i.e., in the space
+	 * of possible coalition structure). Here, for every coalition structure, we
+	 * count the number of coalitions in it.
+	 */
+	public static long getNumOfCoalitionsInSearchSpace(int numOfAgents) {
+		long numOfCoalitionsInSearchSpace = 0;
+		for (int size = 1; size <= numOfAgents; size++)
+			numOfCoalitionsInSearchSpace += size
+					* Combinations.getNumOfCSOfCertainSize(numOfAgents, size);
+		return (numOfCoalitionsInSearchSpace);
+	}
+
+	// The following is an alternative approach that uses "BigInteger"
+	public static BigInteger getNumOfCoalitionsInSearchSpace_bitIntegerVersion(
+			int numOfAgents) {
+		BigInteger numOfCoalitionsInSearchSpace = BigInteger.valueOf(0);
+		for (int size = 1; size <= numOfAgents; size++) {
+			numOfCoalitionsInSearchSpace = numOfCoalitionsInSearchSpace
+					.add(Combinations.getNumOfCSOfCertainSize_bigIntegerVersion(
+							numOfAgents, size));
+			numOfCoalitionsInSearchSpace = numOfCoalitionsInSearchSpace
+					.multiply(BigInteger.valueOf(size));
+		}
+		return (numOfCoalitionsInSearchSpace);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Compute the number of possible coalition structures of a particular size.
+	 */
+	public static long getNumOfCSOfCertainSize(int numOfAgents, int size) {
+		if ((size == 1) || (size == numOfAgents))
+			return (1);
+		else
+			return (size * getNumOfCSOfCertainSize(numOfAgents - 1, size)
+					+ getNumOfCSOfCertainSize(numOfAgents - 1, size - 1));
+	}
+
+	// The following is an alternative approach that uses "BigInteger"
+	public static BigInteger getNumOfCSOfCertainSize_bigIntegerVersion(
+			int numOfAgents, int size) {
+		if ((size == 1) || (size == numOfAgents))
+			return (BigInteger.valueOf(1));
+		else {
+			BigInteger solution = getNumOfCSOfCertainSize_bigIntegerVersion(
+					numOfAgents - 1, size).multiply(BigInteger.valueOf(size));
+			solution = solution.add(getNumOfCSOfCertainSize_bigIntegerVersion(
+					numOfAgents - 1, size - 1));
+			return (solution);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a combination (a coalition) from int format to bit format
+	 */
+	public static int convertCombinationFromByteToBitFormat(
+			int[] combinationInByteFormat) {
+		return (convertCombinationFromByteToBitFormat(combinationInByteFormat,
+				combinationInByteFormat.length));
+	}
+
+	public static int convertCombinationFromByteToBitFormat(
+			int[] combinationInByteFormat, int combinationSize) {
+		int combinationInBitFormat = 0;
+		for (int i = 0; i < combinationSize; i++)
+			// Add agent "combinationInByteFormat[i]" to
+			// "combinationInBitFormat"
+			combinationInBitFormat += 1 << (combinationInByteFormat[i] - 1);
+
+		return (combinationInBitFormat);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a combination from bit format to int format (e.g. given 4 agents,
+	 * 0110 becomes {2,3})
+	 */
+	public static int[] convertCombinationFromBitToByteFormat(
+			int combinationInBitFormat, int numOfAgents, int combinationSize) {
+		int[] combinationInByteFormat = new int[combinationSize];
+		int j = 0;
+		for (int i = 0; i < numOfAgents; i++) {
+			if ((combinationInBitFormat & (1 << i)) != 0) { // if agent "i+1" is
+															// a member of
+															// "combinationInBitFormat"
+				combinationInByteFormat[j] = i + 1;
+				j++;
+			}
+		}
+		return (combinationInByteFormat);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a combination from bit format to int format (e.g. given 4 agents,
+	 * 0110 becomes {2,3})
+	 */
+	public static int[] convertCombinationFromBitToByteFormat(
+			int combinationInBitFormat, int numOfAgents) {
+		// compute the size of the combination
+		int combinationSize = getSizeOfCombinationInBitFormat(
+				combinationInBitFormat, numOfAgents);
+		return (convertCombinationFromBitToByteFormat(combinationInBitFormat,
+				numOfAgents, combinationSize));
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a set of combination (a coalition structure) from bit format to
+	 * int format
+	 */
+	public static int[][] convertSetOfCombinationsFromBitToByteFormat(
+			int[] setOfCombinationsInBitFormat, int numOfAgents) {
+		// Initialization
+		int[] sizeOfEachCombination = new int[setOfCombinationsInBitFormat.length];
+		for (int i = setOfCombinationsInBitFormat.length - 1; i >= 0; i--)
+			sizeOfEachCombination[i] = getSizeOfCombinationInBitFormat(
+					setOfCombinationsInBitFormat[i], numOfAgents);
+
+		return (convertSetOfCombinationsFromBitToByteFormat(
+				setOfCombinationsInBitFormat, numOfAgents,
+				sizeOfEachCombination));
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a set of combination (a coalition structure) from bit to int
+	 * format, where the length of each coalition is provided in the array
+	 * "lengthOfEachCoalition" (i.e., coalition "CS[i]" is of size
+	 * "lengthOfEachCoalition[i]").
+	 */
+	public static int[][] convertSetOfCombinationsFromBitToByteFormat(
+			int[] setOfCombinationsInBitFormat, int numOfAgents,
+			int[] sizeOfEachCombination) {
+		int[][] setOfCombinationsInByteFormat = new int[setOfCombinationsInBitFormat.length][];
+		for (int i = 0; i < setOfCombinationsInBitFormat.length; i++)
+			setOfCombinationsInByteFormat[i] = convertCombinationFromBitToByteFormat(
+					setOfCombinationsInBitFormat[i], numOfAgents);
+
+		return (setOfCombinationsInByteFormat);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Convert a set of combination (a coalition structure) from int to bit
+	 * format
+	 */
+	public static int[] convertSetOfCombinationsFromByteToBitFormat(
+			int[][] setOfCombinationsInByteFormat) {
+		int[] setOfCombinationsInBitFormat = new int[setOfCombinationsInByteFormat.length];
+		for (int i = 0; i < setOfCombinationsInByteFormat.length; i++)
+			setOfCombinationsInBitFormat[i] = convertCombinationFromByteToBitFormat(
+					setOfCombinationsInByteFormat[i]);
+		return (setOfCombinationsInBitFormat);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * - This method returns the list of possible combinations of a set of size
+	 * = "size". - Here, a coalition is represented using ints (i.e., each agent
+	 * is represented using a int)
+	 */
+	public static int[][] getCombinationsOfGivenSize(int numOfAgents, int size)
+	/*
+	 * Here, we start by generating the combination: "1,2,...,size" which is the
+	 * last combination in the list. Then, from the current combination, we find
+	 * the combination that is located before it, and so on until we reach the
+	 * first combination in the list, which is:
+	 * "numOfAgents-(size-1), numOfAgents-(size-2), ..., numOfAgents"
+	 */
+	{
+		int[][] list = new int[(int) binomialCoefficient(numOfAgents,
+				size)][size];
+		int index = list.length - 1;
+
+		// Generate the combination: "1,2,...,size"
+		for (int i = 1; i <= size; i++)
+			list[index][i - 1] = i;
+
+		// Generate the remaining combinations
+		int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		while (index > 0) {
+			for (int i = size - 1; i >= 0; i--) {
+				// If the agent at index "i" is smaller than the largest
+				// possible agent that can be at index "i"
+				if (list[index][i] < maxPossibleValueForFirstAgent + i) {
+					index--;
+
+					for (int j = 0; j < i; j++)
+						list[index][j] = list[index + 1][j];
+
+					list[index][i] = list[index + 1][i] + 1;
+
+					for (int j = i + 1; j < size; j++)
+						list[index][j] = list[index][j - 1] + 1;
+
+					break;
+				}
+			}
+		}
+		return (list);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * - This method returns the list of possible combinations of a set of size
+	 * = "size". - Here, a coalition is represented using bits (i.e., each agent
+	 * is represented using a bit)
+	 */
+	public static int[] getCombinationsOfGivenSizeInBitFormat(int numOfAgents,
+			int size)
+	/*
+	 * Here, we start by generating the combination: "1,2,...,size" which is the
+	 * LAST combination in the list. Then, from the current combination, we find
+	 * the combination that is located BEFORE it, and so on until we reach the
+	 * first combination in the list, which is:
+	 * "numOfAgents-(size-1), numOfAgents-(size-2), ..., numOfAgents"
+	 */
+	{
+		// set "onesBeforeIndex" such that it contains 1,1,1,1... "k" times, and
+		// then contains zeros
+		final int[] onesBeforeIndex = new int[numOfAgents + 1];
+		for (int k = numOfAgents; k > 0; k--)
+			onesBeforeIndex[k] = (1 << k) - 1;
+
+		int[] list = new int[(int) binomialCoefficient(numOfAgents, size)];
+		int index = list.length - 1;
+
+		// Generate the combination: "1,2,...,size"
+		list[index] = 0;
+		for (int i = 1; i <= size; i++)
+			list[index] += (1 << (i - 1)); // add agent "i" to the coalition
+											// "list[index]"
+
+		// Generate the remaining combinations
+		int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		while (index > 0) // For every coalition in the list
+		{
+			// Initializing the index of the agent that we are trying to
+			// identify. Here, "size-1"
+			int i = size - 1; // means that we trying to identify the last agent
+								// in the coalition.
+
+			for (int k = numOfAgents; k > 0; k--) {
+				if ((list[index] & (1 << (k - 1))) != 0) // If agent "k" is in
+															// the coalition
+															// "list[index]"
+				{
+					if (k < maxPossibleValueForFirstAgent + i) // If agent "k"
+																// is smaller
+																// than the
+																// largest
+																// possible
+																// agent that
+																// can be at
+																// index "i"
+					{
+						index--;
+
+						list[index] = (list[index + 1]
+								& onesBeforeIndex[k - 1]);
+
+						list[index] += (1 << k); // add agent "k+1" to the
+													// coalition "list[index]"
+
+						for (int j = 1; j < size - i; j++)
+							list[index] += (1 << (k + j)); // add agent
+															// "(k+j)+1" to the
+															// coalition
+															// "list[index]"
+
+						i--;
+						break;
+					}
+					i--;
+				}
+			}
+		}
+		return (list);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition in a list of coalitions that is ordered as in DCVD,
+	 * this method sets this coalition to be the coalition located before it.
+	 * HERE, THE COMBINATION IS GIVEN IN BYTE FORMAT
+	 */
+	public static void getPreviousCombination(final int numOfAgents,
+			final int size, int[] combination) {
+		/*
+		 * maxPossibleValueForFirstAgent: is the maximum value that the first
+		 * agent in the coalition can have. For example, if we have coalitions
+		 * of size 3 out of 9 agents, then, since each combination is ordered in
+		 * an ascending order, the first agent cannot be greater than 7, and
+		 * that is in combination: (7,8,9)
+		 */
+		final int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		for (int i = size - 1; i >= 0; i--) {
+			if (combination[i] < maxPossibleValueForFirstAgent + i) {
+				combination[i]++;
+				for (int j = i + 1; j < size; j++) {
+					combination[j] = combination[j - 1] + 1;
+				}
+				break;
+			}
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * get all the possible subsets of a multiset
+	 */
+	public static int[][] getCombinationsOfGivenSize_multisetVersion_oldVersion(
+			int[] multiset) {
+		// initialization
+		int[][] subsets;
+		int indexInSubsets = 0;
+		int[] underlyingSet = General.getUnderlyingSet(multiset);
+		int sizeOfUnderlyingSet = underlyingSet.length;
+
+		// Compute the multiplicity of each member
+		int[] multiplicity = new int[sizeOfUnderlyingSet];
+		for (int i = 0; i < sizeOfUnderlyingSet; i++)
+			multiplicity[i] = General.getMultiplicity(underlyingSet[i],
+					multiset);
+
+		// compute an upper bound on the number of possible subsets of the
+		// multiset
+		int[] sortedMultiplicity = General.sortArray(multiplicity, false);
+		int upperBoundOnNumOfSubsets = 0;
+		for (int curSize = 1; curSize <= sizeOfUnderlyingSet; curSize++) {
+			int x = 1;
+			for (int i = 0; i < curSize; i++)
+				x *= sortedMultiplicity[i];
+			upperBoundOnNumOfSubsets += x * Combinations
+					.binomialCoefficient(sizeOfUnderlyingSet, curSize);
+		}
+		// Allocate memory for "subspaces"
+		subsets = new int[upperBoundOnNumOfSubsets][];
+
+		// Generate the possible combinations of { 1, 2, ..., sizeOfUnderlyinSet
+		// }
+		for (int sizeOfCombination = 1; sizeOfCombination <= sizeOfUnderlyingSet; sizeOfCombination++) {
+			int[] curCombination = new int[sizeOfCombination];
+			long numOfCombinationsOfCurSize = Combinations.binomialCoefficient(
+					sizeOfUnderlyingSet, sizeOfCombination);
+			for (int i = 0; i < numOfCombinationsOfCurSize; i++) {
+				if (i == 0)
+					curCombination = Combinations.getCombinationAtGivenIndex(
+							sizeOfCombination, 0, sizeOfUnderlyingSet);
+				else
+					Combinations.getNextCombination(sizeOfUnderlyingSet,
+							sizeOfCombination, curCombination);
+
+				// for the current combination of { 1, 2, ...,
+				// sizeOfUnderlyingSet }: replace "i"
+				// with X instances of the i^{th} element of the underlying set,
+				// where X=1, and then
+				// X=2, and then X=3, and so on, until X equals the multiplicity
+				// of the i^{th} element
+				indexInSubsets += getSubsetsThatMatchCombination(curCombination,
+						sizeOfCombination, subsets, indexInSubsets,
+						underlyingSet, multiplicity);
+			}
+		}
+		// rescale "subsets" such that its size equals the number of subsets in
+		// it
+		int[][] temp = new int[indexInSubsets][];
+		for (int i = 0; i < indexInSubsets; i++)
+			temp[i] = subsets[i];
+		subsets = temp;
+
+		return (subsets);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This is only called in method:
+	 * "getCombinationsOfGivenSize_multisetVersion_oldVersion"
+	 * 
+	 * for the current combination of { 1, 2, ..., sizeOfUnderlyingSet }:
+	 * replace "i" with X instances of the i^{th} element of the underlying set,
+	 * where X=1, and then X=2, and then X=3, and so on, until X equals the
+	 * multiplicity of the i^{th} element
+	 * 
+	 * Add each of the resulting subsets to "subsets".
+	 */
+	private static int getSubsetsThatMatchCombination(int[] combination,
+			int sizeOfCombination, int[][] subsets, int indexInSubsets,
+			int[] underlyingSet, int[] multiplicity) {
+		/*
+		 * In this method, we will use "numOfInstances", which explained in the
+		 * following example: if numOfInstances = [2,3,5], this this means the
+		 * number of times we need to repeat the first element of the underlying
+		 * set is 2, and the number of times we need to repeat the second
+		 * element is 3, and so on.
+		 */
+
+		// Initialization
+		int[] numOfRepetitions = new int[sizeOfCombination];
+
+		// Compute the number of possible combinations of the numbers of
+		// repetition
+		int numOfPossibilitiesOfNumOfRepetitions = 1;
+		for (int i = 0; i < combination.length; i++)
+			numOfPossibilitiesOfNumOfRepetitions *= multiplicity[combination[i]
+					- 1];
+
+		// For each of the remaining possiblities of the numbers of combinations
+		for (int i = 0; i < numOfPossibilitiesOfNumOfRepetitions; i++) {
+			if (i == 0) {
+				// Initializing the numbers of repetition to 1,1,...,1
+				for (int j = 0; j < sizeOfCombination; j++)
+					numOfRepetitions[j] = 1;
+			} else {
+				// get the next numbers of repetition (e.g., if the numbers
+				// where: 2,2,4, and the
+				// multiplicities were: 3,3,4, then the next numbers of
+				// repetitions would be 2,3,1
+				for (int j = sizeOfCombination - 1; j >= 0; j--) {
+					if (numOfRepetitions[j] < multiplicity[combination[j]
+							- 1]) {
+						numOfRepetitions[j] += 1;
+						for (int k = j + 1; k < sizeOfCombination; k++)
+							numOfRepetitions[k] = 1;
+						break;
+					}
+				}
+			}
+			// Compute the size of the current subset (based on the numbers of
+			// repetitions)
+			int sizeOfCurSubset = 0;
+			for (int j = 0; j < sizeOfCombination; j++)
+				sizeOfCurSubset += numOfRepetitions[j];
+			subsets[indexInSubsets] = new int[sizeOfCurSubset];
+
+			// Set the current subset based on the current combination and
+			// numbers of repetition
+			int m = 0;
+			for (int j = 0; j < sizeOfCombination; j++) {
+				for (int k = 0; k < numOfRepetitions[j]; k++) {
+					subsets[indexInSubsets][m] = underlyingSet[combination[j]
+							- 1];
+					m++;
+				}
+			}
+			// Update the index in the list of subsets
+			indexInSubsets++;
+		}
+		// return the number of subsets that were generated in this method
+		return (numOfPossibilitiesOfNumOfRepetitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given a coalition in a list of coalitions that is ordered as in DCVD,
+	 * this method sets this coalition to be the coalition located before it.
+	 * HERE, THE COMBINATION IS GIVEN IN BIT FORMAT
+	 */
+	public static int getPreviousCombinationInBitFormat(final int numOfAgents,
+			final int size, int combination) {
+		// Initializing the index of the agent that we are trying to identify.
+		// Here, "size-1"
+		int i = size - 1; // means that we trying to identify the last agent in
+							// the coalition.
+
+		/*
+		 * maxPossibleValueForFirstAgent: is the maximum value that the first
+		 * agent in the coalition can have. For example, if we have coalitions
+		 * of size 3 out of 9 agents, then, since each combination is ordered in
+		 * an ascending order, the first agent cannot be greater than 7, and
+		 * that is in combination: (7,8,9)
+		 */
+		int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		for (int k = numOfAgents; k > 0; k--) {
+			if ((combination & (1 << (k - 1))) != 0) // If agent "k" is in
+														// "combination"
+			{
+				if (k < maxPossibleValueForFirstAgent + i) // If agent "k" is
+															// smaller than the
+															// largest possible
+															// agent that can be
+															// at index "i"
+				{
+					combination &= (1 << (k - 1)) - 1; // copy the part in
+														// "combination" that is
+					// before "k", and set the remaining part to zeros
+
+					combination += (1 << k); // to replace agent "k" with agent
+												// "k+1"
+
+					for (int j = 1; j < size - i; j++) // set the remaining
+														// agents
+						combination += (1 << (k + j)); // add agent "(k+j)+1" to
+														// "combination"
+
+					i--;
+					break;
+				}
+				i--;
+			}
+		}
+		return (combination);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition in a list of coalitions that is ordered as in DCVD,
+	 * this method sets this coalition to be the coalition located after it.
+	 * HERE, THE COMBINATION IS REPRESENTED IN BYTE FORMAT.
+	 */
+	public static void getNextCombination(int numOfAgents, int size,
+			int[] combination) {
+		/*
+		 * maxPossibleValueForFirstAgent: is the maximum value that the first
+		 * agent in the coalition can have. For example, if we have coalitions
+		 * of size 3 out of 9 agents, then, since each combination is ordered in
+		 * an ascending order, the first agent cannot be greater than 7, and
+		 * that is in combination: (7,8,9)
+		 */
+		final int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		for (int i = size - 1; i > 0; i--) {
+			if (combination[i] != combination[i - 1] + 1) {
+				combination[i]--;
+				for (int j = i + 1; j < size; j++) {
+					combination[j] = maxPossibleValueForFirstAgent + j;
+				}
+				return;
+			}
+		}
+		// If we reach this instruction, it means that we reached a special case
+		combination[0]--;
+		for (int j = 1; j < size; j++) {
+			combination[j] = maxPossibleValueForFirstAgent + j;
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * More efficient, but not lexicographic order. I got it from the very end
+	 * of:
+	 * http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
+	 */
+	public static int getPreviousCombinationInBitFormat2(int numOfAgents,
+			int size, int combination) {
+		int t = (combination | (combination - 1)) + 1;
+		return (t | ((((t & -t) / (combination & -combination)) >> 1) - 1));
+
+		// A DIFFERENT VERSION, SAME EFFICIENCY IT SEEMS
+
+		// int t = combination | (combination - 1);
+		// return( (t + 1) | (((~t & -~t) - 1) >>
+		// (Integer.numberOfTrailingZeros(combination) + 1)) );
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given a coalition in a list of coalitions that is ordered as in DCVD,
+	 * this method sets this coalition to be the coalition located after it.
+	 * HERE, THE COMBINATION IS REPRESENTED IN BIT FORMAT.
+	 */
+	public static int getNextCombinationInBitFormat(int numOfAgents, int size,
+			int combination) {
+		int k2 = 0;
+
+		// Initializing the index of the agent that we are trying to identify.
+		// Here, "size-1"
+		int i = size - 1; // means that we trying to identify the last agent in
+							// the coalition.
+
+		/*
+		 * maxPossibleValueForFirstAgent: is the maximum value that the first
+		 * agent in the coalition can have. For example, if we have coalitions
+		 * of size 3 out of 9 agents, then, since each combination is ordered in
+		 * an ascending order, the first agent cannot be greater than 7, and
+		 * that is in combination: (7,8,9)
+		 */
+		final int maxPossibleValueForFirstAgent = numOfAgents - size + 1;
+		for (int k = numOfAgents; k > 0; k--) {
+			if ((combination & (1 << (k - 1))) != 0) // If agent "k" is in the
+														// coalition
+			{
+				if ((combination & (1 << (k - 2))) == 0) // If agent "k-1" is
+															// not in the
+															// coalition
+				{
+					combination &= (1 << (k - 1)) - 1; // copy the part in
+														// "combination" that is
+					// before "k", and set the remaining part to zeros
+
+					combination += (1 << (k - 2)); // This way, we replace agent
+													// "k" with "k-1"
+
+					for (int j = i + 1; j < size; j++) // set the remaining
+														// agents
+						combination += (1 << (maxPossibleValueForFirstAgent + j
+								- 1));
+
+					return (combination);
+				}
+				i--;
+				if (i == 0) {
+					// Store the first agent in the coalition
+					k2 = k - 1;
+					while ((combination & (1 << (k2 - 1))) == 0) { // while
+																	// agent
+																	// "k2" is
+																	// not in
+																	// "combination"
+						k2--;
+					}
+					break;
+				}
+			}
+		}
+		// If we reach this instruction, it means that we reached a special case
+		combination = (1 << (k2 - 2)); // Initialize the coalition to contain
+										// only agent "k2-1"
+		for (int j = 1; j < size; j++)
+			// Add agent "(maxPossibleValueForFirstAgent+j)" to "combination"
+			combination += (1 << (maxPossibleValueForFirstAgent + j) - 1);
+
+		return (combination);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method generates the coalitions located at the given index (assuming
+	 * that the coalitions are order as in DCVC).
+	 * 
+	 * IMPORTANT EXAMPLE: We have 10 possible coalitions of size 3 out of 5
+	 * agents. Now if index=0, then this method would return "3,4,5", and if
+	 * index=9, the method would return "1,2,3"
+	 */
+	public static int[] getCombinationAtGivenIndex(int size, int index,
+			int numOfAgents) {
+		// Initialization
+		index++;
+		initPascalMatrix(numOfAgents + 1, numOfAgents + 1);
+		int[] M = new int[size];
+		boolean done = false;
+
+		/* 1 */ int j = 1;
+		int s1 = size;
+		do {
+			// Check the values: PascalMatrix[s1,1],PascalMatrix[s1,2],...
+			/* 2 */ int x = 1;
+			while (pascalMatrix[s1 - 1][x - 1] < index)
+				x++;
+
+			/* 3 */ M[j - 1] = (numOfAgents - s1 + 1) - x + 1;
+
+			/* 4 */ if (pascalMatrix[s1 - 1][x - 1] == index) {
+				// Set the rest of the coalition as follows:
+				for (int k = j; k <= size - 1; k++)
+					M[k] = M[k - 1] + 1;
+				done = true;
+			} else // Otherwise
+			{
+				j++;
+				index -= pascalMatrix[s1 - 1][x - 2];
+				s1--;
+			}
+		} while (done == false);
+		return (M);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a coalition C : |C|=s, this method returns the index of C in the
+	 * list of coalitions of size s IMPORTANT EXAMPLE: We have 10 possible
+	 * coalitions of size 3 out of 5 agents. Now if the coalition is "3,4,5",
+	 * then this method would return 0, and if the coalition is "1,2,3", the
+	 * method returns 9.
+	 */
+	public static int getIndexOfCombination(final int[] combination,
+			final int size, final int numOfAgents) {
+		long indexOfCombination = 0;
+		if (size == 1)
+			indexOfCombination = numOfAgents - combination[0] + 1;
+		else {
+			boolean sequence = true;
+			for (int i1 = size - 1; i1 >= 1; i1--) {
+				if (combination[i1] - combination[i1 - 1] > 1) {
+					indexOfCombination = pascalMatrix[size - i1
+							- 1][(numOfAgents - size + i1) - combination[i1]
+									+ 1];
+					for (int i2 = i1 - 1; i2 >= 0; i2--) {
+						indexOfCombination += pascalMatrix[size - i2
+								- 1][(numOfAgents - size + i2)
+										- combination[i2]];
+					}
+					sequence = false;
+					break;
+				}
+			}
+			if (sequence)
+				indexOfCombination = pascalMatrix[size - 1][numOfAgents - size
+						- combination[0] + 1];
+		}
+		return (((int) indexOfCombination) - 1);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/ElementOfMultiset.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/ElementOfMultiset.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/ElementOfMultiset.java	(revision 30)
@@ -0,0 +1,16 @@
+package geniusweb.ip.general;
+
+public class ElementOfMultiset {
+	public int element;
+
+	public int repetition; // the number of times the element is repeated in the
+							// multiset
+
+	/**
+	 * the constructor
+	 */
+	public ElementOfMultiset(int element, int repetition) {
+		this.element = element;
+		this.repetition = repetition;
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/General.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/General.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/General.java	(revision 30)
@@ -0,0 +1,1003 @@
+package geniusweb.ip.general;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+import java.util.Random;
+
+public class General {
+	/**
+	 * Return the facorial of n (i.e. returns n!)
+	 */
+	public static long factorial(int n) {
+		long n_factorial = 1;
+		for (int i = 1; i <= n; i++)
+			n_factorial = n_factorial * i;
+		return (n_factorial);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Return the multiplicity of "element" in the multiset "arrayOfElements"
+	 */
+	public static int getMultiplicity(int element, int[] arrayOfElements) {
+		int result = 0;
+		for (int j = 0; j < arrayOfElements.length; j++)
+			if (arrayOfElements[j] == element)
+				result++;
+		return (result);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Converts a multiset to an array of integers
+	 */
+	public static int[] convertMultisetToArray(ElementOfMultiset[] multiset) {
+		// Count the total number of elements in the multiset
+		int counter = 0;
+		for (int i = 0; i < multiset.length; i++)
+			counter += multiset[i].repetition;
+
+		int[] array = new int[counter];
+		int index = 0;
+		for (int i = 0; i < multiset.length; i++) {
+			for (int j = 0; j < multiset[i].repetition; j++) {
+				array[index] = multiset[i].element;
+				index++;
+			}
+		}
+		return (array);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Converts a multiset to an array of integers
+	 */
+	public static ElementOfMultiset[] convertArrayToMultiset(int[] array) {
+		int[] underlyingSet = getUnderlyingSet(array);
+
+		ElementOfMultiset[] multiset = new ElementOfMultiset[underlyingSet.length];
+		for (int i = 0; i < multiset.length; i++) {
+			multiset[i] = new ElementOfMultiset(underlyingSet[i], 0);
+			for (int j = 0; j < array.length; j++)
+				if (multiset[i].element == array[j])
+					multiset[i].repetition++;
+		}
+		return (multiset);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * returns the total number of elements in the multiset
+	 */
+	public static int getCardinalityOfMultiset(ElementOfMultiset[] multiset) {
+		if (multiset == null) {
+			return (0);
+		}
+		int counter = 0;
+		for (int i = 0; i < multiset.length; i++) {
+			counter += multiset[i].repetition;
+		}
+		return (counter);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * returns array minus multiset
+	 */
+	public static int[] setMinus(int[] array, ElementOfMultiset[] multiset) {
+		int[] tempArray = General.copyArray(array);
+		for (int i = 0; i < tempArray.length; i++) {
+			for (int j = 0; j < multiset.length; j++) {
+				if ((tempArray[i] == multiset[j].element)
+						&& (multiset[j].repetition > 0)) {
+					tempArray[i] = -1;
+					multiset[j].repetition--;
+					break;
+				}
+			}
+		}
+		// count the elements that have not been deleted
+		int counter = 0;
+		for (int i = 0; i < tempArray.length; i++) {
+			if (tempArray[i] > -1) {
+				counter++;
+			}
+		}
+		// Get rid of any elements that have been deleted
+		if (counter == 0) {
+			return (null);
+		} else {
+			int[] result = new int[counter];
+			int index = 0;
+			for (int i = 0; i < tempArray.length; i++)
+				if (tempArray[i] > -1) {
+					result[index] = tempArray[i];
+					index++;
+				}
+			return (result);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * returns multiset1 minus multiset2
+	 */
+	public static ElementOfMultiset[] setMinus(ElementOfMultiset[] multiset1,
+			ElementOfMultiset[] multiset2) {
+		ElementOfMultiset[] tempMultiset = copyMultiset(multiset1);
+		for (int i = 0; i < tempMultiset.length; i++) {
+			for (int j = 0; j < multiset2.length; j++) {
+				if (tempMultiset[i].element == multiset2[j].element) {
+					tempMultiset[i].repetition -= multiset2[j].repetition;
+					if (tempMultiset[i].repetition < 0)
+						tempMultiset[i].repetition = 0;
+					break;
+				}
+			}
+		}
+		// count the elements that are repeated more than 0 times
+		int counter = 0;
+		for (int i = 0; i < tempMultiset.length; i++) {
+			if (tempMultiset[i].repetition > 0) {
+				counter++;
+			}
+		}
+		// Get rid of any elements that are repeated 0 times, and return the
+		// remaining
+		if (counter == 0) {
+			return (null);
+		} else {
+			ElementOfMultiset[] result = new ElementOfMultiset[counter];
+			int index = 0;
+			for (int i = 0; i < tempMultiset.length; i++)
+				if (tempMultiset[i].repetition > 0) {
+					result[index] = new ElementOfMultiset(
+							tempMultiset[i].element,
+							tempMultiset[i].repetition);
+					index++;
+				}
+			return (result);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Get the underlying set of a multiset of Objects
+	 */
+	public static int[] getUnderlyingSet(int[] array) {
+		// Initialization
+		int numOfUniqueElements = 0;
+		int[] uniqueElements = new int[array.length];
+		for (int i = 0; i < uniqueElements.length; i++)
+			uniqueElements[i] = 0;
+
+		// Counting the number of unique elements in the integerPartition
+		for (int i = 0; i < array.length; i++) {
+			boolean weHaveSeenThisElementBefore = false;
+			for (int j = 0; j < numOfUniqueElements; j++) {
+				if (uniqueElements[j] == array[i]) {
+					weHaveSeenThisElementBefore = true;
+					break;
+				}
+			}
+			if (weHaveSeenThisElementBefore == false) {
+				uniqueElements[numOfUniqueElements] = array[i];
+				numOfUniqueElements++;
+			}
+		}
+		// Setting the elements of the underlying set
+		int[] underlyingSet = new int[numOfUniqueElements];
+		for (int i = 0; i < numOfUniqueElements; i++)
+			underlyingSet[i] = uniqueElements[i];
+
+		return (underlyingSet);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Removes from "string" any characters that come after "c"
+	 */
+	public String trimStringAfterChar(String string, char c) {
+		int last;
+		if (string.indexOf(c) == -1)
+			last = string.length();
+		else
+			last = string.indexOf(c);
+		return (string.substring(0, last));
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Given a real number, set the number of digits after the decimal point to
+	 * be equal to "precision"
+	 */
+	public static String setDecimalPrecision(double doubleValue,
+			int precision) {
+		String precisionString = "0.0";
+		for (int i = 2; i <= precision; i++) {
+			precisionString += "#";
+		}
+		return ((new DecimalFormat(precisionString)).format(doubleValue));
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Compares two array, and considers them identical if they contain the same
+	 * elements even if they are ordered differently
+	 * 
+	 * @return "true" if the elements are identical, even if the order of the
+	 *         elements is different
+	 */
+	public static boolean compareTwoArrays_ignoreElementOrder(int[] array1,
+			int[] array2) {
+		// Compare the length
+		if (array1.length != array2.length) {
+			return (false);
+		}
+
+		// sort the arrays
+		int[] sortedArray1 = sortArray(array1, true);
+		int[] sortedArray2 = sortArray(array2, true);
+
+		// Compare the elements of the sorted arrays
+		int length = array1.length;
+		for (int i = 0; i < length; i++) {
+			if (sortedArray1[i] != sortedArray2[i]) {
+				return (false);
+			}
+		}
+		// If we have reached here, then the arrays are identical
+		return (true);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Compares two array, and considers them identical if (1) they contain the
+	 * same elements and (2) the order of the elements is exactly the same in
+	 * each array
+	 * 
+	 * @return "true" if the elements are identical, and the order of the
+	 *         elements is identical
+	 */
+	public static boolean compareTwoArrays_considerElementOrder(int[] array1,
+			int[] array2) {
+		// Compare the length
+		if (array1.length != array2.length) {
+			return (false);
+		}
+
+		// Compare the elements of the sorted arrays
+		int length = array1.length;
+		for (int i = 0; i < length; i++) {
+			if (array1[i] != array2[i]) {
+				return (false);
+			}
+		}
+		// If we have reached here, then the arrays are identical
+		return (true);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Randomize the indices (i.e. the locations) of the values within the array
+	 */
+	public static void randomizeElementsInArray(double[] array) {
+		// Initialization
+		Random r = new Random();
+		double[] tempArray = new double[array.length];
+		int[] availableIndices = new int[array.length];
+		for (int i = 0; i < availableIndices.length; i++)
+			availableIndices[i] = i;
+
+		// The actual randomization
+		int indexInTemp = 0;
+		for (int max = array.length; max >= 1; max--) {
+			int i = r.nextInt(max); // recall that nextInt(max) returns a random
+									// integer i (0 <= i < max)
+			int chosenIndex = availableIndices[i];
+			tempArray[indexInTemp] = array[chosenIndex];
+			indexInTemp++;
+
+			// Update availableIndices
+			for (int j = i; j <= max - 2; j++)
+				availableIndices[j] = availableIndices[j + 1];
+		}
+		for (int i = 0; i < array.length; i++)
+			array[i] = tempArray[i];
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * return a random number from a normal distribution with mean = "mu" and
+	 * standard deviation = "sigma"
+	 */
+	public static double getRandomNumberFromGammaDistribution(double k,
+			double theta, Random random) {
+		boolean accept = false;
+		if (k < 1) {
+			// Weibull algorithm
+			double c = (1 / k);
+			double d = ((1 - k) * Math.pow(k, (k / (1 - k))));
+			double u, v, z, e, x;
+			do {
+				u = random.nextDouble();
+				v = random.nextDouble();
+				z = -Math.log(u);
+				e = -Math.log(v);
+				x = Math.pow(z, c);
+				if ((z + e) >= (d + x)) {
+					accept = true;
+				}
+			} while (!accept);
+			return (x * theta);
+		} else {
+			// Cheng's algorithm
+			double b = (k - Math.log(4));
+			double c = (k + Math.sqrt(2 * k - 1));
+			double lam = Math.sqrt(2 * k - 1);
+			double cheng = (1 + Math.log(4.5));
+			double u, v, x, y, z, r;
+			do {
+				u = random.nextDouble();
+				v = random.nextDouble();
+				y = ((1 / lam) * Math.log(v / (1 - v)));
+				x = (k * Math.exp(y));
+				z = (u * v * v);
+				r = (b + (c * y) - x);
+				if ((r >= ((4.5 * z) - cheng)) || (r >= Math.log(z))) {
+					accept = true;
+				}
+			} while (!accept);
+			return (x * theta);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * return a random number from a standard normal distribution (with mean =
+	 * 0, and standard deviation = 1)
+	 */
+	//
+	public static double getRandomNumberFromNormalDistribution(Random random) {
+		double U = random.nextDouble();
+		double V = random.nextDouble();
+		return Math.sin(2 * Math.PI * V) * Math.sqrt((-2 * Math.log(1 - U)));
+	}
+
+	/**
+	 * return a random number from a normal distribution with mean = "mu" and
+	 * standard deviation = "sigma"
+	 */
+	public static double getRandomNumberFromNormalDistribution(double mu,
+			double sigma, Random random) {
+		return mu + sigma * getRandomNumberFromNormalDistribution(random);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Uses "selection sort" to sort the elements of the array in ascending
+	 * order
+	 */
+	public static int[] sortArray(int[] array, boolean ascending) {
+		// Copy the current array into a new array called "sortedArray"
+		int[] sortedArray = new int[array.length];
+		for (int i = 0; i < array.length; i++)
+			sortedArray[i] = array[i];
+
+		if (ascending) // if the required order is ascending
+		{
+			for (int i = sortedArray.length - 1; i >= 0; i--) // start at the
+																// end of the
+																// array
+			{
+				int highestIndex = i; // (1) default value of the highest
+										// element index.
+				for (int j = i; j >= 0; j--) // (2) loop from the end of
+												// unsorted zone to the
+												// beginning of the array.
+				{
+					if (sortedArray[highestIndex] < sortedArray[j]) // compare
+																	// current
+																	// element
+																	// to
+																	// highest
+						highestIndex = j; // if it's higher, it becomes the new
+											// highest
+				}
+				// swap the two values
+				int temp = sortedArray[i];
+				sortedArray[i] = sortedArray[highestIndex];
+				sortedArray[highestIndex] = temp;
+			}
+		} else // i.e., if the required order is descending
+		{
+			for (int i = 0; i <= sortedArray.length - 1; i++) // start at the
+																// beginning of
+																// the array
+			{
+				int highestIndex = i; // (1) default value of the highest
+										// element index.
+				for (int j = i; j <= sortedArray.length - 1; j++) // (2) loop
+																	// from the
+																	// beginning
+																	// of
+																	// unsorted
+																	// zone to
+																	// the end
+																	// of the
+																	// array.
+				{
+					if (sortedArray[highestIndex] < sortedArray[j]) // compare
+																	// current
+																	// element
+																	// to
+																	// highest
+						highestIndex = j; // if it's higher, it becomes the new
+											// highest
+				}
+				// swap the two values
+				int temp = sortedArray[i];
+				sortedArray[i] = sortedArray[highestIndex];
+				sortedArray[highestIndex] = temp;
+			}
+		}
+		return (sortedArray);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Uses "selection sort" to sort the elements of the array in ascending
+	 * order
+	 */
+	public static int[][] sortArrayLexicographically(int[][] array,
+			boolean ascending) {
+		// Copy the current array into a new string array which will be the one
+		// we sort
+		String[] stringArray = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			stringArray[i] = "";
+			for (int j = 0; j < array[i].length; j++)
+				stringArray[i] += array[i][j];
+		}
+		if (ascending) // if the required order is ascending
+		{
+			for (int i = stringArray.length - 1; i >= 0; i--) // start at the
+																// end of the
+																// array
+			{
+				int highestIndex = i; // (1) default value of the highest
+										// element index.
+				for (int j = i; j >= 0; j--) // (2) loop from the end of
+												// unsorted zone to the
+												// beginning of the array.
+				{
+					if (stringArray[highestIndex].compareTo(stringArray[j]) < 0) // compare
+																					// current
+																					// element
+																					// to
+																					// highest
+						highestIndex = j; // if it's higher, it becomes the new
+											// highest
+				}
+				// swap the two values
+				String temp = stringArray[i];
+				stringArray[i] = stringArray[highestIndex];
+				stringArray[highestIndex] = temp;
+			}
+		} else // i.e., if the required order is descending
+		{
+			for (int i = 0; i <= stringArray.length - 1; i++) // start at the
+																// beginning of
+																// the array
+			{
+				int highestIndex = i; // (1) default value of the highest
+										// element index.
+				for (int j = i; j <= stringArray.length - 1; j++) // (2) loop
+																	// from the
+																	// beginning
+																	// of
+																	// unsorted
+																	// zone to
+																	// the end
+																	// of the
+																	// array.
+				{
+					if (stringArray[highestIndex].compareTo(stringArray[j]) < 0) // compare
+																					// current
+																					// element
+																					// to
+																					// highest
+						highestIndex = j; // if it's higher, it becomes the new
+											// highest
+				}
+				// swap the two values
+				String temp = stringArray[i];
+				stringArray[i] = stringArray[highestIndex];
+				stringArray[highestIndex] = temp;
+			}
+		}
+		// convert the sorted string array into an array of numbers
+		int[][] sortedArray = new int[stringArray.length][];
+		for (int i = 0; i < sortedArray.length; i++) {
+			sortedArray[i] = new int[stringArray[i].length()];
+			for (int j = 0; j < sortedArray[i].length; j++)
+				sortedArray[i][j] = (new Integer(stringArray[i].charAt(j)))
+						.intValue();
+		}
+		return (sortedArray);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This method removes the given element from the given array. IMPORTANT: if
+	 * the array contains repeated elements, then only one of them is removed.
+	 */
+	public static int[] removeElementOnceFromArray(int element, int[] array) {
+		// Initialization
+		int lengthOfArray = array.length;
+		int[] newArray = new int[lengthOfArray - 1];
+
+		// main loop...
+		for (int i = 0; i < lengthOfArray; i++) {
+			if (array[i] == element) {
+				int j = i + 1;
+				while (j < lengthOfArray) {
+					newArray[j - 1] = array[j];
+					j++;
+				}
+				break;
+			} else
+				newArray[i] = array[i];
+		}
+		return (newArray);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This method removes the given set of elements from the given array.
+	 * IMPORTANT: if the array contains repeated elements, then only one of them
+	 * is removed.
+	 */
+	public static int[] removeElementsOnceFromArray(int[] elements,
+			int[] array) {
+		// Initialization
+		int lengthOfArray = array.length;
+		int numOfElements = elements.length;
+		int[] tempArray = new int[array.length];
+		for (int i = 0; i < array.length; i++)
+			tempArray[i] = array[i];
+
+		for (int k = 0; k < numOfElements; k++) // For every element
+		{
+			int curElement = elements[k]; // This is the current element
+			for (int i = 0; i < lengthOfArray; i++) {
+				if (tempArray[i] == curElement) // if the current element is
+												// found in the array...
+				{
+					int j = i + 1;
+					while (j < lengthOfArray) {
+						tempArray[j - 1] = tempArray[j];
+						j++;
+					}
+					break;
+				}
+			}
+		}
+
+		// Copy the elements from tempArray to newArray
+		int lengthOfNewArray = lengthOfArray - numOfElements;
+		int[] newArray = new int[lengthOfNewArray];
+		for (int i = 0; i < lengthOfNewArray; i++) {
+			newArray[i] = tempArray[i];
+		}
+		return (newArray);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Read a text file, and return a string with the contents. Here, you
+	 * specify the path of the file, but that is only starting from the work
+	 * space, e.g., if the file's name is "result.txt", and it is in a folder
+	 * called: "experiments", which is located in the workspace, then
+	 * "filePathAndName" would be: "experiments/result.txt"
+	 */
+	public static String readFile(String filePathAndName) {
+		try {
+			BufferedReader bufferReader = new BufferedReader(
+					new FileReader(filePathAndName));
+			String string = "";
+			String line;
+			while (true) {
+				line = bufferReader.readLine();
+				if (line == null)
+					break;
+				string += line;
+			}
+			bufferReader.close();
+			return (string);
+		} catch (Exception e) {
+			System.out.println(e);
+			return (null);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * merges the first file and the second file into the "mergedFile" The
+	 * merged file can have the same name as the first file, or as the second
+	 * file, or have a different name. The method will work in all of these
+	 * cases.
+	 */
+	public static void mergeTwoFiles(String firstFile, String secondFile,
+			String mergedFile) {
+		if (((new String(mergedFile)).compareTo(firstFile) != 0)
+				&& ((new String(mergedFile)).compareTo(secondFile) != 0)) {
+			clearFile(mergedFile);
+		}
+		if (((new String(mergedFile)).compareTo(firstFile) == 0)
+				|| ((new String(mergedFile)).compareTo(secondFile) != 0)) {
+			try {
+				BufferedReader bufferReader = new BufferedReader(
+						new FileReader(secondFile));
+				String line;
+				while (true) {
+					line = bufferReader.readLine();
+					if (line == null)
+						break;
+					printToFile(mergedFile, line + "\n", false);
+				}
+				bufferReader.close();
+			} catch (Exception e) {
+				System.out.println(e);
+			}
+		}
+		if (((new String(mergedFile)).compareTo(secondFile) == 0)
+				|| ((new String(mergedFile)).compareTo(firstFile) != 0)) {
+			try {
+				BufferedReader bufferReader = new BufferedReader(
+						new FileReader(firstFile));
+				String line;
+				while (true) {
+					line = bufferReader.readLine();
+					if (line == null)
+						break;
+					printToFile(mergedFile, line + "\n", false);
+				}
+				bufferReader.close();
+			} catch (Exception e) {
+				System.out.println(e);
+			}
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This method writes the given string to the given file. Here, you specify
+	 * the path of the file, but that is only starting from the work space,
+	 * e.g., if the file's name is "result.txt", and it is in a folder called:
+	 * "experiments", which is located in the workspace, then "filePathAndName"
+	 * would be: "experiments/result.txt"
+	 * 
+	 * * if there wasn't a file with this name, then it creates one
+	 * 
+	 * * if there was already a file with this name, then it needs to know
+	 * whether you want to erase the previous content of the file before adding
+	 * the string. This is determined by the parameter "erasePreviousContent"
+	 */
+	public static void printToFile(String filePathAndName, String string,
+			boolean erasePreviousContent) {
+		boolean append = !erasePreviousContent;
+		try {
+			FileWriter file = new FileWriter(filePathAndName, append);
+			file.write(string);
+			file.close();
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This method creates the given folder. The folder name may also be a path,
+	 * in which case it creates all the folders in the path.
+	 */
+	public static void createFolder(String filePathAndName) {
+		File folder = new File(filePathAndName);
+		folder.mkdirs();
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * This method creates a new (blank) file. Now if there was a previous file
+	 * with the same name, then it replaces it with the new blank file.
+	 */
+	public static void clearFile(String filePathAndName) {
+		try {
+			FileWriter file = new FileWriter(filePathAndName);
+			file.write("");
+			file.close();
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+	}
+
+	// ************************************************************************************************
+
+	public static String convertArrayToString(byte[] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++)
+			tempStr += (new Byte(array[i]).toString()) + ", ";
+		tempStr += (new Byte(array[array.length - 1]).toString()) + "]";
+		return (tempStr);
+	}
+
+	public static String convertArrayToString(int[] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++)
+			tempStr += (new Integer(array[i]).toString()) + ", ";
+		tempStr += (new Integer(array[array.length - 1]).toString()) + "]";
+		return (tempStr);
+	}
+
+	public static String convertArrayToString(long[] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++)
+			tempStr += (new Long(array[i]).toString()) + ", ";
+		tempStr += (new Long(array[array.length - 1]).toString()) + "]";
+		return (tempStr);
+	}
+
+	public static String convertArrayToString(double[] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++)
+			tempStr += (new Double(array[i]).toString()) + ", ";
+		tempStr += (new Double(array[array.length - 1]).toString()) + "]";
+		return (tempStr);
+	}
+
+	// ************************************************************************************************
+
+	public static String convertArrayToString(final byte[][] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++) {
+			tempStr += "[";
+			for (int j = 0; j < array[i].length - 1; j++) {
+				tempStr += (new Byte(array[i][j]).toString()) + ", ";
+			}
+			int j = array[i].length - 1;
+			tempStr += (new Byte(array[i][j]).toString()) + "], ";
+		}
+		int i = array.length - 1;
+		tempStr += "[";
+		for (int j = 0; j < array[i].length - 1; j++) {
+			tempStr += (new Byte(array[i][j]).toString()) + ", ";
+		}
+		int j = array[i].length - 1;
+		tempStr += (new Byte(array[i][j]).toString()) + "]]";
+		return (tempStr);
+	}
+
+	public static String convertArrayToString(final long[][] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++) {
+			tempStr += "[";
+			for (int j = 0; j < array[i].length - 1; j++) {
+				tempStr += (new Long(array[i][j]).toString()) + ", ";
+			}
+			int j = array[i].length - 1;
+			tempStr += (new Long(array[i][j]).toString()) + "], ";
+		}
+		int i = array.length - 1;
+		tempStr += "[";
+		for (int j = 0; j < array[i].length - 1; j++) {
+			tempStr += (new Long(array[i][j]).toString()) + ", ";
+		}
+		int j = array[i].length - 1;
+		tempStr += (new Long(array[i][j]).toString()) + "]]";
+		return (tempStr);
+	}
+
+	public static String convertArrayToString(final double[][] array) {
+		String tempStr = "[";
+		for (int i = 0; i < array.length - 1; i++) {
+			tempStr += "[";
+			for (int j = 0; j < array[i].length - 1; j++) {
+				tempStr += (new Double(array[i][j]).toString()) + ", ";
+			}
+			int j = array[i].length - 1;
+			tempStr += (new Double(array[i][j]).toString()) + "], ";
+		}
+		int i = array.length - 1;
+		tempStr += "[";
+		for (int j = 0; j < array[i].length - 1; j++) {
+			tempStr += (new Double(array[i][j]).toString()) + ", ";
+		}
+		int j = array[i].length - 1;
+		tempStr += (new Double(array[i][j]).toString()) + "]]";
+		return (tempStr);
+	}
+
+	// ************************************************************************************************
+
+	public static void printArray(String initialString, final byte[] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString, final int[] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString, final long[] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString, final double[] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString, final byte[][] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString, final int[][] array) {
+		System.out.print(initialString + Arrays.deepToString(array));
+	}
+
+	public static void printArray(String initialString, final long[][] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	public static void printArray(String initialString,
+			final double[][] array) {
+		System.out.print(initialString + convertArrayToString(array));
+	}
+
+	// ************************************************************************************************
+
+	public static byte[] copyArray(final byte[] array) {
+		if (array == null)
+			return (null);
+		byte[] newArray = new byte[array.length];
+		for (int i = 0; i < array.length; i++)
+			newArray[i] = array[i];
+		return newArray;
+	}
+
+	public static int[] copyArray(final int[] array) {
+		if (array == null)
+			return (null);
+		int[] newArray = new int[array.length];
+		for (int i = 0; i < array.length; i++)
+			newArray[i] = array[i];
+		return newArray;
+	}
+
+	public static long[] copyArray(final long[] array) {
+		if (array == null)
+			return (null);
+		long[] newArray = new long[array.length];
+		for (int i = 0; i < array.length; i++)
+			newArray[i] = array[i];
+		return newArray;
+	}
+
+	public static double[] copyArray(final double[] array) {
+		if (array == null)
+			return (null);
+		double[] newArray = new double[array.length];
+		for (int i = 0; i < array.length; i++)
+			newArray[i] = array[i];
+		return newArray;
+	}
+
+	public static byte[][] copyArray(final byte[][] array) {
+		if (array == null)
+			return (null);
+		byte[][] newArray = new byte[array.length][];
+		for (int i = 0; i < array.length; i++) {
+			newArray[i] = new byte[array[i].length];
+			for (int j = 0; j < array[i].length; j++)
+				newArray[i][j] = array[i][j];
+		}
+		return newArray;
+	}
+
+	public static int[][] copyArray(final int[][] array) {
+		if (array == null)
+			return (null);
+		int[][] newArray = new int[array.length][];
+		for (int i = 0; i < array.length; i++) {
+			newArray[i] = new int[array[i].length];
+			for (int j = 0; j < array[i].length; j++)
+				newArray[i][j] = array[i][j];
+		}
+		return newArray;
+	}
+
+	public static long[][] copyArray(final long[][] array) {
+		if (array == null)
+			return (null);
+		long[][] newArray = new long[array.length][];
+		for (int i = 0; i < array.length; i++) {
+			newArray[i] = new long[array[i].length];
+			for (int j = 0; j < array[i].length; j++)
+				newArray[i][j] = array[i][j];
+		}
+		return newArray;
+	}
+
+	public static double[][] copyArray(final double[][] array) {
+		if (array == null)
+			return (null);
+		double[][] newArray = new double[array.length][];
+		for (int i = 0; i < array.length; i++) {
+			newArray[i] = new double[array[i].length];
+			for (int j = 0; j < array[i].length; j++)
+				newArray[i][j] = array[i][j];
+		}
+		return newArray;
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Converts from a multiset which is represented concisely to a string
+	 */
+	public static String convertMultisetToString(ElementOfMultiset[] multiset) {
+		String tempStr = "[";
+		for (int i = 0; i < multiset.length; i++)
+			for (int j = 0; j < multiset[i].repetition; j++)
+				if ((i == multiset.length - 1)
+						&& (j == multiset[i].repetition - 1))
+					tempStr += multiset[i].element;
+				else
+					tempStr += multiset[i].element + ", ";
+		tempStr += "]";
+		return (tempStr);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * returns a copy of the given multiset
+	 */
+	public static ElementOfMultiset[] copyMultiset(
+			ElementOfMultiset[] multiset) {
+		if (multiset == null)
+			return (null);
+
+		ElementOfMultiset[] copy = new ElementOfMultiset[multiset.length];
+		for (int i = 0; i < multiset.length; i++) {
+			copy[i] = new ElementOfMultiset(multiset[i].element,
+					multiset[i].repetition);
+		}
+		return (copy);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/IntegerPartition.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/IntegerPartition.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/IntegerPartition.java	(revision 30)
@@ -0,0 +1,643 @@
+package geniusweb.ip.general;
+
+public class IntegerPartition {
+	public int[] partsSortedAscendingly;
+	public int[] sortedMultiplicity;
+	public int[] sortedUnderlyingSet;
+	public int sortedUnderlyingSetInBitFormat;
+	public int numOfAgents;
+	public ElementOfMultiset[] sortedMultiset;
+	public int[] tempIntegersThatResultedFromASplit; // I use it occationally to
+														// store the two
+														// integers that were
+														// the result of a split
+
+	// *****************************************************************************************************
+
+	/**
+	 * Constructor
+	 */
+	public IntegerPartition(int[] parts) {
+		numOfAgents = 0;
+		for (int i = 0; i < parts.length; i++)
+			numOfAgents += parts[i];
+		partsSortedAscendingly = General.sortArray(parts, true);
+		sortedUnderlyingSet = General.getUnderlyingSet(partsSortedAscendingly);
+		sortedMultiplicity = new int[sortedUnderlyingSet.length];
+		int indexInMultiplicity = 0;
+		sortedMultiplicity[indexInMultiplicity] = 1;
+		for (int i = 1; i < partsSortedAscendingly.length; i++) {
+			if (partsSortedAscendingly[i] == partsSortedAscendingly[i - 1])
+				sortedMultiplicity[indexInMultiplicity]++;
+			else {
+				indexInMultiplicity++;
+				sortedMultiplicity[indexInMultiplicity] = 1;
+			}
+		}
+		sortedUnderlyingSetInBitFormat = Combinations
+				.convertCombinationFromByteToBitFormat(sortedUnderlyingSet);
+
+		// The integer partition represented concisely as a multiset
+		sortedMultiset = new ElementOfMultiset[sortedMultiplicity.length];
+		for (int i = 0; i < sortedMultiset.length; i++) {
+			sortedMultiset[i] = new ElementOfMultiset(sortedUnderlyingSet[i],
+					sortedMultiplicity[i]);
+		}
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Returns the NUMBER of Integer partitions that are directly connected to
+	 * this one (which are all in the above level of the integer partition
+	 * graph).
+	 */
+	public int getNumOfDirectedlyConnectedIntegerPartitions(
+			int largestIntegerBeingSplit, int prev_largestIntegerBeingSplit) {
+		// Check the special case where the node is the bottom one in the
+		// graph...
+		if (sortedUnderlyingSet[0] == numOfAgents)
+			return ((int) Math.floor(numOfAgents / (double) 2));
+
+		int counter = 0;
+		for (int i = 0; i < sortedUnderlyingSet.length; i++) {
+			if ((sortedUnderlyingSet[i] > largestIntegerBeingSplit)
+					|| (sortedUnderlyingSet[i] <= prev_largestIntegerBeingSplit)) {
+				continue;
+			}
+			// for every possible way of splitting "underlyingSet[i]" into two
+			// integers
+			for (int j = 1; j <= (int) Math
+					.floor(sortedUnderlyingSet[i] / (double) 2); j++) {
+				int smallHalf = j;
+				int largeHalf = sortedUnderlyingSet[i] - j;
+				if (largeHalf > numOfAgents - smallHalf - largeHalf) {
+					continue;
+				}
+				counter++;
+			}
+		}
+		return (counter);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Returns the LIST of Integer partitions that are directly connected to
+	 * this one (which are all in the above level of the integer partition
+	 * graph).
+	 */
+	public IntegerPartition[] getListOfDirectedlyConnectedIntegerPartitions(
+			int largestIntegerBeingSplit, int prev_largestIntegerBeingSplit) {
+		// Allocate memory for the list of directly connected integer partitions
+		int counter = getNumOfDirectedlyConnectedIntegerPartitions(
+				largestIntegerBeingSplit, prev_largestIntegerBeingSplit);
+		if (counter == 0) {
+			return (null);
+		}
+		IntegerPartition[] listOfDirectlyConnectedIntegerPartitions = new IntegerPartition[counter];
+
+		// Check the special case where the node is the bottom one in the graph
+		if (sortedUnderlyingSet[0] == numOfAgents) {
+			int index = 0;
+			for (int i = 1; i <= (int) Math
+					.floor(numOfAgents / (double) 2); i++) {
+				int[] newSortedParts = new int[2];
+				newSortedParts[0] = i;
+				newSortedParts[1] = numOfAgents - i;
+				listOfDirectlyConnectedIntegerPartitions[index] = new IntegerPartition(
+						newSortedParts);
+				listOfDirectlyConnectedIntegerPartitions[index].tempIntegersThatResultedFromASplit = new int[] {
+						i, numOfAgents - i };
+				index++;
+			}
+		} else {
+			// Compute the list of directly connected integer partitions
+			int index = 0;
+			for (int i = 0; i < sortedUnderlyingSet.length; i++) {
+				final int curPart = sortedUnderlyingSet[i];
+				if ((curPart > largestIntegerBeingSplit)
+						|| (curPart <= prev_largestIntegerBeingSplit)) {
+					continue;
+				}
+				// for every possible way of splitting "curPart" into two
+				// integers
+				for (int j = 1; j <= (int) Math
+						.floor(curPart / (double) 2); j++) {
+					int smallHalf = j;
+					int largeHalf = curPart - j;
+					if (largeHalf > numOfAgents - smallHalf - largeHalf) {
+						continue;
+					}
+					int[] newSortedParts = new int[partsSortedAscendingly.length
+							+ 1];
+					int i1 = 0;
+					int i2 = 0;
+					while (partsSortedAscendingly[i1] < smallHalf) {
+						newSortedParts[i2] = partsSortedAscendingly[i1];
+						i1++;
+						i2++;
+					}
+					newSortedParts[i2] = smallHalf;
+					i2++;
+					while (partsSortedAscendingly[i1] < largeHalf) {
+						newSortedParts[i2] = partsSortedAscendingly[i1];
+						i1++;
+						i2++;
+					}
+					newSortedParts[i2] = largeHalf;
+					i2++;
+					boolean curPartHasBeenSeen = false;
+					while (i1 < partsSortedAscendingly.length) {
+						if ((partsSortedAscendingly[i1] == curPart)
+								&& (curPartHasBeenSeen == false)) {
+							curPartHasBeenSeen = true;
+							i1++;
+						} else {
+							newSortedParts[i2] = partsSortedAscendingly[i1];
+							i1++;
+							i2++;
+						}
+					}
+					listOfDirectlyConnectedIntegerPartitions[index] = new IntegerPartition(
+							newSortedParts);
+					listOfDirectlyConnectedIntegerPartitions[index].tempIntegersThatResultedFromASplit = new int[] {
+							smallHalf, largeHalf };
+					index++;
+				}
+			}
+		}
+		return (listOfDirectlyConnectedIntegerPartitions);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * This method returns the number of integer partitions of "n"
+	 */
+	public static int getNumOfIntegerPartitions(int n) {
+		int numOfIntegerPartitions = 0;
+		for (int level = 1; level <= n; level++) {
+			numOfIntegerPartitions += IntegerPartition
+					.getNumOfIntegerPartitionsInLevel(n, level);
+		}
+		return (numOfIntegerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method returns the number of integer partitions of "n" that are in
+	 * level = "level" of the integer partition graph (i.e. those of size =
+	 * "level")
+	 */
+	public static int getNumOfIntegerPartitionsInLevel(int n, int level) {
+		return (getNumOfIntegerPartitionsInLevel_additionalParameter(n, level,
+				n - level + 1));
+	}
+
+	private static int getNumOfIntegerPartitionsInLevel_additionalParameter(
+			int n, int level, int M) {
+		if ((level == 1) || (level == n)) {
+			return (1);
+		}
+		int sum = 0;
+		for (int M1 = (int) Math.ceil(n / (double) level); M1 <= Math
+				.min(n - level + 1, M); M1++) {
+			sum += getNumOfIntegerPartitionsInLevel_additionalParameter(n - M1,
+					level - 1, M1);
+		}
+		return (sum);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method returns the number of integer partitions of "n" that are in
+	 * level = "level" of of the integer partition graph (i.e. those of size =
+	 * "level"), where the smallest part in each partition is smaller than, or
+	 * equal to, "smallestPart".
+	 */
+	public static int getNumOfIntegerPartitionsInLevel(int n, int level,
+			int smallestPart) {
+		return (getNumOfIntegerPartitionsInLevel_additionalParameter(n, level,
+				smallestPart, n - level + 1));
+	}
+
+	private static int getNumOfIntegerPartitionsInLevel_additionalParameter(
+			int n, int level, int smallestPart, int M) {
+		if (smallestPart == 1) {
+			return (getNumOfIntegerPartitionsInLevel(n, level));
+		} else {
+			if ((n < smallestPart) || (level == n)) {
+				return (0);
+			}
+			if (level == 1) {
+				return (1);
+			}
+			int sum = 0;
+			for (int M1 = (int) Math.ceil(n / (double) level); M1 <= Math
+					.min(n - level + 1, M); M1++) {
+				sum += getNumOfIntegerPartitionsInLevel_additionalParameter(
+						n - M1, level - 1, smallestPart, M1);
+			}
+			return (sum);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method generates the integer partitions of n; it uses the algorithm
+	 * SZ1. For more details, see: "Fast algorithms for generating integer
+	 * partitions", by Antoine Zoghbi and Ivan Stojmenovic, 1998
+	 */
+	public static int[][] getIntegerPartitionsOrderedLexicographically(int n,
+			boolean orderIntegerPartitionsAscendingly) {
+		int index = 0;
+
+		// Memory allocation
+		int[][] listOfIntegerPartitions = new int[IntegerPartition
+				.getNumOfIntegerPartitions(n)][];
+
+		// Initialize "indexOfNewPartition". Here, "indexOfNewPartition[i]"
+		// represents the
+		// index at which any new partition with "i+1" parts must be added to
+		// integerPartitions[i].
+		int[] indexOfNewPartition = new int[n];
+		for (int i = 0; i < n; i++)
+			indexOfNewPartition[i] = 0;
+
+		// We will set x to one integer partition, and then to another, and so
+		// on, until we
+		// are done with all integer partitions.
+		// NOTE: I have made x contain n+1 element. This way, if we ignore the
+		// first element
+		// (i.e. x[0]) then the first element in x becomes x[1] just as in the
+		// paper.
+		//
+		int[] x = new int[n + 1];
+
+		// Setting x to the integer partition containing n parts, all of which
+		// have a value of 1.
+		for (int i = 1; i <= n; i++)
+			x[i] = 1;
+
+		// Initialization
+		x[1] = n;
+		int m = 1;
+		int h = 1;
+
+		// put the first integer partition in the list.
+		listOfIntegerPartitions[index] = new int[x.length];
+		for (int i = 0; i < x.length; i++)
+			listOfIntegerPartitions[index][i] = x[i];
+		index++;
+
+		// Setting x to the other integer partitions
+		while (x[1] != 1) {
+			if (x[h] == 2) {
+				m = m + 1;
+				x[h] = 1;
+				h = h - 1;
+			} else {
+				int r = x[h] - 1;
+				int t = m - h + 1;
+				x[h] = r;
+				while (t >= r) {
+					h = h + 1;
+					x[h] = r;
+					t = t - r;
+				}
+				if (t == 0) {
+					m = h;
+				} else {
+					m = h + 1;
+					if (t > 1) {
+						h = h + 1;
+						x[h] = t;
+					}
+				}
+			}
+			// put the current integer partition in the list.
+			listOfIntegerPartitions[index] = new int[x.length];
+			for (int i = 0; i < x.length; i++)
+				listOfIntegerPartitions[index][i] = x[i];
+			index++;
+		}
+		return (listOfIntegerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method generates the integer partitions of n; it uses the algorithm
+	 * SZ1. For more details, see: "Fast algorithms for generating integer
+	 * partitions", by Antoine Zoghbi and Ivan Stojmenovic, 1998
+	 */
+	public static int[][][] getIntegerPartitions(int n,
+			boolean orderIntegerPartitionsAscendingly) {
+		// Memory allocation
+		int[][][] integerPartitions = allocateMemoryForIntegerPartitions(n);
+
+		// Initialize "indexOfNewPartition". Here, "indexOfNewPartition[i]"
+		// represents the
+		// index at which any new partition with "i+1" parts must be added to
+		// integerPartitions[i].
+		int[] indexOfNewPartition = new int[n];
+		for (int i = 0; i < n; i++)
+			indexOfNewPartition[i] = 0;
+
+		// We will set x to one integer partition, and then to another, and so
+		// on, until we
+		// are done with all integer partitions.
+		// NOTE: I have made x contain n+1 element. This way, if we ignore the
+		// first element
+		// (i.e. x[0]) then the first element in x becomes x[1] just as in the
+		// paper.
+		//
+		int[] x = new int[n + 1];
+
+		// Setting x to the integer partition containing n parts, all of which
+		// have a value of 1.
+		for (int i = 1; i <= n; i++)
+			x[i] = 1;
+
+		// Initialization
+		x[1] = n;
+		int m = 1;
+		int h = 1;
+		fill_x_in_partitions(x, integerPartitions, m,
+				orderIntegerPartitionsAscendingly, indexOfNewPartition);
+
+		// Setting x to the other integer partitions
+		while (x[1] != 1) {
+			if (x[h] == 2) {
+				m = m + 1;
+				x[h] = 1;
+				h = h - 1;
+			} else {
+				int r = x[h] - 1;
+				int t = m - h + 1;
+				x[h] = r;
+				while (t >= r) {
+					h = h + 1;
+					x[h] = r;
+					t = t - r;
+				}
+				if (t == 0) {
+					m = h;
+				} else {
+					m = h + 1;
+					if (t > 1) {
+						h = h + 1;
+						x[h] = t;
+					}
+				}
+			}
+			// This method fills x in the array "integerPartitions"
+			fill_x_in_partitions(x, integerPartitions, m,
+					orderIntegerPartitionsAscendingly, indexOfNewPartition);
+		}
+		return (integerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method generates all integer partitions of m in which every part is
+	 * greater than, or equal to, l1. I also adds these partitions to the
+	 * relevant layers in the array "prevPartitions" (based on the number of
+	 * parts in each partition).
+	 * 
+	 * This method uses the algorithm parta. For more details, see; "Algorithm
+	 * 29 Efficient Algorithms for Double and Multiply Restricted Partitions",
+	 * by W. Riha and K. R. James, 1974.
+	 * 
+	 * Note the following differences between my code and the code of the
+	 * original algorithm "parta"
+	 * 
+	 * * I set parameter "z" to 0 (to avoid generating any partition that
+	 * contains repeated parts, set z to 1). * I removed "num" since I won't be
+	 * using it. * I added the parameter "carryOn" to avoid the use of "goto" *
+	 * I replaced "proc" (which prints x) with "fill_x_in_partitions" (which
+	 * adds x to "prevPartitions").
+	 */
+	public static int[][][] getRestrictedIntegerPartitions(int m, int l1,
+			boolean ascending) {
+		// Memory allocation...
+		int[][][] integerPartitions = allocateMemoryForIntegerPartitions(m, l1);
+
+		// Initialize "indexOfNewPartition". Here, "indexOfNewPartition[i]"
+		// represents the
+		// index at which any new partition with "i+1" parts must be added to
+		// integerPartitionss[i].
+		int[] indexOfNewPartition = new int[m];
+		for (int i = 0; i < integerPartitions.length; i++)
+			indexOfNewPartition[i] = 0;
+
+		// Store the original value of m (this is because "parta" changes the
+		// value of "m" while running)
+		int original_m = m;
+
+		integerPartitions[0][0][0] = m;
+		for (int level = 2; level <= integerPartitions.length; level++) {
+			m = original_m; // Set "m" to its original value.
+			int n = level; // Set the number of parts.
+			int l2 = m; // Set the maximum value that a part in this level can
+						// get.
+			int z = 0;
+
+			// ********************************************
+			// *** ***
+			// *** The "parta" algorithm ***
+			// *** ***
+			// ********************************************
+
+			int i, j;
+			int[] x = new int[n + 1];
+			int[] y = new int[n + 1];
+			j = z * n * (n - 1);
+			m = m - n * l1 - j / 2;
+			l2 = l2 - l1;
+			if ((m >= 0) & (m <= n * l2 - j)) {
+				for (i = 1; i <= n; i++) {
+					x[i] = l1 + z * (n - i);
+					y[i] = l1 + z * (n - i);
+				}
+				i = 1;
+				l2 = l2 - z * (n - 1);
+
+				boolean carryOn = true;
+				while (carryOn) {
+					carryOn = false;
+					while (m > l2) {
+						m = m - l2;
+						x[i] = y[i] + l2;
+						i = i + 1;
+					}
+					x[i] = y[i] + m;
+					fill_x_in_partitions(x, integerPartitions, n, ascending,
+							indexOfNewPartition);
+					if ((i < n) & (m > 1)) {
+						m = 1;
+						x[i] = x[i] - 1;
+						i = i + 1;
+						x[i] = y[i] + 1;
+						fill_x_in_partitions(x, integerPartitions, n, ascending,
+								indexOfNewPartition);
+					}
+					for (j = i - 1; j >= 1; j--) {
+						l2 = x[j] - y[j] - 1;
+						m = m + 1;
+						if (m <= (n - j) * l2) {
+							x[j] = y[j] + l2;
+							carryOn = true;
+							break;
+						}
+						m = m + l2;
+						x[i] = y[i];
+						i = j;
+					}
+				}
+			}
+		}
+		return (integerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This is only used in the method "getIntegerPartitions". It allocates the
+	 * memory required for the array of integer partitions.
+	 */
+	private static int[][][] allocateMemoryForIntegerPartitions(int n) {
+		// Count the number of integerPartitionss in each level
+		int[] numOfIntegerPartitionsInLevel = new int[n];
+		for (int level = 1; level <= n; level++)
+			numOfIntegerPartitionsInLevel[level
+					- 1] = getNumOfIntegerPartitionsInLevel(n, level);
+
+		int[][][] integerPartitions = new int[n][][];
+		for (int level = 1; level <= n; level++) {
+			integerPartitions[level
+					- 1] = new int[numOfIntegerPartitionsInLevel[level - 1]][];
+			for (int i = 0; i < numOfIntegerPartitionsInLevel[level - 1]; i++) {
+				integerPartitions[level - 1][i] = new int[level];
+			}
+		}
+		return (integerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This is only used in the method "generateRestrictedIntegerPartitions". It
+	 * allocates the memory required for the array of integer partitions of "n"
+	 * in which the parts are greater than, or equal to, "smallestPart".
+	 */
+	private static int[][][] allocateMemoryForIntegerPartitions(int n,
+			int smallestPart) {
+		// Count the number of integer partitions in each level, as well as the
+		// number of non-empty levels.
+		int numOfNonEmptyLevels = 0;
+		int[] numOfIntegerPartitions = new int[n];
+		for (int level = 1; level <= n; level++) {
+			numOfIntegerPartitions[level
+					- 1] = getNumOfIntegerPartitionsInLevel(n, level,
+							smallestPart);
+			if (numOfIntegerPartitions[level - 1] > 0) {
+				numOfNonEmptyLevels++;
+			}
+		}
+
+		int[][][] integerPartitions = new int[numOfNonEmptyLevels][][];
+		for (int level = 1; level <= numOfNonEmptyLevels; level++) {
+			integerPartitions[level
+					- 1] = new int[numOfIntegerPartitions[level - 1]][];
+			for (int i = 0; i < numOfIntegerPartitions[level - 1]; i++) {
+				integerPartitions[level - 1][i] = new int[level];
+			}
+		}
+		return (integerPartitions);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This is only used in the methods: "getIntegerPartitions" (which uses the
+	 * SZ1 algorithm) and the method: "generateRestrictedIntegerPartitions"
+	 * (which uses the parta algorithm).
+	 * 
+	 * It fills x (i.e. the current integer partitoin) in the array
+	 * "integerPartitions".
+	 */
+	private static void fill_x_in_partitions(int x[],
+			int[][][] integerPartitions, int m, boolean ascending,
+			int[] indexOfNewPartition) {
+		// Note that the integer partition in x is ordered in a descending
+		// order. However, we
+		// might want to fill in "integerPartitions" in an ascending order.
+		if (ascending == false) {
+			for (int i = 1; i <= m; i++)
+				integerPartitions[m - 1][indexOfNewPartition[m - 1]][i
+						- 1] = x[i];
+			indexOfNewPartition[m - 1]++;
+		} else // fill x in "integerPartitions" such that the elements in
+				// "integerPartitions" are ordered ascendingly
+		{
+			for (int i = 1; i <= m; i++)
+				integerPartitions[m - 1][indexOfNewPartition[m - 1]][i
+						- 1] = x[m - i + 1];
+			indexOfNewPartition[m - 1]++;
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method sorts the integer partitions lexicographically. For example,
+	 * [3,3,2,2,1], [3,3,2,1,1,1], [3,3,3,2], [4,1] will be sorted as follows:
+	 * [4,1] [3,3,3,2] [3,3,2,2,1] [3,3,2,1,1,1] Also: [1,2,2,3,3],
+	 * [1,1,1,2,3,3], [2,3,3,3], [1,4] will be sorted as follows: [2,3,3,3]
+	 * [1,4] [1,2,2,3,3] [1,1,1,2,3,3]
+	 * 
+	 * @return list of indices of the new order. In example 1, it returns
+	 *         [2,3,1,0]. In example 2, it returns [2,3,0,1]
+	 */
+	public static int[] sortListOfIntegerPartitionsLexicographically(
+			int[][] listOfIntegerPartitions,
+			boolean eachIntegerPartitionIsOrderedAscendingly) {
+		int[] sortedIndices = new int[listOfIntegerPartitions.length];
+
+		return (sortedIndices);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * returns true if the integer partition contains the given multiset.
+	 * Otherwise, it returns false
+	 */
+	public boolean contains(ElementOfMultiset[] multiset) {
+		if (sortedMultiset.length < multiset.length)
+			return (false);
+
+		for (int i = 0; i < multiset.length; i++) {
+			boolean found = false;
+			for (int j = 0; j < this.sortedMultiset.length; j++) {
+				if (this.sortedMultiset[j].element == multiset[i].element) {
+					if (this.sortedMultiset[j].repetition < multiset[i].repetition) {
+						return (false);
+					}
+					found = true;
+					break;
+				}
+			}
+			if (found == false) {
+				return (false);
+			}
+		}
+		return (true);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/RandomPartition.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/RandomPartition.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/RandomPartition.java	(revision 30)
@@ -0,0 +1,103 @@
+package geniusweb.ip.general;
+
+import java.math.BigDecimal;
+import java.util.Random;
+
+public class RandomPartition {
+
+	private int n;
+	private double[][] probabilityOfNew;
+
+	public RandomPartition(int n) {
+		this.n = n;
+		this.calculateWeights(n);
+	}
+
+	// random partition of numbers (0, 1, ..., n-1)
+	public int[] get() {
+		int partition[] = new int[n];
+
+		Random randomGenerator = new Random();
+		int coalitionsCount = 1;
+		partition[0] = 1;
+		for (int i = 1; i < n; ++i) {
+			if (randomGenerator
+					.nextDouble() < this.probabilityOfNew[i][coalitionsCount]) {
+				partition[i] = coalitionsCount + 1;
+				++coalitionsCount;
+			} else
+				partition[i] = randomGenerator.nextInt(coalitionsCount) + 1;
+		}
+
+		// Convert the partition to a coalition structure, where every coalition
+		// is a number in which true bits represent the members
+		int[] coalitionStructure = new int[coalitionsCount];
+		for (int i = 0; i < coalitionsCount; i++) {
+			coalitionStructure[i] = 0;
+		}
+		for (int i = 0; i < n; i++) {
+			coalitionStructure[partition[i] - 1] += (1 << i); // add agent "i+1"
+																// to the
+																// coalition
+		}
+
+		return coalitionStructure;
+	}
+
+	private void calculateWeights(int n) {
+		BigDecimal[][] weights = new BigDecimal[n + 1][n + 1];
+		double[][] weightsDouble = new double[n + 1][n + 1];
+		this.probabilityOfNew = new double[n + 1][n + 1];
+
+		weights[0][0] = BigDecimal.ONE;
+		weightsDouble[0][0] = 1;
+		BigDecimal bellNInverse = BigDecimal.ONE.divide(this.getBellNumber(n),
+				500, BigDecimal.ROUND_UP);
+		for (int i = 1; i <= n; ++i) {
+			weights[n][i] = bellNInverse;
+			weightsDouble[n][i] = weights[n][i].doubleValue();
+		}
+
+		for (int j = n - 1; j >= 1; --j) {
+			for (int k = 1; k <= j; ++k) {
+				weights[j][k] = weights[j + 1][k].multiply(new BigDecimal(k))
+						.add(weights[j + 1][k + 1]);
+				weightsDouble[j][k] = weights[j][k].doubleValue();
+				this.probabilityOfNew[j][k] = weights[j + 1][k + 1]
+						.divide(weights[j][k], 500, BigDecimal.ROUND_UP)
+						.doubleValue();
+			}
+		}
+	}
+
+	private BigDecimal getBellNumber(int n) {
+		BigDecimal[] binomials;
+		BigDecimal[] bell = new BigDecimal[n + 1];
+		bell[0] = BigDecimal.ONE;
+		bell[1] = BigDecimal.ONE;
+
+		for (int i = 2; i <= n; ++i) {
+			bell[i] = BigDecimal.ZERO;
+			binomials = computeBinomials(i - 1);
+			for (int k = 0; k < i; ++k)
+				bell[i] = bell[i].add(bell[k].multiply(binomials[k]));
+		}
+
+		return bell[n];
+	}
+
+	private BigDecimal[] computeBinomials(int n) {
+		BigDecimal[] binomials = new BigDecimal[n + 1];
+
+		binomials[0] = BigDecimal.ONE;
+		for (int i = 1; i <= Math.floor(n / 2); ++i) {
+			binomials[i] = binomials[i - 1].multiply(new BigDecimal(n - i + 1))
+					.divide(new BigDecimal(i), 100, BigDecimal.ROUND_UP);
+		}
+		for (int i = (int) Math.floor(n / 2) + 1; i <= n; ++i)
+			binomials[i] = binomials[n - i];
+
+		return binomials;
+	}
+
+}
Index: /ip/src/main/java/geniusweb/ip/general/RandomPermutation.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/RandomPermutation.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/RandomPermutation.java	(revision 30)
@@ -0,0 +1,29 @@
+package geniusweb.ip.general;
+
+import java.util.Random;
+
+public class RandomPermutation {
+
+	private int n;
+	private int[] availableNumbersToChooseFrom;
+
+	public RandomPermutation(int n) {
+		this.n = n;
+		this.availableNumbersToChooseFrom = new int[n];
+	}
+
+	// random permutation of numbers (1, ..., n)
+	public int[] get() {
+		Random randomGenerator = new Random();
+		for (int i = n - 1; i >= 0; i--) {
+			availableNumbersToChooseFrom[i] = i;
+		}
+		int[] permutation = new int[n];
+		for (int i = n - 1; i >= 0; i--) {
+			int j = randomGenerator.nextInt(i + 1);
+			permutation[i] = availableNumbersToChooseFrom[j] + 1;
+			availableNumbersToChooseFrom[j] = availableNumbersToChooseFrom[i];
+		}
+		return permutation;
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/RandomSubsetOfGivenSet.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/RandomSubsetOfGivenSet.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/RandomSubsetOfGivenSet.java	(revision 30)
@@ -0,0 +1,105 @@
+package geniusweb.ip.general;
+
+import java.util.Random;
+
+public class RandomSubsetOfGivenSet {
+	final private int[] givenSet;
+	final private int numOfElementsInGivenSet;
+	final private int sizeOfSubsetToBeSampled;
+	final private boolean subsetEqualsGivenSet;
+	private int[] availableIndicesToChooseFrom;
+	private Random randomGenerator;
+
+	// ******************************************************************************************************
+
+	/**
+	 * The constructor:
+	 * 
+	 * @param givenSet:                The set from which subsets will be
+	 *                                 sampled
+	 * @param numOfElementsInGivenSet: the number of elements in the set from
+	 *                                 which subsets will be sampled
+	 * @param sizeOfSubsetToBeSampled: the size of every subset that will be
+	 *                                 sampled.
+	 */
+	public RandomSubsetOfGivenSet(int[] givenSet, int numOfElementsInGivenSet,
+			int sizeOfSubsetToBeSampled) {
+		this.givenSet = givenSet;
+		this.numOfElementsInGivenSet = numOfElementsInGivenSet;
+		this.sizeOfSubsetToBeSampled = sizeOfSubsetToBeSampled;
+		this.randomGenerator = new Random();
+		this.availableIndicesToChooseFrom = new int[numOfElementsInGivenSet];
+		if (sizeOfSubsetToBeSampled == numOfElementsInGivenSet)
+			subsetEqualsGivenSet = true;
+		else
+			subsetEqualsGivenSet = false;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Returns random subset of size: "sizeOfSubsetToBeSampled", taken from the
+	 * set: "givenSet"
+	 */
+	public int[] getSubsetInByteFormat() {
+		if (this.subsetEqualsGivenSet) {
+			int[] copyOfGivenSet = new int[numOfElementsInGivenSet];
+			for (int i = 0; i < numOfElementsInGivenSet; i++) {
+				copyOfGivenSet[i] = givenSet[i];
+			}
+			return copyOfGivenSet;
+		} else {
+			// this.randomGenerator = new Random();
+			for (int i = numOfElementsInGivenSet - 1; i >= 0; i--) {
+				availableIndicesToChooseFrom[i] = i;
+			}
+			int[] subsetInByteFormat = new int[sizeOfSubsetToBeSampled];
+			int indexInSubset = 0;
+			for (int i = numOfElementsInGivenSet
+					- 1; i >= numOfElementsInGivenSet
+							- sizeOfSubsetToBeSampled; i--) {
+				int j = this.randomGenerator.nextInt(i + 1);
+				subsetInByteFormat[indexInSubset] = givenSet[availableIndicesToChooseFrom[j]];
+				indexInSubset++;
+				availableIndicesToChooseFrom[j] = availableIndicesToChooseFrom[i];
+			}
+			return subsetInByteFormat;
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Returns random subset of size: "sizeOfSubsetToBeSampled", taken from the
+	 * set: "givenSet"
+	 */
+	public int getSubsetInBitFormat() {
+		if (this.subsetEqualsGivenSet) {
+			int copyOfGivenSet = 0;
+			for (int i = 0; i < numOfElementsInGivenSet; i++) {
+				// Add agent "givenSet[i]" to "copyOfGivenSet"
+				copyOfGivenSet += 1 << (givenSet[i] - 1);
+			}
+			return copyOfGivenSet;
+		} else {
+			// this.randomGenerator = new Random();
+			for (int i = numOfElementsInGivenSet - 1; i >= 0; i--) {
+				availableIndicesToChooseFrom[i] = i;
+			}
+			int subsetInBitFormat = 0;
+			int indexInSubset = 0;
+			for (int i = numOfElementsInGivenSet
+					- 1; i >= numOfElementsInGivenSet
+							- sizeOfSubsetToBeSampled; i--) {
+				int j = this.randomGenerator.nextInt(i + 1);
+				// Add agent "givenSet[ availableIndicesToChooseFrom[ j ] ]" to
+				// "subsetInBitFormat"
+				subsetInBitFormat += 1 << (givenSet[availableIndicesToChooseFrom[j]]
+						- 1);
+				indexInSubset++;
+				availableIndicesToChooseFrom[j] = availableIndicesToChooseFrom[i];
+			}
+			return subsetInBitFormat;
+		}
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/SubsetEnumerator.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/SubsetEnumerator.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/SubsetEnumerator.java	(revision 30)
@@ -0,0 +1,85 @@
+package geniusweb.ip.general;
+
+import java.util.Arrays;
+
+public class SubsetEnumerator {
+	/**
+	 * Object used to enumerate all subsets of size k out of n elements. Every
+	 * subset is represented in bit format (i.e., bit mask), e.g.: - given 10
+	 * elements, the first subset is {1,2,3}, represented as: 0000000111
+	 * 
+	 * @param n: The total number of elements
+	 * @param s: The size of the subsets that will be enumerated
+	 */
+	final private int n;
+	final private int s;
+	private int currentSubset;
+
+	// *********************************************************************************************************
+
+	/**
+	 * The constructor...
+	 */
+	public SubsetEnumerator(int n, int s) {
+		this.n = n;
+		this.s = s;
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * An example of how to use this class (just for testing)...
+	 */
+	public static void main(String[] args) {
+		int n = 6;
+		System.out.println(
+				" Testing the subset enumerator object \n The total number of elements is: "
+						+ n);
+		// enumerate all subsets of size = 1, 2, ... n
+		for (int s = 1; s <= n; s++) {
+			System.out.println("Current subset size is: " + s);
+			SubsetEnumerator subsetEnumerator = new SubsetEnumerator(n, s);
+			int subset = subsetEnumerator.getFirstSubset(); // this will return
+															// the first subset
+															// in the list
+			while (subset < (1 << n)) {
+				// Put here the code that needs to deal with "subset", e.g.,
+				// printing it as below:
+				System.out.println("the current subset is " + Arrays.toString(
+						Combinations.convertCombinationFromBitToByteFormat(
+								subset, n, s)));
+				// generate the next subset
+				subset = subsetEnumerator.getNextSubset();
+			}
+		}
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a subset, C, of size "s", this method returns the subset C' that is
+	 * located just after C in the list of subsets of size s. If there is no
+	 * such subset (i.e., if we have reached the end of the list), then the
+	 * method returns 1&lt;&lt;n.
+	 */
+	public int getFirstSubset() {
+		currentSubset = 0;
+		for (int i = 0; i < s; i++)
+			currentSubset += (1 << i);
+		return (currentSubset);
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * If (currentSubset = C), this method sets (currentSubset = C') where C' is
+	 * located just after C in the list of subsets of size s. If there is no
+	 * such subset (i.e., if we have reached the end of the list), then the
+	 * method returns "1&lt;&lt;n".
+	 */
+	public int getNextSubset() {
+		currentSubset = Combinations.getPreviousCombinationInBitFormat2(n, s,
+				currentSubset);
+		return (currentSubset);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/general/SubsetsOfMultiset.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/general/SubsetsOfMultiset.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/general/SubsetsOfMultiset.java	(revision 30)
@@ -0,0 +1,543 @@
+package geniusweb.ip.general;
+
+import java.util.Arrays;
+import java.util.Random;
+
+public class SubsetsOfMultiset {
+	final private ElementOfMultiset[] multiset; // The multiset from which we
+												// want to take subsets
+
+	final private int sizeOfSubsets; // the size of the desired subsets
+
+	private int totalNumOfElementsInMultiset; // the total number of elements
+												// (including repetitions)
+
+	private ElementOfMultiset[] multisetWithIncrementalElements; // for example,
+																	// if
+	// multiset = [2,2,2,5,5,6], then multisetWithIncrementalElements =
+	// [1,1,1,2,2,3]
+
+	private boolean currentSubsetIsFirstSubset; // if "true", then the method
+												// "getNextSubset" would
+	// return the first subset in the list of subsets of
+	// "multisetWithIncrementalElements"
+
+	private ElementOfMultiset[] currentSubset; // current subset in the list of
+												// subsets of
+												// "multisetWithIncrementalElements"
+
+	private int numOfUniqueElementsInCurrentSubset; // the number of unique
+													// elements in the current
+													// subset. this
+	// should be updated every time "currentSubset" is changed
+
+	/*
+	 * if multisetWithIncrementalElements = [1,1,1,2,2,3], and sizeOfSubset=4,
+	 * then then lastSubset = [1,2,2,3]
+	 */
+	private ElementOfMultiset[] lastSubset;
+
+	/*
+	 * For every element in the current subset, this array contains the number
+	 * of copies of that element that did not appear in the subset. For example,
+	 * if multiset = [1,1,1,1,2,2,2,3,3,3,4,4,4], and the current subset is
+	 * [1,1,3,3,4], then this method returns [2,1,2]. This is because the
+	 * numbers of instances outside the current subset are: 2 for element "1", 1
+	 * for element "3", and 2 for element "4"
+	 */
+	private int[] numOfInstancesOutsideSubset;
+
+	/*
+	 * if "true" then for every element "e" in the current subset, the
+	 * "SubsetOfMultiset" object will keep track of how many the object will
+	 * keep track of how many instances of "e" were left out of the current
+	 * subset.
+	 */
+	private final boolean keepTrackOfNumOfInstancesOutsideSubset;
+
+	// ************************************************************************************************
+
+	/**
+	 * The constructor
+	 * 
+	 * @param multiset                               the multiset
+	 * @param sizeOfSubsets                          the sizes of the required
+	 *                                               subsets
+	 * @param keepTrackOfNumOfInstancesOutsideSubset if "true" then for every
+	 *                                               element "e" in the current
+	 *                                               subset, the
+	 *                                               "SubsetOfMultiset" object
+	 *                                               will keep track of how many
+	 *                                               the object will keep track
+	 *                                               of how many instances of
+	 *                                               "e" were left out of the
+	 *                                               current subset.
+	 */
+	public SubsetsOfMultiset(ElementOfMultiset[] multiset, int sizeOfSubsets,
+			boolean keepTrackOfNumOfInstancesOutsideSubset) {
+		// Initialise
+		this.multiset = multiset;
+		this.sizeOfSubsets = sizeOfSubsets;
+		this.keepTrackOfNumOfInstancesOutsideSubset = keepTrackOfNumOfInstancesOutsideSubset;
+		resetParameters();
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * resets all parameters (used mainly in the constructor)
+	 */
+	public void resetParameters() {
+		currentSubsetIsFirstSubset = true;
+
+		multisetWithIncrementalElements = new ElementOfMultiset[multiset.length];
+		for (int i = 0; i < multiset.length; i++) {
+			multisetWithIncrementalElements[i] = new ElementOfMultiset(i + 1,
+					multiset[i].repetition);
+		}
+		setLastSubset();
+		currentSubset = new ElementOfMultiset[multiset.length];
+
+		// Compute the total number of agents
+		totalNumOfElementsInMultiset = 0;
+		for (int i = 0; i < multiset.length; i++) {
+			totalNumOfElementsInMultiset += multiset[i].repetition;
+		}
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * An example of how to use this class (just for testing)...
+	 */
+	public static void main(String[] args) {
+		// Create a test case, which is multiset = [4,4,4,5,5,6]
+		ElementOfMultiset[] multiset = new ElementOfMultiset[3];
+		multiset[0] = new ElementOfMultiset(4, 3);
+		multiset[1] = new ElementOfMultiset(5, 2);
+		multiset[2] = new ElementOfMultiset(6, 1);
+
+		// enumerate all subsets of size = 1, 2, 3, ...
+		for (int size = 1; size <= 3; size++) {
+			System.out.println("Current size is " + size);
+			SubsetsOfMultiset subsetsOfMultiset = new SubsetsOfMultiset(
+					multiset, size, true);
+			ElementOfMultiset[] subset = subsetsOfMultiset.getNextSubset();
+			while (subset != null) {
+				// Put here the code that needs to deal with "subset", e.g.,
+				// printing it as below:
+				System.out.println("the current subset is "
+						+ General.convertMultisetToString(subset)
+						+ " , and numOfInstancesOutsideSubset = "
+						+ Arrays.toString(subsetsOfMultiset
+								.getNumOfInstancesOutsideSubset()));
+				subset = subsetsOfMultiset.getNextSubset();
+			}
+		}
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * Given a subset S of a multiset, this method returns the subset S' that is
+	 * located just before S in the list of subsets of the same size. Here, the
+	 * list is ordered lexicographically.
+	 */
+	public ElementOfMultiset[] getNextSubset() {
+		if (currentSubsetIsFirstSubset) {
+			setCurrentSubsetToFirstSubset();
+			currentSubsetIsFirstSubset = false;
+			return (prepareResult());
+		} else // If the required subset is not the first subset
+		{
+			int totalNumberOfElementsSeenSoFar = 0;
+			int indexInLastSubset = lastSubset.length - 1;
+			for (int indexInCurrentSubset = numOfUniqueElementsInCurrentSubset
+					- 1; indexInCurrentSubset >= 0; indexInCurrentSubset--) {
+				if (currentSubset[indexInCurrentSubset].element != lastSubset[indexInLastSubset].element) {
+					// remove one copy from the CURRENT unique agent, and
+					// replace it with a copy of the NEXT unique agent
+					if (currentSubset[indexInCurrentSubset].repetition > 1) {
+						currentSubset[indexInCurrentSubset].repetition--;
+						currentSubset[indexInCurrentSubset
+								+ 1].element = currentSubset[indexInCurrentSubset].element
+										+ 1;
+						currentSubset[indexInCurrentSubset + 1].repetition = 1;
+						numOfUniqueElementsInCurrentSubset++;
+						fillRemainingAgents(totalNumberOfElementsSeenSoFar,
+								indexInCurrentSubset + 1);
+					} else {
+						currentSubset[indexInCurrentSubset].element++;
+						fillRemainingAgents(totalNumberOfElementsSeenSoFar,
+								indexInCurrentSubset);
+					}
+					return (prepareResult());
+				} else {
+					if (currentSubset[indexInCurrentSubset].repetition < lastSubset[indexInLastSubset].repetition) {
+						totalNumberOfElementsSeenSoFar += currentSubset[indexInCurrentSubset].repetition;
+						numOfUniqueElementsInCurrentSubset--;
+						indexInCurrentSubset--;
+
+						// remove one copy from the CURRENT unique agent, and
+						// replace it with a copy of the NEXT unique agent
+						if (currentSubset[indexInCurrentSubset].repetition > 1) {
+							currentSubset[indexInCurrentSubset].repetition--;
+							currentSubset[indexInCurrentSubset
+									+ 1].element = currentSubset[indexInCurrentSubset].element
+											+ 1;
+							currentSubset[indexInCurrentSubset
+									+ 1].repetition = 1;
+							numOfUniqueElementsInCurrentSubset++;
+							fillRemainingAgents(totalNumberOfElementsSeenSoFar,
+									indexInCurrentSubset + 1);
+						} else {
+							currentSubset[indexInCurrentSubset].element++;
+							fillRemainingAgents(totalNumberOfElementsSeenSoFar,
+									indexInCurrentSubset);
+						}
+						return (prepareResult());
+					} else {
+						totalNumberOfElementsSeenSoFar += currentSubset[indexInCurrentSubset].repetition;
+						indexInLastSubset--;
+						numOfUniqueElementsInCurrentSubset--;
+					}
+				}
+			}
+			return (null);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * fills the remaining agents in a sequence, e.g., if we have 5 copies of
+	 * agent 2, 5 copies of agent 3, and 3 copies of agent 4, and we want to
+	 * fill 8 agents, then we put 5 copies of agent 2, and 3 copies of agent 3
+	 */
+	private void fillRemainingAgents(int totalNumOfAgentsToBeAdded,
+			int indexAtWhichToStartFilling) {
+		if (totalNumOfAgentsToBeAdded == 0) {
+			return;
+		}
+		int firstUniqueAgentToBeAdded = currentSubset[indexAtWhichToStartFilling].element;
+
+		// deal with the first index at which we need to fill elements
+		int max = multisetWithIncrementalElements[firstUniqueAgentToBeAdded
+				- 1].repetition
+				- currentSubset[indexAtWhichToStartFilling].repetition;
+		if (max > 0) {
+			if (totalNumOfAgentsToBeAdded <= max) {
+				currentSubset[indexAtWhichToStartFilling].repetition += totalNumOfAgentsToBeAdded;
+				return;
+			} else {
+				currentSubset[indexAtWhichToStartFilling].repetition += max;
+				totalNumOfAgentsToBeAdded -= max;
+			}
+		}
+		// deal with the remaining indices at which we need to fill the
+		// remaining elements
+		int k = 1;
+		do {
+			numOfUniqueElementsInCurrentSubset++;
+			if (totalNumOfAgentsToBeAdded <= multisetWithIncrementalElements[firstUniqueAgentToBeAdded
+					+ k - 1].repetition) {
+				currentSubset[k
+						+ indexAtWhichToStartFilling] = new ElementOfMultiset(
+								firstUniqueAgentToBeAdded + k,
+								totalNumOfAgentsToBeAdded);
+				break;
+			} else {
+				currentSubset[k
+						+ indexAtWhichToStartFilling] = new ElementOfMultiset(
+								firstUniqueAgentToBeAdded + k,
+								multisetWithIncrementalElements[firstUniqueAgentToBeAdded
+										+ k - 1].repetition);
+				totalNumOfAgentsToBeAdded -= multisetWithIncrementalElements[firstUniqueAgentToBeAdded
+						+ k - 1].repetition;
+				k++;
+			}
+		} while (true);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * If multiset = [2,2,2,5,5,6], then multisetWithIncrementalElements =
+	 * [1,1,1,2,2,3]. We generate subsets of [1,1,1,2,2,3] and not
+	 * [2,2,2,5,5,6]. Then, before returning each subset, we convert it to the
+	 * original elements. For example, if subset=[1,2,2], then after calling
+	 * "prepareResult" we have subsetWithOriginalElements = [2,5,5].
+	 */
+	private ElementOfMultiset[] prepareResult() {
+		// Initialization
+		ElementOfMultiset[] subsetWithOriginalElements = new ElementOfMultiset[numOfUniqueElementsInCurrentSubset];
+		if (keepTrackOfNumOfInstancesOutsideSubset) {
+			numOfInstancesOutsideSubset = new int[numOfUniqueElementsInCurrentSubset];
+		}
+
+		for (int i = 0; i < numOfUniqueElementsInCurrentSubset; i++) {
+			ElementOfMultiset originalElement = multiset[currentSubset[i].element
+					- 1];
+			subsetWithOriginalElements[i] = new ElementOfMultiset(
+					originalElement.element, currentSubset[i].repetition);
+
+			if (keepTrackOfNumOfInstancesOutsideSubset) {
+				numOfInstancesOutsideSubset[i] = originalElement.repetition
+						- currentSubset[i].repetition;
+			}
+		}
+		return (subsetWithOriginalElements);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Returns the first subset in the list, e.g., for
+	 * multisetWithIncrementalElements=[1,1,1,2,2,3], the first subset of size 4
+	 * is [1,1,1,2]
+	 */
+	private void setCurrentSubsetToFirstSubset() {
+		// Initialize the size of the first subset to the size of the multiset,
+		// because the
+		// number of unique elements in first subset is AT MOST equal to the
+		// size of the multiset.
+		currentSubset = new ElementOfMultiset[multisetWithIncrementalElements.length];
+		for (int i = 0; i < currentSubset.length; i++) {
+			currentSubset[i] = new ElementOfMultiset(0, 0);
+		}
+		// Initialize "numOfInstancesOutsideSubset" if necessary
+		if (keepTrackOfNumOfInstancesOutsideSubset)
+			numOfInstancesOutsideSubset = new int[multisetWithIncrementalElements.length];
+
+		int totalNumOfAgentsToBeAdded = sizeOfSubsets;
+		int i = 0;
+		for (int j = 0; j < multisetWithIncrementalElements.length; j++) {
+			if (totalNumOfAgentsToBeAdded <= multisetWithIncrementalElements[j].repetition) {
+				currentSubset[i] = new ElementOfMultiset(
+						multisetWithIncrementalElements[j].element,
+						totalNumOfAgentsToBeAdded);
+				// Update "numOfInstancesOutsideSubset" if necessary
+				if (keepTrackOfNumOfInstancesOutsideSubset) {
+					numOfInstancesOutsideSubset[i] = multisetWithIncrementalElements[j].repetition
+							- totalNumOfAgentsToBeAdded;
+				}
+				break;
+			} else {
+				currentSubset[i] = new ElementOfMultiset(
+						multisetWithIncrementalElements[j].element,
+						multisetWithIncrementalElements[j].repetition);
+				totalNumOfAgentsToBeAdded -= multisetWithIncrementalElements[j].repetition;
+				i++;
+				// Update "numOfInstancesOutsideSubset" if necessary
+				if (keepTrackOfNumOfInstancesOutsideSubset) {
+					numOfInstancesOutsideSubset[i] = 0;
+				}
+			}
+		}
+		numOfUniqueElementsInCurrentSubset = i + 1;
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Returns the first subset in the list, e.g., for
+	 * multisetWithIncrementalElements=[1,1,1,2,2,3], the first subset of size 4
+	 * is [1,1,1,2]
+	 */
+	private void setLastSubset() {
+		// Initialize the size of the last subset to "sizeOfSubsets", because
+		// the
+		// number of unique elements in last subset is AT MOST "sizeOfSubsets".
+		ElementOfMultiset[] temp = new ElementOfMultiset[multisetWithIncrementalElements.length];
+		for (int i = 0; i < temp.length; i++)
+			temp[i] = new ElementOfMultiset(0, 0);
+
+		int totalNumOfAgentsToBeAdded = sizeOfSubsets;
+		int i = temp.length - 1;
+		for (int j = multisetWithIncrementalElements.length - 1; j >= 0; j--) {
+			if (totalNumOfAgentsToBeAdded <= multisetWithIncrementalElements[j].repetition) {
+				temp[i] = new ElementOfMultiset(
+						multisetWithIncrementalElements[j].element,
+						totalNumOfAgentsToBeAdded);
+				break;
+			} else {
+				temp[i] = new ElementOfMultiset(
+						multisetWithIncrementalElements[j].element,
+						multisetWithIncrementalElements[j].repetition);
+				totalNumOfAgentsToBeAdded -= multisetWithIncrementalElements[j].repetition;
+				i--;
+			}
+		}
+		// fill the elements of "temp" in "lastSubset"
+		lastSubset = new ElementOfMultiset[multisetWithIncrementalElements.length
+				- i];
+		for (int j = lastSubset.length - 1; j >= 0; j--)
+			lastSubset[j] = temp[temp.length - lastSubset.length + j];
+	}
+
+	// *********************************************************************************************************
+
+	/**
+	 * For every element in the current subset, this method returns the number
+	 * of copies of that element that did not appear in the subset
+	 * 
+	 * For example, if multiset = [1,1,1,1,2,2,2,3,3,3,4,4,4], and the current
+	 * subset is [1,1,3,3,4], then this method returns [2,1,2]. This is because
+	 * the numbers of instances outside the current subset are: 2 for element
+	 * "1", 1 for element "3", and 2 for element "4"
+	 */
+	public int[] getNumOfInstancesOutsideSubset() {
+		if (keepTrackOfNumOfInstancesOutsideSubset)
+			return (numOfInstancesOutsideSubset);
+		else {
+			System.err.println(
+					"the method getNumOfInstancesOutsideSubset was called while the"
+							+ "parameter keepTrackOfNumOfInstancesOutsideSubset was set to false");
+			return (null);
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Returns a random subset of the multiset
+	 */
+	public ElementOfMultiset[] getRandomSubset() {
+		// deal with the case where the input is incorrect.
+		if ((sizeOfSubsets <= 0)
+				|| (sizeOfSubsets >= totalNumOfElementsInMultiset)) {
+			System.err.println(
+					"ERROR in method: getRandomSubset, the size of the desired subset must be between 1 and totalNumOfElements-1");
+			return (null);
+		}
+
+		Random randomIndex = new Random();
+		Random randomRepetition = new Random();
+
+		// To improve efficiency, if the size of the required subset is greater
+		// than "multiset.length/2", then
+		// we will generate a random subset of size "multiset.length -
+		// sizeOfSubset" instead of "sizeOfSubset"
+		int sizeThatWeWillConsider;
+		if (sizeOfSubsets <= totalNumOfElementsInMultiset / 2)
+			sizeThatWeWillConsider = sizeOfSubsets;
+		else
+			sizeThatWeWillConsider = totalNumOfElementsInMultiset
+					- sizeOfSubsets;
+
+		// this array specifies the indices of unique elements that can still be
+		// added to the subset
+		int[] indicesOfElementsThatCanBeAdded = new int[multiset.length];
+
+		// the number of times each unique element is repeated in the subset
+		int[] numOfRepetitionsInSubset = new int[multiset.length];
+
+		// this specifies the maximum index that we can deal with in the array
+		// "indicesOfElementsThatCanBeAdded",
+		// i.e., we will only deal with: "indicesOfElementsThatCanBeAdded[ i ]"
+		// for all i < maxIndex.
+		int maxIndex = multiset.length;
+
+		// initialization
+		for (int i = multiset.length - 1; i >= 0; i--) {
+			indicesOfElementsThatCanBeAdded[i] = i;
+			numOfRepetitionsInSubset[i] = 0;
+		}
+		// create the random subset of size = "sizeThatWeWillConsider"
+		int sizeOfSubsetSoFar = 0;
+		int numOfUniqueElementsInSubset = 0;
+		do {
+			// choose a random unique element
+			int i = randomIndex.nextInt(maxIndex);
+			int j = indicesOfElementsThatCanBeAdded[i];
+
+			// choose a random number of repetitions for the unique element
+			// (here, the random number will exclude zero)
+			// int k = 1 + randomRepetition.nextInt( Math.min(
+			// sizeThatWeWillConsider - sizeOfSubsetSoFar ,
+			// multiset[j].repetition - numOfRepetitionsInSubset[j] ) );
+			int k = 1; /* To be Unbiased! */
+
+			// Check if this is a new unique element in the subset
+			if (numOfRepetitionsInSubset[j] == 0)
+				numOfUniqueElementsInSubset++;
+
+			// update the number of repetitions in the subset
+			numOfRepetitionsInSubset[j] += k;
+
+			// update the size of the subset so far
+			sizeOfSubsetSoFar += k;
+
+			// if the element cannot be added any more, get rid of it
+			if (numOfRepetitionsInSubset[j] == multiset[j].repetition) {
+				indicesOfElementsThatCanBeAdded[i] = indicesOfElementsThatCanBeAdded[maxIndex
+						- 1];
+				maxIndex--;
+			}
+		} while (sizeOfSubsetSoFar != sizeThatWeWillConsider);
+
+		// Create the random subset based on "numOfRepetitionsInSubset"
+		//
+		// Note: As mentioned earlier, to improve efficiency, if the size of the
+		// required subset is
+		// greater than "multiset.length/2", then we would have generated a
+		// random subset of
+		// size: "multiset.length - sizeOfSubset" instead of "sizeOfSubset"
+		//
+		if (sizeOfSubsets <= totalNumOfElementsInMultiset / 2) {
+			// Intialize "numOfInstancesOutsideSubset" if necessary
+			if (keepTrackOfNumOfInstancesOutsideSubset)
+				numOfInstancesOutsideSubset = new int[numOfUniqueElementsInSubset];
+
+			ElementOfMultiset[] subset = new ElementOfMultiset[numOfUniqueElementsInSubset];
+			int j = 0;
+			for (int i = 0; i < multiset.length; i++) {
+				if (numOfRepetitionsInSubset[i] > 0) {
+					subset[j] = new ElementOfMultiset(multiset[i].element,
+							numOfRepetitionsInSubset[i]);
+
+					// Update "numOfInstancesOutsideSubset" if necessary
+					if (keepTrackOfNumOfInstancesOutsideSubset)
+						numOfInstancesOutsideSubset[j] = multiset[i].repetition
+								- numOfRepetitionsInSubset[i];
+
+					j++;
+					if (j == subset.length) {
+						break;
+					}
+				}
+			}
+			return (subset);
+		} else {
+			// We initially allocate memory for a number of unique elements that
+			// is equal to "multiset.length".
+			// This is because the number of unique elements in the subset is at
+			// most "multiset.length"
+			ElementOfMultiset[] temp_subset = new ElementOfMultiset[multiset.length];
+			int[] temp_numOfInstancesOutsideSubset = new int[multiset.length];
+			int j = 0;
+			for (int i = 0; i < multiset.length; i++) {
+				if (numOfRepetitionsInSubset[i] < multiset[i].repetition) {
+					temp_subset[j] = new ElementOfMultiset(multiset[i].element,
+							multiset[i].repetition
+									- numOfRepetitionsInSubset[i]);
+
+					// Update "numOfInstancesOutsideSubset" if necessary
+					if (keepTrackOfNumOfInstancesOutsideSubset)
+						temp_numOfInstancesOutsideSubset[j] = numOfRepetitionsInSubset[i];
+
+					j++;
+				}
+			}
+			// truncate "temp_subset" and "temp_numOfInstanceOutsideSubset" to
+			// the desired length
+			ElementOfMultiset[] subset = new ElementOfMultiset[j];
+			numOfInstancesOutsideSubset = new int[j];
+			for (int i = 0; i < j; i++) {
+				subset[i] = temp_subset[i];
+				numOfInstancesOutsideSubset[i] = temp_numOfInstancesOutsideSubset[i];
+			}
+			return (subset);
+		}
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/inputOutput/Input.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/inputOutput/Input.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/inputOutput/Input.java	(revision 30)
@@ -0,0 +1,585 @@
+package geniusweb.ip.inputOutput;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.Random;
+import java.util.TreeSet;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.distribution.BetaDistributionImpl;
+import org.apache.commons.math.distribution.ExponentialDistributionImpl;
+import org.apache.commons.math.distribution.GammaDistributionImpl;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.general.General;
+import geniusweb.ip.general.RandomPartition;
+
+public class Input {
+	// The parameters that can only be set here (i.e., not through the GUI):
+	public final boolean printPercentageOf_v_equals_f = false; // only relevant
+																// when using
+																// dynamic
+																// programming.
+																// The
+																// percentage of
+																// coalitions
+																// whose value
+																// equals their
+																// best
+																// partition
+	public final boolean printDistributionOfThefTable = false; // only relevant
+																// when using
+																// dynamic
+																// programming.
+																// The
+																// distribution
+																// of the f
+																// table
+	public final boolean printCoalitionValueDistribution = false; // the
+																	// distribution
+																	// of the
+																	// coalition
+																	// values
+	public final boolean printCoalitionStructureValueDistribution = false; // the
+																			// distribution
+																			// of
+																			// the
+																			// coalition
+																			// structure
+																			// values
+																			// (based
+																			// on
+																			// a
+																			// sample
+																			// of
+																			// coalition
+																			// structures)
+	public final boolean printTheSubspaceThatIsCurrentlyBeingSearched = false; // only
+																				// relevant
+																				// when
+																				// running
+																				// IP
+																				// or
+																				// ODP-IP
+	public final boolean printTheCoalitionsWhenPrintingTheirValues = true;
+	public final boolean pruneSubspaces = true; // only relevant when running IP
+												// or ODP-IP. Determines whether
+												// IP is allowed to prune entire
+												// subspaces based on their
+												// bounds.
+	public final boolean useBranchAndBound = true; // only relevant when running
+													// IP or ODP-IP
+	public final boolean useSamplingWhenSearchingSubspaces = false; // only
+																	// relevant
+																	// when
+																	// running
+																	// IP or
+																	// ODP-IP.
+	public final boolean samplingDoneInGreedyFashion = false;
+	private final double sigma = 0.1; // relevant to the following
+										// distributions: "Normal", "Modified
+										// Normal", and "Agent-based Normal"
+	public final boolean useEfficientImplementationOfIDP = true; // if "true",
+																	// IDP
+																	// evaluates
+																	// more
+																	// splits,
+																	// but this
+																	// allows
+																	// for a
+																	// more
+																	// efficient
+																	// implementation
+																	// and a
+																	// shorter
+																	// runtime.
+
+	public SolverNames solverName;
+	public boolean readCoalitionValuesFromFile;
+	public boolean storeCoalitionValuesInFile;
+
+	// The IP parameters
+	public boolean orderIntegerPartitionsAscendingly; // setting this to false
+														// means integers will
+														// be ordered
+														// descendingly
+	public double acceptableRatio; // only relevant when running IP or ODP-IP
+
+	// The printing parameters
+	public boolean printDetailsOfSubspaces; // only relevant when running IP or
+											// ODP-IP
+	public boolean printNumOfIntegerPartitionsWithRepeatedParts;
+	public boolean printInterimResultsOfIPToFiles; // only relevant when running
+													// IP or ODP-IP
+	public boolean printTimeTakenByIPForEachSubspace; // only relevant when
+														// running IP or ODP-IP
+
+	// General parameters
+	public TreeSet<Integer> feasibleCoalitions;
+	public int numOfAgents;
+	public long numOfRunningTimes;
+	public double[] coalitionValues;
+	public String outputFolder;
+	public ValueDistribution valueDistribution;
+	public String folderInWhichCoalitionValuesAreStored = "D:/CSGtemp/coalitionValues"; // this
+																						// is
+																						// the
+																						// default
+																						// setting,
+																						// unless
+																						// it
+																						// is
+																						// modified
+																						// by
+																						// another
+																						// method
+
+	public String problemID = ""; // This is the ID of the problem instance
+									// (useful when taking average over multiple
+									// instances)
+
+	// ******************************************************************************************************
+
+	/**
+	 * @param numOfAgents      the number of agents
+	 * @param coalitionValues. Should be double[] of size 2^numOfAgents
+	 * @param acceptableRatio  usually 100 (%)
+	 */
+	public Input(int numOfAgents, double[] coalitionValues,
+			double acceptableRatio) {
+		this.numOfAgents = numOfAgents;
+		this.coalitionValues = coalitionValues;
+		this.acceptableRatio = acceptableRatio;
+		feasibleCoalitions = null; // this is only needed for a constraint
+									// solver. Just ignore it.
+		numOfRunningTimes = 1; // Setting this to 1 means we are not taking an
+								// average over multiple run with different
+								// problem instaces.
+		storeCoalitionValuesInFile = false; // Setting this to false means we
+											// won't be storing the coalition
+											// values after solving the problem
+		printDetailsOfSubspaces = false;
+		valueDistribution = ValueDistribution.UNKNOWN;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Return the value of a coalition where every member is represented by an
+	 * bit
+	 */
+	public double getCoalitionValue(int coalitionInBitFormat) {
+		return (coalitionValues[coalitionInBitFormat]);
+	}
+
+	/**
+	 * Return the value of a coalition where every member is represented by an
+	 * integer
+	 */
+	public double getCoalitionValue(int[] coalitionInByteFormat) {
+		int coalitionInBitFormat = Combinations
+				.convertCombinationFromByteToBitFormat(coalitionInByteFormat);
+		return (getCoalitionValue(coalitionInBitFormat));
+	}
+
+	/**
+	 * Return the value of a given coalition structure CS, where each coalition
+	 * member is represented using an integer
+	 */
+	public double getCoalitionStructureValue(int[][] coalitionStructure) {
+		double valueOfCS = 0;
+		for (int i = 0; i < coalitionStructure.length; i++)
+			valueOfCS += getCoalitionValue(coalitionStructure[i]);
+		return (valueOfCS);
+	}
+
+	/**
+	 * Return the value of a given coalition structure CS, where each coalition
+	 * member is represented using a bit
+	 */
+	public double getCoalitionStructureValue(int[] coalitionStructure) {
+		double valueOfCS = 0;
+		for (int i = 0; i < coalitionStructure.length; i++)
+			valueOfCS += getCoalitionValue(coalitionStructure[i]);
+		return (valueOfCS);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * This method sets the values of the coalitions of a particular size.
+	 */
+	public void generateCoalitionValues() {
+		// initialization
+		Random random = new Random();
+		long time = System.currentTimeMillis();
+		coalitionValues = new double[(int) Math.pow(2, numOfAgents)];
+		coalitionValues[0] = 0; // just in case, set the value of the empty set
+								// to 0
+
+		// Give random strengths to the agents. This is only used in
+		// "agent-Based" distributions
+		double[] agentStrength_normal = new double[numOfAgents];
+		double[] agentStrength_uniform = new double[numOfAgents];
+		for (int agent = 1; agent <= numOfAgents; agent++) {
+			agentStrength_normal[agent - 1] = Math.max(0, General
+					.getRandomNumberFromNormalDistribution(10, sigma, random));
+			agentStrength_uniform[agent - 1] = random.nextDouble() * 10;
+		}
+		// Uniform distribution
+		if (valueDistribution == ValueDistribution.UNIFORM)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--)
+				coalitionValues[coalition] = Math.round(
+						(Integer.bitCount(coalition) * random.nextDouble())
+								* 100000000);
+
+		// Normal distribution
+		if (valueDistribution == ValueDistribution.NORMAL)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				coalitionValues[coalition] = Math.max(0,
+						Integer.bitCount(coalition)
+								* General.getRandomNumberFromNormalDistribution(
+										10, sigma, random));
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		// Modified Uniform distribution
+		if (valueDistribution == ValueDistribution.MODIFIEDUNIFORM)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				coalitionValues[coalition] = random.nextDouble() * 10
+						* Integer.bitCount(coalition);
+				int probability = random.nextInt(100);
+				if (probability <= 20)
+					coalitionValues[coalition] += random.nextDouble() * 50;
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		// Modified Normal distribution
+		if (valueDistribution == ValueDistribution.MODIFIEDNORMAL)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				coalitionValues[coalition] = Math.max(0,
+						Integer.bitCount(coalition)
+								* General.getRandomNumberFromNormalDistribution(
+										10, sigma, random));
+				int probability = random.nextInt(100);
+				// if(probability <=20) valuesOfCurSize[index] += r.nextDouble()
+				// * 50;
+				if (probability <= 20)
+					coalitionValues[coalition] += General
+							.getRandomNumberFromNormalDistribution(25, sigma,
+									random);
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		// Exponential distribution
+		if (valueDistribution == ValueDistribution.EXPONENTIAL) {
+			ExponentialDistributionImpl exponentialDistributionImpl = new ExponentialDistributionImpl(
+					1); // the mean of an exponential distribution is 1/lambda.
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				boolean repeat = false;
+				do {
+					try {
+						coalitionValues[coalition] = Math.max(0,
+								Integer.bitCount(coalition)
+										* exponentialDistributionImpl.sample());
+					} catch (MathException e) {
+						repeat = true;
+					}
+				} while (repeat == true);
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		}
+		// Beta distribution
+		if (valueDistribution == ValueDistribution.BETA) {
+			BetaDistributionImpl betaDistributionImpl = new BetaDistributionImpl(
+					0.5, 0.5);
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				boolean repeat = false;
+				do {
+					try {
+						coalitionValues[coalition] = Math.max(0,
+								Integer.bitCount(coalition)
+										* betaDistributionImpl.sample());
+					} catch (MathException e) {
+						repeat = true;
+					}
+				} while (repeat == true);
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		}
+		// Gamma distribution
+		if (valueDistribution == ValueDistribution.GAMMA) {
+			GammaDistributionImpl gammaDistributionImpl = new GammaDistributionImpl(
+					2, 2);
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				boolean repeat = false;
+				do {
+					try {
+						coalitionValues[coalition] = Math.max(0,
+								Integer.bitCount(coalition)
+										* gammaDistributionImpl.sample());
+					} catch (MathException e) {
+						repeat = true;
+					}
+				} while (repeat == true);
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		}
+		// Agent-based Uniform distribution
+		if (valueDistribution == ValueDistribution.AGENTBASEDUNIFORM)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				int[] members = Combinations
+						.convertCombinationFromBitToByteFormat(coalition,
+								numOfAgents);
+
+				// if percentage is, say 60%, then an agent's value can go up by
+				// at most 60%, and down by at most 60%
+				double percentage = 100;
+				coalitionValues[coalition] = 0;
+				for (int m = 0; m < Integer.bitCount(coalition); m++) {
+					double rangeSize = (percentage / 100)
+							* agentStrength_uniform[members[m] - 1] * 2;
+					double startOfRange = ((100 - percentage) / 100)
+							* agentStrength_uniform[members[m] - 1];
+					coalitionValues[coalition] += startOfRange
+							+ (random.nextDouble() * rangeSize);
+				}
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		// Agent-based Normal distribution
+		if (valueDistribution == ValueDistribution.AGENTBASEDNORMAL)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				int[] members = Combinations
+						.convertCombinationFromBitToByteFormat(coalition,
+								numOfAgents);
+				coalitionValues[coalition] = 0;
+				for (int m = 0; m < Integer.bitCount(coalition); m++) {
+					double newValue;
+					newValue = General.getRandomNumberFromNormalDistribution(
+							agentStrength_normal[members[m] - 1], sigma,
+							random);
+					coalitionValues[coalition] += Math.max(0, newValue);
+				}
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+		// NDCS distribution
+		if (valueDistribution == ValueDistribution.NDCS)
+			for (int coalition = coalitionValues.length
+					- 1; coalition > 0; coalition--) {
+				do {
+					coalitionValues[coalition] = (General
+							.getRandomNumberFromNormalDistribution(
+									Integer.bitCount(coalition),
+									Math.sqrt(Integer.bitCount(coalition)),
+									random));
+				} while (coalitionValues[coalition] <= 0);
+				coalitionValues[coalition] = Math
+						.round(coalitionValues[coalition] * 100000000);
+			}
+
+		System.out.println(numOfAgents + " agents, "
+				+ ValueDistribution.toString(valueDistribution)
+				+ " distribution. The time required to generate the coalition values (in milli second): "
+				+ (System.currentTimeMillis() - time));
+
+		// get the coalition value distribution
+		if (printCoalitionValueDistribution)
+			printCoalitionValueDistribution();
+
+		// get the coalition structure value distribution
+		if (printCoalitionStructureValueDistribution)
+			printCoalitionStructureValueDistribution(numOfAgents);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Store the coalition values in a file
+	 */
+	public void storeCoalitionValuesInFile(int problemID) {
+		// Create the output folder
+		General.createFolder(folderInWhichCoalitionValuesAreStored);
+
+		// Set the name of the output file, and empty it
+		String filePathAndName = folderInWhichCoalitionValuesAreStored;
+		filePathAndName += "/" + numOfAgents + "Agents_"
+				+ ValueDistribution.toString(valueDistribution) + "_"
+				+ problemID + ".txt";
+		General.clearFile(filePathAndName);
+
+		// Print the coalition values to a string buffer
+		StringBuffer tempStringBuffer = new StringBuffer();
+		for (int coalition = 0; coalition < coalitionValues.length; coalition++)
+			tempStringBuffer.append(coalitionValues[coalition] + "\n");
+
+		// empty the contents of the string buffer into the output file
+		General.printToFile(filePathAndName, tempStringBuffer.toString(),
+				false);
+		tempStringBuffer.setLength(0);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Read the coalition values from a file
+	 */
+	public void readCoalitionValuesFromFile(int problemID) {
+		coalitionValues = new double[(int) Math.pow(2, numOfAgents)];
+
+		// Set the name and path of the file from which we will read the
+		// coalition values
+		String filePathAndName = folderInWhichCoalitionValuesAreStored;
+		filePathAndName += "/" + numOfAgents + "Agents_"
+				+ ValueDistribution.toString(valueDistribution) + "_"
+				+ problemID + ".txt";
+		try {
+			BufferedReader bufferedReader = new BufferedReader(
+					new FileReader(filePathAndName));
+			for (int coalition = 0; coalition < coalitionValues.length; coalition++)
+				coalitionValues[coalition] = (new Double(
+						bufferedReader.readLine())).doubleValue();
+
+			bufferedReader.close();
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Read the coalition values from a file
+	 */
+	public void readCoalitionValuesFromFile(String fileName) {
+		coalitionValues = new double[(int) Math.pow(2, numOfAgents)];
+
+		// Set the name and path of the file from which we will read the
+		// coalition values
+		// String filePathAndName = folderInWhichCoalitionValuesAreStored;
+		String filePathAndName = fileName;
+		try {
+			BufferedReader bufferedReader = new BufferedReader(
+					new FileReader(filePathAndName));
+			for (int coalition = 0; coalition < coalitionValues.length; coalition++)
+				coalitionValues[coalition] = (new Double(
+						bufferedReader.readLine())).doubleValue();
+
+			bufferedReader.close();
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given the coalition values, print the "weighted" distribution (i.e.,
+	 * where every coalition value is divided by the size of that coalition).
+	 */
+	public void printCoalitionValueDistribution() {
+		// Initialization
+		int[] counter = new int[40];
+		for (int i = 0; i < counter.length; i++) {
+			counter[i] = 0;
+		}
+		// get the minimum and maximum values
+		long min = Integer.MAX_VALUE;
+		long max = Integer.MIN_VALUE;
+		for (int coalition = 1; coalition < coalitionValues.length; coalition++) {
+			long currentWeightedValue = Math.round(
+					coalitionValues[coalition] / Integer.bitCount(coalition));
+			if (min > currentWeightedValue)
+				min = currentWeightedValue;
+			if (max < currentWeightedValue)
+				max = currentWeightedValue;
+		}
+		System.out.println(
+				"The maximum weighted coalition value (i.e., value of coalition divided by size of that coalition) is  "
+						+ max + "  and the minimum one is  " + min);
+
+		// get the distribution
+		for (int coalition = 1; coalition < coalitionValues.length; coalition++) {
+			long currentWeightedValue = Math.round(
+					coalitionValues[coalition] / Integer.bitCount(coalition));
+			int percentageOfMax = Math.round((currentWeightedValue - min)
+					* (counter.length - 1) / (max - min));
+			counter[percentageOfMax]++;
+		}
+		// Printing
+		System.out.println(
+				"The distribution of the weighted coalition values (i.e., every value of coalition is divided by size of that coalition) is:");
+		System.out.print(ValueDistribution.toString(valueDistribution)
+				+ "_coalition = [");
+		for (int i = 0; i < counter.length; i++)
+			System.out.print(counter[i] + " ");
+		System.out.println("]");
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given the coalition values, print the "weighted" distribution (i.e.,
+	 * where every coalition value is divided by the size of that coalition).
+	 */
+	public void printCoalitionStructureValueDistribution(int n) {
+		RandomPartition randomPartition = new RandomPartition(n);
+		int numOfSamples = 10000000;
+		long[] sampleValues = new long[numOfSamples];
+		for (int i = 0; i < numOfSamples; i++) {
+			int[] currentCoalitionStructure = randomPartition.get();
+			long value = 0;
+			for (int j = 0; j < currentCoalitionStructure.length; j++)
+				value += coalitionValues[currentCoalitionStructure[j]];
+			// value /= currentCoalitionStructure.length;
+			sampleValues[i] = value;
+		}
+		// Initialization
+		int[] counter = new int[200];
+		for (int i = 0; i < counter.length; i++) {
+			counter[i] = 0;
+		}
+		// get the minimum and maximum values
+		long min = Integer.MAX_VALUE;
+		long max = Integer.MIN_VALUE;
+		for (int i = 1; i < sampleValues.length; i++) {
+			long currentValue = sampleValues[i];
+			if (min > currentValue)
+				min = currentValue;
+			if (max < currentValue)
+				max = currentValue;
+		}
+		System.out.println(
+				"The maximum weighted coalition value (i.e., value of coalition divided by size of that coalition) is  "
+						+ max + "  and the minimum one is  " + min);
+
+		// get the distribution
+		for (int i = 1; i < sampleValues.length; i++) {
+			long currentValue = sampleValues[i];
+			int percentageOfMax = Math.round(
+					(currentValue - min) * (counter.length - 1) / (max - min));
+			counter[percentageOfMax]++;
+		}
+		// Printing
+		System.out.println(
+				"The distribution of the weighted coalition STRUCTURE values  (i.e., every value of coalition is divided by size of that coalition) is:");
+		System.out.print(ValueDistribution.toString(valueDistribution)
+				+ "_structure = [");
+		for (int i = 0; i < counter.length; i++)
+			System.out.print(counter[i] + " ");
+		System.out.println("]");
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/inputOutput/Output.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/inputOutput/Output.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/inputOutput/Output.java	(revision 30)
@@ -0,0 +1,305 @@
+package geniusweb.ip.inputOutput;
+
+import java.util.Arrays;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.general.General;
+import geniusweb.ip.ipSolver.Node;
+import geniusweb.ip.ipSolver.Subspace;
+import geniusweb.ip.mainSolver.Result;
+
+public class Output {
+	private StringBuffer time_numOfRemainingCoalitions;
+	private StringBuffer time_solutionQuality;
+	private StringBuffer time_boundQuality;
+	private StringBuffer numOfSearchedCoalitions_solutionQuality;
+	private StringBuffer numOfSearchedCoalitions_boundQuality;
+	private double[] prevValues_time_numOfRemainingCoalitions;
+	private double[] prevValues_time_solutionQuality;
+	private double[] prevValues_time_boundQuality;
+	private double[] prevValues_numOfSearchedCoalitions_solutionQuality;
+	private double[] prevValues_numOfSearchedCoalitions_boundQuality;
+
+	// ******************************************************************************************************
+
+	/**
+	 * Initialization
+	 */
+	public Output(Input input) {
+		prevValues_time_solutionQuality = new double[2];
+		prevValues_time_boundQuality = new double[2];
+		prevValues_numOfSearchedCoalitions_boundQuality = new double[2];
+		prevValues_time_numOfRemainingCoalitions = new double[2];
+		prevValues_numOfSearchedCoalitions_solutionQuality = new double[2];
+
+		prevValues_time_solutionQuality[0] = 0;
+		prevValues_time_solutionQuality[1] = 0;
+		prevValues_time_boundQuality[0] = 0;
+		prevValues_time_boundQuality[1] = 0;
+		prevValues_numOfSearchedCoalitions_boundQuality[0] = 0;
+		prevValues_numOfSearchedCoalitions_boundQuality[1] = 0;
+		prevValues_time_numOfRemainingCoalitions[0] = 0;
+		prevValues_time_numOfRemainingCoalitions[1] = 0;
+		prevValues_numOfSearchedCoalitions_solutionQuality[0] = 0;
+		prevValues_numOfSearchedCoalitions_solutionQuality[1] = 0;
+
+		time_numOfRemainingCoalitions = new StringBuffer();
+		time_solutionQuality = new StringBuffer();
+		time_boundQuality = new StringBuffer();
+		numOfSearchedCoalitions_solutionQuality = new StringBuffer();
+		numOfSearchedCoalitions_boundQuality = new StringBuffer();
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Empty the contents of the String-Buffers to the relevant output files.
+	 */
+	public void emptyStringBufferContentsIntoOutputFiles(Input input) {
+		if (input.printInterimResultsOfIPToFiles) {
+			General.createFolder(input.outputFolder);
+			String tempStr = input.outputFolder + "/" + "(" + input.problemID
+					+ ")_";
+
+			if (time_numOfRemainingCoalitions.length() != 0) {
+				General.printToFile(
+						tempStr + "time_numOfRemainingCoalitions.txt",
+						time_numOfRemainingCoalitions.toString(), false);
+				time_numOfRemainingCoalitions.setLength(0);
+			}
+			if (time_solutionQuality.length() != 0) {
+				General.printToFile(tempStr + "time_solutionQuality.txt",
+						time_solutionQuality.toString(), false);
+				time_solutionQuality.setLength(0);
+			}
+			if (time_boundQuality.length() != 0) {
+				General.printToFile(tempStr + "time_boundQuality.txt",
+						time_boundQuality.toString(), false);
+				time_boundQuality.setLength(0);
+			}
+			if (numOfSearchedCoalitions_solutionQuality.length() != 0) {
+				General.printToFile(
+						tempStr + "numOfSearchedCoalitions_solutionQuality.txt",
+						numOfSearchedCoalitions_solutionQuality.toString(),
+						false);
+				numOfSearchedCoalitions_solutionQuality.setLength(0);
+			}
+			if (numOfSearchedCoalitions_boundQuality.length() != 0) {
+				General.printToFile(
+						tempStr + "numOfSearchedCoalitions_boundQuality.txt",
+						numOfSearchedCoalitions_boundQuality.toString(), false);
+				numOfSearchedCoalitions_boundQuality.setLength(0);
+			}
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Print the details of IP (or ODP-IP) to string buffers.
+	 */
+	public void printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+			Input input, Result result) {
+		if (input.printInterimResultsOfIPToFiles) {
+			long timeThatElapsedSinceIPStarted = System.currentTimeMillis()
+					- result.ipStartTime;
+			double boundQuality = result.ipUpperBoundOnOptimalValue
+					/ result.get_ipValueOfBestCSFound();
+
+			if ((prevValues_time_solutionQuality[0] != timeThatElapsedSinceIPStarted)
+					&& (prevValues_time_solutionQuality[1] != result
+							.get_ipValueOfBestCSFound())) {
+				time_solutionQuality.append(timeThatElapsedSinceIPStarted + ", "
+						+ result.get_ipValueOfBestCSFound() + "\n");
+				prevValues_time_solutionQuality[0] = timeThatElapsedSinceIPStarted;
+				prevValues_time_solutionQuality[1] = result
+						.get_ipValueOfBestCSFound();
+			}
+			if ((prevValues_time_boundQuality[0] != timeThatElapsedSinceIPStarted)
+					&& (prevValues_time_boundQuality[1] != boundQuality)) {
+				time_boundQuality.append(timeThatElapsedSinceIPStarted + ", "
+						+ boundQuality + "\n");
+				prevValues_time_boundQuality[0] = timeThatElapsedSinceIPStarted;
+				prevValues_time_boundQuality[1] = boundQuality;
+			}
+			if ((prevValues_numOfSearchedCoalitions_boundQuality[0] != result.ipNumOfExpansions)
+					&& (prevValues_numOfSearchedCoalitions_boundQuality[1] != boundQuality)) {
+				numOfSearchedCoalitions_boundQuality.append(
+						result.ipNumOfExpansions + ", " + boundQuality + "\n");
+				prevValues_numOfSearchedCoalitions_boundQuality[0] = result.ipNumOfExpansions;
+				prevValues_numOfSearchedCoalitions_boundQuality[1] = boundQuality;
+			}
+
+			if ((prevValues_numOfSearchedCoalitions_solutionQuality[0] != result.ipNumOfExpansions)
+					&& (prevValues_numOfSearchedCoalitions_solutionQuality[1] != result
+							.get_ipValueOfBestCSFound())) {
+				numOfSearchedCoalitions_solutionQuality
+						.append(result.ipNumOfExpansions + ", "
+								+ result.get_ipValueOfBestCSFound() + "\n");
+				prevValues_numOfSearchedCoalitions_solutionQuality[0] = result.ipNumOfExpansions;
+				prevValues_numOfSearchedCoalitions_solutionQuality[1] = result
+						.get_ipValueOfBestCSFound();
+			}
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * print the current results of IP to the relevant output files
+	 */
+	public void printFinalResultsOfIPToFiles(Input input, Result result) {
+		if (input.printInterimResultsOfIPToFiles) {
+			General.createFolder(input.outputFolder);
+
+			// Get the optimal coalition structure, and convert it to string
+			String bestCSFoundByIP_asString;
+			if (result.get_ipBestCSFound() == null)
+				bestCSFoundByIP_asString = "null";
+			else
+				bestCSFoundByIP_asString = Arrays
+						.deepToString(result.get_ipBestCSFound());
+
+			// Get the level of the optimal coalition structure, and convert it
+			// to string
+			String levelOfBestCSFoundByIP_asString;
+			if (result.get_ipBestCSFound() == null)
+				levelOfBestCSFoundByIP_asString = "null";
+			else
+				levelOfBestCSFoundByIP_asString = (new Integer(
+						(result.get_ipBestCSFound()).length)).toString();
+
+			// Get the integer partition of the optimal coailtion structure, and
+			// convert it to string
+			String integerPartitionOfBestCSFoundByIP_asString;
+			if (result.get_ipBestCSFound() == null)
+				integerPartitionOfBestCSFoundByIP_asString = "null";
+			else {
+				int[][] ipBestCSFound = result.get_ipBestCSFound();
+				int[] integerPartitionOfBestCSFoundByIP = new int[ipBestCSFound.length];
+				for (int i = 0; i < ipBestCSFound.length; i++)
+					integerPartitionOfBestCSFoundByIP[i] = ipBestCSFound[i].length;
+				integerPartitionOfBestCSFoundByIP_asString = General
+						.convertArrayToString(
+								integerPartitionOfBestCSFoundByIP);
+			}
+
+			// Print the results
+			String tempStr = input.outputFolder + "/";
+			General.printToFile(tempStr + "bestCSFoundByIP.txt",
+					bestCSFoundByIP_asString + "\n", false);
+			General.printToFile(
+					tempStr + "integerPartitionOfBestCSFoundByIP.txt",
+					integerPartitionOfBestCSFoundByIP_asString + "\n", false);
+			General.printToFile(tempStr + "levelOfBestCSFoundByIP.txt",
+					levelOfBestCSFoundByIP_asString + "\n", false);
+			General.printToFile(tempStr + "valueOfBestCSFoundByIP.txt",
+					result.get_ipValueOfBestCSFound() + "\n", false);
+
+			tempStr += "(" + input.problemID + ")_";
+			long timeThatElapsedSinceIPStarted = System.currentTimeMillis()
+					- result.ipStartTime;
+			double boundQuality = 1;// result.ipUpperBoundOnOptimalValue /
+									// result.ipValueOfBestCSFound;
+
+			General.printToFile(tempStr + "time_solutionQuality.txt",
+					timeThatElapsedSinceIPStarted + ", "
+							+ result.get_ipValueOfBestCSFound() + "\n",
+					false);
+			General.printToFile(tempStr + "time_boundQuality.txt",
+					timeThatElapsedSinceIPStarted + ", " + boundQuality + "\n",
+					false);
+			General.printToFile(
+					tempStr + "numOfSearchedCoalitions_boundQuality.txt",
+					result.ipNumOfExpansions + ", " + boundQuality + "\n",
+					false);
+			General.printToFile(
+					tempStr + "numOfSearchedCoalitions_solutionQuality.txt",
+					result.ipNumOfExpansions + ", "
+							+ result.get_ipValueOfBestCSFound() + "\n",
+					false);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Print the size, upperBound, and lowerBound for each subspace
+	 */
+	public void printDetailsOfSubspaces(Input input, Result result) {
+		if (input.printDetailsOfSubspaces) {
+			// Count the number of possible coalition structures
+			long numOfCS = Combinations.getNumOfCS(input.numOfAgents);
+
+			Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+			double[] percentagePerLevel = new double[nodes.length];
+			double maxPercentage = 0;
+			int ii = 0;
+			int jj = 0;
+			for (int i = 0; i < nodes.length; i++) {
+				System.out.println("");
+				percentagePerLevel[i] = 0;
+
+				for (int j = 0; j < nodes[i].length; j++) {
+					Subspace curSubspace = nodes[i][j].subspace;
+
+					double percentagePerSubspace = (double) (curSubspace.sizeOfSubspace
+							* 100) / numOfCS;
+					if (maxPercentage < percentagePerSubspace) {
+						maxPercentage = percentagePerSubspace;
+						ii = i; // Store the location of the biggest subspace
+						jj = j; // Store the location of the biggest subspace
+					}
+
+					// Update the percentage for the current level
+					percentagePerLevel[i] += percentagePerSubspace;
+
+					// Printing the details
+					General.printArray("subspace[" + i + "][" + j + "] = ",
+							curSubspace.integers);
+					System.out.println(",   size = "
+							+ curSubspace.sizeOfSubspace + " ("
+							+ General.setDecimalPrecision(percentagePerSubspace,
+									5)
+							+ "%)," + "  UB = " + curSubspace.UB + ",  LB = "
+							+ curSubspace.LB + ",  AVG = " + curSubspace.Avg);
+				}
+			}
+			// Print the percentage per level
+			System.out.println("");
+			for (int i = 0; i < nodes.length; i++)
+				System.out.println("level" + (i + 1) + ": "
+						+ General.setDecimalPrecision(percentagePerLevel[i], 5)
+						+ "%\n");
+
+			// Printing the biggest subspace:
+			Subspace biggestSubspace = nodes[ii][jj].subspace;
+			General.printArray("The biggest subspace is:",
+					biggestSubspace.integers);
+			System.out.println(" (" + maxPercentage + "%)\n");
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Prints to a file the time required to search a given subspace
+	 */
+	public void printTimeRequiredForEachSubspace(Subspace subspace,
+			Input input) {
+		// Print the time required to search this sub-space
+		if (input.printTimeTakenByIPForEachSubspace) {
+			General.createFolder(input.outputFolder);
+			String integerPartitionAsString = General
+					.convertArrayToString(subspace.integers);
+			System.out.println("Searching " + integerPartitionAsString
+					+ " took " + subspace.timeToSearchThisSubspace + " ms.");
+			General.printToFile(
+					input.outputFolder + "/" + "(" + input.problemID + ")_"
+							+ "ipTimeForEachSubspace.txt",
+					"Searching " + integerPartitionAsString + " took "
+							+ subspace.timeToSearchThisSubspace + "\n",
+					false);
+		}
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/inputOutput/SolverNames.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/inputOutput/SolverNames.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/inputOutput/SolverNames.java	(revision 30)
@@ -0,0 +1,32 @@
+package geniusweb.ip.inputOutput;
+
+public enum SolverNames {
+
+	DP, // [Yeh, 1986]
+
+	IDP, // [Rahwan & Jennings, AAMAS 2008]
+
+	ODP, // [Rahwan et al., 2014]
+
+	IP, // [Rahwan et al., JAIR 2009]
+
+	ODPIP, // [Rahwan et al., AIJ 2014]
+
+	CPLEX; // ILOG's CPLEX
+
+	public static String toString(SolverNames solverName) {
+		if (solverName == DP)
+			return ("DP");
+		if (solverName == IDP)
+			return ("IDP");
+		if (solverName == ODP)
+			return ("ODP");
+		if (solverName == IP)
+			return ("IP");
+		if (solverName == ODPIP)
+			return ("ODP-IP");
+		if (solverName == CPLEX)
+			return ("CPLEX");
+		return "";
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/inputOutput/ValueDistribution.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/inputOutput/ValueDistribution.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/inputOutput/ValueDistribution.java	(revision 30)
@@ -0,0 +1,52 @@
+package geniusweb.ip.inputOutput;
+
+public enum ValueDistribution {
+
+	UNIFORM,
+
+	NORMAL,
+
+	EXPONENTIAL,
+
+	BETA,
+
+	GAMMA,
+
+	NDCS,
+
+	AGENTBASEDUNIFORM,
+
+	AGENTBASEDNORMAL,
+
+	MODIFIEDUNIFORM,
+
+	MODIFIEDNORMAL,
+
+	UNKNOWN;
+
+	public static String toString(ValueDistribution valueDistribution) {
+		if (valueDistribution == UNIFORM)
+			return "Uniform";
+		if (valueDistribution == NORMAL)
+			return "Normal";
+		if (valueDistribution == EXPONENTIAL)
+			return "Exponential";
+		if (valueDistribution == BETA)
+			return "Beta";
+		if (valueDistribution == GAMMA)
+			return "Gamma";
+		if (valueDistribution == NDCS)
+			return "NDCS";
+		if (valueDistribution == AGENTBASEDUNIFORM)
+			return "Agent-based Uniform";
+		if (valueDistribution == AGENTBASEDNORMAL)
+			return "Agent-based Normal";
+		if (valueDistribution == MODIFIEDUNIFORM)
+			return "Modified Unifrom";
+		if (valueDistribution == MODIFIEDNORMAL)
+			return "Modified Normal";
+		if (valueDistribution == UNKNOWN)
+			return "Unknown";
+		return "";
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/Edge.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/Edge.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/Edge.java	(revision 30)
@@ -0,0 +1,18 @@
+package geniusweb.ip.ipSolver;
+
+/**
+ * an object of an edge in the integer partition graph
+ */
+public class Edge {
+	public Node node;
+	public int partThatWasSplit;
+	public int[] twoPartsThatResultedFromTheSplit;
+
+	public Edge(Node node, int partThatWasSplit,
+			int[] twoPartsThatResultedFromTheSplit) {
+		this.node = node;
+		this.partThatWasSplit = partThatWasSplit;
+		this.twoPartsThatResultedFromTheSplit = twoPartsThatResultedFromTheSplit;
+	}
+
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/IDPSolver_whenRunning_ODPIP.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/IDPSolver_whenRunning_ODPIP.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/IDPSolver_whenRunning_ODPIP.java	(revision 30)
@@ -0,0 +1,126 @@
+package geniusweb.ip.ipSolver;
+
+import geniusweb.ip.dpSolver.DPSolver;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.SolverNames;
+import geniusweb.ip.mainSolver.Result;
+
+public class IDPSolver_whenRunning_ODPIP extends Thread {
+	private Input inputToIDPSolver;
+	private Result result;
+	private double[] valueOfBestPartitionFound;
+	private boolean stop = false;
+	public DPSolver dpSolver;
+
+	public void setStop(boolean value) {
+		if (dpSolver != null)
+			dpSolver.setStop(value);
+		stop = value;
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * The constructor
+	 */
+	public IDPSolver_whenRunning_ODPIP(Input input, Result result) {
+		this.stop = false;
+		this.result = result;
+		this.valueOfBestPartitionFound = new double[1 << input.numOfAgents];
+
+		// Initialize the input to, and the result of, the IDP solver
+		this.inputToIDPSolver = getInputToIDPSolver(input);
+
+		// Run the IDP solver
+		this.dpSolver = new DPSolver(inputToIDPSolver, result);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Synchronized set method
+	 */
+	public synchronized void updateValueOfBestPartitionFound(int coalition,
+			double value) {
+		if (valueOfBestPartitionFound[coalition] < value)
+			valueOfBestPartitionFound[coalition] = value;
+	}
+
+	/**
+	 * get method
+	 */
+	public double getValueOfBestPartitionFound(int coalition) {
+		return valueOfBestPartitionFound[coalition];
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * This method uses IDP to compute parts of the table used in the
+	 * local-branch-and-bound technique
+	 */
+	@Override
+	public void run() {
+		this.dpSolver.runDPorIDP();
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Initialize the array "valueOfBestPartitionFound" such that, for every
+	 * coalition C, we simply put: valueOfBestPartitionFound[C] = v[C]
+	 */
+	public void initValueOfBestPartitionFound(Input input,
+			double[][] maxValueForEachSize) {
+		long startTime = System.currentTimeMillis();
+
+		valueOfBestPartitionFound[0] = 0;
+
+		// initialize result.max_f. Basically, result.max_f[3] is the maximum f
+		// value that DP computed for a coalition of size 4
+		result.init_max_f(input, maxValueForEachSize);
+
+		// Initialize the best partition of each coalition C to be the maximum
+		// between
+		// its value, and the sum of values of the singletons made of its
+		// members.
+		for (int coalitionInBitFormat = valueOfBestPartitionFound.length
+				- 1; coalitionInBitFormat >= 1; coalitionInBitFormat--)
+			valueOfBestPartitionFound[coalitionInBitFormat] = input
+					.getCoalitionValue(coalitionInBitFormat);
+
+		// System.out.println("The time required to initialize the
+		// local-branch-and-bound table is
+		// "+(System.currentTimeMillis()-startTime)+" millisec");
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Initialize the parameters of the input object to be passed to the DP
+	 * solver
+	 */
+	private Input getInputToIDPSolver(Input input) {
+		Input inputToDPSolver = new Input(input.numOfAgents,
+				input.coalitionValues, input.acceptableRatio);
+		// inputToDPSolver.coalitionValues = input.coalitionValues;
+		inputToDPSolver.problemID = input.problemID;
+
+		// The DP parameters
+		inputToDPSolver.solverName = SolverNames.ODPIP;
+
+		// The printing parameters
+		inputToDPSolver.storeCoalitionValuesInFile = false;
+		inputToDPSolver.printDetailsOfSubspaces = false;
+		inputToDPSolver.printNumOfIntegerPartitionsWithRepeatedParts = false;
+		inputToDPSolver.printInterimResultsOfIPToFiles = false;
+		inputToDPSolver.printTimeTakenByIPForEachSubspace = false;
+
+		// General parameters
+		inputToDPSolver.feasibleCoalitions = null;
+		// inputToDPSolver.numOfAgents = input.numOfAgents;
+		inputToDPSolver.numOfRunningTimes = 1;
+
+		return (inputToDPSolver);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/IPSolver.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/IPSolver.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/IPSolver.java	(revision 30)
@@ -0,0 +1,1312 @@
+package geniusweb.ip.ipSolver;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.general.ElementOfMultiset;
+import geniusweb.ip.general.General;
+import geniusweb.ip.general.IntegerPartition;
+import geniusweb.ip.general.SubsetsOfMultiset;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.Output;
+import geniusweb.ip.inputOutput.SolverNames;
+import geniusweb.ip.mainSolver.Result;
+
+public class IPSolver {
+	/**
+	 * the IP algorithm.
+	 */
+	public void solve(Input input, Output output, Result result) {
+		// Initialization
+		int numOfAgents = input.numOfAgents;
+		result.initialize();
+
+		// Find the best CS in the first & last Levels of the Integer Partition
+		// Graph
+		searchFirstAndLastLevel(input, result);
+
+		// Calculate the average of the values of coalitions of size:
+		// s=1,2,...,numOfAgents.
+		// While doing this, search the second level of the integer partition
+		// graph. Also, if required,
+		// search the side subspaces of levels 3,4,...,numOfAgents-1 (e.g. the
+		// subspace [5,1,1,...,1])
+		double[] avgValueForEachSize = new double[numOfAgents];
+		MethodsForScanningTheInput.scanTheInputAndComputeAverage(input, output,
+				result, avgValueForEachSize);
+
+		// Calculate the highest value, 2nd highest, 3rd highest, and so on, as
+		// long as this is useful
+		// (e.g. given 15 agents, for the coalitions of size 4, we only need to
+		// calculate the highest
+		// 3 values since it is not possible to have more than 3 fours in a
+		// single integer partition
+		double[][] maxValueForEachSize = setMaxValueForEachSize(input);
+
+		// Create the integer partition graph
+		Subspace[][] subspaces = initSubspaces(avgValueForEachSize,
+				maxValueForEachSize, input);
+		if (input.solverName == SolverNames.ODPIP)
+			result.ipIntegerPartitionGraph = new IntegerPartitionGraph(
+					subspaces, input.numOfAgents, 1);
+		else
+			result.ipIntegerPartitionGraph = new IntegerPartitionGraph(
+					subspaces, input.numOfAgents,
+					((int) Math.floor(2 * input.numOfAgents / (double) 3)));
+
+		result.totalNumOfExpansions = computeTotalNumOfExpansions(result);
+
+		// create subspaces, and calculate their bounds, average values, and
+		// size. Also compute
+		// the overall upper and lower bounds on the value of the optimal
+		// coalition structure
+		long numOfRemainingSubspaces = IntegerPartition
+				.getNumOfIntegerPartitions(numOfAgents);
+		numOfRemainingSubspaces -= disableSubspacesThatWereSearchedWhileScanningTheInput(
+				result, input);
+
+		// Set the upper and lower bounds on the value of the optimal coalition
+		// structure
+		setUpperAndLowerBoundsOnOptimalValue(input, result);
+
+		// Disable subspaces based on their bounds
+		numOfRemainingSubspaces -= disableSubspacesWithUBLowerThanTheHighestLB(
+				input, output, result);
+
+		// Print the size, upperBound, and lowerBound for each subspace
+		output.printDetailsOfSubspaces(input, result);
+
+		// If there are no more subspaces to be searched, then...
+		if (numOfRemainingSubspaces == 0) {
+			finalize(result, input, output);
+			return;
+		}
+		// Initialize the table used in the technique called: "Branch and bound
+		// in opposite direction"
+		result.idpSolver_whenRunning_ODPIP = null;
+		if (input.solverName == SolverNames.ODPIP) {
+			result.idpSolver_whenRunning_ODPIP = new IDPSolver_whenRunning_ODPIP(
+					input, result);
+			result.idpSolver_whenRunning_ODPIP
+					.initValueOfBestPartitionFound(input, maxValueForEachSize);
+			result.idpSolver_whenRunning_ODPIP.start();
+		}
+		// Calculate the value which, if found, causes the search to stop:
+		double acceptableValue = result.ipUpperBoundOnOptimalValue
+				* input.acceptableRatio / 100;
+
+		if (input.pruneSubspaces == false)
+			acceptableValue = Double.MAX_VALUE;
+
+		// Assign to each subspace a priority based on the subspace-selection
+		// function, and then sort them based on priorities
+		Node[] sortedNodes = getListOfSortedNodes(subspaces, input, result);
+
+		// Check whether the current LB of the optimal is within the acceptable
+		// bound...
+		while ((result.ipLowerBoundOnOptimalValue) < acceptableValue) {
+			// Update the integer partition graph based on the max size that was
+			// completed by IDP so far
+			if (input.solverName == SolverNames.ODPIP)
+				result.ipIntegerPartitionGraph.updateEdges(input.numOfAgents,
+						result.get_dpMaxSizeThatWasComputedSoFar());
+
+			// disable any subspaces that are reachable from the bottom node
+			if (input.solverName == SolverNames.ODPIP)
+				numOfRemainingSubspaces -= disableSubspacesReachableFromBottomNode(
+						input, result);
+
+			// Select the subspace to be searched:
+			Node nodeToBeSearched = getFirstEnabledNode(sortedNodes);
+			if (nodeToBeSearched == null)
+				break;
+
+			// if IP is helping DP, then we need to group certain integers
+			// (depending on the size for which DP has finished computing)\
+			ElementOfMultiset[] subsetToBePutAtTheBeginning = getRelevantNodes(
+					nodeToBeSearched, input, result, 1);
+
+			// Place the best subset at the beginning of the node's
+			// subspace.integers.
+			putSubsetAtTheBeginning(nodeToBeSearched,
+					subsetToBePutAtTheBeginning, input);
+
+			// Compute the upper bounds that are used in the branch-and-bound
+			// formula\
+			double[] sumOfMax = computeSumOfMax_splitOneInteger(
+					nodeToBeSearched, maxValueForEachSize, input, result);
+
+			// search the subspace
+			int numOfIntegersToSplit = 0;
+			if ((input.solverName == SolverNames.ODPIP)
+					&& (subsetToBePutAtTheBeginning != null))
+				numOfIntegersToSplit = nodeToBeSearched.subspace.integers.length
+						- General.getCardinalityOfMultiset(
+								subsetToBePutAtTheBeginning);
+			numOfRemainingSubspaces -= nodeToBeSearched.subspace.search(input,
+					output, result, acceptableValue, avgValueForEachSize,
+					sumOfMax, numOfIntegersToSplit);
+
+			// update the upper bound on the optimal CS, as well as the
+			// acceptable value
+			setUpperAndLowerBoundsOnOptimalValue(input, result);
+			acceptableValue = result.ipUpperBoundOnOptimalValue
+					* input.acceptableRatio / 100;
+			if (input.pruneSubspaces == false)
+				acceptableValue = Double.MAX_VALUE;
+
+			// update the lower bound on the optimal CS
+			if (result.ipLowerBoundOnOptimalValue < result
+					.get_ipValueOfBestCSFound()) {
+				result.ipLowerBoundOnOptimalValue = result
+						.get_ipValueOfBestCSFound();
+				numOfRemainingSubspaces -= disableSubspacesWithUBLowerThanTheHighestLB(
+						input, output, result);
+			}
+			// If there are no more subspaces to be searched, or if we found a
+			// good enough value, then...
+			if ((numOfRemainingSubspaces == 0)
+					|| ((input.solverName == SolverNames.ODPIP) && (result
+							.get_dpMaxSizeThatWasComputedSoFar() >= ((int) Math
+									.floor(2 * input.numOfAgents
+											/ (double) 3))))) {
+				break;
+			}
+		}
+		// prepare final result of IP before terminating
+		finalize(result, input, output);
+		if (result.idpSolver_whenRunning_ODPIP != null)
+			result.idpSolver_whenRunning_ODPIP.setStop(true);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Disable any subspaces that are reachable from the bottom node, using only
+	 * the movements that have been evaluated by IDP so far.
+	 * 
+	 * Returns the number of subspaces that it pruned.
+	 */
+	private long disableSubspacesReachableFromBottomNode(Input input,
+			Result result) {
+		Node bottomNode = result.ipIntegerPartitionGraph.nodes[0][0];
+
+		// get the list of nodes that are reachable from the bottom node.
+		Node[] reachableNodes = result.ipIntegerPartitionGraph
+				.getReachableNodes(bottomNode);
+
+		// disable all those nodes
+		int numOfDisabledNodes = 0;
+		if (reachableNodes != null)
+			for (int i = 0; i < reachableNodes.length; i++)
+				if (reachableNodes[i].subspace.enabled) {
+					reachableNodes[i].subspace.enabled = false;
+					// System.out.println(input.problemID+" - &&&&&& IDP pruned
+					// the subspace
+					// "+General.convertArrayToString(reachableNodes[i].subspace.integersSortedAscendingly));
+					numOfDisabledNodes++;
+				}
+		return (numOfDisabledNodes);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Prepare the final result of IP before terminating
+	 */
+	private void finalize(Result result, Input input, Output output) {
+		if (input.solverName == SolverNames.ODPIP) {
+			if (result.get_dpBestCSFound() != null)
+				result.updateIPSolution(result.get_dpBestCSFound(),
+						result.get_dpValueOfBestCSFound());
+		}
+		result.ipTime = System.currentTimeMillis() - result.ipStartTime;
+		output.emptyStringBufferContentsIntoOutputFiles(input);
+		output.printFinalResultsOfIPToFiles(input, result);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Finds the best out of the first and the last level of the Integer
+	 * partition graph
+	 */
+	private void searchFirstAndLastLevel(Input input, Result result) {
+		int numOfAgents = input.numOfAgents;
+		boolean CS1IsFeasible = true;
+		boolean CS2IsFeasible = true;
+
+		// Calculate the value of the coalition structure that is made of
+		// singletons
+		int[][] CS1 = new int[numOfAgents][1];
+		for (int i = 0; i <= numOfAgents - 1; i++)
+			CS1[i][0] = i + 1;
+
+		// Calculate the value of the coalition structure that contains the
+		// grand coalition
+		int[][] CS2 = new int[1][numOfAgents];
+		for (int i = 0; i <= numOfAgents - 1; i++)
+			CS2[0][i] = i + 1;
+
+		if (input.feasibleCoalitions != null) {
+			// Check whether CS1 is feasible
+			for (int i = 0; i < CS1.length; i++) {
+				int curCoalitionInBitFormat = Combinations
+						.convertCombinationFromByteToBitFormat(CS1[i]);
+				if (input.feasibleCoalitions
+						.contains(curCoalitionInBitFormat) == false) {
+					CS1IsFeasible = false;
+					break;
+				}
+			}
+			// Check whether CS2 is feasible
+			for (int i = 0; i < CS2.length; i++) {
+				int curCoalitionInBitFormat = Combinations
+						.convertCombinationFromByteToBitFormat(CS2[i]);
+				if (input.feasibleCoalitions
+						.contains(curCoalitionInBitFormat) == false) {
+					CS2IsFeasible = false;
+					break;
+				}
+			}
+		}
+		// Compute the values of CS1 and CS2
+		double valueOfCS1 = input.getCoalitionStructureValue(CS1);
+		double valueOfCS2 = input.getCoalitionStructureValue(CS2);
+
+		// Compare the two values
+		if (((CS1IsFeasible) && (CS2IsFeasible == false)) || ((CS1IsFeasible)
+				&& (CS2IsFeasible) && (valueOfCS1 >= valueOfCS2))) {
+			result.updateIPSolution(CS1, valueOfCS1);
+		}
+		if (((CS2IsFeasible) && (CS1IsFeasible == false)) || ((CS2IsFeasible)
+				&& (CS1IsFeasible) && (valueOfCS2 >= valueOfCS1))) {
+			result.updateIPSolution(CS2, valueOfCS2);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * create subspaces, and calculate their bounds, average values, and size.
+	 * Also compute the overall upper and lower bounds on the value of the
+	 * optimal coalition structure
+	 */
+	private Subspace[][] initSubspaces(double[] avgValueForEachSize,
+			double[][] maxValueForEachSize, Input input) {
+		// Generate all integer partitions
+		int[][][] integers = IntegerPartition.getIntegerPartitions(
+				input.numOfAgents, input.orderIntegerPartitionsAscendingly);
+
+		// Initialize all subspaces and compute the upper and lower bounds on
+		// the optimal value
+		Subspace[][] subspaces = new Subspace[integers.length][];
+		for (int level = 0; level < input.numOfAgents; level++) {
+			subspaces[level] = new Subspace[integers[level].length];
+			for (int i = 0; i < integers[level].length; i++) {
+				subspaces[level][i] = new Subspace(integers[level][i],
+						avgValueForEachSize, maxValueForEachSize, input);
+			}
+		}
+		return (subspaces);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * For any subspace "S", set "S.enabled" to "false" if it has been searched
+	 * while scanning the input
+	 */
+	private long disableSubspacesThatWereSearchedWhileScanningTheInput(
+			Result result, Input input) {
+		Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+		long numOfSubspacesThatThisMethodHasDisabled = 0;
+		for (int level = 0; level < nodes.length; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Subspace curSubspace = nodes[level][i].subspace;
+				// identify whether the current integer partition is of size "1"
+				// or "2" or "numOfAgents"
+				if ((level == 0) || (level == 1)
+						|| (level == (input.numOfAgents - 1))) {
+					if (curSubspace.enabled) {
+						curSubspace.enabled = false;
+						numOfSubspacesThatThisMethodHasDisabled++;
+					}
+				}
+			}
+		}
+		return (numOfSubspacesThatThisMethodHasDisabled);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Calculate the highest value, 2nd highest, 3rd highest, and so on, as long
+	 * as this is useful (e.g. given 15 agents, for the coalitions of size 4, we
+	 * only need to calculate the highest 3 values since it is not possible to
+	 * have more than 3 fours in a single integer partition
+	 */
+	private double[][] setMaxValueForEachSize(Input input) {
+		// Initialization
+		int numOfAgents = input.numOfAgents;
+		int[] numOfRequiredMaxValues = new int[numOfAgents];
+		long[] numOfCoalitions = new long[numOfAgents];
+		double[][] maxValue = new double[numOfAgents][];
+		for (int size = 1; size <= numOfAgents; size++) {
+			numOfRequiredMaxValues[size - 1] = (int) Math
+					.floor(numOfAgents / (double) size);
+			numOfCoalitions[size - 1] = Combinations
+					.binomialCoefficient(numOfAgents, size);
+			maxValue[size - 1] = new double[numOfRequiredMaxValues[size - 1]];
+			for (int i = 0; i < maxValue[size - 1].length; i++)
+				maxValue[size - 1][i] = 0;
+		}
+		final boolean constraintsExist;
+		if (input.feasibleCoalitions == null)
+			constraintsExist = false;
+		else
+			constraintsExist = true;
+
+		for (int coalitionInBitFormat = (int) Math.pow(2, numOfAgents)
+				- 1; coalitionInBitFormat > 0; coalitionInBitFormat--)
+			if ((constraintsExist == false) || (input.feasibleCoalitions
+					.contains(coalitionInBitFormat)))
+			// if a coalition is feasible, and it's upperBound is greater than
+			// the smallest element in "curMax" then update "curMax"
+			{
+				int size = Combinations.getSizeOfCombinationInBitFormat(
+						coalitionInBitFormat, numOfAgents);
+				double[] curMaxValue = maxValue[size - 1];
+				int j = numOfRequiredMaxValues[size - 1] - 1;
+				if (input.getCoalitionValue(
+						coalitionInBitFormat) > curMaxValue[j]) {
+					while ((j > 0) && (input.getCoalitionValue(
+							coalitionInBitFormat) > curMaxValue[j - 1])) {
+						curMaxValue[j] = curMaxValue[j - 1];
+						j--;
+					}
+					curMaxValue[j] = input
+							.getCoalitionValue(coalitionInBitFormat);
+				}
+			}
+		return (maxValue);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method disables any subspaces with a UB lower that the value of the
+	 * current best solution (i.e. lower than LB*), and returns the number of
+	 * subspaces that this method has disabled
+	 */
+	private long disableSubspacesWithUBLowerThanTheHighestLB(Input input,
+			Output output, Result result) {
+		Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+		if (input.pruneSubspaces == false)
+			return (0);
+
+		long numOfSubspacesThatThisMethodHasDisabled = 0;
+
+		for (int level = 0; level < nodes.length; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Subspace curSubspace = nodes[level][i].subspace;
+				if (curSubspace.enabled == true) {
+					// if( curSubspace.UB < result.ipLowerBoundOnOptimalValue )
+					if (curSubspace.UB
+							- result.ipLowerBoundOnOptimalValue < -0.00000000005) {
+						curSubspace.enabled = false;
+						numOfSubspacesThatThisMethodHasDisabled += 1;
+					}
+				}
+			}
+		}
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+		return (numOfSubspacesThatThisMethodHasDisabled);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Assign to each node a priority based on the subspace-selection function,
+	 * and then sort the nodes based on their priorities.
+	 */
+	private Node[] getListOfSortedNodes(Subspace[][] subspaces, Input input,
+			Result result) {
+		Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+
+		// *******************************************
+		// *** ***
+		// *** Assign priorities to nodes ***
+		// *** ***
+		// *******************************************
+
+		for (int level = 0; level < nodes.length; level++) { // For each level
+			for (int i = 0; i < nodes[level].length; i++) // For each subspace
+															// in the current
+															// level
+			{
+				Subspace curSubspace = nodes[level][i].subspace;
+				curSubspace.priority = curSubspace.UB;
+				// curSubspace.priority = -1 * curSubspace.sizeOfSubspace;
+				// curSubspace.priority = curSubspace.LB;
+			}
+		}
+
+		// ********************************************************
+		// *** ***
+		// *** Sort subspaces based on their priorities ***
+		// *** ***
+		// ********************************************************
+
+		// Initialization
+		Node[] sortedNodes = new Node[IntegerPartition
+				.getNumOfIntegerPartitions(input.numOfAgents)];
+		int k = 0;
+		for (int level = 0; level < nodes.length; level++) { // For each level
+			for (int i = 0; i < nodes[level].length; i++) { // For each subspace
+															// in the current
+															// level
+				sortedNodes[k] = nodes[level][i];
+				k++;
+			}
+		}
+		// Sorting
+		for (int i = sortedNodes.length - 1; i >= 0; i--) // start at the end of
+															// the array
+		{
+			int indexOfSmallestElement = i; // (1) default value of the index
+											// the of smallest element.
+			for (int j = i; j >= 0; j--) // (2) loop from the end of unsorted
+											// zone to the beginning of the
+											// array.
+			{
+				// compare current element to smallest one
+				if ((sortedNodes[j].subspace.priority < sortedNodes[indexOfSmallestElement].subspace.priority)
+						|| ((sortedNodes[j].subspace.priority == sortedNodes[indexOfSmallestElement].subspace.priority)
+								&& (sortedNodes[j].subspace.UB < sortedNodes[indexOfSmallestElement].subspace.UB)))
+					indexOfSmallestElement = j; // if it's smaller, it becomes
+												// the new smallest
+			}
+			// swap the two values
+			Node temp = sortedNodes[i];
+			sortedNodes[i] = sortedNodes[indexOfSmallestElement];
+			sortedNodes[indexOfSmallestElement] = temp;
+		}
+		return (sortedNodes);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Returns the first enabled subspace in the given list of subspaces
+	 */
+	private Node getFirstEnabledNode(Node[] sortedNodes) {
+		for (int i = 0; i < sortedNodes.length; i++) {
+			if (sortedNodes[i].subspace.enabled)
+				return (sortedNodes[i]);
+		}
+		return (null);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method sets "result.ipUpperBoundOnOptimalValue" to the
+	 * "initialValue", and searches through the upper bounds of the "enabled"
+	 * subspaces to see if there is one that is greater than that value
+	 */
+	private void setUpperAndLowerBoundsOnOptimalValue(Input input,
+			Result result) {
+		result.ipUpperBoundOnOptimalValue = result.get_ipValueOfBestCSFound();
+		result.ipLowerBoundOnOptimalValue = result.get_ipValueOfBestCSFound();
+
+		Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+		for (int level = 0; level < nodes.length; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Subspace curSubspace = nodes[level][i].subspace;
+				if (curSubspace.enabled) {
+					if (result.ipUpperBoundOnOptimalValue < curSubspace.UB)
+						result.ipUpperBoundOnOptimalValue = curSubspace.UB;
+
+					if (result.ipLowerBoundOnOptimalValue < curSubspace.LB)
+						result.ipLowerBoundOnOptimalValue = curSubspace.LB;
+				}
+			}
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound". Here, none of the values in f are
+	 * used.
+	 */
+	private double[] computeSumOfMax_splitNoIntegers(int[] integers,
+			double[][] maxValueForEachSize) {
+		// Initialization
+		double[] sumOfMax = new double[integers.length + 1];
+
+		// Compute the upper bounds of the last list
+		sumOfMax[integers.length] = maxValueForEachSize[integers[integers.length
+				- 1] - 1][0];
+
+		// Compute the upper bounds of the remaining lists
+		int j = 0;
+		for (int i = integers.length - 1; i > 0; i--) {
+			if (integers[i - 1] == integers[i])
+				j++;
+			else
+				j = 0;
+			sumOfMax[i] = sumOfMax[i + 1]
+					+ maxValueForEachSize[integers[i - 1] - 1][j];
+		}
+		return (sumOfMax);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound". Here, none of the values in f are
+	 * used.
+	 */
+	private double[] computeSumOfMax_usefInsteadOfv(int[] integers,
+			double[][] maxValueForEachSize, Result result) {
+		// Initialization
+		double[] sumOfMax = new double[integers.length + 1];
+
+		// Compute the upper bounds of the last list
+		sumOfMax[integers.length] = result
+				.get_max_f(integers[integers.length - 1] - 1);
+		for (int i = integers.length - 1; i > 0; i--)
+			sumOfMax[i] = sumOfMax[i + 1]
+					+ result.get_max_f(integers[i - 1] - 1);
+
+		return (sumOfMax);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound". Here, none of the values in f are
+	 * used.
+	 */
+	private double computeUpperBound(ElementOfMultiset[] integers,
+			double[][] maxValueForEachSize) {
+		double upperBound = 0;
+		for (int i = 0; i < integers.length; i++) {
+			int j = 0;
+			for (int k = 0; k < integers[i].repetition; k++) {
+				upperBound += maxValueForEachSize[integers[i].element - 1][j];
+				j++;
+			}
+		}
+		return (upperBound);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound"
+	 */
+	private double[] computeSumOfMax_splitManyIntegers(Node node,
+			double[][] maxValueForEachSize, Input input,
+			ElementOfMultiset[] subsetToBePutAtTheBeginning, Result result) {
+		if ((input.solverName == SolverNames.IP)
+				|| (node.subspace.relevantNodes == null))
+			return (computeSumOfMax_splitNoIntegers(node.subspace.integers,
+					maxValueForEachSize));
+
+		// Initialization
+		int[] integers = node.subspace.integers;
+		Node[] relevantNodes = node.subspace.relevantNodes;
+		double[] sumOfMax = new double[integers.length + 1];
+
+		// Count the number of integers in the subset that will be put at the
+		// beginning, i.e., the subset containing the
+		// integers that are common between this node, and every relevant node
+		// (i.e., the integers that will not be split)
+		int numOfIntegersToBePutAtTheBeginning = 0;
+		if (subsetToBePutAtTheBeginning != null)
+			for (int i = 0; i < subsetToBePutAtTheBeginning.length; i++)
+				numOfIntegersToBePutAtTheBeginning += subsetToBePutAtTheBeginning[i].repetition;
+
+		// For this node, and every relevant node, we are going to keep a copy
+		// of its integer partition
+		int[][] tempIntegers = new int[relevantNodes.length + 1][];
+		int[][] tempIntegerRoots = new int[relevantNodes.length + 1][];
+		tempIntegers[0] = General
+				.copyArray(node.integerPartition.partsSortedAscendingly);
+		tempIntegerRoots[0] = General.copyArray(node.tempIntegerRoots);
+		for (int i = 1; i < tempIntegers.length; i++) {
+			tempIntegers[i] = General.copyArray(relevantNodes[i
+					- 1].integerPartition.partsSortedAscendingly);
+			tempIntegerRoots[i] = General
+					.copyArray(relevantNodes[i - 1].tempIntegerRoots);
+		}
+		// Compute sumOfMax[1]...
+		double maxUB = node.subspace.UB;
+		for (int i = 0; i < relevantNodes.length; i++) {
+			if (maxUB < relevantNodes[i].subspace.UB) {
+				maxUB = relevantNodes[i].subspace.UB;
+			}
+		}
+		sumOfMax[1] = maxUB;
+		int index = 1;
+
+		// Compute sumOfMax[i] : i>1
+		while (index < integers.length) {
+			sumOfMax[index + 1] = Integer.MIN_VALUE;
+			int curInteger = integers[index - 1];
+			for (int i = 0; i < tempIntegers.length; i++) {
+				ElementOfMultiset[] set = null;
+				ElementOfMultiset[] subset = null;
+				if (index <= numOfIntegersToBePutAtTheBeginning) {
+					subset = new ElementOfMultiset[1];
+					subset[0] = new ElementOfMultiset(curInteger, 1);
+				} else {
+					set = getSetOfIntegersGivenTheirRoot(tempIntegers[i],
+							tempIntegerRoots[i], curInteger);
+					subset = getSubsetOfIntegersGivenTheirSum(set, curInteger);
+					if (subset == null) {
+						subset = new ElementOfMultiset[1];
+						subset[0] = new ElementOfMultiset(curInteger, 1);
+					}
+				}
+				if (i == 0) {
+					removeSubset(node, subset, tempIntegers, tempIntegerRoots,
+							i);
+				} else {
+					removeSubset(relevantNodes[i - 1], subset, tempIntegers,
+							tempIntegerRoots, i);
+				}
+				double[] tempSumOfMax = computeSumOfMax_splitNoIntegers(
+						tempIntegers[i], maxValueForEachSize);
+				if (sumOfMax[index + 1] < tempSumOfMax[1])
+					sumOfMax[index + 1] = tempSumOfMax[1];
+			}
+			// Compute the bounds using f, and see if this improves things...
+			double[] sumOfMaxUsingf = computeSumOfMax_usefInsteadOfv(
+					tempIntegers[0], maxValueForEachSize, result);
+			if (sumOfMax[index + 1] > sumOfMaxUsingf[1])
+				sumOfMax[index + 1] = sumOfMaxUsingf[1];
+
+			index++;
+		}
+		return (sumOfMax);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound" IMPORTANT: this version assumes that
+	 * there is ONLY ONE integer that will be split at the end
+	 */
+	private double[] computeSumOfMax_splitOneInteger(Node node,
+			double[][] maxValueForEachSize, Input input, Result result) {
+		if ((input.solverName == SolverNames.IP)
+				|| (node.subspace.relevantNodes == null))
+			return (computeSumOfMax_splitNoIntegers(node.subspace.integers,
+					maxValueForEachSize));
+
+		int[] integers = node.subspace.integers;
+		double[] sumOfMax = new double[integers.length + 1];
+
+		// compute the maximum upper bound of this node and all relevant nodes
+		double maxUB = node.subspace.UB;
+		for (int i = 0; i < node.subspace.relevantNodes.length; i++)
+			if (maxUB < node.subspace.relevantNodes[i].subspace.UB)
+				maxUB = node.subspace.relevantNodes[i].subspace.UB;
+
+		// Compute the upper bound of the last list
+		sumOfMax[integers.length] = maxValueForEachSize[integers[integers.length
+				- 1] - 1][0] + (maxUB - node.subspace.UB);
+		double max_f = result.get_max_f(integers[integers.length - 1] - 1);
+		if ((max_f != 0) && (sumOfMax[integers.length] > max_f))
+			sumOfMax[integers.length] = max_f;
+
+		sumOfMax[integers.length - 1] = sumOfMax[integers.length]
+				+ maxValueForEachSize[integers[integers.length - 2] - 1][0];
+		int k = 2;
+
+		// Compute the upper bounds of the remaining lists
+		int x = integers.length - k;
+		int j = 0;
+		for (int i = x; i > 0; i--) {
+			if (integers[i - 1] == integers[i])
+				j++;
+			else
+				j = 0;
+			sumOfMax[i] = sumOfMax[i + 1]
+					+ maxValueForEachSize[integers[i - 1] - 1][j];
+			k++;
+		}
+		return (sumOfMax);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOfMax, which is used in
+	 * "Subspace.search_useBranchAndBound"
+	 */
+	private double[] computeSumOfMax_splitOneInteger_improveUsingf(Node node,
+			double[][] maxValueForEachSize, Input input, Result result) {
+		if ((input.solverName == SolverNames.IP)
+				|| (node.subspace.relevantNodes == null))
+			return (computeSumOfMax_splitNoIntegers(node.subspace.integers,
+					maxValueForEachSize));
+
+		// Initialization
+		int[] integers = node.subspace.integers;
+		double[] sumOfMax = new double[integers.length + 1];
+
+		// Basically, "sumOfMax[i] = arrayOfMax[i][0] + arrayOfMax[i][1] + ... "
+		double[][] arrayOfMax = new double[integers.length + 1][];
+		for (int i = 1; i < arrayOfMax.length; i++) {
+			arrayOfMax[i] = new double[arrayOfMax.length - i];
+		}
+		// compute the maximum upper bound of this node and all relevant nodes
+		double maxUB = node.subspace.UB;
+		for (int i = 0; i < node.subspace.relevantNodes.length; i++)
+			if (maxUB < node.subspace.relevantNodes[i].subspace.UB)
+				maxUB = node.subspace.relevantNodes[i].subspace.UB;
+
+		// Compute the upper bound of the last list
+		sumOfMax[integers.length] = maxValueForEachSize[integers[integers.length
+				- 1] - 1][0] + (maxUB - node.subspace.UB);
+		double max_f = result.get_max_f(integers[integers.length - 1] - 1);
+		if ((max_f != 0) && (sumOfMax[integers.length] > max_f))
+			sumOfMax[integers.length] = max_f;
+		sumOfMax[integers.length - 1] = sumOfMax[integers.length]
+				+ maxValueForEachSize[integers[integers.length - 2] - 1][0];
+
+		// Update arrayOfMax
+		for (int i = 1; i < arrayOfMax.length; i++)
+			arrayOfMax[i][arrayOfMax[i].length
+					- 1] = maxValueForEachSize[integers[integers.length - 1]
+							- 1][0] + (maxUB - node.subspace.UB);
+		for (int i = 1; i < arrayOfMax.length - 1; i++)
+			arrayOfMax[i][arrayOfMax[i].length
+					- 2] = maxValueForEachSize[integers[integers.length - 2]
+							- 1][0];
+
+		// Compute the upper bounds of the remaining lists
+		int k = 2;
+		int x = integers.length - k;
+		int j = 0;
+		for (int i = x; i > 0; i--) {
+			if (integers[i - 1] == integers[i])
+				j++;
+			else
+				j = 0;
+			sumOfMax[i] = sumOfMax[i + 1]
+					+ maxValueForEachSize[integers[i - 1] - 1][j];
+
+			// Update arrayOfMax
+			for (int w = 1; w < arrayOfMax.length - k; w++)
+				arrayOfMax[w][arrayOfMax[w].length
+						- (k + 1)] = maxValueForEachSize[integers[i - 1]
+								- 1][j];
+			k++;
+		}
+		// the technique below did not make any difference in performace, so, to
+		// be safe, I disabled it
+		if (input.solverName == SolverNames.ODPIP)
+			improveUpperBoundUsingf(input.numOfAgents, sumOfMax, arrayOfMax,
+					maxValueForEachSize, integers, result);
+
+		return (sumOfMax);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Try to reduce the upper bound using f. Example: Given [1,1,2,3],
+	 * max_v1+max_v1+max_v2 can be replaced with: max_f4.
+	 */
+	private void improveUpperBoundUsingf(int numOfAgents, double[] sumOfMax,
+			double[][] arrayOfMax, double[][] maxValueForEachSize,
+			int[] integers, Result result) {
+		// ******************************************************************************
+		// **** ****
+		// **** try to start from the first list, and head towards the last list
+		// ****
+		// **** ****
+		// ******************************************************************************
+
+		// for each list "i"...
+		for (int i = 1; i < sumOfMax.length; i++) // try to reduce "sumOfMax[i]"
+		{
+			// Initialization
+			int start = i;
+			double newSumOfMax = 0;
+			int[] numOfTimesWeComputedASizeRegularly = new int[numOfAgents + 1];
+			for (int size = 0; size <= numOfAgents; size++)
+				numOfTimesWeComputedASizeRegularly[size] = 0;
+
+			/*
+			 * In the 1st loop, we group a few integers (from "start" to "end"),
+			 * where "start" equals "i" In the 2nd loop, we group a few integers
+			 * (from "start" to "end"), where "start" equals the. previous "end"
+			 * plus 1. This is repeated until we reach the last integer...
+			 */
+			do {
+				// every time we enter this loop, we have the same "i", but a
+				// new "start"
+				int sumOfIntegers = 0;
+				int bestSumOfIntegers = -1;
+				double bestSavings = 0;
+				int bestEnd = 0;
+				boolean exitLoop = false;
+				// Given the current "start", we want to find the best "end"
+				for (int end = start; end < sumOfMax.length; end++) {
+					sumOfIntegers += integers[end - 1];
+					if (sumOfIntegers > result
+							.get_dpMaxSizeThatWasComputedSoFar()) {
+						exitLoop = true;
+						break;
+					}
+					// Compute the savings if we use: "result.max_f[
+					// sumOfIntegers-1 ]" instead
+					// of: "arrayOfMax[i][start]+...+arrayOfMax[i][end]"
+					double savings = -1 * result.get_max_f(sumOfIntegers - 1);
+					for (int j = start; j <= end; j++)
+						savings += arrayOfMax[i][j - i];
+
+					if (bestSavings < savings) {
+						bestSavings = savings;
+						bestSumOfIntegers = sumOfIntegers;
+						bestEnd = end;
+					}
+				}
+				// if even the best savings are 0, then the current start is
+				// useless...
+				if (bestSavings < 0.0000000001) {
+					int curSize = integers[start - 1];
+					newSumOfMax += maxValueForEachSize[curSize
+							- 1][numOfTimesWeComputedASizeRegularly[curSize]];
+					numOfTimesWeComputedASizeRegularly[curSize]++;
+					start++;
+				} else {
+					newSumOfMax += result.get_max_f(bestSumOfIntegers - 1);
+					start = bestEnd + 1;
+				}
+			} while (start < sumOfMax.length);
+
+			if (sumOfMax[i] > newSumOfMax)
+				sumOfMax[i] = newSumOfMax;
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * select the subset of integers that will be put at the beginning. The
+	 * remaining integers will be put at the end and will be searched using the
+	 * f table, not the v table.
+	 * 
+	 * Here, you explicitly specify the number of integers to be placed at the
+	 * end.
+	 * 
+	 * Returns the subset of integers that is common between the node and all
+	 * reachable nodes.
+	 */
+	private ElementOfMultiset[] getRelevantNodes(Node node, Input input,
+			Result result, int numOfIntegersToSplitAtTheEnd) {
+		if (input.solverName == SolverNames.IP) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		}
+		int numOfIntegersInNode = node.integerPartition.partsSortedAscendingly.length;
+
+		// get the list of nodes that are reachable from the original node.
+		Node[] reachableNodes = result.ipIntegerPartitionGraph
+				.getReachableNodes(node);
+		if (reachableNodes == null) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		}
+		// create an iterator that will iterate over subsets of the original
+		// integer partition
+		SubsetsOfMultiset[] subsetIterators = new SubsetsOfMultiset[numOfIntegersToSplitAtTheEnd];
+		for (int s = 0; s < numOfIntegersToSplitAtTheEnd; s++)
+			subsetIterators[s] = new SubsetsOfMultiset(
+					node.integerPartition.sortedMultiset,
+					numOfIntegersInNode - (s + 1), false);
+
+		// Initialization
+		ElementOfMultiset[] bestSubset = null;
+		long bestSavings = 0;
+		int bestNumOfRelevantNodes = 0;
+
+		for (int s = 0; s < numOfIntegersToSplitAtTheEnd; s++) {
+			// Iterate over all subsets of size: s+1
+			ElementOfMultiset[] subset = subsetIterators[s].getNextSubset();
+			while (subset != null) {
+				long savings = 0;
+				int numOfRelevantSubspaces = 0;
+				// out of all nodes reachable from "node", find the ones that
+				// contain "subset"
+				for (int i = 0; i < reachableNodes.length; i++) {
+					if ((reachableNodes[i].integerPartition.contains(subset))
+							&& (reachableNodes[i].subspace.enabled)) {
+						savings += reachableNodes[i].subspace.totalNumOfExpansionsInSubspace;
+						numOfRelevantSubspaces++;
+					}
+				}
+				// update the best subset
+				if (bestSavings < savings) {
+					bestSavings = savings;
+					bestSubset = General.copyMultiset(subset);
+					bestNumOfRelevantNodes = numOfRelevantSubspaces;
+				}
+				// Get the next subset
+				subset = subsetIterators[s].getNextSubset();
+			}
+		}
+		// get the list of relevant subspaces, based on the best subset
+		int index = 0;
+		if (bestNumOfRelevantNodes == 0) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		} else {
+			node.subspace.relevantNodes = new Node[bestNumOfRelevantNodes];
+			for (int i = 0; i < reachableNodes.length; i++) {
+				if ((reachableNodes[i].integerPartition.contains(bestSubset))
+						&& (reachableNodes[i].subspace.enabled)) {
+					node.subspace.relevantNodes[index] = reachableNodes[i];
+					index++;
+				}
+			}
+			return (bestSubset);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * select the subset of integers that will be put at the beginning. The
+	 * remaining integers will be put at the end and will be searched using the
+	 * f table, not the v table.
+	 * 
+	 * Here, the number of integers to be put at the end is not limited; it can
+	 * be as large as needed
+	 * 
+	 * Returns the subset of integers that is common between the node and all
+	 * reachable nodes.
+	 */
+	private ElementOfMultiset[] getRelevantNodes(Node node, Input input,
+			Result result) {
+		if (input.solverName == SolverNames.IP) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		}
+		// get the list of nodes that are reachable from the original node.
+		Node[] reachableNodes = result.ipIntegerPartitionGraph
+				.getReachableNodes(node);
+		if (reachableNodes == null) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		}
+		// Count how many of those nodes are actually enabled
+		int numOfEnabledReachableNodes = 0;
+		for (int j = 0; j < reachableNodes.length; j++)
+			if (reachableNodes[j].subspace.enabled)
+				numOfEnabledReachableNodes++;
+
+		// Put all reachable, enabled, nodes in the list of relevant nodes
+		if (numOfEnabledReachableNodes == 0) {
+			node.subspace.relevantNodes = null;
+			return (null);
+		} else {
+			node.subspace.relevantNodes = new Node[numOfEnabledReachableNodes];
+			int index = 0;
+			for (int j = 0; j < reachableNodes.length; j++) {
+				if (reachableNodes[j].subspace.enabled) {
+					node.subspace.relevantNodes[index] = reachableNodes[j];
+					index++;
+				}
+			}
+		}
+		Node[] relevantNodes = node.subspace.relevantNodes;
+
+		// Create a copy of the multiset representation of the integer partition
+		// of the node
+		ElementOfMultiset[] copyOfMultiset = General
+				.copyMultiset(node.integerPartition.sortedMultiset);
+
+		// For every element in the multiset
+		for (int i = 0; i < copyOfMultiset.length; i++) {
+			// count the minimum number of times this element appears in a
+			// reachable node
+			int curElement = copyOfMultiset[i].element;
+			int minNumOfRepetitions = copyOfMultiset[i].repetition;
+			for (int j = 0; j < relevantNodes.length; j++) {
+				boolean foundElement = false;
+				ElementOfMultiset[] multiset = relevantNodes[j].integerPartition.sortedMultiset;
+				for (int k = 0; k < multiset.length; k++)
+					if (multiset[k].element == curElement) {
+						foundElement = true;
+						if (minNumOfRepetitions > multiset[k].repetition)
+							minNumOfRepetitions = multiset[k].repetition;
+						break;
+					}
+				if (foundElement == false) {
+					minNumOfRepetitions = 0;
+					break;
+				}
+			}
+			// set the number of repetitions to be the minimum
+			copyOfMultiset[i].repetition = minNumOfRepetitions;
+		}
+		// Get rid of any elements that are repeated 0 times, and return the
+		// remaining
+		int counter = 0;
+		for (int i = 0; i < copyOfMultiset.length; i++) {
+			if (copyOfMultiset[i].repetition > 0) {
+				counter++;
+			}
+		}
+		if (counter == 0) {
+			return (null);
+		} else {
+			ElementOfMultiset[] subset = new ElementOfMultiset[counter];
+			int index = 0;
+			for (int i = 0; i < copyOfMultiset.length; i++)
+				if (copyOfMultiset[i].repetition > 0) {
+					subset[index] = new ElementOfMultiset(
+							copyOfMultiset[i].element,
+							copyOfMultiset[i].repetition);
+					index++;
+				}
+			return (subset);
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Place the best subset at the beginning of the node's subspace.integers.
+	 * Also, place it at the beginning of every relevant node's
+	 * subspace.integers.
+	 */
+	private void putSubsetAtTheBeginning(Node node, ElementOfMultiset[] subset,
+			Input input) {
+		if ((subset == null) || (input.solverName == SolverNames.IP))
+			return;
+
+		// Put all remaining integers (i.e., those not in the subset) in a set
+		// represented as a MULTISET
+		ElementOfMultiset[] remainingIntegers_multiset = General
+				.copyMultiset(node.integerPartition.sortedMultiset);
+		for (int i = 0; i < subset.length; i++)
+			for (int j = 0; j < remainingIntegers_multiset.length; j++)
+				if (remainingIntegers_multiset[j].element == subset[i].element) {
+					remainingIntegers_multiset[j].repetition -= subset[i].repetition;
+					break;
+				}
+		// Count the total number of remaining integers (i.e., those that are
+		// not in the subset)
+		int counter = 0;
+		for (int i = 0; i < remainingIntegers_multiset.length; i++)
+			counter += remainingIntegers_multiset[i].repetition;
+
+		// Put all remaining integers (i.e., those not in the subset) in a set
+		// represented as an ARRAY.
+		int[] remainingIntegers_array = new int[counter];
+		int index = 0;
+		for (int i = 0; i < remainingIntegers_multiset.length; i++)
+			for (int j = 0; j < remainingIntegers_multiset[i].repetition; j++) {
+				remainingIntegers_array[index] = remainingIntegers_multiset[i].element;
+				index++;
+			}
+		// place the subset at the beginning, and the remaining integers at the
+		// end
+		int[] newIntegers = new int[node.subspace.integers.length];
+		int index1 = 0;
+		int index2 = newIntegers.length - counter;
+		for (int i = 0; i < node.subspace.integers.length; i++) {
+			boolean found = false;
+			for (int j = 0; j < remainingIntegers_array.length; j++) {
+				if (remainingIntegers_array[j] == node.subspace.integers[i]) {
+					newIntegers[index2] = node.subspace.integers[i];
+					index2++;
+					remainingIntegers_array[j] = -1;
+					found = true;
+					break;
+				}
+			}
+			if (found == false) {
+				newIntegers[index1] = node.subspace.integers[i];
+				index1++;
+			}
+		}
+		node.subspace.integers = newIntegers;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Returns the maximum multiplicity of the integer in the node, and in every
+	 * relevant node
+	 */
+	private int getMaximumMultiplicity(int integer, Node node) {
+		// initialize by setting the maximum multiplicity to be within the node
+		// itself
+		int maximumMultiplicity = 0;
+		for (int i = 0; i < node.integerPartition.sortedMultiset.length; i++)
+			if (integer == node.integerPartition.sortedMultiset[i].element)
+				maximumMultiplicity = node.integerPartition.sortedMultiset[i].repetition;
+
+		// Now, consider the relevant nodes
+		for (int k = 0; k < node.subspace.relevantNodes.length; k++) {
+			Node curNode = node.subspace.relevantNodes[k];
+			for (int i = 0; i < curNode.integerPartition.sortedMultiset.length; i++)
+				if (integer == curNode.integerPartition.sortedMultiset[i].element)
+					if (maximumMultiplicity < curNode.integerPartition.sortedMultiset[i].repetition)
+						maximumMultiplicity = curNode.integerPartition.sortedMultiset[i].repetition;
+		}
+		return (maximumMultiplicity);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given "sumOfIntegers", this method returns a subset of "mutliset" of
+	 * which the sum equals "sumOfIntegers"
+	 */
+	private ElementOfMultiset[] getSubsetOfIntegersGivenTheirSum(
+			ElementOfMultiset[] multiset, int sumOfIntegers) {
+		if (multiset == null)
+			return (null);
+
+		int maxSize = Math.min(sumOfIntegers,
+				General.getCardinalityOfMultiset(multiset));
+
+		ElementOfMultiset[] subset;
+		for (int size = 1; size <= maxSize; size++) {
+			SubsetsOfMultiset subsetsOfMultiset = new SubsetsOfMultiset(
+					multiset, size, false);
+			subset = subsetsOfMultiset.getNextSubset();
+			while (subset != null) {
+				int sum = 0;
+				for (int i = 0; i < subset.length; i++)
+					sum += subset[i].element * subset[i].repetition;
+
+				if (sum == sumOfIntegers)
+					return (subset);
+				else
+					subset = subsetsOfMultiset.getNextSubset();
+			}
+		}
+		return (null);
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Given "sumOfIntegers", this method returns a subset of "mutliset" of
+	 * which the sum equals "sumOfIntegers"
+	 */
+	private ElementOfMultiset[] getSetOfIntegersGivenTheirRoot(
+			int[] tempIntegers, int[] tempIntegerRoots, int root) {
+		if (tempIntegerRoots == null)
+			return (null);
+
+		// count the integer that whose root equals "root"
+		int counter = 0;
+		for (int i = 0; i < tempIntegerRoots.length; i++) {
+			if (tempIntegerRoots[i] == root) {
+				counter++;
+			}
+		}
+		// keep only those integers
+		if (counter == 0) {
+			return (null);
+		} else {
+			int[] resultAsArray = new int[counter];
+			int index = 0;
+			for (int i = 0; i < tempIntegerRoots.length; i++) {
+				if (tempIntegerRoots[i] == root) {
+					resultAsArray[index] = tempIntegers[i];
+					index++;
+				}
+			}
+			return (General.convertArrayToMultiset(resultAsArray));
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * remove the subset of integers from the node
+	 */
+	public static void removeSubset(Node node, ElementOfMultiset[] subset,
+			int[][] tempIntegers, int[][] tempIntegerRoots, int e) {
+		if (subset == null)
+			return;
+
+		int[] integers = General.copyArray(tempIntegers[e]);
+		int[] integerRoots = General.copyArray(tempIntegerRoots[e]);
+		ElementOfMultiset[] tempSubset = General.copyMultiset(subset);
+
+		for (int i = 0; i < integers.length; i++) {
+			for (int j = 0; j < tempSubset.length; j++) {
+				if ((integers[i] == tempSubset[j].element)
+						&& (tempSubset[j].repetition > 0)) {
+					integers[i] = -1;
+					tempSubset[j].repetition--;
+					break;
+				}
+			}
+		}
+		// count the elements that have not been deleted
+		int counter = 0;
+		for (int i = 0; i < integers.length; i++) {
+			if (integers[i] > -1) {
+				counter++;
+			}
+		}
+		// Get rid of any elements that have been deleted
+		if (counter == 0) {
+			tempIntegers[e] = integers;
+			tempIntegerRoots[e] = integerRoots;
+		} else {
+			tempIntegers[e] = new int[counter];
+			if (integerRoots != null)
+				tempIntegerRoots[e] = new int[counter];
+			else
+				tempIntegerRoots[e] = null;
+			int index = 0;
+			for (int i = 0; i < integers.length; i++)
+				if (integers[i] > -1) {
+					tempIntegers[e][index] = integers[i];
+					if (integerRoots != null)
+						tempIntegerRoots[e][index] = integerRoots[i];
+					index++;
+				}
+		}
+	}
+
+	// ************************************************************************************************
+
+	/**
+	 * Returns the total number of expansions in the search space, given the
+	 * tree-like representation of different integer-partition-based subspaces
+	 */
+	public long computeTotalNumOfExpansions(Result result) {
+		long totalNumOfExpansions = 0;
+		Node[][] nodes = result.ipIntegerPartitionGraph.nodes;
+		for (int level = 0; level < nodes.length; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Subspace curSubspace = nodes[level][i].subspace;
+				totalNumOfExpansions += curSubspace.totalNumOfExpansionsInSubspace;
+			}
+		}
+		return totalNumOfExpansions;
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/IntegerPartitionGraph.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/IntegerPartitionGraph.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/IntegerPartitionGraph.java	(revision 30)
@@ -0,0 +1,379 @@
+package geniusweb.ip.ipSolver;
+
+import geniusweb.ip.general.IntegerPartition;
+
+public class IntegerPartitionGraph {
+	public Node[][] nodes;
+	public int largestIntegerBeingSplitInThisGraph;
+
+	// *****************************************************************************************************
+
+	/**
+	 * Constructor. Places the subspaces into an integer partition graph
+	 */
+	public IntegerPartitionGraph(Subspace[][] subspaces, int numOfAgents,
+			int largestIntegerBeingSplitInThisGraph) {
+		this.largestIntegerBeingSplitInThisGraph = largestIntegerBeingSplitInThisGraph;
+
+		// create a node for each subspace
+		nodes = new Node[numOfAgents][];
+		for (int level = 0; level < numOfAgents; level++) { // For each level
+			nodes[level] = new Node[subspaces[level].length];
+			for (int i = 0; i < subspaces[level].length; i++) { // For each
+																// subspace in
+																// the current
+																// level
+				nodes[level][i] = new Node(subspaces[level][i]);
+			}
+		}
+		// Create the edges of the graph
+		for (int level = 0; level < numOfAgents - 1; level++) { // For each
+																// level
+			for (int i = 0; i < nodes[level].length; i++) // For each node in
+															// the current level
+			{
+				IntegerPartition[] listOfDirectlyConnectedIntegerPartitions = nodes[level][i].integerPartition
+						.getListOfDirectedlyConnectedIntegerPartitions(
+								largestIntegerBeingSplitInThisGraph, 0);
+				if (listOfDirectlyConnectedIntegerPartitions == null) {
+					nodes[level][i].edgesFromThisNode = null;
+				} else {
+					nodes[level][i].edgesFromThisNode = new Edge[listOfDirectlyConnectedIntegerPartitions.length];
+					int index = 0;
+					for (int j = 0; j < nodes[level + 1].length; j++) { // For
+																		// each
+																		// node
+																		// in
+																		// the
+																		// NEXT
+																		// level
+						int[] integersThatResultedFromTheSplit = getIntegersThatResultedFromTheSplit(
+								nodes[level][i],
+								listOfDirectlyConnectedIntegerPartitions,
+								nodes[level + 1][j],
+								largestIntegerBeingSplitInThisGraph);
+						if (integersThatResultedFromTheSplit != null) {
+							int[] sortedParts1 = nodes[level][i].integerPartition.partsSortedAscendingly;
+							int[] sortedParts2 = nodes[level
+									+ 1][j].integerPartition.partsSortedAscendingly;
+							int partThatWasSplit = -1;
+							for (int k = sortedParts1.length - 1; k >= 0; k--) { // compare
+																					// the
+																					// two
+																					// arrays,
+																					// STARTING
+																					// FROM
+																					// THE
+																					// END
+								if (sortedParts1[k] != sortedParts2[k + 1]) {
+									partThatWasSplit = sortedParts1[k];
+									break;
+								}
+							}
+							nodes[level][i].edgesFromThisNode[index] = new Edge(
+									nodes[level + 1][j], partThatWasSplit,
+									integersThatResultedFromTheSplit);
+							index++;
+							if (index == nodes[level][i].edgesFromThisNode.length)
+								break;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Updates the edges, based on the fact that the current
+	 * "largestIntegerBeingSplitInThisGraph" is greater than the previous
+	 * "largestIntegerBeingSplitInThisGraph".
+	 */
+	public void updateEdges(int numOfAgents,
+			int largestIntegerBeingSplitInThisGraph) {
+		int prev_largestIntegerBeingSplitInThisGraph = this.largestIntegerBeingSplitInThisGraph;
+		if (prev_largestIntegerBeingSplitInThisGraph >= largestIntegerBeingSplitInThisGraph)
+			return;
+
+		// Update the edges of the graph
+		for (int level = 1; level < numOfAgents - 1; level++) { // For each
+																// level
+																// STARTINNG
+																// FROM THE
+																// SECOND ONE!!!
+			for (int i = 0; i < nodes[level].length; i++) // For each node in
+															// the current level
+			{
+				IntegerPartition[] listOfDirectlyConnectedIntegerPartitions = nodes[level][i].integerPartition
+						.getListOfDirectedlyConnectedIntegerPartitions(
+								largestIntegerBeingSplitInThisGraph,
+								prev_largestIntegerBeingSplitInThisGraph);
+				if (listOfDirectlyConnectedIntegerPartitions != null) {
+					// in this case, we need to ADD to the existing list of
+					// edges
+					int index;
+					if (nodes[level][i].edgesFromThisNode == null) { // create a
+																		// list
+																		// of
+																		// edges
+																		// from
+																		// scratch
+						index = 0;
+						nodes[level][i].edgesFromThisNode = new Edge[listOfDirectlyConnectedIntegerPartitions.length];
+					} else { // add to the existing list of edges
+						index = nodes[level][i].edgesFromThisNode.length;
+						Edge[] tempListOfEdges = new Edge[nodes[level][i].edgesFromThisNode.length
+								+ listOfDirectlyConnectedIntegerPartitions.length];
+						for (int j = 0; j < nodes[level][i].edgesFromThisNode.length; j++)
+							tempListOfEdges[j] = nodes[level][i].edgesFromThisNode[j];
+						nodes[level][i].edgesFromThisNode = tempListOfEdges;
+					}
+					for (int j = 0; j < nodes[level + 1].length; j++) { // For
+																		// each
+																		// node
+																		// in
+																		// the
+																		// NEXT
+																		// level
+						int[] integersThatResultedFromTheSplit = getIntegersThatResultedFromTheSplit(
+								nodes[level][i],
+								listOfDirectlyConnectedIntegerPartitions,
+								nodes[level + 1][j],
+								largestIntegerBeingSplitInThisGraph);
+						if (integersThatResultedFromTheSplit != null) {
+							int[] sortedParts1 = nodes[level][i].integerPartition.partsSortedAscendingly;
+							int[] sortedParts2 = nodes[level
+									+ 1][j].integerPartition.partsSortedAscendingly;
+							int partThatWasSplit = -1;
+							for (int k = sortedParts1.length - 1; k >= 0; k--) { // compare
+																					// the
+																					// two
+																					// arrays,
+																					// STARTING
+																					// FROM
+																					// THE
+																					// END
+								if (sortedParts1[k] != sortedParts2[k + 1]) {
+									partThatWasSplit = sortedParts1[k];
+									break;
+								}
+							}
+							nodes[level][i].edgesFromThisNode[index] = new Edge(
+									nodes[level + 1][j], partThatWasSplit,
+									integersThatResultedFromTheSplit);
+							index++;
+							if (index == nodes[level][i].edgesFromThisNode.length)
+								break;
+						}
+					}
+				}
+			}
+		}
+		this.largestIntegerBeingSplitInThisGraph = largestIntegerBeingSplitInThisGraph;
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Given two nodes that are at two consecutive levels, the method returns
+	 * true iff there is an edge between the two nodes
+	 */
+	private int[] getIntegersThatResultedFromTheSplit(Node nodeOnLowLevel,
+			IntegerPartition[] listOfDirectlyConnectedIntegerPartitions,
+			Node nodeOnHighLevel, int largestIntegerBeingSplitInThisGraph) {
+		int[] multiplicity1 = nodeOnHighLevel.integerPartition.sortedMultiplicity;
+		int underlyingSet1 = nodeOnHighLevel.integerPartition.sortedUnderlyingSetInBitFormat;
+
+		for (int i = 0; i < listOfDirectlyConnectedIntegerPartitions.length; i++) {
+			int[] multiplicity2 = listOfDirectlyConnectedIntegerPartitions[i].sortedMultiplicity;
+			int underlyingSet2 = listOfDirectlyConnectedIntegerPartitions[i].sortedUnderlyingSetInBitFormat;
+
+			if (underlyingSet1 != underlyingSet2)
+				continue;
+
+			boolean notEqual = false;
+			for (int j = 0; j < multiplicity1.length; j++) {
+				if (multiplicity1[j] != multiplicity2[j]) {
+					notEqual = true;
+					break;
+				}
+			}
+			if (notEqual)
+				continue;
+
+			return (listOfDirectlyConnectedIntegerPartitions[i].tempIntegersThatResultedFromASplit);
+		}
+		return (null);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * For every node in the graph, this methods determines whether it is
+	 * reachable from the bottom node, and that is given the m parameter
+	 */
+	public void setReachabilityOfSubspaces(int m, int numOfAgents) {
+		// For every node, initlialize its reachability from the bottom node
+		for (int level = 0; level < 2; level++)
+			for (int i = 0; i < nodes[level].length; i++)
+				nodes[level][i].subspace.isReachableFromBottomNode = true;
+		for (int level = 2; level < numOfAgents; level++)
+			for (int i = 0; i < nodes[level].length; i++)
+				nodes[level][i].subspace.isReachableFromBottomNode = false;
+
+		// Delete the edges that split an integer greater than, or equal to, m
+		for (int level = 1; level < numOfAgents - 1; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Node curNode = nodes[level][i];
+				if (curNode.edgesFromThisNode != null)
+					for (int j = 0; j < curNode.edgesFromThisNode.length; j++) {
+						if ((curNode.edgesFromThisNode[j] != null)
+								&& (curNode.edgesFromThisNode[j].partThatWasSplit >= m))
+							curNode.edgesFromThisNode[j] = null;
+					}
+			}
+		}
+		// For every node, compute its reachability from the bottom node
+		for (int level = 1; level < numOfAgents - 1; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				Node curNode = nodes[level][i];
+				if (curNode.subspace.isReachableFromBottomNode == false) {
+					continue;
+				}
+				if (curNode.edgesFromThisNode != null)
+					for (int j = 0; j < curNode.edgesFromThisNode.length; j++) {
+						if (curNode.edgesFromThisNode[j] != null)
+							curNode.edgesFromThisNode[j].node.subspace.isReachableFromBottomNode = true;
+					}
+			}
+		}
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Returns a list of nodes that are reachable from the given node. This, of
+	 * course, only considers the edges that are currently present in this
+	 * integer partition graph.
+	 */
+	public Node[] getReachableNodes(Node node) {
+		if (node.edgesFromThisNode == null)
+			return (null);
+
+		int numOfIntegersInNode = node.integerPartition.partsSortedAscendingly.length;
+
+		// mark all nodes above "node" as unreachable
+		node.tempIntegerRoots = null;
+		for (int level = numOfIntegersInNode; level < nodes.length; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				nodes[level][i].tempFlag = false;
+				nodes[level][i].tempIntegerRoots = null;
+			}
+		}
+		// mark all nodes that are "directly" connected to "node" as reachable
+		for (int i = 0; i < node.edgesFromThisNode.length; i++) {
+			node.edgesFromThisNode[i].node.tempFlag = true;
+			setIntegerRoots(node, node.edgesFromThisNode[i].node,
+					node.edgesFromThisNode[i].twoPartsThatResultedFromTheSplit,
+					node.edgesFromThisNode[i].partThatWasSplit);
+		}
+		// continue to mark nodes as reachable...
+		int numOfReachableNodes = 0;
+		for (int level = numOfIntegersInNode; level < nodes.length
+				- 1; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				if (nodes[level][i].tempFlag) {
+					numOfReachableNodes++;
+					if (nodes[level][i].edgesFromThisNode != null) {
+						for (int j = 0; j < nodes[level][i].edgesFromThisNode.length; j++) {
+							if (nodes[level][i].edgesFromThisNode[j].node.tempFlag == false) {
+								nodes[level][i].edgesFromThisNode[j].node.tempFlag = true;
+								setIntegerRoots(nodes[level][i],
+										nodes[level][i].edgesFromThisNode[j].node,
+										nodes[level][i].edgesFromThisNode[j].twoPartsThatResultedFromTheSplit,
+										nodes[level][i].edgesFromThisNode[j].partThatWasSplit);
+							}
+						}
+					}
+				}
+			}
+		}
+		// Put the nodes that have "flag = true" in the list of reachable nodes
+		int index = 0;
+		Node[] listOfReachableNodes = new Node[numOfReachableNodes];
+		for (int level = numOfIntegersInNode; level < nodes.length
+				- 1; level++) {
+			for (int i = 0; i < nodes[level].length; i++) {
+				if (nodes[level][i].tempFlag) {
+					listOfReachableNodes[index] = nodes[level][i];
+					index++;
+				}
+			}
+		}
+		return (listOfReachableNodes);
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * for every integer "i", we keep track of its root, i.e., the integer that
+	 * I kept splitting until I got "i"
+	 */
+	private void setIntegerRoots(Node lowerNode, Node upperNode,
+			int[] twoPartsThatResultedFromTheSplit, int partThatWasSplit) {
+		int[] upperIntegers = upperNode.integerPartition.partsSortedAscendingly;
+		int[] lowerIntegers = lowerNode.integerPartition.partsSortedAscendingly;
+
+		// initializate all roots to be equal to -1
+		upperNode.tempIntegerRoots = new int[upperIntegers.length];
+		for (int i = 0; i < upperNode.tempIntegerRoots.length; i++)
+			upperNode.tempIntegerRoots[i] = -1;
+
+		if (lowerNode.tempIntegerRoots == null) {
+			// set the root for every one of two parts that resulted from the
+			// split
+			for (int k = 0; k < twoPartsThatResultedFromTheSplit.length; k++)
+				for (int j = 0; j < upperIntegers.length; j++)
+					if ((twoPartsThatResultedFromTheSplit[k] == upperIntegers[j])
+							&& (upperNode.tempIntegerRoots[j] == -1)) {
+						upperNode.tempIntegerRoots[j] = partThatWasSplit;
+						break;
+					}
+			// set the root for every other integer to be equal to the integer
+			// itself
+			for (int j = 0; j < upperIntegers.length; j++)
+				if (upperNode.tempIntegerRoots[j] == -1)
+					upperNode.tempIntegerRoots[j] = upperIntegers[j];
+		} else {
+			// Initialization
+			int newRoot = -10;
+			int indexOfNewRoot = -10;
+
+			// get the new integer root
+			for (int i = 0; i < lowerIntegers.length; i++)
+				if (lowerIntegers[i] == partThatWasSplit) {
+					indexOfNewRoot = i;
+					newRoot = lowerNode.tempIntegerRoots[i];
+				}
+
+			// set the root of every integer except the two that resulted from
+			// the split
+			for (int i = 0; i < lowerNode.tempIntegerRoots.length; i++) {
+				if (i == indexOfNewRoot)
+					continue;
+
+				for (int j = 0; j < upperIntegers.length; j++)
+					if ((upperIntegers[j] == lowerIntegers[i])
+							&& (upperNode.tempIntegerRoots[j] == -1)) {
+						upperNode.tempIntegerRoots[j] = lowerNode.tempIntegerRoots[i];
+						break;
+					}
+			}
+			// set the root for the two integers that resulted from the split
+			for (int j = 0; j < upperIntegers.length; j++)
+				if (upperNode.tempIntegerRoots[j] == -1)
+					upperNode.tempIntegerRoots[j] = newRoot;
+		}
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/MethodsForScanningTheInput.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/MethodsForScanningTheInput.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/MethodsForScanningTheInput.java	(revision 30)
@@ -0,0 +1,211 @@
+package geniusweb.ip.ipSolver;
+
+import java.util.TreeSet;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.Output;
+import geniusweb.ip.mainSolver.Result;
+
+public class MethodsForScanningTheInput {
+	// This class is only used while scanning the input, and that is only if
+	// there are constraints.
+	protected static class CheckFeasibilityOfCoalitions {
+		// The data members
+		public boolean firstCoalitionIsFeasible = true;
+		public boolean firstCoalitionAsSingletonsIsFeasible = true;
+		public boolean secondCoalitionIsFeasible = true;
+		public boolean secondCoalitionAsSingletonsIsFeasible = true;
+
+		/**
+		 * This method fills the data members. Here, "firstCoalition" and
+		 * "secondCoalition" are provided as int arrays In more detail, this
+		 * method determines whether: (1) "firstCoalition" is feasible (2)
+		 * "secondCoalition" is feasible (3) the singletons that can be formed
+		 * from "firstCoalition" are all feasible (4) the singletons that can be
+		 * formed from "secondCoalition" are all feasible
+		 */
+		public void setDataMembers(int numOfAgents, int[] firstCoalition,
+				int[] secondCoalition, TreeSet<Integer> feasibleCoalitions) {
+			// If there are no constraints to be dealt with, then do nothing
+			if (feasibleCoalitions == null)
+				return;
+			int firstCoalitionInBitFormat = Combinations
+					.convertCombinationFromByteToBitFormat(firstCoalition);
+			int secondCoalitionInBitFormat = Combinations
+					.convertCombinationFromByteToBitFormat(secondCoalition);
+			setDataMembers(numOfAgents, firstCoalitionInBitFormat,
+					secondCoalitionInBitFormat, feasibleCoalitions);
+		}
+
+		/**
+		 * This method fills the data members. Here, "firstCoalition" and
+		 * "secondCoalition" are provided in bit format (i.e., as masks) In more
+		 * detail, this method determines whether: (1) "firstCoalition" is
+		 * feasible (2) "secondCoalition" is feasible (3) the singletons that
+		 * can be formed from "firstCoalition" are all feasible (4) the
+		 * singletons that can be formed from "secondCoalition" are all feasible
+		 */
+		public void setDataMembers(int numOfAgents, int firstCoalition,
+				int secondCoalition, TreeSet<Integer> feasibleCoalitions) {
+			// If there are no constraints to be dealt with, then do nothing
+			if (feasibleCoalitions == null)
+				return;
+
+			firstCoalitionIsFeasible = feasibleCoalitions
+					.contains(new Integer(firstCoalition));
+			secondCoalitionIsFeasible = feasibleCoalitions
+					.contains(new Integer(secondCoalition));
+
+			firstCoalitionAsSingletonsIsFeasible = true;
+			for (int i = 0; i < numOfAgents; i++) {
+				if ((firstCoalition & (1 << i)) != 0) { // If agent "i+1" is a
+														// member of
+														// "firstCoalition"
+					if (feasibleCoalitions
+							.contains(new Integer((1 << i))) == false) {
+						firstCoalitionAsSingletonsIsFeasible = false;
+						break;
+					}
+				}
+			}
+			secondCoalitionAsSingletonsIsFeasible = true;
+			for (int i = 0; i < numOfAgents; i++) {
+				if ((secondCoalition & (1 << i)) != 0) { // If agent "i+1" is a
+															// member of
+															// "secondCoalition"
+					if (feasibleCoalitions
+							.contains(new Integer((1 << i))) == false) {
+						secondCoalitionAsSingletonsIsFeasible = false;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * (1) Calculates the average of all the values of sizes
+	 * s=1,...,numOfAgents. (2) Searches the second level of the sie graph
+	 */
+	public static void scanTheInputAndComputeAverage(Input input, Output output,
+			Result result, double[] avgValueForEachSize) {
+		// initialization
+		long startTime = System.currentTimeMillis();
+		int numOfAgents = input.numOfAgents;
+		int totalNumOfCoalitions = (int) Math.pow(2, numOfAgents);
+		double bestValue = result.get_ipValueOfBestCSFound();
+		int bestCoalition1 = 0;
+		int bestCoalition2 = 0;
+		double[] sumOfValues = new double[numOfAgents];
+		for (int size = 1; size <= numOfAgents; size++)
+			sumOfValues[size - 1] = 0;
+
+		// Initialize the variables that are only used if there are constraints
+		boolean coalition1IsFeasible = true;
+		boolean coalition2IsFeasible = true;
+		final boolean constraintsExist;
+		if (input.feasibleCoalitions == null)
+			constraintsExist = false;
+		else
+			constraintsExist = true;
+
+		for (int coalition1 = totalNumOfCoalitions
+				/ 2; coalition1 < totalNumOfCoalitions - 1; coalition1++) {
+			int sizeOfCoalition1 = Combinations
+					.getSizeOfCombinationInBitFormat(coalition1, numOfAgents);
+
+			int coalition2 = totalNumOfCoalitions - 1 - coalition1;
+			int sizeOfCoalition2 = numOfAgents - sizeOfCoalition1;
+
+			sumOfValues[sizeOfCoalition1 - 1] += input
+					.getCoalitionValue(coalition1);
+			sumOfValues[sizeOfCoalition2 - 1] += input
+					.getCoalitionValue(coalition2);
+
+			// Check whether "coalition1" is feasible and whether "coalition2"
+			// is feasible
+			if (constraintsExist) {
+				coalition1IsFeasible = input.feasibleCoalitions
+						.contains(new Integer(coalition1));
+				coalition2IsFeasible = input.feasibleCoalitions
+						.contains(new Integer(coalition2));
+			}
+			// Compute the value of the current pair of coalitions
+			double value = 0;
+			if ((constraintsExist == false)
+					|| ((coalition1IsFeasible) && (coalition2IsFeasible))) {
+				value = input.getCoalitionValue(coalition1)
+						+ input.getCoalitionValue(coalition2);
+			}
+			// if this value is greater than best_value, then update
+			// best_value...
+			if (bestValue < value) {
+				bestCoalition1 = coalition1;
+				bestCoalition2 = coalition2;
+				bestValue = value;
+			}
+		}
+
+		// If this search has improved the value of the best CS found...
+		if (bestValue > result.get_ipValueOfBestCSFound()) {
+			int[][] bestCS = new int[2][];
+			bestCS[0] = Combinations.convertCombinationFromBitToByteFormat(
+					bestCoalition1, numOfAgents);
+			bestCS[1] = Combinations.convertCombinationFromBitToByteFormat(
+					bestCoalition2, numOfAgents);
+			result.updateIPSolution(bestCS, bestValue);
+		}
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+
+		// For every coalition size, calculate the average coalition value
+		for (int size = 1; size <= numOfAgents; size++)
+			avgValueForEachSize[size - 1] = sumOfValues[size - 1]
+					/ Combinations.binomialCoefficient(numOfAgents, size);
+
+		// Update the number of coalitions, and coalition structures, that were
+		// searched after scanning the input
+		updateNumOfSearchedAndRemainingCoalitionsAndCoalitionStructures(input,
+				result);
+
+		// Calculate the time required to scan the input
+		result.ipTimeForScanningTheInput = System.currentTimeMillis()
+				- startTime;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Update the number of coalitions, and coalition structures, that were
+	 * searched after scanning the input
+	 */
+	private static void updateNumOfSearchedAndRemainingCoalitionsAndCoalitionStructures(
+			Input input, Result result) {
+		for (int size1 = 1; size1 <= Math
+				.floor(input.numOfAgents / (double) 2); size1++) {
+			// From size1, calculate size2 (e.g., for 7 agents, if size1=1 then
+			// size2=6, and if size1=2 then size2=5)
+			int size2 = input.numOfAgents - size1;
+
+			// Compute the No. of coalitions of size "size1" (p.s., this is the
+			// same as the No. of coalitions of size "size2")
+			int numOfCombinationsOfSize1 = (int) Combinations
+					.binomialCoefficient(input.numOfAgents, size1);
+
+			// Compute the number of coalitions of size "size1" that IP scanned
+			// (p.s., this is the same as the number of coalitions of size
+			// "size2" that IP scanned)
+			int temp;
+			if (size1 != size2)
+				temp = numOfCombinationsOfSize1;
+			else
+				temp = (numOfCombinationsOfSize1 / 2);
+
+			result.ipNumOfExpansions += 2 * temp;
+			;
+		}
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/Node.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/Node.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/Node.java	(revision 30)
@@ -0,0 +1,23 @@
+package geniusweb.ip.ipSolver;
+
+import geniusweb.ip.general.IntegerPartition;
+
+/**
+ * an object of a node in the integer partition graph
+ */
+public class Node {
+	public Subspace subspace;
+	public IntegerPartition integerPartition;
+	public Edge[] edgesFromThisNode;
+	boolean tempFlag; // this is whenever the node needs to be marked for some
+						// reason
+	public int[] tempIntegerRoots; // for every integer "i", I keep track of its
+									// root, i.e., the integer that I kept
+									// splitting until I got "i"
+
+	public Node(Subspace subspace) {
+		this.subspace = subspace;
+		integerPartition = new IntegerPartition(subspace.integers);
+		edgesFromThisNode = null;
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/ipSolver/Subspace.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/ipSolver/Subspace.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/ipSolver/Subspace.java	(revision 30)
@@ -0,0 +1,1766 @@
+package geniusweb.ip.ipSolver;
+
+import java.util.Arrays;
+import java.util.TreeSet;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.general.General;
+import geniusweb.ip.general.RandomPermutation;
+import geniusweb.ip.general.RandomSubsetOfGivenSet;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.Output;
+import geniusweb.ip.inputOutput.SolverNames;
+import geniusweb.ip.mainSolver.Result;
+
+public class Subspace {
+	public long sizeOfSubspace;
+	public long totalNumOfExpansionsInSubspace;
+	public int[] integers;
+	public int[] integersSortedAscendingly;
+	public double UB, Avg, LB;
+	public boolean enabled;
+	public double priority;
+	public long timeToSearchThisSubspace;
+	public long numOfSearchedCoalitionsInThisSubspace;
+	public double numOfSearchedCoalitionsInThisSubspace_confidenceInterval;
+	public Node[] relevantNodes;
+	public boolean isReachableFromBottomNode;
+
+	// ******************************************************************************************************
+
+	/**
+	 * the constructors
+	 */
+	public Subspace(int[] integers) {
+		this.integers = integers;
+		this.integersSortedAscendingly = General.sortArray(integers, true);
+		this.sizeOfSubspace = computeNumOfCSInSubspace(this.integers);
+	}
+
+	// ******************************************************************************************************
+
+	public Subspace(int[] integers, double[] avgValueForEachSize,
+			double[][] maxValueForEachSize, Input input) {
+		this.integers = integers; // this.integers = orderParts( integers,
+									// avgValueForEachSize, maxValueForEachSize,
+									// input );
+		this.integersSortedAscendingly = General.sortArray(integers, true);
+		this.timeToSearchThisSubspace = 0;
+		this.enabled = true;
+
+		// compute the number of coalition structures in this subspace
+		this.sizeOfSubspace = computeNumOfCSInSubspace(this.integers);
+
+		// compute the total number of expansions in this subspace. This number
+		// depends on the sorting of the integers
+		this.totalNumOfExpansionsInSubspace = computeTotalNumOfExpansionsInSubspace(
+				this.integers, input);
+
+		// initialize the number of searched coalitions (for the subspaces of
+		// which the integer partition is of size: 1, 2, or "n"
+		if (this.integers.length == 2) {
+			int size1 = this.integers[0];
+			int size2 = this.integers[1];
+			int numOfCombinationsOfSize1 = (int) Combinations
+					.binomialCoefficient(input.numOfAgents, size1);
+			int temp;
+			if (size1 != size2)
+				temp = numOfCombinationsOfSize1;
+			else
+				temp = (numOfCombinationsOfSize1 / 2);
+
+			this.numOfSearchedCoalitionsInThisSubspace = 2 * temp;
+			;
+		} else if ((this.integers.length == 1)
+				|| (this.integers.length == input.numOfAgents))
+			this.numOfSearchedCoalitionsInThisSubspace = this.integers.length;
+		else
+			this.numOfSearchedCoalitionsInThisSubspace = 0;
+
+		// Calculating UB:
+		int j = 0;
+		this.UB = 0;
+		for (int k = 0; k <= this.integers.length - 1; k++) {
+			if ((k > 0) && (this.integers[k] == this.integers[k - 1]))
+				j++;
+			else
+				j = 0;
+			this.UB += maxValueForEachSize[this.integers[k] - 1][j];
+		}
+
+		// Calculating Avg:
+		this.Avg = 0;
+		for (int k = 0; k <= this.integers.length - 1; k++)
+			this.Avg += avgValueForEachSize[this.integers[k] - 1];
+
+		// Calculating LB:
+		this.LB = this.Avg;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Search this subspace using the IP algorithm Returns the number of
+	 * subspaces that were searched
+	 */
+	public int search(Input input, Output output, Result result,
+			double acceptableValue, double[] avgValueForEachSize,
+			double[] sumOfMax, int numOfIntegersToSplit) {
+		long timeBeforeSearchingThisSubspace = System.currentTimeMillis();
+
+		if (input.solverName == SolverNames.ODPIP) {
+			if (result.get_dpMaxSizeThatWasComputedSoFar() >= ((int) Math
+					.floor(2 * input.numOfAgents / (double) 3))) {
+				this.enabled = false;
+				return (1);
+			}
+		}
+		if (input.printTheSubspaceThatIsCurrentlyBeingSearched)
+			System.out.println(input.problemID + " - Searching "
+					+ General.convertArrayToString(integersSortedAscendingly)
+					+ "   -->   " + General.convertArrayToString(integers));
+
+		if (input.useSamplingWhenSearchingSubspaces) {
+			if (input.samplingDoneInGreedyFashion)
+				searchUsingSamplingInGreedyFashion(input, output, result,
+						acceptableValue);
+			else
+				searchUsingSampling(input, output, result);
+			// System.out.println("I am sampling from the subspace:
+			// "+General.convertArrayToString(integers));
+		} else
+			search_useBranchAndBound(input, output, result, acceptableValue,
+					sumOfMax, numOfIntegersToSplit);
+
+		// If we are running IDP-IP, then we need to split the resulting CS in
+		// the optimal way
+		if (input.solverName == SolverNames.ODPIP) {
+			int[][] CS = result.idpSolver_whenRunning_ODPIP.dpSolver
+					.getOptimalSplit(result.get_ipBestCSFound());
+			result.updateIPSolution(CS, input.getCoalitionStructureValue(CS));
+		}
+		timeToSearchThisSubspace = System.currentTimeMillis()
+				- timeBeforeSearchingThisSubspace;
+		output.printTimeRequiredForEachSubspace(this, input);
+		this.enabled = false;
+		int numOfSearchedSubspaces = 1;
+		// disable every relevant subspace
+		if ((input.solverName == SolverNames.ODPIP)
+				&& (relevantNodes != null)) {
+			for (int i = 0; i < relevantNodes.length; i++)
+				if (relevantNodes[i].subspace.enabled) {
+					relevantNodes[i].subspace.enabled = false;
+					numOfSearchedSubspaces++;
+					if (input.printTheSubspaceThatIsCurrentlyBeingSearched)
+						System.out.println(input.problemID
+								+ " - ****** with DP's help, IP avoided the subspace: "
+								+ Arrays.toString(
+										relevantNodes[i].integerPartition.partsSortedAscendingly));
+				}
+		}
+		return (numOfSearchedSubspaces);
+	}
+
+	// ******************************************************************************************************
+
+	private void searchUsingSampling(Input input, Output output,
+			Result result) {
+		// Checking the special case where the size of the coalition structure
+		// is either "1" or "numOfAgents"
+		if ((integers.length == 1) || (integers.length == input.numOfAgents)) {
+			searchFirstOrLastLevel(integers, input, output, result);
+			return;
+		}
+		// Initialization
+		int numOfSamples = Math.min(100000,
+				(int) Math.round(sizeOfSubspace * 0.1));
+		RandomPermutation randomPermutation = new RandomPermutation(
+				input.numOfAgents);
+
+		// for every sample
+		for (int i = 0; i < numOfSamples; i++) {
+			int[] permutation = randomPermutation.get();
+			double coalitionStructureValue = 0;
+			int[] coalitionStructure = new int[integers.length];
+			int indexInPermutation = 0;
+			// for every size in the integer partition
+			for (int j = integers.length - 1; j >= 0; j--) {
+				int size = integers[j];
+				int currentCoalition = 0;
+				// Compute the current coalition
+				for (int k = 0; k < size; k++) {
+					currentCoalition += (1 << (permutation[indexInPermutation])); // add
+																					// agent
+																					// "permutation[
+																					// indexInPermutation
+																					// ]"
+																					// to
+																					// "currentCoalition"
+					indexInPermutation++;
+				}
+				coalitionStructure[j] = currentCoalition;
+
+				// update the value of the current coalition structure
+				coalitionStructureValue += input
+						.getCoalitionValue(currentCoalition);
+				// coalitionStructureValue +=
+				// localBranchAndBoundObject.getLowerBoundForCurrentAgents(
+				// currentCoalition );
+			}
+			// If this value is greater than the best value found so far
+			if (result.get_ipValueOfBestCSFound() < coalitionStructureValue) {
+				int[][] CSInByteFormat = Combinations
+						.convertSetOfCombinationsFromBitToByteFormat(
+								coalitionStructure, input.numOfAgents,
+								integers);
+				result.updateIPSolution(CSInByteFormat,
+						coalitionStructureValue);
+			}
+		}
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+	}
+
+	// ******************************************************************************************************
+
+	private void searchUsingSamplingInGreedyFashion(Input input, Output output,
+			Result result, double acceptableValue) {
+		// Checking the special case where the size of the coalition structure
+		// is either "1" or "numOfAgents"
+		if ((integers.length == 1) || (integers.length == input.numOfAgents)) {
+			searchFirstOrLastLevel(integers, input, output, result);
+			return;
+		}
+		// e.g., numOfRemainingAgents[i] is the number of agents that did not
+		// appear in coalitions: CS[0],...,CS[i-1]
+		int[] numOfRemainingAgents = new int[integers.length];
+		numOfRemainingAgents[0] = input.numOfAgents;
+		for (int j = 1; j < integers.length; j++)
+			numOfRemainingAgents[j] = numOfRemainingAgents[j - 1]
+					- integers[j - 1];
+
+		// Compute the number of possible coalitions, given the size, and the
+		// number of remaining agents
+		long[] numOfPossibleCoalitions = new long[integers.length];
+		for (int j = 0; j < integers.length; j++)
+			numOfPossibleCoalitions[j] = Combinations
+					.binomialCoefficient(numOfRemainingAgents[j], integers[j]);
+
+		// While constructing a coalition structure (CS), the algorithm will
+		// take a
+		// number of samples of every coalition size, and select the best one
+		// seen.
+		int[] numOfCoalitionSamplesPerSizePerCS = new int[integers.length];
+		for (int j = 0; j < integers.length; j++)
+			numOfCoalitionSamplesPerSizePerCS[j] = Math.min(100000,
+					(int) Math.ceil(numOfPossibleCoalitions[j] * 0.1));
+
+		// The number of coalition structures (CS) that the algorithm will
+		// construct
+		int numOfCoalitionStructureSamples = Math.min(10000,
+				(int) Math.ceil(sizeOfSubspace * 0.1));
+
+		// This is the coalition structure that will be constructed by the
+		// algorithm
+		int[] CS = new int[integers.length];
+
+		// For every CS that the algorithm will construct
+		for (int i = 0; i < numOfCoalitionStructureSamples; i++) {
+			// initialize the set of remaining agents to be the grand coalition
+			int setOfRemainingAgentsInBitFormat = (1 << input.numOfAgents) - 1;
+			int[] setOfRemainingAgentsInByteFormat = Combinations
+					.convertCombinationFromBitToByteFormat(
+							setOfRemainingAgentsInBitFormat, input.numOfAgents);
+
+			for (int j = 0; j < integers.length - 1; j++) // For every size
+			{
+				RandomSubsetOfGivenSet samplingObject = new RandomSubsetOfGivenSet(
+						setOfRemainingAgentsInByteFormat,
+						numOfRemainingAgents[j], integers[j]);
+				double bestValueOfCurrentSize = Double.MIN_VALUE;
+				// For every sample of the current size
+				for (int k = 0; k < numOfCoalitionSamplesPerSizePerCS[j]; k++) {
+					int C = samplingObject.getSubsetInBitFormat();
+					// Update the best coalition seen of the current size
+					double currentCoalitionValue = input.getCoalitionValue(C);
+					if (bestValueOfCurrentSize < currentCoalitionValue) {
+						bestValueOfCurrentSize = currentCoalitionValue;
+						CS[j] = C;
+					}
+				}
+				// the set of agents not in coalitions: CS[0],...,CS[i-1]
+				setOfRemainingAgentsInBitFormat -= CS[j];
+				setOfRemainingAgentsInByteFormat = Combinations
+						.convertCombinationFromBitToByteFormat(
+								setOfRemainingAgentsInBitFormat,
+								input.numOfAgents, numOfRemainingAgents[j]);
+			}
+			// Add the last coalition in CS
+			CS[integers.length - 1] = setOfRemainingAgentsInBitFormat;
+
+			// Update the currest best CS found
+			if (result.get_ipValueOfBestCSFound() < input
+					.getCoalitionStructureValue(CS)) {
+				int[][] CSInByteFormat = Combinations
+						.convertSetOfCombinationsFromBitToByteFormat(CS,
+								input.numOfAgents, integers);
+				result.updateIPSolution(CSInByteFormat,
+						input.getCoalitionStructureValue(CS));
+				if (result.get_ipValueOfBestCSFound() >= acceptableValue) { // If
+																			// the
+																			// value
+																			// is
+																			// within
+																			// the
+																			// acceptable
+																			// ratio
+																			// {
+					output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+							input, result);
+					return;
+				}
+			}
+		}
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Order the parts of the integer partition. Here, I did it such that the
+	 * first part is the one that has the maximum difference between its upper
+	 * bound and its average. This ended up exactly as the descending order.
+	 */
+	public int[] orderParts(int[] integers, double[] avgValueForEachSize,
+			double[][] maxValueForEachSize, Input input) {
+		// Get (1) the underlying set of the integer partition, and (2) the
+		// multiplicity of each part.
+		int[] underlyingSet = General.getUnderlyingSet(integers);
+		int[] multiplicity = new int[input.numOfAgents];
+		for (int i = 0; i < input.numOfAgents; i++)
+			multiplicity[i] = 0;
+		for (int i = 0; i < integers.length; i++)
+			multiplicity[integers[i] - 1]++;
+
+		// Initialize the new ordering of the underlying set
+		int[] newUnderlyingSet = new int[underlyingSet.length];
+		for (int i = 0; i < newUnderlyingSet.length; i++) {
+			newUnderlyingSet[i] = 0;
+		}
+		// Compute the new ordering of the underlying set
+		for (int i = 0; i < newUnderlyingSet.length; i++) {
+			double biggestDifference = -1;
+			int sizeWithMaxDifference = -1;
+			for (int j = 0; j < underlyingSet.length; j++) {
+				int curSize = underlyingSet[j];
+
+				// if we already put this size in the list "newOrderingOfSizes",
+				// then continue skip
+				boolean sizeAlreadyAddedToList = false;
+				for (int k = 0; k < i; k++)
+					if (newUnderlyingSet[k] == curSize) {
+						sizeAlreadyAddedToList = true;
+						break;
+					}
+				if (sizeAlreadyAddedToList)
+					continue;
+
+				// Check if the current size has the biggest difference between
+				// Max and Avg
+				if (biggestDifference < (maxValueForEachSize[curSize - 1][0]
+						- avgValueForEachSize[curSize - 1])) {
+					biggestDifference = (maxValueForEachSize[curSize - 1][0]
+							- avgValueForEachSize[curSize - 1]);
+					sizeWithMaxDifference = curSize;
+				}
+			}
+			newUnderlyingSet[i] = sizeWithMaxDifference;
+		}
+		// constructing the new Integer partition
+		int[] newIntegers = new int[integers.length];
+		int index = 0;
+		for (int i = 0; i < newUnderlyingSet.length; i++) {
+			int curSize = newUnderlyingSet[i];
+			for (int j = 0; j < multiplicity[curSize - 1]; j++) {
+				newIntegers[index] = curSize;
+				index++;
+			}
+		}
+		return (newIntegers);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Computes the size of the subspace (i.e., the number of coalition
+	 * structures in it). I HIGHLY RECOMMEND reading the comments that are at
+	 * the beginning of the method: "search_usingBranchAndBound".
+	 */
+	public long computeNumOfCSInSubspace(int[] integers) {
+		// Calculate the number of agents from the given integers...
+		int numOfAgents = 0;
+		for (int i = 0; i < integers.length; i++)
+			numOfAgents += integers[i];
+
+		// Check the special case where the size of the integer partition equals
+		// 1 or "numOfAgents"
+		if ((integers.length == 1) || (integers.length == numOfAgents))
+			return (1);
+
+		// Initialization
+		int[] length_of_A = init_length_of_A(numOfAgents, integers);
+		int[] max_first_member_of_M = init_max_first_member_of_M(integers,
+				length_of_A, false);
+		long[][] numOfCombinations = init_numOfCombinations(integers,
+				length_of_A, max_first_member_of_M);
+		long[] sumOf_numOfCombinations = init_sumOf_numOfCombinations(
+				numOfCombinations, integers, length_of_A,
+				max_first_member_of_M);
+		long[] numOfRemovedCombinations = init_numOfRemovedCombinations(
+				integers, length_of_A, max_first_member_of_M);
+		long[][] increment = init_increment(integers, numOfCombinations,
+				sumOf_numOfCombinations, max_first_member_of_M, false);
+
+		// Calculating size of the subspace, i.e., the total number of coalition
+		// structures in it:
+		long sizeOfSubspace = 0;
+		if (numOfRemovedCombinations[0] == 0) { // then the list has a single
+												// increment value
+			sizeOfSubspace = increment[0][0] * sumOf_numOfCombinations[0];
+		} else { // then the list has different increment values
+			for (int i = 0; i <= max_first_member_of_M[0] - 1; i++)
+				sizeOfSubspace += increment[0][i] * numOfCombinations[0][i];
+		}
+		return (sizeOfSubspace);
+
+		// An alternative way for computing the number of CS in a subspace (by
+		// using the equation). However, for
+		// large numbers of agents, the factorial method might overflow.
+		// Therefore, we do not use the equation.
+		/*
+		 * int[] underlyingSet = General.getUnderlyingSet(integers);
+		 * 
+		 * long num1 = Combinations.binomialCoefficient( numOfAgents ,
+		 * integers[0] ); int x=numOfAgents; for(int j=1; j<=integers.length-2;
+		 * j++) { x=(int)(x-integers[j-1]); num1 = num1 *
+		 * Combinations.binomialCoefficient( x, integers[j] ); } long num2=1;
+		 * for(int j=0; j<=underlyingSet.length-1; j++) num2 = num2 *
+		 * General.factorial( General.multiplicity(underlyingSet[j],integers) );
+		 * 
+		 * return( (long)num1/num2 );
+		 */ }
+
+	// ******************************************************************************************************
+
+	/**
+	 * Computes the total number of expansions in each search space. Observe
+	 * that this differs according to whether the integer partition is ordered
+	 * ascendingly or descendingly. This method is thoroughly tested, and is
+	 * guaranteed to work.
+	 */
+	public static long computeTotalNumOfExpansionsInSubspace(int[] integers,
+			Input input) {
+		// Compute the number of agents
+		int numOfAgents = 0;
+		for (int i = 0; i < integers.length; i++)
+			numOfAgents += integers[i];
+
+		// Sort the parts in the integer partition
+		int[] sortedIntegers = General.sortArray(integers,
+				input.orderIntegerPartitionsAscendingly);
+
+		int[] alpha = new int[sortedIntegers.length];
+		long[][] gamma = new long[sortedIntegers.length][];
+		for (int j = 0; j < sortedIntegers.length; j++) {
+			// compute the maximum index (in the integer partition) at which the
+			// current integer appears
+			int maxIndex = 0;
+			for (int k = 0; k < sortedIntegers.length; k++)
+				if (sortedIntegers[k] == sortedIntegers[j])
+					maxIndex = k;
+
+			// compute alpha for the current integer
+			alpha[j] = numOfAgents + 1;
+			for (int k = 0; k <= maxIndex; k++)
+				alpha[j] -= sortedIntegers[k];
+
+			// compute gamma for the current integer
+			gamma[j] = new long[alpha[j]];
+			for (int beta = 0; beta < alpha[j]; beta++) {
+				int sumOfPreviousIntegers = 0;
+				for (int k = 0; k < j; k++)
+					sumOfPreviousIntegers += sortedIntegers[k];
+
+				if (j == 0)
+					gamma[j][beta] = Combinations.binomialCoefficient(
+							numOfAgents - sumOfPreviousIntegers - (beta + 1),
+							sortedIntegers[j] - 1);
+				else {
+					int lambda;
+					if (sortedIntegers[j] == sortedIntegers[j - 1])
+						lambda = beta;
+					else
+						lambda = alpha[j - 1] - 1;
+
+					long sum = 0;
+					for (int k = 0; k <= lambda; k++)
+						sum += gamma[j - 1][k];
+					gamma[j][beta] = sum * Combinations.binomialCoefficient(
+							numOfAgents - sumOfPreviousIntegers - (beta + 1),
+							sortedIntegers[j] - 1);
+				}
+			}
+		}
+		long numOfExpansionsInSubspace = 0;
+		for (int j = 0; j < sortedIntegers.length; j++)
+			for (int beta = 0; beta < alpha[j]; beta++)
+				numOfExpansionsInSubspace += gamma[j][beta];
+
+		return (numOfExpansionsInSubspace);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * //Description of the main variables used (it is HIGHLY RECOMMENDED to
+	 * read these comments before reading the method). //For more detail, see
+	 * Section 4.3, and figure 3 in the paper: "Near-optimal anytime coalition
+	 * structure generation"
+	 * /*_______________________________________________________________________________________________________________________________
+	 * | length_of_A[i]: | A coalition structure consists of a number of
+	 * coalitions (e.g. c[1],c[2],...). | length_of_A[i] represents THE NUMBER
+	 * of agents from which we can select members of c[i] | | (In figure 3,
+	 * "length_of_A" contains the values: 7,5,3)
+	 * |___________________________________________________________________________________________________
+	 * | A[i]: | A coalition structure consists of a number of coalitions (e.g.
+	 * c[1],c[2],...). | A[i] represents THE SET of agents from which we can
+	 * select members of c[i]. | Note: | Every time you update c[i], you would
+	 * have to update A[j]:j>i | | (In figure 3, "A" contains the values:
+	 * {1,2,3,4,5,6,7}, {2,3,4,5,7}, {2,3,7} )
+	 * |___________________________________________________________________________________________________
+	 * | M[i]: | (In figure 3, "M" contains the values: {1,6},{3,4},{1,2,3})
+	 * |___________________________________________________________________________________________________
+	 * | max_first_member_of_M[i] | Represents the maximum value that the first
+	 * member of "M[i]" can take. | | (In figure 3, "max_first_member_of_M"
+	 * contains the values: 4,4,1)
+	 * |___________________________________________________________________________________________________
+	 * | index_of_M[i] | The index of "M" in the list of potential combinations
+	 * for "M" | | (In figure 3, "index_of_M" contains the values: 17,3,1)
+	 * |___________________________________________________________________________________________________
+	 * | numOfCombinations: | Represents the number of possible combinations for
+	 * M[s] | | Note(1) | If( integers[s] != integers[s+1] ), then
+	 * numOfCombinations[s] would actually | be the number of possible
+	 * combinations of size integers[s] out of the set A[s]. | Note(2) | If(
+	 * integers[s] == integers[s+1] ), then the set of possible coalitions would
+	 * | be A SUBSET of the possible combinations of size integers[s] out of the
+	 * set A[s]. | | In more detail, it would be those coalitions containing 1,
+	 * and those not containing | 1 but containing 2, and those not containing
+	 * 1,2 but containing 3, and so on, until | those not containing:
+	 * 1,...,(length_of_A[s+1]-integers[s+1]+1)-1, but containing: |
+	 * (length_of_A[s+1]-integers[s+1]+1). | | numOfCombinations[s][0] contains
+	 * the number of coalitions containing 1. | numOfCombinations[s][1] contains
+	 * the number of coalitions not containing 1, but containing 2 |
+	 * numOfCombinations[s][2] contains the number of coalitions not containing
+	 * 1,2, but containing 3 | and so on... | | (In figure 3,
+	 * "numOfCombinations[0]" contains the values: 6,5,4,3 |
+	 * "numOfCombinations[1]" contains the values: 4,3,2,1 |
+	 * "numOfCombinations[2]" contains the value : 1 )
+	 * |___________________________________________________________________________________________________
+	 * | sumOf_numOfCombinations[i]| Contains the sum of:
+	 * numOfCombinations[i][j] : j = 0,1,2,... | | (In figure 3,
+	 * "sumOf_numOfCombinations" contains the values: 18,10,1)
+	 * |___________________________________________________________________________________________________
+	 * | numOfRemovedCombinations: | As mentioned above: | if( integers[s] !=
+	 * integers[s+1] ) then the set of possible combinations for M[s] | would be
+	 * the set of possible combinations of size integers[s] out of the set A[s].
+	 * In | this case, "numOfRemovedCombinations" would be 0. | | if(
+	 * integers[s] == integers[s+1] ) then the set of possible combinaitons for
+	 * M[s] | would only be a subset of that set. In this case
+	 * "numOfRemovedCombinations" would be the size of | the set minus the size
+	 * of the subset. | | (In figure 3, "numOfRemovedCombinations" contains the
+	 * values: 3,0,0)
+	 * |___________________________________________________________________________________________________
+	 * | indexToStartAt[i] | The index at which M[i] starts. It equals:
+	 * sumOf_numOfCombinations[i] + numOfRemovedCombinations[i] | | (In figure
+	 * 3, "indexToStartAt" contains the values: 21, 10, 1)
+	 * |___________________________________________________________________________________________________
+	 * | indexToStopAt[i] | The index at which M[i] stops. It equals:
+	 * numOfRemovedCombinations[i] + 1 | | (In figure 3, "indexToStopAt"
+	 * contains the values: 4, 1, 1)
+	 * |___________________________________________________________________________________________________
+	 * | CS: | CS[i][j] = A[ M[i][j] ]. In other words, "CS" represents the
+	 * actual coalition, | while "M" only represents indices that point to the
+	 * actual agents | | (In figure 3, "CS" contains the values:
+	 * {1,6},{4,5},{2,3,7})
+	 * |___________________________________________________________________________________________________
+	 * | increment[s]: | Every time we move up one step in the list of
+	 * combinations for M[s], we would have finished | scanning a particular
+	 * number of coalition structures. This number is "increment[s]" | | Note
+	 * that if( integers[s] == integers[s+1] ), then this number would not be
+	 * equal | for every combination in the list. | | (In figure 3, increment[0]
+	 * contains the values: 10,6,3,1 | increment[1] contains the value : 1 |
+	 * increment[2] contains the value : 1)
+	 * |___________________________________________________________________________________________________
+	 * | sumOf_values | Every time we calculate the value of a coalition
+	 * structure, we sum the values of its constituent | coalitions. | | Now,
+	 * due to the way we scan the possible CS, every time we move M[s] one step,
+	 * | (M[i] : i>s) must be updated, while (M[j] : j<s) remain unchanged. This
+	 * implies that: (CS[i] : i>s) | must be updated, while (CS[j] : j<s)
+	 * remains unchanged. Therefore, if we knew the summation of the | values of
+	 * coalitions (CS[j] : j<s), then we would only need to add to it the value
+	 * of coalition | CS[s], plus the values of coalitions (CS[i] : i>s). | |
+	 * sumOf_values[1] = the value of the first coalition (i.e. v(CS[0])) |
+	 * sumOf_values[2] = the sum of the values of the first 2 coalitions (i.e.
+	 * v(CS[0]) + v(CS[1])) | and so on... | | IMPORTANT NOTE: | sumOf_values
+	 * actually contains: "numOfAgents+1" values, where sumOf_values[0] is
+	 * always equal | to 0. This way, the value at location i would be:
+	 * sumOf_values[i], and not: sumOf_values[i-1], | which makes accessing the
+	 * value slightly faster. Moreover, we can now say: | sumOf_values[i] =
+	 * sumOf_values[i-1] + getCoalitionValue(...), even if i=1, because we know
+	 * that | sumOf_values[0] will always be equal to zero.
+	 * |___________________________________________________________________________________________________
+	 * | sumOf_agents | sumOf_agents[s] = the agents that belong to coalitions
+	 * (CS[j] : j<=s), represented as a bit string
+	 * |__________________________________________________________________________________________________
+	 * | sumOf_parts | sumOf_parts[s] = the sum of the following parts in the
+	 * integer partition: 0, 1, ..., s | | (In figure 3, "sumOf_parts" contains
+	 * the values: 2, 4, 7
+	 * ___________________________|__________________________________________________________________________________________________
+	 */
+
+	private void search_useBranchAndBound(Input input, Output output,
+			Result result, double acceptableValue, double[] sumOfMax,
+			int numOfIntegersToSplit) {
+		// Checking the special case where the size of the coalition structure
+		// is either "1" or "numOfAgents"
+		if ((integers.length == 1) || (integers.length == input.numOfAgents)) {
+			searchFirstOrLastLevel(integers, input, output, result);
+			return;
+		}
+		// Initialization
+		final int numOfIntegers = integers.length;
+		final long ipNumOfSearchedCoalitions_beforeSearchingThisSubspace = result.ipNumOfExpansions;
+		final int numOfAgents = input.numOfAgents;
+		final int numOfIntsToSplit = numOfIntegersToSplit;
+		final boolean ipUsesBranchAndBound = input.useBranchAndBound;
+		final boolean constraintsExist;
+		boolean this_CS_is_useless;
+		double valueOfCS = 0;
+		if (input.feasibleCoalitions == null)
+			constraintsExist = false;
+		else
+			constraintsExist = true;
+		final boolean ODPIsHelpingIP;
+		if (input.solverName == SolverNames.ODPIP)
+			ODPIsHelpingIP = true;
+		else
+			ODPIsHelpingIP = false;
+
+		// Initialization (in the right order)
+		final int[] bit = init_bit(numOfAgents);
+		final int[] length_of_A = init_length_of_A(numOfAgents, integers);
+		final int[] max_first_member_of_M = init_max_first_member_of_M(integers,
+				length_of_A, ODPIsHelpingIP);
+		final long[][] numOfCombinations = init_numOfCombinations(integers,
+				length_of_A, max_first_member_of_M);
+		final long[] sumOf_numOfCombinations = init_sumOf_numOfCombinations(
+				numOfCombinations, integers, length_of_A,
+				max_first_member_of_M);
+		final long[] numOfRemovedCombinations = init_numOfRemovedCombinations(
+				integers, length_of_A, max_first_member_of_M);
+		final long[][] increment = init_increment(integers, numOfCombinations,
+				sumOf_numOfCombinations, max_first_member_of_M, ODPIsHelpingIP);
+		final long[] indexToStartAt = init_indexToStartAt(numOfIntegers,
+				numOfRemovedCombinations, sumOf_numOfCombinations);
+		final long[] indexToStopAt = init_indexToStopAt(numOfIntegers,
+				numOfRemovedCombinations);
+		long[] index_of_M = init_index_of_M(1, integers, increment,
+				max_first_member_of_M, numOfCombinations,
+				numOfRemovedCombinations, sumOf_numOfCombinations);
+		int[][] M = init_M(index_of_M, integers, length_of_A, numOfAgents);
+		int[][] A = init_A(numOfAgents, integers, M, length_of_A);
+		int[] CS = init_CS_hashTableVersion(M, A, length_of_A, bit,
+				numOfIntegers);
+		int[] sumOf_agents = init_sumOf_agents_hashTableVersion(numOfIntegers,
+				CS);
+		double[] sumOf_values = init_sumOf_values_hashTableVersion(
+				numOfIntegers, CS, input);
+		result.ipNumOfExpansions += integers.length - 2;
+		IDPSolver_whenRunning_ODPIP localBranchAndBoundObject = result.idpSolver_whenRunning_ODPIP;
+
+		main_loop: while (true) {
+			// In the following loop, we fix the coalitions
+			// 1,2,...,numOfIntegers-3, and try all
+			// the possibilities for the last two coalitions in the integer
+			// partition.
+			do {
+				setTheLastTwoCoalitionsInCS(CS, M[numOfIntegers - 2], A,
+						numOfIntegers, bit);
+
+				// If there are constraints, then check them on the last two
+				// coalitions
+				this_CS_is_useless = false;
+				if ((constraintsExist)
+						&& (checkIfLastTwoCoalitionsSatisfyConstraints(CS,
+								input.feasibleCoalitions) == false))
+					this_CS_is_useless = true;
+
+				// If the current coalition structure satisfies the constraints,
+				// then...
+				if (this_CS_is_useless == false) {
+					// Calculate the value of the current coalition structure
+					switch (numOfIntsToSplit) {
+					case 0:
+						valueOfCS = sumOf_values[numOfIntegers - 2]
+								+ input.getCoalitionValue(CS[numOfIntegers - 2])
+								+ input.getCoalitionValue(
+										CS[numOfIntegers - 1]);
+						break;
+					case 1:
+						valueOfCS = sumOf_values[numOfIntegers - 2]
+								+ input.getCoalitionValue(CS[numOfIntegers - 2])
+								+ localBranchAndBoundObject
+										.getValueOfBestPartitionFound(
+												CS[numOfIntegers - 1]);
+						break;
+					default:
+						valueOfCS = sumOf_values[numOfIntegers - 2]
+								+ localBranchAndBoundObject
+										.getValueOfBestPartitionFound(
+												CS[numOfIntegers - 2])
+								+ localBranchAndBoundObject
+										.getValueOfBestPartitionFound(
+												CS[numOfIntegers - 1]);
+					}
+					// If this value is greater than the best value found so far
+					if (result.get_ipValueOfBestCSFound() < valueOfCS) {
+						int[][] CSInByteFormat = Combinations
+								.convertSetOfCombinationsFromBitToByteFormat(CS,
+										numOfAgents, integers);
+						result.updateIPSolution(CSInByteFormat, valueOfCS);
+
+						if (result
+								.get_ipValueOfBestCSFound() >= acceptableValue) // If
+																				// the
+																				// value
+																				// is
+																				// within
+																				// the
+																				// acceptable
+																				// ratio
+						{
+							output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+									input, result);
+							numOfSearchedCoalitionsInThisSubspace = result.ipNumOfExpansions
+									- ipNumOfSearchedCoalitions_beforeSearchingThisSubspace;
+							return;
+						}
+					}
+				}
+				index_of_M[numOfIntegers - 2]--;
+				Combinations.getPreviousCombination(
+						length_of_A[numOfIntegers - 2],
+						integers[numOfIntegers - 2], M[numOfIntegers - 2]);
+			} while (index_of_M[numOfIntegers
+					- 2] >= indexToStopAt[numOfIntegers - 2]);
+
+			/*
+			 * In the following loop, we keep changing the coalitions
+			 * 1,2,...,numOfIntegers-3 until we find a combinations of them that
+			 * is potentially useful (i.e., until the sum of their values, plus
+			 * the maximum possible value of the remaining coalitions, is
+			 * greater than the best value found so far)
+			 */
+			int s1 = numOfIntegers - 3;
+			sub_loop: while (s1 >= 0) {
+				if (index_of_M[s1] > indexToStopAt[s1]) // If you have NOT
+														// finished scannig this
+														// column:
+				{
+					if (s1 == 0) {
+						output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+								input, result);
+						if (ODPIsHelpingIP) {
+							if (result
+									.get_dpMaxSizeThatWasComputedSoFar() >= ((int) Math
+											.floor(2 * input.numOfAgents
+													/ (double) 3))) {
+								numOfSearchedCoalitionsInThisSubspace = result.ipNumOfExpansions
+										- ipNumOfSearchedCoalitions_beforeSearchingThisSubspace;
+								return;
+							}
+						}
+					}
+					for (int s2 = s1; s2 <= numOfIntegers - 3; s2++) {
+						boolean firstTime = true;
+						do {
+							result.ipNumOfExpansions++; // update the number of
+														// searched coalitions
+
+							if ((firstTime) && (s2 > s1)) {
+								// Set the index of M to be the last index in
+								// the column
+								set_M_and_index_of_M(M, index_of_M, length_of_A,
+										indexToStartAt, s2);
+								firstTime = false;
+							} else {
+								// move M one step upwards, and update the index
+								// of M
+								Combinations.getPreviousCombination(
+										length_of_A[s2], integers[s2], M[s2]);
+								index_of_M[s2]--;
+							}
+							// Set CS, given the new M
+							int temp3 = 0;
+							for (int j1 = integers[s2] - 1; j1 >= 0; j1--)
+								temp3 |= bit[A[s2][M[s2][j1] - 1]];
+							CS[s2] = temp3;
+
+							// If there exists constraints that we need to deal
+							// with, then check them
+							this_CS_is_useless = false;
+							if (constraintsExist) {
+								if (input.feasibleCoalitions
+										.contains(new Integer(CS[s2])) == false)
+									this_CS_is_useless = true;
+							}
+							// If all the possible values that can be found by
+							// fixing this coalition (along
+							// with the ones before it) can never be greater
+							// than the value we found so far, then...
+							if ((ipUsesBranchAndBound)
+									&& (this_CS_is_useless == false)) {
+								// Recalculate sumOf_values[s2+1] and
+								// sumOf_agents[s2+1], given the new CS
+								int newCoalition = CS[s2];
+								double valueOfNewCoalition;
+								if (s2 >= numOfIntegers - numOfIntsToSplit) {
+									valueOfNewCoalition = localBranchAndBoundObject
+											.getValueOfBestPartitionFound(
+													CS[s2]);
+								} else {
+									valueOfNewCoalition = input
+											.getCoalitionValue(CS[s2]);
+								}
+								sumOf_values[s2 + 1] = sumOf_values[s2]
+										+ valueOfNewCoalition;
+								sumOf_agents[s2 + 1] = sumOf_agents[s2]
+										+ CS[s2];
+
+								// Use branch and bound to see if the current
+								// coalitions have potential
+								double upperBoundForRemainingAgents = sumOfMax[s2
+										+ 2];
+								// if( (
+								// (sumOf_values[s2+1]+upperBoundForRemainingAgents)
+								// <= result.get_ipValueOfBestCSFound() )
+								if (((sumOf_values[s2 + 1]
+										+ upperBoundForRemainingAgents)
+										- result.get_ipValueOfBestCSFound() < -0.00000000005)
+										|| ((ODPIsHelpingIP)
+												&& (useLocalBranchAndBound(
+														input,
+														localBranchAndBoundObject,
+														sumOf_values,
+														sumOf_agents, s2,
+														newCoalition,
+														valueOfNewCoalition))))
+									this_CS_is_useless = true;
+							}
+							// If the current combination of coalitions has been
+							// found useless...
+							if (this_CS_is_useless == false)
+								break;
+						} while (index_of_M[s2] > indexToStopAt[s2]);
+
+						if (this_CS_is_useless) { // If we kept moving upwards,
+													// until we reached the top:
+							s1 = s2 - 1;
+							continue sub_loop;
+						}
+						update_A(A[s2 + 1], A[s2], length_of_A[s2], M[s2],
+								integers[s2]);
+					}
+					// Set M, given its new index
+					int s2 = numOfIntegers - 2;
+					set_M_and_index_of_M(M, index_of_M, length_of_A,
+							indexToStartAt, s2);
+
+					continue main_loop;
+				}
+				s1--;
+			}
+			break main_loop;
+		}
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+		numOfSearchedCoalitionsInThisSubspace = result.ipNumOfExpansions
+				- ipNumOfSearchedCoalitions_beforeSearchingThisSubspace;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is called for both the integer partition of size 1, and that
+	 * of size numOfAgents. This is because each of the corresponding subspaces
+	 * contain a single coalition structure, and searching it can be done
+	 * instantly
+	 */
+	private void searchFirstOrLastLevel(int[] integers, Input input,
+			Output output, Result result) {
+		// Initialization
+		int numOfAgents = input.numOfAgents;
+		int[][] curCS;
+
+		if (integers.length == 1) {
+			curCS = new int[1][numOfAgents];
+			for (int i = 0; i <= numOfAgents - 1; i++)
+				curCS[0][i] = i + 1;
+		} else {
+			curCS = new int[numOfAgents][1];
+			for (int i = 0; i <= numOfAgents - 1; i++)
+				curCS[i][0] = i + 1;
+		}
+		double valueOfCurCS = input.getCoalitionStructureValue(curCS);
+
+		result.updateIPSolution(curCS, valueOfCurCS);
+
+		output.printCurrentResultsOfIPToStringBuffer_ifPrevResultsAreDifferent(
+				input, result);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Check if the last two coalitions in the coalition structure satisfy the
+	 * constraints (i.e., chech whether they appear in the list of feasible
+	 * coalitions).
+	 */
+	private boolean checkIfLastTwoCoalitionsSatisfyConstraints(int[] CS,
+			TreeSet<Integer> feasibleCoalitions) {
+		if (feasibleCoalitions
+				.contains(new Integer(CS[CS.length - 1])) == false)
+			return (false);
+		if (feasibleCoalitions
+				.contains(new Integer(CS[CS.length - 2])) == false)
+			return (false);
+		return (true);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * bit[i] is the bit representing agent a_i (e.g. given 4 agents,
+	 * bit[2]=2=0010, bit[3]=4=0100, etc.)
+	 */
+	private int[] init_bit(int numOfAgents) {
+		int[] bit = new int[numOfAgents + 1];
+		for (int i = 0; i < numOfAgents; i++)
+			bit[i + 1] = 1 << i;
+		return (bit);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes indexToStopAt. For more details see the comments
+	 * at the beginning of "search_useBranchAndBound"
+	 */
+	private long[] init_indexToStartAt(int numOfIntegers,
+			long[] numOfRemovedCombinations, long[] sumOf_numOfCombinations) {
+		long[] indexToStartAt = new long[numOfIntegers];
+		for (int i = 0; i < numOfIntegers; i++) {
+			indexToStartAt[i] = sumOf_numOfCombinations[i]
+					+ numOfRemovedCombinations[i];
+		}
+		return (indexToStartAt);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes indexToStopAt. For more details see the comments
+	 * at the beginning of "search_useBranchAndBound"
+	 */
+	private long[] init_indexToStopAt(int numOfIntegers,
+			long[] numOfRemovedCombinations) {
+		long[] indexToStopAt = new long[numOfIntegers];
+		for (int i = 0; i < numOfIntegers; i++) {
+			indexToStopAt[i] = numOfRemovedCombinations[i] + 1;
+		}
+		return (indexToStopAt);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes "init_max_first_member_of_M". For more details
+	 * see the comments at the beginning of "search_useBranchAndBound"
+	 */
+	private int[] init_max_first_member_of_M(int[] integers, int[] length_of_A,
+			boolean ipUsesLocalBranchAndBound) {
+		int[] max_first_member_of_M = new int[integers.length];
+		int i = integers.length - 1;
+
+		if ((ipUsesLocalBranchAndBound) && (relevantNodes != null)
+				&& (integers.length > 2)) {
+			max_first_member_of_M[i] = length_of_A[i] - integers[i] + 1;
+			i--;
+		}
+		while (i >= 0) {
+			max_first_member_of_M[i] = length_of_A[i] - integers[i] + 1;
+			i--;
+			while ((i >= 0) && (integers[i] == integers[i + 1])) {
+				max_first_member_of_M[i] = max_first_member_of_M[i + 1];
+				i--;
+			}
+		}
+		return (max_first_member_of_M);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes "numOfCombinatinos". For more details see the
+	 * comments at the beginning of "search_useBranchAndBound"
+	 */
+	private long[][] init_numOfCombinations(int[] integers, int[] length_of_A,
+			int[] max_first_member_of_M) {
+		long[][] numOfCombinations = new long[integers.length][];
+		for (int i = 0; i <= integers.length - 1; i++) {
+			if (length_of_A[i] == integers[i]) {
+				numOfCombinations[i] = new long[1];
+				numOfCombinations[i][0] = 1;
+			} else {
+				numOfCombinations[i] = new long[max_first_member_of_M[i]];
+				for (int j = 0; j <= max_first_member_of_M[i] - 1; j++) {
+					numOfCombinations[i][j] = Combinations.binomialCoefficient(
+							length_of_A[i] - (j + 1), integers[i] - 1);
+				}
+			}
+		}
+		return (numOfCombinations);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes "sumOf_numOfCombinations". For more details see
+	 * the comments at the beginning of "search_useBranchAndBound"
+	 */
+	private long[] init_sumOf_numOfCombinations(long[][] numOfCombinations,
+			int[] integers, int[] length_of_A, int[] max_first_member_of_M) {
+		long[] sumOf_numOfCombinations = new long[integers.length];
+
+		for (int i = 0; i <= integers.length - 1; i++) {
+			if (length_of_A[i] == integers[i]) {
+				sumOf_numOfCombinations[i] = 1;
+			} else {
+				sumOf_numOfCombinations[i] = 0;
+				for (int j = 0; j <= max_first_member_of_M[i] - 1; j++) {
+					sumOf_numOfCombinations[i] = sumOf_numOfCombinations[i]
+							+ numOfCombinations[i][j];
+				}
+			}
+		}
+		return (sumOf_numOfCombinations);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes: numOfRemovedCombinations. (For more details on:
+	 * "numOfRemovedCombinations", read the comments that are in the beginning
+	 * of method: "search_useBranchAndBound")
+	 */
+	private long[] init_numOfRemovedCombinations(int[] integers,
+			int[] length_of_A, int[] max_first_member_of_M) {
+		long[] numOfRemovedCombinations = new long[integers.length];
+
+		for (int i = 0; i <= integers.length - 1; i++) {
+			if (length_of_A[i] == integers[i]) {
+				numOfRemovedCombinations[i] = 0;
+			} else {
+				numOfRemovedCombinations[i] = 0;
+				for (int j = max_first_member_of_M[i]; j <= length_of_A[i]
+						- integers[i]; j++) {
+					numOfRemovedCombinations[i] = numOfRemovedCombinations[i]
+							+ Combinations.binomialCoefficient(
+									length_of_A[i] - (j + 1), integers[i] - 1);
+				}
+			}
+		}
+		return (numOfRemovedCombinations);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes increment (For more details on: "increment", read
+	 * the comments that are in the beginning of method:
+	 * "search_useBranchAndBound")
+	 */
+	private long[][] init_increment(int[] integers, long[][] numOfCombinations,
+			long[] sumOf_numOfCombinations, int[] max_first_member_of_M,
+			boolean ipUsesLocalBranchAndBound) {
+		long[][] increment = new long[integers.length][];
+		increment[integers.length - 1] = new long[1];
+		increment[integers.length - 1][0] = 1;
+
+		int s = integers.length - 2;
+		while (s >= 0) // make sure to define s as integer
+		{
+			if ((integers[s] != integers[s + 1]) || ((ipUsesLocalBranchAndBound)
+					&& (s == integers.length - 2) && (integers.length > 2))) {
+				// For the possible combinations of M[s], the increment would be
+				// equal to the number of possible
+				// combinations for M[s+1], multiplied by the increment of each
+				// of these combinations.
+				increment[s] = new long[1];
+				increment[s][0] = sumOf_numOfCombinations[s + 1]
+						* increment[s + 1][0];
+				s--;
+			} else {
+				/*
+				 * Calculate the increment for the possible combinations of
+				 * M[s].
+				 * 
+				 * Here, we calculate the increment for the combinations that
+				 * start with j, where: j = 1,2,..., max_first_member_of_M[s]),
+				 * although they will all have the same increment, which is the
+				 * number of possible combinations of M[s+1], multiplied by the
+				 * increment of each of these combinations.
+				 */
+				increment[s] = new long[max_first_member_of_M[s]];
+				for (int i = 0; i <= max_first_member_of_M[s] - 1; i++) {
+					increment[s][i] = 0;
+					for (int j = i; j <= max_first_member_of_M[s] - 1; j++)
+						increment[s][i] += (numOfCombinations[s + 1][j]
+								* increment[s + 1][0]);
+				}
+				s--;
+
+				/*
+				 * If there exists (M[i] : i<s) such that (M[i]=M[i+1]), then
+				 * calculate the increment for the possible combinations of
+				 * M[i].
+				 * 
+				 * For the possible combinations of M[i] that start with j
+				 * (j=1,2,...,max_first_member_of_M[i]), the increment is equal
+				 * to the number of possible combinations of M[i+1] that start
+				 * with j, multiplied by the increment of each of these
+				 * combinations.
+				 */
+				while ((s >= 0) && (integers[s] == integers[s + 1])) {
+					increment[s] = new long[max_first_member_of_M[s]];
+					for (int i = 0; i <= max_first_member_of_M[s] - 1; i++) {
+						increment[s][i] = 0;
+						for (int j = i; j <= max_first_member_of_M[s] - 1; j++)
+							increment[s][i] += (numOfCombinations[s + 1][j]
+									* increment[s + 1][j]);
+					}
+					s--;
+				}
+
+				/*
+				 * Now, knowing that M[s]!=M[s+1], we calculate the increment
+				 * for the possible combinations of M[s]
+				 * 
+				 * Note that these would all have the same increment, which is
+				 * the summation of the following: the number of possible
+				 * combinations for M[s+1] that start with j, multiplied by the
+				 * increment of each of these combinations, where:
+				 * j=1,2,...,max_first_member_of_M[s+1]
+				 */
+				if (s >= 0) {
+					increment[s] = new long[1];
+					increment[s][0] = 0;
+					for (int j = 0; j <= max_first_member_of_M[s + 1] - 1; j++)
+						increment[s][0] += (numOfCombinations[s + 1][j]
+								* increment[s + 1][j]);
+					s--;
+				}
+			}
+		}
+		return (increment);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method sets M[i] to be the combination located at: index_of_M[i]
+	 */
+	private int[][] init_M(long[] index_of_M, int[] integers, int[] length_of_A,
+			int numOfAgents) {
+		long[][] pascalMatrix = Combinations.initPascalMatrix(numOfAgents + 1,
+				numOfAgents + 1);
+
+		// Memory allocation:
+		int[][] M = new int[integers.length][];
+		for (int s = 0; s <= integers.length - 1; s++) {
+			M[s] = new int[integers[s]];
+		}
+
+		// Setting M[0], M[1], ..., M[integers.length-1]:
+		for (int i = 0; i <= integers.length - 1; i++) {
+			/* 1 */int j = 1;
+			long index = index_of_M[i];
+			int s1 = integers[i];
+
+			boolean done = false;
+			do {
+				// Check the values: pascalMatrix[s1,1],pascalMatrix[s1,2],...
+				/* 2 */ int x = 1;
+				while (pascalMatrix[s1 - 1][x - 1] < index)
+					x++;
+
+				/* 3 */ M[i][j - 1] = (length_of_A[i] - s1 + 1) - x + 1;
+
+				/* 4 */ if (pascalMatrix[s1 - 1][x - 1] == index) {
+					// Set the rest of the coalition as follows:
+					for (int k = j; k <= integers[i] - 1; k++)
+						M[i][k] = M[i][k - 1] + 1;
+					done = true;
+				}
+				// Otherwise:
+				else {
+					j = j + 1;
+					index = index - pascalMatrix[s1 - 1][x - 2];
+					s1 = s1 - 1;
+				}
+			} while (done == false);
+		}
+		return (M);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes index_of_M (Converts the index into a list of
+	 * indices for every list) (For more details on: "index_of_M", read the
+	 * comments that are in the beginning of method: "search_useBranchAndBound")
+	 */
+	private long[] init_index_of_M(long index, int[] integers,
+			long[][] increment, int[] max_first_member_of_M,
+			long[][] numOfCombinations, long[] numOfRemovedCombinations,
+			long[] sumOf_numOfCombinations) {
+		/*
+		 * Here, we will use the variables: counter1, counter2. counter1 will be
+		 * used to count the coalition structures that were scanned before
+		 * reaching the current one (i.e., before reaching the coalition
+		 * structure that is located at: "index"). counter2 will be used to
+		 * count the combinations that were scanned before reaching the current
+		 * M
+		 * 
+		 * As an example, I will refer to figure 3 in the paper:
+		 * "Near-optimal anytime coalition structure generation".
+		 * 
+		 * In figure 3, in the list of possible combinations of M[0], we will
+		 * consider the index of combination: "6,7" to be: 1, and the index of
+		 * combination: "5,7" to be: 2, and so on.
+		 * 
+		 * In figure 3, we have:
+		 * 
+		 * "integers" contains the values: 2,2,3
+		 * 
+		 * "numOfCombinations[0]" contains the values: 6,5,4,3
+		 * "numOfCombinations[1]" contains the values: 4,3,2,1
+		 * "numOfCombinations[2]" contains the value : 1
+		 * 
+		 * "sumOf_numOfCombinations" contains the values: 18,10,1
+		 * 
+		 * "numOfRemovedCombinations" contains the values: 3,0,0
+		 * 
+		 * "increment[0]" contains the values: 10,6,3,1 "increment[1]" contains
+		 * the value : 1 "increment[2]" contains the value : 1
+		 * 
+		 * "max_first_member_of_M" contains the values: 4,4,1
+		 * 
+		 * "counter1" contains the value: 48 "counter2" contains the value: 5
+		 * (when searching for M[0]) contians the value: 8 (when searching for
+		 * M[1])
+		 */
+
+		long counter1 = 0;
+		long counter2 = 1;
+		long[] index_of_M = new long[integers.length];
+
+		// Setting index_of_M[integers.length-1]:
+		index_of_M[integers.length - 1] = 1;
+
+		// Setting index_of_M[0], index_of_M[1], ...,
+		// index_of_M[integers.length-2]:
+		int min_first_member_of_M = 0;
+		for (int i = 0; i <= integers.length - 2; i++) {
+			if (sumOf_numOfCombinations[i] == 1) // If the list of possible
+													// combiantions of M[i]
+													// contains one
+													// combinations...
+			{
+				index_of_M[i] = 1;
+			} else if (increment[i].length == 1) // i.e., if all the possible
+													// combinations of M[i] had
+													// equal increments...
+			{
+				counter1 = 0;
+				counter2 = 1;
+				if (min_first_member_of_M > 0)
+					for (int j = 0; j <= min_first_member_of_M - 1; j++)
+						counter2 += numOfCombinations[i][j];
+
+				long steps = (long) (Math.ceil(index / (double) increment[i][0])
+						- 1);
+				counter1 += steps * increment[i][0];
+				counter2 += steps;
+
+				index_of_M[i] = counter2;
+				index -= counter1;
+
+				if ((i >= integers.length - 1)
+						|| (integers[i] != integers[i + 1]))
+					min_first_member_of_M = 0;
+			} else
+			// i.e., if the possible combinations of M[i] had different
+			// increments. (In this case, all the combinations
+			// that start with j, where j=1,2,...,max_first_member_of_M[i],
+			// would have the increment = "increment[i][j]")
+			{
+				counter1 = 0;
+				counter2 = 1;
+				if (min_first_member_of_M > 0)
+					for (int j = 0; j < min_first_member_of_M; j++)
+						counter2 += numOfCombinations[i][j];
+
+				/*
+				 * Note that counter1 is initially equal to 0, and counter2 is
+				 * initially equal to 1. Now, in figure 3, we have the
+				 * following:
+				 *
+				 * In the list of possible combinations of M[0], we have 6
+				 * combinations that start with 1, each of which the increment
+				 * is 10. Therefore, if index <= counter1+60, then M[0] must
+				 * start with 1. Otherwise: (1) we add 60 to counter1, (2) we
+				 * add 6 to counter2, (3) we move to the next setp.
+				 * 
+				 * In the list of possible combinations of M[0], we have 5
+				 * combinations that start with 2, each of which the increment
+				 * is 6. Therefore, if index <= counter1+30, then M[0] must
+				 * start with 2. Otherwise: (1) we add 30 to counter1, (2) we
+				 * add 5 to counter2, (3) we move to the next setp.
+				 * 
+				 * This is done until we find the required first element of
+				 * M[0], let us denote it by "x". Now, we need to know which of
+				 * these combinations (that start with "x") is the required one.
+				 * 
+				 * We count the steps that we take within the coalitions that
+				 * start with "x". For example, if the required combination was
+				 * 80, then M[0] would be one of the combinations that start
+				 * with 2, and the number of steps that we take within these
+				 * combinations is 3.
+				 */
+				for (int j = min_first_member_of_M; j < max_first_member_of_M[i]; j++) {
+					if (index <= counter1
+							+ (numOfCombinations[i][j] * increment[i][j])) {
+						long steps = (long) Math.ceil(
+								(index - counter1) / (double) increment[i][j])
+								- 1;
+						counter1 += steps * increment[i][j];
+						counter2 += steps;
+
+						index_of_M[i] = counter2;
+						index -= counter1;
+
+						if ((i < integers.length - 1)
+								&& (integers[i] == integers[i + 1]))
+							min_first_member_of_M = j;
+						else
+							min_first_member_of_M = 0;
+
+						break;
+					} else {
+						long steps = numOfCombinations[i][j];
+						counter1 += steps * increment[i][j];
+						counter2 += steps;
+					}
+				}
+			}
+		}
+
+		// When calculating a combination, based on its index, we assume
+		// 1,2,..,n to be the last combination
+		// (instead of the first). Therefore, we flip the indices (i.e., the
+		// index: i becomes: n-i)
+		for (int i = 0; i <= index_of_M.length - 1; i++)
+			index_of_M[i] = (sumOf_numOfCombinations[i]
+					+ numOfRemovedCombinations[i]) - index_of_M[i] + 1;
+
+		return (index_of_M);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes A. (For more details on: "A", read the comments
+	 * that are in the beginning of method: "search_useBranchAndBound")
+	 */
+	private int[][] init_A(int numOfAgents, int[] integers, int[][] M,
+			int[] length_of_A) {
+		/*
+		 * Note that if we have a subspace [n_1, n_2, ..., n_s], then we only
+		 * need: A[1], A[2], ... A[s-1]. Because A[s] would simply contain all
+		 * of the remaining agents.
+		 */
+		int[][] A = new int[integers.length - 1][];
+		for (int s = 0; s <= integers.length - 2; s++) {
+			A[s] = new int[length_of_A[s]];
+			if (s == 0) {
+				for (int j1 = 0; j1 <= numOfAgents - 1; j1++) {
+					A[s][j1] = j1 + 1;
+				}
+			} else {
+				int j1 = 0;
+				int j2 = 0;
+				for (int j3 = 0; j3 <= length_of_A[s - 1] - 1; j3++) {
+					if ((j1 >= M[s - 1].length) || (j3 + 1 != M[s - 1][j1])) {
+						A[s][j2] = A[s - 1][j3];
+						j2++;
+					} else
+						j1++;
+				}
+			}
+		}
+		return (A);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes "length_of_A". For more details see the comments
+	 * at the beginning of "search_useBranchAndBound"
+	 */
+	private int[] init_length_of_A(int numOfAgents, int[] integers) {
+		int[] length_of_A = new int[integers.length];
+
+		length_of_A[0] = numOfAgents;
+		if (integers.length > 1)
+			for (int s = 1; s <= integers.length - 1; s++)
+				length_of_A[s] = length_of_A[s - 1] - integers[s - 1];
+
+		return (length_of_A);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is similar to the method: "init_CS", except that it is used
+	 * with CS where the agents are represented as bits, rather than ints.
+	 */
+	private int[] init_CS_hashTableVersion(int[][] M, int[][] A,
+			int[] length_of_A, int[] bit, int numOfIntegers) {
+		int[] CS = new int[integers.length];
+
+		for (int s = 0; s <= integers.length - 2; s++) {
+			CS[s] = 0;
+			for (int j1 = 0; j1 < M[s].length; j1++) {
+				CS[s] |= bit[A[s][M[s][j1] - 1]];
+			}
+		}
+		setTheLastTwoCoalitionsInCS(CS, M[numOfIntegers - 2], A, numOfIntegers,
+				bit);
+		return (CS);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOf_values. (For more details on:
+	 * "sumOf_values", read the comments that are in the beginning of method:
+	 * "search_useBranchAndBound")
+	 */
+	private double[] init_sumOf_values_hashTableVersion(int numOfIntegers,
+			int[] CS, Input input) {
+		double[] sumOf_values = new double[numOfIntegers + 1];
+
+		sumOf_values[0] = 0;
+		for (int i = 1; i <= numOfIntegers; i++) {
+			sumOf_values[i] = sumOf_values[i - 1]
+					+ input.getCoalitionValue(CS[i - 1]);
+		}
+
+		return (sumOf_values);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method initializes sumOf_agents. (For more details on:
+	 * "sumOf_agents", read the comments that are in the beginning of method:
+	 * "search_useBranchAndBound")
+	 */
+	private int[] init_sumOf_agents(int numOfIntegers, int[][] CS, int[] bit) {
+		int[] sumOf_agents = new int[numOfIntegers + 1];
+
+		sumOf_agents[0] = 0;
+
+		for (int i = 1; i <= numOfIntegers; i++) {
+			sumOf_agents[i] = sumOf_agents[i - 1];
+			for (int j = 0; j < integers[i - 1]; j++) {
+				sumOf_agents[i] += bit[CS[i - 1][j]];
+			}
+		}
+
+		return (sumOf_agents);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is similar to the method: "init_sumOf_agents", except that it
+	 * is used with CS where the agents are represented as bits, rather than
+	 * ints.
+	 */
+	private int[] init_sumOf_agents_hashTableVersion(int numOfIntegers,
+			int[] CS) {
+		int[] sumOf_agents = new int[numOfIntegers + 1];
+
+		sumOf_agents[0] = 0;
+
+		for (int i = 1; i <= numOfIntegers; i++) {
+			sumOf_agents[i] = sumOf_agents[i - 1] + CS[i - 1];
+		}
+
+		return (sumOf_agents);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is only used in the subspace search methods. It sets "M" and
+	 * "index_of_M" (For more details, read the comments that are in the
+	 * beginning of method: "search_useBranchAndBound")
+	 */
+	private void set_M_and_index_of_M(int[][] M, long[] index_of_M,
+			final int[] length_of_A, final long[] indexToStartAt, int s2) {
+		// Set the index of M to be the last index in the column
+		index_of_M[s2] = indexToStartAt[s2];
+
+		// Set M, given its new index
+		if (integers[s2] == integers[s2 - 1]) {
+			if (M[s2 - 1][0] > 1)
+				for (int j = 1; j < M[s2 - 1][0]; j++)
+					index_of_M[s2] = index_of_M[s2]
+							- Combinations.binomialCoefficient(
+									length_of_A[s2] - j, integers[s2] - 1);
+
+			for (int j1 = 0; j1 < integers[s2]; j1++)
+				M[s2][j1] = M[s2 - 1][0] + j1;
+		} else
+			for (int j1 = 0; j1 < integers[s2]; j1++)
+				M[s2][j1] = 1 + j1;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Given: A[s-1], M[s-1], it sets the elements of: A[s]. These are simply
+	 * the emelents that are in A[s-1] that were not pointed at by M[s-1].
+	 */
+	private void update_A(int[] A1, int[] A2, final int numOfAgents, int[] M,
+			final int length_of_M) {
+		int j1 = 0;
+		int j2 = 0;
+		for (int j3 = 0; j3 < A2.length; j3++) {
+			if ((j1 >= length_of_M) || ((j3 + 1) != M[j1])) {
+				A1[j2] = A2[j3];
+				j2++;
+			} else
+				j1++;
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * Note that we need both M[i] and A[i] in order to find CS[i]. (for more
+	 * detail on: M, A, CS, see the comments at the beginning of method:
+	 * "search_useBranchAndBound"). However, this does not apply to the last
+	 * coalition (i.e., CS[integers.length-1]). This is because
+	 * M[integers.length-2] and A[integers.length-2] are enough to find both:
+	 * CS[integers.length-2] and CS[integers.length-1]. This is because the
+	 * elements of: A[integers.length-2] that were not pointed at by
+	 * M[integers.length-2] must belong to CS[integers.length-1].
+	 */
+	private void setTheLastTwoCoalitionsInCS(int[][] CS, int[][] M, int[][] A,
+			final int[] length_of_A, final int numOfIntegers) {
+		int j1 = 0;
+		int j2 = 0;
+		for (int j3 = 0; j3 < length_of_A[numOfIntegers - 2]; j3++) {
+			if ((j1 >= integers[numOfIntegers - 2])
+					|| ((j3 + 1) != M[numOfIntegers - 2][j1])) {
+				CS[numOfIntegers - 1][j2] = A[numOfIntegers - 2][j3];
+				j2++;
+			} else {
+				CS[numOfIntegers - 2][j1] = A[numOfIntegers - 2][j3];
+				j1++;
+			}
+		}
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is similar to the method: "setTheLastTwoCoalitionsInCS" that
+	 * is mentioned above, except that it is used with CS where the agents are
+	 * represented as bits, rather than ints.
+	 */
+	private void setTheLastTwoCoalitionsInCS(int[] CS, int[] M, int[][] A,
+			final int numOfIntegers, final int[] bit) {
+		int result1 = 0;
+		int result2 = 0;
+		int m = integers[numOfIntegers - 2] - 1;
+		int a = A[numOfIntegers - 2].length - 1;
+		do {
+			if (a == M[m] - 1) {
+				result1 += bit[A[numOfIntegers - 2][a]];
+				if (m == 0) {
+					a--;
+					break;
+				}
+				m--;
+			} else
+				result2 += bit[A[numOfIntegers - 2][a]];
+
+			a--;
+		} while (a >= 0);
+
+		while (a >= 0) {
+			result2 += bit[A[numOfIntegers - 2][a]];
+			a--;
+		}
+		CS[numOfIntegers - 2] = result1;
+		CS[numOfIntegers - 1] = result2;
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method sets CS to the coalition structure located at the given
+	 * index.
+	 */
+	private void set_CS_given_its_index(final int numOfAgents, int[][] CS,
+			final long index, final int[] length_of_A, final long[][] increment,
+			final int[] max_first_member_of_M, final long[][] numOfCombinations,
+			final long[] numOfRemovedCombinations,
+			final long[] sumOf_numOfCombinations) {
+		// Convert the index into a list of indices for every list
+		long[] index_of_M = init_index_of_M(index, integers, increment,
+				max_first_member_of_M, numOfCombinations,
+				numOfRemovedCombinations, sumOf_numOfCombinations);
+
+		// Set M[i] to be the coalition located at: index_of_M[i]
+		int[][] M = init_M(index_of_M, integers, length_of_A, numOfAgents);
+
+		// Set A
+		int[][] A = init_A(numOfAgents, integers, M, length_of_A);
+
+		// Finding the actual CS...
+		for (int s = 0; s <= integers.length - 3; s++)
+			for (int i = 0; i <= M[s].length - 1; i++)
+				CS[s][i] = A[s][M[s][i] - 1];
+		setTheLastTwoCoalitionsInCS(CS, M, A, length_of_A, integers.length);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method is similar to the method: "set_CS_given_its_index" that is
+	 * mentioned above, except that it is used with CS where the agents are
+	 * represented as bits, rather than ints.
+	 */
+	private void set_CS_given_its_index(final int numOfAgents, int[] CS,
+			final long index, final int[] length_of_A, final long[][] increment,
+			final int[] max_first_member_of_M, final long[][] numOfCombinations,
+			final long[] numOfRemovedCombinations,
+			final long[] sumOf_numOfCombinations, final int[] bit) {
+		// Convert the index into a list of indices for every list
+		long[] index_of_M = init_index_of_M(index, integers, increment,
+				max_first_member_of_M, numOfCombinations,
+				numOfRemovedCombinations, sumOf_numOfCombinations);
+
+		// Set M[i] to be the coalition located at: index_of_M[i]
+		int[][] M = init_M(index_of_M, integers, length_of_A, numOfAgents);
+
+		// Set A
+		int[][] A = init_A(numOfAgents, integers, M, length_of_A);
+
+		// Finding the actual CS...
+		for (int s = 0; s <= integers.length - 3; s++) {
+			CS[s] = 0;
+			for (int j1 = 0; j1 <= M[s].length - 1; j1++)
+				CS[s] |= bit[A[s][M[s][j1] - 1]];
+		}
+
+		int remainingAgents = 0;
+		for (int i = length_of_A[integers.length - 2] - 1; i >= 0; i--) {
+			remainingAgents |= bit[A[integers.length - 2][i]];
+		}
+		setTheLastTwoCoalitionsInCS(CS, M[integers.length - 2], A,
+				integers.length, bit);
+	}
+
+	// ******************************************************************************************************
+
+	/**
+	 * This method uses the technique called: "local branch and bound". In more
+	 * detail, For every subset of agents S, we record the best value found so
+	 * far, and that is regardless of the way the agents in S are partitioned.
+	 * 
+	 * For example, given S={a,b,c,d}, and while we are cycling through
+	 * coalition structures in subspace [2,2,4,4], we we might come across the
+	 * following set of coalitions: {{a,b},{c,d},...}, and suppose that
+	 * v({2,3})+v({5,9})=100 then we store in the table the value: 100.
+	 * 
+	 * After that, while cycling through coalition structures in subspace
+	 * [1,1,2,4,4,4], suppose that we come across the following set of
+	 * coalitions: {{a},{c},{b,d},...}, then: * if( v({a})+v({c})+v({b,d}=80 ),
+	 * then there is no need to continue searching the tree of coalitions that
+	 * comes with {{a},{c},{b,d},...} * if( v({a})+v({c})+v({b,d}=120 ), then
+	 * the table is updated, and the value 120 is stored instead of 100.
+	 */
+	private boolean useLocalBranchAndBound(Input input,
+			IDPSolver_whenRunning_ODPIP localBranchAndBoundObject,
+			double[] sumOf_values, int[] sumOf_agents, int s2, int newCoalition,
+			double valueOfNewCoalition) {
+		boolean result = false;
+
+		// if((
+		// localBranchAndBoundObject.getLowerBoundForCurrentAgents(sumOf_agents[s2+1])
+		// > sumOf_values[s2+1] )||(
+		// localBranchAndBoundObject.getLowerBoundForCurrentAgents(newCoalition)
+		// > valueOfNewCoalition ))
+		if ((localBranchAndBoundObject.getValueOfBestPartitionFound(
+				sumOf_agents[s2 + 1]) - sumOf_values[s2 + 1] > 0.00000000005)
+				|| (localBranchAndBoundObject.getValueOfBestPartitionFound(
+						newCoalition) - valueOfNewCoalition > 0.00000000005))
+			result = true;
+
+		// Update the local-branch-and-bound table if necessary
+		// if(
+		// localBranchAndBoundObject.getLowerBoundForCurrentAgents(sumOf_agents[s2+1])
+		// < sumOf_values[s2+1] )
+		if (localBranchAndBoundObject.getValueOfBestPartitionFound(
+				sumOf_agents[s2 + 1]) - sumOf_values[s2 + 1] < -0.00000000005)
+			localBranchAndBoundObject.updateValueOfBestPartitionFound(
+					sumOf_agents[s2 + 1], sumOf_values[s2 + 1]);
+
+		// if( localBranchAndBoundObject.getLowerBoundForCurrentAgents(
+		// newCoalition ) < valueOfNewCoalition )
+		if (localBranchAndBoundObject.getValueOfBestPartitionFound(newCoalition)
+				- valueOfNewCoalition < -0.00000000005)
+			localBranchAndBoundObject.updateValueOfBestPartitionFound(
+					newCoalition, valueOfNewCoalition);
+
+		return (result);
+	}
+}
Index: /ip/src/main/java/geniusweb/ip/mainSolver/Result.java
===================================================================
--- /ip/src/main/java/geniusweb/ip/mainSolver/Result.java	(revision 30)
+++ /ip/src/main/java/geniusweb/ip/mainSolver/Result.java	(revision 30)
@@ -0,0 +1,268 @@
+package geniusweb.ip.mainSolver;
+
+import java.util.Arrays;
+
+import geniusweb.ip.general.Combinations;
+import geniusweb.ip.general.General;
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.ipSolver.IDPSolver_whenRunning_ODPIP;
+import geniusweb.ip.ipSolver.IntegerPartitionGraph;
+
+public class Result {
+	public Result(Input input) // The constructor
+	{
+		totalNumOfCS = Combinations.getNumOfCS(input.numOfAgents);
+		totalNumOfCoalitionsInSearchSpace = Combinations
+				.getNumOfCoalitionsInSearchSpace(input.numOfAgents);
+		dpMaxSizeThatWasComputedSoFar = 1;
+	}
+
+	public int numOfAgents; // the number of agents
+
+	public long totalNumOfCS; // The total number of coalition structures in the
+								// search space
+
+	public long totalNumOfCoalitionsInSearchSpace; // The number of coailtions
+													// in the search space.
+													// Here,
+	// for every CS, we count the number of coalitions in it.
+
+	public long totalNumOfExpansions; // the total number of expansions in the
+										// search space, given the tree-like
+										// representation of different
+										// integer-partition-based subspaces
+
+	public IDPSolver_whenRunning_ODPIP idpSolver_whenRunning_ODPIP;
+
+	// ***************************************************************************
+	// ***** *****
+	// ***** CPLEX results *****
+	// ***** *****
+	// ***************************************************************************
+
+	public long cplexTime; // only relevant when running ILOG's CPLEX
+
+	public double cpleXTime_confidenceInterval; // the error when averaging over
+												// multiple instances
+
+	public int[][] cplexBestCSFound; // only relevant when running ILOG's CPLEX
+
+	public double cplexValueOfBestCSFound; // only relevant when running ILOG's
+											// CPLEX
+
+	public double cpleXValueOfBestCSFound_confidenceInterval; // the error when
+																// averaging
+																// over multiple
+																// instances
+
+	// ***************************************************************************
+	// ***** *****
+	// ***** Dynamic Programming (DP, IDP, or ODP) results *****
+	// ***** *****
+	// ***************************************************************************
+
+	private int dpMaxSizeThatWasComputedSoFar; // only relevant when running DP,
+												// IDP, or ODP
+
+	private int[][] dpBestCSFound; // The best coalition structure found by DP,
+									// IDP, or ODP
+
+	private double dpValueOfBestCSFound; // The value of the best coalition
+											// structure found by DP, IDP, or
+											// ODP
+
+	public long dpTime; // The time required for DP, IDP, or ODP
+
+	public long[] dpTimeForEachSize; // The time required for DP, IDP, or ODP
+										// for each size
+
+	// ***************************************************************************
+	// ***** *****
+	// ***** IP or ODP-IP results *****
+	// ***** *****
+	// ***************************************************************************
+
+	private int[][] ipBestCSFound; // only relevant when running IP or ODP-IP
+
+	private double ipValueOfBestCSFound; // only relevant when running IP or
+											// ODP-IP
+
+	public double ipValueOfBestCS_confidenceInterval; // the error when
+														// averaging over
+														// multiple runs
+
+	public long ipStartTime; // only relevant when running IP or ODP-IP
+
+	public double ipTimeForScanningTheInput_confidenceInterval; // the error
+																// when
+																// averaging
+																// over multiple
+																// runs
+
+	public long ipTime; // only relevant when running IP or ODP-IP
+
+	public double ipTime_confidenceInterval; // the error when averaging over
+												// multiple runs
+
+	public long ipTimeForScanningTheInput; // only relevant when running IP or
+											// ODP-IP
+
+	public long ipNumOfExpansions; // only relevant when running IP or ODP-IP
+
+	public double ipNumOfExpansions_confidenceInterval; // the error when
+														// averaging over
+														// multiple runs
+
+	public double ipUpperBoundOnOptimalValue; // only relevant when running IP
+												// or ODP-IP
+
+	public double ipUpperBoundOnOptimalValue_confidenceInterval; // the error
+																	// when
+																	// averaging
+																	// over
+																	// multiple
+																	// runs
+
+	public double ipLowerBoundOnOptimalValue; // only relevant when running IP
+												// or ODP-IP
+
+	public IntegerPartitionGraph ipIntegerPartitionGraph; // only relevant when
+															// running IP or
+															// ODP-IP
+
+	private double[] max_f; // e.g., max_f[3] is the maximum f value that the
+							// dynamic programming solver has computed for a
+							// coalition of size 4
+
+	// *****************************************************************************************************
+
+	/**
+	 * Initializes the main parameters
+	 */
+	public void initialize() {
+		ipStartTime = System.currentTimeMillis();
+		ipNumOfExpansions = 0;
+		ipValueOfBestCSFound = -1;
+		ipBestCSFound = null;
+		totalNumOfExpansions = 0;
+	}
+
+	// *****************************************************************************************************
+
+	/**
+	 * Only relevant when running DP, IDP, or ODP
+	 * 
+	 * @param CS    the best coalition structure found so far
+	 * @param value the value of the best coalition structure found so far
+	 */
+	public void updateDPSolution(int[][] CS, double value) {
+		if (get_dpValueOfBestCSFound() < value) {
+			set_dpValueOfBestCSFound(value);
+			set_dpBestCSFound(General.copyArray(CS));
+		}
+	}
+
+	/**
+	 * Only relevant when running IP or ODP-IP
+	 * 
+	 * @param CS    the best coalition structure found so far
+	 * @param value the value of the best coalition structure found so far
+	 */
+	public synchronized void updateIPSolution(int[][] CS, double value) {
+		if (get_ipValueOfBestCSFound() <= value) {
+			set_ipValueOfBestCSFound(value);
+			set_ipBestCSFound(General.copyArray(CS));
+		}
+	}
+
+	// *****************************************************************************************************
+
+	public synchronized void set_dpMaxSizeThatWasComputedSoFar(int size) {
+		dpMaxSizeThatWasComputedSoFar = size;
+	}
+
+	public synchronized int get_dpMaxSizeThatWasComputedSoFar() {
+		return dpMaxSizeThatWasComputedSoFar;
+	}
+
+	public synchronized void set_dpBestCSFound(int[][] CS) {
+		dpBestCSFound = General.copyArray(CS);
+	}
+
+	public synchronized int[][] get_dpBestCSFound() {
+		return dpBestCSFound;
+	}
+
+	public synchronized void set_dpValueOfBestCSFound(double value) {
+		dpValueOfBestCSFound = value;
+	}
+
+	public synchronized double get_dpValueOfBestCSFound() {
+		return dpValueOfBestCSFound;
+	}
+
+	public /* synchronized */ void set_ipBestCSFound(int[][] CS) {
+		ipBestCSFound = General.copyArray(CS);
+	}
+
+	public /* synchronized */ int[][] get_ipBestCSFound() {
+		return ipBestCSFound;
+	}
+
+	public /* synchronized */ void set_ipValueOfBestCSFound(double value) {
+		ipValueOfBestCSFound = value;
+	}
+
+	public /* synchronized */ double get_ipValueOfBestCSFound() {
+		return ipValueOfBestCSFound;
+	}
+
+	public void set_max_f(int index, double value) {
+		max_f[index] = value;
+	}
+
+	public double get_max_f(int index) {
+		return (max_f[index]);
+	}
+
+	public void init_max_f(Input input, double[][] maxValueForEachSize) {
+		max_f = new double[input.numOfAgents];
+		for (int i = 0; i < input.numOfAgents; i++)
+			set_max_f(i, 0);
+		for (int i = 0; i < input.numOfAgents; i++) {
+			double value = input.getCoalitionValue((1 << i)); // compute max_f
+																// for
+																// coalitions of
+																// size = 1
+			if (get_max_f(0) < value)
+				set_max_f(0, value);
+		}
+		for (int i = 1; i < input.numOfAgents; i++) // compute max_f for
+													// coalitions of size = i+1
+			set_max_f(i, maxValueForEachSize[i][0]);
+	}
+
+	@Override
+	public String toString() {
+		// ASSUMES IP. Copied form MainFrame#printResultOnGUI.
+		StringBuilder text = new StringBuilder();
+
+		text.append("\nThe time for IP to scan the input (in milliseconds):\n"
+				+ ipTimeForScanningTheInput + "\n");
+		text.append("\nThe total run time  " + " (in milliseconds):\n" + ipTime
+				+ "\n");
+		text.append("\nThe best coalition structure found " + " is:\n"
+				+ Arrays.deepToString(get_ipBestCSFound()) + "\n");
+		text.append("\nThe value of this coalition structure is:\n"
+				+ get_ipValueOfBestCSFound() + "\n");
+		text.append("\nThe number of expansions made by IP:\n"
+				+ ipNumOfExpansions + "\n");
+		text.append(
+				"\nBased on this, the percentage of search-space that was searched  "
+						+ " is:\n" + (double) (ipNumOfExpansions * 100)
+								/ totalNumOfExpansions
+						+ "%\n");
+		return text.toString();
+	}
+
+}
Index: /ip/src/test/java/geniusweb/ip/IpSolverTest.java
===================================================================
--- /ip/src/test/java/geniusweb/ip/IpSolverTest.java	(revision 30)
+++ /ip/src/test/java/geniusweb/ip/IpSolverTest.java	(revision 30)
@@ -0,0 +1,59 @@
+package geniusweb.ip;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import geniusweb.ip.inputOutput.Input;
+import geniusweb.ip.inputOutput.Output;
+import geniusweb.ip.ipSolver.IPSolver;
+import geniusweb.ip.mainSolver.Result;
+
+public class IpSolverTest {
+
+	@Test
+	public void textBookTest() {
+		System.out.println("textbook test");
+		int numOfAgents = 4;
+		double[] coalitionValues = new double[] { 0d, 30d, 40d, 50d, 25d, 60d,
+				55d, 90d, 45d, 80d, 70d, 120d, 80d, 100d, 115d, 140d };
+		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);
+
+		System.out.println(result);
+
+		assertArrayEquals(new int[][] { { 3, 4 }, { 1 }, { 2 } },
+				result.get_ipBestCSFound());
+		assertEquals(150d, result.get_ipValueOfBestCSFound(), 0.00001);
+	}
+
+	@Test
+	public void anotherTest() {
+		// 4 agents; agent2 can be in coalition1 and coalition2;
+		// but there is a minimum coalition size of 2.
+		System.out.println("another test");
+		int numOfAgents = 4;
+		double[] coalitionValues = new double[] { 0d, 0d, 0d, 2d, 0d, 0d, 2d,
+				0d, 0d, 0d, 0d, 0d, 2d, 0d, 0d, 0d };
+		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);
+
+		System.out.println(result);
+		assertArrayEquals(new int[][] { { 3, 4 }, { 1, 2 } },
+				result.get_ipBestCSFound());
+		assertEquals(4d, result.get_ipValueOfBestCSFound(), 0.00001);
+
+	}
+
+}
Index: /issuevalue/pom.xml
===================================================================
--- /issuevalue/pom.xml	(revision 29)
+++ /issuevalue/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>issuevalue</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>
 
@@ -87,5 +87,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -93,5 +93,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -107,11 +107,15 @@
 			</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>
Index: /issuevalue/src/main/java/geniusweb/issuevalue/Domain.java
===================================================================
--- /issuevalue/src/main/java/geniusweb/issuevalue/Domain.java	(revision 29)
+++ /issuevalue/src/main/java/geniusweb/issuevalue/Domain.java	(revision 30)
@@ -73,4 +73,24 @@
 	/**
 	 * 
+	 * @param bid the {@link Bid} to be checked
+	 * @return null if bid is fitting, or a string containing a message
+	 *         explaining why not. A bid is fitting if all issues are in the
+	 *         domain and all issue values are known values.
+	 */
+	public String isFitting(Bid bid) {
+		for (String issue : bid.getIssues()) {
+			if (!(issuesValues.containsKey(issue)))
+				return "bid " + bid + " refers to non-domain issue '" + issue
+						+ "'";
+			if (!issuesValues.get(issue).contains(bid.getValue(issue))) {
+				return "issue '" + issue + "' in bid has illegal value "
+						+ bid.getValue(issue);
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 
 	 * @param bid a Bid
 	 * @return null if this bid is complete, or an error message explaining why
@@ -85,11 +105,5 @@
 					+ issuesValues.keySet() + ")";
 		}
-		for (String issue : getIssues()) {
-			if (!issuesValues.get(issue).contains(bid.getValue(issue))) {
-				return "bid issue " + issue + " has illegal value "
-						+ bid.getValue(issue);
-			}
-		}
-		return null;
+		return isFitting(bid);
 	}
 
Index: /issuevalue/src/test/java/geniusweb/issuevalue/DomainTest.java
===================================================================
--- /issuevalue/src/test/java/geniusweb/issuevalue/DomainTest.java	(revision 29)
+++ /issuevalue/src/test/java/geniusweb/issuevalue/DomainTest.java	(revision 30)
@@ -2,4 +2,5 @@
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
@@ -24,6 +25,10 @@
 
 public class DomainTest extends GeneralTests<Domain> {
+	private static final BigDecimal TWO = new BigDecimal("2");
+	private static final BigDecimal THREE = new BigDecimal("3");
+
 	private static final DiscreteValue I1V2 = new DiscreteValue("i1v2");
 	private static final DiscreteValue I1V1 = new DiscreteValue("i1v1");
+	private static final NumberValue I2V1 = new NumberValue(TWO);
 	private static final String DOMAINNAME = "testdomain";
 	private static final String ISSUE1 = "issue1";
@@ -33,6 +38,8 @@
 	private static final String domainstring = "{\"name\":\"testdomain\",\"issuesValues\":{\"issue2\":{\"range\":[2,3,0.22]},\"issue1\":{\"values\":[\"i1v1\",\"i1v2\"]}}}";
 
-	private static final String jobsstring = "{\"name\":\"jobs\"," + "\"issuesValues\":{"
-			+ "\"lease car\":{\"values\":[\"yes\",\"no\"]}," + "\"permanent contract\":{\"values\":[\"yes\",\"no\"]},"
+	private static final String jobsstring = "{\"name\":\"jobs\","
+			+ "\"issuesValues\":{"
+			+ "\"lease car\":{\"values\":[\"yes\",\"no\"]},"
+			+ "\"permanent contract\":{\"values\":[\"yes\",\"no\"]},"
 			+ "\"career development opportunities\":{\"values\":[\"low\",\"medium\",\"high\"]},"
 			+ "\"fte\":{\"values\":[\"0.6\",\"0.8\",\"1.0\"]},"
@@ -43,7 +50,4 @@
 	private static final ValueSet values1;
 	private static final ValueSet values2;
-	private static final BigDecimal TWO = new BigDecimal("2");
-	private static final BigDecimal THREE = new BigDecimal("3");
-
 	private static final Domain domain, domain1, domain2, domain3;
 
@@ -64,5 +68,6 @@
 		Map<String, ValueSet> issues1 = new HashMap<>();
 		issues1.put(ISSUE1, values1);
-		issues1.put(ISSUE2, new NumberValueSet(BigDecimal.ONE, TWO, new BigDecimal("0.22")));
+		issues1.put(ISSUE2, new NumberValueSet(BigDecimal.ONE, TWO,
+				new BigDecimal("0.22")));
 		domain3 = new Domain(DOMAINNAME, issues1);
 
@@ -71,10 +76,12 @@
 	@Override
 	public List<List<Domain>> getGeneralTestData() {
-		return Arrays.asList(Arrays.asList(domain, domain1), Arrays.asList(domain2), Arrays.asList(domain3));
+		return Arrays.asList(Arrays.asList(domain, domain1),
+				Arrays.asList(domain2), Arrays.asList(domain3));
 	}
 
 	@Override
 	public List<String> getGeneralTestStrings() {
-		return Arrays.asList("Domain.*testdomain.*", "Domain.*testdomain1.*", "Domain.*testdomain.*");
+		return Arrays.asList("Domain.*testdomain.*", "Domain.*testdomain1.*",
+				"Domain.*testdomain.*");
 	}
 
@@ -97,6 +104,7 @@
 		System.out.println(jobsstring);
 		Domain jobsdomain = jackson.readValue(jobsstring, Domain.class);
-		assertEquals(new HashSet<>(Arrays.asList("lease car", "permanent contract", "career development opportunities",
-				"fte", "salary", "work from home")), jobsdomain.getIssues());
+		assertEquals(new HashSet<>(Arrays.asList("lease car",
+				"permanent contract", "career development opportunities", "fte",
+				"salary", "work from home")), jobsdomain.getIssues());
 
 	}
@@ -121,5 +129,6 @@
 	public void testIsCompleteNoIssuesDefined() {
 		Bid bid = mock(Bid.class);
-		assertThat(domain.isComplete(bid), new PatternMatch("Issues in bid .* do not match issues in domain .*"));
+		assertThat(domain.isComplete(bid), new PatternMatch(
+				"Issues in bid .* do not match issues in domain .*"));
 	}
 
@@ -140,5 +149,6 @@
 		// THREE is NOT a possible value in the NumberValueSet.
 		when(bid.getValue(ISSUE2)).thenReturn(new NumberValue(THREE));
-		assertThat(domain.isComplete(bid), new PatternMatch("bid issue issue2 has illegal value 3"));
+		assertThat(domain.isComplete(bid),
+				new PatternMatch("issue 'issue2' in bid has illegal value 3"));
 	}
 
@@ -152,5 +162,6 @@
 		// THREE is NOT a possible value in the NumberValueSet.
 		when(bid.getValue(ISSUE2)).thenReturn(new NumberValue(THREE));
-		assertThat(domain.isComplete(bid), new PatternMatch("Issues in bid .* do not match issues in domain .*"));
+		assertThat(domain.isComplete(bid), new PatternMatch(
+				"Issues in bid .* do not match issues in domain .*"));
 	}
 
@@ -165,3 +176,29 @@
 	}
 
+	@Test
+	public void testIsFittingPartialBid() {
+		Bid bid = new Bid(ISSUE1, I1V1);
+		assertNull(domain.isFitting(bid));
+	}
+
+	@Test
+	public void testIsFittingWrongTypeBid() {
+		Bid bid = new Bid(ISSUE1, I2V1);
+		assertThat(domain.isFitting(bid),
+				new PatternMatch("issue 'issue1' in bid has illegal value 2"));
+	}
+
+	@Test
+	public void testIsFittingNonsenseValue() {
+		Bid bid = new Bid(ISSUE1, new DiscreteValue("nonsense"));
+		assertThat(domain.isFitting(bid), new PatternMatch(
+				"issue 'issue1' in bid has illegal value \"nonsense\""));
+	}
+
+	@Test
+	public void testIsFittingNonsenseIssue() {
+		Bid bid = new Bid("nonsense", I1V1);
+		assertThat(domain.isFitting(bid), new PatternMatch(
+				"bid .* refers to non-domain issue 'nonsense'"));
+	}
 }
Index: /opponentmodel/pom.xml
===================================================================
--- /opponentmodel/pom.xml	(revision 29)
+++ /opponentmodel/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>opponentmodel</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>
 
@@ -100,5 +100,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.2</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -128,14 +128,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: /opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java
===================================================================
--- /opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java	(revision 29)
+++ /opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java	(revision 30)
@@ -58,5 +58,5 @@
 	 * @param resBid the reservation bid. Can be null
 	 */
-	private FrequencyOpponentModel(Domain domain,
+	protected FrequencyOpponentModel(Domain domain,
 			Map<String, Map<Value, Integer>> freqs, BigDecimal total,
 			Bid resBid) {
@@ -87,8 +87,4 @@
 			throw new IllegalStateException("domain is not initialized");
 		}
-		String err = domain.isComplete(bid);
-		if (err != null) {
-			throw new IllegalArgumentException(err);
-		}
 		if (totalBids == BigDecimal.ZERO) {
 			return BigDecimal.ONE;
@@ -97,5 +93,7 @@
 		// Assume all issues have equal weight.
 		for (String issue : domain.getIssues()) {
-			sum = sum.add(getFraction(issue, bid.getValue(issue)));
+			if (bid.getIssues().contains(issue)) {
+				sum = sum.add(getFraction(issue, bid.getValue(issue)));
+			}
 		}
 		return sum.divide(new BigDecimal(bidFrequencies.size()), DECIMALS,
Index: /opponentmodel/src/test/java/geniusweb/opponentmodel/FrequencyOppModelTest.java
===================================================================
--- /opponentmodel/src/test/java/geniusweb/opponentmodel/FrequencyOppModelTest.java	(revision 30)
+++ /opponentmodel/src/test/java/geniusweb/opponentmodel/FrequencyOppModelTest.java	(revision 30)
@@ -0,0 +1,241 @@
+package geniusweb.opponentmodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.DiscreteValueSet;
+import geniusweb.issuevalue.Domain;
+import geniusweb.issuevalue.NumberValue;
+import geniusweb.issuevalue.NumberValueSet;
+import geniusweb.issuevalue.Value;
+import geniusweb.issuevalue.ValueSet;
+import geniusweb.progress.Progress;
+import tudelft.utilities.junit.GeneralTests;
+
+public class FrequencyOppModelTest
+		extends GeneralTests<FrequencyOpponentModel> {
+	private final static String ISS1 = "issue1";
+	private final static String ISS2 = "issue2";
+	private static final DiscreteValue I1V1 = new DiscreteValue("i1v1");
+	private static final DiscreteValue I1V2 = new DiscreteValue("i1v2");
+	private static final DiscreteValue I2V1 = new DiscreteValue("i2v1");
+	private static final DiscreteValue I2V2 = new DiscreteValue("i2v2");
+	private static final DiscreteValue I1V2b = new DiscreteValue("i1v2b");
+	private static final Progress progress = mock(Progress.class);
+	private static final PartyId other = new PartyId("other");
+
+	private static Domain domain, domain2, domain3;
+	private static FrequencyOpponentModel oppModel1, oppModel1b, oppModel2,
+			oppModel3, oppModel4;
+
+	private final static List<List<Bid>> list = new LinkedList<>();
+
+	private final static Bid bid1, bid2, bid3;
+	private static final BigDecimal HALF = new BigDecimal("0.5");
+
+	static {
+		Map<String, ValueSet> issues = new HashMap<>();
+		Collection<DiscreteValue> discretevalues1 = new LinkedList<>();
+		discretevalues1.add(I1V1);
+		discretevalues1.add(I1V2);
+		DiscreteValueSet values1 = new DiscreteValueSet(discretevalues1);
+		issues.put(ISS1, values1);
+		NumberValueSet values2 = new NumberValueSet(BigDecimal.ZERO,
+				BigDecimal.TEN, new BigDecimal("0.3"));
+		issues.put(ISS2, values2);
+		domain = new Domain("test", issues);
+		domain2 = new Domain("test2", issues);
+
+		// slightly different issue1
+		issues = new HashMap<>();
+		discretevalues1 = new LinkedList<>();
+		discretevalues1.add(I1V1);
+		discretevalues1.add(I1V2b);
+		values1 = new DiscreteValueSet(discretevalues1);
+		issues.put(ISS1, values1);
+		values2 = new NumberValueSet(BigDecimal.ZERO, BigDecimal.TEN,
+				new BigDecimal("0.3"));
+		issues.put(ISS2, values2);
+		domain3 = new Domain("test", issues);
+
+		// all bids are for domain
+		Map<String, Value> issuevalues = new HashMap<>();
+		issuevalues.put(ISS1, I1V1);
+		issuevalues.put(ISS2, new NumberValue(new BigDecimal("1.2")));
+		bid1 = new Bid(issuevalues);
+
+		issuevalues.put(ISS1, I1V1);
+		issuevalues.put(ISS2, new NumberValue(new BigDecimal("1.5")));
+		bid2 = new Bid(issuevalues);
+
+		issuevalues.put(ISS1, I1V2);
+		issuevalues.put(ISS2, new NumberValue(new BigDecimal("1.5")));
+		bid3 = new Bid(issuevalues);
+
+		oppModel1 = new FrequencyOpponentModel().with(domain, null);
+		oppModel1b = new FrequencyOpponentModel().with(domain, null);
+		oppModel2 = new FrequencyOpponentModel().with(domain2, null);
+		oppModel3 = new FrequencyOpponentModel().with(domain3, null);
+		oppModel4 = oppModel3.with(new Offer(other, bid1), progress);
+
+	}
+
+	@Override
+	public List<List<FrequencyOpponentModel>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(oppModel1, oppModel1b),
+				Arrays.asList(oppModel2), Arrays.asList(oppModel3),
+				Arrays.asList(oppModel4));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"FrequencyOpponentModel\\[0,\\{issue2=\\{\\}, issue1=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[0,\\{issue2=\\{\\}, issue1=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[0,\\{issue2=\\{\\}, issue1=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[1,\\{issue2=\\{1.2=1\\}, issue1=\\{\"i1v1\"=1\\}\\}\\]");
+	}
+
+	@Test(expected = NullPointerException.class)
+	public void smokeTestNull() {
+		new FrequencyOpponentModel().with((Domain) null, null);
+	}
+
+	@Test
+	public void smokeTest() {
+		new FrequencyOpponentModel().with(domain, null);
+	}
+
+	@Test
+	public void testEmptyModel() {
+		FrequencyOpponentModel oppModel = new FrequencyOpponentModel()
+				.with(domain, null);
+		assertEquals(BigDecimal.ONE, oppModel.getUtility(bid1));
+		assertEquals(BigDecimal.ONE, oppModel.getUtility(bid2));
+	}
+
+	@Test
+	public void testEmptyModelPartialBid() {
+		FrequencyOpponentModel oppModel = new FrequencyOpponentModel()
+				.with(domain, null);
+		Bid bid = new Bid(Collections.emptyMap());
+		assertEquals(BigDecimal.ONE, oppModel.getUtility(bid));
+	}
+
+	@Test
+	public void testPartialUtility() {
+		Map<String, Map<Value, Integer>> freqs = new HashMap<>();
+		Map<Value, Integer> freqs1 = new HashMap<>();
+		Map<Value, Integer> freqs2 = new HashMap<>();
+
+		freqs1.put(I1V1, 2);
+		freqs1.put(I1V2, 0);
+		freqs2.put(I2V1, 1);
+		freqs2.put(I2V2, 1);
+
+		freqs.put(ISS1, freqs1);
+		freqs.put(ISS2, freqs2);
+		BigDecimal total = new BigDecimal("2");
+
+		FrequencyOpponentModel oppModel = new FrequencyOpponentModel(domain,
+				freqs, total, (Bid) null);
+
+		double E = 0.0000001;
+		assertEquals(0.5, oppModel.getUtility(new Bid(ISS1, I1V1)).floatValue(),
+				E);
+		assertEquals(0, oppModel.getUtility(new Bid(ISS1, I1V2)).floatValue(),
+				E);
+		assertEquals(0.25,
+				oppModel.getUtility(new Bid(ISS2, I2V1)).floatValue(), E);
+		assertEquals(0.25,
+				oppModel.getUtility(new Bid(ISS2, I2V2)).floatValue(), E);
+
+	}
+
+	@Test
+	public void testUpdate() {
+		FrequencyOpponentModel oppModel = oppModel1.with(new Offer(other, bid1),
+				progress);
+		assertTrue(BigDecimal.ONE.compareTo(oppModel.getUtility(bid1)) == 0);
+		assertTrue(BigDecimal.ZERO.compareTo(oppModel.getUtility(bid3)) == 0);
+		// bid2 has 1 of 2 issue values same as bid1.
+		assertTrue(HALF.compareTo(oppModel.getUtility(bid2)) == 0);
+	}
+
+	@Test
+	public void testUpdate2() {
+		// bid1 and bid2 both want I1V1. They differ on the number value.
+		// bid3 wants I1V2 but does have the number value from bid2
+		FrequencyOpponentModel oppModel = oppModel1
+				.with(new Offer(other, bid1), progress)
+				.with(new Offer(other, bid2), progress);
+		assertTrue(new BigDecimal("0.75")
+				.compareTo(oppModel.getUtility(bid1)) == 0);
+		assertTrue(new BigDecimal("0.75")
+				.compareTo(oppModel.getUtility(bid2)) == 0);
+		assertTrue(new BigDecimal("0.25")
+				.compareTo(oppModel.getUtility(bid3)) == 0);
+	}
+
+	@Test
+	public void testPartialBidUpdate() {
+		FrequencyOpponentModel oppModel = oppModel1.with(new Offer(other, bid1),
+				progress);
+		Bid partialbid = new Bid(ISS1, I1V1);
+		oppModel.with(new Offer(other, partialbid), progress);
+	}
+
+	@Test
+	public void testGetCounts() {
+		assertEquals(Collections.emptyMap(), oppModel1.getCounts(ISS1));
+		assertEquals(Collections.emptyMap(), oppModel1.getCounts(ISS2));
+
+		Map<String, Value> values1 = new HashMap<>();
+		values1.put(ISS1, I1V1);
+		values1.put(ISS2, I2V1);
+		Offer offer1 = new Offer(other, new Bid(values1));
+
+		FrequencyOpponentModel oppmod = oppModel1.with(offer1, progress);
+
+		assertEquals(1, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(null, oppmod.getCounts(ISS1).get(I1V2));
+		assertEquals(1, oppmod.getCounts(ISS2).get(I2V1).intValue());
+		assertEquals(null, oppmod.getCounts(ISS2).get(I2V2));
+
+		// same identical bid. Counts should go to 2
+		oppmod = oppmod.with(offer1, progress);
+
+		assertEquals(2, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(2, oppmod.getCounts(ISS2).get(I2V1).intValue());
+
+		Map<String, Value> values2 = new HashMap<>();
+		values2.put(ISS1, I1V2);
+		values2.put(ISS2, I2V2);
+		Offer offer2 = new Offer(other, new Bid(values2));
+
+		// Other bid. with value2 for both issues
+		oppmod = oppmod.with(offer2, progress);
+
+		assertEquals(2, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(2, oppmod.getCounts(ISS2).get(I2V1).intValue());
+		assertEquals(1, oppmod.getCounts(ISS1).get(I1V2).intValue());
+		assertEquals(1, oppmod.getCounts(ISS2).get(I2V2).intValue());
+
+	}
+}
Index: /party/pom.xml
===================================================================
--- /party/pom.xml	(revision 29)
+++ /party/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>party</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>
 
@@ -52,10 +52,10 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>listener</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -117,5 +117,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -133,11 +133,15 @@
 			</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>
Index: /party/src/main/java/geniusweb/party/Capabilities.java
===================================================================
--- /party/src/main/java/geniusweb/party/Capabilities.java	(revision 29)
+++ /party/src/main/java/geniusweb/party/Capabilities.java	(revision 30)
@@ -9,4 +9,5 @@
 
 import geniusweb.profile.Profile;
+import geniusweb.references.ProtocolRef;
 
 /**
@@ -26,5 +27,7 @@
 	/**
 	 * 
-	 * @param behaviours the protocols that a Party can handle
+	 * @param behaviours the {@link ProtocolRef} that a Party can handle, as
+	 *                   returned by NegoProtocol.getRef()
+	 * @param profiles   the {@link Profile} classes that Party acan handle
 	 */
 	@JsonCreator
Index: /party/src/main/java/geniusweb/party/DefaultParty.java
===================================================================
--- /party/src/main/java/geniusweb/party/DefaultParty.java	(revision 29)
+++ /party/src/main/java/geniusweb/party/DefaultParty.java	(revision 30)
@@ -34,4 +34,5 @@
 		if (connection != null) {
 			connection.removeListener(this);
+			connection.close();
 			this.connection = null;
 		}
Index: /party/src/main/java/geniusweb/party/Party.java
===================================================================
--- /party/src/main/java/geniusweb/party/Party.java	(revision 29)
+++ /party/src/main/java/geniusweb/party/Party.java	(revision 30)
@@ -3,4 +3,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.Connectable;
+import geniusweb.connection.ConnectionEnd;
 import geniusweb.inform.Inform;
 
@@ -14,7 +15,24 @@
  * your class may also be created only to call the getCapabilities function.
  * 
+ * <h1>Short outline</h1> After the party is created (empty constructor) it is
+ * first connected through a call to connect (from the {@link Connectable}
+ * interface). The party can subscribe to receive {@link Inform} objects using
+ * {@link ConnectionEnd#addListener(tudelft.utilities.listener.Listener)}. The
+ * party can also send its actions using {@link ConnectionEnd#send(Object)}.
+ * 
+ * The {@link ConnectionEnd} allows the party to receive Inform objects and send
+ * action objects.
+ * 
+ * Incoming Inform objects (eg "Settings" containing the profile, and
+ * "yourturn") are notified to the party throught he notifyChange() call to the
+ * party.
+ * <p>
+ * Outgoing Action objects (eg "Offer") are sent using the send() command.
+ * 
+ * Most parties extend {@link DefaultParty} to handle these connection details.
  * <p>
  * Technical details: normally a Party will be spawned inside a PartiesFactory,
- * and incoming/outgoing calls are routed through a websocket there.
+ * and incoming/outgoing calls are routed through a websocket there. But in
+ * testing the Connectable is often mocked.
  * 
  */
Index: /pom.xml
===================================================================
--- /pom.xml	(revision 29)
+++ /pom.xml	(revision 30)
@@ -32,4 +32,5 @@
 		<module>opponentmodel</module>
 		<module>boa</module>
+		<module>ip</module>
 	</modules>
 </project>
Index: /profile/pom.xml
===================================================================
--- /profile/pom.xml	(revision 29)
+++ /profile/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>profile</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>
 
@@ -42,5 +42,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 
@@ -90,5 +90,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -118,11 +118,15 @@
 			</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>
Index: /profile/src/main/java/geniusweb/profile/DefaultProfile.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/DefaultProfile.java	(revision 29)
+++ /profile/src/main/java/geniusweb/profile/DefaultProfile.java	(revision 30)
@@ -27,4 +27,11 @@
 		this.domain = domain;
 		this.reservationbid = reservationbid;
+		if (reservationbid != null) {
+			String message = domain.isFitting(reservationbid);
+			if (message != null)
+				throw new IllegalArgumentException(
+						"reservationbid is not fitting domain: " + message);
+		}
+
 	}
 
@@ -46,6 +53,6 @@
 	/**
 	 * 
-	 * @return string of values contained in here. Useful to make derived toString
-	 *         functions
+	 * @return string of values contained in here. Useful to make derived
+	 *         toString functions
 	 */
 	@JsonIgnore
@@ -60,5 +67,6 @@
 		result = prime * result + ((domain == null) ? 0 : domain.hashCode());
 		result = prime * result + ((name == null) ? 0 : name.hashCode());
-		result = prime * result + ((reservationbid == null) ? 0 : reservationbid.hashCode());
+		result = prime * result
+				+ ((reservationbid == null) ? 0 : reservationbid.hashCode());
 		return result;
 	}
Index: /profile/src/main/java/geniusweb/profile/Profile.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/Profile.java	(revision 29)
+++ /profile/src/main/java/geniusweb/profile/Profile.java	(revision 30)
@@ -41,4 +41,5 @@
 	 *         of a "reservation value" and is more general. If null, there is
 	 *         no reservation bid and any agreement is better than no agreement.
+	 *         This bid can be partial.
 	 * 
 	 */
Index: /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java	(revision 29)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java	(revision 30)
@@ -1,2 +1,3 @@
+
 package geniusweb.profile.utilityspace;
 
Index: /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java	(revision 29)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java	(revision 30)
@@ -125,4 +125,10 @@
 					+ weights.values() + ") must be 1");
 		}
+		if (resBid != null) {
+			String message = domain.isFitting(resBid);
+			if (message != null)
+				throw new IllegalArgumentException(
+						"reservationbid is not fitting domain: " + message);
+		}
 	}
 
Index: /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveSerializationTest.java
===================================================================
--- /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveSerializationTest.java	(revision 29)
+++ /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveSerializationTest.java	(revision 30)
@@ -38,6 +38,6 @@
 	private DiscreteValue i1v1 = new DiscreteValue("issue1value1");
 	private DiscreteValue i1v2 = new DiscreteValue("issue1value2");
-	private NumberValue i2v1 = new NumberValue("0.36");
-	private NumberValue i2v2 = new NumberValue("0.71");
+	private NumberValue i2v1 = new NumberValue("15");
+	private NumberValue i2v2 = new NumberValue("16");
 	private Domain domain;
 
@@ -50,5 +50,5 @@
 	private LinearAdditiveUtilitySpace space;
 
-	private String serialized = "{\"LinearAdditiveUtilitySpace\":{\"domain\":{\"name\":\"test\",\"issuesValues\":{\"issue2\":{\"range\":[12,18,1]},\"issue1\":{\"values\":[\"issue1value1\",\"issue1value2\"]}}},\"name\":\"testprofile\",\"issueUtilities\":{\"issue2\":{\"numberutils\":{\"lowValue\":12,\"lowUtility\":0.3,\"highValue\":18,\"highUtility\":0.6}},\"issue1\":{\"discreteutils\":{\"valueUtilities\":{\"issue1value1\":0.2,\"issue1value2\":0.3}}}},\"issueWeights\":{\"issue2\":0.4,\"issue1\":0.6},\"reservationBid\":{\"issuevalues\":{\"issue2\":0.36,\"issue1\":\"issue1value2\"}}}}";
+	private String serialized = "{\"LinearAdditiveUtilitySpace\":{\"domain\":{\"name\":\"test\",\"issuesValues\":{\"issue2\":{\"range\":[12,18,1]},\"issue1\":{\"values\":[\"issue1value1\",\"issue1value2\"]}}},\"name\":\"testprofile\",\"issueUtilities\":{\"issue2\":{\"numberutils\":{\"lowValue\":12,\"lowUtility\":0.3,\"highValue\":18,\"highUtility\":0.6}},\"issue1\":{\"discreteutils\":{\"valueUtilities\":{\"issue1value1\":0.2,\"issue1value2\":0.3}}}},\"issueWeights\":{\"issue2\":0.4,\"issue1\":0.6},\"reservationBid\":{\"issuevalues\":{\"issue2\":15,\"issue1\":\"issue1value2\"}}}}";
 
 	private String jobsjson = "{\"LinearAdditiveUtilitySpace\":{\"issueUtilities\":{"
Index: /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveTest.java
===================================================================
--- /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveTest.java	(revision 29)
+++ /profile/src/test/java/geniusweb/profile/utilityspace/LinearAdditiveTest.java	(revision 30)
@@ -25,4 +25,5 @@
 import geniusweb.issuevalue.DiscreteValueSet;
 import geniusweb.issuevalue.Domain;
+import geniusweb.issuevalue.NumberValue;
 import geniusweb.issuevalue.Value;
 import geniusweb.issuevalue.ValueSet;
@@ -171,7 +172,4 @@
 	}
 
-	/**
-	 * Empty profile is not allowed since the weights then don't sum up to 1
-	 */
 	@Test
 	public void constructorOneIssueTest() {
@@ -181,5 +179,5 @@
 		weightset.put(ISS1, BigDecimal.ONE);
 		new LinearAdditiveUtilitySpace(smalldomain, NAME, utilset, weightset,
-				reservationBid);
+				null);
 	}
 
@@ -208,5 +206,5 @@
 		weightset.put(ISS1, BigDecimal.ONE);
 		LinearAdditiveUtilitySpace space = new LinearAdditiveUtilitySpace(
-				smalldomain, NAME, utilset, weightset, reservationBid);
+				smalldomain, NAME, utilset, weightset, null);
 	}
 
@@ -278,3 +276,22 @@
 
 	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testResBidWithNonsenseIssue() {
+		Bid resBid = new Bid("nonsense", i1v1);
+		new LinearAdditiveUtilitySpace(domain, NAME, utils, weights, resBid);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testResBidWithWrongValueType() {
+		Bid resBid = new Bid(ISS1, new NumberValue(BigDecimal.ZERO));
+		new LinearAdditiveUtilitySpace(domain, NAME, utils, weights, resBid);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testResBidWithNonsenseValue() {
+		Bid resBid = new Bid(ISS1, new DiscreteValue("nonsense"));
+		new LinearAdditiveUtilitySpace(domain, NAME, utils, weights, resBid);
+	}
+
 }
Index: /profileconnection/pom.xml
===================================================================
--- /profileconnection/pom.xml	(revision 29)
+++ /profileconnection/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>profileconnection</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>
 
@@ -38,5 +38,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 
@@ -94,5 +94,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.2</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -122,11 +122,15 @@
 			</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>
Index: /profileconnection/src/main/java/geniusweb/profileconnection/FileProfileConnector.java
===================================================================
--- /profileconnection/src/main/java/geniusweb/profileconnection/FileProfileConnector.java	(revision 29)
+++ /profileconnection/src/main/java/geniusweb/profileconnection/FileProfileConnector.java	(revision 30)
@@ -37,3 +37,7 @@
 	}
 
+	@Override
+	public void close() {
+	}
+
 }
Index: /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java
===================================================================
--- /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java	(revision 29)
+++ /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java	(revision 30)
@@ -18,3 +18,11 @@
 	 */
 	public Profile getProfile() throws IOException;
+
+	/**
+	 * This must be called when the user is finished using the interface. The
+	 * interface can not be used after calling this. This function allows
+	 * implementors to release the streams and listeners. Not calling this after
+	 * use may cause resource leaks.
+	 */
+	public void close();
 }
Index: /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java
===================================================================
--- /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java	(revision 29)
+++ /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java	(revision 30)
@@ -7,6 +7,8 @@
 import javax.websocket.ClientEndpoint;
 import javax.websocket.DeploymentException;
+import javax.websocket.OnClose;
 import javax.websocket.OnError;
 import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
 import javax.websocket.Session;
 import javax.websocket.WebSocketContainer;
@@ -29,4 +31,5 @@
 	private final URI uri; // to log possible errors
 	private Profile profile = null;
+	private Session session;
 
 	/**
@@ -58,6 +61,18 @@
 	}
 
+	@OnOpen
 	public void onOpen(Session session) {
+		this.session = session;
 		logger.log(Level.FINE, "Connected: " + session.getBasicRemote());
+	}
+
+	@OnClose
+	public void onClose() {
+		if (session != null) {
+			logger.log(Level.FINE,
+					"Closed websocket: " + session.getBasicRemote());
+			this.session = null;
+			// remove listeners seems not needed
+		}
 	}
 
@@ -94,3 +109,16 @@
 	}
 
+	@Override
+	public void close() {
+		if (session != null) {
+			try {
+				session.close();
+			} catch (IOException e) {
+				logger.log(Level.WARNING,
+						"Falled to close websocket connection: "
+								+ session.getBasicRemote());
+			}
+		}
+	}
+
 }
Index: /profileconnection/src/test/resources/testprofile.json
===================================================================
--- /profileconnection/src/test/resources/testprofile.json	(revision 29)
+++ /profileconnection/src/test/resources/testprofile.json	(revision 30)
@@ -37,6 +37,6 @@
 				"reservationBid": {
 			"issuevalues": {
-				"issue1": "issuevalue1",
-				"issue2": "12"
+				"issue1": "issue1value1",
+				"issue2": 12
 			}
 		}
Index: /protocol/pom.xml
===================================================================
--- /protocol/pom.xml	(revision 29)
+++ /protocol/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>protocol</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>
 
@@ -56,15 +56,15 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>logging</artifactId>
-			<version>1.0.0</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>listener</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
@@ -114,5 +114,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.2</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -142,11 +142,15 @@
 			</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>
Index: otocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java	(revision 29)
+++ 	(revision )
@@ -1,30 +1,0 @@
-package geniusweb.protocol.session;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import geniusweb.references.PartyWithParameters;
-import geniusweb.references.PartyWithProfile;
-import geniusweb.references.ProfileRef;
-
-/**
- * Team that contains just 1 party.
- */
-public class OnePartyTeam extends PartyWithProfile
-		implements TeamOfPartiesAndProfiles {
-
-	@JsonCreator
-	public OnePartyTeam(@JsonProperty("party") PartyWithParameters party,
-			@JsonProperty("profile") ProfileRef profile) {
-		super(party, profile);
-	}
-
-	@Override
-	public List<PartyWithProfile> getAllParties() {
-		return Arrays.asList(this);
-	}
-
-}
Index: /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 30)
@@ -8,6 +8,8 @@
 import geniusweb.protocol.session.amop.AMOPSettings;
 import geniusweb.protocol.session.mopac.MOPACSettings;
+import geniusweb.protocol.session.mopac2.MOPAC2Settings;
 import geniusweb.protocol.session.saop.SAOPSettings;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
+import geniusweb.protocol.tournament.allpermutations.Team;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
@@ -19,13 +21,21 @@
 		@JsonSubTypes.Type(value = SHAOPSettings.class),
 		@JsonSubTypes.Type(value = AMOPSettings.class),
-		@JsonSubTypes.Type(value = MOPACSettings.class) })
+		@JsonSubTypes.Type(value = MOPACSettings.class),
+		@JsonSubTypes.Type(value = MOPAC2Settings.class) })
 public interface SessionSettings extends NegoSettings {
 
 	/**
-	 * Get participants
+	 * Get the {@link Team}s.
 	 * 
 	 * @return list of {@link PartyWithProfile} items.
 	 */
-	List<TeamOfPartiesAndProfiles> getTeams();
+	List<TeamInfo> getTeams();
+
+	/**
+	 * @return the size of each {@link Team}. Usually 1 (SAOP etc) or sometimes
+	 *         2 (SHAOP). Used eg by tournament runner to determine proper team
+	 *         construction.
+	 */
+	Integer getTeamSize();
 
 	/**
@@ -33,8 +43,8 @@
 	 * for tournament auto-configuration of sessions.
 	 * 
-	 * @param partyprofteam the {@link TeamOfPartiesAndProfiles} to be added
+	 * @param partyprofteam the {@link TeamInfo} to be added
 	 * @return new modified SessionSettings object
 	 */
-	SessionSettings with(TeamOfPartiesAndProfiles partyprofteam);
+	SessionSettings with(TeamInfo partyprofteam);
 
 	/**
Index: /protocol/src/main/java/geniusweb/protocol/session/TeamInfo.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/TeamInfo.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/TeamInfo.java	(revision 30)
@@ -0,0 +1,88 @@
+package geniusweb.protocol.session;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+
+/**
+ * Contains a functional team, eg in SHAOP a team would be a SHAOP together with
+ * his COB partner. In SAOP this is just a SAOP party.
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+public class TeamInfo {
+
+	private List<PartyWithProfile> parties;
+
+	/**
+	 * Convenient constructor if team is only 1 party and profile
+	 * 
+	 * @param party   the {@link PartyWithParameters}
+	 * @param profile the ProfileRef to be used by party
+	 */
+	public TeamInfo(PartyWithParameters party, ProfileRef profile) {
+		this(Arrays.asList(new PartyWithProfile(party, profile)));
+	}
+
+	@JsonCreator
+	public TeamInfo(@JsonProperty("teams") List<PartyWithProfile> teams) {
+		this.parties = teams;
+	}
+
+	/**
+	 * 
+	 * @return the list of all parties involved, which is the parties in all
+	 *         teams combined.
+	 */
+	public List<PartyWithProfile> getAllParties() {
+		return Collections.unmodifiableList(parties);
+	}
+
+	/**
+	 * 
+	 * @return the number of parties in the team.
+	 */
+	public Integer getSize() {
+		return parties.size();
+	}
+
+	@Override
+	public String toString() {
+		return "TeamInfo[" + parties + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((parties == null) ? 0 : parties.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;
+		TeamInfo other = (TeamInfo) obj;
+		if (parties == null) {
+			if (other.parties != null)
+				return false;
+		} else if (!parties.equals(other.parties))
+			return false;
+		return true;
+	}
+}
Index: otocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 29)
+++ 	(revision )
@@ -1,33 +1,0 @@
-package geniusweb.protocol.session;
-
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-
-import geniusweb.protocol.session.shaop.ShaopTeam;
-import geniusweb.references.PartyWithProfile;
-
-/**
- * Contains a functional team, eg in SHAOP a team would be a SHAOP together with
- * his COB partner. In SAOP this is just a SAOP party.
- * 
- * This is needed to specify the general SessionSettings functionality in a
- * sufficient general way
- *
- */
-@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
-// we do not use JsonTypeInfo because we always use a specific Team within a protocol
-// and do not (de)serialize individual TeamOfPartiesAndProfiles.
-//@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
-@JsonSubTypes({ @JsonSubTypes.Type(value = OnePartyTeam.class),
-		@JsonSubTypes.Type(value = ShaopTeam.class) })
-public interface TeamOfPartiesAndProfiles {
-	/**
-	 * 
-	 * @return the list of all parties involved, which is the parties in all
-	 *         teams combined.
-	 */
-	List<PartyWithProfile> getAllParties();
-}
Index: /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 30)
@@ -10,8 +10,7 @@
 
 import geniusweb.deadline.Deadline;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
@@ -21,5 +20,5 @@
  */
 public class AMOPSettings implements SessionSettings {
-	private final List<OnePartyTeam> participants;
+	private final List<TeamInfo> participants;
 	private final Deadline deadline;
 
@@ -34,5 +33,5 @@
 	@JsonCreator
 	public AMOPSettings(
-			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("participants") List<TeamInfo> participants,
 			@JsonProperty("deadline") Deadline deadline) {
 		this.participants = participants;
@@ -57,5 +56,5 @@
 
 	@Override
-	public List<TeamOfPartiesAndProfiles> getTeams() {
+	public List<TeamInfo> getTeams() {
 		return Collections.unmodifiableList(participants);
 	}
@@ -114,13 +113,18 @@
 
 	@Override
-	public SessionSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof OnePartyTeam))
+	public SessionSettings with(TeamInfo team) {
+		if (team.getSize() != 1)
 			throw new IllegalArgumentException(
-					"Added party must be AmopPartyWithProfile but got "
-							+ party);
-		List<OnePartyTeam> newparts = new LinkedList<>(participants);
-		newparts.add((OnePartyTeam) party);
+					"Added team must be just 1 party but got " + team);
+		List<TeamInfo> newparts = new LinkedList<>(
+				participants);
+		newparts.add(team);
 		return new AMOPSettings(newparts, deadline);
 	}
 
+	@Override
+	public Integer getTeamSize() {
+		return 1;
+	}
+
 }
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 30)
@@ -10,7 +10,6 @@
 
 import geniusweb.deadline.Deadline;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.PartyWithProfile;
 import geniusweb.voting.VotingEvaluator;
@@ -22,5 +21,5 @@
  */
 public class MOPACSettings implements SessionSettings {
-	private final List<OnePartyTeam> participants;
+	private final List<TeamInfo> participants;
 	private final Deadline deadline;
 	private final VotingEvaluator votingevaluator;
@@ -38,5 +37,5 @@
 	@JsonCreator
 	public MOPACSettings(
-			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("participants") List<TeamInfo> participants,
 			@JsonProperty("deadline") Deadline deadline,
 			@JsonProperty("votingevaluator") VotingEvaluator votingeval) {
@@ -61,5 +60,5 @@
 
 	@Override
-	public List<TeamOfPartiesAndProfiles> getTeams() {
+	public List<TeamInfo> getTeams() {
 		return Collections.unmodifiableList(participants);
 	}
@@ -88,10 +87,10 @@
 
 	@Override
-	public MOPACSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof OnePartyTeam))
+	public MOPACSettings with(TeamInfo team) {
+		if (team.getSize() != 1)
 			throw new IllegalArgumentException(
-					"Added party must be OnePartyTeam but got " + party);
-		List<OnePartyTeam> newparts = new LinkedList<>(participants);
-		newparts.add((OnePartyTeam) party);
+					"Added party must have one party but got " + team);
+		List<TeamInfo> newparts = new LinkedList<>(participants);
+		newparts.add(team);
 		return new MOPACSettings(newparts, deadline, votingevaluator);
 	}
@@ -101,4 +100,9 @@
 		return "MOPACSettings[" + participants + "," + deadline + ","
 				+ votingevaluator.getClass().getSimpleName() + "]";
+	}
+
+	@Override
+	public Integer getTeamSize() {
+		return 1;
 	}
 
@@ -147,5 +151,9 @@
 	 */
 	private void checkTeams() {
-		for (OnePartyTeam party : participants) {
+		for (TeamInfo team : participants) {
+			if (team.getSize() != 1)
+				throw new IllegalArgumentException(
+						"All teams must be size 1 but found " + team);
+			PartyWithProfile party = team.getAllParties().get(0);
 			Object power = party.getParty().getParameters().get("power");
 			if (power != null) {
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2.java	(revision 30)
@@ -0,0 +1,382 @@
+package geniusweb.protocol.session.mopac2;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.deadline.Deadline;
+import geniusweb.events.ProtocolEvent;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.Voting;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.ProgressFactory;
+import geniusweb.protocol.CurrentNegoState;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.mopac.phase.Phase;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+/**
+ * 
+ * All parties are first sent the {@link SessionSettings}. Only parties
+ * specified initially in the settings do participate (no late joining in)
+ * 
+ * <h1>parameter</h1> MOPAC parties can receive a parameter <code>power</code>
+ * containing a Integer. This parameter is picked up by this protocol, for
+ * handling the agreement extraction from the offers. Power=1 for parties that
+ * do not have the power parameter.
+ * <h1>Protocol steps</h1>
+ * 
+ * <ol>
+ * <li>The protocol tries to start all parties. If not all parties start, the
+ * parties are freed up and another attempt is done to start all parties some
+ * time later.
+ * <li>The session deadline clock now starts ticking.
+ * <li>remainingparties are sent their settings.
+ * <li>Loop until {@link Deadline} is reached or |remainingparties| &le; 2:
+ * <ol>
+ * <li>protocol sends {@link YourTurn} to all remainingparties. Each party now
+ * must submit an {@link Offer} within {@link Phase#PHASE_MAXTIME} seconds. If a
+ * party fails to submit it is removed from from remainingparties.
+ * <li>protocol sends a {@link Voting} containing a List of Bid containing all
+ * received {@link Bid}s. Each party must place his {@link Votes} within the
+ * provided deadline. If a party does not submit, it is terminated and removed
+ * from remainingparties. Previous votes for the same bid do not count. But see
+ * {@link Agreements}. A party that misbehaves after submitting its vote is
+ * removed but it Votes remain standing.
+ * <li>protocol sends to all remainingparties a OptIn containing all received
+ * votes. Each party now must submit again a {@link Votes} object within the
+ * deadline. This new Votes must equal or extend the previous Votes of the
+ * party.
+ * <li>The protocol uses the {@link Phase#getEvaluator()} to determine the
+ * agreements from the votes. If there are agreements, the parties that placed
+ * these votes reached an agreement. They are moved to the agreement set and
+ * removed from the remainingparties.
+ * </ol>
+ * <li>Any remaining parties are sent a {@link Finished} object without
+ * agreement bid and are terminated.
+ * </ol>
+ * <p>
+ * A party can send {@link EndNegotiation} at any time to remove itself from the
+ * negotiation. The other parties may continue anyawy without him.
+ * <p>
+ * Parties receive the {@link Finished} information at the very end when all
+ * agreements have been collected.
+ * <p>
+ * This logs to "Protocol" logger if there are issues
+ * <p>
+ * This object is mutable: the internal state changes as parties interact with
+ * the protocol.
+ * <p>
+ * Threadsafe: all entrypoints are synhronized.
+ */
+public class MOPAC2 extends DefaultListenable<ProtocolEvent>
+		implements SessionProtocol {
+	/**
+	 * Tech notes. 0. Everything here is phase driven and runs due to callback
+	 * from parties. 1. Connect all parties. 2. If any party fails at any time,
+	 * add it to exceptions list in the state 3. Run timer to check the phase at
+	 * possible end time. The timer just checks the state, and therefore it does
+	 * no harm if it is double checked. 4. We run phases always with either
+	 * PHASE_TIME or time to the global deadline, whichever comes first.
+	 * Therefore phases naturally end at end of nego. 5. Phases must have at
+	 * least {@link #MINDURATION} otherwise it makes no sense to even start it.
+	 */
+	private static final int MINDURATION = 100;
+	private static final int MIN_SLEEP_TIME = 1000;
+	private static final int MAX_SLEEP_TIME = 60000;
+	private static final ProtocolRef MOPAC2 = new ProtocolRef("MOPAC2");
+
+	private final Reporter log;
+	private MOPAC2State state = null; // mutable!
+	/**
+	 * the existing party connections. we assume ownership of this so it should
+	 * not be modified although connections may of course break. mutable!
+	 */
+	private ProtocolToPartyConnections connections = new ProtocolToPartyConnections(
+			Collections.emptyList());
+
+	/**
+	 * Set to true after all is done: we sent final outcomes. This is needed
+	 * because we can't tell from state what we did at the very end.
+	 */
+	private boolean finished = false;
+	private Timer timer = null;
+
+	/**
+	 * 
+	 * @param state  normally the initial state coming from SAOPSettings
+	 * @param logger the {@link Reporter} to use
+	 */
+	public MOPAC2(MOPAC2State state, Reporter logger) {
+		if (state == null) {
+			throw new NullPointerException("state must be not null");
+		}
+		if (state.getSettings().getDeadline().getDuration() < MINDURATION) {
+			throw new IllegalArgumentException(
+					"Duration must be at least " + MINDURATION);
+		}
+		if (logger == null) {
+			throw new NullPointerException("Logger must be not null");
+		}
+		this.log = logger;
+		this.state = state;
+	}
+
+	@Override
+	public synchronized void start(
+			ProtocolToPartyConnFactory connectionfactory) {
+		try {
+			connect(connectionfactory);
+		} catch (Throwable e) {
+			/** We can't {@link #handleError} yet. FIXME */
+			throw new RuntimeException("Failed to connect", e);
+		}
+		long now = System.currentTimeMillis();
+		state = state.initPhase(
+				ProgressFactory.create(state.getSettings().getDeadline(), now),
+				now);
+		setupParties(now);
+		startPhase(now);
+
+	}
+
+	@Override
+	public String getDescription() {
+		return "All parties get YourTurn. They now can submit their Offer within 30 seconds. "
+				+ "Next they receive a Voting. They can now submit their Votes."
+				+ "Then they receive a a OptIn, now they can widen up their Votes."
+				+ "If one of their Vote succeeds, they finish with an agreement. "
+				+ "The VotingEvaluator setting determines the exact voting behaviour "
+				+ "and if this process repeats";
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party)
+			throws IllegalStateException {
+		throw new IllegalStateException(
+				"Dynamic joining a negotiation is not supported in AMOP");
+	}
+
+	@Override
+	public MOPAC2State getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return MOPAC2;
+	}
+
+	/*******************************************************************
+	 * private functions. Some are protected only, for testing purposes
+	 ********************************************************************/
+	/**
+	 * step 1 in protocol: connect all involved parties and start the clock.
+	 * This always "succeeds" with a valid (but possibly final) state
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @param connectionfactory the connectionfactory for making party
+	 *                          connections
+	 * 
+	 * @throws InterruptedException if the connection procedure is unterrupted
+	 * 
+	 * @throws IOException          if this fails to properly conect to the
+	 *                              parties, eg interrupted or server not
+	 *                              responding..
+	 */
+	protected synchronized void connect(
+			ProtocolToPartyConnFactory connectionfactory)
+			throws InterruptedException, IOException {
+		List<PartyWithProfile> participants = state.getSettings()
+				.getAllParties();
+		List<Reference> parties = participants.stream()
+				.map(parti -> (parti.getParty().getPartyRef()))
+				.collect(Collectors.toList());
+		log.log(Level.INFO, "MOPAC connect " + parties);
+		List<ProtocolToPartyConn> newconnections = null;
+		while (newconnections == null) {
+			try {
+				newconnections = connectionfactory.connect(parties);
+			} catch (NoResourcesNowException e) {
+				long waitms = e.getLater().getTime()
+						- System.currentTimeMillis();
+				log.log(Level.INFO,
+						"No resources available to run session, waiting"
+								+ waitms);
+				Thread.sleep(Math.min(MAX_SLEEP_TIME,
+						Math.max(MIN_SLEEP_TIME, waitms)));
+			}
+		}
+		for (int i = 0; i < participants.size(); i++) {
+			ProtocolToPartyConn conn = newconnections.get(i);
+			connections = connections.with(conn);
+			state = state.with(conn.getParty(), participants.get(i));
+		}
+	}
+
+	/**
+	 * step 2 in protocol: listen to connections and send settings to the
+	 * parties.
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @param now the current time.
+	 */
+	protected synchronized void setupParties(long now) {
+		for (ProtocolToPartyConn conn : connections) {
+			conn.addListener(action -> actionRequest(conn, action, now));
+		}
+		for (ProtocolToPartyConn connection : connections) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				state = state.with(new ProtocolException("Failed to initialize",
+						connection.getParty()));
+			}
+		}
+	}
+
+	/**
+	 * Send the {@link Inform} for the current phase to the remaining parties
+	 * and start the time-out checker
+	 */
+	private void startPhase(long now) {
+		Inform info = state.getPhase().getInform();
+		for (PartyId pid : state.getPhase().getPartyStates().getNotYetActed()) {
+			try {
+				connections.get(pid).send(info);
+			} catch (IOException e) {
+				state = state.with(new ProtocolException(
+						"Party seems to have disconnected", pid, e));
+			}
+		}
+		startTimer(1 + state.getPhase().getDeadline() - now);
+	}
+
+	/**
+	 * Check at given deadline if we already ended the phase.
+	 * 
+	 * @param deadn the deadline
+	 */
+	private void startTimer(long deadln) {
+		if (timer != null)
+			throw new IllegalStateException("Timer is still running!");
+		timer = new Timer("deadlinetimer");
+		timer.schedule(new TimerTask() {
+			@Override
+			public void run() {
+				checkEndPhase(System.currentTimeMillis());
+			}
+		}, deadln);
+
+	}
+
+	/**
+	 * Inform a party about its settings
+	 * 
+	 * @param connection
+	 * @throws IOException if party got disconnected
+	 */
+	private synchronized void sendSettings(ProtocolToPartyConn connection)
+			throws IOException {
+		PartyId partyid = connection.getParty();
+		ProfileRef profile = state.getPartyProfiles().get(partyid).getProfile();
+		Parameters params = state.getPartyProfiles().get(partyid).getParty()
+				.getParameters();
+		connection.send(new Settings(connection.getParty(), profile, getRef(),
+				state.getProgress(), params));
+	}
+
+	/**
+	 * This is called when one of the {@link ProtocolToPartyConn}s does an
+	 * action. Synchronized so that we always handle only 1 action at a time.
+	 * 
+	 * @param partyconn the connection on which the action came in
+	 * @param action    the {@link Action} taken by some party
+	 * @param now       current time
+	 */
+	protected synchronized void actionRequest(
+			final ProtocolToPartyConn partyconn, final Action action,
+			long now) {
+		if (finished)
+			return;
+		state = state.with(partyconn.getParty(), action, now);
+		checkEndPhase(System.currentTimeMillis());
+	}
+
+	/**
+	 * The current phase may be completed. We check, because it may already been
+	 * handled. Proceed to next phase as needed. Reset the deadline timers and
+	 * inform parties. Increase progress if necessary. Must only be called
+	 * through {@link #endPhase} to ensure this is called only once.
+	 * 
+	 */
+	private synchronized void checkEndPhase(long now) {
+		if (!state.getPhase().isFinal(now))
+			return;
+		// phase indeed ended. Check what's up.
+		if (timer != null) {
+			timer.cancel();
+			timer = null;
+		}
+		state = state.finishPhase();
+		if (state.isFinal(now)) {
+			endNegotiation();
+			return;
+		}
+
+		state = state.nextPhase(now);
+
+		startPhase(now);
+	}
+
+	/**
+	 * To be called when we reach final state. Must only be called if
+	 * {@link MOPAC2State#isFinal(long)}. Send finished info to all parties.
+	 * Double calls are automatically ignored using the global finished flag.
+	 */
+	private synchronized void endNegotiation() {
+		if (finished)
+			return;
+		finished = true;
+		Finished info = new Finished(state.getAgreements());
+		for (ProtocolToPartyConn conn : connections) {
+			try {
+				conn.send(info);
+				conn.close();
+			} catch (Exception e) {
+				log.log(Level.INFO, "Failed to send Finished to " + conn, e);
+			}
+		}
+		notifyListeners(new CurrentNegoState(state));
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2Settings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2Settings.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2Settings.java	(revision 30)
@@ -0,0 +1,175 @@
+package geniusweb.protocol.session.mopac2;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.VotingEvaluator;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Settings for MOPAC negotiation. in MOPAC, each party may get a "power"
+ * parameter containing an natural number &le;1.
+ */
+public class MOPAC2Settings implements SessionSettings {
+	private final List<TeamInfo> participants;
+	private final Deadline deadline;
+	private final VotingEvaluatorWithValue votingevaluator;
+
+	/**
+	 * 
+	 * @param participants the list of {@link PartyWithProfile} in clockwise
+	 *                     order. There must be at least 2 to run the MOPAC
+	 *                     protocol. This is not tested in the constructor
+	 *                     because this can be initialized with less, for use in
+	 *                     TournamentSettings.
+	 * @param deadline     the {@link Deadline} for the negotiation
+	 * @param votingeval   the {@link VotingEvaluator} to use.
+	 */
+	@JsonCreator
+	public MOPAC2Settings(
+			@JsonProperty("participants") List<TeamInfo> participants,
+			@JsonProperty("deadline") Deadline deadline,
+			@JsonProperty("votingevaluator") VotingEvaluatorWithValue votingeval) {
+		this.participants = participants;
+		this.deadline = deadline;
+		if (participants == null || deadline == null || votingeval == null)
+			throw new IllegalArgumentException(
+					"participants, deadline and votingeval must be not null");
+		this.votingevaluator = votingeval;
+		checkTeams();
+	}
+
+	@Override
+	public Double getMaxRunTime() {
+		return deadline.getDuration() / 1000d;
+	}
+
+	@Override
+	public MOPAC2 getProtocol(Reporter logger) {
+		return new MOPAC2(new MOPAC2State(this), logger);
+	}
+
+	@Override
+	public List<TeamInfo> getTeams() {
+		return Collections.unmodifiableList(participants);
+	}
+
+	/**
+	 * @return the deadline for this negotiation
+	 */
+	public Deadline getDeadline() {
+		return deadline;
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return participants.stream()
+				.map(particip -> particip.getAllParties().get(0))
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * @return a class that allows us to evaluate the voting results in
+	 *         different ways, selectable by the user.
+	 */
+	public VotingEvaluatorWithValue getVotingEvaluation() {
+		return votingevaluator;
+	}
+
+	@Override
+	public MOPAC2Settings with(TeamInfo team) {
+		if (team.getSize() != 1)
+			throw new IllegalArgumentException(
+					"Added party must be OnePartyTeam but got " + team);
+		List<TeamInfo> newparts = new LinkedList<>(
+				participants);
+		newparts.add(team);
+		return new MOPAC2Settings(newparts, deadline, votingevaluator);
+	}
+
+	@Override
+	public Integer getTeamSize() {
+		return 1;
+	}
+
+	@Override
+	public String toString() {
+		return "MOPAC2Settings[" + participants + "," + deadline + ","
+				+ votingevaluator.getClass().getSimpleName() + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((deadline == null) ? 0 : deadline.hashCode());
+		result = prime * result
+				+ ((participants == null) ? 0 : participants.hashCode());
+		result = prime * result
+				+ ((votingevaluator == null) ? 0 : votingevaluator.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;
+		MOPAC2Settings other = (MOPAC2Settings) obj;
+		if (deadline == null) {
+			if (other.deadline != null)
+				return false;
+		} else if (!deadline.equals(other.deadline))
+			return false;
+		if (participants == null) {
+			if (other.participants != null)
+				return false;
+		} else if (!participants.equals(other.participants))
+			return false;
+		if (votingevaluator == null) {
+			if (other.votingevaluator != null)
+				return false;
+		} else if (!votingevaluator.equals(other.votingevaluator))
+			return false;
+		return true;
+	}
+
+	/**
+	 * @throws IllegalArgumentException if teams have improper power settings.
+	 */
+	private void checkTeams() {
+		for (TeamInfo team : participants) {
+			if (team.getSize() != 1)
+				throw new IllegalArgumentException(
+						"All teams must be size 1 but found " + team);
+			PartyWithProfile party = team.getAllParties().get(0);
+			Object power = party.getParty().getParameters().get("power");
+			if (power != null) {
+				if (!(power instanceof Integer))
+					throw new IllegalArgumentException(
+							"parameter 'power' for party" + party
+									+ " must be integer but found " + power);
+				if ((Integer) power < 1)
+					throw new IllegalArgumentException(
+							"parameter 'power'  for party" + party
+									+ " must be >=1 but found " + power);
+
+			}
+		}
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2State.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2State.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2State.java	(revision 30)
@@ -0,0 +1,358 @@
+package geniusweb.protocol.session.mopac2;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.OptIn;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.NegoProtocol;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.SessionResult;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.mopac2.phase.OfferPhase;
+import geniusweb.protocol.session.mopac2.phase.OptInPhase;
+import geniusweb.protocol.session.mopac2.phase.Phase;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.references.PartyWithProfile;
+
+/**
+ * Keeps track of the current {@link Phase}. Adds initializing stuff and
+ * time/deadline checking. This state does not contain connections, this assumes
+ * that someone else handles (i.e. {@link NegoProtocol} that and the connections
+ * with the parties and that negotiation events are just pumped in from there.
+ * <p>
+ * This object is a bit tricky. It has two states
+ * <ol>
+ * <li>The initial state, where phase=null and connections are being made to all
+ * parties. At this point, problems can not be handled nicely yet because there
+ * are no PartyId's for problematic parties
+ * <li>The working state, where all parties are connected and all problems can
+ * be connected to an offending party.
+ * </ol>
+ */
+public class MOPAC2State implements SessionState {
+
+	private final Phase phase; // maybe null while initializing
+	private final MOPAC2Settings settings;
+	private final Map<PartyId, PartyWithProfile> partyprofiles;
+	private final List<Action> actions;
+	private final Progress progress;
+
+	/**
+	 * Creates the initial state from the given settings and progress=null
+	 * 
+	 * @param settings the {@link SAOPSettings}
+	 */
+	public MOPAC2State(MOPAC2Settings settings) {
+		this(null, Arrays.asList(), null, settings, Collections.emptyMap());
+
+	}
+
+	/**
+	 * @param phase         The Phase, or null if still initializing. Phase is
+	 *                      set something only after we have connections.
+	 * @param actions       the legal actions that have been done in the
+	 *                      negotiation. {@link PartyStates#getActions()} is
+	 *                      called to collect the actions after the phase is
+	 *                      finished.first action in list is the oldest. This
+	 *                      will not contain actions that immediately led to an
+	 *                      agreement, because {@link PartyStates} removes the
+	 *                      actions that led to an agreement. This MUST NOT
+	 *                      contain illegal actions. Parties doing illegal
+	 *                      actions are killed and the offending action ends up
+	 *                      in the stacktrace. Previous actions of crashed
+	 *                      parties remain standing and valid.
+	 * @param progress      the {@link Progress} line. can be null if not yet
+	 *                      known. null happens because during initialization
+	 *                      phase the protocol first add all connections. During
+	 *                      this time, parties may already enter the
+	 *                      failed/exception state.
+	 * @param settings      the {@link SAOPSettings}
+	 * @param partyprofiles map with the {@link PartyWithProfile} for connected
+	 *                      parties. null is equivalent to an empty map.
+	 */
+	@JsonCreator
+	protected MOPAC2State(@JsonProperty("phase") Phase phase,
+			@JsonProperty("actions") List<Action> actions,
+			@JsonProperty("progress") Progress progress,
+			@JsonProperty("settings") MOPAC2Settings settings,
+			@JsonProperty("partyprofiles") Map<PartyId, PartyWithProfile> partyprofiles) {
+		this.phase = phase;
+		this.actions = actions;
+		this.progress = progress;
+		this.settings = settings;
+		this.partyprofiles = partyprofiles;
+	}
+
+	/**
+	 * Sets the progress for this session and initial phase. Must be called
+	 * after all parties have been connected with
+	 * {@link #with(PartyId, PartyWithProfile)}.
+	 * 
+	 * @param newprogress the initial {@link Progress} typically matching the
+	 *                    settings deadline object
+	 * @param now         current time ms since 1970
+	 * 
+	 * @return state with the initial partystates , progress set.
+	 */
+	public MOPAC2State initPhase(Progress newprogress, long now) {
+		if (progress != null || newprogress == null || phase != null) {
+			throw new IllegalArgumentException(
+					"progress must be null, newprogress must be not null and phase must be INIT");
+		}
+
+		PartyStates partyStates = new PartyStates(getPowers());
+		Phase firstPhase = new OfferPhase(partyStates,
+				now + getAvailablePhaseTime(newprogress, now),
+				settings.getVotingEvaluation());
+		return new MOPAC2State(firstPhase, actions, newprogress, settings,
+				partyprofiles);
+	}
+
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		return phase.getPartyStates().getAgreements();
+	}
+
+	@Override
+	public MOPAC2Settings getSettings() {
+		return settings;
+	}
+
+	public Map<PartyId, PartyWithProfile> getPartyProfiles() {
+		return Collections.unmodifiableMap(partyprofiles);
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return phase != null && phase.isFinal(now) && !isNewPhasePossible(now);
+	}
+
+	@Override
+	public SessionResult getResult() {
+		return new SessionResult(partyprofiles, getAgreements(),
+				Collections.emptyMap(), null);
+	}
+
+	/**
+	 * @param progress the Progress that needs to be checked
+	 * @param now      current time ms since 1970
+	 * @return the max possible duration in ms of a phase considering the
+	 *         progress.
+	 */
+	private static Long getAvailablePhaseTime(Progress aprogress, long now) {
+		return Math.min(aprogress.getTerminationTime().getTime() - now,
+				Phase.PHASE_MAXTIME);
+	}
+
+	/**
+	 * @param id           the new {@link PartyId}
+	 * @param partyprofile the {@link PartyWithProfile} that is associated with
+	 *                     this state
+	 * @return new {@link MOPAC2State} with the new party added. This call
+	 *         ignores the progress (does not check isFinal) because we uses
+	 *         this during the setup where the deadline is not yet relevant.
+	 */
+	protected MOPAC2State with(PartyId id, PartyWithProfile partyprofile) {
+		if (phase != null)
+			throw new IllegalStateException(
+					"Adding connections only allowed while initializing");
+
+		Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
+				partyprofiles);
+		newprofiles.put(id, partyprofile);
+		return new MOPAC2State(null, actions, progress, settings, newprofiles);
+	}
+
+	/**
+	 * @param e the {@link ProtocolException} that occured
+	 * @return a new state with the error set. You MUST have called
+	 *         {@link #initPhase(Progress, long)} before using this
+	 */
+	public MOPAC2State with(ProtocolException e) {
+		return new MOPAC2State(phase.with(e), actions, progress, settings,
+				partyprofiles);
+	}
+
+	/**
+	 * Start the next phase. If new phase is OfferPhase, we increase progress.
+	 * Actions is reset to empty. does nothing if not
+	 * {@link #isNewPhasePossible(long)}
+	 * 
+	 * @param now current time
+	 * @return new {@link MOPAC2State} with phase initialized for next phase.
+	 */
+	public MOPAC2State nextPhase(long now) {
+		long remainingNegoTime = progress.getTerminationTime().getTime() - now;
+		Phase newphase = phase.next(now,
+				Math.min(remainingNegoTime, Phase.PHASE_MAXTIME));
+
+		return new MOPAC2State(newphase, actions, increment(progress, phase),
+				getSettings(), partyprofiles);
+	}
+
+	/**
+	 * When this is called, all parties should have acted.
+	 * 
+	 * @param now current time
+	 * @return true if there are still &gt;2 parties active and we have enough
+	 *         time for a new phase.
+	 */
+	public boolean isNewPhasePossible(long now) {
+		// System.out.println("phase=" + phase);
+		Progress newprogress = increment(progress, phase);
+		if (newprogress.isPastDeadline(now + Phase.PHASE_MINTIME))
+			return false;
+
+		return phase.getPartyStates().getNegotiatingParties().size() >= 2
+				&& getAvailablePhaseTime(newprogress,
+						now) > Phase.PHASE_MINTIME;
+	}
+
+	public Phase getPhase() {
+		return phase;
+	}
+
+	/**
+	 * Check if action is allowed. Add action to the list of actions. Notice,
+	 * this does NOT check if we need to step to the next phase, because
+	 * deciding that is also depending on time-outs.
+	 * 
+	 * @param actor  the actor that did this action. Can be used to check if
+	 *               action is valid. NOTICE caller has to make sure the current
+	 *               state is not final. MUST NOT be null.
+	 * @param action the action that was proposed by actor. MUST NOT be null.
+	 * @param now    the current time in ms since 1970, see
+	 *               {@link System#currentTimeMillis()}
+	 * @return new {@link MOPAC2State} with the action checked and registered.
+	 *         If the action is not allowed, the new state may be that the actor
+	 *         is in the exception list.
+	 */
+
+	public MOPAC2State with(PartyId actor, Action action, long now) {
+		return new MOPAC2State(phase.with(actor, action, now), actions,
+				progress, settings, partyprofiles);
+	}
+
+	/**
+	 * @param aprogress the progress that might need to be advanced
+	 * @param aphase    the phase
+	 * @return the next progress. Progress round advances if phase is
+	 *         {@link OptIn}.
+	 */
+	private static Progress increment(Progress aprogress, Phase aphase) {
+		if (aprogress instanceof ProgressRounds && aphase instanceof OptInPhase)
+			return ((ProgressRounds) aprogress).advance();
+		return aprogress;
+	}
+
+	/**
+	 * @return the power of all parties as set in their parameters, default =1.
+	 *         bad power values (non-integer, or <1) are ignored.
+	 */
+	private Map<PartyId, Integer> getPowers() {
+		Map<PartyId, Integer> map = new HashMap<>();
+		for (PartyId pid : partyprofiles.keySet()) {
+			Object power = partyprofiles.get(pid).getParty().getParameters()
+					.get("power");
+			if (power == null || !(power instanceof Integer)
+					|| ((Integer) power) < 1)
+				power = 1;
+			map.put(pid, (Integer) power);
+		}
+		return map;
+	}
+
+	@Override
+	public String toString() {
+		return "MOPAC2State[" + phase + "," + settings + "," + partyprofiles
+				+ "," + progress + "]";
+	}
+
+	/**
+	 * 
+	 * @return a wrapped-up state, with all parties doen an action or kicked,
+	 *         and agreements collected
+	 */
+	public MOPAC2State finishPhase() {
+		Phase newphase = phase.finish();
+		LinkedList<Action> newactions = new LinkedList<Action>(actions);
+		newactions.addAll(newphase.getPartyStates().getActions());
+		return new MOPAC2State(newphase, newactions, progress, settings,
+				partyprofiles);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((actions == null) ? 0 : actions.hashCode());
+		result = prime * result
+				+ ((partyprofiles == null) ? 0 : partyprofiles.hashCode());
+		result = prime * result + ((phase == null) ? 0 : phase.hashCode());
+		result = prime * result
+				+ ((progress == null) ? 0 : progress.hashCode());
+		result = prime * result
+				+ ((settings == null) ? 0 : settings.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;
+		MOPAC2State other = (MOPAC2State) obj;
+		if (actions == null) {
+			if (other.actions != null)
+				return false;
+		} else if (!actions.equals(other.actions))
+			return false;
+		if (partyprofiles == null) {
+			if (other.partyprofiles != null)
+				return false;
+		} else if (!partyprofiles.equals(other.partyprofiles))
+			return false;
+		if (phase == null) {
+			if (other.phase != null)
+				return false;
+		} else if (!phase.equals(other.phase))
+			return false;
+		if (progress == null) {
+			if (other.progress != null)
+				return false;
+		} else if (!progress.equals(other.progress))
+			return false;
+		if (settings == null) {
+			if (other.settings != null)
+				return false;
+		} else if (!settings.equals(other.settings))
+			return false;
+		return true;
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/PartyStates.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/PartyStates.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/PartyStates.java	(revision 30)
@@ -0,0 +1,306 @@
+package geniusweb.protocol.session.mopac2;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+
+/**
+ * Invariant: contains the current state of all participating parties. A party
+ * either notYetActed, did action, reached an agreement, walked away or made a
+ * {@link ProtocolException}.
+ * <p>
+ * After a phase completes, the actions are filtered/handled to collect
+ * agreements and move remaining parties back to notYetActed.
+ * 
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
+public class PartyStates {
+	// all parties must be in exactly one state at all times.
+	private final Set<PartyId> notYetActed;
+	private final List<Action> actions; // chrono order
+	private final Agreements agreements;
+	private final List<PartyId> walkedAway;
+	private final Map<PartyId, ProtocolException> exceptions;
+	private final Map<PartyId, Integer> powers;
+
+	@JsonCreator
+	protected PartyStates(@JsonProperty("notYetActed") Set<PartyId> notYetActed,
+			@JsonProperty("actions") List<Action> actions,
+			@JsonProperty("agreements") Agreements agreements,
+			@JsonProperty("walkedAway") List<PartyId> walkedAway,
+			@JsonProperty("exceptions") Map<PartyId, ProtocolException> exceptions,
+			@JsonProperty("powers") Map<PartyId, Integer> powers) {
+		this.notYetActed = notYetActed;
+		this.actions = actions;
+		this.agreements = agreements;
+		this.walkedAway = walkedAway;
+		this.exceptions = exceptions;
+		this.powers = powers;
+	}
+
+	/**
+	 * Initial constructor.
+	 * 
+	 * @param powers the powers of all parties.
+	 */
+	public PartyStates(Map<PartyId, Integer> powers) {
+		this.notYetActed = new HashSet<>(powers.keySet());
+		this.actions = Collections.emptyList();
+		this.agreements = new Agreements();
+		this.walkedAway = Collections.emptyList();
+		this.exceptions = Collections.emptyMap();
+		this.powers = new HashMap<>(powers);
+	}
+
+	/**
+	 * 
+	 * @param action the action done by some party. The correctness of action,
+	 *               particularly of {@link Action#getActor()}, must be correct.
+	 * @return new state with party done given action. This just accepts any
+	 *         given action and is not considering number of rounds, time or
+	 *         whether an action is allowed.
+	 * 
+	 * @throws IllegalArgumentException if party doing action already acted.
+	 *                                  This is just a safety check as legality
+	 *                                  of actions should be checked before
+	 *                                  calling this.
+	 */
+	public PartyStates with(Action action) {
+		if (!(notYetActed.contains(action.getActor())))
+			throw new IllegalArgumentException(
+					"actor already acted: " + action);
+		if (action instanceof EndNegotiation)
+			return withWalkAway(action.getActor());
+		List<Action> newActions = new LinkedList<Action>(actions);
+		newActions.add(action);
+		return new PartyStates(removeParty(action.getActor()), newActions,
+				agreements, walkedAway, exceptions, powers);
+	}
+
+	/**
+	 * 
+	 * @param newAgree a new agreements to be merged with existing agreements.
+	 *                 The parties in the agreement must have acted and thus in
+	 *                 actions.
+	 * @return new PartyStates with agreeing parties removed from actions and
+	 *         added to Agreements.
+	 */
+	public PartyStates with(Agreements newAgree) {
+		Set<PartyId> parties = newAgree.getMap().keySet();
+		List<Action> newActions = actions.stream()
+				.filter(act -> !(parties.contains(act.getActor())))
+				.collect(Collectors.toList());
+		return new PartyStates(notYetActed, newActions,
+				agreements.with(newAgree), walkedAway, exceptions, powers);
+	}
+
+	public PartyStates withWalkAway(PartyId actor) {
+		if (!(notYetActed.contains(actor)))
+			throw new IllegalArgumentException("actor already acted: " + actor);
+		LinkedList<PartyId> newWalkAway = new LinkedList<>(walkedAway);
+		newWalkAway.add(actor);
+		return new PartyStates(removeParty(actor), actions, agreements,
+				newWalkAway, exceptions, powers);
+	}
+
+	/**
+	 * Move party from active to exceptions.
+	 * 
+	 * @param e the exception that the party caused
+	 * @return new PartyStates with party that caused the exception in the
+	 *         exceptions list and removed from the active list. Nothing happens
+	 *         if party is not active.
+	 */
+	public PartyStates with(ProtocolException e) {
+		if (!notYetActed.contains(e.getParty())) {
+			// complex case. Party did valid action but now is messing around.
+			// Easiest seems to completely ignore it. CHECK.
+			return this;
+		}
+		Map<PartyId, ProtocolException> newExc = new HashMap<>(exceptions);
+		newExc.put(e.getParty(), e);
+		return new PartyStates(removeParty(e.getParty()), actions, agreements,
+				walkedAway, newExc, powers);
+	}
+
+	/**
+	 * 
+	 * @return parties that have not yet acted
+	 */
+	public Set<PartyId> getNotYetActed() {
+		return notYetActed;
+	}
+
+	/**
+	 * 
+	 * @return parties that are still in the negotiation. These are the parties
+	 *         that not yet acted plus the ones that did an action.
+	 */
+	public Set<PartyId> getNegotiatingParties() {
+		Set<PartyId> parties = actions.stream().map(act -> act.getActor())
+				.collect(Collectors.toSet());
+		parties.addAll(notYetActed);
+		return parties;
+	}
+
+	/**
+	 * @param actor
+	 * @return {@link #notYetActed} with actor removed
+	 * @throws IllegalArgumentException if party already acted (not in
+	 *                                  {@link #notYetActed}).
+	 */
+	private Set<PartyId> removeParty(PartyId party) {
+		Set<PartyId> newActiveParties = new HashSet<>(notYetActed);
+		if (!newActiveParties.remove(party))
+			throw new IllegalArgumentException(
+					"Party " + party + " is not active, can't be removed");
+		return newActiveParties;
+
+	}
+
+	public Agreements getAgreements() {
+		return agreements;
+	}
+
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	public Map<PartyId, ProtocolException> getExceptions() {
+		return Collections.unmodifiableMap(exceptions);
+	}
+
+	/**
+	 * @return new state where all {@link #notYetActed} are moved to the
+	 *         exceptions list.
+	 */
+	public PartyStates finish() {
+		PartyStates newstate = this;
+		for (PartyId party : notYetActed) {
+			newstate = newstate
+					.with(new ProtocolException("Party did not act", party));
+		}
+		return newstate;
+	}
+
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	/**
+	 * 
+	 * @return all parties that walked away
+	 */
+	public List<PartyId> getWalkedAway() {
+		return walkedAway;
+	}
+
+	/**
+	 * @param <T>  the type of objects requested
+	 * @param type the type of actions to extract. Must be of type T, needed
+	 *             because of java's type erasure.
+	 * 
+	 * @return all actions of type done in this phase
+	 */
+	public <T extends Action> List<T> getActions(Class<T> type) {
+		return actions.stream().filter(act -> type.isInstance(act))
+				.map(act -> (T) act).collect(Collectors.toList());
+	}
+
+	@Override
+	public String toString() {
+		return "PartyStates[" + notYetActed + "," + actions + "," + agreements
+				+ "," + walkedAway + "," + exceptions + "]";
+	}
+
+	/**
+	 * 
+	 * @return states with all parties that are in {@link #actions} moved back
+	 *         to {@link #notYetActed}. This state is then ready for a next
+	 *         phase.
+	 * @throws IllegalStateException if {@link #notYetActed} is not empty
+	 */
+	public PartyStates flush() {
+		if (!notYetActed.isEmpty())
+			throw new IllegalStateException(
+					"Some parties did not yet act:" + notYetActed);
+		return new PartyStates(getNegotiatingParties(), Collections.emptyList(),
+				agreements, walkedAway, exceptions, powers);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((actions == null) ? 0 : actions.hashCode());
+		result = prime * result
+				+ ((agreements == null) ? 0 : agreements.hashCode());
+		result = prime * result
+				+ ((exceptions == null) ? 0 : exceptions.hashCode());
+		result = prime * result
+				+ ((notYetActed == null) ? 0 : notYetActed.hashCode());
+		result = prime * result + ((powers == null) ? 0 : powers.hashCode());
+		result = prime * result
+				+ ((walkedAway == null) ? 0 : walkedAway.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;
+		PartyStates other = (PartyStates) obj;
+		if (actions == null) {
+			if (other.actions != null)
+				return false;
+		} else if (!actions.equals(other.actions))
+			return false;
+		if (agreements == null) {
+			if (other.agreements != null)
+				return false;
+		} else if (!agreements.equals(other.agreements))
+			return false;
+		if (exceptions == null) {
+			if (other.exceptions != null)
+				return false;
+		} else if (!exceptions.equals(other.exceptions))
+			return false;
+		if (notYetActed == null) {
+			if (other.notYetActed != null)
+				return false;
+		} else if (!notYetActed.equals(other.notYetActed))
+			return false;
+		if (powers == null) {
+			if (other.powers != null)
+				return false;
+		} else if (!powers.equals(other.powers))
+			return false;
+		if (walkedAway == null) {
+			if (other.walkedAway != null)
+				return false;
+		} else if (!walkedAway.equals(other.walkedAway))
+			return false;
+		return true;
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/DefaultPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/DefaultPhase.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/DefaultPhase.java	(revision 30)
@@ -0,0 +1,146 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+public abstract class DefaultPhase implements Phase {
+	// deadline for this phase, ms since 1970
+	protected final Long deadline;
+	protected final PartyStates partyStates;
+	protected final VotingEvaluatorWithValue evaluator;
+
+	/**
+	 * @param partyStates the {@link PartyStates}
+	 * @param deadline    deadline for this phase, ms since 1970
+	 * @param evaluator   the {@link VotingEvaluator} to be used
+	 */
+	public DefaultPhase(PartyStates partyStates, Long deadline,
+			VotingEvaluatorWithValue evaluator) {
+		this.partyStates = partyStates;
+		this.deadline = deadline;
+		this.evaluator = evaluator;
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return now >= deadline || partyStates.getNotYetActed().isEmpty();
+	}
+
+	@Override
+	public long getDeadline() {
+		return deadline;
+	}
+
+	/**
+	 * Check if actor can do given action. Basic checks:
+	 * <ul>
+	 * <li>real actor does not match action's actor
+	 * <li>deadline for this phase has passed
+	 * <li>action is not allowed in this phase
+	 * <li>actor already acted in this phase
+	 * </ul>
+	 * 
+	 * @param actor  the actor that really acted
+	 * @param action the action that was done
+	 * @param now    current time
+	 * @throws ProtocolException if the action violates the protocol
+	 */
+	protected void checkAction(PartyId actor, Action action, long now)
+			throws ProtocolException {
+		if (action == null)
+			throw new ProtocolException("Action=null", actor);
+		if (!(actor.equals(action.getActor())))
+			throw new ProtocolException(
+					"Incorrect actor info in action:" + action.getActor(),
+					actor);
+		if (isFinal(now))
+			throw new ProtocolException("passed deadline", actor);
+		if (!(getAllowedActions().contains(action.getClass())))
+			throw new ProtocolException("Action not allowed in "
+					+ this.getClass().getSimpleName() + ": " + action, actor);
+		if (!partyStates.getNotYetActed().contains(actor))
+			throw new ProtocolException("Actor can not act anymore", actor);
+		return;
+	}
+
+	/**
+	 * @return current PartyStates
+	 */
+	@Override
+	public PartyStates getPartyStates() {
+		return partyStates;
+	}
+
+	@Override
+	public VotingEvaluatorWithValue getEvaluator() {
+		return evaluator;
+	}
+
+	@Override
+	public Phase next(long now, long duration) {
+		if (duration < PHASE_MINTIME || duration > PHASE_MAXTIME)
+			throw new IllegalArgumentException("Bug, illegal duration");
+		if (!isFinal(now))
+			throw new IllegalStateException("phase is not final");
+		return checkedNext(now + duration);
+	}
+
+	/**
+	 * As {@link #next(long, long)} but DefaultPhase already checked that 1.
+	 * there is enough time for a next phase 2. current state is final 3.
+	 * 
+	 * @param dl the deadline for the next phase (ms since 1970).
+	 * @return the next phase
+	 */
+	protected abstract Phase checkedNext(long dl);
+
+	@Override
+	public String toString() {
+		return getClass().getSimpleName() + "[" + partyStates + "," + deadline
+				+ "," + evaluator + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((deadline == null) ? 0 : deadline.hashCode());
+		result = prime * result
+				+ ((evaluator == null) ? 0 : evaluator.hashCode());
+		result = prime * result
+				+ ((partyStates == null) ? 0 : partyStates.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;
+		DefaultPhase other = (DefaultPhase) obj;
+		if (deadline == null) {
+			if (other.deadline != null)
+				return false;
+		} else if (!deadline.equals(other.deadline))
+			return false;
+		if (evaluator == null) {
+			if (other.evaluator != null)
+				return false;
+		} else if (!evaluator.equals(other.evaluator))
+			return false;
+		if (partyStates == null) {
+			if (other.partyStates != null)
+				return false;
+		} else if (!partyStates.equals(other.partyStates))
+			return false;
+		return true;
+	}
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OfferPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OfferPhase.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OfferPhase.java	(revision 30)
@@ -0,0 +1,71 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.inform.YourTurn;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+public class OfferPhase extends DefaultPhase {
+
+	@JsonCreator
+	public OfferPhase(@JsonProperty("partyStates") PartyStates partyStates,
+			@JsonProperty("deadlinems") Long deadlinems,
+			@JsonProperty("evaluator") VotingEvaluatorWithValue evaluator) {
+		super(partyStates, deadlinems, evaluator);
+	}
+
+	@Override
+	public OfferPhase with(PartyId actor, Action action, long timems) {
+		try {
+			checkAction(actor, action, timems);
+		} catch (ProtocolException ex) {
+			return this.with(ex);
+		}
+		return new OfferPhase(partyStates.with(action), deadline, evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new YourTurn();
+	}
+
+	@Override
+	public OfferPhase with(ProtocolException e) {
+		System.out.println("Party kicked because of protocol exception:" + e);
+		return new OfferPhase(partyStates.with(e), deadline, evaluator);
+	}
+
+	@Override
+	public Phase finish() {
+		return new OfferPhase(partyStates.finish(), deadline, evaluator);
+	}
+
+	@Override
+	public VotingPhase checkedNext(long deadln) {
+		return new VotingPhase(getOffers(), partyStates.flush(), deadln,
+				evaluator);
+	}
+
+	protected List<Offer> getOffers() {
+		return partyStates.getActions(Offer.class).stream()
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public List<Class<? extends Action>> getAllowedActions() {
+		return Arrays.asList(Offer.class, EndNegotiation.class);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OptInPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OptInPhase.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/OptInPhase.java	(revision 30)
@@ -0,0 +1,110 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Inform;
+import geniusweb.inform.OptInWithValue;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.CollectedVotesWithValue;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+public class OptInPhase extends DefaultPhase {
+
+	/**
+	 * The votes received in the {@link VotingPhase}
+	 */
+	private final List<VotesWithValue> votes;
+
+	@JsonCreator
+	protected OptInPhase(@JsonProperty("votes") List<VotesWithValue> votes,
+			@JsonProperty("partyStates") PartyStates partyStates,
+			@JsonProperty("deadlinems") Long deadlinems,
+			@JsonProperty("evaluator") VotingEvaluatorWithValue evaluator) {
+		super(partyStates, deadlinems, evaluator);
+		this.votes = votes;
+	}
+
+	@Override
+	public Inform getInform() {
+		return new OptInWithValue(votes);
+	}
+
+	@Override
+	public Phase with(PartyId actor, Action action, long now) {
+		try {
+			checkAction(actor, action, now);
+			if (action instanceof VotesWithValue)
+				checkExtends((VotesWithValue) action);
+
+			return new OptInPhase(votes, partyStates.with(action), deadline,
+					evaluator);
+
+		} catch (ProtocolException e) {
+			return this.with(e);
+		}
+	}
+
+	/**
+	 * Check that this action extends previous action.
+	 * 
+	 * @param newvotes new {@link Votes} just received
+	 * @throws ProtocolException if this action does not correctly extend
+	 *                           previous vote.
+	 */
+	private void checkExtends(VotesWithValue newvotes)
+			throws ProtocolException {
+		PartyId actor = newvotes.getActor();
+		// this actor is active so he must have voted in previous round.
+		VotesWithValue prevVotes = votes.stream()
+				.filter(v -> v.getActor().equals(actor)).findFirst().get();
+		if (!(newvotes.isExtending(prevVotes)))
+			throw new ProtocolException("New votes " + newvotes
+					+ " does not extend previous vote " + prevVotes, actor);
+	}
+
+	@Override
+	public OptInPhase with(ProtocolException e) {
+		return new OptInPhase(votes, partyStates.with(e), deadline, evaluator);
+	}
+
+	@Override
+	public OptInPhase finish() {
+		PartyStates states = partyStates.finish();
+		Map<PartyId, VotesWithValue> votesmap = states
+				.getActions(VotesWithValue.class).stream()
+				.collect(Collectors.toMap(vts -> vts.getActor(), v -> v));
+		CollectedVotesWithValue allvotes = new CollectedVotesWithValue(votesmap,
+				states.getPowers());
+		Agreements newAgree = evaluator.create(allvotes).getAgreements();
+		if (!newAgree.getMap().isEmpty()) {
+			System.out.println("detected new agreements");
+		}
+		PartyStates finalStates = states.with(newAgree);
+
+		return new OptInPhase(votes, finalStates, deadline, evaluator);
+	}
+
+	@Override
+	public OfferPhase checkedNext(long newdeadline) {
+		return new OfferPhase(partyStates.flush(), newdeadline, evaluator);
+	}
+
+	@Override
+	public List<Class<? extends Action>> getAllowedActions() {
+		return Arrays.asList(VotesWithValue.class, EndNegotiation.class);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/Phase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/Phase.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/Phase.java	(revision 30)
@@ -0,0 +1,121 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import java.util.List;
+
+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 geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+/**
+ * A Phase is a part of the round structure. In each round parties have to take
+ * multiple actions. Each action is part of a phase.
+ * 
+ * Invariant: A phase object handles negotiation events, ensuring that the
+ * events are handled according to the possible actions in this phase. A phase
+ * object must always remain in a consistent state. It does so by modifying the
+ * contained {@link PartyStates} as needed.
+ * <p>
+ * The standard exception handling is assumed: unchecked exceptions are thrown
+ * only if there is a bug in the protocol. Because none of the functions in this
+ * interface throws, any error must be either ignored or the party must be
+ * kicked.
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
+@JsonSubTypes({ @JsonSubTypes.Type(value = OfferPhase.class),
+		@JsonSubTypes.Type(value = OptInPhase.class),
+		@JsonSubTypes.Type(value = VotingPhase.class) })
+public interface Phase {
+	public static final Long PHASE_MAXTIME = 30000l; // 30sec
+	public static final Long PHASE_MINTIME = 100l; // 100 millisec
+
+	/**
+	 * Handle an actor's action. If a {@link ProtocolException} occurs, this is
+	 * handled by updating the PartyStates
+	 * 
+	 * @param actor  the real actor (may differ from the actor contained in the
+	 *               action)
+	 * @param action the action submitted by actor, which this phase can really
+	 *               handle.
+	 * @param now    the current time
+	 * @return new VotingPhase
+	 */
+	public Phase with(PartyId actor, Action action, long now);
+
+	/**
+	 * 
+	 * @param e a {@link ProtocolException}
+	 * @return new Phase with a party disabled because it violated a protocol eg
+	 *         breaking a websocket link.
+	 */
+	public Phase with(ProtocolException e);
+
+	/**
+	 * 
+	 * @return the inform object to send to all parties at start of phase.
+	 */
+	public Inform getInform();
+
+	/**
+	 * 
+	 * @param now the current time
+	 * @return true iff past deadline or no more actions are allowed anyway.
+	 *         (which usually means, all parties have done exactly one act).
+	 *         Notice, this is the main reason that it is desirable to require
+	 *         all parties to act exactly once in each phase. Without such a
+	 *         rule, the protocol will always have to wait till the deadline.
+	 */
+	public boolean isFinal(long now);
+
+	/**
+	 * 
+	 * @return finished state. That means, all parties that did not act have
+	 *         been kicked, new agreements have been computed, etc.
+	 */
+	public Phase finish();
+
+	/**
+	 * Determines the next phase. Resets the actions field. Can only be called
+	 * after {@link #finish()}
+	 * 
+	 * @param now      the current time
+	 * @param duration the max duration (ms) of the next phase. Must be between
+	 *                 {@link #PHASE_MINTIME} and {@link #PHASE_MAXTIME}. Also
+	 *                 make sure that now+duration is at most at the total
+	 *                 negotiation deadline.
+	 * @return the next phase, or null if negotiation is complete.
+	 */
+	public Phase next(long now, long duration);
+
+	/**
+	 * @return the voting evaluator
+	 */
+	public VotingEvaluatorWithValue getEvaluator();
+
+	/**
+	 * 
+	 * @return time (ms since 1970) at which phase ends. The phase may end
+	 *         before, but never after this.
+	 */
+	public long getDeadline();
+
+	/**
+	 * @return the party states. Notice that someone must call
+	 *         {@link PartyStates#finish()} if passed the deadline.
+	 */
+	public PartyStates getPartyStates();
+
+	/**
+	 * @return the allowed actinos in this phase
+	 */
+	public abstract List<Class<? extends Action>> getAllowedActions();
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/VotingPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/VotingPhase.java	(revision 30)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac2/phase/VotingPhase.java	(revision 30)
@@ -0,0 +1,85 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Voting;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+
+public class VotingPhase extends DefaultPhase {
+
+	/**
+	 * the offers received in the {@link OfferPhase}
+	 */
+	private final List<Offer> offers;
+
+	@JsonCreator
+	protected VotingPhase(@JsonProperty("offers") List<Offer> offers,
+			@JsonProperty("partyStates") PartyStates partyStates,
+			@JsonProperty("deadlinems") Long deadlinems,
+			@JsonProperty("evaluator") VotingEvaluatorWithValue evaluator) {
+		super(partyStates, deadlinems, evaluator);
+		this.offers = offers;
+	}
+
+	@Override
+	public VotingPhase with(PartyId actor, Action action, long now) {
+		try {
+			checkAction(actor, action, now);
+		} catch (ProtocolException ex) {
+			return this.with(ex);
+		}
+		return new VotingPhase(offers, partyStates.with(action), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new Voting(offers, partyStates.getPowers());
+	}
+
+	@Override
+	public VotingPhase with(ProtocolException e) {
+		return new VotingPhase(offers, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase finish() {
+		return new VotingPhase(offers, partyStates.finish(), deadline,
+				evaluator);
+	}
+
+	@Override
+	protected Phase checkedNext(long deadln) {
+		return new OptInPhase(getVotes(), partyStates.flush(), deadln,
+				evaluator);
+	}
+
+	@Override
+	public List<Class<? extends Action>> getAllowedActions() {
+		return Arrays.asList(VotesWithValue.class, EndNegotiation.class);
+	}
+
+	/**
+	 * @return all votes done in this phase.
+	 */
+	public List<VotesWithValue> getVotes() {
+		return partyStates.getActions().stream()
+				.filter(act -> act instanceof VotesWithValue)
+				.map(act -> (VotesWithValue) act).collect(Collectors.toList());
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 30)
@@ -10,13 +10,12 @@
 
 import geniusweb.deadline.Deadline;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
 
 public class SAOPSettings implements SessionSettings {
-	private final List<OnePartyTeam> participants;
+	private final List<TeamInfo> participants;
 	private final Deadline deadline;
 
@@ -31,5 +30,5 @@
 	@JsonCreator
 	public SAOPSettings(
-			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("participants") List<TeamInfo> participants,
 			@JsonProperty("deadline") Deadline deadline) {
 		this.participants = participants;
@@ -54,5 +53,5 @@
 
 	@Override
-	public List<TeamOfPartiesAndProfiles> getTeams() {
+	public List<TeamInfo> getTeams() {
 		return Collections.unmodifiableList(participants);
 	}
@@ -70,4 +69,9 @@
 				.map(particip -> particip.getAllParties().get(0))
 				.collect(Collectors.toList());
+	}
+
+	@Override
+	public Integer getTeamSize() {
+		return 1;
 	}
 
@@ -111,10 +115,11 @@
 
 	@Override
-	public SessionSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof OnePartyTeam))
+	public SessionSettings with(TeamInfo team) {
+		if (team.getSize() != 1)
 			throw new IllegalArgumentException(
-					"Added party must be OnePartyTeam but got " + party);
-		List<OnePartyTeam> newparts = new LinkedList<>(participants);
-		newparts.add((OnePartyTeam) party);
+					"Team must be size 1 but found " + team);
+		List<TeamInfo> newparts = new LinkedList<>(
+				participants);
+		newparts.add(team);
 		return new SAOPSettings(newparts, deadline);
 	}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 30)
@@ -33,4 +33,5 @@
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyWithProfile;
@@ -59,5 +60,5 @@
  * <p>
  * The Comparison/ComparisonTest actions are private between the
- * {@link ShaopTeam}. A SHAOP party can do a {@link ElicitComparison} action at
+ * {@link TeamInfo}. A SHAOP party can do a {@link ElicitComparison} action at
  * any time. These actions are completely transparent to the other parties and
  * are not dependent or influencing the normal turn taking.
@@ -80,5 +81,5 @@
 			false);
 	private volatile Timer deadlinetimer = null;
-	private List<Class> ALLOWED_ACTIONS = Arrays.asList(Offer.class,
+	private List ALLOWED_ACTIONS = Arrays.asList(Offer.class,
 			ElicitComparison.class, Accept.class, EndNegotiation.class);
 
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java	(revision 30)
@@ -13,22 +13,25 @@
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
 
 public class SHAOPSettings implements SessionSettings {
-	private final List<ShaopTeam> participants;
+	private static final int SHAOP = 0;
+	private static final int COB = 1;
+	private final List<TeamInfo> participants;
 	private final Deadline deadline;
 	private final transient List<PartyWithProfile> allParties = new LinkedList<>();
 
 	/**
-	 * @param teams    the list of {@link ShaopTeam}s containing the shaop
-	 *                 parties and their partner cob There must be at least 2 to
-	 *                 run the SHAOP protocol. But SHAOP can be initialized with
-	 *                 less, for use in TournamentSettings.
+	 * @param teams    the {@link TeamInfo}s each containing the shaop parties
+	 *                 and their partner cob, in that order. There must be at
+	 *                 least 2 {@link TeamInfo}s to run the SHAOP protocol. But
+	 *                 SHAOP can be initialized with less, for use in
+	 *                 TournamentSettings.
 	 * @param deadline the deadline settings for the session
 	 */
 	@JsonCreator
-	public SHAOPSettings(@JsonProperty("participants") List<ShaopTeam> teams,
+	public SHAOPSettings(@JsonProperty("participants") List<TeamInfo> teams,
 			@JsonProperty("deadline") Deadline deadline) {
 		if (teams == null)
@@ -39,7 +42,10 @@
 		this.deadline = deadline;
 		// notice, order here is crucial because of way #isShaopParty works.
-		for (ShaopTeam tuple : teams) {
-			allParties.add(tuple.getShaop());
-			allParties.add(tuple.getCob());
+		for (TeamInfo team : teams) {
+			if (team.getSize() != 2)
+				throw new IllegalArgumentException(
+						"Team must be size 2 but found " + team);
+			allParties.add(team.getAllParties().get(SHAOP));
+			allParties.add(team.getAllParties().get(COB));
 		}
 	}
@@ -56,7 +62,7 @@
 
 	@Override
-	public List<TeamOfPartiesAndProfiles> getTeams() {
+	public List<TeamInfo> getTeams() {
 		// cast needed...
-		return new ArrayList<TeamOfPartiesAndProfiles>(participants);
+		return new ArrayList<TeamInfo>(participants);
 	}
 
@@ -81,11 +87,13 @@
 
 	@Override
-	public SessionSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof ShaopTeam))
-			throw new IllegalArgumentException(
-					"Added party must be ShaopTeam but got " + party);
-		List<ShaopTeam> newparts = new LinkedList<>(participants);
-		newparts.add((ShaopTeam) party);
+	public SessionSettings with(TeamInfo team) {
+		List<TeamInfo> newparts = new LinkedList<>(participants);
+		newparts.add(team);
 		return new SHAOPSettings(newparts, deadline);
+	}
+
+	@Override
+	public Integer getTeamSize() {
+		return 2;
 	}
 
Index: otocol/src/main/java/geniusweb/protocol/session/shaop/ShaopTeam.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/ShaopTeam.java	(revision 29)
+++ 	(revision )
@@ -1,93 +1,0 @@
-package geniusweb.protocol.session.shaop;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import geniusweb.actions.Comparison;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
-import geniusweb.references.PartyWithProfile;
-
-/**
- * A tuple of a SHOAP party and a COB party. Part of the settings.
- *
- */
-@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
-public class ShaopTeam implements TeamOfPartiesAndProfiles {
-	private final PartyWithProfile shaop;
-	private final PartyWithProfile cob;
-
-	/**
-	 * @param shaop {@link PartyWithProfile} participants supporting SHAOP
-	 *              behaviour. SHAOP behaviour has parameters:
-	 *              <ul>
-	 *              <li>elicitationcost: the value must be a double number
-	 *              indicating the costs of each RequestComparison action.
-	 *              <li>credit: the credit the party has to start with. The sum
-	 *              of the elicitationcost should stay below this.
-	 *              </ul>
-	 * @param cob   {@link PartyWithProfile} party, must support the COB
-	 *              behaviour. This is the cob partie that the shaop requests
-	 *              for {@link Comparison} will be forwarded to
-	 */
-	@JsonCreator
-	public ShaopTeam(@JsonProperty("shoap") PartyWithProfile shaop,
-			@JsonProperty("cob") PartyWithProfile cob) {
-		this.shaop = shaop;
-		this.cob = cob;
-	}
-
-	@Override
-	public List<PartyWithProfile> getAllParties() {
-		return Arrays.asList(shaop, cob);
-	}
-
-	@Override
-	public String toString() {
-		return "Parties[" + shaop + "," + cob + "]";
-	}
-
-	public PartyWithProfile getShaop() {
-		return shaop;
-	}
-
-	public PartyWithProfile getCob() {
-		return cob;
-	}
-
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((cob == null) ? 0 : cob.hashCode());
-		result = prime * result + ((shaop == null) ? 0 : shaop.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;
-		ShaopTeam other = (ShaopTeam) obj;
-		if (cob == null) {
-			if (other.cob != null)
-				return false;
-		} else if (!cob.equals(other.cob))
-			return false;
-		if (shaop == null) {
-			if (other.shaop != null)
-				return false;
-		} else if (!shaop.equals(other.shaop))
-			return false;
-		return true;
-	}
-
-}
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 29)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 30)
@@ -3,4 +3,5 @@
 import java.math.BigInteger;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.function.BiFunction;
@@ -9,15 +10,13 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
-import geniusweb.protocol.session.amop.AMOPSettings;
-import geniusweb.protocol.session.mopac.MOPACSettings;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.protocol.session.saop.SAOPSettings;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
-import geniusweb.protocol.session.shaop.ShaopTeam;
 import geniusweb.protocol.tournament.TournamentProtocol;
 import geniusweb.protocol.tournament.TournamentSettings;
+import geniusweb.references.PartyWithParameters;
 import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
 import tudelft.utilities.immutablelist.FixedList;
 import tudelft.utilities.immutablelist.ImmutableList;
@@ -82,7 +81,7 @@
 	 * @param teamsPerSession number of parties per session, must be at least 2.
 	 * @param sesettings      The generic {@link SessionSettings}.
-	 *                        {@link SessionSettings#with(TeamOfPartiesAndProfiles)}
+	 *                        {@link SessionSettings#with(TeamInfo)}
 	 *                        will be used to add the required
-	 *                        {@link TeamOfPartiesAndProfiles}s
+	 *                        {@link TeamInfo}s
 	 * @param nTournaments    the number of times the tournament should be run.
 	 */
@@ -105,14 +104,5 @@
 		if (nTournaments <= 0)
 			throw new IllegalArgumentException("nTournaments must be >0");
-		int teamsize;
-		if (sesettings instanceof SAOPSettings
-				|| sesettings instanceof MOPACSettings
-				|| sesettings instanceof AMOPSettings)
-			teamsize = 1;
-		else if (sesettings instanceof SHAOPSettings)
-			teamsize = 2;
-		else
-			throw new IllegalArgumentException(
-					"Unsupported protocol " + sesettings.getClass());
+		int teamsize = sesettings.getTeamSize();
 
 		for (Team team : teams) {
@@ -156,5 +146,5 @@
 	 */
 	public ImmutableList<SessionSettings> permutations() {
-		ImmutableList<ImmutableList<TeamOfPartiesAndProfiles>> partylistlist;
+		ImmutableList<ImmutableList<TeamInfo>> partylistlist;
 		ImmutableList<Team> partieslist = new FixedList<Team>(teams);
 		ImmutableList<ProfileList> profileslist = new FixedList<ProfileList>(
@@ -227,7 +217,7 @@
 	 */
 	private SessionSettings createSetting(
-			ImmutableList<TeamOfPartiesAndProfiles> partyproflist) {
+			ImmutableList<TeamInfo> partyproflist) {
 		SessionSettings settings = sessionsettings;
-		for (TeamOfPartiesAndProfiles partyprof : partyproflist) {
+		for (TeamInfo partyprof : partyproflist) {
 			settings = settings.with(partyprof);
 		}
@@ -242,9 +232,9 @@
 	 * @param drawPartyWithPutback if parties can be drawn multiple times
 	 * @return list of sessionsets where each sessionset contains settings for a
-	 *         session: a list with {@link TeamOfPartiesAndProfiles}s. The
+	 *         session: a list with {@link TeamInfo}s. The
 	 *         sessionsets are made by making all combinations of parties and
 	 *         profiles. Profiles are drawn with replace.
 	 */
-	private ImmutableList<ImmutableList<TeamOfPartiesAndProfiles>> getParticipants(
+	private ImmutableList<ImmutableList<TeamInfo>> getParticipants(
 			ImmutableList<Team> parties, ImmutableList<ProfileList> profiles,
 			int n, boolean drawPartyWithPutback) {
@@ -265,7 +255,7 @@
 				partiesPermutations, profilesPermutations);
 
-		MapList<Tuple<ImmutableList<Team>, ImmutableList<ProfileList>>, ImmutableList<TeamOfPartiesAndProfiles>> tournamentsettings = new MapList<Tuple<ImmutableList<Team>, ImmutableList<ProfileList>>, ImmutableList<TeamOfPartiesAndProfiles>>(
+		MapList<Tuple<ImmutableList<Team>, ImmutableList<ProfileList>>, ImmutableList<TeamInfo>> tournamentsettings = new MapList<Tuple<ImmutableList<Team>, ImmutableList<ProfileList>>, ImmutableList<TeamInfo>>(
 				tuple -> teamlist(tuple), tuples);
-		return new Repeat<ImmutableList<TeamOfPartiesAndProfiles>>(
+		return new Repeat<ImmutableList<TeamInfo>>(
 				tournamentsettings, BigInteger.valueOf(numberTournaments),
 				true);
@@ -279,29 +269,27 @@
 	 *         in order.
 	 */
-	private ImmutableList<TeamOfPartiesAndProfiles> teamlist(
+	private ImmutableList<TeamInfo> teamlist(
 			Tuple<ImmutableList<Team>, ImmutableList<ProfileList>> tuple) {
-		BiFunction<Team, ProfileList, TeamOfPartiesAndProfiles> makeparty = new BiFunction<Team, ProfileList, TeamOfPartiesAndProfiles>() {
+		BiFunction<Team, ProfileList, TeamInfo> makeparty = new BiFunction<Team, ProfileList, TeamInfo>() {
 			@Override
-			public TeamOfPartiesAndProfiles apply(Team team,
+			public TeamInfo apply(Team team,
 					ProfileList profilelist) {
-				if (sessionsettings instanceof SAOPSettings
-						|| sessionsettings instanceof AMOPSettings
-						|| sessionsettings instanceof MOPACSettings)
-					return new OnePartyTeam(team.getParties().get(0),
-							profilelist.getProfiles().get(0));
-				else if (sessionsettings instanceof SHAOPSettings) {
-					return new ShaopTeam(
-							new PartyWithProfile(team.getParties().get(0),
-									profilelist.getProfiles().get(0)),
-							new PartyWithProfile(team.getParties().get(1),
-									profilelist.getProfiles().get(1)));
-				} else
+				List<PartyWithParameters> parties = team.getParties();
+				List<ProfileRef> profiles = profilelist.getProfiles();
+				if (parties.size() != profiles.size())
 					throw new IllegalArgumentException(
-							"Unknown/unsupported protocol "
-									+ sessionsettings.getClass());
+							"Parties and profiles list must match, but found "
+									+ team + " and " + profilelist);
+
+				List<PartyWithProfile> theteams = new LinkedList<>();
+				for (int n = 0; n < parties.size(); n++) {
+					theteams.add(new PartyWithProfile(parties.get(n),
+							profiles.get(n)));
+				}
+				return new TeamInfo(theteams);
 			}
 
 		};
-		return new MapThreadList<TeamOfPartiesAndProfiles, Team, ProfileList>(
+		return new MapThreadList<TeamInfo, Team, ProfileList>(
 				makeparty, tuple.get1(), tuple.get2());
 	}
Index: /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 30)
@@ -41,5 +41,5 @@
 	private DefaultSessionState1 state1, state1a, stateA, stateE, stateF;
 	// The 1 is because we are testing an instance, not the abstract class
-	private String state1string = "{\"DefaultSessionState1\":{\"actions\":[],\"progress\":{\"time\":{\"duration\":1000,\"start\":1000}},\"settings\":{\"SAOPSettings\":{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}},\"error\":null,\"partyprofiles\":{\"party1\":{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}}}}";
+	private String state1string = "{\"DefaultSessionState1\":{\"actions\":[],\"progress\":{\"time\":{\"duration\":1000,\"start\":1000}},\"settings\":{\"SAOPSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}},\"error\":null,\"partyprofiles\":{\"party1\":{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}}}}";
 	private ProgressTime progr;
 	private final Date start = new Date(1000l);
@@ -60,7 +60,7 @@
 				new PartyRef("party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("profile2");
-		List<OnePartyTeam> participants = Arrays.asList(
-				new OnePartyTeam(party1ref, profile1),
-				new OnePartyTeam(party2ref, profile2));
+		List<TeamInfo> participants = Arrays.asList(
+				new TeamInfo(party1ref, profile1),
+				new TeamInfo(party2ref, profile2));
 		SessionSettings settings = new SAOPSettings(participants, deadline);
 		ProtocolException e = new ProtocolException("test protocol error",
Index: /protocol/src/test/java/geniusweb/protocol/session/TeamInfoTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/TeamInfoTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/TeamInfoTest.java	(revision 30)
@@ -0,0 +1,73 @@
+package geniusweb.protocol.session;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+
+public class TeamInfoTest extends GeneralTests<TeamInfo> {
+
+	private TeamInfo team1 = new TeamInfo(Collections.emptyList());
+	private TeamInfo team1a = new TeamInfo(Collections.emptyList());
+	private PartyRef partyref;
+	private Parameters parameters = new Parameters();;
+	private ProfileRef profileref;
+	private TeamInfo team2, team2a;
+	private String asString1 = "{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}]}}";
+
+	@Before
+	public void before() throws URISyntaxException {
+		partyref = new PartyRef("party1");
+		profileref = new ProfileRef("profile1");
+		team2 = new TeamInfo(new PartyWithParameters(partyref, parameters),
+				profileref);
+		team2a = new TeamInfo(new PartyWithParameters(partyref, parameters),
+				profileref);
+
+	}
+
+	@Override
+	public List<List<TeamInfo>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(team1, team1a),
+				Arrays.asList(team2, team2a));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("TeamInfo.\\[\\].",
+				"TeamInfo.\\[PartyWithProfile.*party1.*profile1.*\\].*");
+	}
+
+	@Test
+	public void testSerialize() throws JsonProcessingException {
+
+		ObjectMapper jackson = new ObjectMapper();
+		System.out.println(jackson.writeValueAsString(team2));
+		assertEquals(asString1, jackson.writeValueAsString(team2));
+
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+
+		ObjectMapper jackson = new ObjectMapper();
+		TeamInfo read = jackson.readValue(asString1, TeamInfo.class);
+		assertEquals(team2, read);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 30)
@@ -19,9 +19,10 @@
 import geniusweb.deadline.DeadlineRounds;
 import geniusweb.deadline.DeadlineTime;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
 import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
 import geniusweb.references.ProfileRef;
 import tudelft.utilities.junit.GeneralTests;
@@ -30,11 +31,11 @@
 public class AMOPSettingsTest extends GeneralTests<AMOPSettings> {
 
-	private OnePartyTeam partyprof1 = mock(OnePartyTeam.class);
-	private OnePartyTeam partyprof2 = mock(OnePartyTeam.class);
-	private OnePartyTeam partyprof3 = mock(OnePartyTeam.class);
-	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
+	private TeamInfo partyprof1 = mock(TeamInfo.class);
+	private TeamInfo partyprof2 = mock(TeamInfo.class);
+	private TeamInfo partyprof3 = mock(TeamInfo.class);
+	private List<TeamInfo> participants2 = Arrays.asList(partyprof1,
 			partyprof2);
-	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
-			partyprof2, partyprof3);
+	private List<TeamInfo> participants3 = Arrays.asList(partyprof1, partyprof2,
+			partyprof3);
 
 	private DeadlineTime deadline = mock(DeadlineTime.class);
@@ -47,6 +48,7 @@
 
 	private AMOPSettings sersettings;
-	private final String serialized = "{\"AMOPSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}}}}";
-	private List<OnePartyTeam> participants;
+	private final String serialized = "{\"AMOPSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}}}}";
+	private List<TeamInfo> participants;
+	private PartyWithProfile partywithprof1, partywithprof2;
 
 	@Before
@@ -60,4 +62,6 @@
 		when(partyprof3.toString()).thenReturn("party and profile 3");
 
+		when(partyprof3.getSize()).thenReturn(1);
+
 		// SERIALIZABLE version with REAL objects. Still no workaround for
 		// this...
@@ -68,7 +72,9 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
-		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
-		participants = Arrays.asList(partywithprof1, partywithprof2);
+		partywithprof1 = new PartyWithProfile(party1, profile1);
+		partywithprof2 = new PartyWithProfile(party2, profile2);
+		participants = Arrays.asList(
+				new TeamInfo(Arrays.asList(partywithprof1)),
+				new TeamInfo(Arrays.asList(partywithprof2)));
 
 		Deadline deadlinetime = new DeadlineTime(100);
@@ -145,5 +151,6 @@
 	@Test
 	public void testGetAllParties() {
-		assertEquals(participants, sersettings.getAllParties());
+		assertEquals(Arrays.asList(partywithprof1, partywithprof2),
+				sersettings.getAllParties());
 	}
 
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 30)
@@ -19,9 +19,10 @@
 import geniusweb.deadline.DeadlineRounds;
 import geniusweb.deadline.DeadlineTime;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
 import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
 import geniusweb.references.ProfileRef;
 import geniusweb.voting.VotingEvaluator;
@@ -34,11 +35,11 @@
 	private final ObjectMapper jackson = new ObjectMapper();
 	private final Parameters noparams = new Parameters();
-	private OnePartyTeam partyprof1 = mockParty("party1", noparams);
-	private OnePartyTeam partyprof2 = mockParty("party2", noparams);
-	private OnePartyTeam partyprof3 = mockParty("party3", noparams);
-	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
+	private TeamInfo partyprof1 = mockParty("party1", noparams);
+	private TeamInfo partyprof2 = mockParty("party2", noparams);
+	private TeamInfo partyprof3 = mockParty("party3", noparams);
+	private List<TeamInfo> participants2 = Arrays.asList(partyprof1,
 			partyprof2);
-	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
-			partyprof2, partyprof3);
+	private List<TeamInfo> participants3 = Arrays.asList(partyprof1, partyprof2,
+			partyprof3);
 
 	private final VotingEvaluator ve = new LargestAgreement();
@@ -56,11 +57,15 @@
 
 	private MOPACSettings sersettings;
-	private final String serialized = "{\"MOPACSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
-	private List<OnePartyTeam> participants;
+	private final String serialized = "{\"MOPACSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
+	private List<TeamInfo> participants;
+	private PartyWithProfile partywithprof1;
+	private PartyWithProfile partywithprof2;
 
-	private OnePartyTeam mockParty(String partyname, Parameters params) {
-		OnePartyTeam team = mock(OnePartyTeam.class);
+	private TeamInfo mockParty(String partyname, Parameters params) {
+		TeamInfo team = mock(TeamInfo.class);
+		when(team.getSize()).thenReturn(1);
 		PartyWithParameters pwithp = mock(PartyWithParameters.class);
-		when(team.getParty()).thenReturn(pwithp);
+		when(team.getAllParties()).thenReturn(Arrays
+				.asList(new PartyWithProfile(pwithp, mock(ProfileRef.class))));
 		when(team.toString()).thenReturn(partyname);
 		when(pwithp.toString()).thenReturn(partyname);
@@ -84,7 +89,9 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
-		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
-		participants = Arrays.asList(partywithprof1, partywithprof2);
+		partywithprof1 = new PartyWithProfile(party1, profile1);
+		partywithprof2 = new PartyWithProfile(party2, profile2);
+		participants = Arrays.asList(
+				new TeamInfo(Arrays.asList(partywithprof1)),
+				new TeamInfo(Arrays.asList(partywithprof2)));
 
 		Deadline deadlinetime = new DeadlineTime(100);
@@ -160,5 +167,6 @@
 	@Test
 	public void testGetAllParties() {
-		assertEquals(participants, sersettings.getAllParties());
+		assertEquals(Arrays.asList(partywithprof1, partywithprof2),
+				sersettings.getAllParties());
 	}
 
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACStateTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACStateTest.java	(revision 30)
@@ -31,5 +31,5 @@
 import geniusweb.progress.ProgressTime;
 import geniusweb.protocol.ProtocolException;
-import geniusweb.protocol.session.OnePartyTeam;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.protocol.session.mopac.phase.OfferPhase;
 import geniusweb.protocol.session.mopac.phase.Phase;
@@ -60,5 +60,7 @@
 			mock(ProfileRef.class));
 
-	private final String mopacsettingstr = "{\"MOPACSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
+	// copy from MopacSettingsTest. We need settings here but don't want to mock
+	// this large structure.
+	private final String mopacsettingstr = "{\"MOPACSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
 	// create real objects for the GeneralTests and for serialization tests
 	private PartyStates partyStates = new PartyStates(Collections.emptyMap());
@@ -72,5 +74,5 @@
 	private MOPACState state1, state1a, state2, state3, state4, state5, state6;
 
-	private String serialized = "{\"MOPACState\":{\"phase\":{\"OfferPhase\":{\"partyStates\":{\"notYetActed\":[],\"actions\":[],\"agreements\":{},\"walkedAway\":[],\"exceptions\":{},\"powers\":{}},\"evaluator\":{\"LargestAgreement\":{}},\"deadline\":100}},\"actions\":[],\"progress\":{\"time\":{\"duration\":100,\"start\":1001}},\"settings\":{\"MOPACSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}},\"partyprofiles\":{}}}";
+	private String serialized = "{\"MOPACState\":{\"phase\":{\"OfferPhase\":{\"partyStates\":{\"notYetActed\":[],\"actions\":[],\"agreements\":{},\"walkedAway\":[],\"exceptions\":{},\"powers\":{}},\"evaluator\":{\"LargestAgreement\":{}},\"deadline\":100}},\"actions\":[],\"progress\":{\"time\":{\"duration\":100,\"start\":1001}},\"settings\":{\"MOPACSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}},\"partyprofiles\":{}}}";
 
 	@Before
@@ -82,8 +84,10 @@
 		MOPACSettings realsettings = jackson.readValue(mopacsettingstr,
 				MOPACSettings.class);
-		OnePartyTeam party = mock(OnePartyTeam.class);
+		TeamInfo party = mock(TeamInfo.class);
 		PartyWithParameters pwithp = mock(PartyWithParameters.class);
 		when(pwithp.getParameters()).thenReturn(new Parameters());
-		when(party.getParty()).thenReturn(pwithp);
+		when(party.getAllParties()).thenReturn(Arrays
+				.asList(new PartyWithProfile(pwithp, mock(ProfileRef.class))));
+		when(party.getSize()).thenReturn(1);
 		MOPACSettings realsettings2 = realsettings.with(party);
 
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 30)
@@ -47,7 +47,7 @@
 	// makes the test unreadable.
 	private final String setstr = "{\"MOPACSettings\":{\"participants\":["
-			+ "{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},"
-			+ "{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"},"
-			+ "{\"party\":{\"partyref\":\"http://party3\",\"parameters\":{}},\"profile\":\"http://profile3\"}],"
+			+ "{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},"
+			+ "{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}},"
+			+ "{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party3\",\"parameters\":{}},\"profile\":\"http://profile3\"}]}}],"
 			+ "\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
 
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2SettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2SettingsTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2SettingsTest.java	(revision 30)
@@ -0,0 +1,173 @@
+package geniusweb.protocol.session.mopac2;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.deadline.DeadlineRounds;
+import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class MOPAC2SettingsTest extends GeneralTests<MOPAC2Settings> {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+	private final Parameters noparams = new Parameters();
+	private TeamInfo partyprof1 = mockParty("party1", noparams);
+	private TeamInfo partyprof2 = mockParty("party2", noparams);
+	private TeamInfo partyprof3 = mockParty("party3", noparams);
+	private List<TeamInfo> participants2 = Arrays.asList(partyprof1,
+			partyprof2);
+	private List<TeamInfo> participants3 = Arrays.asList(partyprof1, partyprof2,
+			partyprof3);
+
+	private final VotingEvaluatorWithValue ve = new LargestAgreementWithValue();
+
+	private DeadlineTime deadline = mock(DeadlineTime.class);
+	private Deadline deadline2 = mock(Deadline.class);
+	private MOPAC2Settings settings1 = new MOPAC2Settings(participants2,
+			deadline, ve);
+	private MOPAC2Settings settings1a = new MOPAC2Settings(participants2,
+			deadline, ve);
+	private MOPAC2Settings settings2 = new MOPAC2Settings(participants3,
+			deadline, ve);
+	private MOPAC2Settings settings3 = new MOPAC2Settings(participants2,
+			deadline2, ve);
+
+	private MOPAC2Settings sersettings;
+	private final String serialized = "{\"MOPAC2Settings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreementWithValue\":{}}}}";
+	private List<TeamInfo> participants;
+	private PartyWithProfile partywithprof1, partywithprof2;
+
+	private TeamInfo mockParty(String partyname, Parameters params) {
+		TeamInfo team = mock(TeamInfo.class);
+		when(team.getSize()).thenReturn(1);
+		PartyWithParameters pwithp = mock(PartyWithParameters.class);
+		when(team.getAllParties()).thenReturn(Arrays
+				.asList(new PartyWithProfile(pwithp, mock(ProfileRef.class))));
+		when(team.toString()).thenReturn(partyname);
+		when(pwithp.toString()).thenReturn(partyname);
+		when(pwithp.getParameters()).thenReturn(params);
+		return team;
+	}
+
+	@Before
+	public void before() throws URISyntaxException {
+
+		when(deadline.getDuration()).thenReturn(1000l);
+		when(deadline.toString()).thenReturn("deadline");
+		when(deadline2.toString()).thenReturn("deadline2");
+
+		// SERIALIZABLE version with REAL objects. Still no workaround for
+		// this...
+		PartyWithParameters party1 = new PartyWithParameters(
+				new PartyRef("http://party1"), new Parameters());
+		ProfileRef profile1 = new ProfileRef("http://profile1");
+		PartyWithParameters party2 = new PartyWithParameters(
+				new PartyRef("http://party2"), new Parameters());
+		ProfileRef profile2 = new ProfileRef("http://profile2");
+		partywithprof1 = new PartyWithProfile(party1, profile1);
+		partywithprof2 = new PartyWithProfile(party2, profile2);
+		participants = Arrays.asList(
+				new TeamInfo(Arrays.asList(partywithprof1)),
+				new TeamInfo(Arrays.asList(partywithprof2)));
+
+		Deadline deadlinetime = new DeadlineTime(100);
+		sersettings = new MOPAC2Settings(participants, deadlinetime, ve);
+
+	}
+
+	@Override
+	public List<List<MOPAC2Settings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"MOPAC2Settings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPAC2Settings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPAC2Settings.*party1.*party2.*deadline2.*LargestAgreement.*");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("MOPAC2", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new MOPAC2Settings(participants2, null, ve);
+	}
+
+	@Test
+	public void testMaxRuntime() {
+		when(deadline.getDuration()).thenReturn(234l);
+		assertEquals((Double) .234d, settings1.getMaxRunTime());
+	}
+
+	@Test
+	public void testMaxRuntimeRounds() {
+		deadline = mock(DeadlineRounds.class);
+		when(deadline.getDuration()).thenReturn(12000l);
+
+		MOPAC2Settings settings = new MOPAC2Settings(participants2, deadline,
+				ve);
+
+		assertEquals((Double) 12d, settings.getMaxRunTime());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		SessionSettings obj = jackson.readValue(serialized,
+				SessionSettings.class);
+		System.out.println(obj);
+		assertEquals(sersettings, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(sersettings);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test
+	public void testWith() {
+		MOPAC2Settings saop = new MOPAC2Settings(participants2, deadline, ve);
+		SessionSettings saop2 = saop.with(partyprof3);
+		assertEquals(3, saop2.getTeams().size());
+	}
+
+	@Test
+	public void testGetAllParties() {
+		assertEquals(Arrays.asList(partywithprof1, partywithprof2),
+				sersettings.getAllParties());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2StateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2StateTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2StateTest.java	(revision 30)
@@ -0,0 +1,284 @@
+package geniusweb.protocol.session.mopac2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressTime;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.TeamInfo;
+import geniusweb.protocol.session.mopac2.phase.OfferPhase;
+import geniusweb.protocol.session.mopac2.phase.Phase;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class MOPAC2StateTest extends GeneralTests<MOPAC2State> {
+	private static final Date NOW = new Date(1001);
+
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private final MOPAC2Settings settings = mock(MOPAC2Settings.class);
+
+	private final MOPAC2State state = new MOPAC2State(settings);
+
+	private PartyId PARTY1 = new PartyId("party1");
+	private PartyId PARTY2 = new PartyId("party2");
+	private PartyId PARTY3 = new PartyId("party3");
+
+	private PartyWithParameters partyparams1 = mock(PartyWithParameters.class);
+	private Parameters params1 = new Parameters();
+
+	private PartyWithProfile profile1 = new PartyWithProfile(partyparams1,
+			mock(ProfileRef.class));
+
+	// settings is copy from the MOPAC2Settings string. Shortcut to creating
+	// this all manually.
+	private final String mopacsettingstr = "{\"MOPAC2Settings\":"
+			+ "{\"participants\":["
+			+ "{\"TeamInfo\":{\"parties\":[ {\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"} ]}},"
+			+ "{\"TeamInfo\":{\"parties\":[ {\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"} ]}}  ],"
+			+ "\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreementWithValue\":{}}}}";;
+	// create real objects for the GeneralTests and for serialization tests
+	private PartyStates partyStates = new PartyStates(Collections.emptyMap());
+	private Phase phase1 = new OfferPhase(partyStates, 100l,
+			new LargestAgreementWithValue());
+	private Phase phase2 = new OfferPhase(partyStates, 200l,
+			new LargestAgreementWithValue());
+	private Progress progress1 = new ProgressTime(100l, NOW);
+	private Progress progress2 = new ProgressTime(200l, NOW);
+
+	private MOPAC2State state1, state1a, state2, state3, state4, state5, state6;
+
+	private String serialized = "{\"MOPAC2State\":{\"phase\":{\"OfferPhase\":{\"partyStates\":{\"notYetActed\":[],\"actions\":[],\"agreements\":{},\"walkedAway\":[],\"exceptions\":{},\"powers\":{}},\"evaluator\":{\"LargestAgreementWithValue\":{}},\"deadline\":100}},\"actions\":[],\"progress\":{\"time\":{\"duration\":100,\"start\":1001}},\"settings\":{\"MOPAC2Settings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreementWithValue\":{}}}},\"partyprofiles\":{}}}";
+
+	@Before
+	public void before()
+			throws JsonParseException, JsonMappingException, IOException {
+		params1.put("power", 2);
+		when(partyparams1.getParameters()).thenReturn(params1);
+
+		MOPAC2Settings realsettings = jackson.readValue(mopacsettingstr,
+				MOPAC2Settings.class);
+		TeamInfo party = mock(TeamInfo.class);
+		PartyWithParameters pwithp = mock(PartyWithParameters.class);
+		when(pwithp.getParameters()).thenReturn(new Parameters());
+		when(party.getAllParties()).thenReturn(Arrays
+				.asList(new PartyWithProfile(pwithp, mock(ProfileRef.class))));
+		when(party.getSize()).thenReturn(1);
+		MOPAC2Settings realsettings2 = realsettings.with(party);
+
+		state1 = new MOPAC2State(phase1, Collections.emptyList(), progress1,
+				realsettings, Collections.emptyMap());
+		state1a = new MOPAC2State(phase1, Collections.emptyList(), progress1,
+				realsettings, Collections.emptyMap());
+		state2 = new MOPAC2State(phase2, Collections.emptyList(), progress1,
+				realsettings, Collections.emptyMap());
+		state3 = new MOPAC2State(phase1, Arrays.asList(mock(Action.class)),
+				progress1, realsettings, Collections.emptyMap());
+		state4 = new MOPAC2State(phase1, Collections.emptyList(), progress2,
+				realsettings, Collections.emptyMap());
+		state5 = new MOPAC2State(phase1, Collections.emptyList(), progress1,
+				realsettings2, Collections.emptyMap());
+		state6 = new MOPAC2State(phase1, Collections.emptyList(), progress1,
+				realsettings,
+				Collections.singletonMap(PARTY1, mock(PartyWithProfile.class)));
+
+	}
+
+	@Override
+	public List<List<MOPAC2State>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(state1, state1a),
+				Arrays.asList(state2), Arrays.asList(state3),
+				Arrays.asList(state4), Arrays.asList(state5),
+				Arrays.asList(state6));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*",
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*",
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*",
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*",
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*",
+				"MOPAC2State.*OfferPhase.*PartyStates.*,MOPAC2Settings.*PartyWithProfile.*ProgressTime.*");
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void addProfilesBeforeInit() {
+		MOPAC2State st = state.with(PARTY1, profile1);
+		assertTrue(st.getPartyProfiles().containsKey(PARTY1));
+	}
+
+	@Test
+	public void testInit() {
+		// no parties were added, should throw? Go to final mode?
+		// Notice that it's possible to create settings with 0 participants,
+		// because this is needed for Tournament mechanism.
+		MOPAC2State state1 = state
+				.initPhase(new ProgressTime(100l, new Date(0)), 1);
+		assertTrue(state1.getActions().isEmpty());
+		assertTrue(state1.getAgreements().getMap().isEmpty());
+		assertTrue(state1.getPartyProfiles().isEmpty());
+		Phase phase1 = state1.getPhase();
+		assertTrue(phase1 instanceof OfferPhase);
+		OfferPhase offer1 = (OfferPhase) phase1;
+		assertEquals(new PartyStates(Collections.emptyMap()),
+				offer1.getPartyStates());
+	}
+
+	@Test
+	public void getAgreementsTest() {
+		MOPAC2State st = state.initPhase(new ProgressTime(100l, new Date(0)),
+				1);
+		assertTrue(st.getAgreements().getMap().isEmpty());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		MOPAC2State obj = jackson.readValue(serialized, MOPAC2State.class);
+		System.out.println(obj);
+		assertEquals(state1, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(state1);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test
+	public void testNewPhasePossible() {
+
+		Phase phase = mock(Phase.class);
+		List<Action> actions = Collections.emptyList();
+		Progress progress = mock(Progress.class);
+		when(progress.getTerminationTime())
+				.thenReturn(new Date(Phase.PHASE_MAXTIME)); // plenty for new
+															// phase
+		Map<PartyId, PartyWithProfile> partyprofiles = mock(Map.class);
+		MOPAC2State s1 = new MOPAC2State(phase, actions, progress, settings,
+				partyprofiles);
+
+		PartyStates states = mock(PartyStates.class);
+		when(phase.getPartyStates()).thenReturn(states);
+		when(states.getNegotiatingParties())
+				.thenReturn(new HashSet<>(Arrays.asList(PARTY1, PARTY2)));
+		assertTrue(s1.isNewPhasePossible(10));
+	}
+
+	@Test
+	public void testNewPhasePossibleOnly1Party() {
+
+		Phase phase = mock(Phase.class);
+		List<Action> actions = Collections.emptyList();
+		Progress progress = mock(Progress.class);
+		when(progress.getTerminationTime())
+				.thenReturn(new Date(Phase.PHASE_MAXTIME)); // plenty for new
+															// phase
+		Map<PartyId, PartyWithProfile> partyprofiles = mock(Map.class);
+		MOPAC2State s1 = new MOPAC2State(phase, actions, progress, settings,
+				partyprofiles);
+
+		PartyStates states = mock(PartyStates.class);
+		when(phase.getPartyStates()).thenReturn(states);
+		when(states.getNegotiatingParties())
+				.thenReturn(new HashSet<>(Arrays.asList(PARTY1)));
+		assertFalse(s1.isNewPhasePossible(10));
+	}
+
+	@Test
+	public void testNewPhasePossibleNotEnoughTime() {
+
+		Phase phase = mock(Phase.class);
+		List<Action> actions = Collections.emptyList();
+		Progress progress = mock(Progress.class);
+		when(progress.getTerminationTime())
+				.thenReturn(new Date(Phase.PHASE_MINTIME - 10));
+		Map<PartyId, PartyWithProfile> partyprofiles = mock(Map.class);
+		MOPAC2State s1 = new MOPAC2State(phase, actions, progress, settings,
+				partyprofiles);
+
+		PartyStates states = mock(PartyStates.class);
+		when(phase.getPartyStates()).thenReturn(states);
+		when(states.getNegotiatingParties())
+				.thenReturn(new HashSet<>(Arrays.asList(PARTY1)));
+		assertFalse(s1.isNewPhasePossible(10));
+	}
+
+	@Test
+	public void nextPhaseTest() {
+		long NOW = 102;
+		Phase phase = mock(Phase.class);
+		List<Action> actions = Collections.emptyList();
+		Progress progress = mock(Progress.class);
+		when(progress.getTerminationTime())
+				.thenReturn(new Date(Phase.PHASE_MAXTIME));
+		Map<PartyId, PartyWithProfile> partyprofiles = mock(Map.class);
+		MOPAC2State s1 = new MOPAC2State(phase, actions, progress, settings,
+				partyprofiles);
+
+		// PhaseB is the next phase, with remaining time
+		Phase phaseB = mock(Phase.class);
+		when(phase.next(eq(NOW), eq(Phase.PHASE_MAXTIME - NOW)))
+				.thenReturn(phaseB);
+
+		MOPAC2State s2 = s1.nextPhase(NOW);
+		assertEquals(phaseB, s2.getPhase());
+	}
+
+	@Test
+	public void exceptionTest() {
+		Phase phase = mock(Phase.class);
+		List<Action> actions = Collections.emptyList();
+		Progress progress = mock(Progress.class);
+		Map<PartyId, PartyWithProfile> partyprofiles = mock(Map.class);
+		MOPAC2State s1 = new MOPAC2State(phase, actions, progress, settings,
+				partyprofiles);
+
+		Phase exphase = mock(Phase.class); // supposed phase that contains the
+											// exception
+		when(phase.with(any(ProtocolException.class))).thenReturn(exphase);
+
+		MOPAC2State errstate = s1.with(new ProtocolException("test", PARTY1));
+		assertEquals(exphase, errstate.getPhase());
+		assertEquals(progress, errstate.getProgress());
+
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2Test.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2Test.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/MOPAC2Test.java	(revision 30)
@@ -0,0 +1,218 @@
+package geniusweb.protocol.session.mopac2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.session.mopac2.phase.OfferPhase;
+import geniusweb.protocol.session.mopac2.phase.Phase;
+import geniusweb.protocol.session.mopac2.phase.VotingPhase;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+public class MOPAC2Test {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final PartyId PARTY1ID = new PartyId("party1");
+	private static final PartyId PARTY2ID = new PartyId("party2");
+	private static final PartyId PARTY3ID = new PartyId("party3");
+
+	// don't try to mock settings, all parts are needed and mocking
+	// makes the test unreadable.
+	// notice, 3 parties, this differs from MopacSettingsTest output.
+	private final String setstr = "{\"MOPAC2Settings\":{\"participants\":["
+			+ "{\"TeamInfo\":{\"parties\":[  {\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"} ]}},"
+			+ "{\"TeamInfo\":{\"parties\":[ {\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}},"
+			+ "{\"TeamInfo\":{\"parties\":[ {\"party\":{\"partyref\":\"http://party3\",\"parameters\":{}},\"profile\":\"http://profile3\"} ]}}  ],"
+			+ "\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreementWithValue\":{}}}}";
+
+	// {"MOPAC2Settings":{"participants":[{"TeamInfo":{"parties":[{"party":{"partyref":"http://party1","parameters":{}},"profile":"http://profile1"}]}},{"TeamInfo":{"parties":[{"party":{"partyref":"http://party2","parameters":{}},"profile":"http://profile2"}]}}],"deadline":{"deadlinetime":{"durationms":100}},"votingevaluator":{"LargestAgreementWithValue":{}}}}
+
+	private MOPAC2Settings settings;
+	private Reporter logger = mock(Reporter.class);
+	private MOPAC2 mopac;
+
+	private final ProtocolToPartyConnFactory factory = mock(
+			ProtocolToPartyConnFactory.class);
+	private final ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
+			conn2 = mock(ProtocolToPartyConn.class),
+			conn3 = mock(ProtocolToPartyConn.class);
+
+	private List<ProtocolToPartyConn> connections;
+
+	@Before
+	public void before() throws JsonParseException, JsonMappingException,
+			IOException, NoResourcesNowException {
+		settings = jackson.readValue(setstr, MOPAC2Settings.class);
+		mopac = settings.getProtocol(logger);
+
+		connections = Arrays.asList(conn1, conn2, conn3);
+		when(factory.connect(any(List.class))).thenReturn(connections);
+
+		// hack the state.with(connection) stuff simplistically
+
+		when(conn1.getParty()).thenReturn(PARTY1ID);
+		when(conn2.getParty()).thenReturn(PARTY2ID);
+		when(conn3.getParty()).thenReturn(PARTY3ID);
+	}
+
+	@Test
+	public void testSmoke() {
+	}
+
+	@Test
+	public void testGetDescr() {
+		assertNotNull(mopac.getDescription());
+	}
+
+	@Test
+	public void testInitialState() {
+		MOPAC2State state = mopac.getState();
+		assertEquals(settings, state.getSettings());
+		assertTrue(state.getActions().isEmpty());
+	}
+
+	@Test
+	public void testProtocol() {
+		assertEquals("MOPAC2", mopac.getRef().getURI().getPath());
+	}
+
+	@Test
+	public void testWorkingCheckDeadline()
+			throws IOException, NoResourcesNowException, InterruptedException {
+
+		mopac.start(factory);
+
+		assertFalse(mopac.getState().isFinal(System.currentTimeMillis()));
+		// we have deadline in 100ms so check
+		Thread.sleep(200);
+		assertTrue(mopac.getState().isFinal(System.currentTimeMillis()));
+	}
+
+	@Test
+	public void testSubscribedToConnections() {
+		mopac.start(factory);
+		verify(conn1, times(1)).addListener(any());
+		verify(conn2, times(1)).addListener(any());
+		verify(conn3, times(1)).addListener(any());
+	}
+
+	@Test
+	public void testActionIsBroadcast()
+			throws IOException, NoResourcesNowException, InterruptedException {
+
+		mopac.start(factory);
+		Action offer = mock(Offer.class);
+		mopac.actionRequest(conn1, offer, 1l);
+
+		// 1 time for YourTurn, one time for ActionDone.
+		// but filtering ActionDone here (instead of Action) does not work,
+		// maybe bug in Mockito?
+		verify(conn1, times(2)).send(any(Inform.class));
+	}
+
+	/**
+	 * Fatal error occurs, run should fail
+	 */
+	@Test(expected = RuntimeException.class)
+	public void testPartyFailsConnect()
+			throws IOException, NoResourcesNowException {
+		ProtocolToPartyConnFactory factory1 = mock(
+				ProtocolToPartyConnFactory.class);
+		// irrecoverable fail test
+		when(factory1.connect(any(List.class)))
+				.thenThrow(new IOException("Failed to connect to parties"));
+
+		mopac.start(factory1);
+
+	}
+
+	/**
+	 * Nonfatal error occurs, protocol should retry till success
+	 */
+	@Test
+	public void testPartyFailsConnect2ndtimeOK()
+			throws IOException, NoResourcesNowException {
+		long now = System.currentTimeMillis();
+		ProtocolToPartyConnFactory factory1 = mock(
+				ProtocolToPartyConnFactory.class);
+		// recoverable fail, and then success.
+		when(factory1.connect(any(List.class)))
+				.thenThrow(new NoResourcesNowException("try again later",
+						new Date(now + 100)))
+				.thenReturn(connections);
+
+		mopac.start(factory1);
+
+	}
+
+	/**
+	 * Test that in Offer phase, and party does action, the next phase is
+	 * entered.
+	 */
+	@Test
+	public void testProceedsNextPhase() {
+		// the state before the last party makes the offer
+		MOPAC2State offerstate = mock(MOPAC2State.class);
+		when(offerstate.getSettings()).thenReturn(settings);
+		OfferPhase offerphase = mock(OfferPhase.class);
+		when(offerphase.isFinal(anyLong())).thenReturn(false);
+		when(offerstate.getPhase()).thenReturn(offerphase);
+
+		// the phase where all parties placed an offer
+		OfferPhase finalofferphase = mock(OfferPhase.class);
+		when(finalofferphase.isFinal(anyLong())).thenReturn(true);
+		MOPAC2State finalofferstate = mock(MOPAC2State.class);
+		when(finalofferstate.getPhase()).thenReturn(finalofferphase);
+		when(finalofferstate.finishPhase()).thenReturn(finalofferstate);
+
+		when(offerstate.with(any(PartyId.class), any(Action.class), anyLong()))
+				.thenReturn(finalofferstate);
+
+		// the phase where all parties can start voting.
+		// this is the state to be reached.
+		long now = System.currentTimeMillis();
+		MOPAC2State votingstate = mock(MOPAC2State.class);
+		Phase votingphase = mock(VotingPhase.class);
+		PartyStates votingpartystates = mock(PartyStates.class);
+		when(votingphase.getDeadline()).thenReturn(now + 100);
+		when(votingphase.getPartyStates()).thenReturn(votingpartystates);
+		when(votingstate.getPhase()).thenReturn(votingphase);
+
+		when(finalofferstate.nextPhase(anyLong())).thenReturn(votingstate);
+
+		mopac = new MOPAC2(offerstate, logger);
+		Offer action = mock(Offer.class);
+		mopac.actionRequest(conn3, action, now);
+
+		// check that the action result in reaching the voting phase
+		assertEquals(votingstate, mopac.getState());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/PartyStatesTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/PartyStatesTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/PartyStatesTest.java	(revision 30)
@@ -0,0 +1,112 @@
+package geniusweb.protocol.session.mopac2;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+import tudelft.utilities.junit.GeneralTests;
+
+public class PartyStatesTest extends GeneralTests<PartyStates> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states1, states1a, states2;
+	private final String serialized = "{\"notYetActed\":[\"party2\",\"party1\",\"party3\"],\"actions\":[],\"agreements\":{},\"walkedAway\":[],\"exceptions\":{},\"powers\":{\"party2\":3,\"party1\":2,\"party3\":3}}";
+
+	public PartyStatesTest() {
+
+		// FIXME use the other constructor to really test this.
+//		PartyStates(Set<PartyId> notYetActed, List<Action> actions,
+//				Agreements agreements, List<PartyId> walkedAway,
+//				Map<PartyId, ProtocolException> exceptions,
+//				Map<PartyId, Integer> powers)
+//		
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		states2 = new PartyStates(powers);
+		powers.put(party3, 3);
+		states1 = new PartyStates(powers);
+		states1a = new PartyStates(powers);
+	}
+
+	@Override
+	public List<List<PartyStates>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(states1, states1a),
+				Arrays.asList(states2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"PartyStates.*\\[party2, party1, party3\\],\\[\\],Agreements\\{\\},\\[\\],\\{\\}.*",
+				"PartyStates.*\\[party2, party1\\],\\[\\],Agreements\\{\\},\\[\\],\\{\\}.*");
+	}
+
+	@Test
+	public void testBasics() {
+		assertEquals(powers.keySet(), states1.getNegotiatingParties());
+		assertEquals(powers.keySet(), states1.getNotYetActed());
+		assertEquals(0, states1.getExceptions().size());
+	}
+
+	@Test
+	public void testException() {
+		PartyStates newstates = states1
+				.with(new ProtocolException("bla", party1));
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states1.getNegotiatingParties());
+		assertEquals(new HashSet<>(Arrays.asList(party2, party3)),
+				newstates.getNotYetActed());
+	}
+
+	@Test
+	public void testFinish() {
+		PartyStates newstates = states1.finish();
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states1.getNegotiatingParties());
+		assertEquals(Collections.emptySet(), newstates.getNotYetActed());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		PartyStates obj = jackson.readValue(serialized, PartyStates.class);
+		System.out.println(obj);
+		assertEquals(states1, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(states1);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test
+	public void testWalkAway() {
+		PartyStates walkawaystate = states1.withWalkAway(party2);
+		assertEquals(new HashSet<>(Arrays.asList(party1, party3)),
+				walkawaystate.getNotYetActed());
+		assertEquals(Arrays.asList(party2), walkawaystate.getWalkedAway());
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OfferPhaseTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OfferPhaseTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OfferPhaseTest.java	(revision 30)
@@ -0,0 +1,272 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+import tudelft.utilities.junit.GeneralTests;
+
+/**
+ * We also test defaultPhase here.
+ */
+public class OfferPhaseTest extends GeneralTests<OfferPhase> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final long DEADLINE = 10l;
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states, states2;
+	private final VotingEvaluatorWithValue evaluator = new LargestAgreementWithValue();
+	private final OfferPhase phase, phase1, phase1a, phase2, phase3;
+
+	// just a test bid
+	private final Bid bid = new Bid("issue", new DiscreteValue("yes"));
+	private final Offer offer = new Offer(party1, bid);
+
+	private final String serialized = "{\"OfferPhase\":{\"partyStates\":{\"notYetActed\":[\"party2\",\"party1\",\"party3\"],\"actions\":[],\"agreements\":{},\"walkedAway\":[],\"exceptions\":{},\"powers\":{\"party2\":3,\"party1\":2,\"party3\":3}},\"evaluator\":{\"LargestAgreementWithValue\":{}},\"deadline\":10}}";
+
+	public OfferPhaseTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		states2 = new PartyStates(powers);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+		phase = new OfferPhase(states, DEADLINE, evaluator);
+
+		phase1 = new OfferPhase(states, 10l, evaluator);
+		phase1a = new OfferPhase(states, 10l, evaluator);
+		phase2 = new OfferPhase(states2, 10l, evaluator);
+		phase3 = new OfferPhase(states, 20l, evaluator);
+
+	}
+
+	@Override
+	public List<List<OfferPhase>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(phase1, phase1a),
+				Arrays.asList(phase2), Arrays.asList(phase3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"OfferPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"OfferPhase.*PartyStates.*party2, party1.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"OfferPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],20,LargestAgreement.*");
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void testInitState() {
+		assertEquals(3, phase.getPartyStates().getNotYetActed().size());
+	}
+
+	@Test
+	public void testInform() {
+		assertEquals(new YourTurn(), phase.getInform());
+	}
+
+	@Test
+	public void isFinalTest() {
+		assertFalse(phase.isFinal(1l));
+		assertTrue(phase.isFinal(10l));
+	}
+
+	@Test
+	public void isFinalTestActorNotYetActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed())
+				.thenReturn(Collections.singleton(party1));
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertFalse(testphase.isFinal(1l));
+	}
+
+	@Test
+	public void isFinalTestAllActorsActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed()).thenReturn(Collections.emptySet());
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertTrue(testphase.isFinal(1l));
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkIncorrectActorTest() throws ProtocolException {
+		phase.checkAction(party1, new EndNegotiation(party2), 1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkNotAllowedActionTest() throws ProtocolException {
+		phase.checkAction(party1, new Votes(party2, Collections.emptySet()), 1);
+	}
+
+	@Test
+	public void testException() {
+		OfferPhase newphase = phase.with(new ProtocolException("bla", party1));
+		assertEquals(10l, newphase.getDeadline());
+		assertEquals(0l, phase.getPartyStates().getActions().size());
+	}
+
+	@Test
+	public void testFinish() {
+		Phase ph = phase.finish();
+		assertTrue(ph.getPartyStates().getNotYetActed().isEmpty());
+		assertEquals(3, ph.getPartyStates().getExceptions().size());
+		assertTrue(phase.getPartyStates().getAgreements().getMap().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextWrong() {
+		// state is not final, should not work
+		phase.next(1, 1000);
+	}
+
+	@Test
+	public void testNextNoParties() {
+		Phase ph = phase.finish();
+		// no parties are left now, all failed
+		// but this is not part of the next() check.
+		Phase next = ph.next(1, 1000);
+		assertTrue(next instanceof VotingPhase);
+		// no remaining parties, since finish kicked all
+		assertTrue(next.getPartyStates().getNotYetActed().isEmpty());
+	}
+
+	@Test
+	public void testAllowed() {
+		phase.with(party1, offer, 1);
+	}
+
+	@Test
+	public void testAllowedWrongClass() {
+		try {
+			phase.checkAction(party1, new Accept(party1, bid), 1);
+		} catch (ProtocolException e) {
+			assertTrue(e.getMessage().contains("not allowed in OfferPhase"));
+			return;
+		}
+		fail("checkAction did not throw as expected");
+	}
+
+	@Test
+	public void testAllowedWrongActor() {
+		try {
+			phase.checkAction(party1, new Accept(party2, bid), 1);
+		} catch (ProtocolException e) {
+			assertTrue(
+					e.getMessage().contains("Incorrect actor info in action"));
+			return;
+		}
+		fail("checkAction did not throw as expected");
+	}
+
+	@Test
+	public void testAllowedActorAlreadyActed() {
+		OfferPhase testphase = phase.with(party1, offer, 1);
+
+		try {
+			testphase.checkAction(party1, offer, 2);
+		} catch (ProtocolException e) {
+			assertTrue(e.getMessage().contains("can not act anymore"));
+			return;
+		}
+		fail("checkAction did not throw as expected");
+	}
+
+	@Test
+	public void testAllowedActorAlreadyActed1() {
+		// as theoprevious test, but using with() instead of checkAction
+		OfferPhase testphase = phase.with(party1, offer, 1);
+
+		OfferPhase newphase = testphase.with(party1, offer, 2);
+		// the party must remain as acted, because we can't retract his
+		// action...
+		assertEquals(1, newphase.getPartyStates().getActions().size());
+		assertEquals(offer, newphase.getPartyStates().getActions().get(0));
+		assertFalse(
+				newphase.getPartyStates().getExceptions().containsKey(party1));
+	}
+
+	@Test
+	public void testAllowedActingTooLate() {
+
+		try {
+			phase.checkAction(party1, offer, DEADLINE + 1);
+		} catch (ProtocolException e) {
+			assertTrue(e.getMessage().contains("passed deadline"));
+			return;
+		}
+		fail("checkAction did not throw as expected");
+	}
+
+	@Test
+	public void getOffersTest() {
+		assertEquals(Collections.emptyList(), phase.getOffers());
+		List<Offer> offs = phase.with(party1, offer, 1).getOffers();
+		assertEquals(1, offs.size());
+		assertEquals(bid, offs.get(0).getBid());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNextTooShortDuration() {
+		phase.next(1, Phase.PHASE_MINTIME - 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextNotFinished() {
+		phase.next(1, Phase.PHASE_MINTIME + 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNext() {
+		phase.next(11, Phase.PHASE_MINTIME + 1);
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		Phase obj = jackson.readValue(serialized, Phase.class);
+		System.out.println(obj);
+		assertEquals(phase1, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(phase1);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OptInPhaseTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OptInPhaseTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/OptInPhaseTest.java	(revision 30)
@@ -0,0 +1,202 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.Votes;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.inform.OptInWithValue;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+import tudelft.utilities.junit.GeneralTests;
+
+/**
+ * We also test defaultPhase here.
+ */
+public class OptInPhaseTest extends GeneralTests<OptInPhase> {
+
+	private static final long DEADLINE = 10l;
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	// private final VotingPhase prevPhase = mock(VotingPhase.class);
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states, states2;
+	private final VotingEvaluatorWithValue evaluator = new LargestAgreementWithValue();
+	private final OptInPhase phase, phase1, phase1a, phase2, phase3;
+
+	// just a test bid
+	private final Bid bid = new Bid("issue", new DiscreteValue("yes"));
+	private final VoteWithValue vote = new VoteWithValue(party1, bid, 2, 3,
+			100);
+	private final VotesWithValue votes = new VotesWithValue(party1,
+			Collections.singleton(vote));
+
+	private final List<VotesWithValue> prevVotes = Arrays.asList(votes);
+
+	public OptInPhaseTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		states2 = new PartyStates(powers);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+
+		phase = new OptInPhase(prevVotes, states, DEADLINE, evaluator);
+
+		// in these phases we just don't set prevPhase.
+		// this to avoid serialization troubles.
+		phase1 = new OptInPhase(null, states, 10l, evaluator);
+		phase1a = new OptInPhase(null, states, 10l, evaluator);
+		phase2 = new OptInPhase(null, states2, 10l, evaluator);
+		phase3 = new OptInPhase(null, states, 20l, evaluator);
+
+	}
+
+	@Override
+	public List<List<OptInPhase>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(phase1, phase1a),
+				Arrays.asList(phase2), Arrays.asList(phase3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"OptInPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"OptInPhase.*PartyStates.*party2, party1.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"OptInPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],20,LargestAgreement.*");
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void testInitState() {
+		assertEquals(3, phase.getPartyStates().getNotYetActed().size());
+	}
+
+	@Test
+	public void testInform() {
+		// when(prevPhase.getVotes()).thenReturn(Arrays.asList(votes));
+		assertEquals(new OptInWithValue(Arrays.asList(votes)),
+				phase.getInform());
+
+	}
+
+	@Test
+	public void isFinalTest() {
+		assertFalse(phase.isFinal(1l));
+		assertTrue(phase.isFinal(10l));
+	}
+
+	@Test
+	public void isFinalTestActorNotYetActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed())
+				.thenReturn(Collections.singleton(party1));
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertFalse(testphase.isFinal(1l));
+	}
+
+	@Test
+	public void isFinalTestAllActorsActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed()).thenReturn(Collections.emptySet());
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertTrue(testphase.isFinal(1l));
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkIncorrectActorTest() throws ProtocolException {
+		phase.checkAction(party1, new EndNegotiation(party2), 1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkNotAllowedActionTest() throws ProtocolException {
+		phase.checkAction(party1, new Votes(party2, Collections.emptySet()), 1);
+	}
+
+	@Test
+	public void testFinish() {
+		Phase ph = phase.finish();
+		assertTrue(ph.getPartyStates().getNotYetActed().isEmpty());
+		assertEquals(3, ph.getPartyStates().getExceptions().size());
+		assertTrue(phase.getPartyStates().getAgreements().getMap().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextWrong() {
+		// state is not final, should not work
+		phase.next(1, 1000);
+	}
+
+	@Test
+	public void testNextNoParties() {
+		Phase ph = phase.finish();
+		// no parties are left now, all failed
+		// but this is not part of the next() check.
+		Phase next = ph.next(1, 1000);
+		assertTrue(next instanceof OfferPhase);
+		// no remaining parties, since finish kicked all
+		assertTrue(next.getPartyStates().getNotYetActed().isEmpty());
+	}
+
+	@Test
+	public void testAllowed() {
+		// when(prevPhase.getVotes()).thenReturn(Arrays.asList(votes));
+		phase.with(party1, votes, 1l);
+	}
+
+	@Test
+	public void testAllowedNarrowingVote() {
+		// the previous vote was maxPower=3,
+		// so this is an illegal narrowing of the previous vote
+		Vote newVote = new Vote(party1, bid, 2, 2);
+		Votes newVotes = new Votes(party1, Collections.singleton(newVote));
+
+		// when(prevPhase.getVotes()).thenReturn(Arrays.asList(votes));
+
+		// check that party1 was kicked
+		Phase newphase = phase.with(party1, newVotes, 1l);
+		assertTrue(
+				newphase.getPartyStates().getExceptions().containsKey(party1));
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNextTooShortDuration() {
+		phase.next(1, Phase.PHASE_MINTIME - 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextNotFinished() {
+		phase.next(1, Phase.PHASE_MINTIME + 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNext() {
+		phase.next(11, Phase.PHASE_MINTIME + 1);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/VotingPhaseTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/VotingPhaseTest.java	(revision 30)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac2/phase/VotingPhaseTest.java	(revision 30)
@@ -0,0 +1,192 @@
+package geniusweb.protocol.session.mopac2.phase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.VoteWithValue;
+import geniusweb.actions.VotesWithValue;
+import geniusweb.inform.Voting;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac2.PartyStates;
+import geniusweb.voting.VotingEvaluatorWithValue;
+import geniusweb.voting.evaluatorwithvalue.LargestAgreementWithValue;
+import tudelft.utilities.junit.GeneralTests;
+
+/**
+ * We also test defaultPhase here.
+ */
+public class VotingPhaseTest extends GeneralTests<VotingPhase> {
+
+	private static final long DEADLINE = 10l;
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	// private final OfferPhase prevPhase = mock(OfferPhase.class);
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states, states2;
+	private final VotingEvaluatorWithValue evaluator = new LargestAgreementWithValue();
+	private final VotingPhase phase, phase1, phase1a, phase2, phase3;
+
+	// just a test bid
+	private final Bid bid = new Bid("issue", new DiscreteValue("yes"));
+	private final VoteWithValue vote = new VoteWithValue(party1, bid, 2, 3,
+			100);
+	private final VotesWithValue votes = new VotesWithValue(party1,
+			Collections.singleton(vote));
+
+	private final List<Offer> prevPhase = Arrays.asList(new Offer(party1, bid));
+
+	public VotingPhaseTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		states2 = new PartyStates(powers);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+
+		phase = new VotingPhase(prevPhase, states, DEADLINE, evaluator);
+
+		// in these phases we just don't set prevPhase.
+		// this to avoid serialization troubles.
+		phase1 = new VotingPhase(null, states, 10l, evaluator);
+		phase1a = new VotingPhase(null, states, 10l, evaluator);
+		phase2 = new VotingPhase(null, states2, 10l, evaluator);
+		phase3 = new VotingPhase(null, states, 20l, evaluator);
+
+	}
+
+	@Override
+	public List<List<VotingPhase>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(phase1, phase1a),
+				Arrays.asList(phase2), Arrays.asList(phase3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"VotingPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"VotingPhase.*PartyStates.*party2, party1.*\\[\\],Agreements.*\\[\\],\\{\\}.*],10,LargestAgreement.*",
+				"VotingPhase.*PartyStates.*party2, party1, party3.*\\[\\],Agreements.*\\[\\],\\{\\}.*],20,LargestAgreement.*");
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void testInitState() {
+		assertEquals(3, phase.getPartyStates().getNotYetActed().size());
+	}
+
+	@Test
+	public void testInform() {
+		// when(prevPhase.getOffers()).thenReturn(Arrays.asList(bid));
+		assertEquals(new Voting(Arrays.asList(new Offer(party1, bid)), powers),
+				phase.getInform());
+
+	}
+
+	@Test
+	public void isFinalTest() {
+		assertFalse(phase.isFinal(1l));
+		assertTrue(phase.isFinal(10l));
+	}
+
+	@Test
+	public void isFinalTestActorNotYetActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed())
+				.thenReturn(Collections.singleton(party1));
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertFalse(testphase.isFinal(1l));
+	}
+
+	@Test
+	public void isFinalTestAllActorsActed() {
+		PartyStates actedstate = mock(PartyStates.class);
+		when(actedstate.getNotYetActed()).thenReturn(Collections.emptySet());
+		OfferPhase testphase = new OfferPhase(actedstate, 10l, evaluator);
+		assertTrue(testphase.isFinal(1l));
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkIncorrectActorTest() throws ProtocolException {
+		phase.checkAction(party1, new EndNegotiation(party2), 1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkNotAllowedActionTest() throws ProtocolException {
+		phase.checkAction(party2,
+				new VotesWithValue(party1, Collections.singleton(vote)), 1);
+	}
+
+	@Test
+	public void testFinish() {
+		Phase ph = phase.finish();
+		assertTrue(ph.getPartyStates().getNotYetActed().isEmpty());
+		assertEquals(3, ph.getPartyStates().getExceptions().size());
+		assertTrue(phase.getPartyStates().getAgreements().getMap().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextWrong() {
+		// state is not final, should not work
+		phase.next(1, 1000);
+	}
+
+	@Test
+	public void testNextNoParties() {
+		Phase ph = phase.finish();
+		// no parties are left now, all failed
+		// but this is not part of the next() check.
+		Phase next = ph.next(1, 1000);
+		assertTrue(next instanceof OptInPhase);
+		// no remaining parties, since finish kicked all
+		assertTrue(next.getPartyStates().getNotYetActed().isEmpty());
+	}
+
+	@Test
+	public void testAllowed() {
+		phase.with(party1, votes, 1);
+	}
+
+	@Test
+	public void getVotesTest() {
+		assertEquals(Collections.emptyList(), phase.getVotes());
+		List<VotesWithValue> vts = phase.with(party1, votes, 1).getVotes();
+		assertEquals(Arrays.asList(votes), vts);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNextTooShortDuration() {
+		phase.next(1, Phase.PHASE_MINTIME - 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNextNotFinished() {
+		phase.next(1, Phase.PHASE_MINTIME + 1);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testNext() {
+		phase.next(11, Phase.PHASE_MINTIME + 1);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 30)
@@ -19,6 +19,6 @@
 import geniusweb.deadline.DeadlineRounds;
 import geniusweb.deadline.DeadlineTime;
-import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -30,11 +30,11 @@
 public class SAOPSettingsTest extends GeneralTests<SAOPSettings> {
 
-	private OnePartyTeam partyprof1 = mock(OnePartyTeam.class);
-	private OnePartyTeam partyprof2 = mock(OnePartyTeam.class);
-	private OnePartyTeam partyprof3 = mock(OnePartyTeam.class);
-	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
+	private TeamInfo partyprof1 = mock(TeamInfo.class);
+	private TeamInfo partyprof2 = mock(TeamInfo.class);
+	private TeamInfo partyprof3 = mock(TeamInfo.class);
+	private List<TeamInfo> participants2 = Arrays.asList(partyprof1,
 			partyprof2);
-	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
-			partyprof2, partyprof3);
+	private List<TeamInfo> participants3 = Arrays.asList(partyprof1, partyprof2,
+			partyprof3);
 
 	private DeadlineTime deadline = mock(DeadlineTime.class);
@@ -47,5 +47,5 @@
 
 	private SAOPSettings sersettings;
-	private final String serialized = "{\"SAOPSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}}}}";
+	private final String serialized = "{\"SAOPSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}}}}";
 
 	@Before
@@ -54,4 +54,8 @@
 		when(deadline.toString()).thenReturn("deadline");
 		when(deadline2.toString()).thenReturn("deadline2");
+
+		when(partyprof1.getSize()).thenReturn(1);
+		when(partyprof2.getSize()).thenReturn(1);
+		when(partyprof3.getSize()).thenReturn(1);
 
 		when(partyprof1.toString()).thenReturn("party and profile 1");
@@ -67,7 +71,7 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
-		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
-		List<OnePartyTeam> participants = Arrays.asList(partywithprof1,
+		TeamInfo partywithprof1 = new TeamInfo(party1, profile1);
+		TeamInfo partywithprof2 = new TeamInfo(party2, profile2);
+		List<TeamInfo> participants = Arrays.asList(partywithprof1,
 				partywithprof2);
 
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 30)
@@ -20,6 +20,6 @@
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
-
+
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -44,6 +44,5 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
-import geniusweb.protocol.session.OnePartyTeam;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -71,5 +70,5 @@
 			finalstate = mock(SAOPState.class);
 
-	private final ProtocolRef SAOP;
+	private ProtocolRef SAOP;
 	private static final PartyId PARTY2ID = new PartyId("party2");
 	private static final PartyId PARTY1ID = new PartyId("party1");
@@ -77,6 +76,6 @@
 	private final PartyRef party2ref = mock(PartyRef.class);
 	private final SAOPSettings settings = mock(SAOPSettings.class);
-	private final OnePartyTeam team1 = mock(OnePartyTeam.class);
-	private final OnePartyTeam team2 = mock(OnePartyTeam.class);
+	private final TeamInfo team1 = mock(TeamInfo.class);
+	private final TeamInfo team2 = mock(TeamInfo.class);
 	private SAOP saop;
 	private ProtocolToPartyConnFactory factory;
@@ -84,5 +83,5 @@
 	private ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
 			conn2 = mock(ProtocolToPartyConn.class);
-	private Map<PartyId, OnePartyTeam> partyprofiles;
+	private Map<PartyId, TeamInfo> partyprofiles;
 	private Progress progress = mock(Progress.class);
 	private ProfileRef profile1;
@@ -102,11 +101,16 @@
 	private final Deadline deadlinetime = mock(DeadlineTime.class);
 
-	public SAOPTest()
+	@Before
+	public void before()
 			throws URISyntaxException, IOException, NoResourcesNowException {
 		SAOP = new ProtocolRef("SAOP");
-		when(team1.getParty()).thenReturn(partywithparam1);
-		when(team2.getParty()).thenReturn(partywithparam2);
-		when(team1.getAllParties()).thenReturn(Arrays.asList(team1));
-		when(team2.getAllParties()).thenReturn(Arrays.asList(team2));
+
+		profile1 = mock(ProfileRef.class);
+		profile2 = mock(ProfileRef.class);
+
+		PartyWithProfile pwp1 = new PartyWithProfile(partywithparam1, profile1);
+		PartyWithProfile pwp2 = new PartyWithProfile(partywithparam2, profile2);
+		when(team1.getAllParties()).thenReturn(Arrays.asList(pwp1));
+		when(team2.getAllParties()).thenReturn(Arrays.asList(pwp2));
 
 		partyprofiles = new HashMap<>();
@@ -116,9 +120,9 @@
 		when(deadlinetime.getDuration()).thenReturn(1000l);
 
-		List<TeamOfPartiesAndProfiles> teams = new ArrayList<>();
+		List<TeamInfo> teams = new ArrayList<>();
 		teams.add(team1);
 		teams.add(team2);
 		when(settings.getTeams()).thenReturn(teams);
-		when(settings.getAllParties()).thenReturn(Arrays.asList(team1, team2));
+		when(settings.getAllParties()).thenReturn(Arrays.asList(pwp1, pwp2));
 		when(settings.getDeadline()).thenReturn(deadlinetime);
 
@@ -131,9 +135,4 @@
 		when(conn1.getParty()).thenReturn(PARTY1ID);
 		when(conn2.getParty()).thenReturn(PARTY2ID);
-
-		profile1 = mock(ProfileRef.class);
-		when(team1.getProfile()).thenReturn(profile1);
-		profile2 = mock(ProfileRef.class);
-		when(team2.getProfile()).thenReturn(profile2);
 
 		mockState(connectedstate, "connected state");
@@ -153,4 +152,10 @@
 		saop = new SAOP(state, new ReportToLogger("test"));
 		saop.addListener(testlistener);
+
+		Map<PartyId, PartyWithProfile> profilesmap = new HashMap<>();
+		profilesmap.put(PARTY1ID, pwp1);
+		profilesmap.put(PARTY2ID, pwp2);
+		when(state.getPartyProfiles()).thenReturn(profilesmap);
+
 	}
 
@@ -164,7 +169,5 @@
 		when(state.getSettings()).thenReturn(settings);
 		when(state.getConnections()).thenReturn(connectionswithparties);
-		when(state.getPartyProfiles())
-				.thenReturn(partyprofiles.entrySet().stream().collect(
-						Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
+
 		when(state.getProgress()).thenReturn(progress);
 		when(state.with(any(ProtocolToPartyConn.class),
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 30)
@@ -17,6 +17,5 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.protocol.session.OnePartyTeam;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -25,11 +24,11 @@
 import tudelft.utilities.junit.GeneralTests;
 
-public class SaopPartyWithProfileTest extends GeneralTests<OnePartyTeam> {
+public class SaopPartyWithProfileTest extends GeneralTests<TeamInfo> {
 
 	private PartyRef party1, party1a, party2, party3;
 	private ProfileRef profile1, profile2;
-	private OnePartyTeam partyprof1, partyprof1a, partyprof2, partyprof3,
+	private TeamInfo partyprof1, partyprof1a, partyprof2, partyprof3,
 			partyprof4;
-	private final String serialized = "{\"party\":{\"partyref\":\"ws:party1\",\"parameters\":{}},\"profile\":\"ws:profile1\"}";
+	private final String serialized = "{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"ws:party1\",\"parameters\":{}},\"profile\":\"ws:profile1\"}]}}";
 
 	@Before
@@ -54,14 +53,14 @@
 				settings1);
 
-		partyprof1 = new OnePartyTeam(party1withparams1, profile1);
-		partyprof1a = new OnePartyTeam(party1withparams1, profile1);
-		partyprof2 = new OnePartyTeam(party2withparams1, profile1);
-		partyprof3 = new OnePartyTeam(party1withparams1, profile2);
-		partyprof4 = new OnePartyTeam(party1withparams2, profile1);
+		partyprof1 = new TeamInfo(party1withparams1, profile1);
+		partyprof1a = new TeamInfo(party1withparams1, profile1);
+		partyprof2 = new TeamInfo(party2withparams1, profile1);
+		partyprof3 = new TeamInfo(party1withparams1, profile2);
+		partyprof4 = new TeamInfo(party1withparams2, profile1);
 
 	}
 
 	@Override
-	public List<List<OnePartyTeam>> getGeneralTestData() {
+	public List<List<TeamInfo>> getGeneralTestData() {
 		return Arrays.asList(Arrays.asList(partyprof1, partyprof1a),
 				Arrays.asList(partyprof2), Arrays.asList(partyprof3),
@@ -72,8 +71,8 @@
 	public List<String> getGeneralTestStrings() {
 		return Arrays.asList(
-				"PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile1.*",
-				"PartyWithProfile.PartyRef.ws:party2.,ProfileRef.ws:profile1.*",
-				"PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile2.*",
-				"PartyWithProfile.PartyRef.ws:party1.\\{a=1\\},ProfileRef.ws:profile1.*");
+				"TeamInfo.*PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile1.*",
+				"TeamInfo.*PartyWithProfile.PartyRef.ws:party2.,ProfileRef.ws:profile1.*",
+				"TeamInfo.*PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile2.*",
+				"TeamInfo.*PartyWithProfile.PartyRef.ws:party1.\\{a=1\\},ProfileRef.ws:profile1.*");
 	}
 
@@ -84,5 +83,5 @@
 	@Test(expected = IllegalArgumentException.class)
 	public void nullTest() throws URISyntaxException {
-		new OnePartyTeam((PartyWithParameters) null, (ProfileRef) null);
+		new TeamInfo((PartyWithParameters) null, (ProfileRef) null);
 	}
 
@@ -102,6 +101,5 @@
 		// always used as part of bigger class, do not include class name
 		// and therfore can not serialize as TeamOfPartiesAndProfiles
-		TeamOfPartiesAndProfiles p = jackson.readValue(serialized,
-				OnePartyTeam.class);
+		TeamInfo p = jackson.readValue(serialized, TeamInfo.class);
 		System.out.println(p);
 		assertEquals(partyprof1, p);
Index: /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java	(revision 30)
@@ -16,4 +16,5 @@
 import geniusweb.deadline.DeadlineTime;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamInfo;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -28,7 +29,7 @@
 	private final ObjectMapper jackson = new ObjectMapper();
 	private Parameters params = new Parameters();
-	private final String serialized = "{\"SHAOPSettings\":{\"participants\":[{\"cob\":{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile1\"},\"shaop\":{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}},{\"cob\":{\"party\":{\"partyref\":\"party4\",\"parameters\":{}},\"profile\":\"profile1\"},\"shaop\":{\"party\":{\"partyref\":\"party3\",\"parameters\":{}},\"profile\":\"profile1\"}}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}}";
+	private final String serialized = "{\"SHAOPSettings\":{\"participants\":[{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile1\"}]}},{\"TeamInfo\":{\"parties\":[{\"party\":{\"partyref\":\"party3\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party4\",\"parameters\":{}},\"profile\":\"profile1\"}]}}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}}";
 	private SHAOPSettings settings1, settings1a, settings2, settings3;
-	private ShaopTeam pair1, pair2, pair3;
+	private TeamInfo pair1, pair2, pair3;
 
 	@Before
@@ -57,7 +58,7 @@
 		DeadlineTime deadline = new DeadlineTime(1000);
 		DeadlineTime deadline2 = new DeadlineTime(2000);
-		pair1 = new ShaopTeam(partyprof1, partyprof2);
-		pair2 = new ShaopTeam(partyprof3, partyprof4);
-		pair3 = new ShaopTeam(partyprof5, partyprof6);
+		pair1 = new TeamInfo(Arrays.asList(partyprof1, partyprof2));
+		pair2 = new TeamInfo(Arrays.asList(partyprof3, partyprof4));
+		pair3 = new TeamInfo(Arrays.asList(partyprof5, partyprof6));
 
 		settings1 = new SHAOPSettings(Arrays.asList(pair1, pair2), deadline);
@@ -76,7 +77,7 @@
 	public List<String> getGeneralTestStrings() {
 		return Arrays.asList(
-				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party3.*party4.*DeadlineTime.*1000.*",
-				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party5.*party6.*DeadlineTime.*1000.*",
-				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party3.*party4.*DeadlineTime.*2000.*");
+				"SHAOPSettings.*TeamInfo.*PartyWithProfile.*party1.*party2.*TeamInfo.*party3.*party4.*DeadlineTime.*1000.*",
+				"SHAOPSettings.*TeamInfo.*PartyWithProfile.*party1.*party2.*TeamInfo.*party5.*party6.*DeadlineTime.*1000.*",
+				"SHAOPSettings.*TeamInfo.*PartyWithProfile.*party1.*party2.*TeamInfo.*party3.*party4.*DeadlineTime.*2000.*");
 	}
 
Index: /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 29)
+++ /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 30)
@@ -32,5 +32,5 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
 import geniusweb.protocol.session.SessionResult;
-import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.TeamInfo;
 import tudelft.utilities.junit.GeneralTests;
 
@@ -75,8 +75,8 @@
 	private Map<PartyId, Integer> partynrs = new HashMap<>();
 
-	private TeamOfPartiesAndProfiles team1 = mock(
-			TeamOfPartiesAndProfiles.class);
-	private TeamOfPartiesAndProfiles team2 = mock(
-			TeamOfPartiesAndProfiles.class);
+	private TeamInfo team1 = mock(
+			TeamInfo.class);
+	private TeamInfo team2 = mock(
+			TeamInfo.class);
 
 	@Before
Index: /pythonadapter/pom.xml
===================================================================
--- /pythonadapter/pom.xml	(revision 29)
+++ /pythonadapter/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>pythonadapter</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>
 
@@ -54,11 +54,15 @@
 			</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>
Index: /references/pom.xml
===================================================================
--- /references/pom.xml	(revision 29)
+++ /references/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>references</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>
 
@@ -32,5 +32,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>listener</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
@@ -38,5 +38,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>repository</artifactId>
-			<version>1.1.1</version>
+			<version>1.1.2</version>
 		</dependency>
 		<!-- the core, which includes Streaming API, shared low-level abstractions 
@@ -96,5 +96,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.3</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -112,14 +112,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: /simplerunner/pom.xml
===================================================================
--- /simplerunner/pom.xml	(revision 29)
+++ /simplerunner/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>simplerunner</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>
 
@@ -64,5 +64,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.2</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -92,11 +92,15 @@
 			</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>
Index: /simplerunner/src/test/resources/mopac.json
===================================================================
--- /simplerunner/src/test/resources/mopac.json	(revision 29)
+++ /simplerunner/src/test/resources/mopac.json	(revision 30)
@@ -1,11 +1,16 @@
 {
 	"MOPACSettings": {
-		"participants": [{
+		"participants": [
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{"power":1}},
 			"profile": "file:src/test/resources/jobs/jobs1.json"
-		}, {
+		} ]}}, 
+		
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{"power":1}},
 			"profile": "file:src/test/resources/jobs/jobs2.json"
-		}],
+		} ]}} ],
 		"deadline": {
 			"deadlinetime": {
Index: /simplerunner/src/test/resources/settings.json
===================================================================
--- /simplerunner/src/test/resources/settings.json	(revision 29)
+++ /simplerunner/src/test/resources/settings.json	(revision 30)
@@ -1,11 +1,16 @@
 {
 	"SAOPSettings": {
-		"participants": [{
+		"participants": [
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{}},
 			"profile": "file:src/test/resources/jobs/jobs1.json"
-		}, {
+		} ]}}, 
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{}},
 			"profile": "file:src/test/resources/jobs/jobs2.json"
-		}],
+		} ]}}
+		],
 		"deadline": {
 			"deadlinetime": {
Index: /simplerunner/src/test/resources/settings2.json
===================================================================
--- /simplerunner/src/test/resources/settings2.json	(revision 29)
+++ /simplerunner/src/test/resources/settings2.json	(revision 30)
@@ -1,11 +1,17 @@
 {
 	"SAOPSettings": {
-		"participants": [{
+		"participants": [
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{}},
 			"profile": "file:src/test/resources/jobs/jobs1partial.json"
-		}, {
+		} ]}}
+		, 
+		{"TeamInfo":{"parties":[
+		{
 			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty","parameters":{}},
 			"profile": "file:src/test/resources/jobs/jobs2.json"
-		}],
+		} ]}}
+		],
 		"deadline": {
 			"deadlinetime": {
Index: /timeline/pom.xml
===================================================================
--- /timeline/pom.xml	(revision 29)
+++ /timeline/pom.xml	(revision 30)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>timeline</artifactId>
-	<version>1.5.8</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.9</version> <!-- must equal ${geniusweb.version} -->
 	<packaging>jar</packaging>
 
@@ -16,5 +16,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>
 
@@ -85,5 +85,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>junit</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.5</version>
 			<scope>test</scope>
 		</dependency>
@@ -100,14 +100,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/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();
+	}
+}
