Package ket.math

Source Code of ket.math.Argument

/*
* 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 java.awt.*;

import static ket.math.ArgumentVector.INCLUDE_ROOT;
import static ket.math.ArgumentVector.REVERSE_SIBLING_ORDER;
import static ket.math.ArgumentVector.EXCLUDE_HIDDEN_CHILDREN;

import ket.display.*;
import ket.display.box.Box;
import ket.display.box.BoxWord;
import ket.math.convert.Like;
import ket.math.purpose.NumberValue;
import ket.math.purpose.Text;
import ket.math.purpose.VariableToken;
import ket.math.purpose.Word;
import ketUI.Ket;

/**
* Provide common methods to equations and their contents (ie branches and tokens).
*/
public abstract class Argument extends Relative implements Displayable, Comparable<Argument> {


  public static Argument PAD_ARG = new Token(new Word("padArg"));

  /**
   * Every argument has a parent unless it is the root of a tree.
   */
  Parent parent;
 
  /**
   * Sub-branches can be hidden to reduce the complexity of large equations.
   */
  boolean visible;

  /**
   * If true this will be drawn in bold.
   */
  boolean bold;

  /**
   * Return a copy of the given argument.  Different methods are used for
   * different objects.  This is an Argument specific specialization
   * similar in implementation to the object.clone() method.  The
   * difference is that all sub classes are explicitly listed in this
   * method.  This is to keep all associate information in one place, but
   * does require updating as each new child class is added.
   */
  public static Argument cloneArgument(Argument argument) {
    if (argument==null) {
      return null;
    } else if (argument instanceof VariableToken) {
      return ((VariableToken) argument).cloneVariableToken();
    } else if (argument instanceof Token) {
      return ((Token) argument).cloneToken();
    } else if (argument instanceof Branch) {
      return ((Branch) argument).cloneBranch();
    } else {
      throw new RuntimeException(
          "Argument.cloneArgument(): unfinished code: passed " +
          argument.getClass());
    }
  }

  public Argument cloneArgument() {
    return Argument.cloneArgument(this);      
  }

  public void TEST__showParentChildren() { //d
    Branch p = getParentBranch();
    if (p==null) {
      Ket.out.println("<null>");
      return;
    }
    Ket.out.print(" {pc}:"+p.size()+": ");
    for (Argument child : p.getChildren()) {
      Ket.out.print("<" + (child==this?".":(child.getParent()==p?"y":"n")) + ">");
    }
    Ket.out.println();
  }

  /**
   * Construct an equation with a given parent.
   */
  public Argument() {
    this.parent = null;
    this.visible = true;
  }

  //////////////
  // ABSTRACT //
  //////////////

  public abstract int recursiveSize();

  /**
   * Includes tokens as well as hidden or childless branches.
   */
  public abstract boolean isLeaf();

  /**
   * If an argument is branch[-](argument) then return argument,
   * otherwise return null.
   */
  public abstract Argument getNegativeFunctionArg();

  /**
   * Check if this argument matches the given string pattern.
   */
  public abstract boolean matches(String pattern);

  /**
   * Ensure that each subclass returns a meaningful value.
   */
  public abstract int hashCode();

  public boolean equals(Object o) {
    if ( ! (o instanceof Argument) ) {
      return false;
    }
    return subBranchEquals( (Argument) o );
  }

  /**
   * Check that this object equals the given argument, including any
   * child components.
   */
  public abstract boolean subBranchEquals(Argument argument);

  /**
   * Check that this object equals the given argument, regardless of any
   * child components.
   */
  public abstract boolean elementEquals(Argument argument);

  /**
   * Check if the state of a given argument is equal to value.
   */
  public abstract boolean elementEquals(double value);

  /**
   * Move recursively through all corresponding sub-branches and return
   * those child arguments that do not match.  Correspondance requires
   * both element equality and a common number of child args.
   */
  public IdentityHashMap<Argument,Argument> cypher(Argument b) { // BEWARE: Map is uncloned and so edits to 'this' or 'b' mutate its elements.
    IdentityHashMap<Argument,Argument> im = new IdentityHashMap<Argument,Argument>();
    cypherRecur(this, b, im);
    return im;
  }

