Package ket

Source Code of ket.Transform

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ket;

import java.util.*;

import ket.Factorize;
import ket.math.*;
import ket.math.convert.ArgumentParser;
import ket.math.convert.Like;
import ket.math.purpose.IntegerValue;
import ket.math.purpose.NumberValue;
import ket.math.purpose.Word;
import ketUI.Ket;

/**
* Handle abstract argument transformations such as a multinomial expansion.
* All operations are performed by static methods and are entirely independent
* of any notion of context.
*/
// TODO: Try to link this to tree regular expression transformations.
// TODO: How does this class relate to Calculate.java
public class Transform {

  private Transform() {
    // No instances.
  }

  public static Branch expandSeries(Argument current) { // sum(i=2, 3, i^2) -> 2^2 + 3^2 (and similar for product).
    Branch currentBranch = current.asBranch();
    if (currentBranch==null) return null;
    if (currentBranch.size()!=3) return null;
    boolean asSum = currentBranch.getFunction()==Function.SUM;
    boolean asProduct = currentBranch.getFunction()==Function.PRODUCT;
    if (!asSum && !asProduct) return null;
    Branch base = currentBranch.getChildBranch(0);
    if (Like.lacksForm(base, Function.EQUALS, 2)) return null;
    Argument variable = base.firstChild().asToken();
    Integer from = Like.getInteger(base.lastChild());
    Integer to = Like.getInteger(currentBranch.getChild(1));
    if (from==null || to==null || variable==null) return null;
    Argument subject = currentBranch.lastChild();
    Branch series = new Branch(asSum ? Function.ADD : Function.TIMES);
    Edit edit = new Edit(series);
    for (int i=from; i<=to; i++) {
      Argument term = subject.cloneArgument();
      series.append(term);
      edit.setCurrent(term);
      edit.substitute(variable, new Token(i));
    }
    return series;
  }

  public static Argument expandPower(Argument argument) { // a^c -> a*a*a...a [c times].
    Integer value = Like.powerOfIntegerLike(argument);
    if (value==null) {
      Ket.out.println(" !!! Power function is of the wrong shape !!! ");
      return null;
    } else if (value<1 || 9<value) {
      Ket.out.println(" !!! Can't expand powers outwith range [1,9] !!! ");
      return null;
    }
    Branch branch = (Branch) Argument.cloneArgument(argument);
    Argument mantissa = branch.firstChild();
    Argument exponent = branch.lastChild();
    exponent.remove();
    for (int i=1; i<value; i++) { // Clone the mantissa (value-1) times.
      Argument clone = Argument.cloneArgument(mantissa);
      branch.append(clone);
    }
    branch.setFunction(Function.TIMES);
    return branch;
  }

  public static Argument powerToInteger(Argument a) { // 10^3 -> 1000
    if (!Like.isPowerLike(a)) return null;
    int[] intArgs = a.asBranch().argsToIntArray();
    if (intArgs==null) return null;
    int sign = sign(intArgs[0] * intArgs[1]);
    int u = abs(intArgs[0]);
    int v = abs(intArgs[1]);
    int power = (int) Math.pow(u, v);
    if (power>Integer.MAX_VALUE) return null;
    boolean even = v%2==0;
    return (even||sign==1) ? new Token(power) : new Token(-power);
  }

  public static Argument toProperFraction(Argument a) { // 3/2 -> 1+1/2
    // TODO: Move to transform:
    if (a.isToken()) return null;
    int[] intArgs = a.asBranch().argsToIntArray();
    if (intArgs==null) return null;
    if (intArgs.length!=2) return null;
    int u = abs(intArgs[0]);
    int v = abs(intArgs[1]);
    if (u/v==0) return null;
    Token quot = new Token(u / v);
    Token mod  = new Token(u % v);
    Branch result = new Branch(Function.ADD);
    result.append(quot);
    result.append(new Branch(Function.FRACTION, mod, new Token(v)));
    return (intArgs[0]<0^intArgs[1]<0) ? new Branch(Function.MINUS, result) : result;
  }

  /**
   * Perform a multinomial expansion of the given argument.
   *    (a+b+...)^n
   */
  public static Argument multinomialExpand(Argument argument) {
    if ( ! Like.multinomialExpandable(argument) ) {
      return null;
    }
    Branch powerBranch = (Branch) Argument.cloneArgument(argument);
    Branch mantissa = (Branch) powerBranch.firstChild();
    Function mantissaFunction = mantissa.getFunction();
    boolean negativeSignature = Type.signatureType(mantissaFunction); // Can terms change sign?
    Ket.out.println("mantissaFunction = " + mantissaFunction + "?" + negativeSignature);
    // (a+b)^n -> sum(r=0, n, nCr(n, r)*a^r * b^(n-r) ).
    Token exponent = (Token) powerBranch.lastChild();
    int n = ((IntegerValue) exponent.getState()).getInt();
    if (n<0) {
      return null;
    } else if (n==0) {
      return new Token(1);
    } else if (n==1) {
      return mantissa;
    }
    Vector<Argument> children = mantissa.getChildren();
    int len = children.size();
    Branch expansion = new Branch(Function.ADD);
    Vector<Vector<Integer>> powers = new Vector<Vector<Integer>>();
    Vector<Integer> part = new Vector<Integer>();
    nMrRecur(powers, part, len, n);
    Collections.reverse(powers);
    Ket.out.println("powers:" + powers);
    for (int i=0; i<powers.size(); i++) { // for each term in the resulting sum...
      Vector<Integer> p = powers.get(i);
      int sign = 0;
      if (negativeSignature) {
        for (int j=1; j<p.size(); j++) { // Ignore the positive signature part a-B-C-D...
          sign += p.get(j);
        }
      }
      Branch term = new Branch(Function.TIMES);
      int weight = nMr(n, p);
      if (weight>1) {
        term.append(new Token(weight));
      }
      for (int j=0; j<p.size(); j++) { // for each power in each term of the resulting sum...
        Argument termPart = getTermPart(children.get(j), p.get(j));
        if (termPart!=null) {
          term.append(termPart);
        }
      }
      Argument result = getResult(term);
      if (sign%2!=0) { // odd
        result = new Branch(mantissaFunction, result);
      }
      expansion.append(result);
      Ket.out.println("expansion = " + expansion);
    }
    return expansion;
  }

