package org.grouplens.lenskit.data.pref;

import org.grouplens.lenskit.params.MaxRating;
import org.grouplens.lenskit.params.MinRating;

import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * An object describing the domain of preference data, used in ratings and predictions.
 *
 * @review Should this be called RatingDomain?
 * @todo Integrate this with {@link MinRating} and {@link MaxRating}.
 * @author Michael Ekstrand
 */
public class PreferenceDomain {
    private final double minimum;
    private final double maximum;
    private final double precision;

    /**
     * Create a discrete bounded preference domain.
     * @param min The minimum preference value.
     * @param max The maximum preference value.
     * @param prec The preference precision.
     */
    public PreferenceDomain(double min, double max, double prec) {
        minimum = min;
        maximum = max;
        precision = prec;
    }

    /**
     * Create a continuous bounded preference domain.
     * @param min The minimum preference value.
     * @param max The maximum preference value.
     */
    public PreferenceDomain(double min, double max) {
        this(min, max, Double.NaN);
    }

    /**
     * The minimum preference value.
     * @return The minimum preference value.
     */
    public double getMinimum() {
        return minimum;
    }

    /**
     * The maximum preference value.
     * @return The maximum preference value.
     */
    public double getMaximum() {
        return maximum;
    }

    /**
     * The precision of preference values. This is the precision with which data is
     * collected from the user — in a 1-5, half-star rating system, it will be 0.5.
     * @return The preference precision, or {@code null} if preferences should
     * be considered continuous.
     * @review Is null the correct return? Should we add hasPrecision()?
     */
    public Double getPrecision() {
        if (Double.isNaN(precision)) {
            return null;
        } else {
            return precision;
        }
    }

    @Override
    public String toString() {
        String str = String.format("[%f,%f]", minimum, maximum);
        if (!Double.isNaN(precision)) {
            str += String.format("/%f", precision);
        }
        return str;
    }

    private static Pattern specRE =
            Pattern.compile("\\s*\\[\\s*((?:\\d*\\.)?\\d+)\\s*,\\s*((?:\\d*\\.)?\\d+)\\s*\\]\\s*(?:/\\s*((?:\\d*\\.)?\\d+))?\\s*");

    /**
     * Parse a preference domain from a string specification.
     * <p/>
     * Continuous preference domains are specified as {@code [min,max]}; discrete domains
     * as {@code min:max[/prec/}.  For example, a 0.5-5.0 half-star rating scale is represented
     * as {@code [0.5,5.0]/0.5}.
     * @param spec The string specifying the preference domain.
     * @return The preference domain represented by {@code spec}.
     * @throws IllegalArgumentException if {@code spec} is not a valid domain specification.
     */
    public static @Nonnull PreferenceDomain fromString(@Nonnull String spec) {
        Matcher m = specRE.matcher(spec);
        if (!m.matches()) {
            throw new IllegalArgumentException("invalid domain specification");
        }
        double min = Double.parseDouble(m.group(1));
        double max = Double.parseDouble(m.group(2));
        String precs = m.group(3);
        if (precs != null) {
            double prec = Double.parseDouble(precs);
            return new PreferenceDomain(min, max, prec);
        } else {
            return new PreferenceDomain(min, max);
        }
    }
}

@@ -1543,32 +1543,329 @@
 maximum;%0A    %7D%0A%0A
+    /**%0A     * Query whether this preference domain has a precision.%0A     * @return %7B@code true%7D if the domain has a precision for discrete rating values, %7B@code false%7D%0A     *         if it is continuous.%0A     */%0A    public boolean hasPrecision() %7B%0A        return !Double.isNaN(precision);%0A    %7D%0A%0A
     /**%0A     * T
@@ -2067,138 +2067,103 @@
 sion
-, or %7B@code null%7D if preferences should%0A     * be considered continuous.%0A     * @review Is null the correct return? Should we add 
+; the return value is undefined if the preference%0A     * domain has no precision.%0A     * @see #
 hasP
@@ -2172,17 +2172,16 @@
 cision()
-?
 %0A     */
@@ -2192,17 +2192,17 @@
  public 
-D
+d
 ouble ge
@@ -2220,93 +2220,8 @@
 ) %7B%0A
-        if (Double.isNaN(precision)) %7B%0A            return null;%0A        %7D else %7B%0A    
     
@@ -2242,26 +2242,16 @@
 cision;%0A
-        %7D%0A
     %7D%0A%0A 

Add hasDomain() method