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}