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 × 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 × 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}