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