001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 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-385, Indriya nor the names of their 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 tech.units.indriya.unit; 031 032import javax.measure.Dimension; 033import javax.measure.Quantity; 034import javax.measure.Unit; 035import javax.measure.UnitConverter; 036 037import tech.units.indriya.AbstractConverter; 038import tech.units.indriya.AbstractUnit; 039import tech.units.indriya.quantity.QuantityDimension; 040 041import java.io.Serializable; 042import java.util.LinkedHashMap; 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 1.5, August 14, 2018 062 * @since 1.0 063 */ 064public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> { 065 066 /** 067 * 068 */ 069 private static final long serialVersionUID = 962983585531030093L; 070 071 /** 072 * Holds the units composing this product unit. 073 */ 074 private final Element[] elements; 075 076 /** 077 * Holds the symbol for this unit. 078 */ 079 private final String symbol; 080 081 /** 082 * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance). 083 */ 084 public ProductUnit() { 085 this.symbol = ""; 086 elements = new Element[0]; 087 } 088 089 /** 090 * Copy constructor (allows for parameterization of product units). 091 * 092 * @param productUnit 093 * the product unit source. 094 * @throws ClassCastException 095 * if the specified unit is not a product unit. 096 */ 097 public ProductUnit(Unit<?> productUnit) { 098 this.symbol = productUnit.getSymbol(); 099 this.elements = ((ProductUnit<?>) productUnit).elements; 100 } 101 102 /** 103 * Product unit constructor. 104 * 105 * @param elements 106 * the product elements. 107 */ 108 private ProductUnit(Element[] elements) { 109 this.elements = elements; 110 // this.symbol = elements[0].getUnit().getSymbol(); // FIXME this should contain ALL elements 111 this.symbol = null; 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<?> ofProduct(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<?> ofQuotient(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<?> ofRoot(Unit<?> 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 public static Unit<?> ofPow(Unit<?> 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 @Override 215 public Unit<?> pow(int n) { 216 return new ProductUnit<>(new Element[] { new Element(this, n, 1) }); 217 } 218 219 /** 220 * Returns the number of unit elements in this product. 221 * 222 * @return the number of unit elements. 223 */ 224 public int getUnitCount() { 225 return elements.length; 226 } 227 228 /** 229 * Returns the unit element at the specified position. 230 * 231 * @param index 232 * the index of the unit element to return. 233 * @return the unit element at the specified position. 234 * @throws IndexOutOfBoundsException 235 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 236 */ 237 public Unit<?> getUnit(int index) { 238 return elements[index].getUnit(); 239 } 240 241 /** 242 * Returns the power exponent of the unit element at the specified position. 243 * 244 * @param index 245 * the index of the unit element. 246 * @return the unit power exponent at the specified position. 247 * @throws IndexOutOfBoundsException 248 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 249 */ 250 public int getUnitPow(int index) { 251 return elements[index].getPow(); 252 } 253 254 /** 255 * Returns the root exponent of the unit element at the specified position. 256 * 257 * @param index 258 * the index of the unit element. 259 * @return the unit root exponent at the specified position. 260 * @throws IndexOutOfBoundsException 261 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 262 */ 263 public int getUnitRoot(int index) { 264 return elements[index].getRoot(); 265 } 266 267 @Override 268 public Map<Unit<?>, Integer> getBaseUnits() { 269 final Map<Unit<?>, Integer> units = new LinkedHashMap<>(); 270 for (int i = 0; i < getUnitCount(); i++) { 271 units.put(getUnit(i), getUnitPow(i)); 272 } 273 return units; 274 } 275 276 @Override 277 public boolean equals(Object obj) { 278 if (this == obj) { 279 return true; 280 } 281 if (obj instanceof ProductUnit<?>) { 282 Element[] elems = ((ProductUnit<?>) obj).elements; 283 if (elements.length != elems.length) 284 return false; 285 for (Element element : elements) { 286 boolean unitFound = false; 287 for (Element elem : elems) { 288 if (element.unit.equals(elem.unit)) 289 if ((element.pow != elem.pow) || (element.root != elem.root)) 290 return false; 291 else { 292 unitFound = true; 293 break; 294 } 295 } 296 if (!unitFound) 297 return false; 298 } 299 return true; 300 } 301 if (obj instanceof AbstractUnit) { 302 return AbstractUnit.Equalizer.areEqual(this, (AbstractUnit) obj); 303 } else { 304 return false; 305 } 306 } 307 308 @Override 309 public int hashCode() { 310 return Objects.hash((Object[]) elements); 311 } 312 313 @SuppressWarnings("unchecked") 314 @Override 315 public Unit<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 @Override 327 public UnitConverter getSystemConverter() { 328 UnitConverter converter = AbstractConverter.IDENTITY; 329 for (Element e : elements) { 330 if (e.unit instanceof AbstractUnit) { 331 UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); 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 } 346 return converter; 347 } 348 349 @Override 350 public Dimension getDimension() { 351 Dimension dimension = QuantityDimension.NONE; 352 for (int i = 0; i < this.getUnitCount(); i++) { 353 Unit<?> unit = this.getUnit(i); 354 if (this.elements != null && unit.getDimension() != null) { 355 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 356 dimension = dimension.multiply(d); 357 } 358 } 359 return dimension; 360 } 361 362 /** 363 * Returns the unit defined from the product of the specified elements. 364 * 365 * @param leftElems 366 * left multiplicand elements. 367 * @param rightElems 368 * right multiplicand elements. 369 * @return the corresponding unit. 370 */ 371 @SuppressWarnings("rawtypes") 372 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 373 374 // Merges left elements with right elements. 375 Element[] result = new Element[leftElems.length + rightElems.length]; 376 int resultIndex = 0; 377 for (Element leftElem : leftElems) { 378 Unit<?> unit = leftElem.unit; 379 int p1 = leftElem.pow; 380 int r1 = leftElem.root; 381 int p2 = 0; 382 int r2 = 1; 383 for (Element rightElem : rightElems) { 384 if (unit.equals(rightElem.unit)) { 385 p2 = rightElem.pow; 386 r2 = rightElem.root; 387 break; // No duplicate. 388 } 389 } 390 int pow = (p1 * r2) + (p2 * r1); 391 int root = r1 * r2; 392 if (pow != 0) { 393 int gcd = gcd(Math.abs(pow), root); 394 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 395 } 396 } 397 398 // Appends remaining right elements not merged. 399 for (Element rightElem : rightElems) { 400 Unit<?> unit = rightElem.unit; 401 boolean hasBeenMerged = false; 402 for (Element leftElem : leftElems) { 403 if (unit.equals(leftElem.unit)) { 404 hasBeenMerged = true; 405 break; 406 } 407 } 408 if (!hasBeenMerged) 409 result[resultIndex++] = rightElem; 410 } 411 412 // Returns or creates instance. 413 if (resultIndex == 0) 414 return AbstractUnit.ONE; 415 else if ((resultIndex == 1) && (result[0].pow == result[0].root)) 416 return result[0].unit; 417 else { 418 Element[] elems = new Element[resultIndex]; 419 System.arraycopy(result, 0, elems, 0, resultIndex); 420 return new ProductUnit(elems); 421 } 422 } 423 424 /** 425 * Returns the greatest common divisor (Euclid's algorithm). 426 * 427 * @param m 428 * the first number. 429 * @param nn 430 * the second number. 431 * @return the greatest common divisor. 432 */ 433 private static int gcd(int m, int n) { 434 if (n == 0) 435 return m; 436 else 437 return gcd(n, m % n); 438 } 439 440 /** 441 * Inner product element represents a rational power of a single unit. 442 */ 443 private final static class Element implements Serializable { 444 445 /** 446 * 447 */ 448 private static final long serialVersionUID = 452938412398890507L; 449 450 /** 451 * Holds the single unit. 452 */ 453 private final Unit<?> unit; 454 455 /** 456 * Holds the power exponent. 457 */ 458 private final int pow; 459 460 /** 461 * Holds the root exponent. 462 */ 463 private final int root; 464 465 /** 466 * Structural constructor. 467 * 468 * @param unit 469 * the unit. 470 * @param pow 471 * the power exponent. 472 * @param root 473 * the root exponent. 474 */ 475 private Element(Unit<?> unit, int pow, int root) { 476 this.unit = unit; 477 this.pow = pow; 478 this.root = root; 479 } 480 481 /** 482 * Returns this element's unit. 483 * 484 * @return the single unit. 485 */ 486 public Unit<?> getUnit() { 487 return unit; 488 } 489 490 /** 491 * Returns the power exponent. The power exponent can be negative but is always different from zero. 492 * 493 * @return the power exponent of the single unit. 494 */ 495 public int getPow() { 496 return pow; 497 } 498 499 /** 500 * Returns the root exponent. The root exponent is always greater than zero. 501 * 502 * @return the root exponent of the single unit. 503 */ 504 public int getRoot() { 505 return root; 506 } 507 508 @Override 509 public boolean equals(Object o) { 510 if (this == o) 511 return true; 512 if (o == null || getClass() != o.getClass()) 513 return false; 514 515 Element element = (Element) o; 516 517 if (pow != element.pow) { 518 return false; 519 } 520 return root == element.root && (unit != null ? unit.equals(element.unit) : element.unit == null); 521 522 } 523 524 @Override 525 public int hashCode() { 526 int result = unit != null ? unit.hashCode() : 0; 527 result = 31 * result + pow; 528 result = 31 * result + root; 529 return result; 530 } 531 } 532 533 @Override 534 public String getSymbol() { 535 return symbol; 536 } 537}