Package ket.math

Source Code of ket.math.Branch

/*
* 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.Color; // TODO: Remove toBox dependence on awt?

import java.util.*;

import ket.display.*;
import ket.display.box.Box;
import ket.display.box.BoxTools;
import ket.math.convert.Like;
import ket.math.purpose.IntegerValue;
import ket.math.purpose.NumberValue;
import ket.math.purpose.SymbolicState;
import ket.math.purpose.VariableState;
import ket.math.purpose.VariableToken;
import ket.math.purpose.Word;

/*
* A branch represents any collection of arguments that undergo a mapping.  It
* also serves as a container class to store other child arguments. 
*/
public class Branch extends Argument implements Parent {

  /**
   * The mapping is described by an instance of Function while null
   * functions imply an unknown type of mapping.
   */
  Function function;

  /**
   * A list of arguments that undergo the mapping.
   */
  Vector<Argument> args;



  //////////////////
  // CONSTRUCTORS //
  //////////////////

  public Branch() {
    super();
    function = null;
    args = new Vector<Argument>();
  }

  public Branch(Function function) {
    super();
    this.function = function;
    args = new Vector<Argument>();
  }

  public Branch(Function function, Argument[] args) {
    super();
    this.function = function;
    this.args = new Vector<Argument>();
    for (Argument child : args) {
      append(child);
    }
  }

  public Branch(Function function, Vector<Argument> args) {
    super();
    this.function = function;
    this.args = new Vector<Argument>();
    for (Argument child : args) {
      append(child);
    }
  }

  public Branch(Function function, Argument first) {
    this(function);
    append(first);
  }

  public Branch(Function function, Argument first, Argument last) {
    this(function);
    append(first);
    append(last);
  }


  ///////////////////
  // OTHER METHODS //
  ///////////////////
 
  ///////////////////////
  // LOW LEVEL METHODS //
  ///////////////////////

  @Override
  public int size() {
    return args.size();
  }

  @Override
  public int recursiveSize() {
    int total = 0;
    for (Argument a : args) {
      total += a.recursiveSize();
    }
    return total;
  }

  @Override
  public String getValue() { // Used?
    return null;
  }

  @Override
  public Function getFunction() {
    return function;
  }

  @Override
  public State getState() {
    return null;
  }

  public void setFunction(Function function) {
    this.function = function;
  }

  public Argument lastChild() {
    return size()>0 ? args.lastElement() : null;
  }

  public Argument firstChild() {
    return size()>0 ? args.firstElement() : null;
  }

  /**
   * Return the index (counting from zero) of a given child, or -1 if it
   * is not found.
   */
  public int getIndex(Argument argument) { // TODO: Rename to getChildIndex() to avoid ambiguity with super.super.getIndex().
    for (int i=0; i<args.size(); i++) {
      if (argument==args.get(i)) {
        return i;
      }
    }
    return -1;
  }

  public void reverseChildren() {
    Collections.reverse(args);
  }
 
  public Branch cloneBranch() {
    Branch branch = new Branch(this.getFunction())
    for (int i=0; i<args.size(); i++) {
      Argument argument = Argument.cloneArgument(args.get(i));
      branch.append(argument);
    }
    if (isBold()) {
      branch.setBold(true);
    }
    return branch;
  }

  public Argument getChild(int index) {
    if (size()==0) {
      return null;
    } else if (index<0) {
      return args.get((size()-index)%size());
    } else {
      return size()>0 ? args.get(index%size()) : null;
    }
  }

  public Branch getChildBranch(int index) {
    Argument child = getChild(index);
    return child instanceof Branch ? (Branch) child : null;
  }

  /**
   * Return a shallow copy of the functions arguments.
   */
  public Vector<Argument> getChildren() {
    return new Vector<Argument>(args);
  }


  /////////////////
  // RE-ORGANIZE //
  /////////////////

  public void cyclicallyPermuteLeft() {
    if (args.size()<=1) {
      return;
    }
    Argument argument = args.remove(0);
    args.add(argument);
  }

  public void cyclicallyPermuteRight() {
    if (args.size()<=1) {
      return;
    }
    Argument argument = args.remove(args.size()-1);
    args.add(0, argument);
  }