  private static Argument getTermPart(Argument child, int power) {
    Argument m = Argument.cloneArgument(child);
    switch (power) {
      case 0:
        return null;

      case 1:
        return m;

      default:
        Token e = new Token(power);
        return new Branch(Function.POWER, m, e);
    }
  }

  private static Argument getResult(Branch term) {
    switch (term.size()) {
      case 0:
        return new Token(1);
      case 1:
        return term.firstChild(); // *a -> a
      default:
        return term;
    }
  }

  private static void nMrRecur(Vector<Vector<Integer>> powers, Vector<Integer> part, int len, int t) {
    if (t==0) {
      // Nothing else to add: fill the rest of the list with zeros and add once.
      int gap = len-part.size();
      for (int i=0; i<gap; i++) {
        part.add(0);
      }
      powers.add(part);
    } else if (part.size()==len-1) {
      // Including 't', the 'part' vector is of the correct length: add it.
      part.add(t);
      powers.add(part);
    } else {
      for (int i=0; i<=t; i++) {
        Vector<Integer> clone = new Vector<Integer>(part);
        clone.add(i);
        nMrRecur(powers, clone, len, t-i);
      }
    }
  }

  /**
   * Calculate the factorial of a number, n!, where 0&le;n.
   */
  private static long factorial(int value) {
    int fact = 1;
    for (int i=1; i<=value; i++) {
      fact *= i;
    }
    return fact;
  }

  /**
   * Determine the binomial coefficent for a given vector of coefficients. 
   * This is given by n! / product(i, factorial(weight_i)).
   *
   * e.g. nMr(5, [1, 3, 1]) = 5!/1!/3!/1!.
   *
   */
  public static int nMr(int n, Vector<Integer> power) {  // Note the dupliate in Calculate.java uses ints and not long internally.
    long num = factorial(n);
    long den = 1l;
    for (int p : power) {
      den *= factorial(p);
    }
    return (int) (num/den);
  }

  /**
   * Return the expansion of a power of products as a product of powers.
   *     (a/b)^3 -> a^3 / b^3
   */
  public static Argument powerOfFractionExpand(Branch branch) {
    if ( ! Like.intPowerOfFraction(branch) ) {
      return null;
    }
    Branch power = branch.cloneBranch(); // (a/b)^c
    Branch mantissa = power.getChildBranch(0); // a/b
    Argument exponent = power.lastChild();
    for (Argument child : mantissa.getChildren()) {
      Branch square = child.addIntermediaryParent(Function.POWER);
      square.append(Argument.cloneArgument(exponent));
    }
    return mantissa;
  }

  /**
   * Return the expansion of a power of products as a product of powers.
   *     (a*b*c...)^3 -> a^3 * b^3 * c^3...
   */
  public static Argument powerOfProductExpand(Argument argument) {
    if ( ! Like.intPowerOfProduct(argument) ) {
      return null;
    }
    Branch power = (Branch) Argument.cloneArgument(argument);
    Branch mantissa = power.firstChild().asBranch(); // a*b*...
    Argument exponent = power.lastChild();
    for (Argument child : mantissa.getChildren()) {
      Branch square = child.addIntermediaryParent(Function.POWER);
      square.append(Argument.cloneArgument(exponent));
    }
    return mantissa;
  }

  /**
   * Safe: No arguments are deleted during this method!
   *     a - b - c -> a + -b + -c
   */
  public static Branch expandSubtraction(Argument argument) {
    if (!Like.shortSubtractionLike(argument)) {
      return null;
    }
    Branch minus = (Branch) Argument.cloneArgument(argument);
    for (int i=1; i<minus.size(); i++) {
      Argument child = minus.getChild(i);
      Branch intermediaryBranch = child.addIntermediaryParent(Function.MINUS);
    }
    minus.setFunction(Function.ADD);
    return minus;
  }

  /**
   * Cuts off minus branches.
   *     a + -b + -c -> a - b - c
   */
  public static Argument contractSubtraction(Argument argument) {
    if (!Like.longSubtractionLike(argument)) {
      return null;
    }
    Branch addBranch = (Branch) Argument.cloneArgument(argument);
    for (int i=1; i<addBranch.size(); i++) {
      Argument child = addBranch.getChild(i);
      Branch childBranch = (Branch) child;
      childBranch.removeIntermediate();
    }
    addBranch.setFunction(Function.MINUS);
    return addBranch;
  }

  /**
   * Remove products of one argument a+*b+c -> a+b+c.
   */
  private static void eliminateOneArgumentProducts(Vector<Argument> sumTerms) {
    for (int i=0; i<sumTerms.size(); i++) {
      Argument next = sumTerms.get(i);
      if (Like.oneTermProductLike(next)) {
        Branch nextBranch = (Branch) next;
        sumTerms.setElementAt(nextBranch.firstChild(), i);
      }
    }
  }

