/*
* 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.math;
import geom.Offset;
import geom.Position;
import java.awt.*;
import java.util.*;
import ket.display.ColourScheme;
import ket.display.ColourSchemeDecorator;
import ket.math.Argument;
import ket.math.Branch;
import ket.math.Function;
import ket.math.Token;
import ket.math.convert.Like;
import ketUI.Ket;
/**
* Various helper methods to aid in the processing of arguments that are
* suitably self contained as to be separately implemented as static methods
* without state.
*/
public class ArgumentTools {
/*-
public static Argument[][] asTable(Branch branch) {
if (Type.sequenceType(branch.getFunction())) {
Vector<Argument> args = branch.getChildren();
return asTable(args);
} else {
return null;
}
}
*/
public static Argument[][] asTable(Vector<Argument> args, boolean transpose) {
if (!isRectangle(args)) return null;
Function commonFunction = getCommonFunction(args);
if (!Type.sequenceType(commonFunction)) return null;
Argument[][] table = toTable(args);
if (table==null) return null;
return transpose ? calcTranspose(table) : table;
}
public static Argument[][] toTable(Vector<Argument> args) {
// Children are all branches with a (positive integer), consistent number of arguments.
if (args.size()==0) {
return null;
}
Argument[] row = toRow(args.firstElement());
if (row==null) {
return null;
}
int size = row.length;
Argument[][] table = new Argument[args.size()][size];
table[0] = row;
for (int i=1; i<args.size(); i++) {
Argument child = args.get(i);
row = toRow(child);
if (row==null || size!=row.length) {
return null;
}
table[i] = row;
}
return table;
}
/**
* Iterate through every element of a list and if every element shares
* a common function, return it.
*/
public static Function getCommonFunction(Vector<Argument> args) {
if (args.size()==0) {
return null;
}
Function commonFunction = args.firstElement().getFunction();
if (commonFunction==null) {
return null;
}
for (int i=1; i<args.size(); i++) {
Argument next = args.get(i);
Function function = next.getFunction();
if (commonFunction!=function) {
return null;
}
}
return commonFunction;
}
/**
* Check that the size of each element of args is the same and return it, and -1 otherwise.
*/
public static int getCommonSize(Vector<Argument> args) {
if (args.size()==0) {
return -1;
}
Argument first = args.firstElement();
if (first instanceof Token) {
return -1;
}
int commonSize = first.asBranch().size();
for (int i=1; i<args.size(); i++) {
Argument next = args.get(i);
if (next instanceof Token) {
return -1;
}
int size = next.asBranch().size();
if (size!=commonSize) {
return -1;
}
}
return commonSize;
}
public static Argument[] toRow(Argument argument) {
// TODO: Does the function type matter: currently commas, vectors, and anything are allowed, but this means that a*b*c -> [a,b,c].
if (argument==null || argument instanceof Token) {
return null;
}
Branch rowBranch = (Branch) argument;
if (rowBranch.size()==0) {
return null;
}
Argument[] row = new Argument[rowBranch.size()];
for (int i=0; i<row.length; i++) {
row[i] = rowBranch.getChild(i);
}
return row;
}
public static Argument[][] calcTranspose(Argument[][] table) {
int nrows = table.length;
int ncols = table[0].length;
Argument[][] transpose = new Argument[ncols][nrows];
for (int j=0; j<ncols; j++) {
for (int i=0; i<nrows; i++) {
transpose[j][i] = table[i][j];
}
}
return transpose;
}
/**
* All arguments have an equal number of arguments and the number of
* elements, and their number of arguments are all non-zero.
*/
public static boolean isRectangle(Vector<Argument> args) {
return getCommonSize(args) != -1;
}
/**
* Return the subset of args that have no ancestor within args. If no
* ancestor is found an argument is a 'root branch'. Note that a root
* branch can be a token or a branch.
*/
public static Vector<Argument> getRootBranches(Vector<Argument> args) {
Vector<Argument> roots = new Vector<Argument>();
for (Argument next : args) {
if ( ! containsAncestor(next, args) ) {
roots.add(next);
}
}
return roots;
}
/**
* Return true if any ancestor of next is inside args.
*/
private static boolean containsAncestor(Argument next, Vector<Argument> args) {
for (Branch ancestor : next.getAncestors()) {
for (Argument a : args) {
if (a==ancestor) {
return true;
}
}
}
return false;
}
/**
* Return true if both arguments share a common ancestor.
*/
public static boolean commonAncestry(Argument a, Argument b) {
if (a.getDepth()!=b.getDepth()) {
return false;
}
Vector<Branch> aAncestors = a.getAncestors();
Vector<Branch> bAncestors = b.getAncestors();
for (int k=0; k<aAncestors.size(); k++) {
Branch aNext = aAncestors.get(k);
Branch bNext = bAncestors.get(k);
if ( ! aNext.elementEquals(bNext) ) {
return false;
}
}
return true;
}
/**
* For each ancestor pair, check that the indices of the ancestors match.
*/
public static boolean commonAncestryIndex(Argument a, Argument b) {
if (a.getDepth()!=b.getDepth()) {
return false;
}
Vector<Branch> aAncestors = a.getAncestors();
Vector<Branch> bAncestors = b.getAncestors();
for (int k=0; k<aAncestors.size(); k++) {
Branch aNext = aAncestors.get(k);
Branch bNext = bAncestors.get(k);
if (aNext.getIndex()!=bNext.getIndex()) {
return false;
}
}
return true;
}
/**
* For a given argument, look for all desendents with the same function.
* TODO: Move this to argument range?
*/
public static Vector<Branch> getSimilarDescendents(Branch branch) {
Vector<Branch> matches = new Vector<Branch>();
Function function = branch.getFunction();
for (Argument child : branch.getChildren()) {
if (child.getFunction()==function) {
matches.add((Branch) child);
matches.addAll(getSimilarDescendents((Branch) child));
}
}
return matches;
}
public static Branch getSameFunctionAncestor(Branch branch) { // dangerous for non-branches.
Function function = branch.getFunction();
Vector<Branch> ancestors = branch.getAncestors();
Branch root = branch;
for (Branch a : ancestors) {
if (a.getFunction()==function) {
root = a;
} else {
return root;
}
}
return root;
}
// Cast didn't work here: why?
public static IdentityHashMap<Argument, Argument> convertToArgumentMap(IdentityHashMap<Token, Token> map) {
IdentityHashMap<Argument, Argument> clone = new IdentityHashMap<Argument, Argument>();
for (Argument a : map.keySet()) {
clone.put( (Argument) a, (Argument) map.get(a) );
}
return clone;
}
// Cast didn't work here: why?
public static IdentityHashMap<Token, Token> convertToTokenMap(IdentityHashMap<Argument, Argument> map) {
IdentityHashMap<Token, Token> clone = new IdentityHashMap<Token, Token>();
for (Argument a : map.keySet()) {
clone.put( (Token) a, (Token) map.get(a) );
}
return clone;
}
/**
* Match target to the LHS of identity and return identity's RHS having
* substituted for relevent tokens that differ between identity and target.
*/
public static Argument identityLike(Argument target, Argument identity) {
Argument[] pair = Like.equalPairLike(identity);
if (pair==null) { // c
return null;
}
// The identity is of the form "pair[0] = pair[1]".
Argument pattern = pair[0];
Argument expansion = pair[1];
// In practice, an indentity may appear using different letters
// and even numbers. Find the cypher to map from 'pattern' to
// 'target' and apply the cypher to 'expansion'.
IdentityHashMap<Token, Token> cypher = pattern.cypherEquals(target);
if (cypher==null) {
return null;
}
for (Token t : cypher.keySet()) {
}
Argument result = Like.substitute(expansion, convertToArgumentMap(cypher));
return result;
}
public static boolean parentsEquals(Argument before, Argument after) {
if (before.isRoot() || after.isRoot()) {
return before.isRoot() && after.isRoot();
} else {
Branch beforeParent = before.getParentBranch();
Branch afterParent = after.getParentBranch();
return beforeParent!=null && beforeParent.elementEquals(afterParent);
}
}
public static boolean childrenElementsEqual(Argument before, Argument after) {
if (before instanceof Branch ^ after instanceof Branch) {
// One is a branch and the other is a token.
return false;
} else if (before instanceof Token) {
// Both are tokens
return true;
} else {
// Both are branches
Branch beforeBranch = (Branch) before;
Branch afterBranch = (Branch) after;
return beforeBranch.childElementsEqual(afterBranch);
}
}
/**
* Append an element to the mapping's value, a list.
*/
public static void appendToMapValue(HashMap<Set<Integer>,Set<Integer>> map, Set<Integer> key, int element) {
// Roughtly: map[key].append(element)
Set<Integer> value = map.get(key);
if (value==null) {
value = new HashSet<Integer>();
map.put(key, value);
}
value.add(element);
}
/**
* Return the most specific common ancestor this shares with argument.
*/
public static Branch getCommonAncestor(Argument a, Argument b) {
if (a==null || b==null) {
return null;
}
Vector<Branch> aAncestors = a.getAncestors();
Vector<Branch> bAncestors = b.getAncestors();
for (Branch aa : aAncestors) {
if (aa.isIn(bAncestors)) {
return aa;
}
}
return null;
}
// TODO: [CLARIFY DEFINITION]
/**
* Iterate through the ancestors of 'a' until 'top' is reached
* returning the composition of the various (unitary) operations
* performed on 'a'. For example, the composition of the product of a
* fraction is the reciprocal (denoted by FRACTION). While the sum of
* a sum is itself a sum (denoted by ADD).
*/
public static Function effectiveUnitaryAncestorOperation(Argument target, Branch top) {
//D Ket.out.println("Route:");
//D Ket.out.println("\targument: " + (target!=null?target:null));
//D Ket.out.println("\ttop: " + (top!=null?top:null));
Branch parent = target.getParentBranch();
Function unitary = Type.getUnitaryFunction(target);
if (unitary==null) {
//D Ket.out.println("\t !!! Null unitary function !!! ");
return null;
}
//D Ket.out.println("\tunitary function = '" + unitary.getName() + "'");
Function composition=unitary;
while (parent!=null && parent!=top) {
//D Ket.out.println("\tparent = '" + parent + "'");
target = parent;
parent = parent.getParentBranch();
if (parent==null || parent==top) {
break;
}
unitary = Type.getUnitaryFunction(target);
if (unitary==null) {
//D Ket.out.println("\t !!! Null unitary function !!! ");
return null;
}
//D Ket.out.println("\tunitary function = '" + unitary.getName() + "'");
composition = Type.getComposition(composition, unitary);
if (composition==null) {
//D Ket.out.println("\t !!! Null composition !!! ");
return null; // Too complicated, e.g. sin(x^2) or (x+3)*2
}
//D Ket.out.println("\tcomposition = '" + composition.getName() + "'");
}
//D Ket.out.println();
return composition;
}
public static Vector<Vector<Argument>> getRepeatedIndices(Branch parentBranch) { // Is this clear enough?
return getRepeatedIndices(parentBranch.getChildren());
}
/**
* Search through a list of arguments and return those that are (sub-branch) equal to one another.
*/
// TODO: Use throughout Calculate, but call once and pass the result throughout.
public static Vector<Vector<Argument>> getRepeatedIndices(Vector<Argument> args) {
IdentityHashMap<Argument, Vector<Argument>> map = new IdentityHashMap<Argument, Vector<Argument>>();
for (Argument a : args) {
boolean otherwise = true;
for (Argument t : map.keySet()) { // Match an existing expression.
if (t.subBranchEquals(a)) {
map.get(t).add(a);
otherwise = false;
break;
}
}
if (otherwise) { // Add a new expression to compare against in the future.
map.put(a, new Vector<Argument>(Arrays.asList(a)));
}
}
Vector<Vector<Argument>> nonNullValuesVector = new Vector<Vector<Argument>>();
for (Vector<Argument> matches : map.values()) {
if (matches.size()>1) {
nonNullValuesVector.add(matches);
}
}
return nonNullValuesVector;
}
/**
* Return the composition of recursively moving out of source to
* a common ancestor (inverting) and into a second argument.
*/
public static Function getRelativeOperation(Argument source, Argument destination, Branch common) {
Ket.out.println("> relative operation");
boolean invert = Type.assignType(common) || Type.inequalityType(common);
if (!invert) {
Ket.out.println(" !!! Can't invert: common function ("+common.getFunction()+") must be an assign or [in]equality.");
return null;
}
// TODO: Use or remove getRelativeOperation().invert
Function sourceOperation = source.getPathOperation(common);
Function destinationOperation = destination.getPathOperation(common);
Ket.out.println("> source = " + source + " -> " + sourceOperation);
Ket.out.println("> destination = " + destination + " -> " + destinationOperation);
return getRelativeOperation(sourceOperation, destinationOperation, invert);
}
/**
* Return the composition of recursively moving out of one function to
* a common ancestor (inverting) and into a second path.
*/
public static Function getRelativeOperation(Function sourceOperation, Function destinationOperation, boolean invert) {
if (sourceOperation!=null && destinationOperation!=null) { // a*x=b*y -> a=b*y/x
Ket.out.println(">> ... -> ...");
if (invert) {
//- return Type.getComposition(destinationOperation, sourceOperation.getInverse());
return Type.getComposition(destinationOperation, Type.getInverse(sourceOperation));
} else {
return Type.getComposition(destinationOperation, sourceOperation);
}
} else if (sourceOperation!=null) { // a+x=y -> a=y-x ; y=x^2 -> sqrt(y)=x
Ket.out.println(">> ... -> null");
if (invert) {
//- return sourceOperation.getInverse();
return Type.getInverse(sourceOperation);
} else {
return sourceOperation;
}
} else if (destinationOperation!=null) { // x=a*y -> 1=a*y/x
Ket.out.println(">> null -> ...");
if (invert) {
//- return destinationOperation.getInverse();
return Type.getInverse(destinationOperation);
} else {
return destinationOperation;
}
} else {
Ket.out.println(">> null -> null");
return null;
}
}
}