/**
 *   Copyright (c) Rich Hickey. All rights reserved.
 *   The use and distribution terms for this software are covered by the
 *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 *   which can be found in the file epl-v10.html at the root of this distribution.
 *   By using this software in any fashion, you are agreeing to be bound by
 * 	 the terms of this license.
 *   You must not remove this notice, or any other, from this software.
 **/

/* rich Mar 31, 2008 */

package clojure.lang;

import java.math.BigInteger;
import java.math.BigDecimal;
import java.math.MathContext;

public class Numbers{

static interface Ops{
	Ops combine(Ops y);

	Ops opsWith(LongOps x);

	Ops opsWith(DoubleOps x);

	Ops opsWith(RatioOps x);

	Ops opsWith(BigIntOps x);

	Ops opsWith(BigDecimalOps x);

	public boolean isZero(Number x);

	public boolean isPos(Number x);

	public boolean isNeg(Number x);

	public Number add(Number x, Number y);
	public Number addP(Number x, Number y);

	public Number multiply(Number x, Number y);
	public Number multiplyP(Number x, Number y);

	public Number divide(Number x, Number y);

	public Number quotient(Number x, Number y);

	public Number remainder(Number x, Number y);

	public boolean equiv(Number x, Number y);

	public boolean lt(Number x, Number y);
	public boolean lte(Number x, Number y);
	public boolean gte(Number x, Number y);

	public Number negate(Number x);
	public Number negateP(Number x);

	public Number inc(Number x);
	public Number incP(Number x);

	public Number dec(Number x);
	public Number decP(Number x);
}

static abstract class OpsP implements Ops{
	public Number addP(Number x, Number y){
		return add(x, y);
	}

	public Number multiplyP(Number x, Number y){
		return multiply(x, y);
	}

	public Number negateP(Number x){
		return negate(x);
	}

	public Number incP(Number x){
		return inc(x);
	}

	public Number decP(Number x){
		return dec(x);
	}

}

static public boolean isZero(Object x){
	return ops(x).isZero((Number)x);
}

static public boolean isPos(Object x){
	return ops(x).isPos((Number)x);
}

static public boolean isNeg(Object x){
	return ops(x).isNeg((Number)x);
}

static public Number minus(Object x){
	return ops(x).negate((Number)x);
}

static public Number minusP(Object x){
	return ops(x).negateP((Number)x);
}

static public Number inc(Object x){
	return ops(x).inc((Number)x);
}

static public Number incP(Object x){
	return ops(x).incP((Number)x);
}

static public Number dec(Object x){
	return ops(x).dec((Number)x);
}

static public Number decP(Object x){
	return ops(x).decP((Number)x);
}

static public Number add(Object x, Object y){
	return ops(x).combine(ops(y)).add((Number)x, (Number)y);
}

static public Number addP(Object x, Object y){
	return ops(x).combine(ops(y)).addP((Number)x, (Number)y);
}

static public Number minus(Object x, Object y){
	Ops yops = ops(y);
	return ops(x).combine(yops).add((Number)x, yops.negate((Number)y));
}

static public Number minusP(Object x, Object y){
	Ops yops = ops(y);
	Number negativeY = yops.negateP((Number) y);
	Ops negativeYOps = ops(negativeY);
	return ops(x).combine(negativeYOps).addP((Number)x, negativeY);
}

static public Number multiply(Object x, Object y){
	return ops(x).combine(ops(y)).multiply((Number)x, (Number)y);
}

static public Number multiplyP(Object x, Object y){
	return ops(x).combine(ops(y)).multiplyP((Number)x, (Number)y);
}

static public Number divide(Object x, Object y){
	Ops yops = ops(y);
	if(yops.isZero((Number)y))
		throw new ArithmeticException("Divide by zero");
	return ops(x).combine(yops).divide((Number)x, (Number)y);
}

static public Number quotient(Object x, Object y){
	Ops yops = ops(y);
	if(yops.isZero((Number) y))
		throw new ArithmeticException("Divide by zero");
	return ops(x).combine(yops).quotient((Number)x, (Number)y);
}

static public Number remainder(Object x, Object y){
	Ops yops = ops(y);
	if(yops.isZero((Number) y))
		throw new ArithmeticException("Divide by zero");
	return ops(x).combine(yops).remainder((Number)x, (Number)y);
}

static public double quotient(double n, double d){
	if(d == 0)
		throw new ArithmeticException("Divide by zero");

	double q = n / d;
	if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
		{
		return (double)(long) q;
		}
	else
		{ //bigint quotient
		return new BigDecimal(q).toBigInteger().doubleValue();
		}
}

static public double remainder(double n, double d){
	if(d == 0)
		throw new ArithmeticException("Divide by zero");

	double q = n / d;
	if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
		{
		return (n - ((long) q) * d);
		}
	else
		{ //bigint quotient
		Number bq = new BigDecimal(q).toBigInteger();
		return (n - bq.doubleValue() * d);
		}
}

static public boolean equiv(Object x, Object y){
	return equiv((Number) x, (Number) y);
}

static public boolean equiv(Number x, Number y){
	return ops(x).combine(ops(y)).equiv(x, y);
}

static public boolean equal(Number x, Number y){
	return category(x) == category(y)
			&& ops(x).combine(ops(y)).equiv(x, y);
}

static public boolean lt(Object x, Object y){
	return ops(x).combine(ops(y)).lt((Number)x, (Number)y);
}

static public boolean lte(Object x, Object y){
	return ops(x).combine(ops(y)).lte((Number)x, (Number)y);
}

static public boolean gt(Object x, Object y){
	return ops(x).combine(ops(y)).lt((Number)y, (Number)x);
}

static public boolean gte(Object x, Object y){
	return ops(x).combine(ops(y)).gte((Number)x, (Number)y);
}

static public int compare(Number x, Number y){
	Ops ops = ops(x).combine(ops(y));
	if(ops.lt(x, y))
		return -1;
	else if(ops.lt(y, x))
		return 1;
	return 0;
}

@WarnBoxedMath(false)
static BigInt toBigInt(Object x){
	if(x instanceof BigInt)
		return (BigInt) x;
	if(x instanceof BigInteger)
		return BigInt.fromBigInteger((BigInteger) x);
	else
		return BigInt.fromLong(((Number) x).longValue());
}

@WarnBoxedMath(false)
static BigInteger toBigInteger(Object x){
	if(x instanceof BigInteger)
		return (BigInteger) x;
	else if(x instanceof BigInt)
		return ((BigInt) x).toBigInteger();	
	else
		return BigInteger.valueOf(((Number) x).longValue());
}

@WarnBoxedMath(false)
static BigDecimal toBigDecimal(Object x){
	if(x instanceof BigDecimal)
		return (BigDecimal) x;
	else if(x instanceof BigInt)
		{
		BigInt bi = (BigInt) x;
		if(bi.bipart == null)
			return BigDecimal.valueOf(bi.lpart);
		else
			return new BigDecimal(bi.bipart);
		}
	else if(x instanceof BigInteger)
		return new BigDecimal((BigInteger) x);
	else if(x instanceof Double)
		return new BigDecimal(((Number) x).doubleValue());
	else if(x instanceof Float)
		return new BigDecimal(((Number) x).doubleValue());
	else if(x instanceof Ratio)
		{
		Ratio r = (Ratio)x;
		return (BigDecimal)divide(new BigDecimal(r.numerator), r.denominator);
		}
	else
		return BigDecimal.valueOf(((Number) x).longValue());
}

@WarnBoxedMath(false)
static public Ratio toRatio(Object x){
	if(x instanceof Ratio)
		return (Ratio) x;
	else if(x instanceof BigDecimal)
		{
		BigDecimal bx = (BigDecimal) x;
		BigInteger bv = bx.unscaledValue();
		int scale = bx.scale();
		if(scale < 0)
			return new Ratio(bv.multiply(BigInteger.TEN.pow(-scale)), BigInteger.ONE);
		else
			return new Ratio(bv, BigInteger.TEN.pow(scale));
		}
	return new Ratio(toBigInteger(x), BigInteger.ONE);
}

@WarnBoxedMath(false)
static public Number rationalize(Number x){
	if(x instanceof Float || x instanceof Double)
		return rationalize(BigDecimal.valueOf(x.doubleValue()));
	else if(x instanceof BigDecimal)
		{
		BigDecimal bx = (BigDecimal) x;
		BigInteger bv = bx.unscaledValue();
		int scale = bx.scale();
		if(scale < 0)
			return BigInt.fromBigInteger(bv.multiply(BigInteger.TEN.pow(-scale)));
		else
			return divide(bv, BigInteger.TEN.pow(scale));
		}
	return x;
}

//static  Number box(int val){
//		return Integer.valueOf(val);
//}

//static  Number box(long val){
//		return Long.valueOf(val);
//}
//
//static  Double box(double val){
//		return Double.valueOf(val);
//}
//
//static  Double box(float val){
//		return Double.valueOf((double) val);
//}

@WarnBoxedMath(false)
static public Number reduceBigInt(BigInt val){
	if(val.bipart == null)
		return num(val.lpart);
	else
		return val.bipart;
}

static public Number divide(BigInteger n, BigInteger d){
	if(d.equals(BigInteger.ZERO))
		throw new ArithmeticException("Divide by zero");
	BigInteger gcd = n.gcd(d);
	if(gcd.equals(BigInteger.ZERO))
		return BigInt.ZERO;
	n = n.divide(gcd);
	d = d.divide(gcd);
	if(d.equals(BigInteger.ONE))
		return BigInt.fromBigInteger(n);
	else if(d.equals(BigInteger.ONE.negate()))
		return BigInt.fromBigInteger(n.negate());
	return new Ratio((d.signum() < 0 ? n.negate() : n),
	                 (d.signum() < 0 ? d.negate() : d));
}

static public int shiftLeftInt(int x, int n){
	return x << n;
}

static public long shiftLeft(Object x, Object y){
    return shiftLeft(bitOpsCast(x),bitOpsCast(y));
}
static public long shiftLeft(Object x, long y){
    return shiftLeft(bitOpsCast(x),y);
}
static public long shiftLeft(long x, Object y){
    return shiftLeft(x,bitOpsCast(y));
}
static public long shiftLeft(long x, long n){
	return x << n;
}

static public int shiftRightInt(int x, int n){
	return x >> n;
}

static public long shiftRight(Object x, Object y){
    return shiftRight(bitOpsCast(x),bitOpsCast(y));
}
static public long shiftRight(Object x, long y){
    return shiftRight(bitOpsCast(x),y);
}
static public long shiftRight(long x, Object y){
    return shiftRight(x,bitOpsCast(y));
}
static public long shiftRight(long x, long n){
	return x >> n;
}

static public int unsignedShiftRightInt(int x, int n){
	return x >>> n;
}

static public long unsignedShiftRight(Object x, Object y){
    return unsignedShiftRight(bitOpsCast(x),bitOpsCast(y));
}
static public long unsignedShiftRight(Object x, long y){
    return unsignedShiftRight(bitOpsCast(x),y);
}
static public long unsignedShiftRight(long x, Object y){
    return unsignedShiftRight(x,bitOpsCast(y));
}
static public long unsignedShiftRight(long x, long n){
	return x >>> n;
}

final static class LongOps implements Ops{
	public Ops combine(Ops y){
		return y.opsWith(this);
	}

