Index: /bidspace/pom.xml
===================================================================
--- /bidspace/pom.xml	(revision 20)
+++ /bidspace/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>bidspace</artifactId>
-	<version>1.4.4</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.0</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.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -43,5 +43,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /boa/pom.xml
===================================================================
--- /boa/pom.xml	(revision 20)
+++ /boa/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>boa</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>
 
Index: /boa/src/main/java/geniusweb/boa/BoaParty.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/BoaParty.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/BoaParty.java	(revision 21)
@@ -6,6 +6,6 @@
 import geniusweb.boa.acceptancestrategy.AcceptanceStrategy;
 import geniusweb.boa.biddingstrategy.BiddingStrategy;
+import geniusweb.inform.Settings;
 import geniusweb.opponentmodel.OpponentModel;
-import geniusweb.party.inform.Settings;
 import tudelft.utilities.logging.Reporter;
 
Index: /boa/src/main/java/geniusweb/boa/BoaState.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/BoaState.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/BoaState.java	(revision 21)
@@ -12,7 +12,7 @@
 import geniusweb.boa.acceptancestrategy.AcceptanceStrategy;
 import geniusweb.boa.biddingstrategy.BiddingStrategy;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
 import geniusweb.opponentmodel.OpponentModel;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.progress.Progress;
Index: /boa/src/main/java/geniusweb/boa/DefaultBoa.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/DefaultBoa.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/DefaultBoa.java	(revision 21)
@@ -11,13 +11,13 @@
 import geniusweb.boa.acceptancestrategy.AcceptanceStrategy;
 import geniusweb.boa.biddingstrategy.BiddingStrategy;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.opponentmodel.OpponentModel;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
