001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2023, 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.function;
031
032import java.math.BigDecimal;
033import java.math.BigInteger;
034import java.util.function.DoubleSupplier;
035
036import javax.measure.Prefix;
037import javax.measure.UnitConverter;
038
039import tech.units.indriya.spi.NumberSystem;
040import tech.uom.lib.common.function.Converter;
041import tech.uom.lib.common.function.FactorSupplier;
042import tech.uom.lib.common.function.ValueSupplier;
043
044/**
045 * <p>
046 * This class represents a converter multiplying numeric values by a constant
047 * scaling factor represented by the {@link Number} type.
048 * </p>
049 * 
050 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
051 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
052 * @author Andi Huber
053 * @version 2.7, October 8, 2020
054 * @since 1.0
055 */
056public interface MultiplyConverter extends UnitConverter, Converter<Number, Number>, 
057 ValueSupplier<Number>, FactorSupplier<Number>, DoubleSupplier, Comparable<UnitConverter> {
058
059        // -- FACTORIES
060
061        public static MultiplyConverter ofRational(RationalNumber factor) {
062                if (factor.equals(RationalNumber.ONE)) {
063                        return identity();
064                }
065                return RationalConverter.of(factor);
066        }
067
068        /**
069         * Creates a MultiplyConverter with the specified rational factor made up of
070         * {@code dividend} and {@code divisor}
071         * 
072         * @param dividend
073         * @param divisor
074         */
075        public static MultiplyConverter ofRational(long dividend, long divisor) {
076                RationalNumber rational = RationalNumber.of(dividend, divisor);
077                return ofRational(rational);
078        }
079
080        /**
081         * Creates a MultiplyConverter with the specified rational factor made up of
082         * {@code dividend} and {@code divisor}
083         * 
084         * @param dividend
085         * @param divisor
086         */
087        public static MultiplyConverter ofRational(BigInteger dividend, BigInteger divisor) {
088                RationalNumber rational = RationalNumber.of(dividend, divisor);
089                return ofRational(rational);
090        }
091
092        /**
093         * Creates a MultiplyConverter with the specified constant factor.
094         * 
095         * @param factor
096         * @return
097         */
098        public static MultiplyConverter of(Number factor) {
099
100                NumberSystem ns = Calculus.currentNumberSystem();
101
102                if (ns.isOne(factor)) {
103                        return identity();
104                }
105
106                Number narrowedFactor = ns.narrow(factor);
107
108                if (narrowedFactor instanceof RationalNumber) {
109                        return ofRational((RationalNumber) narrowedFactor);
110                }
111
112                if (ns.isInteger(narrowedFactor)) {
113                        if (narrowedFactor instanceof BigInteger) {
114                                return ofRational(RationalNumber.ofInteger((BigInteger) narrowedFactor));
115                        }
116
117                        // TODO[220] yet only implemented for the default number system,
118                        // any other implementation might behave differently;
119                        // could fall back to long, but instead fail early
120                        if (!(ns instanceof DefaultNumberSystem)) {
121                                throw new UnsupportedOperationException("not yet supported");
122                        }
123
124                        return ofRational(RationalNumber.ofInteger(narrowedFactor.longValue()));
125                }
126
127                if (narrowedFactor instanceof Double || narrowedFactor instanceof Float) {
128                        return of(narrowedFactor.doubleValue());
129                }
130
131                if (narrowedFactor instanceof BigDecimal) {
132                        BigDecimal decimal = (BigDecimal) narrowedFactor;
133                        RationalNumber rational = RationalNumber.of(decimal);
134                        return ofRational(rational);
135                }
136
137                // TODO[220] any other case not supported yet, could fall back to double, but
138                // instead fail early
139                throw new UnsupportedOperationException("not yet supported");
140        }
141
142        /**
143         * Creates a MultiplyConverter with the specified constant factor.
144         * 
145         * @param factor the double factor.
146         * @return a new MultiplyConverter.
147         */
148        public static MultiplyConverter of(double factor) {
149                if (factor == 1.d) {
150                        return identity();
151                }
152                RationalNumber rational = RationalNumber.of(factor);
153                return ofRational(rational);
154        }
155
156        /**
157         * Creates a MultiplyConverter with the specified Prefix.
158         * 
159         * @param prefix the prefix for the factor.
160         * @return a new MultiplyConverter.
161         */
162        public static MultiplyConverter ofPrefix(Prefix prefix) {
163                if (prefix == null) {
164                        return identity();
165                }
166
167            // this is an optimization for the special case of exponent == 1, where we simply use
168                // Prefix.getValue() as the factor 
169        if (prefix.getExponent() == 1) {
170            return of(prefix.getValue());
171        }
172                
173                // as the spec allows for Prefix.getValue() to also return non integer numbers, 
174                // we do have to account for these (rare) cases 
175                NumberSystem ns = Calculus.currentNumberSystem();
176                if(!ns.isInteger(prefix.getValue())) {
177                    Number factor = ns.power(prefix.getValue(), prefix.getExponent());
178                    return of(factor);    
179                }
180                
181                return PowerOfIntConverter.of(prefix);
182                
183        }
184
185        /**
186         * Creates a MultiplyConverter with the specified exponent of Pi.
187         * 
188         * @param exponent the exponent for the factor π^exponent.
189         * @return a new MultiplyConverter.
190         */
191        public static MultiplyConverter ofPiExponent(int exponent) {
192                if (exponent == 0) {
193                        return identity();
194                }
195                return PowerOfPiConverter.of(exponent);
196        }
197        
198        /**
199         * Creates a MultiplyConverter with the specified base and exponent.
200         * @param base the base.
201         * @param exponent the exponent.
202         * @return a new MultiplyConverter.
203         */
204        public static MultiplyConverter ofExponent(int base, int exponent) {
205                if (exponent == 0) {
206                        return identity();
207                }
208                return PowerOfIntConverter.of(base, exponent);
209        }
210        
211        /**
212         * Creates a MultiplyConverter with base 10 and an exponent.
213         * @param exponent the exponent for the factor 10^exponent.
214         */
215        public static MultiplyConverter ofTenExponent(int exponent) {
216                if (exponent == 0) {
217                        return identity();
218                }
219                return PowerOfIntConverter.of(10, exponent);
220        }
221
222        /**
223         * Returns a MultiplyConverter that acts as a 'pass-through'.
224         * 
225         */
226        public static MultiplyConverter identity() {
227                return IdentityMultiplyConverter.INSTANCE;
228        }
229
230        // -- DEFAULTS
231
232        @Override
233        default boolean isLinear() {
234                return true;
235        }
236
237        /**
238         * Returns the scale factor of this converter.
239         * 
240         * @return the scale factor.
241         */
242        default Number getFactor() {
243                return getValue();
244        }
245}