Package jmathexpr.arithmetic.op

Source Code of jmathexpr.arithmetic.op.Sum$Matcher

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.arithmetic.op;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import jmathexpr.AbstractExpression;
import jmathexpr.Constant;

import jmathexpr.Expression;
import jmathexpr.Precedence;
import jmathexpr.Variable;
import jmathexpr.arithmetic.ANumber;
import jmathexpr.arithmetic.Polynomial;
import jmathexpr.arithmetic.func.Lcm;
import jmathexpr.arithmetic.natural.Naturals;
import jmathexpr.arithmetic.pattern.FractionPattern;
import jmathexpr.arithmetic.pattern.NumberPattern;
import jmathexpr.arithmetic.real.Reals;
import jmathexpr.op.Operation;
import jmathexpr.set.Set;
import jmathexpr.util.logging.Logger;
import jmathexpr.util.pattern.AbstractPattern;
import jmathexpr.util.pattern.AnyPattern;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.pattern.FunctionPattern;
import jmathexpr.util.pattern.TerminationPattern;
import jmathexpr.util.rule.Rule;

/**
* Sum of multiple (one, two or more) terms using either the plus or the minus
* sign.
*
* @author Elemér Furka
*/
public class Sum extends AbstractExpression implements Operation, ExpressionPattern {

    private final List<Expression> terms;

    /**
     * Creates a new Sum instance decomposing the given expression into additive
     * terms.
     *
     * @param expr an arbitrary expression
     */
    public Sum(Expression expr) {
        terms = new ArrayList();

        add(expr);
    }
   
    public Sum(Expression... terms) {
        this.terms = new ArrayList();
       
        for (Expression t : terms) {
            add(t);
        }
    }

    private Sum(List<Expression> operands) {
        terms = operands;
    }

    /**
     * Factory method that creates a new Sum instance by adding the two
     * arguments. Both arguments are extracted if possible.
     *
     * @param augend the augend
     * @param addend the addend
     * @return a new Addition or Sum instance
     */
    public static Operation add(Expression augend, Expression addend) {
        Sum sum = new Sum(augend);

        sum.add(addend);
       
        if (sum.getArity() == 2) {
            return new Addition(augend, addend);
        } else {
            return sum;
        }
    }

    /**
     * Factory method that creates a new Sum instance by subtracting the second
     * argument from the first one. Both arguments are extracted if possible.
     *
     * @param minuend the minuend
     * @param subtrahend the subtrahend
     * @return a new Subtraction or Sum instance
     */
    public static Operation subtract(Expression minuend, Expression subtrahend) {
        Sum sum = new Sum(minuend);

        sum.subtract(subtrahend);

        if (sum.getArity() == 2) {
            return new Subtraction(minuend, subtrahend);
        } else {
            return sum;
        }
    }

    private void add(Sum sum) {
        for (Expression t : sum.terms) {
            add(t);
        }
    }

    private void add(Addition addition) {
        add(addition.lhs());
        add(addition.rhs());
    }

    private void add(Subtraction subtraction) {
        add(subtraction.lhs());
        subtract(subtraction.rhs());
    }
   
    private void add(Expression expr) {
        if (expr instanceof Addition) {
            add((Addition) expr);
        } else if (expr instanceof Sum) {
            add((Sum) expr);
        } else if (expr instanceof Subtraction) {
            add((Subtraction) expr);
        } else {
            terms.add(expr);
        }
    }

    private void subtract(Expression expr) {
        if (expr instanceof Addition) {
            subtract((Addition) expr);
        } else if (expr instanceof Sum) {
            subtract((Sum) expr);
        } else if (expr instanceof Subtraction) {
            subtract((Subtraction) expr);
        } else {
            terms.add(new Negation(expr));
        }
    }
   
    private void subtract(Addition addition) {
        subtract(addition.lhs());
        subtract(addition.rhs());       
    }

    private void subtract(Sum sum) {
        for (Expression t : sum.terms) {
            subtract(t);
        }
    }
   
    private void subtract(Subtraction subtraction) {
        subtract(subtraction.lhs());
        add(subtraction.rhs());       
    }

    @Override
    public List<Expression> operands() {
        return Collections.unmodifiableList(terms);
    }

    @Override
    public List<Expression> getChildren() {
        return operands();
    }
   
