001/*
002 * Units of Measurement Implementation for Java SE
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.uom.se.unit;
031
032import tec.uom.se.AbstractConverter;
033import tec.uom.se.AbstractUnit;
034import tec.uom.se.quantity.QuantityDimension;
035
036import javax.measure.Dimension;
037import javax.measure.Quantity;
038import javax.measure.Unit;
039import javax.measure.UnitConverter;
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 0.3, June 10, 2014
062 */
063public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> {
064
065  /**
066         *
067         */
068  private static final long serialVersionUID = 962983585531030093L;
069
070  /**
071   * Holds the units composing this product unit.
072   */
073  private final Element[] elements;
074
075  /**
076   * Holds the symbol for this unit.
077   */
078  private final String symbol;
079
080  /**
081   * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance).
082   */
083  public ProductUnit() {
084    this.symbol = "";
085    elements = new Element[0];
086  }
087
088  /**
089   * Copy constructor (allows for parameterization of product units).
090   *
091   * @param productUnit
092   *          the product unit source.
093   * @throws ClassCastException
094   *           if the specified unit is not a product unit.
095   */
096  public ProductUnit(Unit<?> productUnit) {
097    this.symbol = productUnit.getSymbol();
098    this.elements = ((ProductUnit<?>) productUnit).elements;
099  }
100
101  /**
102   * Product unit constructor.
103   *
104   * @param elements
105   *          the product elements.
106   */
107  private ProductUnit(Element[] elements) {
108    this.elements = elements;
109    this.symbol = elements[0].getUnit().getSymbol(); // FIXME this should
110    // contain ALL
111    // elements
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<?> getProductInstance(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<?> 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<>(); // Diamond (Java7+)
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    return false;
297  }
298
299  @Override
300  public int hashCode() {
301    return Objects.hash((Object[]) elements);
302  }
303
304  @SuppressWarnings("unchecked")
305  @Override
306  public AbstractUnit<Q> toSystemUnit() {
307    Unit<?> systemUnit = AbstractUnit.ONE;
308    for (Element element : elements) {
309      Unit<?> unit = element.unit.getSystemUnit();
310      unit = unit.pow(element.pow);
311      unit = unit.root(element.root);
312      systemUnit = systemUnit.multiply(unit);
313    }
314    return (AbstractUnit<Q>) systemUnit;
315  }
316
317  @Override
318  public UnitConverter getSystemConverter() {
319    UnitConverter converter = AbstractConverter.IDENTITY;
320    for (Element e : elements) {
321      if (e.unit instanceof AbstractUnit) {
322        UnitConverter cvtr = ((AbstractUnit) e.unit).getSystemConverter();
323        if (!(cvtr.isLinear()))
324          throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert");
325        if (e.root != 1)
326          throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent");
327        int pow = e.pow;
328        if (pow < 0) { // Negative power.
329          pow = -pow;
330          cvtr = cvtr.inverse();
331        }
332        for (int j = 0; j < pow; j++) {
333          converter = converter.concatenate(cvtr);
334        }
335      }
336    }
337    return converter;
338  }
339
340  @Override
341  public Dimension getDimension() {
342    Dimension dimension = QuantityDimension.NONE;
343    for (int i = 0; i < this.getUnitCount(); i++) {
344      Unit<?> unit = this.getUnit(i);
345      if (this.elements != null && unit.getDimension() != null) {
346        Dimension d = unit.getDimension().pow(this.getUnitPow(i)).root(this.getUnitRoot(i));
347        dimension = dimension.multiply(d);
348      }
349    }
350    return dimension;
351  }
352
353  /**
354   * Returns the unit defined from the product of the specified elements.
355   *
356   * @param leftElems
357   *          left multiplicand elements.
358   * @param rightElems
359   *          right multiplicand elements.
360   * @return the corresponding unit.
361   */
362  @SuppressWarnings("rawtypes")
363  private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) {
364
365    // Merges left elements with right elements.
366    Element[] result = new Element[leftElems.length + rightElems.length];
367    int resultIndex = 0;
368    for (Element leftElem : leftElems) {
369      Unit<?> unit = leftElem.unit;
370      int p1 = leftElem.pow;
371      int r1 = leftElem.root;
372      int p2 = 0;
373      int r2 = 1;
374      for (Element rightElem : rightElems) {
375        if (unit.equals(rightElem.unit)) {
376          p2 = rightElem.pow;
377          r2 = rightElem.root;
378          break; // No duplicate.
379        }
380      }
381      int pow = (p1 * r2) + (p2 * r1);
382      int root = r1 * r2;
383      if (pow != 0) {
384        int gcd = gcd(Math.abs(pow), root);
385        result[resultIndex++] = new Element(unit, pow / gcd, root / gcd);
386      }
387    }
388
389    // Appends remaining right elements not merged.
390    for (Element rightElem : rightElems) {
391      Unit<?> unit = rightElem.unit;
392      boolean hasBeenMerged = false;
393      for (Element leftElem : leftElems) {
394        if (unit.equals(leftElem.unit)) {
395          hasBeenMerged = true;
396          break;
397        }
398      }
399      if (!hasBeenMerged)
400        result[resultIndex++] = rightElem;
401    }
402
403    // Returns or creates instance.
404    if (resultIndex == 0)
405      return AbstractUnit.ONE;
406    else if ((resultIndex == 1) && (result[0].pow == result[0].root))
407      return result[0].unit;
408    else {
409      Element[] elems = new Element[resultIndex];
410      System.arraycopy(result, 0, elems, 0, resultIndex);
411      return new ProductUnit(elems);
412    }
413  }
414
415  /**
416   * Returns the greatest common divisor (Euclid's algorithm).
417   *
418   * @param m
419   *          the first number.
420   * @param nn
421   *          the second number.
422   * @return the greatest common divisor.
423   */
424  private static int gcd(int m, int n) {
425    if (n == 0)
426      return m;
427    else
428      return gcd(n, m % n);
429  }
430
431  /**
432   * Inner product element represents a rational power of a single unit.
433   */
434  private final static class Element implements Serializable {
435
436    /**
437                 *
438                 */
439    private static final long serialVersionUID = 452938412398890507L;
440
441    /**
442     * Holds the single unit.
443     */
444    private final Unit<?> unit;
445
446    /**
447     * Holds the power exponent.
448     */
449    private final int pow;
450
451    /**
452     * Holds the root exponent.
453     */
454    private final int root;
455
456    /**
457     * Structural constructor.
458     *
459     * @param unit
460     *          the unit.
461     * @param pow
462     *          the power exponent.
463     * @param root
464     *          the root exponent.
465     */
466    private Element(Unit<?> unit, int pow, int root) {
467      this.unit = unit;
468      this.pow = pow;
469      this.root = root;
470    }
471
472    /**
473     * Returns this element's unit.
474     *
475     * @return the single unit.
476     */
477    public Unit<?> getUnit() {
478      return unit;
479    }
480
481    /**
482     * Returns the power exponent. The power exponent can be negative but is always different from zero.
483     *
484     * @return the power exponent of the single unit.
485     */
486    public int getPow() {
487      return pow;
488    }
489
490    /**
491     * Returns the root exponent. The root exponent is always greater than zero.
492     *
493     * @return the root exponent of the single unit.
494     */
495    public int getRoot() {
496      return root;
497    }
498
499    @Override
500    public boolean equals(Object o) {
501      if (this == o)
502        return true;
503      if (o == null || getClass() != o.getClass())
504        return false;
505
506      Element element = (Element) o;
507
508      if (pow != element.pow) {
509        return false;
510      }
511      return root == element.root && (unit != null ? unit.equals(element.unit) : element.unit == null);
512
513    }
514
515    @Override
516    public int hashCode() {
517      int result = unit != null ? unit.hashCode() : 0;
518      result = 31 * result + pow;
519      result = 31 * result + root;
520      return result;
521    }
522  }
523
524  @Override
525  public String getSymbol() {
526    return symbol;
527  }
528}