Package lipstone.joshua.parser.backend

Source Code of lipstone.joshua.parser.backend.StepSolveThread

package lipstone.joshua.parser.backend;

import java.util.ArrayList;
import java.util.concurrent.RecursiveTask;

import lipstone.joshua.parser.Parser;
import lipstone.joshua.parser.exceptions.ParserException;
import lipstone.joshua.parser.exceptions.UndefinedResultException;
import lipstone.joshua.parser.types.BigDec;
import lipstone.joshua.parser.util.ConsCell;
import lipstone.joshua.parser.util.ConsType;

public class StepSolveThread extends RecursiveTask<ArrayList<BigDec>> {
  private ConsCell equation, slopeEquation;
  private ArrayList<ConsCell> slopeCells, variableCells;
  private static boolean containsFactorial;
  private static ArrayList<String> vars = null;
  private static Parser parser;
  private BigDec xMin, xMax, delta, domainMax;
  private static BigDec answer, accuracy, range;
  public ParserException error;
 
  public StepSolveThread(ConsCell equation, Parser parser, BigDec delta, BigDec accuracy, BigDec xMin, BigDec xMax, ArrayList<String> vars, BigDec range) throws ParserException {
    this(equation, delta, xMin, xMax);
    this.parser = parser;
    this.accuracy = accuracy;
    this.vars = new ArrayList<String>(vars);
    this.range = range;
    ArrayList<ConsCell> equationParts = equation.splitOnIdentifier("=");
    if (equationParts.size() == 1)
      equationParts.add(new ConsCell(BigDec.ZERO, ConsType.NUMBER));
    if (parser.containsVariables(equationParts.get(0), vars) && parser.containsVariables(equationParts.get(1), vars))
      equation = equationParts.get(0).append(parser.getCAS().invertSign(equation));
    else {
      if (parser.containsVariables(equationParts.get(0), vars)) {
        equation = equationParts.get(0);
        ConsCell temp = parser.run(parser.preProcess(equationParts.get(1)));
        if (temp.getCarType() != ConsType.NUMBER || temp.length() != 1)
          throw new UndefinedResultException("The NumericalSolver requires that the equation contain only one variable.", null);
        answer = (BigDec) temp.getCar();
      }
      if (parser.containsVariables(equationParts.get(1), vars)) {
        equation = equationParts.get(1);
        ConsCell temp = parser.run(parser.preProcess(equationParts.get(0)));
        if (temp.getCarType() != ConsType.NUMBER || temp.length() != 1)
          throw new UndefinedResultException("The NumericalSolver requires that the equation contain only one variable.", null);
        answer = (BigDec) temp.getCar();
      }
    }
    this.equation = parser.preProcess(equation).clone();
    variableCells = new ArrayList<ConsCell>();
    containsFactorial = this.equation.containsIdentifier("factorial");
    initVariableCells(this.equation);
  }
 
