CommonInterval.java

/*******************************************************************************
 * Copyright (c) 2013 Stephen F. Siegel, University of Delaware.
 * 
 * This file is part of SARL.
 * 
 * SARL is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * SARL is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with SARL. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package edu.udel.cis.vsl.sarl.number.real;

import java.util.Objects;

import edu.udel.cis.vsl.sarl.IF.number.IntegerNumber;
import edu.udel.cis.vsl.sarl.IF.number.Interval;
import edu.udel.cis.vsl.sarl.IF.number.Number;
import edu.udel.cis.vsl.sarl.IF.number.RationalNumber;

/**
 * Immutable implementation of {@link Interval}.
 */
public class CommonInterval implements Interval {
	// Private Fields ...

	/**
	 * The boolean variable indicating the type of <code>this</code> interval.
	 * (int/real).
	 */
	private boolean isIntegral;

	/**
	 * The lower bound of <code>this</code> interval.
	 */
	private Number lower;

	/**
	 * The lower strict of <code>this</code> interval.
	 */
	private boolean strictLower;

	/**
	 * The upper bound of <code>this</code> interval.
	 */
	private Number upper;

	/**
	 * The upper strict of <code>this</code> interval.
	 */
	private boolean strictUpper;

	// Constructors ...
	/**
	 * <p>
	 * Construct a new instance of {@link CommonInterval}, which is the general
	 * implementation of {@link Interval}.
	 * </p>
	 * 
	 * <strong>Preconditions</strong>:
	 * <ul>
	 * <li>1. Both <code>upper</code> and <code>lower</code> must be non-null
	 * instance.</li>
	 * <li>2. If the <code>isIntegral</code> is <code>true</code>, then both
	 * <code>upper</code> and <code>lower</code> must be instances of
	 * {@link IntegerNumber}; else they have to be instances of
	 * {@link RationalNumber}.</li>
	 * <li>3. If <code>this</code> is a non-empty integer interval, the strict
	 * of it must be <code>false</code> when the corresponding bound is finite.
	 * </li>
	 * <li>4. If <code>this</code> is an empty interval, which is represented as
	 * (0,0), then both its bounds should be <code>true</code>; or if
	 * <code>this</code> interval's bound is infinite, the corresponding strict
	 * must be <code>true</code></li>
	 * <li>5. The <code>lower</code> bound must be less than or equal to the
	 * <code>upper</code> bound. (1) Thus, if the lower bound is infinite, it
	 * must be the negative infinity, and if the upper bound is infinite, it
	 * must be the positive infinity; (2) if both bounds are the same number and
	 * <code>this</code> interval is not empty, both strict must be
	 * <code>false</code>; (3) if both bounds are zero and both stricts are
	 * <code>true</code>, then <code>this</code> interval is an empty interval
	 * </li>
	 * </ul>
	 * 
	 * @param isIntegral
	 *            the type (int/real) of <code>this</code> interval.
	 * @param lower
	 *            the lower bound of <code>this</code> interval.
	 * @param strictLower
	 *            the lower strict of <code>this</code> interval.
	 * @param upper
	 *            the upper bound of <code>this</code> interval.
	 * @param strictUpper
	 *            the upper strict of <code>this</code> interval.
	 */
	public CommonInterval(boolean isIntegral, Number lower, boolean strictLower,
			Number upper, boolean strictUpper) {
		// Precondition 1
		assert lower != null && upper != null;
		// Precondition 2
		assert isIntegral || ((lower instanceof RationalNumber)
				&& (upper instanceof RationalNumber));
		assert !isIntegral || ((lower instanceof IntegerNumber)
				&& (upper instanceof IntegerNumber));
		// Precondition 3
		assert !isIntegral || lower.isZero()
				|| (lower.isInfinite() == strictLower);
		assert !isIntegral || upper.isZero()
				|| (upper.isInfinite() == strictUpper);
		// Precondition 4
		assert !isIntegral || !strictLower || lower.isInfinite()
				|| (lower.isZero() && upper.isZero());
		assert !isIntegral || !strictUpper || upper.isInfinite()
				|| (lower.isZero() && upper.isZero());
		assert (!lower.isInfinite() || strictLower)
				&& (!upper.isInfinite() || strictUpper);

		int compare;

		// <a,b> with a>b is unacceptable
		// (0,0) is fine: the unique representation of the empty set
		// [a,a] is fine, but not (a,a), [a,a), or (a,a]
		assert (compare = lower.numericalCompareTo(upper)) < 0
				|| (compare == 0 && ((!strictLower && !strictUpper)
						|| (lower.isZero() && strictLower && strictUpper)));
		this.isIntegral = isIntegral;
		this.lower = lower;
		this.strictLower = strictLower;
		this.upper = upper;
		this.strictUpper = strictUpper;
	}

	// Methods specified by interface "Object"

	@Override
	public CommonInterval clone() {
		return new CommonInterval(isIntegral, lower, strictLower, upper,
				strictUpper);
	}

	@Override
	public boolean equals(Object object) {
		if (object instanceof CommonInterval) {
			CommonInterval that = (CommonInterval) object;

			return isIntegral == that.isIntegral
					&& strictLower == that.strictLower
					&& strictUpper == that.strictUpper
					&& upper.equals(that.upper) && lower.equals(that.lower);
		} else
			return false;
	}

	@Override
	public int hashCode() {
		return Objects.hash(isIntegral, strictLower, strictUpper, lower, upper);
	}

	@Override
	public String toString() {
		String result;

		result = strictLower ? "(" : "[";
		result += lower.isInfinite() ? "-infty" : lower.toString();
		result += ",";
		result += upper.isInfinite() ? "+infty" : upper.toString();
		result += strictUpper ? ")" : "]";
		return result;
	}

	// Methods specified by interface "Interval"

	@Override
	public Number lower() {
		return lower;
	}

	@Override
	public Number upper() {
		return upper;
	}

	@Override
	public boolean strictLower() {
		return strictLower;
	}

	@Override
	public boolean strictUpper() {
		return strictUpper;
	}

	@Override
	public boolean isIntegral() {
		return isIntegral;
	}

	@Override
	public boolean isEmpty() {
		return strictLower && strictUpper && lower.isZero() && upper.isZero();
	}

	@Override
	public boolean isUniversal() {
		return lower.isInfinite() && upper.isInfinite();
	}

	@Override
	public boolean contains(Number number) {
		if (!lower.isInfinite()) {
			int compare = lower.numericalCompareTo(number);

			if (compare > 0 || (compare == 0 && strictLower))
				return false;
		}
		if (!upper.isInfinite()) {
			int compare = upper.numericalCompareTo(number);

			if (compare < 0 || (compare == 0 && strictUpper))
				return false;
		}
		return true;
	}

	@Override
	public int compare(Number number) {
		if (!lower.isInfinite()) {
			int compare = lower.numericalCompareTo(number);

			if (compare > 0 || (compare == 0 && strictLower))
				return 1;
		}
		if (!upper.isInfinite()) {
			int compare = upper.numericalCompareTo(number);

			if (compare < 0 || (compare == 0 && strictUpper))
				return -1;
		}
		return 0;
	}

	@Override
	public boolean isZero() {
		return lower != null && upper != null && lower.isZero()
				&& upper.isZero() && !strictLower;
	}
}