2
\$\begingroup\$

I am programming this Computer Algebra System, it works fine, but I'm running into some problems where I'm writing a lot of redundant code, I'd like some advice on that.

The most problems I'm having are with Constants having the property that if they're added and are zero then the result is the thing they were added to. This is easily solved in Constant, but all other classes have to implement that check every implementation of an add method. In the Constant class I can make all add method private except for the general one, so a call from e.g. the main method is forced to go through it, so that exceptional cases can be handled there.

Other comments and advice on my code would also be much appreciated. (I pasted all the classes in the same code block even though they're public to prevent having to scroll an insane amount to get to the answers).

public abstract class MathObject{
    public abstract MathObject add(MathObject that);

    //public abstract MathObject subtract(MathObject that); //?

    public abstract MathObject multiply(MathObject that);

    //Can these be summed to get a product?
    public boolean isMultipleOf(MathObject that) {
        return false;
    }

    //Can these be multiplied to get an exponentiation?
    public boolean dependsOn(MathObject that) {
        return false;
    }

    public abstract double evaluate();

    public abstract boolean equals(MathObject that);
}


public class Constant extends MathObject {
    public static final Constant ZERO = new Constant(0);
    public static final Constant ONE = new Constant(1);
    public static final Constant MINUS_ONE = new Constant(-1);

    private final double c;

    public Constant(double c) {
        this.c = c;
    }

    @Override
    public MathObject add(MathObject that) {
        if (this.equals(Constant.ZERO))
        {
            return that;
        }

        if (that instanceof Constant)
        {
            return this.add((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.add((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.add((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.add((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.add((Exponentiation)that);
        }

        //interfaces for better grouping
        //common methods -> less code -> more efficient code
        throw new RuntimeException("Cannot add " + this + " and " + that);
    }

    @Override
    public MathObject multiply(MathObject that) {
        if (this.equals(Constant.ZERO))
        {
            return Constant.ZERO;
        }

        if (this.equals(Constant.ONE))
        {
            return that;
        }

        if (that instanceof Constant)
        {
            return this.multiply((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.multiply((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.multiply((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.multiply((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.multiply((Exponentiation)that);
        }

        throw new RuntimeException("Cannot add " + this + " and " + that);
    }

    //do we need this?
    public MathObject subtract(MathObject that) {
        if (that instanceof Constant)
        {
            return this.subtract((Constant)that);
        }

        if (that instanceof Variable) {
            return this.subtract((Variable)that);
        }

        //...

        throw new RuntimeException("Cannot subtract " + this + " and " + that);
    }


    private Constant add(Constant addend) {
        return new Constant(this.c + addend.evaluate());
    }

    private MathObject add(Variable addend) {
        return addend.add(this);
    }

    private MathObject add(Sum addend) {
        return addend.add(this);
    }

    private MathObject add(Product addend) {
        return addend.add(this);
    }

    private MathObject add(Exponentiation addend) {
        return new Sum(this, addend);
    }

    private Constant subtract(Constant subtrahend) {
        return new Constant(this.c - subtrahend.evaluate());
    }

    private Sum subtract(Variable subtrahend) {
        return new Sum(new Product(Constant.MINUS_ONE, subtrahend), this);
    }

    private Constant multiply(Constant factor) {
        return new Constant(this.c * factor.evaluate());
    }

    private MathObject multiply(Variable factor) {
        return new Product(this, factor);
    }

    private MathObject multiply(Sum factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Product factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Exponentiation factor) {
        return new Product(this, factor);
    }

    @Override
    public String toString() {
        return Double.toString(this.c);
    }

    @Override
    public double evaluate() {
        return this.c;
    }

    public boolean equals(MathObject that) {
        return that instanceof Constant && this.c == that.evaluate();
    }
}


public class Variable extends MathObject {
    private double x;

    public Variable(double x) {
        this.x = x;
    }

    @Override
    public MathObject add(MathObject that) {
        if (that instanceof Constant)
        {
            return this.add((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.add((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.add((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.add((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.add((Exponentiation)that);
        }

        throw new RuntimeException("Cannot add " + this + " and " + that);
    }

    //Should we?
    public MathObject subtract(MathObject that) {
        if (that instanceof Constant)
        {
            return this.subtract((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.subtract((Variable)that);
        }

        //...

        throw new RuntimeException("Cannot subtract " + this + " and " + that);
    }

    @Override
    public MathObject multiply(MathObject that) {
        if (that instanceof Constant)
        {
            return this.multiply((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.multiply((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.multiply((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.multiply((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.multiply((Exponentiation)that);
        }

        throw new RuntimeException("Cannot multiply " + this + " and " + that);
    }

    private MathObject add(Constant addend) {
        if (addend.equals(Constant.ZERO))
        {
            return this;
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject add(Variable addend) {
        if (this.isMultipleOf(addend))
        {
            return new Product(new Constant(2), this);
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject add(Sum addend) {
        return addend.add(this);
    }

    private MathObject add(Product addend) {
        return addend.add(this);
    }

    private MathObject add(Exponentiation addend) {
        return new Sum(this, addend);
    }

    private Sum subtract(Constant subtrahend) {
        return new Sum(this, subtrahend.multiply(Constant.MINUS_ONE));
    }

    private Sum subtract(Variable subtrahend) {
        return new Sum(this, subtrahend.multiply(Constant.MINUS_ONE));
    }

    private MathObject multiply(Constant factor) {
        if (factor.equals(Constant.ZERO))
        {
            return Constant.ZERO;
        }
        else
        {
            return new Product(this, factor);            
        }
    }

    private MathObject multiply(Variable factor) {
        if (this.dependsOn(factor))
        {
            return new Exponentiation(this, new Constant(2));
        }
        else
        {
            return new Product(this, factor);
        }
    }

    private MathObject multiply(Sum factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Product factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Exponentiation factor) {
        return factor.multiply(this);
    }

    @Override
    public String toString() {
        return super.toString();
    }

    @Override
    public double evaluate() {
        return x;
    }

    @Override
    public boolean isMultipleOf(MathObject addend) {
        if (addend instanceof Variable)
        {
            return this.equals(addend);
        }
        else
        {
            return addend.isMultipleOf(this);
        }
    }

    @Override
    public boolean dependsOn(MathObject that) {
        if (this.equals(that))
        {
            return true;
        }
        else
        {
            return that.dependsOn(this);
        }
    }

    public boolean equals(MathObject that) {
        return this == that;
    }

    public void set(double x) {
        this.x = x;
    }
}


import java.util.Arrays;
import java.util.List;

public class Sum extends MathObject {
    private Constant constantPart;
    private MathObject[] summands;

    public Sum(MathObject... summands) {
        constantPart = Constant.ZERO;
        int constants = 0;

        for (MathObject summand : summands)
        {
            if (summand instanceof Constant)
            {
                constantPart = (Constant)constantPart.add(summand);
                ++constants;
            }
        }

        this.summands = new MathObject[summands.length - constants];

        for (int i = 0, j = 0; i < summands.length; ++i)
        {
            if (!(summands[i] instanceof Constant))
            {
                this.summands[j] = summands[i];
                ++j;
            }
        }
    }

    private Sum(Constant constantPart, MathObject... summands) {
        this.constantPart = constantPart;
        this.summands = summands;
    }

    @Override
    public MathObject add(MathObject that) {
        if (that instanceof Constant)
        {
            return this.add((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.add((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.add((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.add((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.add((Exponentiation)that);
        }

        throw new RuntimeException("Cannot add: " + this + " and " + that);
    }

    @Override
    public MathObject multiply(MathObject that) {
        if (that instanceof Constant)
        {
            return this.multiply((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.multiply((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.multiply((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.multiply((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.multiply((Exponentiation)that);
        }

        throw new RuntimeException("Cannot multiply " + this + " and " + that);
    }

    private MathObject add(Constant addend) {
        Constant constantPart = (Constant)this.constantPart.add(addend);

        if (summands.length == 1)
        {
            if (constantPart.equals(Constant.ZERO))
            {
                return summands[0];
            }
        }

        return new Sum(constantPart, summands);
    }

    private MathObject add(Variable addend) {
        for (int i = 0; i < this.summands.length; ++i)
        {
            if (this.summands[i].isMultipleOf(addend))
            {
                MathObject sum = this.summands[i].add(addend);
                if (sum.equals(Constant.ZERO))
                {
                    List<MathObject> summandList = Arrays.asList(this.summands);
                    summandList.remove(i);
                    return new Sum(this.constantPart, (MathObject[])summandList.toArray());
                }
                else
                {
                    MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length);
                    summands[i] = sum;
                    return new Sum(this.constantPart, summands);
                }
            }
        }

        MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length + 1);
        summands[this.summands.length] = addend;
        return new Sum(this.constantPart, summands);
    }

    private MathObject add(Sum addend) {
        MathObject sum = addend.add(this.constantPart);
        for (MathObject summand : this.summands)
        {
            sum = sum.add(summand);
        }

        return sum;
    }

    private MathObject add(Product addend) {
        for (int i = 0; i < this.summands.length; ++i)
        {
            //Do note addend and this.summands[i] are swapped here with respect to add(Variable)
            //This is because Product handles Variable's isMultipleOf if that's given
            //(Long story short it can save a call)
            //(And premature optimization is the root of all evil)
            if (addend.isMultipleOf(this.summands[i]))
            {
                MathObject sum = addend.add(this.summands[i]);
                if (sum.equals(Constant.ZERO))
                {
                    List<MathObject> summandList = Arrays.asList(this.summands);
                    summandList.remove(i);
                    return new Sum(this.constantPart, (MathObject[])summandList.toArray());
                }
                else
                {
                    MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length);
                    summands[i] = sum;
                    return new Sum(this.constantPart, summands);
                }
            }
        }

        MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length + 1);
        summands[summands.length - 1] = addend;
        return new Sum(this.constantPart, summands);
    }

    private MathObject add(Exponentiation addend) {
        for (int i = 0; i < this.summands.length; ++i)
        {
            if (addend.isMultipleOf(this.summands[i]))
            {
                MathObject sum = addend.add(this.summands[i]);
                if (sum.equals(Constant.ZERO))
                {
                    List<MathObject> summandList = Arrays.asList(this.summands);
                    summandList.remove(i);
                    return new Sum(this.constantPart, (MathObject[])summandList.toArray());
                }
                else
                {
                    MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length);
                    summands[i] = sum;
                    return new Sum(this.constantPart, summands);
                }
            }
        }

        MathObject[] summands = Arrays.copyOf(this.summands, this.summands.length + 1);
        summands[this.summands.length] = addend;
        return new Sum(this.constantPart, summands);
    }

    private MathObject multiply(Constant factor) {
        if (factor.equals(Constant.ZERO))
        {
            return Constant.ZERO;
        }

        if (factor.equals(Constant.ONE))
        {
            return this;
        }

        MathObject[] summands = new MathObject[this.summands.length];
        for (int i = 0; i < this.summands.length; ++i)
        {
            summands[i] = this.summands[i].multiply(factor);
        }

        return new Sum((Constant)this.constantPart.multiply(factor), summands);
    }

    private MathObject multiply(Variable factor) {
        MathObject[] summands;
        if (this.constantPart.equals(Constant.ZERO))
        {
            summands = new MathObject[this.summands.length];
        }
        else
        {
            summands = new MathObject[this.summands.length + 1];
            summands[this.summands.length] = this.constantPart.multiply(factor);
        }

        for (int i = 0; i < this.summands.length; ++i)
        {
            summands[i] = this.summands[i].multiply(factor);
        }

        return new Sum(Constant.ZERO, summands);
    }

    private MathObject multiply(Sum factor) {
        MathObject sum = factor.multiply(this.constantPart);

        for (MathObject summand : this.summands)
        {
            sum = sum.add(factor.multiply(summand));
        }

        return sum;
    }

    private MathObject multiply(Product factor) {
        MathObject[] summands;
        if (this.constantPart.equals(Constant.ZERO))
        {
            summands = new MathObject[this.summands.length];
        }
        else
        {
            summands = new MathObject[this.summands.length + 1];
            summands[this.summands.length] = this.constantPart.multiply(factor);
        }

        for (int i = 0; i < this.summands.length; ++i)
        {
            summands[i] = this.summands[i].multiply(factor);
        }

        return new Sum(Constant.ZERO, summands);
    }

    private MathObject multiply(Exponentiation factor) {
        MathObject[] summands;
        if (this.constantPart.equals(Constant.ZERO))
        {
            summands = new MathObject[this.summands.length];
        }
        else
        {
            summands = new MathObject[this.summands.length + 1];
            summands[this.summands.length] = this.constantPart.multiply(factor);
        }

        for (int i = 0; i < this.summands.length; ++i)
        {
            summands[i] = this.summands[i].multiply(factor);
        }

        return new Sum(Constant.ZERO, summands);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (MathObject summand : this.summands)
        {
            builder.append(summand).append(" + ");
        }

        builder.append(this.constantPart);
        return builder.toString();
    }

    @Override
    public double evaluate() {
        double s = 0;

        for (MathObject f : summands)
        {
            s += f.evaluate();
        }

        return s;
    }

    @Override
    public boolean equals(MathObject obj) {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (!(obj instanceof Sum))
        {
            return false;
        }
        Sum other = (Sum)obj;
        if (this.constantPart == null)
        {
            if (other.constantPart != null)
            {
                return false;
            }
        }
        else if (!this.constantPart.equals(other.constantPart))
        {
            return false;
        }
        if (!Arrays.equals(this.summands, other.summands))
        {
            return false;
        }
        return true;
    }
}


import java.util.Arrays;
import java.util.List;

public class Product extends MathObject{
    private Constant constantPart;
    private MathObject[] factors;

    public Product(MathObject... factors) {
        this.constantPart = Constant.ONE;
        int constants = 0;

        for (MathObject factor : factors)
        {
            if (factor instanceof Constant)
            {
                this.constantPart = (Constant)this.constantPart.multiply((Constant)factor);
                ++constants;
            }
        }

        this.factors = new MathObject[factors.length - constants];

        for (int i = 0, j = 0; i < factors.length; ++i)
        {
            if (!(factors[i] instanceof Constant))
            {
                this.factors[j] = factors[i];
                ++j;
            }
        }
    }

    private Product(Constant constantPart, MathObject... factors) {
        this.constantPart = constantPart;
        this.factors = factors;
    }

    @Override
    public MathObject add(MathObject that) {
        if (that instanceof Constant)
        {
            return this.add((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.add((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.add((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.add((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.add((Exponentiation)that);
        }

        throw new RuntimeException("Cannot add " + this + " and " + that);
    }

    @Override
    public MathObject multiply(MathObject that) {
        if (that instanceof Constant)
        {
            return this.multiply((Constant)that);
        }

        if (that instanceof Variable)
        {
            return this.multiply((Variable)that);
        }

        if (that instanceof Sum)
        {
            return this.multiply((Sum)that);
        }

        if (that instanceof Product)
        {
            return this.multiply((Product)that);
        }

        if (that instanceof Exponentiation)
        {
            return this.multiply((Exponentiation)that);
        }

        throw new RuntimeException("Cannot multiply " + this + " and " + that);
    }

    private MathObject add(Constant addend) {
        if (addend.equals(Constant.ZERO))
        {
            return this;
        }

        return new Sum(this, addend);
    }

    private MathObject add(Variable addend) {
        if (this.isMultipleOf(addend))
        {
            return new Product((Constant)this.constantPart.add(Constant.ONE), this.factors);
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject add(Sum addend) {
        return addend.add(this);
    }

    private MathObject add(Product addend) {
        if (this.isMultipleOf(addend))
        {
            return new Product((Constant)this.constantPart.add(addend.constantPart), this.factors);
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject add(Exponentiation addend) {
        if (this.isMultipleOf(addend))
        {
            return new Product(this.constantPart.add(new Constant(1)), addend);
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject multiply(Constant factor) {
        if (factor.equals(Constant.ZERO))
        {
            return Constant.ZERO;
        }

        if (factor.equals(Constant.ONE))
        {
            return this;
        }

        return new Product((Constant)this.constantPart.multiply(factor), factors);
    }

    private MathObject multiply(Variable factor) {
        for (int i = 0; i < factors.length; ++i)
        {
            if (this.factors[i].dependsOn(factor))
            {
                MathObject product = this.factors[i].multiply(factor);
                if (product.equals(Constant.ONE))
                {
                    List<MathObject> factorList = Arrays.asList(this.factors);
                    factorList.remove(i);
                    return new Product(this.constantPart, (MathObject[])factorList.toArray());
                }
                else
                {
                    MathObject[] factors = Arrays.copyOf(this.factors, this.factors.length);
                    factors[i] = product;
                    return new Product(this.constantPart, factors);
                }
            }
        }

        MathObject[] factors = Arrays.copyOf(this.factors, this.factors.length + 1);
        factors[this.factors.length] = factor;
        return new Product(this.constantPart, factors);
    }

    private MathObject multiply(Sum factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Product factor1) {
        MathObject product = factor1;

        for (MathObject factor2 : this.factors)
        {
            product = product.multiply(factor2);
        }

        return product;
    }

    private MathObject multiply(Exponentiation factor) {
        for (int i = 0; i < factors.length; ++i)
        {
            if (this.factors[i].dependsOn(factor))
            {
                MathObject product = this.factors[i].multiply(factor);
                if (product.equals(Constant.ONE))
                {
                    List<MathObject> factorList = Arrays.asList(this.factors);
                    factorList.remove(i);
                    return new Product(this.constantPart, (MathObject[])factorList.toArray());
                }
                else
                {
                    MathObject[] factors = Arrays.copyOf(this.factors, this.factors.length);
                    factors[i] = product;
                    return new Product(this.constantPart, factors);
                }
            }
        }

        MathObject[] factors = Arrays.copyOf(this.factors, this.factors.length + 1);
        factors[this.factors.length] = factor;
        return new Product(this.constantPart, factors);
    }

    //This method doesn't enclose Sums in parentheses as in the current implementation multiplication by Sums is evaluated to a Sum rather than a Product
    //Change this if implementing another way simplification
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(this.constantPart);
        for (MathObject factor : this.factors)
        {
            builder.append(" * ").append(factor);
        }

        return builder.toString();
    }

    @Override
    public double evaluate() {
        double p = factors[0].evaluate();

        for (int i = 1; i < factors.length; ++i)
        {
            p *= factors[i].evaluate();
        }

        return p;
    }

    @Override
    public boolean isMultipleOf(MathObject that) {
        if (that instanceof Variable || that instanceof Exponentiation) {
            if (factors.length == 1 && factors[0].isMultipleOf(that))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        if (that instanceof Product)
        {
            Product product = (Product)that;
            if (this.factors.length != product.factors.length)
            {
                return false;
            }

            for (MathObject factor : factors)
            {
                if (!that.isMultipleOf(factor))
                {
                    return false;
                }
            }

            return true;
        }

        return false;
    }

    //Is this actually useful?
    @Override
    public boolean dependsOn(MathObject that) {
        for (MathObject factor : factors)
        {
            if (factor.isMultipleOf(that))
            {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean equals(MathObject obj) {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (!(obj instanceof Product))
        {
            return false;
        }
        Product other = (Product)obj;
        if (this.constantPart == null)
        {
            if (other.constantPart != null)
            {
                return false;
            }
        }
        else if (!this.constantPart.equals(other.constantPart))
        {
            return false;
        }
        if (!Arrays.equals(this.factors, other.factors))
        {
            return false;
        }
        return true;
    }
}


public class Exponentiation extends MathObject {
    public MathObject base, exponent;

    public Exponentiation(MathObject base, MathObject exponent) {
        this.base = base;
        this.exponent = exponent;
    }

    @Override
    public MathObject add(MathObject addend) {
        if (addend instanceof Constant)
        {
            return this.add((Constant)addend);
        }

        if (addend instanceof Variable)
        {
            return this.add((Variable)addend);
        }

        if (addend instanceof Sum)
        {
            return this.add((Sum)addend);
        }

        if (addend instanceof Product)
        {
            return this.add((Product)addend);
        }

        if (addend instanceof Exponentiation)
        {
            return this.add((Exponentiation)addend);
        }

        throw new RuntimeException("Can not add " + this + " and " + addend);
    }

    @Override
    public MathObject multiply(MathObject factor) {
        if (factor instanceof Constant)
        {
            return this.multiply((Constant)factor);
        }

        if (factor instanceof Variable)
        {
            return this.multiply((Variable)factor);
        }

        if (factor instanceof Sum)
        {
            return this.multiply((Sum)factor);
        }

        if (factor instanceof Product)
        {
            return this.multiply((Product)factor);
        }

        if (factor instanceof Exponentiation)
        {
            return this.multiply((Exponentiation)factor);
        }

        throw new RuntimeException("Can not multiply " + this + " and " + factor);
    }

    private MathObject add(Constant addend) {
        if (addend.equals(Constant.ZERO))
        {
            return this;
        }
        else
        {
            return new Sum(addend, this);
        }
    }

    private MathObject add(Variable addend) {
        return new Sum(this, addend);
    }

    private MathObject add(Sum addend) {
        return addend.add(this);
    }

    private MathObject add(Product addend) {
        return addend.add(this);
    }

    private MathObject add(Exponentiation addend) {
        if (this.isMultipleOf(addend))
        {
            return new Product(new Constant(2), this);
        }
        else
        {
            return new Sum(this, addend);
        }
    }

    private MathObject multiply(Constant factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Variable factor) {
        if (this.dependsOn(factor))
        {
            MathObject exponent = this.exponent.add(new Constant(1));
            if (exponent.equals(Constant.ZERO))
            {
                return Constant.ONE;
            }
            else
            {
                return new Exponentiation(this.base, exponent);
            }
            //You don't need to do this, as this means the exponent == 0 before, and that is already handled earlier
            //Keep as a reminder
            /*if (exponent.equals(Constant.ONE))
            {
                return this.base;
            }*/
        }
        else
        {
            return new Product(this, factor);
        }
    }

    private MathObject multiply(Sum factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Product factor) {
        return factor.multiply(this);
    }

    private MathObject multiply(Exponentiation factor) {
        if (this.dependsOn(factor))
        {
            MathObject exponent = this.exponent.add(((Exponentiation)factor).exponent);
            if (exponent.equals(Constant.ZERO))
            {
                return Constant.ONE;
            }

            if (exponent.equals(Constant.ONE))
            {
                return this.base;
            }

            return new Exponentiation(this.base, exponent);
        }
        else
        {
            return new Product(this, factor);
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        if (this.base instanceof Sum || this.base instanceof Exponentiation)
        {
            builder.append('(').append(this.base).append('(');
        }
        else
        {
            builder.append(this.base);
        }

        builder.append(" ** ").append(this.exponent);
        return builder.toString();
    }

    @Override
    public double evaluate() {
        return Math.pow(this.base.evaluate(), this.exponent.evaluate());
    }

    @Override
    public boolean isMultipleOf(MathObject that) {
        if (that instanceof Product)
        {
            return that.isMultipleOf(this);
        }
        else
        {
            return this.equals(that);
        }
    }

    @Override
    public boolean dependsOn(MathObject that) {
        return this.base.dependsOn(that);
    }

    public boolean equals(MathObject that) {
        if (that instanceof Exponentiation)
        {
            return this.exponent.equals(((Exponentiation)that).exponent)
                    && this.base.equals(((Exponentiation)that).base);
        }

        return false;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.