package com.wesleyhome.math.equation;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import org.jscience.mathematics.number.Float64;
import org.jscience.mathematics.number.Real;
/**
* This class is a helper class that makes decimal math in Java a lot easier.
* Eventually, this class will be package protected once the equation grammar
* can evaluation true equations and not just expressions.
*
* @author Justin Wesley
*
*/
final class SmartNumber extends Number {
private static final long serialVersionUID = 3532519792202894341L;
static SmartNumber eq(final Number n) {
if (n instanceof SmartNumber) {
return (SmartNumber) n;
}
return new SmartNumber(n);
}
static SmartNumber eq(final String s) {
return new SmartNumber(s);
}
private final Real currentValue;
private SmartNumber(final Number initialValue) {
currentValue = v(initialValue);
}
private SmartNumber(final String initialValue) {
currentValue = v(initialValue);
}
private Real v(final Number val) {
if (val instanceof Real) {
return (Real) val;
}
if (val instanceof SmartNumber) {
SmartNumber equation = (SmartNumber) val;
return equation.currentValue;
}
return Real.valueOf(val.doubleValue());
}
private Real v(final String stringValue) {
return Real.valueOf(stringValue);
}
private Real v(final SmartNumber eq) {
return eq.currentValue;
}
public SmartNumber plus(final SmartNumber eq) {
return eq(currentValue.plus(v(eq)));
}
public SmartNumber plus(final Number val) {
return plus(eq(val));
}
public SmartNumber minus(final SmartNumber eq) {
return eq(currentValue.minus(v(eq)));
}
public SmartNumber minus(final Number val) {
return minus(eq(val));
}
public SmartNumber times(final SmartNumber eq) {
return eq(currentValue.times(v(eq)));
}
public SmartNumber times(final Number val) {
return times(eq(val));
}
public SmartNumber dividedBy(final SmartNumber eq) {
return eq(currentValue.divide(v(eq)));
}
public SmartNumber dividedBy(final Number val) {
return dividedBy(eq(val));
}
/**
* If the value is a decimal value, this will utilize {@link Float64} to evaluate, otherwise
* the base {@link Real} is used.
* @param eq
* @return
*/
public SmartNumber toThePowerOf(final SmartNumber eq) {
if (eq.isLessThan(v(0))) {
SmartNumber eq2 = eq(1);
return eq(v(eq2.dividedBy(this.toThePowerOf(eq.negate()))));
}
double doubleValue = eq.currentValue.doubleValue();
int intValue = eq.currentValue.intValue();
if (v(doubleValue).minus(v(intValue)).doubleValue() == 0.0) {
return eq(currentValue.pow(eq.intValue()));
}
Float64 base = Float64.valueOf(currentValue.doubleValue());
Float64 exponent = Float64.valueOf(eq.doubleValue());
Float64 pow = base.pow(exponent);
return eq(pow.doubleValue());
}
public SmartNumber toThePowerOf(final Number val) {
return toThePowerOf(eq(val));
}
public SmartNumber negate() {
return eq(currentValue.opposite());
}
public SmartNumber round(final int precision) {
return round(precision, RoundingMode.HALF_UP);
}
public SmartNumber round(final int precision, final RoundingMode roundingMode) {
BigDecimal bigD = BigDecimal.valueOf(doubleValue());
bigD = bigD.setScale(precision, roundingMode);
return eq(bigD);
}
public SmartNumber minumum(final SmartNumber number) {
return isLessThanOrEqualTo(number) ? this : number;
}
public SmartNumber maximum(final SmartNumber number) {
return isGreaterThanOrEqualTo(number) ? this : number;
}
public SmartNumber maximum(final Number number) {
return maximum(eq(number));
}
public SmartNumber minumum(final Number number) {
return minumum(eq(number));
}
public boolean isGreaterThan(final SmartNumber equation) {
return currentValue.isGreaterThan(equation.currentValue);
}
public boolean isGreaterThan(final Number number) {
return isGreaterThan(eq(number));
}
public boolean isGreaterThanOrEqualTo(final Number number) {
return isGreaterThan(number) || equals(number);
}
public boolean isLessThan(final SmartNumber equation) {
return currentValue.isLessThan(equation.currentValue);
}
public boolean isLessThan(final Number number) {
return isLessThan(eq(number));
}
public boolean isLessThanOrEqualTo(final Number number) {
return isLessThan(number) || equals(number);
}
@Override
public double doubleValue() {
return currentValue.doubleValue();
}
@Override
public int intValue() {
return currentValue.intValue();
}
@Override
public long longValue() {
return currentValue.longValue();
}
@Override
public float floatValue() {
return currentValue.floatValue();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + currentValue.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof Number) {
Number number = (Number) obj;
double doubleValue = eq(currentValue).minus(v(number)).doubleValue();
return Math.abs(doubleValue) < 1e-14;
}
return false;
}
public String stringValue() {
NumberFormat numberFormat = NumberFormat.getNumberInstance();
numberFormat.setGroupingUsed(true);
return numberFormat.format(this);
}
@Override
public String toString() {
return currentValue.toString();
}
public Number absoluteValue() {
return eq(currentValue.abs());
}
}