package org.matheclipse.core.expression;

import java.math.BigDecimal;
import java.math.BigInteger;

import org.apache.commons.math3.fraction.BigFraction;
import org.apache.commons.math3.fraction.FractionConversionException;
import org.matheclipse.core.basic.Config;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.form.output.OutputFormFactory;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IFraction;
import org.matheclipse.core.interfaces.IInteger;
import org.matheclipse.core.interfaces.INumber;
import org.matheclipse.core.interfaces.ISignedNumber;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.visit.IVisitor;
import org.matheclipse.core.visit.IVisitorBoolean;
import org.matheclipse.core.visit.IVisitorInt;

/**
 * 
 */
public class FractionSym extends ExprImpl implements IFraction {

	/**
	 * Be cautious with this method, no new internal rational is created
	 * 
	 * @param numerator
	 * @return
	 */
	protected static FractionSym newInstance(final BigFraction rational) {
		FractionSym r = new FractionSym();
		r.fRational = rational;
		return r;
	}

	public static FractionSym valueOf(final BigInteger numerator) {
		FractionSym r = new FractionSym();
		r.fRational = new BigFraction(numerator, BigInteger.ONE);
		return r;
	}

	/**
	 * 
	 * @param rat
	 * @return
	 * 
	 * @deprecated
	 */
	@Deprecated
	public static FractionSym valueOf(final BigFraction rat) {
		FractionSym r = new FractionSym();
		r.fRational = new BigFraction(rat.getNumerator(), rat.getDenominator());
		return r;
	}

	public static FractionSym valueOf(final BigInteger numerator, final BigInteger denominator) {
		FractionSym r = new FractionSym();
		r.fRational = new BigFraction(numerator, denominator);
		return r;
	}

	public static FractionSym valueOf(final IInteger numerator, final IInteger denominator) {
		FractionSym r = new FractionSym();
		r.fRational = new BigFraction(numerator.getBigNumerator(), denominator.getBigNumerator());
		return r;
	}

	public static FractionSym valueOf(final long numerator, final long denominator) {
		FractionSym r = new FractionSym();
		r.fRational = new BigFraction(numerator, denominator);
		return r;
	}

