001/*
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2015-2024 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.scale;
025
026import static java.math.RoundingMode.DOWN;
027import static java.math.RoundingMode.FLOOR;
028import static java.math.RoundingMode.HALF_EVEN;
029import static java.math.RoundingMode.HALF_UP;
030import static java.math.RoundingMode.UNNECESSARY;
031import static org.decimal4j.truncate.OverflowMode.CHECKED;
032import static org.decimal4j.truncate.OverflowMode.UNCHECKED;
033
034import java.math.BigDecimal;
035import java.math.BigInteger;
036import java.math.RoundingMode;
037
038import org.decimal4j.api.DecimalArithmetic;
039import org.decimal4j.arithmetic.CheckedScaleNfRoundingArithmetic;
040import org.decimal4j.arithmetic.CheckedScaleNfTruncatingArithmetic;
041import org.decimal4j.arithmetic.UncheckedScaleNfRoundingArithmetic;
042import org.decimal4j.arithmetic.UncheckedScaleNfTruncatingArithmetic;
043import org.decimal4j.truncate.DecimalRounding;
044import org.decimal4j.truncate.OverflowMode;
045import org.decimal4j.truncate.TruncationPolicy;
046
047/**
048 * Scale class for decimals with {@link #getScale() scale} 2 and
049 * {@link #getScaleFactor() scale factor} 100.
050 */
051public enum Scale2f implements ScaleMetrics {
052
053        /**
054         * The singleton instance for scale 2.
055         */
056        INSTANCE;
057
058        private static final long LONG_MASK = 0xffffffffL;
059
060        /**
061         * The scale value <code>2</code>.
062         */
063        public static final int SCALE = 2;
064
065        /**
066         * The scale factor <code>10<sup>2</sup></code>.
067         */
068        public static final long SCALE_FACTOR = 100L;
069        
070        /** Long.numberOfLeadingZeros(SCALE_FACTOR)*/
071        private static final int NLZ_SCALE_FACTOR = 57;
072        
073        private static final long MAX_INTEGER_VALUE = Long.MAX_VALUE / SCALE_FACTOR;
074        private static final long MIN_INTEGER_VALUE = Long.MIN_VALUE / SCALE_FACTOR;
075        private static final BigInteger BI_SCALE_FACTOR = BigInteger.valueOf(SCALE_FACTOR);
076        private static final BigDecimal BD_SCALE_FACTOR = BigDecimal.valueOf(SCALE_FACTOR);
077
078        private static final DecimalArithmetic[] UNCHECKED_ARITHMETIC = initArithmetic(UNCHECKED);
079        private static final DecimalArithmetic[] CHECKED_ARITHMETIC = initArithmetic(CHECKED);
080
081        private static final DecimalArithmetic DEFAULT_ARITHMETIC = UNCHECKED_ARITHMETIC[HALF_UP.ordinal()];
082        private static final DecimalArithmetic DEFAULT_CHECKED_ARITHMETIC = CHECKED_ARITHMETIC[HALF_UP.ordinal()];
083        private static final DecimalArithmetic ROUNDING_DOWN_ARITHMETIC = UNCHECKED_ARITHMETIC[DOWN.ordinal()];
084        private static final DecimalArithmetic ROUNDING_FLOOR_ARITHMETIC = UNCHECKED_ARITHMETIC[FLOOR.ordinal()];
085        private static final DecimalArithmetic ROUNDING_HALF_EVEN_ARITHMETIC = UNCHECKED_ARITHMETIC[HALF_EVEN.ordinal()];
086        private static final DecimalArithmetic ROUNDING_UNNECESSARY_ARITHMETIC = UNCHECKED_ARITHMETIC[UNNECESSARY.ordinal()];
087
088        private static final DecimalArithmetic[] initArithmetic(OverflowMode overflowMode) {
089                final boolean checked = overflowMode == CHECKED;
090                final DecimalArithmetic[] arith = new DecimalArithmetic[DecimalRounding.VALUES.size()];
091                for (final DecimalRounding dr : DecimalRounding.VALUES) {
092                        final int index = dr.getRoundingMode().ordinal();
093                        if (dr == DecimalRounding.DOWN) {
094                                arith[index] = checked ? new CheckedScaleNfTruncatingArithmetic(INSTANCE)
095                                                : new UncheckedScaleNfTruncatingArithmetic(INSTANCE);
096                        } else {
097                                arith[index] = checked ? new CheckedScaleNfRoundingArithmetic(INSTANCE, dr)
098                                                : new UncheckedScaleNfRoundingArithmetic(INSTANCE, dr);
099                        }
100                }
101                return arith;
102        }
103
104        @Override
105        public final int getScale() {
106                return SCALE;
107        }
108
109        @Override
110        public final long getScaleFactor() {
111                return SCALE_FACTOR;
112        }
113        
114        @Override
115        public final int getScaleFactorNumberOfLeadingZeros() {
116                return NLZ_SCALE_FACTOR;
117        }
118
119        @Override
120        public final long multiplyByScaleFactor(long factor) {
121                return factor * SCALE_FACTOR;
122        }
123
124        @Override
125        public final BigInteger getScaleFactorAsBigInteger() {
126                return BI_SCALE_FACTOR;
127        }
128
129        @Override
130        public final BigDecimal getScaleFactorAsBigDecimal() {
131                return BD_SCALE_FACTOR;
132        }
133
134        @Override
135        public final long getMaxIntegerValue() {
136                return MAX_INTEGER_VALUE;
137        }
138
139        @Override
140        public final long getMinIntegerValue() {
141                return MIN_INTEGER_VALUE;
142        }
143
144        @Override
145        public final boolean isValidIntegerValue(long value) {
146                return MIN_INTEGER_VALUE <= value & value <= MAX_INTEGER_VALUE;
147        }
148
149        @Override
150        public final long multiplyByScaleFactorExact(long factor) {
151                final long result = factor * SCALE_FACTOR;
152                if (MIN_INTEGER_VALUE <= factor & factor <= MAX_INTEGER_VALUE) {
153                        return result;
154                }
155                throw new ArithmeticException("Overflow: " + factor + " * " + SCALE_FACTOR + " = " + result);
156        }
157        
158        @Override
159        public final long mulloByScaleFactor(int factor) {
160                return (factor & LONG_MASK) * SCALE_FACTOR;
161        }
162
163        @Override
164        public final long mulhiByScaleFactor(int factor) {
165                return 0;
166        }
167
168        @Override
169        public final long divideByScaleFactor(long dividend) {
170                return dividend / SCALE_FACTOR;
171        }
172
173        @Override
174        public final long divideUnsignedByScaleFactor(long unsignedDividend) {
175                //we can do this since SCALE_FACTOR > 1 and even
176                return (unsignedDividend >>> 1) / (SCALE_FACTOR >>> 1);
177        }
178
179        @Override
180        public final long moduloByScaleFactor(long dividend) {
181                return dividend % SCALE_FACTOR;
182        }
183
184        @Override
185        public final String toString(long value) {
186                return DEFAULT_ARITHMETIC.toString(value);
187        }
188
189        @Override
190        public final DecimalArithmetic getDefaultArithmetic() {
191                return DEFAULT_ARITHMETIC;
192        }
193
194        @Override
195        public final DecimalArithmetic getDefaultCheckedArithmetic() {
196                return DEFAULT_CHECKED_ARITHMETIC;
197        }
198
199        @Override
200        public final DecimalArithmetic getRoundingDownArithmetic() {
201                return ROUNDING_DOWN_ARITHMETIC;
202        }
203
204        @Override
205        public final DecimalArithmetic getRoundingFloorArithmetic() {
206                return ROUNDING_FLOOR_ARITHMETIC;
207        }
208
209        @Override
210        public final DecimalArithmetic getRoundingHalfEvenArithmetic() {
211                return ROUNDING_HALF_EVEN_ARITHMETIC;
212        }
213
214        @Override
215        public final DecimalArithmetic getRoundingUnnecessaryArithmetic() {
216                return ROUNDING_UNNECESSARY_ARITHMETIC;
217        }
218
219        @Override
220        public final DecimalArithmetic getArithmetic(RoundingMode roundingMode) {
221                return UNCHECKED_ARITHMETIC[roundingMode.ordinal()];
222        }
223
224        @Override
225        public final DecimalArithmetic getCheckedArithmetic(RoundingMode roundingMode) {
226                return CHECKED_ARITHMETIC[roundingMode.ordinal()];
227        }
228
229        @Override
230        public final DecimalArithmetic getArithmetic(TruncationPolicy truncationPolicy) {
231                final OverflowMode overflow = truncationPolicy.getOverflowMode();
232                final RoundingMode rounding = truncationPolicy.getRoundingMode();
233                return (overflow == UNCHECKED ? UNCHECKED_ARITHMETIC : CHECKED_ARITHMETIC)[rounding.ordinal()];
234        }
235
236        @Override
237        public final String toString() {
238                return "Scale2f";
239        }
240}