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