  public void invert() {
    if (function==null) {
      return;
    }
    //- Function inverse = function.getInverse();
    Function inverse = Type.getInverse(function);
    if (inverse!=null) {
      setFunction(inverse);
    }
  }

  /**
   * Remove this branch from its parent replacing it with with its
   * children in the same order.
   */
  @Override
  public void removeIntermediate() {
    if (isRoot()) {
      getEquation().setRoot(firstChild());
    } else {
      Branch parentBranch = getParentBranch();
      Collections.reverse(args);
      for (Argument child : getChildren()) { // Shallow copy.
        this.removeChild(child.getIndex());
        int index = this.getIndex();
        parentBranch.addAfter(index, child);
      }
      this.remove();
    }
  }


  /////////////////
  // ADD METHODS //
  /////////////////

  /**
   * Append an argument after a given index in the argument list.
   */
  public void addAfter(int index, Argument argument) {
    argument.remove();
    args.add(index+1, argument);
    argument.setParent(this);
  }

  public void prepend(Argument argument) {
    argument.remove();
    args.add(0, argument);
    argument.setParent(this);
  }

  public void append(Argument argument) {
    argument.remove();
    args.add(argument);
    argument.setParent(this);
  }

  public void appendAll(Vector<Argument> args) {
    for (Argument a : args) {
      append(a);
    }
  }

  public void prependAll(Vector<Argument> args) {
    Vector<Argument> r = new Vector<Argument>(args);
    Collections.reverse(r);
    for (Argument a : r) {
      prepend(a);
    }
  }

  ////////////////////
  // REMOVE METHODS //
  ////////////////////

  public void removeChild(int index) {
    Argument argument = args.remove(index);
    argument.setParent(null);
  }

  public void empty() {
    while (args.size()>0) {
      removeChild(0);
    }
  }



  ////////////////////
  // SWAP ARGUMENTS //
  ////////////////////

  public boolean swapArgumentLeft(Argument argument) {
    Argument previous = argument.getPreviousSibling();
    if (previous==null) {
      previous = args.lastElement();
    }
    return swapArguments(argument, previous);
  }

  public boolean swapArgumentRight(Argument argument) {
    Argument next = argument.getNextSibling();
    if (next==null) {
      next = args.firstElement();
    }
    return swapArguments(argument, next);
  }


  ///////////////////
  // NUMBER ACCESS //
  ///////////////////

  /**
   * Extract double values of all arguments and return them in a double
   * array if possible and null if any arguments are not instances of
   * number value.
   */
  public double[] argsToDoubleArray() {
    if (size()==0) {
      return null;
    }
    for (Argument argument : args) {
      State state = argument.getState();
      if ( ! (state instanceof NumberValue)) {
        return null;
      }
    }
    double[] copy = new double[size()];
    int i=0;
    for (Argument argument : args) {
      State state = argument.getState();
      NumberValue numberValue = (NumberValue) state;
      copy[i++] = numberValue.getDouble();
    }
    return copy;
  }

  /**
   * Extract integer values from all arguments into an array and return
   * them if possible and null if any arguments are not instances of
   * integer value.
   */
  public int[] argsToIntArray() {
    if (size()==0) {
      return null;
    }
    for (Argument argument : args) {
      State state = argument.getState();
      if ( ! (state instanceof IntegerValue)) {
        return null;
      }
    }
    int[] copy = new int[size()];
    int i=0;
    for (Argument argument : args) {
      State state = argument.getState();
      IntegerValue integerValue = (IntegerValue) state;
      copy[i++] = integerValue.getInt();
    }
    return copy;
  }


  ////////////////////////////
  //     OUTPUT FORMATS     //
  ////////////////////////////

  /**
   * Determine whether the current branch needs to be displayed with
   * brackets around it for clarity.
   */
  public boolean hasBracket(boolean strict) {
    Function parentFunction = getParentFunction();
    if (areBracketsExcluded(parentFunction, strict)) {
      return false;
    }
    if (areBracketsRequired(parentFunction)) {
      return true;
    }
    return function.getPrecedence() < parentFunction.getPrecedence();  
  }

  private boolean areBracketsExcluded(Function parentFunction, boolean strict) {
    boolean excludesBrackets = this.isRoot();
    excludesBrackets |= hasPrefixAndPostfix();
    excludesBrackets |= parentFunction==null || parentFunction.displayBrackets();
    return excludesBrackets;
  }