Index: /boa/src/main/java/geniusweb/boa/acceptancestrategy/AcceptanceStrategy.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/acceptancestrategy/AcceptanceStrategy.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/acceptancestrategy/AcceptanceStrategy.java	(revision 21)
@@ -3,6 +3,6 @@
 import geniusweb.boa.BoaParty;
 import geniusweb.boa.BoaState;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Settings;
 
 /**
Index: /boa/src/main/java/geniusweb/boa/acceptancestrategy/TimeDependentAcceptanceStrategy.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/acceptancestrategy/TimeDependentAcceptanceStrategy.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/acceptancestrategy/TimeDependentAcceptanceStrategy.java	(revision 21)
@@ -6,6 +6,6 @@
 import geniusweb.boa.BoaState;
 import geniusweb.boa.biddingstrategy.TimeDependentBiddingStrategy;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /boa/src/main/java/geniusweb/boa/biddingstrategy/BiddingStrategy.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/biddingstrategy/BiddingStrategy.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/biddingstrategy/BiddingStrategy.java	(revision 21)
@@ -4,5 +4,5 @@
 import geniusweb.boa.BoaParty;
 import geniusweb.boa.BoaState;
-import geniusweb.party.inform.Settings;
+import geniusweb.inform.Settings;
 
 /**
Index: /boa/src/main/java/geniusweb/boa/biddingstrategy/TimeDependentBiddingStrategy.java
===================================================================
--- /boa/src/main/java/geniusweb/boa/biddingstrategy/TimeDependentBiddingStrategy.java	(revision 20)
+++ /boa/src/main/java/geniusweb/boa/biddingstrategy/TimeDependentBiddingStrategy.java	(revision 21)
@@ -13,6 +13,6 @@
 import geniusweb.actions.PartyId;
 import geniusweb.boa.BoaState;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /boa/src/test/java/geniusweb/boa/BoaPartyTest.java
===================================================================
--- /boa/src/test/java/geniusweb/boa/BoaPartyTest.java	(revision 20)
+++ /boa/src/test/java/geniusweb/boa/BoaPartyTest.java	(revision 21)
@@ -24,12 +24,12 @@
 import geniusweb.boa.biddingstrategy.BiddingStrategy;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
 import geniusweb.issuevalue.Domain;
 import geniusweb.opponentmodel.OpponentModel;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.progress.Progress;
 import geniusweb.references.Parameters;
Index: /boa/src/test/java/geniusweb/boa/acceptancestrategy/TimeDepAccStratTest.java
===================================================================
--- /boa/src/test/java/geniusweb/boa/acceptancestrategy/TimeDepAccStratTest.java	(revision 20)
+++ /boa/src/test/java/geniusweb/boa/acceptancestrategy/TimeDepAccStratTest.java	(revision 21)
@@ -16,6 +16,6 @@
 import geniusweb.bidspace.Interval;
 import geniusweb.boa.BoaState;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
@@ -46,4 +46,5 @@
 
 		TimeDependentAcceptanceStrategy ac = new TimeDependentAcceptanceStrategy() {
+			@Override
 			protected BidsWithUtility getBidspace(Profile profile) {
 				return testbidspace;
@@ -52,5 +53,5 @@
 		// we use default e=1, k=0 which means p(0)=1
 
-		when(progress.get(any())).thenReturn((Double) 0.0d);
+		when(progress.get(any())).thenReturn(0.0d);
 
 		when(linearprofile.getUtility(eq(bid1)))
@@ -67,4 +68,5 @@
 
 		TimeDependentAcceptanceStrategy ac = new TimeDependentAcceptanceStrategy() {
+			@Override
 			protected BidsWithUtility getBidspace(Profile profile) {
 				return testbidspace;
@@ -73,5 +75,5 @@
 		// we use default e=1, k=0 which means p(0)=1
 
-		when(progress.get(any())).thenReturn((Double) 0.0d);
+		when(progress.get(any())).thenReturn(0.0d);
 
 		when(linearprofile.getUtility(eq(bid1)))
@@ -87,4 +89,5 @@
 
 		TimeDependentAcceptanceStrategy ac = new TimeDependentAcceptanceStrategy() {
+			@Override
 			protected BidsWithUtility getBidspace(Profile profile) {
 				return testbidspace;
@@ -93,5 +96,5 @@
 		// we use default e=1, k=0 which means p(1)=0
 
-		when(progress.get(any())).thenReturn((Double) 1.0d);
+		when(progress.get(any())).thenReturn(1.0d);
 
 		when(linearprofile.getUtility(eq(bid1)))
@@ -110,4 +113,5 @@
 
 		TimeDependentAcceptanceStrategy ac = new TimeDependentAcceptanceStrategy() {
+			@Override
 			protected BidsWithUtility getBidspace(Profile profile) {
 				return testbidspace;
@@ -116,5 +120,5 @@
 		// we use default e=1, k=0 which means p(1)=resValue.
 
-		when(progress.get(any())).thenReturn((Double) 1.0d);
+		when(progress.get(any())).thenReturn(1.0d);
 
 		when(linearprofile.getUtility(eq(bid1)))
Index: /boa/src/test/java/geniusweb/boa/biddingstrategy/TimeDepBidStratTest.java
===================================================================
--- /boa/src/test/java/geniusweb/boa/biddingstrategy/TimeDepBidStratTest.java	(revision 20)
+++ /boa/src/test/java/geniusweb/boa/biddingstrategy/TimeDepBidStratTest.java	(revision 21)
@@ -17,7 +17,7 @@
 import geniusweb.actions.Offer;
 import geniusweb.boa.BoaState;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.progress.Progress;
@@ -46,4 +46,5 @@
 
 		biddingstrat = new TimeDependentBiddingStrategy() {
+			@Override
 			protected ExtendedUtilSpace getBidSpace(LinearAdditive profile) {
 				return extUtilSpace;
Index: /collectparties.sh
===================================================================
--- /collectparties.sh	(revision 20)
+++ /collectparties.sh	(revision 21)
@@ -7,5 +7,5 @@
 rm -rf collectedparties
 mkdir collectedparties
-VERSION=1.4.4
+VERSION=1.5.0
 
 cp "exampleparties/anac2019/agentgg/target/agentgg-${VERSION}-jar-with-dependencies.jar" collectedparties
Index: /design/classdiagram.graphml
===================================================================
--- /design/classdiagram.graphml	(revision 20)
+++ /design/classdiagram.graphml	(revision 21)
@@ -795,12 +795,12 @@
           <y:Realizers active="0">
             <y:GroupNode>
-              <y:Geometry height="353.4990234375" width="436.46484375" x="1255.75146484375" y="613.58349609375"/>
+              <y:Geometry height="353.4990234375" width="443.343994140625" x="1248.872314453125" y="613.58349609375"/>
               <y:Fill color="#F5F5F5" transparent="false"/>
               <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="436.46484375" x="0.0" xml:space="preserve" y="0.0">Party</y:NodeLabel>
+              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="443.343994140625" x="0.0" xml:space="preserve" y="0.0">Party</y:NodeLabel>
               <y:Shape type="roundrectangle"/>
               <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
               <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
-              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+              <y:BorderInsets bottom="0" bottomF="0.0" left="7" leftF="6.879150390625" right="0" rightF="0.0" top="0" topF="0.0"/>
             </y:GroupNode>
             <y:GroupNode>
@@ -860,8 +860,8 @@
               <y:Realizers active="0">
                 <y:GroupNode>
-                  <y:Geometry height="202.666015625" width="272.1669921875" x="1270.75146484375" y="650.24951171875"/>
+                  <y:Geometry height="274.3256133960184" width="255.8809034887986" x="1270.75146484375" y="650.24951171875"/>
                   <y:Fill color="#F5F5F5" transparent="false"/>
                   <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
-                  <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="272.1669921875" x="0.0" xml:space="preserve" y="0.0">inform</y:NodeLabel>
+                  <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="255.8809034887986" x="0.0" xml:space="preserve" y="0.0">inform</y:NodeLabel>
                   <y:Shape type="roundrectangle"/>
                   <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
@@ -870,5 +870,5 @@
                 </y:GroupNode>
                 <y:GroupNode>
-                  <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
+                  <y:Geometry height="50.0" width="50.0" x="1270.75146484375" y="650.24951171875"/>
                   <y:Fill color="#F5F5F5" transparent="false"/>
                   <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
@@ -886,5 +886,5 @@
               <data key="d6">
                 <y:ShapeNode>
-                  <y:Geometry height="30.0" width="52.80078125" x="1370.67138671875" y="746.91552734375"/>
+                  <y:Geometry height="30.0" width="52.80078125" x="1353.0337261632803" y="738.8943206568053"/>
                   <y:Fill color="#FFCC00" transparent="false"/>
                   <y:BorderStyle color="#000000" type="line" width="1.0"/>
@@ -897,5 +897,5 @@
               <data key="d6">
                 <y:ShapeNode>
-                  <y:Geometry height="30.0" width="81.693359375" x="1446.22509765625" y="740.91552734375"/>
+                  <y:Geometry height="30.0" width="81.693359375" x="1308.1947553690638" y="686.91552734375"/>
                   <y:Fill color="#FFCC00" transparent="false"/>
                   <y:BorderStyle color="#000000" type="line" width="1.0"/>
@@ -908,5 +908,5 @@
               <data key="d6">
                 <y:ShapeNode>
-                  <y:Geometry height="30.0" width="60.640625" x="1285.75146484375" y="692.91552734375"/>
+                  <y:Geometry height="30.0" width="60.640625" x="1450.9917433325486" y="705.6830051732845"/>
                   <y:Fill color="#FFCC00" transparent="false"/>
                   <y:BorderStyle color="#000000" type="line" width="1.0"/>
@@ -919,5 +919,5 @@
               <data key="d6">
                 <y:ShapeNode>
-                  <y:Geometry height="30.0" width="62.62109375" x="1388.76123046875" y="686.91552734375"/>
+                  <y:Geometry height="30.0" width="62.62109375" x="1384.34912109375" y="809.2347228857868"/>
                   <y:Fill color="#FFCC00" transparent="false"/>
                   <y:BorderStyle color="#000000" type="line" width="1.0"/>
@@ -930,8 +930,20 @@
               <data key="d6">
                 <y:ShapeNode>
-                  <y:Geometry height="30.0" width="68.59765625" x="1402.77294921875" y="807.91552734375"/>
+                  <y:Geometry height="30.0" width="68.59765625" x="1285.75146484375" y="809.2347228857868"/>
                   <y:Fill color="#FFCC00" transparent="false"/>
                   <y:BorderStyle color="#000000" type="line" width="1.0"/>
                   <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="58.59765625" x="5.0" xml:space="preserve" y="5.93359375">YourTurn<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+                  <y:Shape type="rectangle"/>
+                </y:ShapeNode>
+              </data>
+            </node>
+            <node id="n6::n3::n5">
+              <data key="d5"/>
+              <data key="d6">
+                <y:ShapeNode>
+                  <y:Geometry height="30.0" width="83.984375" x="1373.66748046875" y="879.5751251147684"/>
+                  <y:Fill color="#FFCC00" transparent="false"/>
+                  <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+                  <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.984375" x="5.0" xml:space="preserve" y="5.93359375">Agreements<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
                   <y:Shape type="rectangle"/>
                 </y:ShapeNode>
@@ -1254,8 +1266,8 @@
           <y:Realizers active="0">
             <y:GroupNode>
-              <y:Geometry height="370.666015625" width="495.859375" x="739.58447265625" y="0.0"/>
+              <y:Geometry height="370.666015625" width="507.6171875" x="739.58447265625" y="0.0"/>
               <y:Fill color="#F5F5F5" transparent="false"/>
               <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
-              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="495.859375" x="0.0" xml:space="preserve" y="0.0">protocol</y:NodeLabel>
+              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="507.6171875" x="0.0" xml:space="preserve" y="0.0">protocol</y:NodeLabel>
               <y:Shape type="roundrectangle"/>
               <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
@@ -1264,5 +1276,5 @@
             </y:GroupNode>
             <y:GroupNode>
-              <y:Geometry height="50.0" width="50.0" x="373.32568359375" y="2196.0"/>
+              <y:Geometry height="50.0" width="50.0" x="739.58447265625" y="0.0"/>
               <y:Fill color="#F5F5F5" transparent="false"/>
               <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
@@ -1406,4 +1418,28 @@
               <y:BorderStyle color="#000000" type="line" width="1.0"/>
               <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="143.86328125" x="5.0" xml:space="preserve" y="5.93359375">AllPermutationsProtocol<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+              <y:Shape type="rectangle"/>
+            </y:ShapeNode>
+          </data>
+        </node>
+        <node id="n10::n12">
+          <data key="d5"/>
+          <data key="d6">
+            <y:ShapeNode>
+              <y:Geometry height="30.0" width="48.5703125" x="1165.71484375" y="158.666015625"/>
+              <y:Fill color="#FFCC00" transparent="false"/>
+              <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="38.5703125" x="5.0" xml:space="preserve" y="5.93359375">AMOP<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+              <y:Shape type="rectangle"/>
+            </y:ShapeNode>
+          </data>
+        </node>
+        <node id="n10::n13">
+          <data key="d5"/>
+          <data key="d6">
+            <y:ShapeNode>
+              <y:Geometry height="30.0" width="53.515625" x="1178.68603515625" y="98.666015625"/>
+              <y:Fill color="#FFCC00" transparent="false"/>
+              <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="43.515625" x="5.0" xml:space="preserve" y="5.93359375">SHAOP<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
               <y:Shape type="rectangle"/>
             </y:ShapeNode>
@@ -1555,5 +1591,4 @@
     <node id="n12" yfiles.foldertype="group">
       <data key="d4" xml:space="preserve"/>
-      <data key="d5"/>
       <data key="d6">
         <y:ProxyAutoBoundsNode>
@@ -1584,5 +1619,4 @@
       <graph edgedefault="directed" id="n12:">
         <node id="n12::n0">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1596,5 +1630,4 @@
         </node>
         <node id="n12::n1">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1611,5 +1644,4 @@
     <node id="n13" yfiles.foldertype="group">
       <data key="d4" xml:space="preserve"/>
-      <data key="d5"/>
       <data key="d6">
         <y:ProxyAutoBoundsNode>
@@ -1640,5 +1672,4 @@
       <graph edgedefault="directed" id="n13:">
         <node id="n13::n0">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1652,5 +1683,4 @@
         </node>
         <node id="n13::n1">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1664,5 +1694,4 @@
         </node>
         <node id="n13::n2">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1677,5 +1706,4 @@
         </node>
         <node id="n13::n3">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1689,5 +1717,4 @@
         </node>
         <node id="n13::n4">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -1701,5 +1728,4 @@
         </node>
         <node id="n13::n5">
-          <data key="d5"/>
           <data key="d6">
             <y:ShapeNode>
@@ -2156,75 +2182,9 @@
       </data>
     </edge>
-    <edge id="n10::e0" source="n10::n3" target="n10::n1">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n10::e1" source="n10::n4" target="n10::n0">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n10::e2" source="n10::n5" target="n10::n2">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n10::e3" source="n10::n9" target="n10::n8">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n10::e4" source="n10::n10" target="n10::n7">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n10::e5" source="n10::n11" target="n10::n6">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
     <edge id="e15" source="n10::n9" target="n11::n0">
       <data key="d8" xml:space="preserve"/>
       <data key="d10">
         <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:Path sx="9.591198890100713" sy="0.0" tx="0.0" ty="0.0"/>
           <y:LineStyle color="#000000" type="line" width="1.0"/>
           <y:Arrows source="diamond" target="none"/>
@@ -2237,5 +2197,5 @@
       <data key="d10">
         <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:Path sx="9.591198890100713" sy="0.0" tx="0.0" ty="0.0"/>
           <y:LineStyle color="#000000" type="line" width="1.0"/>
           <y:Arrows source="diamond" target="none"/>
@@ -2248,5 +2208,5 @@
       <data key="d10">
         <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:Path sx="5.8684164732106865" sy="0.0" tx="0.0" ty="0.0"/>
           <y:LineStyle color="#000000" type="line" width="1.0"/>
           <y:Arrows source="diamond" target="none"/>
@@ -2577,48 +2537,4 @@
       </data>
     </edge>
-    <edge id="n6::n3::e0" source="n6::n3::n1" target="n6::n3::n0">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n6::n3::e1" source="n6::n3::n2" target="n6::n3::n0">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n6::n3::e2" source="n6::n3::n3" target="n6::n3::n0">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="n6::n3::e3" source="n6::n3::n4" target="n6::n3::n0">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="white_delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
     <edge id="n2::e0" source="n2::n1" target="n2::n0">
       <data key="d8" xml:space="preserve"/>
@@ -2691,5 +2607,5 @@
       <data key="d10">
         <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:Path sx="4.706528967303029" sy="0.0" tx="0.0" ty="0.0"/>
           <y:LineStyle color="#000000" type="line" width="1.0"/>
           <y:Arrows source="diamond" target="none"/>
@@ -2753,23 +2669,12 @@
       </data>
     </edge>
-    <edge id="n10::e6" source="n10::n1" target="n10::n2">
-      <data key="d8" xml:space="preserve"/>
-      <data key="d10">
-        <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
-          <y:Arrows source="none" target="plain"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
     <edge id="e30" source="n0::n1" target="n10::n1">
       <data key="d8" xml:space="preserve"/>
       <data key="d10">
         <y:PolyLineEdge>
-          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="diamond" target="none"/>
-          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.978515625" x="5.430217172490757" xml:space="preserve" y="-23.1328125">   1<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.0" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
+          <y:Path sx="0.0" sy="0.0" tx="5.8684164732106865" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="diamond" target="none"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.978515625" x="5.673614709180811" xml:space="preserve" y="-23.1328125">   1<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.0" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
           <y:BendStyle smoothed="false"/>
         </y:PolyLineEdge>
@@ -2799,5 +2704,4 @@
     </edge>
     <edge id="n12::e0" source="n12::n1" target="n12::n0">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2809,5 +2713,4 @@
     </edge>
     <edge id="e31" source="n12::n0" target="n4::n0">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2819,5 +2722,4 @@
     </edge>
     <edge id="n13::e0" source="n13::n2" target="n13::n1">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2829,5 +2731,4 @@
     </edge>
     <edge id="e32" source="n12::n0" target="n13::n3">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2840,5 +2741,4 @@
     </edge>
     <edge id="n13::e1" source="n13::n5" target="n13::n4">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2850,5 +2750,4 @@
     </edge>
     <edge id="n13::e2" source="n13::n1" target="n13::n0">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2860,5 +2759,4 @@
     </edge>
     <edge id="n13::e3" source="n13::n4" target="n13::n0">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2870,5 +2768,4 @@
     </edge>
     <edge id="n13::e4" source="n13::n3" target="n13::n0">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2880,5 +2777,4 @@
     </edge>
     <edge id="e33" source="n13" target="n6">
-      <data key="d9"/>
       <data key="d10">
         <y:BezierEdge>
@@ -2886,4 +2782,155 @@
           <y:LineStyle color="#000000" type="dashed" width="1.0"/>
           <y:Arrows source="none" target="plain"/>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="n6::n3::e0" source="n6::n3::n1" target="n6::n3::n0">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n6::n3::e1" source="n6::n3::n2" target="n6::n3::n0">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n6::n3::e2" source="n6::n3::n3" target="n6::n3::n0">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n6::n3::e3" source="n6::n3::n4" target="n6::n3::n0">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n6::n3::e4" source="n6::n3::n5" target="n6::n3::n3">
+      <data key="d9"/>
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="diamond"/>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="n10::e0" source="n10::n1" target="n10::n2">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
+          <y:Arrows source="none" target="plain"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e1" source="n10::n3" target="n10::n1">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e2" source="n10::n4" target="n10::n0">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e3" source="n10::n5" target="n10::n2">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e4" source="n10::n9" target="n10::n8">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e5" source="n10::n10" target="n10::n7">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e6" source="n10::n11" target="n10::n6">
+      <data key="d8" xml:space="preserve"/>
+      <data key="d10">
+        <y:PolyLineEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="n10::e7" source="n10::n12" target="n10::n2">
+      <data key="d9"/>
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="n10::e8" source="n10::n13" target="n10::n2">
+      <data key="d9"/>
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="none" target="white_delta"/>
         </y:BezierEdge>
       </data>
Index: /design/classdiagram.svg
===================================================================
--- /design/classdiagram.svg	(revision 20)
+++ /design/classdiagram.svg	(revision 21)
@@ -1,3 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="2257" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="2059" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="2496" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="2118" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
   <!--Generated by ySVG 2.5-->
   <defs id="genericDefs"/>
@@ -17,12 +17,12 @@
       </linearGradient>
       <clipPath clipPathUnits="userSpaceOnUse" id="clipPath1">
-        <path d="M0 0 L2257 0 L2257 2059 L0 2059 L0 0 Z"/>
+        <path d="M0 0 L2496 0 L2496 2118 L0 2118 L0 0 Z"/>
       </clipPath>
       <clipPath clipPathUnits="userSpaceOnUse" id="clipPath2">
-        <path d="M17 -15 L2274 -15 L2274 2044 L17 2044 L17 -15 Z"/>
+        <path d="M17 -15 L2513 -15 L2513 2103 L17 2103 L17 -15 Z"/>
       </clipPath>
     </defs>
     <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="translate(-17,15)" stroke="white">
-      <rect x="17" width="2257" height="2059" y="-15" clip-path="url(#clipPath2)" stroke="none"/>
+      <rect x="17" width="2496" height="2118" y="-15" clip-path="url(#clipPath2)" stroke="none"/>
     </g>
     <g fill="url(#linearGradient1)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="url(#linearGradient1)">
@@ -291,10 +291,10 @@
     </g>
     <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
-      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" width="516.0273" rx="4" ry="4" height="458.1309" stroke="none"/>
-      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="516.0273" height="21.666" stroke="none"/>
-    </g>
-    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
-      <text x="1338.4097" xml:space="preserve" y="1042.9856" clip-path="url(#clipPath2)" stroke="none">Profile</text>
-      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" fill="none" width="516.0273" stroke-dasharray="6,2" rx="4" ry="4" height="458.1309"/>
+      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" width="460.2715" rx="4" ry="4" height="458.1309" stroke="none"/>
+      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="460.2715" height="21.666" stroke="none"/>
+    </g>
+    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
+      <text x="1282.6538" xml:space="preserve" y="1042.9856" clip-path="url(#clipPath2)" stroke="none">Profile</text>
+      <rect x="870.5688" y="1026.4836" clip-path="url(#clipPath2)" fill="none" width="460.2715" stroke-dasharray="6,2" rx="4" ry="4" height="458.1309"/>
     </g>
     <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="white">
@@ -358,9 +358,9 @@
     </g>
     <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="white">
-      <rect x="985.2847" width="122.1816" height="30" y="1143.3489" clip-path="url(#clipPath2)" stroke="none"/>
+      <rect x="998.7144" width="95.3223" height="30" y="1143.3489" clip-path="url(#clipPath2)" stroke="none"/>
     </g>
     <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
       <text x="1005.7144" xml:space="preserve" y="1162.884" clip-path="url(#clipPath2)" stroke="none">TotalOrdering</text>
-      <rect fill="none" x="985.2847" width="122.1816" height="30" y="1143.3489" clip-path="url(#clipPath2)"/>
+      <rect fill="none" x="998.7144" width="95.3223" height="30" y="1143.3489" clip-path="url(#clipPath2)"/>
     </g>
     <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
@@ -370,7 +370,7 @@
       <text x="1201.2769" xml:space="preserve" y="1394.9819" clip-path="url(#clipPath2)" stroke="none">BigDecimal</text>
       <rect fill="none" x="1194.2769" width="78.4414" height="30" y="1375.4468" clip-path="url(#clipPath2)"/>
-      <path fill="none" stroke-dasharray="6,2" d="M870.5613 1400.4462 L756.5829 1464.453" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M753.0953 1466.4116 L766.4962 1465.7673 L753.0953 1466.4116 L760.6204 1455.3043 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M753.0953 1466.4116 L766.4962 1465.7673 L753.0953 1466.4116 L760.6204 1455.3043 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M870.5617 1391.3617 L756.5297 1458.6547" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M753.0848 1460.6876 L766.4689 1459.7561 L753.0848 1460.6876 L760.3701 1449.4215 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M753.0848 1460.6876 L766.4689 1459.7561 L753.0848 1460.6876 L760.3701 1449.4215 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
       <path fill="none" d="M1159.808 1767.553 L1095.0416 1469.6145" clip-path="url(#clipPath2)"/>
       <path d="M1161.2949 1774.3932 L1154.9221 1768.615 L1158.321 1760.7126 L1164.6938 1766.4908 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -399,7 +399,7 @@
       <path fill="white" d="M1092.4135 1399.0833 L1100.616 1384.0927 L1088.7366 1382.3955 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1092.4135 1399.0833 L1100.616 1384.0927 L1088.7366 1382.3955 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1153.2026 1185.2378 L1120.5153 1177.0103" clip-path="url(#clipPath2)"/>
-      <path fill="white" d="M1105.969 1173.3489 L1120.0205 1183.0729 L1122.9496 1171.4358 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1105.969 1173.3489 L1120.0205 1183.0729 L1122.9496 1171.4358 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1153.2026 1185.2378 L1108.5829 1174.0068" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1094.0366 1170.3455 L1108.0881 1180.0695 L1111.0172 1168.4324 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1094.0366 1170.3455 L1108.0881 1180.0695 L1111.0172 1168.4324 Z" clip-path="url(#clipPath2)"/>
       <path fill="none" d="M1066.5248 1143.3489 L1086.464 1128.5052" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1098.496 1119.5481 L1082.079 1124.2896 L1089.2448 1133.9152 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -570,10 +570,10 @@
     </g>
     <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
-      <rect x="1255.7515" y="613.5835" clip-path="url(#clipPath2)" width="436.4648" rx="4" ry="4" height="353.499" stroke="none"/>
-      <rect x="1255.7515" y="613.5835" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="436.4648" height="21.666" stroke="none"/>
+      <rect x="1248.8723" y="613.5835" clip-path="url(#clipPath2)" width="443.344" rx="4" ry="4" height="353.499" stroke="none"/>
+      <rect x="1248.8723" y="613.5835" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="443.344" height="21.666" stroke="none"/>
     </g>
     <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
       <text x="1654.0566" xml:space="preserve" y="630.0854" clip-path="url(#clipPath2)" stroke="none">Party</text>
-      <rect x="1255.7515" y="613.5835" clip-path="url(#clipPath2)" fill="none" width="436.4648" stroke-dasharray="6,2" rx="4" ry="4" height="353.499"/>
+      <rect x="1248.8723" y="613.5835" clip-path="url(#clipPath2)" fill="none" width="443.344" stroke-dasharray="6,2" rx="4" ry="4" height="353.499"/>
     </g>
     <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
@@ -600,13 +600,13 @@
       <text x="1583.5493" xml:space="preserve" y="941.6177" clip-path="url(#clipPath2)" stroke="none">DefaultParty</text>
       <rect fill="none" x="1576.5493" width="85.0449" height="30" y="922.0825" clip-path="url(#clipPath2)"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1821.6045 823.3602 L1697.2003 811.5406" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1693.2183 811.1624 L1704.597 818.2704 L1693.2183 811.1624 L1705.7319 806.3242 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1693.2183 811.1624 L1704.597 818.2704 L1693.2183 811.1624 L1705.7319 806.3242 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1342.7507 967.089 L1301.6271 1022.4778" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1299.2426 1025.6895 L1311.2134 1019.6314 L1299.2426 1025.6895 L1301.5786 1012.478 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1299.2426 1025.6895 L1311.2134 1019.6314 L1299.2426 1025.6895 L1301.5786 1012.478 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1255.7472 840.8061 L776.8444 951.5652" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M772.9473 952.4666 L785.9907 955.6083 L772.9473 952.4666 L783.2867 943.9169 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M772.9473 952.4666 L785.9907 955.6083 L772.9473 952.4666 L783.2867 943.9169 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1821.5979 823.4849 L1697.2019 811.7375" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1693.2196 811.3615 L1704.6023 818.4631 L1693.2196 811.3615 L1705.7305 806.5162 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1693.2196 811.3615 L1704.6023 818.4631 L1693.2196 811.3615 L1705.7305 806.5162 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1330.026 967.089 L1285.9127 1022.5784" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1283.4236 1025.7095 L1295.5878 1020.0499 L1283.4236 1025.7095 L1286.1945 1012.5823 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1283.4236 1025.7095 L1295.5878 1020.0499 L1283.4236 1025.7095 L1286.1945 1012.5823 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1248.9006 841.7592 L776.875 951.2792" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M772.9785 952.1833 L786.0241 955.3159 L772.9785 952.1833 L783.312 943.6264 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M772.9785 952.1833 L786.0241 955.3159 L772.9785 952.1833 L783.312 943.6264 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
       <path fill="none" d="M1621.5209 870.3434 L1619.6222 922.0825" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1621.7776 863.3481 L1626.5175 870.5268 L1621.2642 877.3387 L1616.5243 870.1601 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -614,59 +614,68 @@
     </g>
     <g fill="rgb(225,225,225)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(225,225,225)">
-      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" width="272.167" rx="4" ry="4" height="202.666" stroke="none"/>
-      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="272.167" height="21.666" stroke="none"/>
-    </g>
-    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
-      <text x="1492.4028" xml:space="preserve" y="666.7515" clip-path="url(#clipPath2)" stroke="none">inform</text>
-      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" fill="none" width="272.167" stroke-dasharray="6,2" rx="4" ry="4" height="202.666"/>
-    </g>
-    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
-      <rect x="1370.6714" width="52.8008" height="30" y="746.9155" clip-path="url(#clipPath2)" stroke="none"/>
-    </g>
-    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
-      <text x="1377.6714" xml:space="preserve" y="766.4507" clip-path="url(#clipPath2)" stroke="none">Inform</text>
-      <rect fill="none" x="1370.6714" width="52.8008" height="30" y="746.9155" clip-path="url(#clipPath2)"/>
-    </g>
-    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
-      <rect x="1446.2251" width="81.6934" height="30" y="740.9155" clip-path="url(#clipPath2)" stroke="none"/>
-    </g>
-    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
-      <text x="1453.2251" xml:space="preserve" y="760.4507" clip-path="url(#clipPath2)" stroke="none">ActionDone</text>
-      <rect fill="none" x="1446.2251" width="81.6934" height="30" y="740.9155" clip-path="url(#clipPath2)"/>
-    </g>
-    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
-      <rect x="1285.7515" width="60.6406" height="30" y="692.9155" clip-path="url(#clipPath2)" stroke="none"/>
-    </g>
-    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
-      <text x="1292.7515" xml:space="preserve" y="712.4507" clip-path="url(#clipPath2)" stroke="none">Settings</text>
-      <rect fill="none" x="1285.7515" width="60.6406" height="30" y="692.9155" clip-path="url(#clipPath2)"/>
-    </g>
-    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
-      <rect x="1388.7612" width="62.6211" height="30" y="686.9155" clip-path="url(#clipPath2)" stroke="none"/>
-    </g>
-    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
-      <text x="1395.7612" xml:space="preserve" y="706.4507" clip-path="url(#clipPath2)" stroke="none">Finished</text>
-      <rect fill="none" x="1388.7612" width="62.6211" height="30" y="686.9155" clip-path="url(#clipPath2)"/>
-    </g>
-    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
-      <rect x="1402.7729" width="68.5977" height="30" y="807.9155" clip-path="url(#clipPath2)" stroke="none"/>
-    </g>
-    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
-      <text x="1409.7729" xml:space="preserve" y="827.4507" clip-path="url(#clipPath2)" stroke="none">YourTurn</text>
-      <rect fill="none" x="1402.7729" width="68.5977" height="30" y="807.9155" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1446.2251 758.6386 L1438.439 759.1577" clip-path="url(#clipPath2)"/>
-      <path fill="white" d="M1423.4722 760.1555 L1439.8358 765.0779 L1439.0376 753.1045 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1423.4722 760.1555 L1439.8358 765.0779 L1439.0376 753.1045 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1338.5718 722.9155 L1362.0911 738.595" clip-path="url(#clipPath2)"/>
-      <path fill="white" d="M1374.5718 746.9155 L1364.5872 733.048 L1357.9308 743.0326 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1374.5718 746.9155 L1364.5872 733.048 L1357.9308 743.0326 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1279.0892 719.8394 L712.7974 902.4222" clip-path="url(#clipPath2)"/>
-      <path d="M1285.7515 717.6913 L1280.6235 724.5982 L1272.4269 721.9875 L1277.5548 715.0807 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1414.3218 716.9155 L1408.1908 732.9094" clip-path="url(#clipPath2)"/>
-      <path fill="white" d="M1402.8218 746.9156 L1414.1512 734.1232 L1402.9463 729.8281 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1402.8218 746.9156 L1414.1512 734.1232 L1402.9463 729.8281 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1427.2357 807.9155 L1415.1332 789.4592" clip-path="url(#clipPath2)"/>
-      <path fill="white" d="M1406.9078 776.9155 L1410.6641 793.5856 L1420.699 787.0053 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1406.9078 776.9155 L1410.6641 793.5856 L1420.699 787.0053 Z" clip-path="url(#clipPath2)"/>
+      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" width="255.8809" rx="4" ry="4" height="274.3256" stroke="none"/>
+      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="255.8809" height="21.666" stroke="none"/>
+    </g>
+    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
+      <text x="1476.1167" xml:space="preserve" y="666.7515" clip-path="url(#clipPath2)" stroke="none">inform</text>
+      <rect x="1270.7515" y="650.2495" clip-path="url(#clipPath2)" fill="none" width="255.8809" stroke-dasharray="6,2" rx="4" ry="4" height="274.3256"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1353.0337" width="52.8008" height="30" y="738.8943" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <text x="1360.0337" xml:space="preserve" y="758.4295" clip-path="url(#clipPath2)" stroke="none">Inform</text>
+      <rect fill="none" x="1353.0337" width="52.8008" height="30" y="738.8943" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1308.1948" width="81.6934" height="30" y="686.9155" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <text x="1315.1947" xml:space="preserve" y="706.4507" clip-path="url(#clipPath2)" stroke="none">ActionDone</text>
+      <rect fill="none" x="1308.1948" width="81.6934" height="30" y="686.9155" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1450.9917" width="60.6406" height="30" y="705.683" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <text x="1457.9917" xml:space="preserve" y="725.2181" clip-path="url(#clipPath2)" stroke="none">Settings</text>
+      <rect fill="none" x="1450.9917" width="60.6406" height="30" y="705.683" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1384.3491" width="62.6211" height="30" y="809.2347" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <text x="1391.3491" xml:space="preserve" y="828.7699" clip-path="url(#clipPath2)" stroke="none">Finished</text>
+      <rect fill="none" x="1384.3491" width="62.6211" height="30" y="809.2347" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1285.7515" width="68.5977" height="30" y="809.2347" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <text x="1292.7515" xml:space="preserve" y="828.7699" clip-path="url(#clipPath2)" stroke="none">YourTurn</text>
+      <rect fill="none" x="1285.7515" width="68.5977" height="30" y="809.2347" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1373.6675" width="83.9844" height="30" y="879.5751" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="1373.6675" width="83.9844" height="30" y="879.5751" clip-path="url(#clipPath2)"/>
+      <text x="1380.6675" xml:space="preserve" y="899.1103" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">Agreements</text>
+      <path fill="none" d="M1357.8121 716.9155 L1363.092 725.9454" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1370.6635 738.8943 L1367.7668 722.0536 L1357.4077 728.1108 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1370.6635 738.8943 L1367.7668 722.0536 L1357.4077 728.1108 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1450.9917 730.5671 L1420.0958 740.639" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1405.8345 745.288 L1422.9062 746.0336 L1419.187 734.6245 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1405.8345 745.288 L1422.9062 746.0336 L1419.187 734.6245 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1444.1842 729.574 L712.7974 904.7188" clip-path="url(#clipPath2)"/>
+      <path d="M1450.9917 727.9438 L1445.3486 734.4365 L1437.3766 731.2042 L1443.0198 724.7115 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1407.9346 809.2347 L1394.027 782.2297" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1387.1592 768.8943 L1389.1506 785.8659 L1399.819 780.3716 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1387.1592 768.8943 L1389.1506 785.8659 L1399.819 780.3716 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1332.7139 809.2347 L1357.0942 780.356" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1366.7706 768.8943 L1351.8645 777.2495 L1361.0338 784.9906 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1366.7706 768.8943 L1351.8645 777.2495 L1361.0338 784.9906 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1415.6597 879.5751 L1415.6597 846.2347" clip-path="url(#clipPath2)"/>
+      <path d="M1415.6597 839.2347 L1420.6597 846.2347 L1415.6597 853.2347 L1410.6597 846.2347 Z" clip-path="url(#clipPath2)" stroke="none"/>
     </g>
     <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
@@ -720,9 +729,9 @@
       <text x="879.1353" xml:space="preserve" y="564.7017" clip-path="url(#clipPath2)" stroke="none">DeadlineRound</text>
       <rect fill="none" x="872.1353" width="102.3301" height="30" y="545.1665" clip-path="url(#clipPath2)"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1255.7789 696.4816 L1140.34 646.8304" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1136.6654 645.25 L1145.3184 655.5032 L1136.6654 645.25 L1150.0597 644.4796 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1136.6654 645.25 L1145.3184 655.5032 L1136.6654 645.25 L1150.0597 644.4796 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
-      <path fill="none" d="M1287.2512 689.087 L1091.6481 561.2993" clip-path="url(#clipPath2)"/>
-      <path d="M1293.1115 692.9155 L1284.5166 693.2729 L1281.391 685.2585 L1289.9858 684.9011 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1248.8649 694.3713 L1140.3584 647.4004" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1136.6876 645.8114 L1145.3165 656.0847 L1136.6876 645.8114 L1150.0836 645.0722 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1136.6876 645.8114 L1145.3165 656.0847 L1136.6876 645.8114 L1150.0836 645.0722 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" d="M1444.5558 704.9604 L1108.7046 561.2993" clip-path="url(#clipPath2)"/>
+      <path d="M1450.9917 707.7134 L1442.5894 709.5576 L1438.1199 702.2075 L1446.5222 700.3633 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M915.7327 636.1665 L1015.2271 569.6371" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1027.6963 561.2993 L1011.0607 565.2053 L1017.731 575.1807 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -792,10 +801,10 @@
       <text x="1603.5942" xml:space="preserve" y="1541.1147" clip-path="url(#clipPath2)" stroke="none">size()</text>
       <rect fill="none" x="1582.2065" width="73.7305" height="56.3984" y="1494.2476" clip-path="url(#clipPath2)"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1552.2008 967.111 L1660.5536 1211.9993" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1662.172 1215.6572 L1662.8036 1202.2556 L1662.172 1215.6572 L1651.8297 1207.1111 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1662.172 1215.6572 L1662.8036 1202.2556 L1662.172 1215.6572 L1651.8297 1207.1111 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1567.2159 1409.7681 L1391.3019 1347.9185" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1387.5283 1346.5917 L1396.8589 1356.2322 L1387.5283 1346.5917 L1400.8391 1344.9115 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1387.5283 1346.5917 L1396.8589 1356.2322 L1387.5283 1346.5917 L1400.8391 1344.9115 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1549.6364 967.111 L1659.2053 1212.0077" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1660.8389 1215.6588 L1661.4149 1202.2548 L1660.8389 1215.6588 L1650.4613 1207.1556 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1660.8389 1215.6588 L1661.4149 1202.2548 L1660.8389 1215.6588 L1650.4613 1207.1556 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1567.2101 1412.85 L1335.5917 1334.7506" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1331.8014 1333.4725 L1341.2551 1342.9923 L1331.8014 1333.4725 L1345.0895 1331.6213 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1331.8014 1333.4725 L1341.2551 1342.9923 L1331.8014 1333.4725 L1345.0895 1331.6213 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
       <path fill="none" d="M1892.3491 1482.4468 L1901.077 1442.4995" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1904.2788 1427.8452 L1895.0018 1442.1958 L1906.7253 1444.7572 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -873,15 +882,15 @@
       <text x="1431.8735" xml:space="preserve" y="1141.8176" clip-path="url(#clipPath2)" stroke="none">Boulware, Bayesian</text>
       <rect fill="none" x="1424.8735" width="124.3965" height="70.5312" y="1080.8176" clip-path="url(#clipPath2)"/>
-      <path fill="none" stroke-dasharray="6,2" d="M1484.5331 1044.1659 L1481.5381 972.1007" clip-path="url(#clipPath2)"/>
-      <path stroke-dasharray="6,2" d="M1481.3719 968.1042 L1475.8755 980.343 L1481.3719 968.1042 L1487.8651 979.8447 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" stroke-miterlimit="10" d="M1481.3719 968.1042 L1475.8755 980.343 L1481.3719 968.1042 L1487.8651 979.8447 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" stroke-dasharray="6,2" d="M1483.866 1044.1659 L1480.0837 972.0981" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1479.874 968.1036 L1474.5114 980.4017 L1479.874 968.1036 L1486.4949 979.7726 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1479.874 968.1036 L1474.5114 980.4017 L1479.874 968.1036 L1486.4949 979.7726 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
     </g>
     <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
-      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" width="495.8594" rx="4" ry="4" height="370.666" stroke="none"/>
-      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="495.8594" height="21.666" stroke="none"/>
-    </g>
-    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
-      <text x="1172.5942" xml:space="preserve" y="16.502" clip-path="url(#clipPath2)" stroke="none">protocol</text>
-      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" fill="none" width="495.8594" stroke-dasharray="6,2" rx="4" ry="4" height="370.666"/>
+      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" width="507.6172" rx="4" ry="4" height="370.666" stroke="none"/>
+      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="507.6172" height="21.666" stroke="none"/>
+    </g>
+    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
+      <text x="1184.3521" xml:space="preserve" y="16.502" clip-path="url(#clipPath2)" stroke="none">protocol</text>
+      <rect x="739.5845" y="0" clip-path="url(#clipPath2)" fill="none" width="507.6172" stroke-dasharray="6,2" rx="4" ry="4" height="370.666"/>
     </g>
     <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="white">
@@ -969,7 +978,21 @@
       <text x="832.1401" xml:space="preserve" y="197.2012" clip-path="url(#clipPath2)" stroke="none">AllPermutationsProtocol</text>
       <rect fill="none" x="825.1401" width="153.8633" height="30" y="177.666" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M602.536 594.3585 L961.6526 295.7988" clip-path="url(#clipPath2)"/>
-      <path d="M597.1533 598.8336 L599.3395 590.5137 L607.9187 589.8834 L605.7325 598.2033 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <text x="604.5836" xml:space="preserve" y="589.3022" clip-path="url(#clipPath2)" stroke="none">   1</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1165.7148" width="48.5703" height="30" y="158.666" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="1165.7148" width="48.5703" height="30" y="158.666" clip-path="url(#clipPath2)"/>
+      <text x="1172.7148" xml:space="preserve" y="178.2012" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">AMOP</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1178.686" width="53.5156" height="30" y="98.666" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="1178.686" width="53.5156" height="30" y="98.666" clip-path="url(#clipPath2)"/>
+      <text x="1185.686" xml:space="preserve" y="118.2012" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">SHAOP</text>
+      <path fill="none" d="M602.8268 594.3963 L967.1334 295.8168" clip-path="url(#clipPath2)"/>
+      <path d="M597.4128 598.8335 L599.6573 590.5292 L608.2408 589.959 L605.9963 598.2634 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <text x="605.0865" xml:space="preserve" y="589.3022" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">   1</text>
       <path fill="none" stroke-dasharray="6,2" d="M1001.976 253.5332 L1040.6111 198.752" clip-path="url(#clipPath2)"/>
       <path stroke-dasharray="6,2" d="M1042.9165 195.4832 L1031.097 201.8316 L1042.9165 195.4832 L1040.9036 208.7478 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -978,6 +1001,6 @@
       <path fill="white" d="M971.3823 295.7988 L957.0272 305.0688 L966.6621 312.222 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M971.3823 295.7988 L957.0272 305.0688 L966.6621 312.222 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M935.6086 362.6224 L922.983 475.1665" clip-path="url(#clipPath2)"/>
-      <path d="M936.389 355.666 L940.5775 363.1798 L934.8282 369.5787 L930.6398 362.0649 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M939.6277 362.596 L923.4553 475.1665" clip-path="url(#clipPath2)"/>
+      <path d="M940.6232 355.6672 L944.5769 363.3071 L938.6323 369.5249 L934.6786 361.885 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1057.5162 90.666 L1049.2598 78.9331" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1040.6273 66.666 L1044.9285 83.2039 L1054.7421 76.2979 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -995,4 +1018,10 @@
       <path fill="white" d="M837.2718 252.666 L853.0884 246.1981 L844.9355 237.3929 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M837.2718 252.666 L853.0884 246.1981 L844.9355 237.3929 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1165.7148 174.738 L1121.772 176.6777" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1106.7866 177.3391 L1123.0356 182.6277 L1122.5065 170.6394 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1106.7866 177.3391 L1123.0356 182.6277 L1122.5065 170.6394 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1178.686 125.3327 L1102.2244 158.6709" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1088.4745 164.666 L1105.5391 163.7712 L1100.7429 152.7713 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1088.4745 164.666 L1105.5391 163.7712 L1100.7429 152.7713 Z" clip-path="url(#clipPath2)"/>
     </g>
     <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
@@ -1073,13 +1102,13 @@
       <text x="1618.1763" xml:space="preserve" y="338.1348" clip-path="url(#clipPath2)" stroke="none">DomainRef</text>
       <rect fill="none" x="1611.1763" width="77.791" height="30" y="316.666" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1227.4041 242.5872 L1524.2651 274.368" clip-path="url(#clipPath2)"/>
-      <path d="M1220.4438 241.8421 L1227.9363 237.6156 L1234.3643 243.3324 L1226.8718 247.5588 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1227.4084 241.7495 L1524.2651 274.2892" clip-path="url(#clipPath2)"/>
+      <path d="M1220.4501 240.9868 L1227.9532 236.7793 L1234.3667 242.5123 L1226.8636 246.7198 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1558.9607 292.666 L1575.4185 356.1461" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1579.1829 370.666 L1580.9755 353.6723 L1569.3595 356.6838 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1579.1829 370.666 L1580.9755 353.6723 L1569.3595 356.6838 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1181.4537 251.7088 L1458.9939 385.666" clip-path="url(#clipPath2)"/>
-      <path d="M1175.1497 248.666 L1183.6272 247.2058 L1187.7578 254.7515 L1179.2804 256.2117 Z" clip-path="url(#clipPath2)" stroke="none"/>
-      <path fill="none" d="M1328.016 686.8245 L1481.577 415.666" clip-path="url(#clipPath2)"/>
-      <path d="M1324.5665 692.9155 L1323.6653 684.3605 L1331.4655 680.7334 L1332.3668 689.2883 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1481.9142 698.6856 L1489.6611 415.666" clip-path="url(#clipPath2)"/>
+      <path d="M1481.7227 705.683 L1476.916 698.5488 L1482.1057 691.6882 L1486.9124 698.8224 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1190.1161 251.7621 L1459.8555 385.666" clip-path="url(#clipPath2)"/>
+      <path d="M1183.8462 248.6495 L1192.3394 247.2835 L1196.3861 254.8746 L1187.8929 256.2406 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1524.8892 395.0503 L1532.6401 393.8001" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1547.4487 391.4117 L1530.6975 388.0359 L1532.6083 399.8828 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -1088,11 +1117,11 @@
       <path d="M1604.5685 725.0825 L1598.8954 718.616 L1603.1725 711.1523 L1608.8456 717.6188 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <text x="1594.5719" xml:space="preserve" y="715.5513" clip-path="url(#clipPath2)" stroke="none">   *</text>
-      <path fill="none" d="M1337.8975 688.2268 L1562.4437 485.666" clip-path="url(#clipPath2)"/>
-      <path d="M1332.6998 692.9155 L1334.5483 684.5142 L1343.0951 683.538 L1341.2466 691.9394 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1489.7264 699.1636 L1573.2065 485.666" clip-path="url(#clipPath2)"/>
+      <path d="M1487.1772 705.683 L1485.0698 697.3428 L1492.2756 692.6443 L1494.3831 700.9845 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1579.7777 455.666 L1581.6608 415.6494" clip-path="url(#clipPath2)"/>
       <path fill="white" d="M1582.3658 400.666 L1575.6204 416.3663 L1587.6072 416.9304 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1582.3658 400.666 L1575.6204 416.3663 L1587.6072 416.9304 Z" clip-path="url(#clipPath2)"/>
-      <path fill="none" d="M1046.1815 281.4452 L1431.2983 325.6135" clip-path="url(#clipPath2)"/>
-      <path d="M1039.2271 280.6476 L1046.7511 276.4778 L1053.1359 282.2428 L1045.6118 286.4126 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1046.2069 280.8481 L1431.2983 325.5412" clip-path="url(#clipPath2)"/>
+      <path d="M1039.2535 280.0411 L1046.7833 275.8814 L1053.1602 281.6551 L1045.6305 285.8147 Z" clip-path="url(#clipPath2)" stroke="none"/>
       <path fill="none" d="M1509.3656 312.4284 L1535.3496 292.666" clip-path="url(#clipPath2)"/>
       <path d="M1503.7939 316.666 L1506.3387 308.4487 L1514.9373 308.1909 L1512.3925 316.4082 Z" clip-path="url(#clipPath2)" stroke="none"/>
@@ -1109,4 +1138,102 @@
       <path fill="none" d="M1601.6829 370.666 L1617.9055 365.2972 L1610.3752 355.954 Z" clip-path="url(#clipPath2)"/>
     </g>
+    <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
+      <rect x="1698.2383" y="1879.7272" clip-path="url(#clipPath2)" width="238.343" rx="4" ry="4" height="207.7158" stroke="none"/>
+      <rect x="1698.2383" y="1879.7272" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="238.343" height="21.666" stroke="none"/>
+    </g>
+    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
+      <text x="1819.3347" xml:space="preserve" y="1896.2291" clip-path="url(#clipPath2)" stroke="none">opponentmodel</text>
+      <rect x="1698.2383" y="1879.7272" clip-path="url(#clipPath2)" fill="none" width="238.343" stroke-dasharray="6,2" rx="4" ry="4" height="207.7158"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1713.2383" width="107.2812" height="30" y="1916.3932" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="1713.2383" width="107.2812" height="30" y="1916.3932" clip-path="url(#clipPath2)"/>
+      <text x="1720.2383" xml:space="preserve" y="1935.9283" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">OpponentModel</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="1754.7219" width="166.8594" height="30" y="2042.443" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="1754.7219" width="166.8594" height="30" y="2042.443" clip-path="url(#clipPath2)"/>
+      <text x="1761.7219" xml:space="preserve" y="2061.9783" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">FrequencyOpponentModel</text>
+      <path fill="none" d="M1746.3726 1916.3932 L1147.6935 1478.4703" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1135.5867 1469.6145 L1144.9583 1483.9034 L1152.043 1474.218 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1135.5867 1469.6145 L1144.9583 1483.9034 L1152.043 1474.218 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1829.6702 2042.443 L1782.7434 1959.4504" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M1775.3604 1946.3932 L1778.0127 1963.274 L1788.4585 1957.3677 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M1775.3604 1946.3932 L1778.0127 1963.274 L1788.4585 1957.3677 Z" clip-path="url(#clipPath2)"/>
+    </g>
+    <g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(245,245,245)">
+      <rect x="2132.1243" y="1452.3112" clip-path="url(#clipPath2)" width="365.5882" rx="4" ry="4" height="411.7296" stroke="none"/>
+      <rect x="2132.1243" y="1452.3112" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="365.5882" height="21.666" stroke="none"/>
+    </g>
+    <g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-17,15)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
+      <text x="2468.7739" xml:space="preserve" y="1468.8131" clip-path="url(#clipPath2)" stroke="none">boa</text>
+      <rect x="2132.1243" y="1452.3112" clip-path="url(#clipPath2)" fill="none" width="365.5882" stroke-dasharray="6,2" rx="4" ry="4" height="411.7296"/>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2241.8506" width="63.8281" height="30" y="1488.9772" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2241.8506" width="63.8281" height="30" y="1488.9772" clip-path="url(#clipPath2)"/>
+      <text x="2248.8506" xml:space="preserve" y="1508.5123" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">BoaParty</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2189.171" width="127.6191" height="30" y="1709.6782" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2189.171" width="127.6191" height="30" y="1709.6782" clip-path="url(#clipPath2)"/>
+      <text x="2196.1709" xml:space="preserve" y="1729.2134" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">AcceptanceStrategy</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2186.7135" width="127.6191" height="42.2656" y="1806.7751" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2186.7135" width="127.6191" height="42.2656" y="1806.7751" clip-path="url(#clipPath2)"/>
+      <text x="2204.2837" xml:space="preserve" y="1825.3767" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">TimeDependent</text>
+      <text x="2193.7134" xml:space="preserve" y="1839.5095" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">AcceptanceStrategy</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2147.1243" width="63.6523" height="30" y="1635.019" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2147.1243" width="63.6523" height="30" y="1635.019" clip-path="url(#clipPath2)"/>
+      <text x="2154.1243" xml:space="preserve" y="1654.5542" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">BoaState</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2377.4234" width="105.2891" height="30" y="1696.8975" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2377.4234" width="105.2891" height="30" y="1696.8975" clip-path="url(#clipPath2)"/>
+      <text x="2384.4233" xml:space="preserve" y="1716.4326" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">BiddingStrategy</text>
+    </g>
+    <g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke="rgb(255,204,0)">
+      <rect x="2377.019" width="105.2891" height="42.2656" y="1794.3473" clip-path="url(#clipPath2)" stroke="none"/>
+    </g>
+    <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-17,15)" stroke-linecap="butt">
+      <rect fill="none" x="2377.019" width="105.2891" height="42.2656" y="1794.3473" clip-path="url(#clipPath2)"/>
+      <text x="2385.2495" xml:space="preserve" y="1812.9489" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">TimeDe[endent</text>
+      <text x="2384.019" xml:space="preserve" y="1827.0817" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">BiddingStrategy</text>
+      <path fill="none" stroke-dasharray="6,2" d="M2132.144 1470.3217 L1645.9929 970.6581" clip-path="url(#clipPath2)"/>
+      <path stroke-dasharray="6,2" d="M1643.2035 967.7912 L1647.2715 980.576 L1643.2035 967.7912 L1655.8721 972.2076 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" stroke-miterlimit="10" d="M1643.2035 967.7912 L1647.2715 980.576 L1643.2035 967.7912 L1655.8721 972.2076 Z" clip-path="url(#clipPath2)" stroke-linecap="square"/>
+      <path fill="none" d="M2254.3931 1709.6782 L2271.6958 1525.9463" clip-path="url(#clipPath2)"/>
+      <path d="M2272.3521 1518.9772 L2276.6738 1526.415 L2271.0396 1532.9155 L2266.7178 1525.4775 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M2251.0261 1806.7751 L2252.2666 1754.674" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M2252.6235 1739.6782 L2246.2444 1755.5309 L2258.2412 1755.8164 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M2252.6235 1739.6782 L2246.2444 1755.5309 L2258.2412 1755.8164 Z" clip-path="url(#clipPath2)"/>
+      <path fill="none" d="M1788.8463 1916.3932 L2151.2021 1668.9664" clip-path="url(#clipPath2)"/>
+      <path d="M2156.9829 1665.019 L2154.0217 1673.0956 L2145.4214 1672.9139 L2148.3826 1664.8373 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <text x="2134.3411" xml:space="preserve" y="1683.0403" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">*</text>
+      <path fill="none" d="M2188.6887 1635.019 L2260.2146 1524.8484" clip-path="url(#clipPath2)"/>
+      <path d="M2264.0264 1518.9772 L2264.4082 1527.571 L2256.4028 1530.7195 L2256.021 1522.1257 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M2418.7917 1696.8975 L2289.2471 1524.5725" clip-path="url(#clipPath2)"/>
+      <path d="M2285.0408 1518.9772 L2293.2437 1521.568 L2293.4534 1530.1677 L2285.2505 1527.5769 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M2429.7461 1794.3473 L2429.9507 1741.8973" clip-path="url(#clipPath2)"/>
+      <path fill="white" d="M2430.0093 1726.8975 L2423.9468 1742.8739 L2435.9468 1742.9208 Z" clip-path="url(#clipPath2)" stroke="none"/>
+      <path fill="none" d="M2430.0093 1726.8975 L2423.9468 1742.8739 L2435.9468 1742.9208 Z" clip-path="url(#clipPath2)"/>
+    </g>
   </g>
 </svg>
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));
+	}
+}
Index: /exampleparties/anac2019/agentgg/pom.xml
===================================================================
--- /exampleparties/anac2019/agentgg/pom.xml	(revision 20)
+++ /exampleparties/anac2019/agentgg/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties.anac2019</groupId>
 	<artifactId>agentgg</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -60,5 +60,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/anac2019/agentgg/src/main/java/geniusweb/exampleparties/agentgg/AgentGG.java
===================================================================
--- /exampleparties/anac2019/agentgg/src/main/java/geniusweb/exampleparties/agentgg/AgentGG.java	(revision 20)
+++ /exampleparties/anac2019/agentgg/src/main/java/geniusweb/exampleparties/agentgg/AgentGG.java	(revision 21)
@@ -19,13 +19,13 @@
 import geniusweb.actions.PartyId;
 import geniusweb.bidspace.AllBidsList;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Value;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.PartialOrdering;
 import geniusweb.profileconnection.ProfileConnectionFactory;
Index: /exampleparties/anac2019/agentgg/src/test/java/geniusweb/exampleparties/agentgg/AgentGGTest.java
===================================================================
--- /exampleparties/anac2019/agentgg/src/test/java/geniusweb/exampleparties/agentgg/AgentGGTest.java	(revision 20)
+++ /exampleparties/anac2019/agentgg/src/test/java/geniusweb/exampleparties/agentgg/AgentGGTest.java	(revision 21)
@@ -34,11 +34,11 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.DefaultPartialOrdering;
 import geniusweb.profile.PartialOrdering;
Index: /exampleparties/anac2019/winkyagent/pom.xml
===================================================================
--- /exampleparties/anac2019/winkyagent/pom.xml	(revision 20)
+++ /exampleparties/anac2019/winkyagent/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties.anac2019</groupId>
 	<artifactId>winkyagent</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -60,5 +60,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/anac2019/winkyagent/src/main/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgent.java
===================================================================
--- /exampleparties/anac2019/winkyagent/src/main/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgent.java	(revision 20)
+++ /exampleparties/anac2019/winkyagent/src/main/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgent.java	(revision 21)
@@ -19,4 +19,9 @@
 import geniusweb.actions.PartyId;
 import geniusweb.bidspace.AllBidsList;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
@@ -26,9 +31,4 @@
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.PartialOrdering;
 import geniusweb.profileconnection.ProfileConnectionFactory;
Index: /exampleparties/anac2019/winkyagent/src/test/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgentTest.java
===================================================================
--- /exampleparties/anac2019/winkyagent/src/test/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgentTest.java	(revision 20)
+++ /exampleparties/anac2019/winkyagent/src/test/java/geniusweb/exampleparties/anac2019/winkyagent/WinkyAgentTest.java	(revision 21)
@@ -34,11 +34,11 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.DefaultPartialOrdering;
 import geniusweb.profile.PartialOrdering;
Index: /exampleparties/boulware/pom.xml
===================================================================
--- /exampleparties/boulware/pom.xml	(revision 20)
+++ /exampleparties/boulware/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>boulware</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>
 
Index: /exampleparties/comparebids/pom.xml
===================================================================
--- /exampleparties/comparebids/pom.xml	(revision 20)
+++ /exampleparties/comparebids/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>comparebids</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -55,5 +55,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java
===================================================================
--- /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java	(revision 20)
+++ /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java	(revision 21)
@@ -12,11 +12,11 @@
 import geniusweb.actions.ElicitComparison;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.PartialOrdering;
 import geniusweb.profile.Profile;
Index: /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java
===================================================================
--- /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java	(revision 20)
+++ /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java	(revision 21)
@@ -29,9 +29,9 @@
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/conceder/pom.xml
===================================================================
--- /exampleparties/conceder/pom.xml	(revision 20)
+++ /exampleparties/conceder/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>conceder</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>
 
Index: /exampleparties/hardliner/pom.xml
===================================================================
--- /exampleparties/hardliner/pom.xml	(revision 20)
+++ /exampleparties/hardliner/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>hardliner</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>
 
Index: /exampleparties/humangui/pom.xml
===================================================================
--- /exampleparties/humangui/pom.xml	(revision 20)
+++ /exampleparties/humangui/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>humangui</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -46,9 +46,4 @@
 			<artifactId>profileconnection</artifactId>
 			<version>${geniusweb.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>tudelft.utilities</groupId>
-			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
 		</dependency>
 		<dependency>
Index: /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/BiddingInfo.java
===================================================================
--- /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/BiddingInfo.java	(revision 20)
+++ /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/BiddingInfo.java	(revision 21)
@@ -13,7 +13,7 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profileconnection.ProfileInterface;
Index: /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java
===================================================================
--- /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java	(revision 20)
+++ /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java	(revision 21)
@@ -11,12 +11,12 @@
 import geniusweb.actions.Action;
 import geniusweb.actions.Offer;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
 import geniusweb.party.Party;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 20)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 21)
@@ -29,6 +29,6 @@
 import geniusweb.actions.Accept;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Settings;
 import geniusweb.issuevalue.Bid;
-import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java	(revision 20)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java	(revision 21)
@@ -27,10 +27,10 @@
 import geniusweb.actions.PartyId;
 import geniusweb.bidspace.AllBidsList;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/MyGUITest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/MyGUITest.java	(revision 20)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/MyGUITest.java	(revision 21)
@@ -19,6 +19,6 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/TestConnection.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/TestConnection.java	(revision 20)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/TestConnection.java	(revision 21)
@@ -8,5 +8,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 import geniusweb.references.Reference;
 import tudelft.utilities.listener.DefaultListenable;
Index: /exampleparties/linear/pom.xml
===================================================================
--- /exampleparties/linear/pom.xml	(revision 20)
+++ /exampleparties/linear/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>linear</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>
 
Index: /exampleparties/randomparty/pom.xml
===================================================================
--- /exampleparties/randomparty/pom.xml	(revision 20)
+++ /exampleparties/randomparty/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>randomparty</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -55,5 +55,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java
===================================================================
--- /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 20)
+++ /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 21)
@@ -5,6 +5,8 @@
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Random;
 import java.util.logging.Level;
+import java.util.stream.Collectors;
 
 import geniusweb.actions.Accept;
@@ -12,13 +14,17 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
 import geniusweb.bidspace.AllPartialBidsList;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.OptIn;
+import geniusweb.inform.Settings;
+import geniusweb.inform.Voting;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.PartialOrdering;
 import geniusweb.profile.Profile;
@@ -33,4 +39,7 @@
  * A simple party that places random bids and accepts when it receives an offer
  * with sufficient utility.
+ * <h2>voting</h2> If the party receives a parameter "minPower", it will
+ * {@link Vote} using that minPower instead of the default value 2 if it
+ * receives a {@link Voting} request.
  */
 public class RandomParty extends DefaultParty {
@@ -41,4 +50,6 @@
 	protected ProfileInterface profileint;
 	private Progress progress;
+	private Settings settings;
+	private Votes lastvotes;
 
 	public RandomParty() {
@@ -58,4 +69,5 @@
 				this.me = settings.getID();
 				this.progress = settings.getProgress();
+				this.settings = settings;
 			} else if (info instanceof ActionDone) {
 				Action otheract = ((ActionDone) info).getAction();
@@ -64,10 +76,16 @@
 				}
 			} else if (info instanceof YourTurn) {
-				myTurn();
-				if (progress instanceof ProgressRounds) {
-					progress = ((ProgressRounds) progress).advance();
-				}
+				makeOffer();
+				nextRound();
 			} else if (info instanceof Finished) {
 				getReporter().log(Level.INFO, "Final ourcome:" + info);
+			} else if (info instanceof Voting) {
+				lastvotes = vote((Voting) info);
+				getConnection().send(lastvotes);
+				nextRound();
+			} else if (info instanceof OptIn) {
+				// just repeat our last vote.
+				getConnection().send(lastvotes);
+				nextRound();
 			}
 		} catch (Exception e) {
@@ -76,7 +94,15 @@
 	}
 
