package org.jruby.util;
import org.joni.Regex;
import org.joni.WarnCallback;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.Ruby;
import org.jruby.RubyBignum;
import org.jruby.RubyBoolean;
import org.jruby.RubyComplex;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyInteger;
import org.jruby.RubyNumeric;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import java.math.BigInteger;
public class Numeric {
public static final boolean CANON = true;
public static IRubyObject f_add(ThreadContext context, IRubyObject x, IRubyObject y) {
if (y instanceof RubyFixnum && ((RubyFixnum)y).getLongValue() == 0) return x;
if (x instanceof RubyFixnum && ((RubyFixnum)x).getLongValue() == 0) return y;
return sites(context).op_plus.call(context, x, x, y);
}
public static RubyInteger f_add(ThreadContext context, RubyInteger x, RubyInteger y) {
return (RubyInteger) x.op_plus(context, y);
}
public static IRubyObject f_cmp(ThreadContext context, IRubyObject x, IRubyObject y) {
if (x instanceof RubyInteger && y instanceof RubyInteger) {
return f_cmp(context, (RubyInteger) x, (RubyInteger) y);
}
return sites(context).op_cmp.call(context, x, x, y);
}
public static RubyFixnum f_cmp(ThreadContext context, RubyInteger x, RubyInteger y) {
final int cmp;
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
cmp = Long.compare(((RubyFixnum) x).getLongValue(), ((RubyFixnum) y).getLongValue());
}
else {
cmp = x.getBigIntegerValue().compareTo(y.getBigIntegerValue());
}
return RubyFixnum.newFixnum(context.runtime, cmp);
}
public static RubyFixnum f_cmp(ThreadContext context, RubyInteger x, long y) {
final int cmp;
if (x instanceof RubyFixnum) {
cmp = Long.compare(((RubyFixnum) x).getLongValue(), y);
}
else {
cmp = x.getBigIntegerValue().compareTo(BigInteger.valueOf(y));
}
return RubyFixnum.newFixnum(context.runtime, cmp);
}
public static IRubyObject f_div(ThreadContext context, IRubyObject x, IRubyObject y) {
if (y instanceof RubyFixnum && ((RubyFixnum) y).getLongValue() == 1) return x;
return sites(context).op_quo.call(context, x, x, y);
}
public static boolean f_gt_p(ThreadContext context, IRubyObject x, IRubyObject y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
return ((RubyFixnum)x).getLongValue() > ((RubyFixnum)y).getLongValue();
}
return sites(context).op_gt.call(context, x, x, y).isTrue();
}
public static boolean f_gt_p(ThreadContext context, RubyInteger x, RubyInteger y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
return ((RubyFixnum)x).getLongValue() > ((RubyFixnum)y).getLongValue();
}
return x.getBigIntegerValue().compareTo(y.getBigIntegerValue()) > 0;
}
public static boolean f_lt_p(ThreadContext context, IRubyObject x, IRubyObject y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
return ((RubyFixnum)x).getLongValue() < ((RubyFixnum)y).getLongValue();
}
return sites(context).op_lt.call(context, x, x, y).isTrue();
}
public static boolean f_lt_p(ThreadContext context, RubyInteger x, RubyInteger y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
return ((RubyFixnum)x).getLongValue() < ((RubyFixnum)y).getLongValue();
}
return x.getBigIntegerValue().compareTo(y.getBigIntegerValue()) < 0;
}
public static IRubyObject f_mod(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).op_mod.call(context, x, x, y);
}
public static IRubyObject f_mul(ThreadContext context, IRubyObject x, IRubyObject y) {
Ruby runtime = context.runtime;
if (y instanceof RubyFixnum) {
long iy = ((RubyFixnum) y).getLongValue();
if (iy == 1) return x;
if (x instanceof RubyInteger) {
if (iy == 0) return RubyFixnum.zero(runtime);
return f_mul(context, (RubyInteger) x, (RubyFixnum) y);
}
} else if (x instanceof RubyFixnum) {
long ix = ((RubyFixnum) x).getLongValue();
if (ix == 1) return y;
if (y instanceof RubyInteger) {
if (ix == 0) return RubyFixnum.zero(runtime);
return f_mul(context, (RubyFixnum) x, (RubyInteger) y);
}
}
return sites(context).op_times.call(context, x, x, y);
}
public static RubyInteger f_mul(ThreadContext context, RubyInteger x, RubyInteger y) {
return (RubyInteger) x.op_mul(context, y);
}
public static IRubyObject safe_mul(ThreadContext context, IRubyObject a, IRubyObject b, boolean az, boolean bz) {
Ruby runtime = context.runtime;
double v;
if (!az && bz && a instanceof RubyFloat && !Double.isNaN(v = ((RubyFloat)a).getDoubleValue())) {
a = v < 0.0d ? runtime.newFloat(-1.0d) : runtime.newFloat(1.0d);
}
if (!bz && az && b instanceof RubyFloat && !Double.isNaN(v = ((RubyFloat)b).getDoubleValue())) {
b = v < 0.0d ? runtime.newFloat(-1.0) : runtime.newFloat(1.0);
}
return f_mul(context, a, b);
}
public static IRubyObject f_sub(ThreadContext context, IRubyObject x, IRubyObject y) {
if (y instanceof RubyFixnum && ((RubyFixnum)y).getLongValue() == 0) return x;
return sites(context).op_minus.call(context, x, x, y);
}
public static RubyInteger f_sub(ThreadContext context, RubyInteger x, RubyInteger y) {
return (RubyInteger) x.op_minus(context, y);
}
public static IRubyObject f_xor(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).op_xor.call(context, x, x, y);
}
public static IRubyObject f_xor(ThreadContext context, RubyInteger x, RubyInteger y) {
return x.op_xor(context, y);
}
public static IRubyObject f_abs(ThreadContext context, IRubyObject x) {
return sites(context).abs.call(context, x, x);
}
public static RubyInteger f_abs(ThreadContext context, RubyInteger x) {
return (RubyInteger) x.abs(context);
}
public static RubyFloat f_abs(ThreadContext context, RubyFloat x) {
return (RubyFloat) x.abs(context);
}
public static IRubyObject f_abs2(ThreadContext context, IRubyObject x) {
return sites(context).abs2.call(context, x, x);
}
public static IRubyObject f_arg(ThreadContext context, IRubyObject x) {
return sites(context).arg.call(context, x, x);
}
public static IRubyObject f_conjugate(ThreadContext context, IRubyObject x) {
return sites(context).conjugate.call(context, x, x);
}
public static IRubyObject f_denominator(ThreadContext context, IRubyObject x) {
return sites(context).denominator.call(context, x, x);
}
public static boolean f_exact_p(ThreadContext context, IRubyObject x) {
return sites(context).exact.call(context, x, x).isTrue();
}
public static IRubyObject f_numerator(ThreadContext context, IRubyObject x) {
return sites(context).numerator.call(context, x, x);
}
public static IRubyObject f_polar(ThreadContext context, IRubyObject x) {
return sites(context).polar.call(context, x, x);
}
public static boolean f_real_p(ThreadContext context, IRubyObject x) {
switch (x.getMetaClass().getClassIndex()) {
case FLOAT:
case FIXNUM:
case BIGNUM:
case RATIONAL:
return ((RubyNumeric) x).isReal();
case COMPLEX:
return ((RubyComplex) x).isReal();
}
return sites(context).real.call(context, x, x).isTrue();
}
public static boolean f_integer_p(ThreadContext context, IRubyObject x) {
return sites(context).integer.call(context, x, x).isTrue();
}
public static boolean f_integer_p(ThreadContext context, RubyNumeric x) {
switch (x.getMetaClass().getClassIndex()) {
case FIXNUM:
case BIGNUM:
return true;
case FLOAT:
case RATIONAL:
case COMPLEX:
return false;
}
return sites(context).integer.call(context, x, x).isTrue();
}
public static IRubyObject f_divmod(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).divmod.call(context, x, x, y);
}
public static IRubyObject f_divmod(ThreadContext context, RubyInteger x, IRubyObject y) {
return x.divmod(context, y);
}
public static IRubyObject f_floor(ThreadContext context, IRubyObject x) {
return sites(context).floor.call(context, x, x);
}
public static IRubyObject f_inspect(ThreadContext context, IRubyObject x) {
return sites(context).inspect.call(context, x, x);
}
public static IRubyObject f_negate(ThreadContext context, IRubyObject x) {
return sites(context).op_uminus.call(context, x, x);
}
public static RubyInteger f_negate(ThreadContext context, RubyInteger x) {
return x.negate();
}
public static IRubyObject f_to_f(ThreadContext context, IRubyObject x) {
return sites(context).to_f.call(context, x, x);
}
public static IRubyObject f_to_i(ThreadContext context, IRubyObject x) {
return sites(context).to_i.call(context, x, x);
}
public static IRubyObject f_to_r(ThreadContext context, IRubyObject x) {
return sites(context).to_r.call(context, x, x);
}
public static RubyNumeric f_to_r(ThreadContext context, RubyInteger x) {
return (RubyNumeric) x.to_r(context);
}
public static IRubyObject f_to_s(ThreadContext context, IRubyObject x) {
return sites(context).to_s.call(context, x, x);
}
public static IRubyObject f_truncate(ThreadContext context, IRubyObject x) {
return sites(context).truncate.call(context, x, x);
}
public static IRubyObject f_equal(ThreadContext context, IRubyObject x, IRubyObject y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum) {
return RubyBoolean.newBoolean(context, ((RubyFixnum) x).getLongValue() == ((RubyFixnum) y).getLongValue());
}
return sites(context).op_equals.call(context, x, x, y);
}
public static IRubyObject f_equal(ThreadContext context, RubyInteger x, RubyInteger y) {
return x.op_equal(context, y);
}
public static IRubyObject f_expt(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).op_exp.call(context, x, x, y);
}
public static RubyNumeric f_expt(ThreadContext context, RubyInteger x, RubyInteger y) {
return (RubyNumeric) x.op_pow(context, y);
}
public static IRubyObject f_idiv(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).div.call(context, x, x, y);
}
public static RubyInteger f_idiv(ThreadContext context, RubyInteger x, RubyInteger y) {
return (RubyInteger) x.idiv(context, y);
}
public static IRubyObject f_quo(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).quo.call(context, x, x, y);
}
public static IRubyObject f_quo(ThreadContext context, RubyFloat x, RubyFloat y) {
return x.quo(context, y);
}
public static IRubyObject f_rshift(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).op_rshift.call(context, x, x, y);
}
public static IRubyObject f_lshift(ThreadContext context, IRubyObject x, IRubyObject y) {
return sites(context).op_lshift.call(context, x, x, y);
}
public static boolean f_negative_p(ThreadContext context, IRubyObject x) {
if (x instanceof RubyInteger) return ((RubyInteger) x).signum() == -1;
return sites(context).op_lt.call(context, x, x, RubyFixnum.zero(context.runtime)).isTrue();
}
public static boolean f_negative_p(ThreadContext context, RubyInteger x) {
return x.signum() == -1;
}
public static boolean f_negative_p(ThreadContext context, RubyFloat x) {
return x.signum() == -1;
}
public static boolean f_zero_p(ThreadContext context, IRubyObject x) {
if (x instanceof RubyInteger) return ((RubyInteger) x).isZero();
if (x instanceof RubyFloat) return ((RubyFloat) x).signum() == 0;
return sites(context).op_equals.call(context, x, x, RubyFixnum.zero(context.runtime)).isTrue();
}
public static boolean f_zero_p(ThreadContext context, RubyInteger x) {
return x.isZero();
}
public static boolean f_one_p(ThreadContext context, IRubyObject x) {
if (x instanceof RubyFixnum) return ((RubyFixnum) x).getLongValue() == 1;
return sites(context).op_equals.call(context, x, x, RubyFixnum.one(context.runtime)).isTrue();
}
public static boolean f_minus_one_p(ThreadContext context, IRubyObject x) {
if (x instanceof RubyFixnum) return ((RubyFixnum) x).getLongValue() == -1;
return sites(context).op_equals.call(context, x, x, RubyFixnum.minus_one(context.runtime)).isTrue();
}
public static boolean f_odd_p(ThreadContext context, IRubyObject i) {
if (i instanceof RubyFixnum) return ((RubyFixnum) i).getLongValue() % 2 != 0;
RubyFixnum two = RubyFixnum.two(context.runtime);
return (((RubyFixnum) sites(context).op_mod.call(context, i, i, two)).getLongValue() != 0);
}
public static long i_gcd(long x, long y) {
long shift, uz, vz;
if (x == Long.MIN_VALUE) {
if (y == Long.MIN_VALUE)
return x;
return 1L << Long.numberOfTrailingZeros(Math.abs(y));
}
if (y == Long.MIN_VALUE) {
return 1L << Long.numberOfTrailingZeros(Math.abs(x));
}
x = Math.abs(x);
y = Math.abs(y);
if (x == 0) {
return y;
}
if (y == 0 || x == y) {
return x;
}
uz = Long.numberOfTrailingZeros(x);
vz = Long.numberOfTrailingZeros(y);
shift = Math.min(uz, vz);
x >>= uz;
y >>= vz;
while (x != y) {
if (x > y) {
x -= y;
x >>= Long.numberOfTrailingZeros(x);
} else {
y -= x;
y >>= Long.numberOfTrailingZeros(y);
}
}
return x << shift;
}
public static IRubyObject f_gcd(ThreadContext context, IRubyObject x, IRubyObject y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum && isLongMinValue((RubyFixnum) x)) {
return RubyFixnum.newFixnum(context.runtime, i_gcd(((RubyFixnum)x).getLongValue(), ((RubyFixnum)y).getLongValue()));
}
if (f_negative_p(context, x)) x = f_negate(context, x);
if (f_negative_p(context, y)) y = f_negate(context, y);
if (f_zero_p(context, x)) return y;
if (f_zero_p(context, y)) return x;
for (;;) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum && isLongMinValue((RubyFixnum) x)) {
return RubyFixnum.newFixnum(context.runtime, i_gcd(((RubyFixnum)x).getLongValue(), ((RubyFixnum)y).getLongValue()));
}
IRubyObject z = x;
x = f_mod(context, y, x);
y = z;
}
}
public static RubyInteger f_gcd(ThreadContext context, RubyInteger x, RubyInteger y) {
if (x instanceof RubyFixnum && y instanceof RubyFixnum && isLongMinValue((RubyFixnum) x))
return RubyFixnum.newFixnum(context.runtime, i_gcd(x.getLongValue(), y.getLongValue()));
BigInteger gcd = x.getBigIntegerValue().gcd(y.getBigIntegerValue());
if (gcd.compareTo(RubyBignum.LONG_MAX) <= 0) {
return RubyFixnum.newFixnum(context.runtime, gcd.longValue());
}
return RubyBignum.newBignum(context.runtime, gcd);
}
protected static boolean isLongMinValue(RubyFixnum x) {
return x.getLongValue() != Long.MIN_VALUE;
}
public static IRubyObject f_lcm(ThreadContext context, IRubyObject x, IRubyObject y) {
if (f_zero_p(context, x) || f_zero_p(context, y)) {
return RubyFixnum.zero(context.runtime);
}
return f_abs(context, f_mul(context, f_div(context, x, f_gcd(context, x, y)), y));
}
public static long i_ilog2(ThreadContext context, IRubyObject x) {
return i_ilog2(context, x.convertToInteger());
}
public static long i_ilog2(ThreadContext context, RubyInteger x) {
long q = (x.size(context).convertToInteger().getLongValue() - 8) * 8 + 1;
if (q > 0) {
x = x.op_rshift(context, q);
}
long fx = x.getLongValue();
long r = -1;
while (fx != 0) {
fx >>= 1;
r += 1;
}
return q + r;
}
public static double ldexp(double f, long e) {
return f * Math.pow(2.0, e);
}
public static double frexp(double mantissa, long[]e) {
short sign = 1;
long exponent = 0;
if (Double.isInfinite(mantissa) || Double.isNaN(mantissa)) {
return mantissa;
}
if (mantissa != 0.0) {
if (mantissa < 0) {
mantissa = -mantissa;
sign = -1;
}
for (; mantissa < 0.5; mantissa *= 2.0, exponent -=1) { }
for (; mantissa >= 1.0; mantissa *= 0.5, exponent +=1) { }
}
e[0] = exponent;
return sign * mantissa;
}
private static final long SQRT_LONG_MAX = ((long)1) << ((8 * 8 - 1) / 2);
static boolean fitSqrtLong(long n) {
return n < SQRT_LONG_MAX && n >= -SQRT_LONG_MAX;
}
public static RubyNumeric int_pow(ThreadContext context, long x, long y) {
boolean neg = x < 0;
long z = 1;
if (neg) x = -x;
if ((y & 1) != 0) {
z = x;
} else {
neg = false;
}
y &= ~1;
Ruby runtime = context.runtime;
do {
while (y % 2 == 0) {
if (!fitSqrtLong(x)) {
IRubyObject v = RubyBignum.newBignum(runtime, x).op_pow(context, y);
if (z != 1) v = RubyBignum.newBignum(runtime, neg ? -z : z).op_mul(context, v);
return (RubyNumeric) v;
}
x *= x;
y >>= 1;
}
if (multiplyOverflows(x, z)) {
IRubyObject v = RubyBignum.newBignum(runtime, x).op_pow(context, y);
if (z != 1) v = RubyBignum.newBignum(runtime, neg ? -z : z).op_mul(context, v);
return (RubyNumeric) v;
}
z = x * z;
} while(--y != 0);
if (neg) z = -z;
return RubyFixnum.newFixnum(runtime, z);
}
public static boolean multiplyOverflows(long a, long b) {
return a == 0 ? false :
a == -1 ? b < -Long.MAX_VALUE :
a > 0 ? (b > 0 ? Long.MAX_VALUE / a < b : Long.MIN_VALUE / a > b) :
(b > 0 ? Long.MIN_VALUE / a < b : Long.MAX_VALUE / a > b);
}
public static boolean k_exact_p(IRubyObject x) {
return !(x instanceof RubyFloat);
}
public static boolean k_inexact_p(IRubyObject x) {
return x instanceof RubyFloat;
}
public static boolean k_integer_p(IRubyObject x) {
return x instanceof RubyInteger;
}
public static boolean k_numeric_p(IRubyObject x) {
return x instanceof RubyNumeric;
}
public static final class ComplexPatterns {
public static final Regex comp_pat0, comp_pat1, comp_pat2, underscores_pat;
static {
String WS = "\\s*";
String DIGITS = "(?:\\d(?:_\\d|\\d)*)";
String NUMERATOR = "(?:" + DIGITS + "?\\.)?" + DIGITS + "(?:[eE][-+]?" + DIGITS + ")?";
String DENOMINATOR = DIGITS;
String NUMBER = "[-+]?" + NUMERATOR + "(?:\\/" + DENOMINATOR + ")?";
String NUMBERNOS = NUMERATOR + "(?:\\/" + DENOMINATOR + ")?";
String PATTERN0 = "\\A" + WS + "(" + NUMBER + ")@(" + NUMBER + ")" + WS;
String PATTERN1 = "\\A" + WS + "([-+])?(" + NUMBER + ")?[iIjJ]" + WS;
String PATTERN2 = "\\A" + WS + "(" + NUMBER + ")(([-+])(" + NUMBERNOS + ")?[iIjJ])?" + WS;
comp_pat0 = new Regex(PATTERN0.getBytes(), 0, PATTERN0.length(), 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
comp_pat1 = new Regex(PATTERN1.getBytes(), 0, PATTERN1.length(), 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
comp_pat2 = new Regex(PATTERN2.getBytes(), 0, PATTERN2.length(), 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
underscores_pat = new Regex("_+".getBytes(), 0, 2, 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
}
}
public static final class RationalPatterns {
public static final Regex rat_pat, an_e_pat, a_dot_pat;
static {
String WS = "\\s*";
String DIGITS = "(?:\\d(?:_\\d|\\d)*)";
String NUMERATOR = "(?:" + DIGITS + "?\\.)?" + DIGITS + "(?:[eE][-+]?" + DIGITS + ")?";
String DENOMINATOR = DIGITS;
String PATTERN = "\\A" + WS + "([-+])?(" + NUMERATOR + ")(?:\\/(" + DENOMINATOR + "))?" + WS;
rat_pat = new Regex(PATTERN.getBytes(), 0, PATTERN.length(), 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
an_e_pat = new Regex("[Ee]".getBytes(), 0, 4, 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
a_dot_pat = new Regex("\\.".getBytes(), 0, 2, 0, ASCIIEncoding.INSTANCE, WarnCallback.NONE);
}
}
public static IRubyObject[] nurat_rationalize_internal(ThreadContext context, IRubyObject a, IRubyObject b) {
IRubyObject p, q;
IRubyObject c, k, t, p0, p1, p2, q0, q1, q2;
RubyFixnum zero = RubyFixnum.zero(context.runtime);
RubyFixnum one = RubyFixnum.one(context.runtime);
p0 = q1 = zero;
p1 = q0 = one;
while (true) {
c = sites(context).ceil.call(context, a, a);
if (f_lt_p(context, c, b)) {
break;
}
k = f_sub(context, c, one);
p2 = f_add(context, f_mul(context, k, p1), p0);
q2 = f_add(context, f_mul(context, k, q1), q0);
t = f_quo(context, one, f_sub(context, b, k));
b = f_quo(context, one, f_sub(context, a, k));
a = t;
p0 = p1;
q0 = q1;
p1 = p2;
q1 = q2;
}
p = f_add(context, f_mul(context, c, p1), p0);
q = f_add(context, f_mul(context, c, q1), q0);
return new IRubyObject[] { p, q };
}
public static IRubyObject[] nurat_rationalize_internal(ThreadContext context, IRubyObject[] ary) {
return nurat_rationalize_internal(context, ary[0], ary[1]);
}
public static void checkInteger(ThreadContext context, IRubyObject obj) {
if (!(obj instanceof RubyInteger)) {
throw context.runtime.newTypeError("not an integer");
}
}
private static JavaSites.NumericSites sites(ThreadContext context) {
return context.sites.Numeric;
}
}