denom = new ExprBinary(ExprBinary.BINOP_SUB, ar, br);
denom = new ExprBinary(ExprBinary.BINOP_ADD, ar, br);
    public Object visitExprBinary(ExprBinary exp)    {        Expression left = (Expression)exp.getLeft().accept(this);        Expression right = (Expression)exp.getRight().accept(this);        // We need at least one side to be complex to care.        if ((left instanceof ExprComplex) || (right instanceof ExprComplex))        {            // Okay, build child expressions for the real and imaginary            // parts of both sides.            Expression lr, li, rr, ri;            Expression ar, ai, br, bi, denom;            if (left instanceof ExprComplex)            {                lr = ((ExprComplex)left).getReal();                li = ((ExprComplex)left).getImag();            }            else            {                lr = left;                li = null;            }            if (right instanceof ExprComplex)            {                rr = ((ExprComplex)right).getReal();                ri = ((ExprComplex)right).getImag();            }            else            {                rr = right;                ri = null;            }            // Expressions to be used for returning values:            Expression real, imag;                        // Decide what to do based on the operation:            switch (exp.getOp())            {                // Deal with real and imaginary parts piecewise:            case ExprBinary.BINOP_ADD:            case ExprBinary.BINOP_SUB:                if (lr == null)                    real = rr;                else if (rr == null)                    real = lr;                else                    real = new ExprBinary(exp.getOp(), lr, rr);                                if (li == null)                    imag = ri;                else if (ri == null)                    imag = li;                else                    imag = new ExprBinary(exp.getOp(), li, ri);                                return new ExprComplex(real, imag);                // Complex multiply:            case ExprBinary.BINOP_MUL:                // (lr + i*li) * (rr + i*ri)                // lr * rr + i*lr*ri + i*rr*li - li * ri                // (lr * rr - li * ri) + i*(lr*ri + rr*li)                // Note that some of these terms can be simplified if                // various parts are null (always zero).                // But we won't do that for now; it involves thinking                // more.                if (lr == null) lr = new ExprConstFloat(0.0);                if (li == null) li = new ExprConstFloat(0.0);                if (rr == null) rr = new ExprConstFloat(0.0);                if (ri == null) ri = new ExprConstFloat(0.0);                                ar = new ExprBinary(ExprBinary.BINOP_MUL, lr, rr);                br = new ExprBinary(ExprBinary.BINOP_MUL, li, ri);                real = new ExprBinary(ExprBinary.BINOP_SUB, ar, br);                                ai = new ExprBinary(ExprBinary.BINOP_MUL, lr, ri);                bi = new ExprBinary(ExprBinary.BINOP_MUL, li, rr);                imag = new ExprBinary(ExprBinary.BINOP_ADD, ai, bi);                                return new ExprComplex(real, imag);                                // Complex divide:            case ExprBinary.BINOP_DIV:                // This works out to                // (lr*rr+li*ri)/(rr*rr-ri*ri) + i(li*rr-lr*ri)/(rr*rr-ri*ri)                // So precalculate the denominator, and share it between                // both sides.                // Again, punt on any cleverness involving null bits.                if (lr == null) lr = new ExprConstFloat(0.0);                if (li == null) li = new ExprConstFloat(0.0);                if (rr == null) rr = new ExprConstFloat(0.0);                if (ri == null) ri = new ExprConstFloat(0.0);                ar = new ExprBinary(ExprBinary.BINOP_MUL, rr, rr);                br = new ExprBinary(ExprBinary.BINOP_MUL, ri, ri);                denom = new ExprBinary(ExprBinary.BINOP_SUB, ar, br);                                ar = new ExprBinary(ExprBinary.BINOP_MUL, lr, rr);                br = new ExprBinary(ExprBinary.BINOP_MUL, li, ri);                real = new ExprBinary(ExprBinary.BINOP_ADD, ar, br);                real = new ExprBinary(ExprBinary.BINOP_DIV, real, denom);                                ai = new ExprBinary(ExprBinary.BINOP_MUL, li, rr);                bi = new ExprBinary(ExprBinary.BINOP_MUL, lr, ri);                imag = new ExprBinary(ExprBinary.BINOP_SUB, ai, bi);                imag = new ExprBinary(ExprBinary.BINOP_DIV, imag, denom);                                return new ExprComplex(real, imag);                                // Direct comparisons need to get translated to                // piecewise comparisons, but the result is a single                // real-valued expression.            case ExprBinary.BINOP_EQ:                // Need to construct (lr == rr) && (li == ri),                // forcing things to be 0.0 if needed.                if (lr == null) lr = new ExprConstFloat(0.0);                if (li == null) li = new ExprConstFloat(0.0);                if (rr == null) rr = new ExprConstFloat(0.0);                if (ri == null) ri = new ExprConstFloat(0.0);                left = new ExprBinary(ExprBinary.BINOP_EQ, lr, rr);                right = new ExprBinary(ExprBinary.BINOP_EQ, li, ri);                return new ExprBinary(ExprBinary.BINOP_AND, left, right);            case ExprBinary.BINOP_NEQ:                // Need to construct (lr != rr) || (li != ri),                // forcing things to be 0.0 if needed.                if (lr == null) lr = new ExprConstFloat(0.0);                if (li == null) li = new ExprConstFloat(0.0);                if (rr == null) rr = new ExprConstFloat(0.0);                if (ri == null) ri = new ExprConstFloat(0.0);                left = new ExprBinary(ExprBinary.BINOP_NEQ, lr, rr);                right = new ExprBinary(ExprBinary.BINOP_NEQ, li, ri);                return new ExprBinary(ExprBinary.BINOP_OR, left, right);                                // Anything else is wrong; punt on the problem.            default:            }            // Reconstruct the left and right sides if we need to.            // For now, always recreate them (we need to test whether            // left and right are complex).            // if (lr != left.getReal() || li != left.getImag())            left = new ExprComplex(lr, li);            // if (rr != right.getReal() || ri != right.getImag())            right = new ExprComplex(rr, ri);        }        // Now build the new result, but only if we need to.        if (left == exp.getLeft() && right == exp.getRight())            return exp;        else            return new ExprBinary(exp.getOp(), left, right);    }