+	private void nextRound() {
+		if (progress instanceof ProgressRounds) {
+			progress = ((ProgressRounds) progress).advance();
+		}
+
+	}
+
 	@Override
 	public Capabilities getCapabilities() {
-		return new Capabilities(new HashSet<>(Arrays.asList("SAOP")));
+		return new Capabilities(
+				new HashSet<>(Arrays.asList("SAOP", "AMOP", "MOPAC")));
 	}
 
@@ -86,7 +112,12 @@
 	}
 
-	private void myTurn() throws IOException {
+	/**
+	 * send our next offer
+	 */
+	private void makeOffer() throws IOException {
 		Action action;
-		if (isGood(lastReceivedBid)) {
+		String protocol = settings.getProtocol().getURI().getPath();
+		if ((protocol.equals("SAOP") || protocol.equals("SHAOP"))
+				&& isGood(lastReceivedBid)) {
 			action = new Accept(me, lastReceivedBid);
 		} else {
@@ -105,17 +136,42 @@
 	}
 
-	private boolean isGood(Bid bid) throws IOException {
+	/**
+	 * @param bid the bid to check
+	 * @return true iff bid is good for us.
+	 */
+	private boolean isGood(Bid bid) {
 		if (bid == null)
 			return false;
-		Profile profile = profileint.getProfile();
-		if (profile instanceof UtilitySpace) {
+		Profile profile;
+		try {
+			profile = profileint.getProfile();
+		} catch (IOException e) {
+			throw new IllegalStateException(e);
+		}
+		if (profile instanceof UtilitySpace)
 			return ((UtilitySpace) profile).getUtility(bid).doubleValue() > 0.6;
-		}
 		if (profile instanceof PartialOrdering) {
 			return ((PartialOrdering) profile).isPreferredOrEqual(bid,
 					profile.getReservationBid());
 		}
-		throw new IllegalArgumentException(
-				"Can not handle profile type " + profile.getClass());
+		return false;
+	}
+
+	/**
+	 * @param voting the {@link Voting} object containing the options
+	 * 
+	 * @return our next Votes.
+	 */
+	private Votes vote(Voting voting) throws IOException {
+		Object val = settings.getParameters().get("minPowwer");
+		Integer n = (val instanceof Integer) ? (Integer) val : 2;
+		for (Bid bid : voting.getBids()) {
+			System.out.println("Bid " + bid + "="
+					+ ((UtilitySpace) profileint.getProfile()).getUtility(bid));
+		}
+		List<Vote> votes = voting.getBids().stream().distinct()
+				.filter(bid -> isGood(bid)).map(bid -> new Vote(me, bid, n))
+				.collect(Collectors.toList());
+		return new Votes(me, votes);
 	}
 
Index: /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java
===================================================================
--- /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 20)
+++ /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 21)
@@ -16,4 +16,6 @@
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -32,13 +34,15 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+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.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
@@ -53,4 +57,5 @@
 public class RandomPartyTest {
 
+	private static final PartyId PARTY1 = new PartyId("party1");
 	private static final String SAOP = "SAOP";
 	private static final PartyId otherparty = new PartyId("other");
@@ -60,5 +65,5 @@
 	private RandomParty party;
 	private final TestConnection connection = new TestConnection();
-	private final ProtocolRef protocol = mock(ProtocolRef.class);
+	private final ProtocolRef protocol = new ProtocolRef(SAOP);
 	private final ProgressRounds progress = mock(ProgressRounds.class);
 	private Settings settings;
@@ -70,5 +75,5 @@
 			IOException, URISyntaxException {
 		party = new RandomParty();
-		settings = new Settings(new PartyId("party1"),
+		settings = new Settings(PARTY1,
 				new ProfileRef(new URI("file:" + PROFILE)), protocol, progress,
 				parameters);
@@ -189,4 +194,19 @@
 	}
 
+	@Test
+	public void testVoting() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		Bid bid = findGoodBid();
+		party.notifyChange(new Voting(Arrays.asList(bid),
+				Collections.singletonMap(PARTY1, 1)));
+		assertEquals(1, connection.getActions().size());
+		Action action = connection.getActions().get(0);
+		assertTrue(action instanceof Votes);
+		assertEquals(1, ((Votes) action).getVotes().size());
+		assertEquals(bid, ((Votes) action).getVotes().get(0).getBid());
+	}
+
 }
 
Index: /exampleparties/randompartypy/pom.xml
===================================================================
--- /exampleparties/randompartypy/pom.xml	(revision 20)
+++ /exampleparties/randompartypy/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>randompyparty</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>
 
@@ -48,5 +48,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/randompartypy/src/main/resources/RandomParty.py
===================================================================
--- /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 20)
+++ /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 21)
@@ -2,8 +2,8 @@
 import traceback
 
-import geniusweb.party.inform.ActionDone as ActionDone
-import geniusweb.party.inform.Inform as Inform
-import geniusweb.party.inform.Settings as Settings
-import geniusweb.party.inform.YourTurn as YourTurn
+import geniusweb.inform.ActionDone as ActionDone
+import geniusweb.inform.Inform as Inform
+import geniusweb.inform.Settings as Settings
+import geniusweb.inform.YourTurn as YourTurn
 
 
Index: /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java
===================================================================
--- /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java	(revision 20)
+++ /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java	(revision 21)
@@ -32,10 +32,10 @@
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/simpleboa/pom.xml
===================================================================
--- /exampleparties/simpleboa/pom.xml	(revision 20)
+++ /exampleparties/simpleboa/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>simpleboaparty</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
Index: /exampleparties/simpleboa/src/main/java/geniusweb/exampleparties/simpleboa/SimpleBoa.java
===================================================================
--- /exampleparties/simpleboa/src/main/java/geniusweb/exampleparties/simpleboa/SimpleBoa.java	(revision 20)
+++ /exampleparties/simpleboa/src/main/java/geniusweb/exampleparties/simpleboa/SimpleBoa.java	(revision 21)
@@ -8,8 +8,8 @@
 import geniusweb.boa.biddingstrategy.BiddingStrategy;
 import geniusweb.boa.biddingstrategy.TimeDependentBiddingStrategy;
+import geniusweb.inform.Settings;
 import geniusweb.opponentmodel.FrequencyOpponentModel;
 import geniusweb.opponentmodel.OpponentModel;
 import geniusweb.party.Party;
-import geniusweb.party.inform.Settings;
 import tudelft.utilities.logging.Reporter;
 
Index: /exampleparties/simpleboa/src/test/java/geniusweb/exampleparties/simpleboa/SimpleBoaTest.java
===================================================================
--- /exampleparties/simpleboa/src/test/java/geniusweb/exampleparties/simpleboa/SimpleBoaTest.java	(revision 20)
+++ /exampleparties/simpleboa/src/test/java/geniusweb/exampleparties/simpleboa/SimpleBoaTest.java	(revision 21)
@@ -36,11 +36,11 @@
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/simpleshaop/pom.xml
===================================================================
--- /exampleparties/simpleshaop/pom.xml	(revision 20)
+++ /exampleparties/simpleshaop/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>simpleshaop</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.6</jackson-2-version>
-		<geniusweb.version>1.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
@@ -55,5 +55,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java
===================================================================
--- /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java	(revision 20)
+++ /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java	(revision 21)
@@ -16,12 +16,12 @@
 import geniusweb.actions.PartyId;
 import geniusweb.bidspace.AllBidsList;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.PartialOrdering;
 import geniusweb.profileconnection.ProfileConnectionFactory;
Index: /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java
===================================================================
--- /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java	(revision 20)
+++ /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java	(revision 21)
@@ -36,13 +36,13 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
 import geniusweb.issuevalue.Value;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.progress.ProgressRounds;
Index: /exampleparties/timedependentparty/pom.xml
===================================================================
--- /exampleparties/timedependentparty/pom.xml	(revision 20)
+++ /exampleparties/timedependentparty/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb.exampleparties</groupId>
 	<artifactId>timedependentparty</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>
 
@@ -55,5 +55,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java
===================================================================
--- /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 20)
+++ /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 21)
@@ -13,12 +13,12 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditive;
Index: /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java
===================================================================
--- /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java	(revision 20)
+++ /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java	(revision 21)
@@ -34,11 +34,11 @@
 import geniusweb.bidspace.AllBidsList;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.issuevalue.Bid;
 import geniusweb.party.Capabilities;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
Index: /issuevalue/pom.xml
===================================================================
--- /issuevalue/pom.xml	(revision 20)
+++ /issuevalue/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>issuevalue</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>
 
@@ -93,5 +93,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /opponentmodel/pom.xml
===================================================================
--- /opponentmodel/pom.xml	(revision 20)
+++ /opponentmodel/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>opponentmodel</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>
 
Index: /party/pom.xml
===================================================================
--- /party/pom.xml	(revision 20)
+++ /party/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>party</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>
 
Index: /party/src/main/java/geniusweb/party/DefaultParty.java
===================================================================
--- /party/src/main/java/geniusweb/party/DefaultParty.java	(revision 20)
+++ /party/src/main/java/geniusweb/party/DefaultParty.java	(revision 21)
@@ -3,5 +3,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 import tudelft.utilities.listener.Listener;
 import tudelft.utilities.logging.ReportToLogger;
