Input:
import java.io.Serializable;
import java.util.Iterator;
import edu.uky.cs.nil.sabre.Utilities;
import edu.uky.cs.nil.sabre.logic.Comparison;
import edu.uky.cs.nil.sabre.logic.Value;
import edu.uky.cs.nil.sabre.util.ArrayIterator;
import edu.uky.cs.nil.sabre.util.Countable;

Context:
src/edu/uky/cs/nil/sabre/hg/CostSet.java
package edu.uky.cs.nil.sabre.hg;

/**
 * A cost set is a collection of {@link Value values} where each is associated
 * with a numeric cost.
 *
 * @author Stephen G. Ware
 */
public interface CostSet extends Iterable<CostSet.Entry>, Serializable, Countable {

	/**
	 * An individual {@link Value value}/cost pair in a {@link CostSet cost
	 * set}. Cost set entries are {@link Comparable#compareTo(Object) naturally
	 * ordered} by their costs.
	 *
	 * @author Stephen G. Ware
	 */
	public static class Entry implements Comparable<Entry> {

		/** The value in the cost set */
		public final Value value;

		/** The cost of the value in the cost set */
		public final double cost;

		/**
		 * Constructs a new cost set entry.
		 *
		 * @param value a value from the cost set
		 * @param cost the cost of the value
		 */
		protected Entry(Value value, double cost) {
			this.value = value;
			this.cost = cost;
		}

		@Override
		public boolean equals(Object other) {
			if(getClass().equals(other.getClass())) {
				Entry otherCost = (Entry) other;
				return Utilities.equals(value, otherCost.value) && cost == otherCost.cost;
			}
			return false;
		}

		@Override
		public int hashCode() {
			return Utilities.hashCode(getClass(), value, cost);
		}

		@Override
		public String toString() {
			return Utilities.DEFAULT_PRINTER.toString(this);
		}

		@Override
		public int compareTo(Entry other) {
			double comparison = cost - other.cost;
			if(comparison == 0)
				return 0;
			else if(comparison < 0)
				return -1;
			else
				return 1;
		}
	}

	@Override
	public default Iterator<Entry> iterator() {
		Entry[] entries = new Entry[size()];
		for(int i=0; i<entries.length; i++)
			entries[i] = new Entry(getValue(i), getCost(i));
		return new ArrayIterator<>(entries);
	}

	/**
	 * Returns the {@link Value value} at a given index in the set. The cost of
	 * value can be accessed via {@link #getCost(int)}.
	 *
	 * @param index the index of the desired value
	 * @return the value at that index
	 */
	public Value getValue(int index);

	/**
	 * Returns the cost of the value at a given index in the set. The value
	 * itself can be accessed via {@link #getValue(int)}.
	 *
	 * @param index the index of the value whose cost is desired
	 * @return the cost of the value at that index
	 */
	public double getCost(int index);

	/**
	 * Returns the cost associated with the given value, which is an estimate
	 * of the number of {@link edu.uky.cs.nil.sabre.Action actions} that need
	 * to be taken from the state a heuristic graph was {@link
	 * HeuristicGraph#initialize(edu.uky.cs.nil.sabre.State) initialized to}
	 * before the {@link edu.uky.cs.nil.sabre.logic.Expression logical
	 * expression} this set represents can have the given value. A cost of 0
	 * means the logical expression has this value from the start. A cost of
	 * {@link Double#POSITIVE_INFINITY positive infinity} means the expression
	 * cannot have this value.
	 *
	 * @param value a value the logical expression this set represents might
	 * have
	 * @return an estimate of the number of actions that would need to be
	 * taken before this set can contain that value
	 */
	public default double getCost(Value value) {
		return getCost(Comparison.EQUAL_TO, value);
	}

	/**
	 * Returns the cost of some value in this set satisfying the given {@link
	 * Comparison.Operator comparison operator} when it is on the left and a
	 * given value is on the right. For example, if this method is called with
	 * the {@link Comparison#GREATER_THAN greater than operator} and the value
	 * 0, it means "what is the cost of this set having a value above 0?"
	 *
	 * @param operator the comparison operator to satisfy
	 * @param value the value on the right hand side of the comparison
	 * @return the cost of some value in this set that satisfies the comparison
	 * when it is on the left hand side of the comparison
	 */

src/edu/uky/cs/nil/sabre/util/ArrayIterator.java
public class ArrayIterator<T> implements Iterator<T> {

	private final Object[] array;
	private final int last;
	private int index;

	/**
	 * Constructs a new array iterator that will iterate over a subset of
	 * elements in an array.
	 *
	 * @param array the array to iterate over
	 * @param first index of the first element to return while iterating
	 * @param last index of the last element to return while iterating
	 */
	public ArrayIterator(T[] array, int first, int last) {
		this.array = array;
		this.index = Math.max(first, 0);
		this.last = Math.min(last, array.length - 1);
	}

	/**
	 * Constructs a new array iterator that will iterate over all elements in
	 * an array.
	 *
	 * @param array the array to iterator over
	 */
	@SuppressWarnings("unchecked")
	public ArrayIterator(T...array) {
		this(array, 0, array.length - 1);
	}

	@Override
	public boolean hasNext() {
		return index <= last;
	}

	@Override
	@SuppressWarnings("unchecked")
	public T next() {
		if(!hasNext())
			throw Exceptions.iteratorOutOfElements();
		return (T) array[index++];
	}
}

src/edu/uky/cs/nil/sabre/util/Countable.java
public interface Countable {

	/**
	 * Returns the number of elements in this collection
	 *
	 * @return the number of elements in this collection
	 */
	public int size();
}

src/edu/uky/cs/nil/sabre/logic/Comparison.java
public class Comparison implements Proposition {