  private boolean areBracketsRequired(Function parentFunction) {
    boolean requiresBrackets = function==null;
    requiresBrackets |= Like.nestedFractionShape(parentFunction, function);
    requiresBrackets |= Like.nestedMinusLike(parentFunction, function);
    return requiresBrackets;
  }

  /**
   * Eg |x> or |x| like operations are self bracketing.
   */
  private boolean hasPrefixAndPostfix() {
    if ( ! (function instanceof SymbolicFunction) ) {
      return false;
    }
    SymbolicFunction symbolicFunction = (SymbolicFunction) function;
    return symbolicFunction.isPrefix() && symbolicFunction.isPostfix();
  }

  @Override
  public String toHTML() {
    String html;
    if (getFunction()==null) {
      html = Symbol.UNKNOWN.toHTML();
      html += Symbol.COLON.toHTML();
      html += Function.BRACKET.toHTML(args);
    } else {
      html = function.toHTML(args);
    }
    return hasBracket(true) ? "("+html+")" : html;
  }

  @Override
  public Box toBox(long settings, ColourScheme colourScheme) {
    if ( ! isVisible() ) {
      return super.toBox(settings, colourScheme);
    }
    // Only include scaling information to the argument of
    // a branch if scaling isn't already done by brackets.
    Function nonNullFunction = getFunction()==null ? Function.UNKNOWN : function;
    long innerSettings = isBold() ? Box.BOLD_FONT : 0L;
    boolean noBracketFlag = (Box.SUPPRESS_BRACKETS&settings)==Box.SUPPRESS_BRACKETS;
    //- Color colour = colourScheme.getBoxColour(this);
    if (!noBracketFlag && hasBracket(false)) {
      // Note: Only pass RELATIVE_FONT_FLAGS once (in bracket).
      Box box = nonNullFunction.toBox(this, innerSettings, colourScheme, args);
      box.setArgument(this);
      Box bracketedBox = BoxTools.roundBrackets(this, box, settings, colourScheme);
      bracketedBox.setProperty(Box.PRESERVE_ASPECT_RATIO|Box.VERTICAL_STRETCH);
      bracketedBox.setArgument(this);
      return bracketedBox;
    } else {
      Box box = nonNullFunction.toBox(this, settings|innerSettings, colourScheme, args);
      box.setArgument(this); //? Redundant
      return box;
    }
  }

  public Function getNonNullFunction() {
    return getFunction()!=null ? function : Function.UNKNOWN;
  }
 
  @Override
  public String toPrefixNotation() {
    if (function==Function.VECTOR) { // Special case (let [x,3] (println x)) rather than (let (vector x e) (println x)).
      String notation = "[";
      String gap = "";
      for (Argument c : args) {
        notation += gap + c.toPrefixNotation();
        gap = " ";
      }
      return notation + "]";
    }
    String notation = "(";
    if (function==Function.EQUALS  ||
        function==Function.ADD || function==Function.MINUS ||
        function==Function.TIMES || function==Function.FRACTION ||
        function==Function.LESS_EQUALS || function==Function.LESS_THAN ||
        function==Function.GREATER_EQUALS || function==Function.GREATER_THAN) {
      SymbolicState infix = ((SymbolicFunction) function).getInfix();
      notation += infix.getName(); //i.e. +, -, * or /. 
    } else if (function==null) {
      notation += Function.UNKNOWN.getName();
    } else {
      notation += function.getName();
    }
    for (Argument c : args) {
      notation += " " + c.toPrefixNotation();
    }
    return notation + ")";
  }

  @Override
  /**
   * The same as toString() except that there are never outer brackets around this argument.
   */
  public String toEditString() {
    String text = getNonNullFunction().toString(args);
    return TextTools.removeWhitespace(text);
  }

  @Override
  public String toString() {
    String edit = toEditString();
    return hasBracket(true) ? "("+edit+")" : edit;
  }

  public String getName() {
    return getFunction()==null ? "<null>" : function.getName();
  }

  @Override
  public String toVerboseString() {
    String name = this.getName();
    String argsString = args.toString();
    return "branch"+(function!=null?"'"+name+"'":"") + argsString;
  }

