1 34 package org.jruby; 35 36 import java.io.IOException ; 37 import java.math.BigDecimal ; 38 import java.math.BigInteger ; 39 import org.jruby.runtime.Arity; 40 import org.jruby.runtime.Block; 41 import org.jruby.runtime.CallType; 42 import org.jruby.runtime.CallbackFactory; 43 import org.jruby.runtime.ClassIndex; 44 import org.jruby.runtime.ObjectAllocator; 45 import org.jruby.runtime.ThreadContext; 46 import org.jruby.runtime.builtin.IRubyObject; 47 import org.jruby.runtime.marshal.MarshalStream; 48 import org.jruby.runtime.marshal.UnmarshalStream; 49 50 54 public class RubyBignum extends RubyInteger { 55 public static RubyClass createBignumClass(Ruby runtime) { 56 RubyClass bignum = runtime.defineClass("Bignum", runtime.getClass("Integer"), 57 ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR); 58 bignum.index = ClassIndex.BIGNUM; 59 CallbackFactory callbackFactory = runtime.callbackFactory(RubyBignum.class); 60 61 bignum.defineFastMethod("to_s", callbackFactory.getFastOptMethod("to_s")); 62 bignum.defineFastMethod("coerce", callbackFactory.getFastMethod("coerce", RubyKernel.IRUBY_OBJECT)); 63 bignum.defineFastMethod("-@", callbackFactory.getFastMethod("uminus")); 64 bignum.defineFastMethod("+", callbackFactory.getFastMethod("plus", RubyKernel.IRUBY_OBJECT)); 65 bignum.defineFastMethod("-", callbackFactory.getFastMethod("minus", RubyKernel.IRUBY_OBJECT)); 66 bignum.defineFastMethod("*", callbackFactory.getFastMethod("mul", RubyKernel.IRUBY_OBJECT)); 67 bignum.defineFastMethod("/", callbackFactory.getFastMethod("div", RubyKernel.IRUBY_OBJECT)); 68 bignum.defineFastMethod("%", callbackFactory.getFastMethod("mod", RubyKernel.IRUBY_OBJECT)); 69 bignum.defineFastMethod("div", callbackFactory.getFastMethod("div", RubyKernel.IRUBY_OBJECT)); 70 bignum.defineFastMethod("divmod", callbackFactory.getFastMethod("divmod", RubyKernel.IRUBY_OBJECT)); 71 bignum.defineFastMethod("modulo", callbackFactory.getFastMethod("mod", RubyKernel.IRUBY_OBJECT)); 72 bignum.defineFastMethod("remainder", callbackFactory.getFastMethod("remainder", RubyKernel.IRUBY_OBJECT)); 73 bignum.defineFastMethod("quo", callbackFactory.getFastMethod("quo", RubyKernel.IRUBY_OBJECT)); 74 bignum.defineFastMethod("**", callbackFactory.getFastMethod("pow", RubyKernel.IRUBY_OBJECT)); 75 bignum.defineFastMethod("&", callbackFactory.getFastMethod("and", RubyKernel.IRUBY_OBJECT)); 76 bignum.defineFastMethod("|", callbackFactory.getFastMethod("or", RubyKernel.IRUBY_OBJECT)); 77 bignum.defineFastMethod("^", callbackFactory.getFastMethod("xor", RubyKernel.IRUBY_OBJECT)); 78 bignum.defineFastMethod("~", callbackFactory.getFastMethod("neg")); 79 bignum.defineFastMethod("<<", callbackFactory.getFastMethod("lshift", RubyKernel.IRUBY_OBJECT)); 80 bignum.defineFastMethod(">>", callbackFactory.getFastMethod("rshift", RubyKernel.IRUBY_OBJECT)); 81 bignum.defineFastMethod("[]", callbackFactory.getFastMethod("aref", RubyKernel.IRUBY_OBJECT)); 82 83 bignum.defineFastMethod("<=>", callbackFactory.getFastMethod("cmp", RubyKernel.IRUBY_OBJECT)); 84 bignum.defineFastMethod("==", callbackFactory.getFastMethod("equal", RubyKernel.IRUBY_OBJECT)); 85 bignum.defineFastMethod("eql?", callbackFactory.getFastMethod("eql_p", RubyKernel.IRUBY_OBJECT)); 86 bignum.defineFastMethod("hash", callbackFactory.getFastMethod("hash")); 87 bignum.defineFastMethod("to_f", callbackFactory.getFastMethod("to_f")); 88 bignum.defineFastMethod("abs", callbackFactory.getFastMethod("abs")); 89 bignum.defineFastMethod("size", callbackFactory.getFastMethod("size")); 90 91 return bignum; 92 } 93 94 private static final int BIT_SIZE = 64; 95 private static final long MAX = (1L << (BIT_SIZE - 1)) - 1; 96 private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX); 97 private static final BigInteger LONG_MIN = BigInteger.valueOf(-MAX - 1); 98 99 public static final byte OP_PLUS_SWITCHVALUE = 1; 100 public static final byte OP_MINUS_SWITCHVALUE = 2; 101 public static final byte OP_LT_SWITCHVALUE = 3; 102 103 private final BigInteger value; 104 105 public RubyBignum(Ruby runtime, BigInteger value) { 106 super(runtime, runtime.getClass("Bignum")); 107 this.value = value; 108 } 109 110 public IRubyObject callMethod(ThreadContext context, RubyModule rubyclass, byte switchvalue, String name, 111 IRubyObject[] args, CallType callType, Block block) { 112 switch (switchvalue) { 113 case OP_PLUS_SWITCHVALUE: 114 Arity.singleArgument().checkArity(context.getRuntime(), args); 115 return plus(args[0]); 116 case OP_MINUS_SWITCHVALUE: 117 Arity.singleArgument().checkArity(context.getRuntime(), args); 118 return minus(args[0]); 119 case 0: 120 default: 121 return super.callMethod(context, rubyclass, name, args, callType, block); 122 } 123 } 124 125 public int getNativeTypeIndex() { 126 return ClassIndex.BIGNUM; 127 } 128 129 public static RubyBignum newBignum(Ruby runtime, long value) { 130 return newBignum(runtime, BigInteger.valueOf(value)); 131 } 132 133 public static RubyBignum newBignum(Ruby runtime, double value) { 134 return newBignum(runtime, new BigDecimal (value).toBigInteger()); 135 } 136 137 public static RubyBignum newBignum(Ruby runtime, BigInteger value) { 138 return new RubyBignum(runtime, value); 139 } 140 141 public double getDoubleValue() { 142 return big2dbl(this); 143 } 144 145 public long getLongValue() { 146 return big2long(this); 147 } 148 149 152 public BigInteger getValue() { 153 return value; 154 } 155 156 160 161 162 165 public static RubyInteger bignorm(Ruby runtime, BigInteger bi) { 166 if (bi.compareTo(LONG_MIN) < 0 || bi.compareTo(LONG_MAX) > 0) { 167 return newBignum(runtime, bi); 168 } 169 return runtime.newFixnum(bi.longValue()); 170 } 171 172 175 public static long big2long(RubyBignum value) { 176 BigInteger big = value.getValue(); 177 178 if (big.compareTo(LONG_MIN) < 0 || big.compareTo(LONG_MAX) > 0) { 179 throw value.getRuntime().newRangeError("bignum too big to convert into `long'"); 180 } 181 return big.longValue(); 182 } 183 184 187 public static double big2dbl(RubyBignum value) { 188 BigInteger big = value.getValue(); 189 double dbl = big.doubleValue(); 190 if (dbl == Double.NEGATIVE_INFINITY || dbl == Double.POSITIVE_INFINITY) { 191 value.getRuntime().getWarnings().warn("Bignum out of Float range"); 192 } 193 return dbl; 194 } 195 196 199 public static BigInteger fix2big(RubyFixnum arg) { 200 return BigInteger.valueOf(arg.getLongValue()); 201 } 202 203 207 208 211 public IRubyObject to_s(IRubyObject[] args) { 212 checkArgumentCount(args, 0, 1); 213 214 int base = args.length == 0 ? 10 : num2int(args[0]); 215 if (base < 2 || base > 36) { 216 throw getRuntime().newArgumentError("illegal radix " + base); 217 } 218 return getRuntime().newString(getValue().toString(base)); 219 } 220 221 224 public IRubyObject coerce(IRubyObject other) { 225 if (other instanceof RubyFixnum) { 226 return getRuntime().newArray(newBignum(getRuntime(), ((RubyFixnum) other).getLongValue()), this); 227 } else if (other instanceof RubyBignum) { 228 return getRuntime().newArray(newBignum(getRuntime(), ((RubyBignum) other).getValue()), this); 229 } 230 231 throw getRuntime().newTypeError("Can't coerce " + other.getMetaClass().getName() + " to Bignum"); 232 } 233 234 237 public IRubyObject uminus() { 238 return bignorm(getRuntime(), value.negate()); 239 } 240 241 244 public IRubyObject plus(IRubyObject other) { 245 if (other instanceof RubyFixnum) { 246 return bignorm(getRuntime(), value.add(fix2big(((RubyFixnum) other)))); 247 } 248 if (other instanceof RubyBignum) { 249 return bignorm(getRuntime(), value.add(((RubyBignum) other).value)); 250 } else if (other instanceof RubyFloat) { 251 return RubyFloat.newFloat(getRuntime(), big2dbl(this) + ((RubyFloat) other).getDoubleValue()); 252 } 253 return coerceBin("+", other); 254 } 255 256 259 public IRubyObject minus(IRubyObject other) { 260 if (other instanceof RubyFixnum) { 261 return bignorm(getRuntime(), value.subtract(fix2big(((RubyFixnum) other)))); 262 } 263 if (other instanceof RubyBignum) { 264 return bignorm(getRuntime(), value.subtract(((RubyBignum) other).value)); 265 } else if (other instanceof RubyFloat) { 266 return RubyFloat.newFloat(getRuntime(), big2dbl(this) - ((RubyFloat) other).getDoubleValue()); 267 } 268 return coerceBin("-", other); 269 } 270 271 274 public IRubyObject mul(IRubyObject other) { 275 if (other instanceof RubyFixnum) { 276 return bignorm(getRuntime(), value.multiply(fix2big(((RubyFixnum) other)))); 277 } 278 if (other instanceof RubyBignum) { 279 return bignorm(getRuntime(), value.multiply(((RubyBignum) other).value)); 280 } else if (other instanceof RubyFloat) { 281 return RubyFloat.newFloat(getRuntime(), big2dbl(this) * ((RubyFloat) other).getDoubleValue()); 282 } 283 return coerceBin("*", other); 284 } 285 286 289 public IRubyObject div(IRubyObject other) { 290 final BigInteger otherValue; 291 if (other instanceof RubyFixnum) { 292 otherValue = fix2big((RubyFixnum) other); 293 } else if (other instanceof RubyBignum) { 294 otherValue = ((RubyBignum) other).value; 295 } else if (other instanceof RubyFloat) { 296 return RubyFloat.newFloat(getRuntime(), big2dbl(this) / ((RubyFloat) other).getDoubleValue()); 297 } else { 298 return coerceBin("/", other); 299 } 300 301 if (otherValue.equals(BigInteger.ZERO)) { 302 throw getRuntime().newZeroDivisionError(); 303 } 304 305 BigInteger [] results = value.divideAndRemainder(otherValue); 306 307 if (results[0].signum() == -1 && results[1].signum() != 0) { 308 return bignorm(getRuntime(), results[0].subtract(BigInteger.ONE)); 309 } 310 return bignorm(getRuntime(), results[0]); 311 } 312 313 316 public IRubyObject divmod(IRubyObject other) { 317 final BigInteger otherValue; 318 if (other instanceof RubyFixnum) { 319 otherValue = fix2big((RubyFixnum) other); 320 } else if (other instanceof RubyBignum) { 321 otherValue = ((RubyBignum) other).value; 322 } else { 323 return coerceBin("divmod", other); 324 } 325 326 if (otherValue.equals(BigInteger.ZERO)) { 327 throw getRuntime().newZeroDivisionError(); 328 } 329 330 BigInteger [] results = value.divideAndRemainder(otherValue); 331 332 if (results[0].signum() == -1 && results[1].signum() != 0) { 333 return bignorm(getRuntime(), results[0].subtract(BigInteger.ONE)); 334 } 335 final Ruby runtime = getRuntime(); 336 return RubyArray.newArray(getRuntime(), bignorm(runtime, results[0]), bignorm(runtime, results[1])); 337 } 338 339 342 public IRubyObject mod(IRubyObject other) { 343 final BigInteger otherValue; 344 if (other instanceof RubyFixnum) { 345 otherValue = fix2big((RubyFixnum) other); 346 } else if (other instanceof RubyBignum) { 347 otherValue = ((RubyBignum) other).value; 348 } else { 349 return coerceBin("%", other); 350 } 351 if (otherValue.equals(BigInteger.ZERO)) { 352 throw getRuntime().newZeroDivisionError(); 353 } 354 BigInteger result = value.mod(otherValue.abs()); 355 if (otherValue.signum() == -1) { 356 result = otherValue.add(result); 357 } 358 return bignorm(getRuntime(), result); 359 360 } 361 362 365 public IRubyObject remainder(IRubyObject other) { 366 final BigInteger otherValue; 367 if (other instanceof RubyFixnum) { 368 otherValue = fix2big(((RubyFixnum) other)); 369 } else if (other instanceof RubyBignum) { 370 otherValue = ((RubyBignum) other).value; 371 } else { 372 return coerceBin("remainder", other); 373 } 374 if (otherValue.equals(BigInteger.ZERO)) { 375 throw getRuntime().newZeroDivisionError(); 376 } 377 return bignorm(getRuntime(), value.remainder(otherValue)); 378 } 379 380 384 public IRubyObject quo(IRubyObject other) { 385 if (other instanceof RubyNumeric) { 386 return RubyFloat.newFloat(getRuntime(), big2dbl(this) / ((RubyNumeric) other).getDoubleValue()); 387 } else { 388 return coerceBin("quo", other); 389 } 390 } 391 392 395 public IRubyObject pow(IRubyObject other) { 396 double d; 397 if (other instanceof RubyFixnum) { 398 RubyFixnum fix = (RubyFixnum) other; 399 long fixValue = fix.getLongValue(); 400 if (((value.bitLength() + 7) / 8) * 4 * Math.abs(fixValue) > 1024 * 1024) { 402 getRuntime().getWarnings().warn("in a**b, b may be too big"); 403 } 404 if (fixValue >= 0) { 405 return bignorm(getRuntime(), value.pow((int) fixValue)); } else { 407 return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), (double)fixValue)); 408 } 409 } else if (other instanceof RubyBignum) { 410 getRuntime().getWarnings().warn("in a**b, b may be too big"); 411 d = ((RubyBignum) other).getDoubleValue(); 412 } else if (other instanceof RubyFloat) { 413 d = ((RubyFloat) other).getDoubleValue(); 414 } else { 415 return coerceBin("**", other); 416 417 } 418 return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), d)); 419 } 420 421 424 public IRubyObject and(IRubyObject other) { 425 other = other.convertToInteger(); 426 if (other instanceof RubyBignum) { 427 return bignorm(getRuntime(), value.and(((RubyBignum) other).value)); 428 } else if(other instanceof RubyFixnum) { 429 return bignorm(getRuntime(), value.and(fix2big((RubyFixnum)other))); 430 } 431 return coerceBin("&", other); 432 } 433 434 437 public IRubyObject or(IRubyObject other) { 438 other = other.convertToInteger(); 439 if (other instanceof RubyBignum) { 440 return bignorm(getRuntime(), value.or(((RubyBignum) other).value)); 441 } 442 if (other instanceof RubyFixnum) { return bignorm(getRuntime(), value.or(fix2big((RubyFixnum)other))); 444 } 445 return coerceBin("|", other); 446 } 447 448 451 public IRubyObject xor(IRubyObject other) { 452 other = other.convertToInteger(); 453 if (other instanceof RubyBignum) { 454 return bignorm(getRuntime(), value.xor(((RubyBignum) other).value)); 455 } 456 if (other instanceof RubyFixnum) { 457 return bignorm(getRuntime(), value.xor(BigInteger.valueOf(((RubyFixnum) other).getLongValue()))); 458 } 459 460 return coerceBin("^", other); 461 } 462 463 466 public IRubyObject neg() { 467 return RubyBignum.newBignum(getRuntime(), value.not()); 468 } 469 470 473 public IRubyObject lshift(IRubyObject other) { 474 int width = num2int(other); 475 if (width < 0) { 476 return rshift(RubyFixnum.newFixnum(getRuntime(), -width)); 477 } 478 479 return bignorm(getRuntime(), value.shiftLeft(width)); 480 } 481 482 485 public IRubyObject rshift(IRubyObject other) { 486 int width = num2int(other); 487 488 if (width < 0) { 489 return lshift(RubyFixnum.newFixnum(getRuntime(), -width)); 490 } 491 return bignorm(getRuntime(), value.shiftRight(width)); 492 } 493 494 497 public RubyFixnum aref(IRubyObject other) { 498 if (other instanceof RubyBignum) { 499 if (((RubyBignum) other).value.signum() >= 0 || value.signum() == -1) { 500 return RubyFixnum.zero(getRuntime()); 501 } 502 return RubyFixnum.one(getRuntime()); 503 } 504 int position = num2int(other); 505 if (position < 0) { 506 return RubyFixnum.zero(getRuntime()); 507 } 508 509 return value.testBit(num2int(other)) ? RubyFixnum.one(getRuntime()) : RubyFixnum.zero(getRuntime()); 510 } 511 512 515 public IRubyObject cmp(IRubyObject other) { 516 final BigInteger otherValue; 517 if (other instanceof RubyFixnum) { 518 otherValue = fix2big((RubyFixnum) other); 519 } else if (other instanceof RubyBignum) { 520 otherValue = ((RubyBignum) other).value; 521 } else if (other instanceof RubyFloat) { 522 return dbl_cmp(getRuntime(), big2dbl(this), ((RubyFloat) other).getDoubleValue()); 523 } else { 524 return coerceCmp("<=>", other); 525 } 526 527 return RubyFixnum.newFixnum(getRuntime(), value.compareTo(otherValue)); 529 } 530 531 534 public IRubyObject equal(IRubyObject other) { 535 final BigInteger otherValue; 536 if (other instanceof RubyFixnum) { 537 otherValue = fix2big((RubyFixnum) other); 538 } else if (other instanceof RubyBignum) { 539 otherValue = ((RubyBignum) other).value; 540 } else if (other instanceof RubyFloat) { 541 double a = ((RubyFloat) other).getDoubleValue(); 542 if (Double.isNaN(a)) { 543 return getRuntime().getFalse(); 544 } 545 return RubyBoolean.newBoolean(getRuntime(), a == big2dbl(this)); 546 } else { 547 return super.equal(other); 548 } 549 return RubyBoolean.newBoolean(getRuntime(), value.compareTo(otherValue) == 0); 550 } 551 552 555 public IRubyObject eql_p(IRubyObject other) { 556 if (other instanceof RubyBignum) { 557 return RubyBoolean.newBoolean(getRuntime(), value.compareTo(((RubyBignum)other).value) == 0); 558 } 559 return getRuntime().getFalse(); 560 } 561 562 565 public RubyFixnum hash() { 566 return getRuntime().newFixnum(value.hashCode()); 567 } 568 569 572 public IRubyObject to_f() { 573 return RubyFloat.newFloat(getRuntime(), getDoubleValue()); 574 } 575 576 579 public IRubyObject abs() { 580 return RubyBignum.newBignum(getRuntime(), value.abs()); 581 } 582 583 586 public RubyFixnum size() { 587 return getRuntime().newFixnum((value.bitLength() + 7) / 8); 588 } 589 590 public static void marshalTo(RubyBignum bignum, MarshalStream output) throws IOException { 591 output.write(bignum.value.signum() >= 0 ? '+' : '-'); 592 593 BigInteger absValue = bignum.value.abs(); 594 595 byte[] digits = absValue.toByteArray(); 596 597 boolean oddLengthNonzeroStart = (digits.length % 2 != 0 && digits[0] != 0); 598 int shortLength = digits.length / 2; 599 if (oddLengthNonzeroStart) { 600 shortLength++; 601 } 602 output.writeInt(shortLength); 603 604 for (int i = 1; i <= shortLength * 2 && i <= digits.length; i++) { 605 output.write(digits[digits.length - i]); 606 } 607 608 if (oddLengthNonzeroStart) { 609 output.write(0); 611 } 612 } 613 614 public static RubyBignum unmarshalFrom(UnmarshalStream input) throws IOException { 615 boolean positive = input.readUnsignedByte() == '+'; 616 int shortLength = input.unmarshalInt(); 617 618 byte[] digits = new byte[shortLength * 2 + 1]; 620 621 for (int i = digits.length - 1; i >= 1; i--) { 622 digits[i] = input.readSignedByte(); 623 } 624 625 BigInteger value = new BigInteger (digits); 626 if (!positive) { 627 value = value.negate(); 628 } 629 630 RubyBignum result = newBignum(input.getRuntime(), value); 631 input.registerLinkTarget(result); 632 return result; 633 } 634 } 635 | Popular Tags |