Skip to content

Commit

Permalink
Showing 7 changed files with 199 additions and 65 deletions.
60 changes: 41 additions & 19 deletions core/src/main/java/org/jruby/RubyBignum.java
Original file line number Diff line number Diff line change
@@ -547,7 +547,7 @@ public IRubyObject op_div(ThreadContext context, IRubyObject other) {
*
*/
@Override
public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
public IRubyObject idiv(ThreadContext context, IRubyObject other) {
return op_divide(context, other, false);
}

@@ -683,17 +683,32 @@ public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
Ruby runtime = context.runtime;
if (other == RubyFixnum.zero(runtime)) return RubyFixnum.one(runtime);
final double d;
if (other instanceof RubyFixnum) {
return op_pow(context, ((RubyFixnum) other).getLongValue());
} else if (other instanceof RubyBignum) {
d = ((RubyBignum) other).getDoubleValue();
context.runtime.getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big");
} else if (other instanceof RubyFloat) {
if (other instanceof RubyFloat) {
d = ((RubyFloat) other).getDoubleValue();
if (compareTo(RubyFixnum.zero(runtime)) == -1 && d != Math.round(d)) {
RubyComplex complex = RubyComplex.newComplexRaw(context.runtime, this);
return sites(context).op_exp.call(context, complex, complex, other);
}
} else if (other instanceof RubyBignum) {
d = ((RubyBignum) other).getDoubleValue();
context.runtime.getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big");
} else if (other instanceof RubyFixnum) {
long yy = other.convertToInteger().getLongValue();
if (yy < 0)
return RubyRational.newRationalRaw(runtime, this).op_expt(context, other);
else {
int xbits = value.bitLength();
int BIGLEN_LIMIT = 32*1024*1024;

if ((xbits > BIGLEN_LIMIT) ||
(xbits * yy > BIGLEN_LIMIT)) {
runtime.getWarnings().warn("in a**b, b may be too big");
d = (double)yy;
}
else {
return newBignum(runtime, value.pow((int)yy));
}
}
} else {
return coerceBin(context, sites(context).op_exp, other);
}
@@ -704,18 +719,6 @@ public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
return RubyNumeric.dbl2ival(runtime, pow);
}

public final IRubyObject op_pow(final ThreadContext context, final long other) {
if (other >= 0) {
if (other <= Integer.MAX_VALUE) { // only have BigInteger#pow(int)
return bignorm(context.runtime, value.pow((int) other)); // num2int is also implemented
}
warnIfPowExponentTooBig(context, other);
return RubyFloat.newFloat(context.runtime, Math.pow(big2dbl(this), (double) other));
}
// (other < 0)
return RubyRational.newRationalRaw(context.runtime, this).op_expt(context, other);
}

private void warnIfPowExponentTooBig(final ThreadContext context, final long other) {
// MRI issuses warning here on (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024)
if ( ((value.bitLength() + 7) / 8) * 4 * Math.abs((double) other) > 1024 * 1024 ) {
@@ -1087,6 +1090,25 @@ public IRubyObject isPositive(ThreadContext context) {
return sites(context).basic_op_gt.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
protected boolean int_round_zero_p(ThreadContext context, int ndigits) {
long bytes = value.bitLength() / 8 + 1;
return (-0.415241 * ndigits - 0.125 > bytes);
}

@Deprecated
public final IRubyObject op_pow(final ThreadContext context, final long other) {
if (other >= 0) {
if (other <= Integer.MAX_VALUE) { // only have BigInteger#pow(int)
return bignorm(context.runtime, value.pow((int) other)); // num2int is also implemented
}
warnIfPowExponentTooBig(context, other);
return RubyFloat.newFloat(context.runtime, Math.pow(big2dbl(this), (double) other));
}
// (other < 0)
return RubyRational.newRationalRaw(context.runtime, this).op_expt(context, other);
}

private static JavaSites.BignumSites sites(ThreadContext context) {
return context.sites.Bignum;
}
8 changes: 7 additions & 1 deletion core/src/main/java/org/jruby/RubyFixnum.java
Original file line number Diff line number Diff line change
@@ -642,7 +642,7 @@ public IRubyObject op_mul(ThreadContext context, long otherValue) {
* also note that RubyFloat doesn't override Numeric.div
*/
@Override
public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
public IRubyObject idiv(ThreadContext context, IRubyObject other) {
checkZeroDivisionError(context, other);

return idiv(context, other, sites(context).div);
@@ -1336,6 +1336,12 @@ public IRubyObject isPositive(ThreadContext context) {
return sites(context).basic_op_gt.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
protected boolean int_round_zero_p(ThreadContext context, int ndigits) {
long bytes = 8; // sizeof(long)
return (-0.415241 * ndigits - 0.125 > bytes);
}

private static JavaSites.FixnumSites sites(ThreadContext context) {
return context.sites.Fixnum;
}
154 changes: 120 additions & 34 deletions core/src/main/java/org/jruby/RubyInteger.java
Original file line number Diff line number Diff line change
@@ -40,24 +40,22 @@
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.ast.util.ArgsUtil;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.Signature;
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 java.math.RoundingMode;

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;
import static org.jruby.util.Numeric.*;

/** Implementation of the Integer class.
*
@@ -438,45 +436,127 @@ public IRubyObject round(ThreadContext context) {
}

@JRubyMethod(name = "round")
public IRubyObject round(ThreadContext context, IRubyObject arg) {
int ndigits = RubyNumeric.num2int(arg);
return round(context, ndigits);
public IRubyObject round(ThreadContext context, IRubyObject _digits) {
return round(context, _digits, context.nil);
}

@JRubyMethod(name = "round")
public IRubyObject round(ThreadContext context, IRubyObject _digits, IRubyObject _opts) {
Ruby runtime = context.runtime;
int ndigits = 0;

// options (only "half" right now)
IRubyObject opts = ArgsUtil.getOptionsArg(runtime, _opts);
ndigits = num2int(_digits);

RoundingMode roundingMode = getRoundingMode(context, opts);

if (ndigits > 0) {
return RubyKernel.new_float19(runtime.getFloat(), this);
}
if (ndigits == 0) {
return this;
}

return roundShared(context, ndigits, roundingMode);
}

public IRubyObject round(ThreadContext context, int ndigits) {
if (ndigits > 0) return RubyKernel.new_float(this, this);
if (ndigits == 0) return this;
return roundShared(context, ndigits, RoundingMode.HALF_UP);
}

/*
* MRI: rb_int_round
*/
protected IRubyObject roundShared(ThreadContext context, int ndigits, RoundingMode roundingMode) {
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) {
RubyNumeric f, h, n, r;

if (int_round_zero_p(context, ndigits)) {
return RubyFixnum.zero(runtime);
}

IRubyObject f = Numeric.int_pow(context, 10, -ndigits);

f = (RubyNumeric) int_pow(context, 10, -ndigits);
if (this instanceof RubyFixnum && f instanceof RubyFixnum) {
long x = ((RubyFixnum)this).getLongValue();
long y = ((RubyFixnum)f).getLongValue();
long x = fix2long(this), y = fix2long(f);
boolean neg = x < 0;
if (neg) x = -x;
x = (x + y / 2) / y * y;
x = doRound(context, roundingMode, x, y);
if (neg) x = -x;
return RubyFixnum.newFixnum(runtime, x);
} else if (f instanceof RubyFloat) {
}
if (f instanceof RubyFloat) {
/* then int_pow overflow */
return RubyFixnum.zero(runtime);
} else {
IRubyObject h = sites(context).op_quo.call(context, f, f, RubyFixnum.two(runtime));
IRubyObject r = sites(context).op_mod.call(context, this, this, f);
IRubyObject n = sites(context).op_minus.call(context, this, this, r);
CallSite op = sites(context).op_lt.call(context, this, this, RubyFixnum.zero(runtime)).isTrue()
? sites(context).op_le
: sites(context).op_lt;
if (!op.call(context, r, r, h).isTrue()) n = sites(context).op_plus.call(context, n, n, f);
return n;
}
h = (RubyNumeric) f.idiv(context, int2fix(runtime, 2));
r = (RubyNumeric) this.op_mod(context, f);
n = (RubyNumeric) this.op_minus(context, r);
r = (RubyNumeric) r.op_cmp(context, h);
if (r.isPositive(context).isTrue() ||
(r.zero_p(context).isTrue() && doRoundCheck(context, roundingMode, this, n, f))) {
n = (RubyNumeric) n.op_plus(context, f);
}
return n;
}

private static long doRound(ThreadContext context, RoundingMode roundingMode, long n, long f) {
switch (roundingMode) {
case HALF_UP:
return int_round_half_up(n, f);
case HALF_DOWN:
return int_round_half_down(n, f);
case HALF_EVEN:
return int_round_half_even(n, f);
}
throw context.runtime.newArgumentError("invalid rounding mode: " + roundingMode);
}

private static boolean doRoundCheck(ThreadContext context, RoundingMode roundingMode, RubyInteger num, RubyNumeric n, IRubyObject f) {
switch (roundingMode) {
case HALF_UP:
return int_half_p_half_up(context, num, n, f);
case HALF_DOWN:
return int_half_p_half_down(context, num, n, f);
case HALF_EVEN:
return int_half_p_half_even(context, num, n, f);
}
throw context.runtime.newArgumentError("invalid rounding mode: " + roundingMode);
}

protected boolean int_round_zero_p(ThreadContext context, int ndigits) {
long bytes = num2long(sites(context).size.call(context, this, this));
return (-0.415241 * ndigits - 0.125 > bytes);
}

protected static long int_round_half_even(long x, long y)
{
long z = +(x + y / 2) / y;
if ((z * y - x) * 2 == y) {
z &= ~1;
}
return z * y;
}

protected static long int_round_half_up(long x, long y) {
return (x + y / 2) / y * y;
}

protected static long int_round_half_down(long x, long y) {
return (x + y / 2 - 1) / y * y;
}

protected static boolean int_half_p_half_even(ThreadContext context, RubyInteger num, RubyNumeric n, IRubyObject f) {
return n.div(context, f).convertToInteger().odd_p(context).isTrue();
}

protected static boolean int_half_p_half_up(ThreadContext context, RubyInteger num, RubyNumeric n, IRubyObject f) {
return num.isPositive(context).isTrue();
}

protected static boolean int_half_p_half_down(ThreadContext context, RubyInteger num, RubyNumeric n, IRubyObject f) {
return num.isNegative(context).isTrue();
}

/** integer_to_r
@@ -535,8 +615,8 @@ public IRubyObject fdiv(ThreadContext context, IRubyObject y) {
if (y instanceof RubyInteger && !((RubyInteger) y).zero_p(context).isTrue()) {
IRubyObject gcd = gcd(context, y);
if (!((RubyInteger) gcd).zero_p(context).isTrue()) {
x = (RubyInteger) op_idiv(context, gcd);
y = ((RubyInteger) y).op_idiv(context, gcd);
x = (RubyInteger) div(context, gcd);
y = ((RubyInteger) y).div(context, gcd);
}
}
return x.fdivDouble(context, y);
@@ -604,10 +684,11 @@ public IRubyObject denominator(ThreadContext context) {

// MRI: rb_int_idiv, polymorphism handles fixnum vs bignum
@JRubyMethod(name = "div")
public abstract IRubyObject op_idiv(ThreadContext context, IRubyObject other);
@Override
public abstract IRubyObject idiv(ThreadContext context, IRubyObject other);

public final IRubyObject div_div(ThreadContext context, IRubyObject other) {
return op_idiv(context, other);
return div(context, other);
}

@JRubyMethod(name = "/")
@@ -761,4 +842,9 @@ public final IRubyObject round19() {
public final IRubyObject round19(ThreadContext context, IRubyObject arg) {
return round(context, arg);
}

@Deprecated
public final IRubyObject op_idiv(ThreadContext context, IRubyObject arg) {
return div(context, arg);
}
}
16 changes: 14 additions & 2 deletions core/src/main/java/org/jruby/RubyNumeric.java
Original file line number Diff line number Diff line change
@@ -728,6 +728,11 @@ public IRubyObject op_uminus(ThreadContext context) {
return numFuncall(context, car, sites(context).op_minus, ary.eltInternal(1));
}

// MRI: rb_int_plus and others, handled by polymorphism
public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
return coerceBin(context, sites(context).op_plus, other);
}

/** num_cmp
*
*/
@@ -761,8 +766,8 @@ public final IRubyObject quo_19(ThreadContext context, IRubyObject other) {
return quo(context, other);
}

/** num_div
*
/**
* MRI: num_div
*/
@JRubyMethod(name = "div")
public IRubyObject div(ThreadContext context, IRubyObject other) {
@@ -776,6 +781,13 @@ public IRubyObject div(ThreadContext context, IRubyObject other) {
return sites(context).floor.call(context, quotient, quotient);
}

/**
* MRI: rb_int_idiv and overrides
*/
public IRubyObject idiv(ThreadContext context, IRubyObject other) {
return div(context, other);
}

/** num_divmod
*
*/
12 changes: 7 additions & 5 deletions core/src/main/java/org/jruby/RubyRational.java
Original file line number Diff line number Diff line change
@@ -752,12 +752,9 @@ public IRubyObject op_coerce(ThreadContext context, IRubyObject other) {
/** nurat_idiv
*
*/
public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
return op_idiv19(context, other);
}

@JRubyMethod(name = "div")
public IRubyObject op_idiv19(ThreadContext context, IRubyObject other) {
@Override
public IRubyObject idiv(ThreadContext context, IRubyObject other) {
if (num2dbl(other) == 0.0) throw context.runtime.newZeroDivisionError();

return f_floor(context, f_div(context, this, other));
@@ -1345,6 +1342,11 @@ public IRubyObject op_ceil(ThreadContext context, IRubyObject n) {
return ceil(context, n);
}

@Deprecated
public IRubyObject op_idiv19(ThreadContext context, IRubyObject other) {
return idiv(context, other);
}

private static JavaSites.RationalSites sites(ThreadContext context) {
return context.sites.Rational;
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/runtime/JavaSites.java
Original file line number Diff line number Diff line change
@@ -205,6 +205,7 @@ public static class IntegerSites {
public final CallSite op_minus = new FunctionalCachingCallSite("-");
public final CallSite op_quo = new FunctionalCachingCallSite("/");
public final CallSite op_mod = new FunctionalCachingCallSite("%");
public final CallSite size = new FunctionalCachingCallSite("size");
}

public static class FixnumSites {
13 changes: 9 additions & 4 deletions core/src/main/java/org/jruby/util/Numeric.java
Original file line number Diff line number Diff line change
@@ -370,6 +370,11 @@ public static boolean f_odd_p(ThreadContext context, IRubyObject integer) {
return (((RubyFixnum) sites(context).op_mod.call(context, integer, integer, RubyFixnum.two(runtime))).getLongValue() != 0);
}

/**
* MRI: int_odd_p
*/


/** i_gcd
*
*/
@@ -491,17 +496,17 @@ public static IRubyObject int_pow(ThreadContext context, long x, long y) {
do {
while (y % 2 == 0) {
if (!fitSqrtLong(x)) {
IRubyObject v = RubyBignum.newBignum(runtime, RubyBignum.fix2big(RubyFixnum.newFixnum(runtime, x))).op_pow(context, RubyFixnum.newFixnum(runtime, y));
if (z != 1) v = RubyBignum.newBignum(runtime, RubyBignum.fix2big(RubyFixnum.newFixnum(runtime, neg ? -z : z))).op_mul(context, v);
IRubyObject v = RubyBignum.newBignum(runtime, x).op_pow(context, RubyFixnum.newFixnum(runtime, y));
if (z != 1) v = RubyBignum.newBignum(runtime, neg ? -z : z).op_mul(context, v);
return v;
}
x *= x;
y >>= 1;
}

if (multiplyOverflows(x, z)) {
IRubyObject v = RubyBignum.newBignum(runtime, RubyBignum.fix2big(RubyFixnum.newFixnum(runtime, x))).op_pow(context, RubyFixnum.newFixnum(runtime, y));
if (z != 1) v = RubyBignum.newBignum(runtime, RubyBignum.fix2big(RubyFixnum.newFixnum(runtime, neg ? -z : z))).op_mul(context, v);
IRubyObject v = RubyBignum.newBignum(runtime, x).op_pow(context, RubyFixnum.newFixnum(runtime, y));
if (z != 1) v = RubyBignum.newBignum(runtime, neg ? -z : z).op_mul(context, v);
return v;
}
z = x * z;

0 comments on commit 63ea2ac

Please sign in to comment.