/* 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.doublenum; import java.util.Collection; public class DoubleInterval { protected final double m_lowerBoundInclusive; protected final double m_upperBoundInclusive; public DoubleInterval(double lowerBoundInclusive,double upperBoundInclusive) { assert !isIntervalEmpty(lowerBoundInclusive,upperBoundInclusive); m_lowerBoundInclusive=lowerBoundInclusive; m_upperBoundInclusive=upperBoundInclusive; } /** * Computes the intersection of this interval with the supplied one. If the two intervals do not intersect, the result is null. */ public DoubleInterval intersectWith(DoubleInterval that) { // This code uses the assumption no bound in either interval contains NaN. double newLowerBoundInclusive; if (isSmallerEqual(m_lowerBoundInclusive,that.m_lowerBoundInclusive)) newLowerBoundInclusive=that.m_lowerBoundInclusive; else newLowerBoundInclusive=m_lowerBoundInclusive; double newUpperBoundInclusive; if (isSmallerEqual(m_upperBoundInclusive,that.m_upperBoundInclusive)) newUpperBoundInclusive=m_upperBoundInclusive; else newUpperBoundInclusive=that.m_upperBoundInclusive; if (isIntervalEmpty(newLowerBoundInclusive,newUpperBoundInclusive)) return null; else if (isEqual(newLowerBoundInclusive,newUpperBoundInclusive)) return this; else if (that.isEqual(newLowerBoundInclusive,newUpperBoundInclusive)) return that; else return new DoubleInterval(newLowerBoundInclusive,newUpperBoundInclusive); } protected boolean isEqual(double lowerBoundInclusive,double upperBoundInclusive) { return areIdentical(m_lowerBoundInclusive,lowerBoundInclusive) && areIdentical(m_upperBoundInclusive,upperBoundInclusive); } public int subtractSizeFrom(int argument) { return subtractIntervalSizeFrom(m_lowerBoundInclusive,m_upperBoundInclusive,argument); } public boolean contains(double value) { return contains(m_lowerBoundInclusive,m_upperBoundInclusive,value); } public void enumerateNumbers(Collection<Object> numbers) { // We know that the interval is not empty; hence, neither bound is NaN. double number=m_lowerBoundInclusive; while (!areIdentical(number,m_upperBoundInclusive)) { numbers.add(number); number=nextDouble(number); } numbers.add(m_upperBoundInclusive); } public String toString() { StringBuffer buffer=new StringBuffer(); buffer.append("DOUBLE["); buffer.append(m_lowerBoundInclusive); buffer.append(".."); buffer.append(m_upperBoundInclusive); buffer.append(']'); return buffer.toString(); } public static boolean isNaN(long bits) { return ((bits & 0x7ff0000000000000L)==0x7ff0000000000000L) && ((bits & 0x000fffffffffffffL)!=0); } protected static boolean isIntervalEmpty(double lowerBoundInclusive,double upperBoundInclusive) { return !isSmallerEqual(lowerBoundInclusive,upperBoundInclusive); } public static boolean areIdentical(double value1,double value2) { return Double.doubleToLongBits(value1)==Double.doubleToLongBits(value2); } public static double nextDouble(double value) { long bits=Double.doubleToRawLongBits(value); long magnitude=(bits & 0x7fffffffffffffffL); boolean positive=((bits & 0x8000000000000000L)==0); // The successors of NaN and +INF are these numbers themselves. if (isNaN(bits) || (magnitude==0x7ff0000000000000L && positive)) return value; else { boolean newPositive; long newMagnitude; if (positive) { newPositive=true; newMagnitude=magnitude+1; } else if (!positive && magnitude==0) { // The successor of -0 is +0 newPositive=true; newMagnitude=0; } else { // if (!positive && magnitude!=0) newPositive=false; newMagnitude=magnitude-1; } long newBits=newMagnitude | (newPositive ? 0 : 0x8000000000000000L); return Double.longBitsToDouble(newBits); } } public static double previousDouble(double value) { long bits=Double.doubleToRawLongBits(value); long magnitude=(bits & 0x7fffffffffffffffL); boolean positive=((bits & 0x8000000000000000L)==0); // The predecessors of NaN and -INF are these numbers themselves. if (isNaN(bits) || (magnitude==0x7ff0000000000000L && !positive)) return value; else { boolean newPositive; long newMagnitude; if (!positive) { newPositive=false; newMagnitude=magnitude+1; } else if (positive && magnitude==0) { // The predecessor of +0 is -0 newPositive=false; newMagnitude=0; } else { // if (positive && magnitude!=0) newPositive=true; newMagnitude=magnitude-1; } long newBits=newMagnitude | (newPositive ? 0 : 0x8000000000000000L); return Double.longBitsToDouble(newBits); } } public static int subtractIntervalSizeFrom(double lowerBoundInclusive,double upperBoundInclusive,int argument) { if (argument<=0) return 0; long bitsLowerBoundInclusive=Double.doubleToRawLongBits(lowerBoundInclusive); long bitsUpperBoundInclusive=Double.doubleToRawLongBits(upperBoundInclusive); if (isNaN(bitsLowerBoundInclusive) || isNaN(bitsUpperBoundInclusive)) return argument; boolean positiveLowerBoundInclusive=((bitsLowerBoundInclusive & 0x8000000000000000L)==0); boolean positiveUpperBoundInclusive=((bitsUpperBoundInclusive & 0x8000000000000000L)==0); long magnitudeLowerBoundInclusive=(bitsLowerBoundInclusive & 0x7fffffffffffffffL); long magnitudeUpperBoundInclusive=(bitsUpperBoundInclusive & 0x7fffffffffffffffL); // Check whether the given interval is correctly oriented. if (!isSmallerEqual(positiveLowerBoundInclusive,magnitudeLowerBoundInclusive,positiveUpperBoundInclusive,magnitudeUpperBoundInclusive)) return argument; // Now determine the number of elements. This works even if 'lowerBoundInclusive' or 'upperBoundInclusive' is +INF or -INF. if (positiveLowerBoundInclusive && positiveUpperBoundInclusive) { // It must be that magnitudeLowerBoundInclusive<magnitudeUpperBoundInclusive. long size=magnitudeUpperBoundInclusive-magnitudeLowerBoundInclusive+1; return (int)Math.max(((long)argument)-size,0); } else if (!positiveLowerBoundInclusive && !positiveUpperBoundInclusive) { // It must be that magnitudeUpperBoundInclusive<magnitudeLowerBoundInclusive. long size=magnitudeLowerBoundInclusive-magnitudeUpperBoundInclusive+1; return (int)Math.max(((long)argument)-size,0); } else if (!positiveLowerBoundInclusive && positiveUpperBoundInclusive) { // the number of values from 'lowerBoundInclusive' to -0 long startToMinusZero=magnitudeLowerBoundInclusive+1; if (startToMinusZero>=argument) return 0; argument=(int)(argument-startToMinusZero); // The number of values from +0 to 'upperBoundInclusive'. long plusZeroToEnd=1+magnitudeUpperBoundInclusive; if (plusZeroToEnd>=argument) return 0; return (int)(argument-plusZeroToEnd); } else // if (positiveLowerBoundInclusive && !positiveUpperBoundInclusiev) is impossible at this point throw new IllegalStateException(); } public static boolean contains(double startInclusive,double endInclusive,double value) { long bitsStart=Double.doubleToRawLongBits(startInclusive); long bitsEnd=Double.doubleToRawLongBits(endInclusive); long bitsValue=Double.doubleToRawLongBits(value); if (isNaN(bitsStart) || isNaN(bitsEnd) || isNaN(bitsValue)) return false; boolean positiveStart=((bitsStart & 0x8000000000000000L)==0); boolean positiveEnd=((bitsEnd & 0x8000000000000000L)==0); boolean positiveValue=((bitsValue & 0x8000000000000000L)==0); long magnitudeStart=(bitsStart & 0x7fffffffffffffffL); long magnitudeEnd=(bitsEnd & 0x7fffffffffffffffL); long magnitudeValue=(bitsValue & 0x7fffffffffffffffL); return isSmallerEqual(positiveStart,magnitudeStart,positiveValue,magnitudeValue) && isSmallerEqual(positiveValue,magnitudeValue,positiveEnd,magnitudeEnd); } public static boolean isSmallerEqual(double value1,double value2) { long bitsValue1=Double.doubleToRawLongBits(value1); long bitsValue2=Double.doubleToRawLongBits(value2); if (isNaN(bitsValue1) || isNaN(bitsValue2)) return false; boolean positiveValue1=((bitsValue1 & 0x8000000000000000L)==0); boolean positiveValue2=((bitsValue2 & 0x8000000000000000L)==0); long magnitudeValue1=(bitsValue1 & 0x7fffffffffffffffL); long magnitudeValue2=(bitsValue2 & 0x7fffffffffffffffL); return isSmallerEqual(positiveValue1,magnitudeValue1,positiveValue2,magnitudeValue2); } public static boolean isSmallerEqual(boolean positive1,long magnitude1,boolean positive2,long magnitude2) { if (positive1 && positive2) return magnitude1<=magnitude2; else if (!positive1 && positive2) return true; else if (positive1 && !positive2) return false; else // if (!positive1 && !positive2) return magnitude1>=magnitude2; } }