001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2018, 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.BigDecimal;
033import java.math.MathContext;
034import java.util.Objects;
035
036import javax.measure.UnitConverter;
037
038import tech.units.indriya.AbstractConverter;
039import tech.uom.lib.common.function.ValueSupplier;
040
041/**
042 * <p>
043 * This class represents a logarithmic converter of limited precision. Such converter is typically used to create logarithmic unit. For example:<code>
044 * Unit<Dimensionless> BEL = Unit.ONE.transform(new LogConverter(10).inverse()); </code>
045 *
046 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
047 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
048 * @version 1.0.1, December 28, 2017
049 * @since 1.0
050 */
051public final class LogConverter extends AbstractConverter implements ValueSupplier<String> { // implements
052        // Immutable<String>
053        // {
054
055        /**
056         * 
057         */
058        private static final long serialVersionUID = -7584688290961460870L;
059
060        /**
061         * Holds the logarithmic base.
062         */
063        private final double base;
064        /**
065         * Holds the natural logarithm of the base.
066         */
067        private final double logOfBase;
068
069        /**
070         * Returns a logarithmic converter having the specified base.
071         *
072         * @param base
073         *          the logarithmic base (e.g. <code>Math.E</code> for the Natural Logarithm).
074         */
075        public LogConverter(double base) {
076                this.base = base;
077                this.logOfBase = Math.log(base);
078        }
079
080        /**
081         * Returns the logarithmic base of this converter.
082         *
083         * @return the logarithmic base (e.g. <code>Math.E</code> for the Natural Logarithm).
084         */
085        public double getBase() {
086                return base;
087        }
088
089        @Override
090        public boolean isIdentity() {
091                return false;
092        }
093
094        @Override
095        protected boolean isSimpleCompositionWith(AbstractConverter that) {
096                if(that instanceof ExpConverter) {
097                        return ((ExpConverter)that).getBase() == base; // can compose with exp to identity, provided it has same base
098                }
099                return false;
100        }
101
102        @Override
103        protected AbstractConverter simpleCompose(AbstractConverter that) {
104                return AbstractConverter.IDENTITY;
105        }
106
107        @Override
108        public AbstractConverter inverseWhenNotIdentity() {
109                return new ExpConverter(base);
110        }
111
112        @Override
113        public final String transformationLiteral() {
114                if (base == Math.E) {
115                        return "x -> ln(x)";
116                } else {
117                        return String.format("x -> log(base=%s, x)", base);
118                }
119        }
120        
121        @Override
122        public boolean equals(Object obj) {
123                if (this == obj) {
124                        return true;
125                }
126                if (obj instanceof LogConverter) {
127                        LogConverter that = (LogConverter) obj;
128                        return Objects.equals(base, that.base);
129                }
130                return false;
131        }
132
133        @Override
134        public int hashCode() {
135                return Objects.hash(base);
136        }
137
138        @Override
139        public double convertWhenNotIdentity(double amount) {
140                return Math.log(amount) / logOfBase;
141        }
142
143        @Override
144        public BigDecimal convertWhenNotIdentity(BigDecimal value, MathContext ctx) throws ArithmeticException {
145                return BigDecimal.valueOf(convert(value.doubleValue())); // Reverts to
146                // double
147                // conversion.
148        }
149
150        @Override
151        public boolean isLinear() {
152                return false;
153        }
154
155        @Override
156        public String getValue() {
157                return toString();
158        }
159
160        @Override
161        public int compareTo(UnitConverter o) {
162                if (this == o) {
163                        return 0;
164                }
165                if (o instanceof ValueSupplier) {
166                        return getValue().compareTo(String.valueOf(((ValueSupplier<?>) o).getValue()));
167                }
168                return -1;
169        }
170}