001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2020, Units of Measurement project. 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.AbstractUnit; 038import tech.units.indriya.function.AbstractConverter; 039import tech.units.indriya.unit.UnitDimension; 040 041import java.io.Serializable; 042import java.util.Arrays; 043import java.util.Comparator; 044import java.util.LinkedHashMap; 045import java.util.Map; 046import java.util.Objects; 047 048/** 049 * <p> 050 * This class represents units formed by the product of rational powers of existing physical units. 051 * </p> 052 * 053 * <p> 054 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code> 055 * returns <code>METRE</code>. 056 * </p> 057 * 058 * @param <Q> 059 * The type of the quantity measured by this unit. 060 * 061 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 062 * @author <a href="mailto:werner@units.tech">Werner Keil</a> 063 * @version 1.9, July 05, 2019 064 * @since 1.0 065 */ 066public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> { 067 068 /** 069 * 070 */ 071 private static final long serialVersionUID = 962983585531030093L; 072 073 /** 074 * Holds the units composing this product unit. 075 */ 076 private final Element[] elements; 077 078 /** 079 * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance). 080 */ 081 public ProductUnit() { 082 super(""); 083 elements = new Element[0]; 084 } 085 086 /** 087 * Copy constructor (allows for parameterization of product units). 088 * 089 * @param productUnit 090 * the product unit source. 091 * @throws ClassCastException 092 * if the specified unit is not a product unit. 093 */ 094 public ProductUnit(Unit<?> productUnit) { 095 super(productUnit.getSymbol()); 096 this.elements = ((ProductUnit<?>) productUnit).elements; 097 } 098 099 /** 100 * Product unit constructor. 101 * 102 * @param elements 103 * the product elements. 104 */ 105 private ProductUnit(Element[] elements) { 106 super(null); 107 this.elements = elements; 108 } 109 110 /** 111 * Returns the product of the specified units. 112 * 113 * @param left 114 * the left unit operand. 115 * @param right 116 * the right unit operand. 117 * @return <code>left * right</code> 118 */ 119 public static Unit<?> ofProduct(Unit<?> left, Unit<?> right) { 120 Element[] leftElems; 121 if (left instanceof ProductUnit<?>) { 122 leftElems = ((ProductUnit<?>) left).elements; 123 } else { 124 leftElems = new Element[] { new Element(left, 1, 1) }; 125 } 126 Element[] rightElems; 127 if (right instanceof ProductUnit<?>) { 128 rightElems = ((ProductUnit<?>) right).elements; 129 } else { 130 rightElems = new Element[] { new Element(right, 1, 1) }; 131 } 132 return getInstance(leftElems, rightElems); 133 } 134 135 /** 136 * Returns the quotient of the specified units. 137 * 138 * @param left 139 * the dividend unit operand. 140 * @param right 141 * the divisor unit operand. 142 * @return <code>dividend / divisor</code> 143 */ 144 public static Unit<?> ofQuotient(Unit<?> left, Unit<?> right) { 145 Element[] leftElems; 146 if (left instanceof ProductUnit<?>) 147 leftElems = ((ProductUnit<?>) left).elements; 148 else 149 leftElems = new Element[] { new Element(left, 1, 1) }; 150 Element[] rightElems; 151 if (right instanceof ProductUnit<?>) { 152 Element[] elems = ((ProductUnit<?>) right).elements; 153 rightElems = new Element[elems.length]; 154 for (int i = 0; i < elems.length; i++) { 155 rightElems[i] = new Element(elems[i].unit, -elems[i].pow, elems[i].root); 156 } 157 } else 158 rightElems = new Element[] { new Element(right, -1, 1) }; 159 return getInstance(leftElems, rightElems); 160 } 161 162 /** 163 * Returns the product unit corresponding to the specified root of the specified unit. 164 * 165 * @param unit 166 * the unit. 167 * @param n 168 * the root's order (n > 0). 169 * @return <code>unit^(1/nn)</code> 170 * @throws ArithmeticException 171 * if <code>n == 0</code>. 172 */ 173 public static Unit<?> ofRoot(Unit<?> unit, int n) { 174 Element[] unitElems; 175 if (unit instanceof ProductUnit<?>) { 176 Element[] elems = ((ProductUnit<?>) unit).elements; 177 unitElems = new Element[elems.length]; 178 for (int i = 0; i < elems.length; i++) { 179 int gcd = gcd(Math.abs(elems[i].pow), elems[i].root * n); 180 unitElems[i] = new Element(elems[i].unit, elems[i].pow / gcd, elems[i].root * n / gcd); 181 } 182 } else 183 unitElems = new Element[] { new Element(unit, 1, n) }; 184 return getInstance(unitElems, new Element[0]); 185 } 186 187 /** 188 * Returns the product unit corresponding to this unit raised to the specified exponent. 189 * 190 * @param unit 191 * the unit. 192 * @param nn 193 * the exponent (nn > 0). 194 * @return <code>unit^n</code> 195 */ 196 public static Unit<?> ofPow(Unit<?> unit, int n) { 197 Element[] unitElems; 198 if (unit instanceof ProductUnit<?>) { 199 Element[] elems = ((ProductUnit<?>) unit).elements; 200 unitElems = new Element[elems.length]; 201 for (int i = 0; i < elems.length; i++) { 202 int gcd = gcd(Math.abs(elems[i].pow * n), elems[i].root); 203 unitElems[i] = new Element(elems[i].unit, elems[i].pow * n / gcd, elems[i].root / gcd); 204 } 205 } else 206 unitElems = new Element[] { new Element(unit, n, 1) }; 207 return getInstance(unitElems, new Element[0]); 208 } 209 210 @Override 211 public Unit<?> pow(int n) { 212 return ofPow(this, n); 213 } 214 215 /** 216 * Returns the number of unit elements in this product. 217 * 218 * @return the number of unit elements. 219 */ 220 public int getUnitCount() { 221 return elements.length; 222 } 223 224 /** 225 * Returns the unit element at the specified position. 226 * 227 * @param index 228 * the index of the unit element to return. 229 * @return the unit element at the specified position. 230 * @throws IndexOutOfBoundsException 231 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 232 */ 233 public Unit<?> getUnit(int index) { 234 return elements[index].getUnit(); 235 } 236 237 /** 238 * Returns the power exponent of the unit element at the specified position. 239 * 240 * @param index 241 * the index of the unit element. 242 * @return the unit power exponent at the specified position. 243 * @throws IndexOutOfBoundsException 244 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 245 */ 246 public int getUnitPow(int index) { 247 return elements[index].getPow(); 248 } 249 250 /** 251 * Returns the root exponent of the unit element at the specified position. 252 * 253 * @param index 254 * the index of the unit element. 255 * @return the unit root exponent at the specified position. 256 * @throws IndexOutOfBoundsException 257 * if index is out of range <code>(index < 0 || index >= getUnitCount())</code>. 258 */ 259 public int getUnitRoot(int index) { 260 return elements[index].getRoot(); 261 } 262 263 @Override 264 public Map<Unit<?>, Integer> getBaseUnits() { 265 final Map<Unit<?>, Integer> units = new LinkedHashMap<>(); 266 for (int i = 0; i < getUnitCount(); i++) { 267 units.put(getUnit(i), getUnitPow(i)); 268 } 269 return units; 270 } 271 272 @Override 273 public boolean equals(Object obj) { 274 if (this == obj) { 275 return true; 276 } 277 if (obj instanceof ProductUnit<?>) { 278 Element[] elems = ((ProductUnit<?>) obj).elements; 279 if (elements.length != elems.length) 280 return false; 281 for (Element element : elements) { 282 boolean unitFound = false; 283 for (Element elem : elems) { 284 if (element.unit.equals(elem.unit)) 285 if (element.pow != elem.pow || element.root != elem.root) 286 return false; 287 else { 288 unitFound = true; 289 break; 290 } 291 } 292 if (!unitFound) 293 return false; 294 } 295 return true; 296 } 297 return false; 298 } 299 300 @Override 301 public int hashCode() { 302 Arrays.sort(elements, new Comparator<Element>() { 303 @Override 304 public int compare(Element e0, Element e1) { 305 if (e0.getUnit().getSystemUnit().getSymbol() != null && e1.getUnit().getSystemUnit().getSymbol() != null) { 306 return e0.getUnit().getSystemUnit().getSymbol().compareTo(e1.getUnit().getSystemUnit().getSymbol()); 307 } else { 308 return e0.getUnit().getSystemUnit().toString().compareTo(e1.getUnit().getSystemUnit().toString()); 309 } 310 }}); 311 return Objects.hash((Object[]) elements); 312 } 313 314 @SuppressWarnings("unchecked") 315 @Override 316 public Unit<Q> toSystemUnit() { 317 Unit<?> systemUnit = AbstractUnit.ONE; 318 for (Element element : elements) { 319 Unit<?> unit = element.unit.getSystemUnit(); 320 unit = unit.pow(element.pow); 321 unit = unit.root(element.root); 322 systemUnit = systemUnit.multiply(unit); 323 } 324 return (AbstractUnit<Q>) systemUnit; 325 } 326 327 @Override 328 public UnitConverter getSystemConverter() { 329 UnitConverter converter = AbstractConverter.IDENTITY; 330 for (Element e : elements) { 331 if (e.unit instanceof AbstractUnit) { 332 UnitConverter cvtr = ((AbstractUnit<?>) e.unit).getSystemConverter(); 333 if (!(cvtr.isLinear())) 334 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 335 if (e.root != 1) 336 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 337 int pow = e.pow; 338 if (pow < 0) { // Negative power. 339 pow = -pow; 340 cvtr = cvtr.inverse(); 341 } 342 for (int j = 0; j < pow; j++) { 343 converter = converter.concatenate(cvtr); 344 } 345 } 346 } 347 return converter; 348 } 349 350 @Override 351 public Dimension getDimension() { 352 Dimension dimension = UnitDimension.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 dimension = dimension.multiply(d); 358 } 359 } 360 return dimension; 361 } 362 363 /** 364 * Returns the unit defined from the product of the specified elements. 365 * 366 * @param leftElems 367 * left multiplicand elements. 368 * @param rightElems 369 * right multiplicand elements. 370 * @return the corresponding unit. 371 */ 372 @SuppressWarnings("rawtypes") 373 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 374 375 // Merges left elements with right elements. 376 Element[] result = new Element[leftElems.length + rightElems.length]; 377 int resultIndex = 0; 378 for (Element leftElem : leftElems) { 379 Unit<?> unit = leftElem.unit; 380 int p1 = leftElem.pow; 381 int r1 = leftElem.root; 382 int p2 = 0; 383 int r2 = 1; 384 for (Element rightElem : rightElems) { 385 if (unit.equals(rightElem.unit)) { 386 p2 = rightElem.pow; 387 r2 = rightElem.root; 388 break; // No duplicate. 389 } 390 } 391 int pow = p1 * r2 + p2 * r1; 392 int root = r1 * r2; 393 if (pow != 0) { 394 int gcd = gcd(Math.abs(pow), root); 395 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 396 } 397 } 398 399 // Appends remaining right elements not merged. 400 for (Element rightElem : rightElems) { 401 Unit<?> unit = rightElem.unit; 402 boolean hasBeenMerged = false; 403 for (Element leftElem : leftElems) { 404 if (unit.equals(leftElem.unit)) { 405 hasBeenMerged = true; 406 break; 407 } 408 } 409 if (!hasBeenMerged) 410 result[resultIndex++] = rightElem; 411 } 412 413 // Returns or creates instance. 414 if (resultIndex == 0) 415 return AbstractUnit.ONE; 416 else if (resultIndex == 1 && result[0].pow == result[0].root) 417 return result[0].unit; 418 else { 419 Element[] elems = new Element[resultIndex]; 420 System.arraycopy(result, 0, elems, 0, resultIndex); 421 return new ProductUnit(elems); 422 } 423 } 424 425 /** 426 * Returns the greatest common divisor (Euclid's algorithm). 427 * 428 * @param m 429 * the first number. 430 * @param nn 431 * the second number. 432 * @return the greatest common divisor. 433 */ 434 private static int gcd(int m, int n) { 435 return n == 0 ? m : gcd(n, m % n); 436 } 437 438 /** 439 * Inner product element represents a rational power of a single unit. 440 */ 441 private final static class Element implements Serializable { 442 443 /** 444 * 445 */ 446 private static final long serialVersionUID = 452938412398890507L; 447 448 /** 449 * Holds the single unit. 450 */ 451 private final Unit<?> unit; 452 453 /** 454 * Holds the power exponent. 455 */ 456 private final int pow; 457 458 /** 459 * Holds the root exponent. 460 */ 461 private final int root; 462 463 /** 464 * Structural constructor. 465 * 466 * @param unit 467 * the unit. 468 * @param pow 469 * the power exponent. 470 * @param root 471 * the root exponent. 472 */ 473 private Element(Unit<?> unit, int pow, int root) { 474 this.unit = unit; 475 this.pow = pow; 476 this.root = root; 477 } 478 479 /** 480 * Returns this element's unit. 481 * 482 * @return the single unit. 483 */ 484 public Unit<?> getUnit() { 485 return unit; 486 } 487 488 /** 489 * Returns the power exponent. The power exponent can be negative but is always different from zero. 490 * 491 * @return the power exponent of the single unit. 492 */ 493 public int getPow() { 494 return pow; 495 } 496 497 /** 498 * Returns the root exponent. The root exponent is always greater than zero. 499 * 500 * @return the root exponent of the single unit. 501 */ 502 public int getRoot() { 503 return root; 504 } 505 506 @Override 507 public boolean equals(Object o) { 508 if (this == o) 509 return true; 510 if (o == null || getClass() != o.getClass()) 511 return false; 512 513 Element element = (Element) o; 514 515 if (pow != element.pow) { 516 return false; 517 } 518 return root == element.root && unit != null ? unit.equals(element.unit) : element.unit == null; 519 520 } 521 522 @Override 523 public int hashCode() { 524 return Objects.hash(unit, pow, root); 525 } 526 } 527}