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