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.truncate;
025
026import java.math.RoundingMode;
027import java.util.Collections;
028import java.util.EnumSet;
029import java.util.Set;
030
031/**
032 * Provides rounding constants implementing {@link TruncationPolicy} for
033 * {@link OverflowMode#CHECKED}. The constants are equivalent to the constants
034 * defined by {@link RoundingMode}; the policy's {@link #getOverflowMode()}
035 * method always returns {@link OverflowMode#CHECKED CHECKED} overflow mode.
036 */
037public enum CheckedRounding implements TruncationPolicy {
038        /**
039         * Checked truncation policy with rounding mode to round away from zero.
040         * Always increments the digit prior to a non-zero discarded fraction. Note
041         * that this rounding mode never decreases the magnitude of the calculated
042         * value.
043         * 
044         * @see RoundingMode#UP
045         */
046        UP {
047                @Override
048                public final RoundingMode getRoundingMode() {
049                        return RoundingMode.UP;
050                }
051                @Override
052                public final UncheckedRounding toUncheckedRounding() {
053                        return UncheckedRounding.UP;
054                }
055        },
056
057        /**
058         * Checked truncation policy with rounding mode to round towards zero. Never
059         * increments the digit prior to a discarded fraction (i.e., truncates).
060         * Note that this rounding mode never increases the magnitude of the
061         * calculated value.
062         * 
063         * @see RoundingMode#DOWN
064         */
065        DOWN {
066                @Override
067                public final RoundingMode getRoundingMode() {
068                        return RoundingMode.DOWN;
069                }
070                @Override
071                public final UncheckedRounding toUncheckedRounding() {
072                        return UncheckedRounding.DOWN;
073                }
074        },
075
076        /**
077         * Checked truncation policy with rounding mode to round towards positive
078         * infinity. If the result is positive, behaves as for
079         * {@code RoundingMode.UP}; if negative, behaves as for
080         * {@code RoundingMode.DOWN}. Note that this rounding mode never decreases
081         * the calculated value.
082         * 
083         * @see RoundingMode#CEILING
084         */
085        CEILING {
086                @Override
087                public final RoundingMode getRoundingMode() {
088                        return RoundingMode.CEILING;
089                }
090                @Override
091                public final UncheckedRounding toUncheckedRounding() {
092                        return UncheckedRounding.CEILING;
093                }
094        },
095
096        /**
097         * Checked truncation policy with rounding mode to round towards negative
098         * infinity. If the result is positive, behave as for
099         * {@code RoundingMode.DOWN}; if negative, behave as for
100         * {@code RoundingMode.UP}. Note that this rounding mode never increases the
101         * calculated value.
102         * 
103         * @see RoundingMode#FLOOR
104         */
105        FLOOR {
106                @Override
107                public final RoundingMode getRoundingMode() {
108                        return RoundingMode.FLOOR;
109                }
110                @Override
111                public final UncheckedRounding toUncheckedRounding() {
112                        return UncheckedRounding.FLOOR;
113                }
114        },
115
116        /**
117         * Checked truncation policy with rounding mode to round towards
118         * {@literal "nearest neighbor"} unless both neighbors are equidistant, in
119         * which case round up. Behaves as for {@code RoundingMode.UP} if the
120         * discarded fraction is ≥ 0.5; otherwise, behaves as for
121         * {@code RoundingMode.DOWN}. Note that this is the rounding mode commonly
122         * taught at school.
123         * 
124         * @see RoundingMode#HALF_UP
125         */
126        HALF_UP {
127                @Override
128                public final RoundingMode getRoundingMode() {
129                        return RoundingMode.HALF_UP;
130                }
131                @Override
132                public final UncheckedRounding toUncheckedRounding() {
133                        return UncheckedRounding.HALF_UP;
134                }
135        },
136
137        /**
138         * Checked truncation policy with rounding mode to round towards
139         * {@literal "nearest neighbor"} unless both neighbors are equidistant, in
140         * which case round down. Behaves as for {@code RoundingMode.UP} if the
141         * discarded fraction is > 0.5; otherwise, behaves as for
142         * {@code RoundingMode.DOWN}.
143         * 
144         * @see RoundingMode#HALF_DOWN
145         */
146        HALF_DOWN {
147                @Override
148                public final RoundingMode getRoundingMode() {
149                        return RoundingMode.HALF_DOWN;
150                }
151                @Override
152                public final UncheckedRounding toUncheckedRounding() {
153                        return UncheckedRounding.HALF_DOWN;
154                }
155        },
156
157        /**
158         * Checked truncation policy with rounding mode to round towards the
159         * {@literal "nearest neighbor"} unless both neighbors are equidistant, in
160         * which case, round towards the even neighbor. Behaves as for
161         * {@code RoundingMode.HALF_UP} if the digit to the left of the discarded
162         * fraction is odd; behaves as for {@code RoundingMode.HALF_DOWN} if it's
163         * even. Note that this is the rounding mode that statistically minimizes
164         * cumulative error when applied repeatedly over a sequence of calculations.
165         * It is sometimes known as {@literal "Banker's rounding,"} and is chiefly
166         * used in the USA. This rounding mode is analogous to the rounding policy
167         * used for {@code float} and {@code double} arithmetic in Java.
168         * 
169         * @see RoundingMode#HALF_EVEN
170         */
171        HALF_EVEN {
172                @Override
173                public final RoundingMode getRoundingMode() {
174                        return RoundingMode.HALF_EVEN;
175                }
176                @Override
177                public final UncheckedRounding toUncheckedRounding() {
178                        return UncheckedRounding.HALF_EVEN;
179                }
180        },
181
182        /**
183         * Checked truncation policy with rounding mode to assert that the requested
184         * operation has an exact result, hence no rounding is necessary. If this
185         * rounding mode is specified on an operation that yields an inexact result,
186         * an {@code ArithmeticException} is thrown.
187         * 
188         * @see RoundingMode#UNNECESSARY
189         */
190        UNNECESSARY {
191                @Override
192                public final RoundingMode getRoundingMode() {
193                        return RoundingMode.UNNECESSARY;
194                }
195                @Override
196                public final UncheckedRounding toUncheckedRounding() {
197                        return UncheckedRounding.UNNECESSARY;
198                }
199        };
200
201        /**
202         * Returns {@link OverflowMode#CHECKED}.
203         * 
204         * @return CHECKED overflow mode
205         */
206        @Override
207        public final OverflowMode getOverflowMode() {
208                return OverflowMode.CHECKED;
209        }
210
211        /**
212         * Returns the policy with the same {@link #getRoundingMode() rounding mode}
213         * as this checked rounding policy but for {@link OverflowMode#UNCHECKED
214         * UNCHECKED} {@link #getOverflowMode() overflow mode}.
215         * 
216         * @return the {@link UncheckedRounding} counterpart to this policy.
217         */
218        abstract public UncheckedRounding toUncheckedRounding();
219
220        /**
221         * Returns "CHECKED/(name)" where {@code (name)} stands for the {@link #name()} of this constant.
222         * 
223         * @return a string like "CHECKED/HALF_UP"
224         */
225        @Override
226        public final String toString() {
227                return "CHECKED/" + name();
228        }
229
230        /**
231         * Immutable set with all values of this enum. Avoids object creation in
232         * contrast to {@link #values()}.
233         */
234        public static final Set<CheckedRounding> VALUES = Collections.unmodifiableSet(EnumSet.allOf(CheckedRounding.class));
235
236        /**
237         * Returns the checked rounding constant for the given rounding mode.
238         * 
239         * @param roundingMode
240         *            the rounding mode
241         * @return the constant corresponding to the given rounding mode
242         */
243        public static final CheckedRounding valueOf(RoundingMode roundingMode) {
244                return ByRoundingMode.VALUES_BY_ROUNDING_MODE_ORDINAL[roundingMode.ordinal()];
245        }
246
247        private static class ByRoundingMode {
248                private static final CheckedRounding[] VALUES_BY_ROUNDING_MODE_ORDINAL = sortByRoundingModeOrdinal();
249        
250                private static final CheckedRounding[] sortByRoundingModeOrdinal() {
251                        final CheckedRounding[] sorted = new CheckedRounding[VALUES.size()];
252                        for (final CheckedRounding dr : VALUES) {
253                                sorted[dr.getRoundingMode().ordinal()] = dr;
254                        }
255                        return sorted;
256                }
257        }
258}