# Symbolic Math/Computer Algebra System redundant code problems

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 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
if (this.equals(Constant.ZERO))
{
return that;
}

if (that instanceof Constant)
{
}

if (that instanceof Variable)
{
}

if (that instanceof Sum)
{
}

if (that instanceof Product)
{
}

if (that instanceof Exponentiation)
{
}

//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 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
if (that instanceof Constant)
{
}

if (that instanceof Variable)
{
}

if (that instanceof Sum)
{
}

if (that instanceof Product)
{
}

if (that instanceof Exponentiation)
{
}

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);
}

{
return this;
}
else
{
}
}

{
return new Product(new Constant(2), this);
}
else
{
}
}

}

}

}

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
{
}
else
{
}
}

@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)
{
++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
if (that instanceof Constant)
{
}

if (that instanceof Variable)
{
}

if (that instanceof Sum)
{
}

if (that instanceof Product)
{
}

if (that instanceof Exponentiation)
{
}

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);
}

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

return new Sum(constantPart, summands);
}

for (int i = 0; i < this.summands.length; ++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);
return new Sum(this.constantPart, summands);
}

for (MathObject summand : this.summands)
{
}

return sum;
}

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 (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);
return new Sum(this.constantPart, summands);
}

for (int i = 0; i < this.summands.length; ++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);
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)
{
}

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
if (that instanceof Constant)
{
}

if (that instanceof Variable)
{
}

if (that instanceof Sum)
{
}

if (that instanceof Product)
{
}

if (that instanceof Exponentiation)
{
}

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);
}

{
return this;
}

}

{
}
else
{
}
}

}

{
}
else
{
}
}

{
}
else
{
}
}

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.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.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
{
}

{
}

{
}

{
}

{
}

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);
}

{
return this;
}
else
{
}
}

}

}

}

{
return new Product(new Constant(2), this);
}
else
{
}
}

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

private MathObject multiply(Variable factor) {
if (this.dependsOn(factor))
{
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))
{
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;
}
}