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