	/** Serial version ID */
	private static final long serialVersionUID = Settings.VERSION_UID;

	/**
	 * An operator is a singleton object that represents one of the possible
	 * binary relationship between the left and right expressions of a {@link
	 * Comparison comparison}.
	 *
	 * @author Stephen G. Ware
	 */
	public static abstract class Operator implements Comparable<Operator>, Serializable, Unique {

		/** Serial version ID */
		private static final long serialVersionUID = Comparison.serialVersionUID;

		private Operator() {}

		@Override
		public int compareTo(Operator other) {
			return hashCode() - other.hashCode();
		}

		/**
		 * Tests whether the relationship represented by this operator holds
		 * between two {@link Value values} which appear on the left and right
		 * of a {@link Comparison comparison}.
		 *
		 * @param left the value on the left of the comparison
		 * @param right the value on the right of the comparison
		 * @return true if the relationship holds, false otherwise
		 */
		public abstract boolean test(Value left, Value right);

		/**
		 * Returns the operator this operator would become when {@link
		 * Comparison#flip() flipping} a comparison in which it appears.
		 *
		 * @return the flipped operator
		 */
		public abstract Operator flip();

		/**
		 * Returns the operator this operator would become when {@link
		 * Comparison#negate() negating} a comparison in which it appears.
		 *
		 * @return the negated operator
		 */
		public abstract Operator negate();

		/**
		 * If a {@link Comparison comparison} using this operator with the
		 * given left and right expressions can be simplified, this method
		 * returns the simplified expression; otherwise it returns null. The
		 * most common case in which a comparison can be simplified is when
		 * its left and right expressions are both {@link Value values}, in
		 * which case this method returns {@link True true} if {@link
		 * #test(Value, Value) this operator's test method} returns true, and
		 * {@link False false} if this operator's test method returns false.
		 *
		 * @param left the left expression of a comparison
		 * @param right the right expression of a comparison
		 * @return a simplified expression or null if the comparison cannot be
		 * simplified
		 */
		public Expression simplify(Expression left, Expression right) {
			if(left instanceof Value && right instanceof Value) {
				if(test((Value) left, (Value) right))
					return True.TRUE;
				else
					return False.FALSE;
			}
			else
				return null;
		}

		/**
		 * If a {@link Precondition} (that is, an {@link Atom atomic} {@link
		 * Comparison comparison}) using this operator can be {@link
		 * Atom#combine(Atom) combined} with a second precondition, this method
		 * returns the resulting precondition, or null if they cannot be
		 * combined. This method assumes the {@link Comparison#left left sides}
		 * of both preconditions are the same. If the operators of the two
		 * preconditions are different, this method should be called twice,
		 * once for the first precondition's operator and once for the second
		 * precondition's operator, and only one of those methods needs to
		 * return the combined precondition if it exists.
		 *
		 * @param self the first precondition
		 * @param other the second precondition with matching left side
		 * @return a combined precondition which represents both, if one
		 * exists, or null if one does not exist
		 */
		protected Precondition combine(Precondition self, Precondition other) {
			if(self.equals(other))
				return self;
			else
				return null;
		}

		/**
		 * Tests whether a {@link Precondition} (that is, an {@link Atom
		 * atomic} {@link Comparison comparison}) using this operator {@link
		 * Atom#negates(Atom) negates} a second precondition. This method
		 * assumes the {@link Comparison#left left sides} of both preconditions
		 * are the same. If the operators of the two preconditions are
		 * different, this method should be called twice, once for the first
		 * precondition's operator and once for the second precondition's
		 * operator, and only one of those methods needs to return true if the
		 * preconditions negate one another.
		 *
		 * @param self the first precondition
		 * @param other the second precondition with matching left side
		 * @return true if the preconditions negate one another, false
		 * otherwise
		 */
		protected boolean negates(Precondition self, Precondition other) {
			return false;
		}
	}

	/**
	 * Represents a {@link Comparison} where the left and right are the same.
	 */
	public static final Operator EQUAL_TO = new Operator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 0;
		}

		@Override
		public String toString() {
			return DefaultParser.EQUAL_TO_KEYWORD;
		}

		/**
		 * Returns true if and only if the left and right values are the same.
		 *
		 * @param left the value on the left of the comparison
		 * @param right the value on the right of the comparison
		 * @return true if the values are the same, false otherwise
		 */
		@Override
		public boolean test(Value left, Value right) {
			return left.equals(right);
		}

		/**
		 * Returns {@link Comparison#EQUAL_TO}.
		 *
		 * @returns {@link Comparison#EQUAL_TO}
		 */
		@Override
		public Operator flip() {
			return EQUAL_TO;
		}

		/**
		 * Returns {@link Comparison#NOT_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#NOT_EQUAL_TO}
		 */
		@Override
		public Operator negate() {
			return NOT_EQUAL_TO;
		}

