Package jmathexpr.arithmetic

Source Code of jmathexpr.arithmetic.Polynomial

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/

package jmathexpr.arithmetic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.TreeMap;
import jmathexpr.AbstractExpression;

import jmathexpr.Expression;
import jmathexpr.Precedence;
import jmathexpr.Variable;
import jmathexpr.arithmetic.natural.NaturalNumber;
import jmathexpr.arithmetic.natural.Naturals;
import jmathexpr.arithmetic.op.Addition;
import jmathexpr.arithmetic.op.Division;
import jmathexpr.arithmetic.op.Exponentiation;
import jmathexpr.arithmetic.op.Multiplication;
import jmathexpr.arithmetic.op.Negation;
import jmathexpr.arithmetic.op.Subtraction;
import jmathexpr.arithmetic.op.Sum;
import jmathexpr.arithmetic.pattern.PolynomialTermPattern;
import jmathexpr.op.Sign;
import jmathexpr.set.OrderedPair;
import jmathexpr.set.Set;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.pattern.TerminationPattern;
import jmathexpr.util.rule.Rule;

/**
* This class represents a univariate polynomial. Its general form is
* a<sub>n</sub>x<sup>n</sup> + ... + a<sub>1</sub>x + a<sub>0</sub>.
*
* In this implementation only the coefficients are stored in a NavigableMap<NaturalNumber, Expression>,
* where the keys correspond to the degree of the terms.
*
* @author Elemér Furka
*/
public class Polynomial extends AbstractExpression {
   
    /**
     * The indeterminate.
     */
    private final Variable x;
   
    /**
     * The coefficients mapped to the order of the corresponding term.
     * Thus a<sub>n</sub> is stored as n -> a<sub>n</sub>.
     */
    private final NavigableMap<NaturalNumber, Expression> coeffs;
   
    private final NaturalNumber ZERO = Naturals.zero();
    private final NaturalNumber ONE = Naturals.one();
   
    /**
     * Creates a new polynomial of the given indeterminate and having the specified
     * terms.
     *
     * @param terms list of terms
     * @param x the indeterminate
     */
    public Polynomial(List<Expression> terms, Variable x) {
        this.x = x;
       
        coeffs = extractCoefficients(terms, x);
    }
   
    private Polynomial(NavigableMap<NaturalNumber, Expression> coeffs, Variable x) {
        this.x = x;
        this.coeffs = coeffs;
    }
   
    /**
     * Converts the specified expression into a polynomial instance.
     *
     * @param expr an expression that can be considered as a polynomial
     * @param x the indeterminate
     * @return the expression converted into a polynomial instance
     */
    public static Polynomial create(Expression expr, Variable x) {
        Polynomial p = convert(expr, x);
       
        if (p != null) {
            return p;
        }
       
        throw new IllegalArgumentException("Cannot convert expression into a polynomial: " + expr);
    }
   
    private static Polynomial convert(Expression expr, Variable x) {
        if (expr.isConstant()) {
            return Polynomial.toPolynomial(expr, x);
        } else if (expr instanceof Variable) {
            return toPolynomial((Variable) expr, x);
        } else if (expr instanceof Multiplication) {
            return Polynomial.toPolynomial((Multiplication) expr, x);
        } else if (expr instanceof Addition) {
            return ((Addition) expr).toPolynomial(x);
        } else if (expr instanceof Subtraction) {
            return ((Subtraction) expr).toPolynomial(x);
        } else if (expr instanceof Sum) {
            return ((Sum) expr).toPolynomial(x);
        } else if (expr instanceof Negation) {
            Polynomial p = create(((Negation) expr).getChild(), x);
           
            return p.negate();
        }
       
        return null;   
    }

    @Override
    public Expression evaluate() {
        Expression xvalue = x.evaluate();
       
        if (xvalue instanceof ANumber) {
            return evaluate((ANumber) xvalue);
        }
       
        NavigableMap<NaturalNumber, Expression> map = new TreeMap();
        Expression c;
       
        for (NaturalNumber n : coeffs.descendingKeySet()) {
            c = coeffs.get(n).evaluate();
           
            if (!(c instanceof ANumber && ((ANumber) c).isZero())) {
                map.put(n, c);
            }
        }
       
        if (map.isEmpty()) {
            map.put(ZERO, ZERO);
        }
       
        return new Polynomial(map, x);
    }
   
    private Expression evaluate(ANumber xvalue) {
        Expression sum = null;
        Expression c, term;
       
        for (NaturalNumber n : coeffs.descendingKeySet()) {
            c = coeffs.get(n).evaluate();
            term = new Multiplication(c, new Exponentiation(xvalue, n));
           
            if (sum == null) {
                sum = new Sum(term);
            } else {
                sum = Sum.add(sum, term);
            }
        }
       
        return sum.evaluate();
    }
   
