001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.common; 018 019import java.text.NumberFormat; 020 021/** 022 * Rational number, as used by the TIFF image format. 023 * <p> 024 * The TIFF format specifies two data types for rational numbers based on 025 * a pair of 32-bit integers. Rational is based on unsigned 32-bit integers 026 * and SRational is based on signed 32-bit integers. This treatment is 027 * problematic in Java because Java does not support unsigned types. 028 * To address this challenge, this class stores the numerator and divisor 029 * in long (64-bit) integers, applying masks as necessary for the unsigned 030 * type. 031 */ 032public class RationalNumber extends Number { 033 034 private static final long serialVersionUID = -8412262656468158691L; 035 036 // int-precision tolerance 037 private static final double TOLERANCE = 1E-8; 038 039 // The TIFF and EXIF specifications call for the use 040 // of 32 bit unsigned integers. Since Java does not have an 041 // unsigned type, this class widens the type to long in order 042 // to avoid unintended negative numbers. 043 public final long numerator; 044 public final long divisor; 045 public final boolean unsignedType; 046 047 /** 048 * Constructs an instance based on signed integers 049 * @param numerator a 32-bit signed integer 050 * @param divisor a non-zero 32-bit signed integer 051 */ 052 public RationalNumber(final int numerator, final int divisor) { 053 this.numerator = numerator; 054 this.divisor = divisor; 055 this.unsignedType = false; 056 } 057 058 /** 059 * Constructs an instance supports either signed or unsigned integers. 060 * @param numerator a numerator in the indicated form (signed or unsigned) 061 * @param divisor a non-zero divisor in the indicated form (signed or unsigned) 062 * @param unsignedType indicates whether the input values are to be treated 063 * as unsigned. 064 */ 065 public RationalNumber(final int numerator, final int divisor, final boolean unsignedType) { 066 this.unsignedType = unsignedType; 067 if (unsignedType) { 068 this.numerator = numerator & 0xffffffffL; 069 this.divisor = divisor & 0xffffffffL; 070 } else { 071 this.numerator = numerator; 072 this.divisor = divisor; 073 } 074 } 075 076 /** 077 * A private constructor for methods such as negate() that create instances 078 * of this class using the content of the current instance. 079 * @param numerator a valid numerator 080 * @param divisor a valid denominator 081 * @param unsignedType indicates how numerator and divisor values 082 * are to be interpreted. 083 */ 084 private RationalNumber(final long numerator, final long divisor, boolean unsignedType){ 085 this.numerator = numerator; 086 this.divisor = divisor; 087 this.unsignedType = unsignedType; 088 } 089 090 static RationalNumber factoryMethod(long n, long d) { 091 // safer than constructor - handles values outside min/max range. 092 // also does some simple finding of common denominators. 093 094 if (n > Integer.MAX_VALUE || n < Integer.MIN_VALUE 095 || d > Integer.MAX_VALUE || d < Integer.MIN_VALUE) { 096 while ((n > Integer.MAX_VALUE || n < Integer.MIN_VALUE 097 || d > Integer.MAX_VALUE || d < Integer.MIN_VALUE) 098 && (Math.abs(n) > 1) && (Math.abs(d) > 1)) { 099 // brutal, imprecise truncation =( 100 // use the sign-preserving right shift operator. 101 n >>= 1; 102 d >>= 1; 103 } 104 105 if (d == 0) { 106 throw new NumberFormatException("Invalid value, numerator: " + n + ", divisor: " + d); 107 } 108 } 109 110 final long gcd = gcd(n, d); 111 d = d / gcd; 112 n = n / gcd; 113 114 return new RationalNumber((int) n, (int) d); 115 } 116 117 /** 118 * Return the greatest common divisor 119 */ 120 private static long gcd(final long a, final long b) { 121 if (b == 0) { 122 return a; 123 } 124 return gcd(b, a % b); 125 } 126 127 /** 128 * Negates the value of the RationalNumber. If the numerator of this 129 * instance has its high-order bit set, then its value is too large 130 * to be treated as a Java 32-bit signed integer. In such a case, the 131 * only way that a RationalNumber instance can be negated is to 132 * divide both terms by a common divisor, if a non-zero common divisor exists. 133 * However, if no such divisor exists, there is no numerically correct 134 * way to perform the negation. When a negation cannot be performed correctly, 135 * this method throws an unchecked exception. 136 * @return a valid instance with a negated value. 137 */ 138 public RationalNumber negate() { 139 long n = numerator; 140 long d = divisor; 141 if (unsignedType) { 142 // An instance of an unsigned type can be negated if and only if 143 // its high-order bit (the sign bit) is clear. If the bit is set, 144 // the value will be too large to convert to a signed type. 145 // In such a case it is necessary to adjust the numerator and denominator 146 // by their greatest common divisor (gcd), if one exists. 147 // If no non-zero common divisor exists, an exception is thrown. 148 if ((n >> 31) == 1) { 149 // the unsigned value is so large that the high-order bit is set 150 // it cannot be converted to a negative number. Check to see 151 // whether there is an option to reduce its magnitude. 152 long g = gcd(numerator, divisor); 153 if (g != 0) { 154 n /= g; 155 d /= g; 156 } 157 if ((n >> 31) == 1) { 158 throw new NumberFormatException( 159 "Unsigned numerator is too large to negate " 160 + numerator); 161 } 162 } 163 } 164 return new RationalNumber(-n, d, false); 165 } 166 167 @Override 168 public double doubleValue() { 169 return (double) numerator / (double) divisor; 170 } 171 172 @Override 173 public float floatValue() { 174 // The computation uses double value in order to preserve 175 // as much of the precision of the source numerator and denominator 176 // as possible. Note that the expression 177 // return (float)numerator/(float) denominator 178 // could lose precision since a Java float only carries 24 bits 179 // of precision while an integer carries 32. 180 return (float) doubleValue(); 181 } 182 183 @Override 184 public int intValue() { 185 return (int)(numerator / divisor); 186 } 187 188 @Override 189 public long longValue() { 190 return numerator / divisor; 191 } 192 193 @Override 194 public String toString() { 195 if (divisor == 0) { 196 return "Invalid rational (" + numerator + "/" + divisor + ")"; 197 } 198 final NumberFormat nf = NumberFormat.getInstance(); 199 200 if ((numerator % divisor) == 0) { 201 return nf.format(numerator / divisor); 202 } 203 return numerator + "/" + divisor + " (" + nf.format((double) numerator / divisor) + ")"; 204 } 205 206 public String toDisplayString() { 207 if ((numerator % divisor) == 0) { 208 return Long.toString(numerator / divisor); 209 } 210 final NumberFormat nf = NumberFormat.getInstance(); 211 nf.setMaximumFractionDigits(3); 212 return nf.format((double) numerator / (double) divisor); 213 } 214 215 private static final class Option { 216 public final RationalNumber rationalNumber; 217 public final double error; 218 219 private Option(final RationalNumber rationalNumber, final double error) { 220 this.rationalNumber = rationalNumber; 221 this.error = error; 222 } 223 224 public static Option factory(final RationalNumber rationalNumber, final double value) { 225 return new Option(rationalNumber, Math.abs(rationalNumber .doubleValue() - value)); 226 } 227 228 @Override 229 public String toString() { 230 return rationalNumber.toString(); 231 } 232 } 233 234 /** 235 * Calculate rational number using successive approximations. 236 * 237 * @param value rational number double value 238 * @return the RationalNumber representation of the double value 239 */ 240 public static RationalNumber valueOf(double value) { 241 if (value >= Integer.MAX_VALUE) { 242 return new RationalNumber(Integer.MAX_VALUE, 1); 243 } 244 if (value <= -Integer.MAX_VALUE) { 245 return new RationalNumber(-Integer.MAX_VALUE, 1); 246 } 247 248 boolean negative = false; 249 if (value < 0) { 250 negative = true; 251 value = Math.abs(value); 252 } 253 254 RationalNumber l; 255 RationalNumber h; 256 257 if (value == 0) { 258 return new RationalNumber(0, 1); 259 } 260 if (value >= 1) { 261 final int approx = (int) value; 262 if (approx < value) { 263 l = new RationalNumber(approx, 1); 264 h = new RationalNumber(approx + 1, 1); 265 } else { 266 l = new RationalNumber(approx - 1, 1); 267 h = new RationalNumber(approx, 1); 268 } 269 } else { 270 final int approx = (int) (1.0 / value); 271 if ((1.0 / approx) < value) { 272 l = new RationalNumber(1, approx); 273 h = new RationalNumber(1, approx - 1); 274 } else { 275 l = new RationalNumber(1, approx + 1); 276 h = new RationalNumber(1, approx); 277 } 278 } 279 Option low = Option.factory(l, value); 280 Option high = Option.factory(h, value); 281 282 Option bestOption = (low.error < high.error) ? low : high; 283 284 final int maxIterations = 100; // value is quite high, actually. 285 // shouldn't matter. 286 for (int count = 0; bestOption.error > TOLERANCE 287 && count < maxIterations; count++) { 288 final RationalNumber mediant = RationalNumber.factoryMethod( 289 (long) low.rationalNumber.numerator 290 + (long) high.rationalNumber.numerator, 291 (long) low.rationalNumber.divisor 292 + (long) high.rationalNumber.divisor); 293 final Option mediantOption = Option.factory(mediant, value); 294 295 if (value < mediant.doubleValue()) { 296 if (high.error <= mediantOption.error) { 297 break; 298 } 299 300 high = mediantOption; 301 } else { 302 if (low.error <= mediantOption.error) { 303 break; 304 } 305 306 low = mediantOption; 307 } 308 309 if (mediantOption.error < bestOption.error) { 310 bestOption = mediantOption; 311 } 312 } 313 314 return negative ? bestOption.rationalNumber.negate() 315 : bestOption.rationalNumber; 316 } 317 318}