001/* 002 * Units of Measurement Implementation for Java SE 003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM. 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-363 nor the names of its 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 tec.uom.se.function; 031 032import javax.measure.UnitConverter; 033 034import tec.uom.lib.common.function.ValueSupplier; 035import tec.uom.se.AbstractConverter; 036 037import java.math.BigDecimal; 038import java.math.BigInteger; 039import java.math.MathContext; 040import java.util.Objects; 041import java.util.function.DoubleSupplier; 042import java.util.function.Supplier; 043 044/** 045 * <p> 046 * This class represents a converter multiplying numeric values by an exact scaling factor (represented as the quotient of two <code>BigInteger</code> 047 * numbers). 048 * </p> 049 * 050 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 051 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 052 * @version 1.0, Oct 11, 2016 053 * @since 1.0 054 */ 055public final class RationalConverter extends AbstractConverter implements ValueSupplier<Double>, Supplier<Double>, DoubleSupplier { 056 057 /** 058 * 059 */ 060 private static final long serialVersionUID = 3563384008357680074L; 061 062 /** 063 * Holds the converter dividend. 064 */ 065 private final BigInteger dividend; 066 067 /** 068 * Holds the converter divisor (always positive). 069 */ 070 private final BigInteger divisor; 071 072 /** 073 * Creates a rational converter with the specified dividend and divisor. 074 * 075 * @param dividend 076 * the dividend. 077 * @param divisor 078 * the positive divisor. 079 * @throws IllegalArgumentException 080 * if <code>divisor <= 0</code> 081 * @throws IllegalArgumentException 082 * if <code>dividend == divisor</code> 083 */ 084 public RationalConverter(BigInteger dividend, BigInteger divisor) { 085 if (divisor.compareTo(BigInteger.ZERO) <= 0) 086 throw new IllegalArgumentException("Negative or zero divisor"); 087 if (dividend.equals(divisor)) 088 throw new IllegalArgumentException("Would result in identity converter"); 089 this.dividend = dividend; // Exact conversion. 090 this.divisor = divisor; // Exact conversion. 091 } 092 093 /** 094 * Convenience method equivalent to <code>new RationalConverter(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor))</code> 095 * 096 * @param dividend 097 * the dividend. 098 * @param divisor 099 * the positive divisor. 100 * @throws IllegalArgumentException 101 * if <code>divisor <= 0</code> 102 * @throws IllegalArgumentException 103 * if <code>dividend == divisor</code> 104 */ 105 public RationalConverter(long dividend, long divisor) { 106 this(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor)); 107 } 108 109 /** 110 * Convenience method equivalent to <code>new RationalConverter(dividend, divisor)</code> 111 * 112 * @param dividend 113 * the dividend. 114 * @param divisor 115 * the positive divisor. 116 * @throws IllegalArgumentException 117 * if <code>divisor <= 0</code> 118 * @throws IllegalArgumentException 119 * if <code>dividend == divisor</code> 120 */ 121 public static RationalConverter of(BigInteger dividend, BigInteger divisor) { 122 return new RationalConverter(dividend, divisor); 123 } 124 125 /** 126 * Convenience method equivalent to <code>new RationalConverter(dividend, divisor)</code> 127 * 128 * @param dividend 129 * the dividend. 130 * @param divisor 131 * the positive divisor. 132 * @throws IllegalArgumentException 133 * if <code>divisor <= 0</code> 134 * @throws IllegalArgumentException 135 * if <code>dividend == divisor</code> 136 */ 137 public static RationalConverter of(long dividend, long divisor) { 138 return new RationalConverter(dividend, divisor); 139 } 140 141 /** 142 * Convenience method equivalent to <code>new RationalConverter(BigDecimal.valueOf(dividend).toBigInteger(), 143 * BigDecimal.valueOf(divisor).toBigInteger())</code> 144 * 145 * @param dividend 146 * the dividend. 147 * @param divisor 148 * the positive divisor. 149 * @throws IllegalArgumentException 150 * if <code>divisor <= 0</code> 151 * @throws IllegalArgumentException 152 * if <code>dividend == divisor</code> 153 */ 154 public static RationalConverter of(double dividend, double divisor) { 155 return new RationalConverter(BigDecimal.valueOf(dividend).toBigInteger(), BigDecimal.valueOf(divisor).toBigInteger()); 156 } 157 158 /** 159 * Returns the integer dividend for this rational converter. 160 * 161 * @return this converter dividend. 162 */ 163 public BigInteger getDividend() { 164 return dividend; 165 } 166 167 /** 168 * Returns the integer (positive) divisor for this rational converter. 169 * 170 * @return this converter divisor. 171 */ 172 public BigInteger getDivisor() { 173 return divisor; 174 } 175 176 @Override 177 public double convert(double value) { 178 return value * toDouble(dividend) / toDouble(divisor); 179 } 180 181 // Optimization of BigInteger.doubleValue() (implementation too 182 // inneficient). 183 private static double toDouble(BigInteger integer) { 184 return (integer.bitLength() < 64) ? integer.longValue() : integer.doubleValue(); 185 } 186 187 @Override 188 public BigDecimal convert(BigDecimal value, MathContext ctx) throws ArithmeticException { 189 BigDecimal decimalDividend = new BigDecimal(dividend, 0); 190 BigDecimal decimalDivisor = new BigDecimal(divisor, 0); 191 return value.multiply(decimalDividend, ctx).divide(decimalDivisor, ctx); 192 } 193 194 @Override 195 public UnitConverter concatenate(UnitConverter converter) { 196 if (!(converter instanceof RationalConverter)) 197 return super.concatenate(converter); 198 RationalConverter that = (RationalConverter) converter; 199 BigInteger newDividend = this.getDividend().multiply(that.getDividend()); 200 BigInteger newDivisor = this.getDivisor().multiply(that.getDivisor()); 201 BigInteger gcd = newDividend.gcd(newDivisor); 202 newDividend = newDividend.divide(gcd); 203 newDivisor = newDivisor.divide(gcd); 204 return (newDividend.equals(BigInteger.ONE) && newDivisor.equals(BigInteger.ONE)) ? IDENTITY : new RationalConverter(newDividend, newDivisor); 205 } 206 207 @Override 208 public RationalConverter inverse() { 209 return dividend.signum() == -1 ? new RationalConverter(getDivisor().negate(), getDividend().negate()) : new RationalConverter(getDivisor(), 210 getDividend()); 211 } 212 213 @Override 214 public final String toString() { 215 return "RationalConverter(" + dividend + "," + divisor + ")"; 216 } 217 218 @Override 219 public boolean equals(Object obj) { 220 if (this == obj) { 221 return true; 222 } 223 if (obj instanceof RationalConverter) { 224 225 RationalConverter that = (RationalConverter) obj; 226 return Objects.equals(dividend, that.dividend) && Objects.equals(divisor, that.divisor); 227 } 228 return false; 229 } 230 231 @Override 232 public int hashCode() { 233 return Objects.hash(dividend, divisor); 234 } 235 236 @Override 237 public boolean isLinear() { 238 return true; 239 } 240 241 @Override 242 public Double getValue() { 243 return getAsDouble(); 244 } 245 246 @Override 247 public double getAsDouble() { 248 return toDouble(dividend) / toDouble(divisor); 249 } 250 251 @Override 252 public Double get() { 253 return getValue(); 254 } 255}