/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.arithmetic.equation;
import java.util.ArrayList;
import java.util.List;
import jmathexpr.Expression;
import jmathexpr.Variable;
import jmathexpr.arithmetic.equation.rule.EquationRule;
import jmathexpr.arithmetic.equation.rule.RuleMachine;
import jmathexpr.arithmetic.func.Abs;
import jmathexpr.arithmetic.op.Negation;
import jmathexpr.arithmetic.op.Subtraction;
import jmathexpr.arithmetic.op.Sum;
import jmathexpr.relation.Equality;
import jmathexpr.set.Set;
import jmathexpr.util.pattern.AnyPattern;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.pattern.FunctionPattern;
import jmathexpr.util.pattern.NotPattern;
import jmathexpr.util.pattern.TerminationPattern;
import jmathexpr.util.rule.CompositeRule;
import jmathexpr.util.rule.SubRule;
/**
* Absolute value equation in one unknown.
*
* @author Elemér Furka
*/
public class AbsoluteValueEquation extends Equation {
/**
* Creates a new absolute value equation specifying the two sides of the equality.
*
* @param lhs left hand side of the equality
* @param rhs right hand side of the equality
* @param x the unknown in the equation
*/
public AbsoluteValueEquation(Expression lhs, Expression rhs, Variable x) {
super(lhs, rhs, x);
}
/**
* Creates a new absolute value equation instance using an existing equality.
*
* @param equality the equality to be transformed into an equation
* @param x the unknown in the equation
*/
public AbsoluteValueEquation(Equality equality, Variable x) {
this(equality.lhs(), equality.rhs(), x);
}
@Override
protected AbsoluteValueEquation create(Expression lhs, Expression rhs) {
return new AbsoluteValueEquation(lhs, rhs, x);
}
@Override
public Set solve() throws EquationSolveException {
rules = new RuleMachine(this);
rules.addRule(new Isolate());
rules.addRule(new IterativeRule());
Set result = rules.execute();
result = check(result);
return result;
}
/**
* Tests if the given equality is an absolute value equation.
*
* @param equality the equality to test
* @param x the unknown in the equation
* @return true if the equality is an absolute value equation
*/
public static boolean isA(Equality equality, Variable x) {
return equality.contains(new Abs(new FunctionPattern(x)));
}
/**
* Isolates the absolute value expression if possible.
*/
private class Isolate extends CompositeRule {
private final TerminationPattern a = new AnyPattern();
private final ExpressionPattern b = new NotPattern(new Abs(new FunctionPattern(x)));
private final ExpressionPattern s = new FunctionPattern(
new Abs(new FunctionPattern(x)));
private Isolate() {
super(false);
}
@Override
public boolean matches(Expression expr) {
target = expr;
if ((rule = new Addition()).matches(expr)) return true;
if ((rule = new Difference()).matches(expr)) return true;
return false;
}
/**
* abs(f(x)) + a = b -> abs(f(x)) = b - a
*/
private class Addition extends SubRule {
@Override
public boolean matches(Expression expr) {
ExpressionPattern sum = new Sum(s, a);
return new Equality(sum, b).matches(expr);
}
@Override
public Expression apply() {
Equality eq = (Equality) target;
Expression lhs = Sum.subtract(eq.lhs(), a.hit());
Expression rhs = Sum.subtract(eq.rhs(), a.hit());
return new Equality(lhs, rhs);
}
}
/**
* abs(f(x)) - a = b -> abs(f(x)) = b + a
*/
private class Difference extends SubRule {
@Override
public boolean matches(Expression expr) {
ExpressionPattern diff = new Subtraction(s, a);
return new Equality(diff, b).matches(expr);
}
@Override
public Expression apply() {
Equality eq = (Equality) target;
Expression lhs = Sum.add(eq.lhs(), a.hit());
Expression rhs = Sum.add(eq.rhs(), a.hit());
return new Equality(lhs, rhs);
}
}
}
/**
* This rule transforms the current equation into a pair of linear equations.
* |x| = x if x >= 0 and |x| = -x if x < 0.
*/
public class IterativeRule extends CompositeRule implements EquationRule {
private final FunctionPattern f = new FunctionPattern(x);
private final RuleVariations variations = new RuleVariations();
private final List<Equation> equations = new ArrayList();
private int run = 0;
private IterativeRule() {
super(true);
// ruleList initialization (this could be done automatically using reflection...)
variations.addRule(new NonNegative());
variations.addRule(new Negative());
}
public boolean hasNext() {
boolean hasNext = variations.hasNextVariation();
run++;
if (hasNext) {
rule = variations.nextRule();
}
return hasNext;
}
public void advance() {
if (run == 1) {
variations.initialize();
}
equations.add(Equation.convert((Equality) target, x));
}
@Override
public boolean matches(Expression expr) {
boolean matches;
matches = rule.matches(expr);
if (!variations.isInitialized()) {
if (matches) {
variations.addCase();
}
}
return matches;
}
@Override
public List<Equation> convertedEquations() {
return equations;
}
@Override
public Expression subapply() {
Expression applied = rule.apply();
if (variations.hasNextRule()) {
rule = variations.nextRule();
}
return applied;
}
/**
* |x| = x if x >= 0
*/
private class NonNegative extends SubRule {
@Override
public boolean matches(Expression expr) {
return new Abs(f).matches(expr);
}
@Override
public Expression apply() {
return f.hit();
}
}
/**
* |x| = -x if x < 0
*/
private class Negative extends SubRule {
@Override
public boolean matches(Expression expr) {
return new Abs(f).matches(expr);
}
@Override
public Expression apply() {
return new Negation(f.hit());
}
}
private class RuleVariations {
private final List<SubRule> rules = new ArrayList();
/**
* E.g. (0 0 0), (1 0 0), (2 0 0), (0 1 0), ..., (2 2 2). In this case
* rules.size() == 3 and the expression has three matching subexpressions.
*/
private final List<Integer> variations = new ArrayList();
private boolean initialized = false;
private int idx = 0;
private void addRule(SubRule rule) {
rules.add(rule);
}
private void addCase() {
variations.add(0);
}
private void initialize() {
initialized = true;
}
private boolean isInitialized() {
return initialized;
}
private boolean hasNextVariation() {
if (!initialized) {
return true;
} else {
return increment();
}
}
private boolean increment() {
int p, r = rules.size() - 1;
idx = 0;
for (int i = 0; i < variations.size(); i++) {
p = variations.get(i);
if (p < r) {
variations.set(i, ++p);
if (i > 0) {
variations.set(i - 1, 0);
}
return true;
}
}
return false;
}
public boolean hasNextRule() {
return idx < variations.size();
}
private SubRule nextRule() {
if (!initialized) {
return rules.get(0);
} else {
return rules.get(variations.get(idx++));
}
}
@Override
public String toString() {
return variations.toString();
}
}
}
}