001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2016, 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 0.5.5, July 11, 2015
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(); // FIXME this should
114    // contain ALL
115    // elements
116  }
117
118  /**
119   * Returns the product of the specified units.
120   *
121   * @param left
122   *          the left unit operand.
123   * @param right
124   *          the right unit operand.
125   * @return <code>left * right</code>
126   */
127  public static Unit<?> getProductInstance(AbstractUnit<?> left, AbstractUnit<?> right) {
128    Element[] leftElems;
129    if (left instanceof ProductUnit<?>)
130      leftElems = ((ProductUnit<?>) left).elements;
131    else
132      leftElems = new Element[] { new Element(left, 1, 1) };
133    Element[] rightElems;
134    if (right instanceof ProductUnit<?>)
135      rightElems = ((ProductUnit<?>) right).elements;
136    else
137      rightElems = new Element[] { new Element(right, 1, 1) };
138    return getInstance(leftElems, rightElems);
139  }
140
141  /**
142   * Returns the quotient of the specified units.
143   *
144   * @param left
145   *          the dividend unit operand.
146   * @param right
147   *          the divisor unit operand.
148   * @return <code>dividend / divisor</code>
149   */
150  public static Unit<?> getQuotientInstance(Unit<?> left, Unit<?> right) {
151    Element[] leftElems;
152    if (left instanceof ProductUnit<?>)
153      leftElems = ((ProductUnit<?>) left).elements;
154    else
155      leftElems = new Element[] { new Element(left, 1, 1) };
156    Element[] rightElems;
157    if (right instanceof ProductUnit<?>) {
158      Element[] elems = ((ProductUnit<?>) right).elements;
159      rightElems = new Element[elems.length];
160      for (int i = 0; i < elems.length; i++) {
161        rightElems[i] = new Element(elems[i].unit, -elems[i].pow, elems[i].root);
162      }
163    } else
164      rightElems = new Element[] { new Element(right, -1, 1) };
165    return getInstance(leftElems, rightElems);
166  }
167
168  /**
169   * Returns the product unit corresponding to the specified root of the specified unit.
170   *
171   * @param unit
172   *          the unit.
173   * @param n
174   *          the root's order (n &gt; 0).
175   * @return <code>unit^(1/nn)</code>
176   * @throws ArithmeticException
177   *           if <code>n == 0</code>.
178   */
179  public static Unit<?> getRootInstance(AbstractUnit<?> unit, int n) {
180    Element[] unitElems;
181    if (unit instanceof ProductUnit<?>) {
182      Element[] elems = ((ProductUnit<?>) unit).elements;
183      unitElems = new Element[elems.length];
184      for (int i = 0; i < elems.length; i++) {
185        int gcd = gcd(Math.abs(elems[i].pow), elems[i].root * n);
186        unitElems[i] = new Element(elems[i].unit, elems[i].pow / gcd, elems[i].root * n / gcd);
187      }
188    } else
189      unitElems = new Element[] { new Element(unit, 1, n) };
190    return getInstance(unitElems, new Element[0]);
191  }
192
193  /**
194   * Returns the product unit corresponding to this unit raised to the specified exponent.
195   *
196   * @param unit
197   *          the unit.
198   * @param nn
199   *          the exponent (nn &gt; 0).
200   * @return <code>unit^n</code>
201   */
202  static Unit<?> getPowInstance(AbstractUnit<?> unit, int n) {
203    Element[] unitElems;
204    if (unit instanceof ProductUnit<?>) {
205      Element[] elems = ((ProductUnit<?>) unit).elements;
206      unitElems = new Element[elems.length];
207      for (int i = 0; i < elems.length; i++) {
208        int gcd = gcd(Math.abs(elems[i].pow * n), elems[i].root);
209        unitElems[i] = new Element(elems[i].unit, elems[i].pow * n / gcd, elems[i].root / gcd);
210      }
211    } else
212      unitElems = new Element[] { new Element(unit, n, 1) };
213    return getInstance(unitElems, new Element[0]);
214  }
215
216  /**
217   * Returns the number of unit elements in this product.
218   *
219   * @return the number of unit elements.
220   */
221  public int getUnitCount() {
222    return elements.length;
223  }
224
225  /**
226   * Returns the unit element at the specified position.
227   *
228   * @param index
229   *          the index of the unit element to return.
230   * @return the unit element at the specified position.
231   * @throws IndexOutOfBoundsException
232   *           if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
233   */
234  public Unit<?> getUnit(int index) {
235    return elements[index].getUnit();
236  }
237
238  /**
239   * Returns the power exponent of the unit element at the specified position.
240   *
241   * @param index
242   *          the index of the unit element.
243   * @return the unit power exponent at the specified position.
244   * @throws IndexOutOfBoundsException
245   *           if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
246   */
247  public int getUnitPow(int index) {
248    return elements[index].getPow();
249  }
250
251  /**
252   * Returns the root exponent of the unit element at the specified position.
253   *
254   * @param index
255   *          the index of the unit element.
256   * @return the unit root exponent at the specified position.
257   * @throws IndexOutOfBoundsException
258   *           if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
259   */
260  public int getUnitRoot(int index) {
261    return elements[index].getRoot();
262  }
263
264  @Override
265  public Map<Unit<?>, Integer> getBaseUnits() {
266    final Map<Unit<?>, Integer> units = new HashMap<Unit<?>, Integer>();
267    for (int i = 0; i < getUnitCount(); i++) {
268      units.put(getUnit(i), getUnitPow(i));
269    }
270    return units;
271  }
272
273  @Override
274  public boolean equals(Object that) {
275    if (this == that)
276      return true;
277    if (!(that instanceof ProductUnit<?>))
278      return false;
279    // Two products are equals if they have the same elements
280    // regardless of the elements' order.
281    Element[] elems = ((ProductUnit<?>) that).elements;
282    if (elements.length != elems.length)
283      return false;
284    for (Element element : elements) {
285      boolean unitFound = false;
286      for (Element elem : elems) {
287        if (element.unit.equals(elem.unit))
288          if ((element.pow != elem.pow) || (element.root != elem.root))
289            return false;
290          else {
291            unitFound = true;
292            break;
293          }
294      }
295      if (!unitFound)
296        return false;
297    }
298    return true;
299  }
300
301  @Override
302  public int hashCode() {
303    if (this.hashCode != 0)
304      return this.hashCode;
305    int code = 0;
306    for (Element element : elements) {
307      code += element.unit.hashCode() * (element.pow * 3 - element.root * 2);
308    }
309    this.hashCode = code;
310    return code;
311  }
312
313  @SuppressWarnings("unchecked")
314  @Override
315  public AbstractUnit<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  public UnitConverter getSystemConverter() {
327    UnitConverter converter = AbstractConverter.IDENTITY;
328    for (Element e : elements) {
329      @SuppressWarnings("rawtypes")
330      UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter(); // TODO
331      // check
332      // for
333      // type
334      if (!(cvtr.isLinear()))
335        throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert");
336      if (e.root != 1)
337        throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent");
338      int pow = e.pow;
339      if (pow < 0) { // Negative power.
340        pow = -pow;
341        cvtr = cvtr.inverse();
342      }
343      for (int j = 0; j < pow; j++) {
344        converter = converter.concatenate(cvtr);
345      }
346    }
347    return converter;
348  }
349
350  @Override
351  public Dimension getDimension() {
352    Dimension dimension = QuantityDimension.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        if (dimension != null) {
358          dimension = dimension.multiply(d);
359        } else {
360          dimension = d; // FIXME hackaround
361        }
362      }
363    }
364    return dimension;
365  }
366
367  /**
368   * Returns the unit defined from the product of the specified elements.
369   *
370   * @param leftElems
371   *          left multiplicand elements.
372   * @param rightElems
373   *          right multiplicand elements.
374   * @return the corresponding unit.
375   */
376  @SuppressWarnings("rawtypes")
377  private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) {
378
379    // Merges left elements with right elements.
380    Element[] result = new Element[leftElems.length + rightElems.length];
381    int resultIndex = 0;
382    for (Element leftElem : leftElems) {
383      Unit<?> unit = leftElem.unit;
384      int p1 = leftElem.pow;
385      int r1 = leftElem.root;
386      int p2 = 0;
387      int r2 = 1;
388      for (Element rightElem : rightElems) {
389        if (unit.equals(rightElem.unit)) {
390          p2 = rightElem.pow;
391          r2 = rightElem.root;
392          break; // No duplicate.
393        }
394      }
395      int pow = (p1 * r2) + (p2 * r1);
396      int root = r1 * r2;
397      if (pow != 0) {
398        int gcd = gcd(Math.abs(pow), root);
399        result[resultIndex++] = new Element(unit, pow / gcd, root / gcd);
400      }
401    }
402
403    // Appends remaining right elements not merged.
404    for (Element rightElem : rightElems) {
405      Unit<?> unit = rightElem.unit;
406      boolean hasBeenMerged = false;
407      for (Element leftElem : leftElems) {
408        if (unit.equals(leftElem.unit)) {
409          hasBeenMerged = true;
410          break;
411        }
412      }
413      if (!hasBeenMerged)
414        result[resultIndex++] = rightElem;
415    }
416
417    // Returns or creates instance.
418    if (resultIndex == 0)
419      return AbstractUnit.ONE;
420    else if ((resultIndex == 1) && (result[0].pow == result[0].root))
421      return result[0].unit;
422    else {
423      Element[] elems = new Element[resultIndex];
424      System.arraycopy(result, 0, elems, 0, resultIndex);
425      return new ProductUnit(elems);
426    }
427  }
428
429  /**
430   * Returns the greatest common divisor (Euclid's algorithm).
431   *
432   * @param m
433   *          the first number.
434   * @param nn
435   *          the second number.
436   * @return the greatest common divisor.
437   */
438  private static int gcd(int m, int n) {
439    if (n == 0)
440      return m;
441    else
442      return gcd(n, m % n);
443  }
444
445  /**
446   * Inner product element represents a rational power of a single unit.
447   */
448  @SuppressWarnings("rawtypes")
449  private final static class Element implements UnitSupplier {
450
451    /**
452                 * 
453                 */
454    // private static final long serialVersionUID = 452938412398890507L;
455
456    /**
457     * Holds the single unit.
458     */
459    private final Unit<?> unit;
460
461    /**
462     * Holds the power exponent.
463     */
464    private final int pow;
465
466    /**
467     * Holds the root exponent.
468     */
469    private final int root;
470
471    /**
472     * Structural constructor.
473     *
474     * @param unit
475     *          the unit.
476     * @param pow
477     *          the power exponent.
478     * @param root
479     *          the root exponent.
480     */
481    private Element(Unit<?> unit, int pow, int root) {
482      this.unit = unit;
483      this.pow = pow;
484      this.root = root;
485    }
486
487    /**
488     * Returns this element's unit.
489     *
490     * @return the single unit.
491     */
492    public Unit<?> getUnit() {
493      return unit;
494    }
495
496    /**
497     * Returns the power exponent. The power exponent can be negative but is always different from zero.
498     *
499     * @return the power exponent of the single unit.
500     */
501    public int getPow() {
502      return pow;
503    }
504
505    /**
506     * Returns the root exponent. The root exponent is always greater than zero.
507     *
508     * @return the root exponent of the single unit.
509     */
510    public int getRoot() {
511      return root;
512    }
513  }
514
515  @Override
516  public String getSymbol() {
517    if (super.getSymbol() != null) {
518      return super.getSymbol();
519    }
520    return symbol;
521  }
522}