1 | /* ============================================================= |
2 | * SmallSQL : a free Java DBMS library for the Java(tm) platform |
3 | * ============================================================= |
4 | * |
5 | * (C) Copyright 2004-2006, by Volker Berlin. |
6 | * |
7 | * Project Info: http://www.smallsql.de/ |
8 | * |
9 | * This library is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU Lesser General Public License as published by |
11 | * the Free Software Foundation; either version 2.1 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
17 | * License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with this library; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
22 | * USA. |
23 | * |
24 | * [Java is a trademark or registered trademark of Sun Microsystems, Inc. |
25 | * in the United States and other countries.] |
26 | * |
27 | * --------------- |
28 | * MutableNumeric.java |
29 | * --------------- |
30 | * Author: Volker Berlin |
31 | * |
32 | */ |
33 | package smallsql.database; |
34 | |
35 | import java.math.*; |
36 | |
37 | public class MutableNumeric extends Number implements Mutable{ |
38 | |
39 | int[] value; |
40 | int scale; |
41 | int signum; |
42 | |
43 | private int[] addIntVal; //array with the size of 1 |
44 | |
45 | // most significat value ist an position 0 |
46 | MutableNumeric(byte[] complement){ |
47 | setValue(complement); |
48 | } |
49 | |
50 | private void setValue(byte[] complement){ |
51 | int length = complement.length; |
52 | if(length == 0){ |
53 | value = EMPTY_INTS; |
54 | signum = 0; |
55 | return; |
56 | } |
57 | value = new int[ (length + 3) / 4 ]; |
58 | if(complement[0] < 0){ |
59 | negate( complement ); |
60 | signum = -1; |
61 | }else{ |
62 | signum = 0; |
63 | for(int i=0; i<complement.length; i++) |
64 | if(complement[i] != 0){ |
65 | signum = 1; |
66 | break; |
67 | } |
68 | } |
69 | for(int v=value.length-1; v>=0; v--){ |
70 | int temp = 0; |
71 | for(int i=0; i<4 && 0<length; i++){ |
72 | temp |= (complement[ --length ] & 0xFF) << (i*8); |
73 | } |
74 | value[v] = temp; |
75 | } |
76 | } |
77 | |
78 | MutableNumeric(int complement){ |
79 | if(complement == 0){ |
80 | signum = 0; |
81 | value = EMPTY_INTS; |
82 | }else{ |
83 | value = new int[1]; |
84 | if(complement < 0){ |
85 | value[0] = -complement; |
86 | signum = -1; |
87 | }else{ |
88 | value[0] = complement; |
89 | signum = 1; |
90 | } |
91 | } |
92 | } |
93 | |
94 | MutableNumeric(int complement, int scale){ |
95 | this( complement ); |
96 | this.scale = scale; |
97 | } |
98 | |
99 | MutableNumeric(long complement){ |
100 | if(complement == 0){ |
101 | signum = 0; |
102 | value = EMPTY_INTS; |
103 | }else{ |
104 | value = new int[2]; |
105 | if(complement < 0){ |
106 | value[0] = (int)(~(complement >> 32)); |
107 | value[1] = (int)(-complement); |
108 | signum = -1; |
109 | }else{ |
110 | value[0] = (int)(complement >> 32); |
111 | value[1] = (int)complement; |
112 | signum = 1; |
113 | } |
114 | } |
115 | } |
116 | |
117 | MutableNumeric(long complement, int scale){ |
118 | this( complement ); |
119 | this.scale = scale; |
120 | } |
121 | |
122 | MutableNumeric(double val){ |
123 | //first convert it to a string, because double to BigDecimal has very large rounding bug |
124 | this( new BigDecimal( String.valueOf(val) ) ); |
125 | } |
126 | |
127 | MutableNumeric(float val){ |
128 | //first convert it to a string, because float to BigDecimal has very large rounding bug |
129 | this( new BigDecimal( String.valueOf(val) ) ); |
130 | } |
131 | |
132 | MutableNumeric(String val){ |
133 | this( new BigDecimal( val ) ); |
134 | } |
135 | |
136 | MutableNumeric( BigDecimal big ){ |
137 | this(big.unscaledValue().toByteArray() ); |
138 | scale = big.scale(); |
139 | } |
140 | |
141 | MutableNumeric(int signum, int[] value, int scale){ |
142 | this.signum = signum; |
143 | this.value = value; |
144 | this.scale = scale; |
145 | } |
146 | |
147 | MutableNumeric(MutableNumeric numeric){ |
148 | this.signum = numeric.signum; |
149 | this.value = new int[numeric.value.length]; |
150 | System.arraycopy(numeric.value, 0, value, 0, value.length); |
151 | this.scale = numeric.scale; |
152 | } |
153 | |
154 | |
155 | // Addiert den Wert zum aktuellen MutableNumeric Object und verändert es. |
156 | void add(MutableNumeric num){ |
157 | if(num.scale < scale){ |
158 | num.setScale(scale); |
159 | }else |
160 | if(num.scale > scale){ |
161 | setScale(num.scale); |
162 | } |
163 | add( num.signum, num.value ); |
164 | } |
165 | |
166 | |
167 | private void add( int sig2, int[] val2){ |
168 | if(val2.length > value.length){ |
169 | int[] temp = val2; |
170 | val2 = value; |
171 | value = temp; |
172 | int tempi = signum; |
173 | signum = sig2; |
174 | sig2 = tempi; |
175 | } |
176 | if(signum != sig2) |
177 | sub(val2); |
178 | else |
179 | add(val2); |
180 | } |
181 | |
182 | // val2 ist kürzer oder gleich lang wie value |
183 | // die signum werte habe beide das gleiche Vorzeichen |
184 | private void add( int[] val2){ |
185 | long temp = 0; |
186 | int v1 = value.length; |
187 | for(int v2 = val2.length; v2>0; ){ |
188 | temp = (value[--v1] & 0xFFFFFFFFL) + (val2 [--v2] & 0xFFFFFFFFL) + (temp >>> 32); |
189 | value[v1] = (int)temp; |
190 | } |
191 | boolean uebertrag = (temp >>> 32) != 0; |
192 | while(v1 > 0 && uebertrag) |
193 | uebertrag = (value[--v1] = value[v1] + 1) == 0; |
194 | |
195 | // vergrößern, wenn notwendig |
196 | if(uebertrag){ |
197 | resizeValue(1); |
198 | } |
199 | } |
200 | |
201 | |
202 | |
203 | /** |
204 | * Resize the value mantisse with a carryover. |
205 | * @param highBits Is the high value that is save on the resize place. |
206 | */ |
207 | private void resizeValue(int highBits){ |
208 | int val[] = new int[value.length+1]; |
209 | val[0] = highBits; |
210 | System.arraycopy(value, 0, val, 1, value.length); |
211 | value = val; |
212 | } |
213 | |
214 | |
215 | // Subtrahiert den Wert zum aktuellen MutableNumeric Object und verändert es. |
216 | void sub(MutableNumeric num){ |
217 | if(num.scale < scale){ |
218 | num.setScale(scale); |
219 | }else |
220 | if(num.scale > scale){ |
221 | setScale(num.scale); |
222 | } |
223 | add( -num.signum, num.value ); |
224 | } |
225 | |
226 | // val2 ist kürzer oder gleich lang wie value |
227 | // val2 wird von der aktuellen Zahl abgezogen |
228 | private void sub(int[] val2){ |
229 | long temp = 0; |
230 | int v1 = value.length; |
231 | for(int v2 = val2.length; v2>0; ){ |
232 | temp = (value[--v1] & 0xFFFFFFFFL) - (val2 [--v2] & 0xFFFFFFFFL) + (temp >>>= 32); |
233 | value[v1] = (int)temp; |
234 | } |
235 | |
236 | boolean uebertrag = (temp >>> 32) != 0; |
237 | while(v1 > 0 && uebertrag) |
238 | uebertrag = (value[--v1] = value[v1] - 1) == -1; |
239 | |
240 | if(uebertrag){ |
241 | signum = -signum; |
242 | int last = value.length-1; |
243 | for(int i=0; i<=last; i++){ |
244 | value[i] = (i == last) ? -value[i] : ~value[i]; |
245 | } |
246 | } |
247 | } |
248 | |
249 | void mul(MutableNumeric num){ |
250 | //TODO performance |
251 | BigDecimal big = toBigDecimal().multiply(num.toBigDecimal() ); |
252 | setValue( big.unscaledValue().toByteArray() ); |
253 | scale = big.scale(); |
254 | signum = big.signum(); |
255 | } |
256 | |
257 | final void mul(int factor){ |
258 | if(factor < 0){ |
259 | factor = - factor; |
260 | signum = -signum; |
261 | } |
262 | long carryover = 0; |
263 | for(int i = value.length-1; i>=0; i--){ |
264 | long v = (value[i] & 0xFFFFFFFFL) * factor + carryover; |
265 | value[i] = (int)v; |
266 | carryover = v >> 32; |
267 | } |
268 | if(carryover > 0){ |
269 | resizeValue( (int)carryover ); |
270 | } |
271 | } |
272 | |
273 | |
274 | void div(MutableNumeric num){ |
275 | //TODO performance |
276 | int newScale = Math.max(scale+5, num.scale +4); |
277 | BigDecimal big = toBigDecimal().divide(num.toBigDecimal(), newScale, BigDecimal.ROUND_HALF_EVEN); |
278 | setValue( big.unscaledValue().toByteArray() ); |
279 | scale = big.scale(); |
280 | signum = big.signum(); |
281 | } |
282 | |
283 | |
284 | final void div(int quotient){ |
285 | //increment the scale with 5 |
286 | mul(100000); |
287 | scale += 5; |
288 | |
289 | divImpl(quotient); |
290 | } |
291 | |
292 | |
293 | final private void divImpl(int quotient){ |
294 | if(quotient == 1) return; |
295 | if(quotient < 0){ |
296 | quotient = - quotient; |
297 | signum = -signum; |
298 | } |
299 | int valueLength = value.length; |
300 | long carryover = 0; |
301 | for(int i = 0; i<valueLength; i++){ |
302 | long v = (value[i] & 0xFFFFFFFFL) + carryover; |
303 | value[i] = (int)(v / quotient); |
304 | carryover = ((v % quotient) << 32); |
305 | } |
306 | carryover /= quotient; |
307 | if(carryover > 2147483648L || //2147483648L == Integer.MAX_VALUE+1 |
308 | (carryover == 2147483648L && (value[valueLength-1] % 2 == 1))){ |
309 | int i = valueLength-1; |
310 | boolean isCarryOver = true; |
311 | while(i >= 0 && isCarryOver) |
312 | isCarryOver = (value[i--] += 1) == 0; |
313 | } |
314 | if(valueLength>1 && value[0] == 0){ |
315 | int[] temp = new int[valueLength-1]; |
316 | System.arraycopy(value, 1, temp, 0, valueLength-1); |
317 | value = temp; |
318 | } |
319 | |
320 | } |
321 | |
322 | |
323 | void mod(MutableNumeric num){ |
324 | //TODO performance |
325 | num = new MutableNumeric( doubleValue() % num.doubleValue() ); |
326 | value = num.value; |
327 | scale = num.scale; |
328 | signum = num.signum; |
329 | } |
330 | |
331 | |
332 | |
333 | void setScale(int newScale){ |
334 | if(newScale == scale) return; |
335 | int factor = 1; |
336 | if(newScale > scale){ |
337 | for(;newScale>scale; scale++){ |
338 | factor *=10; |
339 | if(factor == 1000000000){ |
340 | mul(factor); |
341 | factor = 1; |
342 | } |
343 | } |
344 | mul(factor); |
345 | }else{ |
346 | for(;newScale<scale; scale--){ |
347 | factor *=10; |
348 | if(factor == 1000000000){ |
349 | divImpl(factor); |
350 | factor = 1; |
351 | } |
352 | } |
353 | divImpl(factor); |
354 | } |
355 | } |
356 | |
357 | |
358 | void floor(){ |
359 | //TODO performance |
360 | int oldScale = scale; |
361 | setScale(0); |
362 | setScale(oldScale); |
363 | } |
364 | |
365 | |
366 | private void negate(byte[] complement){ |
367 | int last = complement.length-1; |
368 | for(int i=0; i<=last; i++){ |
369 | complement[i] = (byte)( (i == last) ? -complement[i] : ~complement[i]); |
370 | } |
371 | } |
372 | |
373 | // Konvertiert es in ein 2 Komplement, wie es von BigInteger verarbeitet werden kann |
374 | // die Länge ist immer ein vielfaches von 4 |
375 | byte[] toByteArray(){ |
376 | if(signum == 0) return EMPTY_BYTES; |
377 | byte[] complement; |
378 | int offset; |
379 | |
380 | int v = 0; |
381 | for( ;v < value.length && value[v] == 0; v++ ) ; |
382 | if (v == value.length) return EMPTY_BYTES; |
383 | |
384 | if(value[v] < 0){ |
385 | // wenn das most signifikante bit ist gesetzt, dann muß vergrößert werden, |
386 | // weil es für das Vorzeichen bnötigt wird. |
387 | complement = new byte[(value.length-v)*4 + 4]; |
388 | if(signum < 0) |
389 | complement[0] = complement[1] = complement[2] = complement[3] = -1; |
390 | offset = 4; |
391 | }else{ |
392 | complement = new byte[(value.length-v)*4]; |
393 | offset = 0; |
394 | } |
395 | int last = value.length-1; |
396 | for(; v <= last; v++){ |
397 | int val = (signum>0) ? value[v] : (v == last) ? -value[v] : ~value[v]; |
398 | complement[offset++] = (byte)(val >> 24); |
399 | complement[offset++] = (byte)(val >> 16); |
400 | complement[offset++] = (byte)(val >> 8); |
401 | complement[offset++] = (byte)(val); |
402 | } |
403 | return complement; |
404 | } |
405 | |
406 | public int intValue(){ |
407 | return Utils.long2int(longValue()); |
408 | } |
409 | |
410 | |
411 | public long longValue(){ |
412 | if(value.length == 0 || signum == 0){ |
413 | return 0; |
414 | }else{ |
415 | if (value.length == 1 && (value[0] > 0)){ |
416 | // einfacher Integer Wert |
417 | return Utils.double2long(value[0] / scaleDoubleFactor[scale] * signum); |
418 | }else |
419 | if (value.length == 1){ |
420 | // überlaufender Integer Wert |
421 | long temp = value[0] & 0xFFFFFFFFL; |
422 | return Utils.double2long(temp / scaleDoubleFactor[scale] * signum); |
423 | }else |
424 | if (value.length == 2 && (value[0] > 0)){ |
425 | // einfacher Long Wert |
426 | long temp = (((long)value[0]) << 32) | (value[1] & 0xFFFFFFFFL); |
427 | return Utils.double2long(temp / scaleDoubleFactor[scale] * signum); |
428 | }else{ |
429 | if(scale != 0){ |
430 | MutableNumeric numeric = new MutableNumeric(this); |
431 | numeric.setScale(0); |
432 | return numeric.longValue(); |
433 | } |
434 | return (signum > 0) ? Long.MAX_VALUE : Long.MIN_VALUE; |
435 | } |
436 | } |
437 | } |
438 | |
439 | |
440 | public float floatValue(){ |
441 | if(value.length == 0 || signum == 0){ |
442 | return 0; |
443 | }else{ |
444 | if (value.length == 1 && (value[0] > 0)){ |
445 | // einfacher Integer Wert |
446 | return value[0] / scaleFloatFactor[scale] * signum; |
447 | }else |
448 | if (value.length == 1){ |
449 | // überlaufender Integer Wert |
450 | long temp = value[0] & 0xFFFFFFFFL; |
451 | return temp / scaleFloatFactor[scale] * signum; |
452 | }else |
453 | if (value.length == 2 && (value[0] > 0)){ |
454 | // einfacher Long Wert |
455 | long temp = (((long)value[0]) << 32) | (value[1] & 0xFFFFFFFFL); |
456 | return temp / scaleFloatFactor[scale] * signum; |
457 | }else{ |
458 | return new BigDecimal( new BigInteger( toByteArray() ), scale ).floatValue(); |
459 | } |
460 | } |
461 | } |
462 | |
463 | public double doubleValue(){ |
464 | if(value.length == 0 || signum == 0){ |
465 | return 0; |
466 | }else{ |
467 | if (value.length == 1 && (value[0] > 0)){ |
468 | // einfacher Integer Wert |
469 | return value[0] / scaleDoubleFactor[scale] * signum; |
470 | }else |
471 | if (value.length == 1){ |
472 | // überlaufender Integer Wert |
473 | long temp = value[0] & 0xFFFFFFFFL; |
474 | return temp / scaleDoubleFactor[scale] * signum; |
475 | }else |
476 | if (value.length == 2 && (value[0] > 0)){ |
477 | // einfacher Long Wert |
478 | long temp = (((long)value[0]) << 32) | (value[1] & 0xFFFFFFFFL); |
479 | return temp / scaleDoubleFactor[scale] * signum; |
480 | }else{ |
481 | return new BigDecimal( new BigInteger( toByteArray() ), scale ).doubleValue(); |
482 | } |
483 | } |
484 | } |
485 | |
486 | public String toString(){ |
487 | StringBuffer buf = new StringBuffer(); |
488 | if(value.length == 0 || signum == 0){ |
489 | buf.append( '0' ); |
490 | }else{ |
491 | if (value.length == 1 && (value[0] > 0)){ |
492 | // einfacher Integer Wert |
493 | buf.append( Integer.toString(value[0]) ); |
494 | }else |
495 | if (value.length == 1){ |
496 | // überlaufender Integer Wert |
497 | long temp = value[0] & 0xFFFFFFFFL; |
498 | buf.append( Long.toString( temp ) ); |
499 | }else |
500 | if (value.length == 2 && (value[0] > 0)){ |
501 | // einfacher Long Wert |
502 | long temp = (((long)value[0]) << 32) | (value[1] & 0xFFFFFFFFL); |
503 | buf.append( Long.toString( temp ) ); |
504 | }else{ |
505 | return new BigDecimal( new BigInteger( toByteArray() ), scale ).toString(); |
506 | } |
507 | } |
508 | if(scale > 0){ |
509 | while(buf.length() <= scale) buf.insert( 0, '0' ); |
510 | buf.insert( buf.length() - scale, '.' ); |
511 | } |
512 | if (signum < 0) buf.insert( 0, '-'); |
513 | return buf.toString(); |
514 | } |
515 | |
516 | public int compareTo(MutableNumeric numeric){ |
517 | //TODO performance |
518 | return toBigDecimal().compareTo(numeric.toBigDecimal()); |
519 | } |
520 | |
521 | public boolean equals(Object obj){ |
522 | if(!(obj instanceof MutableNumeric)) return false; |
523 | return compareTo((MutableNumeric)obj) == 0; |
524 | } |
525 | |
526 | public BigDecimal toBigDecimal(){ |
527 | if(signum == 0) return new BigDecimal( BigInteger.ZERO, scale); |
528 | return new BigDecimal( new BigInteger( toByteArray() ), scale ); |
529 | } |
530 | |
531 | public BigDecimal toBigDecimal(int scale){ |
532 | if(scale == this.scale) return toBigDecimal(); |
533 | return toBigDecimal().setScale( scale, BigDecimal.ROUND_HALF_EVEN); |
534 | } |
535 | |
536 | public Object getImmutableObject(){ |
537 | return toBigDecimal(); |
538 | } |
539 | |
540 | private static final byte[] EMPTY_BYTES = new byte[0]; |
541 | private static final int [] EMPTY_INTS = new int [0]; |
542 | private static final double[] scaleDoubleFactor = { 1, 10, 100, 1000, 10000, 100000, 1000000 }; |
543 | private static final float[] scaleFloatFactor = { 1, 10, 100, 1000, 10000, 100000, 1000000 }; |
544 | } |