    /**
     * Returns the discriminant of this polynomial. For a quadratic polynomial
     * ax^2 + bx + c it returns b^2 - 4ac.
     *
     * @return the discriminant of this polynomial
     */
    public Expression discriminant() {
        NaturalNumber two = Naturals.getInstance().create(2);
        NaturalNumber four = Naturals.getInstance().create(4);
       
        if (degree().equals(two)) {
            Expression a = coeffs.get(two);
            Expression b = coeffs.get(ONE);
            Expression c = coeffs.get(ZERO);
           
            return new Subtraction(new Exponentiation(b, two),
                    new Multiplication(four, new Multiplication(a, c)));
        } else {
            throw new UnsupportedOperationException("Cannot compute discriminant: " + this);
        }
    }

    @Override
    public boolean isConstant() {
        return coeffs.lastKey().isZero();
    }

    @Override
    public boolean contains(ExpressionPattern pattern) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean isApplicable(Rule rule) {
        boolean matches = rule.matches(this);
       
        if (matches) {
            rule.register(this);
        }
       
        return matches;
    }

    @Override
    public Precedence getPrecedence() {
        return Precedence.Addition;
    }
   
    /**
     * Returns the coefficient of the term of the specified degree.
     *
     * @param degree the degree of the term
     * @return the coefficient
     */
    public Expression getCoefficient(long degree) {
        Expression c = coeffs.get(Naturals.getInstance().create(degree));
       
        return c != null ? c : ZERO;
    }
   
    /**
     * Returns the term of the given degree or 0 if no such term exists.
     *
     * @param degree the degree of the requested term
     * @return the requested term or 0
     */
    public Expression getTerm(NaturalNumber degree) {
        Expression c = coeffs.get(degree);
       
        if (c != null) {
            if (degree.equals(ZERO)) {
                return c;
            } else if (degree.equals(ONE)) {
                return new Multiplication(c, x);
            } else {
                return new Multiplication(c, new Exponentiation(x, degree));
            }
        } else {
            return ZERO;
        }
    }
   
    /**
     * Returns the lead term, the term that has the highest degree.
     *
     * @return the lead term
     */
    public Expression lead() {
        return getTerm(coeffs.lastKey());
    }
   
    /**
     * Returns the coefficient of the lead term.
     *
     * @return the coefficient of the lead term
     */
    public Expression leadCoefficient() {
        return coeffs.lastEntry().getValue();
    }
   
    /**
     * Performs an Euclidean division so that this polynomial A = BQ + R.
     *
     * @param b the divisor B
     * @return the ordered pair (Q, R)
     */
    public OrderedPair euclideanDivision(Polynomial b) {
        if (b.isZero()) {
            throw new IllegalArgumentException("Cannot divide by 0. Dividend: " + this);
        }
       
        NavigableMap<NaturalNumber, Expression> q = new TreeMap(); // quotient
        NavigableMap<NaturalNumber, Expression> r = new TreeMap(); // reminder
        NaturalNumber bdeg = b.degree();
        Expression bc = b.leadCoefficient();
        Expression c; // next coefficient
        NaturalNumber n; // degree of the next term in q
        Expression rp, bi; // temporary variables
        NaturalNumber p; //temporary position
       
        q.put(ZERO, ZERO);
        r.putAll(this.coeffs);
       
        while (!isZero(r) && bdeg.le(r.lastKey())) {
            c = new Division(r.lastEntry().getValue(), bc).evaluate();
            n = (NaturalNumber) r.lastKey().subtract(bdeg);
           
            q.put(n, c);
           
            for (NaturalNumber i : b.coeffs.descendingKeySet()) {
                p = (NaturalNumber) n.add(i);
               
                rp = r.get(p);
                bi = b.coeffs.get(i);
               
                rp = rp != null ? new Subtraction(rp, new Multiplication(c, bi)).evaluate()
                        : new Negation(new Multiplication(c, bi)).evaluate();

                if (rp instanceof ANumber && ((ANumber) rp).isZero() && !p.isZero()) {
                    r.remove(p);
                } else {
                    r.put(p, rp);
                }
            }
        }
       
        if (r.isEmpty()) {
            r.put(ZERO, ZERO);
        }
       
        return new OrderedPair(new Polynomial(q, x), new Polynomial(r, x));
    }
   