  /**
   * Look through a list of arguments and of those that are times branches, gather all minus signs outside each product branch.
   */
  private static void collectMinusSigns(Vector<Argument> sumTerms) {
    for (int i=0; i<sumTerms.size(); i++) {
      Argument next = sumTerms.get(i);
      if (next.getFunction()!=Function.TIMES) {
        continue;
      }
      Branch nextBranch = (Branch) next;
      boolean negative = false;
      for (Argument a : nextBranch.getChildren()) {
        if (Like.negativeLike(a)) {
          Argument child = ((Branch) a).firstChild();
          child.remove();
          a.replace(child);
          negative = !negative;
        }
      }
      if (negative) {
        // Replace nextBranch with a negative branch that it is part of.
        Branch minusBranch = new Branch(Function.MINUS, nextBranch);
        sumTerms.setElementAt(minusBranch, i);
      }
    }
  }

  public static Argument multiplyOut(Argument argument) {
    if (argument.getFunction()!=Function.TIMES) {
      return null;
    }
    Branch timesBranch = (Branch) Argument.cloneArgument(argument);
    Vector<Branch> sumBranches = groupCoefficients(timesBranch);
    if (sumBranches.size()<2) {
      return null;
    }
    Ket.out.println(" --- multiply out --- ");
    Ket.out.println("times branch = " + timesBranch);
    Ket.out.println("sum branches = " + sumBranches);
    Vector<Argument> sumTerms = new Vector<Argument>(sumBranches);
    Ket.out.println("sum terms = " + sumTerms);
    eliminateOneArgumentProducts(sumTerms); // good
    Ket.out.println("sum terms' = " + sumTerms);
    collectMinusSigns(sumTerms); // good
    Ket.out.println("sum terms'' = " + sumTerms);
    return sumTerms.size()==1 ? sumTerms.firstElement() : new Branch(Function.ADD, sumTerms);
  }

  /**
   * Expand *(+(...), +(...)) in to [*(...), *(...), ...].
   */
  private static Vector<Branch> groupCoefficients(Branch originalTimesBranch) {
    Vector<Argument> productTerm = originalTimesBranch.getChildren();
    Argument first = productTerm.firstElement();
    Vector<Branch> sumTerms = padBranchesWithProducts(first);
    // For each arithmetic element in the next product term, multiply existing terms out.
    for (int i=1; i<productTerm.size(); i++) {
      // Multiply the sum terms by the elements of the next product term.
      Argument next = productTerm.get(i);
      if (Like.shortSubtractionLike(next)) {
        next = expandSubtraction(next);
      }
      if (next.getFunction()==Function.ADD) {
        Branch nextBranch = (Branch) next;
        Vector<Branch> expandedSumTerms = new Vector<Branch>();
        for (Branch product : sumTerms) {
          for (Argument term : nextBranch.getChildren()) {
            Branch b = product.cloneBranch();
            Argument a = Argument.cloneArgument(term);
            b.append(a);
            expandedSumTerms.add(b);
          }
        }
        sumTerms = expandedSumTerms;
      } else {
        // TODO: Multiply each term by a clone of the 'next' token.
        for (Branch product : sumTerms) {
          product.append(Argument.cloneArgument(next));
        }
      }
    }
    return sumTerms;
  }

  /**
   * Return a representation of one or more terms in a sum as a vector of products of those terms.
   */
  // First argument [a,b,...] -> [times(a), times(b), ...].
  private static Vector<Branch> padBranchesWithProducts(Argument first) {
    if (Like.shortSubtractionLike(first)) {
      first = expandSubtraction(first); //?
    }
    Vector<Branch> sumTerms = new Vector<Branch>();
    if (first.getFunction()==Function.ADD) {
      Branch firstBranch = (Branch) first;
      for (Argument next : firstBranch.getChildren()) {
        sumTerms.add(new Branch(Function.TIMES, next));
      }
    } else {
      sumTerms.add(new Branch(Function.TIMES, first));
    }
    return sumTerms;
  }

  /**
   * Clone the grandparent of argument and exchange the parent and
   * grandparent branches, returning the argument's clone or null if
   * invalid.
   */
  public static Argument swapAncestors(Argument argument) { // 'tt'; swapAncestors()
    Branch gpb = argument.getGrandparentBranch();
    if (gpb==null) {
      return null;
    }
    Branch grandparentBranch = gpb.cloneBranch();
    int parentIndex = argument.getParentBranch().getIndex();
    Branch parentBranch = grandparentBranch.getChildBranch(parentIndex);
    int cloneIndex = argument.getIndex();
    Argument clone = parentBranch.getChild(cloneIndex);
    //- Parent greatGrandParent = grandparentBranch.getParent();
    parentBranch.replaceArgument(clone, grandparentBranch);
    grandparentBranch.replaceArgument(parentBranch, clone);
    grandparentBranch.setParent(parentBranch);
    return clone;
  }

  public static Argument applyProductRule(Branch derivativeOfProduct) {
    Branch product = (Branch) derivativeOfProduct.getChildBranch(0);
    Function dash = derivativeOfProduct.getFunction();
    Branch sum = new Branch(Function.ADD);
    for (int i=0; i<product.size(); i++) {
      Branch productClone = product.cloneBranch();
      //?- Branch diffOpClone = diffOp.cloneBranch();
      sum.append(productClone);
      Argument subject = productClone.getChild(i);
      Branch derivative = subject.addIntermediaryParent(dash);
      for (int j=1; j<derivativeOfProduct.size(); j++) {
        Argument clone = Argument.cloneArgument(derivativeOfProduct.getChild(j));
        derivative.append(clone);
      }
    }
    return sum;
  }