  /**
   * Given a pair of arguments, recursively check for tokens in a have
   * corresponding sub-branches within b.  For example<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;sin(x) = y
   * and<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;sin(f(u)) = integral(ln(x), x)
   * returns<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;{"x":"f(u)", "y":"integral(ln(x), x)"}.
   */
  public IdentityHashMap<Token, Token> cypherEquals(Argument b) {
    IdentityHashMap<Argument, Argument> c = this.cypher(b);
    IdentityHashMap<Token, Token> t = new IdentityHashMap<Token, Token>();
    for (Argument k : c.keySet() ) {
      if (k instanceof Token && c.get(k) instanceof Token) {
        t.put( (Token) k, (Token) c.get(k));
      } else {
        return null;
      }
    }
    boolean duplicateKeys = unique(t.keySet());
    boolean duplicateValues = unique(t.values());
    if (duplicateKeys ^ duplicateValues) {
      return null;
    }
    // TODO: what happens to duplicates, duplicateKeys && duplicateValues
    return t;
  }

  private static boolean unique(Collection<Token> c) {
    for (Token u : c) {
      int count = 0;
      for (Token v : c) {
        if (u.subBranchEquals(v)) {
          count += 1;
        }
      }
      if (count!=1) {
        return false;
      }
    }
    return true;
  }

  /**
   * Recursively sift through the sub-branches of descendent and root while they match one another and otherwise recording their correspondence in the map.
   */
  private static void cypherRecur(Argument descendent, Argument root, IdentityHashMap<Argument, Argument> im) { // Where should this method be?
    Address a = descendent.getAddress();
    Argument r = root.relativeAddress(a);
    if (descendent.elementEquals(r)) {
      if (descendent instanceof Token) {
        // Both tokens are equal.
        // TODO: Don't bother recording 3=3, but x=x or c_0 = c_0 may be useful?
        return;
      }
      // Both tokens are branches.
      Branch dBranch = (Branch) descendent;
      Branch rBranch = (Branch) r;
      if (dBranch.size()==rBranch.size()) {
        // Both descendents are [element] equal and have the same number of arguments.
        for (Argument child : dBranch.getChildren()) {
          cypherRecur(child, root, im);
        }
        return;
      }
      // Otherwise keep going.
    }
    // Otherwise record the correspondance.
    im.put(descendent, r);
  }


  /**
   * Child classes may contain symbols, and these may be gathered using
   * this method.
   */
  public abstract State getState();

  /**
   * Some arguments (branches) have functions associated with them, this
   * method returns null otherwise.
   */
  public abstract Function getFunction();

  /**
   * Value is a special case of a token's state, which this method
   * returns (or null otherwise).
   */
  public abstract String getValue();

  public abstract int size();



  ////////////////
  // COMPARABLE //
  ////////////////

  @Override
  public int compareTo(Argument that) {
    if (that==null) {
      return 1;
    }

    // Start with numbers
    boolean thisState = this.getPurpose() instanceof NumberValue;
    boolean thatState = that.getPurpose() instanceof NumberValue;
    if (thisState && !thatState) {
      return -1;
    }
    if (!thisState && thatState) {
      return +1;
    }

    // End with square roots
    boolean thisSqrt = this.getPurpose()==Function.SQRT;
    boolean thatSqrt = that.getPurpose()==Function.SQRT;
    if (thisSqrt && !thatSqrt) {
      return 1;
    }
    if (!thisSqrt && thatSqrt) {
      return -1;
    }

    return this.toString().compareTo(that.toString());
  }


  ///////////
  // OTHER //
  ///////////

  public void toggleBold() {
    bold = ! bold;
  }

  public void setBold(boolean bold) {
    this.bold = bold;
  }

  public boolean isBold() {
    return bold;
  }


  ////////////////
  // VISIBILITY //
  ////////////////

  /**
   * Determine whether this argument is to be displayed.
   */
  public boolean isVisible() {
    return visible;
  }

  /**
   * Set the visibility of this argument.
   */
  public void setVisible(boolean visible) {
    this.visible = visible;
  }


  //////////////
  // RELATIVE //
  //////////////

  protected Argument asArgument() {
    return this;
  }

  /**
   * Return the argument's parent, that is the branch or equation that
   * contains it.
   */
  @Override
  public Parent getParent() {
    return parent;
  }

  /**
   * Associate a parent with this branch, but only change this argument
   * and not it's parent.
   */
  public void setParent(Parent parent) {
    this.parent = parent;
  }


  /////////////////////
  // TRANSFORMATIONS //
  /////////////////////