    /**
     * Subtract the specified polynomial (Q(X)) from this one (P(X)).
     *
     * @param subtrahend the subtrahend polynomial
     * @return P(X) - Q(X)
     */
    public Polynomial subtract(Polynomial subtrahend) {
        NavigableMap<NaturalNumber, Expression> map = new TreeMap();
        Expression minuend, c;
       
        map.putAll(coeffs);
       
        for (NaturalNumber n : subtrahend.coeffs.keySet()) {
            minuend = coeffs.get(n);
           
            if (minuend != null) {
                c = new Subtraction(minuend, subtrahend.coeffs.get(n)).evaluate();
            } else {
                c = new Negation(subtrahend.coeffs.get(n)).evaluate();
            }
           
            if (c instanceof ANumber && ((ANumber) c).isZero()) {
                map.remove(n);
            } else {
                map.put(n, c);
            }
        }
       
        return new Polynomial(map, x);
    }
   
    /**
     * Subtract an arbitrary expression from this polynomial. The result is a
     * polynomial iff the subtrahend is (or can be converted to) also a polynomial.
     *
     * @param expr an arbitrary expression
     * @return the result of the subtraction
     */
    public Expression subtract(Expression expr) {
        Polynomial p = convert(expr, x);
       
        if (p != null) {
            return subtract(p);
        } else {
            return new Subtraction(this, expr);
        }
    }
   
    /**
     * Performs a division with the given expression as the divisor.
     *
     * @param expr an arbitrary expression
     * @return a new polynomial if the division can be performed or a new
     * Division instance
     */
    public Expression divide(Expression expr) {
        if (expr.isConstant()) {
            return divideByConstant(expr);
        } else {
            throw new UnsupportedOperationException("Division not yet supported: " + expr);
        }
    }
   
    private Polynomial divideByConstant(Expression expr) {
        NavigableMap<NaturalNumber, Expression> map = new TreeMap();
        Expression c;
       
        for (NaturalNumber n : coeffs.keySet()) {
            c = coeffs.get(n);
           
            map.put(n, new Division(c, expr).evaluate());
        }
       
        return new Polynomial(map, x);
    }
   
    /**
     * Negates this polynomial by negating all coefficients.
     *
     * @return the negated polynomial
     */
    public Polynomial negate() {
        NavigableMap<NaturalNumber, Expression> map = new TreeMap();
        Expression c;
       
        for (NaturalNumber n : coeffs.keySet()) {
            c = coeffs.get(n);
           
            if (c instanceof ANumber) {
                map.put(n, ((ANumber) c).negate());
            } else {
                map.put(n, new Negation(c));
            }
        }
       
        return new Polynomial(map, x);
    }
   
    public Polynomial multiply(Expression multiplier) {
        if (multiplier.isConstant()) {
            NavigableMap<NaturalNumber, Expression> multiplied = new TreeMap();
           
            for (NaturalNumber n : coeffs.keySet()) {
                multiplied.put(n, new Multiplication(coeffs.get(n), multiplier).evaluate());
            }

            return new Polynomial(multiplied, x);
        } else
            throw new UnsupportedOperationException("Missing implementation: " + multiplier);
    }
   
    /**
     * Returns the degree of this polynomial.
     *
     * @return the degree of this polynomial
     */
    public NaturalNumber degree() {
        return coeffs.lastKey();
    }
   
    /**
     * Tests if this polynomial is the zero polynomial.
     *
     * @return true if this instance only consists of a zero constant term
     */
    public boolean isZero() {
        return isZero(coeffs);
    }
   
    private boolean isZero(NavigableMap<NaturalNumber, Expression> map) {
        for (Expression t : map.values()) {
            if (!(t instanceof ANumber) || !((ANumber) t).isZero()) {
                return false;
            }
        }
       
        return true;
    }

    @Override
    public List<Expression> getChildren() {
        List<Expression> children = new ArrayList();
       
        for (NaturalNumber n : coeffs.descendingKeySet()) {
            children.add(getTerm(n));
        }
       
        return Collections.unmodifiableList(children);
    }
   
    /**
     * Tests if this polynomial matches the given addition.
     *
     * @param addition an addition pattern
     * @return true if this polynomial matches the addition
     */
    public boolean matches(Addition addition) {
        if (coeffs.size() == 2) {
            Expression lhs = getTerm(coeffs.lastKey());
            Expression rhs = getTerm(coeffs.firstKey());
           
            return ((ExpressionPattern) addition.lhs()).matches(lhs)
                    && ((ExpressionPattern) addition.rhs()).matches(rhs);
        } else {
            return false;
        }
    }
   