  /**
   * Multiply a given argument by 'scale'.
   */
  public static Argument multiplyTermBy(Argument term, Argument scale) {
    Argument termClone = Argument.cloneArgument(term);
    if ( Like.hasForm(termClone, Function.FRACTION, 2) ) { // (a/b)*scale
      Branch termBranch = (Branch) termClone;
      Argument divisor = termBranch.lastChild();
      if (divisor.subBranchEquals(scale)) { // (a/s)*s -> a
        return termBranch.firstChild();
      } else if (divisor.getFunction()==Function.TIMES && divisor.size()>1) { // (a/u*v*...*s)*s
        Branch divisorBranch = (Branch) divisor;
        for (Argument c : divisorBranch.getChildren()) {
          if (c.subBranchEquals(scale)) { // (a/u*v*s)*s -> a/u*v
            // Return the 'rest' of termClone.
            if (divisorBranch.size()==2) { // return the only sibling
              Argument sibling = c.getIndex()==0 ? divisorBranch.lastChild() : divisorBranch.firstChild();
              divisorBranch.replace(sibling);
              return termClone;
            } else { // return everything without 'c'.
              c.remove();
              return termClone;
            }
          }
        }
      }
    }
    Argument scaleClone = Argument.cloneArgument(scale);
    return new Branch(Function.TIMES, termClone, scaleClone);
  }

  /**
   * Divide 'term' by 'divisor' or null if inappropriate.
   */
  public static Argument divideTermBy(Argument term, Argument divisor) { // TODO: Move to Transform.java and generalize.
    Argument termClone = Argument.cloneArgument(term);
    if (term.subBranchEquals(divisor)) { // q / q
      return new Token(1);
    }
    if (termClone.getFunction()==Function.TIMES) { // a*b*c / q
      Argument result = divideProductBy((Branch) termClone, divisor);
      if (result!=null) {
        return result;
      }
    } // otherwise:
    Argument divisorClone = Argument.cloneArgument(divisor);
    return new Branch(Function.FRACTION, termClone, divisorClone);
  }

  private static Argument divideProductBy(Branch times, Argument divisor) {
    if (times.size()==2) { // a*b -> a, b or a*b/c
      Argument first = times.firstChild();
      Argument last = times.lastChild();
      if (first.subBranchEquals(divisor)) {  // a*b/a -> a
        return last;
      } else if (last.subBranchEquals(divisor)) { // a*b/b -> a
        return first;
      }
    }
    for (Argument child : times.getChildren()) {
      Integer exponent = Like.powerOfIntegerLike(child);
      if (child.subBranchEquals(divisor)) {
        // a*...*q/q -> a*...
        child.remove();
        return times;
      } else if (exponent!=null && exponent>0) {
        switch (exponent) {
          case 1: // a*...*x^1       <--- Also counts in the size()==2 case above.
            child.remove();
            return times;

          case 2:
            Argument mantissa = ((Branch) child).firstChild();
            child.replace(mantissa);
            return times;

          default:
            Argument exp = ((Branch) child).lastChild();
            exp.replace(new Token(exponent-1));
            return times;
        }
      }
    }
    return null;
  }

  /**
   * Substitute the 'replacement' branch into a target branch, were replacement is of the form
   *  branch'='[variable, substitution]
   * or
   *  branch','[branch'='[variable, substitution], ...].
   */
  /*+ After adding cleanlyRemove().
  public void substitute(Branch source, Argument replacement) {
    Branch clone = source.cloneBranch();
    if (replacement.getFunction()==Function.EQUALS || replacement.getFunction()==Function.TO) {
      Argument target = null;
      Argument substitution = null;
      switch (replacement.size()) {
        case 1:
          target = replacement.getChild(0);
          break;

        case 2:
          target = replacement.getChild(0);
          substitution = replacement.getChild(1);
          break;

        default: // What do 0, 3, 4 etc mean?
          Ket.out.println(" !!! Format <variableState>=<Argument> (or a set or vector thereof) !!! ");
          return null;
      }

      for (Argument next : new ArgumentVector(clone, 0)) {
        boolean match = false;
        match = target.subBranchEquals(next);
        if (match) {
          if (substitution!=null) {
            next.replace(Argument.cloneArgument(substitution));
          } else {
            setCurrent(next);
            cleanlyRemove();
          }
        }
      }
    } else if (Function.sequenceType(replacement.getFunction())) {
      for (Argument child : replacement.getChildren()) {
        Ket.out.println("substitute: " + child);
        substitute(child);
      }
    } else {
      Ket.out.println("!!! Clipboard had the wrong form for a substitution !!!");
      Ket.out.println("actual format:");
      Ket.out.println("\t" + replacement);
      Ket.out.println("expected format:");
      Ket.out.println("\t<word>=<argument>");
      Ket.out.println("or:");
      Ket.out.println("\t<word>=<arg>, <word>=<arg>, ..., <word>=<arg>");
      return null;
    }
    return clone;
  }
  */

  public static boolean multiplyThrough(Selection s, Argument end, Branch common) { // Mutable.
    Function op = common.getFunction();
    Branch context = end.getParentBranch();
    boolean currentPlusType = Type.timesType(op);
    boolean timesContext = context!=null?Type.addType(context):null;
    if (!currentPlusType || !timesContext) {
      return false;
    }
    Argument source = s.getCurrent();
    boolean prepend = source.getIndex()<context.getIndex();
    s.cleanlyRemove(false);
    for (Argument child : context.getChildren()) {
      s.setCurrent(child);
      Argument clone = Argument.cloneArgument(source);
      s.setCurrent(child);
      s.cleanlyInsert(clone, op);
    }
    return true;
  }

