tec.units.ri.unit.ProductUnit Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unit-ri Show documentation
Show all versions of unit-ri Show documentation
Unit Standard (JSR 363) Reference Implementation.
/*
* Units of Measurement Reference Implementation
* Copyright (c) 2005-2015, Jean-Marie Dautelle, Werner Keil, V2COM.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of JSR-363 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 HOLDER 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 tec.units.ri.unit;
import java.util.HashMap;
import java.util.Map;
import javax.measure.Dimension;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.UnitConverter;
import tec.units.ri.AbstractConverter;
import tec.units.ri.AbstractUnit;
import tec.units.ri.function.UnitSupplier;
import tec.units.ri.quantity.QuantityDimension;
/**
* This class represents units formed by the product of rational powers of
* existing physical units.
*
* This class maintains the canonical form of this product (simplest form
* after factorization). For example: METRE.pow(2).divide(METRE)
* returns METRE
.
*
* @param The type of the quantity measured by this unit.
*
* @author Jean-Marie Dautelle
* @author Werner Keil
* @version 0.5.5, July 11, 2015
*/
public final class ProductUnit> extends AbstractUnit {
/**
*
*/
// private static final long serialVersionUID = 962983585531030093L;
/**
* Holds the units composing this product unit.
*/
private final Element[] elements;
/**
* Holds the hashcode (optimization).
*/
private int hashCode;
/**
* Holds the symbol for this unit.
*/
private final String symbol;
/**
* Default constructor (used solely to create ONE
instance).
*/
public ProductUnit() {
this.symbol = "";
elements = new Element[0];
}
/**
* Copy constructor (allows for parameterization of product units).
*
* @param productUnit the product unit source.
* @throws ClassCastException if the specified unit is not a product unit.
*/
public ProductUnit(Unit> productUnit) {
this.symbol = productUnit.getSymbol();
this.elements = ((ProductUnit>) productUnit).elements;
}
/**
* Product unit constructor.
*
* @param elements the product elements.
*/
private ProductUnit(Element[] elements) {
this.elements = elements;
this.symbol = elements[0].getUnit().getSymbol(); //FIXME this should contain ALL elements
}
/**
* Returns the product of the specified units.
*
* @param left the left unit operand.
* @param right the right unit operand.
* @return left * right
*/
public static Unit> getProductInstance(AbstractUnit> left, AbstractUnit> 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
*/
public 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/nn)
* @throws ArithmeticException if n == 0
.
*/
public static Unit> getRootInstance(AbstractUnit> 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 nn the exponent (nn > 0).
* @return unit^n
*/
static Unit> getPowInstance(AbstractUnit> 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 unit elements in this product.
*
* @return the number of unit elements.
*/
public int getUnitCount() {
return elements.length;
}
/**
* Returns the unit element at the specified position.
*
* @param index the index of the unit element to return.
* @return the unit element at the specified position.
* @throws IndexOutOfBoundsException if index is out of range
* (index < 0 || index >= getUnitCount())
.
*/
public Unit> getUnit(int index) {
return elements[index].getUnit();
}
/**
* Returns the power exponent of the unit element at the specified position.
*
* @param index the index of the unit element.
* @return the unit power exponent at the specified position.
* @throws IndexOutOfBoundsException if index is out of range
* (index < 0 || index >= getUnitCount())
.
*/
public int getUnitPow(int index) {
return elements[index].getPow();
}
/**
* Returns the root exponent of the unit element at the specified position.
*
* @param index the index of the unit element.
* @return the unit root exponent at the specified position.
* @throws IndexOutOfBoundsException if index is out of range
* (index < 0 || index >= getUnitCount())
.
*/
public int getUnitRoot(int index) {
return elements[index].getRoot();
}
@Override
public Map, Integer> getProductUnits() {
final Map, Integer> units = new HashMap, Integer>();
for (int i = 0; i < getUnitCount(); i++) {
units.put(getUnit(i), getUnitPow(i));
}
return units;
}
@Override
public boolean equals(Object that) {
if (this == that)
return true;
if (!(that instanceof ProductUnit>))
return false;
// 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)
return false;
for (Element element : elements) {
boolean unitFound = false;
for (Element elem : elems) {
if (element.unit.equals(elem.unit))
if ((element.pow != elem.pow) || (element.root != elem.root))
return false;
else {
unitFound = true;
break;
}
}
if (!unitFound)
return false;
}
return true;
}
@Override
public int hashCode() {
if (this.hashCode != 0)
return this.hashCode;
int code = 0;
for (Element element : elements) {
code += element.unit.hashCode() * (element.pow * 3 - element.root * 2);
}
this.hashCode = code;
return code;
}
@SuppressWarnings("unchecked")
@Override
public AbstractUnit toSystemUnit() {
Unit> systemUnit = Units.ONE;
for (Element element : elements) {
Unit> unit = element.unit.getSystemUnit();
unit = unit.pow(element.pow);
unit = unit.root(element.root);
systemUnit = systemUnit.multiply(unit);
}
return (AbstractUnit) systemUnit;
}
public UnitConverter getSystemConverter() {
UnitConverter converter = AbstractConverter.IDENTITY;
for (Element e : elements) {
@SuppressWarnings("rawtypes")
UnitConverter cvtr = ((AbstractUnit)e.unit).getSystemConverter(); // TODO check for type
if (!(cvtr.isLinear()))
throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert");
if (e.root != 1)
throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent");
int pow = e.pow;
if (pow < 0) { // Negative power.
pow = -pow;
cvtr = cvtr.inverse();
}
for (int j = 0; j < pow; j++) {
converter = converter.concatenate(cvtr);
}
}
return converter;
}
@Override
public Dimension getDimension() {
Dimension dimension = QuantityDimension.NONE;
for (int i = 0; i < this.getUnitCount(); i++) {
Unit> unit = this.getUnit(i);
if (this.elements != null && unit.getDimension() != null) {
Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i));
if (dimension!=null) {
dimension = dimension.multiply(d);
} else {
dimension = d; // FIXME hackaround
}
}
}
return dimension;
}
/**
* Returns the unit defined from the product of the specified elements.
*
* @param leftElems left multiplicand elements.
* @param rightElems right multiplicand elements.
* @return the corresponding unit.
*/
@SuppressWarnings("rawtypes")
private 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 (Element leftElem : leftElems) {
Unit> unit = leftElem.unit;
int p1 = leftElem.pow;
int r1 = leftElem.root;
int p2 = 0;
int r2 = 1;
for (Element rightElem : rightElems) {
if (unit.equals(rightElem.unit)) {
p2 = rightElem.pow;
r2 = rightElem.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 (Element rightElem : rightElems) {
Unit> unit = rightElem.unit;
boolean hasBeenMerged = false;
for (Element leftElem : leftElems) {
if (unit.equals(leftElem.unit)) {
hasBeenMerged = true;
break;
}
}
if (!hasBeenMerged)
result[resultIndex++] = rightElem;
}
// Returns or creates instance.
if (resultIndex == 0)
return Units.ONE;
else if ((resultIndex == 1) && (result[0].pow == result[0].root))
return result[0].unit;
else {
Element[] elems = new Element[resultIndex];
System.arraycopy(result, 0, elems, 0, resultIndex);
return new ProductUnit(elems);
}
}
/**
* Returns the greatest common divisor (Euclid's algorithm).
*
* @param m the first number.
* @param nn 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.
*/
@SuppressWarnings("rawtypes")
private final static class Element implements UnitSupplier {
/**
*
*/
// private static final long serialVersionUID = 452938412398890507L;
/**
* 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) {
this.unit = unit;
this.pow = pow;
this.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;
}
}
@Override
public String getSymbol() {
if (super.getSymbol() != null) {
return super.getSymbol();
}
return symbol;
}
}