    /**
     * Tests if this polynomial matches the given product.
     *
     * @param multiplication the product pattern
     * @return true if this polynomial matches the pattern
     */
    public boolean matches(Multiplication multiplication) {
        if (coeffs.size() == 1) {
            Expression term = getTerm(coeffs.lastKey());
           
            return multiplication.matches(term);
        } else {
            return false;
        }
    }
   
    /**
     * Tests if this polynomial matches the given variable.
     *
     * @param var a variable pattern
     * @return true if this polynomial has exactly one term x (= 1*x^1) and it
     * equals to the given variable
     */
    public boolean matches(Variable var) {
        if (coeffs.size() == 1) {
            Expression c = leadCoefficient();
           
            return c instanceof ANumber && ((ANumber) c).isOne() && var.matches(x);
        } else {
            return false;
        }
    }
   
    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        Expression c;
       
        for (NaturalNumber n : coeffs.descendingKeySet()) {
            c = coeffs.get(n);
           
            if (result.length() == 0) {
                result.append(toString(n, c));
            } else if (c instanceof ANumber) {
                if (((ANumber) c).isNegative()) {
                    c = ((ANumber) c).negate();

                    result.append(" - " + toString(n, c));
                } else {
                    result.append(" + " + toString(n, c));
                }
            } else {
                result.append(" + " + toString(n, c));
            }
        }
       
        return result.toString();
    }
   
    private String toString(NaturalNumber n, Expression c) {
        if (n.isZero()) {
            return c.toString();
        } else if (n.isOne()) {
            if (c instanceof ANumber && ((ANumber) c).isOne()) {
                return x.toString();
            } else {
                return String.format("%s%s", c, x);
            }
        } else if (c instanceof ANumber && ((ANumber) c).isOne()) {
            return String.format("%s%s%s", x, Sign.Exponentiation, n);
        } else {
            return String.format("%s%s%s%s", c, x, Sign.Exponentiation, n);
        }
    }

    @Override
    public String toUnicode() {
        return toString();
    }
   
    @Override
    public boolean equals(Object object) {
        if (object == null) return false;
        if (this == object) return true;
        if (object instanceof Polynomial) {
            Polynomial other = (Polynomial) object;
           
            return x.equals(other.x) && coeffs.equals(other.coeffs);
        }
       
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 67 * hash + Objects.hashCode(this.x);
        hash = 67 * hash + Objects.hashCode(this.coeffs);
        return hash;
    }
   
    private NavigableMap<NaturalNumber, Expression> extractCoefficients(
            List<Expression> terms, Variable x) {
        NavigableMap<NaturalNumber, Expression> map = new TreeMap(); // order -> coefficient
        PolynomialTermPattern pt = new PolynomialTermPattern(x);
        NaturalNumber n;
        Expression c, c0;
       
        for (Expression t : terms) {
            if (pt.matches(t)) {
                n = pt.exponent();
                c = pt.coefficient();
            } else {
                throw new IllegalArgumentException(
                        String.format("Illegal polynomial term: %s (%s)", t, t.getClass()));
            }
           
            c0 = map.get(n);
           
            if (c0 != null) {
                c = new Addition(c0, c).evaluate();
            }
           
            map.put(n, c);
        }
       
        return map;
    }
   
    private static Polynomial toPolynomial(Multiplication term, Variable x) {
        NavigableMap<NaturalNumber, Expression> coeffs = new TreeMap();
        TerminationPattern c = Numbers.constant("c");
        TerminationPattern n = Numbers.constant("n");
        ExpressionPattern cxn = new Multiplication(c, new Exponentiation(x, n));

        if (cxn.matches(term)) {
            coeffs.put((NaturalNumber) n.hit(), c.hit());
        } else {
            throw new IllegalArgumentException("Illegal polynomial term: " + term);
        }
       
        return new Polynomial(coeffs, x);
    }
   
    private static Polynomial toPolynomial(Variable v, Variable x) {
        if (v.equals(x)) {
            NavigableMap<NaturalNumber, Expression> coeffs = new TreeMap();

            coeffs.put(Naturals.one(), Naturals.one());

            return new Polynomial(coeffs, x);
        } else {
            return Polynomial.toPolynomial((Expression) v, x);
        }
    }
   
    private static Polynomial toPolynomial(Expression c, Variable x) {
        NavigableMap<NaturalNumber, Expression> coeffs = new TreeMap();
       
        coeffs.put(Naturals.zero(), c);
       
        return new Polynomial(coeffs, x);
    }

    @Override
    public Set domain() {
        return x.domain();
    }

    @Override
    public Set codomain() {
        return x.codomain();
    }
}
TOP

Related Classes of jmathexpr.arithmetic.Polynomial

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.