    @Override
    public Expression evaluate() {
        Expression simplified = simplify();
       
        if (!(simplified instanceof Sum)) return simplified;
       
        List<Expression> evaluated = ((Sum) simplified).terms;
        Map<Expression, List<ANumber>> constants = selectConstants(evaluated);
        Expression constant = combineConstants(constants);
       
        if (constants.size() == terms.size()) {
            return constant;
        }

        Map<Expression, List<Expression>> expressionMap = selectLikeTerms(evaluated);
       
        return build(constant, expressionMap);
    }
   
    private Expression simplify() {
        List<Expression> evaluated = evaluate(terms);
        List<Expression> denominators = new ArrayList();
        FractionPattern p = new FractionPattern();
       
        for (Expression t : evaluated) {
            if (p.matches(t)) {
                denominators.add(p.denominator());
            }
        }
       
        if (!denominators.isEmpty()) { // summing fractions
            Expression lcd = new Lcm(denominators).evaluate();
           
            return new Division(multiply(lcd), lcd).evaluate();
        }
       
        return new Sum(evaluated);
    }
   
    private Expression build(Expression constant, Map<Expression, List<Expression>> expressionMap) {
        Sum sum = null;
        Map<Expression, List<ANumber>> constants;
        Expression coeff;
       
        if (!expressionMap.isEmpty()) {
            sum = new Sum();
           
            for (Expression e : expressionMap.keySet()) {
                constants = selectConstants(expressionMap.get(e));
                coeff = combineConstants(constants);
               
                if (coeff instanceof ANumber && ((ANumber) coeff).isOne()) { // c = 1
                    sum.add(e);
                } else if (coeff instanceof ANumber && ((ANumber) coeff).negate().isOne()) { // c = -1
                    sum.add(new Negation(e));
                } else if (!(coeff instanceof ANumber && ((ANumber) coeff).isZero())) { // c <> 0
                    sum.add(new Multiplication(coeff, e));
                }
            }
        }
       
        if (!(constant instanceof ANumber && ((ANumber) constant).isZero())) {
            if (sum == null) {
                sum = new Sum(constant);
            } else {
                sum.add(constant);
            }
        }
       
        if (sum == null) {
            return Naturals.zero();
        }
       
        return sum.toSimpleExpression();
    }
   
    private Expression toSimpleExpression() {
        if (terms.size() == 1) {
            return terms.get(0);
        } else if (terms.size() == 2) {
            Expression a = terms.get(0);
            Expression b = terms.get(1);
           
            if (b instanceof Negation) {
                return new Subtraction(a, ((Negation) b).getChild());
            } else if (b instanceof ANumber && ((ANumber) b).isNegative()) {
                return new Subtraction(a, ((ANumber) b).negate());
            } else {
                return new Addition(a, b);
            }
        } else {
            return this;
        }
    }

    private List<Expression> evaluate(List<Expression> unevaluated) {
        List<Expression> result = new ArrayList();
        Expression evaluated;

        for (Expression t : unevaluated) {
            evaluated = t.evaluate();
           
            if (evaluated instanceof Addition) {
                result.add(((Addition) evaluated).lhs());
                result.add(((Addition) evaluated).rhs());
            } else if (evaluated instanceof Subtraction) {
                result.add(((Subtraction) evaluated).lhs());
                result.add(toMultiplication(
                        new Negation(((Subtraction) evaluated).rhs()).evaluate()));
            } else if (evaluated instanceof Sum) {
                for (Expression e : ((Sum) evaluated).terms) {
                    result.add(e);
                }
            } else if (evaluated instanceof Negation) {
                result.add(toMultiplication(evaluated));
            } else {
                result.add(evaluated);
            }
        }

        return result;
    }
   
    private static Expression toMultiplication(Expression negation) {
        if (negation instanceof Negation) {
            return ((Negation) negation).toMultiplication();
        } else {
            return negation;
        }
    }
   
    private Map<Expression, List<ANumber>> selectConstants(List<Expression> evaluated) {
        Map<Expression, List<ANumber>> selected = new HashMap(); // ai*c -> a1, a2, ... an
        NumberPattern a = new NumberPattern();
        TerminationPattern c = new Constant();
        ExpressionPattern p = new Multiplication(a, c);
       
        for (Expression e : evaluated) {
            if (e.isConstant()) {
                if (e instanceof ANumber) { // 1 -> numbers
                    addToSelectionMap(selected, Naturals.one(), (ANumber) e);
                } else if (p.matches(e)) {
                    addToSelectionMap(selected, c.hit(), a.hit());
                } else {
                    addToSelectionMap(selected, e, Naturals.one());
                }
            }
        }

        return selected;
    }
   
