Index: geniuswebcore/test/geniusweb/bidspace/pareto/GenericParetoTest.py
===================================================================
--- geniuswebcore/test/geniusweb/bidspace/pareto/GenericParetoTest.py	(revision 84)
+++ geniuswebcore/test/geniusweb/bidspace/pareto/GenericParetoTest.py	(revision 84)
@@ -0,0 +1,149 @@
+from decimal import Decimal
+from typing import Dict, List
+import unittest
+from unittest.mock import Mock
+
+from tudelft.utilities.immutablelist.Range import Range
+
+from geniusweb.bidspace.pareto.GenericPareto import GenericPareto
+from geniusweb.issuevalue.Bid import Bid
+from geniusweb.issuevalue.DiscreteValue import DiscreteValue
+from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
+from geniusweb.issuevalue.Domain import Domain
+from geniusweb.issuevalue.NumberValue import NumberValue
+from geniusweb.issuevalue.NumberValueSet import NumberValueSet
+from geniusweb.issuevalue.Value import Value
+from geniusweb.issuevalue.ValueSet import ValueSet
+from geniusweb.profile.PartialOrdering import PartialOrdering
+
+
+class GenericParetoTest(unittest.TestCase):
+	I1V2 = DiscreteValue("i1v2")
+	I1V1 = DiscreteValue("i1v1")
+	I2V1 = NumberValue(Decimal("2.00"))
+	I2V2 = NumberValue(Decimal("2.45"))
+	I2V3 = NumberValue(Decimal("2.90"))
+	DOMAINNAME = "testdomain"
+	ISSUE1 = "issue1"
+	ISSUE2 = "issue2"
+	issues:Dict[str, ValueSet] = {}
+
+	def setUp(self):
+		discretevalues1 = []
+		discretevalues1.append(self.I1V1)
+		discretevalues1.append(self.I1V2)
+		values1 = DiscreteValueSet(discretevalues1)
+		self.issues[self.ISSUE1] = values1
+
+		values2 = NumberValueSet(Range(Decimal(2), Decimal(3), Decimal("0.45")))
+		self.issues[self.ISSUE2] = values2
+
+		self.domain = Domain(self.DOMAINNAME, self.issues)
+
+		issuevalues:Dict[str, Value] = {}
+		issuevalues[self.ISSUE1] = self.I1V1
+		issuevalues[self.ISSUE2] = self.I2V1
+		self.bid1 = Bid(issuevalues)
+		issuevalues[self.ISSUE1] = self.I1V1
+		issuevalues[self.ISSUE2] = self.I2V2
+		self.bid2 = Bid(issuevalues)
+		issuevalues[self.ISSUE1] = self.I1V2
+		issuevalues[self.ISSUE2] = self.I2V1
+		self.bid3 = Bid(issuevalues)
+
+		self.profile1 = Mock(PartialOrdering)
+		self.profile2 = Mock(PartialOrdering)
+		self.profile3 = Mock(PartialOrdering)
+		self.profile1.getDomain = Mock(return_value=self.domain)
+		self.profile2.getDomain = Mock(return_value=self.domain)
+		self.profile3.getDomain = Mock(return_value=self.domain)
+
+		self.pareto = GenericPareto([self.profile1, self.profile2])
+
+	def testgenericParetoTest(self):
+
+		# fefault: there is no preference at all, all isPreferredOrEqual
+		# returns false and we can't remove pareto points.
+
+		profiles:List[PartialOrdering] = [self.profile1, self.profile2]
+		# In python we need to set also False values
+		self.profile1.isPreferredOrEqual = Mock(return_value=False)
+		self.profile2.isPreferredOrEqual = Mock(return_value=False)
+		pareto = GenericPareto(profiles)
+
+		points = pareto.getPoints()
+		self.assertEqual(6, len(points))
+
+	def testgenericParetoTest1(self):
+
+		# both prefer bid1 over any other bid.
+		self.profile1.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid1
+		self.profile2.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid1
+
+		points = self.pareto.getPoints()
+		self.assertEqual(1, len(points))
+		self.assertEqual(self.bid1, next(iter(points)))
+
+	def testgenericParetoTest2(self):
+
+		# both prefer bid2 over any other bid.
+		self.profile1.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid2
+		self.profile2.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid2
+
+		points = self.pareto.getPoints()
+		# since both prefer bid2, bid is pareto point.
+		self.assertEqual(1, len(points))
+		self.assertEqual(self.bid2, next(iter(points)))
+
+	def testgenericParetoTest3(self):
+		# profile1 prefers bid1, profile2 prefers bid2.
+		# now neither bid1 nor bid2 are dominating each other
+		# and nor does bid1 or bid2 dominate anything else.
+		self.profile1.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid1
+		self.profile2.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid2
+
+		points = self.pareto.getPoints()
+		# since both prefer bid2, bid is pareto point.
+		self.assertEquals(6, len(points))
+
+	def testgenericParetoTest4(self):
+
+		# profile1 prefers bid1, profile2 prefers bid2.
+		# but they both hate bid3.
+		self.profile1.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b1 == self.bid1 or b2 == self.bid3
+		self.profile2.isPreferredOrEqual.side_effect = \
+			lambda b1, b2: b2 == self.bid3 or b1 == self.bid2
+
+		points = self.pareto.getPoints()
+		# both hate bid3 but don't agree on bid1/2. bid3 is ruled out.
+		self.assertEqual(5, len(points))
+
+	def testNullNotOk(self):
+		self.assertRaises(ValueError,
+			lambda:GenericPareto([self.profile1, None, self.profile1]))
+
+	def testImmutable(self):
+		list = [self.profile1, self.profile2]
+		pareto = GenericPareto(list)
+		# if we modify the list, the pareto should not change
+		list.append(self.profile3)
+		self.assertEqual(2, len(pareto.getProfiles()))
+
+	# in python we just copy the list so adding should  do nothing
+	def testGetParetoAndAdd(self):
+		list = self.pareto.getProfiles()
+		list.append(self.profile3)
+		self.assertEqual(2, len(self.pareto.getProfiles()))
+
+	def testGetPointsAndAdd(self):
+		self.assertEqual(1, len(self.pareto.getPoints()))
+		list = self.pareto.getPoints()
+		list.add(self.bid1)
+		self.assertEqual(1, len(self.pareto.getPoints()))
Index: geniuswebcore/test/geniusweb/bidspace/pareto/ParetoE2Etest.py
===================================================================
--- geniuswebcore/test/geniusweb/bidspace/pareto/ParetoE2Etest.py	(revision 84)
+++ geniuswebcore/test/geniusweb/bidspace/pareto/ParetoE2Etest.py	(revision 84)
@@ -0,0 +1,55 @@
+import json
+from pathlib import Path
+import unittest
+
+from pyson.ObjectMapper import ObjectMapper
+
+from geniusweb.bidspace.pareto.GenericPareto import GenericPareto
+from geniusweb.bidspace.pareto.ParetoLinearAdditive import ParetoLinearAdditive
+from geniusweb.profile.Profile import Profile
+
+
+class ParetoE2Etest (unittest.TestCase):
+	'''
+	Test pareto functionality with some real profiles in an e2e test
+	'''
+	jackson = ObjectMapper()
+
+	data = \
+		[\
+				[ "test/resources/jobs/jobs1.json", \
+						"test/resources/jobs/jobs2.json", 18 ], \
+				[ "test/resources/7issues/7issues1.json", \
+						"test/resources/7issues/7issues2.json", 557 ]\
+		]
+	
+	def testAll(self):
+		''' 
+		runs all the tests. Python has no standard 'parameterized' mechanism
+		so we have to do it ourselves.
+		'''
+		for [profile1, profile2, expectedNr] in self.data:
+			self.path1 = profile1
+			self.path2 = profile2
+			self.expectedNrPoints = expectedNr
+			with self.subTest("checkGeneric " + self.path1 + "," + self.path2):
+				self.checkGenerciPareto()
+
+	def checkGenerciPareto(self):
+		file1 = Path(self.path1).read_text("utf-8")
+		profile1 = self.jackson.parse(json.loads(file1), Profile)
+		
+		file2 = Path(self.path2).read_text("utf-8")
+		profile2 = self.jackson.parse(json.loads(file2), Profile)
+
+		profiles = [profile1, profile2]
+		linaddpareto = ParetoLinearAdditive(profiles)
+		print("linaddpareto result" + str(linaddpareto))
+		self.assertEqual(self.expectedNrPoints, len(linaddpareto.getPoints()))
+
+		if self.expectedNrPoints < 40:
+			generalpareto = GenericPareto(profiles)
+			self.assertEqual(generalpareto.getPoints(), linaddpareto.getPoints())
+			print("generalpareto result" + str(generalpareto))
+		else:
+			print("Skippedn generalpareto - expected too slow")
Index: geniuswebcore/test/geniusweb/bidspace/pareto/ParetoPointTest.py
===================================================================
--- geniuswebcore/test/geniusweb/bidspace/pareto/ParetoPointTest.py	(revision 84)
+++ geniuswebcore/test/geniusweb/bidspace/pareto/ParetoPointTest.py	(revision 84)
@@ -0,0 +1,88 @@
+from decimal import Decimal
+import unittest
+from unittest.mock import Mock
+
+from geniusweb.bidspace.pareto.ParetoPoint import ParetoPoint
+from geniusweb.issuevalue.Bid import Bid
+from geniusweb.issuevalue.Value import Value
+from geniusweb.profile.utilityspace.LinearAdditive import LinearAdditive
+
+
+class ParetoPointTest(unittest.TestCase):
+	D0_2 = Decimal("0.2")
+	D0_1 = Decimal("0.1")
+	D0_3 = Decimal("0.3")
+	D0_4 = Decimal("0.4")
+	D0_5 = Decimal("0.5")
+	space1 = Mock(LinearAdditive)	
+	space2 = Mock(LinearAdditive)
+	bid11 = Mock(Bid)
+	bid12 = Mock(Bid)
+	bid21 = Mock(Bid)
+	bid22 = Mock(Bid)
+	
+	def setUp(self):
+		self.space1.getUtility = Mock(side_effect=lambda b: \
+			{self.bid11:self.D0_1, self.bid12:self.D0_1, \
+			self.bid21:self.D0_2, self.bid22:self.D0_2 }[b]) 
+		self.space2.getUtility = Mock(side_effect=lambda b: \
+			{self.bid11:self.D0_1, self.bid12:self.D0_2, \
+			self.bid21:self.D0_1, self.bid22:self.D0_2 }[b]) 
+
+		self.point11 = ParetoPoint.create(self.bid11, [self.space1, self.space2])
+		self.point12 = ParetoPoint.create(self.bid12, [self.space1, self.space2])
+		self.point21 = ParetoPoint.create(self.bid21, [self.space1, self.space2])
+		self.point22 = ParetoPoint.create(self.bid22, [self.space1, self.space2])
+
+	def testconstructorTest(self):
+		self.assertEqual(self.bid11, self.point11.getBid())
+		self.assertEqual(self.bid21, self.point21.getBid())
+
+		self.assertEqual([self.D0_1, self.D0_1], self.point11.getUtilities())
+		self.assertEqual([self.D0_2, self.D0_1], self.point21.getUtilities())
+
+	def testdominationTest(self):
+		self.assertTrue(self.point11.isDominatedBy(self.point11))
+		self.assertTrue(self.point11.isDominatedBy(self.point12))
+		self.assertTrue(self.point11.isDominatedBy(self.point21))
+		self.assertTrue(self.point11.isDominatedBy(self.point22))
+
+		self.assertFalse(self.point12.isDominatedBy(self.point11))
+		self.assertFalse(self.point12.isDominatedBy(self.point21))
+		self.assertTrue(self.point12.isDominatedBy(self.point12))
+		self.assertTrue(self.point12.isDominatedBy(self.point22))
+
+		self.assertFalse(self.point21.isDominatedBy(self.point11))
+		self.assertFalse(self.point21.isDominatedBy(self.point12))
+		self.assertTrue(self.point21.isDominatedBy(self.point21))
+		self.assertTrue(self.point21.isDominatedBy(self.point22))
+
+		self.assertFalse(self.point22.isDominatedBy(self.point11))
+		self.assertFalse(self.point22.isDominatedBy(self.point12))
+		self.assertFalse(self.point22.isDominatedBy(self.point21))
+		self.assertTrue(self.point22.isDominatedBy(self.point22))
+
+	def testmergeTest(self):
+		# partial bid 020_010 has value 0.2 for issue 2 in space1 and 0.1 for
+		# issue 2 in space2.
+		value1 = Mock(Value)
+		value2 = Mock(Value)
+		value3 = Mock(Value)
+
+		partialbid020_010 = Bid({"issue2": value2})
+		partialbid102_301 = Bid({"issue1": value1, "issue3":value3})
+		# merged this should be a bid 122_311 so it has utilities
+		# 1+2+2=0.5 in space1 and 3+1+1=0.5 in space2
+
+		self.space1.getUtility = Mock(side_effect=lambda bid:\
+			{partialbid020_010:self.D0_2, partialbid102_301:self.D0_3}[bid])
+		self.space2.getUtility = Mock(side_effect=lambda bid:
+			{partialbid020_010:self.D0_1, partialbid102_301:self.D0_4 }[bid])
+	
+		p1 = ParetoPoint.create(partialbid020_010, [self.space1, self.space2])
+		p2 = ParetoPoint.create(partialbid102_301, [self.space1, self.space2])
+
+		p = p1.merge(p2)
+		bid = p.getBid()
+		self.assertEqual(self.D0_5, p.getUtilities()[0])
+		self.assertEqual(self.D0_5, p.getUtilities()[1])
Index: geniuswebcore/test/geniusweb/bidspace/pareto/PartialParetoTest.py
===================================================================
--- geniuswebcore/test/geniusweb/bidspace/pareto/PartialParetoTest.py	(revision 84)
+++ geniuswebcore/test/geniusweb/bidspace/pareto/PartialParetoTest.py	(revision 84)
@@ -0,0 +1,85 @@
+from decimal import Decimal
+from typing import Set, Dict
+import unittest
+from unittest.mock import Mock
+
+from geniusweb.bidspace.pareto.PartialPareto import PartialPareto
+from geniusweb.issuevalue.Bid import Bid
+from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
+from geniusweb.issuevalue.Domain import Domain
+from geniusweb.issuevalue.NumberValue import NumberValue
+from geniusweb.issuevalue.NumberValueSet import NumberValueSet
+from geniusweb.issuevalue.Value import Value
+from geniusweb.issuevalue.ValueSet import ValueSet
+from geniusweb.profile.utilityspace.LinearAdditive import LinearAdditive
+
+
+class PartialParetoTest(unittest.TestCase):
+	D0_0 = Decimal(0)
+	D0_2 = Decimal("0.2")
+	D0_1 = Decimal("0.1")
+	D0_3 = Decimal("0.3")
+	D0_4 = Decimal("0.4")
+	D0_5 = Decimal("0.5")
+
+	space1 = Mock(LinearAdditive)
+	space2 = Mock(LinearAdditive)
+
+	domain = Mock(Domain)
+	issues:Set[str] = set(["issue1", "issue2"])
+	value1a = Mock(Value)
+	value1b = Mock(Value)
+	value2a = Mock(Value) 
+	value2b = Mock(Value)
+
+	def setUp(self):
+
+		map:Dict[str, Value] = {}
+		map["issue1"] = self.value1a
+		self.bid10 = Bid(map)
+		map["issue1"] = self.value1b
+		self.bid20 = Bid(map)
+
+		map = {}
+		map["issue2"] = self.value2a
+		self.bid01 = Bid(map)
+		map["issue2"] = self.value2b
+		self.bid02 = Bid(map)
+
+		self.space1.getUtility = Mock(side_effect=lambda b: \
+			{self.bid10:self.D0_1, self.bid20:self.D0_2, \
+			self.bid01:self.D0_0, self.bid02:self.D0_0 }[b]) 
+		self.space2.getUtility = Mock(side_effect=lambda b: \
+			{self.bid10:self.D0_0, self.bid20:self.D0_0, \
+			self.bid01:self.D0_0, self.bid02:self.D0_2 }[b]) 
+
+		self.space1.getDomain = Mock(return_value=self.domain)
+		self.domain.getIssues = Mock(return_value=self.issues)
+		self.issue1valueset = DiscreteValueSet([self.value1a, self.value1b])
+		self.domain.getValues = Mock(side_effect=lambda iss:\
+					{"issue1":self.issue1valueset, "issue2":self.issue2valueset}[iss])
+			
+		# by putting the best value first in the list now,
+		# we may trigger a different evaluation path in the PartialPareto
+		# algorithm, improving coverage
+		self.issue2valueset = DiscreteValueSet([self.value2b, self.value2a])
+
+	def testOneIssueTest(self):
+		pp = PartialPareto.create([self.space1, self.space2], ["issue1"])
+		# bid22 is best for both. Check that's the only remaining point
+		self.assertEqual(1, len(pp.getPoints()))
+		map:Dict[str, Value] = {}
+		map["issue1"] = self.value1b
+		# there is no value for issue2 because we asked to exclude it from the
+		# pareto.
+		self.assertEqual(Bid(map), pp.getPoints()[0].getBid())
+
+	def testTwoIssuesTest(self):
+		pp = PartialPareto.create([self.space1, self.space2],
+				["issue1", "issue2"])
+		# bid22 is best for both. Check that's the only remaining point
+		self.assertEqual(1, len(pp.getPoints()))
+		map:Dict[str, Value] = {}
+		map["issue1"] = self.value1b
+		map["issue2"] = self.value2b
+		self.assertEqual(Bid(map), pp.getPoints()[0].getBid())
Index: geniuswebcore/test/geniusweb/opponentmodel/FrequencyOppModelTest.py
===================================================================
--- geniuswebcore/test/geniusweb/opponentmodel/FrequencyOppModelTest.py	(revision 84)
+++ geniuswebcore/test/geniusweb/opponentmodel/FrequencyOppModelTest.py	(revision 84)
@@ -0,0 +1,193 @@
+from unitpy.GeneralTests import GeneralTests
+from geniusweb.opponentmodel.FrequencyOpponentModel import FrequencyOpponentModel
+from geniusweb.issuevalue.DiscreteValue import DiscreteValue
+from unittest.mock import Mock
+from geniusweb.progress.Progress import Progress
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.actions.Offer import Offer
+from decimal import Decimal
+from typing import Dict, List
+from geniusweb.issuevalue.ValueSet import ValueSet
+from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
+from geniusweb.issuevalue.NumberValueSet import NumberValueSet
+from geniusweb.issuevalue.Domain import Domain
+from geniusweb.issuevalue.Value import Value
+from geniusweb.issuevalue.NumberValue import NumberValue
+from geniusweb.issuevalue.Bid import Bid
+from geniusweb.actions.Offer import Offer
+import unittest
+from tudelft.utilities.immutablelist.Range import Range
+
+class FrequencyOppModelTest (unittest.TestCase, GeneralTests[FrequencyOpponentModel] ):
+	ISS1 = "issue1"
+	ISS2 = "issue2";
+	I1V1 = DiscreteValue("i1v1")
+	I1V2 = DiscreteValue("i1v2");
+	I2V1 = DiscreteValue("i2v1")
+	I2V2 = DiscreteValue("i2v2")
+	I1V2b = DiscreteValue("i1v2b");
+	progress = Mock(Progress);
+	other = PartyId("other")
+	HALF = Decimal("0.5")
+	#list:List[List[Bid]]  = []
+
+	def setUp(self):
+		issues:Dict[str, ValueSet]  = {}
+		discretevalues1:List[DiscreteValue]  = []
+		discretevalues1.append(self.I1V1);
+		discretevalues1.append(self.I1V2);
+		values1 = DiscreteValueSet(discretevalues1)
+		issues[self.ISS1]= values1
+		values2 = NumberValueSet(Range(Decimal(0),Decimal(10), Decimal("0.3")))
+		issues[self.ISS2]=values2
+		self.domain = Domain("test", issues)
+		self.domain2 = Domain("test2", issues)
+
+		# slightly different issue1
+		issues = {}
+		discretevalues1 = []
+		discretevalues1.append(self.I1V1)
+		discretevalues1.append(self.I1V2b)
+		values1 = DiscreteValueSet(discretevalues1)
+		issues[self.ISS1]= values1
+		values2 = NumberValueSet(Range(Decimal(0), Decimal(10),Decimal("0.3")))
+		issues[self.ISS2]= values2
+		self.domain3 = Domain("test", issues)
+
+		# all bids are for domain
+		issuevalues:Dict[str, Value]  = {}
+		issuevalues[self.ISS1]= self.I1V1
+		issuevalues[self.ISS2]=NumberValue(Decimal("1.2"))
+		self.bid1 = Bid(issuevalues)
+
+		issuevalues[self.ISS1]= self.I1V1
+		issuevalues[self.ISS2]= NumberValue(Decimal("1.5"))
+		self.bid2 = Bid(issuevalues)
+
+		issuevalues[self.ISS1]= self.I1V2
+		issuevalues[self.ISS2]= NumberValue(Decimal("1.5"))
+		self.bid3 = Bid(issuevalues)
+
+		self.oppModel1 = FrequencyOpponentModel.create().With(self.domain, None)
+		self.oppModel1b = FrequencyOpponentModel.create().With(self.domain, None)
+		self.oppModel2 = FrequencyOpponentModel.create().With(self.domain2, None)
+		self.oppModel3 = FrequencyOpponentModel.create().With(self.domain3, None)
+		self.oppModel4 = self.oppModel3.WithAction(Offer(self.other, self.bid1), self.progress)
+
+	#Override
+	def getGeneralTestData(self) -> List[List[FrequencyOpponentModel]] :
+		return [[self.oppModel1, self.oppModel1b],
+				[self.oppModel2], [self.oppModel3],	[self.oppModel4]]
+
+	#Override
+	def getGeneralTestStrings(self)->List[str] :
+		return ["FrequencyOpponentModel\\[0,\\{issue.=\\{\\}, issue.=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[0,\\{issue.=\\{\\}, issue.=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[0,\\{issue.=\\{\\}, issue.=\\{\\}\\}\\]",
+				"FrequencyOpponentModel\\[1,\\{issue2=\\{1.2=1\\}, issue1=\\{\"i1v1\"=1\\}\\}\\]"]
+
+	def testsmokeTestNull(self) :
+		self.assertRaises(ValueError, lambda:FrequencyOpponentModel.create().With(None,None))
+
+	def testsmokeTest(self):
+		FrequencyOpponentModel.create().With(self.domain, None)
+
+	def testEmptyModel(self):
+		oppModel = FrequencyOpponentModel.create().With(self.domain, None)
+		self.assertEqual(1, oppModel.getUtility(self.bid1))
+		self.assertEqual(1, oppModel.getUtility(self.bid2))
+		
+	def testEmptyModelPartialBid(self):
+		oppModel =FrequencyOpponentModel.create().With(self.domain, None)
+		bid = Bid({})
+		self.assertEqual(1, oppModel.getUtility(bid))
+
+	def testPartialUtility(self):
+		freqs:Dict[str, Dict[Value, int]] = {}
+		freqs1:Dict[Value, int]  = {}
+		freqs2:Dict[Value, int]  = {}
+
+		freqs1[self.I1V1]= 2
+		freqs1[self.I1V2]= 0
+		freqs2[self.I2V1]= 1
+		freqs2[self.I2V2]= 1
+
+		freqs[self.ISS1]= freqs1
+		freqs[self.ISS2]= freqs2
+		total = Decimal(2)
+
+		oppModel = FrequencyOpponentModel(self.domain,	freqs, total,None)
+
+		E = 0.0000001
+		self.assertAlmostEqual(0.5, oppModel.getUtility(Bid({self.ISS1: self.I1V1})), None,E)
+		self.assertAlmostEqual(0, oppModel.getUtility(Bid({self.ISS1: self.I1V2})))
+		self.assertAlmostEqual(0.25,
+				oppModel.getUtility(Bid({self.ISS2:self.I2V1})), None,E)
+		self.assertAlmostEqual(0.25, oppModel.getUtility(Bid({self.ISS2: self.I2V2})))
+
+
+	def testUpdate(self):
+		oppModel = self.oppModel1.WithAction(Offer(self.other, self.bid1),
+				self.progress);
+		self.assertEqual(1, oppModel.getUtility(self.bid1) )
+		self.assertEqual(0,oppModel.getUtility(self.bid3) )
+		# bid2 has 1 of 2 issue values same as bid1.
+		self.assertTrue(0.5, oppModel.getUtility(self.bid2))
+
+	def testUpdate2(self):
+		# bid1 and bid2 both want I1V1. They differ on the number value.
+		# bid3 wants I1V2 but does have the number value from bid2
+		oppModel = self.oppModel1\
+				.WithAction(Offer(self.other, self.bid1), self.progress)\
+				.WithAction(Offer(self.other, self.bid2), self.progress)
+		self.assertEqual(0.75, oppModel.getUtility(self.bid1))
+		self.assertEqual(0.75, oppModel.getUtility(self.bid2))
+		self.assertEqual(0.25, oppModel.getUtility(self.bid3)) 
+
+	def testPartialBidUpdate(self):
+		oppModel = self.oppModel1.WithAction(Offer(self.other, self.bid1),
+				self.progress)
+		partialbid = Bid({self.ISS1: self.I1V1})
+		oppModel.WithAction(Offer(self.other, partialbid), self.progress)
+
+	def testGetCounts(self):
+		self.assertEqual({}, self.oppModel1.getCounts(self.ISS1))
+		self.assertEquals({}, self.oppModel1.getCounts(self.ISS2))
+
+		values1:Dict[str, Value] = {}
+		values1[self.ISS1]= self.I1V1
+		values1[self.ISS2]= self.I2V1
+		offer1 = Offer(self.other, Bid(values1))
+
+		oppmod = self.oppModel1.WithAction(offer1, self.progress)
+
+		self.assertEqual(1, oppmod.getCounts(self.ISS1)[self.I1V1])
+		self.assertFalse(self.I1V2 in oppmod.getCounts(self.ISS1))
+		self.assertEquals(1, oppmod.getCounts(self.ISS2)[self.I2V1])
+		self.assertFalse(self.I2V2 in oppmod.getCounts(self.ISS2))
+
+		# same identical bid. Counts should go to 2
+		oppmod = oppmod.WithAction(offer1, self.progress)
+
+		self.assertEquals(2, oppmod.getCounts(self.ISS1)[self.I1V1])
+		self.assertEquals(2, oppmod.getCounts(self.ISS2)[self.I2V1])
+
+		values2:Dict[str, Value] = {}
+		values2[self.ISS1]=self.I1V2
+		values2[self.ISS2]=self.I2V2
+		offer2 = Offer(self.other, Bid(values2))
+
+		# Other bid. with value2 for both issues
+		oppmod = oppmod.WithAction(offer2, self.progress)
+
+		self.assertEqual(2, oppmod.getCounts(self.ISS1)[self.I1V1])
+		self.assertEqual(2, oppmod.getCounts(self.ISS2)[self.I2V1])
+		self.assertEqual(1, oppmod.getCounts(self.ISS1)[self.I1V2])
+		self.assertEqual(1, oppmod.getCounts(self.ISS2)[self.I2V2])
+
+
+	def testStableName(self):
+		name = self.oppModel1.getName()
+		self.assertNotEqual(None, name)
+		self.assertEqual(name, self.oppModel1.getName())
+		