	final public Ops opsWith(LongOps x){
		return this;
	}

	final public Ops opsWith(DoubleOps x){
		return DOUBLE_OPS;
	}

	final public Ops opsWith(RatioOps x){
		return RATIO_OPS;
	}

	final public Ops opsWith(BigIntOps x){
		return BIGINT_OPS;
	}

	final public Ops opsWith(BigDecimalOps x){
		return BIGDECIMAL_OPS;
	}

	public boolean isZero(Number x){
		return x.longValue() == 0;
	}

	public boolean isPos(Number x){
		return x.longValue() > 0;
	}

	public boolean isNeg(Number x){
		return x.longValue() < 0;
	}

	final public Number add(Number x, Number y){
		return num(Numbers.add(x.longValue(),y.longValue()));
	}

	final public Number addP(Number x, Number y){
		long lx = x.longValue(), ly = y.longValue();
		long ret = lx + ly;
		if ((ret ^ lx) < 0 && (ret ^ ly) < 0)
			return BIGINT_OPS.add(x, y);
		return num(ret);
	}

	final public Number multiply(Number x, Number y){
		return num(Numbers.multiply(x.longValue(), y.longValue()));
	}

	final public Number multiplyP(Number x, Number y){
		long lx = x.longValue(), ly = y.longValue();
    if (lx == Long.MIN_VALUE && ly < 0)
      return BIGINT_OPS.multiply(x, y);
		long ret = lx * ly;
		if (ly != 0 && ret/ly != lx)
			return BIGINT_OPS.multiply(x, y);
		return num(ret);
	}
	static long gcd(long u, long v){
		while(v != 0)
			{
			long r = u % v;
			u = v;
			v = r;
			}
		return u;
	}

	public Number divide(Number x, Number y){
		long n = x.longValue();
		long val = y.longValue();
		long gcd = gcd(n, val);
		if(gcd == 0)
			return num(0);

		n = n / gcd;
		long d = val / gcd;
		if(d == 1)
			return num(n);
		if(d < 0)
			{
			n = -n;
			d = -d;
			}
		return new Ratio(BigInteger.valueOf(n), BigInteger.valueOf(d));
	}

	public Number quotient(Number x, Number y){
		return num(x.longValue() / y.longValue());
	}

	public Number remainder(Number x, Number y){
		return num(x.longValue() % y.longValue());
	}

	public boolean equiv(Number x, Number y){
		return x.longValue() == y.longValue();
	}

	public boolean lt(Number x, Number y){
		return x.longValue() < y.longValue();
	}

	public boolean lte(Number x, Number y){
		return x.longValue() <= y.longValue();
	}

	public boolean gte(Number x, Number y){
		return x.longValue() >= y.longValue();
	}

	//public Number subtract(Number x, Number y);
	final public Number negate(Number x){
		long val = x.longValue();
		return num(Numbers.minus(val));
	}

	final public Number negateP(Number x){
		long val = x.longValue();
		if(val > Long.MIN_VALUE)
			return num(-val);
		return BigInt.fromBigInteger(BigInteger.valueOf(val).negate());
	}
	public Number inc(Number x){
		long val = x.longValue();
		return num(Numbers.inc(val));
	}

	public Number incP(Number x){
		long val = x.longValue();
		if(val < Long.MAX_VALUE)
			return num(val + 1);
		return BIGINT_OPS.inc(x);
	}

	public Number dec(Number x){
		long val = x.longValue();
		return num(Numbers.dec(val));
	}

	public Number decP(Number x){
		long val = x.longValue();
		if(val > Long.MIN_VALUE)
			return num(val - 1);
		return BIGINT_OPS.dec(x);
	}
}

final static class DoubleOps extends OpsP{
	public Ops combine(Ops y){
		return y.opsWith(this);
	}

	final public Ops opsWith(LongOps x){
		return this;
	}

	final public Ops opsWith(DoubleOps x){
		return this;
	}

	final public Ops opsWith(RatioOps x){
		return this;
	}

	final public Ops opsWith(BigIntOps x){
		return this;
	}

	final public Ops opsWith(BigDecimalOps x){
		return this;
	}

	public boolean isZero(Number x){
		return x.doubleValue() == 0;
	}

	public boolean isPos(Number x){
		return x.doubleValue() > 0;
	}

	public boolean isNeg(Number x){
		return x.doubleValue() < 0;
	}

	final public Number add(Number x, Number y){
		return Double.valueOf(x.doubleValue() + y.doubleValue());
	}

	final public Number multiply(Number x, Number y){
		return Double.valueOf(x.doubleValue() * y.doubleValue());
	}

	public Number divide(Number x, Number y){
		return Double.valueOf(x.doubleValue() / y.doubleValue());
	}

	public Number quotient(Number x, Number y){
		return Numbers.quotient(x.doubleValue(), y.doubleValue());
	}

	public Number remainder(Number x, Number y){
		return Numbers.remainder(x.doubleValue(), y.doubleValue());
	}

	public boolean equiv(Number x, Number y){
		return x.doubleValue() == y.doubleValue();
	}

	public boolean lt(Number x, Number y){
		return x.doubleValue() < y.doubleValue();
	}

	public boolean lte(Number x, Number y){
		return x.doubleValue() <= y.doubleValue();
	}

	public boolean gte(Number x, Number y){
		return x.doubleValue() >= y.doubleValue();
	}

	//public Number subtract(Number x, Number y);
	final public Number negate(Number x){
		return Double.valueOf(-x.doubleValue());
	}

	public Number inc(Number x){
		return Double.valueOf(x.doubleValue() + 1);
	}

	public Number dec(Number x){
		return Double.valueOf(x.doubleValue() - 1);
	}
}

final static class RatioOps extends OpsP{
	public Ops combine(Ops y){
		return y.opsWith(this);
	}

	final public Ops opsWith(LongOps x){
		return this;
	}

	final public Ops opsWith(DoubleOps x){
		return DOUBLE_OPS;
	}

	final public Ops opsWith(RatioOps x){
		return this;
	}

	final public Ops opsWith(BigIntOps x){
		return this;
	}

	final public Ops opsWith(BigDecimalOps x){
		return BIGDECIMAL_OPS;
	}

	public boolean isZero(Number x){
		Ratio r = (Ratio) x;
		return r.numerator.signum() == 0;
	}

	public boolean isPos(Number x){
		Ratio r = (Ratio) x;
		return r.numerator.signum() > 0;
	}

	public boolean isNeg(Number x){
		Ratio r = (Ratio) x;
		return r.numerator.signum() < 0;
	}

	static Number normalizeRet(Number ret, Number x, Number y){
//		if(ret instanceof BigInteger && !(x instanceof BigInteger || y instanceof BigInteger))
//			{
//			return reduceBigInt((BigInteger) ret);
//			}
		return ret;
	}

	final public Number add(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		Number ret = divide(ry.numerator.multiply(rx.denominator)
				.add(rx.numerator.multiply(ry.denominator))
				, ry.denominator.multiply(rx.denominator));
		return normalizeRet(ret, x, y);
	}

	final public Number multiply(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		Number ret = Numbers.divide(ry.numerator.multiply(rx.numerator)
				, ry.denominator.multiply(rx.denominator));
		return normalizeRet(ret, x, y);
	}

	public Number divide(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		Number ret = Numbers.divide(ry.denominator.multiply(rx.numerator)
				, ry.numerator.multiply(rx.denominator));
		return normalizeRet(ret, x, y);
	}

	public Number quotient(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		BigInteger q = rx.numerator.multiply(ry.denominator).divide(
				rx.denominator.multiply(ry.numerator));
		return normalizeRet(BigInt.fromBigInteger(q), x, y);
	}

	public Number remainder(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		BigInteger q = rx.numerator.multiply(ry.denominator).divide(
				rx.denominator.multiply(ry.numerator));
		Number ret = Numbers.minus(x, Numbers.multiply(q, y));
		return normalizeRet(ret, x, y);
	}

	public boolean equiv(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		return rx.numerator.equals(ry.numerator)
		       && rx.denominator.equals(ry.denominator);
	}