  @Override
  public String toLatex() {
    String string;
    if (function==null) { 
      string = "\\invdiameter{null} \\! \\left(";
      for (int i=0; i<args.size(); i++) {
        if (i!=0) {
          string += ", ";
        }
        string += args.get(i).toLatex();
      }
      string += "\\right)";
    } else {
      string = function.toLatex(args);
      if (hasBracket(false)) {
        string = "\\left(" + string + "\\right)"; // Backslash-bang is negative spacing!
      }
    }
    return "{" + string + "}";
  }


  /////////////////////////////
  // EXTRACTING SUB-BRANCHES //
  /////////////////////////////

  public void subBranchBefore(int index, Function function) {
    subBranchBetween(-1, index, function);
  }

  /**
   * Remove all arguments after index and add them to a new branch which
   * is returned.
   */
  public void subBranchAfter(int index, Function function) {
    subBranchBetween(index, args.size(), function);
  }

  public void subBranchBetween(int from, int to, Function function) {
    int indices[] = new int[]{from, to};
    subBranchBetween(indices, function);
  }

  /**
   * The in-between indices are moved to a new branch of given function
   * while the indices themselves are removed.  Divide argument list into
   * a series of sub branches, each defined by
   *     indices[i]+1 ... indices [i+1]-1
   */
  public void subBranchBetween(int indices[], Function function) {
    Branch branch = new Branch(function);
    for (int i=indices.length-1; i>0; i--) {
      Branch child = new Branch();
      int firstIndex = indices[i-1]+1;
      for (int j=firstIndex; j<indices[i]; j++) {
        Argument argument = args.get(firstIndex);
        this.removeChild(firstIndex);
        child.append(argument);
      }
      if (i!=1) {
        this.removeChild(indices[i-1]);
      }
      if (child.size()==1) {
        branch.prepend(child.firstChild());
      } else if (child.size()>1) {
        branch.prepend(child);
      }
    }
    this.addAfter(indices[0], branch);
  }


  ////////////////
  // COMPARISON //
  ////////////////

  /**
   * Check if the state of a given argument is equal to value.
   */
  @Override
  public boolean elementEquals(double value) {
    return false;
  }

  /**
   * Check that this object equals the given argument, regardless of any
   * child components.
   */
  @Override
  public boolean elementEquals(Argument argument) {
    if (argument==null) {
      return false;
    }
    return function==argument.getFunction();
  }

  /**
   * Return true if each child elementEquals the corresponding other branch's argument.
   */
  public boolean childElementsEqual(Branch branch) { //-
    if (this.size()==branch.size()) {
      boolean childrenEqual = true;
      for (int i=0; i<this.size(); i++) {
        if ( ! this.getChild(i).elementEquals(branch.getChild(i)) ) {
          childrenEqual = false;
        }
      }
      return childrenEqual;
    } else {
      return false;
    }
  }

  /**
   * This follows the same model of AbstractList of multiplying each
   * child by 31 and treating nulls as zero.  This approach is also nested: being applied to all child branches as well.
   */
  @Override
  public int hashCode() {
    int functionHash = function==null ? 0 : function.hashCode();
    // Note the args vector automatically performs a weighted sum of each argument and multiplies by 31.
    return functionHash + 31*args.hashCode();
  }

  @Override
  public boolean matches(String pattern) {
    // Note: While there is a function.matches(pattern) method, note
    // that function can be null and arguments should be handled
    // here.
    return getName().equals(pattern);
  }

  /*-
  @Override
  public boolean equals(Object o) {
    if ( ! (o instanceof Argument) ) { // Should really just be branches.
      return false;
    }
    return subBranchEquals( (Argument) o );
  }
  */

  /**
   * Check if this branch and its children are of equal size, function
   * type and children.
   */
  @Override
  public boolean subBranchEquals(Argument argument) {
    if ( ! (argument instanceof Branch) ) {
      return false;
    }
    Branch branch = (Branch) argument;
    if (function!=branch.getFunction()) {
      return false;
    }
    if (this.size()!=branch.size()) {
      return false;
    }
    for (int i=0; i<args.size(); i++) {
      if ( ! this.getChild(i).subBranchEquals(branch.getChild(i))) {
        return false;
      }
    }
    return true;
  }


