package jscl.math;

import org.solovyev.common.Converter;

import jscl.math.function.Constant;
import jscl.math.function.Fraction;
import jscl.math.function.Inverse;
import jscl.math.numeric.Real;
import jscl.math.polynomial.Polynomial;
import jscl.math.polynomial.UnivariatePolynomial;
import jscl.mathml.MathML;
import jscl.text.ExpressionParser;
import jscl.text.ParseException;
import jscl.text.Parser;
import jscl.text.ParserUtils;
import jscl.text.msg.Messages;
import jscl.util.ArrayUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class Expression extends Generic {

    protected static final Converter<Variable, Generic> FACTORIZE_CONVERTER = new Converter<Variable, Generic>() {
        @Nonnull
        public Generic convert(@Nonnull Variable variable) {
            return variable.factorize();
        }
    };
    protected static final Converter<Variable, Generic> ELEMENTARY_CONVERTER = new Converter<Variable, Generic>() {
        @Nonnull
        public Generic convert(@Nonnull Variable variable) {
            return variable.elementary();
        }
    };
    protected static final Converter<Variable, Generic> EXPAND_CONVERTER = new Converter<Variable, Generic>() {
        @Nonnull
        public Generic convert(@Nonnull Variable variable) {
            return variable.expand();
        }
    };
    protected static final Converter<Variable, Generic> NUMERIC_CONVERTER = new Converter<Variable, Generic>() {
        @Nonnull
        public Generic convert(@Nonnull Variable variable) {
            return variable.numeric();
        }
    };
    int size;
    private Literal literals[];
    private JsclInteger coefficients[];

    Expression() {
    }

    Expression(int size) {
        init(size);
    }

    public static Variable[] variables(Generic elements[]) {
        final List<Variable> result = new ArrayList<Variable>();

        for (Generic element : elements) {
            for (Variable variable : element.variables()) {
                if (!result.contains(variable)) {
                    result.add(variable);
                }
            }
        }

        return ArrayUtils.toArray(result, new Variable[result.size()]);
    }

    @Nonnull
    public static Expression valueOf(@Nonnull Variable variable) {
        return valueOf(Literal.valueOf(variable));
    }

    @Nonnull
    public static Expression valueOf(@Nonnull Literal literal) {
        return valueOf(literal, JsclInteger.valueOf(1));
    }

    @Nonnull
    public static Expression valueOf(@Nonnull JsclInteger integer) {
        return valueOf(Literal.newInstance(), integer);
    }

    @Nonnull
    public static Expression valueOf(@Nonnull Literal literal, @Nonnull JsclInteger integer) {
        final Expression result = new Expression();
        result.init(literal, integer);
        return result;
    }

    public static Expression valueOf(Rational rational) {
        Expression ex = new Expression();
        ex.init(rational);
        return ex;
    }

    public static Expression valueOf(@Nonnull Constant constant) {
        final Expression expression = new Expression(1);
        Literal literal = new Literal();
        literal.init(constant, 1);
        expression.init(literal, JsclInteger.ONE);
        return expression;
    }

    public static Expression valueOf(@Nonnull Double value) {
        final Expression expression = new Expression(1);
        Literal literal = new Literal();
        literal.init(new DoubleVariable(new NumericWrapper(Real.valueOf(value))), 1);
        expression.init(literal, JsclInteger.ONE);
        return expression;
    }

    public static Expression valueOf(@Nonnull String expression) throws ParseException {
        final Parser.Parameters p = Parser.Parameters.get(expression);

        final Generic generic = ExpressionParser.parser.parse(p, null);

        ParserUtils.skipWhitespaces(p);

        int index = p.position.intValue();
        if (index < expression.length()) {
            throw new ParseException(index, expression, Messages.msg_1, index + 1);
        }

        return new Expression().init(generic);
    }

    public static Expression init(@Nonnull NumericWrapper numericWrapper) {
        final Expression expression = new Expression(1);
        Literal literal = new Literal();
        literal.init(new ExpressionVariable(numericWrapper), 1);
        expression.init(literal, JsclInteger.ONE);
        return expression;
    }

    public static void separateSign(MathML element, Generic generic) {
        if (generic.signum() < 0) {
            MathML e1 = element.element("mo");
            e1.appendChild(element.text("-"));
            element.appendChild(e1);
            generic.negate().toMathML(element, null);
        } else {
            generic.toMathML(element, null);
        }
    }

    public int size() {
        return size;
    }

    public Literal literal(int n) {
        return literals[n];
    }

    public JsclInteger coef(int n) {
        return coefficients[n];
    }

    void init(int size) {
        literals = new Literal[size];
        coefficients = new JsclInteger[size];
        this.size = size;
    }

    void resize(int size) {
        int length = literals.length;
        if (size < length) {
            Literal literal[] = new Literal[size];
            JsclInteger coef[] = new JsclInteger[size];
            System.arraycopy(this.literals, length - size, literal, 0, size);
            System.arraycopy(this.coefficients, length - size, coef, 0, size);
            this.literals = literal;
            this.coefficients = coef;
            this.size = size;
        }
    }

    public Expression add(@Nonnull Expression that) {
        final Expression result = newInstance(size + that.size);
        int i = result.size;

        int thisI = this.size;
        int thatI = that.size;

        Literal thisLiteral = thisI > 0 ? this.literals[--thisI] : null;
        Literal thatLiteral = thatI > 0 ? that.literals[--thatI] : null;

        while (thisLiteral != null || thatLiteral != null) {
            int c;

            if (thisLiteral == null) {
                c = 1;
            } else if (thatLiteral == null) {
                c = -1;
            } else {
                c = -thisLiteral.compareTo(thatLiteral);
            }

            if (c < 0) {
                final JsclInteger thisCoefficient = this.coefficients[thisI];
                --i;
                result.literals[i] = thisLiteral;
                result.coefficients[i] = thisCoefficient;
                thisLiteral = thisI > 0 ? literals[--thisI] : null;
            } else if (c > 0) {
                JsclInteger en = that.coefficients[thatI];
                --i;
                result.literals[i] = thatLiteral;
                result.coefficients[i] = en;
                thatLiteral = thatI > 0 ? that.literals[--thatI] : null;
            } else {
                JsclInteger sum = coefficients[thisI].add(that.coefficients[thatI]);
                if (sum.signum() != 0) {
                    --i;
                    result.literals[i] = thisLiteral;
                    result.coefficients[i] = sum;
                }

                thisLiteral = thisI > 0 ? literals[--thisI] : null;
                thatLiteral = thatI > 0 ? that.literals[--thatI] : null;
            }
        }

        result.resize(result.size - i);

        return result;
    }

    @Nonnull
    public Generic add(@Nonnull Generic that) {
        if (that instanceof Expression) {
            return add((Expression) that);
        } else if (that instanceof JsclInteger || that instanceof Rational || that instanceof NumericWrapper) {
            return add(valueOf(that));
        } else {
            return that.valueOf(this).add(that);
        }
    }

    public Expression subtract(Expression expression) {
        return multiplyAndAdd(Literal.newInstance(), JsclInteger.valueOf(-1), expression);
    }

    @Nonnull
    public Generic subtract(@Nonnull Generic that) {
        if (that instanceof Expression) {
            return subtract((Expression) that);
        } else if (that instanceof JsclInteger || that instanceof Rational || that instanceof NumericWrapper) {
            return subtract(valueOf(that));
        } else {
            return that.valueOf(this).subtract(that);
        }
    }

    Expression multiplyAndAdd(@Nonnull Literal literal, @Nonnull JsclInteger coefficient, @Nonnull Expression that) {
        if (coefficient.signum() == 0) return this;

        final Expression result = newInstance(size + that.size);
        int i = result.size;

        int thisI = this.size;
        int thatI = that.size;

        Literal thisLiteral = thisI > 0 ? literals[--thisI] : null;
        Literal thatLiteral = thatI > 0 ? that.literals[--thatI].multiply(literal) : null;

        while (thisLiteral != null || thatLiteral != null) {
            int c = thisLiteral == null ? 1 : (thatLiteral == null ? -1 : -thisLiteral.compareTo(thatLiteral));

            if (c < 0) {
                JsclInteger en = coefficients[thisI];
                --i;
                result.literals[i] = thisLiteral;
                result.coefficients[i] = en;
                thisLiteral = thisI > 0 ? literals[--thisI] : null;
            } else if (c > 0) {
                JsclInteger en = that.coefficients[thatI].multiply(coefficient);
                --i;
                result.literals[i] = thatLiteral;
                result.coefficients[i] = en;
                thatLiteral = thatI > 0 ? that.literals[--thatI].multiply(literal) : null;
            } else {
                JsclInteger en = coefficients[thisI].add(that.coefficients[thatI].multiply(coefficient));
                if (en.signum() != 0) {
                    --i;
                    result.literals[i] = thisLiteral;
                    result.coefficients[i] = en;
                }
                thisLiteral = thisI > 0 ? literals[--thisI] : null;
                thatLiteral = thatI > 0 ? that.literals[--thatI].multiply(literal) : null;
            }
        }

        result.resize(result.size - i);

        return result;
    }

    public Expression multiply(Expression expression) {
        Expression result = newInstance(0);

        for (int i = 0; i < size; i++) {
            result = result.multiplyAndAdd(literals[i], coefficients[i], expression);
        }

        return result;
    }

    @Nonnull
    public Generic multiply(@Nonnull Generic that) {
        if (that instanceof Expression) {
            return multiply((Expression) that);
        } else if (that instanceof JsclInteger || that instanceof Rational || that instanceof NumericWrapper) {
            return multiply(valueOf(that));
        } else {
            return that.multiply(this);
        }
    }

    @Nonnull
    public Generic divide(@Nonnull Generic that) throws NotDivisibleException {
        Generic a[] = divideAndRemainder(that);
        if (a[1].signum() == 0) return a[0];
        else throw new NotDivisibleException();
    }

    public Generic[] divideAndRemainder(Generic generic) throws ArithmeticException {
        if (generic instanceof Expression) {
            Expression ex = (Expression) generic;
            Literal l1 = literalScm();
            Literal l2 = ex.literalScm();
            Literal l = l1.gcd(l2);
            Variable va[] = l.variables();
            if (va.length == 0) {
                if (signum() == 0 && ex.signum() != 0) return new Generic[]{this, JsclInteger.valueOf(0)};
                else try {
                    return divideAndRemainder(ex.integerValue());
                } catch (NotIntegerException e) {
                    return new Generic[]{JsclInteger.valueOf(0), this};
                }
            } else {
                Polynomial fact = Polynomial.factory(va[0]);
                Polynomial p[] = fact.valueOf(this).divideAndRemainder(fact.valueOf(ex));
                return new Generic[]{p[0].genericValue(), p[1].genericValue()};
            }
        } else if (generic instanceof JsclInteger) {
            try {
                Expression ex = newInstance(size);
                for (int i = 0; i < size; i++) {
                    ex.literals[i] = literals[i];
                    ex.coefficients[i] = coefficients[i].divide((JsclInteger) generic);
                }
                return new Generic[]{ex, JsclInteger.valueOf(0)};
            } catch (NotDivisibleException e) {
                return new Generic[]{JsclInteger.valueOf(0), this};
            }
        } else if (generic instanceof Rational || generic instanceof NumericWrapper) {
            return divideAndRemainder(valueOf(generic));
        } else {
            return generic.valueOf(this).divideAndRemainder(generic);
        }
    }

    public Generic gcd(@Nonnull Generic generic) {
        if (generic instanceof Expression) {
            final Expression that = (Expression) generic;

            final Literal thisL = this.literalScm();
            final Literal thatL = that.literalScm();

            final Literal gcdL = thisL.gcd(thatL);

            final Variable vars[] = gcdL.variables();
            if (vars.length == 0) {
                if (signum() == 0) {
                    return that;
                } else {
                    return this.gcd(that.gcd());
                }
            } else {
                Polynomial p = Polynomial.factory(vars[0]);
                return p.valueOf(this).gcd(p.valueOf(that)).genericValue();
            }
        } else if (generic instanceof JsclInteger) {
            if (generic.signum() == 0) {
                return this;
            } else {
                return this.gcd().gcd(generic);
            }
        } else if (generic instanceof Rational || generic instanceof NumericWrapper) {
            return gcd(valueOf(generic));
        } else {
            return generic.valueOf(this).gcd(generic);
        }
    }

    @Nonnull
    public Generic gcd() {
        JsclInteger result = JsclInteger.valueOf(0);

        for (int i = size - 1; i >= 0; i--) {
            result = result.gcd(coefficients[i]);
        }

        return result;
    }

    @Nonnull
    public Literal literalScm() {
        Literal result = Literal.newInstance();
        for (int i = 0; i < size; i++) {
            result = result.scm(literals[i]);
        }
        return result;
    }

    public Generic negate() {
        return multiply(JsclInteger.valueOf(-1));
    }

    public int signum() {
        return size == 0 ? 0 : coefficients[0].signum();
    }

    public int degree() {
        return 0;
    }

    public Generic antiDerivative(@Nonnull Variable variable) throws NotIntegrableException {
        if (isPolynomial(variable)) {
            return ((UnivariatePolynomial) Polynomial.factory(variable).valueOf(this)).antiderivative().genericValue();
        } else {
            try {
                Variable v = variableValue();
                try {
                    return v.antiDerivative(variable);
                } catch (NotIntegrableException e) {
                    if (v instanceof Fraction) {
                        Generic g[] = ((Fraction) v).getParameters();
                        if (g[1].isConstant(variable)) {
                            return new Inverse(g[1]).selfExpand().multiply(g[0].antiDerivative(variable));
                        }
                    }
                }
            } catch (NotVariableException e) {
                Generic sumElements[] = sumValue();
                if (sumElements.length > 1) {

                    Generic result = JsclInteger.valueOf(0);
                    for (Generic sumElement : sumElements) {
                        result = result.add(sumElement.antiDerivative(variable));
                    }
                    return result;

                } else {
                    final Generic products[] = sumElements[0].productValue();
                    Generic constantProduct = JsclInteger.valueOf(1);
                    Generic notConstantProduct = JsclInteger.valueOf(1);
                    for (Generic product : products) {
                        if (product.isConstant(variable)) {
                            constantProduct = constantProduct.multiply(product);
                        } else {
                            notConstantProduct = notConstantProduct.multiply(product);
                        }
                    }
                    if (constantProduct.compareTo(JsclInteger.valueOf(1)) != 0) {
                        return constantProduct.multiply(notConstantProduct.antiDerivative(variable));
                    }
                }
            }
        }
        throw new NotIntegrableException(this);
    }

    public Generic derivative(@Nonnull Variable variable) {
        Generic s = JsclInteger.valueOf(0);
        Literal l = literalScm();
        int n = l.size();
        for (int i = 0; i < n; i++) {
            Variable v = l.getVariable(i);
            Generic a = ((UnivariatePolynomial) Polynomial.factory(v).valueOf(this)).derivative(variable).genericValue();
            s = s.add(a);
        }
        return s;
    }

    public Generic substitute(@Nonnull final Variable variable, final Generic generic) {
        final Map<Variable, Generic> content = literalScm().content(new Converter<Variable, Generic>() {
            @Nonnull
            public Generic convert(@Nonnull Variable v) {
                return v.substitute(variable, generic);
            }
        });

        return substitute(content);
    }

    @Nonnull
    private Generic substitute(@Nonnull Map<Variable, Generic> content) {
        // sum = sumElement_0 + sumElement_1 + ... + sumElement_size
        Generic sum = JsclInteger.ZERO;

        for (int i = 0; i < size; i++) {
            final Literal literal = literals[i];

            // sumElement = variable_1 ^ power_1 * variable_2 ^ power_2 * ... * variable_size ^ power_size
            Generic sumElement = coefficients[i];

            for (int j = 0; j < literal.size(); j++) {
                final Variable variable = literal.getVariable(j);

                Generic b = content.get(variable).pow(literal.getPower(j));

                if (Matrix.isMatrixProduct(sumElement, b)) {
                    throw new ArithmeticException("Should not be matrix!");
                }

                sumElement = sumElement.multiply(b);
            }

            sum = sum.add(sumElement);
        }

        return sum;
    }

    public Generic expand() {
        return substitute(literalScm().content(EXPAND_CONVERTER));
    }

    public Generic factorize() {
        return Factorization.compute(substitute(literalScm().content(FACTORIZE_CONVERTER)));
    }

    public Generic elementary() {
        return substitute(literalScm().content(ELEMENTARY_CONVERTER));
    }

    public Generic simplify() {
        return Simplification.compute(this);
    }

    public Generic numeric() {
        try {
            return integerValue().numeric();
        } catch (NotIntegerException ex) {
            final Literal literal = literalScm();

            final Map<Variable, Generic> content = literal.content(NUMERIC_CONVERTER);

            return substitute(content);
        }
    }

    @Nonnull
    public Generic valueOf(@Nonnull Generic generic) {
        final Expression result = newInstance(0);

        result.init(generic);

        return result;
    }

    @Nonnull
    public Generic[] sumValue() {
        final Generic result[] = new Generic[size];

        for (int i = 0; i < result.length; i++) {
            result[i] = valueOf(literals[i], coefficients[i]);
        }

        return result;
    }

    @Nonnull
    public Generic[] productValue() throws NotProductException {
        if (size == 0) {
            return new Generic[]{JsclInteger.valueOf(0)};
        } else if (size == 1) {
            final Literal l = literals[0];
            final JsclInteger k = coefficients[0];

            Generic productElements[] = l.productValue();
            if (k.compareTo(JsclInteger.valueOf(1)) == 0) {
                return productElements;
            } else {
                final Generic result[] = new Generic[productElements.length + 1];
                System.arraycopy(productElements, 0, result, 1, productElements.length);
                result[0] = k;
                return result;
            }
        } else {
            throw new NotProductException();
        }
    }

    public Power powerValue() throws NotPowerException {
        if (size == 0) return new Power(JsclInteger.valueOf(0), 1);
        else if (size == 1) {
            Literal l = literals[0];
            JsclInteger en = coefficients[0];
            if (en.compareTo(JsclInteger.valueOf(1)) == 0) return l.powerValue();
            else if (l.degree() == 0) return en.powerValue();
            else throw new NotPowerException();
        } else throw new NotPowerException();
    }

    public Expression expressionValue() throws NotExpressionException {
        return this;
    }

    @Override
    public boolean isInteger() {
        try {
            integerValue();
            return true;
        } catch (NotIntegerException e) {
            return false;
        }
    }

    public JsclInteger integerValue() throws NotIntegerException {
        if (size == 0) {
            return JsclInteger.valueOf(0);
        } else if (size == 1) {

            final Literal l = literals[0];
            final JsclInteger c = coefficients[0];

            if (l.degree() == 0) {
                return c;
            } else {
                throw new NotIntegerException();
            }
        } else {
            throw new NotIntegerException();
        }
    }

    public Variable variableValue() throws NotVariableException {
        if (size == 0) {
            throw new NotVariableException();
        } else if (size == 1) {
            final Literal l = literals[0];
            final JsclInteger c = coefficients[0];
            if (c.compareTo(JsclInteger.valueOf(1)) == 0) {
                return l.variableValue();
            } else {
                throw new NotVariableException();
            }
        } else {
            throw new NotVariableException();
        }
    }

    public Variable[] variables() {
        return literalScm().variables();
    }

    public boolean isPolynomial(@Nonnull Variable variable) {
        boolean result = true;

        final Literal l = literalScm();
        for (int i = 0; i < l.size(); i++) {

            final Variable v = l.getVariable(i);
            if (!v.isConstant(variable) && !v.isIdentity(variable)) {
                result = false;
                break;
            }
        }

        return result;
    }

    public boolean isConstant(@Nonnull Variable variable) {

        Literal l = literalScm();
        for (int i = 0; i < l.size(); i++) {
            if (!l.getVariable(i).isConstant(variable)) {
                return false;
            }
        }

        return true;
    }

    public JsclVector grad(Variable variable[]) {
        Generic v[] = new Generic[variable.length];
        for (int i = 0; i < variable.length; i++) v[i] = derivative(variable[i]);
        return new JsclVector(v);
    }

    public Generic laplacian(Variable variable[]) {
        return grad(variable).divergence(variable);
    }

    public Generic dalembertian(Variable variable[]) {
        Generic a = derivative(variable[0]).derivative(variable[0]);
        for (int i = 1; i < 4; i++) a = a.subtract(derivative(variable[i]).derivative(variable[i]));
        return a;
    }

    public int compareTo(Expression expression) {
        int i1 = size;
        int i2 = expression.size;
        Literal l1 = i1 == 0 ? null : literals[--i1];
        Literal l2 = i2 == 0 ? null : expression.literals[--i2];
        while (l1 != null || l2 != null) {
            int c = l1 == null ? -1 : (l2 == null ? 1 : l1.compareTo(l2));
            if (c < 0) return -1;
            else if (c > 0) return 1;
            else {
                c = coefficients[i1].compareTo(expression.coefficients[i2]);
                if (c < 0) return -1;
                else if (c > 0) return 1;
                l1 = i1 == 0 ? null : literals[--i1];
                l2 = i2 == 0 ? null : expression.literals[--i2];
            }
        }
        return 0;
    }

    public int compareTo(@Nonnull Generic generic) {
        if (generic instanceof Expression) {
            return compareTo((Expression) generic);
        } else if (generic instanceof JsclInteger || generic instanceof Rational || generic instanceof NumericWrapper) {
            return compareTo(valueOf(generic));
        } else {
            return generic.valueOf(this).compareTo(generic);
        }
    }

    void init(Literal lit, JsclInteger integer) {
        if (integer.signum() != 0) {
            init(1);
            literals[0] = lit;
            coefficients[0] = integer;
        } else init(0);
    }

    void init(Expression expression) {
        init(expression.size);
        System.arraycopy(expression.literals, 0, literals, 0, size);
        System.arraycopy(expression.coefficients, 0, coefficients, 0, size);
    }

    void init(JsclInteger integer) {
        init(Literal.newInstance(), integer);
    }

    void init(Rational rational) {
        try {
            init(Literal.newInstance(), rational.integerValue());
        } catch (NotIntegerException e) {
            init(Literal.valueOf(rational.variableValue()), JsclInteger.valueOf(1));
        }
    }

    Expression init(@Nonnull Generic generic) {
        if (generic instanceof Expression) {
            init((Expression) generic);
        } else if (generic instanceof JsclInteger) {
            init((JsclInteger) generic);
        } else if (generic instanceof NumericWrapper) {
            init((NumericWrapper) generic);
        } else if (generic instanceof Rational) {
            init((Rational) generic);
        } else throw new ArithmeticException("Could not initialize expression with " + generic.getClass());

        return this;
    }

    public String toString() {
        final StringBuilder result = new StringBuilder();

        if (signum() == 0) {
            result.append("0");
        }

        // result = coef[0] * literal[0] + coef[1] * literal[1] + ... +
        for (int i = 0; i < size; i++) {
            final Literal literal = literals[i];
            final JsclInteger coefficient = coefficients[i];

            if (coefficient.signum() > 0 && i > 0) {
                result.append("+");
            }

            if (literal.degree() == 0) {
                result.append(coefficient);
            } else {
                if (coefficient.abs().compareTo(JsclInteger.valueOf(1)) == 0) {
                    if (coefficient.signum() < 0) {
                        result.append("-");
                    }
                } else {
                    result.append(coefficient).append("*");
                }
                result.append(literal);
            }
        }

        return result.toString();
    }

    public String toJava() {
        final StringBuilder result = new StringBuilder();
        if (signum() == 0) {
            result.append("JsclDouble.valueOf(0)");
        }

        for (int i = 0; i < size; i++) {
            Literal l = literals[i];
            JsclInteger en = coefficients[i];
            if (i > 0) {
                if (en.signum() < 0) {
                    result.append(".subtract(");
                    en = (JsclInteger) en.negate();
                } else result.append(".add(");
            }
            if (l.degree() == 0) result.append(en.toJava());
            else {
                if (en.abs().compareTo(JsclInteger.valueOf(1)) == 0) {
                    if (en.signum() > 0) result.append(l.toJava());
                    else if (en.signum() < 0) result.append(l.toJava()).append(".negate()");
                } else result.append(en.toJava()).append(".multiply(").append(l.toJava()).append(")");
            }
            if (i > 0) result.append(")");
        }

        return result.toString();
    }

    public void toMathML(MathML element, @Nullable Object data) {
        MathML e1 = element.element("mrow");
        if (signum() == 0) {
            MathML e2 = element.element("mn");
            e2.appendChild(element.text("0"));
            e1.appendChild(e2);
        }
        for (int i = 0; i < size; i++) {
            Literal l = literals[i];
            JsclInteger en = coefficients[i];
            if (en.signum() > 0 && i > 0) {
                MathML e2 = element.element("mo");
                e2.appendChild(element.text("+"));
                e1.appendChild(e2);
            }
            if (l.degree() == 0) separateSign(e1, en);
            else {
                if (en.abs().compareTo(JsclInteger.valueOf(1)) == 0) {
                    if (en.signum() < 0) {
                        MathML e2 = element.element("mo");
                        e2.appendChild(element.text("-"));
                        e1.appendChild(e2);
                    }
                } else separateSign(e1, en);
                l.toMathML(e1, null);
            }
        }
        element.appendChild(e1);
    }

    @Nonnull
    @Override
    public Set<? extends Constant> getConstants() {
        final Set<Constant> result = new HashSet<Constant>();

        for (Literal literal : literals) {
            for (Variable variable : literal.variables()) {
                result.addAll(variable.getConstants());
            }
        }

        return result;
    }

    @Nonnull
    private Expression newInstance(int n) {
        return new Expression(n);
    }
}
