Package calculus

Source Code of calculus.Calculus

package calculus;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lipstone.joshua.parser.Tokenizer;
import lipstone.joshua.parser.exceptions.InvalidOperationException;
import lipstone.joshua.parser.exceptions.ParserException;
import lipstone.joshua.parser.exceptions.PluginConflictException;
import lipstone.joshua.parser.exceptions.UndefinedResultException;
import lipstone.joshua.parser.plugin.ParserPlugin;
import lipstone.joshua.parser.plugin.helpdata.Operation;
import lipstone.joshua.parser.plugin.helpdata.Parameter;
import lipstone.joshua.parser.plugin.types.OperationPlugin;
import lipstone.joshua.parser.types.BigDec;
import lipstone.joshua.parser.util.ConsCell;
import lipstone.joshua.parser.util.ConsType;
import lipstone.joshua.parser.util.PairedList;

public class Calculus extends ParserPlugin implements OperationPlugin {
  private static double accuracy = 0.00000001;
  private int N = 10000, maxN = N * 16;
  private String var = "[xyz]";
  private static String[] functionDerivatives = {"sin", "cos(n)", "cos", "-sin(n)", "tan", "(sec(n))^2", "sec", "sec(n)*tan(n)", "csc", "-csc(n)*cot(n)", "cot", "-(csc(n))^2",
      "arcsin", "1/((1-(n)^2)^(1/2))", "arccos", "-1/((1-(n)^2)^(1/2))", "arctan", "1/(1+(n)^2)", "arcsec", "1/(abs(n)*(1+(n)^2)^(1/2))", "arccsc", "-1/(abs(n)*(1+(n)^2)^(1/2))", "arccot", "-1/(1+(n)^2)",
      "sinh", "cosh(n)", "cosh", "sinh(n)", "tanh", "(sech(n))^2", "sech", "-sech(n)*tanh(n)", "csch", "-csch(n)*coth(n)", "coth", "-(csch(n))^2",
      "arcsinh", "1/(((n)^2+1)^(1/2))", "arccosh", "1/(((n)^2-1)^(1/2))", "arctanh", "1/(1-(n)^2)", "arcsech", "-1/((n)*(1-(n)^2)^(1/2))", "arccsch", "-1/(abs(n)*(1+(n)^2)^(1/2))", "arccoth", "1/(1-(n)^2)",
      "ln", "1/(n)", "log", "ln(10)/(n)"};
 
  //private static String[] integralTable = {"sin({VAR})", "-cos({VAR})", "(sin({VAR}))^({CONST1})", "-(sin({VAR}))^({CONST1}-1)*cos({VAR})/{CONST1}+({CONST1}-1)/{CONST1}*{INTEGRATE:(sin({VAR}))^({CONST1}-2)}"};
 
  private BigDec integrate(ConsCell input) throws ParserException {
    return integrate(input, "trapezoidal");
  }
 
  private BigDec integrate(ConsCell input, String method) throws ParserException {
    ArrayList<String> vars = parser.getVariables(input);
    ArrayList<ConsCell> parts = input.splitOnSeparator();
    if (parts.size() < 3)
      return BigDec.ZERO;
    if (!parser.containsVariables(parts.get(0)))
      var = "[xyz]";
    ConsCell temp = parts.get(0);
    Pattern p = Pattern.compile("d[xyz" + vars.get(0) + "]");
    do {
      if (temp.getCarType() != ConsType.IDENTIFIER)
        continue;
      Matcher m = p.matcher((String) temp.getCar());
      if (m.find())
        temp = temp.remove();
      if (temp == null)
        break;
    } while (!(temp = temp.getNextConsCell()).isNull());
   
    BigDec limits[] = {BigDec.ZERO, BigDec.ZERO};
    temp = parser.run(parts.get(1));
    if (temp.getCarType() != ConsType.NUMBER)
      throw new InvalidOperationException("The second and third parameters for the integrate operation must both evaluate to a number", this, "integrate", parts);
    limits[0] = (BigDec) temp.getCar();
    temp = parser.run(parts.get(2));
    if (temp.getCarType() != ConsType.NUMBER)
      throw new InvalidOperationException("The second and third parameters for the integrate operation must both evaluate to a number", this, "integrate", parts);
    limits[1] = (BigDec) temp.getCar();
    if (limits[1] == limits[0])
      return BigDec.ZERO;
    if (limits[1].lt(limits[0])) {
      BigDec tempLim = limits[0];
      limits[0] = limits[1];
      limits[1] = tempLim;
    };
    BigDec answer = BigDec.ZERO;
    if (method.equalsIgnoreCase("trapezoidal"))
      answer = trapezoidal(parts.get(0), limits[0], limits[1], N);
    var = "[xyz]";
    return answer;
  }
 
