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