  public static void applyIntegrationByParts(Selection s, Branch common) {
    System.out.println(" --- integration by parts --- ");
    System.out.println("common = " + common);
    Branch times = common.firstChild().asBranch();
    System.out.println("times = " + times);
    Argument end = times.firstChild();
    System.out.println(" end = " + end);
    if (end.getFunction()!=Function.DERIVATIVE || end.size()!=2) {
      end = times.lastChild();
    }
    System.out.println(" end' = " + end);
    Argument u1 = end.asBranch().firstChild(); // end = u'x
    Argument u2 = Argument.cloneArgument(u1);
    Argument v1 = end.getOnlySibling();
    Argument v2 = Argument.cloneArgument(v1);
    //- Argument x1 = end.asBranch().lastChild();
    Argument x2 = common.asBranch().lastChild();
    Argument x3 = Argument.cloneArgument(x2);
    Branch a = new Branch(Function.TIMES, u1, v1);
    Branch vDash = new Branch(Function.DERIVATIVE, v2, x2);
    Branch b = new Branch(Function.INTEGRAL, new Branch(Function.TIMES, u2, vDash), x3);
    s.replace(common, new Branch(Function.MINUS, a, b));
  }

  public static void applyChainRule(Selection s, Branch common, Branch end, KnownArguments knownArguments) {
    // (derivative (f g) x) -> (times (derivative f g) (derivative g x))
    Token q = new Token(new Word("q")); // knownArguments.getUnusedVariable()
    Branch f = end.getParentBranch().cloneBranch();
    Argument g = Argument.cloneArgument(end);
    for (Argument a : new ArgumentVector(f, ArgumentVector.REVERSE_ITERATOR_ORDER)) { // exp(1+cos(x)) -> exp(q) @ q=1+cos(x)
      if (a.equals(g)) {
        a.replace(Argument.cloneArgument(q));
      }
    }
    Vector<Argument> x = common.cloneBranch().getChildren();
    x.remove(0); // f'x'y -> x,y
    Branch a = new Branch(Function.RANGE, new Branch(Function.DERIVATIVE, f, q), g);
    Branch to = g.addIntermediaryParent(Function.TO);
    to.prepend(Argument.cloneArgument(q));
    Branch b = new Branch(Function.DERIVATIVE, end);
    b.appendAll(x);
    s.replace(common, new Branch(Function.TIMES, a, b));
  }

  public static void applyQuotientRule(Selection s, Branch common, Argument end) {
    s.setCurrent(common);
    Argument a = end.getParentBranch().firstChild();
    Argument b = end.getParentBranch().lastChild();
    Branch aDash = common.cloneBranch();
    aDash.removeChild(0); // i.e. 'x or ;x;y... etc.
    Branch bDash = aDash.cloneBranch();
    aDash.prepend(Argument.cloneArgument(a)); // i.e. a'x
    bDash.prepend(Argument.cloneArgument(b)); // i.e. b'x
    Branch aDashTimesB = new Branch(Function.TIMES, aDash, Argument.cloneArgument(b));
    Branch aTimesBDash = new Branch(Function.TIMES, Argument.cloneArgument(a), bDash);
    Branch diff = new Branch(Function.MINUS, aDashTimesB, aTimesBDash);
    Branch sqr = new Branch(Function.POWER, Argument.cloneArgument(b), new Token(2));
    Branch frac = new Branch(Function.FRACTION, diff, sqr);
    s.replace(frac);
  }

  public static void expandRange(Selection s, Branch common) {
    Ket.out.println(" --- expand range --- ");
    Ket.out.println("\t" + common);
    switch (common.size()) {
      case 2: // cos(t) @ (t=2) -> cos(2)
        Ket.out.println(" --- 2 --- ");
        s.replace(common, common.firstChild());
        Branch child = common.lastChild().asBranch();
        if (child!=null) {
          s.substitute(child.asBranch());
        }
        break;
      case 3: // e.g. sin(a) @ (a=1) @ (a=3) -> sin(1) - sin(3)
        Ket.out.println(" --- 3 --- ");
        Argument subject = common.getChild(0);
        Branch from = common.getChildBranch(1);
        Branch to = common.getChildBranch(2);
        if (to!=null && from!=null) {
          Ket.out.println("[] - []");
          Argument toSubject = Argument.cloneArgument(subject);
          Argument fromSubject = Argument.cloneArgument(subject);
          Branch minus = new Branch(Function.MINUS, toSubject, fromSubject);
          s.replace(common, minus);
          s.setCurrent(toSubject);
          s.substitute(to);
          s.setCurrent(fromSubject);
          s.substitute(from);
          s.setCurrent(minus);
        }
    }
  }


  public static Argument organizeAddType(Argument current) {
    if (!Type.addType(current)) {
      return null;
    }
    IdentityHashMap<Argument, Function> map = new IdentityHashMap<Argument, Function>();
    Argument clone = Argument.cloneArgument(current);
    organizeAddTypeRecur(map, clone, null);
    if (map.size()<2) { // Can't be further simplified.
      return null;
    }
    Vector<Argument> positive = new Vector<Argument>();
    Vector<Argument> negative = new Vector<Argument>();
    boolean onlyPlusAndMinus = frequencies(map, positive, negative);
    if (!onlyPlusAndMinus) {
      Branch add = new Branch(Function.ADD);
      for (Map.Entry<Argument, Function> es : map.entrySet()) {
        add.append(es.getValue()!=Function.ADD ? new Branch(es.getValue(), es.getKey()) : es.getKey());
      }
      return add;
    } // otherwise:
    int ps = positive.size();
    int ns = negative.size();
    Argument pFirst = ps>0 ? positive.firstElement() : null;
    Argument nFirst = ns>0 ? negative.firstElement() : null;
    if (ps>1 && ns==0) {
      return new Branch(Function.ADD, positive);
    } else if (ns==1) {
      if (ps==1) {
        return new Branch(Function.MINUS, pFirst, nFirst);
      } else if (ps>1) {
        positive.add(new Branch(Function.MINUS, nFirst));
        return new Branch(Function.ADD, positive);
      }
    } else if (ns>1) {
      if (ps==0) {
        return new Branch(Function.MINUS, new Branch(Function.ADD, negative));
      } else if (ps==1) {
        Branch r = new Branch(Function.MINUS, pFirst);
        r.appendAll(negative);
        return r;
      } else if (ps>1) {
        Branch p = new Branch(Function.ADD, positive);
        Branch m = new Branch(Function.ADD, negative);
        return new Branch(Function.MINUS, p, m);
      }
    }
    return null; // TODO: When does this occur?
  }