	public static FractionSym valueOf(final double value) {
		FractionSym r = new FractionSym();
		try {
			r.fRational = new BigFraction(value, Config.DOUBLE_EPSILON, 200);
		} catch (FractionConversionException e) {
			r.fRational = new BigFraction(value);
		}
		return r;
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 2396715994276842438L;

	/* package private */BigFraction fRational;

	private transient int fHashValue = 0;

	private FractionSym() {
		fRational = null;
	}

	/** {@inheritDoc} */
	@Override
	public boolean isZero() {
		return fRational.getNumerator().equals(BigInteger.ZERO);
	}

	/** {@inheritDoc} */
	@Override
	public boolean equalsInt(final int i) {
		return fRational.getNumerator().equals(BigInteger.valueOf(i)) && fRational.getDenominator().equals(BigInteger.ONE);
	}

	/** {@inheritDoc} */
	@Override
	public BigInteger getBigDenominator() {
		return fRational.getDenominator();
	}

	/** {@inheritDoc} */
	@Override
	public BigInteger getBigNumerator() {
		return fRational.getNumerator();
	}

	/** {@inheritDoc} */
	@Override
	public BigFraction getFraction() {
		return fRational;
	}

	/**
	 * Returns the denominator of this fraction.
	 * 
	 * @return denominator
	 */
	@Override
	public IInteger getDenominator() {
		return IntegerSym.valueOf(fRational.getDenominator());
	}

	/**
	 * Returns the numerator of this Rational.
	 * 
	 * @return numerator
	 */
	@Override
	public IInteger getNumerator() {
		return IntegerSym.valueOf(fRational.getNumerator());
	}

	/** {@inheritDoc} */
	@Override
	public int hierarchy() {
		return FRACTIONID;
	}

	@Override
	public IFraction add(final IFraction parm1) {
		return newInstance(fRational.add(((FractionSym) parm1).fRational));
	}

	@Override
	public IFraction multiply(final IFraction parm1) {
		return newInstance(fRational.multiply(((FractionSym) parm1).fRational));
	}

	/** {@inheritDoc} */
	@Override
	public boolean isNegative() {
		return (fRational.getNumerator().compareTo(BigInteger.ZERO) == -1);
	}

	/** {@inheritDoc} */
	@Override
	public boolean isPositive() {
		return (fRational.getNumerator().compareTo(BigInteger.ZERO) == 1);
	}

	/** {@inheritDoc} */
	@Override
	public FractionSym eabs() {
		return newInstance(fRational.abs());
	}

	/**
	 * @param that
	 * @return
	 */
	public BigFraction add(final BigFraction that) {
		return fRational.add(that);
	}

	/**
	 * @param that
	 * @return
	 */
	public BigFraction divide(final BigFraction that) {
		return fRational.divide(that);
	}

	/**
	 * @return
	 */
	@Override
	public double doubleValue() {
		return fRational.doubleValue();
	}

	@Override
	public boolean equals(final Object obj) {
		if (hashCode() != obj.hashCode()) {
			return false;
		}
		if (obj instanceof FractionSym) {
			if (this == obj) {
				return true;
			}
			return fRational.equals(((FractionSym) obj).fRational);
		}
		return false;
	}

	/** {@inheritDoc} */
	@Override
	public IExpr evaluate(EvalEngine engine) {
		if (engine.isNumericMode()) {
			return numericNumber();
		}
		final INumber cTemp = normalize();
		return (cTemp == this) ? null : cTemp;
	}

	public final INumber numericNumber() {
		return F.num(this);
	}

	public INumber normalize() {
		if (getBigDenominator().equals(BigInteger.ONE)) {
			return F.integer(getBigNumerator());
		}
		if (getBigNumerator().equals(BigInteger.ZERO)) {
			return F.C0;
		}
		return this;
	}

	/**
	 * @return
	 */
	public BigInteger getDividend() {
		return fRational.getNumerator();
	}

	/**
	 * @return
	 */
	public BigInteger getDivisor() {
		return fRational.getDenominator();
	}

	@Override
	public int hashCode() {
		if (fHashValue == 0) {
			fHashValue = fRational.hashCode();
		}
		return fHashValue;
	}

	/**
	 * @return
	 */
	public long longValue() {
		return fRational.longValue();
	}

	/**
	 * @param that
	 * @return
	 */
	public BigFraction multiply(final BigFraction that) {
		return fRational.multiply(that);
	}

	/**
	 * @return
	 */
	@Override
	public ISignedNumber negate() {
		return newInstance(fRational.negate());
	}

	/**
	 * @return
	 */
	@Override
	public ISignedNumber opposite() {
		return newInstance(fRational.negate());
	}

	/**
	 * @param that
	 * @return
	 */
	@Override
	public IExpr plus(final IExpr that) {
		if (that instanceof FractionSym) {
			return this.add((FractionSym) that);
		}
		if (that instanceof IntegerSym) {
			return this.add(valueOf(((IntegerSym) that).fInteger));
		}
		return super.plus(that);
	}

	/** {@inheritDoc} */
	@Override
	public ISignedNumber divideBy(ISignedNumber that) {
		if (that instanceof FractionSym) {
			return newInstance(this.divide(((FractionSym) that).fRational));
		}
		if (that instanceof IntegerSym) {
			return this.divideBy(valueOf(((IntegerSym) that).fInteger));
		}
		return Num.valueOf(doubleValue() / that.doubleValue());
	}

	/** {@inheritDoc} */
	@Override
	public ISignedNumber subtractFrom(ISignedNumber that) {
		if (that instanceof FractionSym) {
			return this.add((FractionSym) that.negate());
		}
		if (isZero()) {
			return that.negate();
		}
		if (that instanceof IntegerSym) {
			return this.subtractFrom(valueOf(((IntegerSym) that).fInteger));
		}
		return Num.valueOf(doubleValue() - that.doubleValue());
	}

	/**
	 * Returns this number raised at the specified positive exponent.
	 * 
	 * @param exp
	 *            the positive exponent.
	 * @return thisexp
	 * @throws IllegalArgumentException
	 *             if exp <= 0
	 */
	@Override
	public IFraction pow(int exp) {
		if (exp <= 0)
			throw new IllegalArgumentException("exp: " + exp + " should be a positive number");
		IFraction powSqr = this;
		IFraction result = null;
		while (exp >= 1) { // Iteration.
			if ((exp & 1) == 1) {
				result = (result == null) ? powSqr : result.multiply(powSqr);
			}
			powSqr = powSqr.multiply(powSqr);
			exp >>>= 1;
		}
		return result;
	}

	/** {@inheritDoc} */
	@Override
	public ISignedNumber inverse() {
		return newInstance(NumberUtil.inverse(fRational));
	}

	/**
	 * @param that
	 * @return
	 */
	public BigFraction subtract(final BigFraction that) {
		return fRational.subtract(that);
	}

	/** {@inheritDoc} */
	@Override
	public IExpr times(final IExpr that) {
		if (that instanceof FractionSym) {
			return this.multiply((FractionSym) that);
		}
		if (that instanceof IntegerSym) {
			return this.multiply(valueOf(((IntegerSym) that).fInteger));
		}
		return super.times(that);
	}

	/** {@inheritDoc} */
	@Override
	public String internalFormString(boolean symbolsAsFactoryMethod, int depth) {
		int numerator = fRational.getNumerator().intValue();
		int denominator = fRational.getDenominator().intValue();
		if (numerator == 1) {
			switch (denominator) {
			case 2:
				return "C1D2";
			case 3:
				return "C1D3";
			case 4:
				return "C1D4";
			}
		}
		if (numerator == -1) {
			switch (denominator) {
			case 2:
				return "CN1D2";
			case 3:
				return "CN1D3";
			case 4:
				return "CN1D4";
			}
		}
		return "QQ(" + numerator + "L," + denominator + "L)";
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int toInt() throws ArithmeticException {
		if (fRational.getDenominator().equals(BigInteger.ONE)) {
			return NumberUtil.toInt(fRational.getNumerator());
		}
		if (fRational.getNumerator().equals(BigInteger.ZERO)) {
			return 0;
		}
		throw new ArithmeticException("toInt: denominator != 1");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long toLong() throws ArithmeticException {
		if (fRational.getDenominator().equals(BigInteger.ONE)) {
			return NumberUtil.toLong(fRational.getNumerator());
		}
		if (fRational.getNumerator().equals(BigInteger.ZERO)) {
			return 0L;
		}
		throw new ArithmeticException("toLong: denominator != 1");
	}

	@Override
	public String toString() {
		try {
			StringBuilder sb = new StringBuilder();
			OutputFormFactory.get().convertFraction(sb, fRational, Integer.MIN_VALUE);
			return sb.toString();
		} catch (Exception e1) {
		}
		// fall back to simple output format
		return fRational.getNumerator().toString() + "/" + fRational.getDenominator().toString();
	}

	@Override
	public String fullFormString() {
		StringBuffer buf = new StringBuffer("Rational");
		if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
			buf.append('(');
		} else {
			buf.append('[');
		}
		buf.append(fRational.getNumerator().toString().toString());
		buf.append(',');
		buf.append(fRational.getDenominator().toString().toString());
		if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
			buf.append(')');
		} else {
			buf.append(']');
		}
		return buf.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.matheclipse.parser.interfaces.IFraction#getRational()
	 */
	@Override
	public BigFraction getRational() {
		return fRational;
	}

	/** {@inheritDoc} */
	@Override
	public int sign() {
		return fRational.getNumerator().signum();
	}

	/** {@inheritDoc} */
	@Override
	public int complexSign() {
		return sign();
	}

	/** {@inheritDoc} */
	@Override
	public IInteger ceil() {
		return IntegerSym.valueOf(NumberUtil.ceiling(fRational));
	}

	/** {@inheritDoc} */
	@Override
	public IInteger floor() {
		return IntegerSym.valueOf(NumberUtil.floor(fRational));
	}

	/** {@inheritDoc} */
	@Override
	public IInteger round() {
		return IntegerSym.valueOf(NumberUtil.round(fRational, BigDecimal.ROUND_HALF_EVEN));
	}

	/**
	 * Compares this expression with the specified expression for order. Returns a negative integer, zero, or a positive integer as
	 * this expression is canonical less than, equal to, or greater than the specified expression.
	 */
	@Override
	public int compareTo(final IExpr obj) {
		if (obj instanceof FractionSym) {
			return fRational.compareTo(((FractionSym) obj).fRational);
		}
		if (obj instanceof IntegerSym) {
			return fRational.compareTo(new BigFraction(((IntegerSym) obj).fInteger, BigInteger.ONE));
		}
		return (hierarchy() - (obj).hierarchy());
	}

	@Override
	public boolean isLessThan(ISignedNumber obj) {
		if (obj instanceof FractionSym) {
			return fRational.compareTo(((FractionSym) obj).fRational) < 0;
		}
		if (obj instanceof IntegerSym) {
			return fRational.compareTo(new BigFraction(((IntegerSym) obj).fInteger, BigInteger.ONE)) < 0;
		}
		return fRational.doubleValue() < obj.doubleValue();
	}

	@Override
	public boolean isGreaterThan(ISignedNumber obj) {
		if (obj instanceof FractionSym) {
			return fRational.compareTo(((FractionSym) obj).fRational) > 0;
		}
		if (obj instanceof IntegerSym) {
			return fRational.compareTo(new BigFraction(((IntegerSym) obj).fInteger, BigInteger.ONE)) > 0;
		}
		return fRational.doubleValue() < obj.doubleValue();
	}

	@Override
	public ISymbol head() {
		return F.Rational;
	}

	/** {@inheritDoc} */
	@Override
	public  T accept(IVisitor visitor) {
		return visitor.visit(this);
	}

	/** {@inheritDoc} */
	@Override
	public boolean accept(IVisitorBoolean visitor) {
		return visitor.visit(this);
	}

	/** {@inheritDoc} */
	@Override
	public int accept(IVisitorInt visitor) {
		return visitor.visit(this);
	}

	/** {@inheritDoc} */
	@Override
	public ISignedNumber getIm() {
		return F.C0;
	}

	/** {@inheritDoc} */
	@Override
	public ISignedNumber getRe() {
		return this;
	}
}
 

Ads help maintain this website.