Matrix.java

/*
 * Created on 2009/11/12
 * Copyright (C) 2009 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.sdpj.convert;

import java.util.HashMap;
import java.util.Stack;
import java.util.Vector;


/**
 * 行列のような式を扱うクラスです.
 * 
 * @author kai
 * @version $Revision$, 2009/12/02
 */
public class Matrix {

  /** 行列を一時的に置き換える時に利用するための文字列 */
  private static final String REPLACE_STRING = "<#"; //$NON-NLS-1$
  /** 行列を一時的に置き換える時に利用する時に割り振る数字 */
  private static int number = 0;
  /** MaTX  */
  private static final String MATX = "MATX"; //$NON-NLS-1$
  /** Matlab  */
  private static final String MATLAB = "MATLAB"; //$NON-NLS-1$

  /** 値 */
  private String value;
  /** 部分行列 */
  private Vector<Vector<Matrix>> matrix = new Vector<>();
  /** 転置であるかどうか */
  private boolean transposition = false;

  /**
   * Matrixを表示します.
   */
  public void printMatrix() {
    System.out.println(getString());
  }

  /**
   * 文字列として返します.
   * 
   * @return MatrixString
   */
  public String getString() {
    String matrixString = ""; //$NON-NLS-1$

    if (this.getMatrix().isEmpty()) {
      return this.getValue();
    }
    for (int i = 0; i < getRowSize(); i++) {
      for (int j = 0; j < getRowColumnSize(i); j++) {
        if (getElement(i, j).getValue() != null) {
          if (getRowColumnSize(i) == (j + 1)) {
            matrixString += getElement(i, j).getValue();
          } else {
            matrixString += getElement(i, j).getValue() + " "; //$NON-NLS-1$
          }
        } else {
          Matrix subMatrix = new Matrix();
          subMatrix.setMatrix(getElement(i, j).getMatrix());
          matrixString += "["; //$NON-NLS-1$
          subMatrix.printMatrix();
          matrixString += "]"; //$NON-NLS-1$
        }
      }
      if (getRowSize() != (i + 1)) {
        matrixString += "\n"; //$NON-NLS-1$
      }
    }
    return matrixString;
  }

  /**
   * 行のサイズを返します.
   * 
   * @return rowSize
   */
  public int getRowSize() {
    return getMatrix().size();
  }

  /**
   * row行目の列のサイズを返します.
   * 
   * @param row 行を指定します
   * @return row columnSize
   */
  public int getRowColumnSize(int row) {
    return getMatrix().get(row).size();
  }

  /**
   * row行,column列の要素を返します.
   * 
   * @param row 行
   * @param column 列
   * @return Matrix
   */
  public Matrix getElement(int row, int column) {
    return getMatrix().get(row).get(column);
  }

  /**
   * 行を追加します.
   * 
   * @param rowVector 行
   */
  public void addRowVector(Vector<Matrix> rowVector) {
    getMatrix().add(rowVector);
  }

  /**
   * @param value valueを設定します。
   */
  public void setValue(String value) {
    this.value = value;
  }

  /**
   * @return valueを返します。
   */
  public String getValue() {
    return this.value;
  }

  /**
   * @param matrix matrixを設定します。
   */
  public void setMatrix(Vector<Vector<Matrix>> matrix) {
    this.matrix = matrix;
  }

  /**
   * @return matrixを返します。
   */
  public Vector<Vector<Matrix>> getMatrix() {
    return this.matrix;
  }

  /**
   * @param transposition transpositionを設定します。
   */
  public void setTransposition(boolean transposition) {
    this.transposition = transposition;
  }

  /**
   * @return transpositionを返します。
   */
  public boolean isTransposition() {
    return this.transposition;
  }

  /**
   * MatrixをMaTX文法に変換します.
   * 
   * @param matrix Matrix
   * @return MaTX文法式
   */
  public static String matrixToMaTX(Matrix matrix) {
    return matrixTo(MATX, matrix);
  }

  /**
   * MatrixをMatlab文法に変換します.
   * 
   * @param matrix Matrix
   * @return Matlab文法式
   */
  public static String matrixToMatlab(Matrix matrix) {
    String matlab = matrixTo(MATLAB, matrix);
    // [ と ]の部分をきれいにしているはず
    matlab = matlab.replaceAll("\u005D;\\u005B", ";"); //$NON-NLS-1$ //$NON-NLS-2$
    matlab = matlab.replaceAll("\u005D;\\u005D", ";]"); //$NON-NLS-1$ //$NON-NLS-2$
    matlab = matlab.replaceAll("\u005D\u005D*", "]"); //$NON-NLS-1$ //$NON-NLS-2$
    matlab = matlab.replaceAll("\\u005B\\u005B*", "["); //$NON-NLS-1$ //$NON-NLS-2$
    return matlab;
  }