  private BigDec trapezoidal(ConsCell equation, BigDec min, BigDec max, int N) throws ParserException {
    BigDec output = BigDec.ZERO;
    NIThread thread = new NIThread(equation, parser, N, maxN, accuracy, min, max, new BigDec(500), var, this);
    output = parser.getFjPool().invoke(thread);
    if (thread.error != null)
      throw thread.error;
    return output;
  }
 
  private ConsCell derivative(ConsCell input) throws ParserException {
    BigDec slope = BigDec.ZERO, slopePos = BigDec.ZERO, slopeNeg = BigDec.ZERO, x = BigDec.ZERO;
    ArrayList<ConsCell> parts = input.splitOnSeparator();
    ConsCell temp = parser.run(parts.get(1));
    if (temp.getCarType() != ConsType.NUMBER) {
      return symbolicDerivative(input);
    }
    x = (BigDec) temp.getCar();
    slopePos = pointDerivative(parts.get(0).clone(), x, 1.0);
    slopeNeg = pointDerivative(parts.get(0), x, -1.0);
    slope = slopePos.add(slopeNeg).divide(new BigDec(2));
    return new ConsCell(slope, ConsType.NUMBER);
  }
 
  private BigDec pointDerivative(ConsCell equation, BigDec x, double direction) throws ParserException {
    var = parser.getVariables(equation).get(0);
    ArrayList<ConsCell> varCells = equation.allInstancesOf(new ConsCell(var, ConsType.IDENTIFIER));
    for (ConsCell varCell : varCells)
      varCell.replaceCar(new ConsCell(x, ConsType.NUMBER));
    ConsCell temp = parser.run(equation);
    if (temp.getCarType() != ConsType.NUMBER)
      throw new UndefinedResultException("The second argument to the derivative operation must cause the first to evaluate to a number.", this);
    BigDec slope = BigDec.ZERO, prevSlope = BigDec.ONE, deltaX = new BigDec(0.0001), y1 = (BigDec) temp.getCar();
   
    BigDec x2 = x.add(deltaX.multiply(new BigDec(direction)));
    for (ConsCell varCell : varCells)
      varCell.replaceCar(new ConsCell(x2, ConsType.NUMBER));
    temp = parser.run(equation);
    if (temp.getCarType() != ConsType.NUMBER)
      throw new UndefinedResultException("The second argument to the derivative operation must cause the first to evaluate to a number.", this);
    //In basic types:  slope = (y1 - Double.parseDouble(parser.run(equation.replaceAll(var, new Double(x+deltaX*direction).toString())))/(x-(x+deltaX*direction)));
    slope = y1.subtract((BigDec) temp.getCar()).divide(deltaX.multiply(new BigDec(direction)));
   
    while (slope.subtract(prevSlope).abs().doubleValue() > accuracy) {
      prevSlope = slope;
      deltaX = deltaX.divide(new BigDec(10.0));
      x2 = x.add(deltaX.multiply(new BigDec(direction)));
      for (ConsCell varCell : varCells)
        varCell.replaceCar(new ConsCell(x2, ConsType.NUMBER));
      temp = parser.run(equation);
      if (temp.getCarType() != ConsType.NUMBER)
        throw new UndefinedResultException("The second argument to the derivative operation must cause the first to evaluate to a number.", this);
      //In basic types:  slope = (y1 - Double.parseDouble(parser.run(equation.replaceAll(var, new Double(x+deltaX*direction).toString())))/(x-(x+deltaX*direction)));
      slope = y1.subtract((BigDec) temp.getCar()).divide(deltaX.multiply(new BigDec(direction)));
    }
    if ((slope.abs().doubleValue() < accuracy) && equation.length() < 4)
      slope = BigDec.ZERO;
    var = "[xyz]";
    return slope.multiply(BigDec.MINUSONE);
  }
 
