org.jruby.RubyInteger Maven / Gradle / Ivy
/*
**** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common 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/cpl-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
* Copyright (C) 2001-2004 Jan Arne Petersen
* Copyright (C) 2002 Anders Bengtsson
* Copyright (C) 2002 Benoit Cerrina
* Copyright (C) 2002-2004 Thomas E Enebo
* Copyright (C) 2004 Stefan Matthias Aust
* Copyright (C) 2005 Charles O Nutter
*
* 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 CPL, 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 CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;
import static org.jruby.RubyEnumerator.enumeratorize;
import static org.jruby.util.Numeric.checkInteger;
import static org.jruby.util.Numeric.f_gcd;
import static org.jruby.util.Numeric.f_lcm;
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;
/** 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.index = ClassIndex.INTEGER;
integer.setReifiedClass(RubyInteger.class);
integer.kindOf = new RubyModule.KindOf() {
public boolean isKindOf(IRubyObject obj, RubyModule type) {
return obj instanceof RubyInteger;
}
};
integer.getSingletonClass().undefineMethod("new");
if (!runtime.is1_9()) {
integer.includeModule(runtime.getPrecision());
}
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);
}
public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace, boolean canBeTainted) {
super(runtime, rubyClass, useObjectSpace, canBeTainted);
}
public RubyInteger convertToInteger() {
return this;
}
// conversion
protected RubyFloat toFloat() {
return RubyFloat.newFloat(getRuntime(), getDoubleValue());
}
/* ================
* Instance Methods
* ================
*/
/** int_int_p
*
*/
@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 enumeratorize(context.getRuntime(), this, "upto", to);
}
}
private static void fixnumUpto(ThreadContext context, long from, long to, Block block) {
Ruby runtime = context.getRuntime();
if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
IRubyObject nil = runtime.getNil();
for (long i = from; i <= to; i++) {
block.yield(context, nil);
}
} else {
for (long i = from; i <= to; i++) {
block.yield(context, RubyFixnum.newFixnum(runtime, i));
}
}
}
private static void duckUpto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
Ruby runtime = context.getRuntime();
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);
}
}
/** 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 enumeratorize(context.getRuntime(), this, "downto", to);
}
}
private static void fixnumDownto(ThreadContext context, long from, long to, Block block) {
Ruby runtime = context.getRuntime();
if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
final IRubyObject nil = runtime.getNil();
for (long i = from; i >= to; i--) {
block.yield(context, nil);
}
} else {
for (long i = from; i >= to; i--) {
block.yield(context, RubyFixnum.newFixnum(runtime, i));
}
}
}
private static void duckDownto(ThreadContext context, IRubyObject from, IRubyObject to, Block block) {
Ruby runtime = context.getRuntime();
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);
}
}
@JRubyMethod
public IRubyObject times(ThreadContext context, Block block) {
if (block.isGiven()) {
Ruby runtime = context.getRuntime();
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 enumeratorize(context.getRuntime(), this, "times");
}
}
/** int_succ
*
*/
@JRubyMethod(name = {"succ", "next"})
public IRubyObject succ(ThreadContext context) {
if (this instanceof RubyFixnum) {
return RubyFixnum.newFixnum(context.getRuntime(), getLongValue() + 1L);
} else {
return callMethod(context, "+", RubyFixnum.one(context.getRuntime()));
}
}
static final ByteList[] SINGLE_CHAR_BYTELISTS;
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
*
*/
@JRubyMethod(name = "chr", compat = CompatVersion.RUBY1_8)
public RubyString chr(ThreadContext context) {
Ruby runtime = context.getRuntime();
long value = getLongValue();
if (value < 0 || value > 0xff) throw runtime.newRangeError(this.toString() + " out of char range");
return RubyString.newStringShared(runtime, SINGLE_CHAR_BYTELISTS[(int)value]);
}
@JRubyMethod(name = "chr", compat = CompatVersion.RUBY1_9)
public RubyString chr19(ThreadContext context) {
Ruby runtime = context.getRuntime();
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", compat = CompatVersion.RUBY1_9)
public RubyString chr19(ThreadContext context, IRubyObject arg) {
Ruby runtime = context.getRuntime();
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);
enc.codeToMbc(value, bytes.getUnsafeBytes(), 0);
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;
}
@JRubyMethod(name = "round", compat = CompatVersion.RUBY1_8)
public IRubyObject round() {
return this;
}
@JRubyMethod(name = "round", compat = CompatVersion.RUBY1_9)
public IRubyObject round19() {
return this;
}
@JRubyMethod(name = "round", compat = CompatVersion.RUBY1_9)
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;
ndigits = -ndigits;
Ruby runtime = context.getRuntime();
if (ndigits < 0) throw runtime.newArgumentError("ndigits out of range");
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 {
IRubyObject h = f.callMethod(context, "/", RubyFixnum.two(runtime));
IRubyObject r = callMethod(context, "%", f);
IRubyObject n = callMethod(context, "-", r);
if (!r.callMethod(context, "<", h).isTrue()) n = n.callMethod(context, "+", f);
return n;
}
}
/** integer_to_r
*
*/
@JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
public IRubyObject to_r(ThreadContext context) {
return RubyRational.newRationalCanonicalize(context, this);
}
/** integer_rationalize
*
*/
@JRubyMethod(name = "rationalize", optional = 1, compat = CompatVersion.RUBY1_9)
public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
return to_r(context);
}
@JRubyMethod(name = "odd?")
public RubyBoolean odd_p(ThreadContext context) {
Ruby runtime = context.getRuntime();
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.getRuntime();
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.getRuntime()));
}
/** rb_gcd
*
*/
@JRubyMethod(name = "gcd", compat = CompatVersion.RUBY1_9)
public IRubyObject gcd(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_gcd(context, this, RubyRational.intValue(context, other));
}
/** rb_lcm
*
*/
@JRubyMethod(name = "lcm", compat = CompatVersion.RUBY1_9)
public IRubyObject lcm(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_lcm(context, this, RubyRational.intValue(context, other));
}
/** rb_gcdlcm
*
*/
@JRubyMethod(name = "gcdlcm", compat = CompatVersion.RUBY1_9)
public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
other = RubyRational.intValue(context, other);
return context.getRuntime().newArray(f_gcd(context, this, other), f_lcm(context, this, other));
}
@JRubyMethod(name = "numerator", compat = CompatVersion.RUBY1_9)
public IRubyObject numerator(ThreadContext context) {
return this;
}
@JRubyMethod(name = "denominator", compat = CompatVersion.RUBY1_9)
public IRubyObject denominator(ThreadContext context) {
return RubyFixnum.one(context.getRuntime());
}
/* ================
* Singleton Methods
* ================
*/
/** rb_int_induced_from
*
*/
@JRubyMethod(name = "induced_from", meta = true, compat = CompatVersion.RUBY1_8)
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");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy