Index: bidspace/pom.xml
===================================================================
--- bidspace/pom.xml	(revision 2)
+++ bidspace/pom.xml	(revision 4)
@@ -140,4 +140,16 @@
 					<target>1.8</target>
 				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
 			</plugin>
 
Index: bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java
===================================================================
--- bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java	(revision 2)
+++ bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java	(revision 4)
@@ -3,57 +3,226 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.Domain;
+import geniusweb.issuevalue.Value;
 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
 import tudelft.utilities.immutablelist.AbstractImmutableList;
+import tudelft.utilities.immutablelist.FixedList;
+import tudelft.utilities.immutablelist.ImmutableList;
+import tudelft.utilities.immutablelist.JoinedList;
+import tudelft.utilities.immutablelist.MapList;
+import tudelft.utilities.immutablelist.Tuple;
 
 /**
- * Object containing a subset of all bids, that have utility within given range.
+ * Tool class containing functions dealing with utilities of all bids in a given
+ * {@link LinearAdditiveUtilitySpace}. This class caches previously computed
+ * values to accelerate the calls and subsequent calls. Re-use the object to
+ * keep/reuse the cache.
  */
-public class BidsWithUtility extends AbstractImmutableList<Bid> {
-
-	private final List<Bid> collectedbids;
-
-	/**
-	 * Create Bids with utility within given bounds.
-	 * 
-	 * @param space the {@link LinearAdditiveUtilitySpace} to search
-	 * @param min   the minimum required utility
-	 * @param max   the maximum required utility
-	 */
-	public BidsWithUtility(LinearAdditiveUtilitySpace space, BigDecimal min, BigDecimal max) {
-		this.collectedbids = new ArrayList<Bid>(getBids(space, min, max));
-	}
-
-	/**
-	 * Brute force hack for this time. Maybe we can construct this in a lazy way.
-	 * 
-	 * @param space
-	 * @param min
-	 * @param max
-	 * @return
-	 */
-	private List<Bid> getBids(LinearAdditiveUtilitySpace space, BigDecimal min, BigDecimal max) {
-		List<Bid> goodbids = new LinkedList<Bid>();
-		for (Bid bid : new AllBidsList(space.getDomain())) {
-			BigDecimal util = space.getUtility(bid);
-			if (util.compareTo(min) >= 0 && util.compareTo(max) <= 0) {
-				goodbids.add(bid);
-			}
-		}
-		return goodbids;
+public class BidsWithUtility {
+
+	private final List<IssueInfo> issueInfo;
+	private final int precision; // #digits used for Intervals
+
+	/**
+	 * cache. Key = call arguments for {@link #get(int, Interval)}. Value=return
+	 * value of that call.
+	 */
+	private final Map<Tuple<Integer, Interval>, ImmutableList<Bid>> cache = new HashMap<>();
+
+	/**
+	 * Default constructor, uses default precision 6. This value seems practical
+	 * for the common range of issues, utilities and weights. See
+	 * {@link #BidsWithUtil(LinearAdditiveUtilitySpace, int)} for more details
+	 * on the precision.
+	 * 
+	 * @param space the {@link LinearAdditiveUtilitySpace} to analyze
+	 */
+	public BidsWithUtility(LinearAdditiveUtilitySpace space) {
+		this(space, 6);
+	}
+
+	/**
+	 * 
+	 * @param space     the {@link LinearAdditiveUtilitySpace} to analyze
+	 * @param precision the number of digits to use for computations. This value
+	 *                  should match the max number of (digits used in the
+	 *                  weight of an issue + number of digits used in the issue
+	 *                  utility). Usually you also look at the step size, and
+	 *                  the range of interest. For instance if your utility
+	 *                  function has values 1/3 and 2/3, then these have an
+	 *                  'infinite' number of relevant digits. But if we want to
+	 *                  search bids between utility 0.1 and 0.2, then computing
+	 *                  in 2 digits might already be sufficient. This algorithm
+	 *                  has memory and space complexity O( |nissues|
+	 *                  10^precision ). For spaces up to 7 issues, 7 digits
+	 *                  should be feasible; for 9 issues, 6 digits may be the
+	 *                  maximum.
+	 */
+	public BidsWithUtility(LinearAdditiveUtilitySpace space, int precision) {
+		this(getInfo(space, precision), precision);
+	}
+
+	/**
+	 * 
+	 * @param issuesInfo List of the relevant issues (in order of relevance) and
+	 *                   all info of each issue.
+	 * @param precision  the number of digits used in Intervals.
+	 */
+	public BidsWithUtility(List<IssueInfo> issuesInfo, int precision) {
+		if (issuesInfo == null || issuesInfo.isEmpty()) {
+			throw new IllegalArgumentException(
+					"sortedissues list must contain at least 1 element");
+		}
+
+		this.issueInfo = issuesInfo;
+		this.precision = precision;
+
+	}
+
+	/**
+	 * @return the utility {@link Interval} of this space: minimum and maximum
+	 *         achievable utility.
+	 */
+	public Interval getRange() {
+		return getRange(issueInfo.size() - 1);
+	}
+
+	/**
+	 * 
+	 * @param range the minimum and maximum utility required of the bids. to be
+	 *              included (both ends inclusive).
+	 * @return a list with bids that have utility inside range. possibly empty.
+	 *         With decreasing precision, more bids may drop out due to rounding
+	 *         errors.
+	 */
+	public ImmutableList<Bid> getBids(Interval range) {
+		return get(issueInfo.size() - 1, range.round(precision));
+	}
+
+	public List<IssueInfo> getInfo() {
+		return Collections.unmodifiableList(issueInfo);
+	}
+
+	/**
+	 * Create partial BidsWithUtil list considering only issues 0..n, with
+	 * utilities in given range.
+	 * 
+	 * @param n    the number of {@link #issueRanges} to consider, we consider
+	 *             0..n here. The recursion decreases n until n=0
+	 * @param goal the minimum and maximum utility required of the bids. to be
+	 *             included (both ends inclusive)
+	 * @return BidsWithUtil list, possibly empty.
+	 */
+	protected ImmutableList<Bid> get(int n, Interval goal) {
+		if (goal == null) {
+			throw new NullPointerException("Interval=null");
+		}
+
+		// clamp goal into what is reachable. Avoid caching empty
+		goal = goal.intersect(getRange(n));
+		if (goal.isEmpty()) {
+			return new FixedList<>();
+		}
+
+		Tuple<Integer, Interval> cachetuple = new Tuple<>(n, goal);
+		ImmutableList<Bid> cached = cache.get(cachetuple);
+		if (cached != null) {
+			// hits++;
+			return cached;
+		}
+
+		ImmutableList<Bid> result = checkedGet(n, goal);
+		cache.put(cachetuple, result);
+		return result;
+	}
+
+	private static List<IssueInfo> getInfo(LinearAdditiveUtilitySpace space2,
+			int precision) {
+		Domain dom = space2.getDomain();
+		return space2.getDomain().getIssues().stream()
+				.map(issue -> new IssueInfo(issue, dom.getValues(issue),
+						space2.getUtilities().get(issue),
+						space2.getWeight(issue), precision))
+				.collect(Collectors.toList());
+	}
+
+	private ImmutableList<Bid> checkedGet(int n, Interval goal) {
+		IssueInfo info = issueInfo.get(n);
+		// issue is the first issuesWithRange.
+		String issue = issueInfo.get(n).getName();
+
+		if (n == 0)
+			return new OneIssueSubset(info, goal);
+
+		// make new list, joining all sub-lists
+		ImmutableList<Bid> fulllist = new FixedList<>();
+		for (Value val : info.getValues()) {
+			BigDecimal weightedutil = info.getWeightedUtil(val);
+			Interval subgoal = goal.subtract(weightedutil);
+			// recurse: get list of bids for the subspace
+			ImmutableList<Bid> partialbids = get(n - 1, subgoal);
+
+			Bid bid = new Bid(issue, val);
+			ImmutableList<Bid> fullbids = new MapList<Bid, Bid>(
+					pbid -> pbid.merge(bid), partialbids);
+			if (!fullbids.size().equals(BigInteger.ZERO))
+				fulllist = new JoinedList<Bid>(fullbids, fulllist);
+		}
+		return fulllist;
+	}
+
+	/**
+	 * @param n the maximum issuevalue utility to include. Use n=index of last
+	 *          issue s= (#issues in the domain - 1) for the full range of this
+	 *          domain.
+	 * @return Interval (min, max) of the total weighted utility Interval of
+	 *         issues 0..n. All weighted utilities have been rounded to the set
+	 *         {@link #precision}
+	 */
+	private Interval getRange(int n) {
+		Interval value = Interval.ZERO;
+		for (int i = 0; i <= n; i++) {
+			value = value.add(issueInfo.get(i).getInterval());
+		}
+		return value;
+	}
+
+}
+
+/**
+ * List of all one-issue bids that have utility inside given interval.
+ */
+class OneIssueSubset extends AbstractImmutableList<Bid> {
+	private final IssueInfo info;
+	private final Interval interval;
+	private BigInteger size;
+
+	/**
+	 * 
+	 * @param info     the {@link IssueInfo}
+	 * @param interval a utility interval (weighted)
+	 */
+	OneIssueSubset(IssueInfo info, Interval interval) {
+		this.info = info;
+		this.interval = interval;
+		this.size = BigInteger.valueOf(info.subsetSize(interval));
 	}
 
 	@Override
 	public Bid get(BigInteger index) {
-		return collectedbids.get(index.intValue());
+		return new Bid(info.getName(),
+				info.subset(interval).get(index.intValue()));
 	}
 
 	@Override
 	public BigInteger size() {
-		return BigInteger.valueOf(collectedbids.size());
+		return size;
 	}
 
Index: bidspace/src/main/java/geniusweb/bidspace/Interval.java
===================================================================
--- bidspace/src/main/java/geniusweb/bidspace/Interval.java	(revision 4)
+++ bidspace/src/main/java/geniusweb/bidspace/Interval.java	(revision 4)
@@ -0,0 +1,149 @@
+package geniusweb.bidspace;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * An interval [min, max].
+ *
+ */
+public class Interval {
+	public static Interval ZERO = new Interval(BigDecimal.ZERO,
+			BigDecimal.ZERO);
+
+	private final BigDecimal min;
+	private final BigDecimal max;
+
+	/**
+	 * AN interval [min,max]. If min>max, then there are no values in this
+	 * interval.
+	 * 
+	 * @param min
+	 * @param max
+	 */
+	public Interval(BigDecimal min, BigDecimal max) {
+		if (min == null || max == null) {
+			throw new NullPointerException("min and max must contain not null");
+		}
+		this.min = min;
+		this.max = max;
+	}
+
+	public BigDecimal getMin() {
+		return min;
+	}
+
+	public BigDecimal getMax() {
+		return max;
+	}
+
+	/**
+	 * 
+	 * @return true iff this range does not contain any element.
+	 */
+	public boolean isEmpty() {
+		return min.compareTo(max) > 0;
+	}
+
+	/**
+	 * 
+	 * @param value the value to test
+	 * @return true iff min<=value<=max
+	 */
+	public boolean contains(BigDecimal value) {
+		return min.compareTo(value) <= 0 && max.compareTo(value) >= 0;
+	}
+
+	/**
+	 * 
+	 * @param other {@link Interval} to be added to this
+	 * @return new interval [ this.min + other.min , this.max + other.max ]
+	 */
+	public Interval add(Interval other) {
+		return new Interval(min.add(other.min), max.add(other.max));
+	}
+
+	/**
+	 * 
+	 * @param minMax another minMax to intersect with
+	 * @return intersection with other. returns null if intersection is empty.
+	 */
+	public Interval intersect(Interval other) {
+		return new Interval(min.max(other.min), max.min(other.max));
+	}
+
+	/**
+	 * 
+	 * @param other the other minmax to deal with
+	 * @return the range of values that, when added to a value from other, will
+	 *         possibly get in our range. effectively, [min-other.max,
+	 *         max-other.min]. Returns null if the resulting range is empty.
+	 */
+	public Interval invert(Interval other) {
+		BigDecimal newmin = min.subtract(other.max);
+		BigDecimal newmax = max.subtract(other.min);
+		if (newmin.compareTo(newmax) > 0)
+			return null;
+		return new Interval(newmin, newmax);
+	}
+
+	/**
+	 * 
+	 * @param value the value to subtract
+	 * @return Interval with both min and max reduced by value.
+	 */
+	public Interval subtract(BigDecimal value) {
+		return new Interval(min.subtract(value), max.subtract(value));
+	}
+
+	public Interval multiply(BigDecimal weight) {
+		return new Interval(min.multiply(weight), max.multiply(weight));
+	}
+
+	@Override
+	public String toString() {
+		return "Interval[" + min + "," + max + "]";
+	}
+
+	/**
+	 * @param precision number of digits required
+	 * @return this but with modified precision. The interval is rounded so that
+	 *         the new interval is inside the old one.
+	 */
+	public Interval round(int precision) {
+		return new Interval(min.setScale(precision, RoundingMode.UP),
+				max.setScale(precision, RoundingMode.DOWN));
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((max == null) ? 0 : max.hashCode());
+		result = prime * result + ((min == null) ? 0 : min.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;
+		Interval other = (Interval) obj;
+		if (max == null) {
+			if (other.max != null)
+				return false;
+		} else if (!max.equals(other.max))
+			return false;
+		if (min == null) {
+			if (other.min != null)
+				return false;
+		} else if (!min.equals(other.min))
+			return false;
+		return true;
+	}
+
+}
Index: bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java
===================================================================
--- bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java	(revision 4)
+++ bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java	(revision 4)
@@ -0,0 +1,122 @@
+package geniusweb.bidspace;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import geniusweb.issuevalue.Value;
+import geniusweb.issuevalue.ValueSet;
+import geniusweb.profile.utilityspace.ValueSetUtilities;
+
+/**
+ * Tool class to collect all relevant info about one issue (from LinearAdditive)
+ * in one class. Used for internally grouping data for more efficient
+ * processing. This class may change in the future, not recommended for direct
+ * use.
+ * <p>
+ * immutable
+ */
+public class IssueInfo {
+	private final String name;
+	private final ValueSet values;
+	private final Interval interval;
+	private final Map<Value, BigDecimal> weightedUtils;
+
+	public IssueInfo(String name, ValueSet values, ValueSetUtilities utils,
+			BigDecimal weight, int precision) {
+		this.name = name;
+		this.values = values;
+		this.weightedUtils = computeWeightedUtils(utils, weight, precision);
+		this.interval = getRange();
+	}
+
+	public ValueSet getValues() {
+		return values;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * 
+	 * @return weighted minimum and maximum utility achievable with this issue,
+	 *         rounded to the {@link #precision}.
+	 */
+	public Interval getInterval() {
+		return interval;
+	}
+
+	/**
+	 * @param val the issue value to be evaluated
+	 * @return weighted utility of given value, rounded to nearest value with
+	 *         {@link #precision} digits.
+	 */
+	public BigDecimal getWeightedUtil(Value val) {
+		return weightedUtils.get(val);
+	}
+
+	/**
+	 * 
+	 * @param interval an {@link Interval} of utility values.
+	 * @return all values that are inside the interval.
+	 */
+	protected List<Value> subset(Interval interval) {
+
+		List<Value> selection = new ArrayList<>();
+		for (Value value : values) {
+			if (interval.contains(getWeightedUtil(value)))
+				selection.add(value);
+		}
+		return selection;
+	}
+
+	/**
+	 * Faster way to determine subset size, it does not create a list
+	 * 
+	 * @param interval an {@link Interval} of utility values.
+	 * @return size of the subset that you will get from calling subset
+	 */
+	protected int subsetSize(Interval interval) {
+		int n = 0;
+		for (Value value : values)
+			if (interval.contains(getWeightedUtil(value)))
+				n++;
+		return n;
+	}
+
+	/**
+	 * @return the {@link Interval} (minimum and maximum) of the utility of the
+	 *         weighted utility of this issue, properly rounded to the
+	 *         {@link #precision}/
+	 * 
+	 */
+	private Interval getRange() {
+		BigDecimal min = BigDecimal.ONE;
+		BigDecimal max = BigDecimal.ZERO;
+		for (Value value : values) {
+			BigDecimal util = getWeightedUtil(value);
+			if (util.compareTo(min) < 0)
+				min = util;
+			if (util.compareTo(max) > 0)
+				max = util;
+		}
+		return new Interval(min, max);
+
+	}
+
+	private Map<Value, BigDecimal> computeWeightedUtils(
+			ValueSetUtilities utilities, BigDecimal w, int prec) {
+		Map<Value, BigDecimal> map = new HashMap<>();
+
+		for (Value val : values) {
+			map.put(val, utilities.getUtility(val).multiply(w).setScale(prec,
+					RoundingMode.HALF_UP));
+		}
+		return map;
+	}
+
+}
Index: bidspace/src/test/java/geniusweb/bidspace/BidsWithUtilityTest.java
===================================================================
--- bidspace/src/test/java/geniusweb/bidspace/BidsWithUtilityTest.java	(revision 2)
+++ bidspace/src/test/java/geniusweb/bidspace/BidsWithUtilityTest.java	(revision 4)
@@ -1,99 +1,112 @@
 package geniusweb.bidspace;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.math.RoundingMode;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
+import java.util.Random;
 
-import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import geniusweb.issuevalue.Bid;
-import geniusweb.issuevalue.DiscreteValue;
-import geniusweb.issuevalue.DiscreteValueSet;
-import geniusweb.issuevalue.Domain;
-import geniusweb.issuevalue.NumberValue;
-import geniusweb.issuevalue.NumberValueSet;
-import geniusweb.issuevalue.ValueSet;
 import geniusweb.profile.Profile;
 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import tudelft.utilities.immutablelist.ImmutableList;
 
+@RunWith(Parameterized.class)
 public class BidsWithUtilityTest {
-	private static final DiscreteValue I1V2 = new DiscreteValue("i1v2");
-	private static final DiscreteValue I1V1 = new DiscreteValue("i1v1");
-	private static final NumberValue I2V1 = new NumberValue("2.00");
-	private static final NumberValue I2V2 = new NumberValue("2.45");
-	private static final NumberValue I2V3 = new NumberValue("2.90");
-	private static final String DOMAINNAME = "testdomain";
-	private static final String ISSUE1 = "issue1";
-	private static final String ISSUE2 = "issue2";
-	private static final Map<String, ValueSet> issues = new HashMap<>();
+	private final static ObjectMapper jackson = new ObjectMapper();
+	private final Interval utilityGoal = new Interval(new BigDecimal("0.50"),
+			new BigDecimal("0.51"));
+	private LinearAdditiveUtilitySpace profile;
+	private long expectedSize;
+	private int accuracy = 5;
 
-	private static ValueSet values1;
-	private static ValueSet values2;
-	private static final BigDecimal TWO = new BigDecimal("2");
-	private static final BigDecimal THREE = new BigDecimal("3");
-	private final static BigDecimal ZERO_1 = new BigDecimal("0.1"), ZERO_2 = new BigDecimal("0.2"),
-			ZERO_3 = new BigDecimal("0.3");
+	@Parameters
+	public static Collection<Object[]> data() {
+		return Arrays.asList(new Object[][] {
+				{ "src/test/resources/jobs/jobs1.json", 11 },
+				{ "src/test/resources/7issues/7issues1.json", 260000 },
+				{ "src/test/resources/9issues/9issues1.json", 25000000 } });
+	}
 
-	private static Domain domain;
-	private final static ObjectMapper jackson = new ObjectMapper();
+	public BidsWithUtilityTest(String filename, long expectedsize)
+			throws IOException {
+		String file = new String(Files.readAllBytes(Paths.get(filename)));
+		profile = (LinearAdditiveUtilitySpace) jackson.readValue(file,
+				Profile.class);
+		this.expectedSize = expectedsize;
+	}
 
-	@Before
-	public void before() {
-		Collection<DiscreteValue> discretevalues1 = new LinkedList<>();
-		discretevalues1.add(I1V1);
-		discretevalues1.add(I1V2);
-		values1 = new DiscreteValueSet(discretevalues1);
-		issues.put(ISSUE1, values1);
-
-		values2 = new NumberValueSet(TWO, THREE, new BigDecimal("0.45"));
-		issues.put(ISSUE2, values2);
-
-		domain = new Domain(DOMAINNAME, issues);
+	/**
+	 * Test if the values are within acceptable range from the goal.
+	 */
+	@Test
+	public void test() {
+		ImmutableList<Bid> list = new BidsWithUtility(profile, accuracy)
+				.getBids(utilityGoal);
+		Random rand = new Random();
+		// check not all but only 10000 random bids as list may be way too large
+		// to test them all. Testing 1 billion bids may take 15 minutes or so on
+		// quad core i7 @2.4GHz.....
+		// also notice that we may be checking only part of the list if
+		// the size of the list would become bigger than maxint.
+		for (int n = 0; n < 10000; n++) {
+			Bid bid = list.get(rand.nextInt(list.size().intValue()));
+			assertTrue(utilityGoal.contains(profile.getUtility(bid)
+					.setScale(accuracy - 1, RoundingMode.HALF_UP)));
+		}
+		assertTrue(
+				list.size().compareTo(BigInteger.valueOf(expectedSize)) >= 0);
 
 	}
 
 	@Test
-	public void smokeTest() {
-		LinearAdditiveUtilitySpace space = mock(LinearAdditiveUtilitySpace.class);
-		when(space.getDomain()).thenReturn(domain);
-		when(space.getUtility(any(Bid.class))).thenReturn(ZERO_2);
-		new BidsWithUtility(space, new BigDecimal("0.1"), new BigDecimal("0.3"));
+	public void testMaxUtil() {
+		BidsWithUtility bidswithutil = new BidsWithUtility(profile);
+		// notice, this is the *rounded* max util
+		BigDecimal maxutil = bidswithutil.getRange().getMax();
+		Interval goal = new Interval(
+				maxutil.subtract(new BigDecimal("0.00001")), maxutil);
+
+		ImmutableList<Bid> bidsnearmax = bidswithutil.getBids(goal);
+		assertTrue(bidsnearmax.size() != BigInteger.ZERO);
+
+		BigDecimal foundmax = BigDecimal.ZERO;
+		for (Bid bid : bidsnearmax) {
+			BigDecimal util = profile.getUtility(bid);
+			if (util.compareTo(foundmax) > 0) {
+				foundmax = util;
+			}
+		}
+		// found maximum may be slightly lower or higher because we rounded
+		// all weighted utilities.
+		assertTrue(Math
+				.abs(foundmax.doubleValue() - maxutil.doubleValue()) < 0.0001);
 	}
 
 	@Test
-	public void testSelection() {
-		LinearAdditiveUtilitySpace space = mock(LinearAdditiveUtilitySpace.class);
+	public void benchmark() {
+		System.out.println("\nBenchmarking " + profile.getName());
+		BigInteger domainsize = new AllBidsList(profile.getDomain()).size();
 
-		when(space.getDomain()).thenReturn(domain);
-		when(space.getUtility(any(Bid.class))).thenReturn(ZERO_2);
-		BidsWithUtility selected = new BidsWithUtility(space, new BigDecimal("0.1"), new BigDecimal("0.3"));
-		assertEquals(new BigInteger("6"), selected.size());
-	}
-
-	@Test
-	public void testRealSpace() throws IOException {
-		String file1 = new String(Files.readAllBytes(Paths.get("src/test/resources/jobs/jobs1.json")));
-		LinearAdditiveUtilitySpace profile = (LinearAdditiveUtilitySpace) jackson.readValue(file1, Profile.class);
-		BidsWithUtility selected = new BidsWithUtility(profile, ZERO_1, ZERO_3);
-
-		assertEquals(new BigInteger("56"), selected.size());
-		for (Bid bid : selected) {
-			assertEquals(1, profile.getUtility(bid).compareTo(ZERO_1));
-			assertEquals(-1, profile.getUtility(bid).compareTo(ZERO_3));
-		}
+		long start = System.currentTimeMillis();
+		BidsWithUtility body = new BidsWithUtility(profile, accuracy);
+		ImmutableList<Bid> list = body.getBids(utilityGoal);
+		long end = System.currentTimeMillis();
+		System.out.println("run time: " + (end - start) / 1000. + "s");
+		System.out.println("Total size of bidspace:" + domainsize);
+		System.out.println("Result size: " + list.size());
 	}
 
Index: bidspace/src/test/java/geniusweb/bidspace/IntervalTest.java
===================================================================
--- bidspace/src/test/java/geniusweb/bidspace/IntervalTest.java	(revision 4)
+++ bidspace/src/test/java/geniusweb/bidspace/IntervalTest.java	(revision 4)
@@ -0,0 +1,118 @@
+package geniusweb.bidspace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import tudelft.utilities.junit.GeneralTests;
+
+public class IntervalTest extends GeneralTests<Interval> {
+
+	private static final BigDecimal ZERO = BigDecimal.ZERO;
+	private static final BigDecimal ONE = BigDecimal.ONE;
+	private static final BigDecimal TWO = BigDecimal.valueOf(2);
+	private static final BigDecimal THREE = BigDecimal.valueOf(3);
+	private static final BigDecimal TEN = BigDecimal.TEN;
+	private static final Interval int1 = new Interval(ZERO, ONE);
+	private static final Interval int1a = new Interval(ZERO, ONE);
+	private static final Interval int2 = new Interval(ONE, TEN);
+	private static final Interval int3 = new Interval(TWO, TEN);
+
+	@Override
+	public List<List<Interval>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(int1, int1a), Arrays.asList(int2),
+				Arrays.asList(int3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList("Interval\\[0,1\\]", "Interval\\[1,10\\]",
+				"Interval\\[2,10\\]");
+	}
+
+	@Test
+	public void smokeTest() {
+		new Interval(BigDecimal.ZERO, BigDecimal.ONE);
+	}
+
+	@Test
+	public void smokeTestBad() {
+		Interval i = new Interval(BigDecimal.TEN, BigDecimal.ONE);
+		assertTrue(i.isEmpty());
+	}
+
+	@Test(expected = NullPointerException.class)
+	public void smokeNull1() {
+		new Interval(null, BigDecimal.ZERO);
+	}
+
+	@Test(expected = NullPointerException.class)
+	public void smokeNull2() {
+		new Interval(BigDecimal.ZERO, null);
+	}
+
+	@Test
+	public void testMin() {
+		assertEquals(BigDecimal.ZERO, int1.getMin());
+		assertEquals(BigDecimal.ONE, int2.getMin());
+	}
+
+	@Test
+	public void testMax() {
+		assertEquals(BigDecimal.ONE, int1.getMax());
+		assertEquals(BigDecimal.TEN, int2.getMax());
+	}
+
+	@Test
+	public void containsTest() {
+		assertTrue(int1.contains(BigDecimal.valueOf(0.3)));
+		assertFalse(int1.contains(BigDecimal.valueOf(-0.3)));
+		assertFalse(int1.contains(BigDecimal.valueOf(1.3)));
+
+		assertFalse(int3.contains(ZERO));
+		assertFalse(int3.contains(ONE));
+		assertTrue(int3.contains(TWO));
+		assertTrue(int3.contains(THREE));
+		assertTrue(int3.contains(TEN));
+		assertFalse(int3.contains(BigDecimal.valueOf(10.3)));
+
+	}
+
+	@Test
+	public void testIntersect() {
+		assertEquals(int1, int1.intersect(int1));
+		assertEquals(new Interval(ONE, ONE), int1.intersect(int2));
+		assertTrue(int1.intersect(int3).isEmpty());
+		assertEquals(new Interval(ONE, TWO),
+				new Interval(ZERO, TEN).intersect(new Interval(ONE, TWO)));
+		assertEquals(new Interval(ONE, TWO),
+				new Interval(ONE, TWO).intersect(new Interval(ZERO, TEN)));
+		assertEquals(new Interval(ONE, TWO),
+				new Interval(ZERO, TWO).intersect(new Interval(ONE, TEN)));
+		assertEquals(new Interval(ONE, TWO),
+				new Interval(ONE, TEN).intersect(new Interval(ZERO, TWO)));
+	}
+
+	@Test
+	public void subtractTest() {
+		assertEquals(new Interval(BigDecimal.valueOf(-1), ZERO),
+				int1.subtract(ONE));
+	}
+
+	@Test
+	public void invertTest() {
+		assertEquals(new Interval(BigDecimal.valueOf(-10), ZERO),
+				int1.invert(int2));
+		assertEquals(new Interval(ZERO, TEN), int2.invert(int1));
+		assertEquals(
+				new Interval(BigDecimal.valueOf(-9), BigDecimal.valueOf(8)),
+				int2.invert(int3));
+	}
+
+}
Index: bidspace/src/test/resources/9issues/9issues1.json
===================================================================
--- bidspace/src/test/resources/9issues/9issues1.json	(revision 4)
+++ bidspace/src/test/resources/9issues/9issues1.json	(revision 4)
@@ -0,0 +1,134 @@
+{
+	"LinearAdditiveUtilitySpace": {
+		"name": "9issues1",
+		"issueUtilities": {
+			"i1": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.11,
+					"highValue": 9,
+					"highUtility": 0.99
+
+				}
+			},
+			"i2": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.12,
+					"highValue": 9,
+					"highUtility": 0.97
+
+				}
+			},
+			"i3": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.13,
+					"highValue": 9,
+					"highUtility": 0.96
+
+				}
+			},
+			"i4": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.14,
+					"highValue": 9,
+					"highUtility": 0.94
+
+				}
+			},
+			"i5": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.15,
+					"highValue": 9,
+					"highUtility": 0.93
+
+				}
+			},
+			"i6": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.16,
+					"highValue": 9,
+					"highUtility": 0.92
+
+				}
+			},
+			"i7": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.16,
+					"highValue": 9,
+					"highUtility": 0.91
+
+				}
+			},
+			"i8": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.7664,
+					"highValue": 9,
+					"highUtility": 0.183
+
+				}
+			},
+
+			"i9": {
+				"numberutils": {
+					"lowValue": 0,
+					"lowUtility": 0.191,
+					"highValue": 9,
+					"highUtility": 0.891
+
+				}
+			}
+
+		},
+		"issueWeights": {
+			"i1": 0.07531677352656704,
+			"i2": 0.18148468268008364,
+			"i3": 0.12035193969082373,
+			"i4": 0.21649985443378972,
+			"i5": 0.15621694966873587,
+			"i6": 0.111253,
+			"i7": 0.0656768,
+			"i8": 0.0201,
+			"i9": 0.0531
+		},
+		"domain": {
+			"name": "9issues",
+			"issuesValues": {
+				"i1": {
+					"range": ["0", "9", "1"]
+				},
+				"i2": {
+					"range": ["0", "9", "1"]
+				},
+				"i3": {
+					"range": ["0", "9", "1"]
+				},
+				"i4": {
+					"range": ["0", "9", "1"]
+				},
+				"i5": {
+					"range": ["0", "9", "1"]
+				},
+				"i6": {
+					"range": ["0", "9", "1"]
+				},
+				"i7": {
+					"range": ["0", "9", "1"]
+				},
+				"i8": {
+					"range": ["0", "9", "1"]
+				},
+				"i9": {
+					"range": ["0", "9", "1"]
+				}
+
+			}
+		}
+	}
+}