  private StepSolveThread(ConsCell equation, BigDec delta, BigDec xMin, BigDec xMax) throws ParserException {
    super();
    //Yes, I know that this looks horrible, but it works.
    slopeCells = new ArrayList<ConsCell>();
    //First, create the slope structure.  This creates: (a - b)/(c - d)
    slopeEquation = new ConsCell(new ConsCell("a", ConsType.IDENTIFIER, new ConsCell('-', ConsType.OPERATOR, new ConsCell("b", ConsType.IDENTIFIER), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL,
        new ConsCell('/', ConsType.OPERATOR, new ConsCell(new ConsCell("c", ConsType.IDENTIFIER, new ConsCell('-', ConsType.OPERATOR, new ConsCell("d", ConsType.IDENTIFIER), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL);
    slopeCells.add((ConsCell) slopeEquation.cadr("a").getElementA()); //This saves a's spot.
    slopeCells.add((ConsCell) slopeEquation.cadr("add").getElementA()); //This saves b's spot.
    slopeCells.add((ConsCell) slopeEquation.cadr("dda").getElementA()); //This saves c's spot.
    slopeCells.add((ConsCell) slopeEquation.cadr("ddadd").getElementA()); //This saves d's spot.
    this.delta = delta;
    this.xMin = xMin;
    this.xMax = xMax;
    domainMax = xMin.add(xMax);
    if (vars != null) {
      this.equation = equation.clone();
      variableCells = new ArrayList<ConsCell>();
      initVariableCells(this.equation);
    }
  }
 
  private void initVariableCells(ConsCell equation) {
    ConsCell current = equation;
    do {
      if (current.getCarType() == ConsType.CONS_CELL)
        initVariableCells((ConsCell) current.getCar());
      if (current.getCarType() == ConsType.IDENTIFIER && ((String) current.getCar()).equals(vars.get(0)))
        variableCells.add(current);
    } while (!(current = current.getNextConsCell()).isNull());
  }
 
  private ArrayList<BigDec> stepSolveDomain() throws ParserException {
    ArrayList<BigDec> solutions = new ArrayList<BigDec>();
    BigDec accuracy = this.accuracy;
    if ((answer.abs().gteq(new BigDec(10000000)) || answer.abs().lteq(new BigDec(0.00000001))) && answer.neq(BigDec.ZERO))
      accuracy = answer.multiply(accuracy).abs();
    int i = 0;
    if (containsFactorial) {
      delta = BigDec.ONE;
      xMin = BigDec.ZERO;
      xMax = new BigDec(50);
      i = 6;
    }
    while ((solutions.size() == 0 && i < 4) || i == 6) {
      int maxIterations = (int) Math.round(xMax.subtract(xMin).divide(delta).doubleValue());
      for (int sign = 1; sign >= -1; sign -= 2) {
        int iterationsSinceDirectionChange = 0;
        delta = delta.multiply(new BigDec(sign));
        BigDec slope = BigDec.ZERO, prevSlope = BigDec.ZERO;
        BigDec point1[] = {xMin, getY(xMin)}, point2[] = {xMin.add(delta), getY(xMin.add(delta))};
        slope = getSlope(point1, point2);
        int prevDirection = getDirection(prevSlope, point1[1], sign), direction = getDirection(slope, point2[1], sign);
        //Check if either of the starting points is a solution
        if (point1[1].lteq(answer.add(accuracy)) && point1[1].gteq(answer.subtract(accuracy)))
          solutions.add(new BigDec(point1[0]));
        if (point2[1].lteq(answer.add(accuracy)) && point2[1].gteq(answer.subtract(accuracy)))
          solutions.add(new BigDec(point2[0]));
        if (solutions.size() > 0) //If the equation has solutions from the starting points, stop
          return solutions;
       
        while (iterationsSinceDirectionChange < maxIterations) {
          if (direction != prevDirection && (prevDirection != 0 || (point1[0].eq(xMin) && !solutions.contains(xMin))) && (direction != 0 || (point2[0].eq(xMax) && !solutions.contains(xMax)))) {
            BigDec possibleSolution = oscillator(point1, point2, slope, prevDirection, direction, sign, delta, accuracy);
            if (possibleSolution != null) {
              System.out.println("possibleSolution: (" + possibleSolution + ", " + getY(possibleSolution) + ")");
              if (isAnswer(possibleSolution, accuracy))
                solutions.add(possibleSolution);
            }
           
          }
          else if (direction == 2) //Only need to check direction because all prevDirections except for the first one (which is checked elsewhere) were first stored in direction
            solutions.add(point2[0]);
          //Iteration
          point1[0] = point2[0];
          point1[1] = point2[1];
          point2[0] = point2[0].add(delta);
          point2[1] = getY(point2[0]);
          prevSlope = new BigDec(slope);
          slope = getSlope(point1, point2);
          prevDirection = direction;
          direction = getDirection(slope, point2[1], sign);
          iterationsSinceDirectionChange++;
          if (direction != prevDirection)
            iterationsSinceDirectionChange = 0;
          if ((containsFactorial && point2[0].multiply(new BigDec(sign)).gteq(xMax.multiply(new BigDec(sign)).add(xMin))) || solutions.size() >= 10 || point2[0].gt(domainMax.abs().multiply(new BigDec(3))))
            break;
        }
      }
     
      i++;
      if (i < 4)
        delta = delta.divide(new BigDec(2.0));
    }
   
    return solutions;
  }
 
  /**
   * Finds a possible answer by reversing direction and shrinking the jump after each slope change
   *
   * @return a possible answer
   * @throws ParserException
   */
  private BigDec oscillator(BigDec point1[], BigDec point2[], BigDec slope, int prevDirection, int direction, int sign, BigDec delta, BigDec accuracy) throws ParserException {
    BigDec solution = null;
    int numSwitches = 0, maxSteps = 20, steps = 0;
    while (numSwitches <= 20 && steps < maxSteps) {
      if (direction == 0 && prevDirection == 0) //If it is a straight line
        break;
      if (direction != prevDirection) {
        delta = delta.multiply(new BigDec(-0.25));
        numSwitches++;
        steps = 0;
        if (isAnswer(point1[0], accuracy))
          solution = point1[0];
        if (isAnswer(point2[0], accuracy))
          solution = point2[0];
      }
      //Iteration
      point1[0] = point2[0];
      point1[1] = point2[1];
      point2[0] = point2[0].add(delta);
      point2[1] = getY(point2[0]);
      slope = getSlope(point1, point2);
      prevDirection = direction;
      direction = getDirection(slope, point2[1], sign);
      steps++;
    }
    if (isAnswer(point1[0], accuracy))
      solution = point1[0];
    if (isAnswer(point2[0], accuracy))
      solution = point2[0];
    return solution;
  }
 
  /**
   * -1 = away, 0 = neutral, 1 = towards, 2 = answer
   *
   * @param slope
   *            the slope at the given point
   * @param y
   *            the y-value at the given point
   * @param sign
   *            the direction that the algorithm is looking
   * @return -1 if it is sloping away from the answer, 0 if it is neutral but not on the answer (slope == 0), 1 if it is
   *         sloping towards the answer, 2 if it is at the answer
   * @throws UndefinedResultException
   */
  private int getDirection(BigDec slope, BigDec y, int sign) throws UndefinedResultException {
    slope = slope.multiply(new BigDec(sign));
    if (y.eq(answer))
      return 2;
    if (slope.eq(BigDec.ZERO))
      return 0;
    if (slope.lt(BigDec.ZERO))
      return y.gt(answer) ? 1 : -1;
    //If the slope is neither equal to or less than 0, it must be greater than 0, so no if statement is needed
    return y.lt(answer) ? 1 : -1;
  }
 
  private BigDec getY(BigDec x) throws ParserException {
    for (ConsCell c : variableCells)
      c.replaceCar(new ConsCell(x, ConsType.NUMBER));
    ConsCell result = parser.run(equation);
    if (result.getCarType() != ConsType.NUMBER || result.length() != 1)
      throw new UndefinedResultException("The NumericalSolver requires that the equation contain only one variable.", null);
    return (BigDec) result.getCar();
  }
 
  private BigDec getSlope(BigDec point1[], BigDec point2[]) throws ParserException {
    //First, replace the needed parts of the slope equation with the new numbers
    slopeCells.get(0).replaceCar(new ConsCell(point1[1], ConsType.NUMBER));
    slopeCells.get(1).replaceCar(new ConsCell(point2[1], ConsType.NUMBER));
    slopeCells.get(2).replaceCar(new ConsCell(point1[0], ConsType.NUMBER));
    slopeCells.get(3).replaceCar(new ConsCell(point2[0], ConsType.NUMBER));
    ConsCell result = parser.run(equation);
    if (result.getCarType() != ConsType.NUMBER || result.length() != 1)
      throw new UndefinedResultException("The NumericalSolver requires that the equation contain only one variable.", null);
    return (BigDec) result.getCar();
  }
 
  private boolean isAnswer(BigDec point, BigDec accuracy) throws ParserException {
    try { //Try-Catch here because an UndefinedResultException means false, not error.
      BigDec ans = getY(point);
      //The slightly awkward comparison set is used here because it is faster than creating a new object for the absolute value method
      return ans.lteq(answer.add(accuracy)) && ans.gteq(answer.subtract(accuracy));
    }
    catch (UndefinedResultException e) {
      return false;
    }
  }
 
  @Override
  public ArrayList<BigDec> compute() {
    ArrayList<BigDec> answers = new ArrayList<BigDec>();
    if (equation.length() == 1) {
      answers.add(new BigDec(answer));
      return answers;
    }
    try {
      if (xMax.subtract(xMin).lteq(range)) {
        try {
          answers.addAll(stepSolveDomain());
        }
        catch (ParserException pe) {
          error = pe;
        }
      }
      else {
        StepSolveThread left = new StepSolveThread(equation, delta, xMin, xMin.add(xMax.subtract(xMin).divide(new BigDec(2))));
        StepSolveThread right = new StepSolveThread(equation, delta, xMin.add(xMax.subtract(xMin).divide(new BigDec(2))), xMax);
        left.fork();
        answers.addAll(right.compute());
        if (right.error != null) {
          error = right.error;
          return answers;
        }
        answers.addAll(left.join());
        if (left.error != null) {
          error = left.error;
          return answers;
        }
      }
    }
    catch (ParserException e) {
      error = e;
    }
    return answers;
  }
 
}
TOP

Related Classes of lipstone.joshua.parser.backend.StepSolveThread

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.