package org.mozartoz.truffle.nodes.builtins;

import static org.mozartoz.truffle.nodes.builtins.Builtin.ALL;

import java.math.BigInteger;

import org.mozartoz.truffle.nodes.OzNode;

import com.oracle.truffle.api.ExactMath;
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;

public abstract class NumberBuiltins {

	@Builtin(name = "is")
	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class IsNumberNode extends OzNode {

		@Specialization
		Object isNumber(Object value) {
			return unimplemented();
		}

	}

	@Builtin(name = "~", deref = ALL)
	@GenerateNodeFactory
	@NodeChild("operand")
	public static abstract class NegNode extends OzNode {

		@Specialization
		long neg(long value) {
			return Math.subtractExact(0L, value);
		}

		@Specialization
		double neg(double value) {
			return -value;
		}

	}

	@GenerateNodeFactory
	@NodeChild("operand")
	public static abstract class AbsNode extends OzNode {

		@Specialization
		Object abs(Object operand) {
			return unimplemented();
		}

	}

	@Builtin(name = "+", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class AddNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long add(long a, long b) {
			return ExactMath.addExact(a, b);
		}

		@Specialization
		BigInteger add(BigInteger a, BigInteger b) {
			return a.add(b);
		}

	}

	@Builtin(name = "-", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class SubNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long sub(long a, long b) {
			return ExactMath.subtractExact(a, b);
		}

		@Specialization
		BigInteger sub(BigInteger a, BigInteger b) {
			return a.subtract(b);
		}

	}

	@Builtin(name = "*", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class MulNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long mul(long a, long b) {
			return ExactMath.multiplyExact(a, b);
		}

		@Specialization
		BigInteger mul(BigInteger a, BigInteger b) {
			return a.multiply(b);
		}

		@Specialization
		double mul(double a, double b) {
			return a * b;
		}

	}

}

package org.mozartoz.truffle.nodes.builtins;

import static org.mozartoz.truffle.nodes.builtins.Builtin.ALL;

import java.math.BigInteger;

import org.mozartoz.truffle.nodes.OzNode;

import com.oracle.truffle.api.ExactMath;
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;

public abstract class NumberBuiltins {

	@Builtin(name = "is")
	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class IsNumberNode extends OzNode {

		@Specialization
		Object isNumber(Object value) {
			return unimplemented();
		}

	}

	@Builtin(name = "~", deref = ALL)
	@GenerateNodeFactory
	@NodeChild("operand")
	public static abstract class NegNode extends OzNode {

		@Specialization
		long neg(long value) {
			return Math.subtractExact(0L, value);
		}

		@Specialization
		double neg(double value) {
			return -value;
		}

	}

	@GenerateNodeFactory
	@NodeChild("operand")
	public static abstract class AbsNode extends OzNode {

		@Specialization
		Object abs(Object operand) {
			return unimplemented();
		}

	}

	@Builtin(name = "+", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class AddNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long add(long a, long b) {
			return ExactMath.addExact(a, b);
		}

		@Specialization
		BigInteger add(BigInteger a, BigInteger b) {
			return a.add(b);
		}

	}

	@Builtin(name = "-", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class SubNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long sub(long a, long b) {
			return ExactMath.subtractExact(a, b);
		}

		@Specialization
		double sub(double a, double b) {
			return a - b;
		}

		@Specialization
		BigInteger sub(BigInteger a, BigInteger b) {
			return a.subtract(b);
		}

	}

	@Builtin(name = "*", deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("left"), @NodeChild("right") })
	public static abstract class MulNode extends OzNode {

		@Specialization(rewriteOn = ArithmeticException.class)
		long mul(long a, long b) {
			return ExactMath.multiplyExact(a, b);
		}

		@Specialization
		BigInteger mul(BigInteger a, BigInteger b) {
			return a.multiply(b);
		}

		@Specialization
		double mul(double a, double b) {
			return a * b;
		}

	}

}

Add SubNode specialization for doubles