Index: /party/src/main/java/geniusweb/party/Party.java
===================================================================
--- /party/src/main/java/geniusweb/party/Party.java	(revision 20)
+++ /party/src/main/java/geniusweb/party/Party.java	(revision 21)
@@ -3,5 +3,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.Connectable;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 
 /**
Index: /party/src/test/java/geniusweb/party/inform/ActionDoneTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/ActionDoneTest.java	(revision 20)
+++ /party/src/test/java/geniusweb/party/inform/ActionDoneTest.java	(revision 21)
@@ -21,10 +21,10 @@
 import geniusweb.actions.Accept;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Inform;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
 import geniusweb.issuevalue.NumberValue;
 import geniusweb.issuevalue.Value;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Inform;
 import tudelft.utilities.junit.GeneralTests;
 
Index: /party/src/test/java/geniusweb/party/inform/AgreementsTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/AgreementsTest.java	(revision 21)
+++ /party/src/test/java/geniusweb/party/inform/AgreementsTest.java	(revision 21)
@@ -0,0 +1,67 @@
+package geniusweb.party.inform;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+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.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class AgreementsTest extends GeneralTests<Agreements> {
+	private final ObjectMapper jackson = new ObjectMapper();
+	private String jsonstring = "{\"party2\":{\"issuevalues\":{\"i1\":\"v1\"}},\"party1\":{\"issuevalues\":{\"i1\":\"v1\"}}}";
+
+	private Bid bid1 = new Bid("i1", new DiscreteValue("v1"));
+	private Bid bid2 = new Bid("i1", new DiscreteValue("v2"));
+	private PartyId party1 = new PartyId("party1");
+	private PartyId party2 = new PartyId("party2");
+	private PartyId party3 = new PartyId("party3");
+	private Agreements agree1 = new Agreements().with(bid1,
+			new HashSet<>(Arrays.asList(party1, party2)));
+	private Agreements agree1a = new Agreements().with(bid1,
+			new HashSet<>(Arrays.asList(party1, party2)));
+	private Agreements agree1b = new Agreements().with(bid1,
+			new HashSet<>(Arrays.asList(party2, party1)));
+
+	private Agreements agree2 = new Agreements().with(bid2,
+			new HashSet<>(Arrays.asList(party1, party2)));
+	private Agreements agree3 = new Agreements().with(bid1,
+			new HashSet<>(Arrays.asList(party1, party2, party3)));
+
+	@Override
+	public List<List<Agreements>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(agree1, agree1a, agree1b),
+				Arrays.asList(agree2), Arrays.asList(agree3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"Agreements.*party2.*Bid.*i1=\"v1\".*party1=Bid.*i1=\"v1\".*",
+				"Agreements.*party2.*Bid.*i1=\"v2\".*party1=Bid.*i1=\"v2\".*",
+				"Agreements.*party2.*Bid.*i1=\"v1\".*party1=Bid.*i1=\"v1\".*party3=Bid.*i1=\\\"v1\\\".*");
+	}
+
+	@Test
+	public void serializeTest() throws JsonProcessingException {
+		System.out.println(jackson.writeValueAsString(agree1));
+		assertEquals(jsonstring, jackson.writeValueAsString(agree1));
+	}
+
+	@Test
+	public void deserializeTest() throws IOException {
+		Agreements act = jackson.readValue(jsonstring, Agreements.class);
+		assertEquals(agree1, act);
+	}
+}
Index: /party/src/test/java/geniusweb/party/inform/FinishedTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/FinishedTest.java	(revision 20)
+++ /party/src/test/java/geniusweb/party/inform/FinishedTest.java	(revision 21)
@@ -8,4 +8,5 @@
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -19,10 +20,12 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
 import geniusweb.issuevalue.NumberValue;
 import geniusweb.issuevalue.Value;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
 import tudelft.utilities.junit.GeneralTests;
 
@@ -31,5 +34,8 @@
 	private Finished finished1, finished1a, finished2, finished3;
 
-	private String asJson = "{\"Finished\":{\"agreedBid\":{\"issuevalues\":{\"issue3\":9012,\"issue2\":1,\"issue1\":\"b\"}}}}";
+	private String asJson = "{\"Finished\":{\"agreements\":{\"party2\":{\"issuevalues\":{\"issue3\":9012,\"issue2\":1,\"issue1\":\"b\"}},\"party1\":{\"issuevalues\":{\"issue3\":9012,\"issue2\":1,\"issue1\":\"b\"}}}}}";
+
+	private PartyId party1 = new PartyId("party1");
+	private PartyId party2 = new PartyId("party2");
 
 	@Before
@@ -57,8 +63,15 @@
 		Bid bidc = new Bid(issuevaluesc);
 
-		finished1 = new Finished(bid);
-		finished1a = new Finished(bid);
-		finished2 = new Finished(bidb);
-		finished3 = new Finished(bidc);
+		Agreements agree = new Agreements().with(bid,
+				new HashSet<>(Arrays.asList(party1, party2)));
+		Agreements agreeb = new Agreements().with(bidb,
+				new HashSet<>(Arrays.asList(party1, party2)));
+		Agreements agreec = new Agreements().with(bidc,
+				new HashSet<>(Arrays.asList(party1, party2)));
+
+		finished1 = new Finished(agree);
+		finished1a = new Finished(agree);
+		finished2 = new Finished(agreeb);
+		finished3 = new Finished(agreec);
 	}
 
@@ -93,4 +106,7 @@
 	public void testDeserialize()
 			throws JsonParseException, JsonMappingException, IOException {
+		// FIXME there seems a disturbing bug in this test,
+		// jackson seems to ignore the @JsonProperty annotation and therefore
+		// this test does not work in some cases.
 		ObjectMapper jackson = new ObjectMapper();
 		Finished p = (Finished) jackson.readValue(asJson, Inform.class);
Index: /party/src/test/java/geniusweb/party/inform/OptInTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/OptInTest.java	(revision 21)
+++ /party/src/test/java/geniusweb/party/inform/OptInTest.java	(revision 21)
@@ -0,0 +1,76 @@
+package geniusweb.party.inform;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+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.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Inform;
+import geniusweb.inform.OptIn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class OptInTest extends GeneralTests<OptIn> {
+
+	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 Vote voteA1 = new Vote(partyA, bid1, 2);
+	private final Vote voteB1 = new Vote(partyB, bid1, 2);
+	private final Vote voteB2 = new Vote(partyB, bid2, 2);
+
+	private Votes votesA = new Votes(partyA, Arrays.asList(voteA1));
+	private Votes votesB = new Votes(partyB, Arrays.asList(voteB1, voteB2));
+
+	private final OptIn optIn1 = new OptIn(Arrays.asList(votesA, votesB));
+	private final OptIn optIn1a = new OptIn(Arrays.asList(votesA, votesB));
+	private final OptIn optIn2 = new OptIn(Arrays.asList(votesA));
+
+	private String asJson = "{\"OptIn\":{\"votes\":[{\"Votes\":{\"actor\":\"partyA\",\"votes\":[{\"vote\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2}}]}},{\"Votes\":{\"actor\":\"partyB\",\"votes\":[{\"vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2}},{\"vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val2\"}},\"minPower\":2}}]}}]}}";
+
+	@Override
+	public List<List<OptIn>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(optIn1, optIn1a),
+				Arrays.asList(optIn2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"OptIn.*Votes.*Vote.*partyA.*iss.*val1.*2.*Votes.*Vote.*partyB.*iss.*val1.*.*2.*Vote.*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: /party/src/test/java/geniusweb/party/inform/SettingsTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/SettingsTest.java	(revision 20)
+++ /party/src/test/java/geniusweb/party/inform/SettingsTest.java	(revision 21)
@@ -21,4 +21,6 @@
 
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
 import geniusweb.progress.ProgressRounds;
 import geniusweb.references.Parameters;
Index: /party/src/test/java/geniusweb/party/inform/VotingTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/VotingTest.java	(revision 21)
+++ /party/src/test/java/geniusweb/party/inform/VotingTest.java	(revision 21)
@@ -0,0 +1,72 @@
+package geniusweb.party.inform;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+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.inform.Inform;
+import geniusweb.inform.Voting;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import tudelft.utilities.junit.GeneralTests;
+
+public class VotingTest extends GeneralTests<Voting> {
+
+	private PartyId party1 = new PartyId("party1");
+	private Bid bid1 = new Bid("iss", new DiscreteValue("val1"));
+	private Bid bid2 = new Bid("iss", new DiscreteValue("val2"));
+
+	private Map<PartyId, Integer> powers1 = Collections.singletonMap(party1, 2);
+	private Map<PartyId, Integer> powers2 = Collections.singletonMap(party1, 3);
+
+	private final Voting voting1 = new Voting(Arrays.asList(bid1), powers1);
+	private final Voting voting1a = new Voting(Arrays.asList(bid1), powers1);
+	private final Voting voting2 = new Voting(Arrays.asList(bid2), powers1);
+	private final Voting voting3 = new Voting(Arrays.asList(bid1), powers2);
+
+	private String asJson = "{\"Voting\":{\"bids\":[{\"issuevalues\":{\"iss\":\"val1\"}}],\"powers\":{\"party1\":2}}}";
+
+	@Override
+	public List<List<Voting>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(voting1, voting1a),
+				Arrays.asList(voting2), Arrays.asList(voting3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("Voting.*Bid.*iss.*val1.*party1.*2.*",
+				"Voting.*Bid.*iss.*val2.*party1.*2.*",
+				"Voting.*Bid.*iss.*val1.*party1.*3.*");
+	}
+
+	@Test
+	public void testSerialize() throws JsonProcessingException {
+		ObjectMapper jackson = new ObjectMapper();
+
+		String json = jackson.writeValueAsString(voting1);
+		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(voting1, p);
+	}
+
+}
Index: /party/src/test/java/geniusweb/party/inform/YourTurnTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/inform/YourTurnTest.java	(revision 20)
+++ /party/src/test/java/geniusweb/party/inform/YourTurnTest.java	(revision 21)
@@ -16,6 +16,6 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.Inform;
+import geniusweb.inform.YourTurn;
 import tudelft.utilities.junit.GeneralTests;
 
Index: /pom.xml
===================================================================
--- /pom.xml	(revision 20)
+++ /pom.xml	(revision 21)
@@ -22,4 +22,5 @@
 		<module>profile</module>
 		<module>events</module>
+		<module>voting</module>
 		<module>bidspace</module>
 		<module>party</module>
Index: /profile/pom.xml
===================================================================
--- /profile/pom.xml	(revision 20)
+++ /profile/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>profile</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>
 
@@ -42,5 +42,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 
Index: /profileconnection/pom.xml
===================================================================
--- /profileconnection/pom.xml	(revision 20)
+++ /profileconnection/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>profileconnection</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>
 
Index: /protocol/pom.xml
===================================================================
--- /protocol/pom.xml	(revision 20)
+++ /protocol/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>protocol</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>
 
@@ -28,5 +28,14 @@
 
 	<dependencies>
-
+			<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>issuevalue</artifactId>
+			<version>${geniusweb.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>voting</artifactId>
+			<version>${geniusweb.version}</version>
+		</dependency>
 		<dependency>
 			<groupId>geniusweb</groupId>
@@ -34,10 +43,10 @@
 			<version>${geniusweb.version}</version>
 		</dependency>
-		<dependency>
+<!-- 		<dependency>
 			<groupId>geniusweb</groupId>
 			<artifactId>party</artifactId>
 			<version>${geniusweb.version}</version>
 		</dependency>
-		<dependency>
+ -->		<dependency>
 			<groupId>geniusweb</groupId>
 			<artifactId>references</artifactId>
@@ -52,5 +61,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>immutablelist</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 		<dependency>
Index: /protocol/src/main/java/geniusweb/protocol/NegoProtocol.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/NegoProtocol.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/NegoProtocol.java	(revision 21)
@@ -8,4 +8,5 @@
 
 import geniusweb.connection.ConnectionFactory;
+import geniusweb.deadline.Deadline;
 import geniusweb.events.CurrentState;
 import geniusweb.events.ProtocolEvent;
@@ -26,5 +27,5 @@
  * {@link #getDescription()}. A protocol reports the progress through its
  * {@link Listenable} interface. <br>
- * <p>
+ * <h2>General information</h2>
  * 
  * A protocol is mutable because the incoming connections cause state changes.
@@ -33,9 +34,4 @@
  * Because a protocol contains an internal state, it can be used only once.
  * <p>
- * The protocol also needs to keep an eye on the deadline and take appropriate
- * actions when the deadline is reached. <br>
- * <p>
- * A protocol should emit a {@link SessionEnded} or {@link TournamentEnded}
- * event when it is finished.
  * <p>
  * The protocol can emit a {@link CurrentState} event at any time. It should do
@@ -48,8 +44,21 @@
  * {@link #start(SessionSettings, ConnectionFactory)}.
  * 
+ * <h2>Ensure time deadline</h2>
+ * 
+ * The protocol also needs to keep an eye on the deadline and take appropriate
+ * actions when the deadline is reached. <br>
+ * A protocol should emit a {@link SessionEnded} or {@link TournamentEnded}
+ * event when it is finished.
+ * <p>
+ * All protocol implementations must ensure that the deadline is kept and that
+ * the session ends at the agreed time {@link Deadline#getDuration()}. This is
+ * to ensure that the negotiation ends and resources are freed up at or before
+ * some known time.
+ * 
  */
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
-@JsonSubTypes({ @Type(value = SessionProtocol.class), @Type(value = TournamentProtocol.class) })
+@JsonSubTypes({ @Type(value = SessionProtocol.class),
+		@Type(value = TournamentProtocol.class) })
 
 public interface NegoProtocol extends Listenable<ProtocolEvent> {
@@ -64,13 +73,13 @@
 	 * <p>
 	 * 
-	 * All errors are to be handled through {@link SessionState#getError()} except
-	 * for plain {@link IllegalArgumentException}s. <br>
-	 * The protocol usually uses the incoming connections to keep running. It does
-	 * not need to run in a separate thread or so.
+	 * All errors are to be handled through {@link SessionState#getError()}
+	 * except for plain {@link IllegalArgumentException}s. <br>
+	 * The protocol usually uses the incoming connections to keep running. It
+	 * does not need to run in a separate thread or so.
 	 * 
 	 * 
-	 * @param connectionfactory the {@link ProtocolToPartyConnFactory} that allows
-	 *                          the protocol to connect with the {@link Reference}s
-	 *                          in the settings
+	 * @param connectionfactory the {@link ProtocolToPartyConnFactory} that
+	 *                          allows the protocol to connect with the
+	 *                          {@link Reference}s in the settings
 	 */
 	void start(ProtocolToPartyConnFactory connectionfactory);
@@ -78,7 +87,7 @@
 	/**
 	 * 
-	 * @return a complete description of how this protocol behaves. Presented to the
-	 *         end users who should know negotiation basics but not all technical
-	 *         terms.
+	 * @return a complete description of how this protocol behaves. Presented to
+	 *         the end users who should know negotiation basics but not all
+	 *         technical terms.
 	 */
 	String getDescription();
@@ -86,6 +95,6 @@
 	/**
 	 * @return current state: the results of all sessions run so far and how to
-	 *         continue from that point. Changes over time as the session proceeds.
-	 *         Errors are also stored in the state.
+	 *         continue from that point. Changes over time as the session
+	 *         proceeds. Errors are also stored in the state.
 	 */
 	NegoState getState();
@@ -98,6 +107,7 @@
 
 	/**
-	 * Add a party after the protocol has started. Only some protocols can handle
-	 * this call. Usual protocols take the settings with their constructor.
+	 * Add a party after the protocol has started. Only some protocols can
+	 * handle this call. Usual protocols take the settings with their
+	 * constructor.
 	 * 
 	 * @param party the {@link PartyWithProfile} to be added.
Index: /protocol/src/main/java/geniusweb/protocol/NegoState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/NegoState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/NegoState.java	(revision 21)
@@ -5,13 +5,12 @@
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 import geniusweb.protocol.session.SessionState;
 import geniusweb.protocol.tournament.TournamentState;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-
 /**
- * The current state of the tournament. Must be serializabl;e so that it can be
- * restarted if a crash occurs.
+ * The current state of the session/tournament. Must be serializabl;e so that it
+ * can be restarted if a crash occurs.
  */
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
@@ -36,9 +35,3 @@
 	boolean isFinal(long currentTimeMs);
 
-	/**
-	 * 
-	 * @return a fatal error occured during the tournament. returns null if no
-	 *         errors occured.
-	 */
-	ProtocolException getError();
 }
Index: /protocol/src/main/java/geniusweb/protocol/ProtocolException.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/ProtocolException.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/ProtocolException.java	(revision 21)
@@ -2,5 +2,4 @@
 
 import geniusweb.actions.PartyId;
-import geniusweb.party.Party;
 import geniusweb.references.PartyRef;
 
@@ -10,5 +9,6 @@
  */
 public class ProtocolException extends Exception {
-	private String party; // the failing party
+	private static final long serialVersionUID = 4618901784366523980L;
+	private PartyId party; // the failing party
 
 	/**
@@ -17,5 +17,5 @@
 	 *                {@link PartyRef}
 	 */
-	public ProtocolException(String message, String party) {
+	public ProtocolException(String message, PartyId party) {
 		this(message, party, null);
 	}
@@ -30,9 +30,8 @@
 	 * 
 	 * @param message the error message
-	 * @param party   offending party, either the {@link PartyId} or a
-	 *                {@link PartyRef}
+	 * @param party   offending {@link PartyId}
 	 * @param e       the cause of the error
 	 */
-	public ProtocolException(String message, String party, Throwable e) {
+	public ProtocolException(String message, PartyId party, Throwable e) {
 		super(party + ":" + message, e, true, false);
 		this.party = party;
@@ -43,5 +42,5 @@
 	 * @return offending party, either the {@link PartyId} or a {@link PartyRef}
 	 */
-	public String getParty() {
+	public PartyId getParty() {
 		return party;
 	}
Index: /protocol/src/main/java/geniusweb/protocol/WillBeCalled.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/WillBeCalled.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/WillBeCalled.java	(revision 21)
@@ -0,0 +1,46 @@
+package geniusweb.protocol;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Object containing function that will be called exactly once, on demand or at
+ * given deadline.
+ *
+ */
+public class WillBeCalled {
+	private static volatile ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
+			2);
+	private final Runnable runnable;
+	private volatile AtomicBoolean done = new AtomicBoolean(false);
+
+	/**
+	 * @param r       the runnable that will be run once, either when the user
+	 *                calls {@link #complete(Void)} or after delayMs, whichever
+	 *                comes first.
+	 * @param delayMs the delay after this will be called anywa (the 'deadline'
+	 *                in milliseconds. If null, no deadline call will be made
+	 *                and the user must call {@link #complete(Void)} himself.
+	 */
+	public WillBeCalled(Runnable r, Long delayMs) {
+		this.runnable = r;
+		if (delayMs != null) {
+			executor.schedule(() -> complete(), delayMs, TimeUnit.MILLISECONDS);
+		}
+	}
+
+	/**
+	 * Tells the runnable to be executed. Only the first time the function is
+	 * run, subsequent calls do nothing. Failures in runnable are NOT caught
+	 * here, as they are probably bugs
+	 * 
+	 * We must avoid synchronized here because users may use synchronized blocks
+	 * already.
+	 */
+	public void complete() {
+		if (!done.compareAndSet(false, true))
+			return;
+		runnable.run();
+	}
+}
Index: /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java	(revision 21)
@@ -4,5 +4,5 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 
 /**
@@ -14,5 +14,5 @@
 	/**
 	 * 
-	 * @return the partyId of the party that this connects to
+	 * @return the partyId of the party that this connects to.
 	 */
 	PartyId getParty();
Index: /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java	(revision 21)
@@ -6,5 +6,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionFactory;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 import geniusweb.references.Reference;
 import tudelft.utilities.repository.NoResourcesNowException;
Index: /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java	(revision 21)
@@ -9,10 +9,11 @@
 
 import geniusweb.actions.PartyId;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 
 /**
  * Contains all parties with their connections. immutable
  */
-public class ProtocolToPartyConnections implements Iterable<ProtocolToPartyConn> {
+public class ProtocolToPartyConnections
+		implements Iterable<ProtocolToPartyConn> {
 	private List<ProtocolToPartyConn> connections;
 
Index: /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 21)
@@ -57,5 +57,5 @@
 	 *                      {@link ProtocolException}s. All errors in our own
 	 *                      code are bugs (not ProtocolExceptions) and should
-	 *                      result in a throw.
+	 *                      result in a throw and terminate the session.
 	 */
 	public DefaultSessionState(List<Action> actions,
@@ -130,5 +130,4 @@
 	}
 
-	@Override
 	public ProtocolException getError() {
 		return error;
Index: /protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java	(revision 21)
@@ -0,0 +1,30 @@
+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/SessionResult.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/SessionResult.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/SessionResult.java	(revision 21)
@@ -10,5 +10,5 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.references.PartyWithProfile;
 
@@ -20,5 +20,5 @@
 public class SessionResult {
 	private final List<PartyWithProfile> participants;
-	private final Bid agreement;
+	private final Agreements agreements;
 	private final List<Double> penalties;
 
@@ -33,6 +33,5 @@
 	 *                     never be null. Some of them may have entered later of
 	 *                     left early. This list should contain them all.
-	 * @param agreement    the final agreement {@link Bid} of the session. Null
-	 *                     if no agreement was reached.
+	 * @param agreements   the final Agreements.
 	 * @param penalties    the penalties in [0,1] for each participant, same
 	 *                     order as the participants list.
@@ -48,9 +47,9 @@
 	public SessionResult(
 			@JsonProperty("participants") List<PartyWithProfile> participants,
-			@JsonProperty("agreement") Bid agreement,
+			@JsonProperty("agreements") Agreements agreements,
 			@JsonProperty("penalties") List<Double> penalties,
 			@JsonProperty("error") Throwable error) {
 		this.participants = participants;
-		this.agreement = agreement;
+		this.agreements = agreements;
 		this.penalties = penalties;
 		this.error = error;
@@ -69,9 +68,9 @@
 
 	/**
-	 * 8return the final agreement {@link Bid} of the session. Null if no
-	 * agreement was reached.
+	 * 8return the final {@link Agreements} of the session. May be empty, not
+	 * null
 	 */
-	public Bid getAgreement() {
-		return agreement;
+	public Agreements getAgreements() {
+		return agreements;
 	};
 
@@ -99,5 +98,5 @@
 	@Override
 	public String toString() {
-		return "SessionResult[" + participants + "," + agreement + ","
+		return "SessionResult[" + participants + "," + agreements + ","
 				+ penalties + "," + error + "]";
 	}
@@ -108,5 +107,5 @@
 		int result = 1;
 		result = prime * result
-				+ ((agreement == null) ? 0 : agreement.hashCode());
+				+ ((agreements == null) ? 0 : agreements.hashCode());
 		result = prime * result + ((error == null) ? 0 : error.hashCode());
 		result = prime * result
@@ -126,8 +125,8 @@
 			return false;
 		SessionResult other = (SessionResult) obj;
-		if (agreement == null) {
-			if (other.agreement != null)
+		if (agreements == null) {
+			if (other.agreements != null)
 				return false;
-		} else if (!agreement.equals(other.agreement))
+		} else if (!agreements.equals(other.agreements))
 			return false;
 		if (error == null) {
Index: /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 21)
@@ -6,4 +6,6 @@
 
 import geniusweb.protocol.NegoSettings;
+import geniusweb.protocol.session.amop.AMOPSettings;
+import geniusweb.protocol.session.mopac.MOPACSettings;
 import geniusweb.protocol.session.saop.SAOPSettings;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
@@ -15,5 +17,7 @@
  */
 @JsonSubTypes({ @JsonSubTypes.Type(value = SAOPSettings.class),
-		@JsonSubTypes.Type(value = SHAOPSettings.class) })
+		@JsonSubTypes.Type(value = SHAOPSettings.class),
+		@JsonSubTypes.Type(value = AMOPSettings.class),
+		@JsonSubTypes.Type(value = MOPACSettings.class) })
 public interface SessionSettings extends NegoSettings {
 
Index: /protocol/src/main/java/geniusweb/protocol/session/SessionState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/SessionState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/SessionState.java	(revision 21)
@@ -6,7 +6,9 @@
 
 import geniusweb.actions.Action;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.progress.Progress;
 import geniusweb.protocol.NegoState;
+import geniusweb.protocol.session.amop.AMOPState;
+import geniusweb.protocol.session.mopac.MOPACState;
 import geniusweb.protocol.session.saop.SAOPState;
 import geniusweb.protocol.session.shaop.SHAOPState;
@@ -35,6 +37,7 @@
  */
 @JsonSubTypes({ @JsonSubTypes.Type(value = SAOPState.class),
-		@JsonSubTypes.Type(value = SHAOPState.class) })
-
+		@JsonSubTypes.Type(value = SHAOPState.class),
+		@JsonSubTypes.Type(value = AMOPState.class),
+		@JsonSubTypes.Type(value = MOPACState.class) })
 public interface SessionState extends NegoState {
 
@@ -45,5 +48,6 @@
 	 * 
 	 * @return unmodifyable list of actions done so far, in the order in which
-	 *         they arrived.
+	 *         they arrived. List of list allows implementations to add some
+	 *         extra structure to the actions, eg one list per phase
 	 */
 	List<Action> getActions();
@@ -64,5 +68,5 @@
 	 *         {@link #isFinal()}.
 	 */
-	Bid getAgreement();
+	Agreements getAgreements();
 
 	/**
Index: /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 21)
@@ -7,5 +7,4 @@
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.protocol.session.shaop.ShaopTeam;
 import geniusweb.references.PartyWithProfile;
@@ -23,5 +22,5 @@
 // and do not (de)serialize individual TeamOfPartiesAndProfiles.
 //@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
-@JsonSubTypes({ @JsonSubTypes.Type(value = SaopPartyWithProfile.class),
+@JsonSubTypes({ @JsonSubTypes.Type(value = OnePartyTeam.class),
 		@JsonSubTypes.Type(value = ShaopTeam.class) })
 public interface TeamOfPartiesAndProfiles {
Index: /protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java	(revision 21)
@@ -0,0 +1,434 @@
+package geniusweb.protocol.session.amop;
+
+import java.io.IOException;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
+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.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.deadline.Deadline;
+import geniusweb.events.ProtocolEvent;
+import geniusweb.inform.ActionDone;
+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.WillBeCalled;
+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.amop.AMOPState.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.
+ * 
+ * <h1>parameter</h1> AMOP parties can receive a parameter: minVotes containing
+ * a Double. If set, the AMOP protocol checks that all {@link Votes} from that
+ * party have {@link Vote#getMinPower()} > minVotes.
+ * <h1>Protocol steps</h1> 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 variable remainingparties = {all parties}
+ * <li>The session deadline clock now starts ticking.
+ * <li>All parties are sent their settings.
+ * <li>Loop until {@link Deadline} is reached or |remainingparties|<2:
+ * <ol>
+ * <li>protocol sends {@link YourTurn} to all remainingparties. Each party now
+ * must submit an {@link Offer} within {@link #PHASE_TIME} seconds. If a party
+ * fails to submit it is send Finished and removed from from remainingparties.
+ * <li>protocol sends a {@link Voting} containing a List<Bid> containing all
+ * received {@link Bid}s. Each party must place his {@link Votes} within
+ * {@link #PHASE_TIME} seconds. If a party does not submit, it is send a
+ * {@link Finished} and removed from remainingparties. Previous votes for the
+ * same bid do not count. But see {@link Agreements}.
+ * <li>The protocol determines a maximum sized subset of size N>=2 of the votes
+ * for bid B for which the vote conditions hold. If there is such a subset, the
+ * parties that placed these votes reached an agreement. They are added to the
+ * agreement set, sent the {@link Finished} info and terminated. They are
+ * 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 AMOP extends DefaultListenable<ProtocolEvent>
+		implements SessionProtocol {
+	private static final long PHASE_TIME = 30000; // millis
+	private static final long TIME_MARGIN = 20l;// ms extra delay after deadline
+	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 AMOP = new ProtocolRef("AMOP");
+
+	private final Reporter log;
+	private AMOPState state = null; // mutable!
+	private volatile WillBeCalled finish = new WillBeCalled(() -> finish(),
+			null);
+	private volatile WillBeCalled endPhase = new WillBeCalled(() -> endPhase(),
+			null);
+
+	/**
+	 * 
+	 * @param state normally the initial state coming from SAOPSettings
+	 */
+	public AMOP(AMOPState 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 {
+			// we're in Phase.INIT still
+			connect(connectionfactory);
+			// this moves to
+			setDeadline();
+			setupParties();
+			endPhase.complete(); // end of INIT
+		} catch (Throwable e) {
+			handleError("Failed to start up session", new PartyId("UNKNOWN"),
+					e);
+		}
+	}
+
+	@Override
+	public String getDescription() {
+		return "All parties get YourTurn. They now can submit their Offer within 30 seconds. "
+				+ "Next they receive a ElicitComparison. They can now submit their Votes."
+				+ "If one of their Vote succeeds, they finish with an agreement. "
+				+ "Parties that did not yet reach agreement continue negotiating. "
+				+ "A parameter 'minVotes', if set, contains the the smallest value to be used in each Vote";
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party)
+			throws IllegalStateException {
+		throw new IllegalStateException(
+				"Dynamic joining a negotiation is not supported in AMOP");
+	}
+
+	@Override
+	public AMOPState getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return AMOP;
+	}
+
+	/*******************************************************************
+	 * 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());
+		List<ProtocolToPartyConn> connections = null;
+		log.log(Level.INFO, "AMOP connect " + parties);
+		while (connections == null) {
+			try {
+				connections = 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++) {
+			setState(this.state.with(connections.get(i), participants.get(i)));
+		}
+	}
+
+	/**
+	 * overridable factory method, used for testing.
+	 * 
+	 * @param r
+	 * @param delayMs
+	 * @return new WillBeCalled(r,delayms)
+	 */
+	protected WillBeCalled createWillBeCalled(Runnable r, Long delayMs) {
+		return new WillBeCalled(r, delayMs);
+	}
+
+	/**
+	 * Set state to proper deadline. Starts the timer tasks. This tasks triggers
+	 * a call to handleError when the session times out.
+	 */
+	private void setDeadline() {
+		long now = System.currentTimeMillis();
+		Deadline deadline = state.getSettings().getDeadline();
+		setState(state.with(ProgressFactory.create(deadline, now)));
+
+		finish = createWillBeCalled(() -> finish(),
+				TIME_MARGIN + deadline.getDuration());
+		log.log(Level.INFO, "AMOP deadline set to "
+				+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
+						.format(new Date(System.currentTimeMillis()
+								+ deadline.getDuration() + TIME_MARGIN)));
+	}
+
+	/**
+	 * 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.
+	 * 
+	 * @throws ProtocolException if a party does not follow the protocol
+	 */
+	protected synchronized void setupParties() throws ProtocolException {
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			conn.addListener(action -> actionRequest(conn, action));
+		}
+
+		for (ProtocolToPartyConn connection : state.getConnections()) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				throw new ProtocolException("Failed to initialize",
+						connection.getParty(), e);
+			}
+		}
+
+	}
+
+	/**
+	 * 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();
+		if (profile == null) {
+			throw new IllegalArgumentException(
+					"Missing profile for party " + connection.getReference());
+		}
+		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
+	 */
+	protected synchronized void actionRequest(
+			final ProtocolToPartyConn partyconn, final Action action) {
+		if (action == null) {
+			Throwable err = partyconn.getError();
+			if (err == null) {
+				err = new ProtocolException("Party sent a null action",
+						partyconn.getParty());
+			}
+			handleError(partyconn + "Protocol error", partyconn.getParty(),
+					err);
+			return;
+		}
+
+		try {
+			setState(state.with(partyconn.getParty(), action));
+			state.getConnections().broadcast(new ActionDone(action));
+			if (state.isAllPartiesActed()) {
+				endPhase.complete();
+			}
+		} catch (Throwable e) {
+			handleError("failed to handle action " + action,
+					partyconn.getParty(), e);
+		}
+
+	}
+
+	/**
+	 * The current phase is completed. 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 endPhase() {
+		if (state.isFinal(System.currentTimeMillis()))
+			return;
+
+		List<Bid> bids = null;
+		if (state.getPhase() == Phase.OFFER) {
+			bids = state.getPhaseActions().values().stream()
+					.map(act -> ((Offer) act).getBid())
+					.collect(Collectors.toList());
+		}
+
+		setState(state.nextPhase());
+
+		if (state.getPhase() == Phase.OFFER) {
+			broadcast(new YourTurn());
+		} else {
+			Map<PartyId, Integer> powers = state.getActiveParties().stream()
+					.collect(Collectors.toMap(pid -> pid, pid -> 1));
+			broadcast(new Voting(bids, powers));
+		}
+		/**
+		 * Really tricky but we can safely replace {@link #endPhase}: all
+		 * functions are synchronized so the state is always consistent when
+		 * this is called. MNMMM Yes and then?? need proof that not called right
+		 * after replace. I think we need proof that #endPhase is always
+		 * consistent with state , that phase in state is coupled
+		 */
+		endPhase = createWillBeCalled(() -> endPhase(),
+				PHASE_TIME + TIME_MARGIN);
+	}
+
+	/**
+	 * Alternative to {@link ProtocolToPartyConnections#broadcast(Inform)},
+	 * sending only to active parties
+	 * 
+	 * @param info the info to broadcast
+	 */
+	private void broadcast(Inform info) {
+		for (PartyId party : state.getActiveParties()) {
+			try {
+				state.getConnections().get(party).send(info);
+			} catch (IOException e) {
+				handleError("Party seems to have disconnected", party, e);
+			}
+		}
+	}
+
+	/**
+	 * Update state to include the given error and closes the party.
+	 * 
+	 * @param message The message to attach to the error
+	 * @param party   the party where the error occured
+	 * @param e       the exception that occured.
+	 */
+	private synchronized void handleError(final String message,
+			final PartyId partyid, final Throwable e) {
+		if (e instanceof ProtocolException) {
+			setState(state.with(partyid, (ProtocolException) e));
+		} else {
+			setState(state.with(partyid,
+					new ProtocolException(message, partyid, e)));
+		}
+		log.log(Level.WARNING,
+				"AMOP intercepted error from party " + partyid + ":" + message,
+				e);
+	}
+
+	/**
+	 * Sets the new state. If the new state is final, the finish-up procedure is
+	 * executed. All state changes go through here to ensure we check isFinal
+	 * before any state change.
+	 * 
+	 * @param newstate the new state.
+	 */
+	private synchronized void setState(AMOPState newstate) {
+		long now = System.currentTimeMillis();
+		if (state.isFinal(now)) {
+			finish.complete();
+			return;
+		}
+		this.state = newstate;
+		if (newstate.isFinal(now)) {
+			finish.complete();
+		}
+	}
+
+	/**
+	 * Called when we reach final state. Must be called through {@link #finish}
+	 * to ensure this is called only once. Send finished info to all parties,
+	 * notify current nego state as final and set {@link #isFinishedInfoSent}.
+	 * Double calls are automatically ignored.
+	 */
+	private synchronized void finish() {
+		Finished finished = new Finished(state.getAgreements());
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			try {
+				conn.send(finished);
+				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/amop/AMOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 21)
@@ -0,0 +1,127 @@
+package geniusweb.protocol.session.amop;
+
+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.OnePartyTeam;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Settings for AMOP negotiation.
+ */
+public class AMOPSettings implements SessionSettings {
+	private final List<OnePartyTeam> participants;
+	private final Deadline deadline;
+
+	/**
+	 * 
+	 * @param participants the list of {@link PartyWithProfile} in clockwise
+	 *                     order. There must be at least 2 to run the SAOP
+	 *                     protocol. But SAOP can be initialized with less, for
+	 *                     use in TournamentSettings.
+	 * @param deadline
+	 * @param minimumVotes the minimum number of votes allowed for a bid.
+	 */
+	@JsonCreator
+	public AMOPSettings(
+			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("deadline") Deadline deadline) {
+		this.participants = participants;
+		this.deadline = deadline;
+		if (participants == null) {
+			throw new IllegalArgumentException("participants must not be null");
+		}
+		if (deadline == null) {
+			throw new IllegalArgumentException("deadline must not be null");
+		}
+	}
+
+	@Override
+	public Double getMaxRunTime() {
+		return deadline.getDuration() / 1000d;
+	}
+
+	@Override
+	public SessionProtocol getProtocol(Reporter logger) {
+		return new AMOP(new AMOPState(this), logger);
+	}
+
+	@Override
+	public List<TeamOfPartiesAndProfiles> 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());
+	}
+
+	@Override
+	public String toString() {
+		return "AMOPSettings" + participants + "," + deadline + "]";
+	}
+
+	@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());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AMOPSettings other = (AMOPSettings) 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;
+		return true;
+	}
+
+	@Override
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof OnePartyTeam))
+			throw new IllegalArgumentException(
+					"Added party must be AmopPartyWithProfile but got "
+							+ party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
+		return new AMOPSettings(newparts, deadline);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java	(revision 21)
@@ -0,0 +1,456 @@
+package geniusweb.protocol.session.amop;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionResult;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.CollectedVotes;
+
+public class AMOPState implements SessionState {
+	/**
+	 * Phase determines what actions are allowed now. #isFinal terminates the
+	 * state.
+	 */
+	public enum Phase {
+		INIT, OFFER, VOTE;
+
+		public Phase next() {
+			return this == OFFER ? VOTE : OFFER;
+		}
+	}
+
+	private final Phase phase;
+	private final AMOPSettings settings;
+	private final Map<PartyId, PartyWithProfile> partyprofiles;
+	private final ProtocolToPartyConnections connections;
+	private final Agreements agreements;
+	private final List<PartyId> walkedAway;
+	private final List<Action> actions;
+	private final Progress progress;
+	private final Map<PartyId, ProtocolException> exceptions;
+
+	/**
+	 * Creates the initial state from the given settings and progress=null
+	 * 
+	 * @param settings the {@link SAOPSettings}
+	 */
+	public AMOPState(AMOPSettings settings) {
+		this(Phase.INIT, Collections.emptyList(),
+				new ProtocolToPartyConnections(Collections.emptyList()), null,
+				settings, Collections.emptyMap(), new Agreements(),
+				Collections.emptyMap(), Collections.emptyList());
+
+	}
+
+	/**
+	 * @param phase         The Phase
+	 * @param actions       the legal actions that have been done in the
+	 *                      negotiation. first action is the oldest. This MUST
+	 *                      NOT contain illegal actions, otherwise we can not
+	 *                      decypher the actions list anymore and find the
+	 *                      proper phase boundaries. Instead parties doing
+	 *                      illegal actions must be killed.
+	 * @param conns         the existing party connections. we assume ownership
+	 *                      of this so it should not be modified although
+	 *                      connections may of course break.
+	 * @param progress      the {@link Progress} line. can be null if not yet
+	 *                      known
+	 * @param settings      the {@link SAOPSettings}
+	 * @param partyprofiles map with the {@link PartyWithProfile} for connected
+	 *                      parties. null is equivalent to an empty map.
+	 * @param agreements    the agreements reached.
+	 * @param e             Possibly empty map of {@link ProtocolException}, the
+	 *                      keys are the party that failed to follow the
+	 *                      protocol.
+	 * @param walkedAway    list of parties that walked away from the
+	 *                      negotiation.
+	 */
+	protected AMOPState(Phase phase, List<Action> actions,
+			ProtocolToPartyConnections conns, Progress progress,
+			AMOPSettings settings, Map<PartyId, PartyWithProfile> partyprofiles,
+			Agreements agreements, Map<PartyId, ProtocolException> e,
+			List<PartyId> walkedAway) {
+		this.phase = phase;
+		this.actions = actions;
+		this.connections = conns;
+		this.progress = progress;
+		this.settings = settings;
+		this.partyprofiles = partyprofiles;
+		this.agreements = agreements;
+		this.exceptions = e;
+		this.walkedAway = walkedAway;
+	}
+
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	public Map<PartyId, PartyWithProfile> getPartyProfiles() {
+		return Collections.unmodifiableMap(partyprofiles);
+	}
+
+	/**
+	 * @return all current/remaining active party connections. Finished/crashed
+	 *         parties should be removed immediately.
+	 */
+	public ProtocolToPartyConnections getConnections() {
+		return connections;
+	}
+
+	/**
+	 * 
+	 * @param connection   the new {@link ProtocolToPartyConn}
+	 * @param partyprofile the {@link PartyWithProfile} that is associated with
+	 *                     this state
+	 * @return new SessionState with the new connection 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 AMOPState with(ProtocolToPartyConn connection,
+			PartyWithProfile partyprofile) {
+		ProtocolToPartyConnections newconns = getConnections().with(connection);
+		Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
+				partyprofiles);
+		newprofiles.put(connection.getParty(), partyprofile);
+		return new AMOPState(Phase.INIT, actions, newconns, progress, settings,
+				newprofiles, agreements, exceptions, walkedAway);
+	}
+
+	/**
+	 * @param id the {@link PartyId} of the party that failed.
+	 * @param e  the {@link ProtocolException} that occured in the party
+	 * @return a new state with the error set.
+	 */
+	public AMOPState with(PartyId id, ProtocolException e) {
+		Map<PartyId, ProtocolException> newExc = exceptions;
+		if (!exceptions.containsKey(id)) {
+			newExc = new HashMap<>(exceptions);
+			newExc.put(id, e);
+		}
+		return new AMOPState(phase, actions, connections, progress, settings,
+				partyprofiles, agreements, newExc, walkedAway);
+	}
+
+	/**
+	 * Sets the progress for this session. Can be set only if progress=null.
+	 * Should be set in INIT phase.
+	 * 
+	 * @param newprogress the new progress
+	 * @return new SAOPState with the progress set
+	 */
+	public AMOPState with(Progress newprogress) {
+		if (progress != null || newprogress == null || phase != Phase.INIT) {
+			throw new IllegalArgumentException(
+					"progress must be null, newprogress must be not null and phase must be INIT");
+		}
+		return new AMOPState(phase, actions, connections, newprogress,
+				getSettings(), partyprofiles, agreements, exceptions,
+				walkedAway);
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		return agreements;
+	}
+
+	@Override
+	public boolean isFinal(long currentTimeMs) {
+		boolean pastDeadline = progress != null
+				&& progress.isPastDeadline(currentTimeMs);
+		return phase != Phase.INIT
+				&& (pastDeadline || getActiveParties().size() < 2);
+	}
+
+	@Override
+	public AMOPSettings getSettings() {
+		return settings;
+	}
+
+	/**
+	 * @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 currentTimeMs the current time in ms since 1970, see
+	 *                      {@link System#currentTimeMillis()}
+	 * @return new SessionState with the action added as last action.
+	 * @throws ProtocolException if actor is violating the protocol
+	 * @throws RuntimeException  if we hit a bug.
+	 */
+
+	public AMOPState with(PartyId actor, Action action)
+			throws ProtocolException {
+		if (!actor.equals(action.getActor())) {
+			throw new ProtocolException(
+					"act by " + actor + " contains wrong actorid: " + action,
+					actor);
+		}
+		if (!getActiveParties().contains(actor))
+			throw new ProtocolException("Deactivated actor tried to act",
+					actor);
+		if (getPhaseActions().containsKey(actor))
+			throw new ProtocolException(
+					"Attempt to act twice in phase:" + action, actor);
+
+		List<Action> newactions = new LinkedList<>(getActions());
+		newactions.add(action);
+		List<PartyId> newWalkedAway = walkedAway;
+
+		// check protocol is followed for specific actions
+		if (action instanceof Votes) {
+			if (phase != Phase.VOTE)
+				throw new ProtocolException(
+						"Vote can only be placed in VOTE phase", actor);
+			// Notice we don't check the votes. You can actually vote for
+			// other bids. But such action would be useless as others can't vote
+			// on it.
+		} else if (action instanceof Offer) {
+			if (phase != Phase.OFFER) {
+				throw new ProtocolException(
+						"Offer can only be placed in OFFER phase", actor);
+			}
+		} else if (action instanceof EndNegotiation) {
+			newWalkedAway = new LinkedList<>(walkedAway);
+			newWalkedAway.add(action.getActor());
+
+		} else {
+			throw new ProtocolException(
+					"Action " + action + " is not allowed in AMOP", actor);
+		}
+
+		return new AMOPState(phase, newactions, connections, progress, settings,
+				partyprofiles, agreements, exceptions, newWalkedAway);
+	}
+
+	@Override
+	public SessionResult getResult() {
+		List<Double> emptylist = new LinkedList<>();
+		for (int n = 0; n < getConnections().size(); n++)
+			emptylist.add(0d);
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
+				emptylist, null);
+	}
+
+	/**
+	 * @return true iff all parties acted as required in the current Phase. We
+	 *         search back until the last {@link YourTurn} if Phase=VOTE. /
+	 *         {@link Vote} if Phase=OFFER and check that all current
+	 *         connections have acted.
+	 */
+	public boolean isAllPartiesActed() {
+		return getPhaseActions().keySet().containsAll(getActiveParties());
+	}
+
+	/**
+	 * @return all actions of the current phase NOTE this assumes at least 1
+	 *         action is done in each phase, so that we can detect the previous
+	 *         phase actions in the actions list.
+	 * 
+	 */
+	public Map<PartyId, Action> getPhaseActions() {
+		Map<PartyId, Action> newactions = new HashMap<>();
+		for (int n = actions.size() - 1; n >= 0; n--) {
+			Action act = actions.get(n);
+			if (act instanceof EndNegotiation)
+				continue;
+			if (phase == Phase.VOTE && !(act instanceof Votes))
+				break;
+			if (phase == Phase.OFFER && !(act instanceof Offer))
+				break;
+			newactions.put(act.getActor(), act);
+		}
+		return newactions;
+
+	}
+
+	/**
+	 * 
+	 * @return all currently active parties. These are the parties that do not
+	 *         yet have an {@link #agreements} nor caused {@link #exceptions}
+	 *         nor {@link #walkedAway}
+	 */
+	public List<PartyId> getActiveParties() {
+		List<PartyId> active = connections.stream().map(conn -> conn.getParty())
+				.collect(Collectors.toList());
+		active.removeAll(agreements.getMap().keySet());
+		active.removeAll(exceptions.keySet());
+		active.removeAll(walkedAway);
+		return active;
+	}
+
+	public Phase getPhase() {
+		return phase;
+	}
+
+	/**
+	 * 
+	 * @return list of parties that walked away with {@link EndNegotiation}
+	 */
+	public List<PartyId> getWalkedAway() {
+		return walkedAway;
+	}
+
+	/**
+	 * 
+	 * @return new state with next phase selected and a updated list of
+	 *         agreements. This must be called to end a phase, it's not
+	 *         triggered by an action. If current phase is VOTE and next phase
+	 *         OFFER then progress is also incremented.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public AMOPState nextPhase() {
+		Agreements newagreements = agreements;
+		Progress newprogress = progress;
+		if (phase == Phase.VOTE) {
+			// hacky cast
+			newagreements = collectVotes((Map) getPhaseActions());
+			if (newprogress instanceof ProgressRounds) {
+				newprogress = ((ProgressRounds) newprogress).advance();
+			}
+		}
+		return new AMOPState(phase.next(), actions, connections, newprogress,
+				settings, partyprofiles, newagreements, exceptions, walkedAway);
+	}
+
+	/**
+	 * 
+	 * @param allvotes the Votes of each party
+	 * @return all agreements that were reached from the given votes
+	 */
+	protected Agreements collectVotes(Map<PartyId, Votes> allvotes) {
+		Agreements newagreements = agreements;
+		while (true) {
+			// power is 1 for each known party.
+			Map<PartyId, Integer> powers = allvotes.keySet().stream()
+					.collect(Collectors.toMap(p -> p, p -> 1));
+			Map<Bid, Set<PartyId>> agrees = new CollectedVotes(allvotes, powers)
+					.getMaxAgreements();
+			if (agrees.isEmpty())
+				break;
+			// find the best one
+			Bid maxbid = agrees.keySet().stream()
+					.max(Comparator.comparingInt(bid -> agrees.get(bid).size()))
+					.get();
+			newagreements = newagreements.with(maxbid, agrees.get(maxbid));
+			for (PartyId party : agrees.get(maxbid)) {
+				allvotes.remove(party);
+			}
+		}
+		return newagreements;
+	}
+
+	@Override
+	public String toString() {
+		return "AMOPState[" + phase + "," + settings + "," + partyprofiles + ","
+				+ connections + "," + agreements + "," + walkedAway + ","
+				+ actions + "," + progress + "," + exceptions + "," + "]";
+	}
+
+	@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
+				+ ((connections == null) ? 0 : connections.hashCode());
+		result = prime * result
+				+ ((exceptions == null) ? 0 : exceptions.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());
+		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;
+		AMOPState other = (AMOPState) 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 (connections == null) {
+			if (other.connections != null)
+				return false;
+		} else if (!connections.equals(other.connections))
+			return false;
+		if (exceptions == null) {
+			if (other.exceptions != null)
+				return false;
+		} else if (!exceptions.equals(other.exceptions))
+			return false;
+		if (partyprofiles == null) {
+			if (other.partyprofiles != null)
+				return false;
+		} else if (!partyprofiles.equals(other.partyprofiles))
+			return false;
+		if (phase != 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;
+		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/mopac/MOPAC.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPAC.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPAC.java	(revision 21)
@@ -0,0 +1,424 @@
+package geniusweb.protocol.session.mopac;
+
+import java.io.IOException;
+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 variable remainingparties = {all parties}
+ * <li>The session deadline clock now starts ticking.
+ * <li>remainingparties are sent their settings.
+ * <li>Loop until {@link Deadline} is reached or |remainingparties|<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<Bid> containing all
+ * received {@link Bid}s. Each party must place his {@link Votes} within
+ * {@link #PHASE_TIME} seconds. 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
+ * {@link #PHASE_TIME} seconds. This new Votes must equal or extend the previous
+ * Votes of the party.
+ * <li>The protocol use the {@link MOPACState#getEvaluator()} to determine the
+ * ... . If there is such a subset, the parties that placed these votes reached
+ * an agreement. They are added to the agreement set, sent the {@link Finished}
+ * info and terminated. They are 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 MOPAC 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 MOPAC = new ProtocolRef("MOPAC");
+
+	private final Reporter log;
+	private MOPACState state = null; // mutable!
+
+	/**
+	 * 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
+	 */
+	public MOPAC(MOPACState 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 {
+			System.out.println("starting MOPAC");
+			// we're in Phase.INIT still
+			connect(connectionfactory);
+			setupParties();
+			long now = System.currentTimeMillis();
+			state = state.initPhase(now);
+			startPhase(now);
+		} catch (Throwable e) {
+			e.printStackTrace();
+			handleError("Failed to start MOPAC session", new PartyId("BUG"), e);
+		}
+	}
+
+	@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 MOPACState getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return MOPAC;
+	}
+
+	/*******************************************************************
+	 * 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());
+		List<ProtocolToPartyConn> connections = null;
+		log.log(Level.INFO, "MOPAC connect " + parties);
+		while (connections == null) {
+			try {
+				connections = 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++) {
+			state = state.with(connections.get(i), 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.
+	 * 
+	 * @throws ProtocolException if a party does not follow the protocol
+	 */
+	protected synchronized void setupParties() throws ProtocolException {
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			conn.addListener(action -> actionRequest(conn, action,
+					System.currentTimeMillis()));
+		}
+		Long now = System.currentTimeMillis();
+		state = state.with(
+				ProgressFactory.create(state.getSettings().getDeadline(), now));
+		for (ProtocolToPartyConn connection : state.getConnections()) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				state = state.with(new ProtocolException("Failed to initialize",
+						connection.getParty()));
+			}
+		}
+
+	}
+
+	/**
+	 * Send the {@link Inform} for the curreent phase to all parties and start
+	 * the time-out checker
+	 */
+	private void startPhase(long now) {
+		broadcastNegotiators(state.getPhase().getInform());
+		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) {
+		System.out.println("received " + action);
+		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);
+	}
+
+	/**
+	 * Alternative to {@link ProtocolToPartyConnections#broadcast(Inform)},
+	 * sending only to currently negotiating parties
+	 * 
+	 * @param info the info to broadcast
+	 */
+	private void broadcastNegotiators(Inform info) {
+		System.out.println("broadcasting " + info);
+		for (PartyId party : state.getPhase().getPartyStates()
+				.getNegotiatingParties()) {
+			try {
+				state.getConnections().get(party).send(info);
+			} catch (IOException e) {
+				handleError("Party seems to have disconnected", party, e);
+			}
+		}
+	}
+
+	/**
+	 * Update state to include the given error and closes the party.
+	 * 
+	 * @param message The message to attach to the error
+	 * @param party   the party where the error occured
+	 * @param e       the exception that occured.
+	 */
+	private void handleError(final String message, final PartyId partyid,
+			final Throwable e) {
+		if (e instanceof ProtocolException) {
+			state = state.with((ProtocolException) e);
+		} else {
+			state = state.with(new ProtocolException(message, partyid, e));
+		}
+		log.log(Level.WARNING,
+				"MOPAC intercepted error from party " + partyid + ":" + message,
+				e);
+	}
+
+//	/**
+//	 * Sets the new state. If the new state is final, the finish-up procedure is
+//	 * executed. All state changes go through here to ensure we check isFinal
+//	 * before any state change.
+//	 * 
+//	 * @param newstate the new state.
+//	 */
+//	private synchronized void setState(MOPACState newstate) {
+//		long now = System.currentTimeMillis();
+//		if (state.isFinal(now)) {
+//			endNegotiation();
+//			return;
+//		}
+//
+//		this.state = newstate;
+//		if (newstate.isFinal(now)) {
+//			endNegotiation();
+//		}
+//	}
+
+	/**
+	 * To be called when we reach final state. Must only be called if
+	 * {@link MOPACState#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 : state.getConnections()) {
+			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/mopac/MOPACSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 21)
@@ -0,0 +1,167 @@
+package geniusweb.protocol.session.mopac;
+
+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.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.VotingEvaluator;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Settings for MOPAC negotiation. in MOPAC, each party may get a "power"
+ * parameter containing an natural number >=1.
+ */
+public class MOPACSettings implements SessionSettings {
+	private final List<OnePartyTeam> participants;
+	private final Deadline deadline;
+	private final VotingEvaluator votingevaluator;
+
+	/**
+	 * 
+	 * @param participants the list of {@link PartyWithProfile} in clockwise
+	 *                     order. There must be at least 2 to run the MOPAC
+	 *                     protocol. But SAOP can be initialized with less, for
+	 *                     use in TournamentSettings.
+	 * 
+	 *                     All provided participants must
+	 * 
+	 * @param deadline
+	 * @param minimumVotes the minimum number of votes allowed for a bid.
+	 */
+	@JsonCreator
+	public MOPACSettings(
+			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("deadline") Deadline deadline,
+			@JsonProperty("votingevaluator") VotingEvaluator 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 MOPAC getProtocol(Reporter logger) {
+		return new MOPAC(new MOPACState(this), logger);
+	}
+
+	@Override
+	public List<TeamOfPartiesAndProfiles> 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 VotingEvaluator getVotingEvaluation() {
+		return votingevaluator;
+	}
+
+	@Override
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof OnePartyTeam))
+			throw new IllegalArgumentException(
+					"Added party must be OnePartyTeam but got " + party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
+		return new MOPACSettings(newparts, deadline, votingevaluator);
+	}
+
+	@Override
+	public String toString() {
+		return "MOPACSettings[" + 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;
+		MOPACSettings other = (MOPACSettings) 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 (OnePartyTeam party : participants) {
+			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/mopac/MOPACState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACState.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACState.java	(revision 21)
@@ -0,0 +1,300 @@
+package geniusweb.protocol.session.mopac;
+
+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.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionResult;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.mopac.phase.OfferPhase;
+import geniusweb.protocol.session.mopac.phase.Phase;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.VotingEvaluator;
+
+public class MOPACState implements SessionState {
+
+	private final Phase phase;
+	private final MOPACSettings settings;
+	private final Map<PartyId, PartyWithProfile> partyprofiles;
+	private final ProtocolToPartyConnections connections;
+	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 MOPACState(MOPACSettings settings) {
+		this(null, Arrays.asList(),
+				new ProtocolToPartyConnections(Collections.emptyList()), 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. A new-sub-list is added for each phase
+	 *                      change. It is assumed that the last element of the
+	 *                      list is the list with actions taken in the current
+	 *                      phase. first action in first list is the oldest.
+	 *                      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 conns         the existing party connections. we assume ownership
+	 *                      of this so it should not be modified although
+	 *                      connections may of course break.
+	 * @param progress      the {@link Progress} line. can be null if not yet
+	 *                      known. This happens because during setup we first
+	 *                      add all connections, and only when we have that we
+	 *                      set the correct deadline.
+	 * @param settings      the {@link SAOPSettings}
+	 * @param partyprofiles map with the {@link PartyWithProfile} for connected
+	 *                      parties. null is equivalent to an empty map.
+	 * @param evaluator     the {@link VotingEvaluator}
+	 */
+	@JsonCreator
+	protected MOPACState(@JsonProperty("phase") Phase phase,
+			@JsonProperty("actions") List<Action> actions,
+			@JsonProperty("connections") ProtocolToPartyConnections conns,
+			@JsonProperty("progress") Progress progress,
+			@JsonProperty("settings") MOPACSettings settings,
+			@JsonProperty("partyprofiles") Map<PartyId, PartyWithProfile> partyprofiles) {
+		this.phase = phase;
+		this.actions = actions;
+		this.connections = conns;
+		this.progress = progress;
+		this.settings = settings;
+		this.partyprofiles = partyprofiles;
+	}
+
+	/**
+	 * Can be called after all connections are initialized.
+	 * 
+	 * @param now current time ms since 1970
+	 * 
+	 * @return state with the initial partystates set.
+	 */
+	public MOPACState initPhase(long now) {
+		PartyStates partyStates = new PartyStates(getPowers());
+		Phase firstPhase = new OfferPhase(null, partyStates,
+				now + getAvailablePhaseTime(now),
+				settings.getVotingEvaluation());
+		return new MOPACState(firstPhase, actions, connections, progress,
+				settings, partyprofiles);
+	}
+
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	public Map<PartyId, PartyWithProfile> getPartyProfiles() {
+		return Collections.unmodifiableMap(partyprofiles);
+	}
+
+	/**
+	 * @return all current/remaining active party connections. Finished/crashed
+	 *         parties should be removed immediately.
+	 */
+	public ProtocolToPartyConnections getConnections() {
+		return connections;
+	}
+
+	/**
+	 * @param now current time ms since 1970
+	 * @return the max possible duration in ms of this phase. Maybe negative if
+	 *         ew are past deadline
+	 */
+	private Long getAvailablePhaseTime(long now) {
+		return Math.min(progress.getTerminationTime().getTime() - now,
+				Phase.PHASE_MAXTIME);
+	}
+
+	/**
+	 * 
+	 * @param connection   the new {@link ProtocolToPartyConn}
+	 * @param partyprofile the {@link PartyWithProfile} that is associated with
+	 *                     this state
+	 * @return new {@link MOPACState} with the new connection 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 MOPACState with(ProtocolToPartyConn connection,
+			PartyWithProfile partyprofile) {
+		if (phase != null)
+			throw new IllegalStateException(
+					"Adding connections only allowed while initializing");
+
+		ProtocolToPartyConnections newconns = getConnections().with(connection);
+		Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
+				partyprofiles);
+		newprofiles.put(connection.getParty(), partyprofile);
+		return new MOPACState(null, actions, newconns, progress, settings,
+				newprofiles);
+	}
+
+	/**
+	 * @param id the {@link PartyId} of the party that failed.
+	 * @param e  the {@link ProtocolException} that occurred in the party
+	 * @return a new state with the error set.
+	 */
+	public MOPACState with(ProtocolException e) {
+		return new MOPACState(phase.with(e), actions, connections, progress,
+				settings, partyprofiles);
+	}
+
+	/**
+	 * Sets the progress for this session. Can be set only if progress=null.
+	 * Should be set in INIT phase.
+	 * 
+	 * @param newprogress the new progress
+	 * @return new {@link MOPACState} with the progress set
+	 */
+	public MOPACState with(Progress newprogress) {
+		if (progress != null || newprogress == null || phase != null) {
+			throw new IllegalArgumentException(
+					"progress must be null, newprogress must be not null and phase must be INIT");
+		}
+		return new MOPACState(phase, actions, connections, newprogress,
+				getSettings(), 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 MOPACState} with phase initialized for next phase.
+	 */
+	public MOPACState nextPhase(long now) {
+		long remainingNegoTime = progress.getTerminationTime().getTime() - now;
+		Phase newphase = phase.next(now,
+				Math.min(remainingNegoTime, Phase.PHASE_MAXTIME));
+		Progress newprogress = progress;
+		if (newphase instanceof OfferPhase
+				&& progress instanceof ProgressRounds)
+			newprogress = ((ProgressRounds) progress).advance();
+
+		return new MOPACState(newphase, actions, connections, newprogress,
+				getSettings(), partyprofiles);
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return phase != null && phase.isFinal(now) && !isNewPhasePossible(now);
+	}
+
+	/**
+	 * When this is called, all parties should have acted.
+	 * 
+	 * @param now
+	 * @return
+	 */
+	public boolean isNewPhasePossible(long now) {
+		return phase.getPartyStates().getNegotiatingParties().size() >= 2
+				&& getAvailablePhaseTime(now) > Phase.PHASE_MINTIME;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		return phase.getPartyStates().getAgreements();
+	}
+
+	@Override
+	public MOPACSettings getSettings() {
+		return settings;
+	}
+
+	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()}
+	 */
+
+	public MOPACState with(PartyId actor, Action action, long now) {
+		return new MOPACState(phase.with(actor, action, now), actions,
+				connections, progress, settings, partyprofiles);
+	}
+
+	@Override
+	public SessionResult getResult() {
+		List<Double> emptylist = new LinkedList<>();
+		for (int n = 0; n < getConnections().size(); n++)
+			emptylist.add(0d);
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
+				emptylist, null);
+	}
+
+	/**
+	 * @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 "MOPACState[" + phase + "," + settings + "," + partyprofiles
+				+ "," + connections + "," + progress + "," + "]";
+	}
+
+	/**
+	 * 
+	 * @return a wrapped-up state, with all parties doen an action or kicked,
+	 *         and agreements collected
+	 */
+	public MOPACState finishPhase() {
+		Phase newphase = phase.finish();
+		LinkedList<Action> newactions = new LinkedList<Action>(actions);
+		newactions.addAll(newphase.getPartyStates().getActions());
+		return new MOPACState(newphase, newactions, connections, progress,
+				settings, partyprofiles);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java	(revision 21)
@@ -0,0 +1,235 @@
+package geniusweb.protocol.session.mopac;
+
+import java.util.Arrays;
+import java.util.Collection;
+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 geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+
+/**
+ * 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.
+ * 
+ * This is not considering current time or checking actions.
+ */
+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 Map<PartyId, Integer> 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);
+	}
+
+	public PartyStates(Set<PartyId> notYetActed, List<Action> actions,
+			Agreements agreements, List<PartyId> walkedAway,
+			Map<PartyId, ProtocolException> exceptions,
+			Map<PartyId, Integer> powers) {
+		this.notYetActed = notYetActed;
+		this.actions = actions;
+		this.agreements = agreements;
+		this.walkedAway = walkedAway;
+		this.exceptions = exceptions;
+		this.powers = powers;
+	}
+
+	/**
+	 * 
+	 * @param action
+	 * @return new state with party done given action.
+	 * @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
+	 */
+	private Set<PartyId> removeParty(PartyId actor) {
+		return removeParties(new HashSet<>(Arrays.asList(actor)));
+	}
+
+	public Agreements getAgreements() {
+		return agreements;
+	}
+
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	public Map<PartyId, ProtocolException> getExceptions() {
+		return Collections.unmodifiableMap(exceptions);
+	}
+
+	/**
+	 * 
+	 * @param parties the parties to remove
+	 * @return {@link #notYetActed} without the given parties.
+	 */
+	private Set<PartyId> removeParties(Collection<PartyId> parties) {
+		Set<PartyId> newActiveParties = new HashSet<>(notYetActed);
+		for (PartyId party : parties) {
+			if (!newActiveParties.remove(party))
+				throw new IllegalArgumentException(
+						"Party " + party + " is not active, can't be removed");
+		}
+		return newActiveParties;
+	}
+
+	/**
+	 * @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);
+	}
+
+	/**
+	 * @param type the type of actions to extract
+	 * @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);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java	(revision 21)
@@ -0,0 +1,133 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public abstract class DefaultPhase implements Phase {
+	protected final List<Class<? extends Action>> allowedActions;
+	protected final Long deadline; // unix timestamp ms since 1970
+	protected final Phase prevPhase;
+	protected final PartyStates partyStates;
+	protected final VotingEvaluator evaluator;
+
+	/**
+	 * 
+	 * @param actions
+	 * @param prevPhases
+	 * @param partyStates
+	 * @param allowed
+	 * @param deadline    deadline time ms since 1970
+	 */
+	public DefaultPhase(Phase prevPhase, PartyStates partyStates,
+			List<Class<? extends Action>> allowed, Long deadline,
+			VotingEvaluator evaluator) {
+		this.allowedActions = allowed;
+		this.partyStates = partyStates;
+		this.deadline = deadline;
+		this.prevPhase = prevPhase;
+		this.evaluator = evaluator;
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return now >= deadline || partyStates.getNotYetActed().isEmpty();
+	}
+
+	@Override
+	public long getDeadline() {
+		return deadline;
+	}
+
+	/**
+	 * @return all votes done in this phase.
+	 */
+	public List<Votes> getVotes() {
+		return partyStates.getActions().stream()
+				.filter(act -> act instanceof Votes).map(act -> (Votes) act)
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * 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 activeParties the currently active parties
+	 * @param actor
+	 * @param action
+	 * @param timems
+	 * @throws ProtocolException
+	 * @throw ProtocolException if the action violates the protocol, or null if
+	 *        ok
+	 */
+	protected void checkAction(PartyId actor, Action action, long timems)
+			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(timems))
+			throw new ProtocolException("passed deadline", actor);
+		if (!(allowedActions.contains(action.getClass())))
+			throw new ProtocolException(
+					"Action not allowed in " + this.getClass() + ":" + action,
+					actor);
+		if (!partyStates.getNotYetActed().contains(actor))
+			throw new ProtocolException("Actor can not act anymore", actor);
+		return;
+	}
+
+	/**
+	 * @param timems current time
+	 * @return current PartyStates
+	 */
+	@Override
+	public PartyStates getPartyStates() {
+		return partyStates;
+	}
+
+	@Override
+	public VotingEvaluator 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(
+					"next must only be called when phase isFinal");
+		if (!partyStates.getNotYetActed().isEmpty())
+			throw new IllegalStateException("Bug. State has not been finished");
+		return checkedNext(now + duration);
+	}
+
+	/**
+	 * 
+	 * @param deadline
+	 * @return the next phase
+	 */
+	protected abstract Phase checkedNext(long deadline);
+
+	@Override
+	public String toString() {
+		// don't print prev state, it will give HUGE list because it also
+		// contains prevstate.....
+		return getClass().getSimpleName() + "[" + partyStates + "," + deadline
+				+ "," + allowedActions + "," + evaluator + "]";
+	}
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java	(revision 21)
@@ -0,0 +1,66 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+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.issuevalue.Bid;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public class OfferPhase extends DefaultPhase {
+
+	public OfferPhase(Phase prevPhase, PartyStates partyStates, Long deadlinems,
+			VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Offer.class, EndNegotiation.class), 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(prevPhase, 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(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Phase finish() {
+		return new OfferPhase(prevPhase, partyStates.finish(), deadline,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase checkedNext(long deadln) {
+		return new VotingPhase(this, partyStates.flush(), deadln, evaluator);
+	}
+
+	public List<Bid> getOffers() {
+		return partyStates.getActions(Offer.class).stream()
+				.map(offer -> offer.getBid()).collect(Collectors.toList());
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java	(revision 21)
@@ -0,0 +1,96 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Inform;
+import geniusweb.inform.OptIn;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+
+public class OptInPhase extends DefaultPhase {
+
+	protected OptInPhase(Phase prevPhase, PartyStates partyStates,
+			Long deadlinems, VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Votes.class, EndNegotiation.class), deadlinems,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new OptIn(((VotingPhase) prevPhase).getVotes());
+	}
+
+	@Override
+	public Phase with(PartyId actor, Action action, long now) {
+		try {
+			checkAction(actor, action, now);
+			if (action instanceof Votes)
+				checkExtends(action);
+
+			return new OptInPhase(prevPhase, partyStates.with(action), deadline,
+					evaluator);
+
+		} catch (ProtocolException e) {
+			return this.with(e);
+		}
+	}
+
+	/**
+	 * Check that this action extends previous action.
+	 * 
+	 * @param action
+	 * @param phase
+	 * @throws ProtocolException if this action does not correctly extend
+	 *                           previous vote.
+	 */
+	private void checkExtends(Action action) throws ProtocolException {
+
+		Votes votes = (Votes) action;
+		PartyId actor = action.getActor();
+		// this actor is active so he must have voted in previous round.
+		Votes prevVotes = ((VotingPhase) prevPhase).getVotes().stream()
+				.filter(v -> v.getActor().equals(actor)).findFirst().get();
+		if (!(votes.isExtending(prevVotes)))
+			throw new ProtocolException("New votes " + action
+					+ " does not extend previous vote " + prevVotes, actor);
+	}
+
+	@Override
+	public OptInPhase with(ProtocolException e) {
+		return new OptInPhase(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public OptInPhase finish() {
+		PartyStates states = partyStates.finish();
+		Map<PartyId, Votes> votesmap = states.getActions(Votes.class).stream()
+				.collect(Collectors.toMap(votes -> votes.getActor(), v -> v));
+		CollectedVotes allvotes = new CollectedVotes(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(prevPhase, finalStates, deadline, evaluator);
+	}
+
+	@Override
+	public OfferPhase checkedNext(long newdeadline) {
+		return new OfferPhase(this, partyStates.flush(), newdeadline,
+				evaluator);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java	(revision 21)
@@ -0,0 +1,101 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Set;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * 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.
+ *
+ */
+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 prevPhases the list of all previous phases
+	 * @param actor      the real actor
+	 * @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 curren time
+	 * @param duration the max duration 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);
+
+	/**
+	 * Get the voting evaluator
+	 * 
+	 * @return
+	 */
+	public VotingEvaluator getEvaluator();
+
+	/**
+	 * 
+	 * @return time (ms since 1970) at which phase ends. The phase may end
+	 *         before, but never after this.
+	 */
+	public long getDeadline();
+
+	/**
+	 * @param now the current time
+	 * 
+	 * @return the party states. Notice that someone must call
+	 *         {@link PartyStates#finish(Set)} if passed the deadline.
+	 */
+	public PartyStates getPartyStates();
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java	(revision 21)
+++ /protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java	(revision 21)
@@ -0,0 +1,58 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Voting;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public class VotingPhase extends DefaultPhase {
+
+	VotingPhase(Phase prevPhase, PartyStates partyStates, Long deadlinems,
+			VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Votes.class, EndNegotiation.class), deadlinems,
+				evaluator);
+	}
+
+	@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(prevPhase, partyStates.with(action), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new Voting(((OfferPhase) prevPhase).getOffers(),
+				partyStates.getPowers());
+	}
+
+	@Override
+	public VotingPhase with(ProtocolException e) {
+		return new VotingPhase(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase finish() {
+		return new VotingPhase(prevPhase, partyStates.finish(), deadline,
+				evaluator);
+	}
+
+	@Override
+	protected Phase checkedNext(long deadln) {
+		return new OptInPhase(this, partyStates.flush(), deadln, evaluator);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 21)
@@ -15,9 +15,9 @@
 import geniusweb.deadline.Deadline;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.ProgressFactory;
 import geniusweb.protocol.CurrentNegoState;
@@ -227,5 +227,5 @@
 			} catch (IOException e) {
 				throw new ProtocolException("Failed to initialize",
-						connection.getParty().getName(), e);
+						connection.getParty(), e);
 			}
 		}
@@ -257,5 +257,5 @@
 	 * Synchronized so that we always handle only 1 action at a time.
 	 * 
-	 * @param partyconn the connection on which the action came in
+	 * @param partyconn the connection on which the action came in.
 	 * @param action    the {@link Action} taken by some party
 	 */
@@ -266,8 +266,8 @@
 			if (err == null) {
 				err = new ProtocolException("Party sent a null action",
-						partyconn.getParty().getName());
-			}
-			handleError(partyconn + "Protocol error",
-					partyconn.getParty().getName(), err);
+						partyconn.getParty());
+			}
+			handleError(partyconn + "Protocol error", partyconn.getParty(),
+					err);
 			return;
 		}
@@ -278,5 +278,5 @@
 				throw new ProtocolException(
 						"Party acts without having the turn",
-						partyconn.getParty().getName());
+						partyconn.getParty());
 			}
 			state.getConnections().broadcast(new ActionDone(action));
@@ -286,5 +286,5 @@
 		} catch (Throwable e) {
 			handleError("failed to handle action " + action,
-					partyconn.getParty().getName(), e);
+					partyconn.getParty(), e);
 		}
 
@@ -301,5 +301,5 @@
 			state.getConnections().get(party).send(new YourTurn());
 		} catch (IOException e) {
-			handleError("failed to send YourTurn", party.getName(), e);
+			handleError("failed to send YourTurn", party, e);
 		}
 	}
