Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,11 @@ public boolean isImmediate() {
return false;
}

@Override
public boolean isSpecialConst() {
return isImmediate() || !isTrue();
}

/**
* if exist return the meta-class else return the type of the object.
*
Expand Down
22 changes: 12 additions & 10 deletions core/src/main/java/org/jruby/RubyFixnum.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ private static IRubyObject fixCoerce(IRubyObject x) {
return x;
}

private static IRubyObject bitCoerce(IRubyObject x) {
do {
if (x instanceof RubyFloat)
throw x.getRuntime().newTypeError("can't convert Float to Integer");
x = x.convertToInteger("to_int");
} while (!(x instanceof RubyFixnum) && !(x instanceof RubyBignum));
return x;
private IRubyObject bitCoerce(ThreadContext context, IRubyObject y) {
if(!(y instanceof RubyFixnum || y instanceof RubyBignum)) {
RubyArray ary = doCoerce(context, y, true);
y = ary.last();
if(!(y instanceof RubyFixnum || y instanceof RubyBignum)) {
coerceFailed(context, y);
}
}
return y;
}

public RubyFixnum(Ruby runtime) {
Expand Down Expand Up @@ -1075,7 +1077,7 @@ public IRubyObject op_and(ThreadContext context, long other) {
}

private IRubyObject op_and19(ThreadContext context, IRubyObject other) {
if (!((other = bitCoerce(other)) instanceof RubyFixnum)) {
if (!((other = bitCoerce(context, other)) instanceof RubyFixnum)) {
return ((RubyBignum) other).op_and(context, this);
}

Expand All @@ -1087,7 +1089,7 @@ private IRubyObject op_and19(ThreadContext context, IRubyObject other) {
*/
@JRubyMethod(name = "|")
public IRubyObject op_or(ThreadContext context, IRubyObject other) {
if ((other = bitCoerce(other)) instanceof RubyFixnum) {
if ((other = bitCoerce(context, other)) instanceof RubyFixnum) {
return newFixnum(context.runtime, value | ((RubyFixnum) other).value);
}

Expand Down Expand Up @@ -1118,7 +1120,7 @@ public IRubyObject op_xor(ThreadContext context, long other) {
}

private IRubyObject op_xor19(ThreadContext context, IRubyObject other) {
if (!((other = bitCoerce(other)) instanceof RubyFixnum)) {
if (!((other = bitCoerce(context, other)) instanceof RubyFixnum)) {
return ((RubyBignum) other).op_xor(context, this);
}
return op_xor18(context, other);
Expand Down
150 changes: 115 additions & 35 deletions core/src/main/java/org/jruby/RubyNumeric.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.ast.util.ArgsUtil;
import org.jruby.common.RubyWarnings;
import org.jruby.exceptions.RaiseException;
import org.jruby.javasupport.JavaUtil;
Expand Down Expand Up @@ -468,8 +469,7 @@ protected final RubyArray doCoerce(ThreadContext context, IRubyObject other, boo
warnings.warn("Numerical comparison operators will no more rescue exceptions of #coerce");
warnings.warn("in the next release. Return nil in #coerce if the coercion is impossible.");
if (err) {
throw getRuntime().newTypeError(
other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
coerceFailed(context, other);
} else {
context.runtime.getGlobalVariables().set("$!", savedError);
}
Expand All @@ -478,7 +478,7 @@ protected final RubyArray doCoerce(ThreadContext context, IRubyObject other, boo

if (!(result instanceof RubyArray) || ((RubyArray) result).getLength() != 2) {
if (err) {
throw getRuntime().newTypeError("coerce must return [x, y]");
throw context.runtime.newTypeError("coerce must return [x, y]");
} else if (!result.isNil()) {
RubyWarnings warnings = context.runtime.getWarnings();
warnings.warn("Bad return value for #coerce, called by numerical comparison operators.");
Expand All @@ -489,6 +489,14 @@ protected final RubyArray doCoerce(ThreadContext context, IRubyObject other, boo
return (RubyArray) result;
}

/** coerce_failed
*
*/
protected final void coerceFailed(ThreadContext context, IRubyObject other) {
throw context.runtime.newTypeError(String.format("%s can't be coerced into %s",
(other.isSpecialConst() ? other.inspect() : other.getMetaClass().getName()), getMetaClass()));
}

/** rb_num_coerce_bin
* coercion taking two arguments
*/
Expand All @@ -503,7 +511,7 @@ protected final IRubyObject coerceBin(ThreadContext context, String method, IRub
protected final IRubyObject coerceCmp(ThreadContext context, String method, IRubyObject other) {
RubyArray ary = doCoerce(context, other, false);
if (ary == null) {
return getRuntime().getNil(); // MRI does it!
return context.runtime.getNil(); // MRI does it!
}
return (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
}
Expand All @@ -519,8 +527,8 @@ protected final IRubyObject coerceRelOp(ThreadContext context, String method, IR

return unwrapCoerced(context, method, other, ary);
}
private final IRubyObject unwrapCoerced(ThreadContext context, String method, IRubyObject other, RubyArray ary) {

private IRubyObject unwrapCoerced(ThreadContext context, String method, IRubyObject other, RubyArray ary) {
IRubyObject result = (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
if (result.isNil()) {
return RubyComparable.cmperr(this, other);
Expand Down Expand Up @@ -624,7 +632,7 @@ public IRubyObject quo(ThreadContext context, IRubyObject other) {
*/
@JRubyMethod(name = "quo")
public IRubyObject quo_19(ThreadContext context, IRubyObject other) {
return RubyRational.newRationalRaw(context.runtime, this).callMethod(context, "/", other);
return RubyRational.numericQuo(context, this, other);
}

/** num_div
Expand Down Expand Up @@ -798,22 +806,68 @@ public IRubyObject truncate() {
return convertToFloat().truncate();
}

@JRubyMethod
public IRubyObject step(ThreadContext context, IRubyObject arg0, Block block) {
if (block.isGiven()) {
return stepCommon(context, arg0, RubyFixnum.one(context.runtime), block);
} else {
return enumeratorizeWithSize(context, this, "step", new IRubyObject[] { arg0 }, stepSizeFn(context, this, arg0, RubyFixnum.one(context.runtime)));
// TODO: Fold kwargs into the @JRubyMethod decorator
static final String[] validStepArgs = new String[] {"to", "by"};

/**
* num_step
*/
@JRubyMethod(optional = 2)
public IRubyObject step(ThreadContext context, IRubyObject[] args, Block block) {
if (!block.isGiven()) {
return enumeratorizeWithSize(context, this, "step", args, stepSizeFn(context, this, args));
}

IRubyObject[] scannedArgs = scanStepArgs(context, args);
return stepCommon(context, scannedArgs[0], scannedArgs[1], block);
}

@JRubyMethod
public IRubyObject step(ThreadContext context, IRubyObject to, IRubyObject step, Block block) {
if (block.isGiven()) {
return stepCommon(context, to, step, block);
/** num_step_scan_args
*
*/

private IRubyObject[] scanStepArgs(ThreadContext context, IRubyObject[] args) {
IRubyObject to = context.runtime.getNil();
IRubyObject step = context.runtime.newFixnum(1);

if (args.length >= 1) to = args[0];
if (args.length >= 2) step = args[1];

// TODO: Fold kwargs into the @JRubyMethod decorator
IRubyObject[] kwargs = ArgsUtil.extractKeywordArgs(context, args, validStepArgs);

if (kwargs != null) {
to = kwargs[0];
step = kwargs[1];

if(!to.isNil() && args.length > 1) {
throw context.runtime.newArgumentError("to is given twice");
}
if(!step.isNil() && args.length > 2) {
throw context.runtime.newArgumentError("step is given twice");
}
} else {
return enumeratorizeWithSize(context, this, "step", new IRubyObject[]{to, step}, stepSizeFn(context, this, to, step));
if (RubyBasicObject.equalInternal(context, step, RubyFixnum.zero(context.runtime))) {
throw context.runtime.newArgumentError("step can't be 0");
}
if (step.isNil()) {
throw context.runtime.newTypeError("step must be numeric");
}
}

if (step.isNil()) {
step = RubyFixnum.one(context.runtime);
}

if (to.isNil()) {
if ( f_negative_p(context, step) ) {
to = RubyFloat.newFloat(context.runtime, Double.NEGATIVE_INFINITY);
} else {
to = RubyFloat.newFloat(context.runtime, Double.POSITIVE_INFINITY);
}
}

return new IRubyObject[] {to, step};
}

private IRubyObject stepCommon(ThreadContext context, IRubyObject to, IRubyObject step, Block block) {
Expand All @@ -833,8 +887,7 @@ private IRubyObject stepCommon(ThreadContext context, IRubyObject to, IRubyObjec

private static void fixnumStep(ThreadContext context, Ruby runtime, long from, long to, long step, Block block) {
// We must avoid integer overflows in "i += step".
if (step == 0) throw runtime.newArgumentError("step cannot be 0");
if (step > 0) {
if (step >= 0) {
long tov = Long.MAX_VALUE - step;
if (to < tov) tov = to;
long i;
Expand Down Expand Up @@ -862,8 +915,6 @@ protected static void floatStep(ThreadContext context, Ruby runtime, IRubyObject
double end = num2dbl(to);
double unit = num2dbl(step);

if (unit == 0) throw runtime.newArgumentError("step cannot be 0");

double n = (end - beg)/unit;
double err = (Math.abs(beg) + Math.abs(end) + Math.abs(end - beg)) / Math.abs(unit) * DBL_EPSILON;

Expand All @@ -880,12 +931,14 @@ static void floatStep19(ThreadContext context, Ruby runtime, IRubyObject from, I
double end = num2dbl(to);
double unit = num2dbl(step);

if (unit == 0) throw runtime.newArgumentError("step cannot be 0");

double n = floatStepSize(beg, end, unit, excl);

if (Double.isInfinite(unit)) {
if (unit > 0) block.yield(context, RubyFloat.newFloat(runtime, beg));
if (n != 0) block.yield(context, from);
} else if (unit == 0) {
while(true) {
block.yield(context, from);
}
} else {
for (long i = 0; i < n; i++){
double d = i * unit + beg;
Expand All @@ -899,7 +952,10 @@ static void floatStep19(ThreadContext context, Ruby runtime, IRubyObject from, I

private static void duckStep(ThreadContext context, Ruby runtime, IRubyObject from, IRubyObject to, IRubyObject step, Block block) {
IRubyObject i = from;
String cmpString = step.callMethod(context, ">", RubyFixnum.zero(runtime)).isTrue() ? ">" : "<";

String cmpString = step.callMethod(context, ">", RubyFixnum.newFixnum(context.runtime, 0)).isTrue() ? ">" : "<";
if(step.callMethod(context, "==", RubyFixnum.newFixnum(context.runtime, 0)).isTrue())
cmpString = "==";

while (true) {
if (i.callMethod(context, cmpString, to).isTrue()) break;
Expand All @@ -910,16 +966,23 @@ private static void duckStep(ThreadContext context, Ruby runtime, IRubyObject fr

public static RubyNumeric intervalStepSize(ThreadContext context, IRubyObject from, IRubyObject to, IRubyObject step, boolean excludeLast) {
Ruby runtime = context.runtime;

if (from instanceof RubyFixnum && to instanceof RubyFixnum && step instanceof RubyFixnum) {
long diff = ((RubyFixnum) step).getLongValue();
long delta = ((RubyFixnum) to).getLongValue() - ((RubyFixnum) from).getLongValue();
if (diff == 0) {
return RubyFloat.newFloat(runtime, Double.POSITIVE_INFINITY);
}

long delta = ((RubyFixnum) to).getLongValue() - ((RubyFixnum) from).getLongValue();
if (diff < 0) {
diff = -diff;
delta = -delta;
}
if (excludeLast) {
delta += (diff > 0 ? -1 : +1);
delta--;
}

if (diff == 0) {
return RubyFloat.newFloat(runtime, Double.POSITIVE_INFINITY);
if (delta < 0) {
return runtime.newFixnum(0);
}

long result = delta / diff;
Expand All @@ -929,10 +992,22 @@ public static RubyNumeric intervalStepSize(ThreadContext context, IRubyObject fr

if (Double.isInfinite(n)) {
return runtime.newFloat(n);
} else {
return runtime.newFloat(n).convertToInteger();
}
return runtime.newFixnum((long) n);
} else {
String cmpString = step.callMethod(context, ">", RubyFixnum.zero(runtime)).isTrue() ? ">" : "<";
String cmpString = ">";
RubyFixnum zero = RubyFixnum.zero(runtime);
IRubyObject comparison = zero.coerceCmp(context, "<=>", step);

switch (RubyComparable.cmpint(context, comparison, step, zero)) {
case 0:
return RubyFloat.newFloat(runtime, Float.POSITIVE_INFINITY);
case 1:
cmpString = "<";
break;
}

if (from.callMethod(context, cmpString, to).isTrue()) {
return RubyFixnum.zero(runtime);
}
Expand All @@ -946,11 +1021,12 @@ public static RubyNumeric intervalStepSize(ThreadContext context, IRubyObject fr
}
}

private SizeFn stepSizeFn(final ThreadContext context, final IRubyObject from, final IRubyObject to, final IRubyObject step) {
private SizeFn stepSizeFn(final ThreadContext context, final IRubyObject from, final IRubyObject[] args) {
return new SizeFn() {
@Override
public IRubyObject size(IRubyObject[] args) {
return intervalStepSize(context, from, to, step, false);
IRubyObject[] scannedArgs = scanStepArgs(context, args);
return intervalStepSize(context, from, scannedArgs[0], scannedArgs[1], false);
}
};
}
Expand All @@ -972,6 +1048,10 @@ public static double floatStepSize(double beg, double end, double unit, boolean
}
}

if (unit == 0) {
return Float.POSITIVE_INFINITY;
}

if (err > 0.5) err = 0.5;
if (excludeLast) {
if (n <= 0) {
Expand Down
19 changes: 18 additions & 1 deletion core/src/main/java/org/jruby/RubyRational.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.util.ByteList;
import org.jruby.util.Numeric;
import org.jruby.util.TypeConverter;

import static org.jruby.runtime.Helpers.invokedynamic;
import static org.jruby.runtime.invokedynamic.MethodNames.HASH;
Expand Down Expand Up @@ -1058,5 +1059,21 @@ private static IRubyObject str_to_r_strict(ThreadContext context, IRubyObject re
throw context.runtime.newArgumentError("invalid value for convert(): " + s.convertToString());
}
return a.eltInternal(0);
}
}

/**
* numeric_quo
*/
public static IRubyObject numericQuo(ThreadContext context, IRubyObject x, IRubyObject y) {
if (y instanceof RubyFloat) {
return ((RubyNumeric)x).fdiv(context, y);
}

if (Numeric.CANON && canonicalization) {
x = newRationalRaw(context.runtime, x);
} else {
x = TypeConverter.convertToType(x, context.runtime.getRational(), "to_r");
}
return x.callMethod(context, "/", y);
}
}
Loading