/* * Copyright (c) 2004, JSR-108 group (http://www.jcp.org/en/jsr/detail?id=108) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - Neither the name of the JSR-108 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package javax.units; // J2SE direct dependencies import java.io.Serializable; /** *
This class represents a product unit. Product units are formed by * the product of rational powers of existing units.
* This class maintains the canonical form of this product (simplest
* form after factorization). For example:
* METER.pow(2).divide(METER)
returns
* METER
.
ONE
instance).
*/
ProductUnit() { // Package private.
super(null); // No symbol.
_elements = new Element[0];
}
/**
* Product unit constructor.
*
* @param elements the product elements.
*/
private ProductUnit(Element[] elements) {
super(null); // No symbol.
_elements = elements;
}
/**
* Returns the unit defined from the product of the specifed elements.
*
* @param leftElems left multiplicand elements.
* @param rightElems right multiplicand elements.
* @return the corresponding unit.
*/
static Unit getInstance(Element[] leftElems, Element[] rightElems) {
// Merges left elements with right elements.
Element[] result = new Element[leftElems.length + rightElems.length];
int resultIndex = 0;
for (int i=0; i < leftElems.length; i++) {
Unit unit = leftElems[i]._unit;
int p1 = leftElems[i]._pow;
int r1 = leftElems[i]._root;
int p2 = 0;
int r2 = 1;
for (int j = 0; j < rightElems.length; j++) {
if (unit == rightElems[j]._unit) {
p2 = rightElems[j]._pow;
r2 = rightElems[j]._root;
break; // No duplicate.
}
}
int pow = (p1 * r2) + (p2 * r1);
int root = r1 * r2;
if (pow != 0) {
int gcd = gcd(Math.abs(pow), root);
result[resultIndex++] = new Element(unit, pow/gcd, root/gcd);
}
}
// Appends remaining right elements not merged.
for (int i=0; i < rightElems.length; i++) {
Unit unit = rightElems[i]._unit;
boolean hasBeenMerged = false;
for (int j = 0; j < leftElems.length; j++) {
if (unit == leftElems[j]._unit) {
hasBeenMerged = true;
break;
}
}
if (!hasBeenMerged) {
result[resultIndex++] = rightElems[i];
}
}
// Returns or creates instance.
if (resultIndex == 0) {
return ONE;
} else if ( (resultIndex == 1) && (result[0]._pow == result[0]._root) ){
return result[0]._unit;
} else {
Element[] elems = new Element[resultIndex];
for (int i=0; i < resultIndex; i++) {
elems[i] = result[i];
}
return getInstance(new ProductUnit(elems));
}
}
/**
* Returns the product of the specified units.
*
* @param left the left unit operand.
* @param right the right unit operand.
* @return left * right
*/
static Unit getProductInstance(Unit left, Unit right) {
Element[] leftElems;
if (left instanceof ProductUnit) {
leftElems = ((ProductUnit)left)._elements;
} else {
leftElems = new Element[] { new Element(left, 1, 1) };
}
Element[] rightElems;
if (right instanceof ProductUnit) {
rightElems = ((ProductUnit)right)._elements;
} else {
rightElems = new Element[] { new Element(right, 1, 1) };
}
return getInstance(leftElems, rightElems);
}
/**
* Returns the quotient of the specified units.
*
* @param left the dividend unit operand.
* @param right the divisor unit operand.
* @return dividend / divisor
*/
static Unit getQuotientInstance(Unit left, Unit right) {
Element[] leftElems;
if (left instanceof ProductUnit) {
leftElems = ((ProductUnit)left)._elements;
} else {
leftElems = new Element[] { new Element(left, 1, 1) };
}
Element[] rightElems;
if (right instanceof ProductUnit) {
Element[] elems = ((ProductUnit)right)._elements;
rightElems = new Element[elems.length];
for (int i=0; i < elems.length; i++) {
rightElems[i] = new Element(
elems[i]._unit, -elems[i]._pow, elems[i]._root);
}
} else {
rightElems = new Element[] { new Element(right, -1, 1) };
}
return getInstance(leftElems, rightElems);
}
/**
* Returns the product unit corresponding to the specified root of
* the specified unit.
*
* @param unit the unit.
* @param n the root's order (n > 0).
* @return unit^(1/n)
* @throws ArithmeticException if n == 0
.
*/
static Unit getRootInstance(Unit unit, int n) {
Element[] unitElems;
if (unit instanceof ProductUnit) {
Element[] elems = ((ProductUnit)unit)._elements;
unitElems = new Element[elems.length];
for (int i=0; i < elems.length; i++) {
int gcd = gcd(Math.abs(elems[i]._pow), elems[i]._root * n);
unitElems[i] = new Element(
elems[i]._unit,
elems[i]._pow / gcd,
elems[i]._root * n / gcd);
}
} else {
unitElems = new Element[] { new Element(unit, 1, n) };
}
return getInstance(unitElems, new Element[0]);
}
/**
* Returns the product unit corresponding to this unit raised to
* the specified exponent.
*
* @param unit the unit.
* @param n the exponent (n > 0).
* @return unit^n
*/
static Unit getPowInstance(Unit unit, int n) {
Element[] unitElems;
if (unit instanceof ProductUnit) {
Element[] elems = ((ProductUnit)unit)._elements;
unitElems = new Element[elems.length];
for (int i=0; i < elems.length; i++) {
int gcd = gcd(Math.abs(elems[i]._pow * n), elems[i]._root);
unitElems[i] = new Element(
elems[i]._unit,
elems[i]._pow * n / gcd,
elems[i]._root / gcd);
}
} else {
unitElems = new Element[] { new Element(unit, n, 1) };
}
return getInstance(unitElems, new Element[0]);
}
/**
* Returns the number of units in this product.
*
* @return the number of units being multiplied.
*/
public int size() {
return _elements.length;
}
/**
* Returns the product element at the specified position.
*
* @param index the index of the element to return.
* @return the element at the specified position.
* @throws IndexOutOfBoundsException if index is out of range
* (index < 0 || index >= size())
.
*/
public Element get(int index) {
return _elements[index];
}
// Implements abstract method.
public Unit getSystemUnit() {
if (_systemUnitCache == null) {
Unit systemUnit = ONE;
for (int i=0; i < _elements.length; i++) {
Unit unit = _elements[i]._unit.getSystemUnit();
unit = unit.pow(_elements[i]._pow);
unit = unit.root(_elements[i]._root);
systemUnit = systemUnit.multiply(unit);
}
_systemUnitCache = systemUnit;
}
return _systemUnitCache;
}
private transient Unit _systemUnitCache;
// Implements abstract method.
public boolean equals(Object that) {
if (this == that) {
return true;
} else if (that instanceof ProductUnit) {
// Two products are equals if they have the same elements
// regardless of the elements' order.
Element[] elems = ((ProductUnit)that)._elements;
if (_elements.length == elems.length) {
for (int i=0; i < _elements.length; i++) {
boolean unitFound = false;
for (int j=0; j < elems.length; j++) {
if (_elements[i]._unit == elems[j]._unit) {
if ( (_elements[i]._pow != elems[j]._pow) ||
(_elements[i]._root != elems[j]._root) ) {
return false;
} else {
unitFound = true;
break;
}
}
}
if (!unitFound) {
return false;
}
}
return true;
}
}
return false;
}
// Implements abstract method.
int calculateHashCode() { // Package private.
int code = 0;
for (int i=0; i < _elements.length; i++) {
code += _elements[i]._unit.hashCode() *
(_elements[i]._pow * 3 - _elements[i]._root * 2);
}
return code;
}
// Implements abstract method.
Unit getCtxDimension() {
Unit dimension = ONE;
for (int i=0; i < _elements.length; i++) {
Unit unit = _elements[i]._unit.getCtxDimension();
unit = unit.pow(_elements[i]._pow);
unit = unit.root(_elements[i]._root);
dimension = dimension.multiply(unit);
}
return dimension;
}
// Implements abstract method.
Converter getCtxToDimension() {
double factor = 1.0;
for (int i=0; i < _elements.length; i++) {
Converter toDimension = _elements[i]._unit.getCtxToDimension();
if (toDimension.isLinear()) {
factor *= Math.pow(toDimension.derivative(0),
((double)_elements[i]._pow) /
((double)_elements[i]._root));
} else {
// Non-linear, cannot convert.
throw new ConversionException(
_elements[i]._unit + " is non-linear, cannot convert");
}
}
if (Math.abs(factor - 1.0) < 1e-9) {
return Converter.IDENTITY;
} else {
return new MultiplyConverter(factor);
}
}
/**
* Returns the greatest common divisor (Euclid's algorithm).
*
* @param m the first number.
* @param n the second number.
* @return the greatest common divisor.
*/
private static int gcd(int m, int n) {
if (n == 0) {
return m;
} else {
return gcd(n, m % n);
}
}
/**
* Inner product element represents a rational power of a single unit.
*/
public final static class Element implements Serializable {
/**
* Holds the single unit.
*/
private final Unit _unit;
/**
* Holds the power exponent.
*/
private final int _pow;
/**
* Holds the root exponent.
*/
private final int _root;
/**
* Structural constructor.
*
* @param unit the unit.
* @param pow the power exponent.
* @param root the root exponent.
*/
private Element(Unit unit, int pow, int root) {
_unit = unit;
_pow = pow;
_root = root;
}
/**
* Returns this element's unit.
*
* @return the single unit.
*/
public Unit getUnit() {
return _unit;
}
/**
* Returns the power exponent. The power exponent can be negative
* but is always different from zero.
*
* @return the power exponent of the single unit.
*/
public int getPow() {
return _pow;
}
/**
* Returns the root exponent. The root exponent is always greater
* than zero.
*
* @return the root exponent of the single unit.
*/
public int getRoot() {
return _root;
}
}
}