/*
* 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.util.*;
import ket.math.purpose.CompoundState;
import ket.math.purpose.SymbolicState;
import ket.math.purpose.VariableState;
import ket.math.purpose.Word;
import ketUI.Ket;
/**
* This contains user-defined variables and functions inferred from previous
* use are recorded here for future reference such as to auto-complete user
* input.
*/
public class KnownArguments {
public static final Comparator<String> STRING_LENGTH_COMPARATOR = new LengthComparator();
/**
* Store all known functions that will be used in the given equation.
*/
Set<Function> functionSet;
/**
* Some symbols are available as states of tokens and for the sake of
* auto-completion, these are stored here.
*/
Set<Symbol> operandSymbolSet;
/**
* Word tokens excluding quoted text.
*/
Set<String> variableNameStringSet;
/**
* A collection of all variables that are represented by symbols.
*/
Set<State> variableState;
/**
* A map from the name of a symbol to its instance.
*/
Vector<Symbol> allSymbols;
HashMap<Character, Function> charToFunctionMap;
public KnownArguments(Collection<Symbol> operandSymbolSet, Collection<Function> defaultOperators, Collection<Symbol> allSymbols, Map<Character,Function> defaultCharToFunctionMap) {
// Create a shallow copy of the function list as it will be
// modified.
functionSet = new HashSet<Function>();
this.operandSymbolSet = new HashSet<Symbol>(operandSymbolSet);
//- operandSymbolSet.addAll(Symbol.DEFAULT_OPERAND_SYMBOLS);
//- operandSymbolSet.addAll(Symbol.GREEK_LETTERS);
variableState = new HashSet<State>();
variableNameStringSet = new HashSet<String>();
//- for (Function function : Function.DEFAULT_OPERATORS) {
for (Function function : defaultOperators) {
recordUniqueFunction(function);
}
this.allSymbols = new Vector<Symbol>();
//- allSymbols.addAll(Symbol.ALL_SYMBOLS);
this.allSymbols.addAll(allSymbols);
//- charToFunctionMap = new HashMap<Character, Function>(Type.DEFAULT_CHAR_TO_FUNCTION_MAP);
charToFunctionMap = new HashMap<Character,Function>(defaultCharToFunctionMap);
}
public void recordVariable(State state) {
if (state instanceof Word) {
Word word = (Word) state;
String name = word.getValue();
recordUniqueVariable(name);
} else {
recordUniqueVariable(state);
}
}
/**
* Find a symbol by its associated digraph consisting two characters.
*/
public Symbol getDigraph(String digraph) {
Ket.out.println(" --- known arguments::get digraph --- ");
Ket.out.println("\tdigraph = '"+digraph+"'");
for (Symbol symbol: allSymbols) {
String[] digraphArray = symbol.getDigraphs();
for (int i=0; i<digraphArray.length; i++) {
if (digraphArray[i].equals(digraph)) {
return symbol;
}
}
}
// otherwise:
Ket.out.println(" !!! Unknown !!! ");
return Symbol.UNKNOWN;
}
/**
* Convert a given function to a corresponding state, keeping a record
* of it as appropriate.
*/
public VariableState functionToState(Function function) {
if (function==null) return null;
if (function instanceof SymbolicFunction) {
SymbolicFunction symbolicFunction = (SymbolicFunction) function;
VariableState state = null;
if (symbolicFunction.isPrefixOnly()) {
state = (VariableState) symbolicFunction.getPrefix();
addState(state);
} else {
Ket.out.println(" !!! The only functions that can be converted to variables are prefix-only !!! ");
}
return state;
} else {
String name = function.getName();
recordUniqueVariable(name);
return new Word(name);
}
}
public State toState(Purpose purpose) {
if (purpose instanceof Function) {
return (State) functionToState((Function) purpose);
} else {
return (State) purpose;
}
}
public Function toFunction(Purpose purpose) {
if (purpose instanceof Function) {
return (Function) purpose;
} else {
return stateToFunction((State) purpose);
}
}
public Function stateToFunction(State state) {
if (state==null) {
return null;
} else if ( ! (state instanceof VariableState) ) {
Ket.out.println(" !!! cannot convert state to a function !!! ");
Ket.out.println("state = " + state + ", " + state.getClass());
return null;
} else if (state instanceof Word) {
String pattern = ((Word) state).getValue();
return recordUniqueFunction(pattern);
} else if (state instanceof SymbolicState) {
SymbolicState prefixState = (SymbolicState) state;
SymbolicFunction symbolicFunction = new SymbolicFunctionBuilder(
prefixState.getName(),
Children.ANY_NUMBER_OF_CHILDREN,
Function.DEFAULT_PRECEDENCE)
.setPrefix(prefixState)
.setShowBrackets()
.build();
return recordUniqueFunction(symbolicFunction);
} else {
return null;
}
}
// ------------------------------------------------------------------------
/**
* Given the start of an argument's name, return the a Vector of
* possible argument names that begin with this string. Selection can
* be restricted with appropriate flags (FUNCTIONS, TOKENS or ALL).
*/
public Vector<String> autocompleteArgumentNames(String startOfName, char initialCharacter) {
SortedSet<String> suggestions = new TreeSet<String>(STRING_LENGTH_COMPARATOR);
for (Function function : functionSet) {
boolean matchTypeChar = function.matchTypeChar(initialCharacter);
String name = function.getName();
boolean nameStartMatches = name.startsWith(startOfName);
if (matchTypeChar && nameStartMatches) {
suggestions.add(function.getFullName()); // &fraction
//- suggestions.add(function.getName()); // fraction // WARNING: Auto complete integral gets stuck on \integral without &.
}
}
for (String name : variableNameStringSet) {
if (name.startsWith(startOfName)) {
suggestions.add(name);
}
}
for (State state : variableState) {
String name = state.toString();
if (name.startsWith(startOfName)) {
suggestions.add(state.getFullName());
}
}
// Search known symbols, e.g. \alpha etc.
for (Symbol symbol : operandSymbolSet) {
boolean matchTypeChar = symbol.matchTypeChar(initialCharacter);
String name = symbol.getName();
boolean nameStartMatches = name.startsWith(startOfName);
String word = symbol.getWord();
boolean wordStartMatches = word.startsWith(startOfName);
if (matchTypeChar && nameStartMatches && wordStartMatches) {
suggestions.add(symbol.getFullName());
}
}
Ket.out.println(" suggestions = " + suggestions + " <--------------- " );
return new Vector<String>(suggestions);
}
////////////////////////////////////////////////////////////////////////////////
// TODO: Transforming form a set to a sorted list every time is
// impractical: use some kind of sorted set for concision.
public Vector<Function> getSortedFunctions() {
Vector<Function> sortedFunctions = new Vector<Function>(functionSet);
Collections.sort(sortedFunctions);
return sortedFunctions;
}
public Vector<SymbolicFunction> getSortedSymbolicFunctions() {
Vector<SymbolicFunction> sortedSymbolicFunctions = new Vector<SymbolicFunction>();
for (Function function : functionSet) {
if (function instanceof SymbolicFunction) {
sortedSymbolicFunctions.add((SymbolicFunction) function);
}
}
Collections.sort(sortedSymbolicFunctions);
return sortedSymbolicFunctions;
}
// Used in equation reading next symbol when tokenizing.
public Set<Symbol> getOperandSymbolSet() {
return operandSymbolSet;
}
/**
* Search through the list of existing function names for one in which
* its name '.equals()' the string argument.
*/
public Function matchExistingFunction(String name) {
Vector<Function> matches = new Vector<Function>();
for (Function function : functionSet) {
if (function.getName().equals(name)) {
matches.add(function);
}
}
assert matches.size()<=1 : "Multiple functions of the same name were found.";
return matches.size()==1 ? matches.firstElement() : null;
}
/**
* Search for a given function that has exactly the same name, and if
* it does not exist, then create and add it.
*/
public Function recordUniqueFunction(String name) {
Function existingFunction = matchExistingFunction(name);
if (existingFunction!=null) {
return existingFunction;
}
Function function = new NamedFunction(name, Children.ANY_NUMBER_OF_CHILDREN);
recordUniqueFunction(function);
return function;
}
/**
* Keep a record of all known functions and if it is a symbolic
* function then also record the constituent symbols.
*/
private Function recordUniqueFunction(Function function) {
for (Function next : functionSet) {
// Search for an existing version of the function.
if (next.equals(function)) {
Ket.out.println("!!! function already found !!! ");
Ket.out.println("old function = " + next.toString() + ", " + next.getName());
Ket.out.println("new function = " + function.toString() + ", " + function.getName());
Ket.out.println("");
return next;
}
}
// Otherwise the functions must be new.
functionSet.add(function);
if (function instanceof SymbolicFunction) {
SymbolicFunction symbolicFunction = (SymbolicFunction) function;
Vector<SymbolicState> stateVector = symbolicFunction.getSymbolicStates();
for (SymbolicState state : stateVector) {
if (state instanceof Symbol) {
addState((Symbol) state);
} else if (state instanceof CompoundState) {
addState((CompoundState) state);
}
}
}
return function;
}
// -------------------------------------------------------------------------------------------------------
//////////////////////////
// TODO: CLEAN UP. //
//////////////////////////
/**
* Record any variable names in order to perform auto-complete and
* intelligent string matching.
*/
public void recordUniqueVariable(String variableName) {
variableNameStringSet.add(variableName);
}
public void recordUniqueVariable(State state) {
variableState.add(state);
}
private void addState(Symbol symbol) { // ?
operandSymbolSet.add(symbol);
}
// -------------------------------------------------------------------------------------------------------
private void addState(CompoundState compoundState) {
// TODO: Should this method's application be restricted to one
// of either states of functions or states of variables?
String name = compoundState.getName();
recordUniqueVariable(name);
}
private void addState(VariableState variableState) {
}
/**
* Return a function defined by a corresponding character.
*/
public Function getFunctionByChar(char character) {
return charToFunctionMap.get(character);
}
/**
* Return a quick summary of the known tokens and functions.
*/
public String toString() {
String string = "";
string += "function set:\n";
string += functionSet;
string += "\n\n";
string += "operandSymbolSet:\n";
string += operandSymbolSet;
string += "\n\n";
string += "variableNameStringSet:\n";
string += variableNameStringSet;
string += "\n\n";
string += "variableState:\n";
string += variableState;
string += "\n\n";
string += "allSymbols:\n";
string += allSymbols;
string += "\n\n";
string += "charToFunctionMap:\n";
string += charToFunctionMap;
return string;
}
}