		/**
		 * If the left and right expressions are {@link Object#equals(Object)
		 * equal to} each other, this method returns {@link True true}. If the
		 * left and right expressions are different {@link Value values}, this
		 * method returns {@link False false}. Otherwise, this method returns
		 * null.
		 *
		 * @param left the left expression of an equal to comparison
		 * @param right the right expression of an equal to comparison
		 * @return true if the left and right must be the same, false if they
		 * cannot be the same, or null if the comparison cannot be simplified
		 */
		@Override
		public Expression simplify(Expression left, Expression right) {
			if(left.equals(right))
				return True.TRUE;
			else
				return super.simplify(left, right);
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other) {
			if(other.operator.equals(NOT_EQUAL_TO) && self.right instanceof Value && other.right instanceof Value && !self.right.equals(other.right))
				return self;
			else if(other.operator.equals(GREATER_THAN_OR_EQUAL_TO) && self.right.equals(other.right))
				return self;
			else if(other.operator.equals(LESS_THAN_OR_EQUAL_TO) && self.right.equals(other.right))
				return self;
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other) {
			if(other.operator.equals(EQUAL_TO))
				return self.right instanceof Value && other.right instanceof Value && !self.right.equals(other.right);
			else if(other.operator.equals(NOT_EQUAL_TO))
				return self.right.equals(other.right);
			else
				return super.negates(self, other);
		}

		private Object readResolve() throws ObjectStreamException {
			return EQUAL_TO;
		}
	};

	/**
	 * Represents a {@link Comparison} where the left and right are not the
	 * same.
	 */
	public static final Operator NOT_EQUAL_TO = new Operator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 1;
		}

		@Override
		public String toString() {
			return DefaultParser.NOT_EQUAL_TO_KEYWORD;
		}

		/**
		 * Returns true if and only if the left and right values are different.
		 *
		 * @param left the value on the left of the comparison
		 * @param right the value on the right of the comparison
		 * @return true if the values are different, false otherwise
		 */
		@Override
		public boolean test(Value left, Value right) {
			return !left.equals(right);
		}

		/**
		 * Returns {@link Comparison#NOT_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#NOT_EQUAL_TO}
		 */
		@Override
		public Operator flip() {
			return NOT_EQUAL_TO;
		}

		/**
		 * Returns {@link Comparison#EQUAL_TO}.
		 *
		 * @returns {@link Comparison#EQUAL_TO}
		 */
		@Override
		public Operator negate() {
			return EQUAL_TO;
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other) {
			if(other.operator.equals(GREATER_THAN_OR_EQUAL_TO) && self.right.equals(other.right))
				return new Precondition(GREATER_THAN, self.left, self.right);
			else if(other.operator.equals(LESS_THAN_OR_EQUAL_TO) && self.right.equals(other.right))
				return new Precondition(LESS_THAN, self.left, self.right);
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other) {
			if(other.operator.equals(EQUAL_TO))
				return self.right.equals(other.right);
			else
				return super.negates(self, other);
		}

		private Object readResolve() throws ObjectStreamException {
			return NOT_EQUAL_TO;
		}
	};

	/**
	 * An numeric operator is an {@link Operator operator} which compares
	 * expressions of type {@code number}.
	 *
	 * @author Stephen G. Ware
	 */
	public static abstract class NumericOperator extends Operator {

		/** Serial version ID */
		private static final long serialVersionUID = Comparison.serialVersionUID;

		private NumericOperator() {}

		@Override
		public boolean test(Value left, Value right) {
			if(left instanceof Number && right instanceof Number)
				return test((Number) left, (Number) right);
			else
				return false;
		}

		/**
		 * Tests whether the relationship represented by this numeric operator
		 * holds between two {@link Number numbers} which appear on the left
		 * and right of a {@link Comparison comparison}.
		 *
		 * @param left the number on the left of the comparison
		 * @param right the number on the right of the comparison
		 * @return true if the relationship holds, false otherwise
		 */
		public abstract boolean test(Number left, Number right);

		@Override
		public Expression simplify(Expression left, Expression right) {
			if(left.equals(Unknown.UNKNOWN) || right.equals(Unknown.UNKNOWN))
				return new Comparison(EQUAL_TO, left, right).simplify();
			else
				return super.simplify(left, right);
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other) {
			if(self.right instanceof Number && other.right instanceof Number)
				return combine(self, other, (Number) self.right, (Number) other.right);
			else
				return super.combine(self, other);
		}

		/**
		 * This convenience method behaves exactly like {@link
		 * Operator#simplify(Expression, Expression)} and makes the same
		 * assumptions, except that it only handles cases where the right sides
		 * of both preconditions are {@link Number numbers}.
		 *
		 * @param self the first precondition
		 * @param other the second precondition with matching left side
		 * @param sr the right side of the first precondition
		 * @param or the right side of the second precondition
		 * @return a combined precondition which represents both, if one
		 * exists, or null if one does not exist
		 */
		protected Precondition combine(Precondition self, Precondition other, Number sr, Number or) {
			return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other) {
			if(self.right instanceof Number && other.right instanceof Number)
				return negates(self, other, (Number) self.right, (Number) other.right);
			else
				return super.negates(self, other);
		}

		/**
		 * This convenience method behaves exactly like {@link
		 * Operator#negates(Precondition, Precondition)} and makes the same
		 * assumptions, except that it only handles cases where the right sides
		 * of both preconditions are {@link Number numbers}.
		 *
		 * @param self the first precondition
		 * @param other the second precondition with matching left side
		 * @param sr the right side of the first precondition
		 * @param or the right side of the second precondition
		 * @return true if the preconditions negate one another, false
		 * otherwise
		 */
		protected boolean negates(Precondition self, Precondition other, Number sr, Number or) {
			return super.negates(self, other);
		}
	}

	/**
	 * Represents a {@link Comparison} where the left is greater than the
	 * right.
	 */
	public static final Operator GREATER_THAN = new NumericOperator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 2;
		}

		@Override
		public String toString() {
			return DefaultParser.GREATER_THAN_KEYWORD;
		}

		/**
		 * Returns true if and only if the left {@link Number number} {@link
		 * Number#isGreaterThan(Number) is greater than} the right.
		 *
		 * @param left the number on the left of the comparison
		 * @param right the number on the right of the comparison
		 * @return true if the left is greater than the right
		 */
		@Override
		public boolean test(Number left, Number right) {
			return left.isGreaterThan(right);
		}

		/**
		 * Returns {@link Comparison#LESS_THAN}.
		 *
		 * @returns {@link Comparison#LESS_THAN}
		 */
		@Override
		public Operator flip() {
			return LESS_THAN;
		}

		/**
		 * Returns {@link Comparison#LESS_THAN_OR_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#LESS_THAN_OR_EQUAL_TO}
		 */
		@Override
		public Operator negate() {
			return LESS_THAN_OR_EQUAL_TO;
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) && sr.isLessThan(or))
				return other;
			else if(other.operator.equals(GREATER_THAN) || other.operator.equals(GREATER_THAN_OR_EQUAL_TO)) {
				if(sr.isLessThan(or))
					return other;
				else
					return self;
			}
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) || other.operator.equals(LESS_THAN) || other.operator.equals(LESS_THAN_OR_EQUAL_TO))
				return sr.isGreaterThanOrEqualTo(or);
			else
				return super.negates(self, other, sr, or);
		}

		private Object readResolve() throws ObjectStreamException {
			return GREATER_THAN;
		}
	};

	/**
	 * Represents a {@link Comparison} where the left is greater than or equal
	 * to the right.
	 */
	public static final Operator GREATER_THAN_OR_EQUAL_TO = new NumericOperator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 3;
		}

		@Override
		public String toString() {
			return DefaultParser.GREATER_THAN_OR_EQUAL_TO_KEYWORD;
		}

		@Override
		public boolean test(Value left, Value right) {
			return EQUAL_TO.test(left, right) || GREATER_THAN.test(left, right);
		}

		/**
		 * Returns true if and only if the left {@link Number number} {@link
		 * Number#isGreaterThanOrEqualTo(Number) is greater than or equal to}
		 * the right.
		 *
		 * @param left the number on the left of the comparison
		 * @param right the number on the right of the comparison
		 * @return true if the left is greater than or equal to the right
		 */
		@Override
		public boolean test(Number left, Number right) {
			return left.isGreaterThanOrEqualTo(right);
		}

		/**
		 * Returns {@link Comparison#LESS_THAN_OR_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#LESS_THAN_OR_EQUAL_TO}
		 */
		@Override
		public Operator flip() {
			return LESS_THAN_OR_EQUAL_TO;
		}

		/**
		 * Returns {@link Comparison#LESS_THAN}.
		 *
		 * @returns {@link Comparison#LESS_THAN}
		 */
		@Override
		public Operator negate() {
			return LESS_THAN;
		}

		@Override
		public Expression simplify(Expression left, Expression right) {
			if(!left.isNumber() || !right.isNumber())
				return new Comparison(Comparison.EQUAL_TO, left, right);
			else
				return super.simplify(left, right);
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) && sr.isLessThanOrEqualTo(or))
				return other;
			else if(other.operator.equals(GREATER_THAN) || other.operator.equals(GREATER_THAN_OR_EQUAL_TO)) {
				if(sr.isLessThanOrEqualTo(or))
					return other;
				else
					return self;
			}
			else if(other.operator.equals(LESS_THAN_OR_EQUAL_TO) && sr.equals(or))
				return new Precondition(EQUAL_TO, self.left, sr);
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) || other.operator.equals(LESS_THAN_OR_EQUAL_TO))
				return sr.isGreaterThan(or);
			else if(other.operator.equals(LESS_THAN))
				return sr.isGreaterThanOrEqualTo(or);
			else
				return super.negates(self, other, sr, or);
		}

		private Object readResolve() throws ObjectStreamException {
			return GREATER_THAN_OR_EQUAL_TO;
		}
	};

	/**
	 * Represents a {@link Comparison} where the left is less than the right.
	 */
	public static final Operator LESS_THAN = new NumericOperator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 4;
		}

		@Override
		public String toString() {
			return DefaultParser.LESS_THAN_KEYWORD;
		}

		/**
		 * Returns true if and only if the left {@link Number number} {@link
		 * Number#isLessThan(Number) is less than} the right.
		 *
		 * @param left the number on the left of the comparison
		 * @param right the number on the right of the comparison
		 * @return true if the left is less than the right
		 */
		@Override
		public boolean test(Number left, Number right) {
			return left.isLessThan(right);
		}

		/**
		 * Returns {@link Comparison#GREATER_THAN}.
		 *
		 * @returns {@link Comparison#GREATER_THAN}
		 */
		@Override
		public Operator flip() {
			return GREATER_THAN;
		}

		/**
		 * Returns {@link Comparison#GREATER_THAN_OR_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#GREATER_THAN_OR_EQUAL_TO}
		 */
		@Override
		public Operator negate() {
			return GREATER_THAN_OR_EQUAL_TO;
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) && sr.isGreaterThan(or))
				return other;
			else if(other.operator.equals(LESS_THAN) || other.operator.equals(LESS_THAN_OR_EQUAL_TO)) {
				if(sr.isGreaterThan(or))
					return other;
				else
					return self;
			}
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) || other.operator.equals(GREATER_THAN) || other.operator.equals(GREATER_THAN_OR_EQUAL_TO))
				return sr.isLessThanOrEqualTo(or);
			else
				return super.negates(self, other, sr, or);
		}

		private Object readResolve() throws ObjectStreamException {
			return LESS_THAN;
		}
	};

	/**
	 * Represents a {@link Comparison} where the left is less than or equal to
	 * the right.
	 */
	public static final Operator LESS_THAN_OR_EQUAL_TO = new NumericOperator() {

		/** Serial version ID */
		private static final long serialVersionUID = 1;

		@Override
		public int hashCode() {
			return 5;
		}

		@Override
		public String toString() {
			return DefaultParser.LESS_THAN_OR_EQUAL_TO_KEYWORD;
		}

		@Override
		public boolean test(Value left, Value right) {
			return EQUAL_TO.test(left, right) || LESS_THAN.test(left, right);
		}

		/**
		 * Returns true if and only if the left {@link Number number} {@link
		 * Number#isLessThanOrEqualTo(Number) is less than or equal to} the
		 * right.
		 *
		 * @param left the number on the left of the comparison
		 * @param right the number on the right of the comparison
		 * @return true if the left is less than or equal to the right
		 */
		@Override
		public boolean test(Number left, Number right) {
			return left.isLessThanOrEqualTo(right);
		}

		/**
		 * Returns {@link Comparison#GREATER_THAN_OR_EQUAL_TO}.
		 *
		 * @returns {@link Comparison#GREATER_THAN_OR_EQUAL_TO}
		 */
		@Override
		public Operator flip() {
			return GREATER_THAN_OR_EQUAL_TO;
		}

		/**
		 * Returns {@link Comparison#GREATER_THAN}.
		 *
		 * @returns {@link Comparison#GREATER_THAN}
		 */
		@Override
		public Operator negate() {
			return GREATER_THAN;
		}

		@Override
		public Expression simplify(Expression left, Expression right) {
			if(!left.isNumber() || !right.isNumber())
				return new Comparison(Comparison.EQUAL_TO, left, right);
			else
				return super.simplify(left, right);
		}

		@Override
		protected Precondition combine(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) && sr.isGreaterThanOrEqualTo(or))
				return other;
			else if(other.operator.equals(LESS_THAN) || other.operator.equals(LESS_THAN_OR_EQUAL_TO)) {
				if(sr.isGreaterThanOrEqualTo(or))
					return other;
				else
					return self;
			}
			else if(other.operator.equals(GREATER_THAN_OR_EQUAL_TO) && sr.equals(or))
				return new Precondition(EQUAL_TO, self.left, sr);
			else
				return super.combine(self, other);
		}

		@Override
		protected boolean negates(Precondition self, Precondition other, Number sr, Number or) {
			if(other.operator.equals(EQUAL_TO) || other.operator.equals(GREATER_THAN_OR_EQUAL_TO))
				return sr.isLessThan(or);
			else if(other.operator.equals(GREATER_THAN))
				return sr.isLessThanOrEqualTo(or);
			else
				return super.negates(self, other, sr, or);
		}

		private Object readResolve() throws ObjectStreamException {
			return LESS_THAN_OR_EQUAL_TO;
		}
	};

	/** The relationship between the left and right sides */
	public final Operator operator;

	/** The left side expression */
	public final Expression left;

	/** The right side expression */
	public final Expression right;

	/**
	 * Constructs a new comparison with a given {@link Operator relationship},
	 * left side, and right side.
	 *
	 * @param operator the relationship between the sides
	 * @param left the left side
	 * @param right the right side
	 */
	public Comparison(Operator operator, Expression left, Expression right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	public boolean equals(Object other) {
		if(getClass().equals(other.getClass())) {
			Comparison otherComparison = (Comparison) other;
			return operator.equals(otherComparison.operator) && left.equals(otherComparison.left) && right.equals(otherComparison.right);
		}
		return false;
	}

	@Override
	public int hashCode() {
		return Utilities.hashCode(getClass(), operator, left, right);
	}

	@Override
	public String toString() {
		return "(" + left + " " + operator + " " + right + ")";
	}

	@Override
	public int compareTo(Logical other) {
		if(getClass().equals(other.getClass())) {
			Comparison otherComparison = (Comparison) other;
			return Utilities.compare(left, otherComparison.left, operator, otherComparison.operator, right, otherComparison.right);
		}
		else
			return Proposition.super.compareTo(other);
	}

	@Override
	public boolean isGround() {
		return left.isGround() && right.isGround();
	}

	@Override
	public Comparison apply(Function<Object, Object> function) {
		Expression left = (Expression) function.apply(this.left);
		Expression right = (Expression) function.apply(this.right);
		if(left != this.left || right != this.right)
			return new Comparison(operator, left, right);
		else
			return this;
	}

	@Override
	public Expression simplify() {
		Comparison simplified = (Comparison) Proposition.super.simplify();
		Expression further = operator.simplify(simplified.left, simplified.right);
		if(further == null)
			return simplified;
		else
			return further;
	}

	/**
	 * Returns {@link True true} if the relationship represented by this
	 * comparison's {@link #operator operator} holds between the left and right
	 * sides, or {@link False false} otherwise.
	 *
	 * @param state the state in which this comparison will be evaluated
	 * @return the Boolean value of this expression
	 */
	@Override
	public Value evaluate(State state) {
		if(operator.test(left.evaluate(state), right.evaluate(state)))
			return True.TRUE;
		else
			return False.FALSE;
	}

	@Override
	public Comparison prepend(Parameter character) {
		return (Comparison) Proposition.super.prepend(character);
	}

	/**
	 * Returns a new comparison with the same left and right sides, but with a
	 * {@link Operator#negate() negated operator}.
	 *
	 * @return a comparison with the opposite value of this comparison
	 */
	@Override
	public Comparison negate() {
		return new Comparison(operator.negate(), left, right);
	}

	@Override
	public Disjunction<Clause<Precondition>> toPrecondition() {
		// Case 1: Two values. Simplify.
		if(left instanceof Value && right instanceof Value)
			return simplify().toPrecondition();
		// Case 2: Numeric. Make constraints on all variables and fluents explicit.
		else if(left.isNumber() && left.isValued() && right.isNumber() && right.isValued()) {
			ArrayList<Expression> constraints = new ArrayList<>();
			for(Fluent fluent : collect(Fluent.class))
				constraints.add(isolate(fluent));
			return new Conjunction<>(constraints).toPrecondition();
		}
		// Case 3: Valid left side and valid right side.
		else if((left instanceof Variable || left instanceof Fluent) && !left.isNumber() && right.isValued()) {
			if(operator.equals(NOT_EQUAL_TO) && right.isBoolean())
				return new Comparison(operator.negate(), left, right.negate()).toPrecondition();
			else
				return new Precondition(operator, left, right).toPrecondition();
		}
		else if((right instanceof Variable || right instanceof Fluent) && !right.isNumber() && left.isValued())
			return flip().toPrecondition();
		// Case 4: Need to factor out something that is not valued.
		else if(!right.isValued()) {
			Conditional<Disjunction<Clause<Precondition>>> valued = right.toValued();
			Expression[] branches = new Expression[valued.branches.size()];
			for(int i=0; i<branches.length; i++)
				branches[i] = new Comparison(operator, left, valued.branches.get(i));
			return new Conditional<>(valued.conditions, new ImmutableArray<>(branches)).toPrecondition();
		}
		// Case 5: Flip
		else
			return flip().toPrecondition();
	}

	@Override
	public Clause<Effect> toEffect() {
		if(left instanceof Fluent && operator.equals(EQUAL_TO))
			return new Assignment((Fluent) left, right).toEffect();
		else if(left instanceof Fluent && left.isBoolean() && operator.equals(NOT_EQUAL_TO))
			return new Assignment((Fluent) left, right.negate()).toEffect();
		else
			return Proposition.super.toEffect();
	}

	/**
	 * Attempts to isolate a given {@link Fluent fluent} which might appear
	 * anywhere in this comparison on the left side of this comparison. If the
	 * fluent is numeric, {@link
	 * Arithmetic#isolate(Fluent, Operator, Expression) some arithmetic may
	 * need to be done to isolate the fluent}, but the comparison returned
	 * should be logically equivalent to this comparison.
	 *
	 * @param query the query which should be isolated on the left side
	 * @return an equivalent comparison with the query on the left side
	 * @throws FormatException if the query cannot be isolated
	 */
	Expression isolate(Fluent query) {
		if(left.equals(query))
			return new Precondition(operator, query, right.simplify());
		else if(right.equals(query))
			return flip().isolate(query);
		else if(left.occurs(query) && left instanceof Arithmetic)
			return ((Arithmetic) left).isolate(query, operator, right);
		else if(right.occurs(query) && right instanceof Arithmetic)
			return flip().isolate(query);
		else
			throw Exceptions.cannotIsolate(query, this);
	}

	/**
	 * Returns a new comparison where the left is now on the right, the right
	 * is now on the left, and the operator has been {@link Operator#flip()
	 * flipped}.
	 *
	 * @return an equivalent comparison, but with its sides flipped
	 */
	public Comparison flip() {
		return new Comparison(operator.flip(), right, left);
	}
}