    private Map<Expression, List<Expression>> selectLikeTerms(List<Expression> evaluated) {
        Map<Expression, List<Expression>> selected = new HashMap(); // ai*f(x) -> a1, a2, ... an
        TerminationPattern c = new Constant();
        TerminationPattern f = new FunctionPattern(new Variable());
        ExpressionPattern p = new Multiplication(c, f);
       
        for (Expression e : evaluated) {
            if (e.isConstant()) {
                continue;
            } else if (p.matches(e)) {
                addToSelectionMap(selected, f.hit(), c.hit());
            } else {
                addToSelectionMap(selected, e, Naturals.one());
            }
        }

        return selected;
    }
   
    private static <E extends Expression> void addToSelectionMap(
            Map<Expression, List<E>> selected, Expression key, E value) {
        if (selected.containsKey(key)) {
            selected.get(key).add(value);
        } else {
            List<E> list = new ArrayList();

            list.add(value);                   
            selected.put(key, list);
        }       
    }
   
    private Expression combineConstants(Map<Expression, List<ANumber>> selected) {
        Sum combined = new Sum();
        ANumber sum;

        for (Expression c : selected.keySet()) {
            sum = Naturals.zero();
           
            for (ANumber a : selected.get(c)) {
                sum = sum.add(a);
            }
           
            if (sum.isOne()) {
                combined.add(c);
            } else if (!sum.isZero()) {
                if (c instanceof ANumber) { // c = 1
                    combined.add(sum);
                } else if (sum.negate().isOne()) {
                    combined.add(new Negation(c));
                } else {
                    combined.add(new Multiplication(sum, c));
                }
            }
        }
       
        if (combined.terms.isEmpty()) {
            return Naturals.zero();
        }
       
        return combined.toSimpleExpression();
    }
   
    @Override
    public Precedence getPrecedence() {
        return Precedence.Addition;
    }

    @Override
    public int getArity() {
        return terms.size();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();

        for (Expression t : terms) {
            if (builder.length() == 0) {
                    builder.append(t);
            } else if (t instanceof Negation) {
                builder.append(" - " + ((Negation) t).getChild());
            } else {
                builder.append(" + " + t);               
            }
        }

        return builder.toString();
    }
   
