001/*
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2015-2023 decimal4j (tools4j), Marco Terzer
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in all
014 * copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024package org.decimal4j.arithmetic;
025
026import org.decimal4j.api.DecimalArithmetic;
027
028/**
029 * Helper class for arithmetic operations with overflow checks.
030 */
031final class Checked {
032
033        /**
034         * Returns true if the addition {@code long1 + long2 = result} has resulted
035         * in an overflow.
036         * 
037         * @param long1
038         *            the first summand
039         * @param long2
040         *            the second summand
041         * @param result
042         *            the sum
043         * @return true if the calculation resulted in an overflow
044         */
045        static final boolean isAddOverflow(long long1, long long2, long result) {
046                return (long1 ^ long2) >= 0 & (long1 ^ result) < 0;
047        }
048
049        /**
050         * Returns true if the subtraction {@code minuend - subtrahend = result} has
051         * resulted in an overflow.
052         * 
053         * @param minuend
054         *            the minuend to subtract from
055         * @param subtrahend
056         *            the subtrahend to subtract
057         * @param result
058         *            the difference
059         * @return true if the calculation resulted in an overflow
060         */
061        static final boolean isSubtractOverflow(long minuend, long subtrahend, long result) {
062                return (minuend ^ subtrahend) < 0 & (minuend ^ result) < 0;
063        }
064
065        /**
066         * Returns true if the quotient {@code dividend / divisor} will result in an
067         * overflow.
068         * 
069         * @param dividend
070         *            the dividend
071         * @param divisor
072         *            the divisor
073         * @return true if the calculation will result in an overflow
074         */
075        static final boolean isDivideOverflow(long dividend, long divisor) {
076                return dividend == Long.MIN_VALUE & divisor == -1;
077        }
078
079        /**
080         * Returns the sum {@code (long1 + long2)} of the two {@code long} values
081         * throwing an exception if an overflow occurs.
082         * 
083         * @param long1
084         *            the first summand
085         * @param long2
086         *            the second summand
087         * @return the sum of the two values
088         * @throws ArithmeticException
089         *             if the calculation results in an overflow
090         */
091        public static final long addLong(long long1, long long2) {
092                final long result = long1 + long2;
093                if (isAddOverflow(long1, long2, result)) {
094                        throw new ArithmeticException("Overflow: " + long1 + " + " + long2 + " = " + result);
095                }
096                return result;
097        }
098
099        /**
100         * Returns the sum {@code (uDecimal1 + uDecimal2)} of the two unsigned
101         * decimal values throwing an exception if an overflow occurs.
102         * 
103         * @param arith
104         *            the arithmetic associated with the two unsigned decimals
105         * @param uDecimal1
106         *            the first summand
107         * @param uDecimal2
108         *            the second summand
109         * @return the sum of the two values
110         * @throws ArithmeticException
111         *             if the calculation results in an overflow
112         */
113        public static final long add(DecimalArithmetic arith, long uDecimal1, long uDecimal2) {
114                final long result = uDecimal1 + uDecimal2;
115                if ((uDecimal1 ^ uDecimal2) >= 0 & (uDecimal1 ^ result) < 0) {
116                        throw new ArithmeticException("Overflow: " + arith.toString(uDecimal1) + " + " + arith.toString(uDecimal2)
117                                        + " = " + arith.toString(result));
118                }
119                return result;
120        }
121
122        /**
123         * Returns the difference {@code (lMinuend - lSubtrahend)} of the two
124         * {@code long} values throwing an exception if an overflow occurs.
125         * 
126         * @param lMinuend
127         *            the minuend
128         * @param lSubtrahend
129         *            the subtrahend
130         * @return the difference of the two values
131         * @throws ArithmeticException
132         *             if the calculation results in an overflow
133         */
134        public static final long subtractLong(long lMinuend, long lSubtrahend) {
135                final long result = lMinuend - lSubtrahend;
136                if (isSubtractOverflow(lMinuend, lSubtrahend, result)) {
137                        throw new ArithmeticException("Overflow: " + lMinuend + " - " + lSubtrahend + " = " + result);
138                }
139                return result;
140        }
141
142        /**
143         * Returns the difference {@code (uDecimalMinuend - uDecimalSubtrahend)} of
144         * the two unscaled decimal values throwing an exception if an overflow
145         * occurs.
146         * 
147         * @param arith
148         *            the arithmetic associated with the two unsigned decimals
149         * @param uDecimalMinuend
150         *            the minuend
151         * @param uDecimalSubtrahend
152         *            the subtrahend
153         * @return the difference of the two values
154         * @throws ArithmeticException
155         *             if the calculation results in an overflow
156         */
157        public static final long subtract(DecimalArithmetic arith, long uDecimalMinuend, long uDecimalSubtrahend) {
158                final long result = uDecimalMinuend - uDecimalSubtrahend;
159                if (isSubtractOverflow(uDecimalMinuend, uDecimalSubtrahend, result)) {
160                        throw new ArithmeticException("Overflow: " + arith.toString(uDecimalMinuend) + " - "
161                                        + arith.toString(uDecimalSubtrahend) + " = " + arith.toString(result));
162                }
163                return result;
164        }
165
166        /**
167         * Returns the product {@code (lValue1 * lValue2)} of the two {@code long}
168         * values throwing an exception if an overflow occurs.
169         * 
170         * @param lValue1
171         *            the first factor
172         * @param lValue2
173         *            the second factor
174         * @return the product of the two values
175         * @throws ArithmeticException
176         *             if the calculation results in an overflow
177         */
178        public static final long multiplyLong(long lValue1, long lValue2) {
179                // Hacker's Delight, Section 2-12
180                final int leadingZeros = Long.numberOfLeadingZeros(lValue1) + Long.numberOfLeadingZeros(~lValue1)
181                                + Long.numberOfLeadingZeros(lValue2) + Long.numberOfLeadingZeros(~lValue2);
182                /*
183                 * If leadingZeros > Long.SIZE + 1 it's definitely fine, if it's <
184                 * Long.SIZE it's definitely bad. We do the leadingZeros check to avoid
185                 * the division below if at all possible.
186                 * 
187                 * Otherwise, if b == Long.MIN_VALUE, then the only allowed values of a
188                 * are 0 and 1. We take care of all a < 0 with their own check, because
189                 * in particular, the case a == -1 will incorrectly pass the division
190                 * check below.
191                 * 
192                 * In all other cases, we check that either a is 0 or the result is
193                 * consistent with division.
194                 */
195                final long result = lValue1 * lValue2;
196                if (leadingZeros > Long.SIZE + 1) {
197                        return result;
198                }
199                if (leadingZeros < Long.SIZE || (lValue1 < 0 & lValue2 == Long.MIN_VALUE)
200                                || (lValue1 != 0 && (result / lValue1) != lValue2)) {
201                        throw new ArithmeticException("Overflow: " + lValue1 + " * " + lValue2 + " = " + result);
202                }
203                return result;
204        }
205
206        /**
207         * Returns the product {@code (uDecimal * lValue)} of an unsigned decimal
208         * value and a {@code long} value throwing an exception if an overflow
209         * occurs.
210         * 
211         * @param arith
212         *            the arithmetic associated with the first unsigned decimal
213         *            argument
214         * @param uDecimal
215         *            the first factor
216         * @param lValue
217         *            the second factor
218         * @return the product of the two values
219         * @throws ArithmeticException
220         *             if the calculation results in an overflow
221         */
222        public static final long multiplyByLong(DecimalArithmetic arith, long uDecimal, long lValue) {
223                // Hacker's Delight, Section 2-12
224                final int leadingZeros = Long.numberOfLeadingZeros(uDecimal) + Long.numberOfLeadingZeros(~uDecimal)
225                                + Long.numberOfLeadingZeros(lValue) + Long.numberOfLeadingZeros(~lValue);
226                /*
227                 * If leadingZeros > Long.SIZE + 1 it's definitely fine, if it's <
228                 * Long.SIZE it's definitely bad. We do the leadingZeros check to avoid
229                 * the division below if at all possible.
230                 * 
231                 * Otherwise, if b == Long.MIN_VALUE, then the only allowed values of a
232                 * are 0 and 1. We take care of all a < 0 with their own check, because
233                 * in particular, the case a == -1 will incorrectly pass the division
234                 * check below.
235                 * 
236                 * In all other cases, we check that either a is 0 or the result is
237                 * consistent with division.
238                 */
239                final long result = uDecimal * lValue;
240                if (leadingZeros > Long.SIZE + 1) {
241                        return result;
242                }
243                if (leadingZeros < Long.SIZE || (uDecimal < 0 & lValue == Long.MIN_VALUE)
244                                || (uDecimal != 0 && (result / uDecimal) != lValue)) {
245                        throw new ArithmeticException(
246                                        "Overflow: " + arith.toString(uDecimal) + " * " + lValue + " = " + arith.toString(result));
247                }
248                return result;
249        }
250
251        /**
252         * Returns the quotient {@code (lDividend / lDivisor)} of the two
253         * {@code long} values throwing an exception if an overflow occurs.
254         * 
255         * @param lDividend
256         *            the dividend to divide
257         * @param lDivisor
258         *            the divisor to divide by
259         * @return the quotient of the two values
260         * @throws ArithmeticException
261         *             if the calculation results in an overflow
262         */
263        public static final long divideLong(long lDividend, long lDivisor) {
264                if (lDivisor == -1 & lDividend == Long.MIN_VALUE) {
265                        throw new ArithmeticException("Overflow: " + lDividend + " / " + lDivisor + " = " + Long.MIN_VALUE);
266                }
267                return lDividend / lDivisor;
268        }
269
270        /**
271         * Returns the quotient {@code (uDecimalDividend / lDivisor)} of an unscaled
272         * decimal value and a {@code long} value throwing an exception if an
273         * overflow occurs.
274         * 
275         * @param arith
276         *            the arithmetic associated with the first unsigned decimal
277         *            argument
278         * @param uDecimalDividend
279         *            the dividend to divide
280         * @param lDivisor
281         *            the divisor to divide by
282         * @return the quotient of the two values
283         * @throws ArithmeticException
284         *             if the calculation results in an overflow
285         */
286        public static final long divideByLong(DecimalArithmetic arith, long uDecimalDividend, long lDivisor) {
287                if (lDivisor == 0) {
288                        throw new ArithmeticException("Division by zero: " + arith.toString(uDecimalDividend) + " / " + lDivisor);
289                }
290                if (lDivisor == -1 & uDecimalDividend == Long.MIN_VALUE) {
291                        throw new ArithmeticException("Overflow: " + arith.toString(uDecimalDividend) + " / " + lDivisor + " = "
292                                        + arith.toString(Long.MIN_VALUE));
293                }
294                return uDecimalDividend / lDivisor;
295        }
296
297        /**
298         * Returns the absolute value {@code |value|} throwing an exception if an
299         * overflow occurs.
300         * 
301         * @param arith
302         *            the arithmetic associated with the value
303         * @param value
304         *            the number whose absolute value to return
305         * @return the absolute of the specified value
306         * @throws ArithmeticException
307         *             if the calculation results in an overflow
308         */
309        public static final long abs(DecimalArithmetic arith, long value) {
310                final long abs = Math.abs(value);
311                if (abs < 0) {
312                        throw new ArithmeticException("Overflow: abs(" + arith.toString(value) + ") = " + abs);
313                }
314                return abs;
315        }
316
317        /**
318         * Returns the negation {@code (-value)} throwing an exception if an
319         * overflow occurs.
320         * 
321         * @param arith
322         *            the arithmetic associated with the value
323         * @param value
324         *            the number to negate
325         * @return the negation of the specified value
326         * @throws ArithmeticException
327         *             if the calculation results in an overflow
328         */
329        public static final long negate(DecimalArithmetic arith, long value) {
330                final long neg = -value;
331                if (value != 0 & (value ^ neg) >= 0) {
332                        throw new ArithmeticException("Overflow: -" + arith.toString(value) + " = " + neg);
333                }
334                return neg;
335        }
336
337        // no instances
338        private Checked() {
339        }
340
341}