src/edu/uky/cs/nil/sabre/Utilities.java
public class Utilities {

	/**
	 * A default {@link edu.uky.cs.nil.sabre.io.Printer printer} generally used
	 * for converting objects to strings
	 */
	public static final DefaultPrinter DEFAULT_PRINTER = new DefaultPrinter();

	/**
	 * Checks whether two objects are {@link Object#equals(Object) equal to}
	 * one another without causing a {@link NullPointerException} if one or
	 * both of them is null.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @return true if they are equal or both null, false otherwise
	 */
	public static final boolean equals(Object o1, Object o2) {
		if(o1 == null)
			return o2 == null;
		else if(o2 == null)
			return false;
		else
			return o1.equals(o2);
	}

	/**
	 * Returns an object's {@link Object#hashCode() hash code} or 0 if the
	 * object is null.
	 *
	 * @param object the object or null
	 * @return the object's hash code or 0 if the object was null
	 */
	public static final int hashCode(Object object) {
		if(object == null)
			return 0;
		else
			return object.hashCode();
	}

	/**
	 * Combines two {@link #hashCode(Object) hash code} values.
	 *
	 * @param hc1 the first hash code
	 * @param hc2 the second hash code
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc1, int hc2) {
		return hc1 * 31 + hc2;
	}

	/**
	 * Combines a {@link #hashCode(Object) hash code} value with the {@link
	 * #hashCode(Object) hash code} of an object.
	 *
	 * @param hc the hash code value
	 * @param o1 the object
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc, Object o1) {
		return hashCode(hc, hashCode(o1));
	}

	/**
	 * Combines the {@link #hashCode(Object) hash code} values of two objects.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @return the combined hash code
	 */
	public static final int hashCode(Object o1, Object o2) {
		return hashCode(hashCode(o1), hashCode(o2));
	}

