001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2023, 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 java.io.InvalidObjectException; 033import java.io.ObjectInputStream; 034import java.io.Serializable; 035import java.util.Arrays; 036import java.util.LinkedHashMap; 037import java.util.Map; 038import java.util.Objects; 039 040import javax.measure.Dimension; 041import javax.measure.Quantity; 042import javax.measure.Unit; 043import javax.measure.UnitConverter; 044 045import tech.units.indriya.AbstractUnit; 046import tech.units.indriya.function.AbstractConverter; 047import tech.units.indriya.internal.function.Lazy; 048 049/** 050 * <p> 051 * This class represents units formed by the product of rational powers of existing physical units. 052 * </p> 053 * 054 * <p> 055 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code> 056 * returns <code>METRE</code>. 057 * </p> 058 * 059 * @param <Q> 060 * The type of the quantity measured by this unit. 061 * 062 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 063 * @author <a href="mailto:werner@units.tech">Werner Keil</a> 064 * @author Andi Huber 065 * @version 2.0, November 21, 2020 066 * @since 1.0 067 */ 068public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> { 069 070 /** 071 * 072 */ 073 private static final long serialVersionUID = 962983585531030093L; 074 075 /** 076 * Holds the units composing this product unit. 077 * 078 * <dl> 079 * <dt><span class="strong">Implementation Note:</span></dt><dd>considered immutable after constructor was called</dd> 080 * </dl> 081 */ 082 private final Element[] elements; 083 084 /** 085 * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance). 086 */ 087 public ProductUnit() { 088 super(""); 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 super(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 super(null); 113 this.elements = 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<?> ofProduct(Unit<?> left, Unit<?> 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 } 132 Element[] rightElems; 133 if (right instanceof ProductUnit<?>) { 134 rightElems = ((ProductUnit<?>) right).elements; 135 } else { 136 rightElems = new Element[] { new Element(right, 1, 1) }; 137 } 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<?> ofQuotient(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<?> ofRoot(Unit<?> 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 public static Unit<?> ofPow(Unit<?> 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 @Override 217 public Unit<?> pow(int n) { 218 return ofPow(this, n); 219 } 220 221 /** 222 * Returns the number of unit elements in this product. 223 * 224 * @return the number of unit elements. 225 */ 226 public int getUnitCount() { 227 return elements.length; 228 } 229 230 /** 231 * Returns the unit element at the specified position. 232 * 233 * @param index 234 * the index of the unit element to return. 235 * @return the unit element at the specified position. 236 * @throws IndexOutOfBoundsException 237 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 238 */ 239 public Unit<?> getUnit(int index) { 240 return elements[index].getUnit(); 241 } 242 243 /** 244 * Returns the power exponent of the unit element at the specified position. 245 * 246 * @param index 247 * the index of the unit element. 248 * @return the unit power exponent at the specified position. 249 * @throws IndexOutOfBoundsException 250 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 251 */ 252 public int getUnitPow(int index) { 253 return elements[index].getPow(); 254 } 255 256 /** 257 * Returns the root exponent of the unit element at the specified position. 258 * 259 * @param index 260 * the index of the unit element. 261 * @return the unit root exponent at the specified position. 262 * @throws IndexOutOfBoundsException 263 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 264 */ 265 public int getUnitRoot(int index) { 266 return elements[index].getRoot(); 267 } 268 269 @Override 270 public Map<Unit<?>, Integer> getBaseUnits() { 271 final Map<Unit<?>, Integer> units = new LinkedHashMap<>(); 272 for (int i = 0; i < getUnitCount(); i++) { 273 units.put(getUnit(i), getUnitPow(i)); 274 } 275 return units; 276 } 277 278 @Override 279 public boolean equals(Object obj) { 280 if (this == obj) { 281 return true; 282 } 283 if (obj instanceof ProductUnit<?>) { 284 final ProductUnit<?> other = ((ProductUnit<?>) obj); 285 return ElementUtil.arrayEqualsArbitraryOrder(this.elements, other.elements); 286 } 287 return false; 288 } 289 290 // thread safe cache for the expensive hashCode calculation 291 private transient Lazy<Integer> hashCode = new Lazy<>(this::calculateHashCode); 292 private int calculateHashCode() { 293 return Objects.hash((Object[]) ElementUtil.copyAndSort(elements)); 294 } 295 296 @Override 297 public int hashCode() { 298 return hashCode.get(); // lazy and thread-safe 299 } 300 301 @SuppressWarnings("unchecked") 302 @Override 303 public Unit<Q> toSystemUnit() { 304 Unit<?> systemUnit = AbstractUnit.ONE; 305 for (Element element : elements) { 306 Unit<?> unit = element.unit.getSystemUnit(); 307 unit = unit.pow(element.pow); 308 unit = unit.root(element.root); 309 systemUnit = systemUnit.multiply(unit); 310 } 311 return (AbstractUnit<Q>) systemUnit; 312 } 313 314 @Override 315 public UnitConverter getSystemConverter() { 316 UnitConverter converter = AbstractConverter.IDENTITY; 317 for (Element e : elements) { 318 if (e.unit instanceof AbstractUnit) { 319 UnitConverter cvtr = ((AbstractUnit<?>) e.unit).getSystemConverter(); 320 if (!(cvtr.isLinear())) 321 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 322 if (e.root != 1) 323 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 324 int pow = e.pow; 325 if (pow < 0) { // Negative power. 326 pow = -pow; 327 cvtr = cvtr.inverse(); 328 } 329 for (int j = 0; j < pow; j++) { 330 converter = converter.concatenate(cvtr); 331 } 332 } 333 } 334 return converter; 335 } 336 337 @Override 338 public Dimension getDimension() { 339 Dimension dimension = UnitDimension.NONE; 340 for (int i = 0; i < this.getUnitCount(); i++) { 341 Unit<?> unit = this.getUnit(i); 342 if (this.elements != null && unit.getDimension() != null) { 343 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 344 dimension = dimension.multiply(d); 345 } 346 } 347 return dimension; 348 } 349 350 /** 351 * Returns the unit defined from the product of the specified elements. 352 * 353 * @param leftElems 354 * left multiplicand elements. 355 * @param rightElems 356 * right multiplicand elements. 357 * @return the corresponding unit. 358 */ 359 @SuppressWarnings("rawtypes") 360 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 361 362 // Merges left elements with right elements. 363 Element[] result = new Element[leftElems.length + rightElems.length]; 364 int resultIndex = 0; 365 for (Element leftElem : leftElems) { 366 Unit<?> unit = leftElem.unit; 367 int p1 = leftElem.pow; 368 int r1 = leftElem.root; 369 int p2 = 0; 370 int r2 = 1; 371 for (Element rightElem : rightElems) { 372 if (unit.equals(rightElem.unit)) { 373 p2 = rightElem.pow; 374 r2 = rightElem.root; 375 break; // No duplicate. 376 } 377 } 378 int pow = p1 * r2 + p2 * r1; 379 int root = r1 * r2; 380 if (pow != 0) { 381 int gcd = gcd(Math.abs(pow), root); 382 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 383 } 384 } 385 386 // Appends remaining right elements not merged. 387 for (Element rightElem : rightElems) { 388 Unit<?> unit = rightElem.unit; 389 boolean hasBeenMerged = false; 390 for (Element leftElem : leftElems) { 391 if (unit.equals(leftElem.unit)) { 392 hasBeenMerged = true; 393 break; 394 } 395 } 396 if (!hasBeenMerged) 397 result[resultIndex++] = rightElem; 398 } 399 400 // Returns or creates instance. 401 if (resultIndex == 0) 402 return AbstractUnit.ONE; 403 else if (resultIndex == 1 && result[0].pow == result[0].root) 404 return result[0].unit; 405 else { 406 Element[] elems = new Element[resultIndex]; 407 System.arraycopy(result, 0, elems, 0, resultIndex); 408 return new ProductUnit(elems); 409 } 410 } 411 412 /** 413 * Returns the greatest common divisor (Euclid's algorithm). 414 * 415 * @param m 416 * the first number. 417 * @param nn 418 * the second number. 419 * @return the greatest common divisor. 420 */ 421 private static int gcd(int m, int n) { 422 return n == 0 ? m : gcd(n, m % n); 423 } 424 425 /** 426 * Inner product element represents a rational power of a single unit. 427 */ 428 private final static class Element implements Serializable { 429 430 /** 431 * 432 */ 433 private static final long serialVersionUID = 452938412398890507L; 434 435 /** 436 * Holds the single unit. 437 */ 438 private final Unit<?> unit; 439 440 /** 441 * Holds the power exponent. 442 */ 443 private final int pow; 444 445 /** 446 * Holds the root exponent. 447 */ 448 private final int root; 449 450 /** 451 * Structural constructor. 452 * 453 * @param unit 454 * the unit. 455 * @param pow 456 * the power exponent. 457 * @param root 458 * the root exponent. 459 */ 460 private Element(Unit<?> unit, int pow, int root) { 461 this.unit = unit; 462 this.pow = pow; 463 this.root = root; 464 } 465 466 /** 467 * Returns this element's unit. 468 * 469 * @return the single unit. 470 */ 471 public Unit<?> getUnit() { 472 return unit; 473 } 474 475 /** 476 * Returns the power exponent. The power exponent can be negative but is always different from zero. 477 * 478 * @return the power exponent of the single unit. 479 */ 480 public int getPow() { 481 return pow; 482 } 483 484 /** 485 * Returns the root exponent. The root exponent is always greater than zero. 486 * 487 * @return the root exponent of the single unit. 488 */ 489 public int getRoot() { 490 return root; 491 } 492 493 @Override 494 public boolean equals(Object o) { 495 if (this == o) 496 return true; 497 if (o == null || getClass() != o.getClass()) 498 return false; 499 500 final Element other = (Element) o; 501 502 if (!Objects.equals(this.pow, other.pow)) { 503 return false; 504 } 505 if (!Objects.equals(this.root, other.root)) { 506 return false; 507 } 508 return Objects.equals(this.unit, other.unit); 509 } 510 511 @Override 512 public int hashCode() { 513 return Objects.hash(unit, pow, root); 514 } 515 } 516 517 // Element specific algorithms provided locally to this class 518 private final static class ElementUtil { 519 520 // -- returns a defensive sorted copy, unless size <= 1 521 private static Element[] copyAndSort(final Element[] elements) { 522 if (elements == null || elements.length <= 1) { 523 return elements; 524 } 525 final Element[] elementsSorted = Arrays.copyOf(elements, elements.length); 526 Arrays.sort(elementsSorted, ElementUtil::compare); 527 return elementsSorted; 528 } 529 530 private static int compare(final Element e0, final Element e1) { 531 final Unit<?> sysUnit0 = e0.getUnit().getSystemUnit(); 532 final Unit<?> sysUnit1 = e1.getUnit().getSystemUnit(); 533 final String symbol0 = sysUnit0.getSymbol(); 534 final String symbol1 = sysUnit1.getSymbol(); 535 536 if (symbol0 != null && symbol1 != null) { 537 return symbol0.compareTo(symbol1); 538 } else { 539 return sysUnit0.toString().compareTo(sysUnit1.toString()); 540 } 541 } 542 543 // optimized for the fact, that can only return true, if for each element in e0 there exist a single match in e1 544 private static boolean arrayEqualsArbitraryOrder(final Element[] e0, final Element[] e1) { 545 if (e0.length != e1.length) { 546 return false; 547 } 548 for (Element left : e0) { 549 boolean unitFound = false; 550 for (Element right : e1) { 551 if (left.unit.equals(right.unit)) { 552 if (left.pow != right.pow || left.root != right.root) { 553 return false; 554 } else { 555 unitFound = true; 556 break; 557 } 558 } 559 } 560 if (!unitFound) { 561 return false; 562 } 563 } 564 return true; 565 } 566 } 567 568 569 // -- SERIALIZATION PROXY 570 571 // Chapter 12. Serialization 572 // Bloch, Joshua. Effective Java (p. 339). Pearson Education. 573 574 private Object writeReplace() { 575 return new SerializationProxy(this); 576 } 577 578 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 579 throw new InvalidObjectException("Proxy required"); 580 } 581 582 private static class SerializationProxy implements Serializable { 583 private static final long serialVersionUID = 1L; 584 private final Element[] elements; 585 private final String symbol; 586 587 private SerializationProxy(ProductUnit<?> productUnit) { 588 this.elements = productUnit.elements; 589 this.symbol = productUnit.getSymbol(); 590 } 591 592 private Object readResolve() { 593 ProductUnit<?> pu = new ProductUnit<>(elements); 594 pu.setSymbol(symbol); 595 return pu; 596 } 597 } 598 599}