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}