	/**
	 * Combines a {@link #hashCode(Object) hash code} value with the {@link
	 * #hashCode(Object) hash code} of two objects.
	 *
	 * @param hc the hash code value
	 * @param o1 the first object
	 * @param o2 the second object
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc, Object o1, Object o2) {
		return hashCode(hashCode(hc, o1), hashCode(o2));
	}

	/**
	 * Combines the {@link #hashCode(Object) hash code} values of three
	 * objects.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @return the combined hash code
	 */
	public static final int hashCode(Object o1, Object o2, Object o3) {
		return hashCode(hashCode(o1, o2), hashCode(o3));
	}

	/**
	 * Combines a {@link #hashCode(Object) hash code} value with the {@link
	 * #hashCode(Object) hash code} of three objects.
	 *
	 * @param hc the hash code value
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc, Object o1, Object o2, Object o3) {
		return hashCode(hashCode(hc, o1, o2), hashCode(o3));
	}

	/**
	 * Combines the {@link #hashCode(Object) hash code} values of four objects.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @param o4 the fourth object
	 * @return the combined hash code
	 */
	public static final int hashCode(Object o1, Object o2, Object o3, Object o4) {
		return hashCode(hashCode(o1, o2, o3), hashCode(o4));
	}

	/**
	 * Combines a {@link #hashCode(Object) hash code} value with the {@link
	 * #hashCode(Object) hash code} of four objects.
	 *
	 * @param hc the hash code value
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @param o4 the fourth object
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc, Object o1, Object o2, Object o3, Object o4) {
		return hashCode(hashCode(hc, o1, o2, o3), hashCode(o4));
	}

	/**
	 * Combines the {@link #hashCode(Object) hash code} values of five objects.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @param o4 the fourth object
	 * @param o5 the fifth object
	 * @return the combined hash code
	 */
	public static final int hashCode(Object o1, Object o2, Object o3, Object o4, Object o5) {
		return hashCode(hashCode(o1, o2, o3, o4), hashCode(o5));
	}

