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 &gt; 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 &gt; 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 &lt; 0 || index &gt;= 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 &lt; 0 || index &gt;= 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 &lt; 0 || index &gt;= 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}