001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2016, Jean-Marie Dautelle, Werner Keil, V2COM. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tec.units.ri.unit; 031 032import java.util.HashMap; 033import java.util.Map; 034 035import javax.measure.Dimension; 036import javax.measure.Quantity; 037import javax.measure.Unit; 038import javax.measure.UnitConverter; 039 040import tec.units.ri.AbstractConverter; 041import tec.units.ri.AbstractUnit; 042import tec.units.ri.quantity.QuantityDimension; 043import tec.uom.lib.common.function.UnitSupplier; 044 045/** 046 * <p> 047 * This class represents units formed by the product of rational powers of existing physical units. 048 * </p> 049 * 050 * <p> 051 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code> 052 * returns <code>METRE</code>. 053 * </p> 054 * 055 * @param <Q> 056 * The type of the quantity measured by this unit. 057 * 058 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 059 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 060 * @version 0.5.5, July 11, 2015 061 */ 062public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> { 063 064 /** 065 * 066 */ 067 // private static final long serialVersionUID = 962983585531030093L; 068 069 /** 070 * Holds the units composing this product unit. 071 */ 072 private final Element[] elements; 073 074 /** 075 * Holds the hashcode (optimization). 076 */ 077 private int hashCode; 078 079 /** 080 * Holds the symbol for this unit. 081 */ 082 private final String symbol; 083 084 /** 085 * Default constructor (used solely to create <code>ONE</code> instance). 086 */ 087 public ProductUnit() { 088 this.symbol = ""; 089 elements = new Element[0]; 090 } 091 092 /** 093 * Copy constructor (allows for parameterization of product units). 094 * 095 * @param productUnit 096 * the product unit source. 097 * @throws ClassCastException 098 * if the specified unit is not a product unit. 099 */ 100 public ProductUnit(Unit<?> productUnit) { 101 this.symbol = productUnit.getSymbol(); 102 this.elements = ((ProductUnit<?>) productUnit).elements; 103 } 104 105 /** 106 * Product unit constructor. 107 * 108 * @param elements 109 * the product elements. 110 */ 111 private ProductUnit(Element[] elements) { 112 this.elements = elements; 113 this.symbol = elements[0].getUnit().getSymbol(); // FIXME this should 114 // contain ALL 115 // elements 116 } 117 118 /** 119 * Returns the product of the specified units. 120 * 121 * @param left 122 * the left unit operand. 123 * @param right 124 * the right unit operand. 125 * @return <code>left * right</code> 126 */ 127 public static Unit<?> getProductInstance(AbstractUnit<?> left, AbstractUnit<?> right) { 128 Element[] leftElems; 129 if (left instanceof ProductUnit<?>) 130 leftElems = ((ProductUnit<?>) left).elements; 131 else 132 leftElems = new Element[] { new Element(left, 1, 1) }; 133 Element[] rightElems; 134 if (right instanceof ProductUnit<?>) 135 rightElems = ((ProductUnit<?>) right).elements; 136 else 137 rightElems = new Element[] { new Element(right, 1, 1) }; 138 return getInstance(leftElems, rightElems); 139 } 140 141 /** 142 * Returns the quotient of the specified units. 143 * 144 * @param left 145 * the dividend unit operand. 146 * @param right 147 * the divisor unit operand. 148 * @return <code>dividend / divisor</code> 149 */ 150 public static Unit<?> getQuotientInstance(Unit<?> left, Unit<?> right) { 151 Element[] leftElems; 152 if (left instanceof ProductUnit<?>) 153 leftElems = ((ProductUnit<?>) left).elements; 154 else 155 leftElems = new Element[] { new Element(left, 1, 1) }; 156 Element[] rightElems; 157 if (right instanceof ProductUnit<?>) { 158 Element[] elems = ((ProductUnit<?>) right).elements; 159 rightElems = new Element[elems.length]; 160 for (int i = 0; i < elems.length; i++) { 161 rightElems[i] = new Element(elems[i].unit, -elems[i].pow, elems[i].root); 162 } 163 } else 164 rightElems = new Element[] { new Element(right, -1, 1) }; 165 return getInstance(leftElems, rightElems); 166 } 167 168 /** 169 * Returns the product unit corresponding to the specified root of the specified unit. 170 * 171 * @param unit 172 * the unit. 173 * @param n 174 * the root's order (n > 0). 175 * @return <code>unit^(1/nn)</code> 176 * @throws ArithmeticException 177 * if <code>n == 0</code>. 178 */ 179 public static Unit<?> getRootInstance(AbstractUnit<?> unit, int n) { 180 Element[] unitElems; 181 if (unit instanceof ProductUnit<?>) { 182 Element[] elems = ((ProductUnit<?>) unit).elements; 183 unitElems = new Element[elems.length]; 184 for (int i = 0; i < elems.length; i++) { 185 int gcd = gcd(Math.abs(elems[i].pow), elems[i].root * n); 186 unitElems[i] = new Element(elems[i].unit, elems[i].pow / gcd, elems[i].root * n / gcd); 187 } 188 } else 189 unitElems = new Element[] { new Element(unit, 1, n) }; 190 return getInstance(unitElems, new Element[0]); 191 } 192 193 /** 194 * Returns the product unit corresponding to this unit raised to the specified exponent. 195 * 196 * @param unit 197 * the unit. 198 * @param nn 199 * the exponent (nn > 0). 200 * @return <code>unit^n</code> 201 */ 202 static Unit<?> getPowInstance(AbstractUnit<?> unit, int n) { 203 Element[] unitElems; 204 if (unit instanceof ProductUnit<?>) { 205 Element[] elems = ((ProductUnit<?>) unit).elements; 206 unitElems = new Element[elems.length]; 207 for (int i = 0; i < elems.length; i++) { 208 int gcd = gcd(Math.abs(elems[i].pow * n), elems[i].root); 209 unitElems[i] = new Element(elems[i].unit, elems[i].pow * n / gcd, elems[i].root / gcd); 210 } 211 } else 212 unitElems = new Element[] { new Element(unit, n, 1) }; 213 return getInstance(unitElems, new Element[0]); 214 } 215 216 /** 217 * Returns the number of unit elements in this product. 218 * 219 * @return the number of unit elements. 220 */ 221 public int getUnitCount() { 222 return elements.length; 223 } 224 225 /** 226 * Returns the unit element at the specified position. 227 * 228 * @param index 229 * the index of the unit element to return. 230 * @return the unit element at the specified position. 231 * @throws IndexOutOfBoundsException 232 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 233 */ 234 public Unit<?> getUnit(int index) { 235 return elements[index].getUnit(); 236 } 237 238 /** 239 * Returns the power exponent of the unit element at the specified position. 240 * 241 * @param index 242 * the index of the unit element. 243 * @return the unit power exponent at the specified position. 244 * @throws IndexOutOfBoundsException 245 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 246 */ 247 public int getUnitPow(int index) { 248 return elements[index].getPow(); 249 } 250 251 /** 252 * Returns the root exponent of the unit element at the specified position. 253 * 254 * @param index 255 * the index of the unit element. 256 * @return the unit root exponent at the specified position. 257 * @throws IndexOutOfBoundsException 258 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 259 */ 260 public int getUnitRoot(int index) { 261 return elements[index].getRoot(); 262 } 263 264 @Override 265 public Map<Unit<?>, Integer> getBaseUnits() { 266 final Map<Unit<?>, Integer> units = new HashMap<Unit<?>, Integer>(); 267 for (int i = 0; i < getUnitCount(); i++) { 268 units.put(getUnit(i), getUnitPow(i)); 269 } 270 return units; 271 } 272 273 @Override 274 public boolean equals(Object that) { 275 if (this == that) 276 return true; 277 if (!(that instanceof ProductUnit<?>)) 278 return false; 279 // Two products are equals if they have the same elements 280 // regardless of the elements' order. 281 Element[] elems = ((ProductUnit<?>) that).elements; 282 if (elements.length != elems.length) 283 return false; 284 for (Element element : elements) { 285 boolean unitFound = false; 286 for (Element elem : elems) { 287 if (element.unit.equals(elem.unit)) 288 if ((element.pow != elem.pow) || (element.root != elem.root)) 289 return false; 290 else { 291 unitFound = true; 292 break; 293 } 294 } 295 if (!unitFound) 296 return false; 297 } 298 return true; 299 } 300 301 @Override 302 public int hashCode() { 303 if (this.hashCode != 0) 304 return this.hashCode; 305 int code = 0; 306 for (Element element : elements) { 307 code += element.unit.hashCode() * (element.pow * 3 - element.root * 2); 308 } 309 this.hashCode = code; 310 return code; 311 } 312 313 @SuppressWarnings("unchecked") 314 @Override 315 public AbstractUnit<Q> toSystemUnit() { 316 Unit<?> systemUnit = AbstractUnit.ONE; 317 for (Element element : elements) { 318 Unit<?> unit = element.unit.getSystemUnit(); 319 unit = unit.pow(element.pow); 320 unit = unit.root(element.root); 321 systemUnit = systemUnit.multiply(unit); 322 } 323 return (AbstractUnit<Q>) systemUnit; 324 } 325 326 public UnitConverter getSystemConverter() { 327 UnitConverter converter = AbstractConverter.IDENTITY; 328 for (Element e : elements) { 329 @SuppressWarnings("rawtypes") 330 UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); // TODO 331 // check 332 // for 333 // type 334 if (!(cvtr.isLinear())) 335 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 336 if (e.root != 1) 337 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 338 int pow = e.pow; 339 if (pow < 0) { // Negative power. 340 pow = -pow; 341 cvtr = cvtr.inverse(); 342 } 343 for (int j = 0; j < pow; j++) { 344 converter = converter.concatenate(cvtr); 345 } 346 } 347 return converter; 348 } 349 350 @Override 351 public Dimension getDimension() { 352 Dimension dimension = QuantityDimension.NONE; 353 for (int i = 0; i < this.getUnitCount(); i++) { 354 Unit<?> unit = this.getUnit(i); 355 if (this.elements != null && unit.getDimension() != null) { 356 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 357 if (dimension != null) { 358 dimension = dimension.multiply(d); 359 } else { 360 dimension = d; // FIXME hackaround 361 } 362 } 363 } 364 return dimension; 365 } 366 367 /** 368 * Returns the unit defined from the product of the specified elements. 369 * 370 * @param leftElems 371 * left multiplicand elements. 372 * @param rightElems 373 * right multiplicand elements. 374 * @return the corresponding unit. 375 */ 376 @SuppressWarnings("rawtypes") 377 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 378 379 // Merges left elements with right elements. 380 Element[] result = new Element[leftElems.length + rightElems.length]; 381 int resultIndex = 0; 382 for (Element leftElem : leftElems) { 383 Unit<?> unit = leftElem.unit; 384 int p1 = leftElem.pow; 385 int r1 = leftElem.root; 386 int p2 = 0; 387 int r2 = 1; 388 for (Element rightElem : rightElems) { 389 if (unit.equals(rightElem.unit)) { 390 p2 = rightElem.pow; 391 r2 = rightElem.root; 392 break; // No duplicate. 393 } 394 } 395 int pow = (p1 * r2) + (p2 * r1); 396 int root = r1 * r2; 397 if (pow != 0) { 398 int gcd = gcd(Math.abs(pow), root); 399 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 400 } 401 } 402 403 // Appends remaining right elements not merged. 404 for (Element rightElem : rightElems) { 405 Unit<?> unit = rightElem.unit; 406 boolean hasBeenMerged = false; 407 for (Element leftElem : leftElems) { 408 if (unit.equals(leftElem.unit)) { 409 hasBeenMerged = true; 410 break; 411 } 412 } 413 if (!hasBeenMerged) 414 result[resultIndex++] = rightElem; 415 } 416 417 // Returns or creates instance. 418 if (resultIndex == 0) 419 return AbstractUnit.ONE; 420 else if ((resultIndex == 1) && (result[0].pow == result[0].root)) 421 return result[0].unit; 422 else { 423 Element[] elems = new Element[resultIndex]; 424 System.arraycopy(result, 0, elems, 0, resultIndex); 425 return new ProductUnit(elems); 426 } 427 } 428 429 /** 430 * Returns the greatest common divisor (Euclid's algorithm). 431 * 432 * @param m 433 * the first number. 434 * @param nn 435 * the second number. 436 * @return the greatest common divisor. 437 */ 438 private static int gcd(int m, int n) { 439 if (n == 0) 440 return m; 441 else 442 return gcd(n, m % n); 443 } 444 445 /** 446 * Inner product element represents a rational power of a single unit. 447 */ 448 @SuppressWarnings("rawtypes") 449 private final static class Element implements UnitSupplier { 450 451 /** 452 * 453 */ 454 // private static final long serialVersionUID = 452938412398890507L; 455 456 /** 457 * Holds the single unit. 458 */ 459 private final Unit<?> unit; 460 461 /** 462 * Holds the power exponent. 463 */ 464 private final int pow; 465 466 /** 467 * Holds the root exponent. 468 */ 469 private final int root; 470 471 /** 472 * Structural constructor. 473 * 474 * @param unit 475 * the unit. 476 * @param pow 477 * the power exponent. 478 * @param root 479 * the root exponent. 480 */ 481 private Element(Unit<?> unit, int pow, int root) { 482 this.unit = unit; 483 this.pow = pow; 484 this.root = root; 485 } 486 487 /** 488 * Returns this element's unit. 489 * 490 * @return the single unit. 491 */ 492 public Unit<?> getUnit() { 493 return unit; 494 } 495 496 /** 497 * Returns the power exponent. The power exponent can be negative but is always different from zero. 498 * 499 * @return the power exponent of the single unit. 500 */ 501 public int getPow() { 502 return pow; 503 } 504 505 /** 506 * Returns the root exponent. The root exponent is always greater than zero. 507 * 508 * @return the root exponent of the single unit. 509 */ 510 public int getRoot() { 511 return root; 512 } 513 } 514 515 @Override 516 public String getSymbol() { 517 if (super.getSymbol() != null) { 518 return super.getSymbol(); 519 } 520 return symbol; 521 } 522}