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.base;
025
026import java.math.BigDecimal;
027import java.math.BigInteger;
028import java.math.RoundingMode;
029
030import org.decimal4j.api.Decimal;
031import org.decimal4j.api.MutableDecimal;
032import org.decimal4j.arithmetic.Exceptions;
033import org.decimal4j.scale.ScaleMetrics;
034import org.decimal4j.scale.Scales;
035
036/**
037 * Base class for mutable {@link Decimal} classes of different scales.
038 * 
039 * @param <S>
040 *            the scale metrics type associated with this Decimal
041 * @param <D>
042 *            the concrete class implementing this {@code MutableDecimal}
043 */
044@SuppressWarnings("serial")
045abstract public class AbstractMutableDecimal<S extends ScaleMetrics, D extends AbstractMutableDecimal<S, D>>
046                extends AbstractDecimal<S, D> implements MutableDecimal<S> {
047
048        private long unscaled;
049
050        /**
051         * Constructor with unscaled value.
052         * 
053         * @param unscaled
054         *            the unscaled value
055         */
056        public AbstractMutableDecimal(long unscaled) {
057                this.unscaled = unscaled;
058        }
059
060        @Override
061        public final long unscaledValue() {
062                return unscaled;
063        }
064
065        /**
066         * Returns {@code this} decimal after assigning the value
067         * <code>(unscaled &times; 10<sup>-scale</sup>)</code>.
068         * 
069         * @param unscaled
070         *            unscaled value to assign to this {@code Decimal}
071         * @return {@code this} decimal value now representing
072         *         <code>(unscaled &times; 10<sup>-scale</sup>)</code>
073         */
074        @Override
075        protected D createOrAssign(long unscaled) {
076                this.unscaled = unscaled;
077                return self();
078        }
079
080        @Override
081        public MutableDecimal<?> scale(int scale) {
082                return scale(scale, RoundingMode.HALF_UP);
083        }
084
085        @Override
086        @SuppressWarnings("hiding")
087        public <S extends ScaleMetrics> MutableDecimal<S> scale(S scaleMetrics) {
088                return scale(scaleMetrics, RoundingMode.HALF_UP);
089        }
090
091        @Override
092        public MutableDecimal<?> scale(int scale, RoundingMode roundingMode) {
093                final int myScale = getScale();
094                if (scale == myScale) {
095                        return this;
096                }
097                final ScaleMetrics targetMetrics = Scales.getScaleMetrics(scale);
098                try {
099                        final long targetUnscaled = targetMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, myScale);
100                        return getFactory().deriveFactory(scale).newMutable().setUnscaled(targetUnscaled);
101                } catch (IllegalArgumentException e) {
102                        throw Exceptions.newArithmeticExceptionWithCause("Overflow: cannot convert " + this + " to scale " + scale, e);
103                }
104        }
105
106        @Override
107        @SuppressWarnings("hiding")
108        public <S extends ScaleMetrics> MutableDecimal<S> scale(S scaleMetrics, RoundingMode roundingMode) {
109                if (scaleMetrics == getScaleMetrics()) {
110                        @SuppressWarnings("unchecked")
111                        //safe: we know it is the same scale metrics
112                        final MutableDecimal<S> self = (MutableDecimal<S>) this;
113                        return self;
114                }
115                try {
116                        final long targetUnscaled = scaleMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, getScale());
117                        return getFactory().deriveFactory(scaleMetrics).newMutable().setUnscaled(targetUnscaled);
118                } catch (IllegalArgumentException e) {
119                        throw Exceptions.newArithmeticExceptionWithCause("Overflow: cannot convert " + this + " to scale " + scaleMetrics.getScale(), e);
120                }
121        }
122
123        @Override
124        public MutableDecimal<?> multiplyExact(Decimal<?> multiplicand) {
125                final int targetScale = getScale() + multiplicand.getScale();
126                if (targetScale > Scales.MAX_SCALE) {
127                        throw new IllegalArgumentException("sum of scales exceeds max scale: " + targetScale + " > " + Scales.MAX_SCALE);
128                }
129                try {
130                        final long unscaledProduct = getCheckedArithmeticFor(RoundingMode.DOWN).multiplyByLong(unscaled, multiplicand.unscaledValue());
131                        return getFactory().deriveFactory(targetScale).newMutable().setUnscaled(unscaledProduct);
132                } catch (ArithmeticException e) {
133                        throw new ArithmeticException("Overflow: " + this + " * " + multiplicand);
134                }
135        }
136
137        @Override
138        public D setZero() {
139                unscaled = 0;
140                return self();
141        }
142
143        @Override
144        public D setOne() {
145                unscaled = getScaleMetrics().getScaleFactor();
146                return self();
147        }
148
149        @Override
150        public D setMinusOne() {
151                unscaled = -getScaleMetrics().getScaleFactor();
152                return self();
153        }
154
155        @Override
156        public D setUlp() {
157                unscaled = 1;
158                return self();
159        }
160
161        @Override
162        public D set(Decimal<S> value) {
163                return setUnscaled(value.unscaledValue());
164        }
165
166        @Override
167        public D set(Decimal<?> value, RoundingMode roundingMode) {
168                return setUnscaled(value.unscaledValue(), value.getScale(), roundingMode);
169        }
170
171        @Override
172        public D set(long value) {
173                unscaled = getDefaultCheckedArithmetic().fromLong(value);
174                return self();
175        }
176
177        @Override
178        public D set(BigInteger value) {
179                unscaled = getDefaultCheckedArithmetic().fromBigInteger(value);
180                return self();
181        }
182
183        @Override
184        public D set(float value) {
185                unscaled = getDefaultCheckedArithmetic().fromFloat(value);
186                return self();
187        }
188
189        @Override
190        public D set(float value, RoundingMode roundingMode) {
191                unscaled = getCheckedArithmeticFor(roundingMode).fromFloat(value);
192                return self();
193        }
194
195        @Override
196        public D set(double value) {
197                unscaled = getDefaultCheckedArithmetic().fromDouble(value);
198                return self();
199        }
200
201        @Override
202        public D set(double value, RoundingMode roundingMode) {
203                unscaled = getCheckedArithmeticFor(roundingMode).fromDouble(value);
204                return self();
205        }
206
207        @Override
208        public D set(BigDecimal value) {
209                unscaled = getDefaultCheckedArithmetic().fromBigDecimal(value);
210                return self();
211        }
212
213        @Override
214        public D set(BigDecimal value, RoundingMode roundingMode) {
215                unscaled = getCheckedArithmeticFor(roundingMode).fromBigDecimal(value);
216                return self();
217        }
218
219        @Override
220        public D setUnscaled(long unscaledValue) {
221                unscaled = unscaledValue;
222                return self();
223        }
224
225        @Override
226        public D setUnscaled(long unscaledValue, int scale) {
227                unscaled = getDefaultCheckedArithmetic().fromUnscaled(unscaledValue, scale);
228                return self();
229        }
230
231        @Override
232        public D setUnscaled(long unscaledValue, int scale, RoundingMode roundingMode) {
233                unscaled = getCheckedArithmeticFor(roundingMode).fromUnscaled(unscaledValue, scale);
234                return self();
235        }
236
237        @Override
238        public D set(String value) {
239                unscaled = getDefaultCheckedArithmetic().parse(value);
240                return self();
241        }
242
243        @Override
244        public D set(String value, RoundingMode roundingMode) {
245                unscaled = getCheckedArithmeticFor(roundingMode).parse(value);
246                return self();
247        }
248        
249        @Override
250        public MutableDecimal<S> min(MutableDecimal<S> val) {
251                return isLessThanOrEqualTo(val) ? this : val;
252        }
253
254        @Override
255        public MutableDecimal<S> max(MutableDecimal<S> val) {
256                return isGreaterThanOrEqualTo(val) ? this : val;
257        }
258
259        @Override
260        abstract public D clone();
261}