	public boolean lt(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		return Numbers.lt(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
	}

	public boolean lte(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		return Numbers.lte(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
	}

	public boolean gte(Number x, Number y){
		Ratio rx = toRatio(x);
		Ratio ry = toRatio(y);
		return Numbers.gte(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
	}

	//public Number subtract(Number x, Number y);
	final public Number negate(Number x){
		Ratio r = (Ratio) x;
		return new Ratio(r.numerator.negate(), r.denominator);
	}

	public Number inc(Number x){
		return Numbers.add(x, 1);
	}

	public Number dec(Number x){
		return Numbers.add(x, -1);
	}

}

final static class BigIntOps extends OpsP{
	public Ops combine(Ops y){
		return y.opsWith(this);
	}

	final public Ops opsWith(LongOps x){
		return this;
	}

	final public Ops opsWith(DoubleOps x){
		return DOUBLE_OPS;
	}

	final public Ops opsWith(RatioOps x){
		return RATIO_OPS;
	}

	final public Ops opsWith(BigIntOps x){
		return this;
	}

	final public Ops opsWith(BigDecimalOps x){
		return BIGDECIMAL_OPS;
	}

	public boolean isZero(Number x){
		BigInt bx = toBigInt(x);
		if(bx.bipart == null)
			return bx.lpart == 0;
		return bx.bipart.signum() == 0;
	}

	public boolean isPos(Number x){
		BigInt bx = toBigInt(x);
		if(bx.bipart == null)
			return bx.lpart > 0;
		return bx.bipart.signum() > 0;
	}

	public boolean isNeg(Number x){
		BigInt bx = toBigInt(x);
		if(bx.bipart == null)
			return bx.lpart < 0;
		return bx.bipart.signum() < 0;
	}

	final public Number add(Number x, Number y){
        return toBigInt(x).add(toBigInt(y));
	}

	final public Number multiply(Number x, Number y){
        return toBigInt(x).multiply(toBigInt(y));
	}

	public Number divide(Number x, Number y){
		return Numbers.divide(toBigInteger(x), toBigInteger(y));
	}

	public Number quotient(Number x, Number y){
        return toBigInt(x).quotient(toBigInt(y));
	}

	public Number remainder(Number x, Number y){
        return toBigInt(x).remainder(toBigInt(y));
	}

	public boolean equiv(Number x, Number y){
		return toBigInt(x).equals(toBigInt(y));
	}

	public boolean lt(Number x, Number y){
        return toBigInt(x).lt(toBigInt(y));
	}

	public boolean lte(Number x, Number y){
		return toBigInteger(x).compareTo(toBigInteger(y)) <= 0;
	}

	public boolean gte(Number x, Number y){
		return toBigInteger(x).compareTo(toBigInteger(y)) >= 0;
	}

	//public Number subtract(Number x, Number y);
	final public Number negate(Number x){
		return BigInt.fromBigInteger(toBigInteger(x).negate());
	}

	public Number inc(Number x){
		BigInteger bx = toBigInteger(x);
		return BigInt.fromBigInteger(bx.add(BigInteger.ONE));
	}

	public Number dec(Number x){
		BigInteger bx = toBigInteger(x);
		return BigInt.fromBigInteger(bx.subtract(BigInteger.ONE));
	}
}


final static class BigDecimalOps extends OpsP{
	final static Var MATH_CONTEXT = RT.MATH_CONTEXT;

	public Ops combine(Ops y){
		return y.opsWith(this);
	}

	final public Ops opsWith(LongOps x){
		return this;
	}

	final public Ops opsWith(DoubleOps x){
		return DOUBLE_OPS;
	}

	final public Ops opsWith(RatioOps x){
		return this;
	}

	final public Ops opsWith(BigIntOps x){
		return this;
	}

	final public Ops opsWith(BigDecimalOps x){
		return this;
	}

	public boolean isZero(Number x){
		BigDecimal bx = (BigDecimal) x;
		return bx.signum() == 0;
	}

	public boolean isPos(Number x){
		BigDecimal bx = (BigDecimal) x;
		return bx.signum() > 0;
	}

	public boolean isNeg(Number x){
		BigDecimal bx = (BigDecimal) x;
		return bx.signum() < 0;
	}

	final public Number add(Number x, Number y){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? toBigDecimal(x).add(toBigDecimal(y))
		       : toBigDecimal(x).add(toBigDecimal(y), mc);
	}

	final public Number multiply(Number x, Number y){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? toBigDecimal(x).multiply(toBigDecimal(y))
		       : toBigDecimal(x).multiply(toBigDecimal(y), mc);
	}

	public Number divide(Number x, Number y){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? toBigDecimal(x).divide(toBigDecimal(y))
		       : toBigDecimal(x).divide(toBigDecimal(y), mc);
	}

	public Number quotient(Number x, Number y){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? toBigDecimal(x).divideToIntegralValue(toBigDecimal(y))
		       : toBigDecimal(x).divideToIntegralValue(toBigDecimal(y), mc);
	}

	public Number remainder(Number x, Number y){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? toBigDecimal(x).remainder(toBigDecimal(y))
		       : toBigDecimal(x).remainder(toBigDecimal(y), mc);
	}

	public boolean equiv(Number x, Number y){
		return toBigDecimal(x).compareTo(toBigDecimal(y)) == 0;
	}

	public boolean lt(Number x, Number y){
		return toBigDecimal(x).compareTo(toBigDecimal(y)) < 0;
	}

	public boolean lte(Number x, Number y){
		return toBigDecimal(x).compareTo(toBigDecimal(y)) <= 0;
	}

	public boolean gte(Number x, Number y){
		return toBigDecimal(x).compareTo(toBigDecimal(y)) >= 0;
	}

	//public Number subtract(Number x, Number y);
	final public Number negate(Number x){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		return mc == null
		       ? ((BigDecimal) x).negate()
		       : ((BigDecimal) x).negate(mc);
	}

	public Number inc(Number x){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		BigDecimal bx = (BigDecimal) x;
		return mc == null
		       ? bx.add(BigDecimal.ONE)
		       : bx.add(BigDecimal.ONE, mc);
	}

	public Number dec(Number x){
		MathContext mc = (MathContext) MATH_CONTEXT.deref();
		BigDecimal bx = (BigDecimal) x;
		return mc == null
		       ? bx.subtract(BigDecimal.ONE)
		       : bx.subtract(BigDecimal.ONE, mc);
	}
}

static final LongOps LONG_OPS = new LongOps();
static final DoubleOps DOUBLE_OPS = new DoubleOps();
static final RatioOps RATIO_OPS = new RatioOps();
static final BigIntOps BIGINT_OPS = new BigIntOps();
static final BigDecimalOps BIGDECIMAL_OPS = new BigDecimalOps();

static public enum Category {INTEGER, FLOATING, DECIMAL, RATIO};

static Ops ops(Object x){
	Class xc = x.getClass();

	if(xc == Long.class)
		return LONG_OPS;
	else if(xc == Double.class)
		return DOUBLE_OPS;
	else if(xc == Integer.class)
		return LONG_OPS;
	else if(xc == Float.class)
		return DOUBLE_OPS;
	else if(xc == BigInt.class)
		return BIGINT_OPS;
	else if(xc == BigInteger.class)
		return BIGINT_OPS;
	else if(xc == Ratio.class)
		return RATIO_OPS;
	else if(xc == BigDecimal.class)
		return BIGDECIMAL_OPS;
	else
		return LONG_OPS;
}

@WarnBoxedMath(false)
static int hasheq(Number x){
	Class xc = x.getClass();

	if(xc == Long.class
		|| xc == Integer.class
		|| xc == Short.class
		|| xc == Byte.class
		|| (xc == BigInteger.class && lte(x, Long.MAX_VALUE) && gte(x,Long.MIN_VALUE)))
		{
		long lpart = x.longValue();
		return Murmur3.hashLong(lpart);
		//return (int) (lpart ^ (lpart >>> 32));
		}
	if(xc == BigDecimal.class)
		{
		// stripTrailingZeros() to make all numerically equal
		// BigDecimal values come out the same before calling
		// hashCode.  Special check for 0 because
		// stripTrailingZeros() does not do anything to values
		// equal to 0 with different scales.
		if (isZero(x))
			return BigDecimal.ZERO.hashCode();
		else
			{
			BigDecimal tmp = ((BigDecimal) x).stripTrailingZeros();
			return tmp.hashCode();
			}
		}
	return x.hashCode();
}

static Category category(Object x){
	Class xc = x.getClass();

	if(xc == Integer.class)
		return Category.INTEGER;
	else if(xc == Double.class)
		return Category.FLOATING;
	else if(xc == Long.class)
		return Category.INTEGER;
	else if(xc == Float.class)
		return Category.FLOATING;
	else if(xc == BigInt.class)
		return Category.INTEGER;
	else if(xc == Ratio.class)
		return Category.RATIO;
	else if(xc == BigDecimal.class)
		return Category.DECIMAL;
	else
		return Category.INTEGER;
}

static long bitOpsCast(Object x){
	Class xc = x.getClass();

	if(xc == Long.class
	        || xc == Integer.class
	        || xc == Short.class
	        || xc == Byte.class)
		return RT.longCast(x);
	// no bignums, no decimals
	throw new IllegalArgumentException("bit operation not supported for: " + xc);
}

	@WarnBoxedMath(false)
	static public float[] float_array(int size, Object init){
		float[] ret = new float[size];
		if(init instanceof Number)
			{
			float f = ((Number) init).floatValue();
			for(int i = 0; i < ret.length; i++)
				ret[i] = f;
			}
		else
			{
			ISeq s = RT.seq(init);
			for(int i = 0; i < size && s != null; i++, s = s.next())
				ret[i] = ((Number) s.first()).floatValue();
			}
		return ret;
	}

	@WarnBoxedMath(false)
	static public float[] float_array(Object sizeOrSeq){
		if(sizeOrSeq instanceof Number)
			return new float[((Number) sizeOrSeq).intValue()];
		else
			{
			ISeq s = RT.seq(sizeOrSeq);
			int size = RT.count(s);
			float[] ret = new float[size];
			for(int i = 0; i < size && s != null; i++, s = s.next())
				ret[i] = ((Number) s.first()).floatValue();
			return ret;
			}
	}

@WarnBoxedMath(false)
static public double[] double_array(int size, Object init){
	double[] ret = new double[size];
	if(init instanceof Number)
		{
		double f = ((Number) init).doubleValue();
		for(int i = 0; i < ret.length; i++)
			ret[i] = f;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).doubleValue();
		}
	return ret;
}

@WarnBoxedMath(false)
static public double[] double_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new double[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		double[] ret = new double[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).doubleValue();
		return ret;
		}
}

@WarnBoxedMath(false)
static public int[] int_array(int size, Object init){
	int[] ret = new int[size];
	if(init instanceof Number)
		{
		int f = ((Number) init).intValue();
		for(int i = 0; i < ret.length; i++)
			ret[i] = f;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).intValue();
		}
	return ret;
}

@WarnBoxedMath(false)
static public int[] int_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new int[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		int[] ret = new int[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).intValue();
		return ret;
		}
}

@WarnBoxedMath(false)
static public long[] long_array(int size, Object init){
	long[] ret = new long[size];
	if(init instanceof Number)
		{
		long f = ((Number) init).longValue();
		for(int i = 0; i < ret.length; i++)
			ret[i] = f;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).longValue();
		}
	return ret;
}