    @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 Sum)) return false;

        Sum other = (Sum) object;

        if (terms.size() != other.terms.size()) return false;

        for (int i = 0; i < terms.size(); i++) {
            if (!terms.get(i).equals(other.terms.get(i))) {
                return false;
            }
        }

        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 53 * hash + Objects.hashCode(this.terms);
        return hash;
    }

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

    @Override
    public Set codomain() {
        return Reals.getInstance();
    }

    @Override
    public boolean isConstant() {
        for (Expression t : terms) {
            if (!t.isConstant()) {
                return false;
            }
        }

        return true;
    }

    @Override
    public boolean contains(ExpressionPattern pattern) {
        if (pattern instanceof Sum) {
            Sum p = (Sum) pattern;
            boolean matches = true;

            if (terms.size() == p.terms.size()) {
                Expression t;

                for (int i = 0; matches && i < terms.size(); i++) {
                    t = terms.get(i);

                    if (!t.contains((ExpressionPattern) p.terms.get(i))) {
                        matches = false;
                    }
                }
            }
           
            if (matches) {
                return true;
            }
        }
       
        for (Expression t : terms) {
            if (t.contains(pattern)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean isApplicable(Rule rule) {
        boolean isApplicable = false;
        List<Expression> subs = new ArrayList();
        Expression t;
       
        subs.addAll(terms);
       
        for (int i = 0; i < subs.size(); i++) {
            t = subs.get(i);
           
            if (t.isApplicable(rule)) {
                isApplicable = true;
                if (rule.isRecursive()) {
                    subs.add(i, rule.subapply());
                }
            }
        }
       
        Expression transformed = isApplicable && rule.isRecursive() ? new Sum(subs) : this;
       
        if (rule.matches(transformed)) {
            return true;
        } else if (isApplicable) {
            rule.register(transformed);
        }
       
        return isApplicable;
    }
   
    /**
     * Converts this sum into a polynomial.
     *
     * @param x the indeterminate
     * @return a newly created polynomial instance
     */
    public Polynomial toPolynomial(Variable x) {
        return new Polynomial(terms, x);
    }
   
    /**
     * Used to test other additive operation patterns (Addition and Subtraction).
     *
     * @param pattern an Addition or Subtraction instance
     * @return true if this sum matches the given pattern
     */
    public boolean matches(ExpressionPattern pattern) {
        if (pattern instanceof Addition) {
            Addition p = (Addition) pattern;
           
            return terms.size() == 2
                    && ((ExpressionPattern) p.lhs()).matches(terms.get(0))
                    && ((ExpressionPattern) p.rhs()).matches(terms.get(1));
        } else if (pattern instanceof Subtraction) {
            Subtraction p = (Subtraction) pattern;
           
            if (terms.size() == 2) {
                if (!((ExpressionPattern) p.lhs()).matches(terms.get(0))) {
                    return false;
                }
               
                Expression t1 = terms.get(1);
               
                if (t1 instanceof Negation) {
                    return ((ExpressionPattern) p.rhs()).matches(((Negation) t1).getChild());
                }
            }

            return false;
        } else {
            throw new IllegalArgumentException("Unexpected pattern: " + pattern);
        }
    }

    @Override
    public boolean matches(Expression expr) {
        return new Matcher().matches(expr);
    }
   
    /**
     * Multiplies all terms of this sum by the given expression from left.
     *
     * @param expr an arbitrary expression
     * @return a new Sum instance having its all terms multiplied by the
     * specified expression
     */
    public Sum multiply(Expression expr) {
        List<Expression> multiplied = new ArrayList();
       
        for (Expression t : terms) {
            multiplied.add(new Multiplication(expr, t));
        }
       
        return new Sum(multiplied);
    }
   
    /**
     * Negates this sum by negating each of its terms.
     *
     * @return a new Sum instance having negated terms
     */
    public Sum negate() {
        List<Expression> negated = new ArrayList();
       
        for (Expression t : terms) {
            negated.add(new Negation(t));
        }
       
        return new Sum(negated);       
    }
   
    private class Matcher extends AbstractPattern {

        private final List<Expression> expressions = new ArrayList();
       
        /**
         * Pattern index -> matching expression
         */
        private final Map<Integer, Expression> matchingPatterns = new HashMap();
       
        @Override
        public boolean matches(Expression expr) {
            if (expr instanceof Addition) {
                expressions.add(((Addition) expr).lhs());
                expressions.add(((Addition) expr).rhs());

                if (terms.size() == 2) {
                    return hasMatchingTerm() && hasMatchingTerm();
                }
            } else if (expr instanceof Subtraction) {
                expressions.add(((Subtraction) expr).lhs());
                expressions.add(new Negation(((Subtraction) expr).rhs()));

                if (terms.size() == 2) {
                    return hasMatchingTerm() && hasMatchingTerm();
                }
            } else if (expr instanceof Sum) {
                if (terms.size() > ((Sum) expr).terms.size()) {
                    return false;
                }
               
                for (Expression t : ((Sum) expr).terms) {
                    expressions.add(t);
                }

                 for (int i = 0; i < terms.size(); i++) {
                    if (!hasMatchingTerm()) {
                        return false;
                    }
                }
               
                return true;
            }

            return false;
        }
   
        private boolean hasMatchingTerm() {
            ExpressionPattern p;
            Expression e;
           
            for (int i = 0; i < terms.size(); i++) { // looping over the patterns...
                if (matchingPatterns.containsKey(i)) {
                    continue;
                }
               
                p = (ExpressionPattern) terms.get(i);
               
                if (p instanceof AnyPattern && i == terms.size() - 1) {
                    Sum rest = new Sum(expressions);
                   
                    return p.matches(rest);
                }
               
                for (int j = 0; j < expressions.size(); j++) { // ... to find a matching term
                    e = expressions.get(j);

                    if (p.matches(e)) {
                        matchingPatterns.put(i, e);
                        expressions.remove(j);

                        return true;
                    }
                }
               
                return false;
            }

            return false;
        }
    }
}
TOP

Related Classes of jmathexpr.arithmetic.op.Sum$Matcher

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.