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;
027
028/**
029 * Defines different inverses of a {@link DecimalRounding}. Each constant
030 * provides its own implementation of an {@link #invert(DecimalRounding)}
031 * method.
032 */
033enum RoundingInverse {
034        /**
035         * Constant calculating inverted rounding due to sign reversion.
036         * <p>
037         * The inverted rounding mode can be used to round a value {@code a} instead
038         * of the value {@code -a}.
039         */
040        SIGN_REVERSION {
041                @Override
042                public final DecimalRounding invert(DecimalRounding rounding) {
043                        switch (rounding) {
044                        case FLOOR:
045                                return DecimalRounding.CEILING;
046                        case CEILING:
047                                return DecimalRounding.FLOOR;
048                        case DOWN:
049                                return DecimalRounding.DOWN;
050                        case HALF_DOWN:
051                                return DecimalRounding.HALF_DOWN;
052                        case UP:
053                                return DecimalRounding.UP;
054                        case HALF_UP:
055                                return DecimalRounding.HALF_UP;
056                        case HALF_EVEN:
057                                return DecimalRounding.HALF_EVEN;
058                        case UNNECESSARY:
059                                return DecimalRounding.UNNECESSARY;
060                        default:
061                                // should not get here
062                                throw new IllegalArgumentException("Unsupported rounding mode: " + rounding);
063                        }
064                }
065        },
066        /**
067         * Constant calculating inverted rounding due to sign reversion occurring
068         * after an addition or subtraction.
069         * <p>
070         * The inverted rounding mode can be used to round a value {@code x} instead
071         * of the sum {@code (a + x)} when sum and {@code x} have opposite sign
072         * (equivalent for a difference {@code (a - x)}).
073         */
074        ADDITIVE_REVERSION {
075                public final DecimalRounding invert(DecimalRounding rounding) {
076                        switch (rounding) {
077                        case FLOOR:
078                                return DecimalRounding.FLOOR;
079                        case CEILING:
080                                return DecimalRounding.CEILING;
081                        case DOWN:
082                                return DecimalRounding.UP;
083                        case HALF_DOWN:
084                                return DecimalRounding.HALF_UP;
085                        case UP:
086                                return DecimalRounding.DOWN;
087                        case HALF_UP:
088                                return DecimalRounding.HALF_DOWN;
089                        case HALF_EVEN:
090                                return DecimalRounding.HALF_EVEN;
091                        case UNNECESSARY:
092                                return DecimalRounding.UNNECESSARY;
093                        default:
094                                // should not get here
095                                throw new IllegalArgumentException("Unsupported rounding mode: " + rounding);
096                        }
097                }
098        },
099        /**
100         * Constant calculating inverted rounding due to reciprocal of a value.
101         * <p>
102         * The inverted rounding mode can be used to round a value {@code x} instead
103         * of the reciprocal value {@code 1/x}.
104         */
105        RECIPROCAL {
106                public final DecimalRounding invert(DecimalRounding rounding) {
107                        switch (rounding) {
108                        case UP:
109                                return DecimalRounding.DOWN;
110                        case DOWN:
111                                return DecimalRounding.UP;
112                        case CEILING:
113                                return DecimalRounding.FLOOR;
114                        case FLOOR:
115                                return DecimalRounding.CEILING;
116                        case HALF_UP:
117                                return DecimalRounding.HALF_DOWN;
118                        case HALF_DOWN:
119                                return DecimalRounding.HALF_UP;
120                        case HALF_EVEN:
121                                return DecimalRounding.HALF_EVEN;// HALF_UNEVEN?
122                        case UNNECESSARY:
123                                return DecimalRounding.UNNECESSARY;
124                        default:
125                                // should not get here
126                                throw new IllegalArgumentException("Unsupported rounding mode: " + rounding);
127                        }
128                }
129        };
130        /**
131         * Returns the inverted rounding for the inversion case defined by this
132         * constant.
133         * 
134         * @param rounding
135         *            the original rounding
136         * @return the inverted rounding
137         */
138        abstract public DecimalRounding invert(DecimalRounding rounding);
139}