1package org.bouncycastle.math.ec;
2
3import java.math.BigInteger;
4
5/**
6 * Class representing a simple version of a big decimal. A
7 * <code>SimpleBigDecimal</code> is basically a
8 * {@link java.math.BigInteger BigInteger} with a few digits on the right of
9 * the decimal point. The number of (binary) digits on the right of the decimal
10 * point is called the <code>scale</code> of the <code>SimpleBigDecimal</code>.
11 * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
12 * automatically, but must be set manually. All <code>SimpleBigDecimal</code>s
13 * taking part in the same arithmetic operation must have equal scale. The
14 * result of a multiplication of two <code>SimpleBigDecimal</code>s returns a
15 * <code>SimpleBigDecimal</code> with double scale.
16 */
17class SimpleBigDecimal
18    //extends Number   // not in J2ME - add compatibility class?
19{
20    private static final long serialVersionUID = 1L;
21
22    private final BigInteger bigInt;
23    private final int scale;
24
25    /**
26     * Returns a <code>SimpleBigDecimal</code> representing the same numerical
27     * value as <code>value</code>.
28     * @param value The value of the <code>SimpleBigDecimal</code> to be
29     * created.
30     * @param scale The scale of the <code>SimpleBigDecimal</code> to be
31     * created.
32     * @return The such created <code>SimpleBigDecimal</code>.
33     */
34    public static SimpleBigDecimal getInstance(BigInteger value, int scale)
35    {
36        return new SimpleBigDecimal(value.shiftLeft(scale), scale);
37    }
38
39    /**
40     * Constructor for <code>SimpleBigDecimal</code>. The value of the
41     * constructed <code>SimpleBigDecimal</code> equals <code>bigInt /
42     * 2<sup>scale</sup></code>.
43     * @param bigInt The <code>bigInt</code> value parameter.
44     * @param scale The scale of the constructed <code>SimpleBigDecimal</code>.
45     */
46    public SimpleBigDecimal(BigInteger bigInt, int scale)
47    {
48        if (scale < 0)
49        {
50            throw new IllegalArgumentException("scale may not be negative");
51        }
52
53        this.bigInt = bigInt;
54        this.scale = scale;
55    }
56
57    private void checkScale(SimpleBigDecimal b)
58    {
59        if (scale != b.scale)
60        {
61            throw new IllegalArgumentException("Only SimpleBigDecimal of " +
62                "same scale allowed in arithmetic operations");
63        }
64    }
65
66    public SimpleBigDecimal adjustScale(int newScale)
67    {
68        if (newScale < 0)
69        {
70            throw new IllegalArgumentException("scale may not be negative");
71        }
72
73        if (newScale == scale)
74        {
75            return this;
76        }
77
78        return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale),
79                newScale);
80    }
81
82    public SimpleBigDecimal add(SimpleBigDecimal b)
83    {
84        checkScale(b);
85        return new SimpleBigDecimal(bigInt.add(b.bigInt), scale);
86    }
87
88    public SimpleBigDecimal add(BigInteger b)
89    {
90        return new SimpleBigDecimal(bigInt.add(b.shiftLeft(scale)), scale);
91    }
92
93    public SimpleBigDecimal negate()
94    {
95        return new SimpleBigDecimal(bigInt.negate(), scale);
96    }
97
98    public SimpleBigDecimal subtract(SimpleBigDecimal b)
99    {
100        return add(b.negate());
101    }
102
103    public SimpleBigDecimal subtract(BigInteger b)
104    {
105        return new SimpleBigDecimal(bigInt.subtract(b.shiftLeft(scale)),
106                scale);
107    }
108
109    public SimpleBigDecimal multiply(SimpleBigDecimal b)
110    {
111        checkScale(b);
112        return new SimpleBigDecimal(bigInt.multiply(b.bigInt), scale + scale);
113    }
114
115    public SimpleBigDecimal multiply(BigInteger b)
116    {
117        return new SimpleBigDecimal(bigInt.multiply(b), scale);
118    }
119
120    public SimpleBigDecimal divide(SimpleBigDecimal b)
121    {
122        checkScale(b);
123        BigInteger dividend = bigInt.shiftLeft(scale);
124        return new SimpleBigDecimal(dividend.divide(b.bigInt), scale);
125    }
126
127    public SimpleBigDecimal divide(BigInteger b)
128    {
129        return new SimpleBigDecimal(bigInt.divide(b), scale);
130    }
131
132    public SimpleBigDecimal shiftLeft(int n)
133    {
134        return new SimpleBigDecimal(bigInt.shiftLeft(n), scale);
135    }
136
137    public int compareTo(SimpleBigDecimal val)
138    {
139        checkScale(val);
140        return bigInt.compareTo(val.bigInt);
141    }
142
143    public int compareTo(BigInteger val)
144    {
145        return bigInt.compareTo(val.shiftLeft(scale));
146    }
147
148    public BigInteger floor()
149    {
150        return bigInt.shiftRight(scale);
151    }
152
153    public BigInteger round()
154    {
155        SimpleBigDecimal oneHalf = new SimpleBigDecimal(ECConstants.ONE, 1);
156        return add(oneHalf.adjustScale(scale)).floor();
157    }
158
159    public int intValue()
160    {
161        return floor().intValue();
162    }
163
164    public long longValue()
165    {
166        return floor().longValue();
167    }
168          /* NON-J2ME compliant.
169    public double doubleValue()
170    {
171        return Double.valueOf(toString()).doubleValue();
172    }
173
174    public float floatValue()
175    {
176        return Float.valueOf(toString()).floatValue();
177    }
178       */
179    public int getScale()
180    {
181        return scale;
182    }
183
184    public String toString()
185    {
186        if (scale == 0)
187        {
188            return bigInt.toString();
189        }
190
191        BigInteger floorBigInt = floor();
192
193        BigInteger fract = bigInt.subtract(floorBigInt.shiftLeft(scale));
194        if (bigInt.signum() == -1)
195        {
196            fract = ECConstants.ONE.shiftLeft(scale).subtract(fract);
197        }
198
199        if ((floorBigInt.signum() == -1) && (!(fract.equals(ECConstants.ZERO))))
200        {
201            floorBigInt = floorBigInt.add(ECConstants.ONE);
202        }
203        String leftOfPoint = floorBigInt.toString();
204
205        char[] fractCharArr = new char[scale];
206        String fractStr = fract.toString(2);
207        int fractLen = fractStr.length();
208        int zeroes = scale - fractLen;
209        for (int i = 0; i < zeroes; i++)
210        {
211            fractCharArr[i] = '0';
212        }
213        for (int j = 0; j < fractLen; j++)
214        {
215            fractCharArr[zeroes + j] = fractStr.charAt(j);
216        }
217        String rightOfPoint = new String(fractCharArr);
218
219        StringBuffer sb = new StringBuffer(leftOfPoint);
220        sb.append(".");
221        sb.append(rightOfPoint);
222
223        return sb.toString();
224    }
225
226    public boolean equals(Object o)
227    {
228        if (this == o)
229        {
230            return true;
231        }
232
233        if (!(o instanceof SimpleBigDecimal))
234        {
235            return false;
236        }
237
238        SimpleBigDecimal other = (SimpleBigDecimal)o;
239        return ((bigInt.equals(other.bigInt)) && (scale == other.scale));
240    }
241
242    public int hashCode()
243    {
244        return bigInt.hashCode() ^ scale;
245    }
246
247}
248