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;
027import org.decimal4j.truncate.OverflowMode;
028
029/**
030 * Helper class used by pow methods to handle special cases.
031 */
032enum SpecialPowResult {
033        /**
034         * {@code a^n} with {@code n==0} leading to {@code 1}
035         */
036        EXPONENT_IS_ZERO {
037                @Override
038                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
039                        return arithmetic.one();// yes 0^0 is also 1
040                }
041        },
042        /**
043         * {@code a^n} with {@code n==1} leading to {@code a}
044         */
045        EXPONENT_IS_ONE {
046                @Override
047                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
048                        return uDecimal;
049                }
050        },
051        /**
052         * {@code a^n} with {@code a==0} leading to {@code 0} if {@code n>=0} and to
053         * an arithmetic exception if {@code n<0}
054         */
055        BASE_IS_ZERO {
056                @Override
057                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
058                        if (exponent >= 0) {
059                                // uDecimal == 0 should never happen (0^0 is usually defined as
060                                // 1)
061                                return 0;
062                        }
063                        throw new ArithmeticException("Division by zero: " + arithmetic.toString(uDecimal) + "^" + exponent);
064                }
065        },
066        /**
067         * {@code a^n} with {@code a==1} leading to {@code 1}
068         */
069        BASE_IS_ONE {
070                @Override
071                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
072                        return uDecimal;// uDecimal is 1
073                }
074        },
075        /**
076         * {@code a^n} with {@code a==-1} leading to {@code 1} if {@code n} is even
077         * and to {@code -1} if {@code n} is odd.
078         */
079        BASE_IS_MINUS_ONE {
080                @Override
081                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
082                        return ((exponent & 0x1) == 0) ? -uDecimal : uDecimal;// uDecimal is
083                                                                                                                                        // one and
084                                                                                                                                        // it's
085                                                                                                                                        // negation
086                                                                                                                                        // cannot
087                                                                                                                                        // overflow
088                }
089        },
090        /**
091         * {@code a^n} with {@code n==-1} leading to {@code 1/a}
092         */
093        EXPONENT_IS_MINUS_ONE {
094                @Override
095                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
096                        return arithmetic.invert(uDecimal);
097                }
098        },
099        /**
100         * {@code a^n} with {@code n==2} leading to {@code square(a)}
101         */
102        EXPONENT_IS_TWO {
103                @Override
104                final long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent) {
105                        return arithmetic.square(uDecimal);
106                }
107        };
108
109        /**
110         * Performs the exponentiation for this special pow result. The arithmetics
111         * overflow mode is considered.
112         * 
113         * @param arithmetic
114         *            the arithmetic associated with the values
115         * @param uDecimal
116         *            the base value
117         * @param exponent
118         *            the exponent
119         * @return <code>uDecimal<sup>exponent</sup></code>
120         * @throws ArithmeticException
121         *             if {@code uDecimal==0} and exponent is negative or if an
122         *             overflow occurs and the arithmetic's {@link OverflowMode} is
123         *             set to throw an exception
124         */
125        abstract long pow(DecimalArithmetic arithmetic, long uDecimal, int exponent);
126
127        /**
128         * Returns the special power case if it is one and null otherwise.
129         * 
130         * @param arithmetic
131         *            the arithmetic object
132         * @param uDecimal
133         *            the base
134         * @param n
135         *            the exponent
136         * @return the special case if it is one and null otherwise
137         */
138        static final SpecialPowResult getFor(DecimalArithmetic arithmetic, long uDecimal, long n) {
139                if (n == 0) {
140                        return EXPONENT_IS_ZERO;
141                }
142                if (n == 1) {
143                        return EXPONENT_IS_ONE;
144                }
145                if (uDecimal == 0) {
146                        return BASE_IS_ZERO;
147                }
148                final long one = arithmetic.one();
149                if (uDecimal == one) {
150                        return BASE_IS_ONE;
151                }
152                if (uDecimal == -one) {
153                        return BASE_IS_MINUS_ONE;
154                }
155                if (n == -1) {
156                        return EXPONENT_IS_MINUS_ONE;
157                }
158                if (n == 2) {
159                        return EXPONENT_IS_TWO;
160                }
161                return null;
162        }
163}