  ///////////////////
  // TO BE REMOVED //
  ///////////////////
 
  /**
   * Any branch that has a null child with a single grandchild inside can
   * be skipped.
   */
  public void removeIntermdiateNullChildBranches() {
    ArgumentVector vector = new ArgumentVector(this, ArgumentVector.BRANCHES_ONLY);
    for (Branch child : vector.toBranchVector()) { //?
      if (child.getFunction()==null && child.size()==1) {
        child.replace(child.getChild(0));
      }
    }
  }

  /**
   * When splitting lists of tokens, there may be empty end bits which
   * correspond to assuming an argument that isn't there.  These appear
   * as empty, null branches this method eliminates.
   */
  public void removeNullChildren() {
    ArgumentVector vector = new ArgumentVector(this, ArgumentVector.BRANCHES_ONLY);
    for (Branch child : vector.toBranchVector()) {
      if (child.getFunction()==null && child.size()==0) {
        // TODO: check that this is not called for
        // functions that need two or more arguments
        // and in which case add some kind of
        // placeholder thingy.
        child.remove();
      }
    }
  }

  /**
   * Sort the arguments of a branch into their natural order.
   */
  public boolean sort() {
    int hash = hashCode();
    Collections.sort((List<Argument>) args);
    return hash!=hashCode();
  }

  /**
   * If an argument is branch[-](argument) then return argument,
   * otherwise return null.
   */
  @Override
  public Argument getNegativeFunctionArg() { // TODO: Rethink this approach.
    if (getFunction()==Function.MINUS) {
      if (size()==1) {
        return firstChild();
      }
    }
    return null;
  }

  public Branch moveFirstChildOut() {
    //- Function function = this.getFunction();
    //- Branch newBranch = this.addIntermediaryParent(function);
    Branch newBranch = this.addIntermediaryParent(this.getFunction());
    newBranch.prepend(this.firstChild());
    return newBranch;
  }

  public Branch moveLastChildOut() {
    Function function = this.getFunction();
    //- Branch newBranch = new Branch(function);
    //- this.addIntermediaryParent(newBranch, false);
    Branch newBranch = this.addIntermediaryParent(function);
    newBranch.append(this.lastChild());
    return newBranch;
  }

  public boolean isLeaf() {
    boolean folded = ! isVisible();
    boolean childless = size()==0;
    return folded || childless;
  }


  /////////////////////
  // PROBLEM METHODS //
  /////////////////////

  public Argument getNextArgument(Argument argument) {
    int index = getIndex(argument) + 1;
    return (0<index && index<args.size()) ? args.get(index) : null;
  }

  public Argument getPreviousArgument(Argument argument) {
    int index = getIndex(argument) - 1;
    return (index >= 0) ? args.get(index) : null;
  }

  public boolean addBefore(Argument current, Argument newArgument) {
    int indexOf = getIndex(current);
    newArgument.remove();
    args.add(indexOf, newArgument);
    newArgument.setParent(this);
    return true;
  }

  // TODO: This is ambiguous as it could conceptually be applied to any
  // argument written as
  //  current.addAfter(newArgument).
  // which enforces the found-indexOf check performed below.
  public boolean addAfter(Argument current, Argument newArgument) {
    int indexOf = getIndex(current) + 1;
    newArgument.remove();
    args.add(indexOf, newArgument);
    newArgument.setParent(this);
    return true;
  }

  public boolean swapArguments(Argument a, Argument b) { // Rename to swapChildren().
    int i = getIndex(a);
    int j = getIndex(b);
    if (i==-1 || j==-1) {
      return false;
    }
    Collections.swap(args, i, j);
    return true;
  }

  @Override
  public void replaceArgument(Argument original, Argument replacement) {
    int index = getIndex(original);
    original.setParent(null);
    args.setElementAt(replacement, index);
    replacement.setParent(this);
  }

  public VariableToken asVariableToken(KnownArguments knownArguments) {
    Function function = this.getFunction();
    VariableState state = knownArguments.functionToState(function);
    if (state==null) {
      state = new Word(function.getName());
    }
    return new VariableToken(state);
  }

}
TOP

Related Classes of ket.math.Branch

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.