@WarnBoxedMath(false)
static public long[] long_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new long[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		long[] ret = new long[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).longValue();
		return ret;
		}
}

@WarnBoxedMath(false)
static public short[] short_array(int size, Object init){
	short[] ret = new short[size];
	if(init instanceof Short)
		{
		short s = (Short) init;
		for(int i = 0; i < ret.length; i++)
			ret[i] = s;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).shortValue();
		}
	return ret;
}

@WarnBoxedMath(false)
static public short[] short_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new short[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		short[] ret = new short[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).shortValue();
		return ret;
		}
}

@WarnBoxedMath(false)
static public char[] char_array(int size, Object init){
	char[] ret = new char[size];
	if(init instanceof Character)
		{
		char c = (Character) init;
		for(int i = 0; i < ret.length; i++)
			ret[i] = c;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = (Character) s.first();
		}
	return ret;
}

@WarnBoxedMath(false)
static public char[] char_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new char[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		char[] ret = new char[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = (Character) s.first();
		return ret;
		}
}

@WarnBoxedMath(false)
static public byte[] byte_array(int size, Object init){
	byte[] ret = new byte[size];
	if(init instanceof Byte)
		{
		byte b = (Byte) init;
		for(int i = 0; i < ret.length; i++)
			ret[i] = b;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).byteValue();
		}
	return ret;
}

@WarnBoxedMath(false)
static public byte[] byte_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new byte[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		byte[] ret = new byte[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = ((Number) s.first()).byteValue();
		return ret;
		}
}

@WarnBoxedMath(false)
static public boolean[] boolean_array(int size, Object init){
	boolean[] ret = new boolean[size];
	if(init instanceof Boolean)
		{
		boolean b = (Boolean) init;
		for(int i = 0; i < ret.length; i++)
			ret[i] = b;
		}
	else
		{
		ISeq s = RT.seq(init);
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = (Boolean)s.first();
		}
	return ret;
}

@WarnBoxedMath(false)
static public boolean[] boolean_array(Object sizeOrSeq){
	if(sizeOrSeq instanceof Number)
		return new boolean[((Number) sizeOrSeq).intValue()];
	else
		{
		ISeq s = RT.seq(sizeOrSeq);
		int size = RT.count(s);
		boolean[] ret = new boolean[size];
		for(int i = 0; i < size && s != null; i++, s = s.next())
			ret[i] = (Boolean)s.first();
		return ret;
		}
}

@WarnBoxedMath(false)
static public boolean[] booleans(Object array){
	return (boolean[]) array;
}

@WarnBoxedMath(false)
static public byte[] bytes(Object array){
	return (byte[]) array;
}

@WarnBoxedMath(false)
static public char[] chars(Object array){
	return (char[]) array;
}

@WarnBoxedMath(false)
static public short[] shorts(Object array){
	return (short[]) array;
}

@WarnBoxedMath(false)
static public float[] floats(Object array){
	return (float[]) array;
}

@WarnBoxedMath(false)
static public double[] doubles(Object array){
	return (double[]) array;
}

@WarnBoxedMath(false)
static public int[] ints(Object array){
	return (int[]) array;
}

@WarnBoxedMath(false)
static public long[] longs(Object array){
	return (long[]) array;
}

static public Number num(Object x){
	return (Number) x;
}

static public Number num(float x){
	return Float.valueOf(x);
}

static public Number num(double x){
	return Double.valueOf(x);
}

static public double add(double x, double y){
	return x + y;
}

static public double addP(double x, double y){
	return x + y;
}

static public double minus(double x, double y){
	return x - y;
}

static public double minusP(double x, double y){
	return x - y;
}

static public double minus(double x){
	return -x;
}

static public double minusP(double x){
	return -x;
}

static public double inc(double x){
	return x + 1;
}

static public double incP(double x){
	return x + 1;
}

static public double dec(double x){
	return x - 1;
}

static public double decP(double x){
	return x - 1;
}

static public double multiply(double x, double y){
	return x * y;
}

static public double multiplyP(double x, double y){
	return x * y;
}

static public double divide(double x, double y){
	return x / y;
}

static public boolean equiv(double x, double y){
	return x == y;
}

static public boolean lt(double x, double y){
	return x < y;
}

static public boolean lte(double x, double y){
	return x <= y;
}

static public boolean gt(double x, double y){
	return x > y;
}

static public boolean gte(double x, double y){
	return x >= y;
}

static public boolean isPos(double x){
	return x > 0;
}

static public boolean isNeg(double x){
	return x < 0;
}

static public boolean isZero(double x){
	return x == 0;
}

static int throwIntOverflow(){
	throw new ArithmeticException("integer overflow");
}

//static public Number num(int x){
//	return Integer.valueOf(x);
//}

static public int unchecked_int_add(int x, int y){
	return x + y;
}

static public int unchecked_int_subtract(int x, int y){
	return x - y;
}

static public int unchecked_int_negate(int x){
	return -x;
}

static public int unchecked_int_inc(int x){
	return x + 1;
}

static public int unchecked_int_dec(int x){
	return x - 1;
}

static public int unchecked_int_multiply(int x, int y){
	return x * y;
}

//static public int add(int x, int y){
//	int ret = x + y;
//	if ((ret ^ x) < 0 && (ret ^ y) < 0)
//		return throwIntOverflow();
//	return ret;
//}

//static public int not(int x){
//	return ~x;
//}

static public long not(Object x){
    return not(bitOpsCast(x));
}
static public long not(long x){
	return ~x;
}
//static public int and(int x, int y){
//	return x & y;
//}

static public long and(Object x, Object y){
    return and(bitOpsCast(x),bitOpsCast(y));
}
static public long and(Object x, long y){
    return and(bitOpsCast(x),y);
}
static public long and(long x, Object y){
    return and(x,bitOpsCast(y));
}
static public long and(long x, long y){
	return x & y;
}

//static public int or(int x, int y){
//	return x | y;
//}

static public long or(Object x, Object y){
    return or(bitOpsCast(x),bitOpsCast(y));
}
static public long or(Object x, long y){
    return or(bitOpsCast(x),y);
}
static public long or(long x, Object y){
    return or(x,bitOpsCast(y));
}
static public long or(long x, long y){
    return x | y;
}

//static public int xor(int x, int y){
//	return x ^ y;
//}

static public long xor(Object x, Object y){
    return xor(bitOpsCast(x),bitOpsCast(y));
}
static public long xor(Object x, long y){
    return xor(bitOpsCast(x),y);
}
static public long xor(long x, Object y){
    return xor(x,bitOpsCast(y));
}
static public long xor(long x, long y){
    return x ^ y;
}

static public long andNot(Object x, Object y){
    return andNot(bitOpsCast(x),bitOpsCast(y));
}
static public long andNot(Object x, long y){
    return andNot(bitOpsCast(x),y);
}
static public long andNot(long x, Object y){
    return andNot(x,bitOpsCast(y));
}
static public long andNot(long x, long y){
    return x & ~y;
}

static public long clearBit(Object x, Object y){
    return clearBit(bitOpsCast(x),bitOpsCast(y));
}
static public long clearBit(Object x, long y){
    return clearBit(bitOpsCast(x),y);
}
static public long clearBit(long x, Object y){
    return clearBit(x,bitOpsCast(y));
}
static public long clearBit(long x, long n){
    return x & ~(1L << n);
}

static public long setBit(Object x, Object y){
    return setBit(bitOpsCast(x),bitOpsCast(y));
}
static public long setBit(Object x, long y){
    return setBit(bitOpsCast(x),y);
}
static public long setBit(long x, Object y){
    return setBit(x,bitOpsCast(y));
}
static public long setBit(long x, long n){
    return x | (1L << n);
}

static public long flipBit(Object x, Object y){
    return flipBit(bitOpsCast(x),bitOpsCast(y));
}
static public long flipBit(Object x, long y){
    return flipBit(bitOpsCast(x),y);
}
static public long flipBit(long x, Object y){
    return flipBit(x,bitOpsCast(y));
}
static public long flipBit(long x, long n){
    return x ^ (1L << n);
}

static public boolean testBit(Object x, Object y){
    return testBit(bitOpsCast(x),bitOpsCast(y));
}
static public boolean testBit(Object x, long y){
    return testBit(bitOpsCast(x),y);
}
static public boolean testBit(long x, Object y){
    return testBit(x,bitOpsCast(y));
}
static public boolean testBit(long x, long n){
    return (x & (1L << n)) != 0;
}

//static public int minus(int x, int y){
//	int ret = x - y;
//	if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
//		return throwIntOverflow();
//	return ret;
//}

//static public int minus(int x){
//	if(x == Integer.MIN_VALUE)
//		return throwIntOverflow();
//	return -x;
//}

//static public int inc(int x){
//	if(x == Integer.MAX_VALUE)
//		return throwIntOverflow();
//	return x + 1;
//}

//static public int dec(int x){
//	if(x == Integer.MIN_VALUE)
//		return throwIntOverflow();
//	return x - 1;
//}

//static public int multiply(int x, int y){
//	int ret = x * y;
//	if (y != 0 && ret/y != x)
//		return throwIntOverflow();
//	return ret;
//}

static public int unchecked_int_divide(int x, int y){
	return x / y;
}

static public int unchecked_int_remainder(int x, int y){
	return x % y;
}

//static public boolean equiv(int x, int y){
//	return x == y;
//}

//static public boolean lt(int x, int y){
//	return x < y;
//}

//static public boolean lte(int x, int y){
//	return x <= y;
//}

//static public boolean gt(int x, int y){
//	return x > y;
//}

//static public boolean gte(int x, int y){
//	return x >= y;
//}

//static public boolean isPos(int x){
//	return x > 0;
//}

//static public boolean isNeg(int x){
//	return x < 0;
//}

//static public boolean isZero(int x){
//	return x == 0;
//}

static public Number num(long x){
	return Long.valueOf(x);
}

static public long unchecked_add(long x, long y){return x + y;}
static public long unchecked_minus(long x, long y){return x - y;}
static public long unchecked_multiply(long x, long y){return x * y;}
static public long unchecked_minus(long x){return -x;}
static public long unchecked_inc(long x){return x + 1;}
static public long unchecked_dec(long x){return x - 1;}

