RubyInteger.java

  1. /*
  2.  **** BEGIN LICENSE BLOCK *****
  3.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Eclipse Public
  6.  * License Version 1.0 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.eclipse.org/legal/epl-v10.html
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  16.  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  17.  * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  18.  * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  19.  * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  20.  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  21.  * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  25.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the EPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the EPL, the GPL or the LGPL.
  34.  ***** END LICENSE BLOCK *****/
  35. package org.jruby;

  36. import org.jcodings.Encoding;
  37. import org.jcodings.exception.EncodingException;
  38. import org.jcodings.specific.ASCIIEncoding;
  39. import org.jcodings.specific.USASCIIEncoding;
  40. import org.jruby.anno.JRubyClass;
  41. import org.jruby.anno.JRubyMethod;
  42. import org.jruby.runtime.Block;
  43. import org.jruby.runtime.BlockBody;
  44. import org.jruby.runtime.ClassIndex;
  45. import org.jruby.runtime.ObjectAllocator;
  46. import org.jruby.runtime.ThreadContext;
  47. import org.jruby.runtime.builtin.IRubyObject;
  48. import org.jruby.util.ByteList;
  49. import org.jruby.util.Numeric;
  50. import org.jruby.util.StringSupport;

  51. import static org.jruby.RubyEnumerator.enumeratorizeWithSize;
  52. import static org.jruby.util.Numeric.checkInteger;
  53. import static org.jruby.util.Numeric.f_gcd;
  54. import static org.jruby.util.Numeric.f_lcm;
  55. import static org.jruby.RubyEnumerator.SizeFn;

  56. /** Implementation of the Integer class.
  57.  *
  58.  * @author  jpetersen
  59.  */
  60. @JRubyClass(name="Integer", parent="Numeric", include="Precision")
  61. public abstract class RubyInteger extends RubyNumeric {

  62.     public static RubyClass createIntegerClass(Ruby runtime) {
  63.         RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
  64.                 ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  65.         runtime.setInteger(integer);

  66.         integer.setClassIndex(ClassIndex.INTEGER);
  67.         integer.setReifiedClass(RubyInteger.class);
  68.        
  69.         integer.kindOf = new RubyModule.JavaClassKindOf(RubyInteger.class);

  70.         integer.getSingletonClass().undefineMethod("new");

  71.         integer.defineAnnotatedMethods(RubyInteger.class);
  72.        
  73.         return integer;
  74.     }

  75.     public RubyInteger(Ruby runtime, RubyClass rubyClass) {
  76.         super(runtime, rubyClass);
  77.     }

  78.     public RubyInteger(RubyClass rubyClass) {
  79.         super(rubyClass);
  80.     }
  81.    
  82.     public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
  83.         super(runtime, rubyClass, useObjectSpace);
  84.     }    

  85.     @Deprecated
  86.     public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace, boolean canBeTainted) {
  87.         super(runtime, rubyClass, useObjectSpace, canBeTainted);
  88.     }    

  89.     @Override
  90.     public RubyInteger convertToInteger() {
  91.         return this;
  92.     }

  93.     // conversion
  94.     protected RubyFloat toFloat() {
  95.         return RubyFloat.newFloat(getRuntime(), getDoubleValue());
  96.     }

  97.     /*  ================
  98.      *  Instance Methods
  99.      *  ================
  100.      */

  101.     /** int_int_p
  102.      *
  103.      */
  104.     @Override
  105.     @JRubyMethod(name = "integer?")
  106.     public IRubyObject integer_p() {
  107.         return getRuntime().getTrue();
  108.     }

  109.     /** int_upto
  110.      *
  111.      */
  112.     @JRubyMethod
  113.     public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
  114.         if (block.isGiven()) {
  115.             if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
  116.                 fixnumUpto(context, ((RubyFixnum)this).getLongValue(), ((RubyFixnum)to).getLongValue(), block);
  117.             } else {
  118.                 duckUpto(context, this, to, block);
  119.             }
  120.             return this;
  121.         } else {
  122.             return enumeratorizeWithSize(context, this, "upto", new IRubyObject[] { to }, uptoSize(context, this, to));
  123.         }
  124.     }

  125.     private static void fixnumUpto(ThreadContext context, long from, long to, Block block) {
  126.         // We must avoid "i++" integer overflow when (to == Long.MAX_VALUE).
  127.         Ruby runtime = context.runtime;
  128.         if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
  129.             IRubyObject nil = runtime.getNil();
  130.             long i;
  131.             for (i = from; i < to; i++) {
  132.                 block.yield(context, nil);
  133.             }
  134.             if (i <= to) {
  135.                 block.yield(context, nil);
  136.             }
  137.         } else {
  138.             long i;
  139.             for (i = from; i < to; i++) {
  140.                 block.yield(context, RubyFixnum.newFixnum(runtime, i));
  141.             }
  142.             if (i <= to) {
  143.                 block.yield(context, RubyFixnum.newFixnum(runtime, i));
  144.             }
  145.         }
  146.     }

  147.     private static void duckUpto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
  148.         Ruby runtime = context.runtime;
  149.         IRubyObject i = from;
  150.         RubyFixnum one = RubyFixnum.one(runtime);
  151.         while (true) {
  152.             if (i.callMethod(context, ">", to).isTrue()) {
  153.                 break;
  154.             }
  155.             block.yield(context, i);
  156.             i = i.callMethod(context, "+", one);
  157.         }
  158.     }

  159.     private static SizeFn uptoSize(final ThreadContext context, final IRubyObject from, final IRubyObject to) {
  160.         return new SizeFn() {
  161.             @Override
  162.             public IRubyObject size(IRubyObject[] args) {
  163.                 return intervalStepSize(context, from, to, RubyFixnum.one(context.runtime), false);
  164.             }
  165.         };
  166.     }

  167.     /** int_downto
  168.      *
  169.      */
  170.     // TODO: Make callCoerced work in block context...then fix downto, step, and upto.
  171.     @JRubyMethod
  172.     public IRubyObject downto(ThreadContext context, IRubyObject to, Block block) {
  173.         if (block.isGiven()) {
  174.             if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
  175.                 fixnumDownto(context, ((RubyFixnum)this).getLongValue(), ((RubyFixnum)to).getLongValue(), block);
  176.             } else {
  177.                 duckDownto(context, this, to, block);
  178.             }
  179.             return this;
  180.         } else {
  181.             return enumeratorizeWithSize(context, this, "downto", new IRubyObject[] { to }, downToSize(context, this, to));
  182.         }
  183.     }

  184.     private static void fixnumDownto(ThreadContext context, long from, long to, Block block) {
  185.         // We must avoid "i--" integer overflow when (to == Long.MIN_VALUE).
  186.         Ruby runtime = context.runtime;
  187.         if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
  188.             IRubyObject nil = runtime.getNil();
  189.             long i;
  190.             for (i = from; i > to; i--) {
  191.                 block.yield(context, nil);
  192.             }
  193.             if (i >= to) {
  194.                 block.yield(context, nil);
  195.             }
  196.         } else {
  197.             long i;
  198.             for (i = from; i > to; i--) {
  199.                 block.yield(context, RubyFixnum.newFixnum(runtime, i));
  200.             }
  201.             if (i >= to) {
  202.                 block.yield(context, RubyFixnum.newFixnum(runtime, i));
  203.             }
  204.         }
  205.     }

  206.     private static void duckDownto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
  207.         Ruby runtime = context.runtime;
  208.         IRubyObject i = from;
  209.         RubyFixnum one = RubyFixnum.one(runtime);
  210.         while (true) {
  211.             if (i.callMethod(context, "<", to).isTrue()) {
  212.                 break;
  213.             }
  214.             block.yield(context, i);
  215.             i = i.callMethod(context, "-", one);
  216.         }
  217.     }

  218.     private static SizeFn downToSize(final ThreadContext context, final IRubyObject from, final IRubyObject to) {
  219.         return new SizeFn() {
  220.             @Override
  221.             public IRubyObject size(IRubyObject[] args) {
  222.                 return intervalStepSize(context, from, to, RubyFixnum.newFixnum(context.runtime, -1), false);
  223.             }
  224.         };
  225.     }

  226.     @JRubyMethod
  227.     public IRubyObject times(ThreadContext context, Block block) {
  228.         if (block.isGiven()) {
  229.             Ruby runtime = context.runtime;
  230.             IRubyObject i = RubyFixnum.zero(runtime);
  231.             RubyFixnum one = RubyFixnum.one(runtime);
  232.             while (true) {
  233.                 if (!i.callMethod(context, "<", this).isTrue()) {
  234.                     break;
  235.                 }
  236.                 block.yield(context, i);
  237.                 i = i.callMethod(context, "+", one);
  238.             }
  239.             return this;
  240.         } else {
  241.             return enumeratorizeWithSize(context, this, "times", timesSizeFn(context.runtime));
  242.         }
  243.     }

  244.     protected SizeFn timesSizeFn(final Ruby runtime) {
  245.         final RubyInteger self = this;
  246.         return new SizeFn() {
  247.             @Override
  248.             public IRubyObject size(IRubyObject[] args) {
  249.                 RubyFixnum zero = RubyFixnum.zero(runtime);
  250.                 if ((self instanceof RubyFixnum && getLongValue() < 0)
  251.                         || self.callMethod("<", zero).isTrue()) {
  252.                     return zero;
  253.                 }

  254.                 return self;
  255.             }
  256.         };
  257.     }

  258.     /** int_succ
  259.      *
  260.      */
  261.     @JRubyMethod(name = {"succ", "next"})
  262.     public IRubyObject succ(ThreadContext context) {
  263.         if (this instanceof RubyFixnum) {
  264.             return ((RubyFixnum) this).op_plus_one(context);
  265.         } else {
  266.             return callMethod(context, "+", RubyFixnum.one(context.runtime));
  267.         }
  268.     }

  269.     static final ByteList[] SINGLE_CHAR_BYTELISTS;
  270.     public static final ByteList[] SINGLE_CHAR_BYTELISTS19;
  271.     static {
  272.         SINGLE_CHAR_BYTELISTS = new ByteList[256];
  273.         SINGLE_CHAR_BYTELISTS19 = new ByteList[256];
  274.         for (int i = 0; i < 256; i++) {
  275.             ByteList usascii = new ByteList(new byte[]{(byte)i}, false);
  276.             SINGLE_CHAR_BYTELISTS[i] = usascii;
  277.             SINGLE_CHAR_BYTELISTS19[i] = i < 0x80 ?
  278.                 new ByteList(new byte[]{(byte)i}, USASCIIEncoding.INSTANCE)
  279.                 :
  280.                 new ByteList(
  281.                     new byte[]{(byte)i},
  282.                     ASCIIEncoding.INSTANCE);
  283.         }
  284.     }

  285.     /** int_chr
  286.      *
  287.      */
  288.     public RubyString chr(ThreadContext context) {
  289.         return chr19(context);
  290.     }

  291.     @JRubyMethod(name = "chr")
  292.     public RubyString chr19(ThreadContext context) {
  293.         Ruby runtime = context.runtime;
  294.         int value = (int)getLongValue();
  295.         if (value >= 0 && value <= 0xFF) {
  296.             ByteList bytes = SINGLE_CHAR_BYTELISTS19[value];
  297.             return RubyString.newStringShared(runtime, bytes, bytes.getEncoding());
  298.         } else {
  299.             Encoding enc = runtime.getDefaultInternalEncoding();
  300.             if (value > 0xFF && (enc == null || enc == ASCIIEncoding.INSTANCE)) {
  301.                 throw runtime.newRangeError(this.toString() + " out of char range");
  302.             } else {
  303.                 if (enc == null) enc = USASCIIEncoding.INSTANCE;
  304.                 return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
  305.             }
  306.         }
  307.     }

  308.     @JRubyMethod(name = "chr")
  309.     public RubyString chr19(ThreadContext context, IRubyObject arg) {
  310.         Ruby runtime = context.runtime;
  311.         long value = getLongValue();
  312.         Encoding enc;
  313.         if (arg instanceof RubyEncoding) {
  314.             enc = ((RubyEncoding)arg).getEncoding();
  315.         } else {
  316.             enc =  arg.convertToString().toEncoding(runtime);
  317.         }
  318.         if (enc == ASCIIEncoding.INSTANCE && value >= 0x80) {
  319.             return chr19(context);
  320.         }
  321.         return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
  322.     }

  323.     private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, int value) {
  324.         int n;
  325.         try {
  326.             n = value < 0 ? 0 : enc.codeToMbcLength(value);
  327.         } catch (EncodingException ee) {
  328.             n = 0;
  329.         }

  330.         if (n <= 0) throw runtime.newRangeError(this.toString() + " out of char range");
  331.        
  332.         ByteList bytes = new ByteList(n);
  333.        
  334.         try {
  335.             enc.codeToMbc(value, bytes.getUnsafeBytes(), 0);
  336.         } catch (EncodingException e) {
  337.             throw runtime.newRangeError("invalid codepoint " + String.format("0x%x in ", value) + enc.getCharsetName());
  338.         }
  339.         bytes.setRealSize(n);
  340.         return bytes;
  341.     }

  342.     /** int_ord
  343.      *
  344.      */
  345.     @JRubyMethod(name = "ord")
  346.     public IRubyObject ord(ThreadContext context) {
  347.         return this;
  348.     }

  349.     /** int_to_i
  350.      *
  351.      */
  352.     @JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "truncate"})
  353.     public IRubyObject to_i() {
  354.         return this;
  355.     }

  356.     @Override
  357.     public IRubyObject round() {
  358.         return this;
  359.     }

  360.     @JRubyMethod(name = "round")
  361.     public IRubyObject round19() {
  362.         return this;
  363.     }

  364.     @JRubyMethod(name = "round")
  365.     public IRubyObject round19(ThreadContext context, IRubyObject arg) {
  366.         int ndigits = RubyNumeric.num2int(arg);
  367.         if (ndigits > 0) return RubyKernel.new_float(this, this);
  368.         if (ndigits == 0) return this;
  369.         Ruby runtime = context.runtime;
  370.        
  371.         long bytes = (this instanceof RubyFixnum) ? 8 : RubyFixnum.fix2long(callMethod("size"));
  372.         /* If 10**N/2 > this, return 0 */
  373.         /* We have log_256(10) > 0.415241 and log_256(1/2)=-0.125 */
  374.         if (-0.415241 * ndigits - 0.125 > bytes) {
  375.             return RubyFixnum.zero(runtime);
  376.         }
  377.        
  378.         IRubyObject f = Numeric.int_pow(context, 10, -ndigits);

  379.         if (this instanceof RubyFixnum && f instanceof RubyFixnum) {
  380.             long x = ((RubyFixnum)this).getLongValue();
  381.             long y = ((RubyFixnum)f).getLongValue();
  382.             boolean neg = x < 0;
  383.             if (neg) x = -x;
  384.             x = (x + y / 2) / y * y;
  385.             if (neg) x = -x;
  386.             return RubyFixnum.newFixnum(runtime, x);
  387.         } else if (f instanceof RubyFloat) {
  388.             return RubyFixnum.zero(runtime);
  389.         } else {
  390.             IRubyObject h = f.callMethod(context, "/", RubyFixnum.two(runtime));
  391.             IRubyObject r = callMethod(context, "%", f);
  392.             IRubyObject n = callMethod(context, "-", r);
  393.             String op = callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue() ? "<=" : "<";
  394.             if (!r.callMethod(context, op, h).isTrue()) n = n.callMethod(context, "+", f);
  395.             return n;
  396.         }
  397.     }

  398.     /** integer_to_r
  399.      *
  400.      */
  401.     @JRubyMethod(name = "to_r")
  402.     public IRubyObject to_r(ThreadContext context) {
  403.         return RubyRational.newRationalCanonicalize(context, this);
  404.     }

  405.     /** integer_rationalize
  406.      *
  407.      */
  408.     @JRubyMethod(name = "rationalize", optional = 1)
  409.     public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
  410.         return to_r(context);
  411.     }
  412.    

  413.     @JRubyMethod(name = "odd?")
  414.     public RubyBoolean odd_p(ThreadContext context) {
  415.         Ruby runtime = context.runtime;
  416.         if (callMethod(context, "%", RubyFixnum.two(runtime)) != RubyFixnum.zero(runtime)) {
  417.             return runtime.getTrue();
  418.         }
  419.         return runtime.getFalse();
  420.     }

  421.     @JRubyMethod(name = "even?")
  422.     public RubyBoolean even_p(ThreadContext context) {
  423.         Ruby runtime = context.runtime;
  424.         if (callMethod(context, "%", RubyFixnum.two(runtime)) == RubyFixnum.zero(runtime)) {
  425.             return runtime.getTrue();
  426.         }
  427.         return runtime.getFalse();
  428.     }

  429.     @JRubyMethod(name = "pred")
  430.     public IRubyObject pred(ThreadContext context) {
  431.         return callMethod(context, "-", RubyFixnum.one(context.runtime));
  432.     }

  433.     /** rb_gcd
  434.      *
  435.      */
  436.     @JRubyMethod(name = "gcd")
  437.     public IRubyObject gcd(ThreadContext context, IRubyObject other) {
  438.         checkInteger(context, other);
  439.         return f_gcd(context, this, RubyRational.intValue(context, other));
  440.     }    

  441.     /** rb_lcm
  442.      *
  443.      */
  444.     @JRubyMethod(name = "lcm")
  445.     public IRubyObject lcm(ThreadContext context, IRubyObject other) {
  446.         checkInteger(context, other);
  447.         return f_lcm(context, this, RubyRational.intValue(context, other));
  448.     }    

  449.     /** rb_gcdlcm
  450.      *
  451.      */
  452.     @JRubyMethod(name = "gcdlcm")
  453.     public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
  454.         checkInteger(context, other);
  455.         other = RubyRational.intValue(context, other);
  456.         return context.runtime.newArray(f_gcd(context, this, other), f_lcm(context, this, other));
  457.     }

  458.     @Override
  459.     @JRubyMethod(name = "numerator")
  460.     public IRubyObject numerator(ThreadContext context) {
  461.         return this;
  462.     }

  463.     @Override
  464.     @JRubyMethod(name = "denominator")
  465.     public IRubyObject denominator(ThreadContext context) {
  466.         return RubyFixnum.one(context.runtime);
  467.     }

  468.     /*  ================
  469.      *  Singleton Methods
  470.      *  ================
  471.      */

  472.     /** rb_int_induced_from
  473.      *
  474.      */
  475.     @Deprecated
  476.     public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
  477.         if (other instanceof RubyFixnum || other instanceof RubyBignum) {
  478.             return other;
  479.         } else if (other instanceof RubyFloat || other instanceof RubyRational) {
  480.             return other.callMethod(context, "to_i");
  481.         } else {
  482.             throw recv.getRuntime().newTypeError(
  483.                     "failed to convert " + other.getMetaClass().getName() + " into Integer");
  484.         }
  485.     }
  486. }