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.internal.function;
031
032import static org.apiguardian.api.API.Status.INTERNAL;
033
034import java.util.Objects;
035
036import org.apiguardian.api.API;
037
038import tech.units.indriya.function.Calculus;
039import tech.units.indriya.spi.NumberSystem;
040
041/**
042 * Provides arithmetic on Java {@link Number}s utilizing a provided {@link NumberSystem}.    
043 * 
044 * @author Andi Huber
045 * @author Werner Keil
046 * @version 2.1, May 14, 2021
047 * @since 2.0
048 */
049@API(status=INTERNAL)
050public final class Calculator {
051
052    /**
053     * Returns a new instance of a {@code Calculator} initialized with the default {@link NumberSystem}, 
054     * as set at {@link Calculus#currentNumberSystem()}
055     * <p>
056     * This implementation is *not* thread-safe, hence threads should not share instances of this. 
057     * @return a {@code Calculator} initialized with the default {@link NumberSystem} 
058     */
059    private static Calculator getInstance() {
060        return new Calculator(Calculus.currentNumberSystem());
061    }
062
063    /**
064     * Shortcut for {@code getDefault().load(number)}. See {@link #getInstance()} and {@link #load(Number)}
065     * @param number
066     * @return default {@code Calculator} with {@code number} loaded into its accumulator
067     */
068    public static Calculator of(Number number) {
069        return getInstance().load(number);
070    }
071
072    private final NumberSystem ns;
073    private Number acc = 0;
074    
075    /**
076     * Returns a new instance of a {@code Calculator} initialized with the given {@link NumberSystem}.
077     * @return a {@code Calculator} initialized with the given {@link NumberSystem}.
078     * @param ns the {@link NumberSystem} 
079     */
080    private Calculator(NumberSystem ns) {
081        this.ns = ns;
082    }
083    
084    /**
085     * Returns a new instance of a {@code Calculator} initialized with the default {@link NumberSystem}, 
086     * as set at {@link Calculus#currentNumberSystem()}
087     * <p>
088     * This implementation is *not* thread-safe, hence threads should not share instances of this. 
089     * @return a {@code Calculator} initialized with the default {@link NumberSystem} 
090     */
091    private Calculator() {
092        this(Calculus.currentNumberSystem());
093    }
094
095    /**
096     * Loads {@code number} into this {@code Calculator}´s accumulator. 
097     * @param number
098     * @return self
099     */
100    private Calculator load(Number number) {
101        Objects.requireNonNull(number);
102        this.acc = ns.narrow(number);
103        return this;
104    }
105    
106    /**
107     * Adds {@code number} to this {@code Calculator}´s accumulator, 
108     * then stores the result in the accumulator.
109     * @param number
110     * @return self
111     */
112    public Calculator add(Number number) {
113        Objects.requireNonNull(number);
114        acc = ns.add(acc, ns.narrow(number));    
115        return this;
116    }
117
118    /**
119     * Subtracts {@code number} from this {@code Calculator}´s accumulator, 
120     * then stores the result in the accumulator.
121     * @param number
122     * @return self
123     */
124    public Calculator subtract(Number number) {
125        Objects.requireNonNull(number);
126        acc = ns.subtract(acc, ns.narrow(number));
127        return this;
128    }
129    
130    /**
131     * Multiplies {@code number} with this {@code Calculator}´s accumulator, 
132     * then stores the result in the accumulator.
133     * @param number
134     * @return self
135     */
136    public Calculator multiply(Number number) {
137        acc = ns.multiply(acc, ns.narrow(number));    
138        return this;
139    }
140
141    /**
142     * Divides this {@code Calculator}´s accumulator by {@code number}, 
143     * then stores the result in the accumulator.
144     * @param number
145     * @return self
146     */
147    public Calculator divide(Number number) {
148        acc = ns.divide(acc, ns.narrow(number));    
149        return this;
150    }
151    
152    /**
153     * Takes this {@code Calculator}´s accumulator to the integer power of {@code exponent},
154     * then stores the result in the accumulator.
155     * @param exponent
156     * @return self
157     */
158    public Calculator power(int exponent) {
159        acc = ns.power(acc, exponent);    
160        return this;
161    }
162    
163    /**
164     * Calculates the absolute value of this {@code Calculator}´s accumulator,
165     * then stores the result in the accumulator.
166     * @return self
167     */
168    public Calculator abs() {
169        acc = ns.abs(acc);
170        return this;
171    }
172
173    /**
174     * Calculates the additive inverse value of this {@code Calculator}´s accumulator,
175     * then stores the result in the accumulator.
176     * @return self
177     */
178    public Calculator negate() {
179        acc = ns.negate(acc);
180        return this;
181    }
182
183    /**
184     * Calculates the multiplicative inverse value of this {@code Calculator}´s accumulator,
185     * then stores the result in the accumulator.
186     * @return self
187     */
188    public Calculator reciprocal() {
189        acc = ns.reciprocal(acc);
190        return this;
191    }
192    
193    /**
194     * Calculates Euler's constant taken to the power of this {@code Calculator}´s accumulator,
195     * then stores the result in the accumulator.
196     * @return self
197     */
198    public Calculator exp() {
199        acc = ns.exp(acc);
200        return this;
201    }
202    
203    /**
204     * Calculates the natural logarithm of this {@code Calculator}´s accumulator,
205     * then stores the result in the accumulator.
206     * @return self
207     */
208    public Calculator log() {
209        acc = ns.log(acc);
210        return this;
211    }
212    
213    // -- TERMINALS
214    
215    /**
216     * Allows to 'peek' at this {@code Calculator}´s accumulator. The {@link Number} returned is narrowed
217     * to best represent the numerical value w/o loss of precision within the {@link NumberSystem} as 
218     * configured for this {@code Calculator} instance.
219     * @return a narrowed version of this {@code Calculator}´s accumulator
220     */
221    public Number peek() {
222        return ns.narrow(acc);
223    }
224    
225    /**
226     * @return whether this {@code Calculator}´s accumulator is less than ONE
227     */
228    public boolean isLessThanOne() {
229        return ns.isLessThanOne(acc);
230    }
231}