static public Number unchecked_add(Object x, Object y){return add(x,y);}
static public Number unchecked_minus(Object x, Object y){return minus(x,y);}
static public Number unchecked_multiply(Object x, Object y){return multiply(x,y);}
static public Number unchecked_minus(Object x){return minus(x);}
static public Number unchecked_inc(Object x){return inc(x);}
static public Number unchecked_dec(Object x){return dec(x);}

static public double unchecked_add(double x, double y){return add(x,y);}
static public double unchecked_minus(double x, double y){return minus(x,y);}
static public double unchecked_multiply(double x, double y){return multiply(x,y);}
static public double unchecked_minus(double x){return minus(x);}
static public double unchecked_inc(double x){return inc(x);}
static public double unchecked_dec(double x){return dec(x);}

static public double unchecked_add(double x, Object y){return add(x,y);}
static public double unchecked_minus(double x, Object y){return minus(x,y);}
static public double unchecked_multiply(double x, Object y){return multiply(x,y);}
static public double unchecked_add(Object x, double y){return add(x,y);}
static public double unchecked_minus(Object x, double y){return minus(x,y);}
static public double unchecked_multiply(Object x, double y){return multiply(x,y);}

static public double unchecked_add(double x, long y){return add(x,y);}
static public double unchecked_minus(double x, long y){return minus(x,y);}
static public double unchecked_multiply(double x, long y){return multiply(x,y);}
static public double unchecked_add(long x, double y){return add(x,y);}
static public double unchecked_minus(long x, double y){return minus(x,y);}
static public double unchecked_multiply(long x, double y){return multiply(x,y);}

static public Number unchecked_add(long x, Object y){return add(x,y);}
static public Number unchecked_minus(long x, Object y){return minus(x,y);}
static public Number unchecked_multiply(long x, Object y){return multiply(x,y);}
static public Number unchecked_add(Object x, long y){return add(x,y);}
static public Number unchecked_minus(Object x, long y){return minus(x,y);}
static public Number unchecked_multiply(Object x, long y){return multiply(x,y);}

static public Number quotient(double x, Object y){return quotient((Object)x,y);}
static public Number quotient(Object x, double y){return quotient(x,(Object)y);}
static public Number quotient(long x, Object y){return quotient((Object)x,y);}
static public Number quotient(Object x, long y){return quotient(x,(Object)y);}
static public double quotient(double x, long y){return quotient(x,(double)y);}
static public double quotient(long x, double y){return quotient((double)x,y);}

static public Number remainder(double x, Object y){return remainder((Object)x,y);}
static public Number remainder(Object x, double y){return remainder(x,(Object)y);}
static public Number remainder(long x, Object y){return remainder((Object)x,y);}
static public Number remainder(Object x, long y){return remainder(x,(Object)y);}
static public double remainder(double x, long y){return remainder(x,(double)y);}
static public double remainder(long x, double y){return remainder((double)x,y);}

static public long add(long x, long y){
	long ret = x + y;
	if ((ret ^ x) < 0 && (ret ^ y) < 0)
		return throwIntOverflow();
	return ret;
}

static public Number addP(long x, long y){
	long ret = x + y;
	if ((ret ^ x) < 0 && (ret ^ y) < 0)
		return addP((Number)x,(Number)y);
	return num(ret);
}

static public long minus(long x, long y){
	long ret = x - y;
	if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
		return throwIntOverflow();
	return ret;
}

static public Number minusP(long x, long y){
	long ret = x - y;
	if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
		return minusP((Number)x,(Number)y);
	return num(ret);
}

static public long minus(long x){
	if(x == Long.MIN_VALUE)
		return throwIntOverflow();
	return -x;
}

static public Number minusP(long x){
	if(x == Long.MIN_VALUE)
		return BigInt.fromBigInteger(BigInteger.valueOf(x).negate());
	return num(-x);
}

static public long inc(long x){
	if(x == Long.MAX_VALUE)
		return throwIntOverflow();
	return x + 1;
}

static public Number incP(long x){
	if(x == Long.MAX_VALUE)
		return BIGINT_OPS.inc(x);
	return num(x + 1);
}

static public long dec(long x){
	if(x == Long.MIN_VALUE)
		return throwIntOverflow();
	return x - 1;
}

static public Number decP(long x){
	if(x == Long.MIN_VALUE)
		return BIGINT_OPS.dec(x);
	return num(x - 1);
}


static public long multiply(long x, long y){
  if (x == Long.MIN_VALUE && y < 0)
		return throwIntOverflow();
	long ret = x * y;
	if (y != 0 && ret/y != x)
		return throwIntOverflow();
	return ret;
}

static public Number multiplyP(long x, long y){
  if (x == Long.MIN_VALUE && y < 0)
		return multiplyP((Number)x,(Number)y);
	long ret = x * y;
	if (y != 0 && ret/y != x)
		return multiplyP((Number)x,(Number)y);
	return num(ret);
}

static public long quotient(long x, long y){
	return x / y;
}

static public long remainder(long x, long y){
	return x % y;
}

static public boolean equiv(long x, long y){
	return x == y;
}

static public boolean lt(long x, long y){
	return x < y;
}

static public boolean lte(long x, long y){
	return x <= y;
}

static public boolean gt(long x, long y){
	return x > y;
}

static public boolean gte(long x, long y){
	return x >= y;
}

static public boolean isPos(long x){
	return x > 0;
}

static public boolean isNeg(long x){
	return x < 0;
}

static public boolean isZero(long x){
	return x == 0;
}

