/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT 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.
HermiT 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 HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.datatypes.owlreal;
import java.util.Collection;
public class NumberInterval {
protected final NumberRange m_baseRange;
protected final NumberRange m_excludedRange;
protected final Number m_lowerBound;
protected final BoundType m_lowerBoundType;
protected final Number m_upperBound;
protected final BoundType m_upperBoundType;
public NumberInterval(NumberRange baseRange,NumberRange excludedRange,Number lowerBound,BoundType lowerBoundType,Number upperBound,BoundType upperBoundType) {
assert !isIntervalEmpty(baseRange,excludedRange,lowerBound,lowerBoundType,upperBound,upperBoundType);
m_baseRange=baseRange;
m_excludedRange=excludedRange;
if (m_baseRange==NumberRange.INTEGER) {
// For efficiency, adjust the end-points so that they fit into the INTEGER range.
if (MinusInfinity.INSTANCE.equals(lowerBound)) {
m_lowerBound=lowerBound;
m_lowerBoundType=lowerBoundType;
}
else {
m_lowerBound=Numbers.getNearestIntegerInBound(lowerBound,Numbers.BoundaryDirection.LOWER,lowerBoundType==BoundType.INCLUSIVE);
m_lowerBoundType=BoundType.INCLUSIVE;
}
if (PlusInfinity.INSTANCE.equals(upperBound)) {
m_upperBound=upperBound;
m_upperBoundType=upperBoundType;
}
else {
m_upperBound=Numbers.getNearestIntegerInBound(upperBound,Numbers.BoundaryDirection.UPPER,upperBoundType==BoundType.INCLUSIVE);
m_upperBoundType=BoundType.INCLUSIVE;
}
}
else {
m_lowerBound=lowerBound;
m_lowerBoundType=lowerBoundType;
m_upperBound=upperBound;
m_upperBoundType=upperBoundType;
}
}
/**
* Computes the intersection of this interval with the supplied one. If the two intervals
* do not intersect, the result is null.
*/
public NumberInterval intersectWith(NumberInterval that) {
NumberRange newBaseRange=NumberRange.intersection(m_baseRange,that.m_baseRange);
NumberRange newExcludedRange=NumberRange.union(m_excludedRange,that.m_excludedRange);
if (NumberRange.isSubsetOf(newBaseRange,newExcludedRange))
return null;
Number newLowerBound;
BoundType newLowerBoundType;
int lowerBoundComparison=Numbers.compare(m_lowerBound,that.m_lowerBound);
if (lowerBoundComparison<0) {
newLowerBound=that.m_lowerBound;
newLowerBoundType=that.m_lowerBoundType;
}
else if (lowerBoundComparison>0) {
newLowerBound=m_lowerBound;
newLowerBoundType=m_lowerBoundType;
}
else {
newLowerBound=m_lowerBound;
newLowerBoundType=BoundType.getMoreRestrictive(m_lowerBoundType,that.m_lowerBoundType);
}
Number newUpperBound;
BoundType newUpperBoundType;
int upperBoundComparison=Numbers.compare(m_upperBound,that.m_upperBound);
if (upperBoundComparison<0) {
newUpperBound=m_upperBound;
newUpperBoundType=m_upperBoundType;
}
else if (upperBoundComparison>0) {
newUpperBound=that.m_upperBound;
newUpperBoundType=that.m_upperBoundType;
}
else {
newUpperBound=m_upperBound;
newUpperBoundType=BoundType.getMoreRestrictive(m_upperBoundType,that.m_upperBoundType);
}
if (isIntervalEmpty(newBaseRange,newExcludedRange,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return null;
// The following lines ensure that we don't create a new interval object unless there is need to.
// WARNING: The static initializer in OWLRealDatatypeHandler depends on this!
if (isEqual(newBaseRange,newExcludedRange,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return this;
else if (that.isEqual(newBaseRange,newExcludedRange,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return that;
else
return new NumberInterval(newBaseRange,newExcludedRange,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType);
}
protected boolean isEqual(NumberRange baseRange,NumberRange excludedRange,Number lowerBound,BoundType lowerBoundType,Number upperBound,BoundType upperBoundType) {
return m_baseRange.equals(baseRange) && m_excludedRange.equals(excludedRange) && m_lowerBound.equals(lowerBound) && m_lowerBoundType.equals(lowerBoundType) && m_upperBound.equals(upperBound) && m_upperBoundType.equals(upperBoundType);
}
/**
* Subtracts from the given argument the number of distinct objects that are contained in this interval.
* If the interval contains more objects than argument, the result is zero.
*/
public int subtractSizeFrom(int argument) {
if (argument<=0)
return 0;
else if (m_lowerBound.equals(m_upperBound)) {
// The interval is not empty, and we know that it is not empty; hence, it is a singleton.
return argument-1;
}
else {
// The lower bound must be smaller than the upper bound because the interval is not empty.
// If the base range is dense, since the excluded range is a proper subset of the base range,
// there are infinitely many numbers in the interval.
if (m_baseRange.isDense())
return 0;
// The base range is INTEGER; since the excluded range is a proper subset, it must be NOTHING.
if (MinusInfinity.INSTANCE.equals(m_lowerBound) || PlusInfinity.INSTANCE.equals(m_upperBound))
return 0;
// In the constructor, the lower and upper bounds are adjusted to the first integer in the range.
// Hence, we just subtract the bounds.
return Numbers.subtractIntegerIntervalSizeFrom(m_lowerBound,m_upperBound,argument);
}
}
public boolean containsNumber(Number number) {
NumberRange mostSpecificRange=NumberRange.getMostSpecificRange(number);
if (!NumberRange.isSubsetOf(mostSpecificRange,m_baseRange) || NumberRange.isSubsetOf(mostSpecificRange,m_excludedRange))
return false;
int lowerBoundComparison=Numbers.compare(m_lowerBound,number);
if (lowerBoundComparison>0 || (lowerBoundComparison==0 && m_lowerBoundType==BoundType.EXCLUSIVE))
return false;
int upperBoundComparison=Numbers.compare(m_upperBound,number);
if (upperBoundComparison<0 || (upperBoundComparison==0 && m_upperBoundType==BoundType.EXCLUSIVE))
return false;
return true;
}
public void enumerateNumbers(Collection<Object> numbers) {
if (m_lowerBound.equals(m_upperBound)) {
// The interval is not empty, and we know that it is not empty; hence, it is a singleton.
numbers.add(m_lowerBound);
}
else {
// The lower bound must be smaller than the upper bound because the interval is not empty.
// If the base range is dense, since the excluded range is a proper subset of the base range,
// there are infinitely many numbers in the interval.
if (m_baseRange.isDense())
throw new IllegalStateException("The data range is infinite.");
// The base range is INTEGER; since the excluded range is a proper subset, it must be NOTHING.
if (MinusInfinity.INSTANCE.equals(m_lowerBound) || PlusInfinity.INSTANCE.equals(m_upperBound))
throw new IllegalStateException("The data range is infinite.");
// In the constructor, the lower and upper bounds are adjusted to the first integer in the range.
// Hence, we just go through all the integers in the range.
Number integer=m_lowerBound;
while (!integer.equals(m_upperBound)) {
numbers.add(integer);
integer=Numbers.nextInteger(integer);
}
numbers.add(m_upperBound);
}
}
protected static boolean isIntervalEmpty(NumberRange baseRange,NumberRange excludedRange,Number lowerBound,BoundType lowerBoundType,Number upperBound,BoundType upperBoundType) {
if (NumberRange.isSubsetOf(baseRange,excludedRange))
return true;
int boundComparison=Numbers.compare(lowerBound,upperBound);
if (boundComparison>0)
return true;
else if (boundComparison==0) {
if (lowerBoundType==BoundType.EXCLUSIVE || upperBoundType==BoundType.EXCLUSIVE || MinusInfinity.INSTANCE.equals(lowerBound) || PlusInfinity.INSTANCE.equals(lowerBound))
return true;
// Both end-points are INCLUSIVE, so the cardinality is at most one
NumberRange mostSpecificRange=NumberRange.getMostSpecificRange(lowerBound);
return !NumberRange.isSubsetOf(mostSpecificRange,baseRange) || NumberRange.isSubsetOf(mostSpecificRange,excludedRange);
}
else {
// Lower bound is smaller than the upper bound.
// If the base range is dense, since the excluded range is a proper subset of the base range
// there are infinitely many numbers in the interval.
if (baseRange.isDense())
return false;
// The base range is INTEGER; since the excluded range is a proper subset, it must be NOTHING.
if (MinusInfinity.INSTANCE.equals(lowerBound) || PlusInfinity.INSTANCE.equals(upperBound))
return false;
Number lowerBoundInclusive=Numbers.getNearestIntegerInBound(lowerBound,Numbers.BoundaryDirection.LOWER,lowerBoundType==BoundType.INCLUSIVE);
Number upperBoundInclusive=Numbers.getNearestIntegerInBound(upperBound,Numbers.BoundaryDirection.UPPER,upperBoundType==BoundType.INCLUSIVE);
return Numbers.compare(lowerBoundInclusive,upperBoundInclusive)>0;
}
}
public String toString() {
StringBuffer buffer=new StringBuffer();
buffer.append(m_baseRange.toString());
if (m_excludedRange!=NumberRange.NOTHING) {
buffer.append('\\');
buffer.append(m_excludedRange.toString());
}
if (m_lowerBoundType==BoundType.INCLUSIVE)
buffer.append('[');
else
buffer.append('<');
buffer.append(m_lowerBound.toString());
buffer.append(" .. ");
buffer.append(m_upperBound.toString());
if (m_upperBoundType==BoundType.INCLUSIVE)
buffer.append(']');
else
buffer.append('>');
return buffer.toString();
}
}