/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 1992-2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.math; /** * A fraction made of a numerator and a denominator. This is not the purpose of this class * to provides a full-fledged library for fractional number handling. This class exists mostly * for the limited needs of some operations on tiled images. * <p> * For performance reasons, the methods in this class never create new objects. They always * operate on an object specified in argument, and store the result in the object on which * the method was invoked. * <p> * This class is final for performance reason. * * @since 2.5 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (MPO) */ public final class Fraction extends Number implements Comparable<Fraction>, Cloneable { /** * For cross-version compatibility. */ private static final long serialVersionUID = -4501644254763471216L; /** * The numerator. */ private int numerator; /** * The denominator. */ private int denominator; /** * Creates a new fraction initialized to 0/0, which is an indetermined value. * Note that this is not the same than initializing a fraction to 0. */ public Fraction() { } /** * Creates a new fraction initialized to the same value than the given fraction. * * @param other The fraction to copy in this fraction. */ public Fraction(final Fraction other) { this.numerator = other.numerator; this.denominator = other.denominator; } /** * Creates a new fraction initialized to the given numerator. * * @param numerator The numerator. */ public Fraction(final int numerator) { this.numerator = numerator; denominator = 1; } /** * Creates a new fraction. * * @param numerator The numerator. * @param denominator The denominator. */ public Fraction(final int numerator, final int denominator) { this.numerator = numerator; this.denominator = denominator; simplify(); } /** * Sets this fraction to the given value. * * @param numerator The numerator. * @param denominator The denominator. */ public void set(final int numerator, final int denominator) { this.numerator = numerator; this.denominator = denominator; simplify(); } /** * Adds to this fraction the values given by the given fraction. * The results is stored in this fraction. * * @param other The fraction to add to this fraction. */ public void add(final Fraction other) { numerator = numerator * other.denominator + other.numerator * denominator; denominator *= other.denominator; simplify(); } /** * Subtracts to this fraction the values given by the given fraction. * The results is stored in this fraction. * * @param other The fraction to subtract to this fraction. */ public void subtract(final Fraction other) { numerator = numerator * other.denominator - other.numerator * denominator; denominator *= other.denominator; simplify(); } /** * Multiplies this fraction by the given fraction. * The results is stored in this fraction. * * @param other The fraction to multiply to this fraction. */ public void multiply(final Fraction other) { numerator *= other.numerator; denominator *= other.denominator; simplify(); } /** * Divides this fraction by the given fraction. * The results is stored in this fraction. * * @param other The fraction to divide to this fraction. */ public void divide(final Fraction other) { numerator *= other.denominator; denominator *= other.numerator; simplify(); } /** * Simplifies this fraction in-place. */ private void simplify() { // Simplify 0/x as 0/1 if (numerator == 0) { denominator = XMath.sgn(denominator); return; } // Simplify x/0 as 1/0 if (denominator == 0) { numerator = XMath.sgn(numerator); return; } // Simplify x/xy as 1/y if (denominator % numerator == 0) { denominator /= numerator; if (denominator < 0) { denominator = -denominator; numerator = -1; } else { numerator = 1; } return; } int num = Math.abs(numerator); int den = Math.abs(denominator); num %= den; // Simplify xy/x as y/1 if (num == 0) { numerator /= denominator; denominator = 1; return; } // Search for greater common multiple. int pgcd = 1; int remainder = num; do { num = den; den = remainder; pgcd = remainder; remainder = num % den; } while (remainder != 0); numerator /= pgcd; denominator /=pgcd; if (denominator < 0) { numerator = -numerator; denominator = -denominator; } } /** * Returns the numerator. * * @return The numerator. */ public int numerator() { return numerator; } /** * Returns the denominator. * * @return The denominator. */ public int denominator() { return denominator; } /** * Returns the fraction as a floating point number. * * @return This fraction as a floating point number. */ @Override public double doubleValue() { return (double) numerator / (double) denominator; } /** * Returns the fraction as a floating point number. * * @return This fraction as a floating point number. */ @Override public float floatValue() { return (float) doubleValue(); } /** * Returns this fraction rounded to nearest integer. * * @return This fraction rounded to nearest integer. */ @Override public long longValue() { return intValue(); } /** * Returns this fraction {@linkplain #round rounded} to nearest integer. * * @return This fraction rounded to nearest integer. */ @Override public int intValue() { return round(numerator, denominator); } /** * Computes {@code numerator / denominator} and rounds the result toward nearest integer. * If the result is located at equal distance from the two nearest integers, then rounds * to the even one. * * @param numerator The numerator in the division. * @param denominator The denominator in the division. * @return {@code numerator / denominator} rounded toward nearest integer. */ public static long round(final long numerator, final long denominator) { long n = numerator / denominator; long r = numerator % denominator; if (r != 0) { r = Math.abs(r << 1); final long d = Math.abs(denominator); if (r > d || (r == d && (n & 1) != 0)) { if ((numerator ^ denominator) >= 0) { n++; } else { n--; } } } return n; } /** * Computes {@code numerator / denominator} and rounds the result toward nearest integer. * If the result is located at equal distance from the two nearest integers, then rounds * to the even one. * * @param numerator The numerator in the division. * @param denominator The denominator in the division. * @return {@code numerator / denominator} rounded toward nearest integer. */ public static int round(final int numerator, final int denominator) { int n = numerator / denominator; int r = numerator % denominator; if (r != 0) { r = Math.abs(r << 1); final int d = Math.abs(denominator); if (r > d || (r == d && (n & 1) != 0)) { if ((numerator ^ denominator) >= 0) { n++; } else { n--; } } } return n; } /** * Computes {@code numerator / denominator} and rounds the result toward negative infinity. * This is different from the default operation on primitive types, which rounds toward zero. * <p> * <b>Tip:</b> if the numerator and the denominator are both positive or both negative, * then the result is positive and identical to {@code numerator / denominator}. * * @param numerator The numerator in the division. * @param denominator The denominator in the division. * @return {@code numerator / denominator} rounded toward negative infinity. */ public static int floor(final int numerator, final int denominator) { int n = numerator / denominator; if ((numerator ^ denominator) < 0 && (numerator % denominator) != 0) { n--; } return n; } /** * Computes {@code numerator / denominator} and rounds the result toward positive infinity. * This is different from the default operation on primitive types, which rounds toward zero. * * @param numerator The numerator in the division. * @param denominator The denominator in the division. * @return {@code numerator / denominator} rounded toward positive infinity. */ public static int ceil(final int numerator, final int denominator) { int n = numerator / denominator; if ((numerator ^ denominator) >= 0 && (numerator % denominator) != 0) { n++; } return n; } /** * Compares this fraction with the given one for order. * * @param other The fraction to compare to this fraction for ordering. * @return A negative number if this fraction is smaller than the given fraction, * a positive number if greater, or 0 if equals. */ public int compareTo(final Fraction other) { return numerator * other.denominator - other.numerator * denominator; } /** * Compares this fraction with the given object for equality. * * @param other The object to compare with this fraction for equality. * @return {@code true} if the given object is an other fraction numerically * equals to this fraction. */ @Override public boolean equals(final Object other) { if (other instanceof Fraction) { final Fraction that = (Fraction) other; return numerator == that.numerator && denominator == that.denominator; } return false; } /** * Returns a clone of this fraction. * * @return A clone of this fraction. */ @Override public Fraction clone() { try { return (Fraction) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } /** * Returns a string representation of this fraction. * * @return A string representation of this fraction. */ @Override public String toString() { return String.valueOf(numerator) + '/' + denominator; } }