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