	/**
	 * Combines a {@link #hashCode(Object) hash code} value with the {@link
	 * #hashCode(Object) hash code} of five objects.
	 *
	 * @param hc the hash code value
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @param o4 the fourth object
	 * @param o5 the fifth object
	 * @return the combined hash code
	 */
	public static final int hashCode(int hc, Object o1, Object o2, Object o3, Object o4, Object o5) {
		return hashCode(hashCode(hc, o1, o2, o3, o4), hashCode(o5));
	}

	/**
	 * Combines the {@link #hashCode(Object) hash code} values of six objects.
	 *
	 * @param o1 the first object
	 * @param o2 the second object
	 * @param o3 the third object
	 * @param o4 the fourth object
	 * @param o5 the fifth object
	 * @param o6 the sixth object
	 * @return the combined hash code
	 */
	public static final int hashCode(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) {
		return hashCode(hashCode(o1, o2, o3, o4, o5), hashCode(o6));
	}

	/**
	 * Rounds a {@code double} up to the nearest whole number.
	 *
	 * @param value the value to round up
	 * @return the nearest whole number that is greater than or equal to the
	 * value
	 */
	public static final double roundUp(double value) {
		return new BigDecimal(value).setScale(0, RoundingMode.UP).doubleValue();
	}

	/**
	 * Compares a pair of {@link Comparable comparable} objects.
	 *
	 * @param <C1> the type of the objects
	 * @param o1 the first object
	 * @param o2 the second object
	 * @return a negative integer, zero, or a positive integer as the first
	 * object is less than, equal to, or greater than the second object
	 */
	public static final <C1 extends Comparable<C1>> int compare(C1 o1, C1 o2) {
		return o1.compareTo(o2);
	}

	/**
	 * Compares two pairs of {@link Comparable comparable} objects. The first
	 * pair (that is, the first two arguments) is compared. If the result is
	 * not 0, it is returned, otherwise the second pair (that is, the third and
	 * fourth arguments) is compared, and so on.
	 *
	 * @param <C1> the type of the first pair of objects
	 * @param <C2> the type of the second pair of objects
	 * @param o1 the first object in the first pair
	 * @param o2 the second object in the first pair
	 * @param o3 the first object in the second pair
	 * @param o4 the second object in the second pair
	 * @return a negative integer, zero, or a positive integer as the earlier
	 * pairs are less than, equal to, or greater than the later pairs
	 */
	public static final <C1 extends Comparable<C1>, C2 extends Comparable<C2>> int compare(C1 o1, C1 o2, C2 o3, C2 o4) {
		int comparison = compare(o1, o2);
		if(comparison == 0)
			comparison = compare(o3, o4);
		return comparison;
	}

	/**
	 * Compares three pairs of {@link Comparable comparable} objects. The first
	 * pair (that is, the first two arguments) is compared. If the result is not
	 * 0, it is returned, otherwise the second pair (that is, the third and
	 * fourth arguments) is compared, and so on.
	 *
	 * @param <C1> the type of the first pair of objects
	 * @param <C2> the type of the second pair of objects
	 * @param <C3> the type of the third pair of objects
	 * @param o1 the first object in the first pair
	 * @param o2 the second object in the first pair
	 * @param o3 the first object in the second pair
	 * @param o4 the second object in the second pair
	 * @param o5 the first object in the third pair
	 * @param o6 the second object in the third pair
	 * @return a negative integer, zero, or a positive integer as the earlier
	 * pairs are less than, equal to, or greater than the later pairs
	 */
	public static final <C1 extends Comparable<C1>, C2 extends Comparable<C2>, C3 extends Comparable<C3>> int compare(C1 o1, C1 o2, C2 o3, C2 o4, C3 o5, C3 o6) {
		int comparison = compare(o1, o2, o3, o4);
		if(comparison == 0)
			comparison = compare(o5, o6);
		return comparison;
	}

