package geniusweb.bidspace; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; 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. *

* immutable */ public class IssueInfo { private final @NonNull String name; private final @NonNull ValueSet values; private final @NonNull Interval interval; private final @NonNull Map<@NonNull Value, @NonNull BigDecimal> weightedUtils; /** * * @param name the issue name * @param values the {@link ValueSet} of the issue * @param utils the {@link ValueSetUtilities} of the issue profile. * FIXME is it required that all values have a utility? * @param weight the weight of the {@link ValueSetUtilities} * @param precision the precision to compute with. Basically the number of * decimal places used for the computations. * */ public IssueInfo(@NonNull String name, @NonNull ValueSet values, @NonNull ValueSetUtilities utils, @NonNull BigDecimal weight, int precision) { if (name == null || values == null || utils == null || weight == null) throw new NullPointerException( "name values utils and weight must be not null."); if (values.size() == BigInteger.ZERO) throw new IllegalArgumentException("Values must not be empty"); this.name = name; this.values = values; this.weightedUtils = computeWeightedUtils(utils, weight, precision); this.interval = getRange(); } public @NonNull ValueSet getValues() { return values; } public @NonNull String getName() { return name; } /** * * @return weighted minimum and maximum utility achievable with this issue, * rounded to the requested precision. */ public @NonNull Interval getInterval() { return interval; } /** * * @param isMax if true the max {@link Value} is returned, else the min is * returned. * @return the extreme value, either the minimum if isMax=false or maximum * if isMax=true. * @throws IllegalStateException if there are no values */ public @NonNull Value getExtreme(boolean isMax) { BigDecimal extremeutil = null; Value extremeval = null; for (final @NonNull Value val : values) { final @NonNull BigDecimal util = weightedUtils.get(val); if (extremeval == null) { extremeutil = weightedUtils.get(val); extremeval = val; } else { if (isMax) { if (util.compareTo(extremeutil) > 0) { extremeutil = util; extremeval = val; } } else { if (util.compareTo(extremeutil) < 0) { extremeutil = util; extremeval = val; } } } } if (extremeval == null) throw new IllegalStateException( "Extreme does not exist as IssueInfo values is empty"); return extremeval; } /** * @param val the issue value to be evaluated * @return weighted utility of given value, rounded to nearest value with * the requested precision number of digits. returns 0 if value is * unknown. */ public @NonNull BigDecimal getWeightedUtil(@NonNull Value val) { BigDecimal util = weightedUtils.get(val); return util == null ? BigDecimal.ZERO : util; } /** * * @param interval an {@link Interval} of utility values. * @return all values that are inside the interval. */ protected @NonNull List<@NonNull Value> subset(@NonNull Interval interval) { final @NonNull List<@NonNull Value> selection = new ArrayList<>(); for (final @NonNull 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(@NonNull Interval interval) { int n = 0; for (final @NonNull Value value : values) if (interval.contains(getWeightedUtil(value))) n = n + 1; 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 @NonNull Interval getRange() { @NonNull BigDecimal min = BigDecimal.ONE; @NonNull BigDecimal max = BigDecimal.ZERO; for (final @NonNull Value value : values) { final @NonNull BigDecimal util = getWeightedUtil(value); if (util.compareTo(min) < 0) min = util; if (util.compareTo(max) > 0) max = util; } return new Interval(min, max); } private @NonNull Map<@NonNull Value, @NonNull BigDecimal> computeWeightedUtils( @NonNull ValueSetUtilities utilities, @NonNull BigDecimal w, int prec) { final @NonNull Map<@NonNull Value, @NonNull BigDecimal> map = new HashMap<>(); for (final @NonNull Value val : values) { map.put(val, utilities.getUtility(val).multiply(w).setScale(prec, RoundingMode.HALF_UP)); } return map; } }