001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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.AbstractUnit;
038import tech.units.indriya.function.AbstractConverter;
039import tech.units.indriya.unit.UnitDimension;
040
041import java.io.Serializable;
042import java.util.Arrays;
043import java.util.Comparator;
044import java.util.LinkedHashMap;
045import java.util.Map;
046import java.util.Objects;
047
048/**
049 * <p>
050 * This class represents units formed by the product of rational powers of existing physical units.
051 * </p>
052 *
053 * <p>
054 * This class maintains the canonical form of this product (simplest form after factorization). For example: <code>METRE.pow(2).divide(METRE)</code>
055 * returns <code>METRE</code>.
056 * </p>
057 *
058 * @param <Q>
059 *            The type of the quantity measured by this unit.
060 *
061 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
062 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
063 * @version 1.9, July 05, 2019
064 * @since 1.0
065 */
066public final class ProductUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> {
067
068    /**
069    *
070    */
071    private static final long serialVersionUID = 962983585531030093L;
072
073    /**
074     * Holds the units composing this product unit.
075     */
076    private final Element[] elements;
077
078    /**
079     * DefaultQuantityFactory constructor (used solely to create <code>ONE</code> instance).
080     */
081    public ProductUnit() {
082        super("");
083        elements = new Element[0];
084    }
085
086    /**
087     * Copy constructor (allows for parameterization of product units).
088     *
089     * @param productUnit
090     *            the product unit source.
091     * @throws ClassCastException
092     *             if the specified unit is not a product unit.
093     */
094    public ProductUnit(Unit<?> productUnit) {
095        super(productUnit.getSymbol());
096        this.elements = ((ProductUnit<?>) productUnit).elements;
097    }
098
099    /**
100     * Product unit constructor.
101     *
102     * @param elements
103     *            the product elements.
104     */
105    private ProductUnit(Element[] elements) {
106        super(null);
107        this.elements = elements;
108    }
109
110    /**
111     * Returns the product of the specified units.
112     *
113     * @param left
114     *            the left unit operand.
115     * @param right
116     *            the right unit operand.
117     * @return <code>left * right</code>
118     */
119    public static Unit<?> ofProduct(Unit<?> left, Unit<?> right) {
120        Element[] leftElems;
121        if (left instanceof ProductUnit<?>) {
122            leftElems = ((ProductUnit<?>) left).elements;
123        } else {
124            leftElems = new Element[] { new Element(left, 1, 1) };
125        }
126        Element[] rightElems;
127        if (right instanceof ProductUnit<?>) {
128            rightElems = ((ProductUnit<?>) right).elements;
129        } else {
130            rightElems = new Element[] { new Element(right, 1, 1) };
131        }
132        return getInstance(leftElems, rightElems);
133    }
134
135    /**
136     * Returns the quotient of the specified units.
137     *
138     * @param left
139     *            the dividend unit operand.
140     * @param right
141     *            the divisor unit operand.
142     * @return <code>dividend / divisor</code>
143     */
144    public static Unit<?> ofQuotient(Unit<?> left, Unit<?> right) {
145        Element[] leftElems;
146        if (left instanceof ProductUnit<?>)
147            leftElems = ((ProductUnit<?>) left).elements;
148        else
149            leftElems = new Element[] { new Element(left, 1, 1) };
150        Element[] rightElems;
151        if (right instanceof ProductUnit<?>) {
152            Element[] elems = ((ProductUnit<?>) right).elements;
153            rightElems = new Element[elems.length];
154            for (int i = 0; i < elems.length; i++) {
155                rightElems[i] = new Element(elems[i].unit, -elems[i].pow, elems[i].root);
156            }
157        } else
158            rightElems = new Element[] { new Element(right, -1, 1) };
159        return getInstance(leftElems, rightElems);
160    }
161
162    /**
163     * Returns the product unit corresponding to the specified root of the specified unit.
164     *
165     * @param unit
166     *            the unit.
167     * @param n
168     *            the root's order (n &gt; 0).
169     * @return <code>unit^(1/nn)</code>
170     * @throws ArithmeticException
171     *             if <code>n == 0</code>.
172     */
173    public static Unit<?> ofRoot(Unit<?> unit, int n) {
174        Element[] unitElems;
175        if (unit instanceof ProductUnit<?>) {
176            Element[] elems = ((ProductUnit<?>) unit).elements;
177            unitElems = new Element[elems.length];
178            for (int i = 0; i < elems.length; i++) {
179                int gcd = gcd(Math.abs(elems[i].pow), elems[i].root * n);
180                unitElems[i] = new Element(elems[i].unit, elems[i].pow / gcd, elems[i].root * n / gcd);
181            }
182        } else
183            unitElems = new Element[] { new Element(unit, 1, n) };
184        return getInstance(unitElems, new Element[0]);
185    }
186
187    /**
188     * Returns the product unit corresponding to this unit raised to the specified exponent.
189     *
190     * @param unit
191     *            the unit.
192     * @param nn
193     *            the exponent (nn &gt; 0).
194     * @return <code>unit^n</code>
195     */
196    public static Unit<?> ofPow(Unit<?> unit, int n) {
197        Element[] unitElems;
198        if (unit instanceof ProductUnit<?>) {
199            Element[] elems = ((ProductUnit<?>) unit).elements;
200            unitElems = new Element[elems.length];
201            for (int i = 0; i < elems.length; i++) {
202                int gcd = gcd(Math.abs(elems[i].pow * n), elems[i].root);
203                unitElems[i] = new Element(elems[i].unit, elems[i].pow * n / gcd, elems[i].root / gcd);
204            }
205        } else
206            unitElems = new Element[] { new Element(unit, n, 1) };
207        return getInstance(unitElems, new Element[0]);
208    }
209
210    @Override
211    public Unit<?> pow(int n) {
212      return ofPow(this, n);
213    }
214
215    /**
216     * Returns the number of unit elements in this product.
217     *
218     * @return the number of unit elements.
219     */
220    public int getUnitCount() {
221        return elements.length;
222    }
223
224    /**
225     * Returns the unit element at the specified position.
226     *
227     * @param index
228     *            the index of the unit element to return.
229     * @return the unit element at the specified position.
230     * @throws IndexOutOfBoundsException
231     *             if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
232     */
233    public Unit<?> getUnit(int index) {
234        return elements[index].getUnit();
235    }
236
237    /**
238     * Returns the power exponent of the unit element at the specified position.
239     *
240     * @param index
241     *            the index of the unit element.
242     * @return the unit power exponent at the specified position.
243     * @throws IndexOutOfBoundsException
244     *             if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
245     */
246    public int getUnitPow(int index) {
247        return elements[index].getPow();
248    }
249
250    /**
251     * Returns the root exponent of the unit element at the specified position.
252     *
253     * @param index
254     *            the index of the unit element.
255     * @return the unit root exponent at the specified position.
256     * @throws IndexOutOfBoundsException
257     *             if index is out of range <code>(index &lt; 0 || index &gt;= getUnitCount())</code>.
258     */
259    public int getUnitRoot(int index) {
260        return elements[index].getRoot();
261    }
262
263    @Override
264    public Map<Unit<?>, Integer> getBaseUnits() {
265        final Map<Unit<?>, Integer> units = new LinkedHashMap<>();
266        for (int i = 0; i < getUnitCount(); i++) {
267            units.put(getUnit(i), getUnitPow(i));
268        }
269        return units;
270    }
271
272    @Override
273    public boolean equals(Object obj) {
274        if (this == obj) {
275            return true;
276        }
277        if (obj instanceof ProductUnit<?>) {
278            Element[] elems = ((ProductUnit<?>) obj).elements;
279            if (elements.length != elems.length)
280                return false;
281            for (Element element : elements) {
282                boolean unitFound = false;
283                for (Element elem : elems) {
284                    if (element.unit.equals(elem.unit))
285                        if (element.pow != elem.pow || element.root != elem.root)
286                            return false;
287                        else {
288                            unitFound = true;
289                            break;
290                        }
291                }
292                if (!unitFound)
293                    return false;
294            }
295            return true;
296        }
297        return false;
298    }
299
300    @Override
301    public int hashCode() {
302        Arrays.sort(elements, new Comparator<Element>() {
303          @Override
304          public int compare(Element e0, Element e1) {
305                  if (e0.getUnit().getSystemUnit().getSymbol() != null && e1.getUnit().getSystemUnit().getSymbol() != null) {
306                          return e0.getUnit().getSystemUnit().getSymbol().compareTo(e1.getUnit().getSystemUnit().getSymbol());
307                  } else {
308                          return e0.getUnit().getSystemUnit().toString().compareTo(e1.getUnit().getSystemUnit().toString());
309                  }
310          }});
311        return Objects.hash((Object[]) elements);
312    }
313
314    @SuppressWarnings("unchecked")
315    @Override
316    public Unit<Q> toSystemUnit() {
317        Unit<?> systemUnit = AbstractUnit.ONE;
318        for (Element element : elements) {
319            Unit<?> unit = element.unit.getSystemUnit();
320            unit = unit.pow(element.pow);
321            unit = unit.root(element.root);
322            systemUnit = systemUnit.multiply(unit);
323        }
324        return (AbstractUnit<Q>) systemUnit;
325    }
326
327    @Override
328    public UnitConverter getSystemConverter() {
329        UnitConverter converter = AbstractConverter.IDENTITY;
330        for (Element e : elements) {
331            if (e.unit instanceof AbstractUnit) {
332                UnitConverter cvtr = ((AbstractUnit<?>) e.unit).getSystemConverter();
333                if (!(cvtr.isLinear()))
334                    throw new UnsupportedOperationException(e.unit + " is non-linear, cannot convert");
335                if (e.root != 1)
336                    throw new UnsupportedOperationException(e.unit + " holds a base unit with fractional exponent");
337                int pow = e.pow;
338                if (pow < 0) { // Negative power.
339                    pow = -pow;
340                    cvtr = cvtr.inverse();
341                }
342                for (int j = 0; j < pow; j++) {
343                    converter = converter.concatenate(cvtr);
344                }
345            }
346        }
347        return converter;
348    }
349
350    @Override
351    public Dimension getDimension() {
352        Dimension dimension = UnitDimension.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                dimension = dimension.multiply(d);
358            }
359        }
360        return dimension;
361    }
362
363    /**
364     * Returns the unit defined from the product of the specified elements.
365     *
366     * @param leftElems
367     *            left multiplicand elements.
368     * @param rightElems
369     *            right multiplicand elements.
370     * @return the corresponding unit.
371     */
372    @SuppressWarnings("rawtypes")
373    private static Unit<?> getInstance(Element[] leftElems, Element[] rightElems) {
374
375        // Merges left elements with right elements.
376        Element[] result = new Element[leftElems.length + rightElems.length];
377        int resultIndex = 0;
378        for (Element leftElem : leftElems) {
379            Unit<?> unit = leftElem.unit;
380            int p1 = leftElem.pow;
381            int r1 = leftElem.root;
382            int p2 = 0;
383            int r2 = 1;
384            for (Element rightElem : rightElems) {
385                if (unit.equals(rightElem.unit)) {
386                    p2 = rightElem.pow;
387                    r2 = rightElem.root;
388                    break; // No duplicate.
389                }
390            }
391            int pow = p1 * r2 + p2 * r1;
392            int root = r1 * r2;
393            if (pow != 0) {
394                int gcd = gcd(Math.abs(pow), root);
395                result[resultIndex++] = new Element(unit, pow / gcd, root / gcd);
396            }
397        }
398
399        // Appends remaining right elements not merged.
400        for (Element rightElem : rightElems) {
401            Unit<?> unit = rightElem.unit;
402            boolean hasBeenMerged = false;
403            for (Element leftElem : leftElems) {
404                if (unit.equals(leftElem.unit)) {
405                    hasBeenMerged = true;
406                    break;
407                }
408            }
409            if (!hasBeenMerged)
410                result[resultIndex++] = rightElem;
411        }
412
413        // Returns or creates instance.
414        if (resultIndex == 0)
415            return AbstractUnit.ONE;
416        else if (resultIndex == 1 && result[0].pow == result[0].root)
417            return result[0].unit;
418        else {
419            Element[] elems = new Element[resultIndex];
420            System.arraycopy(result, 0, elems, 0, resultIndex);
421            return new ProductUnit(elems);
422        }
423    }
424
425    /**
426     * Returns the greatest common divisor (Euclid's algorithm).
427     *
428     * @param m
429     *            the first number.
430     * @param nn
431     *            the second number.
432     * @return the greatest common divisor.
433     */
434    private static int gcd(int m, int n) {
435        return n == 0 ? m : gcd(n, m % n);
436    }
437
438    /**
439     * Inner product element represents a rational power of a single unit.
440     */
441    private final static class Element implements Serializable {
442
443        /**
444         *
445         */
446        private static final long serialVersionUID = 452938412398890507L;
447
448        /**
449         * Holds the single unit.
450         */
451        private final Unit<?> unit;
452
453        /**
454         * Holds the power exponent.
455         */
456        private final int pow;
457
458        /**
459         * Holds the root exponent.
460         */
461        private final int root;
462
463        /**
464         * Structural constructor.
465         *
466         * @param unit
467         *            the unit.
468         * @param pow
469         *            the power exponent.
470         * @param root
471         *            the root exponent.
472         */
473        private Element(Unit<?> unit, int pow, int root) {
474            this.unit = unit;
475            this.pow = pow;
476            this.root = root;
477        }
478
479        /**
480         * Returns this element's unit.
481         *
482         * @return the single unit.
483         */
484        public Unit<?> getUnit() {
485            return unit;
486        }
487
488        /**
489         * Returns the power exponent. The power exponent can be negative but is always different from zero.
490         *
491         * @return the power exponent of the single unit.
492         */
493        public int getPow() {
494            return pow;
495        }
496
497        /**
498         * Returns the root exponent. The root exponent is always greater than zero.
499         *
500         * @return the root exponent of the single unit.
501         */
502        public int getRoot() {
503            return root;
504        }
505
506        @Override
507        public boolean equals(Object o) {
508            if (this == o)
509                return true;
510            if (o == null || getClass() != o.getClass())
511                return false;
512
513            Element element = (Element) o;
514
515            if (pow != element.pow) {
516                return false;
517            }
518            return root == element.root && unit != null ? unit.equals(element.unit) : element.unit == null;
519
520        }
521
522        @Override
523        public int hashCode() {
524            return Objects.hash(unit, pow, root);
525        }
526    }
527}