@@ -313,5 +313,5 @@
 	 */
 	private synchronized void handleError(final String message,
-			final String party, final Throwable e) {
+			final PartyId party, final Throwable e) {
 		if (e instanceof ProtocolException) {
 			setState(state.with((ProtocolException) e));
@@ -353,5 +353,5 @@
 		if (!isFinishedInfoSent.compareAndSet(false, true))
 			return;
-		Inform finished = new Finished(state.getAgreement());
+		Inform finished = new Finished(state.getAgreements());
 		for (ProtocolToPartyConn conn : state.getConnections()) {
 			sendFinish(conn, finished);
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 21)
@@ -10,4 +10,5 @@
 
 import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
@@ -17,5 +18,5 @@
 
 public class SAOPSettings implements SessionSettings {
-	private final List<SaopPartyWithProfile> participants;
+	private final List<OnePartyTeam> participants;
 	private final Deadline deadline;
 
@@ -30,5 +31,5 @@
 	@JsonCreator
 	public SAOPSettings(
-			@JsonProperty("participants") List<SaopPartyWithProfile> participants,
+			@JsonProperty("participants") List<OnePartyTeam> participants,
 			@JsonProperty("deadline") Deadline deadline) {
 		this.participants = participants;
@@ -111,10 +112,9 @@
 	@Override
 	public SessionSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof SaopPartyWithProfile))
+		if (!(party instanceof OnePartyTeam))
 			throw new IllegalArgumentException(
-					"Added party must be SaopPartyWithProfile but got "
-							+ party);
-		List<SaopPartyWithProfile> newparts = new LinkedList<>(participants);
-		newparts.add((SaopPartyWithProfile) party);
+					"Added party must be OnePartyTeam but got " + party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
 		return new SAOPSettings(newparts, deadline);
 	}
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 21)
@@ -6,4 +6,6 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import geniusweb.actions.Accept;
@@ -12,4 +14,5 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.progress.Progress;
@@ -113,13 +116,14 @@
 
 	@Override
