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