001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2017, 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 1.0, Oct 6, 2016 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(); // TODO should contain ALL elements 114 } 115 116 /** 117 * Returns the product of the specified units. 118 * 119 * @param left 120 * the left unit operand. 121 * @param right 122 * the right unit operand. 123 * @return <code>left * right</code> 124 */ 125 public static Unit<?> getProductInstance(AbstractUnit<?> left, AbstractUnit<?> right) { 126 Element[] leftElems; 127 if (left instanceof ProductUnit<?>) 128 leftElems = ((ProductUnit<?>) left).elements; 129 else 130 leftElems = new Element[] { new Element(left, 1, 1) }; 131 Element[] rightElems; 132 if (right instanceof ProductUnit<?>) 133 rightElems = ((ProductUnit<?>) right).elements; 134 else 135 rightElems = new Element[] { new Element(right, 1, 1) }; 136 return getInstance(leftElems, rightElems); 137 } 138 139 /** 140 * Returns the quotient of the specified units. 141 * 142 * @param left 143 * the dividend unit operand. 144 * @param right 145 * the divisor unit operand. 146 * @return <code>dividend / divisor</code> 147 */ 148 public static Unit<?> getQuotientInstance(Unit<?> left, Unit<?> right) { 149 Element[] leftElems; 150 if (left instanceof ProductUnit<?>) 151 leftElems = ((ProductUnit<?>) left).elements; 152 else 153 leftElems = new Element[] { new Element(left, 1, 1) }; 154 Element[] rightElems; 155 if (right instanceof ProductUnit<?>) { 156 Element[] elems = ((ProductUnit<?>) right).elements; 157 rightElems = new Element[elems.length]; 158 for (int i = 0; i < elems.length; i++) { 159 rightElems[i] = new Element(elems[i].unit, -elems[i].pow, elems[i].root); 160 } 161 } else 162 rightElems = new Element[] { new Element(right, -1, 1) }; 163 return getInstance(leftElems, rightElems); 164 } 165 166 /** 167 * Returns the product unit corresponding to the specified root of the specified unit. 168 * 169 * @param unit 170 * the unit. 171 * @param n 172 * the root's order (n > 0). 173 * @return <code>unit^(1/nn)</code> 174 * @throws ArithmeticException 175 * if <code>n == 0</code>. 176 */ 177 public static Unit<?> getRootInstance(AbstractUnit<?> unit, int n) { 178 Element[] unitElems; 179 if (unit instanceof ProductUnit<?>) { 180 Element[] elems = ((ProductUnit<?>) unit).elements; 181 unitElems = new Element[elems.length]; 182 for (int i = 0; i < elems.length; i++) { 183 int gcd = gcd(Math.abs(elems[i].pow), elems[i].root * n); 184 unitElems[i] = new Element(elems[i].unit, elems[i].pow / gcd, elems[i].root * n / gcd); 185 } 186 } else 187 unitElems = new Element[] { new Element(unit, 1, n) }; 188 return getInstance(unitElems, new Element[0]); 189 } 190 191 /** 192 * Returns the product unit corresponding to this unit raised to the specified exponent. 193 * 194 * @param unit 195 * the unit. 196 * @param nn 197 * the exponent (nn > 0). 198 * @return <code>unit^n</code> 199 */ 200 static Unit<?> getPowInstance(AbstractUnit<?> unit, int n) { 201 Element[] unitElems; 202 if (unit instanceof ProductUnit<?>) { 203 Element[] elems = ((ProductUnit<?>) unit).elements; 204 unitElems = new Element[elems.length]; 205 for (int i = 0; i < elems.length; i++) { 206 int gcd = gcd(Math.abs(elems[i].pow * n), elems[i].root); 207 unitElems[i] = new Element(elems[i].unit, elems[i].pow * n / gcd, elems[i].root / gcd); 208 } 209 } else 210 unitElems = new Element[] { new Element(unit, n, 1) }; 211 return getInstance(unitElems, new Element[0]); 212 } 213 214 /** 215 * Returns the number of unit elements in this product. 216 * 217 * @return the number of unit elements. 218 */ 219 public int getUnitCount() { 220 return elements.length; 221 } 222 223 /** 224 * Returns the unit element at the specified position. 225 * 226 * @param index 227 * the index of the unit element to return. 228 * @return the unit element at the specified position. 229 * @throws IndexOutOfBoundsException 230 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 231 */ 232 public Unit<?> getUnit(int index) { 233 return elements[index].getUnit(); 234 } 235 236 /** 237 * Returns the power exponent of the unit element at the specified position. 238 * 239 * @param index 240 * the index of the unit element. 241 * @return the unit power exponent at the specified position. 242 * @throws IndexOutOfBoundsException 243 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 244 */ 245 public int getUnitPow(int index) { 246 return elements[index].getPow(); 247 } 248 249 /** 250 * Returns the root exponent of the unit element at the specified position. 251 * 252 * @param index 253 * the index of the unit element. 254 * @return the unit root exponent at the specified position. 255 * @throws IndexOutOfBoundsException 256 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 257 */ 258 public int getUnitRoot(int index) { 259 return elements[index].getRoot(); 260 } 261 262 @Override 263 public Map<Unit<?>, Integer> getBaseUnits() { 264 final Map<Unit<?>, Integer> units = new HashMap<Unit<?>, Integer>(); 265 for (int i = 0; i < getUnitCount(); i++) { 266 units.put(getUnit(i), getUnitPow(i)); 267 } 268 return units; 269 } 270 271 @Override 272 public boolean equals(Object that) { 273 if (this == that) 274 return true; 275 if (!(that instanceof ProductUnit<?>)) 276 return false; 277 // Two products are equals if they have the same elements 278 // regardless of the elements' order. 279 Element[] elems = ((ProductUnit<?>) that).elements; 280 if (elements.length != elems.length) 281 return false; 282 for (Element element : elements) { 283 boolean unitFound = false; 284 for (Element elem : elems) { 285 if (element.unit.equals(elem.unit)) 286 if ((element.pow != elem.pow) || (element.root != elem.root)) 287 return false; 288 else { 289 unitFound = true; 290 break; 291 } 292 } 293 if (!unitFound) 294 return false; 295 } 296 return true; 297 } 298 299 @Override 300 public int hashCode() { 301 if (this.hashCode != 0) 302 return this.hashCode; 303 int code = 0; 304 for (Element element : elements) { 305 code += element.unit.hashCode() * (element.pow * 3 - element.root * 2); 306 } 307 this.hashCode = code; 308 return code; 309 } 310 311 @SuppressWarnings("unchecked") 312 @Override 313 public AbstractUnit<Q> toSystemUnit() { 314 Unit<?> systemUnit = AbstractUnit.ONE; 315 for (Element element : elements) { 316 Unit<?> unit = element.unit.getSystemUnit(); 317 unit = unit.pow(element.pow); 318 unit = unit.root(element.root); 319 systemUnit = systemUnit.multiply(unit); 320 } 321 return (AbstractUnit<Q>) systemUnit; 322 } 323 324 public UnitConverter getSystemConverter() { 325 UnitConverter converter = AbstractConverter.IDENTITY; 326 for (Element e : elements) { 327 @SuppressWarnings("rawtypes") 328 UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); // TODO 329 // check 330 // for 331 // type 332 if (!(cvtr.isLinear())) 333 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 334 if (e.root != 1) 335 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 336 int pow = e.pow; 337 if (pow < 0) { // Negative power. 338 pow = -pow; 339 cvtr = cvtr.inverse(); 340 } 341 for (int j = 0; j < pow; j++) { 342 converter = converter.concatenate(cvtr); 343 } 344 } 345 return converter; 346 } 347 348 @Override 349 public Dimension getDimension() { 350 Dimension dimension = QuantityDimension.NONE; 351 for (int i = 0; i < this.getUnitCount(); i++) { 352 Unit<?> unit = this.getUnit(i); 353 if (this.elements != null && unit.getDimension() != null) { 354 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 355 if (dimension != null) { 356 dimension = dimension.multiply(d); 357 } else { 358 dimension = d; // XXX hackaround 359 } 360 } 361 } 362 return dimension; 363 } 364 365 /** 366 * Returns the unit defined from the product of the specified elements. 367 * 368 * @param leftElems 369 * left multiplicand elements. 370 * @param rightElems 371 * right multiplicand elements. 372 * @return the corresponding unit. 373 */ 374 @SuppressWarnings("rawtypes") 375 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 376 377 // Merges left elements with right elements. 378 Element[] result = new Element[leftElems.length + rightElems.length]; 379 int resultIndex = 0; 380 for (Element leftElem : leftElems) { 381 Unit<?> unit = leftElem.unit; 382 int p1 = leftElem.pow; 383 int r1 = leftElem.root; 384 int p2 = 0; 385 int r2 = 1; 386 for (Element rightElem : rightElems) { 387 if (unit.equals(rightElem.unit)) { 388 p2 = rightElem.pow; 389 r2 = rightElem.root; 390 break; // No duplicate. 391 } 392 } 393 int pow = (p1 * r2) + (p2 * r1); 394 int root = r1 * r2; 395 if (pow != 0) { 396 int gcd = gcd(Math.abs(pow), root); 397 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 398 } 399 } 400 401 // Appends remaining right elements not merged. 402 for (Element rightElem : rightElems) { 403 Unit<?> unit = rightElem.unit; 404 boolean hasBeenMerged = false; 405 for (Element leftElem : leftElems) { 406 if (unit.equals(leftElem.unit)) { 407 hasBeenMerged = true; 408 break; 409 } 410 } 411 if (!hasBeenMerged) 412 result[resultIndex++] = rightElem; 413 } 414 415 // Returns or creates instance. 416 if (resultIndex == 0) 417 return AbstractUnit.ONE; 418 else if ((resultIndex == 1) && (result[0].pow == result[0].root)) 419 return result[0].unit; 420 else { 421 Element[] elems = new Element[resultIndex]; 422 System.arraycopy(result, 0, elems, 0, resultIndex); 423 return new ProductUnit(elems); 424 } 425 } 426 427 /** 428 * Returns the greatest common divisor (Euclid's algorithm). 429 * 430 * @param m 431 * the first number. 432 * @param nn 433 * the second number. 434 * @return the greatest common divisor. 435 */ 436 private static int gcd(int m, int n) { 437 if (n == 0) 438 return m; 439 else 440 return gcd(n, m % n); 441 } 442 443 /** 444 * Inner product element represents a rational power of a single unit. 445 */ 446 @SuppressWarnings("rawtypes") 447 private final static class Element implements UnitSupplier { 448 449 /** 450 * 451 */ 452 // private static final long serialVersionUID = 452938412398890507L; 453 454 /** 455 * Holds the single unit. 456 */ 457 private final Unit<?> unit; 458 459 /** 460 * Holds the power exponent. 461 */ 462 private final int pow; 463 464 /** 465 * Holds the root exponent. 466 */ 467 private final int root; 468 469 /** 470 * Structural constructor. 471 * 472 * @param unit 473 * the unit. 474 * @param pow 475 * the power exponent. 476 * @param root 477 * the root exponent. 478 */ 479 private Element(Unit<?> unit, int pow, int root) { 480 this.unit = unit; 481 this.pow = pow; 482 this.root = root; 483 } 484 485 /** 486 * Returns this element's unit. 487 * 488 * @return the single unit. 489 */ 490 public Unit<?> getUnit() { 491 return unit; 492 } 493 494 /** 495 * Returns the power exponent. The power exponent can be negative but is always different from zero. 496 * 497 * @return the power exponent of the single unit. 498 */ 499 public int getPow() { 500 return pow; 501 } 502 503 /** 504 * Returns the root exponent. The root exponent is always greater than zero. 505 * 506 * @return the root exponent of the single unit. 507 */ 508 public int getRoot() { 509 return root; 510 } 511 } 512 513 @Override 514 public String getSymbol() { 515 if (super.getSymbol() != null) { 516 return super.getSymbol(); 517 } 518 return symbol; 519 } 520}