001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2006 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package javax.measure.converter;
010
011/**
012 * <p> This class represents a converter multiplying numeric values by an
013 *     exact scaling factor (represented as the quotient of two 
014 *     <code>long</code> numbers).</p>
015 *  
016 * <p> Instances of this class are immutable.</p>
017 *
018 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
019 * @version 3.1, April 22, 2006
020 */
021public final class RationalConverter extends UnitConverter {
022
023    /**
024     * Holds the converter dividend.
025     */
026    private final long _dividend;
027
028    /**
029     * Holds the converter divisor (always positive).
030     */
031    private final long _divisor;
032
033    /**
034     * Creates a rational converter with the specified dividend and 
035     * divisor.
036     *
037     * @param dividend the dividend.
038     * @param divisor the positive divisor.
039     * @throws IllegalArgumentException if <code>divisor &lt; 0</code>
040     * @throws IllegalArgumentException if <code>dividend == divisor</code>
041     */
042    public RationalConverter(long dividend, long divisor) {
043        if (divisor < 0)
044            throw new IllegalArgumentException("Negative divisor");
045        if (dividend == divisor) 
046            throw new IllegalArgumentException("Identity converter not allowed");
047        _dividend = dividend;
048        _divisor = divisor;
049    }
050
051    /**
052     * Returns the dividend for this rational converter.
053     *
054     * @return this converter dividend.
055     */
056    public long getDividend() {
057        return _dividend;
058    }
059
060    /**
061     * Returns the positive divisor for this rational converter.
062     *
063     * @return this converter divisor.
064     */
065    public long getDivisor() {
066        return _divisor;
067    }
068
069    @Override
070    public UnitConverter inverse() {
071        return _dividend < 0 ? new RationalConverter(-_divisor, -_dividend)
072                : new RationalConverter(_divisor, _dividend);
073    }
074
075    @Override
076    public double convert(double amount) {
077        return amount * _dividend / _divisor;
078    }
079
080    @Override
081    public boolean isLinear() {
082        return true;
083    }
084
085    @Override
086    public UnitConverter concatenate(UnitConverter converter) {
087        if (converter instanceof RationalConverter) {
088            RationalConverter that = (RationalConverter) converter;
089            long dividendLong = this._dividend * that._dividend;
090            long divisorLong = this._divisor * that._divisor;
091            double dividendDouble = ((double)this._dividend) * that._dividend;
092            double divisorDouble = ((double)this._divisor) * that._divisor;
093            if ((dividendLong != dividendDouble) || 
094                    (divisorLong != divisorDouble)) { // Long overflows.
095                return new MultiplyConverter(dividendDouble / divisorDouble);
096            }
097            long gcd = gcd(dividendLong, divisorLong);
098            return RationalConverter.valueOf(dividendLong / gcd, divisorLong / gcd);
099        } else if (converter instanceof MultiplyConverter) {
100            return converter.concatenate(this);
101        } else {
102            return super.concatenate(converter);
103        }
104    }
105
106    private static UnitConverter valueOf(long dividend, long divisor) {
107        return (dividend == 1L) && (divisor == 1L) ? UnitConverter.IDENTITY
108                : new RationalConverter(dividend, divisor);
109    }
110
111    /**
112     * Returns the greatest common divisor (Euclid's algorithm).
113     *
114     * @param  m the first number.
115     * @param  nn the second number.
116     * @return the greatest common divisor.
117     */
118    private static long gcd(long m, long n) {
119        if (n == 0L) {
120            return m;
121        } else {
122            return gcd(n, m % n);
123        }
124    }
125
126    private static final long serialVersionUID = 1L;
127}