-	public Bid getAgreement() {
+	public Agreements getAgreements() {
+		Agreements agree = new Agreements();
 		List<Action> acts = getActions();
 		int nparticipants = getConnections().size();
 		if (nparticipants < 2 || acts.size() < nparticipants) {
-			return null;
+			return agree;
 		}
 		Action offer = acts.get(acts.size() - nparticipants);
 		if (!(offer instanceof Offer))
-			return null;
+			return agree;
 		Bid bid = ((Offer) offer).getBid();
 
@@ -128,5 +132,16 @@
 				.allMatch(act -> act instanceof Accept
 						&& bid.equals(((Accept) act).getBid()));
-		return allaccept ? bid : null;
+		if (allaccept)
+			agree = agree.with(bid, getParties());
+		return agree;
+	}
+
+	/**
+	 * 
+	 * @return all currently connected parties.
+	 */
+	private Set<PartyId> getParties() {
+		return getConnections().stream().map(conn -> conn.getParty())
+				.collect(Collectors.toSet());
 	}
 
@@ -134,6 +149,6 @@
 	public boolean isFinal(long currentTimeMs) {
 		List<Action> acts = getActions();
-		return super.isFinal(currentTimeMs) || getAgreement() != null
-				|| (!acts.isEmpty()
+		return super.isFinal(currentTimeMs)
+				|| !getAgreements().getMap().isEmpty() || (!acts.isEmpty()
 						&& acts.get(acts.size() - 1) instanceof EndNegotiation);
 	}
@@ -254,5 +269,5 @@
 		for (int n = 0; n < getConnections().size(); n++)
 			emptylist.add(0d);
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				emptylist, getError());
 	}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 21)
@@ -158,5 +158,4 @@
 	}
 
-	@Override
 	public ProtocolException getError() {
 		return error;
@@ -182,5 +181,5 @@
 			penalties[partyNumbers.get(party)] = spent;
 		}
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				Arrays.asList(penalties), getError());
 
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 21)
@@ -21,9 +21,9 @@
 import geniusweb.deadline.Deadline;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.ProgressFactory;
 import geniusweb.protocol.CurrentNegoState;
@@ -238,5 +238,5 @@
 			} catch (IOException e) {
 				throw new ProtocolException("Failed to initialize",
-						connection.getParty().getName(), e);
+						connection.getParty(), e);
 			}
 		}
@@ -276,5 +276,5 @@
 		} catch (Throwable e) {
 			handleError("failed to handle action " + action,
-					partyconn.getParty().getName(), e);
+					partyconn.getParty(), e);
 		}
 	}
@@ -289,6 +289,5 @@
 
 		if (action == null)
-			throw new ProtocolException("Party sent a null action",
-					partyid.getName());
+			throw new ProtocolException("Party sent a null action", partyid);
 
 		// check if action allowed
@@ -296,12 +295,10 @@
 			if (!ALLOWED_ACTIONS.contains(action.getClass())) {
 				throw new ProtocolException(
-						"Illegal action for SHAOP Party:" + action,
-						partyid.getName());
+						"Illegal action for SHAOP Party:" + action, partyid);
 			}
 		} else {
 			if (!(action instanceof Comparison))
 				throw new ProtocolException(
-						"Illegal action for COB Party:" + action,
-						partyid.getName());
+						"Illegal action for COB Party:" + action, partyid);
 		}
 
@@ -316,6 +313,5 @@
 			if (!partyconn.getParty().equals(state.getCurrentTeam()))
 				throw new ProtocolException(
-						"Party acts without having the turn",
-						partyid.getName());
+						"Party acts without having the turn", partyid);
 			setState(state.with(partyconn.getParty(), action));
 			state.connections.broadcast(new ActionDone(action));
@@ -336,5 +332,5 @@
 			state.getConnections().get(party).send(new YourTurn());
 		} catch (IOException e) {
-			handleError("failed to send YourTurn", party.getName(), e);
+			handleError("failed to send YourTurn", party, e);
 		}
 	}
@@ -348,5 +344,5 @@
 	 */
 	private synchronized void handleError(final String message,
-			final String party, final Throwable e) {
+			final PartyId party, final Throwable e) {
 		log.log(Level.WARNING, "SHAOP protocol intercepted error due to party "
 				+ party + ": " + message, e);
@@ -388,5 +384,5 @@
 		if (!isFinishedInfoSent.compareAndSet(false, true))
 			return;
-		Inform finished = new Finished(state.getAgreement());
+		Inform finished = new Finished(state.getAgreements());
 		state.connections.stream().forEach(conn -> sendFinish(conn, finished));
 		notifyListeners(new CurrentNegoState(state));
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 21)
@@ -6,4 +6,6 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -17,4 +19,5 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.progress.Progress;
@@ -78,4 +81,13 @@
 	/**
 	 * 
+	 * @return the team leader {@link PartyId}s.
+	 */
+	private Set<PartyId> getLeaders() {
+		return partyNumbers.keySet().stream().filter(id -> isShaopParty(id))
+				.collect(Collectors.toSet());
+	}
+
+	/**
+	 * 
 	 * @param party a Party Id
 	 * @return the PartyId of the party (COB and SHAOP are partners)
@@ -88,6 +100,6 @@
 	public boolean isFinal(long currentTimeMs) {
 		List<Action> acts = getActions();
-		return super.isFinal(currentTimeMs) || getAgreement() != null
-				|| (!acts.isEmpty()
+		return super.isFinal(currentTimeMs)
+				|| !getAgreements().getMap().isEmpty() || (!acts.isEmpty()
 						&& acts.get(acts.size() - 1) instanceof EndNegotiation);
 	}
@@ -166,8 +178,9 @@
 
 	@Override
-	public Bid getAgreement() {
+	public Agreements getAgreements() {
+		Agreements agrees = new Agreements();
 		List<Action> acts = getActions();
 		if (acts.isEmpty())
-			return null;
+			return agrees;
 
 		int requiredaccepts = getSettings().getTeams().size() - 1;
@@ -179,10 +192,10 @@
 				continue;
 			if (requiredaccepts == 0 && act instanceof Offer)
-				return ((Offer) act).getBid();
+				return agrees.with(((Offer) act).getBid(), getLeaders());
 			if (!(act instanceof Accept))
-				return null;
+				return agrees;
 			requiredaccepts--;
 		}
-		return null;
+		return agrees;
 	}
 
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 21)
@@ -9,8 +9,10 @@
 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.saop.SAOPSettings;
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
 import geniusweb.protocol.session.shaop.ShaopTeam;
@@ -106,5 +108,7 @@
 			throw new IllegalArgumentException("nTournaments must be >0");
 		int teamsize;
-		if (sesettings instanceof SAOPSettings)
+		if (sesettings instanceof SAOPSettings
+				|| sesettings instanceof MOPACSettings
+				|| sesettings instanceof AMOPSettings)
 			teamsize = 1;
 		else if (sesettings instanceof SHAOPSettings)
@@ -283,6 +287,8 @@
 			public TeamOfPartiesAndProfiles apply(Team team,
 					ProfileList profilelist) {
-				if (sessionsettings instanceof SAOPSettings)
-					return new SaopPartyWithProfile(team.getParties().get(0),
+				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) {
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 20)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 21)
@@ -5,5 +5,4 @@
 import java.util.List;
 
-import geniusweb.protocol.ProtocolException;
 import geniusweb.protocol.session.SessionResult;
 import geniusweb.protocol.session.SessionSettings;
@@ -65,9 +64,4 @@
 	}
 
-	@Override
-	public ProtocolException getError() {
-		return null;
-	}
-
 	/**
 	 * 
Index: /protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java	(revision 21)
@@ -0,0 +1,37 @@
+package geniusweb.protocol;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class WillBeCalledTest {
+	int counter;
+
+	@Test
+	public void testDeadline() throws InterruptedException {
+		counter = 0;
+		new WillBeCalled(() -> {
+			counter++;
+		}, 100l);
+		assertEquals(0, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+
+	}
+
+	@Test
+	public void testManual() throws InterruptedException {
+		counter = 0;
+		WillBeCalled w = new WillBeCalled(() -> {
+			counter++;
+		}, 100l);
+		assertEquals(0, counter);
+		w.complete();
+		assertEquals(1, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 21)
@@ -23,5 +23,5 @@
 import geniusweb.actions.PartyId;
 import geniusweb.deadline.DeadlineTime;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.progress.Progress;
 import geniusweb.progress.ProgressTime;
@@ -29,5 +29,4 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
 import geniusweb.protocol.session.saop.SAOPSettings;
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -60,10 +59,10 @@
 				new PartyRef("party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("profile2");
-		List<SaopPartyWithProfile> participants = Arrays.asList(
-				new SaopPartyWithProfile(party1ref, profile1),
-				new SaopPartyWithProfile(party2ref, profile2));
+		List<OnePartyTeam> participants = Arrays.asList(
+				new OnePartyTeam(party1ref, profile1),
+				new OnePartyTeam(party2ref, profile2));
 		SessionSettings settings = new SAOPSettings(participants, deadline);
 		ProtocolException e = new ProtocolException("test protocol error",
-				party1.getName());
+				party1);
 		HashMap<PartyId, PartyWithProfile> partyprofiles = new HashMap<PartyId, PartyWithProfile>();
 		partyprofiles.put(party1, new PartyWithProfile(party1ref, profile1));
@@ -135,9 +134,4 @@
 
 	@Override
-	public Bid getAgreement() {
-		return null;
-	}
-
-	@Override
 	public SessionResult getResult() {
 
@@ -145,8 +139,13 @@
 		for (int n = 0; n < getConnections().size(); n++)
 			emptylist.add(0d);
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				emptylist, getError());
 
 	}
 
+	@Override
+	public Agreements getAgreements() {
+		return new Agreements();
+	}
+
 }
Index: /protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java	(revision 21)
@@ -7,4 +7,5 @@
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -16,4 +17,6 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
@@ -32,15 +35,17 @@
 	private final String ISSUE1 = "issue1";
 	private SessionResult result1, result1a, result2, result3, result4;
-	private String errorstring; // created dynamically: eclipse and maven
-								// generate different stacktrace.
-	private String jsonstring = "{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"agreement\":{\"issuevalues\":{\"issue1\":\"a\"}},\"penalties\":[0.0,0.0],\"error\":null}";
+	private String jsonstring = "{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"agreements\":{\"party2\":{\"issuevalues\":{\"issue1\":\"a\"}},\"party1\":{\"issuevalues\":{\"issue1\":\"a\"}}},\"penalties\":[0.0,0.0],\"error\":null}";
 	private List<Double> nopenalties = Arrays.asList(0d, 0d);
 	private List<Double> penalties = Arrays.asList(0.1d, 0.2d);
 
+	private PartyId PARTY1 = new PartyId("party1");
+	private PartyId PARTY2 = new PartyId("party2");
+	private PartyId PARTY3 = new PartyId("party3");
+
 	@Before
 	public void before() throws URISyntaxException, JsonProcessingException {
-		errorstring = "\"error\":{\"java.lang.RuntimeException\":"
+		String errorstring = "\"error\":{\"java.lang.RuntimeException\":"
 				+ jackson.writeValueAsString(error) + "}";
-		System.out.println(errorstring);
+		// System.out.println(errorstring);
 
 		PartyWithParameters party1 = new PartyWithParameters(
@@ -57,4 +62,6 @@
 		issuevalues1.put(ISSUE1, new DiscreteValue("a"));
 		Bid bid1 = new Bid(issuevalues1);
+		Agreements agreement1 = new Agreements().with(bid1,
+				new HashSet<>(Arrays.asList(PARTY1, PARTY2)));
 
 		// different order but that shouldn't matter
@@ -62,16 +69,18 @@
 		issuevalues2.put(ISSUE1, new DiscreteValue("b"));
 		Bid bid2 = new Bid(issuevalues2);
+		Agreements agreement2 = new Agreements().with(bid2,
+				new HashSet<>(Arrays.asList(PARTY1, PARTY3)));
 
 		result1 = new SessionResult(Arrays.asList(partyprofile1, partyprofile2),
-				bid1, nopenalties, null);
+				agreement1, nopenalties, null);
 		result1a = new SessionResult(
-				Arrays.asList(partyprofile1, partyprofile2), bid1, nopenalties,
-				null);
+				Arrays.asList(partyprofile1, partyprofile2), agreement1,
+				nopenalties, null);
 		result2 = new SessionResult(Arrays.asList(partyprofile1, partyprofile2),
-				bid2, nopenalties, null);
+				agreement2, nopenalties, null);
 		result3 = new SessionResult(Arrays.asList(partyprofile2, partyprofile1),
-				bid2, nopenalties, null);
+				agreement1, nopenalties, null);
 		result4 = new SessionResult(Arrays.asList(partyprofile2, partyprofile1),
-				bid2, penalties, null);
+				agreement1, penalties, null);
 
 		// IGNORE ERROR for now, it fails somewhere deep in maven suddenly.
@@ -90,8 +99,8 @@
 	public List<String> getGeneralTestStrings() {
 		return Arrays.asList(
-				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Bid.*issue1=\"b\".*0\\.1.*0\\.2.*null.*"
+				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Agreements.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Agreements.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Agreements.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Agreements.*Bid.*issue1=\"a\".*0\\.1.*0\\.2.*null.*"
 
 		);
Index: /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 21)
@@ -0,0 +1,149 @@
+package geniusweb.protocol.session.amop;
+
+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.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+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,
+			partyprof2);
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
+			partyprof2, partyprof3);
+
+	private DeadlineTime deadline = mock(DeadlineTime.class);
+	private Deadline deadline2 = mock(Deadline.class);
+	private AMOPSettings settings1 = new AMOPSettings(participants2, deadline);
+	private AMOPSettings settings1a = new AMOPSettings(participants2, deadline);
+	private AMOPSettings settings2 = new AMOPSettings(participants3, deadline);
+	private AMOPSettings settings3 = new AMOPSettings(participants2, deadline2);
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	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;
+
+	@Before
+	public void before() throws URISyntaxException {
+		when(deadline.getDuration()).thenReturn(1000l);
+		when(deadline.toString()).thenReturn("deadline");
+		when(deadline2.toString()).thenReturn("deadline2");
+
+		when(partyprof1.toString()).thenReturn("party and profile 1");
+		when(partyprof2.toString()).thenReturn("party and profile 2");
+		when(partyprof3.toString()).thenReturn("party and profile 3");
+
+		// 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");
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		participants = Arrays.asList(partywithprof1, partywithprof2);
+
+		Deadline deadlinetime = new DeadlineTime(100);
+		sersettings = new AMOPSettings(participants, deadlinetime);
+
+	}
+
+	@Override
+	public List<List<AMOPSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"AMOPSettings.party and profile 1, party and profile 2.,deadline.",
+				"AMOPSettings.party and profile 1, party and profile 2, party and profile 3.,deadline.",
+				"AMOPSettings.party and profile 1, party and profile 2.,deadline2.");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("AMOP", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new AMOPSettings(participants2, null);
+	}
+
+	@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);
+
+		AMOPSettings settings = new AMOPSettings(participants2, deadline);
+
+		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() {
+		AMOPSettings saop = new AMOPSettings(participants2, deadline);
+		SessionSettings saop2 = saop.with(partyprof3);
+		assertEquals(3, saop2.getTeams().size());
+	}
+
+	@Test
+	public void testGetAllParties() {
+		assertEquals(participants, sersettings.getAllParties());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java	(revision 21)
@@ -0,0 +1,436 @@
+package geniusweb.protocol.session.amop;
+
+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.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.progress.ProgressTime;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.amop.AMOPState.Phase;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.junit.GeneralTests;
+
+public class AMOPStateTest extends GeneralTests<AMOPState> {
+	private final long NOW = 1000;
+	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 List<Action> actions = new LinkedList<>();
+	private final Action action = mock(Action.class);
+	private final List<Action> actions2 = Arrays.asList(action);
+
+	private final ProtocolToPartyConn party1conn = mock(
+			ProtocolToPartyConn.class);
+	private final ProtocolToPartyConn party2conn = mock(
+			ProtocolToPartyConn.class);
+	private final ProtocolToPartyConn party3conn = mock(
+			ProtocolToPartyConn.class);
+
+	private final ProtocolToPartyConnections connections = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party2conn));
+	private final ProtocolToPartyConnections connections2 = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party3conn));
+	private final ProtocolToPartyConnections connections3 = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party2conn, party3conn));
+
+	private final Progress progresstime = mock(ProgressTime.class);
+	private final ProgressRounds progressrounds = mock(ProgressRounds.class);
+	private final ProgressRounds progressrounds1 = mock(ProgressRounds.class);
+	private final AMOPSettings settings = mock(AMOPSettings.class);
+	private final AMOPSettings settings2 = mock(AMOPSettings.class);
+	private AMOPState state1, state1a, state2, state3, state4, state5, state6,
+			state7, state8, state9;
+
+	private final Bid bid1 = mock(Bid.class);
+	private final Bid otherbid = mock(Bid.class);
+	private final Accept accept1 = new Accept(party1, bid1);
+	private final Accept accept2 = new Accept(party2, bid1);
+	private final Accept acceptother = new Accept(party2, otherbid);
+	private final Offer offer1 = new Offer(party1, bid1);
+	private final EndNegotiation endNegotiation1 = new EndNegotiation(party1);
+
+	private final Map<PartyId, PartyWithProfile> partyprofiles1 = mock(
+			HashMap.class);
+	private final Map<PartyId, PartyWithProfile> partyprofiles2 = mock(
+			HashMap.class);
+
+	private final Agreements agreements1 = new Agreements();
+	private final Agreements agreements2 = mock(Agreements.class);
+
+	private final Map<PartyId, ProtocolException> exceptions1 = mock(
+			HashMap.class);
+	private final Map<PartyId, ProtocolException> exceptions2 = mock(
+			HashMap.class);
+
+	private final List<PartyId> walkedaway0 = Arrays.asList();
+	private final List<PartyId> walkedaway1 = Arrays.asList(party1);
+	private final List<PartyId> walkedaway3 = Arrays.asList(party3);
+
+	@Before
+	public void before() {
+		when(party1conn.getParty()).thenReturn(party1);
+		when(party2conn.getParty()).thenReturn(party2);
+		when(party3conn.getParty()).thenReturn(party3);
+		when(party1conn.toString()).thenReturn("conn1");
+		when(party2conn.toString()).thenReturn("conn2");
+		when(party3conn.toString()).thenReturn("conn3");
+		when(action.toString()).thenReturn("act1");
+
+		when(progressrounds.getTerminationTime())
+				.thenReturn(new Date(System.currentTimeMillis() + 3600000l));
+		when(progressrounds1.getTerminationTime())
+				.thenReturn(new Date(System.currentTimeMillis() + 3600000l));
+		when(progressrounds.advance()).thenReturn(progressrounds1);
+
+		state1 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state1a = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state2 = new AMOPState(Phase.INIT, actions2, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state3 = new AMOPState(Phase.INIT, actions, connections2, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state4 = new AMOPState(Phase.INIT, actions, connections, progressrounds,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state5 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings2, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state6 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles2, agreements1, exceptions1,
+				walkedaway0);
+		state7 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements2, exceptions1,
+				walkedaway0);
+		state8 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions2,
+				walkedaway0);
+		state9 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway3);
+	}
+
+	@Override
+	public List<List<AMOPState>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(state1, state1a),
+				Arrays.asList(state2), Arrays.asList(state3),
+				Arrays.asList(state4), Arrays.asList(state5),
+				Arrays.asList(state6), Arrays.asList(state7),
+				Arrays.asList(state8), Arrays.asList(state9));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		// being lazy, toString is terrible large and everything's mocked
+		return Arrays.asList("AMOPState.*", "AMOPState.*", "AMOPState.*",
+				"AMOPState.*", "AMOPState.*", "AMOPState.*", "AMOPState.*",
+				"AMOPState.*", "AMOPState.*");
+	}
+
+	@Test
+	public void constructWith0Connection() {
+		state1 = new AMOPState(Phase.INIT, actions,
+				new ProtocolToPartyConnections(Arrays.asList()), progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+	}
+
+	@Test
+	public void constructWithNullConnection() {
+		// is allowed
+		new AMOPState(Phase.INIT, actions, null, progresstime, settings,
+				partyprofiles1, agreements1, exceptions1, walkedaway0);
+	}
+
+	@Test
+	public void isInitFinalTest() {
+		assertFalse(state1.isFinal(NOW));
+	}
+
+	@Test
+	public void isInitFinalTest2() {
+		assertFalse(state1.isFinal(NOW + 12000));
+	}
+
+	@Test
+	public void withExceptionTest() {
+		AMOPState newstate1 = state1.with(party1,
+				mock(ProtocolException.class));
+		// party errors are not terminating in AMOP
+		assertFalse(newstate1.isFinal(NOW));
+		AMOPState newstate2 = newstate1.with(party2,
+				mock(ProtocolException.class));
+		assertFalse(newstate2.isFinal(NOW));
+	}
+
+	@Test
+	public void isOfferAgreementTest1() {
+		List<Action> actions = Arrays.asList(offer1);
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		assertTrue(state.getAgreements().getMap().isEmpty());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void illegalActionInIllegalState() throws ProtocolException {
+		// party can not do accept. And certainly not in INIT phase.
+		state1.with(party1, offer1).with(party1, accept2);
+	}
+
+	@Test
+	public void isOfferAllowedInOFFER() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAllowedInINIT() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAllowedInVOTE() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAcceptAgreementTest2() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1).with(party1, accept2);
+	}
+
+	@Test
+	public void testWithProgress() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections, null,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state = state.with(progresstime);
+		assertEquals(progresstime, state.getProgress());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithProgressAlreadySet() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(progresstime);
+	}
+
+	@Test
+	public void activePartiesWalkedAwayTest() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway3);
+		assertEquals(Arrays.asList(party1, party2), state.getActiveParties());
+	}
+
+	@Test
+	public void activePartiesAgreedTest() {
+		Agreements agreements = new Agreements().with(bid1,
+				new HashSet<>(Arrays.asList(party1, party2)));
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements,
+				exceptions1, walkedaway0);
+		assertEquals(Arrays.asList(party3), state.getActiveParties());
+	}
+
+	@Test
+	public void activePartiesErrorTest() {
+		Map<PartyId, ProtocolException> exceptions = Collections
+				.singletonMap(party2, new ProtocolException("test", party2));
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions, walkedaway0);
+		assertEquals(Arrays.asList(party1, party3), state.getActiveParties());
+	}
+
+	/************** isFinal tests *****************/
+	@Test
+	public void deadlineProgressTimeDeadlineTest() {
+		Progress progress = mock(Progress.class);
+		when(progress.isPastDeadline(any())).thenReturn(true);
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progress, settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		assertTrue(state.isFinal(10));
+	}
+
+	@Test
+	public void deadlineProgressTestOnly1Active() {
+		Progress progress = mock(Progress.class);
+		when(progress.isPastDeadline(any())).thenReturn(false);
+		List<PartyId> walkedaway13 = Arrays.asList(party1, party3);
+
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progress, settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway13);
+		assertTrue(state.isFinal(10));
+	}
+
+	/*************
+	 * action tests
+	 ***************/
+	@Test
+	public void OfferTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new Offer(party1, bid1));
+		assertEquals(1, state.getActions().size());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void OfferTwiceTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new Offer(party1, bid1));
+		// party1 may offer only once
+		state = state.with(party1, new Offer(party1, bid1));
+	}
+
+	@Test
+	public void endNegoOfferTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new EndNegotiation(party1));
+		assertEquals(1, state.getActions().size());
+		assertEquals(1, state.getWalkedAway().size());
+	}
+
+	@Test
+	public void endNegoVoteTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new EndNegotiation(party1));
+		assertEquals(1, state.getActions().size());
+		assertEquals(1, state.getWalkedAway().size());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void OfferButInactive() throws ProtocolException {
+		// party walked away or error'ed and then votes again
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		state = state.with(party1, new Offer(party1, bid1));
+	}
+
+	/************** NextPhase tests *****************/
+	@Test
+	public void isInitToOfferWorking() throws ProtocolException {
+		AMOPState state = state1.nextPhase();
+		assertEquals(Phase.OFFER, state.getPhase());
+	}
+
+	@Test
+	public void testCollectVotes() {
+		Votes vote1AB = new Votes(party1,
+				Arrays.asList(new Vote(party1, a, 2), new Vote(party1, b, 2)));
+		Votes vote2AB = new Votes(party2,
+				Arrays.asList(new Vote(party2, a, 2), new Vote(party2, b, 2)));
+		Votes vote3C = new Votes(party3, Arrays.asList(new Vote(party3, c, 2)));
+		Votes vote4AC = new Votes(party4,
+				Arrays.asList(new Vote(party4, a, 2), new Vote(party4, c, 2)));
+		// party 1,2,4 vote for A, party 1,2 vote for B, party 3,4 vote for C.
+		// the biggest vote is P,Q,S
+		Map<PartyId, Votes> votes = new HashMap<>();
+		votes.put(party1, vote1AB);
+		votes.put(party2, vote2AB);
+		votes.put(party3, vote3C);
+		votes.put(party4, vote4AC);
+
+		List<Action> actions1 = Arrays.asList(vote1AB, vote2AB, vote3C,
+				vote4AC);
+		AMOPState state = new AMOPState(Phase.VOTE, actions1, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		Agreements agrees = state.collectVotes(votes);
+		System.out.println(agrees);
+
+		// biggest agreement is party 1,2,4 for bid a.
+		assertEquals(new HashSet<>(Arrays.asList(party1, party2, party4)),
+				agrees.getMap().keySet());
+
+	}
+
+	@Test
+	public void testNextPhase() {
+		// copy of above. todo cleanup
+		Votes vote1AB = new Votes(party1,
+				Arrays.asList(new Vote(party1, a, 2), new Vote(party1, b, 2)));
+		Votes vote2AB = new Votes(party2,
+				Arrays.asList(new Vote(party2, a, 2), new Vote(party2, b, 2)));
+		Votes vote3C = new Votes(party3, Arrays.asList(new Vote(party3, c, 2)));
+		Votes vote4AC = new Votes(party4,
+				Arrays.asList(new Vote(party4, a, 2), new Vote(party4, c, 2)));
+		// party 1,2,4 vote for A, party 1,2 vote for B, party 3,4 vote for C.
+		// the biggest vote is P,Q,S
+		Map<PartyId, Votes> votes = new HashMap<>();
+		votes.put(party1, vote1AB);
+		votes.put(party2, vote2AB);
+		votes.put(party3, vote3C);
+		votes.put(party4, vote4AC);
+
+		List<Action> actions1 = Arrays.asList(vote1AB, vote2AB, vote3C,
+				vote4AC);
+		AMOPState state = new AMOPState(Phase.VOTE, actions1, connections,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		state = state.nextPhase();
+		assertEquals(Phase.OFFER, state.getPhase());
+		assertEquals(new HashSet<>(Arrays.asList(party1, party2, party4)),
+				state.getAgreements().getMap().keySet());
+		assertEquals(progressrounds.advance(), state.getProgress());
+
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java	(revision 21)
@@ -0,0 +1,6 @@
+package geniusweb.protocol.session.amop;
+
+public class AMOPTest {
+
+	// TODO did not have time for this.
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 21)
@@ -0,0 +1,165 @@
+package geniusweb.protocol.session.mopac;
+
+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.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import geniusweb.voting.VotingEvaluator;
+import geniusweb.voting.votingevaluators.LargestAgreement;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class MOPACSettingsTest extends GeneralTests<MOPACSettings> {
+
+	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,
+			partyprof2);
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
+			partyprof2, partyprof3);
+
+	private final VotingEvaluator ve = new LargestAgreement();
+
+	private DeadlineTime deadline = mock(DeadlineTime.class);
+	private Deadline deadline2 = mock(Deadline.class);
+	private MOPACSettings settings1 = new MOPACSettings(participants2, deadline,
+			ve);
+	private MOPACSettings settings1a = new MOPACSettings(participants2,
+			deadline, ve);
+	private MOPACSettings settings2 = new MOPACSettings(participants3, deadline,
+			ve);
+	private MOPACSettings settings3 = new MOPACSettings(participants2,
+			deadline2, ve);
+
+	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 OnePartyTeam mockParty(String partyname, Parameters params) {
+		OnePartyTeam team = mock(OnePartyTeam.class);
+		PartyWithParameters pwithp = mock(PartyWithParameters.class);
+		when(team.getParty()).thenReturn(pwithp);
+		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");
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		participants = Arrays.asList(partywithprof1, partywithprof2);
+
+		Deadline deadlinetime = new DeadlineTime(100);
+		sersettings = new MOPACSettings(participants, deadlinetime, ve);
+
+	}
+
+	@Override
+	public List<List<MOPACSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"MOPACSettings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPACSettings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPACSettings.*party1.*party2.*deadline2.*LargestAgreement.*");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("MOPAC", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new MOPACSettings(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);
+
+		MOPACSettings settings = new MOPACSettings(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() {
+		MOPACSettings saop = new MOPACSettings(participants2, deadline, ve);
+		SessionSettings saop2 = saop.with(partyprof3);
+		assertEquals(3, saop2.getTeams().size());
+	}
+
+	@Test
+	public void testGetAllParties() {
+		assertEquals(participants, sersettings.getAllParties());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 21)
@@ -0,0 +1,140 @@
+package geniusweb.protocol.session.mopac;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+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.List;
+
+import org.junit.Before;
+import org.junit.Ignore;
+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 tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+public class MOPACTest {
+
+	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.
+	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\"}],"
+			+ "\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
+
+	private MOPACSettings settings;
+	private Reporter logger = mock(Reporter.class);
+	private MOPAC mopac;
+
+	private final ProtocolToPartyConnFactory factory = mock(
+			ProtocolToPartyConnFactory.class);
+	private final ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
+			conn2 = mock(ProtocolToPartyConn.class),
+			conn3 = mock(ProtocolToPartyConn.class);
+
+	@Before
+	public void before() throws JsonParseException, JsonMappingException,
+			IOException, NoResourcesNowException {
+		settings = jackson.readValue(setstr, MOPACSettings.class);
+		mopac = settings.getProtocol(logger);
+
+		List 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() {
+		MOPACState state = mopac.getState();
+		assertEquals(settings, state.getSettings());
+		assertTrue(state.getActions().isEmpty());
+	}
+
+	@Test
+	public void testProtocol() {
+		assertEquals("MOPAC", mopac.getRef().getURI().getPath());
+	}
+
+	@Ignore
+	@Test
+	public void testWorkingCheckDeadline()
+			throws IOException, NoResourcesNowException, InterruptedException {
+
+		mopac.start(factory);
+
+		System.out.println(mopac.getState());
+
+		// we have deadline in 100ms so check
+		Thread.sleep(200);
+
+		System.out.println(mopac.getState());
+		assertTrue(mopac.getState().isFinal(System.currentTimeMillis()));
+	}
+
+	@Ignore
+	@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());
+	}
+
+	@Ignore
+	@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));
+	}
+
+	@Test
+	public void testAgreementTerminates() {
+
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java	(revision 21)
@@ -0,0 +1,57 @@
+package geniusweb.protocol.session.mopac;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+
+public class PartyStatesTest {
+
+	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;
+
+	public PartyStatesTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+	}
+
+	@Test
+	public void testBasics() {
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(powers.keySet(), states.getNotYetActed());
+		assertEquals(0, states.getExceptions().size());
+	}
+
+	@Test
+	public void testException() {
+		PartyStates newstates = states
+				.with(new ProtocolException("bla", party1));
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(new HashSet<>(Arrays.asList(party2, party3)),
+				newstates.getNotYetActed());
+	}
+
+	@Test
+	public void testFinish() {
+		PartyStates newstates = states.finish();
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(Collections.emptySet(), newstates.getNotYetActed());
+	}
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java	(revision 21)
+++ /protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java	(revision 21)
@@ -0,0 +1,67 @@
+package geniusweb.protocol.session.mopac.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 java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * We also test defaultPhase here.
+ */
+public class OfferPhaseTest {
+
+	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;
+	private final VotingEvaluator evaluator = mock(VotingEvaluator.class);
+	private final OfferPhase phase;
+
+	public OfferPhaseTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+		phase = new OfferPhase(null, states, 10l, evaluator);
+	}
+
+	@Test
+	public void isInitFinalTest() {
+		assertFalse(phase.isFinal(1l));
+		assertTrue(phase.isFinal(10l));
+	}
+
+	@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.emptyList()),
+				1);
+	}
+
+	@Test
+	public void testException() {
+		OfferPhase newphase = phase.with(new ProtocolException("bla", party1));
+		assertEquals(10l, newphase.getDeadline());
+		assertEquals(0l, phase.getPartyStates().getActions().size());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 21)
@@ -19,4 +19,5 @@
 import geniusweb.deadline.DeadlineRounds;
 import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
 import geniusweb.references.Parameters;