  /**
   * This is for the inner recursion DO NOT USE FOR INITIAL PASSTHROUGH
   *
   * @param section
   *            the section of the equation
   * @param vars
   *            the variables
   * @return the symbolic derivative of the part of the equation in section
   */
  private ConsCell symbolicDerivative(ConsCell section, ArrayList<String> vars) throws ParserException {
    parser.setVars(vars);
    ArrayList<ConsCell> equation = parser.getCAS().getTerms(section);
    equation = parser.getCAS().foldSigns(equation);
   
    for (int i = 0; i < equation.size(); i++) {
      ConsCell term = equation.get(i);
      term = stopCases(term, vars);
      boolean stopped = ((String) term.getLastConsCell().getCar()).equals("{true}"); //Guaranteed by stopCases that the last ConsCell in term will be an OBJECT cell,
      term.getLastConsCell().remove();
      if (parser.containsVariables(term, vars) && !stopped)
        term = rules(term, vars);
      equation.set(i, term);
    }
    section = new ConsCell();
    for (ConsCell term : equation)
      if (term.getCarType() != ConsType.OPERATOR)
        section = section.append(new ConsCell('+', ConsType.OPERATOR, term, ConsType.CONS_CELL));
      else
        section = section.append(term);
    section = parser.getCAS().simplifyTerms(parser.removeDoubles(section.getFirstConsCell()));
    if (section.getCarType() == ConsType.OPERATOR && ((Character) section.getCar()) == '+')
      section = section.remove();
    if (section == null)
      return new ConsCell();
    if (section.getLastConsCell().getCarType() == ConsType.OPERATOR && ((Character) section.getLastConsCell().getCar()) == '+')
      section.getLastConsCell().remove();
    return section;
  }
 
  private ConsCell stopCases(ConsCell term, ArrayList<String> vars) throws ParserException {
    BigDec coefficient = parser.getCAS().getCoefficient(term, vars);
    if (!parser.containsVariables(term, vars) || coefficient.eq(BigDec.ZERO))
      return new ConsCell(BigDec.ZERO, ConsType.NUMBER, new ConsCell("{true}", ConsType.OBJECT), ConsType.CONS_CELL);
    PairedList<ConsCell, ConsCell> orders = parser.getCAS().orderOfTerm(term, vars);
    int remaining = orders.size();
    for (String str : vars) { //Basically, if a variable's power is 1, taking its derivative gets rid of it.
      if (!orders.containsKey(new ConsCell(str, ConsType.IDENTIFIER)))
        continue;
      ConsCell result = parser.run(orders.get(new ConsCell(str, ConsType.IDENTIFIER)));
      if (orders.containsKey(new ConsCell(str, ConsType.IDENTIFIER)) && result.getCarType() == ConsType.NUMBER && result.length() == 1 && ((BigDec) result.getCar()).eq(BigDec.ONE))
        remaining--;
    }
    if (remaining == 0)
      return new ConsCell(coefficient, ConsType.NUMBER, new ConsCell("{true}", ConsType.OBJECT), ConsType.CONS_CELL);
    if (orders.size() == 1)
      return chainFunctions(parser.getCAS().makeTerm(coefficient, orders, false), vars);
    return term.getLastConsCell().append(new ConsCell("{false}", ConsType.OBJECT)).getFirstConsCell();
  }
 