  public static void simplifyLnWithinProduct(CursorSelection s, Branch common, Argument end) {
    Argument current = s.getCurrent();
    Branch ln = end.getParentBranch();
    s.replace(common, ln); // x*ln(y) -> ln(y)
    Branch power = end.addIntermediaryParent(Function.POWER); // ln:y -> ln:^:y
    power.append(current); // ln(^y) -> ln(y^x)
  }


  /**
   * Count the number of ADD and MINUS functions are in the map's values
   * and return true, unless a PM or MP is found and then return false.
   */
  private static boolean frequencies(IdentityHashMap<Argument, Function> map, Vector<Argument> positive, Vector<Argument> negative) {
    for (Argument term : map.keySet()) {
      Function op = map.get(term);
      if (op==Function.ADD) {
        positive.add(term);
      } else if (op==Function.MINUS) {
        negative.add(term);
      } else if (op==Function.PM) {
        return false;
      } else if (op==Function.MP) {
        return false;
      }
    }
    return true;
  }

  /**
   * Drill down to the first non-positive-type argument within current
   * and add them to the map along with their unitary function.
   */
  private static void organizeAddTypeRecur(IdentityHashMap<Argument, Function> map, Argument a, Function path) {
    Ket.out.println("recur: " + a + ", " + path);
    Ket.out.println("map=" + map);
    Function f = a.getFunction();
    Function op = Type.getUnitaryFunction(a);
    if (Type.addType(f)) {
      Ket.out.println("op=" + op);
      // Drill down further.
      for (Argument child : a.asBranch().getChildren()) {
        Ket.out.println("child: " + child);
        Ket.out.println("op=" + op);
        Function comp = path!=null?Type.getComposition(path, op):op;
        Ket.out.println("comp=" + comp);
        organizeAddTypeRecur(map, child, comp);
      }
    } else {
      Function comp = path!=null?Type.getComposition(path, op):op;
      Ket.out.println("put: " + a + ", op=" + op);
      // Add this along with its unitary function.
      map.put(a, comp);
   
  }

  // ^String like -> result
  static final String[][] DASHES = new String[][]{
    new String[]{"sin(\\1)'\\1", "cos(x)"},
    new String[]{"cos(\\1)'\\1", "-sin(x)"},
    new String[]{"tan(\\1)'\\1", "sec(x)^2"},
    new String[]{"sec(\\1)'\\1", "sec(x)*tan(x)"},
    new String[]{"csc(\\1)'\\1", "-csc(x)*cot(x)"},
    new String[]{"cot(\\1)'\\1", "-csc(x)^2"},
    new String[]{"sqrt(\\1)'\\1", "1/2*sqrt(x)"},
    new String[]{"exp(\\1)'\\1", "exp(x)"},
    new String[]{"ln(\\1)'\\1", "1/x"},
    new String[]{"log(\\1)'\\1", "1/x*ln:10"},
    new String[]{"asin(\\1)'\\1", "1/sqrt(1-x^2)"},
    new String[]{"acos(\\1)'\\1", "-1/sqrt(1-x^2)"},
    new String[]{"atan(\\1)'\\1", "1/(1+x^2)"},
    new String[]{"asec(\\1)'\\1", "1/abs(x)*sqrt(x^2-1)"},
    new String[]{"acsc(\\1)'\\1", "(-1)/abs(x)*sqrt(x^2-1)"},
    new String[]{"acot(\\1)'\\1", "-1/(1+x^2)"},
    new String[]{"sinh(\\1)'\\1", "cosh(x)"},
    new String[]{"cosh(\\1)'\\1", "sinh(x)"},
    new String[]{"tanh(\\1)'\\1", "sech(x)^2"},
    new String[]{"sech(\\1)'\\1", "-tanh(x)*sech(x)"},
    new String[]{"csch(\\1)'\\1", "-coth(x)*csch(x)"},
    new String[]{"coth(\\1)'\\1", "-csch(x)^2"},
    new String[]{"asinh(\\1)'\\1", "1/sqrt(x^2+1)"},
    new String[]{"acosh(\\1)'\\1", "1/sqrt(x^2-1)"},
    new String[]{"atanh(\\1)'\\1", "1/(1-x^2)"},
    new String[]{"asech(\\1)'\\1", "-1/x*sqrt(1-x^2)"},
    new String[]{"acsch(\\1)'\\1", "(-1)/abs(x)*sqrt(1+x^2)"},
    new String[]{"acoth(\\1)'\\1", "1/(x^2-1)"}};

  public static Argument differentiate(Argument a) { //? Also apply to partial deriviative.
    Ket.out.println(" ~~~ DASH ~~~ ");
    Ket.out.println(" a=" + a);
    Argument[] tokens = Like.getRelatives(a, "\\1'\\2", 1, 2);
    if (tokens!=null && tokens.length==2 && tokens[0].isToken() && tokens[1].isToken()) {
      if (tokens[0].equals(tokens[1])) {
        return new Token(1);
      } else {
        return new Token(0);
      }
    }
    Argument[] power = Like.getRelatives(a, "(\\1^\\2)'\\3", 11, 12, 2);
    if (power!=null && power.length==3 && power[0].equals(power[2])) {
      Ket.out.println("[power']" + power[2]);
      Integer z = Like.getInteger(power[1]);
      Ket.out.println("z = " + z);
      if (z!=null) { // TODO: Only integers?
        Argument clone = Argument.cloneArgument(power[0]);
        if (z==-1) {
          return new Branch(Function.MINUS, new Branch(Function.POWER, clone, new Token(z-1)));
        } else if (z==0) {
          return new Token(0);
        } else if (z-1==0) { // n*x^0 -> n
          return new Token(z);
        } else if (z-1==1) { // n*x^1 -> n*x
          return new Branch(Function.TIMES, new Token(z), clone);
        } else {
          return new Branch(Function.TIMES, new Token(z), new Branch(Function.POWER, clone, new Token(z-1)));
        }
      }
    }
    for (String[] pair : DASHES) {
      Argument x2 = Like.getRelative(a, pair[0], 12);
      if (x2!=null) {
        String p = String.format("[good %s -> %s]\n", pair[0], pair[1]);
        Ket.out.printf("%72s\n", p);
        return ArgumentParser.parseAs(pair[1], x2);
      }
    }
    Argument[] logB = Like.getRelatives(a, "log(\\1, \\2)'\\3", 11, 12, 2);
    if (logB!=null && logB.length==3 && logB[1].equals(logB[2])) { // log(b, x)'x -> 1/(x*ln:b)
      Argument lnB = new Branch(Function.LN, Argument.cloneArgument(logB[0]));
      Argument x = Argument.cloneArgument(logB[1]);
      Branch tms = new Branch(Function.TIMES, lnB, x);
      return new Branch(Function.FRACTION, new Token(1), tms);
    }
    return null;
  }

  public static Argument integrate(Argument a) {
    Ket.out.println("integrate::a = " + a);
    Argument[] tokens = Like.getRelatives(a, "integral(\\1, \\2)", 1, 2);
    if (tokens!=null && tokens.length==2 && tokens[0].isToken() && tokens[1].isToken()) {
      // integral: a->a*x    a*x -> a*x^2    a*x^n   ->   ln:x or n*a*x^(n-1).
      if (tokens[0].equals(new Token(1))) { // 1 -> x
        return Argument.cloneArgument(tokens[1]);
      } else if (tokens[0].equals(tokens[1])) { // x -> x^2/2
        Argument clone = Argument.cloneArgument(tokens[0]);
        return new Branch(Function.FRACTION, new Branch(Function.POWER, clone, new Token(2)), new Token(2));
      } else { // a->a*x
        Argument w = Argument.cloneArgument(tokens[0]);
        Argument x = Argument.cloneArgument(tokens[1]);
        return new Branch(Function.TIMES, x, w);
      }
    }

    Argument[] reciprocal = Like.getRelatives(a, "integral(\\1/\\2, \\3)", 11, 12, 2);
    if (reciprocal!=null && reciprocal.length==3) { // integral(a/x, x) -> ln:x or a*ln:x
             Argument u = reciprocal[0];
             Argument v = reciprocal[1];
             Argument w = reciprocal[2];
      if (u.isToken() && v.isToken() && w.isToken() && v.equals(w)) {
        Argument ln = new Branch(Function.LN, Argument.cloneArgument(w));
        if (reciprocal[0].equals(new Token(1))) { // 1/x
          return ln;
        } else {
          return new Branch(Function.TIMES, Argument.cloneArgument(u), ln);
        }
      }
    }

    Argument[] power = Like.getRelatives(a, "integral(\\1^\\2,\\3)", 11, 12, 2);
    if (power!=null && power.length==3 && power[0].equals(power[2])) {
      Ket.out.println("[power']" + power[2]);
      Integer z = Like.getInteger(power[1]);
      Ket.out.println("z = " + z);
      if (z!=null && z==0) { // x^(-1) -> ln:x
        return Argument.cloneArgument(power[0]);
      } else if (z!=null && z==-1) { // x^(-1) -> ln:x
        Argument clone = Argument.cloneArgument(power[0]);
        return new Branch(Function.LN, clone);
      } else if (z!=null) { // x^n -> (n+1)*x^(n+1)
        Argument clone = Argument.cloneArgument(power[0]);
        Argument exponent = new Token(z+1);
        Argument post = new Token(z+1);
        return new Branch(Function.FRACTION, new Branch(Function.POWER, clone, exponent), post);
      }
    }
    Function simple = Like.simple(a); // f(g(x), x) -> f
    if (simple!=null) {
      Ket.out.println("simple = " + simple);
      Argument x = Argument.cloneArgument(a.asBranch().lastChild());
      if (simple==Function.SIN) { // sin:x -> -cos:x
        return new Branch(Function.MINUS, new Branch(Function.COS, x));
      } else if (simple==Function.COS) { // cos:x -> sin:x
        return new Branch(Function.SIN, x);
      } else if (simple==Function.EXP) { // exp(x) -> exp(x)
        return new Branch(Function.EXP, x);
      } else if (simple==Function.LN) { // ln:x -> x*ln:x-x
        Argument x2 = Argument.cloneArgument(x);
        Argument x3 = Argument.cloneArgument(x);
        return new Branch(Function.MINUS, new Branch(Function.TIMES, x, new Branch(Function.LN, x2)), x3);
      }
    }
    return null;
  }

  public static Argument integerDivideFraction(Argument a) { // TODO: Handle DIVIDE too?
    if (Like.lacksForm(a, Function.FRACTION, 2)) return null;
    int[] args = a.asBranch().argsToIntArray();
    if (args==null) return null;
    int u = abs(args[0]);
    int v = abs(args[1]);
    if (v==0) return null;
    if (u%v!=0) return null;
    return new Token(sign(args[0]*args[1])*u/v);
  }

  public static Argument divideFraction(Argument a) {
    if (Like.lacksForm(a, Function.FRACTION, 2)) return null;
    double[] args = a.asBranch().argsToDoubleArray();
    if (args==null) return null;
    return new Token(args[0] / args[1]);
  }

  public static Branch scientificNotation(double number) { // Duplicated code in Calculate?
    System.out.println(" --- scientific notation --- ");
    double log10 = Math.log10(number);
    System.out.println("log10 = "+log10);
    int exponent = log10<0 ? (int) log10 - 1 : (int) log10;
    System.out.println("exponent = " +exponent);
    double mantissa = Math.pow(10.0, log10-exponent);
    System.out.println("mantissa = " + mantissa);
    return new Branch(Function.TIMES, new Token(mantissa), new Branch(Function.POWER, new Token(10), new Token(exponent)));
  }

  public static Argument fractionToScientificNotation(Argument a) {
    if (Like.lacksForm(a, Function.FRACTION, 2)) return null;
    double[] args = a.asBranch().argsToDoubleArray();
    if (args==null) return null;
    if (args.length!=2) return null; // non-number args
    if (args[1]==0) return null;
    return scientificNotation(args[0] / args[1]);
  }

  public static Argument sumFractions(Argument a) { // Apply to all + and - but not \pm or \mp.
    int[] q = Like.sumOfFractions(a);
    if (q==null) return null;
    //- Ket.out.println(" --- sum fractions --- ");
    int n = q.length/2;
    for (int i=0; i<n; i++) {
      //- Ket.out.println("\t" + q[2*i] + " / " + q[2*i+1]);
    }
    // a/b+c/d -> (a*d+b*c)/b*d
    int sum = 0;
    for (int i=0; i<n; i++) {
      int term = q[2*i];
      for (int j=0; j<n; j++) {
        if (i!=j) {
          //- Ket.out.println("\t\t" + q[2*j+1]);
          term *= q[2*j+1];
        }
      }
      sum += term;
      //- Ket.out.println("\t" + term);
    }
    Token numerator = new Token(sum); // Sum of: i'th numerator multiplied by all other denomonators.
    int den = 1;
    for (int i=0; i<n; i++) {
      den *= q[2*i+1];
    }

    Argument denominator = new Token(den); // product of all denomonators
    /*-
    return new Branch(Function.FRACTION, numerator, denominator);
    */

    Argument fraction = new Branch(Function.FRACTION, numerator, denominator);
    Argument simple = cancelCommonDivisor(fraction);
    return simple!=null ? simple : fraction;
  }

  public static Argument cancelCommonDivisor(Argument a) {
    if (Like.lacksForm(a, Function.FRACTION, 2)) return null;
    int[] args = a.asBranch().argsToIntArray();
    if (args==null) return null;
    return cancelCommonDivisor(args[0], args[1]);
  }

  public static Argument cancelCommonDivisor(int numerator, int denominator) {
    int commonDivisor = Factorize.findCommonDivisor(numerator, denominator);
    if (commonDivisor<=1) {
      return null;
    }
    Token n = new Token(numerator/commonDivisor);
    int d = denominator/commonDivisor;
    if (d==1) {
      return n;
    }
    return new Branch(Function.FRACTION, n, new Token(d));
  }

  public static Argument expandChildArith(Argument current) {
    // Expand out all minus and plus children.
    //     (a-c) + (b+d) -> a + -c + b + d
    Branch currentBranch = current.asBranch();
    if (currentBranch==null) return null;
    if (currentBranch.getFunction()!=Function.ADD && currentBranch.getFunction()!=Function.MINUS) return null;
    boolean invalid = true;
    for (Argument child : currentBranch.getChildren()) { // Is there something to change?
      Function childFunction = child.getFunction();
      if (childFunction==Function.ADD || (childFunction==Function.MINUS && child.size()>1)) {
        invalid = false;
      }
    }
    if (invalid) return null;
    Ket.out.println(" --- expand child arith --- ");
    Branch clone = currentBranch.cloneBranch();
    Ket.out.println("\t" + clone);
    if (currentBranch.getFunction()==Function.MINUS) {
      Branch ex = expandSubtraction(clone);
      if (ex!=null) {
        clone = ex;
      }
    }
    Ket.out.println("\t" + clone);
    for (Argument child : clone.getChildren()) { // a-b -> a+(-b)
      Argument ex = expandSubtraction(child);
      if (ex!=null) {
        child.replace(ex);
      }
    }
    Ket.out.println("\t" + clone);
    for (Argument child : clone.getChildren()) { // (a+b)+c -> a+b+c
      if (child.getFunction()==Function.ADD) {
        Branch childBranch = child.asBranch();
        for (Argument grandchild : childBranch.getChildren()) {
          clone.addBefore(childBranch, grandchild);
        }
        child.remove();
      }
    }
    Ket.out.println("\t" + clone);
    // (+ ... (- (- a )) ...) -> (+ ... a ...)
    for (Argument child : clone.getChildren()) {
      if (Like.hasForm(child, Function.MINUS, 1)) {
        Argument grandchild = child.asBranch().firstChild();
        if (Like.hasForm(grandchild, Function.MINUS, 1)) {
          child.replace(grandchild.asBranch().firstChild());
        }
      }
    }
    Ket.out.println("\t" + clone);
    return clone;
  }

  private static int abs(int x) {
    return x<0 ? -x : x;
  }

  private static int sign(int x) {
    return x<0 ? -1 : +1;
  }

  /*-
  private static boolean isNeg(int x) {
    return x<0;
  }*/

}
 
TOP

Related Classes of ket.Transform

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.