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; 034 035import javax.measure.Prefix; 036import javax.measure.UnitConverter; 037 038import tech.units.indriya.internal.function.Calculator; 039import tech.uom.lib.common.function.IntBaseSupplier; 040import tech.uom.lib.common.function.IntExponentSupplier; 041 042/** 043 * UnitConverter for numbers in base^exponent representation. 044 * @author Andi Huber 045 * @author Werner Keil 046 * @version 2.0, Oct 8, 2020 047 * @since 2.0 048 */ 049// As it's used in the "format" package, we may not be able to make it package-private here 050public final class PowerOfIntConverter extends AbstractConverter 051 implements MultiplyConverter, IntBaseSupplier, IntExponentSupplier { 052 private static final long serialVersionUID = 3546932001671571300L; 053 054 private final int base; 055 private final int exponent; 056 private final int hashCode; 057 private final RationalNumber rationalFactor; 058 059 /** 060 * Creates a converter with the specified Prefix. 061 * 062 * @param prefix 063 * the prefix for the factor. 064 */ 065 static PowerOfIntConverter of(Prefix prefix) { 066 return of(prefix.getValue(), prefix.getExponent()); 067 } 068 069 /** 070 * Creates a converter with a factor represented by specified base^exponent. 071 * 072 * @param base 073 * @param exponent 074 * @return 075 */ 076 static PowerOfIntConverter of(int base, int exponent) { 077 return new PowerOfIntConverter(base, exponent); 078 } 079 080 /** 081 * Creates a converter with a factor represented by specified base^exponent. 082 * 083 * @param base 084 * @param exponent 085 * @return 086 */ 087 static PowerOfIntConverter of(Number base, int exponent) { 088 return new PowerOfIntConverter(base.intValue(), exponent); 089 } 090 091 protected PowerOfIntConverter(int base, int exponent) { 092 if(base == 0) { 093 throw new IllegalArgumentException("base cannot be zero (because 0^0 is undefined)"); 094 } 095 this.base = base; 096 this.exponent = exponent; 097 this.hashCode = Objects.hash(base, exponent); 098 this.rationalFactor = calculateRationalNumberFactor(); 099 } 100 101 public int getBase() { 102 return base; 103 } 104 105 public int getExponent() { 106 return exponent; 107 } 108 109 @Override 110 public boolean isIdentity() { 111 if( base == 1 ) { 112 return true; // 1^x = 1 113 } 114 return exponent == 0; // x^0 = 1, for any x!=0 115 // [ahuber] 0^0 is undefined, but we guard against base==0 in the constructor, 116 // and there is no composition, that changes the base 117 } 118 119 @Override 120 protected boolean canReduceWith(AbstractConverter that) { 121 if (that instanceof PowerOfIntConverter) { 122 return ((PowerOfIntConverter) that).base == this.base; 123 } 124 return that instanceof RationalConverter; 125 } 126 127 @Override 128 protected AbstractConverter reduce(AbstractConverter that) { 129 if (that instanceof PowerOfIntConverter) { 130 PowerOfIntConverter other = (PowerOfIntConverter) that; 131 if(this.base == other.base) { // always true due to guard above 132 return composeSameBaseNonIdentity(other); 133 } 134 } 135 if (that instanceof RationalConverter) { 136 return (AbstractConverter) toRationalConverter().concatenate(that); 137 } 138 throw new IllegalStateException(String.format( 139 "%s.simpleCompose() not handled for converter %s", 140 this, that)); 141 } 142 143 @Override 144 public AbstractConverter inverseWhenNotIdentity() { 145 return new PowerOfIntConverter(base, -exponent); 146 } 147 148 @Override 149 protected Number convertWhenNotIdentity(Number value) { 150 return Calculator.of(rationalFactor) 151 .multiply(value) 152 .peek(); 153 } 154 155 @Override 156 public boolean equals(Object obj) { 157 if (this == obj) { 158 return true; 159 } 160 if (obj instanceof UnitConverter) { 161 UnitConverter other = (UnitConverter) obj; 162 if(this.isIdentity() && other.isIdentity()) { 163 return true; 164 } 165 } 166 if (obj instanceof PowerOfIntConverter) { 167 PowerOfIntConverter other = (PowerOfIntConverter) obj; 168 return this.base == other.base && this.exponent == other.exponent; 169 } 170 return false; 171 } 172 173 @Override 174 public final String transformationLiteral() { 175 if(base<0) { 176 return String.format("x -> x * (%s)^%s", base, exponent); 177 } 178 return String.format("x -> x * %s^%s", base, exponent); 179 } 180 181 @Override 182 public Number getValue() { 183 return rationalFactor; 184 } 185 186 @Override 187 public double getAsDouble() { 188 return rationalFactor.doubleValue(); 189 } 190 191 @Override 192 public int compareTo(UnitConverter o) { 193 if (this == o) { 194 return 0; 195 } 196 if(this.isIdentity() && o.isIdentity()) { 197 return 0; 198 } 199 if (o instanceof PowerOfIntConverter) { 200 PowerOfIntConverter other = (PowerOfIntConverter) o; 201 int c = Integer.compare(base, other.base); 202 if(c!=0) { 203 return c; 204 } 205 return Integer.compare(exponent, other.exponent); 206 } 207 return this.getClass().getName().compareTo(o.getClass().getName()); 208 } 209 210 @Override 211 public int hashCode() { 212 return hashCode; 213 } 214 215 // -- HELPER 216 217 private RationalNumber calculateRationalNumberFactor() { 218 if(exponent==0) { 219 return RationalNumber.ONE; 220 } 221 BigInteger bintFactor = BigInteger.valueOf(base).pow(Math.abs(exponent)); 222 if(exponent>0) { 223 return RationalNumber.ofInteger(bintFactor); 224 } 225 return RationalNumber.of(BigInteger.ONE, bintFactor); 226 } 227 228 private PowerOfIntConverter composeSameBaseNonIdentity(PowerOfIntConverter other) { 229 // no check for identity required 230 return new PowerOfIntConverter(this.base, this.exponent + other.exponent); 231 } 232 233 public RationalConverter toRationalConverter() { 234 return new RationalConverter(rationalFactor); 235 } 236}