  private ConsCell chainFunctions(ConsCell term, ArrayList<String> vars) throws ParserException {
    term = parser.getCAS().removeExcessParentheses(term);
    int type = -1, offset = 1;
    if (term.getCarType() == ConsType.OPERATOR && term.getCdrType() == ConsType.CONS_CELL)
      offset = 2;
    String check = term.getNextConsCell(offset - 1).singular().toString().trim();
    for (int i = 0; i < functionDerivatives.length; i += 2)
      if (check.equalsIgnoreCase(functionDerivatives[i])) {
        type = i;
        break;
      }
    BigDec coefficient = parser.getCAS().getCoefficient(term, vars);
    ConsCell result = coefficient.neq(BigDec.ONE) ? new ConsCell(coefficient, ConsType.NUMBER, new ConsCell('*', ConsType.OPERATOR), ConsType.CONS_CELL).getLastConsCell() : new ConsCell();
    if (type > -1 && term.getNextConsCell(offset).length() == 1) {
      ConsCell inner = term.getNextConsCell(offset).getCarType() == ConsType.CONS_CELL ? (ConsCell) term.getNextConsCell(offset).getCar() : term.getNextConsCell(offset);
      ConsCell functionOutput = Tokenizer.tokenizeString(functionDerivatives[type + 1]);
      ArrayList<ConsCell> cells = functionOutput.allInstancesOf(new ConsCell("n", ConsType.IDENTIFIER));
      for (ConsCell cell : cells)
        cell.replaceCar(inner.clone()); //Switch out any instances of n with the inner part of the chain function.
      result = result.append(functionOutput);
      ConsCell chain = symbolicDerivative(inner, vars);
      if (chain.length() == 1 && chain.getCarType() == ConsType.NUMBER && ((BigDec) chain.getCar()).eq(BigDec.ONE))
        return result.append(new ConsCell("{true}", ConsType.OBJECT)).getFirstConsCell();
      return result.append(new ConsCell('*', ConsType.OPERATOR, chain, ConsType.CONS_CELL)).append(new ConsCell("{true}", ConsType.OBJECT)).getFirstConsCell();
    }
    term.getLastConsCell().append(new ConsCell("{false}", ConsType.OBJECT));
    return term;
  }
 
  private ConsCell rules(ConsCell term, ArrayList<String> vars) throws ParserException {
    for (int priority = 0; priority < 2; priority++) {
      ConsCell current = term;
      while (!(current = current.getNextConsCell()).isNull() && !current.getNextConsCell().isNull()) {
        if (current.getCarType() != ConsType.OPERATOR || !parser.containsVariables(current.getPreviousConsCell(), vars) && !parser.containsVariables(current.getNextConsCell(), vars))
          continue;
        if ((Character) current.getCar() == '*' && priority == 0)
          return multiplicationRule(current, vars);
        else if ((Character) current.getCar() == '/' && priority == 0)
          return divisionRule(current, vars);
        else if ((Character) current.getCar() == '^' && priority == 1) {
          current.replaceCar(powerRule(current, vars));
          term = current.getFirstConsCell();
        }
      }
    }
    return term;
  }
 