  /**
   * Matrixからmatlabもしくは、MaTX文法に変換します
   * 
   * @param formula Matlab文法 or MaTX文法
   * @param matrix 行列
   * @return 変換行列式
   */
  private static String matrixTo(String formula, Matrix matrix) {
    String grammer = ""; //$NON-NLS-1$
    String tmp = ""; //$NON-NLS-1$
    if (matrix.getValue() != null) {
      if (formula.equals(MATX)) {
        grammer = "[" + matrix.getValue() + "]"; //$NON-NLS-1$//$NON-NLS-2$
        //        grammer = matrix.getValue();
      } else if (formula.equals(MATLAB)) {
        grammer = matrix.getValue();
      }
      return grammer;
    }
    for (int i = 0; i < matrix.getRowSize(); i++) {
      for (int j = 0; j < matrix.getRowColumnSize(i); j++) {
        if (matrix.getElement(i, j).getValue() != null) {
          //一列の場合
          if (matrix.getRowColumnSize(i) == (j + 1)) {
            tmp = tmp + matrix.getElement(i, j).getValue();
          }
          //複数列の場合
          else {
            tmp = tmp + matrix.getElement(i, j).getValue() + ", "; //$NON-NLS-1$
          }
        } else {
          Matrix subMatrix = new Matrix();
          //          subMatrix.setMatrix(matrix.getElement(i, j).getMatrix());
          subMatrix = matrix.getElement(i, j);
          subMatrix.setTransposition(matrix.getElement(i, j).isTransposition());
          String tmp2 = ""; //$NON-NLS-1$
          if (formula.equals(MATX)) {
            tmp2 = matrixTo(MATX, subMatrix);
          } else if (formula.equals(MATLAB)) {
            tmp2 = matrixTo(MATLAB, subMatrix);
          }

          //一列の場合
          if (matrix.getRowColumnSize(i) == (j + 1)) {
            tmp = tmp + tmp2;
          }
          //複数列の場合
          else {
            tmp = tmp + tmp2 + ", "; //$NON-NLS-1$
          }
        }
      }
      //一行の場合
      if (matrix.getRowSize() == 1) {
        grammer = tmp;
      }
      //複数行の場合
      else {
        if (formula.equals(MATX)) {
          grammer = grammer + "[" + tmp + "]"; //$NON-NLS-1$ //$NON-NLS-2$
        } else if (formula.equals(MATLAB)) {
          grammer = grammer + tmp + ";"; //$NON-NLS-1$
        }
      }
      tmp = ""; //$NON-NLS-1$
    }

    grammer = "[" + grammer + "]"; //$NON-NLS-1$ //$NON-NLS-2$

    if (matrix.isTransposition()) {
      grammer = grammer + "'"; //$NON-NLS-1$
    }

    return grammer;
  }

  /**
   * Matlab文法の行列式をMatX文法に変換します
   * 
   * @param matlabFormula Matlab文法式
   * @return MatXFormula
   */
  public static String convertMatX(String matlabFormula) {
    Matrix matrix = matlabToMatrix(matlabFormula);
    String matx = matrixToMaTX(matrix);
    return matx;
  }

  /**
   * MaTX文法の行列式をMatlab文法に変換します
   * 
   * @param matxFormula MaTX文法式
   * @return MatlabFormula
   */
  public static String convertMatlab(String matxFormula) {
    Matrix matrix = matxToMatrix(matxFormula);
    String matlab = matrixToMatlab(matrix);

    return matlab;
  }

  /**
   * Matlab文法をMatrixに変換します.
   * 
   * @param matlabFormula Matlab文法式
   * @return Matrix
   */
  public static Matrix matlabToMatrix(String matlabFormula) {
    String formula = initMatlab(matlabFormula);
    return toMatrix(formula);
  }

  /**
   * MaTX文法をMatrixに変換します.
   * 
   * @param matxFormula MaTX文法式
   * @return Matrix
   */
  public static Matrix matxToMatrix(String matxFormula) {
    String formula = initMaTX(matxFormula);
    return toMatrix(formula);
  }

