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; 034 035import javax.measure.Prefix; 036import javax.measure.UnitConverter; 037 038import tech.units.indriya.internal.function.calc.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 1.5, Jun 25, 2019 047 * @since 2.0 048 */ 049//TODO[220] make this like all the other MultiplyConverter package private 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 int compareTo(UnitConverter o) { 183 if (this == o) { 184 return 0; 185 } 186 if(this.isIdentity() && o.isIdentity()) { 187 return 0; 188 } 189 if (o instanceof PowerOfIntConverter) { 190 PowerOfIntConverter other = (PowerOfIntConverter) o; 191 int c = Integer.compare(base, other.base); 192 if(c!=0) { 193 return c; 194 } 195 return Integer.compare(exponent, other.exponent); 196 } 197 return this.getClass().getName().compareTo(o.getClass().getName()); 198 } 199 200 @Override 201 public int hashCode() { 202 return hashCode; 203 } 204 205 // -- HELPER 206 207 private RationalNumber calculateRationalNumberFactor() { 208 if(exponent==0) { 209 return RationalNumber.ONE; 210 } 211 BigInteger bintFactor = BigInteger.valueOf(base).pow(Math.abs(exponent)); 212 if(exponent>0) { 213 return RationalNumber.ofInteger(bintFactor); 214 } 215 return RationalNumber.of(BigInteger.ONE, bintFactor); 216 } 217 218 private PowerOfIntConverter composeSameBaseNonIdentity(PowerOfIntConverter other) { 219 // no check for identity required 220 return new PowerOfIntConverter(this.base, this.exponent + other.exponent); 221 } 222 223 public RationalConverter toRationalConverter() { 224 return new RationalConverter(rationalFactor); 225 } 226 227 @Override 228 public Number getValue() { 229 return rationalFactor; 230 } 231 232}