/*
* 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≤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;
}*/
}