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.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 1.4.2, November 7, 2017 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 /** 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<>(); 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 if (obj instanceof AbstractUnit) { 297 return AbstractUnit.Equalizer.areEqual(this, (AbstractUnit) obj); 298 } else { 299 return false; 300 } 301 } 302 303 @Override 304 public int hashCode() { 305 return Objects.hash((Object[]) elements); 306 } 307 308 @SuppressWarnings("unchecked") 309 @Override 310 public Unit<Q> toSystemUnit() { 311 Unit<?> systemUnit = AbstractUnit.ONE; 312 for (Element element : elements) { 313 Unit<?> unit = element.unit.getSystemUnit(); 314 unit = unit.pow(element.pow); 315 unit = unit.root(element.root); 316 systemUnit = systemUnit.multiply(unit); 317 } 318 return (AbstractUnit<Q>) systemUnit; 319 } 320 321 @Override 322 public UnitConverter getSystemConverter() { 323 UnitConverter converter = AbstractConverter.IDENTITY; 324 for (Element e : elements) { 325 if (e.unit instanceof AbstractUnit) { 326 UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); 327 if (!(cvtr.isLinear())) 328 throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert"); 329 if (e.root != 1) 330 throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent"); 331 int pow = e.pow; 332 if (pow < 0) { // Negative power. 333 pow = -pow; 334 cvtr = cvtr.inverse(); 335 } 336 for (int j = 0; j < pow; j++) { 337 converter = converter.concatenate(cvtr); 338 } 339 } 340 } 341 return converter; 342 } 343 344 @Override 345 public Dimension getDimension() { 346 Dimension dimension = QuantityDimension.NONE; 347 for (int i = 0; i < this.getUnitCount(); i++) { 348 Unit<?> unit = this.getUnit(i); 349 if (this.elements != null && unit.getDimension() != null) { 350 Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i)); 351 dimension = dimension.multiply(d); 352 } 353 } 354 return dimension; 355 } 356 357 /** 358 * Returns the unit defined from the product of the specified elements. 359 * 360 * @param leftElems 361 * left multiplicand elements. 362 * @param rightElems 363 * right multiplicand elements. 364 * @return the corresponding unit. 365 */ 366 @SuppressWarnings("rawtypes") 367 private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) { 368 369 // Merges left elements with right elements. 370 Element[] result = new Element[leftElems.length + rightElems.length]; 371 int resultIndex = 0; 372 for (Element leftElem : leftElems) { 373 Unit<?> unit = leftElem.unit; 374 int p1 = leftElem.pow; 375 int r1 = leftElem.root; 376 int p2 = 0; 377 int r2 = 1; 378 for (Element rightElem : rightElems) { 379 if (unit.equals(rightElem.unit)) { 380 p2 = rightElem.pow; 381 r2 = rightElem.root; 382 break; // No duplicate. 383 } 384 } 385 int pow = (p1 * r2) + (p2 * r1); 386 int root = r1 * r2; 387 if (pow != 0) { 388 int gcd = gcd(Math.abs(pow), root); 389 result[resultIndex++] = new Element(unit, pow / gcd, root / gcd); 390 } 391 } 392 393 // Appends remaining right elements not merged. 394 for (Element rightElem : rightElems) { 395 Unit<?> unit = rightElem.unit; 396 boolean hasBeenMerged = false; 397 for (Element leftElem : leftElems) { 398 if (unit.equals(leftElem.unit)) { 399 hasBeenMerged = true; 400 break; 401 } 402 } 403 if (!hasBeenMerged) 404 result[resultIndex++] = rightElem; 405 } 406 407 // Returns or creates instance. 408 if (resultIndex == 0) 409 return AbstractUnit.ONE; 410 else if ((resultIndex == 1) && (result[0].pow == result[0].root)) 411 return result[0].unit; 412 else { 413 Element[] elems = new Element[resultIndex]; 414 System.arraycopy(result, 0, elems, 0, resultIndex); 415 return new ProductUnit(elems); 416 } 417 } 418 419 /** 420 * Returns the greatest common divisor (Euclid's algorithm). 421 * 422 * @param m 423 * the first number. 424 * @param nn 425 * the second number. 426 * @return the greatest common divisor. 427 */ 428 private static int gcd(int m, int n) { 429 if (n == 0) 430 return m; 431 else 432 return gcd(n, m % n); 433 } 434 435 /** 436 * Inner product element represents a rational power of a single unit. 437 */ 438 private final static class Element implements Serializable { 439 440 /** 441 * 442 */ 443 private static final long serialVersionUID = 452938412398890507L; 444 445 /** 446 * Holds the single unit. 447 */ 448 private final Unit<?> unit; 449 450 /** 451 * Holds the power exponent. 452 */ 453 private final int pow; 454 455 /** 456 * Holds the root exponent. 457 */ 458 private final int root; 459 460 /** 461 * Structural constructor. 462 * 463 * @param unit 464 * the unit. 465 * @param pow 466 * the power exponent. 467 * @param root 468 * the root exponent. 469 */ 470 private Element(Unit<?> unit, int pow, int root) { 471 this.unit = unit; 472 this.pow = pow; 473 this.root = root; 474 } 475 476 /** 477 * Returns this element's unit. 478 * 479 * @return the single unit. 480 */ 481 public Unit<?> getUnit() { 482 return unit; 483 } 484 485 /** 486 * Returns the power exponent. The power exponent can be negative but is always different from zero. 487 * 488 * @return the power exponent of the single unit. 489 */ 490 public int getPow() { 491 return pow; 492 } 493 494 /** 495 * Returns the root exponent. The root exponent is always greater than zero. 496 * 497 * @return the root exponent of the single unit. 498 */ 499 public int getRoot() { 500 return root; 501 } 502 503 @Override 504 public boolean equals(Object o) { 505 if (this == o) 506 return true; 507 if (o == null || getClass() != o.getClass()) 508 return false; 509 510 Element element = (Element) o; 511 512 if (pow != element.pow) { 513 return false; 514 } 515 return root == element.root && (unit != null ? unit.equals(element.unit) : element.unit == null); 516 517 } 518 519 @Override 520 public int hashCode() { 521 int result = unit != null ? unit.hashCode() : 0; 522 result = 31 * result + pow; 523 result = 31 * result + root; 524 return result; 525 } 526 } 527 528 @Override 529 public String getSymbol() { 530 return symbol; 531 } 532}