/*
static public class F{
	static public float add(float x, float y){
		return x + y;
	}

	static public float subtract(float x, float y){
		return x - y;
	}

	static public float negate(float x){
		return -x;
	}

	static public float inc(float x){
		return x + 1;
	}

	static public float dec(float x){
		return x - 1;
	}

	static public float multiply(float x, float y){
		return x * y;
	}

	static public float divide(float x, float y){
		return x / y;
	}

	static public boolean equiv(float x, float y){
		return x == y;
	}

	static public boolean lt(float x, float y){
		return x < y;
	}

	static public boolean lte(float x, float y){
		return x <= y;
	}

	static public boolean gt(float x, float y){
		return x > y;
	}

	static public boolean gte(float x, float y){
		return x >= y;
	}

	static public boolean pos(float x){
		return x > 0;
	}

	static public boolean neg(float x){
		return x < 0;
	}

	static public boolean zero(float x){
		return x == 0;
	}

	static public float aget(float[] xs, int i){
		return xs[i];
	}

	static public float aset(float[] xs, int i, float v){
		xs[i] = v;
		return v;
	}

	static public int alength(float[] xs){
		return xs.length;
	}

	static public float[] aclone(float[] xs){
		return xs.clone();
	}

	static public float[] vec(int size, Object init){
		float[] ret = new float[size];
		if(init instanceof Number)
			{
			float f = ((Number) init).floatValue();
			for(int i = 0; i < ret.length; i++)
				ret[i] = f;
			}
		else
			{
			ISeq s = RT.seq(init);
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).floatValue();
			}
		return ret;
	}

	static public float[] vec(Object sizeOrSeq){
		if(sizeOrSeq instanceof Number)
			return new float[((Number) sizeOrSeq).intValue()];
		else
			{
			ISeq s = RT.seq(sizeOrSeq);
			int size = s.count();
			float[] ret = new float[size];
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).intValue();
			return ret;
			}
	}


	static public float[] vsadd(float[] x, float y){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += y;
		return xs;
	}

	static public float[] vssub(float[] x, float y){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= y;
		return xs;
	}

	static public float[] vsdiv(float[] x, float y){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= y;
		return xs;
	}

	static public float[] vsmul(float[] x, float y){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= y;
		return xs;
	}

	static public float[] svdiv(float y, float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = y / xs[i];
		return xs;
	}

	static public float[] vsmuladd(float[] x, float y, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + zs[i];
		return xs;
	}

	static public float[] vsmulsub(float[] x, float y, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - zs[i];
		return xs;
	}

	static public float[] vsmulsadd(float[] x, float y, float z){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + z;
		return xs;
	}

	static public float[] vsmulssub(float[] x, float y, float z){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - z;
		return xs;
	}

	static public float[] vabs(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.abs(xs[i]);
		return xs;
	}

	static public float[] vnegabs(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -Math.abs(xs[i]);
		return xs;
	}

	static public float[] vneg(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -xs[i];
		return xs;
	}

	static public float[] vsqr(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= xs[i];
		return xs;
	}

	static public float[] vsignedsqr(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= Math.abs(xs[i]);
		return xs;
	}

	static public float[] vclip(float[] x, float low, float high){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				xs[i] = low;
			else if(xs[i] > high)
				xs[i] = high;
			}
		return xs;
	}

	static public IPersistentVector vclipcounts(float[] x, float low, float high){
		final float[] xs = x.clone();
		int lowc = 0;
		int highc = 0;

		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				{
				++lowc;
				xs[i] = low;
				}
			else if(xs[i] > high)
				{
				++highc;
				xs[i] = high;
				}
			}
		return RT.vector(xs, lowc, highc);
	}

	static public float[] vthresh(float[] x, float thresh, float otherwise){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < thresh)
				xs[i] = otherwise;
			}
		return xs;
	}

	static public float[] vreverse(float[] x){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[xs.length - i - 1];
		return xs;
	}

	static public float[] vrunningsum(float[] x){
		final float[] xs = x.clone();
		for(int i = 1; i < xs.length; i++)
			xs[i] = xs[i - 1] + xs[i];
		return xs;
	}

	static public float[] vsort(float[] x){
		final float[] xs = x.clone();
		Arrays.sort(xs);
		return xs;
	}

	static public float vdot(float[] xs, float[] ys){
		float ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * ys[i];
		return ret;
	}

	static public float vmax(float[] xs){
		if(xs.length == 0)
			return 0;
		float ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.max(ret, xs[i]);
		return ret;
	}

	static public float vmin(float[] xs){
		if(xs.length == 0)
			return 0;
		float ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.min(ret, xs[i]);
		return ret;
	}

	static public float vmean(float[] xs){
		if(xs.length == 0)
			return 0;
		return vsum(xs) / xs.length;
	}

	static public double vrms(float[] xs){
		if(xs.length == 0)
			return 0;
		float ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * xs[i];
		return Math.sqrt(ret / xs.length);
	}

	static public float vsum(float[] xs){
		float ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i];
		return ret;
	}

	static public boolean vequiv(float[] xs, float[] ys){
		return Arrays.equals(xs, ys);
	}

	static public float[] vadd(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += ys[i];
		return xs;
	}

	static public float[] vsub(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= ys[i];
		return xs;
	}

	static public float[] vaddmul(float[] x, float[] ys, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * zs[i];
		return xs;
	}

	static public float[] vsubmul(float[] x, float[] ys, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * zs[i];
		return xs;
	}

	static public float[] vaddsmul(float[] x, float[] ys, float z){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * z;
		return xs;
	}

	static public float[] vsubsmul(float[] x, float[] ys, float z){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * z;
		return xs;
	}

	static public float[] vmulsadd(float[] x, float[] ys, float z){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + z;
		return xs;
	}

	static public float[] vdiv(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= ys[i];
		return xs;
	}

	static public float[] vmul(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= ys[i];
		return xs;
	}

	static public float[] vmuladd(float[] x, float[] ys, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + zs[i];
		return xs;
	}

	static public float[] vmulsub(float[] x, float[] ys, float[] zs){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) - zs[i];
		return xs;
	}

	static public float[] vmax(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.max(xs[i], ys[i]);
		return xs;
	}

	static public float[] vmin(float[] x, float[] ys){
		final float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.min(xs[i], ys[i]);
		return xs;
	}

	static public float[] vmap(IFn fn, float[] x) {
		float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i])).floatValue();
		return xs;
	}

	static public float[] vmap(IFn fn, float[] x, float[] ys) {
		float[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i], ys[i])).floatValue();
		return xs;
	}

}

static public class D{
	static public double add(double x, double y){
		return x + y;
	}

	static public double subtract(double x, double y){
		return x - y;
	}

	static public double negate(double x){
		return -x;
	}

	static public double inc(double x){
		return x + 1;
	}

	static public double dec(double x){
		return x - 1;
	}

	static public double multiply(double x, double y){
		return x * y;
	}

	static public double divide(double x, double y){
		return x / y;
	}

	static public boolean equiv(double x, double y){
		return x == y;
	}

	static public boolean lt(double x, double y){
		return x < y;
	}

	static public boolean lte(double x, double y){
		return x <= y;
	}

	static public boolean gt(double x, double y){
		return x > y;
	}

	static public boolean gte(double x, double y){
		return x >= y;
	}

	static public boolean pos(double x){
		return x > 0;
	}

	static public boolean neg(double x){
		return x < 0;
	}

	static public boolean zero(double x){
		return x == 0;
	}

	static public double aget(double[] xs, int i){
		return xs[i];
	}

	static public double aset(double[] xs, int i, double v){
		xs[i] = v;
		return v;
	}

	static public int alength(double[] xs){
		return xs.length;
	}

	static public double[] aclone(double[] xs){
		return xs.clone();
	}

	static public double[] vec(int size, Object init){
		double[] ret = new double[size];
		if(init instanceof Number)
			{
			double f = ((Number) init).doubleValue();
			for(int i = 0; i < ret.length; i++)
				ret[i] = f;
			}
		else
			{
			ISeq s = RT.seq(init);
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).doubleValue();
			}
		return ret;
	}

	static public double[] vec(Object sizeOrSeq){
		if(sizeOrSeq instanceof Number)
			return new double[((Number) sizeOrSeq).intValue()];
		else
			{
			ISeq s = RT.seq(sizeOrSeq);
			int size = s.count();
			double[] ret = new double[size];
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).intValue();
			return ret;
			}
	}

	static public double[] vsadd(double[] x, double y){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += y;
		return xs;
	}

	static public double[] vssub(double[] x, double y){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= y;
		return xs;
	}

	static public double[] vsdiv(double[] x, double y){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= y;
		return xs;
	}

	static public double[] vsmul(double[] x, double y){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= y;
		return xs;
	}

	static public double[] svdiv(double y, double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = y / xs[i];
		return xs;
	}

	static public double[] vsmuladd(double[] x, double y, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + zs[i];
		return xs;
	}

	static public double[] vsmulsub(double[] x, double y, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - zs[i];
		return xs;
	}

	static public double[] vsmulsadd(double[] x, double y, double z){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + z;
		return xs;
	}

	static public double[] vsmulssub(double[] x, double y, double z){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - z;
		return xs;
	}

	static public double[] vabs(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.abs(xs[i]);
		return xs;
	}

	static public double[] vnegabs(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -Math.abs(xs[i]);
		return xs;
	}

	static public double[] vneg(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -xs[i];
		return xs;
	}

	static public double[] vsqr(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= xs[i];
		return xs;
	}

	static public double[] vsignedsqr(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= Math.abs(xs[i]);
		return xs;
	}

	static public double[] vclip(double[] x, double low, double high){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				xs[i] = low;
			else if(xs[i] > high)
				xs[i] = high;
			}
		return xs;
	}

	static public IPersistentVector vclipcounts(double[] x, double low, double high){
		final double[] xs = x.clone();
		int lowc = 0;
		int highc = 0;

		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				{
				++lowc;
				xs[i] = low;
				}
			else if(xs[i] > high)
				{
				++highc;
				xs[i] = high;
				}
			}
		return RT.vector(xs, lowc, highc);
	}

	static public double[] vthresh(double[] x, double thresh, double otherwise){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < thresh)
				xs[i] = otherwise;
			}
		return xs;
	}

	static public double[] vreverse(double[] x){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[xs.length - i - 1];
		return xs;
	}

	static public double[] vrunningsum(double[] x){
		final double[] xs = x.clone();
		for(int i = 1; i < xs.length; i++)
			xs[i] = xs[i - 1] + xs[i];
		return xs;
	}

	static public double[] vsort(double[] x){
		final double[] xs = x.clone();
		Arrays.sort(xs);
		return xs;
	}

	static public double vdot(double[] xs, double[] ys){
		double ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * ys[i];
		return ret;
	}

	static public double vmax(double[] xs){
		if(xs.length == 0)
			return 0;
		double ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.max(ret, xs[i]);
		return ret;
	}

	static public double vmin(double[] xs){
		if(xs.length == 0)
			return 0;
		double ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.min(ret, xs[i]);
		return ret;
	}

	static public double vmean(double[] xs){
		if(xs.length == 0)
			return 0;
		return vsum(xs) / xs.length;
	}

	static public double vrms(double[] xs){
		if(xs.length == 0)
			return 0;
		double ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * xs[i];
		return Math.sqrt(ret / xs.length);
	}

	static public double vsum(double[] xs){
		double ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i];
		return ret;
	}

	static public boolean vequiv(double[] xs, double[] ys){
		return Arrays.equals(xs, ys);
	}

	static public double[] vadd(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += ys[i];
		return xs;
	}

	static public double[] vsub(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= ys[i];
		return xs;
	}

	static public double[] vaddmul(double[] x, double[] ys, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * zs[i];
		return xs;
	}

	static public double[] vsubmul(double[] x, double[] ys, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * zs[i];
		return xs;
	}

	static public double[] vaddsmul(double[] x, double[] ys, double z){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * z;
		return xs;
	}

	static public double[] vsubsmul(double[] x, double[] ys, double z){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * z;
		return xs;
	}

	static public double[] vmulsadd(double[] x, double[] ys, double z){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + z;
		return xs;
	}

	static public double[] vdiv(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= ys[i];
		return xs;
	}

	static public double[] vmul(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= ys[i];
		return xs;
	}

	static public double[] vmuladd(double[] x, double[] ys, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + zs[i];
		return xs;
	}

	static public double[] vmulsub(double[] x, double[] ys, double[] zs){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) - zs[i];
		return xs;
	}

	static public double[] vmax(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.max(xs[i], ys[i]);
		return xs;
	}

	static public double[] vmin(double[] x, double[] ys){
		final double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.min(xs[i], ys[i]);
		return xs;
	}

	static public double[] vmap(IFn fn, double[] x) {
		double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i])).doubleValue();
		return xs;
	}

	static public double[] vmap(IFn fn, double[] x, double[] ys) {
		double[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i], ys[i])).doubleValue();
		return xs;
	}
}

static public class I{
	static public int add(int x, int y){
		return x + y;
	}

	static public int subtract(int x, int y){
		return x - y;
	}

	static public int negate(int x){
		return -x;
	}

	static public int inc(int x){
		return x + 1;
	}

	static public int dec(int x){
		return x - 1;
	}

	static public int multiply(int x, int y){
		return x * y;
	}

	static public int divide(int x, int y){
		return x / y;
	}

	static public boolean equiv(int x, int y){
		return x == y;
	}

	static public boolean lt(int x, int y){
		return x < y;
	}

	static public boolean lte(int x, int y){
		return x <= y;
	}

	static public boolean gt(int x, int y){
		return x > y;
	}

	static public boolean gte(int x, int y){
		return x >= y;
	}

	static public boolean pos(int x){
		return x > 0;
	}

	static public boolean neg(int x){
		return x < 0;
	}

	static public boolean zero(int x){
		return x == 0;
	}

	static public int aget(int[] xs, int i){
		return xs[i];
	}

	static public int aset(int[] xs, int i, int v){
		xs[i] = v;
		return v;
	}

	static public int alength(int[] xs){
		return xs.length;
	}

	static public int[] aclone(int[] xs){
		return xs.clone();
	}

	static public int[] vec(int size, Object init){
		int[] ret = new int[size];
		if(init instanceof Number)
			{
			int f = ((Number) init).intValue();
			for(int i = 0; i < ret.length; i++)
				ret[i] = f;
			}
		else
			{
			ISeq s = RT.seq(init);
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).intValue();
			}
		return ret;
	}

	static public int[] vec(Object sizeOrSeq){
		if(sizeOrSeq instanceof Number)
			return new int[((Number) sizeOrSeq).intValue()];
		else
			{
			ISeq s = RT.seq(sizeOrSeq);
			int size = s.count();
			int[] ret = new int[size];
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).intValue();
			return ret;
			}
	}

	static public int[] vsadd(int[] x, int y){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += y;
		return xs;
	}

	static public int[] vssub(int[] x, int y){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= y;
		return xs;
	}

	static public int[] vsdiv(int[] x, int y){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= y;
		return xs;
	}

	static public int[] vsmul(int[] x, int y){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= y;
		return xs;
	}

	static public int[] svdiv(int y, int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = y / xs[i];
		return xs;
	}

	static public int[] vsmuladd(int[] x, int y, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + zs[i];
		return xs;
	}

	static public int[] vsmulsub(int[] x, int y, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - zs[i];
		return xs;
	}

	static public int[] vsmulsadd(int[] x, int y, int z){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + z;
		return xs;
	}

	static public int[] vsmulssub(int[] x, int y, int z){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - z;
		return xs;
	}

	static public int[] vabs(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.abs(xs[i]);
		return xs;
	}

	static public int[] vnegabs(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -Math.abs(xs[i]);
		return xs;
	}

	static public int[] vneg(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -xs[i];
		return xs;
	}

	static public int[] vsqr(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= xs[i];
		return xs;
	}

	static public int[] vsignedsqr(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= Math.abs(xs[i]);
		return xs;
	}

	static public int[] vclip(int[] x, int low, int high){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				xs[i] = low;
			else if(xs[i] > high)
				xs[i] = high;
			}
		return xs;
	}

	static public IPersistentVector vclipcounts(int[] x, int low, int high){
		final int[] xs = x.clone();
		int lowc = 0;
		int highc = 0;

		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				{
				++lowc;
				xs[i] = low;
				}
			else if(xs[i] > high)
				{
				++highc;
				xs[i] = high;
				}
			}
		return RT.vector(xs, lowc, highc);
	}

	static public int[] vthresh(int[] x, int thresh, int otherwise){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < thresh)
				xs[i] = otherwise;
			}
		return xs;
	}

	static public int[] vreverse(int[] x){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[xs.length - i - 1];
		return xs;
	}

	static public int[] vrunningsum(int[] x){
		final int[] xs = x.clone();
		for(int i = 1; i < xs.length; i++)
			xs[i] = xs[i - 1] + xs[i];
		return xs;
	}

	static public int[] vsort(int[] x){
		final int[] xs = x.clone();
		Arrays.sort(xs);
		return xs;
	}

	static public int vdot(int[] xs, int[] ys){
		int ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * ys[i];
		return ret;
	}

	static public int vmax(int[] xs){
		if(xs.length == 0)
			return 0;
		int ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.max(ret, xs[i]);
		return ret;
	}

	static public int vmin(int[] xs){
		if(xs.length == 0)
			return 0;
		int ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.min(ret, xs[i]);
		return ret;
	}

	static public double vmean(int[] xs){
		if(xs.length == 0)
			return 0;
		return vsum(xs) / (double) xs.length;
	}

	static public double vrms(int[] xs){
		if(xs.length == 0)
			return 0;
		int ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * xs[i];
		return Math.sqrt(ret / (double) xs.length);
	}

	static public int vsum(int[] xs){
		int ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i];
		return ret;
	}

	static public boolean vequiv(int[] xs, int[] ys){
		return Arrays.equals(xs, ys);
	}

	static public int[] vadd(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += ys[i];
		return xs;
	}

	static public int[] vsub(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= ys[i];
		return xs;
	}

	static public int[] vaddmul(int[] x, int[] ys, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * zs[i];
		return xs;
	}

	static public int[] vsubmul(int[] x, int[] ys, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * zs[i];
		return xs;
	}

	static public int[] vaddsmul(int[] x, int[] ys, int z){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * z;
		return xs;
	}

	static public int[] vsubsmul(int[] x, int[] ys, int z){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * z;
		return xs;
	}

	static public int[] vmulsadd(int[] x, int[] ys, int z){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + z;
		return xs;
	}

	static public int[] vdiv(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= ys[i];
		return xs;
	}

	static public int[] vmul(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= ys[i];
		return xs;
	}

	static public int[] vmuladd(int[] x, int[] ys, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + zs[i];
		return xs;
	}

	static public int[] vmulsub(int[] x, int[] ys, int[] zs){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) - zs[i];
		return xs;
	}

	static public int[] vmax(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.max(xs[i], ys[i]);
		return xs;
	}

	static public int[] vmin(int[] x, int[] ys){
		final int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.min(xs[i], ys[i]);
		return xs;
	}

	static public int[] vmap(IFn fn, int[] x) {
		int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i])).intValue();
		return xs;
	}

	static public int[] vmap(IFn fn, int[] x, int[] ys) {
		int[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i], ys[i])).intValue();
		return xs;
	}

}

static public class L{
	static public long add(long x, long y){
		return x + y;
	}

	static public long subtract(long x, long y){
		return x - y;
	}

	static public long negate(long x){
		return -x;
	}

	static public long inc(long x){
		return x + 1;
	}

	static public long dec(long x){
		return x - 1;
	}

	static public long multiply(long x, long y){
		return x * y;
	}

	static public long divide(long x, long y){
		return x / y;
	}

	static public boolean equiv(long x, long y){
		return x == y;
	}

	static public boolean lt(long x, long y){
		return x < y;
	}

	static public boolean lte(long x, long y){
		return x <= y;
	}

	static public boolean gt(long x, long y){
		return x > y;
	}

	static public boolean gte(long x, long y){
		return x >= y;
	}

	static public boolean pos(long x){
		return x > 0;
	}

	static public boolean neg(long x){
		return x < 0;
	}

	static public boolean zero(long x){
		return x == 0;
	}

	static public long aget(long[] xs, int i){
		return xs[i];
	}

	static public long aset(long[] xs, int i, long v){
		xs[i] = v;
		return v;
	}

	static public int alength(long[] xs){
		return xs.length;
	}

	static public long[] aclone(long[] xs){
		return xs.clone();
	}

	static public long[] vec(int size, Object init){
		long[] ret = new long[size];
		if(init instanceof Number)
			{
			long f = ((Number) init).longValue();
			for(int i = 0; i < ret.length; i++)
				ret[i] = f;
			}
		else
			{
			ISeq s = RT.seq(init);
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).longValue();
			}
		return ret;
	}

	static public long[] vec(Object sizeOrSeq){
		if(sizeOrSeq instanceof Number)
			return new long[((Number) sizeOrSeq).intValue()];
		else
			{
			ISeq s = RT.seq(sizeOrSeq);
			int size = s.count();
			long[] ret = new long[size];
			for(int i = 0; i < size && s != null; i++, s = s.rest())
				ret[i] = ((Number) s.first()).intValue();
			return ret;
			}
	}


	static public long[] vsadd(long[] x, long y){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += y;
		return xs;
	}

	static public long[] vssub(long[] x, long y){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= y;
		return xs;
	}

	static public long[] vsdiv(long[] x, long y){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= y;
		return xs;
	}

	static public long[] vsmul(long[] x, long y){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= y;
		return xs;
	}

	static public long[] svdiv(long y, long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = y / xs[i];
		return xs;
	}

	static public long[] vsmuladd(long[] x, long y, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + zs[i];
		return xs;
	}

	static public long[] vsmulsub(long[] x, long y, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - zs[i];
		return xs;
	}

	static public long[] vsmulsadd(long[] x, long y, long z){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y + z;
		return xs;
	}

	static public long[] vsmulssub(long[] x, long y, long z){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[i] * y - z;
		return xs;
	}

	static public long[] vabs(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.abs(xs[i]);
		return xs;
	}

	static public long[] vnegabs(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -Math.abs(xs[i]);
		return xs;
	}

	static public long[] vneg(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = -xs[i];
		return xs;
	}

	static public long[] vsqr(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= xs[i];
		return xs;
	}

	static public long[] vsignedsqr(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= Math.abs(xs[i]);
		return xs;
	}

	static public long[] vclip(long[] x, long low, long high){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				xs[i] = low;
			else if(xs[i] > high)
				xs[i] = high;
			}
		return xs;
	}

	static public IPersistentVector vclipcounts(long[] x, long low, long high){
		final long[] xs = x.clone();
		int lowc = 0;
		int highc = 0;

		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < low)
				{
				++lowc;
				xs[i] = low;
				}
			else if(xs[i] > high)
				{
				++highc;
				xs[i] = high;
				}
			}
		return RT.vector(xs, lowc, highc);
	}

	static public long[] vthresh(long[] x, long thresh, long otherwise){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			{
			if(xs[i] < thresh)
				xs[i] = otherwise;
			}
		return xs;
	}

	static public long[] vreverse(long[] x){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = xs[xs.length - i - 1];
		return xs;
	}

	static public long[] vrunningsum(long[] x){
		final long[] xs = x.clone();
		for(int i = 1; i < xs.length; i++)
			xs[i] = xs[i - 1] + xs[i];
		return xs;
	}

	static public long[] vsort(long[] x){
		final long[] xs = x.clone();
		Arrays.sort(xs);
		return xs;
	}

	static public long vdot(long[] xs, long[] ys){
		long ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * ys[i];
		return ret;
	}

	static public long vmax(long[] xs){
		if(xs.length == 0)
			return 0;
		long ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.max(ret, xs[i]);
		return ret;
	}

	static public long vmin(long[] xs){
		if(xs.length == 0)
			return 0;
		long ret = xs[0];
		for(int i = 0; i < xs.length; i++)
			ret = Math.min(ret, xs[i]);
		return ret;
	}

	static public double vmean(long[] xs){
		if(xs.length == 0)
			return 0;
		return vsum(xs) / (double) xs.length;
	}

	static public double vrms(long[] xs){
		if(xs.length == 0)
			return 0;
		long ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i] * xs[i];
		return Math.sqrt(ret / (double) xs.length);
	}

	static public long vsum(long[] xs){
		long ret = 0;
		for(int i = 0; i < xs.length; i++)
			ret += xs[i];
		return ret;
	}

	static public boolean vequiv(long[] xs, long[] ys){
		return Arrays.equals(xs, ys);
	}

	static public long[] vadd(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] += ys[i];
		return xs;
	}

	static public long[] vsub(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] -= ys[i];
		return xs;
	}

	static public long[] vaddmul(long[] x, long[] ys, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * zs[i];
		return xs;
	}

	static public long[] vsubmul(long[] x, long[] ys, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * zs[i];
		return xs;
	}

	static public long[] vaddsmul(long[] x, long[] ys, long z){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] + ys[i]) * z;
		return xs;
	}

	static public long[] vsubsmul(long[] x, long[] ys, long z){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] - ys[i]) * z;
		return xs;
	}

	static public long[] vmulsadd(long[] x, long[] ys, long z){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + z;
		return xs;
	}

	static public long[] vdiv(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] /= ys[i];
		return xs;
	}

	static public long[] vmul(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] *= ys[i];
		return xs;
	}

	static public long[] vmuladd(long[] x, long[] ys, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) + zs[i];
		return xs;
	}

	static public long[] vmulsub(long[] x, long[] ys, long[] zs){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = (xs[i] * ys[i]) - zs[i];
		return xs;
	}

	static public long[] vmax(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.max(xs[i], ys[i]);
		return xs;
	}

	static public long[] vmin(long[] x, long[] ys){
		final long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = Math.min(xs[i], ys[i]);
		return xs;
	}

	static public long[] vmap(IFn fn, long[] x) {
		long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i])).longValue();
		return xs;
	}

	static public long[] vmap(IFn fn, long[] x, long[] ys) {
		long[] xs = x.clone();
		for(int i = 0; i < xs.length; i++)
			xs[i] = ((Number) fn.invoke(xs[i], ys[i])).longValue();
		return xs;
	}

}
*/


//overload resolution
//*

static public Number add(long x, Object y){
	return add((Object)x,y);
}

static public Number add(Object x, long y){
	return add(x,(Object)y);
}

static public Number addP(long x, Object y){
	return addP((Object)x,y);
}

static public Number addP(Object x, long y){
	return addP(x,(Object)y);
}

static public double add(double x, Object y){
	return add(x,((Number)y).doubleValue());
}

static public double add(Object x, double y){
	return add(((Number)x).doubleValue(),y);
}

static public double add(double x, long y){
	return x + y;
}

static public double add(long x, double y){
	return x + y;
}

static public double addP(double x, Object y){
	return addP(x,((Number)y).doubleValue());
}

static public double addP(Object x, double y){
	return addP(((Number)x).doubleValue(),y);
}

static public double addP(double x, long y){
	return x + y;
}

static public double addP(long x, double y){
	return x + y;
}

static public Number minus(long x, Object y){
	return minus((Object)x,y);
}

static public Number minus(Object x, long y){
	return minus(x,(Object)y);
}

static public Number minusP(long x, Object y){
	return minusP((Object)x,y);
}

static public Number minusP(Object x, long y){
	return minusP(x,(Object)y);
}

static public double minus(double x, Object y){
	return minus(x,((Number)y).doubleValue());
}

