/*
* 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 ketUI.modes;
import java.util.*;
import javax.swing.*;
import java.io.*;
import java.awt.event.KeyEvent;
import geom.Position;
import ket.*;
import ket.display.HTMLTools;
import ket.display.box.Box;
import ket.math.*;
import ket.math.convert.*;
import ket.math.purpose.*;
import ket.treeDiff.*;
import ketUI.Album;
import ketUI.Clipboard;
import ketUI.Document;
import ketUI.DocumentManager;
import ketUI.Ket;
import ketUI.MenuEventHandler;
import ketUI.chord.Chord;
import ketUI.chord.KeyPress;
import ketUI.chord.KeyboardEventHandler;
import ketUI.chord.Macros;
import ketUI.panel.KetPanel;
public class NormalMode {
public static final String ATAN_DASH_FILE = "/data/atanDashTutorial.template";
public static final String COMMANDS_REFERENCE_FILE = "/data/chords.template";
public static final String CONSTANTS_FILE = "/data/constants.template";
public static final String CONTOUR_INTEGRAL_FILE = "/data/contour.template";
public static final String DERIVATIVES_FILE = "/data/derivatives.template";
public static final String DE_FILE = "/data/differentialEquations.template";
public static final String EDITING_FILE = "/data/advancedEditing.template";
public static final String FOURIER_TRANSFORMS_FILE = "/data/fourier.template";
public static final String FUNCTIONS_REFERENCE_FILE = "/data/functions.template";
public static final String INTEGRALS_FILE = "/data/integral.template";
public static final String KEPLER_FILE = "/data/keplerTutorial.template";
public static final String LAPLACE_FILE = "/data/Laplace.template";
public static final String MAXWELL_FILE = "/data/maxwellTutorial.template";
public static final String OVERVIEW_FILE = "/data/overview.template";
public static final String PHYSICS_FILE = "/data/physics.template";
public static final String SIMPLE_FILE = "/data/simple.template";
public static final String STATS_FILE = "/data/stats.template";
public static final String SYMBOLS_REFERENCE_FILE = "/data/symbols.template";
public static final String TRIG_IDENTITIES_FILE = "/data/trigIdentities.template";
public static final String TUTORIAL_FILE = "/data/tutorial.template";
public static final String VECTOR_FILE = "/data/vectors.template";
public static final String[] FILES = {
CONSTANTS_FILE, CONTOUR_INTEGRAL_FILE, DERIVATIVES_FILE, DE_FILE,
FOURIER_TRANSFORMS_FILE, INTEGRALS_FILE, LAPLACE_FILE, PHYSICS_FILE,
SIMPLE_FILE, STATS_FILE, TRIG_IDENTITIES_FILE, VECTOR_FILE};
final Modes modes;
final NormalMode normalMode;
final MathCollection mathCollection;
final KeyboardEventHandler keyboardEventHandler;
final Cycle cycle;
Vector<Argument> referenceMatches = new Vector<Argument>(); // i.e. empty and not null.
public NormalMode(Modes modes) {
this.modes = modes;
this.normalMode = modes.getNormalMode();
this.mathCollection = modes.getMathCollection();
this.keyboardEventHandler = getDocument().getKeyboardEventHandler();
this.cycle = new Cycle(mathCollection.getKnownArguments());
}
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////// UTILITY METHODS ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/////////////
// FOLDING //
/////////////
// :normal 'h', 'o', 'te'.
public void expandLeft() {
getCursor().selectLeft(); // 'h'
getCursor().selectInOrOutRight(); // 'o'
getSelection().duplicateSwapAncestors(); // 'te'
getCursor().selectInOrOutRight(); // 'o'
getCursor().selectInOrOutRight(); // 'o'
}
// :normal 'l', 'i', 'te'.
public void expandRight() {
getCursor().selectRight(); // 'l'
getCursor().selectInOrOutLeft(); // 'i'
getSelection().duplicateSwapAncestors(); // 'te'
getCursor().selectInOrOutLeft(); // 'i'
getCursor().selectInOrOutLeft(); // 'i'
}
public boolean closeAncestorFold() {
if (getCurrent() instanceof Branch) {
Equation equation = getCursor().getEquation();
equation.setVisibleRoot((Branch) getCurrent());
} else {
modes.error("Cannot fold a non-branch.");
}
return false;
}
public void openFold() {
openAncestorFold();
openDescendentFold();
}
public void openAncestorFold() {
if (getCurrent() instanceof Branch) {
getCursor().getEquation().unfold();
}
}
public void closeDescendentFold() {
if (getSelection().areMultipleEquationsSelected()) {
getSelection().addIntermediaryParent(Function.VECTOR);
}
getCurrent().setVisible(false);
}
public void openDescendentFold() {
getCurrent().setVisible(true);
}
public void selectInOrOutLeft(Chord chord) {
getSelection().shatterText();
if (chord.countChanged()) {
int count = chord.getCounts();
Address address = new Address(count);
chord.setLoopSkipped(true);
Highlight highlight = mathCollection.getHighlight();
Argument relative = highlight.relativeAddress(address);
if (relative!=null) {
getSelection().setOnly(relative);
}
} else {
getCursor().selectInOrOutLeft();
}
}
public void selectInOrOutRight(Chord chord) {
getSelection().shatterText();
if (chord.countChanged()) {
int count = chord.getCounts();
Address address = new Address(count);
chord.setLoopSkipped(true);
Highlight highlight = mathCollection.getHighlight();
Argument relative = highlight.relativeAddress(address);
if (relative!=null) {
getSelection().setOnly(relative);
}
} else {
getCursor().selectInOrOutRight();
}
}
public void selectLeft() {
if (mathCollection.isColumnSelectionSet()) {
getCursor().selectLeftByAddress();
} else {
getCursor().selectLeft();
}
}
public void selectRight() {
if (mathCollection.isColumnSelectionSet()) {
getCursor().selectRightByAddress();
} else {
getCursor().selectRight();
}
}
public void selectUp() {
if (mathCollection.isColumnSelectionSet()) {
getCursor().selectUpByAddress();
} else {
getCursor().selectUp();
}
}
public void selectDown() {
if (mathCollection.isColumnSelectionSet()) {
getCursor().selectDownByAddress();
} else {
getCursor().selectDown();
}
}
private RangeSelection getRangeSelection() {
return mathCollection.getRangeSelection();
}
/**
* Append a clone after the current selection within a new intermediate
* branch of a use-specified operation.
*/
public void argumentClone(Chord chord) {
Branch intermediate = getCurrent().addIntermediaryParent(Function.UNKNOWN);
int n = 2;
if (chord.countChanged()) {
n = chord.getCounts();
chord.setLoopSkipped(true);
}
for (int i=1; i<n; i++) { // Include the original in the count.
Argument clone = Argument.cloneArgument(getCurrent());
intermediate.append(clone);
}
getSelection().setOnly(intermediate);
chord.setComplete(false);
modes.setDocumentState(DocumentState.QUICK_RENAME);
}
public void equate() {
Ket.out.printf("%60sDEBUG CYPHER:\n", "");
Argument root = getCurrent().getRoot();
if (Like.lacksForm(root, Function.EQUALS, 2) && Like.lacksForm(root, Function.TO, 2))
return;
Branch branch = (Branch) root;
Argument clone = Argument.cloneArgument(branch.firstChild());
IdentityHashMap<Argument, Argument> t = clone.cypher(branch.lastChild());
Branch v = new Branch(Function.VECTOR);
for (Argument k : t.keySet()) {
Argument from = Argument.cloneArgument(k);
Argument to = Argument.cloneArgument(t.get(k));
v.append(new Branch(Function.TO, from, to));
}
EquationList el = root.getEquationList();
el.addAfter(root.getEquation(), new Equation(v));
}
public boolean recursiveDifferentiate() {
Ket.out.println("[recursiveDifferentiate-0]" + getCurrent());
if (getCurrent().isToken()) return false;
boolean change = true;
getSelection().simplify();
EquationList el = getCurrent().getEquationList();
Address address = getCurrent().getAddress();
while (change && getCurrent().isBranch()) {
Ket.out.println(" ============================= ");
Ket.out.println(" before = " + getCurrent() + "\t" + getCurrent().getAddress());
change = recursiveDifferentiate(getCurrent().asBranch());
getSelection().setCurrent(el.relativeAddress(address));
Ket.out.println(" dash = " + getCurrent() + "\t" + getCurrent().getAddress());
getSelection().simplify();
getSelection().setCurrent(el.relativeAddress(address));
Ket.out.println(" simple = " + getCurrent() + "\t" + getCurrent().getAddress());
Ket.out.println(" ============================= ");
for (int i=0; i<5; i++) Ket.out.println();
}
return true;
}
public boolean recursiveDifferentiate(Branch next) {
Ket.out.println("[recursiveDifferentiate-1]" + next);
getSelection().setOnly(next);
Ket.out.println("Recur differentiate:" + getCurrent());
if (expandDash(next)) {
Ket.out.println("\texpand dash -> true $" + getCurrent());
return true;
} else if (dash(next)) {
Ket.out.println("\tdash -> true $" + getCurrent());
return true;
} else if (Type.transparent(next.getFunction())) {
return mapThrough(next);
} else {
return false;
}
}
private boolean dash(Branch next) {
Ket.out.println("[dash]" + next);
if (Like.firstDerivativeLike(next)==null) return false;
Argument derivative = Transform.differentiate(next);
if (derivative==null) return false;
Ket.out.println("DASH: " + next); //*
Ket.out.println("\t -> " + derivative); //*
getSelection().replace(derivative);
Ket.out.println("\tselection=" + getCurrent());
return true;
}
private boolean expandDash(Branch next) {
Ket.out.println("[expand dash]" + next);
Argument subject = next.asBranch().firstChild();
if (subject==null || subject.size()<1) return false;
Argument end = subject.asBranch().firstChild();
if (Like.isLinearDerivative(next, end)) { // derivative(a+y+..., x)
getSelection().setCurrent(end);
getSelection().duplicateSwapAncestors();
return true;
} else if (Like.isProductDerivative(next, end)) { // (a*b)'x -> a'x*b + a*b'x
getSelection().replace(Transform.applyProductRule(next));
return true;
} else if (Like.isQuotientDerivative(next, end)) { // (a/b)'x -> (a'x*b-a*b'x)/b^2
Transform.applyQuotientRule(getSelection(), next, end);
return true;
} else if (Like.isCompositionDerivative(next, end)) { // TODO: Clean up.
Ket.out.println(" --- <composition> --- ");
Ket.out.println("before: "+next);
Transform.applyChainRule(getSelection(), next, (Branch) end, mathCollection.getKnownArguments());
next = getCurrent().asBranch(); // (f(q)'q)@(q=...) * g(x)'x
Ket.out.println("chain rule: " + next);
// next = (f(q)'q)@(q=?)*? so solve for f(q)'q then substitute.
try {
Branch firstGrandChild = next.firstChild().asBranch().firstChild().asBranch(); // f(q)'q
Ket.out.println("grandchild = " + firstGrandChild);
getSelection().setOnly(firstGrandChild);
recursiveDifferentiate(firstGrandChild);
Ket.out.println("dash = " + getCurrent());
Branch range = next.asBranch().firstChild().asBranch(); // h(q)'q=...
Ket.out.println("range: " + range);
getSelection().setCurrent(range);
Transform.expandRange(getSelection(), range);
} catch (NullPointerException e) {
Ket.out.println("null pointer: " + next);
return false;
}
Ket.out.println("substt: " + getCurrent());
setCurrent(next); //?
Ket.out.println("next = " + next);
Ket.out.println(" --- </composition> --- ");
return true;
} else {
return false;
}
}
private boolean mapThrough(Branch next) {
Ket.out.println("[map through]" + next);
boolean changed = false;
for (Argument child : next.getChildren()) {
if (child.isBranch()) {
Ket.out.println("CHILD: " + child);
changed |= recursiveDifferentiate(child.asBranch());
}
}
return changed;
}
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// GENERAL ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
public void toggleNetworkDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleNetworkDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void toggleGraphDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleGraphDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void toggleHistoryDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleHistoryDisplay();
}
public void toggleListDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleListDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void togglePerspectiveDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.togglePerspectiveDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void toggleScatterDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleScatterDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void toggleAnimatedDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleAnimatedDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void toggleGridDisplay() {
KetPanel ketPanel = getKetPanel();
ketPanel.toggleGridDisplay();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void cygleDisplayMode() {
KetPanel ketPanel = getKetPanel();
ketPanel.cygleDisplayMode();
ketPanel.fromMiddle(getCursor().getEquation().getEquationIndex());
MenuEventHandler menuEventHandler = getDocument().getFrameManager().getMenuEventHandler();
if (menuEventHandler!=null) {
menuEventHandler.updateMultipleEquations();
}
}
public void openDocument(String filename) {
if ( ! getDocument().getFrameManager().isStandAlone() ) {
Ket.out.println("[Can't open "+filename+"]");
return;
}
DocumentManager documentManager = getDocument().getDocumentManager();
Document document = new Document(documentManager, null, filename);
document.getKetPanel().toggleGridDisplay();
if (documentManager!=null) {
documentManager.addDocument(document);
}
}
public boolean openLink() {
if ( ! getDocument().getFrameManager().isStandAlone() ) {
return false;
}
Branch branch = getCursor().asBranch();
if (branch==null || branch.getFunction()!=Function.LINK) {
branch = getCurrent().getParentBranch();
getSelection().setOnly(branch);
}
if (branch==null || branch.getFunction()!=Function.LINK) {
Ket.out.println(" !!! Open link reqiures the current or parent branch to be a link !!! ");
return false;
}
DocumentManager documentManager = getDocumentManager();
if (documentManager==null) {
Ket.out.println(" !!! Can't follow links from within another GUI !!! ");
return false;
}
switch (branch.size()) {
case 1: // format: link(pathString)
openLinkDocument(recognizePath(0), documentManager, null);
return true; // TODO: Return true if the file actually loaded as required.
case 2: // format: link(label, pathString)
openLinkDocument(recognizePath(1), documentManager, null);
return true; // TODO: Return true if the file actually loaded as required.
case 3: // format: link(label, pathString, lineNumber)
openLinkDocument(recognizePath(1), documentManager, getLocation());
return true;
default:
return false;
}
}
// Recursively perform monte carlo integration until interupted.
public void monte() {
Ket.out.println("<Monte>");
Argument current = getCurrent();
if (current.getFunction()==Function.INTEGRAL && current.size()==4) { //> Like.numericIntegralLike(current).
Monte monte = new Monte(getCurrent().asBranch());
Thread thread = new Thread(monte);
thread.start();
Ket.out.println("</Monte>");
}
}
class Monte implements Runnable {
Branch integral; // required?
Argument subject;
Token x;
Double from;
Double to;
double total;
double total2;
int n;
Address address;
private Monte() { }
public Monte(Argument integral) {
Ket.out.println("[monte...]");
this.integral = (Branch) Argument.cloneArgument(integral);
n = 0;
total = 0.0;
total2 = 0.0;
address = integral.getAddress();
process();
}
private double sample() {
return Math.random()*Math.abs(to-from) + Math.min(from, to);
}
private double eval() {
return Calculate.eval(Argument.cloneArgument(subject), sample(), Argument.cloneArgument(x)); // Are the clones required?
}
private void process() {
from = Like.getDouble(integral.getChild(0));
to = Like.getDouble(integral.getChild(1));
subject = Argument.cloneArgument(integral.getChild(2));
x = Argument.cloneArgument(integral.getChild(3)).asToken();
Ket.out.println("integrate: " + from + "->" + to + " a subject " + subject + " wrt " + x);
}
public DoubleValue integrate() { // WARNING: Returns the fractional error change not the integral!
// Iterate over a time limit.
for (int i=0; i<1000; i++) {
n += 1;
double y = eval();
total += y;
total2 += y*y;
}
double scale = (to-from)/n;
double mean = total/n;
double stdev = Math.sqrt(total2/n - mean*mean); // sqrt(E(X^2) - E(X)^2) -> integral
double result = total*scale;
double error = stdev*scale;
Ket.out.printf("\rintegral = %13.7g \\pm %13.7g %s&%s\t", result, error, address, getCurrent().getAddress());
DoubleValue doubleValue = new DoubleValue(result);
doubleValue.setPrecision((int) Math.floor(Math.log10(error))); // 0.012 -> -2
return doubleValue;
}
public void run() {
Ket.out.println("[sampling...]");
DoubleValue doubleValue = integrate();
while (address.equals(getCurrent().getAddress())) {
getSelection().replace(new Token(doubleValue));
getKetPanel().updateAndRepaint();
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
Ket.out.println(ie);
return;
}
doubleValue = integrate();
}
Ket.out.println("\n[...done]");
}
}
/**
* Open a given document at a location specified by the given state:
* either an instance of text, word or integer value. A null location
* specifies that the program should select the start of the document.
*/
private boolean openLinkDocument(String filename, DocumentManager documentManager, State location) {
if ( ! getDocument().getFrameManager().isStandAlone() ) {
return false;
}
if (filename==null) {
Ket.out.println(" !!! Unexpected filename !!! ");
return false;
}
// TODO: Check that the filename exists before actually openning the window.
Document document = null;
String label = Like.getString(location);
Integer equationNumber = Like.getInteger(location);
if (location==null) {
document = new Document(documentManager, null, filename);
} else if (equationNumber!=null) {
int equationIndex = equationNumber-1;
document = new Document(documentManager, null, filename, equationIndex);
} else if (label!=null) {
// TODO: Check that the filename exists before actually openning the window.
document = new Document(documentManager, null, filename, label);
} else {
Ket.out.println(" !!! Unexpected label() syntax !!! ");
return false;
}
documentManager.addDocument(document);
getKetPanel().updateAndRepaint();
return true;
}
private String recognizeName() {
// TODO: Check that the file was opened rather than only creating a blank document.
Branch currentBranch = getCursor().asBranch();
Argument first = currentBranch.getChild(0);
return Like.getString(first);
}
private String recognizePath(int index) {
Branch currentBranch = getCursor().asBranch();
Argument first = currentBranch.getChild(index);
return Like.getString(first);
}
private State getLocation() {
Branch currentBranch = getCursor().asBranch();
return currentBranch.getChild(2).getState();
}
/**
* If current or one of its ancestor has the given function, move up to
* the first instance and set it current, returning true on success.
*/
private boolean moveUpToFunction(Function function) {
if (getCurrent().getFunction()==function) {
return true;
}
Vector<Branch> ancestors = getCurrent().getAncestors();
boolean set = false;
for (Branch parent : ancestors) {
if (parent.getFunction()==function) {
getSelection().setOnly(parent);
set = true;
}
}
return set;
}
/**
* Expand powers (a+b)^c to (a+b)*(a+b)...(a+b).
*/
public boolean expandPower() { // NOTE: This moves up while Transform.expandPower() does not!
boolean ok = moveUpToFunction(Function.POWER);
if ( ! ok ) {
Ket.out.println(" !!! Can't expand powers of a non-power function !!! ");
return false;
}
Argument current = Transform.expandPower(getCurrent());
if (current==null) {
return false;
}
getSelection().replace(current);
return true;
}
/**
* Multiply out a series of brackets or the squared sum of terms.
*/
public boolean expandTimes() {
// Can move up to times or power branch?
Vector<Argument> ancestors = new Vector<Argument>(getCurrent().getAncestors());
Collections.reverse(ancestors);
ancestors.add(getCurrent());
Argument multinomial = null;
Argument powerOfProduct = null;
Argument times = null;
for (Argument a : ancestors) {
// ASSUMPTION: prefer to expand (a*b)^n over expanding a*b.
if (powerOfProduct!=a.getParent() && a.getFunction()==Function.TIMES) {
times = a;
} else if (Like.multinomialExpandable(a)) {
multinomial = a;
} else if (Like.intPowerOfProduct(a)) {
powerOfProduct = a;
}
}
if (multinomial!=null) { // (a+b...)^n -> ...+...+...
setCurrent(multinomial);
return multinomialExpand(); // (a+b)^n
// Fully done?
} else if (powerOfProduct!=null) { // (a*b*...)^n -> a^n * b^n * ...
setCurrent(powerOfProduct);
return powerOfProductExpand();
} else if (times!=null) {
setCurrent(times);
return multiplyOut();
}
return false;
}
private boolean multiplyOut() {
Argument current = Transform.multiplyOut(getCurrent());
if (current==null) {
return false;
}
getSelection().replace(current);
return true;
}
private boolean powerOfProductExpand() {
Argument current = Transform.powerOfProductExpand(getCurrent());
if (current==null) {
return false;
}
getSelection().replace(current);
return true;
}
private boolean multinomialExpand() {
Argument current = Transform.multinomialExpand(getCurrent());
if (current==null) {
return false;
}
getSelection().replace(current);
return true;
}
/**
* Drill down to the first non-product-type argument within current and
* add tem to a map to the associated function. This also handles
* minus signs.
*/
public boolean organizeTimesType() { // TODO: Fix for -ve cases.
if (!Type.timesType(getCurrent()) && !Like.negativeLike(getCurrent())) { // limit to a*b or -x etc.
return false;
}
IdentityHashMap<Argument, Function> map = new IdentityHashMap<Argument, Function>();
boolean positive = organizeTimesTypeRecur(map, getCurrent(), null);
Ket.out.println("positive = " + positive);
Ket.out.println("map = " + map);
Vector<Argument> times = new Vector<Argument>();
Vector<Argument> fraction = new Vector<Argument>();
for (Argument a : map.keySet()) {
Function op = map.get(a);
if (op==Function.TIMES) {
times.add(a);
} else {
fraction.add(a);
}
}
int tSize = times.size();
int fSize = fraction.size();
Argument firstTimes = tSize!=0 ? times.firstElement() : null;
Argument firstFraction = fSize!=0 ? fraction.firstElement() : null;
Argument replacement = null;
switch (fSize) {
case 0:
if (tSize==0) { // 1/1 -> 1
replacement = new Token(1);
} else if (tSize==1) { // a/1 -> a
replacement = firstTimes;
} else if (tSize>=2) { // a*b*c*...
replacement = new Branch(Function.TIMES, times);
}
break;
case 1:
if (tSize==0) { // 1/a
replacement = new Branch(Function.FRACTION, new Token(1), firstFraction);
} else if (tSize==1) { // a/b
replacement = new Branch(Function.FRACTION, firstTimes, firstFraction);
} else if (tSize>=2) { // a*b*c*d/z
Branch t = new Branch(Function.TIMES, times);
replacement = new Branch(Function.FRACTION, t, firstFraction);
}
break;
default:
if (tSize==0) { // 1/(a*b*c)
Branch f = new Branch(Function.TIMES, fraction);
replacement = new Branch(Function.FRACTION, new Token(1), f);
} else if (tSize==1) {
Branch f = new Branch(Function.TIMES, fraction);
replacement = new Branch(Function.FRACTION, firstTimes, f);
} else if (tSize>=2) {
Branch t = new Branch(Function.TIMES, times);
Branch f = new Branch(Function.TIMES, fraction);
replacement = new Branch(Function.FRACTION, t, f);
}
}
if (positive) {
getSelection().replace(replacement);
} else {
Branch negative = new Branch(Function.MINUS, replacement);
getSelection().replace(negative);
}
return true;
}
/**
* Clean up times and fraction sub-branches recursively into their cannonical form.
*/
private boolean organizeTimesTypeRecur(IdentityHashMap<Argument,Function> map, Argument a, Function path) {// returns sign information
Function op = Type.getUnitaryFunction(a);
if (Type.timesType(a)) { // recur
int sign = 0;
for (Argument child : a.asBranch().getChildren()) {
Function comp = null;
if (path==null) {
comp = op;
} else if (op==null || op==Function.MINUS) { // <--- Think about this more carefully.
comp = path;
} else {
comp = Type.getComposition(path, op);
}
sign += organizeTimesTypeRecur(map, child, comp) ? 0 : 1;
}
return sign%2==0; // Odd number of minus signs are positive.
} else if (Like.negativeLike(a)) { // change sign, recur
Argument child = a.asBranch().firstChild();
return ! organizeTimesTypeRecur(map, child, path);
} else { // Update map and sign.
State s = a.getState();
if (s instanceof NumberValue && ((NumberValue) s).getDouble()==1.0) {
// Skip all 1's.
return true;
}
if (op==Function.MINUS) { // ignore minus sign.
map.put(a, path);
} else if (path==null) {
map.put(a, op);
} else {
map.put(a, Type.getComposition(path, op));
}
return true;
}
}
/**
* Drill down to the first non-positive-type argument within current
* and add them to the map along with their unitary function.
**/
public boolean organizeAddType() {
Argument organized = Transform.organizeAddType(getCurrent());
if (organized==null) {
return false;
}
getSelection().replace(organized);
return true;
}
public boolean editCurrentArgument() {
//- if (getCurrent().isText()) {
if (getCurrent().isMixed()) {
modes.getMessage().setMessage(getCurrent().getText());
modes.setDocumentState(DocumentState.UPDATE_TEXT);
} else {
modes.getMessage().setMessage(getCurrent().toEditString()); //?
modes.setDocumentState(DocumentState.UPDATE_REPLACE);
}
return true;
}
public boolean editCurrentPurpose() {
modes.getMessage().setMessage(getCurrent().getPurpose().getName());
DocumentState ds = getCurrent() instanceof Branch ? DocumentState.APPEND_PURPOSE : DocumentState.UPDATE_REPLACE;
modes.setDocumentState(ds);
return true;
}
public void slideLeft() { //! a*b^x -> a^x*b
Argument current = getCurrent();
Argument sibling = current.getOnlySibling(); // Generalize to zero/multiple siblings.
Branch parentBranch = getCurrent().getParentBranch();
Argument neighbour = parentBranch.getPreviousSibling();
if (neighbour==null) return;
sibling.remove();
parentBranch.replace(sibling);
neighbour.replace(parentBranch);
parentBranch.prepend(neighbour);
setCurrent(current);
}
public void slideRight() {
Argument current = getCurrent();
Argument sibling = current.getOnlySibling(); // Generalize to zero/multiple siblings.
Branch parentBranch = getCurrent().getParentBranch();
Argument neighbour = parentBranch.getNextSibling();
if (neighbour==null) return;
sibling.remove();
parentBranch.replace(sibling);
neighbour.replace(parentBranch);
parentBranch.prepend(neighbour);
setCurrent(current);
}
public boolean gatherLeft() {
Branch grandparentBranch1 = getCursor().getGrandparentBranch();
if (grandparentBranch1==null) {
Ket.out.println(" !!! Can't gather left: current argument's grandparent is not a branch !!! ");
return false;
}
for (Argument child : grandparentBranch1.getChildren()) {
if (child instanceof Branch) {
Branch childBranch = (Branch) child;
if (childBranch.size()>2) {
childBranch.moveFirstChildOut();
}
}
}
getSelection().transpose();
getDeleteMode().deleteParent();
return true; //~
}
public boolean gatherRight() {
Branch grandparentBranch2 = getCursor().getGrandparentBranch();
if (grandparentBranch2==null) {
Ket.out.println(" !!! Can't gather right: current argument's grandparent is not a branch !!! ");
return false;
}
for (Argument child : grandparentBranch2.getChildren()) {
if (child instanceof Branch) {
Branch childBranch = (Branch) child;
if (childBranch.size()>2) {
childBranch.moveLastChildOut();
}
}
}
getSelection().transpose();
getDeleteMode().deleteParent();
return true; //~
}
public boolean actInLeft() {
Argument current = getCurrent();
if (current.isRoot()) {
if (mathCollection.isRangeSelectionSet()) {
return false;
}
mathCollection.getCursorSelection().insertIntoPreviousEquation();
return true;
} else if (current.isFirstArgument()) {
return getSelection().swapOut();
} // otherwise...
Branch pb = getCursor().getParentBranch();
if (pb.size()>2) {
// When moving into the previous sibling, don't affect those other siblings around it.
Function pf = pb.getFunction();
Argument previousSibling = current.getPreviousSibling();
Branch ipb = getSelection().addIntermediaryParent(pf);
getSelection().setOnly(current); // Go back to old current.
ipb.prepend(previousSibling); // Move previous aunt to previous sibling.
}
getCursor().selectLeft(); // 'h'
getCursor().selectInOrOutRight(); // 'o'
boolean ok = getSelection().swapAncestors(); // 'tt' // Can this fail?
if (ok) {
getSelection().setOnly(current);
}
return true;
}
public boolean actInRight() {
Argument current = getCurrent();
if (current.isRoot()) {
if (mathCollection.isRangeSelectionSet()) {
return false;
}
mathCollection.getCursorSelection().insertIntoBelowEquation();
return true;
} else if (current.isLastArgument()) {
return getSelection().swapOut();
} // otherwise
Branch parentBranch = getCursor().getParentBranch();
if (parentBranch.size()>2) {
// When moving into the next sibling, don't affect those other siblings around it.
Function pf = parentBranch.getFunction();
Argument nextSibling = current.getNextSibling();
Branch ipb = getSelection().addIntermediaryParent(pf);
getSelection().setOnly(current); // Go back to old current.
ipb.append(nextSibling); // Move next aunt to next sibling.
}
getCursor().selectRight(); // 'l'
getCursor().selectInOrOutLeft(); // 'i'
getSelection().swapAncestors(); // 'tt' // Can this fail?
Ket.out.println(" *** &c:" + current.getAddress());
getSelection().setOnly(current);
return true;
}
public boolean startOrStopRecordingMacro(Chord chord) {
Macros macros = getDocument().getKeyboardEventHandler().getMacros();
if ( ! macros.isRecordingMacro()) {
// Record a macro.
chord.setComplete(false);
modes.setDocumentState(DocumentState.RECORD_MACRO);
} else {
// Stop recording the macro.
macros.stopRecordingMacro();
}
return false;
}
public boolean replaceSelectionWithIdentity() {
Ket.out.printf("%60s--- replace selection with identity --- \n", "");
Argument current = getCursor().getCurrent();
int index = current.indexIn(referenceMatches);
if (index==-1) {
// New search
Ket.out.println(" --- new search --- ");
KnownArguments knownArguments = mathCollection.getKnownArguments();
referenceMatches = findIdentity(current, knownArguments);
if (referenceMatches.size()==0) {
Ket.out.println(" !!! No identities in the references match the current selection !!! ");
return false;
}
Ket.out.println("matches:");
for (Argument match : referenceMatches) {
Ket.out.println("\t" + match);
}
Argument substitute = referenceMatches.firstElement();
getSelection().replace(substitute);
// Current is now identically equal to the first element
// of referenceMatches.
} else {
Ket.out.println("[cycle through existing matches "+referenceMatches.size()+"]");
// Cycle through the various matches.
int nextIndex = (index + 1) % referenceMatches.size();
Argument substitute = referenceMatches.get(nextIndex);
getSelection().replace(substitute);
}
return true;
}
static Vector<Argument> identities = null;
private static Vector<Argument> findIdentity(Argument target, KnownArguments knownArguments) {
Ket.out.println(" === find identity === ");
Ket.out.println("\ttarget = " + target);
if (identities==null) {
loadIdentity(knownArguments);
}
Vector<Argument> matches = new Vector<Argument>();
for (Argument potentialIdentity : identities) {
Argument substitute = ArgumentTools.identityLike(target, potentialIdentity);
if (substitute!=null) {
Ket.out.println("\tid:" + potentialIdentity);
Ket.out.println("\tsub:" + substitute);
matches.add(substitute);
}
}
return matches;
}
private static void loadIdentity(KnownArguments knownArguments) { // TODO: Move this to a suitable class. // Static method?
Ket.out.printf("%sLOAD IDENTITIES\n", "");
identities = new Vector<Argument>();
for (String filename : FILES) {
MathCollection mathCollection = new MathCollection(knownArguments); // NOTE: knownArguments isn't used but does avoid null pointer exceptions: remove code weakness and remove replace with null.
boolean ok = mathCollection.readJar(filename);
if (!ok) {
Ket.out.println(" !!! Can't read from '" + filename + "' to search for identities !!! ");
continue;
}
Ket.out.println("Loading '" + filename + "'");
Vector<Equation> equations = mathCollection.getEquationList().getEquations();
for (Equation e : equations) {
Argument root = e.getRoot();
// if (root.getFunction()==Function.EQUALS) {
Argument[] pair = Like.equalPairLike(root);
if (pair!=null) { // ?=?
identities.add(root);
}
}
}
}
public boolean replacePrefixNotation(Chord chord) {
getClipboard().setDeletedArgument(getCurrent());
modes.setDocumentState(DocumentState.UPDATE_PREFIX);
chord.setComplete(false);
return true;
}
public boolean replaceCurrentArgument(Chord chord) {
getClipboard().setDeletedArgument(getCurrent());
modes.setDocumentState(DocumentState.UPDATE_REPLACE);
chord.setComplete(false);
return true;
}
public void halfSelection() {
NumberValue numberValue = getCurrent().getNumberValue();
if (numberValue==null) {
getSelection().replace(new Token(1));
} else {
State decreasedState = numberValue.half();
getCurrent().asToken().setState(decreasedState);
}
}
public void twiceSelection() {
NumberValue numberValue = getCurrent().getNumberValue();
if (numberValue==null) {
getSelection().replace(new Token(1));
} else {
State decreasedState = numberValue.twice();
getCurrent().asToken().setState(decreasedState);
}
}
public void incrementSelection() {
NumberValue numberValue = getCurrent().getNumberValue();
if (numberValue==null) {
getSelection().replace(new Token(1));
} else {
State decreasedState = numberValue.increase();
getCurrent().asToken().setState(decreasedState);
}
}
public void decrementSelection() {
NumberValue numberValue = getCurrent().getNumberValue();
if (numberValue==null) {
getSelection().replace(new Token(-1));
} else {
State decreasedState = numberValue.decrease();
getCurrent().asToken().setState(decreasedState);
}
}
public boolean zeroSelection() {
getSelection().replace(new Token(0));
return true;
}
public boolean cyclicallyPermuteLeft() {
if (getCurrentParent() instanceof Branch) {
((Branch) getCurrentParent()).cyclicallyPermuteLeft();
return true;
} else {
modes.error(" !!! Can't cyclically permute (left) the order of a non-branch !!! ");
return false;
}
}
public boolean cyclicallyPermuteRight() {
if (getCurrentParent() instanceof Branch) {
((Branch) getCurrentParent()).cyclicallyPermuteRight();
return true;
} else {
modes.error(" !!! Can't cyclically permute (right) the order of a non-branch !!! ");
return false;
}
}
public boolean substitutePurpose(Chord chord) {
if (mathCollection.isRangeSelectionSet()) {
modes.getAddMode().appendPurpose(chord);
return true;
}
getClipboard().setDeletedArgument(getCurrent());
chord.setComplete(false);
if (getCurrent() instanceof Branch) {
modes.setDocumentState(DocumentState.APPEND_PURPOSE);
return true;
} else if (getCurrent().isText()) {
modes.setDocumentState(DocumentState.UPDATE_TEXT);
return true;
} else {
// Simple version.
modes.setDocumentState(DocumentState.UPDATE_REPLACE); // Replace rather than append the state.
return true;
}
}
/**
* Return true if a change was made.
*/
public boolean toggleSubtraction() {
// TODO: Extend to multiple selections.
// TODO: Extend range so move up to ADD or MINUS or PM or MP?
Branch currentBranch = getCursor().asBranch();
if (Like.longSubtractionLike(currentBranch)) {
getSelection().replace(Transform.contractSubtraction(currentBranch));
return true;
} else if (Like.shortSubtractionLike(currentBranch)) {
getSelection().replace(Transform.expandSubtraction(currentBranch));
getSelection().flatten();
return true;
} else {
return false;
}
}
public boolean saveAndExit() {
// TODO: Ensure that any differences are saved.
Ket.out.println();
String name = getDocument().getFrameManager().getRawFilename();
if (name!=null) {
getDocument().getFrameManager().dispose();
} else {
Ket.out.println(" !!! No filename !!! ");
modes.error("No filename.");
}
return false;
}
class WindowOpener implements Runnable {
public WindowOpener() {
}
public void run() {
DocumentManager documentManager = getDocumentManager();
Document d = new Document(documentManager, null, null);
documentManager.addDocument(d);
}
}
public boolean openNewWindow() {
if (getDocument().getFrameManager().isFullScreen()) return false;
if (getDocumentManager()==null) return false;
SwingUtilities.invokeLater(new WindowOpener());
return true;
}
public void showAlbum() {
new Album(getDocument(), cycle);
}
public void cycleFamily(boolean forward) {
cycle.cycleFamily(forward, getSelection());
}
public void cycleRelation(boolean forward) {
cycle.cycleRelation(forward, getSelection());
}
public void DEBUG() {
for (int i=0; i<10; i++) {
Ket.out.println();
}
}
public boolean selectNextUnknown(boolean searchBackwards) {
int settings = ArgumentVector.INCLUDE_ROOT;
if (searchBackwards) {
settings |= ArgumentVector.REVERSE_ITERATOR_ORDER;
}
ArgumentVector av = new ArgumentVector(getCurrent().getVisibleRoot(), settings);
boolean seek = true;
for (Argument a : av) {
if (seek) { // i.e. Skip until after current.
if (a==getCurrent()) {
seek = false;
}
continue;
} // Afterwards:
//- Purpose purpose = a.getPurpose();
//- if (purpose==Function.UNKNOWN || Symbol.UNKNOWN==purpose) {
if (Like.isUnknown(a)) {
getSelection().setCurrent(a);
return true;
}
}
for (Argument a : av) {
if (a==getCurrent()) break; // i.e. Search until current.
//- Purpose purpose = a.getPurpose();
//- if (purpose==Function.UNKNOWN || Symbol.UNKNOWN==purpose) {
if (Like.isUnknown(a)) {
getSelection().setCurrent(a);
return true;
}
}
return false;
}
public boolean moveToNephew() {
Branch parentBranch = getCurrent().getParentBranch();
if (parentBranch==null) return false;
Argument sibling = getCurrent().getPreviousSibling();
if (sibling==null || sibling instanceof Token) return false;
Argument nephew = sibling.asBranch().firstChild();
if (nephew==null) return false;
mathCollection.setCursorSelection();
mathCollection.getDrag().moveToNephew(parentBranch, nephew, mathCollection.getKnownArguments());
return true;
}
public boolean invertInequality() {
Ket.out.println(" --- Invert inequality --- ");
Vector<Argument> roots = getSelection().getSubbranchRoots();
if (roots.size()>1) {
Ket.out.println("[multiple]");
boolean change = false;
EquationList el = getSelection().getEquationList();
for (Argument a : roots) { // WARNING: Some roots may be affected by previous edits
if (a.getEquationList()!=el) {
continue;
}
getSelection().setOnly(a);
change |= invertInequality();
}
return change;
}
Ket.out.println("[next "+getCurrent()+"]");
Vector<Branch> ancestors = getCurrent().getAncestors();
Ket.out.println("ancestors: "+ancestors);
if (ancestors.size()>0) {
Function f = ancestors.firstElement().getFunction();
Ket.out.println("f=" + f);
if (f==Function.ADD || f==Function.TIMES || f==Function.MINUS || f==Function.FRACTION) { // Too specific: use Function.*Type().
Ket.out.println("[relative]");
invertRelative(ancestors);
return true;
}
}
Argument parentBranch = getCurrent().getParentBranch();
Ket.out.println("parent branch="+ parentBranch);
if (parentBranch==null) {
Ket.out.println(" !!! invert inequality :: null parent branch !!! ");
return false;
}
Argument end = getCurrent().getParentBranch().getOnlySibling();
if (end==null) {
Ket.out.println("[null end]");
Argument otherEnd = getCurrent().getOnlySibling();
if (otherEnd==null || !otherEnd.isBranch()) return false;
Function otherFunction = otherEnd.getFunction();
if (Type.addType(otherFunction) || Type.timesType(otherFunction)) { // a+b=x -> a+b=x+0 -> (a+b)-x=0
Branch positive = getCurrent().addIntermediaryParent(Type.getPositiveCase(otherFunction));
positive.append(Type.getNullToken(otherFunction));
invertRelative(getCurrent().getAncestors());
return true; //?
} else {
Argument child = otherEnd.asBranch().firstChild();
otherEnd = getCurrent();
getCursor().setOnly(child);
return mathCollection.getDrag().invertEquality(otherEnd);
}
} else {
Ket.out.println("end=" + end);
if (! getCurrent().hasUncle(end)) return false;
mathCollection.setCursorSelection();
return mathCollection.getDrag().invertEquality(end);
}
}
/**
* Handle +, -, * and / inversion as a special case.
*/
private void invertRelative(Vector<Branch> ancestors) {
Branch common = null;
Argument old = getCurrent();
Argument end = null;
for (Branch ancestor : ancestors) {
// TODO: Generalize to include all inequalities (INEQUALITY_TYPE).
//- Argument[] args = Like.equalPairLike(ancestor);
Argument[] args = Like.equalityPairLike(ancestor);
if ( args != null ) {
common = ancestor;
if (args[0]==old) {
end = args[1];
break;
} else if (args[1]==old) {
end = args[0];
break;
}
}
old = ancestor;
}
if (end!=null) {
mathCollection.getDrag().relatedAlgebra(common, end);
}
}
public boolean togglePurpose() { // 'Q'
KnownArguments knownArguments = mathCollection.getKnownArguments();
return getSelection().togglePurpose(knownArguments);
}
public boolean swapBetweenTokenAndBranch() {
KnownArguments knownArguments = mathCollection.getKnownArguments();
getSelection().togglePurpose(knownArguments);
return true;
}
public boolean map() {
Ket.out.println(" --- Map --- ");
Branch branch = getCurrent().asBranch();
Branch parentBranch = getCurrent().getParentBranch();
if (parentBranch==null || branch==null) return false;
Function function = branch.getFunction();
Ket.out.println("\tfunction = " + function);
for (Argument c : parentBranch.getChildren()) {
if (c==branch) continue;
setCurrent(c);
Branch result = getSelection().addIntermediaryParent(function);
}
setCurrent(branch);
Ket.out.println("\tparent = " + parentBranch);
return true;
}
public boolean substituteUpdate(Chord chord) {
getClipboard().setDeletedArgument(getCursor().getRoot());
modes.setDocumentState(DocumentState.UPDATE_SUBSTITUTE);
chord.setComplete(false);
return true;
}
/**
* Convert a given integer into its power of ten multiple by a list of
* powers of its factors.
*/
public boolean factorizeIntegers() {
Integer value = Like.getInteger(getCurrent());
if (value==null) {
Ket.out.println(" !!! Cannot factorize a non-integer !!! ");
return false;
}
Argument factorizedInteger = Factorize.factorize(value.intValue(), false);
if (factorizedInteger==null) {
Ket.out.println(" !!! Can't factorize "+value+" !!! ");
return false;
}
getSelection().getCurrent().replace(factorizedInteger);
setCurrent(factorizedInteger);
return true;
}
public void equateEvaluate() { // Convert 3*2 into 3*2 -> 6
Argument a = getCurrent();
Argument clone = a.cloneArgument();
Branch to = a.addIntermediaryParent(Function.TO);
to.append(clone);
Selection s = getSelection();
ArgumentVector av = new ArgumentVector(clone, ArgumentVector.INCLUDE_ROOT);
//?+ Collections.reverse(av);
for (Argument next : av.toBranchVector()) { // 3/4 -> 0.75
Argument frac = Transform.integerDivideFraction(next);
if (frac==null) {
frac = Transform.divideFraction(next);
}
if (frac!=null) {
s.replace(next, frac);
}
}
s.setCurrent(to.lastChild());
getSelection().evaluate();
}
public long pow10(int exponent) {
long p = 1L;
for (int i=0; i<exponent; i++) {
p *= 10;
}
return p;
}
public void setPrecision(Chord chord) {
if (chord.countChanged()) {
chord.setLoopSkipped(true);
}
Double value = Like.getNumber(getCurrent());
if (value==null) return;
int counts = chord.getCounts();
if (counts<0) return;
long p = pow10(counts);
value = Math.floor(value * p);
value /= p;
getSelection().replace(new Token(value));
}
public boolean replaceAllMatches() {
Ket.out.println(" --- replace all matches --- ");
boolean changed = false;
Argument before = getCurrent();
EquationList el = getCursor().getEquationList();
for (Argument a : new ArgumentVector(getCurrent(), ArgumentVector.INCLUDE_ROOT)) {
Ket.out.println("next = " + a);
if (a.getState() instanceof NumberValue) continue;
Ket.out.println("[non-number]");
if (a.getEquationList()!=el) continue;
Ket.out.println("[not removed]");
changed |= replaceWithMatches(a);
Ket.out.println("result: " + before);
}
if (before.getEquationList()==el) {
getSelection().setCurrent(before);
}
return changed;
}
public boolean replaceWithMatches(Argument target) {
getSelection().setCurrent(target);
EquationList el = getCursor().getEquationList();
Vector<Argument> matches = new Vector<Argument>();
//- for (Equation e : el.getEquations()) { // Search all other equations for "<target>=<anything>".
for (int i=0; i<getCursor().getEquationIndex(); i++) { // Search all previous equations.
Equation e = el.getEquation(i);
if (e==target.getEquation()) continue;
Argument root = e.getVisibleRoot();
Vector<Argument> args = new ArgumentVector(root, ArgumentVector.INCLUDE_ROOT);
for (Argument assignment : args) {
Argument q = Like.assignmentLike(target, assignment);
if (q!=null) {
matches.add(q.cloneArgument());
}
}
}
if (matches.isEmpty()) return false;
// if current equals
Ket.out.println(" --- replace with matches ("+target+") --- ");
Ket.out.println("matches = " + matches);
Ket.out.println("current = " + getCurrent());
for (int i=matches.size()-1; i>=0; i--) { // Remove duplicates.
Argument m = matches.get(i);
for (int j=0; j<i; j++) {
Argument n = matches.get(j);
if (m.equals(n)) {
matches.remove(i);
break;
}
}
}
Branch currentParent = getCurrent().getParentBranch();
Ket.out.println("matches' = " + matches);
if (currentParent!=null && Type.assignType(currentParent)) {
// Avoid recursively substituting the previous copy of the current equation.
Ket.out.println("current parent = " + currentParent);
for (int i=matches.size()-1; i>=0; i--) { // Look through current subject's siblings and exclude matches that it is equated to.
Argument m = matches.get(i);
Ket.out.println("(match "+i+") = "+m);
for (Argument child : currentParent.getChildren()) {
Ket.out.println("\tchild:" + child);
if (m.equals(child)) {
Ket.out.println("\t\t[EQUALS]");
matches.remove(i);
break;
}
}
}
Ket.out.println("matches'' = " + matches);
}
switch (matches.size()) { // TODO: Simplify
case 0:
return false;
/*-
case 1:
getSelection().replace(matches.firstElement());
return true;
*/
default:
//- getSelection().replace(new Branch(Function.VECTOR, matches));
getSelection().replace(matches.lastElement());
return true;
}
}
// --------
public boolean variablesToList() {
Ket.out.println("[perturb as flat]");
Branch root = getCursor().getRoot().asBranch();
if (root==null) return false;
if (Like.hasForm(root, Function.TO, 2)) { // f(x)+y^z -> f x plus power y z
Ket.out.println("[Contract]");
Branch source = root.firstChild().asBranch();
Branch destination = root.lastChild().asBranch();
source.remove();
getSelection().setCurrent(root);
getSelection().deleteIntermediate();
if (destination==null || source==null) return false;
//- Vector<Argument> src = new ArgumentVector(source, 0);
Vector<Argument> src = source.getChildren();
Vector<Argument> dest = new ArgumentVector(destination, 0);
for (int i=0,j=0; i<src.size() && j<dest.size(); j++) {
Argument s = src.get(i);
Argument d = dest.get(j);
if (d.isBranch() && d.size()>=1) {
// ?
} else {
d.replace(s);
i++;
}
}
} else {
Ket.out.println("[Expand]");
getSelection().setCurrent(root);
Branch to = getSelection().addIntermediaryParent(Function.TO);
Branch flat = new Branch(Function.COMMA);
to.prepend(flat);
Vector<Argument> av = new ArgumentVector(root, 0); //- ArgumentVector.INCLUDE_ROOT
for (Argument next : av) {
Argument clone;
if (next.isBranch() && next.size()>=1) {
//- clone = new Token(new Word(next.getFunction().getName()));
continue;
} else {
clone = Argument.cloneArgument(next);
}
flat.append(clone);
}
getSelection().setCurrent(flat.firstChild());
}
return true;
}
// --------
////////////////////////////////////////////////////////////////////////////////
////////////////////////// SHORTCUT ACCESSOR METHODS ///////////////////////////
////////////////////////////////////////////////////////////////////////////////
private void setOnly(Argument current) {
getSelection().setOnly(current);
}
public Argument getCurrent() {
return getCursor().getCurrent();
}
private void setCurrent(Argument current) {
getCursor().setCurrent(current);
}
private Cursor getCursor() {
return mathCollection.getCursor();
}
private Parent getCurrentParent() {
return getCurrent().getParent();
}
public Document getDocument() {
return modes.getDocument();
}
public KetPanel getKetPanel() {
return modes.getDocument().getKetPanel();
}
public EquationList getEquationList() {
return getSelection().getEquationList();
}
private Selection getSelection() {
return mathCollection.getSelection();
}
private Clipboard getClipboard() {
return modes.getClipboard();
}
private DocumentManager getDocumentManager() {
return modes.getDocumentManager();
}
private DeleteMode getDeleteMode() {
return modes.getDeleteMode();
}
}