package gve.calc.formula;

public class OperatorMult extends InfixBinaryOp {
	public String getName() { return "*"; }
	public int getPri() { return 1900; }

	public OperatorMult(Part l,Part r) {
		super(l,r);
	}

	public Part evaluate(Evaluator ev) {
		Object l = left.evaluate(ev);
		Object r = right.evaluate(ev);
		return multiply(ev,l,r);
	}

	private static Part multiply(Evaluator ev,Object l,Object r) {
		if (l instanceof Real && r instanceof Real) {
			return new Real(((Real)l).doubleValue
				* ((Real)r).doubleValue);
		} else if (l instanceof Real && r instanceof Interval) {
			Interval inter = (Interval)r;
			double left = ((Real)l).doubleValue;
			if (left == 0) return new Real(0);
			else if (left > 0)
				return new Interval(inter.low*left,inter.high*left,inter.openLow,inter.openHigh);
			else	// left < 0
				return new Interval(inter.high*left,inter.low*left,inter.openHigh,inter.openLow);
		} else if (l instanceof Interval && r instanceof Real) {
			Interval inter = (Interval)l;
			double right = ((Real)r).doubleValue;
			if (right == 0) return new Real(0);
			else if (right > 0)
				return new Interval(inter.low*right,inter.high*right,inter.openLow,inter.openHigh);
			else	// left < 0
				return new Interval(inter.high*right,inter.low*right,inter.openHigh,inter.openLow);
		} else if (l instanceof Interval && r instanceof Interval) {
			Interval left = (Interval)l;
			Interval right = (Interval)r;
			// Strategy: we know for sure that two of the 4 numbers {left.low,left.high} * {right.low,right.high}
			// form the boundaries of the new interval
			double min,max,test;
			boolean openL,openH;
			// test left.low * right.low
			min = max = left.low * right.low;
			openL = openH = left.openLow || right.openLow;
			// test left.low * right.high
			test = left.low * right.high;
			if (test < min) {
				min = test;
				openL = left.openLow || right.openHigh;
			}
			if (test > max) {
				max = test;
				openH = left.openLow || right.openHigh;
			}
			// test next number
			test = left.high * right.low;
			if (test < min) {
				min = test;
				openL = left.openHigh || right.openLow;
			}
			if (test > max) {
				max = test;
				openH = left.openHigh || right.openLow;
			}
			// test last number
			test = left.high * right.high;
			if (test < min) {
				min = test;
				openL = left.openHigh || right.openHigh;
			}
			if (test > max) {
				max = test;
				openH = left.openHigh || right.openHigh;
			}

			return new Interval(min,max,openL,openH);
		} else if (l instanceof Real && r instanceof OperatorUnion) {
			OperatorUnion un = (OperatorUnion)r;
			return new OperatorUnion(
				(Part)multiply(ev,l,un.left),
				(Part)multiply(ev,l,un.right)
			);
		} else return null;
	}
}