static public double minus(Object x, double y){
	return minus(((Number)x).doubleValue(),y);
}

static public double minus(double x, long y){
	return x - y;
}

static public double minus(long x, double y){
	return x - y;
}

static public double minusP(double x, Object y){
	return minus(x,((Number)y).doubleValue());
}

static public double minusP(Object x, double y){
	return minus(((Number)x).doubleValue(),y);
}

static public double minusP(double x, long y){
	return x - y;
}

static public double minusP(long x, double y){
	return x - y;
}

static public Number multiply(long x, Object y){
	return multiply((Object)x,y);
}

static public Number multiply(Object x, long y){
	return multiply(x,(Object)y);
}

static public Number multiplyP(long x, Object y){
	return multiplyP((Object)x,y);
}

static public Number multiplyP(Object x, long y){
	return multiplyP(x,(Object)y);
}

static public double multiply(double x, Object y){
	return multiply(x,((Number)y).doubleValue());
}

static public double multiply(Object x, double y){
	return multiply(((Number)x).doubleValue(),y);
}

static public double multiply(double x, long y){
	return x * y;
}

static public double multiply(long x, double y){
	return x * y;
}

static public double multiplyP(double x, Object y){
	return multiplyP(x,((Number)y).doubleValue());
}

static public double multiplyP(Object x, double y){
	return multiplyP(((Number)x).doubleValue(),y);
}

static public double multiplyP(double x, long y){
	return x * y;
}

static public double multiplyP(long x, double y){
	return x * y;
}

static public Number divide(long x, Object y){
	return divide((Object)x,y);
}

static public Number divide(Object x, long y){
	return divide(x,(Object)y);
}

static public double divide(double x, Object y){
	return x / ((Number)y).doubleValue();
}

static public double divide(Object x, double y){
	return ((Number)x).doubleValue() / y;
}

static public double divide(double x, long y){
	return x / y;
}

static public double divide(long x, double y){
    return x / y;
}

static public Number divide(long x, long y){
	return divide((Number)x, (Number)y);
}

static public boolean lt(long x, Object y){
	return lt((Object)x,y);
}

static public boolean lt(Object x, long y){
	return lt(x,(Object)y);
}

static public boolean lt(double x, Object y){
	return x < ((Number)y).doubleValue();
}

static public boolean lt(Object x, double y){
	return ((Number)x).doubleValue() < y;
}

static public boolean lt(double x, long y){
	return x < y;
}

static public boolean lt(long x, double y){
	return x < y;
}

static public boolean lte(long x, Object y){
	return lte((Object)x,y);
}

static public boolean lte(Object x, long y){
	return lte(x,(Object)y);
}

static public boolean lte(double x, Object y){
	return x <= ((Number)y).doubleValue();
}

static public boolean lte(Object x, double y){
	return ((Number)x).doubleValue() <= y;
}

static public boolean lte(double x, long y){
	return x <= y;
}

static public boolean lte(long x, double y){
	return x <= y;
}

static public boolean gt(long x, Object y){
	return gt((Object)x,y);
}

static public boolean gt(Object x, long y){
	return gt(x,(Object)y);
}

static public boolean gt(double x, Object y){
	return x > ((Number)y).doubleValue();
}

static public boolean gt(Object x, double y){
	return ((Number)x).doubleValue() > y;
}

static public boolean gt(double x, long y){
	return x > y;
}

static public boolean gt(long x, double y){
	return x > y;
}

static public boolean gte(long x, Object y){
	return gte((Object)x,y);
}

static public boolean gte(Object x, long y){
	return gte(x,(Object)y);
}

static public boolean gte(double x, Object y){
	return x >= ((Number)y).doubleValue();
}

static public boolean gte(Object x, double y){
	return ((Number)x).doubleValue() >= y;
}

static public boolean gte(double x, long y){
	return x >= y;
}

static public boolean gte(long x, double y){
	return x >= y;
}

static public boolean equiv(long x, Object y){
	return equiv((Object)x,y);
}

static public boolean equiv(Object x, long y){
	return equiv(x,(Object)y);
}

static public boolean equiv(double x, Object y){
	return x == ((Number)y).doubleValue();
}

static public boolean equiv(Object x, double y){
	return ((Number)x).doubleValue() == y;
}

static public boolean equiv(double x, long y){
	return x == y;
}

static public boolean equiv(long x, double y){
	return x == y;
}


static boolean isNaN(Object x){
	return (x instanceof Double) && ((Double)x).isNaN()
		|| (x instanceof Float) && ((Float)x).isNaN();
}

static public double max(double x, double y){
	return Math.max(x, y);
}

static public Object max(double x, long y){
	if(Double.isNaN(x)){
		return x;
	}
	if(x > y){
		return x;
	} else {
		return y;
	}
}

static public Object max(double x, Object y){
	if(Double.isNaN(x)){
		return x;
	} else if(isNaN(y)){
		return y;
	}
	if(x > ((Number)y).doubleValue()){
		return x;
	} else {
		return y;
	}
}

static public Object max(long x, double y){
	if(Double.isNaN(y)){
		return y;
	}
	if(x > y){
		return x;
	} else {
		return y;
	}
}


static public long max(long x, long y){
	if(x > y) {
		return x;
	} else {
		return y;
	}
}


static public Object max(long x, Object y){
	if(isNaN(y)){
		return y;
	}
	if(gt(x,y)){
		return x;
	} else {
		return y;
	}
}

static public Object max(Object x, long y){
	if(isNaN(x)){
		return x;
	}
	if(gt(x,y)){
		return x;
	} else {
		return y;
	}
}

static public Object max(Object x, double y){
	if (isNaN(x)){
		return x;
	} else if(Double.isNaN(y)){
		return y;
	}
	if(((Number)x).doubleValue() > y){
		return x;
	} else {
		return y;
	}
}

static public Object max(Object x, Object y){
	if(isNaN(x)){
		return x;
	} else if(isNaN(y)){
		return y;
	}
	if(gt(x, y)) {
		return x;
	} else {
		return y;
	}
}


static public double min(double x, double y){
	return Math.min(x, y);
}

static public Object min(double x, long y){
	if (Double.isNaN(x)){
		return x;
	}
	if(x < y){
		return x;
	} else {
		return y;
	}
}

static public Object min(double x, Object y){
	if(Double.isNaN(x)){
		return x;
	} else if(isNaN(y)){
		return y;
	}
	if(x < ((Number)y).doubleValue()){
		return x;
	} else {
		return y;
	}
}

static public Object min(long x, double y){
	if(Double.isNaN(y)){
		return y;
	}
	if(x < y){
		return x;
	} else {
		return y;
	}
}


static public long min(long x, long y){
	if(x < y) {
		return x;
	} else {
		return y;
	}
}

static public Object min(long x, Object y){
	if(isNaN(y)){
		return y;
	}
	if(lt(x,y)){
		return x;
	} else {
		return y;
	}
}

static public Object min(Object x, long y){
	if(isNaN(x)){
		return x;
	}
	if(lt(x,y)){
		return x;
	} else {
		return y;
	}
}

static public Object min(Object x, double y){
	if(isNaN(x)){
		return x;
	} else if(Double.isNaN(y)){
		return y;
	}
	if(((Number)x).doubleValue() < y){
		return x;
	} else {
		return y;
	}
}

static public Object min(Object x, Object y){
	if (isNaN(x)){
		return x;
	} else if(isNaN(y)){
		return y;
	}
	if(lt(x,y)) {
		return x;
	} else {
		return y;
	}
}

}
