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