001/**
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2015 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.RoundingMode;
027
028import org.decimal4j.api.Decimal;
029import org.decimal4j.api.ImmutableDecimal;
030import org.decimal4j.arithmetic.Exceptions;
031import org.decimal4j.scale.ScaleMetrics;
032import org.decimal4j.scale.Scales;
033
034/**
035 * Base class for immutable {@link Decimal} classes of different scales.
036 * 
037 * @param <S>
038 *            the scale metrics type associated with this decimal
039 * @param <D>
040 *            the concrete class implementing this {@code ImmutableDecimal}
041 */
042@SuppressWarnings("serial")
043abstract public class AbstractImmutableDecimal<S extends ScaleMetrics, D extends AbstractImmutableDecimal<S, D>>
044                extends AbstractDecimal<S, D>implements ImmutableDecimal<S> {
045
046        private final long unscaled;
047
048        /**
049         * Constructor with unscaled value.
050         * 
051         * @param unscaled
052         *            the unscaled value
053         */
054        public AbstractImmutableDecimal(long unscaled) {
055                this.unscaled = unscaled;
056        }
057
058        @Override
059        public final long unscaledValue() {
060                return unscaled;
061        }
062
063        @Override
064        public ImmutableDecimal<?> scale(int scale) {
065                return scale(scale, RoundingMode.HALF_UP);
066        }
067
068        @Override
069        @SuppressWarnings("hiding")
070        public <S extends ScaleMetrics> ImmutableDecimal<S> scale(S scaleMetrics) {
071                return scale(scaleMetrics, RoundingMode.HALF_UP);
072        }
073
074        @Override
075        public ImmutableDecimal<?> scale(int scale, RoundingMode roundingMode) {
076                final int myScale = getScale();
077                if (scale == myScale) {
078                        return this;
079                }
080                final ScaleMetrics targetMetrics = Scales.getScaleMetrics(scale);
081                try {
082                        final long targetUnscaled = targetMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, myScale);
083                        return getFactory().deriveFactory(targetMetrics).valueOfUnscaled(targetUnscaled);
084                } catch (IllegalArgumentException e) {
085                        throw Exceptions.newArithmeticExceptionWithCause("Overflow: cannot convert " + this + " to scale " + scale,
086                                        e);
087                }
088        }
089
090        @Override
091        @SuppressWarnings("hiding")
092        public <S extends ScaleMetrics> ImmutableDecimal<S> scale(S scaleMetrics, RoundingMode roundingMode) {
093                if (scaleMetrics == getScaleMetrics()) {
094                        @SuppressWarnings("unchecked")
095                        // safe: we know it is the same scale metrics
096                        final ImmutableDecimal<S> self = (ImmutableDecimal<S>) this;
097                        return self;
098                }
099                try {
100                        final long targetUnscaled = scaleMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, getScale());
101                        return getFactory().deriveFactory(scaleMetrics).valueOfUnscaled(targetUnscaled);
102                } catch (IllegalArgumentException e) {
103                        throw Exceptions.newArithmeticExceptionWithCause(
104                                        "Overflow: cannot convert " + this + " to scale " + scaleMetrics.getScale(), e);
105                }
106        }
107
108        @Override
109        public ImmutableDecimal<?> multiplyExact(Decimal<?> multiplicand) {
110                final int targetScale = getScale() + multiplicand.getScale();
111                if (targetScale > Scales.MAX_SCALE) {
112                        throw new IllegalArgumentException("sum of scales in exact multiplication exceeds max scale "
113                                        + Scales.MAX_SCALE + ": " + this + " * " + multiplicand);
114                }
115                try {
116                        final long unscaledProduct = getDefaultCheckedArithmetic().multiplyByLong(unscaled,
117                                        multiplicand.unscaledValue());
118                        return getFactory().deriveFactory(targetScale).valueOfUnscaled(unscaledProduct);
119                } catch (ArithmeticException e) {
120                        throw new ArithmeticException("Overflow: " + this + " * " + multiplicand);
121                }
122        }
123
124        @Override
125        public ImmutableDecimal<S> min(ImmutableDecimal<S> val) {
126                return isLessThanOrEqualTo(val) ? this : val;
127        }
128
129        @Override
130        public ImmutableDecimal<S> max(ImmutableDecimal<S> val) {
131                return isGreaterThanOrEqualTo(val) ? this : val;
132        }
133}