public Object activate(IokeObject method, IokeObject context, IokeObject message, Object on) throws ControlFlow {
getArguments().getEvaluatedArguments(context, message, on, new ArrayList<Object>(), new HashMap<String, Object>());
RatNum value = ((Number)IokeObject.data(on)).value;
if(value instanceof IntFraction) {
IntNum num = value.numerator();
IntNum den = value.denominator();
BigDecimal nums = new BigSquareRoot().get(num.asBigDecimal());
BigDecimal dens = new BigSquareRoot().get(den.asBigDecimal());
try {
num = IntNum.valueOf(nums.toBigIntegerExact().toString());
den = IntNum.valueOf(dens.toBigIntegerExact().toString());
return context.runtime.newNumber(new IntFraction(num, den));
} catch(ArithmeticException e) {
// Ignore and fall through
}
}
if(RatNum.compare(value, IntNum.zero()) < 1) {
final IokeObject condition = IokeObject.as(IokeObject.getCellChain(context.runtime.condition,
message,
context,
"Error",
"Arithmetic"), context).mimic(message, context);
condition.setCell("message", message);
condition.setCell("context", context);
condition.setCell("receiver", on);
context.runtime.errorCondition(condition);
}
return context.runtime.newDecimal(new BigSquareRoot().get(value.asBigDecimal()));
}
}));
number.registerMethod(runtime.newNativeMethod("returns true if the left hand side number is equal to the right hand side number.", new TypeCheckingNativeMethod("==") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(runtime.number)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject self, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
getArguments().getEvaluatedArguments(context, message, on, args, new HashMap<String, Object>());
Number d = (Number)IokeObject.data(on);
Object other = args.get(0);
return ((other instanceof IokeObject) &&
(IokeObject.data(other) instanceof Number)
&& (((d.kind || ((Number)IokeObject.data(other)).kind) ? on == other :
d.value.equals(((Number)IokeObject.data(other)).value)))) ? context.runtime._true : context.runtime._false;
}
}));
rational.registerMethod(runtime.newNativeMethod("compares this number against the argument, returning -1, 0 or 1 based on which one is larger. if the argument is a decimal, the receiver will be converted into a form suitable for comparing against a decimal, and then compared - it's not specified whether this will actually call Decimal#<=> or not. if the argument is neither a Rational nor a Decimal, it tries to call asRational, and if that doesn't work it returns nil.", new TypeCheckingNativeMethod("<=>") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(rational)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(data instanceof Decimal) {
return context.runtime.newNumber(Number.value(on).asBigDecimal().compareTo(Decimal.value(arg)));
} else {
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, false);
if(!(IokeObject.data(arg) instanceof Number)) {
// Can't compare, so bail out
return context.runtime.nil;
}
}
if(on == rational || arg == rational || on == integer || arg == integer || on == ratio || arg == ratio) {
if(arg == on) {
return context.runtime.newNumber(0);
}
return context.runtime.nil;
}
return context.runtime.newNumber(IntNum.compare(Number.value(on),Number.value(arg)));
}
}
}));
number.registerMethod(runtime.newNativeMethod("compares this against the argument. should be overridden - in this case only used to check for equivalent number kinds", new NativeMethod("==") {
private final DefaultArgumentsDefinition ARGUMENTS = DefaultArgumentsDefinition
.builder()
.withRequiredPositional("other")
.getArguments();
@Override
public DefaultArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, IokeObject context, IokeObject message, Object on) throws ControlFlow {
List<Object> args = new ArrayList<Object>();
getArguments().getEvaluatedArguments(context, message, on, args, new HashMap<String, Object>());
Object arg = args.get(0);
if(on == arg) {
return context.runtime._true;
} else {
return context.runtime._false;
}
}
}));
rational.registerMethod(runtime.newNativeMethod("compares this number against the argument, true if this number is the same, otherwise false", new TypeCheckingNativeMethod("==") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(number)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
if(on == rational || arg == rational || on == integer || arg == integer || on == ratio || arg == ratio || on == infinity || arg == infinity || on == infinity2 || arg == infinity2) {
if(arg == on) {
return context.runtime._true;
}
return context.runtime._false;
}
if(IokeObject.data(arg) instanceof Decimal) {
return (Number.value(on).asBigDecimal().compareTo(Decimal.value(arg)) == 0) ? context.runtime._true : context.runtime._false;
} else if(IokeObject.data(arg) instanceof Number) {
return IntNum.compare(Number.value(on),Number.value(arg)) == 0 ? context.runtime._true : context.runtime._false;
} else {
return context.runtime._false;
}
}
}));
rational.registerMethod(runtime.newNativeMethod("returns the difference between this number and the argument. if the argument is a decimal, the receiver will be converted into a form suitable for subtracting against a decimal, and then subtracted. if the argument is neither a Rational nor a Decimal, it tries to call asRational, and if that fails it signals a condition.", new TypeCheckingNativeMethod("-") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(number)
.withRequiredPositional("subtrahend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(data instanceof Decimal) {
return Interpreter.send(context.runtime.minusMessage, context, context.runtime.newDecimal(((Number)IokeObject.data(on))), arg);
} else {
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, true);
}
return context.runtime.newNumber((RatNum)Number.value(on).sub(Number.value(arg)));
}
}
}));
rational.registerMethod(runtime.newNativeMethod("returns the addition of this number and the argument. if the argument is a decimal, the receiver will be converted into a form suitable for addition against a decimal, and then added. if the argument is neither a Rational nor a Decimal, it tries to call asRational, and if that fails it signals a condition.", new TypeCheckingNativeMethod("+") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(number)
.withRequiredPositional("addend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(data instanceof Decimal) {
return Interpreter.send(context.runtime.plusMessage, context, context.runtime.newDecimal(((Number)IokeObject.data(on))), arg);
} else {
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, true);
}
return context.runtime.newNumber(RatNum.add(Number.value(on),Number.value(arg),1));
}
}
}));
rational.registerMethod(runtime.newNativeMethod("returns the product of this number and the argument. if the argument is a decimal, the receiver will be converted into a form suitable for multiplying against a decimal, and then multiplied. if the argument is neither a Rational nor a Decimal, it tries to call asRational, and if that fails it signals a condition.", new TypeCheckingNativeMethod("*") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(number)
.withRequiredPositional("multiplier")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(data instanceof Decimal) {
return Interpreter.send(context.runtime.multMessage, context, context.runtime.newDecimal(((Number)IokeObject.data(on))), arg);
} else {
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, true);
}
return context.runtime.newNumber(RatNum.times(Number.value(on),Number.value(arg)));
}
}
}));
rational.registerMethod(runtime.newNativeMethod("returns the quotient of this number and the argument. if the division is not exact, it will return a Ratio.", new TypeCheckingNativeMethod("/") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(number)
.withRequiredPositional("dividend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, final IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(data instanceof Decimal) {
return Interpreter.send(context.runtime.divMessage, context, context.runtime.newDecimal(((Number)IokeObject.data(on))), arg);
} else {
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, true);
}
while(Number.value(arg).isZero()) {
final IokeObject condition = IokeObject.as(IokeObject.getCellChain(context.runtime.condition,
message,
context,
"Error",
"Arithmetic",
"DivisionByZero"), context).mimic(message, context);
condition.setCell("message", message);
condition.setCell("context", context);
condition.setCell("receiver", on);
final Object[] newCell = new Object[]{arg};
context.runtime.withRestartReturningArguments(new RunnableWithControlFlow() {
public void run() throws ControlFlow {
context.runtime.errorCondition(condition);
}},
context,
new Restart.ArgumentGivingRestart("useValue") {
public List<String> getArgumentNames() {
return new ArrayList<String>(Arrays.asList("newValue"));
}
public IokeObject invoke(IokeObject c2, List<Object> arguments) throws ControlFlow {
newCell[0] = arguments.get(0);
return c2.runtime.nil;
}
}
);
arg = newCell[0];
}
return context.runtime.newNumber(RatNum.divide(Number.value(on),Number.value(arg)));
}
}
}));
integer.registerMethod(runtime.newNativeMethod("returns the modulo of this number and the argument", new TypeCheckingNativeMethod("%") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(integer)
.withRequiredPositional("dividend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
IokeData data = IokeObject.data(arg);
if(!(data instanceof Number)) {
arg = IokeObject.convertToRational(arg, message, context, true);
}
return context.runtime.newNumber(IntNum.modulo(Number.intValue(on),Number.intValue(arg)));
}
}));
integer.registerMethod(runtime.newNativeMethod("returns how many times the first number can be divided by the second one", new TypeCheckingNativeMethod("div") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(integer)
.withRequiredPositional("dividend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, final IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
while(Number.value(arg).isZero()) {
final IokeObject condition = IokeObject.as(IokeObject.getCellChain(context.runtime.condition,
message,
context,
"Error",
"Arithmetic",
"DivisionByZero"), context).mimic(message, context);
condition.setCell("message", message);
condition.setCell("context", context);
condition.setCell("receiver", on);
final Object[] newCell = new Object[]{arg};
context.runtime.withRestartReturningArguments(new RunnableWithControlFlow() {
public void run() throws ControlFlow {
context.runtime.errorCondition(condition);
}},
context,
new Restart.ArgumentGivingRestart("useValue") {
public List<String> getArgumentNames() {
return new ArrayList<String>(Arrays.asList("newValue"));
}
public IokeObject invoke(IokeObject c2, List<Object> arguments) throws ControlFlow {
newCell[0] = arguments.get(0);
return c2.runtime.nil;
}
}
);
arg = newCell[0];
}
IokeData data = IokeObject.data(arg);
return context.runtime.newNumber(IntNum.quotient(Number.intValue(on),Number.intValue(arg), IntNum.TRUNCATE));
}
}));
integer.registerMethod(runtime.newNativeMethod("returns a tuple of how many times the first number can be divided by the second one, and the remainder", new TypeCheckingNativeMethod("divmod") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(integer)
.withRequiredPositional("dividend")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, final IokeObject context, IokeObject message) throws ControlFlow {
Object arg = args.get(0);
while(Number.value(arg).isZero()) {
final IokeObject condition = IokeObject.as(IokeObject.getCellChain(context.runtime.condition,
message,
context,
"Error",
"Arithmetic",
"DivisionByZero"), context).mimic(message, context);
condition.setCell("message", message);
condition.setCell("context", context);
condition.setCell("receiver", on);
final Object[] newCell = new Object[]{arg};
context.runtime.withRestartReturningArguments(new RunnableWithControlFlow() {
public void run() throws ControlFlow {
context.runtime.errorCondition(condition);
}},
context,
new Restart.ArgumentGivingRestart("useValue") {
public List<String> getArgumentNames() {
return new ArrayList<String>(Arrays.asList("newValue"));
}
public IokeObject invoke(IokeObject c2, List<Object> arguments) throws ControlFlow {
newCell[0] = arguments.get(0);
return c2.runtime.nil;
}
}
);
arg = newCell[0];
}
IokeData data = IokeObject.data(arg);
IntNum q = new IntNum();
IntNum r = new IntNum();
IntNum.divide(Number.intValue(on),Number.intValue(arg), q, r, IntNum.TRUNCATE);
return context.runtime.newTuple(context.runtime.newNumber(q.canonicalize()), context.runtime.newNumber(r.canonicalize()));
}
}));
rational.registerMethod(runtime.newNativeMethod("returns this number to the power of the argument", new TypeCheckingNativeMethod("**") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition