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.truncate.DecimalRounding;
027import org.decimal4j.truncate.TruncatedPart;
028
029/**
030 * Utility class to calculate rounding increments in different situations; 
031 * utilizes functionality provided by {@link DecimalRounding} and
032 * {@link TruncatedPart}.
033 */
034final class Rounding {
035
036        /**
037         * Returns the rounding increment appropriate for the specified
038         * {@code rounding}. The returned value is one of -1, 0 or 1.
039         * 
040         * @param rounding
041         *            the rounding mode to apply
042         * @param sign
043         *            the sign of the total value, either +1 or -1; determines the
044         *            result value if rounded
045         * @param truncatedValue
046         *            the truncated result before rounding is applied (only used for
047         *            HALF_EVEN rounding)
048         * @param firstTruncatedDigit
049         *            the first truncated digit, must be in {@code [0, 1, ..., 9]}
050         * @param zeroAfterFirstTruncatedDigit
051         *            true if all truncated digits after the first truncated digit
052         *            are zero, and false otherwise
053         * @return the value to add to {@code truncatedValue} to get the rounded
054         *         result, one of -1, 0 or 1
055         */
056        public static final int calculateRoundingIncrement(DecimalRounding rounding, int sign, long truncatedValue, int firstTruncatedDigit, boolean zeroAfterFirstTruncatedDigit) {
057                return rounding.calculateRoundingIncrement(sign, truncatedValue, TruncatedPart.valueOf(firstTruncatedDigit, zeroAfterFirstTruncatedDigit));
058        }
059
060        /**
061         * Returns the rounding increment appropriate for the specified
062         * {@code rounding} given the remaining truncated digits truncated by a
063         * given divisor. The returned value is one of -1, 0 or 1.
064         * 
065         * @param rounding
066         *            the rounding mode to apply
067         * @param truncatedValue
068         *            the truncated result before rounding is applied (only used for
069         *            HALF_EVEN rounding)
070         * @param truncatedDigits
071         *            the truncated part, it most hold that
072         *            {@code abs(truncatedDigits) < abs(divisor)}
073         * @param divisor
074         *            the divisor that led to the truncated digits
075         * @return the value to add to {@code truncatedValue} to get the rounded
076         *         result, one of -1, 0 or 1
077         */
078        public static final int calculateRoundingIncrementForDivision(DecimalRounding rounding, long truncatedValue, long truncatedDigits, long divisor) {
079                if (truncatedDigits == 0) {
080                        return 0;
081                }
082                final TruncatedPart truncatedPart = truncatedPartFor(Math.abs(truncatedDigits), Math.abs(divisor));
083                return rounding.calculateRoundingIncrement(Long.signum(truncatedDigits ^ divisor), truncatedValue, truncatedPart);
084        }
085
086        /**
087         * Returns the rounding increment appropriate for the specified
088         * {@code rounding} given the remaining truncated digits truncated by modulo
089         * one. The returned value is one of -1, 0 or 1.
090         * 
091         * @param rounding
092         *            the rounding mode to apply
093         * @param truncatedValue
094         *            the truncated result before rounding is applied (only used for
095         *            HALF_EVEN rounding)
096         * @param truncatedDigits
097         *            the truncated part of a double, must be {@code >-one} and
098         *            {@code <one}
099         * @param one
100         *            the value representing 1 which is {@code 10^scale}, must be
101         *            {@code >= 10}
102         * @return the value to add to {@code truncatedValue} to get the rounded
103         *         result, one of -1, 0 or 1
104         */
105        public static final int calculateRoundingIncrement(DecimalRounding rounding, long truncatedValue, long truncatedDigits, long one) {
106                if (truncatedDigits == 0) {
107                        return 0;
108                }
109                final TruncatedPart truncatedPart = truncatedPartFor(Math.abs(truncatedDigits), one);
110                return rounding.calculateRoundingIncrement(Long.signum(truncatedDigits), truncatedValue, truncatedPart);
111        }
112
113        /**
114         * Returns a truncated part constant given a non-negative remainder
115         * resulting from a division by the given non-negative divisor.
116         * 
117         * @param nonNegativeRemainder
118         *            the remainder part, not negative and
119         *            {@code nonNegativeRemainder < nonNegativeDivisor}
120         * @param nonNegativeDivisor
121         *            the divisor, not negative or LONG.MIN_VALUE --- the latter
122         *            equal to {@code abs(Long.MIN_VALUE)}
123         * @return the truncated part constant equivalent to the given arguments
124         */
125        public static final TruncatedPart truncatedPartFor(long nonNegativeRemainder, long nonNegativeDivisor) {
126                if (nonNegativeRemainder == 0) {
127                        return TruncatedPart.ZERO;
128                }
129                final long halfNonNegativeDivisor = nonNegativeDivisor >>> 1;
130                //NOTE: halfNonNegativeDivisor cannot be zero, because if it was 1 then nonNegativeRemainder was 0
131
132                if (halfNonNegativeDivisor < nonNegativeRemainder) {
133                        return TruncatedPart.GREATER_THAN_HALF;
134                }
135                if ((nonNegativeDivisor & 0x1) == 0 & halfNonNegativeDivisor == nonNegativeRemainder) {
136                        return TruncatedPart.EQUAL_TO_HALF;
137                }
138                return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
139        }
140
141        /**
142         * Returns a truncated part constant given a non-negative remainder
143         * resulting from a division by 10^19.
144         * 
145         * @param remainder
146         *            the remainder part
147         * @return the truncated part constant equivalent to the given arguments
148         */
149        public static final TruncatedPart truncatedPartForScale19(long remainder) {
150                if (remainder == 0) {
151                        return TruncatedPart.ZERO;
152                }
153                if (5000000000000000000L > remainder & remainder > -5000000000000000000L) {
154                        return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
155                }
156                if (remainder == 5000000000000000000L | remainder == -5000000000000000000L) {
157                        return TruncatedPart.EQUAL_TO_HALF;
158                }
159                return TruncatedPart.GREATER_THAN_HALF;
160        }
161
162        /**
163         * Returns a truncated part constant given a non-negative remainder
164         * resulting from a division by 2^n
165         * 
166         * @param remainder
167         *            the remainder part
168         * @param n
169         *            the power of 2 of the divisor, {@code n > 0}
170         * @return the truncated part constant equivalent to the given arguments
171         */
172        public static final TruncatedPart truncatedPartFor2powN(long remainder, int n) {
173                if (n < 63) {
174                        return truncatedPartFor(remainder, 1L << n);
175                } else if (n == 63) {
176                        return truncatedPartFor2pow63(remainder);
177                } else if (n == 64) {
178                        return truncatedPartFor2pow64(remainder);
179                } else {
180                        return remainder == 0 ? TruncatedPart.ZERO : TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
181                }
182        }
183
184        /**
185         * Returns a truncated part constant given a non-negative 128 bit remainder
186         * resulting from a division by 2^n
187         * 
188         * @param hRemainder
189         *            the high bits of the remainder part
190         * @param lRemainder
191         *            the low bits of the remainder part
192         * @param n
193         *            the power of 2 of the divisor, {@code n > 0}
194         * @return the truncated part constant equivalent to the given arguments
195         */
196        public static final TruncatedPart truncatedPartFor2powN(long hRemainder, long lRemainder, int n) {
197                if (hRemainder == 0) {
198                        return truncatedPartFor2powN(lRemainder, n);
199                }
200                final TruncatedPart hPart = truncatedPartFor2powN(hRemainder, n - Long.SIZE);
201                switch (hPart) {
202                case ZERO:
203                        return lRemainder == 0 ? TruncatedPart.ZERO : TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
204                case LESS_THAN_HALF_BUT_NOT_ZERO:
205                        return hPart; 
206                case EQUAL_TO_HALF:
207                        return lRemainder == 0 ? TruncatedPart.EQUAL_TO_HALF: TruncatedPart.GREATER_THAN_HALF;
208                case GREATER_THAN_HALF:
209                        return hPart; 
210                default:
211                        throw new RuntimeException("internal error: unsupported truncated part: " + hPart);
212                }
213        }
214
215        /**
216         * Returns a truncated part constant given a non-negative remainder
217         * resulting from a division by 2^63
218         * 
219         * @param remainder
220         *            the remainder part
221         * @return the truncated part constant equivalent to the given arguments
222         */
223        public static final TruncatedPart truncatedPartFor2pow63(long remainder) {
224                if (remainder == 0) {
225                        return TruncatedPart.ZERO;
226                }
227                if ((1L << 62) > remainder & remainder > -(1L << 62)) {
228                        return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
229                }
230                if (remainder == (1L << 62) | remainder == -(1L << 62)) {
231                        return TruncatedPart.EQUAL_TO_HALF;
232                }
233                return TruncatedPart.GREATER_THAN_HALF;
234        }
235
236        /**
237         * Returns a truncated part constant given a non-negative remainder
238         * resulting from a division by 2^64
239         * 
240         * @param remainder
241         *            the remainder part
242         * @return the truncated part constant equivalent to the given arguments
243         */
244        public static final TruncatedPart truncatedPartFor2pow64(long remainder) {
245                if (remainder == 0) {
246                        return TruncatedPart.ZERO;
247                }
248                if (remainder == 0x8000000000000000L) {
249                        return TruncatedPart.EQUAL_TO_HALF;
250                }
251                if (remainder < 0) {
252                        return TruncatedPart.GREATER_THAN_HALF;
253                }
254                return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO;
255        }
256
257        // no instances
258        private Rounding() {
259                super();
260        }
261}