001/* 002 * Units of Measurement Implementation for Java SE 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.uom.se.unit; 031 032import tec.uom.se.AbstractConverter; 033import tec.uom.se.AbstractUnit; 034import tec.uom.se.quantity.QuantityDimension; 035 036import javax.measure.Dimension; 037import javax.measure.Quantity; 038import javax.measure.Unit; 039import javax.measure.UnitConverter; 040 041import java.io.Serializable; 042import java.util.HashMap; 043import java.util.Map; 044import java.util.Objects; 045 046/** 047 * <p> 048 * This class represents units formed by the product of rational powers of existing physical units. 049 * </p> 050 * 051 * <p> 052 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code> 053 * returns <code>METRE</code>. 054 * </p> 055 * 056 * @param <Q> 057 * The type of the quantity measured by this unit. 058 * 059 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 060 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 061 * @version 0.3, June 10, 2014 062 */ 063public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> { 064 065 /** 066 * 067 */ 068 private static final long serialVersionUID = 962983585531030093L; 069 070 /** 071 * Holds the units composing this product unit. 072 */ 073 private final Element[] elements; 074 075 /** 076 * Holds the symbol for this unit. 077 */ 078 private final String symbol; 079 080 /** 081 * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance). 082 */ 083 public ProductUnit() { 084 this.symbol = ""; 085 elements = new Element[0]; 086 } 087 088 /** 089 * Copy constructor (allows for parameterization of product units). 090 * 091 * @param productUnit 092 * the product unit source. 093 * @throws ClassCastException 094 * if the specified unit is not a product unit. 095 */ 096 public ProductUnit(Unit<?> productUnit) { 097 this.symbol = productUnit.getSymbol(); 098 this.elements = ((ProductUnit<?>) productUnit).elements; 099 } 100 101 /** 102 * Product unit constructor. 103 * 104 * @param elements 105 * the product elements. 106 */ 107 private ProductUnit(Element[] elements) { 108 this.elements = elements; 109 this.symbol = elements[0].getUnit().getSymbol(); // FIXME this should 110 // contain ALL 111 // elements 112 } 113 114 /** 115 * Returns the product of the specified units. 116 * 117 * @param left 118 * the left unit operand. 119 * @param right 120 * the right unit operand. 121 * @return <code>left * right</code> 122 */ 123 public static Unit<?> getProductInstance(Unit<?> left, Unit<?> right) { 124 Element[] leftElems; 125 if (left instanceof ProductUnit<?>) { 126 leftElems = ((ProductUnit<?>) left).elements; 127 } else { 128 leftElems = new Element[] { new Element(left, 1, 1) }; 129 } 130 Element[] rightElems; 131 if (right instanceof ProductUnit<?>) { 132 rightElems = ((ProductUnit<?>) right).elements; 133 } else { 134 rightElems = new Element[] { new Element(right, 1, 1) }; 135 } 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<>(); // Diamond (Java7+) 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 obj) { 273 if (this == obj) { 274 return true; 275 } 276 if (obj instanceof ProductUnit<?>) { 277 Element[] elems = ((ProductUnit<?>) obj).elements; 278 if (elements.length != elems.length) 279 return false; 280 for (Element element : elements) { 281 boolean unitFound = false; 282 for (Element elem : elems) { 283 if (element.unit.equals(elem.unit)) 284 if ((element.pow != elem.pow) || (element.root != elem.root)) 285 return false; 286 else { 287 unitFound = true; 288 break; 289 } 290 } 291 if (!unitFound) 292 return false; 293 } 294 return true; 295 } 296 return false; 297 } 298 299 @Override 300 public int hashCode() { 301 return Objects.hash((Object[]) elements); 302 } 303 304 @SuppressWarnings("unchecked") 305 @Override 306 public AbstractUnit<Q> toSystemUnit() { 307 Unit<?> systemUnit = AbstractUnit.ONE; 308 for (Element element : elements) { 309 Unit<?> unit = element.unit.getSystemUnit(); 310 unit = unit.pow(element.pow); 311 unit = unit.root(element.root); 312 systemUnit = systemUnit.multiply(unit); 313 } 314 return (AbstractUnit<Q>) systemUnit; 315 } 316 317 @Override 318 public UnitConverter getSystemConverter() { 319 UnitConverter converter = AbstractConverter.IDENTITY; 320 for (Element e : elements) { 321 if (e.unit instanceof AbstractUnit) { 322 UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); 323 if (!(cvtr.isLinear())) 324 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 325 if (e.root != 1) 326 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 327 int pow = e.pow; 328 if (pow < 0) { // Negative power. 329 pow = -pow; 330 cvtr = cvtr.inverse(); 331 } 332 for (int j = 0; j < pow; j++) { 333 converter = converter.concatenate(cvtr); 334 } 335 } 336 } 337 return converter; 338 } 339 340 @Override 341 public Dimension getDimension() { 342 Dimension dimension = QuantityDimension.NONE; 343 for (int i = 0; i < this.getUnitCount(); i++) { 344 Unit<?> unit = this.getUnit(i); 345 if (this.elements != null && unit.getDimension() != null) { 346 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 347 dimension = dimension.multiply(d); 348 } 349 } 350 return dimension; 351 } 352 353 /** 354 * Returns the unit defined from the product of the specified elements. 355 * 356 * @param leftElems 357 * left multiplicand elements. 358 * @param rightElems 359 * right multiplicand elements. 360 * @return the corresponding unit. 361 */ 362 @SuppressWarnings("rawtypes") 363 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 364 365 // Merges left elements with right elements. 366 Element[] result = new Element[leftElems.length + rightElems.length]; 367 int resultIndex = 0; 368 for (Element leftElem : leftElems) { 369 Unit<?> unit = leftElem.unit; 370 int p1 = leftElem.pow; 371 int r1 = leftElem.root; 372 int p2 = 0; 373 int r2 = 1; 374 for (Element rightElem : rightElems) { 375 if (unit.equals(rightElem.unit)) { 376 p2 = rightElem.pow; 377 r2 = rightElem.root; 378 break; // No duplicate. 379 } 380 } 381 int pow = (p1 * r2) + (p2 * r1); 382 int root = r1 * r2; 383 if (pow != 0) { 384 int gcd = gcd(Math.abs(pow), root); 385 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 386 } 387 } 388 389 // Appends remaining right elements not merged. 390 for (Element rightElem : rightElems) { 391 Unit<?> unit = rightElem.unit; 392 boolean hasBeenMerged = false; 393 for (Element leftElem : leftElems) { 394 if (unit.equals(leftElem.unit)) { 395 hasBeenMerged = true; 396 break; 397 } 398 } 399 if (!hasBeenMerged) 400 result[resultIndex++] = rightElem; 401 } 402 403 // Returns or creates instance. 404 if (resultIndex == 0) 405 return AbstractUnit.ONE; 406 else if ((resultIndex == 1) && (result[0].pow == result[0].root)) 407 return result[0].unit; 408 else { 409 Element[] elems = new Element[resultIndex]; 410 System.arraycopy(result, 0, elems, 0, resultIndex); 411 return new ProductUnit(elems); 412 } 413 } 414 415 /** 416 * Returns the greatest common divisor (Euclid's algorithm). 417 * 418 * @param m 419 * the first number. 420 * @param nn 421 * the second number. 422 * @return the greatest common divisor. 423 */ 424 private static int gcd(int m, int n) { 425 if (n == 0) 426 return m; 427 else 428 return gcd(n, m % n); 429 } 430 431 /** 432 * Inner product element represents a rational power of a single unit. 433 */ 434 private final static class Element implements Serializable { 435 436 /** 437 * 438 */ 439 private static final long serialVersionUID = 452938412398890507L; 440 441 /** 442 * Holds the single unit. 443 */ 444 private final Unit<?> unit; 445 446 /** 447 * Holds the power exponent. 448 */ 449 private final int pow; 450 451 /** 452 * Holds the root exponent. 453 */ 454 private final int root; 455 456 /** 457 * Structural constructor. 458 * 459 * @param unit 460 * the unit. 461 * @param pow 462 * the power exponent. 463 * @param root 464 * the root exponent. 465 */ 466 private Element(Unit<?> unit, int pow, int root) { 467 this.unit = unit; 468 this.pow = pow; 469 this.root = root; 470 } 471 472 /** 473 * Returns this element's unit. 474 * 475 * @return the single unit. 476 */ 477 public Unit<?> getUnit() { 478 return unit; 479 } 480 481 /** 482 * Returns the power exponent. The power exponent can be negative but is always different from zero. 483 * 484 * @return the power exponent of the single unit. 485 */ 486 public int getPow() { 487 return pow; 488 } 489 490 /** 491 * Returns the root exponent. The root exponent is always greater than zero. 492 * 493 * @return the root exponent of the single unit. 494 */ 495 public int getRoot() { 496 return root; 497 } 498 499 @Override 500 public boolean equals(Object o) { 501 if (this == o) 502 return true; 503 if (o == null || getClass() != o.getClass()) 504 return false; 505 506 Element element = (Element) o; 507 508 if (pow != element.pow) { 509 return false; 510 } 511 return root == element.root && (unit != null ? unit.equals(element.unit) : element.unit == null); 512 513 } 514 515 @Override 516 public int hashCode() { 517 int result = unit != null ? unit.hashCode() : 0; 518 result = 31 * result + pow; 519 result = 31 * result + root; 520 return result; 521 } 522 } 523 524 @Override 525 public String getSymbol() { 526 return symbol; 527 } 528}