	/**
	 * Compares four pairs of {@link Comparable comparable} objects. The first
	 * pair (that is, the first two arguments) is compared. If the result is
	 * not 0, it is returned, otherwise the second pair (that is, the third and
	 * fourth arguments) is compared, and so on.
	 *
	 * @param <C1> the type of the first pair of objects
	 * @param <C2> the type of the second pair of objects
	 * @param <C3> the type of the third pair of objects
	 * @param <C4> the type of the fourth pair of objects
	 * @param o1 the first object in the first pair
	 * @param o2 the second object in the first pair
	 * @param o3 the first object in the second pair
	 * @param o4 the second object in the second pair
	 * @param o5 the first object in the third pair
	 * @param o6 the second object in the third pair
	 * @param o7 the first object in the fourth pair
	 * @param o8 the second object in the fourth pair
	 * @return a negative integer, zero, or a positive integer as the earlier
	 * pairs are less than, equal to, or greater than the later pairs
	 */
	public static final <C1 extends Comparable<C1>, C2 extends Comparable<C2>, C3 extends Comparable<C3>, C4 extends Comparable<C4>> int compare(C1 o1, C1 o2, C2 o3, C2 o4, C3 o5, C3 o6, C4 o7, C4 o8) {
		int comparison = compare(o1, o2, o3, o4, o5, o6);
		if(comparison == 0)
			comparison = compare(o7, o8);
		return comparison;
	}

	/**
	 * Compares two {@link ImmutableArray arrays} of {@link Logical logical}
	 * objects. The first elements of both arrays are compared. If the result
	 * is not 0, it is returned, otherwise the second elements of both arrays
	 * are compared, and so on.
	 *
	 * @param array1 the first array
	 * @param array2 the second array
	 * @return a negative integer, zero, or a positive integer as the earlier
	 * elements are less than, equal to, or greater than the later elements
	 */
	public static final int compare(ImmutableArray<? extends Logical> array1, ImmutableArray<? extends Logical> array2) {
		int comparison = 0;
		int size = Math.min(array1.size(), array2.size());
		for(int i=0; i<size && comparison==0; i++)
			comparison = array1.get(i).compareTo(array2.get(i));
		if(comparison == 0) {
			if(array1.size() > array2.size())
				return 1;
			else if(array2.size() > array1.size())
				return -1;
		}
		return comparison;
	}

	/**
	 * Iterates through all elements of an {@link Iterable iterable} and places
	 * them into an array of a given type.
	 *
	 * @param <T> the component type of the array
	 * @param iterable the iterable
	 * @param type the class of the component type of the array
	 * @return an array containing all the elements in the iterable
	 */
	public static final <T> T[] toArray(Iterable<? extends T> iterable, Class<T> type) {
		return toArray(iterable.iterator(), 0, type);
	}

	@SuppressWarnings("unchecked")
	private static final <T> T[] toArray(Iterator<? extends T> iterator, int index, Class<T> type) {
		if(iterator.hasNext()) {
			T element = iterator.next();
			T[] array = toArray(iterator, index + 1, type);
			array[index] = element;
			return array;
		}
		else
			return (T[]) Array.newInstance(type, index);
	}

	/**
	 * Iterates through all elements of an {@link Iterable iterable} and places
	 * into a {@link ImmutableSet set} only those items which are of a given
	 * type.
	 *
	 * @param <T> the type of elements to collect
	 * @param type the class object of the type
	 * @param iterable the iterable to iterate through
	 * @return a set of elements from the iterable of the given type
	 */
	@SuppressWarnings("unchecked")
	public static final <T> ImmutableSet<T> collect(Class<T> type, Iterable<?> iterable) {
		LinkedHashSet<T> set = new LinkedHashSet<>();
		for(Object element : iterable)
			if(type.isAssignableFrom(element.getClass()))
				set.add((T) element);
		return new ImmutableSet<>(set);
	}

	/**
	 * Wrap a string in parentheses, unless it is already so wrapped.
	 *
	 * @param string the second
	 * @return the string, wrapped in parentheses
	 */
	public static final String wrap(String string) {
		if(string.startsWith("(") && string.endsWith(")"))
			return string;
		else
			return "(" + string + ")";
	}

	/**
	 * Converts a number of milliseconds into a short string representation
	 * that gives the numbers of days, hours, minutes, seconds, and
	 * milliseconds. For example, {@code "2d1h3m45s20ms"} represents two days,
	 * 1 hour, 3 minutes, fourty-five seconds, and twenty milliseconds.
	 *
	 * @param ms the number of milliseconds
	 * @return a string representation of that amount of time
	 */
	public static final String time(long ms) {
		long days = TimeUnit.MILLISECONDS.toDays(ms);
		ms -= TimeUnit.DAYS.toMillis(days);
		long hours = TimeUnit.MILLISECONDS.toHours(ms);
		ms -= TimeUnit.HOURS.toMillis(hours);
		long minutes = TimeUnit.MILLISECONDS.toMinutes(ms);
		ms -= TimeUnit.MINUTES.toMillis(minutes);
		long seconds = TimeUnit.MILLISECONDS.toSeconds(ms);
		ms -= TimeUnit.SECONDS.toMillis(seconds);
		String string = "";
		if(days > 0)
			string += days + "d";
		if(hours > 0)
			string += hours + "h";
		if(minutes > 0)
			string += minutes + "m";
		if(seconds > 0)
			string += seconds + "s";
		if(ms > 0)
			string += ms + "ms";
		if(string.isEmpty())
			return "0ms";
		return string;
	}
}

Answer: public double getCost(Comparison.Operator operator, Value value);

<think>
The comment just before the placeholder describes a method that returns the cost of a value satisfying a given comparison operator. In the CostSet interface, after this comment the next line should be the method declaration matching the description. Therefore the appropriate next line is the method signature for getCost with an operator and a value.
</think>
public double getCost(Comparison.Operator operator, Value value);