@@ -29,10 +30,10 @@
 public class SAOPSettingsTest extends GeneralTests<SAOPSettings> {
 
-	private SaopPartyWithProfile partyprof1 = mock(SaopPartyWithProfile.class);
-	private SaopPartyWithProfile partyprof2 = mock(SaopPartyWithProfile.class);
-	private SaopPartyWithProfile partyprof3 = mock(SaopPartyWithProfile.class);
-	private List<SaopPartyWithProfile> participants2 = Arrays.asList(partyprof1,
+	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,
 			partyprof2);
-	private List<SaopPartyWithProfile> participants3 = Arrays.asList(partyprof1,
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
 			partyprof2, partyprof3);
 
@@ -66,9 +67,7 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		SaopPartyWithProfile partywithprof1 = new SaopPartyWithProfile(party1,
-				profile1);
-		SaopPartyWithProfile partywithprof2 = new SaopPartyWithProfile(party2,
-				profile2);
-		List<SaopPartyWithProfile> participants = Arrays.asList(partywithprof1,
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		List<OnePartyTeam> participants = Arrays.asList(partywithprof1,
 				partywithprof2);
 
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 21)
@@ -176,5 +176,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -184,5 +184,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -192,5 +192,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -200,5 +200,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(bid1, state.getAgreement());
+		assertEquals(bid1, state.getAgreements().getMap().get(party1));
 		assertTrue(state.isFinal(NOW));
 	}
@@ -209,5 +209,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -217,5 +217,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -246,5 +246,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 		assertEquals(null, state.getError());
 		assertTrue(state.isFinal(NOW));
@@ -255,5 +255,6 @@
 	public void isProtocolErrorFinal() {
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
-				settings, null, new ProtocolException("test error", "test"));
+				settings, null,
+				new ProtocolException("test error", new PartyId("test")));
 		assertTrue(state.isFinal(NOW));
 		assertNotNull(state.getError());
@@ -309,5 +310,5 @@
 		assertTrue(state.isFinal(NOW));
 		assertNull(state.getError());
-		assertEquals(bid1, state.getAgreement());
+		assertEquals(bid1, state.getAgreements().getMap().get(party1));
 
 	}
@@ -325,5 +326,5 @@
 		assertTrue(state.isFinal(NOW));
 		assertNull(state.getError());
-		assertEquals(bid2, state.getAgreement());
+		assertEquals(bid2, state.getAgreements().getMap().get(party1));
 
 	}
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 21)
@@ -34,8 +34,8 @@
 import geniusweb.events.CurrentState;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.Progress;
 import geniusweb.protocol.ProtocolException;
@@ -43,4 +43,5 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.Parameters;
@@ -75,6 +76,6 @@
 	private final PartyRef party2ref = mock(PartyRef.class);
 	private final SAOPSettings settings = mock(SAOPSettings.class);
-	private final SaopPartyWithProfile team1 = mock(SaopPartyWithProfile.class);
-	private final SaopPartyWithProfile team2 = mock(SaopPartyWithProfile.class);
+	private final OnePartyTeam team1 = mock(OnePartyTeam.class);
+	private final OnePartyTeam team2 = mock(OnePartyTeam.class);
 	private SAOP saop;
 	private ProtocolToPartyConnFactory factory;
@@ -82,5 +83,5 @@
 	private ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
 			conn2 = mock(ProtocolToPartyConn.class);
-	private Map<PartyId, SaopPartyWithProfile> partyprofiles;
+	private Map<PartyId, OnePartyTeam> partyprofiles;
 	private Progress progress = mock(Progress.class);
 	private ProfileRef profile1;
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 21)
@@ -17,4 +17,5 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.Parameters;
@@ -24,11 +25,10 @@
 import tudelft.utilities.junit.GeneralTests;
 
-public class SaopPartyWithProfileTest
-		extends GeneralTests<SaopPartyWithProfile> {
+public class SaopPartyWithProfileTest extends GeneralTests<OnePartyTeam> {
 
 	private PartyRef party1, party1a, party2, party3;
 	private ProfileRef profile1, profile2;
-	private SaopPartyWithProfile partyprof1, partyprof1a, partyprof2,
-			partyprof3, partyprof4;
+	private OnePartyTeam partyprof1, partyprof1a, partyprof2, partyprof3,
+			partyprof4;
 	private final String serialized = "{\"party\":{\"partyref\":\"ws:party1\",\"parameters\":{}},\"profile\":\"ws:profile1\"}";
 
@@ -54,14 +54,14 @@
 				settings1);
 
-		partyprof1 = new SaopPartyWithProfile(party1withparams1, profile1);
-		partyprof1a = new SaopPartyWithProfile(party1withparams1, profile1);
-		partyprof2 = new SaopPartyWithProfile(party2withparams1, profile1);
-		partyprof3 = new SaopPartyWithProfile(party1withparams1, profile2);
-		partyprof4 = new SaopPartyWithProfile(party1withparams2, profile1);
+		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);
 
 	}
 
 	@Override
-	public List<List<SaopPartyWithProfile>> getGeneralTestData() {
+	public List<List<OnePartyTeam>> getGeneralTestData() {
 		return Arrays.asList(Arrays.asList(partyprof1, partyprof1a),
 				Arrays.asList(partyprof2), Arrays.asList(partyprof3),
@@ -84,5 +84,5 @@
 	@Test(expected = IllegalArgumentException.class)
 	public void nullTest() throws URISyntaxException {
-		new SaopPartyWithProfile((PartyWithParameters) null, (ProfileRef) null);
+		new OnePartyTeam((PartyWithParameters) null, (ProfileRef) null);
 	}
 
@@ -103,5 +103,5 @@
 		// and therfore can not serialize as TeamOfPartiesAndProfiles
 		TeamOfPartiesAndProfiles p = jackson.readValue(serialized,
-				SaopPartyWithProfile.class);
+				OnePartyTeam.class);
 		System.out.println(p);
 		assertEquals(partyprof1, p);
Index: /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 21)
@@ -4,5 +4,4 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -194,5 +193,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -201,6 +200,6 @@
 		List<Action> actions = Arrays.asList(offer1, accept2);
 		SHAOPState state = new SHAOPState(actions, connections4, progressrounds,
-				settings, null, 0, null, null);
-		assertNotNull(state.getAgreement());
+				settings, null, 0, partynrs, null);
+		assertFalse(state.getAgreements().getMap().isEmpty());
 		assertTrue(state.isFinal(NOW));
 	}
@@ -211,5 +210,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertNull(state.getAgreement());
+		assert (state.getAgreements().getMap().isEmpty());
 	}
 
@@ -219,5 +218,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -245,5 +244,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 		assertEquals(null, state.getError());
 		assertTrue(state.isFinal(NOW));
@@ -254,6 +253,7 @@
 	public void isProtocolErrorFinal() {
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
-				settings, new ProtocolException("test error", "test"), 0, null,
-				null);
+				settings,
+				new ProtocolException("test error", new PartyId("test")), 0,
+				null, null);
 		assertTrue(state.isFinal(NOW));
 		assertNotNull(state.getError());
Index: /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 21)
@@ -20,5 +20,5 @@
 
 import geniusweb.events.ProtocolEvent;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.protocol.CurrentNegoState;
 import geniusweb.protocol.NegoState;
@@ -44,5 +44,5 @@
 	private final SessionSettings settings = mock(SessionSettings.class);
 	private final SessionState finalsessionstate = mock(SessionState.class);
-	private Bid agreement = mock(Bid.class);
+	private Agreements agreement = mock(Agreements.class);
 	private final long NOW = 1000;
 	private SessionResult finalstate = mock(SessionResult.class);
@@ -53,7 +53,7 @@
 		when(finalsessionstate.isFinal(anyLong())).thenReturn(true);
 		when(finalsessionstate.getSettings()).thenReturn(settings);
-		when(finalsessionstate.getAgreement()).thenReturn(agreement);
+		when(finalsessionstate.getAgreements()).thenReturn(agreement);
 		when(finalsessionstate.getResult()).thenReturn(finalstate);
-		when(finalstate.getAgreement()).thenReturn(agreement);
+		when(finalstate.getAgreements()).thenReturn(agreement);
 		when(state.with(any())).thenReturn(newstate);
 	}
@@ -112,5 +112,5 @@
 				.forClass(SessionResult.class);
 		verify(state, times(1)).with(argument.capture());
-		assertEquals(agreement, argument.getValue().getAgreement());
+		assertEquals(agreement, argument.getValue().getAgreements());
 		assertNull(argument.getValue().getError());
 	}
Index: /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 20)
+++ /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 21)
@@ -88,5 +88,4 @@
 		AllPermutationsState state1 = state.with(result);
 
-		assertEquals(null, state1.getError());
 		assertEquals(8, state1.getResults().size());
 		assertEquals(toursettings, state1.getSettings());
Index: /pythonadapter/pom.xml
===================================================================
--- /pythonadapter/pom.xml	(revision 20)
+++ /pythonadapter/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>pythonadapter</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>
 
Index: /pythonadapter/src/main/java/geniusweb/pythonadapter/PythonPartyAdapter.java
===================================================================
--- /pythonadapter/src/main/java/geniusweb/pythonadapter/PythonPartyAdapter.java	(revision 20)
+++ /pythonadapter/src/main/java/geniusweb/pythonadapter/PythonPartyAdapter.java	(revision 21)
@@ -8,8 +8,8 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionEnd;
+import geniusweb.inform.Inform;
 import geniusweb.party.Capabilities;
 import geniusweb.party.DefaultParty;
 import geniusweb.party.Party;
-import geniusweb.party.inform.Inform;
 import tudelft.utilities.listener.Listener;
 
Index: /references/pom.xml
===================================================================
--- /references/pom.xml	(revision 20)
+++ /references/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>references</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>
 
@@ -38,5 +38,5 @@
 			<groupId>tudelft.utilities</groupId>
 			<artifactId>repository</artifactId>
-			<version>1.1.0</version>
+			<version>1.1.1</version>
 		</dependency>
 		<!-- the core, which includes Streaming API, shared low-level abstractions 
Index: /references/src/test/java/geniusweb/references/ProtocolRefTest.java
===================================================================
--- /references/src/test/java/geniusweb/references/ProtocolRefTest.java	(revision 20)
+++ /references/src/test/java/geniusweb/references/ProtocolRefTest.java	(revision 21)
@@ -17,5 +17,4 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.references.ProtocolRef;
 import tudelft.utilities.junit.GeneralTests;
 
@@ -58,4 +57,11 @@
 
 	@Test
+	public void testSaop() {
+		ProtocolRef saop = new ProtocolRef("SAOP");
+		System.out.println(saop.getURI());
+		assertEquals("SAOP", saop.getURI().getPath());
+	}
+
+	@Test
 	public void testSerialize() throws JsonProcessingException {
 		ObjectMapper jackson = new ObjectMapper();
Index: /simplerunner/pom.xml
===================================================================
--- /simplerunner/pom.xml	(revision 20)
+++ /simplerunner/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>simplerunner</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>
 
Index: /simplerunner/src/main/java/geniusweb/simplerunner/BasicConnection.java
===================================================================
--- /simplerunner/src/main/java/geniusweb/simplerunner/BasicConnection.java	(revision 20)
+++ /simplerunner/src/main/java/geniusweb/simplerunner/BasicConnection.java	(revision 21)
@@ -3,4 +3,5 @@
 import java.io.IOException;
 import java.net.URI;
+import java.util.concurrent.ArrayBlockingQueue;
 
 import geniusweb.connection.ConnectionEnd;
@@ -22,4 +23,6 @@
 	// to be initialized
 	private Listener<OUT> handler = null;
+	private Thread handlerThread;
+	private ArrayBlockingQueue<OUT> messages = new ArrayBlockingQueue<OUT>(4);
 
 	/**
@@ -33,4 +36,5 @@
 		this.reference = reference;
 		this.uri = uri;
+
 	}
 
@@ -47,4 +51,20 @@
 		}
 		this.handler = newhandler;
+
+		handlerThread = new Thread(new Runnable() {
+
+			@Override
+			public void run() {
+				try {
+					while (true) {
+						handler.notifyChange(messages.take());
+					}
+				} catch (InterruptedException e) {
+					System.out.println("connection handler interrupted");
+				}
+			}
+		});
+
+		handlerThread.start();
 	}
 
@@ -55,5 +75,9 @@
 					"BasicConnection has not been initialized");
 		}
-		handler.notifyChange(data);
+		try {
+			messages.put(data);
+		} catch (InterruptedException e) {
+			throw new IOException(e);
+		}
 	}
 
@@ -70,5 +94,7 @@
 	@Override
 	public void close() {
-
+		handlerThread.interrupt();
+		handlerThread = null;
+		handler = null;
 	}
 
Index: /simplerunner/src/main/java/geniusweb/simplerunner/ClassPathConnectionFactory.java
===================================================================
--- /simplerunner/src/main/java/geniusweb/simplerunner/ClassPathConnectionFactory.java	(revision 20)
+++ /simplerunner/src/main/java/geniusweb/simplerunner/ClassPathConnectionFactory.java	(revision 21)
@@ -11,6 +11,6 @@
 import geniusweb.actions.Action;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
 import geniusweb.party.Party;
-import geniusweb.party.inform.Inform;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
@@ -22,6 +22,4 @@
  * <code>classpath:org/my/package/class</code>
  * 
- * 
- *
  */
 public class ClassPathConnectionFactory implements ProtocolToPartyConnFactory {
Index: /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java
===================================================================
--- /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java	(revision 20)
+++ /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java	(revision 21)
@@ -29,8 +29,19 @@
 	private final ProtocolToPartyConnFactory connectionfactory;
 	protected final Reporter log;
+	private boolean properlyStopped = false;
 	private final static ObjectMapper jackson = new ObjectMapper();
+	private final int LOOPTIME = 200;// ms
+	private long maxruntime;
 
+	/**
+	 * 
+	 * @param settings
+	 * @param connectionfactory
+	 * @param logger
+	 * @param maxruntime        limit in millisecs. Ignored if 0
+	 */
 	public NegoRunner(NegoSettings settings,
-			ProtocolToPartyConnFactory connectionfactory, Reporter logger) {
+			ProtocolToPartyConnFactory connectionfactory, Reporter logger,
+			long maxruntime) {
 		if (settings == null || connectionfactory == null) {
 			throw new NullPointerException("Arguments must be not null");
@@ -40,4 +51,9 @@
 		this.protocol = settings.getProtocol(log);
 		this.connectionfactory = connectionfactory;
+		this.maxruntime = maxruntime;
+	}
+
+	public boolean isProperlyStopped() {
+		return properlyStopped;
 	}
 
@@ -46,4 +62,14 @@
 		protocol.addListener(evt -> handle(evt));
 		protocol.start(connectionfactory);
+		long remainingtime = maxruntime;
+		while (!properlyStopped && (maxruntime == 0 || remainingtime > 0)) {
+			try {
+				Thread.sleep(LOOPTIME);
+				remainingtime -= LOOPTIME;
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+		System.out.println("end run");
 	}
 
@@ -56,7 +82,6 @@
 
 	protected void stop() {
-		Level level = protocol.getState().getError() == null ? Level.INFO
-				: Level.WARNING;
-		logFinal(level, protocol.getState());
+		logFinal(Level.INFO, protocol.getState());
+		properlyStopped = true;
 	}
 
@@ -88,10 +113,10 @@
 
 		NegoRunner runner = new NegoRunner(settings,
-				new ClassPathConnectionFactory(), new StdOutReporter());
+				new ClassPathConnectionFactory(), new StdOutReporter(), 0);
 		runner.run();
 	}
 
 	private static void showusage() {
-		System.err.println("GeniusWeb stand-alone runner. ");
+		System.err.println("GeniusWeb stand-alone runner.");
 		System.err.println("first argument should be <settings.json>.");
 		System.err.println(
@@ -100,4 +125,11 @@
 				"See the settings.json example file and the GeniusWeb wiki pages. ");
 
+	}
+
+	/**
+	 * @return protocol that runs/ran the session.
+	 */
+	public NegoProtocol getProtocol() {
+		return protocol;
 	}
 }
Index: /simplerunner/src/test/java/geniusweb/simplerunner/ClassPathConnectionFactoryTest.java
===================================================================
--- /simplerunner/src/test/java/geniusweb/simplerunner/ClassPathConnectionFactoryTest.java	(revision 20)
+++ /simplerunner/src/test/java/geniusweb/simplerunner/ClassPathConnectionFactoryTest.java	(revision 21)
@@ -11,7 +11,6 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 import geniusweb.references.PartyRef;
-import geniusweb.simplerunner.ClassPathConnectionFactory;
 
 public class ClassPathConnectionFactoryTest {
Index: /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java
===================================================================
--- /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java	(revision 20)
+++ /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java	(revision 21)
@@ -1,3 +1,5 @@
 package geniusweb.simplerunner;
+
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
@@ -27,4 +29,5 @@
 @RunWith(Parameterized.class)
 public class SessionRunnerE2ETest {
+	private static final int TEST_RUNTIME = 5000;
 	private final ObjectMapper jackson = new ObjectMapper();
 	private NegoRunner runner;
@@ -37,5 +40,6 @@
 				.asList(new Object[][] { { "src/test/resources/settings.json" },
 						{ "src/test/resources/settings2.json" },
-						{ "src/test/resources/shaoptoursettings.json" } });
+						{ "src/test/resources/shaoptoursettings.json" },
+						{ "src/test/resources/mopac.json" } });
 	}
 
@@ -53,5 +57,5 @@
 
 		runner = new NegoRunner(settings, new ClassPathConnectionFactory(),
-				logger);
+				logger, TEST_RUNTIME);
 
 	}
@@ -64,4 +68,6 @@
 	public void runTest() throws IOException {
 		runner.run();
+		assertTrue(runner.isProperlyStopped());
+		System.out.println("Final state:\n" + runner.getProtocol().getState());
 	}
 
Index: /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerTest.java
===================================================================
--- /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerTest.java	(revision 20)
+++ /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerTest.java	(revision 21)
@@ -16,4 +16,5 @@
 import org.mockito.ArgumentCaptor;
 
+import geniusweb.actions.PartyId;
 import geniusweb.protocol.CurrentNegoState;
 import geniusweb.protocol.NegoState;
@@ -29,5 +30,5 @@
 public class SessionRunnerTest {
 	private static final ProtocolException PROTOCOL_EXC = new ProtocolException(
-			"fake protocol exception", "test");
+			"fake protocol exception", new PartyId("test"));
 	private CurrentNegoState finishedEvent;
 	private final long NOW = 1000;
@@ -47,5 +48,5 @@
 	public void smokeTest() {
 		new NegoRunner(mock(SessionSettings.class),
-				mock(ProtocolToPartyConnFactory.class), logger);
+				mock(ProtocolToPartyConnFactory.class), logger, 5000);
 	}
 
@@ -59,5 +60,6 @@
 				ProtocolToPartyConnFactory.class);
 
-		NegoRunner runner = spy(new NegoRunner(settings, factory, logger));
+		NegoRunner runner = spy(
+				new NegoRunner(settings, factory, logger, 5000));
 		runner.run();
 
@@ -75,5 +77,4 @@
 		when(settings.getProtocol(any())).thenReturn(protocol);
 		SessionState state = mock(SessionState.class);
-		when(state.getError()).thenReturn(null);
 		when(protocol.getState()).thenReturn(state);
 		Reporter logger = mock(Reporter.class);
@@ -81,10 +82,11 @@
 		ProtocolToPartyConnFactory factory = mock(
 				ProtocolToPartyConnFactory.class);
-		NegoRunner runner = spy(new NegoRunner(settings, factory, logger) {
-			@Override
-			protected void logFinal(Level level, NegoState state) {
-				log.log(level, state.toString());
-			}
-		});
+		NegoRunner runner = spy(
+				new NegoRunner(settings, factory, logger, 5000) {
+					@Override
+					protected void logFinal(Level level, NegoState state) {
+						log.log(level, state.toString());
+					}
+				});
 		ArgumentCaptor<Listener> listener = ArgumentCaptor
 				.forClass(Listener.class);
@@ -107,15 +109,15 @@
 		when(settings.getProtocol(any())).thenReturn(protocol);
 		SessionState state = mock(SessionState.class);
-		when(state.getError()).thenReturn(PROTOCOL_EXC);
 		when(protocol.getState()).thenReturn(state);
 		@SuppressWarnings("unchecked")
 		ProtocolToPartyConnFactory factory = mock(
 				ProtocolToPartyConnFactory.class);
-		NegoRunner runner = spy(new NegoRunner(settings, factory, logger) {
-			@Override
-			protected void logFinal(Level level, NegoState state) {
-				log.log(level, state.toString());
-			}
-		});
+		NegoRunner runner = spy(
+				new NegoRunner(settings, factory, logger, 5000) {
+					@Override
+					protected void logFinal(Level level, NegoState state) {
+						log.log(level, state.toString());
+					}
+				});
 		ArgumentCaptor<Listener> listener = ArgumentCaptor
 				.forClass(Listener.class);
@@ -126,6 +128,8 @@
 		listener.getValue().notifyChange(finishedEvent);
 		verify(runner, times(1)).stop();
-		// check that the protocol error is logged properly
-		verify(logger, times(1)).log(eq(Level.WARNING), any(String.class));
+		// check final state is logged
+		String statestr = state.toString();
+		verify(logger, times(1)).log(any(), eq(statestr));
+
 	}
 
Index: /simplerunner/src/test/resources/jobs/jobs1.json
===================================================================
--- /simplerunner/src/test/resources/jobs/jobs1.json	(revision 20)
+++ /simplerunner/src/test/resources/jobs/jobs1.json	(revision 21)
@@ -34,6 +34,6 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"yes":1,
-						"no":0
+						"yes": 1,
+						"no": 0
 					}
 				}
@@ -42,6 +42,6 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"yes":1,
-						"no":0
+						"yes": 1,
+						"no": 0
 					}
 				}
@@ -50,7 +50,7 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"low":0,
-						"medium":0.5,
-						"high":1
+						"low": 0,
+						"medium": 0.5,
+						"high": 1
 					}
 				}
@@ -112,5 +112,15 @@
 			}
 		},
-		"name": "jobs1"
+		"name": "jobs1",
+		"reservationBid": {
+			"issuevalues": {
+				"salary": "3500",
+				"fte": "0.8",
+				"work from home": "1",
+				"lease car": "no",
+				"permanent contract": "yes",
+				"career development opportunities": "medium"
+			}
+		}
 	}
 }
Index: /simplerunner/src/test/resources/jobs/jobs2.json
===================================================================
--- /simplerunner/src/test/resources/jobs/jobs2.json	(revision 20)
+++ /simplerunner/src/test/resources/jobs/jobs2.json	(revision 21)
@@ -34,6 +34,6 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"yes":0,
-						"no":1
+						"yes": 0,
+						"no": 1
 					}
 				}
@@ -42,6 +42,6 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"yes":0.23809523809523808,
-						"no":0.761904761904762
+						"yes": 0.23809523809523808,
+						"no": 0.761904761904762
 					}
 				}
@@ -50,7 +50,7 @@
 				"discreteutils": {
 					"valueUtilities": {
-						"low":1.0,
-						"medium":0.5,
-						"high":0
+						"low": 1.0,
+						"medium": 0.5,
+						"high": 0
 					}
 				}
@@ -112,5 +112,15 @@
 			}
 		},
-		"name": "jobs2"
+		"name": "jobs2",
+		"reservationBid": {
+			"issuevalues": {
+				"salary": "3000",
+				"fte": "0.8",
+				"work from home": "0",
+				"lease car": "no",
+				"permanent contract": "no",
+				"career development opportunities": "medium"
+			}
+		}
 	}
 }
