RubyInteger.java
- /*
- **** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jcodings.Encoding;
- import org.jcodings.exception.EncodingException;
- import org.jcodings.specific.ASCIIEncoding;
- import org.jcodings.specific.USASCIIEncoding;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.BlockBody;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.ByteList;
- import org.jruby.util.Numeric;
- import org.jruby.util.StringSupport;
- import static org.jruby.RubyEnumerator.enumeratorizeWithSize;
- import static org.jruby.util.Numeric.checkInteger;
- import static org.jruby.util.Numeric.f_gcd;
- import static org.jruby.util.Numeric.f_lcm;
- import static org.jruby.RubyEnumerator.SizeFn;
- /** Implementation of the Integer class.
- *
- * @author jpetersen
- */
- @JRubyClass(name="Integer", parent="Numeric", include="Precision")
- public abstract class RubyInteger extends RubyNumeric {
- public static RubyClass createIntegerClass(Ruby runtime) {
- RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
- ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setInteger(integer);
- integer.setClassIndex(ClassIndex.INTEGER);
- integer.setReifiedClass(RubyInteger.class);
-
- integer.kindOf = new RubyModule.JavaClassKindOf(RubyInteger.class);
- integer.getSingletonClass().undefineMethod("new");
- integer.defineAnnotatedMethods(RubyInteger.class);
-
- return integer;
- }
- public RubyInteger(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
- }
- public RubyInteger(RubyClass rubyClass) {
- super(rubyClass);
- }
-
- public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
- super(runtime, rubyClass, useObjectSpace);
- }
- @Deprecated
- public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace, boolean canBeTainted) {
- super(runtime, rubyClass, useObjectSpace, canBeTainted);
- }
- @Override
- public RubyInteger convertToInteger() {
- return this;
- }
- // conversion
- protected RubyFloat toFloat() {
- return RubyFloat.newFloat(getRuntime(), getDoubleValue());
- }
- /* ================
- * Instance Methods
- * ================
- */
- /** int_int_p
- *
- */
- @Override
- @JRubyMethod(name = "integer?")
- public IRubyObject integer_p() {
- return getRuntime().getTrue();
- }
- /** int_upto
- *
- */
- @JRubyMethod
- public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
- if (block.isGiven()) {
- if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
- fixnumUpto(context, ((RubyFixnum)this).getLongValue(), ((RubyFixnum)to).getLongValue(), block);
- } else {
- duckUpto(context, this, to, block);
- }
- return this;
- } else {
- return enumeratorizeWithSize(context, this, "upto", new IRubyObject[] { to }, uptoSize(context, this, to));
- }
- }
- private static void fixnumUpto(ThreadContext context, long from, long to, Block block) {
- // We must avoid "i++" integer overflow when (to == Long.MAX_VALUE).
- Ruby runtime = context.runtime;
- if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
- IRubyObject nil = runtime.getNil();
- long i;
- for (i = from; i < to; i++) {
- block.yield(context, nil);
- }
- if (i <= to) {
- block.yield(context, nil);
- }
- } else {
- long i;
- for (i = from; i < to; i++) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- if (i <= to) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- }
- }
- private static void duckUpto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
- Ruby runtime = context.runtime;
- IRubyObject i = from;
- RubyFixnum one = RubyFixnum.one(runtime);
- while (true) {
- if (i.callMethod(context, ">", to).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = i.callMethod(context, "+", one);
- }
- }
- private static SizeFn uptoSize(final ThreadContext context, final IRubyObject from, final IRubyObject to) {
- return new SizeFn() {
- @Override
- public IRubyObject size(IRubyObject[] args) {
- return intervalStepSize(context, from, to, RubyFixnum.one(context.runtime), false);
- }
- };
- }
- /** int_downto
- *
- */
- // TODO: Make callCoerced work in block context...then fix downto, step, and upto.
- @JRubyMethod
- public IRubyObject downto(ThreadContext context, IRubyObject to, Block block) {
- if (block.isGiven()) {
- if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
- fixnumDownto(context, ((RubyFixnum)this).getLongValue(), ((RubyFixnum)to).getLongValue(), block);
- } else {
- duckDownto(context, this, to, block);
- }
- return this;
- } else {
- return enumeratorizeWithSize(context, this, "downto", new IRubyObject[] { to }, downToSize(context, this, to));
- }
- }
- private static void fixnumDownto(ThreadContext context, long from, long to, Block block) {
- // We must avoid "i--" integer overflow when (to == Long.MIN_VALUE).
- Ruby runtime = context.runtime;
- if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
- IRubyObject nil = runtime.getNil();
- long i;
- for (i = from; i > to; i--) {
- block.yield(context, nil);
- }
- if (i >= to) {
- block.yield(context, nil);
- }
- } else {
- long i;
- for (i = from; i > to; i--) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- if (i >= to) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- }
- }
- private static void duckDownto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
- Ruby runtime = context.runtime;
- IRubyObject i = from;
- RubyFixnum one = RubyFixnum.one(runtime);
- while (true) {
- if (i.callMethod(context, "<", to).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = i.callMethod(context, "-", one);
- }
- }
- private static SizeFn downToSize(final ThreadContext context, final IRubyObject from, final IRubyObject to) {
- return new SizeFn() {
- @Override
- public IRubyObject size(IRubyObject[] args) {
- return intervalStepSize(context, from, to, RubyFixnum.newFixnum(context.runtime, -1), false);
- }
- };
- }
- @JRubyMethod
- public IRubyObject times(ThreadContext context, Block block) {
- if (block.isGiven()) {
- Ruby runtime = context.runtime;
- IRubyObject i = RubyFixnum.zero(runtime);
- RubyFixnum one = RubyFixnum.one(runtime);
- while (true) {
- if (!i.callMethod(context, "<", this).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = i.callMethod(context, "+", one);
- }
- return this;
- } else {
- return enumeratorizeWithSize(context, this, "times", timesSizeFn(context.runtime));
- }
- }
- protected SizeFn timesSizeFn(final Ruby runtime) {
- final RubyInteger self = this;
- return new SizeFn() {
- @Override
- public IRubyObject size(IRubyObject[] args) {
- RubyFixnum zero = RubyFixnum.zero(runtime);
- if ((self instanceof RubyFixnum && getLongValue() < 0)
- || self.callMethod("<", zero).isTrue()) {
- return zero;
- }
- return self;
- }
- };
- }
- /** int_succ
- *
- */
- @JRubyMethod(name = {"succ", "next"})
- public IRubyObject succ(ThreadContext context) {
- if (this instanceof RubyFixnum) {
- return ((RubyFixnum) this).op_plus_one(context);
- } else {
- return callMethod(context, "+", RubyFixnum.one(context.runtime));
- }
- }
- static final ByteList[] SINGLE_CHAR_BYTELISTS;
- public static final ByteList[] SINGLE_CHAR_BYTELISTS19;
- static {
- SINGLE_CHAR_BYTELISTS = new ByteList[256];
- SINGLE_CHAR_BYTELISTS19 = new ByteList[256];
- for (int i = 0; i < 256; i++) {
- ByteList usascii = new ByteList(new byte[]{(byte)i}, false);
- SINGLE_CHAR_BYTELISTS[i] = usascii;
- SINGLE_CHAR_BYTELISTS19[i] = i < 0x80 ?
- new ByteList(new byte[]{(byte)i}, USASCIIEncoding.INSTANCE)
- :
- new ByteList(
- new byte[]{(byte)i},
- ASCIIEncoding.INSTANCE);
- }
- }
- /** int_chr
- *
- */
- public RubyString chr(ThreadContext context) {
- return chr19(context);
- }
- @JRubyMethod(name = "chr")
- public RubyString chr19(ThreadContext context) {
- Ruby runtime = context.runtime;
- int value = (int)getLongValue();
- if (value >= 0 && value <= 0xFF) {
- ByteList bytes = SINGLE_CHAR_BYTELISTS19[value];
- return RubyString.newStringShared(runtime, bytes, bytes.getEncoding());
- } else {
- Encoding enc = runtime.getDefaultInternalEncoding();
- if (value > 0xFF && (enc == null || enc == ASCIIEncoding.INSTANCE)) {
- throw runtime.newRangeError(this.toString() + " out of char range");
- } else {
- if (enc == null) enc = USASCIIEncoding.INSTANCE;
- return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
- }
- }
- }
- @JRubyMethod(name = "chr")
- public RubyString chr19(ThreadContext context, IRubyObject arg) {
- Ruby runtime = context.runtime;
- long value = getLongValue();
- Encoding enc;
- if (arg instanceof RubyEncoding) {
- enc = ((RubyEncoding)arg).getEncoding();
- } else {
- enc = arg.convertToString().toEncoding(runtime);
- }
- if (enc == ASCIIEncoding.INSTANCE && value >= 0x80) {
- return chr19(context);
- }
- return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
- }
- private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, int value) {
- int n;
- try {
- n = value < 0 ? 0 : enc.codeToMbcLength(value);
- } catch (EncodingException ee) {
- n = 0;
- }
- if (n <= 0) throw runtime.newRangeError(this.toString() + " out of char range");
-
- ByteList bytes = new ByteList(n);
-
- try {
- enc.codeToMbc(value, bytes.getUnsafeBytes(), 0);
- } catch (EncodingException e) {
- throw runtime.newRangeError("invalid codepoint " + String.format("0x%x in ", value) + enc.getCharsetName());
- }
- bytes.setRealSize(n);
- return bytes;
- }
- /** int_ord
- *
- */
- @JRubyMethod(name = "ord")
- public IRubyObject ord(ThreadContext context) {
- return this;
- }
- /** int_to_i
- *
- */
- @JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "truncate"})
- public IRubyObject to_i() {
- return this;
- }
- @Override
- public IRubyObject round() {
- return this;
- }
- @JRubyMethod(name = "round")
- public IRubyObject round19() {
- return this;
- }
- @JRubyMethod(name = "round")
- public IRubyObject round19(ThreadContext context, IRubyObject arg) {
- int ndigits = RubyNumeric.num2int(arg);
- if (ndigits > 0) return RubyKernel.new_float(this, this);
- if (ndigits == 0) return this;
- Ruby runtime = context.runtime;
-
- long bytes = (this instanceof RubyFixnum) ? 8 : RubyFixnum.fix2long(callMethod("size"));
- /* If 10**N/2 > this, return 0 */
- /* We have log_256(10) > 0.415241 and log_256(1/2)=-0.125 */
- if (-0.415241 * ndigits - 0.125 > bytes) {
- return RubyFixnum.zero(runtime);
- }
-
- IRubyObject f = Numeric.int_pow(context, 10, -ndigits);
- if (this instanceof RubyFixnum && f instanceof RubyFixnum) {
- long x = ((RubyFixnum)this).getLongValue();
- long y = ((RubyFixnum)f).getLongValue();
- boolean neg = x < 0;
- if (neg) x = -x;
- x = (x + y / 2) / y * y;
- if (neg) x = -x;
- return RubyFixnum.newFixnum(runtime, x);
- } else if (f instanceof RubyFloat) {
- return RubyFixnum.zero(runtime);
- } else {
- IRubyObject h = f.callMethod(context, "/", RubyFixnum.two(runtime));
- IRubyObject r = callMethod(context, "%", f);
- IRubyObject n = callMethod(context, "-", r);
- String op = callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue() ? "<=" : "<";
- if (!r.callMethod(context, op, h).isTrue()) n = n.callMethod(context, "+", f);
- return n;
- }
- }
- /** integer_to_r
- *
- */
- @JRubyMethod(name = "to_r")
- public IRubyObject to_r(ThreadContext context) {
- return RubyRational.newRationalCanonicalize(context, this);
- }
- /** integer_rationalize
- *
- */
- @JRubyMethod(name = "rationalize", optional = 1)
- public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
- return to_r(context);
- }
-
- @JRubyMethod(name = "odd?")
- public RubyBoolean odd_p(ThreadContext context) {
- Ruby runtime = context.runtime;
- if (callMethod(context, "%", RubyFixnum.two(runtime)) != RubyFixnum.zero(runtime)) {
- return runtime.getTrue();
- }
- return runtime.getFalse();
- }
- @JRubyMethod(name = "even?")
- public RubyBoolean even_p(ThreadContext context) {
- Ruby runtime = context.runtime;
- if (callMethod(context, "%", RubyFixnum.two(runtime)) == RubyFixnum.zero(runtime)) {
- return runtime.getTrue();
- }
- return runtime.getFalse();
- }
- @JRubyMethod(name = "pred")
- public IRubyObject pred(ThreadContext context) {
- return callMethod(context, "-", RubyFixnum.one(context.runtime));
- }
- /** rb_gcd
- *
- */
- @JRubyMethod(name = "gcd")
- public IRubyObject gcd(ThreadContext context, IRubyObject other) {
- checkInteger(context, other);
- return f_gcd(context, this, RubyRational.intValue(context, other));
- }
- /** rb_lcm
- *
- */
- @JRubyMethod(name = "lcm")
- public IRubyObject lcm(ThreadContext context, IRubyObject other) {
- checkInteger(context, other);
- return f_lcm(context, this, RubyRational.intValue(context, other));
- }
- /** rb_gcdlcm
- *
- */
- @JRubyMethod(name = "gcdlcm")
- public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
- checkInteger(context, other);
- other = RubyRational.intValue(context, other);
- return context.runtime.newArray(f_gcd(context, this, other), f_lcm(context, this, other));
- }
- @Override
- @JRubyMethod(name = "numerator")
- public IRubyObject numerator(ThreadContext context) {
- return this;
- }
- @Override
- @JRubyMethod(name = "denominator")
- public IRubyObject denominator(ThreadContext context) {
- return RubyFixnum.one(context.runtime);
- }
- /* ================
- * Singleton Methods
- * ================
- */
- /** rb_int_induced_from
- *
- */
- @Deprecated
- public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
- if (other instanceof RubyFixnum || other instanceof RubyBignum) {
- return other;
- } else if (other instanceof RubyFloat || other instanceof RubyRational) {
- return other.callMethod(context, "to_i");
- } else {
- throw recv.getRuntime().newTypeError(
- "failed to convert " + other.getMetaClass().getName() + " into Integer");
- }
- }
- }