Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 59671 invoked from network); 20 Jan 2006 13:51:27 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 20 Jan 2006 13:51:26 -0000 Received: (qmail 48455 invoked by uid 500); 20 Jan 2006 13:51:19 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 48310 invoked by uid 500); 20 Jan 2006 13:51:17 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 47889 invoked by uid 99); 20 Jan 2006 13:51:14 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 Jan 2006 05:51:14 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Fri, 20 Jan 2006 05:50:55 -0800 Received: (qmail 58233 invoked by uid 65534); 20 Jan 2006 13:50:34 -0000 Message-ID: <20060120135034.58231.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r370807 [18/22] - in /directory/sandbox/trustin/mina-spi: ./ core/src/main/java/org/apache/mina/common/ core/src/main/java/org/apache/mina/common/support/ core/src/main/java/org/apache/mina/common/support/discovery/ core/src/main/java/org/a... Date: Fri, 20 Jan 2006 13:48:55 -0000 To: commits@directory.apache.org From: trustin@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/Fraction.java URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/Fraction.java?rev=370807&view=auto ============================================================================== --- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/Fraction.java (added) +++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/Fraction.java Fri Jan 20 05:47:50 2006 @@ -0,0 +1,921 @@ +/* + * Copyright 2002-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.mina.common.support.lang.math; + +import java.io.Serializable; +import java.math.BigInteger; + +/** + *

Fraction is a Number implementation that + * stores fractions accurately.

+ * + *

This class is immutable, and interoperable with most methods that accept + * a Number.

+ * + * @author Travis Reeder + * @author Stephen Colebourne + * @author Tim O'Brien + * @author Pete Gieser + * @author C. Scott Ananian + * @since 2.0 + * @version $Id$ + */ +public final class Fraction extends Number implements Serializable, Comparable { + + /** Serialization lock, Lang version 2.0 */ + private static final long serialVersionUID = 65382027393090L; + + /** + * Fraction representation of 0. + */ + public static final Fraction ZERO = new Fraction(0, 1); + /** + * Fraction representation of 1. + */ + public static final Fraction ONE = new Fraction(1, 1); + /** + * Fraction representation of 1/2. + */ + public static final Fraction ONE_HALF = new Fraction(1, 2); + /** + * Fraction representation of 1/3. + */ + public static final Fraction ONE_THIRD = new Fraction(1, 3); + /** + * Fraction representation of 2/3. + */ + public static final Fraction TWO_THIRDS = new Fraction(2, 3); + /** + * Fraction representation of 1/4. + */ + public static final Fraction ONE_QUARTER = new Fraction(1, 4); + /** + * Fraction representation of 2/4. + */ + public static final Fraction TWO_QUARTERS = new Fraction(2, 4); + /** + * Fraction representation of 3/4. + */ + public static final Fraction THREE_QUARTERS = new Fraction(3, 4); + /** + * Fraction representation of 1/5. + */ + public static final Fraction ONE_FIFTH = new Fraction(1, 5); + /** + * Fraction representation of 2/5. + */ + public static final Fraction TWO_FIFTHS = new Fraction(2, 5); + /** + * Fraction representation of 3/5. + */ + public static final Fraction THREE_FIFTHS = new Fraction(3, 5); + /** + * Fraction representation of 4/5. + */ + public static final Fraction FOUR_FIFTHS = new Fraction(4, 5); + + + /** + * The numerator number part of the fraction (the three in three sevenths). + */ + private final int numerator; + /** + * The denominator number part of the fraction (the seven in three sevenths). + */ + private final int denominator; + + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode = 0; + /** + * Cached output toString (class is immutable). + */ + private transient String toString = null; + /** + * Cached output toProperString (class is immutable). + */ + private transient String toProperString = null; + + /** + *

Constructs a Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + */ + private Fraction(int numerator, int denominator) { + super(); + this.numerator = numerator; + this.denominator = denominator; + } + + /** + *

Creates a Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + *

Any negative signs are resolved to be on the numerator.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + * @return a new fraction instance + * @throws ArithmeticException if the denomiator is zero + */ + public static Fraction getFraction(int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } + numerator = -numerator; + denominator = -denominator; + } + return new Fraction(numerator, denominator); + } + + /** + *

Creates a Fraction instance with the 3 parts + * of a fraction X Y/Z.

+ * + *

The negative sign must be passed in on the whole number part.

+ * + * @param whole the whole number, for example the one in 'one and three sevenths' + * @param numerator the numerator, for example the three in 'one and three sevenths' + * @param denominator the denominator, for example the seven in 'one and three sevenths' + * @return a new fraction instance + * @throws ArithmeticException if the denomiator is zero + * @throws ArithmeticException if the denominator is negative + * @throws ArithmeticException if the numerator is negative + * @throws ArithmeticException if the resulting numerator exceeds + * Integer.MAX_VALUE + */ + public static Fraction getFraction(int whole, int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (denominator < 0) { + throw new ArithmeticException("The denominator must not be negative"); + } + if (numerator < 0) { + throw new ArithmeticException("The numerator must not be negative"); + } + long numeratorValue; + if (whole < 0) { + numeratorValue = whole * (long)denominator - numerator; + } else { + numeratorValue = whole * (long)denominator + numerator; + } + if (numeratorValue < Integer.MIN_VALUE || + numeratorValue > Integer.MAX_VALUE) { + throw new ArithmeticException("Numerator too large to represent as an Integer."); + } + return new Fraction((int) numeratorValue, denominator); + } + + /** + *

Creates a Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + *

Any negative signs are resolved to be on the numerator.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + * @return a new fraction instance, with the numerator and denominator reduced + * @throws ArithmeticException if the denominator is zero + */ + public static Fraction getReducedFraction(int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (numerator==0) { + return ZERO; // normalize zero. + } + // allow 2^k/-2^31 as a valid fraction (where k>0) + if (denominator==Integer.MIN_VALUE && (numerator&1)==0) { + numerator/=2; denominator/=2; + } + if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } + numerator = -numerator; + denominator = -denominator; + } + // simplify fraction. + int gcd = greatestCommonDivisor(numerator, denominator); + numerator /= gcd; + denominator /= gcd; + return new Fraction(numerator, denominator); + } + + /** + *

Creates a Fraction instance from a double value.

+ * + *

This method uses the + * continued fraction algorithm, computing a maximum of + * 25 convergents and bounding the denominator by 10,000.

+ * + * @param value the double value to convert + * @return a new fraction instance that is close to the value + * @throws ArithmeticException if |value| > Integer.MAX_VALUE + * or value = NaN + * @throws ArithmeticException if the calculated denominator is zero + * @throws ArithmeticException if the the algorithm does not converge + */ + public static Fraction getFraction(double value) { + int sign = (value < 0 ? -1 : 1); + value = Math.abs(value); + if (value > Integer.MAX_VALUE || Double.isNaN(value)) { + throw new ArithmeticException + ("The value must not be greater than Integer.MAX_VALUE or NaN"); + } + int wholeNumber = (int) value; + value -= wholeNumber; + + int numer0 = 0; // the pre-previous + int denom0 = 1; // the pre-previous + int numer1 = 1; // the previous + int denom1 = 0; // the previous + int numer2 = 0; // the current, setup in calculation + int denom2 = 0; // the current, setup in calculation + int a1 = (int) value; + int a2 = 0; + double x1 = 1; + double x2 = 0; + double y1 = value - a1; + double y2 = 0; + double delta1, delta2 = Double.MAX_VALUE; + double fraction; + int i = 1; +// System.out.println("---"); + do { + delta1 = delta2; + a2 = (int) (x1 / y1); + x2 = y1; + y2 = x1 - a2 * y1; + numer2 = a1 * numer1 + numer0; + denom2 = a1 * denom1 + denom0; + fraction = (double) numer2 / (double) denom2; + delta2 = Math.abs(value - fraction); +// System.out.println(numer2 + " " + denom2 + " " + fraction + " " + delta2 + " " + y1); + a1 = a2; + x1 = x2; + y1 = y2; + numer0 = numer1; + denom0 = denom1; + numer1 = numer2; + denom1 = denom2; + i++; +// System.out.println(">>" + delta1 +" "+ delta2+" "+(delta1 > delta2)+" "+i+" "+denom2); + } while ((delta1 > delta2) && (denom2 <= 10000) && (denom2 > 0) && (i < 25)); + if (i == 25) { + throw new ArithmeticException("Unable to convert double to fraction"); + } + return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0); + } + + /** + *

Creates a Fraction from a String.

+ * + *

The formats accepted are:

+ * + *
    + *
  1. double String containing a dot
  2. + *
  3. 'X Y/Z'
  4. + *
  5. 'Y/Z'
  6. + *
  7. 'X' (a simple whole number)
  8. + *
+ * and a .

+ * + * @param str the string to parse, must not be null + * @return the new Fraction instance + * @throws IllegalArgumentException if the string is null + * @throws NumberFormatException if the number format is invalid + */ + public static Fraction getFraction(String str) { + if (str == null) { + throw new IllegalArgumentException("The string must not be null"); + } + // parse double format + int pos = str.indexOf('.'); + if (pos >= 0) { + return getFraction(Double.parseDouble(str)); + } + + // parse X Y/Z format + pos = str.indexOf(' '); + if (pos > 0) { + int whole = Integer.parseInt(str.substring(0, pos)); + str = str.substring(pos + 1); + pos = str.indexOf('/'); + if (pos < 0) { + throw new NumberFormatException("The fraction could not be parsed as the format X Y/Z"); + } else { + int numer = Integer.parseInt(str.substring(0, pos)); + int denom = Integer.parseInt(str.substring(pos + 1)); + return getFraction(whole, numer, denom); + } + } + + // parse Y/Z format + pos = str.indexOf('/'); + if (pos < 0) { + // simple whole number + return getFraction(Integer.parseInt(str), 1); + } else { + int numer = Integer.parseInt(str.substring(0, pos)); + int denom = Integer.parseInt(str.substring(pos + 1)); + return getFraction(numer, denom); + } + } + + // Accessors + //------------------------------------------------------------------- + + /** + *

Gets the numerator part of the fraction.

+ * + *

This method may return a value greater than the denominator, an + * improper fraction, such as the seven in 7/4.

+ * + * @return the numerator fraction part + */ + public int getNumerator() { + return numerator; + } + + /** + *

Gets the denominator part of the fraction.

+ * + * @return the denominator fraction part + */ + public int getDenominator() { + return denominator; + } + + /** + *

Gets the proper numerator, always positive.

+ * + *

An improper fraction 7/4 can be resolved into a proper one, 1 3/4. + * This method returns the 3 from the proper fraction.

+ * + *

If the fraction is negative such as -7/4, it can be resolved into + * -1 3/4, so this method returns the positive proper numerator, 3.

+ * + * @return the numerator fraction part of a proper fraction, always positive + */ + public int getProperNumerator() { + return Math.abs(numerator % denominator); + } + + /** + *

Gets the proper whole part of the fraction.

+ * + *

An improper fraction 7/4 can be resolved into a proper one, 1 3/4. + * This method returns the 1 from the proper fraction.

+ * + *

If the fraction is negative such as -7/4, it can be resolved into + * -1 3/4, so this method returns the positive whole part -1.

+ * + * @return the whole fraction part of a proper fraction, that includes the sign + */ + public int getProperWhole() { + return numerator / denominator; + } + + // Number methods + //------------------------------------------------------------------- + + /** + *

Gets the fraction as an int. This returns the whole number + * part of the fraction.

+ * + * @return the whole number fraction part + */ + public int intValue() { + return numerator / denominator; + } + + /** + *

Gets the fraction as a long. This returns the whole number + * part of the fraction.

+ * + * @return the whole number fraction part + */ + public long longValue() { + return (long) numerator / denominator; + } + + /** + *

Gets the fraction as a float. This calculates the fraction + * as the numerator divided by denominator.

+ * + * @return the fraction as a float + */ + public float floatValue() { + return ((float) numerator) / ((float) denominator); + } + + /** + *

Gets the fraction as a double. This calculates the fraction + * as the numerator divided by denominator.

+ * + * @return the fraction as a double + */ + public double doubleValue() { + return ((double) numerator) / ((double) denominator); + } + + // Calculations + //------------------------------------------------------------------- + + /** + *

Reduce the fraction to the smallest values for the numerator and + * denominator, returning the result..

+ * + * @return a new reduce fraction instance, or this if no simplification possible + */ + public Fraction reduce() { + int gcd = greatestCommonDivisor(Math.abs(numerator), denominator); + return Fraction.getFraction(numerator / gcd, denominator / gcd); + } + + /** + *

Gets a fraction that is the inverse (1/fraction) of this one.

+ * + *

The returned fraction is not reduced.

+ * + * @return a new fraction instance with the numerator and denominator + * inverted. + * @throws ArithmeticException if the fraction represents zero. + */ + public Fraction invert() { + if (numerator == 0) { + throw new ArithmeticException("Unable to invert zero."); + } + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate numerator"); + } + if (numerator<0) { + return new Fraction(-denominator, -numerator); + } else { + return new Fraction(denominator, numerator); + } + } + + /** + *

Gets a fraction that is the negative (-fraction) of this one.

+ * + *

The returned fraction is not reduced.

+ * + * @return a new fraction instance with the opposite signed numerator + */ + public Fraction negate() { + // the positive range is one smaller than the negative range of an int. + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: too large to negate"); + } + return new Fraction(-numerator, denominator); + } + + /** + *

Gets a fraction that is the positive equivalent of this one.

+ *

More precisely: (fraction >= 0 ? this : -fraction)

+ * + *

The returned fraction is not reduced.

+ * + * @return this if it is positive, or a new positive fraction + * instance with the opposite signed numerator + */ + public Fraction abs() { + if (numerator >= 0) { + return this; + } + return negate(); + } + + /** + *

Gets a fraction that is raised to the passed in power.

+ * + *

The returned fraction is in reduced form.

+ * + * @param power the power to raise the fraction to + * @return this if the power is one, ONE if the power + * is zero (even if the fraction equals ZERO) or a new fraction instance + * raised to the appropriate power + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction pow(int power) { + if (power == 1) { + return this; + } else if (power == 0) { + return ONE; + } else if (power < 0) { + if (power==Integer.MIN_VALUE) { // MIN_VALUE can't be negated. + return this.invert().pow(2).pow(-(power/2)); + } + return this.invert().pow(-power); + } else { + Fraction f = this.multiplyBy(this); + if ((power % 2) == 0) { // if even... + return f.pow(power/2); + } else { // if odd... + return f.pow(power/2).multiplyBy(this); + } + } + } + + /** + *

Gets the greatest common divisor of the absolute value of + * two numbers, using the "binary gcd" method which avoids + * division and modulo operations. See Knuth 4.5.2 algorithm B. + * This algorithm is due to Josef Stein (1961).

+ * + * @param u a non-zero number + * @param v a non-zero number + * @return the greatest common divisor, never zero + */ + private static int greatestCommonDivisor(int u, int v) { + // keep u and v negative, as negative integers range down to + // -2^31, while positive numbers can only be as large as 2^31-1 + // (i.e. we can't necessarily negate a negative number without + // overflow) + /* assert u!=0 && v!=0; */ + if (u>0) { u=-u; } // make u negative + if (v>0) { v=-v; } // make v negative + // B1. [Find power of 2] + int k=0; + while ((u&1)==0 && (v&1)==0 && k<31) { // while u and v are both even... + u/=2; v/=2; k++; // cast out twos. + } + if (k==31) { + throw new ArithmeticException("overflow: gcd is 2^31"); + } + // B2. Initialize: u and v have been divided by 2^k and at least + // one is odd. + int t = ((u&1)==1) ? v : -(u/2)/*B3*/; + // t negative: u was odd, v may be even (t replaces v) + // t positive: u was even, v is odd (t replaces u) + do { + /* assert u<0 && v<0; */ + // B4/B3: cast out twos from t. + while ((t&1)==0) { // while t is even.. + t/=2; // cast out twos + } + // B5 [reset max(u,v)] + if (t>0) { + u = -t; + } else { + v = t; + } + // B6/B3. at this point both u and v should be odd. + t = (v - u)/2; + // |u| larger: t positive (replace u) + // |v| larger: t negative (replace v) + } while (t!=0); + return -u*(1<x*y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulAndCheck(int x, int y) { + long m = ((long)x)*((long)y); + if (m < Integer.MIN_VALUE || + m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mul"); + } + return (int)m; + } + + /** + * Multiply two non-negative integers, checking for overflow. + * + * @param x a non-negative factor + * @param y a non-negative factor + * @return the product x*y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulPosAndCheck(int x, int y) { + /* assert x>=0 && y>=0; */ + long m = ((long)x)*((long)y); + if (m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mulPos"); + } + return (int)m; + } + + /** + * Add two integers, checking for overflow. + * + * @param x an addend + * @param y an addend + * @return the sum x+y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int addAndCheck(int x, int y) { + long s = (long)x+(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + * Subtract two integers, checking for overflow. + * + * @param x the minuend + * @param y the subtrahend + * @return the difference x-y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int subAndCheck(int x, int y) { + long s = (long)x-(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + *

Adds the value of this fraction to another, returning the result in reduced form. + * The algorithm follows Knuth, 4.5.1.

+ * + * @param fraction the fraction to add, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction add(Fraction fraction) { + return addSub(fraction, true /* add */); + } + + /** + *

Subtracts the value of another fraction from the value of this one, + * returning the result in reduced form.

+ * + * @param fraction the fraction to subtract, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. + */ + public Fraction subtract(Fraction fraction) { + return addSub(fraction, false /* subtract */); + } + + /** + * Implement add and subtract using algorithm described in Knuth 4.5.1. + * + * @param fraction the fraction to subtract, must not be null + * @param isAdd true to add, false to subtract + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. + */ + private Fraction addSub(Fraction fraction, boolean isAdd) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + // zero is identity for addition. + if (numerator == 0) { + return isAdd ? fraction : fraction.negate(); + } + if (fraction.numerator == 0) { + return this; + } + // if denominators are randomly distributed, d1 will be 1 about 61% + // of the time. + int d1 = greatestCommonDivisor(denominator, fraction.denominator); + if (d1==1) { + // result is ( (u*v' +/- u'v) / u'v') + int uvp = mulAndCheck(numerator, fraction.denominator); + int upv = mulAndCheck(fraction.numerator, denominator); + return new Fraction + (isAdd ? addAndCheck(uvp, upv) : subAndCheck(uvp, upv), + mulPosAndCheck(denominator, fraction.denominator)); + } + // the quantity 't' requires 65 bits of precision; see knuth 4.5.1 + // exercise 7. we're going to use a BigInteger. + // t = u(v'/d1) +/- v(u'/d1) + BigInteger uvp = BigInteger.valueOf(numerator) + .multiply(BigInteger.valueOf(fraction.denominator/d1)); + BigInteger upv = BigInteger.valueOf(fraction.numerator) + .multiply(BigInteger.valueOf(denominator/d1)); + BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); + // but d2 doesn't need extra precision because + // d2 = gcd(t,d1) = gcd(t mod d1, d1) + int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); + int d2 = (tmodd1==0)?d1:greatestCommonDivisor(tmodd1, d1); + + // result is (t/d2) / (u'/d1)(v'/d2) + BigInteger w = t.divide(BigInteger.valueOf(d2)); + if (w.bitLength() > 31) { + throw new ArithmeticException + ("overflow: numerator too large after multiply"); + } + return new Fraction + (w.intValue(), + mulPosAndCheck(denominator/d1, fraction.denominator/d2)); + } + + /** + *

Multiplies the value of this fraction by another, returning the + * result in reduced form.

+ * + * @param fraction the fraction to multiply by, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction multiplyBy(Fraction fraction) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + if (numerator == 0 || fraction.numerator == 0) { + return ZERO; + } + // knuth 4.5.1 + // make sure we don't overflow unless the result *must* overflow. + int d1 = greatestCommonDivisor(numerator, fraction.denominator); + int d2 = greatestCommonDivisor(fraction.numerator, denominator); + return getReducedFraction + (mulAndCheck(numerator/d1, fraction.numerator/d2), + mulPosAndCheck(denominator/d2, fraction.denominator/d1)); + } + + /** + *

Divide the value of this fraction by another.

+ * + * @param fraction the fraction to divide by, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the fraction to divide by is zero + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction divideBy(Fraction fraction) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + if (fraction.numerator == 0) { + throw new ArithmeticException("The fraction to divide by must not be zero"); + } + return multiplyBy(fraction.invert()); + } + + // Basics + //------------------------------------------------------------------- + + /** + *

Compares this fraction to another object to test if they are equal.

. + * + *

To be equal, both values must be equal. Thus 2/4 is not equal to 1/2.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Fraction == false) { + return false; + } + Fraction other = (Fraction) obj; + return (getNumerator() == other.getNumerator() && + getDenominator() == other.getDenominator()); + } + + /** + *

Gets a hashCode for the fraction.

+ * + * @return a hash code value for this object + */ + public int hashCode() { + if (hashCode == 0) { + // hashcode update should be atomic. + hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator(); + } + return hashCode; + } + + /** + *

Compares this object to another based on size.

+ * + * @param object the object to compare to + * @return -1 if this is less, 0 if equal, +1 if greater + * @throws ClassCastException if the object is not a Fraction + * @throws NullPointerException if the object is null + */ + public int compareTo(Object object) { + Fraction other = (Fraction) object; + if (this==other) { + return 0; + } + if (numerator == other.numerator && denominator == other.denominator) { + return 0; + } + + // otherwise see which is less + long first = (long) numerator * (long) other.denominator; + long second = (long) other.numerator * (long) denominator; + if (first == second) { + return 0; + } else if (first < second) { + return -1; + } else { + return 1; + } + } + + /** + *

Gets the fraction as a String.

+ * + *

The format used is 'numerator/denominator' always. + * + * @return a String form of the fraction + */ + public String toString() { + if (toString == null) { + toString = new StringBuffer(32) + .append(getNumerator()) + .append('/') + .append(getDenominator()).toString(); + } + return toString; + } + + /** + *

Gets the fraction as a proper String in the format X Y/Z.

+ * + *

The format used in 'wholeNumber numerator/denominator'. + * If the whole number is zero it will be ommitted. If the numerator is zero, + * only the whole number is returned.

+ * + * @return a String form of the fraction + */ + public String toProperString() { + if (toProperString == null) { + if (numerator == 0) { + toProperString = "0"; + } else if (numerator == denominator) { + toProperString = "1"; + } else if ((numerator>0?-numerator:numerator) < -denominator) { + // note that we do the magnitude comparison test above with + // NEGATIVE (not positive) numbers, since negative numbers + // have a larger range. otherwise numerator==Integer.MIN_VALUE + // is handled incorrectly. + int properNumerator = getProperNumerator(); + if (properNumerator == 0) { + toProperString = Integer.toString(getProperWhole()); + } else { + toProperString = new StringBuffer(32) + .append(getProperWhole()).append(' ') + .append(properNumerator).append('/') + .append(getDenominator()).toString(); + } + } else { + toProperString = new StringBuffer(32) + .append(getNumerator()).append('/') + .append(getDenominator()).toString(); + } + } + return toProperString; + } +} Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/Fraction.java ------------------------------------------------------------------------------ svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/IntRange.java URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/IntRange.java?rev=370807&view=auto ============================================================================== --- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/IntRange.java (added) +++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/IntRange.java Fri Jan 20 05:47:50 2006 @@ -0,0 +1,378 @@ +/* + * Copyright 2002-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.mina.common.support.lang.math; + +import java.io.Serializable; + +/** + *

IntRange represents an inclusive range of ints.

+ * + * @author Stephen Colebourne + * @since 2.0 + * @version $Id$ + */ +public final class IntRange extends Range implements Serializable { + + private static final long serialVersionUID = 71849363892730L; + + /** + * The minimum number in this range (inclusive). + */ + private final int min; + /** + * The maximum number in this range (inclusive). + */ + private final int max; + + /** + * Cached output minObject (class is immutable). + */ + private transient Integer minObject = null; + /** + * Cached output maxObject (class is immutable). + */ + private transient Integer maxObject = null; + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode = 0; + /** + * Cached output toString (class is immutable). + */ + private transient String toString = null; + + /** + *

Constructs a new IntRange using the specified + * number as both the minimum and maximum in this range.

+ * + * @param number the number to use for this range + */ + public IntRange(int number) { + super(); + this.min = number; + this.max = number; + } + + /** + *

Constructs a new IntRange using the specified + * number as both the minimum and maximum in this range.

+ * + * @param number the number to use for this range, must not be null + * @throws IllegalArgumentException if the number is null + */ + public IntRange(Number number) { + super(); + if (number == null) { + throw new IllegalArgumentException("The number must not be null"); + } + this.min = number.intValue(); + this.max = number.intValue(); + if (number instanceof Integer) { + this.minObject = (Integer) number; + this.maxObject = (Integer) number; + } + } + + /** + *

Constructs a new IntRange with the specified + * minimum and maximum numbers (both inclusive).

+ * + *

The arguments may be passed in the order (min,max) or (max,min). The + * getMinimum and getMaximum methods will return the correct values.

+ * + * @param number1 first number that defines the edge of the range, inclusive + * @param number2 second number that defines the edge of the range, inclusive + */ + public IntRange(int number1, int number2) { + super(); + if (number2 < number1) { + this.min = number2; + this.max = number1; + } else { + this.min = number1; + this.max = number2; + } + } + + /** + *

Constructs a new IntRange with the specified + * minimum and maximum numbers (both inclusive).

+ * + *

The arguments may be passed in the order (min,max) or (max,min). The + * getMinimum and getMaximum methods will return the correct values.

+ * + * @param number1 first number that defines the edge of the range, inclusive + * @param number2 second number that defines the edge of the range, inclusive + * @throws IllegalArgumentException if either number is null + */ + public IntRange(Number number1, Number number2) { + super(); + if (number1 == null || number2 == null) { + throw new IllegalArgumentException("The numbers must not be null"); + } + int number1val = number1.intValue(); + int number2val = number2.intValue(); + if (number2val < number1val) { + this.min = number2val; + this.max = number1val; + if (number2 instanceof Integer) { + this.minObject = (Integer) number2; + } + if (number1 instanceof Integer) { + this.maxObject = (Integer) number1; + } + } else { + this.min = number1val; + this.max = number2val; + if (number1 instanceof Integer) { + this.minObject = (Integer) number1; + } + if (number2 instanceof Integer) { + this.maxObject = (Integer) number2; + } + } + } + + // Accessors + //-------------------------------------------------------------------- + + /** + *

Returns the minimum number in this range.

+ * + * @return the minimum number in this range + */ + public Number getMinimumNumber() { + if (minObject == null) { + minObject = new Integer(min); + } + return minObject; + } + + /** + *

Gets the minimum number in this range as a long.

+ * + * @return the minimum number in this range + */ + public long getMinimumLong() { + return min; + } + + /** + *

Gets the minimum number in this range as a int.

+ * + * @return the minimum number in this range + */ + public int getMinimumInteger() { + return min; + } + + /** + *

Gets the minimum number in this range as a double.

+ * + * @return the minimum number in this range + */ + public double getMinimumDouble() { + return min; + } + + /** + *

Gets the minimum number in this range as a float.

+ * + * @return the minimum number in this range + */ + public float getMinimumFloat() { + return min; + } + + /** + *

Returns the maximum number in this range.

+ * + * @return the maximum number in this range + */ + public Number getMaximumNumber() { + if (maxObject == null) { + maxObject = new Integer(max); + } + return maxObject; + } + + /** + *

Gets the maximum number in this range as a long.

+ * + * @return the maximum number in this range + */ + public long getMaximumLong() { + return max; + } + + /** + *

Gets the maximum number in this range as a int.

+ * + * @return the maximum number in this range + */ + public int getMaximumInteger() { + return max; + } + + /** + *

Gets the maximum number in this range as a double.

+ * + * @return the maximum number in this range + */ + public double getMaximumDouble() { + return max; + } + + /** + *

Gets the maximum number in this range as a float.

+ * + * @return the maximum number in this range + */ + public float getMaximumFloat() { + return max; + } + + // Tests + //-------------------------------------------------------------------- + + /** + *

Tests whether the specified number occurs within + * this range using int comparison.

+ * + *

null is handled and returns false.

+ * + * @param number the number to test, may be null + * @return true if the specified number occurs within this range + */ + public boolean containsNumber(Number number) { + if (number == null) { + return false; + } + return containsInteger(number.intValue()); + } + + /** + *

Tests whether the specified int occurs within + * this range using int comparison.

+ * + *

This implementation overrides the superclass for performance as it is + * the most common case.

+ * + * @param value the int to test + * @return true if the specified number occurs within this + * range by int comparison + */ + public boolean containsInteger(int value) { + return value >= min && value <= max; + } + + // Range tests + //-------------------------------------------------------------------- + + /** + *

Tests whether the specified range occurs entirely within this range + * using int comparison.

+ * + *

null is handled and returns false.

+ * + * @param range the range to test, may be null + * @return true if the specified range occurs entirely within this range + * @throws IllegalArgumentException if the range is not of this type + */ + public boolean containsRange(Range range) { + if (range == null) { + return false; + } + return containsInteger(range.getMinimumInteger()) && + containsInteger(range.getMaximumInteger()); + } + + /** + *

Tests whether the specified range overlaps with this range + * using int comparison.

+ * + *

null is handled and returns false.

+ * + * @param range the range to test, may be null + * @return true if the specified range overlaps with this range + */ + public boolean overlapsRange(Range range) { + if (range == null) { + return false; + } + return range.containsInteger(min) || + range.containsInteger(max) || + containsInteger(range.getMinimumInteger()); + } + + // Basics + //-------------------------------------------------------------------- + + /** + *

Compares this range to another object to test if they are equal.

. + * + *

To be equal, the class, minimum and maximum must be equal.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof IntRange == false) { + return false; + } + IntRange range = (IntRange) obj; + return min == range.min && max == range.max; + } + + /** + *

Gets a hashCode for the range.

+ * + * @return a hash code value for this object + */ + public int hashCode() { + if (hashCode == 0) { + hashCode = 17; + hashCode = 37 * hashCode + getClass().hashCode(); + hashCode = 37 * hashCode + min; + hashCode = 37 * hashCode + max; + } + return hashCode; + } + + /** + *

Gets the range as a String.

+ * + *

The format of the String is 'Range[min,max]'.

+ * + * @return the String representation of this range + */ + public String toString() { + if (toString == null) { + StringBuffer buf = new StringBuffer(32); + buf.append("Range["); + buf.append(min); + buf.append(','); + buf.append(max); + buf.append(']'); + toString = buf.toString(); + } + return toString; + } + +} Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/IntRange.java ------------------------------------------------------------------------------ svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/JVMRandom.java URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/JVMRandom.java?rev=370807&view=auto ============================================================================== --- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/JVMRandom.java (added) +++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/JVMRandom.java Fri Jan 20 05:47:50 2006 @@ -0,0 +1,160 @@ +/* + * Copyright 2002,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.mina.common.support.lang.math; + +import java.util.Random; + +/** + *

JVMRandom is a wrapper that supports all possible + * Random methods via the {@link java.lang.Math#random()} method + * and its system-wide {@link Random} object.

+ * + * @author Henri Yandell + * @since 2.0 + * @version $Id$ + */ +public final class JVMRandom extends Random { + + /** + * Ensures that only the constructor can call reseed. + */ + private boolean constructed = false; + + /** + * Constructs a new instance. + */ + public JVMRandom() { + this.constructed = true; + } + + /** + * Unsupported in 2.0. + * + * @param seed ignored + * @throws UnsupportedOperationException + */ + public synchronized void setSeed(long seed) { + if (this.constructed) { + throw new UnsupportedOperationException(); + } + } + + /** + * Unsupported in 2.0. + * + * @return Nothing, this method always throws an UnsupportedOperationException. + * @throws UnsupportedOperationException + */ + public synchronized double nextGaussian() { + throw new UnsupportedOperationException(); + } + + /** + * Unsupported in 2.0. + * + * @param byteArray ignored + * @throws UnsupportedOperationException + */ + public void nextBytes(byte[] byteArray) { + throw new UnsupportedOperationException(); + } + + /** + *

Returns the next pseudorandom, uniformly distributed int value + * from the Math.random() sequence.

+ * + * @return the random int + */ + public int nextInt() { + return nextInt(Integer.MAX_VALUE); + } + /** + *

Returns a pseudorandom, uniformly distributed int value between + * 0 (inclusive) and the specified value (exclusive), from + * the Math.random() sequence.

+ * + * @param n the specified exclusive max-value + * @return the random int + * @throws IllegalArgumentException when n <= 0 + */ + public int nextInt(int n) { + if (n <= 0) { + throw new IllegalArgumentException( + "Upper bound for nextInt must be positive" + ); + } + // TODO: check this cannot return 'n' + return (int)(Math.random() * n); + } + /** + *

Returns the next pseudorandom, uniformly distributed long value + * from the Math.random() sequence.

+ * @return the random long + */ + public long nextLong() { + // possible loss of precision? + return nextLong(Long.MAX_VALUE); + } + + + /** + *

Returns a pseudorandom, uniformly distributed long value between + * 0 (inclusive) and the specified value (exclusive), from + * the Math.random() sequence.

+ * + * @param n the specified exclusive max-value + * @return the random long + * @throws IllegalArgumentException when n <= 0 + */ + public static long nextLong(long n) { + if (n <= 0) { + throw new IllegalArgumentException( + "Upper bound for nextInt must be positive" + ); + } + // TODO: check this cannot return 'n' + return (long)(Math.random() * n); + } + + /** + *

Returns the next pseudorandom, uniformly distributed boolean value + * from the Math.random() sequence.

+ * + * @return the random boolean + */ + public boolean nextBoolean() { + return Math.random() > 0.5; + } + /** + *

Returns the next pseudorandom, uniformly distributed float value + * between 0.0 and 1.0 from the Math.random() + * sequence.

+ * + * @return the random float + */ + public float nextFloat() { + return (float)Math.random(); + } + /** + *

Synonymous to the Math.random() call.

+ * + * @return the random double + */ + public double nextDouble() { + return Math.random(); + } + +} Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/JVMRandom.java ------------------------------------------------------------------------------ svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/LongRange.java URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/LongRange.java?rev=370807&view=auto ============================================================================== --- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/LongRange.java (added) +++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/LongRange.java Fri Jan 20 05:47:50 2006 @@ -0,0 +1,391 @@ +/* + * Copyright 2002-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.mina.common.support.lang.math; + +import java.io.Serializable; + +/** + *

LongRange represents an inclusive range of longs.

+ * + * @author Stephen Colebourne + * @since 2.0 + * @version $Id$ + */ +public final class LongRange extends Range implements Serializable { + + private static final long serialVersionUID = 71849363892720L; + + /** + * The minimum number in this range (inclusive). + */ + private final long min; + /** + * The maximum number in this range (inclusive). + */ + private final long max; + + /** + * Cached output minObject (class is immutable). + */ + private transient Long minObject = null; + /** + * Cached output maxObject (class is immutable). + */ + private transient Long maxObject = null; + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode = 0; + /** + * Cached output toString (class is immutable). + */ + private transient String toString = null; + + /** + *

Constructs a new LongRange using the specified + * number as both the minimum and maximum in this range.

+ * + * @param number the number to use for this range + */ + public LongRange(long number) { + super(); + this.min = number; + this.max = number; + } + + /** + *

Constructs a new LongRange using the specified + * number as both the minimum and maximum in this range.

+ * + * @param number the number to use for this range, must not + * be null + * @throws IllegalArgumentException if the number is null + */ + public LongRange(Number number) { + super(); + if (number == null) { + throw new IllegalArgumentException("The number must not be null"); + } + this.min = number.longValue(); + this.max = number.longValue(); + if (number instanceof Long) { + this.minObject = (Long) number; + this.maxObject = (Long) number; + } + } + + /** + *

Constructs a new LongRange with the specified + * minimum and maximum numbers (both inclusive).

+ * + *

The arguments may be passed in the order (min,max) or (max,min). The + * getMinimum and getMaximum methods will return the correct values.

+ * + * @param number1 first number that defines the edge of the range, inclusive + * @param number2 second number that defines the edge of the range, inclusive + */ + public LongRange(long number1, long number2) { + super(); + if (number2 < number1) { + this.min = number2; + this.max = number1; + } else { + this.min = number1; + this.max = number2; + } + } + + /** + *

Constructs a new LongRange with the specified + * minimum and maximum numbers (both inclusive).

+ * + *

The arguments may be passed in the order (min,max) or (max,min). The + * getMinimum and getMaximum methods will return the correct values.

+ * + * @param number1 first number that defines the edge of the range, inclusive + * @param number2 second number that defines the edge of the range, inclusive + * @throws IllegalArgumentException if either number is null + */ + public LongRange(Number number1, Number number2) { + super(); + if (number1 == null || number2 == null) { + throw new IllegalArgumentException("The numbers must not be null"); + } + long number1val = number1.longValue(); + long number2val = number2.longValue(); + if (number2val < number1val) { + this.min = number2val; + this.max = number1val; + if (number2 instanceof Long) { + this.minObject = (Long) number2; + } + if (number1 instanceof Long) { + this.maxObject = (Long) number1; + } + } else { + this.min = number1val; + this.max = number2val; + if (number1 instanceof Long) { + this.minObject = (Long) number1; + } + if (number2 instanceof Long) { + this.maxObject = (Long) number2; + } + } + } + + // Accessors + //-------------------------------------------------------------------- + + /** + *

Returns the minimum number in this range.

+ * + * @return the minimum number in this range + */ + public Number getMinimumNumber() { + if (minObject == null) { + minObject = new Long(min); + } + return minObject; + } + + /** + *

Gets the minimum number in this range as a long.

+ * + * @return the minimum number in this range + */ + public long getMinimumLong() { + return min; + } + + /** + *

Gets the minimum number in this range as a int.

+ * + *

This conversion can lose information for large values.

+ * + * @return the minimum number in this range + */ + public int getMinimumInteger() { + return (int) min; + } + + /** + *

Gets the minimum number in this range as a double.

+ * + *

This conversion can lose information for large values.

+ * + * @return the minimum number in this range + */ + public double getMinimumDouble() { + return min; + } + + /** + *

Gets the minimum number in this range as a float.

+ * + *

This conversion can lose information for large values.

+ * + * @return the minimum number in this range + */ + public float getMinimumFloat() { + return min; + } + + /** + *

Returns the maximum number in this range.

+ * + * @return the maximum number in this range + */ + public Number getMaximumNumber() { + if (maxObject == null) { + maxObject = new Long(max); + } + return maxObject; + } + + /** + *

Gets the maximum number in this range as a long.

+ * + * @return the maximum number in this range + */ + public long getMaximumLong() { + return max; + } + + /** + *

Gets the maximum number in this range cast to an int.

+ * + *

This conversion can lose information for large values.

+ * + * @return the maximum number in this range cast to an int. + */ + public int getMaximumInteger() { + return (int) max; + } + + /** + *

Gets the maximum number in this range as a double.

+ * + *

This conversion can lose information for large values.

+ * + * @return The maximum number in this range as a double. + */ + public double getMaximumDouble() { + return max; + } + + /** + *

Gets the maximum number in this range as a float.

+ * + *

This conversion can lose information for large values.

+ * + * @return The maximum number in this range as a float. + */ + public float getMaximumFloat() { + return max; + } + + // Tests + //-------------------------------------------------------------------- + + /** + *

Tests whether the specified number occurs within + * this range using long comparison.

+ * + *

null is handled and returns false.

+ * + * @param number the number to test, may be null + * @return true if the specified number occurs within this range + */ + public boolean containsNumber(Number number) { + if (number == null) { + return false; + } + return containsLong(number.longValue()); + } + + /** + *

Tests whether the specified long occurs within + * this range using long comparison.

+ * + *

This implementation overrides the superclass for performance as it is + * the most common case.

+ * + * @param value the long to test + * @return true if the specified number occurs within this + * range by long comparison + */ + public boolean containsLong(long value) { + return value >= min && value <= max; + } + + // Range tests + //-------------------------------------------------------------------- + + /** + *

Tests whether the specified range occurs entirely within this range + * using long comparison.

+ * + *

null is handled and returns false.

+ * + * @param range the range to test, may be null + * @return true if the specified range occurs entirely within this range + * @throws IllegalArgumentException if the range is not of this type + */ + public boolean containsRange(Range range) { + if (range == null) { + return false; + } + return containsLong(range.getMinimumLong()) && + containsLong(range.getMaximumLong()); + } + + /** + *

Tests whether the specified range overlaps with this range + * using long comparison.

+ * + *

null is handled and returns false.

+ * + * @param range the range to test, may be null + * @return true if the specified range overlaps with this range + */ + public boolean overlapsRange(Range range) { + if (range == null) { + return false; + } + return range.containsLong(min) || + range.containsLong(max) || + containsLong(range.getMinimumLong()); + } + + // Basics + //-------------------------------------------------------------------- + + /** + *

Compares this range to another object to test if they are equal.

. + * + *

To be equal, the class, minimum and maximum must be equal.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof LongRange == false) { + return false; + } + LongRange range = (LongRange) obj; + return min == range.min && max == range.max; + } + + /** + *

Gets a hashCode for the range.

+ * + * @return a hash code value for this object + */ + public int hashCode() { + if (hashCode == 0) { + hashCode = 17; + hashCode = 37 * hashCode + getClass().hashCode(); + hashCode = 37 * hashCode + ((int) (min ^ (min >> 32))); + hashCode = 37 * hashCode + ((int) (max ^ (max >> 32))); + } + return hashCode; + } + + /** + *

Gets the range as a String.

+ * + *

The format of the String is 'Range[min,max]'.

+ * + * @return the String representation of this range + */ + public String toString() { + if (toString == null) { + StringBuffer buf = new StringBuffer(32); + buf.append("Range["); + buf.append(min); + buf.append(','); + buf.append(max); + buf.append(']'); + toString = buf.toString(); + } + return toString; + } + +} Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/LongRange.java ------------------------------------------------------------------------------ svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision Added: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/NumberRange.java URL: http://svn.apache.org/viewcvs/directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/NumberRange.java?rev=370807&view=auto ============================================================================== --- directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/NumberRange.java (added) +++ directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/NumberRange.java Fri Jan 20 05:47:50 2006 @@ -0,0 +1,236 @@ +/* + * Copyright 2002-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.mina.common.support.lang.math; + +import java.io.Serializable; + +/** + *

NumberRange represents an inclusive range of + * {@link java.lang.Number} objects of the same type.

+ * + * @author Christopher Elkins + * @author Stephen Colebourne + * @since 2.0 (previously in org.apache.mina.common.support.lang) + * @version $Id$ + */ +public final class NumberRange extends Range implements Serializable { + + private static final long serialVersionUID = 71849363892710L; + + /** + * The minimum number in this range. + */ + private final Number min; + /** + * The maximum number in this range. + */ + private final Number max; + + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode = 0; + /** + * Cached output toString (class is immutable). + */ + private transient String toString = null; + + /** + *

Constructs a new NumberRange using the specified + * number as both the minimum and maximum in this range.

+ * + * @param num the number to use for this range + * @throws IllegalArgumentException if the number is null + * @throws IllegalArgumentException if the number doesn't implement Comparable + * @throws IllegalArgumentException if the number is Double.NaN or Float.NaN + */ + public NumberRange(Number num) { + if (num == null) { + throw new IllegalArgumentException("The number must not be null"); + } + if (num instanceof Comparable == false) { + throw new IllegalArgumentException("The number must implement Comparable"); + } + if (num instanceof Double && ((Double) num).isNaN()) { + throw new IllegalArgumentException("The number must not be NaN"); + } + if (num instanceof Float && ((Float) num).isNaN()) { + throw new IllegalArgumentException("The number must not be NaN"); + } + + this.min = num; + this.max = num; + } + + /** + *

Constructs a new NumberRange with the specified + * minimum and maximum numbers (both inclusive).

+ * + *

The arguments may be passed in the order (min,max) or (max,min). The + * {@link #getMinimumNumber()} and {@link #getMaximumNumber()} methods will return the + * correct value.

+ * + *

This constructor is designed to be used with two Number + * objects of the same type. If two objects of different types are passed in, + * an exception is thrown.

+ * + * @param num1 first number that defines the edge of the range, inclusive + * @param num2 second number that defines the edge of the range, inclusive + * @throws IllegalArgumentException if either number is null + * @throws IllegalArgumentException if the numbers are of different types + * @throws IllegalArgumentException if the numbers don't implement Comparable + */ + public NumberRange(Number num1, Number num2) { + if (num1 == null || num2 == null) { + throw new IllegalArgumentException("The numbers must not be null"); + } + if (num1.getClass() != num2.getClass()) { + throw new IllegalArgumentException("The numbers must be of the same type"); + } + if (num1 instanceof Comparable == false) { + throw new IllegalArgumentException("The numbers must implement Comparable"); + } + if (num1 instanceof Double) { + if (((Double) num1).isNaN() || ((Double) num2).isNaN()) { + throw new IllegalArgumentException("The number must not be NaN"); + } + } else if (num1 instanceof Float) { + if (((Float) num1).isNaN() || ((Float) num2).isNaN()) { + throw new IllegalArgumentException("The number must not be NaN"); + } + } + + int compare = ((Comparable) num1).compareTo(num2); + if (compare == 0) { + this.min = num1; + this.max = num1; + } else if (compare > 0) { + this.min = num2; + this.max = num1; + } else { + this.min = num1; + this.max = num2; + } + } + + // Accessors + //-------------------------------------------------------------------- + + /** + *

Returns the minimum number in this range.

+ * + * @return the minimum number in this range + */ + public Number getMinimumNumber() { + return min; + } + + /** + *

Returns the maximum number in this range.

+ * + * @return the maximum number in this range + */ + public Number getMaximumNumber() { + return max; + } + + // Tests + //-------------------------------------------------------------------- + + /** + *

Tests whether the specified number occurs within + * this range.

+ * + *

null is handled and returns false.

+ * + * @param number the number to test, may be null + * @return true if the specified number occurs within this range + * @throws IllegalArgumentException if the number is of a different type to the range + */ + public boolean containsNumber(Number number) { + if (number == null) { + return false; + } + if (number.getClass() != min.getClass()) { + throw new IllegalArgumentException("The number must be of the same type as the range numbers"); + } + int compareMin = ((Comparable) min).compareTo(number); + int compareMax = ((Comparable) max).compareTo(number); + return compareMin <= 0 && compareMax >= 0; + } + + // Range tests + //-------------------------------------------------------------------- + // use Range implementations + + // Basics + //-------------------------------------------------------------------- + + /** + *

Compares this range to another object to test if they are equal.

. + * + *

To be equal, the class, minimum and maximum must be equal.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof NumberRange == false) { + return false; + } + NumberRange range = (NumberRange) obj; + return min.equals(range.min) && max.equals(range.max); + } + + /** + *

Gets a hashCode for the range.

+ * + * @return a hash code value for this object + */ + public int hashCode() { + if (hashCode == 0) { + hashCode = 17; + hashCode = 37 * hashCode + getClass().hashCode(); + hashCode = 37 * hashCode + min.hashCode(); + hashCode = 37 * hashCode + max.hashCode(); + } + return hashCode; + } + + /** + *

Gets the range as a String.

+ * + *

The format of the String is 'Range[min,max]'.

+ * + * @return the String representation of this range + */ + public String toString() { + if (toString == null) { + StringBuffer buf = new StringBuffer(32); + buf.append("Range["); + buf.append(min); + buf.append(','); + buf.append(max); + buf.append(']'); + toString = buf.toString(); + } + return toString; + } + +} Propchange: directory/sandbox/trustin/mina-spi/core/src/main/java/org/apache/mina/common/support/lang/math/NumberRange.java ------------------------------------------------------------------------------ svn:keywords = HeadURL Id LastChangedBy LastChangedDate LastChangedRevision