Index: /simplerunner/src/test/resources/mopac.json
===================================================================
--- /simplerunner/src/test/resources/mopac.json	(revision 21)
+++ /simplerunner/src/test/resources/mopac.json	(revision 21)
@@ -0,0 +1,17 @@
+{
+	"MOPACSettings": {
+		"participants": [{
+			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{"power":1}},
+			"profile": "file:src/test/resources/jobs/jobs1.json"
+		}, {
+			"party": {"partyref":"classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters":{"power":1}},
+			"profile": "file:src/test/resources/jobs/jobs2.json"
+		}],
+		"deadline": {
+			"deadlinetime": {
+				"durationms": 2000000
+			}
+		},
+		"votingevaluator":{"LargestAgreementsLoop":{}}
+	}
+}
Index: /simplerunner/src/test/resources/shaoptoursettings.json
===================================================================
--- /simplerunner/src/test/resources/shaoptoursettings.json	(revision 20)
+++ /simplerunner/src/test/resources/shaoptoursettings.json	(revision 21)
@@ -34,5 +34,5 @@
 			{
 				"ProfileList": [
-					"file:src/test/resources/jobs/jobs1partial.json?partial=10",
+					"file:src/test/resources/jobs/jobs1partial.json",
 					"file:src/test/resources/jobs/jobs1partial.json"
 					
@@ -41,5 +41,5 @@
 			{
 				"ProfileList": [
-					"file:src/test/resources/jobs/jobs2.json?partial=15",
+					"file:src/test/resources/jobs/jobs2.json",
 					"file:src/test/resources/jobs/jobs2.json"
 					
@@ -55,5 +55,5 @@
 					"deadlinerounds": {
 						"rounds": 10,
-						"durationms": 10000
+						"durationms": 1000
 					}
 				}
Index: /timeline/pom.xml
===================================================================
--- /timeline/pom.xml	(revision 20)
+++ /timeline/pom.xml	(revision 21)
@@ -6,5 +6,5 @@
 	<groupId>geniusweb</groupId>
 	<artifactId>timeline</artifactId>
-	<version>1.4.4</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.5.0</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.4.4</geniusweb.version>
+		<geniusweb.version>1.5.0</geniusweb.version>
 	</properties>
 
Index: /voting/pom.xml
===================================================================
--- /voting/pom.xml	(revision 21)
+++ /voting/pom.xml	(revision 21)
@@ -0,0 +1,271 @@
+<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>voting</artifactId>
+	<version>1.5.0</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.0</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>geniusweb</groupId>
+			<artifactId>events</artifactId>
+			<version>${geniusweb.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>immutablelist</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+		<!-- the core, which includes Streaming API, shared low-level abstractions 
+			(but NOT data-binding) -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+
+		<!-- Just the annotations; use this dependency if you want to attach annotations 
+			to classes without connecting them to the code. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+
+		<!-- databinding; ObjectMapper, JsonNode and related classes are here -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+
+		<!-- smile (binary JSON). Other artifacts in this group do other formats. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-smile</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+		<!-- JAX-RS provider -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.jaxrs</groupId>
+			<artifactId>jackson-jaxrs-json-provider</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+		<!-- Support for JAX-B annotations as additional configuration -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-jaxb-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>junit</artifactId>
+			<version>1.0.2</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-all</artifactId>
+			<version>1.9.5</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>jcenter</id>
+			<url>https://jcenter.bintray.com</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-enforcer-plugin</artifactId>
+				<version>3.0.0-M2</version>
+				<executions>
+					<execution>
+						<id>enforce</id>
+						<configuration>
+							<rules>
+								<dependencyConvergence />
+							</rules>
+						</configuration>
+						<goals>
+							<goal>enforce</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-javadoc-plugin</artifactId>
+				<version>2.10.1</version>
+				<executions>
+					<execution>
+						<id>attach-javadocs</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+						<configuration>
+							<additionalparam>${javadoc.opts}</additionalparam>
+							<additionalparam>-Xdoclint:none</additionalparam>
+						</configuration>
+					</execution>
+				</executions>
+			</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>
+
+			<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: /voting/src/main/java/geniusweb/voting/CollectedVotes.java
===================================================================
--- /voting/src/main/java/geniusweb/voting/CollectedVotes.java	(revision 21)
+++ /voting/src/main/java/geniusweb/voting/CollectedVotes.java	(revision 21)
@@ -0,0 +1,275 @@
+package geniusweb.voting;
+
+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 java.util.stream.Collectors;
+
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.issuevalue.Bid;
+import tudelft.utilities.immutablelist.FixedList;
+import tudelft.utilities.immutablelist.ImmutableList;
+import tudelft.utilities.immutablelist.PowerSet;
+
+/**
+ * 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
+ */
+public class CollectedVotes {
+	private final Map<PartyId, Votes> allvotes;
+	private final Map<PartyId, Integer> powers;
+
+	// caching
+	private transient Map<Bid, Set<Vote>> allBids = null;
+
+	/**
+	 * 
+	 * @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.
+	 */
+	public CollectedVotes(Map<PartyId, Votes> votesMap,
+			Map<PartyId, Integer> power) {
+		this.allvotes = new HashMap<>(votesMap);
+		this.powers = new HashMap<>(power);
+		if (!powers.keySet().containsAll(allvotes.keySet())) {
+			throw new IllegalArgumentException(
+					"All voting parties must have a power");
+		}
+	}
+
+	/**
+	 * @param votes additional set of votes for a new party that did not yet
+	 *              vote.
+	 * @param power powers for the new party, can be null if power already set
+	 *              previously.
+	 * @return new CollectedVotes with the new votes added.
+	 */
+	public CollectedVotes with(Votes votes, Integer power) {
+		PartyId actor = votes.getActor();
+		if (allvotes.containsKey(actor))
+			throw new IllegalArgumentException(
+					"Party " + actor + " already voted");
+
+		Map<PartyId, Integer> newpowers = new HashMap<>(powers);
+		if (!powers.containsKey(actor)) {
+			if (power == null)
+				throw new IllegalArgumentException(
+						"Power must be set for new actor " + actor);
+			newpowers.put(actor, power);
+		}
+
+		Map<PartyId, Votes> newmap = new HashMap<>(allvotes);
+		newmap.put(actor, votes);
+		return new CollectedVotes(newmap, newpowers);
+	}
+
+	/**
+	 * @return all the votes placed so far
+	 */
+	public Map<PartyId, Votes> getVotes() {
+		return Collections.unmodifiableMap(allvotes);
+	}
+
+	/**
+	 * @return the powers of all parties
+	 */
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	/**
+	 * @return a hashmap containing the {@link Bid}s and a maximum-sized list of
+	 *         {@link PartyId}s that placed a satisfied vote for that bid.
+	 *         maximum-sized means that there is no larger subset of satisfied
+	 *         votes for that bid. There may be other subsets of equal size for
+	 *         the bid. If there are no agreements at all for a bid, the bid is
+	 *         not placed in the map.
+	 */
+	public Map<Bid, Set<PartyId>> getMaxAgreements() {
+		Map<Bid, Set<PartyId>> agreements = new HashMap<>();
+		Map<Bid, Set<Vote>> allbidsmap = getAllBids();
+		for (Bid bid : allbidsmap.keySet()) {
+			Set<PartyId> max = getMaxPowerGroup(allbidsmap.get(bid));
+			if (!max.isEmpty())
+				agreements.put(bid, max);
+		}
+		return agreements;
+	}
+
+	/**
+	 * @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<Vote>> getAllBids() {
+		if (allBids == null)
+			allBids = getAllBids1();
+		return allBids;
+	}
+
+	/**
+	 * 
+	 * @param parties the parties to be removed
+	 * @return new CollectedVotes without given parties
+	 */
+	public CollectedVotes without(Set<PartyId> parties) {
+		Map<PartyId, Votes> newvotes = new HashMap<>(allvotes);
+		newvotes.keySet().removeAll(parties);
+		Map<PartyId, Integer> newpower = new HashMap<>(powers);
+		newpower.keySet().removeAll(parties);
+		return new CollectedVotes(newvotes, newpower);
+	}
+
+	/**
+	 * 
+	 * @param votes a list of all votes for a particular bid. The parties that
+	 *              placed the votes are called 'voters'
+	 * @return a subset of the voters for which holds (1) the min-power
+	 *         condition of all these voters is satisfied (2) the power of this
+	 *         subset of voters is maximal (no other satisfied set of voters
+	 *         with bigger power exists) maximum-power that placed votes with
+	 *         holding conditions.This algorithm works in quadratic time.
+	 */
+	protected Set<PartyId> getMaxPowerGroup(Set<Vote> votes) {
+		Set<PartyId> maxgroup = Collections.emptySet();
+		Integer maxpower = 0;
+
+		for (Vote vote : votes) {
+			int target = vote.getMinPower();
+
+			// find parties that would join if we reach that power
+			Set<PartyId> group = votes.stream()
+					.filter(v -> target >= v.getMinPower())
+					.map(v -> v.getActor()).collect(Collectors.toSet());
+			// check what the group's power is
+			Integer groupPower = getTotalPower(group);
+			if (groupPower >= target && groupPower > maxpower) {
+				maxgroup = group;
+				maxpower = groupPower;
+			}
+		}
+		return maxgroup;
+	}
+
+	/**
+	 * 
+	 * @param votes a set of {@link Votes} for one single {@link Bid}
+	 * @return all the consensus groups. A consensus group is a subset of the
+	 *         votes, for which the conditions holds (the combined power of
+	 *         these parties exceeds all voting conditions) if all these parties
+	 *         as a group are accepting that bid. This algorithm works in
+	 *         exponential space and the outcome may also be exponential in the
+	 *         input size.
+	 */
+	protected Set<Set<PartyId>> getConsensusGroups(List<Vote> votes) {
+		Set<Set<PartyId>> groups = new HashSet<>();
+
+		PowerSet<Vote> allVotePermutations = new PowerSet<Vote>(
+				new FixedList<Vote>(votes));
+		// check each possible subset.
+		for (ImmutableList<Vote> voteList : allVotePermutations) {
+			Set<PartyId> parties = getParties(voteList);
+			if (parties.size() >= 2
+					&& getTotalPower(parties) >= getMinPower(voteList)) {
+				groups.add(parties);
+			}
+		}
+		return groups;
+	}
+
+	/**
+	 * @param voteList
+	 * @return all parties that did a vote
+	 */
+	public static Set<PartyId> getParties(ImmutableList<Vote> voteList) {
+		Set<PartyId> parties = new HashSet<>();
+		for (Vote vote : voteList) {
+			parties.add(vote.getActor());
+		}
+		return parties;
+	}
+
+	/**
+	 * 
+	 * @param votes a list of {@link Vote}s on one Bid. All parties must be
+	 *              known
+	 * @return the minimum voting power needed for this group, so that all can
+	 *         accept. This is equal to the max of the vote.getMinPower
+	 */
+	private Integer getMinPower(ImmutableList<Vote> votes) {
+		int max = 0;
+		for (Vote vote : votes) {
+			max = Math.max(max, vote.getMinPower());
+		}
+		return max;
+	}
+
+	/**
+	 * 
+	 * @param parties a list of parties
+	 * @return total voting power of the parties
+	 */
+	private Integer getTotalPower(Set<PartyId> parties) {
+		return parties.stream().map(p -> powers.get(p)).reduce(0, Integer::sum);
+	}
+
+	private Map<Bid, Set<Vote>> getAllBids1() {
+		Map<Bid, Set<Vote>> bids = new HashMap<>();
+		for (Votes votes : allvotes.values()) {
+			for (Vote vote : votes.getVotes()) {
+				Bid bid = vote.getBid();
+				Set<Vote> list = bids.get(bid);
+				if (list == null)
+					list = new HashSet<Vote>();
+				list.add(vote);
+				bids.put(bid, list);
+			}
+		}
+		return bids;
+	}
+
+	@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;
+		CollectedVotes other = (CollectedVotes) 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 "CollectedVotes[" + allvotes + "," + powers + "]";
+	}
+
+}
Index: /voting/src/main/java/geniusweb/voting/VotingEvaluator.java
===================================================================
--- /voting/src/main/java/geniusweb/voting/VotingEvaluator.java	(revision 21)
+++ /voting/src/main/java/geniusweb/voting/VotingEvaluator.java	(revision 21)
@@ -0,0 +1,58 @@
+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.votingevaluators.LargestAgreement;
+import geniusweb.voting.votingevaluators.LargestAgreementsLoop;
+
+/**
+ * Evaluates the {@link CollectedVotes}, determining the agreements and if the
+ * negotiation should continue.
+ * 
+ * Implementations should be immutable and not serialize internal variables (eg
+ * 
+ * @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 = LargestAgreementsLoop.class),
+		@JsonSubTypes.Type(value = LargestAgreement.class) })
+public interface VotingEvaluator {
+	/**
+	 * 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. 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.
+	 */
+	VotingEvaluator create(CollectedVotes 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/votingevaluators/LargestAgreement.java
===================================================================
--- /voting/src/main/java/geniusweb/voting/votingevaluators/LargestAgreement.java	(revision 21)
+++ /voting/src/main/java/geniusweb/voting/votingevaluators/LargestAgreement.java	(revision 21)
@@ -0,0 +1,109 @@
+package geniusweb.voting.votingevaluators;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * Take the first possible agreement, and take the largest possible agreement.
+ * Stop after first agreement.
+ */
+public class LargestAgreement implements VotingEvaluator {
+	@JsonIgnore
+	private final CollectedVotes allVotes;
+
+	// caching
+	@JsonIgnore
+	private Agreements maxAgreements = null;
+
+	@JsonCreator
+	public LargestAgreement() {
+		this(new CollectedVotes(Collections.emptyMap(),
+				Collections.emptyMap()));
+	}
+
+	protected LargestAgreement(CollectedVotes votes) {
+		this.allVotes = votes;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		if (maxAgreements == null)
+			maxAgreements = collectVotes();
+		return maxAgreements;
+	}
+
+	@Override
+	public boolean isFinished() {
+		HashSet<PartyId> parties = new HashSet<PartyId>(
+				allVotes.getVotes().keySet());
+		parties.removeAll(getAgreements().getMap().keySet());
+		return parties.size() < 2;
+	}
+
+	@Override
+	public VotingEvaluator create(CollectedVotes votes) {
+		return new LargestAgreementsLoop(votes);
+	}
+
+	/**
+	 * @return the agreements that were reached from the given votes, using the
+	 *         greedy algorithm picking the largets votesets.
+	 */
+	protected Agreements collectVotes() {
+		Agreements agreement = new Agreements();
+
+		Map<Bid, Set<PartyId>> agrees = allVotes.getMaxAgreements();
+		if (agrees.isEmpty())
+			return agreement;// none
+
+		// find the best one
+		Bid maxbid = agrees.keySet().stream()
+				.max(Comparator.comparingInt(bid -> agrees.get(bid).size()))
+				.get();
+		agreement = agreement.with(maxbid, agrees.get(maxbid));
+		return agreement;
+	}
+
+	@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;
+		LargestAgreement other = (LargestAgreement) 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/main/java/geniusweb/voting/votingevaluators/LargestAgreementsLoop.java
===================================================================
--- /voting/src/main/java/geniusweb/voting/votingevaluators/LargestAgreementsLoop.java	(revision 21)
+++ /voting/src/main/java/geniusweb/voting/votingevaluators/LargestAgreementsLoop.java	(revision 21)
@@ -0,0 +1,120 @@
+package geniusweb.voting.votingevaluators;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * {@link #getAgreements()} collects all possible {@link Agreements}. We're
+ * finished only when {@link #getAgreements()} covers all except possibly 1
+ * party.
+ * 
+ * The agreements is a list of {@link Bid}s and a maximum-sized list of
+ * {@link PartyId}s that placed a satisfied vote for that bid. maximum-sized
+ * means that there is no larger subset of votes for that bid. There may be
+ * other subsets of equal size for the bid. If there are no agreements at all
+ * for a bid, the bid is not placed in the map.
+ * 
+ */
+public class LargestAgreementsLoop implements VotingEvaluator {
+	@JsonIgnore
+	private final CollectedVotes allVotes;
+
+	// caching
+	@JsonIgnore
+	private Agreements maxAgreements = null;
+
+	@JsonCreator
+	public LargestAgreementsLoop() {
+		this(new CollectedVotes(Collections.emptyMap(),
+				Collections.emptyMap()));
+	}
+
+	protected LargestAgreementsLoop(CollectedVotes votes) {
+		this.allVotes = votes;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		if (maxAgreements == null)
+			maxAgreements = collectAgreements();
+		return maxAgreements;
+	}
+
+	@Override
+	public boolean isFinished() {
+		HashSet<PartyId> parties = new HashSet<PartyId>(
+				allVotes.getVotes().keySet());
+		parties.removeAll(getAgreements().getMap().keySet());
+		return parties.size() < 2;
+	}
+
+	@Override
+	public VotingEvaluator create(CollectedVotes votes) {
+		return new LargestAgreementsLoop(votes);
+	}
+
+	/**
+	 * @return the agreements that were reached from the given votes, using the
+	 *         greedy algorithm picking the largets votesets.
+	 */
+	protected Agreements collectAgreements() {
+		CollectedVotes remainingvotes = allVotes;
+		Agreements newagreements = new Agreements();
+		while (true) {
+			Map<Bid, Set<PartyId>> agrees = remainingvotes.getMaxAgreements();
+			if (agrees.isEmpty())
+				break;
+			// find the best one
+			Bid maxbid = agrees.keySet().stream()
+					.max(Comparator.comparingInt(bid -> agrees.get(bid).size()))
+					.get();
+			Set<PartyId> maxparties = agrees.get(maxbid);
+			newagreements = newagreements.with(maxbid, maxparties);
+			remainingvotes = remainingvotes.without(maxparties);
+		}
+		return newagreements;
+	}
+
+	@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;
+		LargestAgreementsLoop other = (LargestAgreementsLoop) 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 "LargestAgreementsLoop";
+	}
+}
Index: /voting/src/test/java/geniusweb/voting/CollectedVotesTest.java
===================================================================
--- /voting/src/test/java/geniusweb/voting/CollectedVotesTest.java	(revision 21)
+++ /voting/src/test/java/geniusweb/voting/CollectedVotesTest.java	(revision 21)
@@ -0,0 +1,246 @@
+package geniusweb.voting;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+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.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.issuevalue.Bid;
+import tudelft.utilities.junit.GeneralTests;
+
+public class CollectedVotesTest extends GeneralTests<CollectedVotes> {
+
+	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 Vote votePA2 = new Vote(partyP, bidA, 2);
+	private final Vote voteQA2 = new Vote(partyQ, bidA, 2);
+	private final Vote voteQB3 = new Vote(partyQ, bidB, 3);
+	private final Vote voteQB2 = new Vote(partyQ, bidB, 2);
+	private final Vote voteRA2 = new Vote(partyR, bidA, 2);
+	private final Vote voteRA3 = new Vote(partyR, bidA, 3);
+	private final Vote voteRA4 = new Vote(partyR, bidA, 4);
+	private final Vote voteRB2 = new Vote(partyR, bidB, 2);
+
+	private Map<PartyId, Integer> allpowers = new HashMap<>();
+
+	private CollectedVotes cv1 = new CollectedVotes(Collections.emptyMap(),
+			Collections.emptyMap());
+	private CollectedVotes cv1a = new CollectedVotes(Collections.emptyMap(),
+			Collections.emptyMap());
+
+	private Votes votes = new Votes(partyP, Arrays.asList(votePA2));
+	private CollectedVotes cv2 = new CollectedVotes(
+			Collections.singletonMap(partyP, votes),
+			Collections.singletonMap(partyP, 1));
+
+	@Override
+	public List<List<CollectedVotes>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(cv1, cv1a), Arrays.asList(cv2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("CollectedVotes.\\{\\}.*\\{\\}.*",
+				"CollectedVotes.\\{partyP=Votes.*Vote.partyP,Mock.*,2.*\\}.*\\{partyP=1\\}.*");
+	}
+
+	@Before
+	public void before() {
+		allpowers.put(partyP, 1);
+		allpowers.put(partyQ, 1);
+		allpowers.put(partyR, 1);
+	}
+
+	@Test
+	public void testGetAllBids() {
+		Map<PartyId, Votes> allvotes = new HashMap<>();
+		allvotes.put(partyP, new Votes(partyP, Arrays.asList(votePA2)));
+		allvotes.put(partyQ,
+				new Votes(partyQ, Arrays.asList(voteQA2, voteQB3)));
+
+		Map<Bid, Set<Vote>> allbids = new CollectedVotes(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 testGetMaxSubset2() {
+		Set<Vote> votes = new HashSet<>(Arrays.asList(votePA2, voteQA2));
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+		Set<PartyId> parties = cv.getMaxPowerGroup(votes);
+		assertEquals(new HashSet<>(Arrays.asList(partyP, partyQ)), parties);
+	}
+
+	@Test
+	public void testGetMaxSubset3() {
+		Set<Vote> votes = new HashSet<>(
+				Arrays.asList(votePA2, voteQA2, voteRA4));
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+
+		Set<PartyId> parties = cv.getMaxPowerGroup(votes);
+		// party R wants 4 votes so we can't satisfy
+		assertEquals(new HashSet<>(Arrays.asList(partyP, partyQ)), parties);
+	}
+
+	@Test
+	public void testGetMaxSubset3b() {
+		Set<Vote> votes = new HashSet<>(
+				Arrays.asList(votePA2, voteQA2, voteRA3));
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+		Set<PartyId> parties = cv.getMaxPowerGroup(votes);
+		// party R wants 4 votes so we can't satisfy
+		assertEquals(new HashSet<>(Arrays.asList(partyP, partyQ, partyR)),
+				parties);
+	}
+
+	@Test
+	public void testGetMaxAgreements() {
+		Map<PartyId, Votes> allvotes = new HashMap<>();
+		allvotes.put(partyP, new Votes(partyP, Arrays.asList(votePA2)));
+		allvotes.put(partyQ,
+				new Votes(partyQ, Arrays.asList(voteQA2, voteQB2)));
+		allvotes.put(partyR,
+				new Votes(partyR, Arrays.asList(voteRA4, voteRB2)));
+
+		Map<Bid, Set<PartyId>> allagrees = new CollectedVotes(allvotes,
+				allpowers).getMaxAgreements();
+		assertEquals(new HashSet<>(Arrays.asList(partyP, partyQ)),
+				allagrees.get(bidA));
+		assertEquals(new HashSet<>(Arrays.asList(partyQ, partyR)),
+				allagrees.get(bidB));
+
+	}
+
+	@Test
+	public void testGetNoAgreements() {
+		Map<PartyId, Votes> allvotes = new HashMap<>();
+		allvotes.put(partyP, new Votes(partyP, Arrays.asList(votePA2)));
+		Map<Bid, Set<PartyId>> allagrees = new CollectedVotes(allvotes,
+				allpowers).getMaxAgreements();
+		assertEquals(Collections.emptyMap(), allagrees);
+
+	}
+
+	@Test
+	public void getConsensusGroupsTest() {
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+
+		Set<Set<PartyId>> consensus = cv
+				.getConsensusGroups(Arrays.asList(votePA2, voteQA2));
+		// should give 1 solution: both parties join in.
+		System.out.println(consensus);
+		assertEquals(1, consensus.size());
+		assertEquals(2, consensus.iterator().next().size());
+	}
+
+	@Test
+	public void getConsensusGroupsTestPQR() {
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+
+		Set<Set<PartyId>> consensus = cv
+				.getConsensusGroups(Arrays.asList(votePA2, voteQA2, voteRA4));
+		// should give 1 solution: P and Q join in. 4 is unreachable.
+		System.out.println(consensus);
+		assertEquals(1, consensus.size());
+		assertEquals(2, consensus.iterator().next().size());
+	}
+
+	@Test
+	public void getConsensusGroupsTestPQR2() {
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+
+		Set<Set<PartyId>> consensus = cv
+				.getConsensusGroups(Arrays.asList(votePA2, voteQA2, voteRA2));
+		// should give 4 solutions: P+Q, P+R, Q+R and P+Q+R
+		System.out.println(consensus);
+		assertEquals(4, consensus.size());
+	}
+
+	@Test
+	public void getConsensusGroupsTestRExtraPower() {
+		// give R extra power to reach the 4 if other 2 join
+		allpowers = new HashMap<>();
+		allpowers.put(partyP, 1);
+		allpowers.put(partyQ, 1);
+		allpowers.put(partyR, 2);
+
+		CollectedVotes cv = new CollectedVotes(Collections.emptyMap(),
+				allpowers);
+
+		Set<Set<PartyId>> consensus = cv
+				.getConsensusGroups(Arrays.asList(votePA2, voteQA2, voteRA4));
+		// should give solutions P+Q and P+Q+R
+		System.out.println(consensus);
+		assertEquals(2, consensus.size());
+		Iterator<Set<PartyId>> it = consensus.iterator();
+		assertEquals(2, it.next().size());
+		assertEquals(3, it.next().size());
+	}
+
+	@Test
+	public void testWith() {
+		Map<PartyId, Votes> allvotes = new HashMap<>();
+		allvotes.put(partyP, new Votes(partyP, Arrays.asList(votePA2)));
+		CollectedVotes cv = new CollectedVotes(allvotes, allpowers);
+		assertEquals(1, cv.getVotes().keySet().size());
+		cv = cv.with(new Votes(partyQ, Arrays.asList(voteQA2)), 2);
+		assertEquals(2, cv.getVotes().keySet().size());
+	}
+
+	@Test
+	public void testWithout() {
+		Map<PartyId, Votes> allvotes = new HashMap<>();
+		allvotes.put(partyP, new Votes(partyP, Arrays.asList(votePA2)));
+		allvotes.put(partyQ,
+				new Votes(partyQ, Arrays.asList(voteQA2, voteQB2)));
+		allvotes.put(partyR,
+				new Votes(partyR, Arrays.asList(voteRA4, voteRB2)));
+
+		// give R extra power to reach the 4 if other 2 join
+		allpowers = new HashMap<>();
+		allpowers.put(partyP, 1);
+		allpowers.put(partyQ, 1);
+		allpowers.put(partyR, 2);
+
+		CollectedVotes cv = new CollectedVotes(allvotes, allpowers);
+
+		assertEquals(3, cv.getVotes().size());
+		cv = cv.without(new HashSet<>(Arrays.asList(partyP, partyQ)));
+		assertEquals(1, cv.getVotes().size());
+		assertEquals(1, cv.getPowers().size());
+
+	}
+
+}
Index: /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementTest.java
===================================================================
--- /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementTest.java	(revision 21)
+++ /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementTest.java	(revision 21)
@@ -0,0 +1,60 @@
+package geniusweb.voting.votingevaluators;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+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.Votes;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+import tudelft.utilities.junit.GeneralTests;
+
+public class LargestAgreementTest extends GeneralTests<LargestAgreement> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private LargestAgreement lal1 = new LargestAgreement();
+	private LargestAgreement lal1a = new LargestAgreement();
+	private PartyId party1 = new PartyId("party1");
+	private Votes votes1 = mock(Votes.class);
+	private LargestAgreement lal2 = new LargestAgreement(
+			new CollectedVotes(Collections.singletonMap(party1, votes1),
+					Collections.singletonMap(party1, 1)));
+
+	private String lalstring = "{\"LargestAgreement\":{}}";
+
+	@Override
+	public List<List<LargestAgreement>> 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 {
+		VotingEvaluator eval = jackson.readValue(lalstring,
+				VotingEvaluator.class);
+		// it should deserialize as the empty lal1 because we ignore the fields.
+		assertEquals(lal1, eval);
+	}
+
+}
Index: /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementsLoopTest.java
===================================================================
--- /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementsLoopTest.java	(revision 21)
+++ /voting/src/test/java/geniusweb/voting/votingevaluators/LargestAgreementsLoopTest.java	(revision 21)
@@ -0,0 +1,108 @@
+package geniusweb.voting.votingevaluators;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+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 java.util.Map;
+
+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.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+import tudelft.utilities.junit.GeneralTests;
+
+public class LargestAgreementsLoopTest
+		extends GeneralTests<LargestAgreementsLoop> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private final Bid a = mock(Bid.class), b = mock(Bid.class),
+			c = mock(Bid.class), d = mock(Bid.class);
+
+	private LargestAgreementsLoop lal1 = new LargestAgreementsLoop();
+	private LargestAgreementsLoop lal1a = new LargestAgreementsLoop();
+	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 Votes votes1 = mock(Votes.class);
+	private LargestAgreementsLoop lal2 = new LargestAgreementsLoop(
+			new CollectedVotes(Collections.singletonMap(party1, votes1),
+					Collections.singletonMap(party1, 1)));
+
+	private String lalstring = "{\"LargestAgreementsLoop\":{}}";
+
+	@Override
+	public List<List<LargestAgreementsLoop>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(lal1, lal1a), Arrays.asList(lal2));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("LargestAgreementsLoop", "LargestAgreementsLoop");
+	}
+
+	@Test
+	public void serializeAcceptTest() 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 deserializeAcceptTest() throws IOException {
+		VotingEvaluator eval = jackson.readValue(lalstring,
+				VotingEvaluator.class);
+		// it should deserialize as the empty lal1 because we ignore the fields.
+		assertEquals(lal1, eval);
+	}
+
+	@Test
+	public void testCollectVotes() {
+		Votes vote1AB = new Votes(party1,
+				Arrays.asList(new Vote(party1, a, 2), new Vote(party1, b, 2)));
+		Votes vote2AB = new Votes(party2,
+				Arrays.asList(new Vote(party2, a, 2), new Vote(party2, b, 2)));
+		Votes vote3C = new Votes(party3, Arrays.asList(new Vote(party3, c, 2)));
+		Votes vote4AC = new Votes(party4,
+				Arrays.asList(new Vote(party4, a, 2), new Vote(party4, c, 2)));
+		// party 1,2,4 vote for A, party 1,2 vote for B, party 3,4 vote for C.
+		// the biggest vote is P,Q,S
+		Map<PartyId, Votes> votes = new HashMap<>();
+		votes.put(party1, vote1AB);
+		votes.put(party2, vote2AB);
+		votes.put(party3, vote3C);
+		votes.put(party4, vote4AC);
+
+		List<List<Action>> actions1 = Arrays
+				.asList(Arrays.asList(vote1AB, vote2AB, vote3C, vote4AC));
+		Map<PartyId, Integer> power = new HashMap<>();
+		power.put(party1, 1);
+		power.put(party2, 1);
+		power.put(party3, 1);
+		power.put(party4, 1);
+		Agreements agrees = new LargestAgreementsLoop()
+				.create(new CollectedVotes(votes, power)).getAgreements();
+		System.out.println(agrees);
+
+		// biggest agreement is party 1,2,4 for bid a.
+		assertEquals(new HashSet<>(Arrays.asList(party1, party2, party4)),
+				agrees.getMap().keySet());
+
+	}
+
+}
