001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2021, 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.BigInteger; 033import java.util.Objects; 034import java.util.function.DoubleSupplier; 035 036import javax.measure.UnitConverter; 037 038import tech.units.indriya.internal.function.Calculator; 039 040/** 041 * <p> 042 * This class represents a converter multiplying numeric values by an exact scaling factor (represented as the quotient of two <code>BigInteger</code> 043 * numbers). 044 * </p> 045 * 046 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 047 * @author <a href="mailto:werner@units.tech">Werner Keil</a> 048 * @author Andi Huber 049 * @version 2.0, October 8, 2020 050 * @since 1.0 051 */ 052public final class RationalConverter extends AbstractConverter 053implements MultiplyConverter { 054 055 /** 056 * 057 */ 058 private static final long serialVersionUID = -9192231963353351648L; 059 060 /** 061 * Holds the scale factor. 062 */ 063 private final RationalNumber factor; 064 065 /** 066 * Creates a rational converter with the specified scale factor. 067 * 068 * @param factor 069 * the scale factor. 070 * @throws NullPointerException 071 * if factor is {@code null} 072 */ 073 RationalConverter(RationalNumber factor) { 074 Objects.requireNonNull(factor); 075 this.factor = factor; 076 } 077 078 /** 079 * Creates a rational converter with the specified dividend and divisor. 080 * 081 * @param dividend 082 * the dividend. 083 * @param divisor 084 * the non-zero divisor. 085 * @throws IllegalArgumentException 086 * if <code>divisor = 0</code> 087 * @throws NullPointerException 088 * if dividend is {@code null} or divisor is {@code null} 089 */ 090 RationalConverter(BigInteger dividend, BigInteger divisor) { 091 factor = RationalNumber.of(dividend, divisor); 092 } 093 094 /** 095 * Convenience method equivalent to <code>new RationalConverter(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor))</code> 096 * 097 * @param dividend 098 * the dividend. 099 * @param divisor 100 * the positive divisor. 101 * @throws IllegalArgumentException 102 * if <code>divisor = 0</code> 103 */ 104 RationalConverter(long dividend, long divisor) { 105 factor = RationalNumber.of(dividend, divisor); 106 } 107 108 /** 109 * Creates a rational converter with the specified scale factor. 110 * 111 * @param factor 112 * the scale factor. 113 * @throws NullPointerException 114 * if factor is {@code null} 115 */ 116 static RationalConverter of(RationalNumber factor) { 117 return new RationalConverter(factor); 118 } 119 120 /** 121 * Convenience method equivalent to <code>new RationalConverter(dividend, divisor)</code> 122 * 123 * @param dividend 124 * the dividend. 125 * @param divisor 126 * the positive divisor. 127 * @throws IllegalArgumentException 128 * if <code>divisor = 0</code> 129 * @throws NullPointerException 130 * if dividend is {@code null} or divisor is {@code null} 131 */ 132 static RationalConverter of(BigInteger dividend, BigInteger divisor) { 133 return new RationalConverter(dividend, divisor); 134 } 135 136 /** 137 * Convenience method equivalent to <code>new RationalConverter(dividend, divisor)</code> 138 * 139 * @param dividend 140 * the dividend. 141 * @param divisor 142 * the positive divisor. 143 * @throws IllegalArgumentException 144 * if <code>divisor = 0</code> 145 */ 146 static RationalConverter of(long dividend, long divisor) { 147 return new RationalConverter(dividend, divisor); 148 } 149 150 /** 151 * Returns the integer dividend for this rational converter. 152 * 153 * @return this converter dividend. 154 */ 155 public BigInteger getDividend() { 156 return factor.getDividend(); 157 } 158 159 /** 160 * Returns the integer (positive) divisor for this rational converter. 161 * 162 * @return this converter divisor. 163 */ 164 public BigInteger getDivisor() { 165 return factor.getDivisor(); 166 } 167 168 @Override 169 protected Number convertWhenNotIdentity(Number value) { 170 return Calculator.of(factor) 171 .multiply(value) 172 .peek(); 173 } 174 175 @Override 176 public boolean isIdentity() { 177 return factor.compareTo(RationalNumber.ONE)==0; 178 } 179 180 @Override 181 protected boolean canReduceWith(AbstractConverter that) { 182 if (that instanceof RationalConverter) { 183 return true; 184 } 185 return that instanceof PowerOfIntConverter; 186 } 187 188 @Override 189 protected AbstractConverter reduce(AbstractConverter that) { 190 if (that instanceof RationalConverter) { 191 return composeSameType((RationalConverter) that); 192 } 193 if (that instanceof PowerOfIntConverter) { 194 return composeSameType(((PowerOfIntConverter) that).toRationalConverter()); 195 } 196 throw new IllegalStateException(String.format( 197 "%s.simpleCompose() not handled for converter %s", 198 this, that)); 199 } 200 201 202 @Override 203 protected RationalConverter inverseWhenNotIdentity() { 204 return RationalConverter.of(factor.reciprocal()); 205 } 206 207 @Override 208 protected final String transformationLiteral() { 209 return String.format("x -> x * %s", factor); 210 } 211 212 @Override 213 public boolean equals(Object obj) { 214 if (this == obj) { 215 return true; 216 } 217 if (obj instanceof RationalConverter) { 218 RationalConverter that = (RationalConverter) obj; 219 return Objects.equals(this.factor, that.factor); 220 } 221 return false; 222 } 223 224 @Override 225 public int hashCode() { 226 return factor.hashCode(); 227 } 228 229 @Override 230 public Number getValue() { 231 return factor; 232 } 233 234 @Override 235 public double getAsDouble() { 236 return factor.doubleValue(); 237 } 238 239 @Override 240 public int compareTo(UnitConverter o) { 241 if (this == o) { 242 return 0; 243 } 244 if (o instanceof RationalConverter) { 245 RationalConverter that = (RationalConverter) o; 246 return this.factor.compareTo(that.factor); 247 } 248 return this.getClass().getName().compareTo(o.getClass().getName()); 249 } 250 251 // -- HELPER 252 253 private AbstractConverter composeSameType(RationalConverter that) { 254 BigInteger newDividend = this.getDividend().multiply(that.getDividend()); 255 BigInteger newDivisor = this.getDivisor().multiply(that.getDivisor()); 256 BigInteger gcd = newDividend.gcd(newDivisor); 257 newDividend = newDividend.divide(gcd); 258 newDivisor = newDivisor.divide(gcd); 259 return (newDividend.equals(BigInteger.ONE) && newDivisor.equals(BigInteger.ONE)) 260 ? IDENTITY 261 : new RationalConverter(newDividend, newDivisor); 262 } 263}