1 35 package org.jruby; 36 37 import java.math.BigInteger ; 38 import java.util.List ; 39 40 import org.jruby.exceptions.RaiseException; 41 import org.jruby.runtime.Block; 42 import org.jruby.runtime.CallbackFactory; 43 import org.jruby.runtime.ObjectAllocator; 44 import org.jruby.runtime.ThreadContext; 45 import org.jruby.runtime.builtin.IRubyObject; 46 import org.jruby.util.ByteList; 47 import org.jruby.util.Convert; 48 49 52 public class RubyNumeric extends RubyObject { 56 57 public static RubyClass createNumericClass(Ruby runtime) { 58 RubyClass numeric = runtime.defineClass("Numeric", runtime.getObject(), NUMERIC_ALLOCATOR); 59 CallbackFactory callbackFactory = runtime.callbackFactory(RubyNumeric.class); 60 numeric.defineFastMethod("singleton_method_added", callbackFactory.getFastMethod("sadded", 61 RubyKernel.IRUBY_OBJECT)); 62 63 numeric.includeModule(runtime.getModule("Comparable")); 64 65 numeric.defineFastMethod("initialize_copy", callbackFactory.getFastMethod("init_copy", RubyKernel.IRUBY_OBJECT)); 66 numeric.defineFastMethod("coerce", callbackFactory.getFastMethod("coerce", RubyKernel.IRUBY_OBJECT)); 67 68 numeric.defineFastMethod("+@", callbackFactory.getFastMethod("uplus")); 69 numeric.defineFastMethod("-@", callbackFactory.getFastMethod("uminus")); 70 numeric.defineFastMethod("<=>", callbackFactory.getFastMethod("cmp", RubyKernel.IRUBY_OBJECT)); 71 numeric.defineFastMethod("quo", callbackFactory.getFastMethod("quo", RubyKernel.IRUBY_OBJECT)); 72 numeric.defineFastMethod("eql?", callbackFactory.getFastMethod("eql_p", RubyKernel.IRUBY_OBJECT)); 73 numeric.defineFastMethod("div", callbackFactory.getFastMethod("div", RubyKernel.IRUBY_OBJECT)); 74 numeric.defineFastMethod("divmod", callbackFactory.getFastMethod("divmod", RubyKernel.IRUBY_OBJECT)); 75 numeric.defineFastMethod("modulo", callbackFactory.getFastMethod("modulo", RubyKernel.IRUBY_OBJECT)); 76 numeric.defineFastMethod("remainder", callbackFactory.getFastMethod("remainder", RubyKernel.IRUBY_OBJECT)); 77 numeric.defineFastMethod("abs", callbackFactory.getFastMethod("abs")); 78 numeric.defineFastMethod("to_int", callbackFactory.getFastMethod("to_int")); 79 numeric.defineFastMethod("integer?", callbackFactory.getFastMethod("int_p")); 80 numeric.defineFastMethod("zero?", callbackFactory.getFastMethod("zero_p")); 81 numeric.defineFastMethod("nonzero?", callbackFactory.getFastMethod("nonzero_p")); 82 numeric.defineFastMethod("floor", callbackFactory.getFastMethod("floor")); 83 numeric.defineFastMethod("ceil", callbackFactory.getFastMethod("ceil")); 84 numeric.defineFastMethod("round", callbackFactory.getFastMethod("round")); 85 numeric.defineFastMethod("truncate", callbackFactory.getFastMethod("truncate")); 86 numeric.defineMethod("step", callbackFactory.getOptMethod("step")); 87 88 return numeric; 89 } 90 91 protected static ObjectAllocator NUMERIC_ALLOCATOR = new ObjectAllocator() { 92 public IRubyObject allocate(Ruby runtime, RubyClass klass) { 93 return new RubyNumeric(runtime, klass); 94 } 95 }; 96 97 public static double DBL_EPSILON=2.2204460492503131e-16; 98 99 public RubyNumeric(Ruby runtime, RubyClass metaClass) { 100 super(runtime, metaClass); 101 } 102 103 public double getDoubleValue() { 107 return 0; 108 } 109 110 public long getLongValue() { 111 return 0; 112 } 113 114 public static RubyNumeric newNumeric(Ruby runtime) { 115 return new RubyNumeric(runtime, runtime.getClass("Numeric")); 116 } 117 118 122 123 126 public static int num2int(IRubyObject arg) { 127 long num = num2long(arg); 128 129 checkInt(arg, num); 130 return (int)num; 131 } 132 133 136 public static void checkInt(IRubyObject arg, long num){ 137 String s; 138 if (num < Integer.MIN_VALUE) { 139 s = "small"; 140 } else if (num > Integer.MAX_VALUE) { 141 s = "big"; 142 } else { 143 return; 144 } 145 throw arg.getRuntime().newRangeError("integer " + num + " too " + s + " to convert to `int'"); 146 } 147 148 152 public static long num2long(IRubyObject arg) { 153 if (arg instanceof RubyFixnum) { 154 return ((RubyFixnum) arg).getLongValue(); 155 } 156 if (arg.isNil()) { 157 throw arg.getRuntime().newTypeError("no implicit conversion from nil to integer"); 158 } 159 160 if (arg instanceof RubyFloat) { 161 double aFloat = ((RubyFloat) arg).getDoubleValue(); 162 if (aFloat <= (double) Long.MAX_VALUE && aFloat >= (double) Long.MIN_VALUE) { 163 return (long) aFloat; 164 } else { 165 throw arg.getRuntime().newTypeError("float " + aFloat + "out of range of integer"); 167 } 168 } else if (arg instanceof RubyBignum) { 169 return RubyBignum.big2long((RubyBignum) arg); 170 } 171 return arg.convertToInteger().getLongValue(); 172 } 173 174 177 public static IRubyObject dbl2num(Ruby runtime, double val) { 178 if (Double.isInfinite(val)) { 179 throw runtime.newFloatDomainError(val < 0 ? "-Infinity" : "Infinity"); 180 } 181 if (Double.isNaN(val)) { 182 throw runtime.newFloatDomainError("NaN"); 183 } 184 185 if (val > (double) RubyFixnum.MAX || val < (double) RubyFixnum.MIN) { 186 return RubyBignum.newBignum(runtime, val); 187 } 188 return RubyFixnum.newFixnum(runtime, (long) val); 189 } 190 191 194 public static double num2dbl(IRubyObject arg) { 195 if (arg instanceof RubyFloat) { 196 return ((RubyFloat) arg).getDoubleValue(); 197 } else if (arg instanceof RubyString) { 198 throw arg.getRuntime().newTypeError("no implicit conversion to float from string"); 199 } else if (arg == arg.getRuntime().getNil()) { 200 throw arg.getRuntime().newTypeError("no implicit conversion to float from nil"); 201 } 202 return arg.convertToFloat().getDoubleValue(); 203 } 204 205 208 public static IRubyObject dbl_cmp(Ruby runtime, double a, double b) { 209 if (Double.isNaN(a) || Double.isNaN(b)) { 210 return runtime.getNil(); 211 } 212 if (a > b) { 213 return RubyFixnum.one(runtime); 214 } 215 if (a < b) { 216 return RubyFixnum.minus_one(runtime); 217 } 218 return RubyFixnum.zero(runtime); 219 } 220 221 public static long fix2long(IRubyObject arg) { 222 return ((RubyFixnum) arg).getLongValue(); 223 } 224 225 public static int fix2int(IRubyObject arg) { 226 long num = arg instanceof RubyFixnum ? fix2long(arg) : num2long(arg); 227 228 checkInt(arg, num); 229 return (int) num; 230 } 231 232 public static RubyInteger str2inum(Ruby runtime, RubyString str, int base) { 233 return str2inum(runtime,str,base,false); 234 } 235 236 public static RubyNumeric int2fix(Ruby runtime, long val) { 237 return RubyFixnum.newFixnum(runtime,val); 238 } 239 240 243 public static IRubyObject num2fix(IRubyObject val) { 244 if (val instanceof RubyFixnum) { 245 return val; 246 } 247 if (val instanceof RubyBignum) { 248 throw val.getRuntime().newRangeError("integer " + val + " out of range of fixnum"); 250 } 251 return RubyFixnum.newFixnum(val.getRuntime(), num2long(val)); 252 } 253 254 284 public static RubyInteger str2inum(Ruby runtime, RubyString str, int base, boolean strict) { 285 if (base != 0 && (base < 2 || base > 36)) { 286 throw runtime.newArgumentError("illegal radix " + base); 287 } 288 ByteList bytes = str.getByteList(); 289 try { 290 return runtime.newFixnum(Convert.byteListToLong(bytes,base,strict)); 291 292 } catch (InvalidIntegerException e) { 293 if (strict) { 294 throw runtime.newArgumentError("invalid value for Integer: " 295 + str.callMethod(runtime.getCurrentContext(), "inspect").toString()); 296 } 297 return RubyFixnum.zero(runtime); 298 } catch (NumberTooLargeException e) { 299 try { 300 BigInteger bi = Convert.byteListToBigInteger(bytes,base,strict); 301 return new RubyBignum(runtime,bi); 302 } catch (InvalidIntegerException e2) { 303 if(strict) { 304 throw runtime.newArgumentError("invalid value for Integer: " 305 + str.callMethod(runtime.getCurrentContext(), "inspect").toString()); 306 } 307 return RubyFixnum.zero(runtime); 308 } 309 } 310 } 311 312 public static RubyFloat str2fnum(Ruby runtime, RubyString arg) { 313 return str2fnum(runtime,arg,false); 314 } 315 316 329 public static RubyFloat str2fnum(Ruby runtime, RubyString arg, boolean raise) { 330 String str = arg.toString().trim(); 331 double d = 0.0; 332 int pos = str.length(); 333 for (int i = 0; i < pos; i++) { 334 if ("0123456789eE+-.".indexOf(str.charAt(i)) == -1) { 335 if(raise) { 336 throw runtime.newArgumentError("invalid value for Float(): " 337 + arg.callMethod(runtime.getCurrentContext(), "inspect").toString()); 338 } 339 pos = i + 1; 340 break; 341 } 342 } 343 for (; pos > 0; pos--) { 344 try { 345 d = Double.parseDouble(str.substring(0, pos)); 346 } catch (NumberFormatException ex) { 347 if(raise) { 348 throw runtime.newArgumentError("invalid value for Float(): " 349 + arg.callMethod(runtime.getCurrentContext(), "inspect").toString()); 350 } 351 continue; 352 } 353 break; 354 } 355 return new RubyFloat(runtime, d); 356 } 357 358 361 362 protected IRubyObject[] getCoerced(IRubyObject other, boolean error) { 363 IRubyObject result; 364 365 try { 366 result = other.callMethod(getRuntime().getCurrentContext(), "coerce", this); 367 } catch (RaiseException e) { 368 if (error) { 369 throw getRuntime().newTypeError( 370 other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName()); 371 } 372 373 return null; 374 } 375 376 if (!(result instanceof RubyArray) || ((RubyArray)result).getLength() != 2) { 377 throw getRuntime().newTypeError("coerce must return [x, y]"); 378 } 379 380 return ((RubyArray)result).toJavaArray(); 381 } 382 383 protected IRubyObject callCoerced(String method, IRubyObject other, boolean err) { 384 IRubyObject[] args = getCoerced(other, err); 385 return args[0].callMethod(getRuntime().getCurrentContext(), method, args[1]); 386 } 387 388 protected IRubyObject callCoerced(String method, IRubyObject other) { 389 IRubyObject[] args = getCoerced(other, true); 390 return args[0].callMethod(getRuntime().getCurrentContext(), method, args[1]); 391 } 392 393 395 398 protected final IRubyObject coerceBody(IRubyObject other) { 399 return other.callMethod(getRuntime().getCurrentContext(), "coerce", this); 400 } 401 402 405 protected final List doCoerce(IRubyObject other, boolean err) { 406 IRubyObject result; 407 try { 408 result = coerceBody(other); 409 } catch (RaiseException e) { 410 if (err) { 411 throw getRuntime().newTypeError( 412 other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName()); 413 } 414 return null; 415 } 416 417 if (!(result instanceof RubyArray) || ((RubyArray) result).getLength() != 2) { 418 throw getRuntime().newTypeError("coerce must return [x, y]"); 419 } 420 return ((RubyArray) result).getList(); 421 } 422 423 426 protected final IRubyObject coerceBin(String method, IRubyObject other) { 427 List list = doCoerce(other, true); 428 return ((RubyObject) list.get(0)) 429 .callMethod(getRuntime().getCurrentContext(), method, (RubyObject) list.get(1)); 430 } 431 432 435 protected final IRubyObject coerceCmp(String method, IRubyObject other) { 436 List list = doCoerce(other, false); 437 if (list == null) { 438 return getRuntime().getNil(); } 440 return ((RubyObject) list.get(0)) 441 .callMethod(getRuntime().getCurrentContext(), method, (RubyObject) list.get(1)); 442 } 443 444 447 protected final IRubyObject coerceRelOp(String method, IRubyObject other) { 448 List list = doCoerce(other, false); 449 if (list != null) { 450 IRubyObject result = ((RubyObject) list.get(0)).callMethod(getRuntime().getCurrentContext(), method, 451 (RubyObject) list.get(1)); 452 if (!result.isNil()) { 453 return result; 454 } 455 } 456 457 RubyComparable.cmperr(this, other); return null; } 460 461 public RubyNumeric asNumeric() { 462 return this; 463 } 464 465 469 470 473 public IRubyObject sadded(IRubyObject name) { 474 throw getRuntime().newTypeError("can't define singleton method " + name + " for " + getType().getName()); 475 } 476 477 480 public IRubyObject init_copy(IRubyObject arg) { 481 throw getRuntime().newTypeError("can't copy " + getType().getName()); 482 } 483 484 487 public IRubyObject coerce(IRubyObject other) { 488 if (getMetaClass() == other.getMetaClass()) { 489 return getRuntime().newArray(other, this); 490 } 491 492 return getRuntime().newArray(other.convertToFloat(), convertToFloat()); 493 } 494 495 498 public IRubyObject uplus() { 499 return this; 500 } 501 502 505 public IRubyObject uminus() { 506 RubyFixnum zero = RubyFixnum.zero(getRuntime()); 507 List list = zero.doCoerce(this, true); 508 return ((RubyObject) list.get(0)).callMethod(getRuntime().getCurrentContext(), "-", (RubyObject) list.get(1)); 509 } 510 511 514 public IRubyObject cmp(IRubyObject other) { 515 if (this == other) { return RubyFixnum.zero(getRuntime()); 517 } 518 return getRuntime().getNil(); 519 } 520 521 524 public IRubyObject eql_p(IRubyObject other) { 525 if (getMetaClass() != other.getMetaClass()) { 526 return getRuntime().getFalse(); 527 } 528 return super.equal(other); 529 } 530 531 534 public IRubyObject quo(IRubyObject other) { 535 return callMethod(getRuntime().getCurrentContext(), "/", other); 536 } 537 538 541 public IRubyObject div(IRubyObject other) { 542 return callMethod(getRuntime().getCurrentContext(), "/", other).convertToFloat().floor(); 543 } 544 545 548 public IRubyObject divmod(IRubyObject other) { 549 IRubyObject cdr = callMethod(getRuntime().getCurrentContext(), "%", other); 550 IRubyObject car = div(other); 551 return RubyArray.newArray(getRuntime(), car, cdr); 552 553 } 554 555 558 public IRubyObject modulo(IRubyObject other) { 559 return callMethod(getRuntime().getCurrentContext(), "%", other); 560 } 561 562 565 public IRubyObject remainder(IRubyObject y) { 566 ThreadContext context = getRuntime().getCurrentContext(); 567 IRubyObject z = callMethod(context, "%", y); 568 IRubyObject x = this; 569 RubyFixnum zero = RubyFixnum.zero(getRuntime()); 570 571 if (!(((RubyNumeric)z).equal(zero).isTrue()) 572 && ((x.callMethod(context, "<", zero)).isTrue() 573 && (y.callMethod(context, ">", zero)).isTrue()) 574 || ((x.callMethod(context, ">", zero)).isTrue() 575 && (y.callMethod(context, "<", zero)).isTrue())) { 576 return z.callMethod(context, "-",y); 577 } 578 579 return z; 580 } 581 582 585 public IRubyObject abs() { 586 ThreadContext context = getRuntime().getCurrentContext(); 587 if (callMethod(context, "<", RubyFixnum.zero(getRuntime())).isTrue()) { 588 return (RubyNumeric) callMethod(context, "-@"); 589 } 590 return this; 591 } 592 593 596 public IRubyObject to_int() { 597 return callMethod(getRuntime().getCurrentContext(), "to_i", IRubyObject.NULL_ARRAY); 598 } 599 600 603 public IRubyObject int_p() { 604 return getRuntime().getFalse(); 605 } 606 607 610 public IRubyObject zero_p() { 611 return equal(RubyFixnum.zero(getRuntime())).isTrue() ? getRuntime().getTrue() : getRuntime().getFalse(); 613 } 614 615 618 public IRubyObject nonzero_p() { 619 if (callMethod(getRuntime().getCurrentContext(), "zero?").isTrue()) { 620 return getRuntime().getNil(); 621 } 622 return this; 623 } 624 625 628 public IRubyObject floor() { 629 return convertToFloat().floor(); 630 } 631 632 635 public IRubyObject ceil() { 636 return convertToFloat().ceil(); 637 } 638 639 642 public IRubyObject round() { 643 return convertToFloat().round(); 644 } 645 646 649 public IRubyObject truncate() { 650 return convertToFloat().truncate(); 651 } 652 653 public IRubyObject step(IRubyObject[] args, Block block) { 654 IRubyObject to; 655 IRubyObject step; 656 657 if(args.length == 1){ 658 to = args[0]; 659 step = RubyFixnum.one(getRuntime()); 660 } else if (args.length == 2) { 661 to = args[0]; 662 step = args[1]; 663 }else{ 664 throw getRuntime().newTypeError("wrong number of arguments"); 665 } 666 667 ThreadContext context = getRuntime().getCurrentContext(); 668 if (this instanceof RubyFixnum && to instanceof RubyFixnum && step instanceof RubyFixnum) { 669 long value = getLongValue(); 670 long end = ((RubyFixnum) to).getLongValue(); 671 long diff = ((RubyFixnum) step).getLongValue(); 672 673 if (diff == 0) { 674 throw getRuntime().newArgumentError("step cannot be 0"); 675 } 676 if (diff > 0) { 677 for (long i = value; i <= end; i += diff) { 678 context.yield(RubyFixnum.newFixnum(getRuntime(), i), block); 679 } 680 } else { 681 for (long i = value; i >= end; i += diff) { 682 context.yield(RubyFixnum.newFixnum(getRuntime(), i), block); 683 } 684 } 685 } else if (this instanceof RubyFloat || to instanceof RubyFloat || step instanceof RubyFloat) { 686 double beg = num2dbl(this); 687 double end = num2dbl(to); 688 double unit = num2dbl(step); 689 690 if (unit == 0) { 691 throw getRuntime().newArgumentError("step cannot be 0"); 692 } 693 694 double n = (end - beg)/unit; 695 double err = (Math.abs(beg) + Math.abs(end) + Math.abs(end - beg)) / Math.abs(unit) * DBL_EPSILON; 696 697 if (err>0.5) { 698 err=0.5; 699 } 700 n = Math.floor(n + err) + 1; 701 702 for(double i = 0; i < n; i++){ 703 context.yield(RubyFloat.newFloat(getRuntime(), i * unit + beg), block); 704 } 705 706 } else { 707 RubyNumeric i = this; 708 709 String cmp; 710 if (((RubyBoolean) step.callMethod(context, ">", RubyFixnum.zero(getRuntime()))).isTrue()) { 711 cmp = ">"; 712 } else { 713 cmp = "<"; 714 } 715 716 while (true) { 717 if (i.callMethod(context, cmp, to).isTrue()) { 718 break; 719 } 720 context.yield(i, block); 721 i = (RubyNumeric) i.callMethod(context, "+", step); 722 } 723 } 724 return this; 725 } 726 727 739 public IRubyObject equal(IRubyObject other) { 740 if (this == other) { return getRuntime().getTrue(); 742 } 743 744 return other.callMethod(getRuntime().getCurrentContext(), "==", this); 745 } 746 747 public static class InvalidIntegerException extends NumberFormatException { 748 private static final long serialVersionUID = 55019452543252148L; 749 750 public InvalidIntegerException() { 751 super(); 752 } 753 public InvalidIntegerException(String message) { 754 super(message); 755 } 756 } 757 758 public static class NumberTooLargeException extends NumberFormatException { 759 private static final long serialVersionUID = -1835120694982699449L; 760 public NumberTooLargeException() { 761 super(); 762 } 763 public NumberTooLargeException(String message) { 764 super(message); 765 } 766 767 } 768 769 } 770 | Popular Tags |