001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
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-363 nor the names of its 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 tec.units.ri.unit;
031
032import java.util.HashMap;
033import java.util.Map;
034
035import javax.measure.Dimension;
036import javax.measure.Quantity;
037import javax.measure.Unit;
038import javax.measure.UnitConverter;
039
040import tec.units.ri.AbstractConverter;
041import tec.units.ri.AbstractUnit;
042import tec.units.ri.quantity.QuantityDimension;
043import tec.uom.lib.common.function.UnitSupplier;
044
045/**
046 * <p>
047 * This class represents units formed by the product of rational powers of existing physical units.
048 * </p>
049 *
050 * <p>
051 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code>
052 * returns <code>METRE</code>.
053 * </p>
054 *
055 * @param <Q>
056 *          The type of the quantity measured by this unit.
057 *
058 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
059 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
060 * @version 1.0, Oct 6, 2016
061 */
062public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> {
063
064  /**
065         * 
066         */
067  // private static final long serialVersionUID = 962983585531030093L;
068
069  /**
070   * Holds the units composing this product unit.
071   */
072  private final Element[] elements;
073
074  /**
075   * Holds the hashcode (optimization).
076   */
077  private int hashCode;
078
079  /**
080   * Holds the symbol for this unit.
081   */
082  private final String symbol;
083
084  /**
085   * Default constructor (used solely to create <code>ONE</code> instance).
086   */
087  public ProductUnit() {
088    this.symbol = "";
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    this.symbol = 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    this.elements = elements;
113    this.symbol = elements[0].getUnit().getSymbol(); // TODO should contain ALL 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<?> getProductInstance(AbstractUnit<?> left, AbstractUnit<?> 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    Element[] rightElems;
132    if (right instanceof ProductUnit<?>)
133      rightElems = ((ProductUnit<?>) right).elements;
134    else
135      rightElems = new Element[] { new Element(right, 1, 1) };
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<?> getQuotientInstance(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<?> getRootInstance(AbstractUnit<?> 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  static Unit<?> getPowInstance(AbstractUnit<?> 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<Unit<?>, Integer>();
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 that) {
273    if (this == that)
274      return true;
275    if (!(that instanceof ProductUnit<?>))
276      return false;
277    // Two products are equals if they have the same elements
278    // regardless of the elements' order.
279    Element[] elems = ((ProductUnit<?>) that).elements;
280    if (elements.length != elems.length)
281      return false;
282    for (Element element : elements) {
283      boolean unitFound = false;
284      for (Element elem : elems) {
285        if (element.unit.equals(elem.unit))
286          if ((element.pow != elem.pow) || (element.root != elem.root))
287            return false;
288          else {
289            unitFound = true;
290            break;
291          }
292      }
293      if (!unitFound)
294        return false;
295    }
296    return true;
297  }
298
299  @Override
300  public int hashCode() {
301    if (this.hashCode != 0)
302      return this.hashCode;
303    int code = 0;
304    for (Element element : elements) {
305      code += element.unit.hashCode() * (element.pow * 3 - element.root * 2);
306    }
307    this.hashCode = code;
308    return code;
309  }
310
311  @SuppressWarnings("unchecked")
312  @Override
313  public AbstractUnit<Q> toSystemUnit() {
314    Unit<?> systemUnit = AbstractUnit.ONE;
315    for (Element element : elements) {
316      Unit<?> unit = element.unit.getSystemUnit();
317      unit = unit.pow(element.pow);
318      unit = unit.root(element.root);
319      systemUnit = systemUnit.multiply(unit);
320    }
321    return (AbstractUnit<Q>) systemUnit;
322  }
323
324  public UnitConverter getSystemConverter() {
325    UnitConverter converter = AbstractConverter.IDENTITY;
326    for (Element e : elements) {
327      @SuppressWarnings("rawtypes")
328      UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); // TODO
329      // check
330      // for
331      // type
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    return converter;
346  }
347
348  @Override
349  public Dimension getDimension() {
350    Dimension dimension = QuantityDimension.NONE;
351    for (int i = 0; i < this.getUnitCount(); i++) {
352      Unit<?> unit = this.getUnit(i);
353      if (this.elements != null && unit.getDimension() != null) {
354        Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i));
355        if (dimension != null) {
356          dimension = dimension.multiply(d);
357        } else {
358          dimension = d; // XXX hackaround
359        }
360      }
361    }
362    return dimension;
363  }
364
365  /**
366   * Returns the unit defined from the product of the specified elements.
367   *
368   * @param leftElems
369   *          left multiplicand elements.
370   * @param rightElems
371   *          right multiplicand elements.
372   * @return the corresponding unit.
373   */
374  @SuppressWarnings("rawtypes")
375  private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) {
376
377    // Merges left elements with right elements.
378    Element[] result = new Element[leftElems.length + rightElems.length];
379    int resultIndex = 0;
380    for (Element leftElem : leftElems) {
381      Unit<?> unit = leftElem.unit;
382      int p1 = leftElem.pow;
383      int r1 = leftElem.root;
384      int p2 = 0;
385      int r2 = 1;
386      for (Element rightElem : rightElems) {
387        if (unit.equals(rightElem.unit)) {
388          p2 = rightElem.pow;
389          r2 = rightElem.root;
390          break; // No duplicate.
391        }
392      }
393      int pow = (p1 * r2) + (p2 * r1);
394      int root = r1 * r2;
395      if (pow != 0) {
396        int gcd = gcd(Math.abs(pow), root);
397        result[resultIndex++] = new Element(unit, pow / gcd, root / gcd);
398      }
399    }
400
401    // Appends remaining right elements not merged.
402    for (Element rightElem : rightElems) {
403      Unit<?> unit = rightElem.unit;
404      boolean hasBeenMerged = false;
405      for (Element leftElem : leftElems) {
406        if (unit.equals(leftElem.unit)) {
407          hasBeenMerged = true;
408          break;
409        }
410      }
411      if (!hasBeenMerged)
412        result[resultIndex++] = rightElem;
413    }
414
415    // Returns or creates instance.
416    if (resultIndex == 0)
417      return AbstractUnit.ONE;
418    else if ((resultIndex == 1) && (result[0].pow == result[0].root))
419      return result[0].unit;
420    else {
421      Element[] elems = new Element[resultIndex];
422      System.arraycopy(result, 0, elems, 0, resultIndex);
423      return new ProductUnit(elems);
424    }
425  }
426
427  /**
428   * Returns the greatest common divisor (Euclid's algorithm).
429   *
430   * @param m
431   *          the first number.
432   * @param nn
433   *          the second number.
434   * @return the greatest common divisor.
435   */
436  private static int gcd(int m, int n) {
437    if (n == 0)
438      return m;
439    else
440      return gcd(n, m % n);
441  }
442
443  /**
444   * Inner product element represents a rational power of a single unit.
445   */
446  @SuppressWarnings("rawtypes")
447  private final static class Element implements UnitSupplier {
448
449    /**
450                 * 
451                 */
452    // private static final long serialVersionUID = 452938412398890507L;
453
454    /**
455     * Holds the single unit.
456     */
457    private final Unit<?> unit;
458
459    /**
460     * Holds the power exponent.
461     */
462    private final int pow;
463
464    /**
465     * Holds the root exponent.
466     */
467    private final int root;
468
469    /**
470     * Structural constructor.
471     *
472     * @param unit
473     *          the unit.
474     * @param pow
475     *          the power exponent.
476     * @param root
477     *          the root exponent.
478     */
479    private Element(Unit<?> unit, int pow, int root) {
480      this.unit = unit;
481      this.pow = pow;
482      this.root = root;
483    }
484
485    /**
486     * Returns this element's unit.
487     *
488     * @return the single unit.
489     */
490    public Unit<?> getUnit() {
491      return unit;
492    }
493
494    /**
495     * Returns the power exponent. The power exponent can be negative but is always different from zero.
496     *
497     * @return the power exponent of the single unit.
498     */
499    public int getPow() {
500      return pow;
501    }
502
503    /**
504     * Returns the root exponent. The root exponent is always greater than zero.
505     *
506     * @return the root exponent of the single unit.
507     */
508    public int getRoot() {
509      return root;
510    }
511  }
512
513  @Override
514  public String getSymbol() {
515    if (super.getSymbol() != null) {
516      return super.getSymbol();
517    }
518    return symbol;
519  }
520}