Index: events/pom.xml
===================================================================
--- events/pom.xml	(revision 20)
+++ events/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>events</artifactId>
-	<version>1.4.4</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.0</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.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -40,5 +40,9 @@
 
 	<dependencies>
-
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>timeline</artifactId>
+			<version>${geniusweb.version}</version>
+		</dependency>
 		<dependency>
 			<groupId>geniusweb</groupId>
Index: events/src/main/java/geniusweb/actions/Action.java
===================================================================
--- events/src/main/java/geniusweb/actions/Action.java	(revision 20)
+++ events/src/main/java/geniusweb/actions/Action.java	(revision 21)
@@ -16,5 +16,7 @@
 		@JsonSubTypes.Type(value = Accept.class),
 		@JsonSubTypes.Type(value = Comparison.class),
-		@JsonSubTypes.Type(value = ElicitComparison.class) })
+		@JsonSubTypes.Type(value = ElicitComparison.class),
+		@JsonSubTypes.Type(value = Vote.class),
+		@JsonSubTypes.Type(value = Votes.class) })
 public interface Action {
 	/**
Index: events/src/main/java/geniusweb/actions/Vote.java
===================================================================
--- events/src/main/java/geniusweb/actions/Vote.java	(revision 21)
+++ events/src/main/java/geniusweb/actions/Vote.java	(revision 21)
@@ -0,0 +1,75 @@
+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("vote")
+public class Vote extends ActionWithBid {
+	private final Integer minPower;
+
+	/**
+	 * @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. If power=1 for all participants (usually the
+	 *                 default) power can be interpreted as number of votes
+	 */
+	@JsonCreator
+	public Vote(@JsonProperty("actor") PartyId id, @JsonProperty("bid") Bid bid,
+			@JsonProperty("minPower") Integer minPower) {
+		super(id, bid);
+		this.minPower = minPower;
+		if (bid == null || minPower == null || minPower < 1) {
+			throw new IllegalArgumentException(
+					"Vote must have non-null bid and minVotes, and minVotes must be >=1");
+		}
+	}
+
+	/**
+	 * 
+	 * @return the minimum number of votes this bid must get in order for the
+	 *         vote to be valid.
+	 */
+	public Integer getMinPower() {
+		return minPower;
+	}
+
+	@Override
+	public String toString() {
+		return "Vote[" + getActor() + "," + getBid() + "," + minPower + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result
+				+ ((minPower == null) ? 0 : minPower.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;
+		Vote other = (Vote) obj;
+		if (minPower == null) {
+			if (other.minPower != null)
+				return false;
+		} else if (!minPower.equals(other.minPower))
+			return false;
+		return true;
+	}
+
+}
Index: events/src/main/java/geniusweb/actions/Votes.java
===================================================================
--- events/src/main/java/geniusweb/actions/Votes.java	(revision 21)
+++ events/src/main/java/geniusweb/actions/Votes.java	(revision 21)
@@ -0,0 +1,121 @@
+package geniusweb.actions;
+
+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.issuevalue.Bid;
+
+/**
+ * Indicates that a party conditionally agrees with any of the {@link Vote}s
+ * provided.
+ */
+public class Votes extends AbstractAction {
+	private final List<Vote> votes;
+
+	/**
+	 * 
+	 * @param id    party id
+	 * @param votes the {@link Vote}s that the party can agree on, if the
+	 *              condition of the Vote holds. There can be at most 1
+	 *              {@link Vote} for each bid.
+	 */
+	@JsonCreator
+	public Votes(@JsonProperty("actor") PartyId id,
+			@JsonProperty("votes") List<Vote> votes) {
+		super(id);
+		this.votes = votes;
+		if (votes == null)
+			throw new NullPointerException("votes must be not null");
+		for (Vote vote : votes) {
+			if (!vote.getActor().equals(id)) {
+				throw new IllegalArgumentException("All votes must come from "
+						+ id + " but found " + vote);
+			}
+		}
+		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
+	 * @return true iff this extends the otherVotes.
+	 */
+	public boolean isExtending(Votes otherVotes) {
+		if (!otherVotes.getActor().equals(getActor()))
+			return false;
+		for (Vote vote : otherVotes.getVotes()) {
+			Vote myvote = getVote(vote.getBid());
+			if (myvote == null)
+				return false;
+			if (myvote.getMinPower() > vote.getMinPower())
+				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 Vote getVote(Bid bid) {
+		for (Vote vote : votes) {
+			if (vote.getBid().equals(bid))
+				return vote;
+		}
+		return null;
+	}
+
+	public List<Vote> getVotes() {
+		return Collections.unmodifiableList(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;
+		Votes other = (Votes) 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 "Votes[" + getActor() + "," + votes + "]";
+	}
+}
Index: events/src/main/java/geniusweb/inform/ActionDone.java
===================================================================
--- events/src/main/java/geniusweb/inform/ActionDone.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/ActionDone.java	(revision 21)
@@ -0,0 +1,53 @@
+package geniusweb.inform;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+
+/**
+ * Informs that someone did an action
+ */
+public class ActionDone implements Inform {
+
+	private final Action action;
+
+	@JsonCreator
+	public ActionDone(@JsonProperty("action") Action action) {
+		this.action = action;
+	}
+
+	public Action getAction() {
+		return action;
+	}
+
+	@Override
+	public String toString() {
+		return "ActionDone[" + action + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((action == null) ? 0 : action.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;
+		ActionDone other = (ActionDone) obj;
+		if (action == null) {
+			if (other.action != null)
+				return false;
+		} else if (!action.equals(other.action))
+			return false;
+		return true;
+	}
+}
Index: events/src/main/java/geniusweb/inform/Agreements.java
===================================================================
--- events/src/main/java/geniusweb/inform/Agreements.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/Agreements.java	(revision 21)
@@ -0,0 +1,111 @@
+package geniusweb.inform;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+
+/**
+ * There can be multiple agreements from a single negotiation. This object
+ * contains the agreements. Each party can agree only on 1 bid and there are
+ * always at least 2 parties agreeing on a bid.
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class Agreements {
+	@JsonValue
+	private final Map<PartyId, Bid> agreements;
+
+	public Agreements() {
+		this.agreements = Collections.emptyMap();
+	}
+
+	@JsonCreator
+	private Agreements(Map<PartyId, Bid> agrees) {
+		this.agreements = agrees;
+	}
+
+	/**
+	 * @param other {@link Agreements}
+	 * @return a new Agreement containing this plus other agreements
+	 * @throws IllegalArgumentException if parties in other are already in this
+	 *                                  agreement.
+	 */
+	public Agreements with(Agreements other) {
+		Map<PartyId, Bid> newagrees = new HashMap<>(agreements);
+		for (PartyId pid : other.agreements.keySet()) {
+			if (newagrees.containsKey(pid))
+				throw new IllegalArgumentException(
+						"party " + pid + " already has agreement");
+			newagrees.put(pid, other.agreements.get(pid));
+		}
+		return new Agreements(newagrees);
+	}
+
+	/**
+	 * @param bid
+	 * @param parties
+	 * @return new Agreements, with the new bid/parties added.
+	 */
+	public Agreements with(Bid bid, Set<PartyId> parties) {
+		if (parties.size() < 2) {
+			throw new IllegalArgumentException(
+					"Agreement must contain at least 2 parties");
+		}
+		Map<PartyId, Bid> newagreements = new HashMap<>(agreements);
+		for (PartyId party : parties) {
+			if (agreements.containsKey(party)) {
+				throw new IllegalArgumentException(
+						"A party can agree only on 1 bid");
+			}
+			newagreements.put(party, bid);
+		}
+		return new Agreements(newagreements);
+	}
+
+	/**
+	 * @return actual agreemenets contained here
+	 */
+	public Map<PartyId, Bid> getMap() {
+		return Collections.unmodifiableMap(agreements);
+	}
+
+	@Override
+	public String toString() {
+		return "Agreements" + agreements;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((agreements == null) ? 0 : agreements.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;
+		Agreements other = (Agreements) obj;
+		if (agreements == null) {
+			if (other.agreements != null)
+				return false;
+		} else if (!agreements.equals(other.agreements))
+			return false;
+		return true;
+	}
+
+}
Index: events/src/main/java/geniusweb/inform/Finished.java
===================================================================
--- events/src/main/java/geniusweb/inform/Finished.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/Finished.java	(revision 21)
@@ -0,0 +1,59 @@
+package geniusweb.inform;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Informs that the session is finished, and also contains the bid that was
+ * agreed on.
+ *
+ */
+public class Finished implements Inform {
+	private final Agreements agreements;
+
+	/**
+	 * The session finished (can be for any reason).
+	 * 
+	 * @param agreedBid the bid that was finally agreed on; null if no
+	 *                  agreement.
+	 */
+	@JsonCreator
+	public Finished(@JsonProperty("agreements") Agreements agreements) {
+		this.agreements = agreements;
+	}
+
+	public Agreements getAgreement() {
+		return agreements;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((agreements == null) ? 0 : agreements.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;
+		Finished other = (Finished) obj;
+		if (agreements == null) {
+			if (other.agreements != null)
+				return false;
+		} else if (!agreements.equals(other.agreements))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "Finished[" + agreements + "]";
+	}
+}
Index: events/src/main/java/geniusweb/inform/Inform.java
===================================================================
--- events/src/main/java/geniusweb/inform/Inform.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/Inform.java	(revision 21)
@@ -0,0 +1,22 @@
+package geniusweb.inform;
+
+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;
+
+/**
+ * base class of all information sent to the {@link Party}
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+@JsonSubTypes({ @JsonSubTypes.Type(value = ActionDone.class),
+		@JsonSubTypes.Type(value = Inform.class),
+		@JsonSubTypes.Type(value = Settings.class),
+		@JsonSubTypes.Type(value = Finished.class),
+		@JsonSubTypes.Type(value = YourTurn.class),
+		@JsonSubTypes.Type(value = Voting.class),
+		@JsonSubTypes.Type(value = OptIn.class) })
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public interface Inform {
+
+}
Index: events/src/main/java/geniusweb/inform/OptIn.java
===================================================================
--- events/src/main/java/geniusweb/inform/OptIn.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/OptIn.java	(revision 21)
@@ -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.Votes;
+
+/**
+ * Informs party that it's time to Opt-in.
+ */
+public class OptIn implements Inform {
+
+	private final List<Votes> votes;
+
+	/**
+	 * @param votes a list of votes. There may be only one votes per party.
+	 */
+	@JsonCreator
+	public OptIn(@JsonProperty("votes") List<Votes> 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<Votes> 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;
+		OptIn other = (OptIn) 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 "OptIn[" + votes + "]";
+	}
+
+}
Index: events/src/main/java/geniusweb/inform/Settings.java
===================================================================
--- events/src/main/java/geniusweb/inform/Settings.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/Settings.java	(revision 21)
@@ -0,0 +1,152 @@
+package geniusweb.inform;
+
+import java.util.HashMap;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.PartyId;
+import geniusweb.progress.Progress;
+import geniusweb.references.Parameters;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+
+/**
+ * Informs a {@link Party} about all settings for the upcoming negotiation
+ * session. This should be sent to a party one time, so that the party knows the
+ * situation.
+ *
+ */
+public class Settings implements Inform {
+	private ProfileRef profile;
+	private ProtocolRef protocol;
+	private Progress progress;
+	private PartyId id;
+	private Parameters parameters;
+
+	/**
+	 * 
+	 * @param id         the {@link PartyId} for this party
+	 * @param profile    the profile used for this party in this session
+	 * @param protocol   the protocol used in this session
+	 * @param progress   the {@link Progress} object used for this session
+	 * @param parameters a Map<String, Object> containing initialization
+	 *                   parameters for this party. The Object can be a HashMap,
+	 *                   ArrayList, String, or number (Integer, Double, etc).
+	 *                   The Object should not be just any Object because
+	 *                   deserialization will work only with the mentioned
+	 *                   types.
+	 */
+	@JsonCreator
+	public Settings(@JsonProperty("id") PartyId id,
+			@JsonProperty("profile") ProfileRef profile,
+			@JsonProperty("protocol") ProtocolRef protocol,
+			@JsonProperty("progress") Progress progress,
+			@JsonProperty("parameters") Parameters parameters) {
+		if (profile == null || protocol == null || progress == null
+				|| parameters == null) {
+			throw new IllegalArgumentException(
+					"party, profile, protocol, parameters and progress must be not null.");
+		}
+		this.profile = profile;
+		this.protocol = protocol;
+		this.progress = progress;
+		this.parameters = parameters;
+		this.id = id;
+	}
+
+	/**
+	 * 
+	 * @return the profile used for this party in this session
+	 */
+	public ProfileRef getProfile() {
+		return profile;
+	}
+
+	public ProtocolRef getProtocol() {
+		return protocol;
+	}
+
+	/**
+	 * 
+	 * @return the {@link Progress} object used for this session
+	 */
+	public Progress getProgress() {
+		return progress;
+	}
+
+	/**
+	 * @return the party ID of this party
+	 */
+	public PartyId getID() {
+		return id;
+	}
+
+	/**
+	 * 
+	 * @return a HashMap<String,Object> containing initialization parameters
+	 *         that can be used by the party.
+	 */
+	public HashMap<String, Object> getParameters() {
+		return parameters;
+	}
+
+	@Override
+	public String toString() {
+		return "Settings[" + id + "," + profile + "," + protocol + ","
+				+ progress + "," + parameters + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
+		result = prime * result
+				+ ((parameters == null) ? 0 : parameters.hashCode());
+		result = prime * result + ((profile == null) ? 0 : profile.hashCode());
+		result = prime * result
+				+ ((progress == null) ? 0 : progress.hashCode());
+		result = prime * result
+				+ ((protocol == null) ? 0 : protocol.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;
+		Settings other = (Settings) obj;
+		if (id == null) {
+			if (other.id != null)
+				return false;
+		} else if (!id.equals(other.id))
+			return false;
+		if (parameters == null) {
+			if (other.parameters != null)
+				return false;
+		} else if (!parameters.equals(other.parameters))
+			return false;
+		if (profile == null) {
+			if (other.profile != null)
+				return false;
+		} else if (!profile.equals(other.profile))
+			return false;
+		if (progress == null) {
+			if (other.progress != null)
+				return false;
+		} else if (!progress.equals(other.progress))
+			return false;
+		if (protocol == null) {
+			if (other.protocol != null)
+				return false;
+		} else if (!protocol.equals(other.protocol))
+			return false;
+		return true;
+	}
+
+}
Index: events/src/main/java/geniusweb/inform/Voting.java
===================================================================
--- events/src/main/java/geniusweb/inform/Voting.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/Voting.java	(revision 21)
@@ -0,0 +1,81 @@
+package geniusweb.inform;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+
+/**
+ * Informs a party that it's time to do voting.
+ */
+public class Voting implements Inform {
+
+	private final List<Bid> bids;
+	private final Map<PartyId, Integer> powers;
+
+	@JsonCreator
+	public Voting(@JsonProperty("bids") List<Bid> bids,
+			@JsonProperty("powers") Map<PartyId, Integer> powers) {
+		this.bids = bids;
+		this.powers = powers;
+	}
+
+	/**
+	 * @return list of bids to be voted on
+	 */
+	public List<Bid> getBids() {
+		return Collections.unmodifiableList(bids);
+	}
+
+	/**
+	 * 
+	 * @return map with the power of each party in the negotiation. This map may
+	 *         contain parties that are already finished, eg they reached an
+	 *         agreement or walked away.
+	 */
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((bids == null) ? 0 : bids.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;
+		Voting other = (Voting) obj;
+		if (bids == null) {
+			if (other.bids != null)
+				return false;
+		} else if (!bids.equals(other.bids))
+			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 "Voting[" + bids + "," + powers + "]";
+	}
+
+}
Index: events/src/main/java/geniusweb/inform/YourTurn.java
===================================================================
--- events/src/main/java/geniusweb/inform/YourTurn.java	(revision 21)
+++ events/src/main/java/geniusweb/inform/YourTurn.java	(revision 21)
@@ -0,0 +1,28 @@
+package geniusweb.inform;
+
+/**
+ * Informs a party that he has the turn and can choose the next action.
+ */
+public class YourTurn implements Inform {
+
+	@Override
+	public int hashCode() {
+		return 1;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "YourTurn";
+	}
+}
Index: events/src/test/java/actions/VoteTest.java
===================================================================
--- events/src/test/java/actions/VoteTest.java	(revision 21)
+++ events/src/test/java/actions/VoteTest.java	(revision 21)
@@ -0,0 +1,99 @@
+package actions;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+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.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.NumberValue;
+import geniusweb.issuevalue.Value;
+import tudelft.utilities.junit.GeneralTests;
+
+public class VoteTest extends GeneralTests<Vote> {
+	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 = "{\"vote\":{\"actor\":\"party1\",\"bid\":{\"issuevalues\":{\"issue2\":10,\"issue1\":\"value1\"}},\"minPower\":1}}";
+
+	private static Vote vote1, vote1a, vote2, vote3, vote4;
+
+	static {
+		issuevalues.put(ISSUE1, VALUE1);
+		issuevalues.put(ISSUE2, VALUE2);
+		bid = new Bid(issuevalues);
+		vote1 = new Vote(id, bid, 1);
+		vote1a = new Vote(id, bid, 1);
+
+		vote2 = new Vote(id2, bid, 1);
+
+		// values swapped, so different issuevalues.
+		issuevaluesb.put(ISSUE1, VALUE2);
+		issuevaluesb.put(ISSUE2, VALUE2);
+		bidb = new Bid(issuevaluesb);
+		vote3 = new Vote(id, bidb, 1);
+
+		vote4 = new Vote(id, bid, 2);
+
+	}
+
+	@Override
+	public List<List<Vote>> getGeneralTestData() {
+		List<List<Vote>> list = new LinkedList<>();
+		list.add(Arrays.asList(vote1, vote1a));
+		list.add(Arrays.asList(vote2));
+		list.add(Arrays.asList(vote3));
+		list.add(Arrays.asList(vote4));
+		return list;
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("Vote.*party1.*issue2=10.*issue1=.value1.*1.*",
+				"Vote.*party2.*issue2=10.*issue1=.value1.*1.*",
+				"Vote.*party1.*issue2=10.*issue1=10.*1.*",
+				"Vote.*party1.*issue2=10.*issue1=.value1.*2.*");
+	}
+
+	@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/actions/VotesTest.java
===================================================================
--- events/src/test/java/actions/VotesTest.java	(revision 21)
+++ events/src/test/java/actions/VotesTest.java	(revision 21)
@@ -0,0 +1,108 @@
+package 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.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class VotesTest extends GeneralTests<Votes> {
+	private final ObjectMapper jackson = new ObjectMapper();
+	private final String votestring = "{\"Votes\":{\"actor\":\"partyA\",\"votes\":[{\"vote\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"is1\":\"val1\"}},\"minPower\":2}},{\"vote\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"is1\":\"val2\"}},\"minPower\":2}}]}}";
+
+	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 Vote voteA1 = new Vote(partyA, bid1, 2);
+	private Vote voteA2 = new Vote(partyA, bid2, 2);
+	private Vote voteB1 = new Vote(partyB, bid1, 2);
+	private Vote voteB2 = new Vote(partyB, bid2, 2);
+
+	private Votes votes1 = new Votes(partyA, Arrays.asList(voteA1, voteA2));
+	private Votes votes1a = new Votes(partyA, Arrays.asList(voteA1, voteA2));
+	private Votes votes2 = new Votes(partyB, Arrays.asList(voteB1, voteB2));
+
+	@Override
+	public List<List<Votes>> 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.*val1.*2.*Vote.*partyB,Bid.*is1.*val2.*2.*");
+	}
+
+	@SuppressWarnings("unused")
+	@Test(expected = IllegalArgumentException.class)
+	public void testRejectVoteWithIncorrectParty() {
+		new Votes(partyA, Arrays.asList(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 Votes(partyA, Arrays.asList(voteB1, voteB2));
+	}
+
+	@Test
+	public void isExtendingTestExtraVote() {
+		Votes otherVotes = new Votes(partyA, Arrays.asList(voteA1));
+		assertTrue(votes1.isExtending(otherVotes));
+	}
+
+	@Test
+	public void isExtendingTestNoVotes() {
+		Votes otherVotes = new Votes(partyA, Arrays.asList());
+		assertTrue(votes1.isExtending(otherVotes));
+	}
+
+	@Test
+	public void isExtendingTestMissing() {
+		Votes otherVotes = new Votes(partyA, Arrays.asList(voteA1));
+		assertFalse(otherVotes.isExtending(votes1));
+	}
+
+	@Test
+	public void isExtendingTestLessPower() {
+		Vote powervoteA1 = new Vote(partyA, bid1, 3);
+		Votes powerVotes = new Votes(partyA, Arrays.asList(powervoteA1));
+		assertFalse(powerVotes.isExtending(votes1));
+		assertTrue(votes1.isExtending(powerVotes));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testDuplicateBidsNotAllowed() {
+		Vote voteA1similar = new Vote(partyA, bid1, 3);
+		Votes votes = new Votes(partyA, Arrays.asList(voteA1, voteA1similar));
+	}
+}
