1 package net.sf.yacas;
2 
3 import java.io.Writer;
4 import java.math.*;
5 
6 final class BigNumber
7 {
8   //constructors
BigNumber( String aString,int aBasePrecision,int aBase )9   public BigNumber( String aString,int aBasePrecision,int aBase/*=10*/)
10   {
11     SetTo(aString, aBasePrecision, aBase);
12   }
13   /// copy constructor
BigNumber( BigNumber aOther)14   public BigNumber( BigNumber aOther)
15   {
16     SetTo(aOther);
17   }
18   // no constructors from int or double to avoid automatic conversions
BigNumber(int aPrecision )19   public BigNumber(int aPrecision/* = 20*/)
20   {
21     iPrecision = aPrecision;
22     iTensExp = 0;
23     integer = new BigInteger("0");
24   }
25   // assign from another number
SetTo( BigNumber aOther)26   public void SetTo( BigNumber aOther)
27   {
28     iPrecision = aOther.GetPrecision();
29     iTensExp = aOther.iTensExp;
30     integer  = aOther.integer;
31     decimal  = aOther.decimal;
32   }
IsFloat(String aString,int aBase)33   boolean IsFloat(String aString,int aBase)
34   {
35     if (aString.indexOf('.')>=0)
36       return true;
37     if (aBase>10)
38       return false;
39     if (aString.indexOf('e')>=0)
40       return true;
41     return aString.indexOf('E')>=0;
42   }
43   // assign from string, precision in base digits
SetTo( String aString,int aPrecision,int aBase )44   public void SetTo( String aString,int aPrecision,int aBase/*=10*/)
45   {
46     integer = null;
47     decimal = null;
48     boolean isFloat = IsFloat(aString,aBase);
49 
50     iPrecision = aPrecision;
51     iTensExp = 0;
52     if (isFloat)
53     {
54       int decimalPos;
55       decimalPos = aString.indexOf('e');
56       if (decimalPos < 0)
57         decimalPos = aString.indexOf('E');
58       if (decimalPos > 0) // will never be zero
59       {
60         iTensExp = Integer.parseInt(aString.substring(decimalPos+1,aString.length()));
61         aString = aString.substring(0,decimalPos);
62       }
63 
64       decimal = new BigDecimal(aString); //TODO FIXME does not listen to aBase!!!
65       if (decimal.scale() > iPrecision)
66         iPrecision = decimal.scale();
67     }
68     else
69     {
70       integer = new BigInteger(aString, aBase);
71     }
72   }
73   // assign from a platform type
SetTo(long value)74   public void SetTo(long value)
75   {
76     SetTo(""+value,iPrecision,10);
77   }
SetTo(int value)78   public void SetTo(int value)
79   {
80     SetTo((long)value);
81   }
SetTo(double value)82   public void SetTo(double value)
83   {
84     SetTo(""+value,iPrecision,10);
85   }
86   // Convert back to other types
87   /// ToString : return string representation of number in aResult to given precision (base digits)
ToString(int aPrecision, int aBase )88   public String ToString(int aPrecision, int aBase/*=10*/)
89   {
90     if (integer != null)
91       return integer.toString(aBase);
92     else
93     {
94       String result = decimal.toString();
95 
96       int extraExp = 0;
97       // Parse out the exponent
98       {
99         int pos = result.indexOf('E');
100         if (pos<0) pos = result.indexOf('e');
101         if (pos > 0)
102         {
103           extraExp = Integer.parseInt(result.substring(pos+1));
104           result = result.substring(0,pos);
105         }
106       }
107 
108 
109       int dotPos = result.indexOf('.');
110       if (dotPos >= 0)
111       {
112         int endpos = result.length();
113         while (endpos>dotPos && result.charAt(endpos-1) == '0') endpos--;
114         if (endpos > 1)
115         {
116           if (result.charAt(endpos-1) == '.' && result.charAt(endpos-2) >= '0' && result.charAt(endpos-2) <= '9')
117           {
118             endpos--;
119           }
120         }
121         result = result.substring(0,endpos);
122       }
123 
124       if (iTensExp + extraExp != 0)
125         result = result + "e" + (iTensExp+extraExp);
126 
127       return result;
128     }
129   }
130   /// Give approximate representation as a double number
Double()131   public double Double()
132   {
133     if (integer != null)
134       return integer.doubleValue();
135     else
136       return decimal.doubleValue();
137   }
Long()138   public long Long()
139   {
140     if (integer != null)
141       return integer.longValue();
142     else
143       return decimal.longValue();
144   }
145 
146   //basic object manipulation
Equals( BigNumber aOther)147   public boolean Equals( BigNumber aOther)
148   {
149     if (integer != null) {
150         if (aOther.integer != null)
151             return (integer.compareTo(aOther.integer) == 0);
152 
153         BigDecimal x = GetDecimal(this);
154         BigDecimal y = aOther.decimal;
155 
156         if (iTensExp > aOther.iTensExp)
157             x = x.movePointRight(iTensExp - aOther.iTensExp);
158         else if (iTensExp < aOther.iTensExp)
159             y = y.movePointRight(iTensExp - aOther.iTensExp);
160 
161         return x.compareTo(y) == 0;
162     }
163 
164     if (decimal != null) {
165         BigDecimal thisd = decimal;
166         BigDecimal otherd = aOther.decimal;
167         if (otherd == null)
168             otherd = GetDecimal(aOther);
169 
170         if (iTensExp > aOther.iTensExp)
171             thisd = thisd.movePointRight(iTensExp - aOther.iTensExp);
172         else if (iTensExp < aOther.iTensExp)
173             otherd = otherd.movePointRight(iTensExp - aOther.iTensExp);
174 
175         return thisd.compareTo(otherd) == 0;
176     }
177     return true;
178   }
IsInt()179   public boolean IsInt()
180   {
181     return (integer != null && decimal == null);
182   }
IsSmall()183   public boolean IsSmall()
184   {
185     if (IsInt())
186     {
187       BigInteger i = integer.abs();
188       return (i.compareTo(new BigInteger("65535"))<0);
189     }
190     else
191     // a function to test smallness of a float is not present in ANumber, need to code a workaround to determine whether a number fits into double.
192     {
193       //TODO fixme
194       return true;
195 /*
196       LispInt tensExp = iNumber->iTensExp;
197       if (tensExp<0)tensExp = -tensExp;
198       return
199       (
200         iNumber->iPrecision <= 53  // standard float is 53 bits
201         && tensExp<1021 // 306  // 1021 bits is about 306 decimals
202       );
203       // standard range of double precision is about 53 bits of mantissa and binary exponent of about 1021
204 */
205     }
206   }
BecomeInt()207   public void BecomeInt()
208   {
209     if (decimal != null)
210     {
211       integer = decimal.toBigInteger();
212       decimal = null;
213     }
214   }
BecomeFloat(int aPrecision )215   public void BecomeFloat(int aPrecision/*=0*/)
216   {
217     if (integer != null)
218     {
219       decimal = new BigDecimal(integer);
220       iTensExp = 0;
221       integer = null;
222     }
223   }
LessThan( BigNumber aOther)224   public boolean LessThan( BigNumber aOther)
225   {
226     boolean floatResult = (decimal != null || aOther.decimal != null);
227     if (floatResult)
228     {
229       BigDecimal dX = GetDecimal(this);
230       BigDecimal dY = GetDecimal(aOther);
231       return dX.compareTo(dY)<0;
232     }
233     else
234     {
235       return integer.compareTo(aOther.integer)<0;
236     }
237   }
238   //arithmetic
239   /// Multiply two numbers at given precision and put result in *this
Multiply( BigNumber aX, BigNumber aY, int aPrecision)240   public void Multiply( BigNumber aX,  BigNumber aY, int aPrecision)
241   {
242     boolean floatResult = (aX.decimal != null || aY.decimal != null);
243     if (floatResult)
244     {
245       BigDecimal dX = GetDecimal(aX);
246       BigDecimal dY = GetDecimal(aY);
247       integer = null;
248       decimal = dX.multiply(dY);
249       int newScale = iPrecision;
250       if (newScale < decimal.scale())
251         decimal = decimal.setScale(newScale,BigDecimal.ROUND_HALF_EVEN);
252       iTensExp = aX.iTensExp + aY.iTensExp;
253     }
254     else
255     {
256       decimal = null;
257       integer = aX.integer.multiply(aY.integer);
258     }
259   }
260   /// Add two numbers at given precision and return result in *this
Add( BigNumber aX, BigNumber aY, int aPrecision)261   public void Add( BigNumber aX,  BigNumber aY, int aPrecision)
262   {
263     boolean floatResult = (aX.decimal != null || aY.decimal != null);
264     if (floatResult)
265     {
266       integer = null;
267 
268       BigDecimal dX = GetDecimal(aX);
269       BigDecimal dY = GetDecimal(aY);
270 
271       iTensExp = aX.iTensExp;
272       if (aX.iTensExp > aY.iTensExp)
273       {
274         dY = dY.movePointLeft(aX.iTensExp-aY.iTensExp);
275         iTensExp = aX.iTensExp;
276       }
277       else if (aX.iTensExp < aY.iTensExp)
278       {
279         dX = dX.movePointLeft(aY.iTensExp-aX.iTensExp);
280         iTensExp = aY.iTensExp;
281       }
282 
283       decimal = dX.add(dY);
284     }
285     else
286     {
287       decimal = null;
288       integer = aX.integer.add(aY.integer);
289     }
290   }
291   /// Negate the given number, return result in *this
Negate( BigNumber aX)292   public void Negate( BigNumber aX)
293   {
294     if (aX.integer != null)
295     {
296       decimal = null;
297       integer = aX.integer.negate();
298     }
299     if (aX.decimal != null)
300     {
301       integer = null;
302       decimal = aX.decimal.negate();
303       iTensExp = aX.iTensExp;
304     }
305   }
306   /// Divide two numbers and return result in *this. Note: if the two arguments are integer, it should return an integer result!
Divide( BigNumber aX, BigNumber aY, int aPrecision)307   public void Divide( BigNumber aX,  BigNumber aY, int aPrecision)
308   {
309     boolean floatResult = (aX.decimal != null || aY.decimal != null);
310     if (floatResult)
311     {
312       BigDecimal dX = GetDecimal(aX);
313       BigDecimal dY = GetDecimal(aY);
314       integer = null;
315       int newScale = aPrecision+aY.GetPrecision();
316       if (newScale > dX.scale())
317         dX = dX.setScale(newScale);
318       decimal = dX.divide(dY,BigDecimal.ROUND_HALF_EVEN);
319       iPrecision = decimal.scale();
320       iTensExp = aX.iTensExp-aY.iTensExp;
321     }
322     else
323     {
324       decimal = null;
325       integer = aX.integer.divide(aY.integer);
326     }
327   }
328 
329   /// integer operation: *this = y mod z
Mod( BigNumber aY, BigNumber aZ)330   public void Mod( BigNumber aY,  BigNumber aZ) throws Exception
331   {
332     LispError.Check(aY.integer != null, LispError.KLispErrNotInteger);
333     LispError.Check(aZ.integer != null, LispError.KLispErrNotInteger);
334 //TODO fixme    LispError.Check(!IsZero(aZ),LispError.KLispErrInvalidArg);
335     integer = aY.integer.mod(aZ.integer);
336     decimal = null;
337   }
338 
339   /// For debugging purposes, dump internal state of this object into a string
DumpDebugInfo(Writer aOutput)340   public void DumpDebugInfo(Writer aOutput) throws Exception
341   {
342     if (integer != null)
343     {
344       aOutput.write(("integer: "+integer.toString()+"\n"));
345     }
346     else
347     {
348       aOutput.write(("decimal: "+decimal.unscaledValue()+" scale "+decimal.scale()+" x 10^("+iTensExp+")\n"));
349     }
350   }
351 
352   /// assign self to Floor(aX) if possible
Floor( BigNumber aX)353   public void Floor( BigNumber aX)
354   {
355     if (aX.decimal != null)
356     {
357       BigDecimal d = aX.decimal;
358       if (aX.iTensExp != 0)
359       {
360         d = d.movePointRight(aX.iTensExp);
361       }
362       BigInteger rounded = d.toBigInteger();
363       if (aX.decimal.signum()<0)
364       {
365         BigDecimal back =  new BigDecimal(rounded);
366         BigDecimal difference = aX.decimal.subtract(back);
367         if (difference.signum() != 0)
368         {
369           rounded = rounded.add(new BigInteger("-1"));
370         }
371       }
372       integer = rounded;
373     }
374     else
375     {
376       integer = aX.integer;
377     }
378     decimal = null;
379   }
380   /// set precision (in bits)
Precision(int aPrecision)381   public void Precision(int aPrecision)
382   {
383     iPrecision = aPrecision;
384     if (decimal != null)
385     {
386       if (decimal.scale() > aPrecision)
387       {
388         decimal = decimal.setScale(aPrecision, BigDecimal.ROUND_HALF_EVEN);
389       }
390     }
391   }
392 
393   /// Bitwise operations, return result in *this.
ShiftLeft( BigNumber aX, int aNrToShift)394   void ShiftLeft(  BigNumber aX, int aNrToShift) throws Exception
395   {
396     LispError.LISPASSERT(aX.integer != null);
397     decimal = null;
398     integer = aX.integer.shiftLeft(aNrToShift);
399   }
ShiftRight( BigNumber aX, int aNrToShift)400   void ShiftRight(  BigNumber aX, int aNrToShift) throws Exception
401   {
402     LispError.LISPASSERT(aX.integer != null);
403     decimal = null;
404     integer = aX.integer.shiftRight(aNrToShift);
405   }
406 
Gcd( BigNumber aX, BigNumber aY)407   void Gcd( BigNumber aX,  BigNumber aY) throws Exception
408   {
409     LispError.LISPASSERT(aX.integer != null);
410     LispError.LISPASSERT(aY.integer != null);
411     integer = aX.integer.gcd(aY.integer);
412     decimal = null;
413   }
BitAnd( BigNumber aX, BigNumber aY)414   void BitAnd( BigNumber aX,  BigNumber aY) throws Exception
415   {
416     LispError.LISPASSERT(aX.integer != null);
417     LispError.LISPASSERT(aY.integer != null);
418     integer = aX.integer.and(aY.integer);
419     decimal = null;
420   }
BitOr( BigNumber aX, BigNumber aY)421   void BitOr( BigNumber aX,  BigNumber aY) throws Exception
422   {
423     LispError.LISPASSERT(aX.integer != null);
424     LispError.LISPASSERT(aY.integer != null);
425     integer = aX.integer.or(aY.integer);
426     decimal = null;
427   }
BitXor( BigNumber aX, BigNumber aY)428   void BitXor( BigNumber aX,  BigNumber aY) throws Exception
429   {
430     LispError.LISPASSERT(aX.integer != null);
431     LispError.LISPASSERT(aY.integer != null);
432     integer = aX.integer.xor(aY.integer);
433     decimal = null;
434   }
BitNot( BigNumber aX)435   void BitNot( BigNumber aX) throws Exception
436   {
437     LispError.LISPASSERT(aX.integer != null);
438     integer = aX.integer.not();
439     decimal = null;
440   }
441   /// Bit count operation: return the number of significant bits if integer, return the binary exponent if float (shortcut for binary logarithm)
442   /// give bit count as a platform integer
443   private static BigDecimal zero = new BigDecimal("0");
444   private static BigDecimal one = new BigDecimal("1");
445   private static BigDecimal two = new BigDecimal("2");
446   private static BigDecimal ten = new BigDecimal("10");
BitCount()447   public long BitCount()
448   {
449     //TODO fixme check that it works as needed
450     if (integer != null)
451       return integer.abs().bitLength();
452     {
453       BigDecimal d = decimal.abs();
454       if (iTensExp != 0)
455         d = d.movePointRight(iTensExp);
456       if (d.compareTo(one)>0)
457         return d.toBigInteger().bitLength();
458       BigDecimal integerPart = new BigDecimal(d.toBigInteger());
459       integerPart = integerPart.negate();
460       d = d.add(integerPart);
461       if (d.compareTo(zero) == 0)
462         return 0;
463       int bitCount = 0;
464 
465       //TODO OPTIMIZE
466       d = d.multiply(two);
467       while (d.compareTo(one)<0)
468       {
469         d = d.multiply(two);
470         bitCount--;
471       }
472       return bitCount;
473     }
474   }
475 
476   /// Give sign (-1, 0, 1)
Sign()477   public int Sign()
478   {
479     if (integer != null)
480       return integer.signum();
481     if (decimal != null)
482       return decimal.signum();
483 
484     return 0;
485   }
486 
GetPrecision()487   public int GetPrecision()
488   {
489     return iPrecision;
490   }
491 
492   int iPrecision;
493   int iTensExp;
494 
GetDecimal(BigNumber aNumber)495   BigDecimal GetDecimal(BigNumber aNumber)
496   {
497     if (aNumber.decimal != null)
498       return aNumber.decimal;
499     return new BigDecimal(aNumber.integer);
500   }
501 
502    /// Internal library wrapper starts here.
503   BigInteger integer = null;
504   BigDecimal decimal= null;
505   /// Internal library wrapper ends here.
506 }
507