/** | |
* Copyright (c) 2005, 2011, Werner Keil, Ikayzo and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Werner Keil, Ikayzo and others - initial API and implementation | |
*/ | |
package org.eclipse.uomo.units.impl.converter; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.math.MathContext; | |
import org.eclipse.uomo.units.AbstractConverter; | |
import javax.measure.UnitConverter; | |
/** | |
* <p> This class represents a converter multiplying numeric values by an | |
* exact scaling factor (represented as the quotient of two | |
* <code>BigInteger</code> numbers).</p> | |
* | |
* <p> Instances of this class are immutable.</p> | |
* | |
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> | |
* @author <a href="mailto:uomo@catmedia.us">Werner Keil</a> | |
* @version 1.3 ($Revision: 212 $), $Date: 2010-09-13 23:50:44 +0200 (Mo, 13 Sep 2010) $ | |
*/ | |
public final class RationalConverter extends AbstractConverter { | |
/** The serialVersionUID */ | |
private static final long serialVersionUID = 5313011404391445406L; | |
/** | |
* Holds the converter dividend. | |
*/ | |
private final BigInteger dividend; | |
/** | |
* Holds the converter divisor (always positive). | |
*/ | |
private final BigInteger divisor; | |
/** | |
* Creates a rational converter with the specified dividend and | |
* divisor. | |
* | |
* @param dividend the dividend. | |
* @param divisor the positive divisor. | |
* @throws IllegalArgumentException if <code>divisor <= 0</code> | |
* @throws IllegalArgumentException if <code>dividend == divisor</code> | |
*/ | |
public RationalConverter(BigInteger dividend, BigInteger divisor) { | |
if (divisor.compareTo(BigInteger.ZERO) <= 0) | |
throw new IllegalArgumentException("Negative or zero divisor"); | |
if (dividend.equals(divisor)) | |
throw new IllegalArgumentException("Would result in identity converter"); | |
this.dividend = dividend; // Exact conversion. | |
this.divisor = divisor; // Exact conversion. | |
} | |
/** | |
* Creates a rational converter with the specified dividend and | |
* divisor. | |
* | |
* @param dividend the dividend. | |
* @param divisor the positive divisor. | |
* @throws IllegalArgumentException if <code>divisor <= 0</code> | |
* @throws IllegalArgumentException if <code>dividend == divisor</code> | |
*/ | |
public RationalConverter(long dividend, long divisor) { | |
this(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor)); | |
} | |
/** | |
* Returns the integer dividend for this rational converter. | |
* | |
* @return this converter dividend. | |
*/ | |
public BigInteger getDividend() { | |
return dividend; | |
} | |
/** | |
* Returns the integer (positive) divisor for this rational converter. | |
* | |
* @return this converter divisor. | |
*/ | |
public BigInteger getDivisor() { | |
return divisor; | |
} | |
@Override | |
public double convert(double value) { | |
return value * toDouble(dividend) / toDouble(divisor); | |
} | |
// Optimization of BigInteger.doubleValue() (implementation too inneficient). | |
private static double toDouble(BigInteger integer) { | |
return (integer.bitLength() < 64) ? integer.longValue() : integer.doubleValue(); | |
} | |
@Override | |
public BigDecimal convert(BigDecimal value, MathContext ctx) throws ArithmeticException { | |
BigDecimal decimalDividend = new BigDecimal(dividend, 0); | |
BigDecimal decimalDivisor = new BigDecimal(divisor, 0); | |
return value.multiply(decimalDividend, ctx).divide(decimalDivisor, ctx); | |
} | |
@Override | |
public Number convert(Number value) { | |
if (value instanceof BigDecimal) { | |
return convert((BigDecimal) value, MathContext.DECIMAL128); | |
} else { | |
return convert(value.doubleValue()); | |
} | |
} | |
@Override | |
public UnitConverter concatenate(UnitConverter converter) { | |
if (converter instanceof RationalConverter) { | |
RationalConverter that = (RationalConverter) converter; | |
BigInteger newDividend = this.getDividend().multiply(that.getDividend()); | |
BigInteger newDivisor = this.getDivisor().multiply(that.getDivisor()); | |
BigInteger gcd = newDividend.gcd(newDivisor); | |
newDividend = newDividend.divide(gcd); | |
newDivisor = newDivisor.divide(gcd); | |
return (newDividend.equals(BigInteger.ONE) && newDivisor.equals(BigInteger.ONE)) | |
? IDENTITY : new RationalConverter(newDividend, newDivisor); | |
} else | |
return super.concatenate(converter); | |
} | |
@Override | |
public RationalConverter inverse() { | |
return dividend.signum() == -1 ? new RationalConverter(getDivisor().negate(), getDividend().negate()) | |
: new RationalConverter(getDivisor(), getDividend()); | |
} | |
@Override | |
public final String toString() { | |
return "RationalConverter("+ dividend + "," + divisor + ")"; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (!(obj instanceof RationalConverter)) | |
return false; | |
RationalConverter that = (RationalConverter) obj; | |
return this.dividend.equals(that.dividend) && | |
this.divisor.equals(that.divisor); | |
} | |
@Override | |
public int hashCode() { | |
return dividend.hashCode() + divisor.hashCode(); | |
} | |
@Override | |
public boolean isLinear() { | |
return true; | |
} | |
} |