  /**
   * MatxもしくはMatlabの文法であった文字列をMatrixに変更します.
   * 
   * @param matlabFormula 文字列
   * @return Matrix
   */
  public static Matrix toMatrix(String matlabFormula) {
    String formula = matlabFormula;

    HashMap<String, Matrix> map = new HashMap<>();
    Matrix matrix = new Matrix();
    boolean transposition = false;

    if (formula.endsWith("'")) { //$NON-NLS-1$
      formula = formula.substring(0, formula.length() - 1);
      transposition = true;
    }

    if (formula.startsWith("[") == false) { //$NON-NLS-1$
      matrix.setValue(formula);
      return matrix;
    }
    Stack<String> stack = new Stack<>();
    String element = ""; //$NON-NLS-1$
    for (int i = 0; i < formula.length(); i++) {
      String charactor = String.valueOf(formula.charAt(i));

      if (charactor.equals("]") == false) { //$NON-NLS-1$
        stack.push(charactor);
      } else {
        while (stack.get(stack.size() - 1).equals("[") == false) { //$NON-NLS-1$
          if (formula.length() > i + 1 && String.valueOf(formula.charAt(i + 1)) == "'") { //$NON-NLS-1$
            element = element + "'"; //$NON-NLS-1$
          }
          element = stack.pop() + element;
        }
        stack.pop();
        Matrix subMatrix = new Matrix();
        String[] row = element.split(";"); //$NON-NLS-1$
        for (int rowNumber = 0; rowNumber < row.length; rowNumber++) {
          Vector<Matrix> rowVector = new Vector<>();
          String[] array = row[rowNumber].trim().split(" "); //$NON-NLS-1$
          if (row[rowNumber].trim().equals("") == false) { //$NON-NLS-1$
            for (int loop = 0; loop < array.length; loop++) {
              Matrix subsubMatrix = new Matrix();

              if (array[loop].contains(REPLACE_STRING)) {
                subsubMatrix = map.get(array[loop].trim().replace("'", "")); //$NON-NLS-1$ //$NON-NLS-2$
                if (array[loop].contains("'")) { //$NON-NLS-1$
                  subsubMatrix.setTransposition(true);
                }
              } else {
                subsubMatrix.setValue(array[loop].trim());
              }
              rowVector.add(subsubMatrix);
            }
            subMatrix.addRowVector(rowVector);
          }
        }
        String key = REPLACE_STRING + getNumber();
        map.put(key, subMatrix);
        setNumber(getNumber() + 1);
        stack.push(key);
        element = ""; //$NON-NLS-1$
      }
    }

    matrix = map.get(stack.pop());

    matrix.setTransposition(transposition);
    return matrix;
  }

  /**
   * Matlab文法式の事前処理
   * 
   * @param matlabFormula Matlab文法式文字列
   * @return 事後処理Matlab文法式文字列
   */
  private static String initMatlab(String matlabFormula) {

    String formula = matlabFormula.replaceAll("\n", ";"); //$NON-NLS-1$ //$NON-NLS-2$
    // [ と ]の部分をきれいにしているはず
    formula = formula.replaceAll("\u005D;\\u005B", ";"); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("\u005D;\\u005D", ";]"); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("\u005D\u005D*", "]"); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("\\u005B\\u005B*", "["); //$NON-NLS-1$ //$NON-NLS-2$

    formula = formula.replaceAll("\t", " "); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll(",", " "); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("  *", " "); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.trim();
    return formula;
  }

  /**
   * MaTX文法式の事前処理
   * 
   * @param matxFormula MaTX文法式文字列
   * @return 事後処理MaTX文法式文字列
   */
  private static String initMaTX(String matxFormula) {
    String formula = matxFormula;

    // \u005B = [ (左大かっこ)  , \u005D = ] (右大かっこ) 
    // ](空白*)[ を ];[に置き換えて,相互変換しやすい形に移動 
    formula = formula.replaceAll("\u005D *\\u005B", "\u005D;\u005B"); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("\t", " "); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll(",", " "); //$NON-NLS-1$ //$NON-NLS-2$
    formula = formula.replaceAll("  *", " "); //$NON-NLS-1$//$NON-NLS-2$
    formula = formula.trim();
    return formula;
  }

  /**
   * @param number numberを設定します。
   */
  public static void setNumber(int number) {
    Matrix.number = number;
  }

  /**
   * @return numberを返します。
   */
  public static int getNumber() {
    return number;
  }

}