  private ConsCell multiplicationRule(ConsCell current, ArrayList<String> vars) throws ParserException {
    ConsCell head = current.getPreviousConsCell(), left = head.singular(), right = current.getNextConsCell().clone();
    while (!(head = head.getPreviousConsCell()).isNull() && !(head.getCarType() == ConsType.OPERATOR && ((Character) head.getCar() == '*' || (Character) head.getCar() == '/')))
      left = head.singular().append(left.getFirstConsCell());
    left = left.getFirstConsCell();
    for (int i = 0; i < left.length(); i++)
      current.getPreviousConsCell().remove();
   
    ConsCell derivative = new ConsCell();
    if (parser.containsVariables(left, vars))
      derivative = new ConsCell(symbolicDerivative(left, vars), ConsType.CONS_CELL, new ConsCell('*', ConsType.OPERATOR, new ConsCell(right.clone(), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL).getLastConsCell();
    if (parser.containsVariables(right, vars))
      derivative = derivative.append(new ConsCell('+', ConsType.OPERATOR, new ConsCell(left.clone(), ConsType.CONS_CELL, new ConsCell('*', ConsType.OPERATOR, new ConsCell(symbolicDerivative(right, vars),
          ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL));
    current.getNextConsCell().removeAll();
    current = current.replace(derivative.getFirstConsCell()).getFirstConsCell();
    return current;
  }
 
  private ConsCell divisionRule(ConsCell current, ArrayList<String> vars) throws ParserException {
    ConsCell head = current.getPreviousConsCell(), left = head.singular(), right = current.getNextConsCell().clone();
    while (!(head = head.getPreviousConsCell()).isNull() && !(head.getCarType() == ConsType.OPERATOR && ((Character) head.getCar() == '*' || (Character) head.getCar() == '/')))
      left = head.singular().append(left.getFirstConsCell());
    left = left.getFirstConsCell();
    for (int i = 0; i < left.length(); i++)
      current.getPreviousConsCell().remove();
   
    ConsCell derivative = new ConsCell();
    if (parser.containsVariables(left, vars))
      derivative = new ConsCell(symbolicDerivative(left, vars), ConsType.CONS_CELL, new ConsCell('*', ConsType.CONS_CELL, new ConsCell(right.clone(), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL);
    if (parser.containsVariables(right, vars))
      derivative = derivative.append(new ConsCell('-', ConsType.OPERATOR, new ConsCell(left.clone(), ConsType.CONS_CELL, new ConsCell('*', ConsType.OPERATOR, new ConsCell(symbolicDerivative(right, vars),
          ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL));
    derivative = new ConsCell(derivative.getFirstConsCell(), ConsType.CONS_CELL);
    derivative.append(new ConsCell('/', ConsType.OPERATOR, new ConsCell(right.clone(), ConsType.CONS_CELL, new ConsCell('^', ConsType.OPERATOR, new ConsCell(new BigDec(2),
        ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL));
    current.getNextConsCell().removeAll();
    current.replace(derivative);
    return current.getFirstConsCell();
  }
 
  private ConsCell powerRule(ConsCell current, ArrayList<String> vars) throws ParserException {
    ConsCell derivative = new ConsCell();
    ConsCell left = current.getPreviousConsCell().singular(), right = current.getNextConsCell().singular();
    current.getPreviousConsCell().remove();
    current.getNextConsCell().remove();
    ConsCell temp = current;
    while (!(temp = temp.getPreviousConsCell()).isNull() && temp.getCarType() != ConsType.OPERATOR) {
      left = temp.singular().append(left).getFirstConsCell();
      temp = temp.remove();
    }
    temp = current;
    while (!(temp = temp.getNextConsCell()).isNull() && temp.getCarType() != ConsType.OPERATOR) {
      right = right.append(temp.singular());
      temp = temp.remove();
    }
    right = right.getFirstConsCell();
    if (parser.containsVariables(right, vars)) {
      derivative = derivative.append(new ConsCell(symbolicDerivative(right, vars), ConsType.CONS_CELL, new ConsCell('*', ConsType.OPERATOR), ConsType.CONS_CELL));
      derivative = derivative.append(new ConsCell(left.clone(), ConsType.CONS_CELL, new ConsCell('^', ConsType.OPERATOR, new ConsCell(right.clone(), ConsType.CONS_CELL), ConsType.CONS_CELL), ConsType.CONS_CELL));
      derivative = derivative.append(new ConsCell('*', ConsType.OPERATOR, new ConsCell("ln", ConsType.IDENTIFIER, new ConsCell(left.clone(), ConsType.CONS_CELL),
          ConsType.CONS_CELL), ConsType.CONS_CELL));
    }
    if (parser.containsVariables(left, vars)) {
      if (!derivative.isNull())
        derivative = derivative.append(new ConsCell('+', ConsType.OPERATOR));
      derivative = derivative.append(new ConsCell(right.clone(), ConsType.CONS_CELL, new ConsCell('*', ConsType.OPERATOR, left.clone(), ConsType.CONS_CELL), ConsType.CONS_CELL));
      derivative = derivative.append(new ConsCell('^', ConsType.OPERATOR, new ConsCell(parser.run(right.clone().append(
          new ConsCell('-', ConsType.OPERATOR, new ConsCell(BigDec.ONE, ConsType.NUMBER), ConsType.CONS_CELL)).getFirstConsCell()), ConsType.CONS_CELL), ConsType.CONS_CELL));
      derivative = derivative.append(new ConsCell('*', ConsType.OPERATOR, new ConsCell(symbolicDerivative(left, vars), ConsType.CONS_CELL), ConsType.CONS_CELL));
    }
    derivative = parser.getCAS().simplifyTerm(parser.getCAS().removeExcessParentheses(derivative.getFirstConsCell()));
    return derivative;
  }
 
  /**
   * Takes the symbolic derivative of a function
   *
   * @param input
   *            the function
   * @return the symbolic derivative of the function
   */
  private ConsCell symbolicDerivative(ConsCell input) throws ParserException {
    ArrayList<ConsCell> parts = input.splitOnSeparator();
    ArrayList<String> vars = new ArrayList<String>();
    if (parts.size() < 1)
      return input;
    if (parts.size() > 1) {
      vars = new ArrayList<String>(parts.size() - 1);
      for (int i = 1; i < parts.size(); i++)
        vars.set(i - 1, parts.get(i).toString());
    }
    else
      vars = parser.getVariables(input);
    parser.setVars(vars);
    parts.set(0, parser.preProcess(parts.get(0)));
    ConsCell output = symbolicDerivative(parts.get(0), vars);
    if (output.toString().equalsIgnoreCase(""))
      return new ConsCell(BigDec.ZERO, ConsType.NUMBER);
    return output;
  }
 
  public String symbolicIntegration(String input) throws UnableToIntegrateException {
    String output = new String(input);
   
    if (output.equals(input))
      throw new UnableToIntegrateException("Unable to integrate " + input, this);
    return output;
  }
 
  @Override
  public void loadOperations() throws PluginConflictException {
    ArrayList<Parameter> params = new ArrayList<Parameter>();
    params.add(new Parameter("equation", "the equation to be integrated", false));
    params.add(new Parameter("min", "the lower bound of the integration, any real number", false));
    params.add(new Parameter("max", "the upper bound of the integration, any real number greater than min", false));
    addOperation(new Operation("integrate", params, "The numeric value of the integral of the function between min and max",
        "Numerically integrates the function over [min, max] via the trapezoid rule.", new String[]{"integral"}, this));
   
    params.clear();
    params.add(new Parameter("equation", "the equation to be derived", false));
    params.add(new Parameter("x-coordinate", "the point at which to calculate the slope of the tangent line", true));
    addOperation(new Operation("derivative", params, "The symbolic derivative of the function, or, if the x-coordinate is provided, the slope of the tangent line at that point.",
        "Finds the symbolic derivative of the equation using the complete chain rule (x^n, n^x, x^x), product, quoteint, and logrithmic derivation formulae.", this, true));
  }
 
  @Override
  public ConsCell runOperation(String operation, ConsCell input) throws ParserException {
    if (operation.equals("integrate"))
      return new ConsCell(integrate(input), ConsType.NUMBER);
    ArrayList<ConsCell> parts = input.splitOnSeparator();
    if (operation.equals("derivative") && parts.size() == 2)
      return new ConsCell(derivative(input), ConsType.NUMBER);
    if (operation.equals("derivative") && parts.size() >= 1)
      return symbolicDerivative(input);
    return null;
  }
 
  @Override
  public void unloadOperations() {/* Don't need to do anything here */}
}
TOP

Related Classes of calculus.Calculus

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.