  public abstract String toEditString();
  public abstract void removeIntermediate();

  /**
   * This argument is linked (bidirectionally to its parent, both links
   * of which are severed.
   */
  public void remove() {
    if (parent==null) {
      return;
    } else if (this.isRoot()) {
      getEquation().setRoot(new Token(Symbol.UNKNOWN));
    } else {
      getParentBranch().removeChild(this.getIndex());
    }
  }

  public void replaceIntermediate(Argument replacement) {
    // If this and the replacement are branches, then arguments are copied over.
    this.replace(replacement);
  }

  /**
   * Replace this argument with an alternative replacement argument.
   */
  public void replace(Argument replacement) {
    if (this==replacement) {
      Ket.out.println(" !!! Argument::replace(): Can't replace self !!! ");
    } else if (this.isRoot()) {
      //- ((Equation) parent).setRoot(replacement);
      getEquation().setRoot(replacement);
    } else {
      //- ((Branch) parent).replaceArgument(this, replacement);
      getParentBranch().replaceArgument(this, replacement);
    }
  }

  /**
   * Move this argument from being linked to its current parent to the
   * parent of that branch.
   */
  public boolean deleteParent() {
    Branch parentBranch = getParentBranch();
    if (parentBranch==null) {
      Ket.out.println(" !!! Can't delete the parent of a null branch !!! ");
      return false;
    }
    Branch grandparent = parentBranch.getParentBranch();
    if (grandparent!=null) {
      int index = grandparent.getIndex(parentBranch);
      grandparent.removeChild(index);
      this.remove();
      if (grandparent.size()==0) {
        grandparent.append(this);
      } else {
        if (index==0) {
          grandparent.prepend(this);
        } else {
          grandparent.addAfter(index-1, this);
        }
      }
    } else {
      getEquation().setRoot(this);
    }
    return true;
  }

  public Branch addIntermediaryParent(Function function) {
    Branch intermediaryBranch = new Branch(function);
    if (this.isRoot()) {
      Equation currentEquation = this.getEquation();
      intermediaryBranch.append(this);
      currentEquation.setRoot(intermediaryBranch);
    } else {
      Branch oldParent = (Branch) parent;
      oldParent.replaceArgument(this, intermediaryBranch);
      intermediaryBranch.append(this);
    }
    return intermediaryBranch;
  }

  /**
   * Inserts the given branch inbetween the current argument and its
   * current parent.  That is, it transforms
   *  oldParent[this]
   * into
   *  oldParent[newParent[this]].
   */
  public void addIntermediaryParent(Branch intermediaryBranch, boolean prepend) {
    if (this.isRoot()) {
      Equation currentEquation = this.getEquation();
      if (prepend) {
        intermediaryBranch.prepend(this);
      } else {
        intermediaryBranch.append(this);
      }
      currentEquation.setRoot(intermediaryBranch);
    } else {
      Branch oldParent = (Branch) parent;
      oldParent.replaceArgument(this, intermediaryBranch);
      if (prepend) {
        intermediaryBranch.prepend(this);
      } else {
        intermediaryBranch.append(this);
      }
    }
  }

  /////////////
  // DISPLAY //
  /////////////

  /**
   * Record this as lisp-like nested brackets.
   */
  public abstract String toPrefixNotation();

  /**
   * The default appearance of an argument is as having been hidden.
   */
  @Override
  public Box toBox(long settings, ColourScheme colourScheme) {
    if (isBold()) { //?
      settings |= Box.BOLD_FONT;
    }
    //- Box box = new BoxWord(this, Symbol.HIDDEN.toUnicode(), settings, colourScheme.getBoxColour(this));
    Box box = new BoxWord(this, Symbol.HIDDEN.toUnicode(), settings);
    box.setArgument(this); //? Redundant?
    return box;
  }

  /**
   * Return true if any element of vector is ientically equal to this argument.
   */
  public boolean isIn(Vector vector) { // TODO: Move to relative?
    for (Object element : vector) {
      if (this==element) {
        return true;
      }
    }
    return false;
  }

  /**
   * Find the index of the vector identically equal to the this argument.
   */
  public int indexIn(Vector vector) { // TODO: Move to relative?
    for (int i=0; i<vector.size(); i++) {
      if (this==vector.get(i)) {
        return i;
      }
    }
    return -1;
  }
}

TOP

Related Classes of ket.math.Argument

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.