/*
* 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 java.awt.*;
import java.util.*;
import ket.display.*;
import ket.math.Symbol;
import ket.display.box.Box;
import ket.display.box.BoxTools;
import ket.math.purpose.SymbolicState;
import ketUI.Ket;
/*
* An operation extends the concept of a function to include (all-optional)
* prefix, infix and postfix operators. Although the name suggests they
* consist only of symbols, they can also contain compoundStates that in turn
* contain symbols.
*/
public class SymbolicFunction extends AbstractFunction implements Function {
public static final Set<Symbol> MATCHES = new HashSet<Symbol>(Arrays.asList(new Symbol[]{Symbol.RE_ARGUMENT, Symbol.RE_FUNCTION, Symbol.RE_BRANCH}));
final SymbolicState prefix;
final SymbolicState infix;
final SymbolicState postfix;
final int precedence;
// Displays "\alpha:x" -> "&alpha(x)" not "&alpha x".
final boolean hasBrackets;
// Handle toBox() etc. with dependency injection or similar.
private SymbolicFunctionForm form;
public SymbolicFunction(
String name,
int[] numberOfArguments, // TODO: Rename to numberOfChildren.
int precedence,
SymbolicState prefix,
SymbolicState infix,
SymbolicState postfix,
boolean hasBrackets,
SymbolicFunctionForm form) {
super(name, numberOfArguments);
if (form==null) {
form = new SymbolicFunctionForm(this);
}
setForm(form);
this.precedence = precedence;
this.prefix = prefix;
this.infix = infix;
this.postfix = postfix;
this.hasBrackets = hasBrackets;
}
// How do you restrict a composition type in the sub-class?
public void setForm(SymbolicFunctionForm form) {
if (form!=null) {
this.form = form;
form.setFunction(this);
}
}
@Override
public boolean equals(Object object) {
if ( ! (object instanceof SymbolicFunction) ) {
return false;
}
SymbolicFunction symbolicFunction = (SymbolicFunction) object;
boolean namesAndPrecidenceMatch = super.equals(symbolicFunction);
SymbolicState thatPrefix = symbolicFunction.getPrefix();
boolean prefixesMatch = prefix==null ? thatPrefix==null: prefix.equals(thatPrefix);
SymbolicState thatInfix = symbolicFunction.getInfix();
boolean infixesMatch = infix==null ? thatInfix==null: infix.equals(thatInfix);
SymbolicState thatPostfix = symbolicFunction.getPostfix();
boolean postfixesMatch = postfix==null ? thatPostfix==null: postfix.equals(thatPostfix);
return namesAndPrecidenceMatch & prefixesMatch & infixesMatch & postfixesMatch;
}
@Override
public String toVerboseString() {
String string = "";
if (isPrefix()) {
string += prefix.getName();
}
string += " ? ";
if (isInfix()) {
string += infix.getName();
string += " ? ";
}
if (isPostfix()) {
string += postfix.getName();
}
return string;
}
public String toUnicodeString() {
String string = "";
if (isPrefix()) {
string += prefix.toUnicode();
}
string += " ? ";
if (isInfix()) {
string += infix.toUnicode();
string += " ? ";
}
if (isPostfix()) {
string += postfix.toUnicode();
}
return string;
}
@Override
public int getPrecedence() {
return precedence;
}
public SymbolicState getPrefix() {
return prefix;
}
public SymbolicState getInfix() {
return infix;
}
public SymbolicState getPostfix() {
return postfix;
}
public boolean isPrefixAndInfix() {
return isPrefix() && isInfix();
}
public boolean isInfixAndPostfix() {
return isInfix() && isPostfix();
}
public boolean isInfixOnly() {
return !isPrefix() && isInfix() && !isPostfix();
}
public boolean isPrefixOnly() {
return isPrefix() && !isInfix() && !isPostfix();
}
public boolean isPostfixOnly() {
return !isPrefix() && !isInfix() && isPostfix();
}
public boolean isPrefix() {
return prefix!=null;
}
public boolean isInfix() {
return infix!=null;
}
public boolean isPostfix() {
return postfix!=null;
}
@Override
public boolean displayBrackets() {
return hasBrackets;
}
/**
* All defined prefix, infix and postfix operators can be accessed in a
* vector using this method. Undefined elements are ignored.
*/
public Vector<SymbolicState> getSymbolicStates() {
Vector<SymbolicState> symbols = new Vector<SymbolicState>();
if (isPrefix()) {
symbols.add(prefix);
}
if (isInfix()) {
symbols.add(infix);
}
if (isPostfix()) {
symbols.add(postfix);
}
return symbols;
}
public boolean isPrefixPossible() {
return prefix!=null;
}
public boolean isInfixPossible() {
return infix!=null;
}
public boolean isPostfixPossible() {
return postfix!=null;
}
public boolean isPrefixNecessary() {
return isPrefixPossible();
}
public boolean isInfixNecessary() {
return isInfixPossible() && !(isPrefixPossible() && isPostfixPossible());
}
public boolean isPostfixNecessary() {
return isPostfixPossible();
}
@Override
public Set<Symbol> getMatchSymbols() {
// This is only updated dynamically in case the prefix, infix
// or posfix symbols are modified during runtime.
Set<Symbol> set = new HashSet<Symbol>(MATCHES);
for (SymbolicState fix : getSymbolicStates()) {
set.addAll(fix.getMatchSymbols());
}
// Individual symbols are tokens, but a function is not.
set.remove(Symbol.RE_TOKEN);
return set;
}
public boolean split(Branch branch) {
// TODO: Make more robust: This is too simplistic a treatment.
// You need to take into account optional prefixes (e.g., ket
// <?|?> or |?>) and optional infixes (e.g., [?] or [?, ?]).
Vector<Argument> args = branch.getChildren();
if (args.size()==0) {
return false;
}
if (isPrefixOnly()) { // eg #3
// iterate backwards to find inner function first: ln (ln x)
int index = prefix.iterateToStateBackwards(args);
if (index==-1) {
return false;
}
branch.subBranchAfter(index, this);
branch.removeChild(index);
return true;
} else if (isPostfixOnly()) { // eg 3!
int index = postfix.iterateToStateForwards(args);
if (index==-1) {
return false;
}
//- int size = branch.size();
branch.removeChild(index); // <--- THE RECENT CHANGE
branch.subBranchBefore(index, this);
return true;
} else if (isPrefix() && !isInfix() && isPostfix()) {
return splitPrefixAndPostfixOnly(branch, args);
} else if (isInfixOnly()) {
// Comma-like; a,b,c,d
int[] indices = infix.findAllStateIndices(args, true, true);
if (indices.length<=2) {
return false;
} else {
branch.subBranchBetween(indices, this);
return true;
}
} else if (isPrefix() && isInfix() && isPostfix()) {
return splitPrefixInfixPostfix(branch, args);
} else {
Ket.out.println(" <--- unfinished:SymbolicFunction'"+
getName()+"'.split() "+ isPrefix()+", "+
isInfix()+", "+isPostfix()+" ---> ");
return false;
}
}
private boolean splitPrefixAndPostfixOnly(Branch branch, Vector<Argument> args) {
if (prefix==postfix) {
// modulus like |x|
int start = prefix.iterateToStateForwards(args);
int end = postfix.iterateToStateForwards(args, start+1);
if (start!=-1 && end!=-1) {
branch.subBranchBetween(start, end, this);
branch.removeChild(start); // remove open
branch.removeChild(start+1); // remove close
return true;
} else {
return false;
}
} else {
// bracket like (x)
int start = prefix.iterateToStateForwards(args);
if (start<0) {
return false;
}
int nextClose, nextOpen;
while (true) {
nextClose = postfix.iterateToStateForwards(args, start+1);
nextOpen = prefix.iterateToStateForwards(args, start+1);
if (nextClose<0) {
return false;
} else if (nextOpen<0 || nextClose<nextOpen) {
branch.subBranchBetween(start, nextClose, this);
branch.removeChild(start); // remove open
branch.removeChild(start+1); // remove close
return true;
} else if (nextClose > nextOpen) {
start = nextOpen;
}
}
}
}
private boolean splitPrefixInfixPostfix(Branch branch, Vector<Argument> args) {
// Note: Find the first innermost bracket.
int start = prefix.iterateToStateForwards(args);
if (start<0) {
return false;
}
int from = start; // This is the index from which to search.
Vector<Integer> infixVector = new Vector<Integer>();
while (true) {
int nextOpen = prefix.iterateToStateForwards(args, from+1);
int nextInfix = infix.iterateToStateForwards(args, from+1);
int nextClose = postfix.iterateToStateForwards(args, from+1);
if (nextClose<0) {
// Brackets cannot be closed.
return false;
} else if (nextInfix>start &&
((nextOpen<0 || nextInfix<nextOpen)
&& nextInfix<nextClose)) {
// Infix is the next symbol.
from = nextInfix;
infixVector.add(new Integer(nextInfix));
} else if (nextOpen<0 || nextClose<nextOpen) {
// The next symbol is a postfix.
int[] range = new int[infixVector.size()+2];
range[0] = start;
for (int i=0; i<range.length-2; i++) {
range[i+1] = infixVector.get(i);
}
range[range.length-1] = nextClose;
branch.subBranchBetween(range, this);
branch.removeChild(start); // remove open
branch.removeChild(start+1); // remove close
return true;
} else if (nextClose>nextOpen) {
start = from = nextOpen;
infixVector = new Vector<Integer>();
}
}
}
protected Form getForm() {
return form;
}
}