DoubleAdjacencyMatrix.java

/*
 * $Id: AdjacencyMatrix.java,v 1.115 2008/07/15 15:27:15 koga Exp $
 *
 * Copyright (C) 2004-2005 Koga Laboratory. All rights reserved.
 *
 */

package org.mklab.tool.control.system;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.mklab.nfc.matrix.BaseArray;
import org.mklab.nfc.matrix.BooleanMatrix;
import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.matrix.GridUtil;
import org.mklab.nfc.matrix.IntMatrix;
import org.mklab.nfc.ode.DoublePiecewiseContinuousAlgebraicSystem;
import org.mklab.nfc.ode.DoublePiecewiseContinuousDiscreteAlgebraicSystem;
import org.mklab.nfc.ode.DoublePiecewiseDifferentialDifferenceSystem;
import org.mklab.nfc.ode.DoublePiecewiseDifferentialSystem;
import org.mklab.nfc.rpn.ExpressionProcessor;
import org.mklab.nfc.rpn.ReversePolishNotationOperand;
import org.mklab.nfc.rpn.ReversePolishNotationProcessor;
import org.mklab.nfc.rpn.WithoutCancellationExpressionProcessor;
import org.mklab.nfc.scalar.DoubleNumber;
import org.mklab.tool.control.DoubleLinearSystem;
import org.mklab.tool.control.DoubleLinearSystemFactory;
import org.mklab.tool.control.TimeDomainType;
import org.mklab.tool.control.system.continuous.DoubleBlockContinuousExplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleBlockContinuousImplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleBlockContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.DoubleBlockPiecewiseContinuousDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleBlockPiecewiseContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.DoubleContinuousDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleContinuousImplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleContinuousLinearDynamicSystem;
import org.mklab.tool.control.system.continuous.DoubleContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.DoubleIntegratorSystem;
import org.mklab.tool.control.system.discrete.DoubleBlockDiscreteDynamicSystem;
import org.mklab.tool.control.system.discrete.DoubleBlockDiscreteStaticSystem;
import org.mklab.tool.control.system.discrete.DoubleDiscreteDynamicSystem;
import org.mklab.tool.control.system.discrete.DoubleDiscreteStaticSystem;
import org.mklab.tool.control.system.discrete.DoubleHoldSystem;
import org.mklab.tool.control.system.discrete.DoubleUnitDelaySystem;
import org.mklab.tool.control.system.graph.CycleMatrix;
import org.mklab.tool.control.system.graph.DoubleConnectionMatrix;
import org.mklab.tool.control.system.graph.DoubleCycleMatrix;
import org.mklab.tool.control.system.graph.DoubleReachableMatrix;
import org.mklab.tool.control.system.math.DoubleConstantSystem;
import org.mklab.tool.control.system.math.DoubleDeMultiplexer;
import org.mklab.tool.control.system.math.DoubleMultiplexer;
import org.mklab.tool.control.system.math.DoubleNegativeUnitSystem;
import org.mklab.tool.control.system.math.DoubleUnitSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockPiecewiseSampledDataDynamicSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockPiecewiseSampledDataStaticSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockSampledDataDynamicSystem;
import org.mklab.tool.control.system.sampled.DoubleBlockSampledDataStaticSystem;
import org.mklab.tool.control.system.sampled.DoubleSampler;
import org.mklab.tool.control.system.sink.DoubleContinuousSink;
import org.mklab.tool.control.system.sink.DoubleOutputPort;
import org.mklab.tool.control.system.sink.Exporter;
import org.mklab.tool.control.system.source.DoubleContinuousSource;
import org.mklab.tool.control.system.source.DoubleInputPort;
import org.mklab.tool.control.system.source.Importer;


/**
 * 隣接行列(システムオペレータを成分とする行列)を表わすクラスです。
 * 
 * @author Koga Laboratory
 * @version $Revision: 1.115 $
 */
public class DoubleAdjacencyMatrix extends BaseArray<DoubleSystemOperator,DoubleAdjacencyMatrix> {

  /** シリアルバージョン */
  private static final long serialVersionUID = 5494894107335358500L;

  /** 線形システムを求めるならばtrue */
  private boolean requiringLinearSystem = false;

  /** ディスクリプタ形式で求めるならばtrue */
  private boolean requiringDescriptor = false;

  /** 最小の要素で式を表現するならばtrue */
  private boolean requiringPrimitiveExpression = false;

  /** 入力ポートが接続されているノードの番号(番号は1から始まります)のリスト */
  private List<Integer> inputNodes;

  /** Sourceが接続されているノードの番号(番号は1から始まります)のリスト */
  private List<Integer> sourceNodes;

  /** 出力ポートが接続されているノードの番号(番号は1から始まります)のリスト */
  private List<Integer> outputNodes;

  /** Sinkが接続されているのノードの番号(番号は1から始まります)のリスト */
  private List<Integer> sinkNodes;

  /** 入力ポートのノード番号(番号は1から始まります)とタグのマップ */
  private Map<Integer, String> inputPortTags;

  /** 出力ポートのノード番号(番号は1から始まります)とタグのマップ */
  private Map<Integer, String> outputPortTags;

//  /**
//   * 新しく生成された<code>AdjacencyMatrix</code>オブジェクトを初期化します。
//   */
//  public AdjacencyMatrix() {
//    this(0, 0);
//  }

  /**
   * 新しく生成された<code>AdjacencyMatrix</code>オブジェクトを初期化します。
   * 
   * @param rowSize 行の数
   * @param columnSize 列の数
   */
  public DoubleAdjacencyMatrix(final int rowSize, final int columnSize) {
    this(new DoubleSystemOperator[rowSize][columnSize]);
  }

//  /**
//   * このコンストラクターは使用できません。正方の行列しか生成できません。
//   * 
//   * @param elements 成分をもつ配列
//   */
//  public AdjacencyMatrix( DoubleSystemOperator[] elements) {
//    throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.0")); //$NON-NLS-1$
//  }

  /**
   * 引数を成分とする行列を生成します。
   * 
   * @param elements 成分をもつ配列
   */
  @SuppressWarnings("boxing")
  public DoubleAdjacencyMatrix(final DoubleSystemOperator[][] elements) {
    super(elements);
    DoubleAdjacencyMatrixUtil.setZeroSystemToNullElement(elements);

    this.inputNodes = new ArrayList<>(Arrays.asList(1));
    this.sourceNodes = new ArrayList<>();
    this.outputNodes = new ArrayList<>(Arrays.asList(getRowSize()));
    this.sinkNodes = new ArrayList<>();

    this.inputPortTags = new TreeMap<>();
    this.inputPortTags.put(1, "u"); //$NON-NLS-1$
    this.outputPortTags = new TreeMap<>();
    this.outputPortTags.put(getRowSize(), "y"); //$NON-NLS-1$
  }

  /**
   * 入力ポートが接続されているノードの番号のリストを設定します。
   * 
   * @param inputNodes 入力ポートが接続されているノードの番号のリスト
   */
  public void setInputNodes(final List<Integer> inputNodes) {
    final Integer[] nodes = new Integer[inputNodes.size()];
    this.inputNodes = new ArrayList<>(Arrays.asList(inputNodes.toArray(nodes)));
  }

  /**
   * Sourceが接続されているノードの番号のリストを設定します。
   * 
   * @param sourceNodes Sourceが接続されているノードの番号のリスト
   */
  public void setSourceNodes(final List<Integer> sourceNodes) {
    final Integer[] nodes = new Integer[sourceNodes.size()];
    this.sourceNodes = new ArrayList<>(Arrays.asList(sourceNodes.toArray(nodes)));
  }

  /**
   * Sourceが接続されているノードの番号のリストを返します。
   * 
   * @return Sourceが接続されているノードの番号のリスト
   */
  List<Integer> getSourceNodes() {
    return this.sourceNodes;
  }

  /**
   * 入力器のリストを返します。
   * 
   * @return 入力器のリスト
   */
  public List<Importer> getImporters() {
    final List<Importer> importers = new ArrayList<>();

    for (int sourceNode : this.sourceNodes) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[sourceNode - 1][column];
        if (system == null || system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        if (system instanceof Importer) {
          importers.add((Importer)system);
        }
      }
    }

    return importers;
  }

  /**
   * 出力器のリストを返します。
   * 
   * @return 出力器のリスト
   */
  public List<Exporter> getExporters() {
    final List<Exporter> exporters = new ArrayList<>();

    for (int sinkNode : this.sinkNodes) {
      for (int row = 0; row < getRowSize(); row++) {
        final DoubleSystemOperator system = this.elements[row][sinkNode - 1];
        if (system == null || system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        if (system instanceof Exporter) {
          exporters.add((Exporter)system);
        }
      }
    }

    return exporters;
  }

  /**
   * 出力ポートが接続されているノードの番号のリストを設定します。
   * 
   * @param outputNodes 出力ポートが接続されているノードの番号のリスト
   */
  public void setOutputNodes(final List<Integer> outputNodes) {
    final Integer[] nodes = new Integer[outputNodes.size()];
    this.outputNodes = new ArrayList<>(Arrays.asList(outputNodes.toArray(nodes)));
  }

  /**
   * Sinkが接続されているノードの番号のリストを設定します。
   * 
   * @param sinkNodes Sinkが接続されているノードの番号のリスト
   */
  public void setSinkNodes(final List<Integer> sinkNodes) {
    final Integer[] nodes = new Integer[sinkNodes.size()];
    this.sinkNodes = new ArrayList<>(Arrays.asList(sinkNodes.toArray(nodes)));
  }

  /**
   * Sinkが接続されているノードの番号のリストを返します。
   * 
   * @return Sinkが接続されているノードの番号のリスト
   */
  List<Integer> getSinkNodes() {
    return this.sinkNodes;
  }

  /**
   * 線形システムを求めるか設定します。
   * 
   * @param requiringLinearSystem 線形システムを求めるならばtrue、そうでなければfalse
   */
  void setRequiringLinearSystem(final boolean requiringLinearSystem) {
    this.requiringLinearSystem = requiringLinearSystem;
  }

  /**
   * 線形システムを求めるか判定します。
   * 
   * @return 線形システムを求めるならばtrue、そうでなければfalse
   */
  boolean isRequiringLinearSystem() {
    return this.requiringLinearSystem;
  }

  /**
   * @see org.mklab.nfc.matrix.BaseArray#printElements(java.io.Writer)
   */
  @SuppressWarnings("nls")//$NON-NLS-1$
  @Override
  public void printElements(final Writer output) {
    int maxColumnSize = 2;
    DoubleAdjacencyMatrixUtil.print(this.elements, output, maxColumnSize);
  }

  /**
   * @see org.mklab.nfc.matrix.BaseArray#printElements(java.io.Writer, int)
   */
  @Override
  public void printElements(final Writer output, final int maxColumnSize) {
    DoubleAdjacencyMatrixUtil.print(this.elements, output, maxColumnSize);
  }

  /**
   * 隣接行列が表わすブロックシステムを返します。
   * 
   * @return 隣接行列が表わすブロックシステム
   */
  public DoubleBlockSystem getBlockSystem() {
    if (isPiecewiseContinuous()) {
      if (isContinuousDynamic()) {
        return new DoubleBlockPiecewiseContinuousDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
      }

      if (isContinuous()) {
        return new DoubleBlockPiecewiseContinuousStaticSystem((this.elements), this.inputNodes, this.outputNodes);  
      }
      
      if (isDiscreteDynamic()) {
        return new DoubleBlockDiscreteDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
      }

      if (isDiscrete()) {
        return new DoubleBlockDiscreteStaticSystem((this.elements), this.inputNodes, this.outputNodes);
      }
      
      if (isSampledDataDynamic()) {
        return new DoubleBlockPiecewiseSampledDataDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
      }

      return new DoubleBlockPiecewiseSampledDataStaticSystem((this.elements), this.inputNodes, this.outputNodes);
    }
    
    if (isContinuousImplicitDynamic()) {
      return new DoubleBlockContinuousImplicitDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    if (isContinuousDynamic()) {
      return new DoubleBlockContinuousExplicitDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    if (isContinuous()) {
      return new DoubleBlockContinuousStaticSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    if (isDiscreteDynamic()) {
      return new DoubleBlockDiscreteDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    if (isDiscrete()) {
      return new DoubleBlockDiscreteStaticSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    if (isSampledDataDynamic()) {
      return new DoubleBlockSampledDataDynamicSystem((this.elements), this.inputNodes, this.outputNodes);
    }

    return new DoubleBlockSampledDataStaticSystem((this.elements), this.inputNodes, this.outputNodes);
  }

  /**
   * 連続時間陰的動的システムが含まれ、離散時間動的システムが含まれていないか判定します。
   * 
   * @return 連続時間陰的動的システムが含まれ、離散時間動的システムが含まれていなければtrue、そうでなければfalse
   */
  public boolean isContinuousImplicitDynamic() {
    boolean continuousImplicitDynamic = false;

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (continuousImplicitDynamic == false && system instanceof DoubleContinuousImplicitDynamicSystem) {
          continuousImplicitDynamic = true;
        }
        if (system instanceof DoubleSampler) {
          return false;
        }
      }
    }

    return continuousImplicitDynamic;
  }
  
  /**
   * 連続時間動的システムが含まれ、離散時間動的システムが含まれていないか判定します。
   * 
   * @return 連続時間動的システムが含まれ、離散時間動的システムが含まれていなければtrue、そうでなければfalse
   */
  public boolean isContinuousDynamic() {
    boolean continuousDynamic = false;

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (continuousDynamic == false && system instanceof DoubleContinuousDynamicSystem) {
          continuousDynamic = true;
        }
        if (system instanceof DoubleSampler) {
          return false;
        }
      }
    }

    return continuousDynamic;
  }

  /**
   * 離散時間動的システムが含まれ、連続時間動的システムが含まれていないか判定します。
   * 
   * @return 離散時間動的システムが含まれ、連続時間動的システムが含まれていなければtrue、そうでなければfalse
   */
  public boolean isDiscreteDynamic() {
    boolean discreteDynamic = false;

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (discreteDynamic == false && system instanceof DoubleDiscreteDynamicSystem) {
          discreteDynamic = true;
        }
        if (system instanceof DoubleContinuousDynamicSystem) {
          return false;
        }
      }
    }

    return discreteDynamic;
  }

  /**
   * 連続時間システム(連続時間動的システム又は連続時間静的システム)であるか判定します。
   * 
   * @return 連続時間システムならばtrue、そうでなければfalse
   */
  public boolean isContinuous() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoubleConstantSystem) {
          continue;
        }

        if (system instanceof DoubleDiscreteDynamicSystem) {
          return false;
        }
        if (system instanceof DoubleDiscreteStaticSystem) {
          return false;
        }
        if (system instanceof DoubleHoldSystem) {
          return false;
        }
      }
    }

    return true;
  }

  /**
   * 区分的連続システム(区分的連続動的システム又は区分的連続静的システム)を含むか判定します。
   * 
   * @return 区分的連続システムを含むならばtrue、そうでなければfalse
   */
  public boolean isPiecewiseContinuous() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoublePiecewiseDifferentialSystem) {
          return true;
        }
        if (system instanceof DoublePiecewiseContinuousAlgebraicSystem) {
          return true;
        }
        if (system instanceof DoublePiecewiseDifferentialDifferenceSystem) {
          return true;
        }
        if (system instanceof DoublePiecewiseContinuousDiscreteAlgebraicSystem) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * 離散時間システム(離散時間動的システム又は離散時間静的システム)であるか判定します。
   * 
   * @return 離散時間システムならばtrue、そうでなければfalse
   */
  public boolean isDiscrete() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoubleConstantSystem) {
          continue;
        }

        if (system instanceof DoubleContinuousDynamicSystem) {
          return false;
        }
//        if (system instanceof ContinuousStaticSystem) {
//          return false;
//        }
      }
    }

    return true;
  }

  /**
   * サンプル値動的システム(連続時間動的システムと離散時間動的システムの両方が含まれる)であるか判定します。
   * 
   * @return サンプル値動的システムならばtrue、そうでなければfalse
   */
  public boolean isSampledDataDynamic() {
    boolean hasContinuousDynamics = false;
    boolean hasSampler = false;

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoubleContinuousDynamicSystem) {
          hasContinuousDynamics = true;
        }
        if (system instanceof DoubleSampler) {
          hasSampler = true;
        }
      }
    }

    return hasContinuousDynamics && hasSampler;
  }

  /**
   * サンプル値動的システム(連続時間動的システムと離散時間動的システムの両方が含まれる)であるか判定します。
   * 
   * @return サンプル値動的システムならばtrue、そうでなければfalse
   */
  public boolean isSampledData() {
    boolean hasContinuousSystem = false;
    boolean hasSampler = false;

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (hasContinuousSystem == false && system instanceof DoubleContinuousDynamicSystem) {
          hasContinuousSystem = true;
        }

        if (hasContinuousSystem == false && system instanceof DoubleContinuousStaticSystem) {
          hasContinuousSystem = true;
        }

        if (hasSampler == false && system instanceof DoubleSampler) {
          hasSampler = true;
        }
      }
    }

    return hasContinuousSystem && hasSampler;
  }

  /**
   * 線形システムだけが含まれているか判定します。
   * 
   * @return 線形システムだけが含まれていればtrue、そうでなければfalse
   */
  public boolean isLinear() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        final boolean notZeroSystem = system != DoubleZeroSystem.getInstance();
        final boolean notInputPort = (system instanceof DoubleInputPort) == false;
        final boolean notOutputPort = (system instanceof DoubleOutputPort) == false;
        final boolean notLinear = system.isLinear() == false;
        if (notZeroSystem && notInputPort && notOutputPort && notLinear) {
          return false;
        }
      }
    }

    return true;
  }

  /**
   * 分離器から多重器へのエッジを縮約した隣接行列を返します。
   * 
   * @return 分離器から多重器へのエッジを縮約した隣接行列
   */
  @SuppressWarnings("boxing")
  DoubleAdjacencyMatrix contractDeMultiplexerAndMultiplexer() {
    boolean contracting = false;

    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);

    for (int row = 0; row < getRowSize(); row++) {
      final Map<Integer, Integer> deMultiplexer = findDeMultiplexerInRow(matrix, row);

      if (deMultiplexer.size() == 0) {
        continue;
      }

      final Map<Integer, Integer> multiplexer = new TreeMap<>();

      for (int node : new ArrayList<>(deMultiplexer.values())) {
        final Map<Integer, Integer> singleMultiplexer = findMultiplexerInRow(matrix, node);
        if (singleMultiplexer.size() != 1) {
          continue;
        }

        multiplexer.putAll(singleMultiplexer);
      }

      if (deMultiplexer.size() != multiplexer.size()) {
        continue;
      }

      final List<Integer> multiplexerOutputNodes = new ArrayList<>(multiplexer.values());
      final int firstNode = multiplexerOutputNodes.get(0);
      for (int node : multiplexerOutputNodes) {
        if (node != firstNode) {
          multiplexer.clear();
          break;
        }
        if (findMultiplexerInColumn(matrix, node).size() != deMultiplexer.size()) {
          multiplexer.clear();
          break;
        }
      }

      contracting = true;

      shortDeMultiplexerAndMultiplexer(matrix, row, multiplexer, deMultiplexer);
    }

    if (contracting) {
      final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
      ans.setInputNodes(this.inputNodes);
      ans.setSourceNodes(this.sourceNodes);
      ans.setOutputNodes(this.outputNodes);
      ans.setSinkNodes(this.sinkNodes);
      ans.setInputPortTags(this.inputPortTags);
      ans.setOutputPortTags(this.outputPortTags);
      ans.setRequiringLinearSystem(this.requiringLinearSystem);
      ans.setRequiringDescriptor(this.requiringDescriptor);
      ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
      return ans;
    }

    return this;
  }

  /**
   * 多重器から分離器へのエッジを縮約した隣接行列を返します。
   * 
   * @return 多重器と分離器を縮約した隣接行列
   */
  DoubleAdjacencyMatrix contractMultiplexerAndDeMultiplexer() {
    boolean contracting = false;

    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);

    for (int column = 0; column < getColumnSize(); column++) {
      final Map<Integer, Integer> multiplexer = findMultiplexerInColumn(matrix, column);

      if (multiplexer.size() == 0) {
        continue;
      }

      final Map<Integer, Integer> deMultiplexer = findDeMultiplexerInRow(matrix, column);

      if (deMultiplexer.size() == 0) {
        continue;
      }

      contracting = true;

      shortMultiplexerAndDeMultiplexer(matrix, column, multiplexer, deMultiplexer);
    }

    if (contracting) {
      final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
      ans.setInputNodes(this.inputNodes);
      ans.setSourceNodes(this.sourceNodes);
      ans.setOutputNodes(this.outputNodes);
      ans.setSinkNodes(this.sinkNodes);
      ans.setInputPortTags(this.inputPortTags);
      ans.setOutputPortTags(this.outputPortTags);
      ans.setRequiringLinearSystem(this.requiringLinearSystem);
      ans.setRequiringDescriptor(this.requiringDescriptor);
      ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
      return ans;
    }

    return this;
  }

  /**
   * 多重器から分離器へのエッジを短絡します。
   * 
   * @param matrix 隣接行列
   * @param column 短絡する多重器を探す列番号
   * @param multiplexer 多重器の入力ポート番号と入力ポートのノード番号のマップ
   * @param deMultiplexer 分離器の出力ポート番号と出力ポートのノード番号のマップ
   */
  @SuppressWarnings("boxing")
  private void shortMultiplexerAndDeMultiplexer(final DoubleSystemOperator[][] matrix, int column, final Map<Integer, Integer> multiplexer, final Map<Integer, Integer> deMultiplexer) {
    for (int i = 1; i <= multiplexer.size(); i++) {
      final int inputNode = multiplexer.get(i);
      for (int j = 1; j <= deMultiplexer.size(); j++) {
        final int outputNode = deMultiplexer.get(j);
        DoubleSystemOperator system = matrix[inputNode][outputNode];
        if (system != DoubleZeroSystem.getInstance() && (system instanceof DoubleConstantSystem) == false) {
          return;
        }
      }
    }

    for (int i = 1; i <= multiplexer.size(); i++) {
      final int inputNode = multiplexer.get(i);
      DoubleMultiplexer mux = (DoubleMultiplexer)matrix[inputNode][column];
      for (int j = 1; j <= deMultiplexer.size(); j++) {
        final int outputNode = deMultiplexer.get(j);
        DoubleDeMultiplexer dex = (DoubleDeMultiplexer)matrix[column][outputNode];
        DoubleConstantSystem system = dex.multiply(mux);

        DoubleSystemOperator oldSystem = matrix[inputNode][outputNode];
        if (system.getGain().isZero() == false) {
          if (oldSystem == DoubleZeroSystem.getInstance()) {
            matrix[inputNode][outputNode] = system;
          } else {
            matrix[inputNode][outputNode] = ((DoubleConstantSystem)oldSystem).add(system);
          }
        }
      }
    }

    for (int i = 1; i <= multiplexer.size(); i++) {
      final int inputNode = multiplexer.get(i);
      matrix[inputNode][column] = DoubleZeroSystem.getInstance();
    }
    for (int i = 1; i <= deMultiplexer.size(); i++) {
      final int outputNode = deMultiplexer.get(i);
      matrix[column][outputNode] = DoubleZeroSystem.getInstance();
    }
  }

  /**
   * 分離器から多重器へのエッジを短絡します。
   * 
   * @param matrix 隣接行列
   * @param row 短絡する分離器を探す行番号
   * @param multiplexer 多重器の入力ポート番号と入力ポートのノード番号のマップ
   * @param deMultiplexer 分離器の出力ポート番号と出力ポートのノード番号のマップ
   */
  @SuppressWarnings("boxing")
  private void shortDeMultiplexerAndMultiplexer(final DoubleSystemOperator[][] matrix, int row, final Map<Integer, Integer> multiplexer, final Map<Integer, Integer> deMultiplexer) {
    for (int i = 1; i <= deMultiplexer.size(); i++) {
      final int removingNode = deMultiplexer.get(i);
      DoubleDeMultiplexer dex = (DoubleDeMultiplexer)matrix[row][removingNode];
      for (int j = 1; j <= multiplexer.size(); j++) {
        final int outputNode = multiplexer.get(j);
        DoubleMultiplexer mux = (DoubleMultiplexer)matrix[removingNode][outputNode];
        DoubleConstantSystem system = mux.multiply(dex);
        if (system.getGain().isZero()) {
          matrix[row][outputNode] = DoubleZeroSystem.getInstance();
        } else {
          matrix[row][outputNode] = system;
        }
      }
    }

    for (int i = 1; i <= deMultiplexer.size(); i++) {
      final int removingNode = deMultiplexer.get(i);
      for (int j = 1; j <= multiplexer.size(); j++) {
        final int outputNode = multiplexer.get(j);
        matrix[row][removingNode] = DoubleZeroSystem.getInstance();
        matrix[removingNode][outputNode] = DoubleZeroSystem.getInstance();
      }
    }
  }

  /**
   * 指定された行に存在する分離器の出力ポート番号と出力ポートのノード番号のマップを返します。
   * 
   * @param matrix 隣接行列
   * @param row 行番号
   * @return 指定された行に存在する分離器の出力ポート番号と出力ポートのノード番号のマップ <p> 分離器が無い場合、または、分離器以外のシステムが存在する場合は、成分が0個のマップ
   */
  @SuppressWarnings("boxing")
  private Map<Integer, Integer> findDeMultiplexerInRow(final DoubleSystemOperator[][] matrix, final int row) {
    final Map<Integer, Integer> deMultiplexer = new TreeMap<>();

    for (int column = 0; column < getColumnSize(); column++) {
      final DoubleSystemOperator system = matrix[row][column];
      if (system == null || system == DoubleZeroSystem.getInstance()) {
        continue;
      }

      if (system instanceof DoubleDeMultiplexer) {
        deMultiplexer.put(((DoubleDeMultiplexer)system).getOutputNumber(), column);
        continue;
      }

      deMultiplexer.clear();
      return deMultiplexer;
    }
    return deMultiplexer;
  }

  /**
   * 指定された行に存在する多重器の入力ポート番号と入力ポートのノード番号のマップを返します。
   * 
   * @param matrix 隣接行列
   * @param row 行番号
   * @return 指定された行に存在する多重器の入力ポート番号と入力ポートのノード番号のマップ <p> 多重器が無い場合、または、多重器以外のシステムが存在する場合は、成分が0個のマップ
   */
  @SuppressWarnings("boxing")
  private Map<Integer, Integer> findMultiplexerInRow(final DoubleSystemOperator[][] matrix, final int row) {
    final Map<Integer, Integer> multiplexer = new TreeMap<>();

    for (int column = 0; column < getColumnSize(); column++) {
      final DoubleSystemOperator system = matrix[row][column];
      if (system == null || system == DoubleZeroSystem.getInstance()) {
        continue;
      }

      if (system instanceof DoubleMultiplexer) {
        multiplexer.put(((DoubleMultiplexer)system).getInputNumber(), column);
        continue;
      }

      multiplexer.clear();
      return multiplexer;
    }
    return multiplexer;
  }

  /**
   * 指定された列に存在する多重器の入力ポートの番号と入力ポートのノード番号のマップを返します。
   * 
   * @param matrix 隣接行列
   * @param column 列番号
   * @return 指定された列に存在する多重器の入力ポートの番号と入力ポートのノード番号のマップ
   */
  @SuppressWarnings("boxing")
  private Map<Integer, Integer> findMultiplexerInColumn(final DoubleSystemOperator[][] matrix, int column) {
    final Map<Integer, Integer> multiplexer = new TreeMap<>();

    for (int row = 0; row < getRowSize(); row++) {
      final DoubleSystemOperator system = matrix[row][column];
      if (system == null || system == DoubleZeroSystem.getInstance()) {
        continue;
      }

      if (system instanceof DoubleMultiplexer) {
        multiplexer.put(((DoubleMultiplexer)system).getInputNumber(), row);
        continue;
      }

      multiplexer.clear();
    }
    return multiplexer;
  }

  /**
   * 定数システムを除去(定数エッジを縮約)した等価なシステム行列を生成します。
   * 
   * @param onlyUnit 単位システムのみを縮約するならばtrue
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 定数を除去した等価なシステム行列
   */
  DoubleAdjacencyMatrix contractConstantEdge(final boolean onlyUnit, final boolean onlyConstant) {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);
    DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    boolean contracted = false;
    boolean changed = false;

    do {
      if (changed) {
        contracted = true;
        ans = ans.removeZeroRowColumn();
        ans.modifyID();
      }

      changed = ans.contractConstantEdgeForwardColumnWise(onlyUnit, onlyConstant);
      if (changed) {
        continue;
      }

      if (this.requiringDescriptor == false) {
        changed = ans.removeArgebraicloop();
        if (changed) {
          continue;
        }
      }

      changed = ans.removeRedundantNodes();
      if (changed) {
        continue;
      }

      changed = ans.contractLoopColumnWithUnit();
      if (changed) {
        continue;
      }

      DoubleAdjacencyMatrix oldAns = ans;
      ans = oldAns.expandDegree();
      if (oldAns.equals(ans) == true) {
        changed = false;
      } else {
        changed = true;
      }
    }

    while (changed);

    if (ans.elements == null || ans.elements.length == 0) {
      return createZeroSystem();
    }

    if (contracted) {
      return ans;
    }

    return this;
  }

  /**
   * ループが存在するノードと積分器の間にある単位行列を縮約します.
   * 
   * @return ループノードと積分器の間の単位行列が縮約されたならばtrue,そうでなければfalse
   */
  @SuppressWarnings("boxing")
  private boolean contractLoopColumnWithUnit() {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;

    final int integratorSize = this.getIntegratorOrUnitDelaySystemSize();

    final int inputSize = this.inputNodes.size();
    final int outputSize = this.outputNodes.size();
    if ((localColumnSize - (inputSize + outputSize + integratorSize * 2)) <= 0) {
      return false;
    }

    final List<Integer> connectedLoopNodes = getConnectedLoopNodes();

    boolean changed = false;
    for (final Integer unit : connectedLoopNodes) {
      int target = 0;
      for (int row = 0; row < localRowSize; row++) {
        if (matrix[row][unit].isZero() == false) {
          target = row;
          break;
        }
      }

      for (int row = 0; row < localColumnSize; row++) {
        if (row == target) {
          continue;
        }
        if (matrix[row][target] instanceof DoubleZeroSystem) {
          continue;
        }
        matrix[row][unit] = matrix[row][target];
        matrix[row][target] = DoubleZeroSystem.getInstance();
      }

      matrix[unit][unit] = matrix[target][target];
      matrix[target][target] = DoubleZeroSystem.getInstance();
      matrix[target][unit] = DoubleZeroSystem.getInstance();

      changed = true;
    }

    return changed;
  }

  /**
   * 自己ループのノードにつながる,かつ,列に唯一の単位行列が存在するノードのリストを得ます.
   * 
   * @return 自己ループのノードにつながる,かつ,列に唯一の単位行列が存在するノードのリスト
   */
  @SuppressWarnings("boxing")
  private List<Integer> getConnectedLoopNodes() {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int localRowSize = matrix.length;

    final List<Integer> connectedIntegrator = getConnectedIntegratorNodes();
    final List<Integer> selfLoopNumbers = getSelfLoopNodes();

    final List<Integer> connectedLoopNodes = new ArrayList<>();
    for (final Integer unit : connectedIntegrator) {
      for (int row = 0; row < localRowSize; row++) {
        if (matrix[row][unit].isZero() == false) {
          if (selfLoopNumbers.contains(row)) {
            connectedLoopNodes.add(unit);
          }
        }
      }
    }
    return connectedLoopNodes;
  }

  /**
   * 積分器につながる,かつ,列に唯一の単位行列が存在するノードのリストを得ます.
   * 
   * @return 積分器につながる単位行列が存在するノードのリスト
   */
  @SuppressWarnings("boxing")
  private List<Integer> getConnectedIntegratorNodes() {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;

    final List<Integer> onlyUnitColumnList = getOnlyUnitColumnList();

    final List<Integer> connectedIntegrator = new ArrayList<>();
    for (final Integer unit : onlyUnitColumnList) {
      for (int column = 0; column < localColumnSize; column++) {
        if (matrix[unit][column] instanceof DoubleIntegratorSystem) {
          connectedIntegrator.add(unit);
        }
      }
    }
    return connectedIntegrator;
  }

  /**
   * 自己ループが発生するノードのリストを得ます.
   * 
   * @return 自己ループノードのリスト
   */
  private List<Integer> getSelfLoopNodes() {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;

    final List<Integer> selfLoopNumbers = new ArrayList<>();
    for (int column = 0; column < localColumnSize; column++) {
      final boolean hasSelfLoop = matrix[column][column] != DoubleZeroSystem.getInstance();
      if (hasSelfLoop) {
        selfLoopNumbers.add(Integer.valueOf(column));
      }
    }
    return selfLoopNumbers;
  }

  /**
   * 列ベクトル要素(ConstantSystem)を比較します.
   * 
   * @param opponent 比較対象
   * @return 一致するならばtrue,不一致ならばfalse
   */
  private boolean compareColumnVectors(DoubleAdjacencyMatrix opponent) {
    for (int i = 1; i < this.getRowSize(); i++) {
      if (this.getElement(i, 1) instanceof DoubleConstantSystem && opponent.getElement(i, 1) instanceof DoubleConstantSystem) {
        final DoubleConstantSystem element = (DoubleConstantSystem)this.getElement(i, 1);
        final DoubleConstantSystem opponentElement = (DoubleConstantSystem)opponent.getElement(i, 1);

        if ((element.getGain() == null ? opponentElement.getGain() == null : false == element.getGain().equals(opponentElement.getGain()))
            && (element.getExpression() == null ? opponentElement.getExpression() == null : false == element.getExpression().equals(opponentElement.getExpression()))
            && false == (element.isSingleTerm() == opponentElement.isSingleTerm()) && false == (element.isNegative() == opponentElement.isNegative())) {
          return false;
        }
      } else {
        final DoubleSystemOperator element = this.getElement(i, 1);
        final DoubleSystemOperator opponentElement = opponent.getElement(i, 1);
        if (element == DoubleZeroSystem.getInstance()) {
          if (opponentElement instanceof DoubleConstantSystem && opponentElement.isLinear() && opponentElement.getLinearSystem().getD().isZero()) {
            continue;
          }
        }
        if (opponentElement == DoubleZeroSystem.getInstance()) {
          if (element instanceof DoubleConstantSystem && element.isLinear() && element.getLinearSystem().getD().isZero()) {
            continue;
          }
        }

        if (element.equals(opponentElement) == false) {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * 代数ループを見つけ解決します。
   * 
   * @return 代数ループを解決すればtrue、そうでなければfalse
   */
  @SuppressWarnings({"boxing"})
  private boolean removeArgebraicloop() {
    DoubleSystemOperator[][] matrix = this.elements;
    final int localColumnSize = matrix.length;
    final List<Integer> selfLoopNumbers = new ArrayList<>();

    final List<Integer> loopNode = detectLoopNode();
    for (int column = 0; column < localColumnSize; column++) {
      final boolean hasSelfLoop = matrix[column][column] != DoubleZeroSystem.getInstance();
      if (hasSelfLoop && (this.requiringDescriptor == false || false == loopNode.contains(column))) {
        selfLoopNumbers.add(Integer.valueOf(column));
      }
    }

    if (selfLoopNumbers.isEmpty()) {
      return false;
    }

    for (int i = 0; i < selfLoopNumbers.size() - 1; i++) {
      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, selfLoopNumbers.get(0) + 1 + i, selfLoopNumbers.get(i + 1));
    }

    int selfLoopBaseMatrixIndex = selfLoopNumbers.get(0);
    for (int i = 1; i < selfLoopNumbers.size(); i++) {
      selfLoopNumbers.set(i, ++selfLoopBaseMatrixIndex);
    }

    final DoubleAdjacencyConstantMatrix allMatrix = new DoubleAdjacencyConstantMatrix(matrix).transpose().createAdjacencyConstantMatrix();
    final DoubleAdjacencyConstantMatrix baseMatrix = allMatrix.getLoopMatrix((ArrayList<Integer>)selfLoopNumbers);
    final DoubleAdjacencyConstantMatrix subtractedMatrix = baseMatrix.subtractFromUnit();

    final DoubleAdjacencyConstantMatrix inverseLoopMatrix;
    try {
      inverseLoopMatrix = subtractedMatrix.inverse();
    } catch (@SuppressWarnings("unused") RuntimeException e) {
      //if (this.requiringABCD) {
      return false;
      //}
      //throw new RuntimeException(Messages.getString("AdjacencyMatrix.8")); //$NON-NLS-1$
    }

    for (int row = selfLoopNumbers.get(0); row < selfLoopNumbers.get(0) + selfLoopNumbers.size(); row++) {
      for (int column = selfLoopNumbers.get(0); column < selfLoopNumbers.get(0) + selfLoopNumbers.size(); column++) {
        matrix[row][column] = DoubleZeroSystem.getInstance();
      }
    }

    final DoubleAdjacencyConstantMatrix multiplier = (new DoubleAdjacencyConstantMatrix(matrix).getSubMatrix(1, matrix.length, selfLoopNumbers.get(0) + 1, selfLoopNumbers.get(0) + selfLoopNumbers.size())
        .transpose()).createAdjacencyConstantMatrix();

    final DoubleAdjacencyConstantMatrix multipliedMatrix = inverseLoopMatrix.multiply(multiplier);
    final DoubleAdjacencyConstantMatrix answer = multipliedMatrix.transpose().createAdjacencyConstantMatrix();

    for (int row = 0; row < matrix.length; row++) {
      for (int column = selfLoopNumbers.get(0); column < selfLoopNumbers.get(0) + selfLoopNumbers.size(); column++) {
        matrix[row][column] = answer.getElement(row + 1, column - selfLoopNumbers.get(0) + 1);
      }
    }
    return true;
  }

  /**
   * 線形システムまたは定数システムに定数行列を左から掛けた結果を求めます。
   * 
   * @param element 隣接行列
   * @param gain 定数行列
   * @return 線形システムまたは定数システムに定数行列を左から掛けた結果
   */
  static DoubleSystemOperator leftMultiplyGain(final DoubleSystemOperator element, final DoubleMatrix gain) {
    final DoubleSystemOperator newSystem;
    if (element instanceof DoubleConstantSystem) {
      final DoubleMatrix gain1 = ((DoubleConstantSystem)element).getGain();
      newSystem = new DoubleConstantSystem(gain.multiply(gain1));
    } else {
      newSystem = new DoubleContinuousLinearDynamicSystem(element.getLinearSystem().leftMultiply(gain));
    }
    return newSystem;
  }

  /**
   * (列方向に)定数システムを前方のシステムに結合(定数直列エッジを縮約)します。
   * 
   * @param onlyUnit 単位システムのみを縮約するならばtrue
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 縮約したならばtrue、そうでなければfalse
   */
  private boolean contractConstantEdgeForwardColumnWise(final boolean onlyUnit, final boolean onlyConstant) {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;

    boolean debugMode = false;
    boolean changed = false;

    showMatrix(matrix, debugMode, "start"); //$NON-NLS-1$

    for (int column = 0; column < localColumnSize; column++) {
      boolean hasSelfLoop = false;
      if (matrix[column][column] != DoubleZeroSystem.getInstance()) {
        hasSelfLoop = true;
      }
      if (matrix[column][column] instanceof DoubleConstantSystem && matrix[column][column].getLinearSystem().getD().isZero() == false) {
        hasSelfLoop = true;
      }

      showMatrix(matrix, debugMode, "change column"); //$NON-NLS-1$

      if (hasSelfLoop) {
        if (debugMode) {
          System.out.println("!detect self loop!"); //$NON-NLS-1$
        }
        continue;
      }

      for (int row = 0; row < localRowSize; row++) {
        if (!(matrix[row][column] instanceof DoubleConstantSystem)) {
          continue;
        }

        if (onlyUnit && !(matrix[row][column] instanceof DoubleUnitSystem)) {
          continue;
        }

        setupInputSizeAndOutputSize(matrix[row][column], row, column);

        if (!DoubleAdjacencyMatrixUtil.isAllZeroInColumn(matrix, row, column) && !DoubleAdjacencyMatrixUtil.isAllConstantOrZeroInRow(matrix, column, onlyConstant)) {
          continue;
        }

        if (!DoubleAdjacencyMatrixUtil.isEitherRowZeroOrBothConstant(matrix, row, column, onlyConstant)) {
          continue;
        }

        if (!DoubleAdjacencyMatrixUtil.isAllLinearOrConstnatOrZeroInRow(matrix, column, onlyConstant)) {
          continue;
        }

        if (this.requiringLinearSystem && DoubleAdjacencyMatrixUtil.hasIntegratorOrUnitDelayInRow(matrix, column)) {
          continue;
        }

        final DoubleConstantSystem gainSystem = (DoubleConstantSystem)matrix[row][column];

        if (onlyConstant && gainSystem.isVariable()) {
          continue;
        }

        boolean allChanged = true;
        boolean changed1 = false;
        for (int k = 0; k < localColumnSize; k++) {

          final DoubleSystemOperator systemInColumn = matrix[column][k];

          if (systemInColumn == DoubleZeroSystem.getInstance()) {
            continue;
          }

          final DoubleSystemOperator systemInRow = matrix[row][k];

          if (systemInRow instanceof DoubleConstantSystem && systemInColumn instanceof DoubleConstantSystem) {
            if (onlyConstant) {
              if (((DoubleConstantSystem)systemInRow).isVariable() || ((DoubleConstantSystem)systemInColumn).isVariable()) {
                allChanged = false;
                continue;
              }
            }

            setupInputSizeAndOutputSize(systemInRow, row, k);
            setupInputSizeAndOutputSize(systemInColumn, column, k);

            if (systemInRow.isSizeDefined() == false || systemInColumn.isSizeDefined() == false) {
              allChanged = false;
              continue;
            }

            if (gainSystem instanceof DoubleUnitSystem) {
              matrix[row][k] = ((DoubleConstantSystem)systemInRow).add(((DoubleConstantSystem)systemInColumn));
            } else if (gainSystem.isSizeDefined()) {
              matrix[row][k] = ((DoubleConstantSystem)systemInRow).add(((DoubleConstantSystem)systemInColumn).multiply(gainSystem));
            } else {
              allChanged = false;
              continue;
            }
          } else {
            assert systemInRow == DoubleZeroSystem.getInstance() : Messages.getString("AdjacencyMatrix.1"); //$NON-NLS-1$
            assert systemInColumn instanceof DoubleLinearSystemOperator : Messages.getString("AdjacencyMatrix.2"); //$NON-NLS-1$

            if (this.requiringLinearSystem && (systemInColumn instanceof DoubleIntegratorSystem || systemInColumn instanceof DoubleUnitDelaySystem)) {
              throw new RuntimeException(Messages.getString("AdjacencyMatrix.3")); //$NON-NLS-1$
            }

            setupInputSizeAndOutputSize(systemInColumn, column, k);

            if (gainSystem instanceof DoubleUnitSystem) {
              matrix[row][k] = systemInColumn;
            } else if (systemInColumn instanceof DoubleUnitSystem) {
              matrix[row][k] = gainSystem;
            } else if (systemInColumn instanceof DoubleConstantSystem && systemInColumn.isSizeDefined()) {
              if (onlyConstant && ((DoubleConstantSystem)systemInColumn).isVariable()) {
                allChanged = false;
                continue;
              }

              matrix[row][k] = ((DoubleConstantSystem)systemInColumn).multiply(gainSystem);
            } else {
              allChanged = false;
              continue;
            }
          }

          changed1 = true;
          showMatrix(matrix, debugMode, "changed"); //$NON-NLS-1$

          if (DoubleAdjacencyMatrixUtil.isAllZeroInColumn(matrix, row, column)) {
            matrix[column][k] = DoubleZeroSystem.getInstance();

            showMatrix(matrix, debugMode, "remove multiplicand"); //$NON-NLS-1$
          }
        }

        if (changed1 && allChanged) {
          changed = true;
          matrix[row][column] = DoubleZeroSystem.getInstance();
          showMatrix(matrix, debugMode, "remove multiplier"); //$NON-NLS-1$
        }
      }
    }
    return changed;
  }

  /**
   * ループを形成するノードを特定し,それぞれのループのノードの番号をリストで返します.
   * 
   * @return List&lt; Integer &gt; ループ毎のノード番号のリスト
   */
  @SuppressWarnings("boxing")
  private List<Integer> detectLoopNode() {
    final DoubleSystemOperator[][] matrix = this.elements;
    final DoubleAdjacencyMatrix tmpAdjMatrix = createClone();

    tmpAdjMatrix.setIntegratorToZeroSystem();
    final List<List<Integer>> localMaximumCycles = getLocalMaximumCycles(tmpAdjMatrix);
    final List<List<Integer>> sortedLocalMaximumCycle = getSortedLocalMaximumCycle(tmpAdjMatrix, localMaximumCycles);

    for (Iterator<List<Integer>> iterator = sortedLocalMaximumCycle.iterator(); iterator.hasNext();) {
      final List<Integer> loopPath = iterator.next();
      final Integer node = loopPath.get(0);

      final DoubleConstantSystem weight;
      if (loopPath.size() == 1) {
        weight = (DoubleConstantSystem)matrix[node][node];
      } else {
        weight = contractLoopNode(node, new ArrayList<>(loopPath), null);
      }

      if (weight == null) {
        continue;
      }
      
      try {
        weight.unaryMinus().add(new DoubleUnitSystem(weight.getOutputSize())).inverse();
      } catch (@SuppressWarnings("unused") Exception e) {
        continue;
      }
      iterator.remove();
    }

    return getLoopStartNode(matrix, sortedLocalMaximumCycle);
  }

  /**
   * {@link CycleMatrix#getLocalMaximumCycles()}の結果から各要素毎に1を引いた結果をリストで返します。
   * 
   * @param tmpAdjMatrix 仮の隣接行列
   * @return 通常のgetLocalMaximumCyclesから各要素毎に1を引いたList
   */
  @SuppressWarnings("boxing")
  private List<List<Integer>> getLocalMaximumCycles(final DoubleAdjacencyMatrix tmpAdjMatrix) {
    final List<List<Integer>> localMaximumCycles = new DoubleCycleMatrix(tmpAdjMatrix).getLocalMaximumCycles();
    for (List<Integer> localCycle : localMaximumCycles) {
      for (int i = 0; i < localCycle.size(); i++) {
        localCycle.set(i, localCycle.get(i) - 1);
      }
    }
    return localMaximumCycles;
  }

  /**
   * それぞれの極大閉路のリストに対して,ループの外から入ってくるノードがリストの先頭に来るようにソートします.
   * 
   * @param tmpAdjMatrix 仮の隣接行列
   * @param localMaximumCycle 極大閉路
   * @return sortedLocalMaximumCycle ソーティングされた極大閉路のリスト
   */
  @SuppressWarnings("boxing")
  private List<List<Integer>> getSortedLocalMaximumCycle(final DoubleAdjacencyMatrix tmpAdjMatrix, final List<List<Integer>> localMaximumCycle) {
    final List<List<Integer>> sortedLocalMaximumCycle = new ArrayList<>();
    for (List<Integer> localMaximumCycleList : localMaximumCycle) {
      final List<Integer> tmpList = new ArrayList<>();
      for (Integer node : localMaximumCycleList) {
        for (int row = 0; row < tmpAdjMatrix.elements[0].length; row++) {
          final DoubleSystemOperator targetNode = tmpAdjMatrix.elements[row][node];
          if (targetNode == DoubleZeroSystem.getInstance()) {
            continue;
          }
          if (localMaximumCycleList.contains(row)) {
            continue;
          }
          if (false == tmpList.contains(node)) {
            tmpList.add(0, node);
          }
        }
        if (false == tmpList.contains(node)) {
          tmpList.add(node);
        }
      }
      sortedLocalMaximumCycle.add(tmpList);
    }
    return sortedLocalMaximumCycle;
  }

  /**
   * @param matrix 隣接行列
   * @param localMaximumCycle 極大閉路
   * @return ループの開始ノード
   */
  @SuppressWarnings("boxing")
  private List<Integer> getLoopStartNode(final DoubleSystemOperator[][] matrix, final List<List<Integer>> localMaximumCycle) {
    final List<Integer> loopStartList = new ArrayList<>();
    final int localRowSize = matrix.length;
    for (List<Integer> loopPath : localMaximumCycle) {
      for (Integer node : loopPath) {
        for (int row = 0; row < localRowSize; row++) {
          if (false == matrix[node][row].isZero() && false == loopPath.contains(Integer.valueOf(row)) && false == loopStartList.contains(Integer.valueOf(node))) {
            loopStartList.add(Integer.valueOf(node));
          }
        }
      }
    }
    return loopStartList;
  }

  /**
   */
  private void setIntegratorToZeroSystem() {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;
    for (int column = 0; column < localColumnSize; column++) {
      for (int row = 0; row < localRowSize; row++) {
        if (this.getElement(row + 1, column + 1) instanceof DoubleIntegratorSystem) {
          this.elements[row][column] = DoubleZeroSystem.getInstance();
        }
      }
    }
  }

  /**
   * ループを構成するノードを縮約し,その重みを返します.
   * 
   * @param node 対象のノード
   * @param loopList ループを構成するノードのリスト
   * @param weight 対象の重み
   * @return ConstantSystem ループを構成するノードの重み
   */
  @SuppressWarnings("boxing")
  private DoubleConstantSystem contractLoopNode(final Integer node, final List<Integer> loopList, final DoubleConstantSystem weight) {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;
    DoubleConstantSystem weights = weight;
    if (loopList.isEmpty()) return weights;

    for (int column = 0; column < localColumnSize; column++) {

      if (node != column && matrix[node][column].isZero() == false && loopList.contains(column)) {
        if (false == (matrix[node][column] instanceof DoubleConstantSystem)) {
          //continue;
          return null;
        }

        final DoubleConstantSystem targetNode = (DoubleConstantSystem)matrix[node][column];
        if (weight == null) {
          loopList.remove(Integer.valueOf(node));
          weights = contractLoopNode(column, loopList, targetNode.clone());
        } else {
          loopList.remove(Integer.valueOf(node));
          if (targetNode instanceof DoubleUnitSystem) {
            weights = contractLoopNode(column, loopList, weight);
            continue;
          }
          if (targetNode instanceof DoubleNegativeUnitSystem) {
            weights = contractLoopNode(column, loopList, weight.unaryMinus());
            continue;
          }

          weights = contractLoopNode(column, loopList, targetNode.clone().multiply(weight));
        }
      }
    }
    return weights;
  }

  /**
   * 与えられたシステムの入出力数が決定されていなければ、入出力数を設定します。
   * 
   * @param system 対象となるシステム
   * @param inputNode 入力ノード
   * @param outputNode 出力ノード
   */
  private void setupInputSizeAndOutputSize(final DoubleSystemOperator system, int inputNode, int outputNode) {
    if (system.getInputSize() == -1) {
      final int inputSize = findNodeSize(inputNode);
      if (inputSize != -1) {
        system.setInputSize(inputSize);
      }
    }
    if (system.getOutputSize() == -1) {
      final int outputSize = findNodeSize(outputNode);
      if (outputSize != -1) {
        system.setOutputSize(outputSize);
      }
    }
  }

  /**
   * ノードの信号の大きさを返します。
   * 
   * @param node ノード番号
   * @return ノードの信号の大きさ
   */
  private int findNodeSize(final int node) {
    for (int column = 0; column < getColumnSize(); column++) {
      final DoubleSystemOperator system = this.elements[node][column];
      if (system == null || system == DoubleZeroSystem.getInstance()) {
        continue;
      }

      if (system.getInputSize() != -1) {
        return system.getInputSize();
      }
    }

    for (int row = 0; row < getRowSize(); row++) {
      final DoubleSystemOperator system = this.elements[row][node];
      if (system == null || system == DoubleZeroSystem.getInstance()) {
        continue;
      }

      if (system.getOutputSize() != -1) {
        return system.getOutputSize();
      }
    }

    return -1;
  }

  /**
   * 行と列の全ての成分がゼロの場合、その行とその列を削除します。
   * 
   * @return 成分がゼロの行と列が削除された行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix removeZeroRowColumn() {
    DoubleSystemOperator[][] newElements = new DoubleSystemOperator[getRowSize()][getColumnSize()];

    final BooleanMatrix mat = new BooleanMatrix(getRowSize(), getColumnSize());
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        DoubleSystemOperator systemOperator = this.elements[row][column];
        if (systemOperator != DoubleZeroSystem.getInstance()) {
          if (systemOperator instanceof DoubleConstantSystem && false == (systemOperator.getLinearSystem() == null)) {
            if (systemOperator.getLinearSystem().getD().isZero() == false) mat.setElement(row + 1, column + 1, true);
          } else {
            mat.setElement(row + 1, column + 1, true);
          }
        }
        newElements[row][column] = systemOperator;
      }
    }

    final BooleanMatrix anyInput = mat.anyTrueRowWise().transpose();
    final BooleanMatrix anyOutput = mat.anyTrueColumnWise();
    final IntMatrix noInputNoOutput = anyInput.orElementWise(anyOutput).notElementWise().find();

    final int removingSize = noInputNoOutput.length();
    int[] removingNodes = null;
    if (removingSize > 0) {
      removingNodes = new int[removingSize];
      for (int i = 0; i < removingSize; i++) {
        removingNodes[i] = noInputNoOutput.getIntElement(i + 1) - 1; // GridUtilのインデックスは0から
      }

      newElements = GridUtil.removeColumnVectors(newElements, removingNodes);
      newElements = GridUtil.removeRowVectors(newElements, removingNodes);

      for (int removingNode : removingNodes) {
        if (this.inputNodes.contains(removingNode + 1)) {
          this.inputNodes.remove(Integer.valueOf(removingNode + 1));
          this.inputPortTags.remove(Integer.valueOf(removingNode + 1));
        }
        if (this.outputNodes.contains(removingNode + 1)) {
          this.outputNodes.remove(Integer.valueOf(removingNode + 1));
          this.outputPortTags.remove(Integer.valueOf(removingNode + 1));
        }
      }
    }

    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(newElements);
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    if (removingSize > 0) {
      ans.shiftInputNodeByRemoving(removingNodes);
      ans.shiftSourceNodeByRemoving(removingNodes);
      ans.shiftOutputNodeByRemoving(removingNodes);
      ans.shiftSinkNodeByRemoving(removingNodes);
    }

    return ans;
  }

  /**
   * 線形動的システムを状態空間表現による隣接行列に換えて拡張した行列を生成します。
   * 
   * @return 線形動的システムを状態空間表現による隣接行列に換えて拡張した行列
   */
  DoubleAdjacencyMatrix expandLinearDynamicSystem() {
    final DoubleSystemOperator[][] blockMatrix = DoubleAdjacencyMatrixUtil.replaceLinearDynamicSystemWithBLockSystem(this.elements, this.isRequiringPrimitiveExpression());
    final DoubleAdjacencyMatrix blockSystem = new DoubleAdjacencyMatrix(blockMatrix);
    blockSystem.setInputNodes(new ArrayList<>(this.inputNodes));
    blockSystem.setSourceNodes(new ArrayList<>(this.sourceNodes));
    blockSystem.setOutputNodes(new ArrayList<>(this.outputNodes));
    blockSystem.setSinkNodes(new ArrayList<>(this.sinkNodes));
    blockSystem.setInputPortTags(new TreeMap<>(this.inputPortTags));
    blockSystem.setOutputPortTags(new TreeMap<>(this.outputPortTags));
    blockSystem.setRequiringLinearSystem(this.requiringLinearSystem);
    blockSystem.setRequiringDescriptor(this.requiringDescriptor);
    blockSystem.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
    //    blockSystem.setInputNodes(this.inputNodes);
    //    blockSystem.setSourceNodes(this.sourceNodes);
    //    blockSystem.setOutputNodes(this.outputNodes);
    //    blockSystem.setSinkNodes(this.sinkNodes);
    //    blockSystem.setInputPortTags(this.inputPortTags);
    //    blockSystem.setOutputPortTags(this.outputPortTags);
    //    blockSystem.setRequiringLinearSystem(this.requiringLinearSystem);
    //    blockSystem.setRequiringDescriptor(this.requiringDescriptor);
    //    blockSystem.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    return blockSystem.expandAllBlockSystem();
  }

  /**
   * 全てのブロックシステム成分を展開したシステムの隣接行列を生成します。
   * 
   * @return 全てのブロックシステム成分を展開したシステムの隣接行列
   */
  DoubleAdjacencyMatrix expandAllBlockSystem() {
    DoubleAdjacencyMatrix oldSystem = this;
    DoubleAdjacencyMatrix newSystem = oldSystem.expandBlockSystem();
    while (newSystem != oldSystem) {
      oldSystem = newSystem;
      newSystem = oldSystem.expandBlockSystem();
    }

    return newSystem;
  }

  /**
   * ブロックシステム成分を拡張したブロックシステムを生成します。
   * 
   * @return 拡張された場合、ブロックシステム成分を拡張したブロックシステム。拡張されない場合、同一システム。
   */
  private DoubleAdjacencyMatrix expandBlockSystem() {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int size = matrix.length;

    for (int row = 0; row < size; row++) {
      for (int column = 0; column < size; column++) {
        if (matrix[row][column] instanceof DoubleBlockSystem) {
          final DoubleBlockSystem blockSystem = (DoubleBlockSystem)matrix[row][column];
          if (blockSystem.isSingleSystem()) {
            matrix[row][column] = blockSystem.getSingleSystem();
            continue;
          }
          return createExpandedSystem(blockSystem, row, column);
        }
      }
    }

    return this;
  }

  /**
   * ブロックシステム成分を展開したシステムを返します。
   * 
   * @param blockSystem ブロックシステム成分
   * @param row ブロックシステム成分の行番号(0から始まります)
   * @param column ブロックシステム成分の列番号(0から始まります)
   * @return ブロックシステム成分を展開したシステム
   */
  private DoubleAdjacencyMatrix createExpandedSystem(final DoubleBlockSystem blockSystem, final int row, final int column) {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int inputNodeSize = blockSystem.getInputNodeSize();
    final int outputNodeSize = blockSystem.getOutputNodeSize();
    final int inputOutputSize = Math.min(inputNodeSize, outputNodeSize);

    final int blockSize = blockSystem.getNodeSize() - inputOutputSize; // 最初のinputNodeSize列と最後のoutputNodeSize行は全て零成分

    if (blockSize < 2) {
      throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.4")); //$NON-NLS-1$
    }

    final int min = column - outputNodeSize + 1;
    final int max = min + blockSize - 2;

    final DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertRowAndColumn(matrix, min, max);
    final List<List<Integer>> sourceNodesSinkNodes = DoubleBlockSystem.setBlockMatrix(newMatrix, row, column, blockSystem);
    final List<Integer> insertedSourceNodes = sourceNodesSinkNodes.get(0);
    final List<Integer> insertedSinkNodes = sourceNodesSinkNodes.get(1);

    final int[] insertingNodes = new int[blockSize - 1];
    for (int i = 0; i < blockSize - 1; i++) {
      insertingNodes[i] = column + i;
    }

    final DoubleAdjacencyMatrix ans = updateExpandedAdjacencyMatrix(newMatrix, insertingNodes, insertingNodes);

    ans.sourceNodes.addAll(insertedSourceNodes);
    ans.sinkNodes.addAll(insertedSinkNodes);

    return ans;
  }

  /**
   * システムのIDをノード番号の変更に対応させるために修正します。
   */
  private void modifyID() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == null || system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        system.setID("" + row + "," + column); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  /**
   * {@link DoubleAdjacencyMatrix}の内部データである{@link SystemOperator}の2次元配列を返します。
   * 
   * @return 隣接行列({@link SystemOperator}の2次元配列)
   */
  DoubleSystemOperator[][] getSystemEquationArray() {
    return DoubleAdjacencyMatrixUtil.createClone(this.elements);
  }

  /**
   * 積分器を下側へソートします。
   * 
   * @return 積分器を下側へソートした隣接行列
   */
  private DoubleAdjacencyMatrix sortIntegratorLower() {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);
    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    final int inputNodeSize = ans.inputNodes.size();
    final int numberOfIntegrator = ans.getIntegratorOrUnitDelaySystemSize();

    for (int row = 0; row < inputNodeSize + numberOfIntegrator; row++) {
      ans.moveIntegratorLower(row);
    }

    for (int row = inputNodeSize + numberOfIntegrator * 2; row < ans.getRowSize(); row++) {
      ans.moveIntegratorLower(row);
    }

    return ans;
  }

  /**
   * 積分器が指定された行にあれば、下側へ移動する。
   * 
   * @param row 対象となる行番号
   */
  private void moveIntegratorLower(int row) {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int outputNodeSize = this.outputNodes.size();
    final int numberOfIntegrator = this.getIntegratorOrUnitDelaySystemSize();

    if (DoubleAdjacencyMatrixUtil.hasIntegratorOrUnitDelayInRow(matrix, row) == false) {
      return;
    }

    final int newRow = DoubleAdjacencyMatrixUtil.findRowWithoutIntegrator(matrix, numberOfIntegrator, outputNodeSize);
    if (newRow < 0) {
      throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.5")); //$NON-NLS-1$
    }

    DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, row, newRow);
    exchangeInputNode(row, newRow);
    exchangeOutputNode(row, newRow);
  }

  /**
   * 積分器を左側へソートします。
   * 
   * @return 積分器を左側へソートした隣接行列
   */
  private DoubleAdjacencyMatrix sortIntegratorLeft() {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);
    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    final int inputNodeSize = ans.inputNodes.size();
    final int numberOfIntegrator = ans.getIntegratorOrUnitDelaySystemSize();

    for (int column = 0; column < inputNodeSize; column++) {
      ans.moveIntegratorLeft(column);
    }

    for (int column = inputNodeSize + numberOfIntegrator; column < ans.getColumnSize(); column++) {
      ans.moveIntegratorLeft(column);
    }

    return ans;
  }

  /**
   * 積分器が指定された列にあれば、左側へ移動する。
   * 
   * @param column 対象となる列番号
   */
  private void moveIntegratorLeft(final int column) {
    final DoubleSystemOperator[][] matrix = this.elements;
    final int inputNodeSize = this.inputNodes.size();
    final int numberOfIntegrator = this.getIntegratorOrUnitDelaySystemSize();

    if (DoubleAdjacencyMatrixUtil.hasIntegratorOrUnitDelayInColumn(matrix, column) == false) {
      return;
    }

    final int newColumn = DoubleAdjacencyMatrixUtil.findColumnWithoutIntegrator(matrix, numberOfIntegrator, inputNodeSize);
    if (newColumn < 0) {
      throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.6")); //$NON-NLS-1$
    }

    DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, column, newColumn);
    exchangeInputNode(column, newColumn);
    exchangeOutputNode(column, newColumn);
  }

  /**
   * 積分器が適正な場所に対角に並ぶようソートした隣接行列を生成します。
   * 
   * @return 積分器が適正な場所に対角に並ぶようソートした隣接行列
   */
  private DoubleAdjacencyMatrix sortIntegratorIntoDiagonal() {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);
    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    final int inputNodeSize = ans.inputNodes.size();
    final int outputNodeSize = ans.outputNodes.size();

    final int integratorSize = getIntegratorOrUnitDelaySystemSize();
    final int size = ans.getRowSize();

    for (int i = 0; i < integratorSize; i++) {
      final int row = size - outputNodeSize - integratorSize + i;
      final int column = DoubleAdjacencyMatrixUtil.findIntegratorOrUnitDelayInRow(matrix, row);
      if (column == -1) {
        throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.7")); //$NON-NLS-1$
      }
      if (column == inputNodeSize + i) {
        continue;
      }

      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, column, inputNodeSize + i);
    }

    return ans;
  }

  /**
   * 入力ノードの番号が該当すれば、入力ノードの番号を交換します。
   * 
   * @param node1 ノード番号1
   * @param node2 ノード番号2
   */
  @SuppressWarnings("boxing")
  private void exchangeInputNode(final int node1, final int node2) {
    final int size = this.inputNodes.size();
    for (int i = 0; i < size; i++) {
      if (this.inputNodes.get(i) == node1 + 1) {
        final int inputNode = this.inputNodes.get(i);
        final String tag = this.inputPortTags.get(inputNode);

        this.inputNodes.set(i, node2 + 1);
        this.inputPortTags.remove(inputNode);
        this.inputPortTags.put(node2 + 1, tag);
        continue;
      }
      if (this.inputNodes.get(i) == node2 + 1) {
        final int inputNode = this.inputNodes.get(i);
        final String tag = this.inputPortTags.get(inputNode);

        this.inputNodes.set(i, node1 + 1);
        this.inputPortTags.remove(inputNode);
        this.inputPortTags.put(node1 + 1, tag);
      }
    }
  }

  /**
   * 出力ノードの番号が該当すれば、出力ノードの番号を交換します。
   * 
   * @param node1 ノード番号1
   * @param node2 ノード番号2
   */
  @SuppressWarnings("boxing")
  private void exchangeOutputNode(final int node1, final int node2) {
    final int size = this.outputNodes.size();
    for (int i = 0; i < size; i++) {
      if (this.outputNodes.get(i) == node1 + 1) {
        final int outputNode = this.outputNodes.get(i);
        final String tag = this.outputPortTags.get(outputNode);

        this.outputNodes.set(i, node2 + 1);
        this.outputPortTags.remove(outputNode);
        this.outputPortTags.put(node2 + 1, tag);
        continue;
      }
      if (this.outputNodes.get(i) == node2 + 1) {
        final int outputNode = this.outputNodes.get(i);
        final String tag = this.outputPortTags.get(outputNode);

        this.outputNodes.set(i, node1 + 1);
        this.outputPortTags.remove(outputNode);
        this.outputPortTags.put(node1 + 1, tag);
      }
    }
  }

  /**
   * 挿入するノードの分だけ、入力ポートが接続されているノードの番号をシフトします。
   * 
   * @param insertingNodes 挿入するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftInputNodeByInserting(final int[] insertingNodes) {
    // 挿入するノードの番号を昇順にソート
    final int[] sortedInsertingNodes = new int[insertingNodes.length];
    System.arraycopy(insertingNodes, 0, sortedInsertingNodes, 0, insertingNodes.length);
    Arrays.sort(sortedInsertingNodes);

    final Map<Integer, String> originalInputPortTags = new TreeMap<>();
    for (final Integer key : this.inputPortTags.keySet()) {
      originalInputPortTags.put(key, this.inputPortTags.get(key));
    }

    final Map<Integer, String> newInputPortTags = new TreeMap<>();

    for (int i = 0; i < this.inputNodes.size(); i++) {
      final int inputNode = this.inputNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedInsertingNodes.length; j++) {
        final int node = sortedInsertingNodes[j];
        if (node + 1 <= inputNode) {
          shiftSize = sortedInsertingNodes.length - j;
          break;
        }
      }

      this.inputNodes.set(i, inputNode + shiftSize);
      final String tag = originalInputPortTags.get(inputNode);
      this.inputPortTags.remove(inputNode);
      newInputPortTags.put(inputNode + shiftSize, tag);
    }

    this.inputPortTags.putAll(newInputPortTags);
  }

  /**
   * 挿入するノードの分だけ、Sourceが接続されているノードの番号をシフトします。
   * 
   * @param insertingNodes 挿入するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftSourceNodeByInserting(final int[] insertingNodes) {
    // 挿入するノードの番号を昇順にソート
    final int[] sortedInsertingNodes = new int[insertingNodes.length];
    System.arraycopy(insertingNodes, 0, sortedInsertingNodes, 0, insertingNodes.length);
    Arrays.sort(sortedInsertingNodes);

    for (int i = 0; i < this.sourceNodes.size(); i++) {
      final int sourceNode = this.sourceNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedInsertingNodes.length; j++) {
        final int node = sortedInsertingNodes[j];
        if (node + 1 <= sourceNode) {
          shiftSize = sortedInsertingNodes.length - j;
          break;
        }
      }

      this.sourceNodes.set(i, sourceNode + shiftSize);
    }
  }

  /**
   * 挿入するノードの分だけ、出力ポートが接続されているノードの番号をシフトします。
   * 
   * @param insertingNodes 挿入するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftOutputNodeByInserting(final int[] insertingNodes) {
    // 挿入するノードの番号を昇順にソート
    final int[] sortedInsertingNodes = new int[insertingNodes.length];
    System.arraycopy(insertingNodes, 0, sortedInsertingNodes, 0, insertingNodes.length);
    Arrays.sort(sortedInsertingNodes);

    final Map<Integer, String> originalOutputPortTags = new TreeMap<>();
    for (final Integer key : this.outputPortTags.keySet()) {
      originalOutputPortTags.put(key, this.outputPortTags.get(key));
    }

    final Map<Integer, String> newOutputPortTags = new TreeMap<>();

    for (int i = 0; i < this.outputNodes.size(); i++) {
      final int outputNode = this.outputNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedInsertingNodes.length; j++) {
        final int node = sortedInsertingNodes[j];
        if (node + 1 <= outputNode) {
          shiftSize = sortedInsertingNodes.length - j;
          break;
        }
      }

      this.outputNodes.set(i, outputNode + shiftSize);
      final String tag = originalOutputPortTags.get(outputNode);
      this.outputPortTags.remove(outputNode);
      newOutputPortTags.put(outputNode + shiftSize, tag);
    }

    this.outputPortTags.putAll(newOutputPortTags);
  }

  /**
   * 挿入するノードの分だけ、Sinkが接続されているノードの番号をシフトします。
   * 
   * @param insertingNodes 挿入するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftSinkNodeByInserting(final int[] insertingNodes) {
    // 挿入するノードの番号を昇順にソート
    final int[] sortedInsertingNodes = new int[insertingNodes.length];
    System.arraycopy(insertingNodes, 0, sortedInsertingNodes, 0, insertingNodes.length);
    Arrays.sort(sortedInsertingNodes);

    for (int i = 0; i < this.sinkNodes.size(); i++) {
      final int sinkNode = this.sinkNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedInsertingNodes.length; j++) {
        final int node = sortedInsertingNodes[j];
        if (node + 1 <= sinkNode) {
          shiftSize = sortedInsertingNodes.length - j;
          break;
        }
      }

      this.sinkNodes.set(i, sinkNode + shiftSize);
    }
  }

  /**
   * 削除するノードの分だけ、入力ポートが接続されているノードの番号をシフトします。
   * 
   * @param removingNodes 削除するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftInputNodeByRemoving(final int[] removingNodes) {
    // 削除するノードの番号を昇順にソート
    final int[] sortedRemovingNodes = new int[removingNodes.length];
    System.arraycopy(removingNodes, 0, sortedRemovingNodes, 0, removingNodes.length);
    Arrays.sort(sortedRemovingNodes);

    final Map<Integer, String> originalInputPortTags = new TreeMap<>();
    for (final Integer key : this.inputPortTags.keySet()) {
      originalInputPortTags.put(key, this.inputPortTags.get(key));
    }

    final Map<Integer, String> newInputPortTags = new TreeMap<>();

    for (int i = 0; i < this.inputNodes.size(); i++) {
      final int inputNode = this.inputNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedRemovingNodes.length; j++) {
        final int node = sortedRemovingNodes[j];
        if (node + 1 <= inputNode) {
          shiftSize++;
        }
      }

      this.inputNodes.set(i, inputNode - shiftSize);
      final String tag = originalInputPortTags.get(inputNode);
      if (tag != null) {
        this.inputPortTags.remove(inputNode);
        newInputPortTags.put(inputNode - shiftSize, tag);
      }
    }

    this.inputPortTags.putAll(newInputPortTags);
  }

  /**
   * 削除するノードの分だけ、Sourceが接続されているノードの番号をシフトします。
   * 
   * @param removingNodes 削除するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftSourceNodeByRemoving(final int[] removingNodes) {
    // 削除するノードの番号を昇順にソート
    final int[] sortedRemovingNodes = new int[removingNodes.length];
    System.arraycopy(removingNodes, 0, sortedRemovingNodes, 0, removingNodes.length);
    Arrays.sort(sortedRemovingNodes);

    for (int i = 0; i < this.sourceNodes.size(); i++) {
      final int sourceNode = this.sourceNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedRemovingNodes.length; j++) {
        final int node = sortedRemovingNodes[j];
        if (node + 1 <= sourceNode) {
          shiftSize++;
        }
      }

      this.sourceNodes.set(i, sourceNode - shiftSize);
    }
  }

  /**
   * 削除するノードの分だけ、出力ポートが接続されているノードの番号をシフトします。
   * 
   * @param removingNodes 削除するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftOutputNodeByRemoving(final int[] removingNodes) {
    // 削除するノードの番号を昇順にソート
    final int[] sortedRemovingNodes = new int[removingNodes.length];
    System.arraycopy(removingNodes, 0, sortedRemovingNodes, 0, removingNodes.length);
    Arrays.sort(sortedRemovingNodes);

    final Map<Integer, String> originalOutputPortTags = new TreeMap<>();
    for (final Integer key : this.outputPortTags.keySet()) {
      originalOutputPortTags.put(key, this.outputPortTags.get(key));
    }

    final Map<Integer, String> newOutputPortTags = new TreeMap<>();

    for (int i = 0; i < this.outputNodes.size(); i++) {
      final int outputNode = this.outputNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedRemovingNodes.length; j++) {
        final int node = sortedRemovingNodes[j];
        if (node + 1 <= outputNode) {
          shiftSize++;
        }
      }

      this.outputNodes.set(i, outputNode - shiftSize);
      final String tag = originalOutputPortTags.get(outputNode);
      if (tag != null) {
        this.outputPortTags.remove(outputNode);
        newOutputPortTags.put(outputNode - shiftSize, tag);
      }
    }

    this.outputPortTags.putAll(newOutputPortTags);
  }

  /**
   * 削除するノードの分だけ、Sinkが接続されているノードの番号をシフトします。
   * 
   * @param removingNodes 削除するノードの番号(0から始まります)
   */
  @SuppressWarnings("boxing")
  private void shiftSinkNodeByRemoving(final int[] removingNodes) {
    // 削除するノードの番号を昇順にソート
    final int[] sortedRemovingNodes = new int[removingNodes.length];
    System.arraycopy(removingNodes, 0, sortedRemovingNodes, 0, removingNodes.length);
    Arrays.sort(sortedRemovingNodes);

    for (int i = 0; i < this.sinkNodes.size(); i++) {
      final int sinkNode = this.sinkNodes.get(i);
      int shiftSize = 0;
      for (int j = 0; j < sortedRemovingNodes.length; j++) {
        final int node = sortedRemovingNodes[j];
        if (node + 1 <= sinkNode) {
          shiftSize++;
        }
      }

      this.sinkNodes.set(i, sinkNode - shiftSize);
    }
  }

  /**
   * 隣接行列に含まれる積分器(または1サンプル遅れ器)の数を返します。
   * 
   * @return 隣接行列に含まれる積分器(または1サンプル遅れ器)の数
   */
  private int getIntegratorOrUnitDelaySystemSize() {
    int count = 0;
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        if (this.elements[row][column] instanceof DoubleIntegratorSystem) {
          count++;
        }
        if (this.elements[row][column] instanceof DoubleUnitDelaySystem) {
          count++;
        }
      }
    }

    return count;
  }

  /**
   * SourceブロックとSinkブロックにZeroSystemを代入します。
   */
  void setZeroToSourceAndSink() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system instanceof DoubleInputPort || system instanceof DoubleOutputPort) {
          continue;
        }

        if (system instanceof DoubleContinuousSource) {
          this.elements[row][column] = DoubleZeroSystem.getInstance();
          this.inputNodes.remove(Integer.valueOf(row + 1));
          for (int i = 0; i < getColumnSize(); i++) {
            this.elements[column][i] = DoubleZeroSystem.getInstance();
          }
        } else if (system instanceof DoubleContinuousSink) {
          this.elements[row][column] = DoubleZeroSystem.getInstance();
          this.outputNodes.remove(Integer.valueOf(column + 1));
          for (int i = 0; i < getRowSize(); i++) {
            this.elements[i][row] = DoubleZeroSystem.getInstance();
          }
        }
      }
    }
  }

  /**
   * 出力数が未定の多重器の出力数を設定します。
   */
  void setupUndefinedMultiplexer() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == null || system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoubleMultiplexer == false) {
          continue;
        }

        if (system.getOutputSize() != -1) {
          continue;
        }

        int outputSize = 0;
        boolean allInputSizeDefined = true;
        for (DoubleMultiplexer multiplexer : ((DoubleMultiplexer)system).getGroup().getMultiplexers()) {
          int oneInputSize = multiplexer.getInputSize();
          if (oneInputSize == -1) {
            allInputSizeDefined = false;
            break;
          }
          outputSize += oneInputSize;
        }
        if (allInputSizeDefined == false) {
          continue;
        }

        for (DoubleMultiplexer multiplexer : ((DoubleMultiplexer)system).getGroup().getMultiplexers()) {
          multiplexer.setOutputSize(outputSize);
        }
      }
    }

  }

  /**
   * 入力数が未定の分離器の入力数を設定します。
   */
  void setupUndefinedDeMultiplexer() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == null || system == DoubleZeroSystem.getInstance()) {
          continue;
        }

        if (system instanceof DoubleDeMultiplexer == false) {
          continue;
        }

        if (system.getInputSize() != -1) {
          continue;
        }

        int inputSize = 0;
        boolean allOutputSizeDefined = true;
        for (DoubleDeMultiplexer deMultiplexer : ((DoubleDeMultiplexer)system).getGroup().getDeMultiplexers()) {
          int oneOutputSize = deMultiplexer.getOutputSize();
          if (oneOutputSize == -1) {
            allOutputSizeDefined = false;
            break;
          }
          inputSize += oneOutputSize;
        }
        if (allOutputSizeDefined == false) {
          continue;
        }

        for (DoubleDeMultiplexer deMultiplexer : ((DoubleDeMultiplexer)system).getGroup().getDeMultiplexers()) {
          deMultiplexer.setInputSize(inputSize);
        }
      }
    }

  }

  /**
   * SourceブロックとSinkブロックの間にあるシステムを返します。
   * 
   * @return SourceブロックとSinkブロックの間にあるシステム
   */
  DoubleAdjacencyMatrix getSystemBetweenSourceAndSink() {
    final List<Integer> localSourceNodes = new ArrayList<>();
    final List<Integer> localSinkNodes = new ArrayList<>();

    //inputSourceNodes.addAll(this.inputNodes);
    //outputSinkNodes.addAll(this.outputNodes);

    localSourceNodes.addAll(this.sourceNodes);
    localSinkNodes.addAll(this.sinkNodes);

    if (localSourceNodes.size() == 0 || localSinkNodes.size() == 0) {
      return this;
    }

    final DoubleAdjacencyMatrix selectedSystem = getSystemBetweenNodes(localSourceNodes, localSinkNodes, false);
    final DoubleAdjacencyMatrix simplifiedSystem = selectedSystem.removeZeroRowColumn();
    
    simplifiedSystem.modifyID();
    return simplifiedSystem;
  }

  /**
   * 入力ノードから出力ノードまでの線形システムを返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * 
   * @return 入力ノードから出力ノードまでの線形システム
   */
  public DoubleLinearSystem getLinearSystem(final boolean requiringReachableSubSystem) {
    final DoubleAdjacencyMatrix allSystem = getLinearSystemFromInputToOutput(requiringReachableSubSystem);

    final int[] stateSizes = allSystem.getStateSizes();
    int stateSize = 0;
    for (int i = 0; i < stateSizes.length; i++) {
      stateSize += stateSizes[i];
    }

    final int inputSize = allSystem.getInputSize();
    final int outputSize = allSystem.getOutputSize();

    final DoubleMatrix a = allSystem.createNumericalA(stateSizes, stateSize);
    final DoubleMatrix b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final DoubleMatrix c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final DoubleMatrix d = allSystem.createNumericalD(inputSize, outputSize);
    final DoubleMatrix e = allSystem.createNumericalE(stateSizes, stateSize);

    final DoubleLinearSystem linearSystem = this.requiringDescriptor ? DoubleLinearSystemFactory.createLinearSystem(a, b, c, d, e, true) : DoubleLinearSystemFactory.createLinearSystem(a, b, c, d, e, false);

    allSystem.setInputOutputStateTagToLinearSystem(linearSystem, stateSizes);

    if (allSystem.isContinuous()) {
      linearSystem.setTimeDomainType(TimeDomainType.CONTINUOUS);
    } else if (isDiscrete()) {
      linearSystem.setTimeDomainType(TimeDomainType.DISCRETE);
    } else {
      linearSystem.setTimeDomainType(TimeDomainType.SAMPLED);
    }
    return linearSystem;
  }

  /**
   * 入力ノードから出力ノードまでの線形システムを返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @param processor プロセッサ
   * 
   * @return 入力ノードから出力ノードまでの線形システム
   */
  public DoubleLinearSystem getLinearSystemForMaxima(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final DoubleAdjacencyMatrix allSystem = getLinearSystemFromInputToOutput(requiringReachableSubSystem);

    final int[] stateSizes = allSystem.getStateSizes();
    int stateSize = 0;
    for (int i = 0; i < stateSizes.length; i++) {
      stateSize += stateSizes[i];
    }

    final int inputSize = allSystem.getInputSize();
    final int outputSize = allSystem.getOutputSize();

    final DoubleMatrix a = allSystem.createNumericalA(stateSizes, stateSize);
    final DoubleMatrix b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final DoubleMatrix c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final DoubleMatrix d = allSystem.createNumericalD(inputSize, outputSize);

    final DoubleLinearSystem linearSystem = DoubleLinearSystemFactory.createLinearSystem(a, b, c, d);

    final String[][] aSymbol = allSystem.createSymbolicA(stateSizes, processor);
    final String[][] bSymbol = allSystem.createSymbolicB(stateSizes, processor);
    final String[][] cSymbol = allSystem.createSymbolicC(stateSizes, processor);
    final String[][] dSymbol = allSystem.createSymbolicD(processor);

    try (PrintWriter writer = new PrintWriter(new FileWriter("test.txt"))) { //$NON-NLS-1$
      writer.print("A = ["); //$NON-NLS-1$
      writer.flush();
      createDeclearMM(aSymbol, writer);
      writer.print("B = ["); //$NON-NLS-1$
      writer.flush();
      createDeclearMM(bSymbol, writer);
      writer.print("C = ["); //$NON-NLS-1$
      writer.flush();
      createDeclearMM(cSymbol, writer);
      writer.print("D = ["); //$NON-NLS-1$
      writer.flush();
      createDeclearMM(dSymbol, writer);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }

    linearSystem.setSymbolicString(aSymbol, bSymbol, cSymbol, dSymbol, null);

    allSystem.setInputOutputStateTagToLinearSystem(linearSystem, stateSizes);

    if (allSystem.isContinuous()) {
      linearSystem.setTimeDomainType(TimeDomainType.CONTINUOUS);
    } else if (isDiscrete()) {
      linearSystem.setTimeDomainType(TimeDomainType.DISCRETE);
    } else {
      linearSystem.setTimeDomainType(TimeDomainType.SAMPLED);
    }
    return linearSystem;
  }

  /**
   * @param symbol シンボルの配列
   * @param writer 出力先
   */
  private void createDeclearMM(final String[][] symbol, PrintWriter writer) {
    for (int i = 0; i < symbol.length; i++) {
      if ((symbol.length == 1) == false) {
        writer.print("["); //$NON-NLS-1$
      }
      for (int j = 0; j < symbol[0].length; j++) {
        writer.print(symbol[i][j]);
        writer.flush();
        if ((j == symbol[0].length - 1) == false) {
          writer.print(","); //$NON-NLS-1$
        }
      }
      if ((symbol.length == 1) == false) {
        writer.print("]"); //$NON-NLS-1$
      }
    }
    writer.print("];"); //$NON-NLS-1$
    writer.print(System.getProperty("line.separator")); //$NON-NLS-1$
    writer.flush();
  }

  /**
   * 入力ノードから出力ノードまでの線形システムを返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @param processor 逆ポーランド記法のプロセッサー
   * 
   * @return 入力ノードから出力ノードまでの線形システム
   */
  public DoubleLinearSystem getLinearSystemByProcessor(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final DoubleAdjacencyMatrix allSystem = getLinearSystemFromInputToOutput(requiringReachableSubSystem);

    final int[] stateSizes = allSystem.getStateSizes();
    int stateSize = 0;
    for (int i = 0; i < stateSizes.length; i++) {
      stateSize += stateSizes[i];
    }

    final int inputSize = allSystem.getInputSize();
    final int outputSize = allSystem.getOutputSize();

    final DoubleMatrix a = allSystem.createNumericalA(stateSizes, stateSize);
    final DoubleMatrix b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final DoubleMatrix c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final DoubleMatrix d = allSystem.createNumericalD(inputSize, outputSize);
    final DoubleMatrix e = allSystem.createNumericalE(stateSizes, stateSize);

    final String[][] aSymbol = allSystem.createSymbolicA(stateSizes, processor);
    final String[][] bSymbol = allSystem.createSymbolicB(stateSizes, processor);
    final String[][] cSymbol = allSystem.createSymbolicC(stateSizes, processor);
    final String[][] dSymbol = allSystem.createSymbolicD(processor);
    final String[][] eSymbol = allSystem.createSymbolicE(stateSizes, processor);

    DoubleLinearSystem linearSystem;
    if (this.requiringDescriptor) {
      linearSystem = DoubleLinearSystemFactory.createLinearSystem(a, b, c, d, e, true);
    } else {
      linearSystem = DoubleLinearSystemFactory.createLinearSystem(a, b, c, d, e, false);
    }

    linearSystem.setSymbolicString(aSymbol, bSymbol, cSymbol, dSymbol, eSymbol);

    allSystem.setInputOutputStateTagToLinearSystem(linearSystem, stateSizes);

    if (allSystem.isContinuous()) {
      linearSystem.setTimeDomainType(TimeDomainType.CONTINUOUS);
    } else if (isDiscrete()) {
      linearSystem.setTimeDomainType(TimeDomainType.DISCRETE);
    } else {
      linearSystem.setTimeDomainType(TimeDomainType.SAMPLED);
    }
    return linearSystem;
  }

  /**
   * 入力ノードから出力ノードまでの線形システムを返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @return 入力ノードから出力ノードまでの線形システム
   */
  DoubleAdjacencyMatrix getLinearSystemFromInputToOutput(final boolean requiringReachableSubSystem) {
    setZeroToSourceAndSink();

    DoubleAdjacencyMatrix selectedSystem = this;
    if (requiringReachableSubSystem) {
      selectedSystem = getSystemBetweenNodes(this.inputNodes, this.outputNodes, true);
    }

    if (selectedSystem.isLinear() == false) {
      throw new RuntimeException(Messages.getString("AdjacencyMatrix.9")); //$NON-NLS-1$
    }

    final DoubleAdjacencyMatrix linearDynamicExpandedSystem = selectedSystem.expandLinearDynamicSystem();
    final DoubleAdjacencyMatrix expandedSystem = this.isRequiringPrimitiveExpression() == false ? linearDynamicExpandedSystem : linearDynamicExpandedSystem.expandAllConstantAndIntegratorSystem();
    expandedSystem.setRequiringLinearSystem(this.requiringLinearSystem);
    expandedSystem.setRequiringDescriptor(this.requiringDescriptor);
    expandedSystem.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    DoubleAdjacencyMatrix oldSystem = expandedSystem;
    DoubleAdjacencyMatrix contractedSystem = expandedSystem;
    do {
      oldSystem = contractedSystem;
      contractedSystem = oldSystem.contractConstantEdge(false, false);
    } while (contractedSystem != oldSystem);

    final DoubleAdjacencyMatrix lowerSortedSystem = contractedSystem.sortIntegratorLower();
    final DoubleAdjacencyMatrix leftSortedSystem = lowerSortedSystem.sortIntegratorLeft();
    final DoubleAdjacencyMatrix diagonalIntegratorSystem = leftSortedSystem.sortIntegratorIntoDiagonal();
    final DoubleAdjacencyMatrix inputOutputSortedSystem = diagonalIntegratorSystem.sortByInputNodeAndOutputNode();
    final DoubleAdjacencyMatrix allSystem = inputOutputSortedSystem.sortByStateNumber();

    allSystem.setDefaultSizeToUnknownPort();
    return allSystem;
  }

  /**
   * 数式伝達関数のために システムのすべての定数行列を最小の要素に分割した隣接行列を返します.
   * 
   * @return すべての定数行列を展開した隣接行列
   */
  DoubleAdjacencyMatrix expandAllConstantAndIntegratorSystem() {
    DoubleAdjacencyMatrix oldSystem = this.expandInputAndOutputNode();
    DoubleAdjacencyMatrix newSystem = oldSystem.expandConstantAndIntegratorSystem();
    while (newSystem != oldSystem) {
      oldSystem = newSystem;
      newSystem = oldSystem.expandConstantAndIntegratorSystem();
    }

    return newSystem;
  }

  /**
   * input,outputノードに繋る定数システムに応じて input,outputノードの数を拡張します.
   * 
   * @return 拡張されたinput,outputノードを持つ隣接行列
   */
  DoubleAdjacencyMatrix expandInputAndOutputNode() {
    return expandInput().expandOutput();
  }

  /**
   * inputノードに繋る定数システムに応じて inputノードの数を拡張します.
   * 
   * @return 拡張されたinputノードを持つ隣接行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix expandInput() {
    DoubleAdjacencyMatrix ans = this;

    List<Integer> tempInputNodes = this.inputNodes;
    List<Integer> tempSourceNodes = this.sourceNodes;
    List<Integer> tempOutputNodes = this.outputNodes;
    Map<Integer, String> tempInputPortTags = this.inputPortTags;
    Map<Integer, String> tempOutputPortTags = this.outputPortTags;

    int offset = 0; //前回の処理で挿入した列の数

    for (Integer input : this.inputNodes) {
      final DoubleSystemOperator[][] matrix = this.elements;
      final int inputNode = input - 1;

      int expandInputSize = getExpandInputSize(inputNode, matrix);
      if (expandInputSize == 0) continue;

      final DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertColumn(ans.elements, inputNode + 1, inputNode + expandInputSize);

      ans = new DoubleAdjacencyMatrix(newMatrix);
      ans.modifyID();
      ans.setInputNodes(tempInputNodes);
      ans.setSourceNodes(tempSourceNodes);
      ans.setOutputNodes(tempOutputNodes);
      ans.setInputPortTags(tempInputPortTags);
      ans.setOutputPortTags(tempOutputPortTags);
      ans.setRequiringLinearSystem(this.requiringLinearSystem);
      ans.setRequiringDescriptor(this.requiringDescriptor);
      ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

      final int[] insertingNodes = createInsertingNodesArray(offset, input, expandInputSize);
      ans.shiftInputNodeByInserting(insertingNodes);
      ans.shiftSourceNodeByInserting(insertingNodes);

      tempInputNodes = createInputNodes(ans, offset, input, expandInputSize);
      tempInputPortTags = createInputPortTags(ans, offset, input, expandInputSize);
      offset = expandInputSize;
    }

    return ans;
  }

  /**
   * 挿入対象のノードの配列を返します.
   * 
   * @param offset 挿入した列の数
   * @param input 挿入対象の列
   * @param expandInputSize 挿入した列の数
   * @return 挿入対象のノードの配列
   */
  @SuppressWarnings("boxing")
  private int[] createInsertingNodesArray(int offset, Integer input, int expandInputSize) {
    final int[] insertingNodes = new int[expandInputSize];
    for (int i = 0; i < expandInputSize; i++) {
      insertingNodes[i] = input + offset + i;
    }
    return insertingNodes;
  }

  /**
   * 拡張されるinputのサイズを返します.
   * 
   * @param inputNode 拡張対象のノード
   * @param matrix 隣接行列の要素
   * @return 拡張されるサイズ
   */
  private int getExpandInputSize(final int inputNode, final DoubleSystemOperator[][] matrix) {
    final int size = matrix[0].length;
    int expandInputSize = 0;
    for (int column = 0; column < size; column++) {
      final DoubleSystemOperator system = matrix[inputNode][column];
      if (system instanceof DoubleZeroSystem) continue;
      if (system.isSISO()) continue;
      expandInputSize = (system.getInputSize() - 1);
      break;
    }
    return expandInputSize;
  }

  /**
   * 入力ポートに対応するノードを拡張します.
   * 
   * @param ans 対象となる隣接行列
   * @param offset 隣接行列が拡張された分のオフセット
   * @param input 拡張対象の入力ポートのノード
   * @param expandInputSize 拡張された入力の数
   * @return 拡張された入力ポートのList
   */
  @SuppressWarnings("boxing")
  private List<Integer> createInputNodes(DoubleAdjacencyMatrix ans, int offset, Integer input, int expandInputSize) {
    final List<Integer> newInputNodes = new ArrayList<>();

    for (Integer node : ans.inputNodes) {
      newInputNodes.add(node);
      if (false == (node.equals(input + offset))) continue;
      for (int i = 1; i <= expandInputSize; i++) {
        newInputNodes.add(i + input + offset);
      }
    }
    ans.setInputNodes(newInputNodes);

    return newInputNodes;
  }

  /**
   * 入力ポートタグを拡張します.
   * 
   * @param ans 対象となる隣接行列
   * @param offset 隣接行列が拡張された分のオフセット
   * @param input 拡張対象の入力ポートのノード
   * @param expandInputSize 拡張された入力の数
   * @return 拡張された入力ポートタグのMap
   */
  @SuppressWarnings("boxing")
  private Map<Integer, String> createInputPortTags(DoubleAdjacencyMatrix ans, int offset, Integer input, int expandInputSize) {
    final Map<Integer, String> newInputPortTags = new HashMap<>();
    final String tag = ans.inputPortTags.get(input + offset);
    Map<Integer, String> tempInputPortTags;
    for (Integer node : ans.inputNodes) {
      if (node < input + expandInputSize) {
        if (node.equals(input + offset)) {
          newInputPortTags.put(node, tag + "_" + 1); //$NON-NLS-1$
          for (int i = 1; i <= expandInputSize; i++) {
            newInputPortTags.put(node + i, tag + "_" + (i + 1)); //$NON-NLS-1$
          }
          continue;
        }
        newInputPortTags.put(node, ans.inputPortTags.get(node));
      }
      if (node > input + offset + expandInputSize) {
        newInputPortTags.put(node, ans.inputPortTags.get(node));
      }
    }
    ans.setInputPortTags(newInputPortTags);
    tempInputPortTags = newInputPortTags;
    return tempInputPortTags;
  }

  /**
   * outputノードに繋る定数システムに応じて outputノードの数を拡張します.
   * 
   * @return 拡張されたoutputノードを持つ隣接行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix expandOutput() {
    DoubleAdjacencyMatrix ans = this;

    List<Integer> tempInputNodes = this.inputNodes;
    List<Integer> tempSourceNodes = this.sourceNodes;
    List<Integer> tempOutputNodes = this.outputNodes;
    List<Integer> tempSinkNodes = this.sinkNodes;
    Map<Integer, String> tempInputPortTags = this.inputPortTags;
    Map<Integer, String> tempOutputPortTags = this.outputPortTags;

    int offset = 0; //挿入した列の数

    for (Integer output : ans.outputNodes) {
      final int outputNode = output - 1;
      final DoubleSystemOperator[][] matrix = this.elements;
      final int size = matrix.length;

      int expandOutputSize = 0;
      for (int row = 0; row < size; row++) {
        final DoubleSystemOperator system = matrix[row][matrix[0].length - (size - outputNode)];
        if (system instanceof DoubleZeroSystem) {
          continue;
        }
        if (system.isSISO()) {
          continue;
        }
        expandOutputSize = (system.getOutputSize() - 1);
        break;
      }

      if (expandOutputSize == 0) {
        continue;
      }

      final DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertRow(ans.elements, matrix.length - expandOutputSize + 1, matrix.length);
      final int[] insertingNodes = createInsertingNodesArray(offset, output, expandOutputSize);

      ans = new DoubleAdjacencyMatrix(newMatrix);
      ans.modifyID();
      ans.setInputNodes(tempInputNodes);
      ans.setSourceNodes(tempSourceNodes);
      ans.setOutputNodes(tempOutputNodes);
      ans.setSinkNodes(tempSinkNodes);
      ans.setInputPortTags(tempInputPortTags);
      ans.setOutputPortTags(tempOutputPortTags);
      ans.setRequiringLinearSystem(this.requiringLinearSystem);
      ans.setRequiringDescriptor(this.requiringDescriptor);
      ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

      ans.shiftInputNodeByInserting(insertingNodes);
      ans.shiftSourceNodeByInserting(insertingNodes);
      ans.shiftOutputNodeByInserting(insertingNodes);
      ans.shiftSinkNodeByInserting(insertingNodes);

      final List<Integer> newOutputNodes = new ArrayList<>();
      for (Integer node : ans.outputNodes) {
        newOutputNodes.add(node);
        if (false == (node.equals(output + offset))) continue;
        for (int i = 1; i <= expandOutputSize; i++) {
          newOutputNodes.add(i + output + offset);
        }
      }
      ans.setOutputNodes(newOutputNodes);
      tempOutputNodes = newOutputNodes;

      final Map<Integer, String> newOutputPortTags = new HashMap<>();
      final String tag = ans.outputPortTags.get(output + offset);
      for (Integer node : ans.outputNodes) {
        if (node < output + offset + 1) {
          if (node.equals(output + offset)) {
            newOutputPortTags.put(node, tag + "_" + 1); //$NON-NLS-1$
            for (int i = 1; i <= expandOutputSize; i++) {
              newOutputPortTags.put(node + i, tag + "_" + (i + 1)); //$NON-NLS-1$
            }
            continue;
          }
          newOutputPortTags.put(node, ans.outputPortTags.get(node));
        }
        if (node > output + offset + expandOutputSize) {
          newOutputPortTags.put(node, ans.outputPortTags.get(node));
        }
      }
      ans.setOutputPortTags(newOutputPortTags);
      tempOutputPortTags = newOutputPortTags;

      offset = expandOutputSize;
    }

    return ans;
  }

  /**
   * 定数行列を最小の要素に分割した隣接行列を返します.
   * 
   * @return 定数行列を展開した隣接行列
   */
  private DoubleAdjacencyMatrix expandConstantAndIntegratorSystem() {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int rowSize = matrix.length;
    final int columnSize = matrix[0].length;

    for (int row = 0; row < rowSize; row++) {
      for (int column = 0; column < columnSize; column++) {
        if (false == matrix[row][column] instanceof DoubleConstantSystem && false == matrix[row][column] instanceof DoubleIntegratorSystem) {
          continue;
        }
        final DoubleSystemOperator system = matrix[row][column];
        if (system.isSISO()) {
          continue;
        }

        for (int k = 0; k < matrix[0].length; k++) {
          if (k == column) {
            continue;
          }
          if (false == matrix[row][k] instanceof DoubleConstantSystem && false == matrix[row][k] instanceof DoubleIntegratorSystem) {
            continue;
          }
          return createExpandUnitInRow(system, row);
        }

        for (int k = 0; k < matrix.length; k++) {
          if (k == row) {
            continue;
          }
          if (false == matrix[k][column] instanceof DoubleConstantSystem && false == matrix[k][column] instanceof DoubleIntegratorSystem) {
            continue;
          }
          return createExpandUnitInColumn(system, column);
        }

        return createExpandConstantAndIntegratorSystem(system, row, column);
      }
    }
    return this;
  }

  /**
   * 列中に含まれるすべての単位行列を展開します. 
   * まず,対応する列を拡張します.
   *  
   * 次に,列中の単位行列を順番に見つけ, 
   * その都度行を拡張してから単位行列を展開します.
   * 
   * @param system 展開対象の列で一番若い行のシステム
   * @param column 展開対象の列
   * @return 列中に含まれるすべての単位行列が展開された隣接行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix createExpandUnitInColumn(DoubleSystemOperator system, int column) {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int rowSize = system.getOutputSize();
    final int columnSize = system.getInputSize();

    DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertColumn(matrix, column + 1, column + columnSize - 1);

    int rowOffset = 0;

    final List<Integer> insertingRowNodeList = new ArrayList<>();

    for (int k = 0; k < newMatrix.length; k++) {
      if (false == newMatrix[k][column] instanceof DoubleConstantSystem && false == newMatrix[k][column] instanceof DoubleIntegratorSystem) {
        continue;
      }
      newMatrix = DoubleAdjacencyMatrixUtil.insertRow(newMatrix, k + 1, k + rowSize - 1);
      for (int i = k; i < k + rowSize + rowOffset; i++) {
        for (int j = column; j < column + columnSize; j++) {
          if (false == ((i - k) == (j - column))) {
            continue;
          }
          if (newMatrix[k][column] instanceof DoubleUnitSystem) {
            newMatrix[i][j] = new DoubleUnitSystem(1);
            continue;
          }
          newMatrix[i][j] = new DoubleNegativeUnitSystem(1);
        }
      }
      insertingRowNodeList.add(k);
      rowOffset += rowSize - 1;
    }

    final int[] insertingColumnNodes = new int[columnSize - 1];
    int c = 0;
    for (int i = column; i < column + columnSize - 1; i++) {
      insertingColumnNodes[c] = i;
      c++;
    }
    final int[] insertingRowNodes = new int[insertingRowNodeList.size() * (rowSize - 1)];
    int counter = 0;
    for (Integer i : insertingRowNodeList) {
      for (int j = i.intValue(); j < i.intValue() + rowSize - 1; j++) {
        insertingRowNodes[counter] = j;
        counter++;
      }
    }

    final DoubleAdjacencyMatrix ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);

    return ans;
  }

  /**
   * 行中に含まれるすべての単位行列を展開します.
   * 
   * <p>まず,対応する行を拡張します.
   * 
   * <p>次に,行中の単位行列を順番に見つけ,
   * 
   * <p>その都度列を拡張してから単位行列を展開します.
   * 
   * @param system 展開対象の行で一番若い列のシステム
   * @param row 展開対象の行
   * @return 行中に含まれるすべての単位行列が展開された隣接行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix createExpandUnitInRow(DoubleSystemOperator system, int row) {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int rowSize = system.getOutputSize();
    final int columnSize = system.getInputSize();

    DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertRow(matrix, row + 1, row + rowSize - 1);

    int columnOffset = 0;

    final List<Integer> insertingColumnNodeList = new ArrayList<>();

    for (int k = 0; k < newMatrix[0].length; k++) {
      if (false == newMatrix[row][k] instanceof DoubleConstantSystem && false == newMatrix[row][k] instanceof DoubleIntegratorSystem) {
        continue;
      }
      newMatrix = DoubleAdjacencyMatrixUtil.insertColumn(newMatrix, k + 1, k + columnSize - 1);
      for (int i = row; i < row + rowSize; i++) {
        for (int j = k; j < k + columnSize + columnOffset; j++) {
          if (false == ((i - row) == (j - k))) {
            continue;
          }
          if (newMatrix[row][k] instanceof DoubleUnitSystem) {
            newMatrix[i][j] = new DoubleUnitSystem(1);
            continue;
          }
          newMatrix[i][j] = new DoubleNegativeUnitSystem(1);
        }
      }
      insertingColumnNodeList.add(k);
      columnOffset += columnSize - 1;
    }

    final int[] insertingRowNodes = new int[rowSize - 1];
    int c = 0;
    for (int i = row; i < row + rowSize - 1; i++) {
      insertingRowNodes[c] = i;
      c++;
    }
    final int[] insertingColumnNodes = new int[insertingColumnNodeList.size() * (columnSize - 1)];
    int counter = 0;
    for (Integer i : insertingColumnNodeList) {
      for (int j = i.intValue(); j < i.intValue() + columnSize - 1; j++) {
        insertingColumnNodes[counter] = j;
        counter++;
      }
    }

    final DoubleAdjacencyMatrix ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);

    return ans;
  }

  /**
   * newMatrixをelementに持ち,基の隣接行列からinsertingRowNodesとinsertingColumnNodesを 用いて入力・出力ポートを拡張した隣接行列を返します.
   * 
   * @param newMatrix 新しいelement要素
   * @param insertingRowNodes 挿入された行ノードの配列
   * @param insertingColumnNodes 挿入された列ノードの配列
   * @return 与えられたelementを持つ拡張された隣接行列
   */
  private DoubleAdjacencyMatrix updateExpandedAdjacencyMatrix(DoubleSystemOperator[][] newMatrix, final int[] insertingRowNodes, final int[] insertingColumnNodes) {
    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(newMatrix);
    ans.modifyID();
    ans.setInputNodes(this.inputNodes);
    ans.setSourceNodes(this.sourceNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setSinkNodes(this.sinkNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    ans.shiftInputNodeByInserting(insertingColumnNodes);
    ans.shiftSourceNodeByInserting(insertingColumnNodes);
    ans.shiftOutputNodeByInserting(insertingRowNodes);
    ans.shiftSinkNodeByInserting(insertingRowNodes);
    return ans;
  }

  /**
   * 挿入された列ノードを新しい列サイズと古い列サイズを基に計算して返します.
   * 
   * @param column 拡張対象のある列
   * @param newMatrixColumnSize 新しい行列の列サイズ
   * @param oldMatrixColumnSize 古い行列の列サイズ
   * @return 挿入された列ノードの配列
   */
  private int[] getInsertingColumnNodes(int column, final int newMatrixColumnSize, final int oldMatrixColumnSize) {
    final int[] insertingColumnNodes = new int[newMatrixColumnSize - oldMatrixColumnSize];
    for (int i = 0; i < newMatrixColumnSize - oldMatrixColumnSize; i++) {
      insertingColumnNodes[i] = column + i;
    }
    return insertingColumnNodes;
  }

  /**
   * 挿入された行ノードを新しい行サイズと古い行サイズを基に計算して返します.
   * 
   * @param row 拡張対象のある行
   * @param newMatrixRowSize 新しい行列の行サイズ
   * @param oldMatrixRowSize 古い行列の行サイズ
   * @return 挿入された行ノードの配列
   */
  private int[] getInsertingRowNodes(int row, final int newMatrixRowSize, final int oldMatrixRowSize) {
    final int[] insertingRowNodes = getInsertingColumnNodes(row, newMatrixRowSize, oldMatrixRowSize);
    return insertingRowNodes;
  }

  /**
   * 定数行列を最小の要素に分割した隣接行列を作り,返します.
   * 
   * @param column 定数行列の行番号(0から始まります)
   * @param row 定数行列の列番号(0から始まります)
   * @param system 定数行列
   * 
   * @return 定数行列を展開した隣接行列
   */
  private DoubleAdjacencyMatrix createExpandConstantAndIntegratorSystem(final DoubleSystemOperator system, final int row, final int column) {
    final DoubleSystemOperator[][] matrix = this.elements;

    final int rowSize = system.getOutputSize();
    final int columnSize = system.getInputSize();

    DoubleSystemOperator[][] newMatrix = expandMatrixByInputOutputSize(matrix, row, column, rowSize, columnSize);

    int rowOffset = 0;

    for (int i = row; i < row + columnSize; i++) {
      for (int j = column; j < column + rowSize + rowOffset; j++) {
        if (system instanceof DoubleUnitSystem || system instanceof DoubleNegativeUnitSystem) {
          if ((i - row + rowOffset) != (j - column)) {
            continue;
          }
          newMatrix[i][j] = new DoubleUnitSystem(1);
        } else if (system instanceof DoubleIntegratorSystem) {
          if ((i - row + rowOffset) != (j - column)) {
            continue;
          }
          newMatrix[i][j] = createPrimitiveIntegratorSystem((DoubleIntegratorSystem)system, row, column);
        } else {
          newMatrix[i][j] = createPrimitiveConstantSystem(system, row, column, i, j);
        }
      }
    }

    final int[] insertingRowNodes = getInsertingRowNodes(row, newMatrix.length, matrix.length);
    final int[] insertingColumnNodes = getInsertingColumnNodes(column, newMatrix[0].length, matrix[0].length);
    final DoubleAdjacencyMatrix ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);
    return ans;
  }

  /**
   * 指定された行を基点として最小要素となる積分器システムを作成します.
   * 
   * @param system 展開対象の積分器
   * @param row 展開の基点となる行
   * @param i 展開される行
   * @return 新たにtagを追加された積分器
   */
  private DoubleIntegratorSystem createPrimitiveIntegratorSystem(final DoubleIntegratorSystem system, final int row, int i) {
    final DoubleIntegratorSystem integrator = system;
    final DoubleIntegratorSystem newIntegrator = new DoubleIntegratorSystem(1);
    newIntegrator.setDynamic(integrator.isDynamic());
    newIntegrator.setTag(integrator.getTag() + "_" + (i - row + 1)); //$NON-NLS-1$
    newIntegrator.setAutoSize(true);
    return newIntegrator;
  }

  /**
   * 指定された行と列に最小要素となる定数システムを作成します.
   * 
   * @param system 展開対象の定数行列
   * @param row 展開の基点となる行
   * @param column 展開の基点となる行
   * @param i 展開される行
   * @param j 展開される列
   * @return 新たにtagを追加された定数システム
   */
  private DoubleConstantSystem createPrimitiveConstantSystem(final DoubleSystemOperator system, final int row, final int column, int i, int j) {
    final DoubleConstantSystem constantSystem = (DoubleConstantSystem)system;
    final DoubleConstantSystem element = new DoubleConstantSystem(new DoubleMatrix(new DoubleNumber[] {constantSystem.getGain().transpose().getElement(i - row + 1, j - column + 1)}));
    element.setExpression(constantSystem.getExpression() + "_" + (j + 1 - column) + "_" + (i + 1 - row)); //$NON-NLS-1$ //$NON-NLS-2$
    element.setVariable(constantSystem.isVariable());
    return element;
  }

  /**
   * 展開対象のシステムの入力サイズ・出力サイズに応じてmatrixを展開します.
   * 
   * @param row 展開の基点となる行
   * @param column 展開の基点となる列
   * @param matrix 展開対象のmatrix
   * @param rowSize matrixの行サイズ
   * @param columnSize matrixの列サイズ
   * @return 拡張されたmatrix
   */
  private DoubleSystemOperator[][] expandMatrixByInputOutputSize(final DoubleSystemOperator[][] matrix, final int row, final int column, final int rowSize, final int columnSize) {
    final int rowMin = row + 1;
    final int rowMax = row + columnSize - 1;
    final int columnMin = column + 1;
    final int columnMax = column + rowSize - 1;

    DoubleSystemOperator[][] newMatrix = DoubleAdjacencyMatrixUtil.insertRowAndColumn(matrix, rowMin, rowMax, columnMin, columnMax);
    return newMatrix;
  }

  /**
   * 入力ポートと出力ポートが全く接続されていないシステムの隣接行列を返します。
   * 
   * @return 入力ポートと出力ポートが全く接続されていないシステムの隣接行列
   */
  private DoubleAdjacencyMatrix createZeroSystem() {
    DoubleSystemOperator[][] system = new DoubleSystemOperator[2][2];
    system[0][0] = DoubleZeroSystem.getInstance();
    system[0][1] = new DoubleConstantSystem(new DoubleMatrix(1, 1));
    system[1][0] = DoubleZeroSystem.getInstance();
    system[1][1] = DoubleZeroSystem.getInstance();

    return new DoubleAdjacencyMatrix(system);
  }

  /**
   * 入出力数が未定のポートの入出力を1に設定します。
   */
  private void setDefaultSizeToUnknownPort() {
    final int defaultSize = 1;

    for (int inputNode : this.inputNodes) {
      for (int outputNode : this.outputNodes) {
        final DoubleSystemOperator system = this.elements[inputNode - 1][outputNode - 1];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        if (system instanceof DoubleUnitSystem && (system.getInputSize() == -1 || system.getOutputSize() == -1)) {
          system.setInputSize(defaultSize);
          system.setOutputSize(defaultSize);
        }
      }
    }
  }

  /**
   * 線形システムに入力、出力、状態のタグを設定します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param linearSystem 線形システム
   */
  private void setInputOutputStateTagToLinearSystem(final DoubleLinearSystem linearSystem, final int[] stateSizes) {
    final List<String> inputPortTagList = new ArrayList<>();
    for (final Integer inputNode : this.inputNodes) {
      final String tag = this.inputPortTags.get(inputNode);
      if (tag == null) {
        throw new RuntimeException(Messages.getString("AdjacencyMatrix.10")); //$NON-NLS-1$
      }
      inputPortTagList.add(tag);
    }

    final List<String> outputPortTagList = new ArrayList<>();
    for (final Integer outputNode : this.outputNodes) {
      final String tag = this.outputPortTags.get(outputNode);
      if (tag == null) {
        throw new RuntimeException(Messages.getString("AdjacencyMatrix.11")); //$NON-NLS-1$
      }
      outputPortTagList.add(tag);
    }

    final List<String> stateTags = getStateTags(stateSizes.length);

    linearSystem.setInputPortTags(inputPortTagList);
    linearSystem.setOutputTags(outputPortTagList);
    linearSystem.setStateTags(stateTags);
  }

  /**
   * サブシステムの状態数を返します。
   * 
   * @return サブシステムの状態数
   */
  private int[] getStateSizes() {
    final int allSystemSize = getRowSize();
    final int numberOfIntegrator = getIntegratorOrUnitDelaySystemSize();
    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();

    final int[] stateSizes = new int[numberOfIntegrator];
    for (int i = 0; i < numberOfIntegrator; i++) {
      final int row = allSystemSize - numberOfIntegrator - outputNodeSize + i;
      final int column = inputNodeSize + i;
      final DoubleSystemOperator system = this.elements[row][column];
      stateSizes[i] = system.getStateSize();
    }
    return stateSizes;
  }

  /**
   * 指定されたノード間に存在するシステムを返します。
   * 
   * @param sources 出発ノード
   * @param destinations 到着ノード
   * @param isBidirectional 双方向に繋がっているパス上にあるシステムを求めるならばtrue、そうでなければfalse
   * @return 指定されたノード間に存在するシステム
   */
  DoubleAdjacencyMatrix getSystemBetweenNodes(final List<Integer> sources, final List<Integer> destinations, final boolean isBidirectional) {
    final BooleanMatrix connection = new DoubleConnectionMatrix(this).getBooleanMatrix();
    final BooleanMatrix reachableFromSource = getReachableMatrixFromNodes(connection, sources);
    final BooleanMatrix reachableToDestination = getReachableMatrixToNodes(connection, destinations);

    for (int row = 1; row <= getRowSize(); row++) {
      for (int column = 1; column <= getColumnSize(); column++) {
        final DoubleSystemOperator system = getElement(row, column);
        if (system == DoubleZeroSystem.getInstance()) {
          connection.setElement(row, column, false);
        }
      }
    }

    BooleanMatrix selectedElement;
    if (isBidirectional) {
      selectedElement = reachableFromSource.andElementWise(reachableToDestination).andElementWise(connection);
    } else {
      selectedElement = reachableFromSource.orElementWise(reachableToDestination).andElementWise(connection);
    }

    final DoubleAdjacencyMatrix selectedSystem = getSubgraph(selectedElement);

    // TODO findINputNodes と findOutputNodesを使用するように要変更
    selectedSystem.setInputNodes(this.inputNodes);
    selectedSystem.setSourceNodes(this.sourceNodes);
    selectedSystem.setOutputNodes(this.outputNodes);
    selectedSystem.setSinkNodes(this.sinkNodes);
    selectedSystem.setInputPortTags(this.inputPortTags);
    selectedSystem.setOutputPortTags(this.outputPortTags);
    selectedSystem.setRequiringLinearSystem(this.requiringLinearSystem);
    selectedSystem.setRequiringDescriptor(this.requiringDescriptor);
    selectedSystem.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
    return selectedSystem;
  }

  /**
   * 指定されたノードへの可到達行列を返します。
   * 
   * @param connection 連結行列
   * @param destinationNodes 到着ノード
   * @return 指定されたノードへの可到達行列
   */
  private BooleanMatrix getReachableMatrixToNodes(final BooleanMatrix connection, final List<Integer> destinationNodes) {
    final BooleanMatrix reachableToDestination = new DoubleReachableMatrix(connection.transpose()).getBooleanMatrix().transpose();

    final boolean[] reachableNodeToDestination = new boolean[getRowSize()];
    for (final int outputNode : destinationNodes) {
      for (int row = 1; row <= getRowSize(); row++) {
        if (reachableToDestination.getElement(row, outputNode) == true) {
          reachableNodeToDestination[row - 1] = true;
        }
      }
    }

    for (int column = 1; column <= getColumnSize(); column++) {
      if (reachableNodeToDestination[column - 1] == true) {
        continue;
      }

      for (int row = 1; row <= getRowSize(); row++) {
        reachableToDestination.setElement(row, column, false);
      }
      for (int i = 1; i <= getColumnSize(); i++) {
        reachableToDestination.setElement(column, i, false);
      }
    }
    return reachableToDestination;
  }

  /**
   * 指定されたノードからの可到達行列を返します。
   * 
   * @param connection 連結行列
   * @param sources 出発ノード
   * @return 指定されたノードからの可到達行列
   */
  private BooleanMatrix getReachableMatrixFromNodes(final BooleanMatrix connection, final List<Integer> sources) {
    final BooleanMatrix reachableFromSource = new DoubleReachableMatrix(connection).getBooleanMatrix();
    final boolean[] reachableNodeFromSource = new boolean[getColumnSize()];
    for (final int sourceNode : sources) {
      for (int column = 1; column <= getColumnSize(); column++) {
        if (reachableFromSource.getElement(sourceNode, column) == true) {
          reachableNodeFromSource[column - 1] = true;
        }
      }
    }

    for (int row = 1; row <= getRowSize(); row++) {
      if (reachableNodeFromSource[row - 1] == true) {
        continue;
      }

      for (int column = 1; column <= getColumnSize(); column++) {
        reachableFromSource.setElement(row, column, false);
      }
      for (int i = 1; i <= getRowSize(); i++) {
        reachableFromSource.setElement(i, row, false);
      }
    }
    return reachableFromSource;
  }

  /**
   * 入力ノードと出力ノードが指定された順に並ぶようにソートしたシステムを返します。
   * 
   * @return 入力ノードと出力ノードが指定された順に並ぶようにソートしたシステム
   */
  private DoubleAdjacencyMatrix sortByInputNodeAndOutputNode() {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);
    final List<Integer> newInputNodes = sortByInputNode(matrix); // this.inputPortTags
    final List<Integer> newOutputNodes = sortByOutputNode(matrix); // this.outputPortTags

    DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(newInputNodes);
    ans.setOutputNodes(newOutputNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
    return ans;
  }

  /**
   * 状態番号の昇順に状態に関するノードをソートした隣接行列を返します。
   * 
   * @return 状態番号の昇順に状態に関するノードをソートした隣接行列
   */
  @SuppressWarnings("boxing")
  private DoubleAdjacencyMatrix sortByStateNumber() {
    final DoubleSystemOperator[][] matrix = DoubleAdjacencyMatrixUtil.createClone(this.elements);

    final int integratorSize = getIntegratorOrUnitDelaySystemSize();
    final Integer[] originalStateNumbers = new Integer[integratorSize];
    final Integer[] sortedStateNumbers = new Integer[integratorSize];

    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();
    final int size = matrix.length;

    for (int i = 0; i < integratorSize; i++) {
      final int row = size - outputNodeSize - integratorSize + i;
      final int column = inputNodeSize + i;

      final int stateNumber = ((DoubleDynamicSystem)matrix[row][column]).getStateNumber();
      originalStateNumbers[i] = stateNumber;
      sortedStateNumbers[i] = stateNumber;
    }

    // 昇順にソート
    Arrays.sort(sortedStateNumbers);

    for (int i2 = 0; i2 < integratorSize; i2++) {
      final int i1 = findIndex(sortedStateNumbers[i2], originalStateNumbers);

      if (i1 == i2) {
        continue;
      }

      if (i1 < i2) {
        continue;
      }

      final int row1 = size - outputNodeSize - integratorSize + i1;
      final int row2 = size - outputNodeSize - integratorSize + i2;

      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, row1, row2);

      final int column1 = inputNodeSize + i1;
      final int column2 = inputNodeSize + i2;

      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, column1, column2);

      final int swapValue = originalStateNumbers[i1];
      originalStateNumbers[i1] = originalStateNumbers[i2];
      originalStateNumbers[i2] = swapValue;
    }

    final DoubleAdjacencyMatrix ans = new DoubleAdjacencyMatrix(matrix);
    ans.setInputNodes(this.inputNodes);
    ans.setOutputNodes(this.outputNodes);
    ans.setInputPortTags(this.inputPortTags);
    ans.setOutputPortTags(this.outputPortTags);
    ans.setRequiringLinearSystem(this.requiringLinearSystem);
    ans.setRequiringDescriptor(this.requiringDescriptor);
    ans.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);
    return ans;
  }

  /**
   * 配列中の指定されたデータが存在する指数を返します。
   * 
   * @param value 指定されたデータ
   * @param data 配列
   * @return 配列中の指定されたデータが存在する指数を返します。
   */
  @SuppressWarnings("boxing")
  private int findIndex(final int value, final Integer[] data) {
    for (int i = 0; i < data.length; i++) {
      if (data[i] == value) {
        return i;
      }
    }

    assert false : Messages.getString("AdjacencyMatrix.12"); //$NON-NLS-1$

    return -1;
  }

  /**
   * 入力ノードが指定された順に並ぶようにソートします。
   * 
   * @param matrix 隣接行列
   * @return 指定された順にソートされた入力ノード
   */
  @SuppressWarnings("boxing")
  private List<Integer> sortByInputNode(final DoubleSystemOperator[][] matrix) {
    final int inputNodeSize = this.inputNodes.size();
    final Integer[] inputNodes1 = new Integer[inputNodeSize];
    final Integer[] inputNodes2 = new Integer[inputNodeSize];
    this.inputNodes.toArray(inputNodes1);
    this.inputNodes.toArray(inputNodes2);

    // 昇順にソート
    Arrays.sort(inputNodes2);

    for (int i = 0; i < inputNodeSize; i++) {
      final int node1 = inputNodes1[i];
      final int node2 = inputNodes2[i];

      if (node1 == node2) {
        continue;
      }

      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, node1 - 1, node2 - 1);

      for (int j = 0; j < inputNodeSize; j++) {
        if (inputNodes1[j] == node2) {
          inputNodes1[j] = node1;
        }
      }

      inputNodes1[i] = node2;

      final String tag1 = this.inputPortTags.get(node1);
      final String tag2 = this.inputPortTags.get(node2);

      this.inputPortTags.put(node1, tag2);
      this.inputPortTags.put(node2, tag1);
    }

    return Arrays.asList(inputNodes2);
  }

  /**
   * 出力ノードが指定された順に並ぶようにソートします。
   * 
   * @param matrix 隣接行列
   * @return 指定された順にソートされた出力ノード
   */
  @SuppressWarnings("boxing")
  private List<Integer> sortByOutputNode(final DoubleSystemOperator[][] matrix) {
    final int outputNodeSize = this.outputNodes.size();
    final Integer[] outputNodes1 = new Integer[outputNodeSize];
    final Integer[] outputNodes2 = new Integer[outputNodeSize];
    this.outputNodes.toArray(outputNodes1);
    this.outputNodes.toArray(outputNodes2);

    // 昇順にソート
    Arrays.sort(outputNodes2);

    for (int i = 0; i < outputNodeSize; i++) {
      final int node1 = outputNodes1[i];
      final int node2 = outputNodes2[i];

      if (node1 == node2) {
        continue;
      }

      DoubleAdjacencyMatrixUtil.exchangeRowAndColumn(matrix, node1 - 1, node2 - 1);

      for (int j = 0; j < outputNodeSize; j++) {
        if (outputNodes1[j] == node2) {
          outputNodes1[j] = node1;
        }
      }

      outputNodes1[i] = node2;

      final String tag1 = this.outputPortTags.get(node1);
      final String tag2 = this.outputPortTags.get(node2);

      this.outputPortTags.put(node1, tag2);
      this.outputPortTags.put(node2, tag1);
    }

    return Arrays.asList(outputNodes2);
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Aを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @return 線形動的システムのシステム行列A
   */
  private DoubleMatrix createNumericalA(final int[] stateSizes, final int stateSize) {
    final DoubleMatrix a = new DoubleMatrix(stateSize, stateSize);
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();

    int offsetColumn = 1;
    int k1 = 0;
    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + i;
      int offsetRow = 1;
      int k2 = 0;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final DoubleMatrix aij = ((DoubleConstantSystem)system).getGain();
        final int aijRowSize = aij.getRowSize();
        final int aijColumnSize = aij.getColumnSize();
        if (aij.isEmpty() == false) {
          a.setSubMatrix(offsetRow, offsetRow + aijRowSize - 1, offsetColumn, offsetColumn + aijColumnSize - 1, aij);
        }
        offsetRow += aijRowSize;

        k2++;
      }
      offsetColumn += stateSizes[k1++];
    }

    return a;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Aの式を生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param processor プロセッサ
   * @return 線形動的システムのシステム行列Aの式
   */
  private String[][] createSymbolicA(final int[] stateSizes, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final String[][] a = new String[numberOfSubsystem][numberOfSubsystem];

    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + i;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];

        if (system == DoubleZeroSystem.getInstance()) {
          a[j][i] = "0"; //$NON-NLS-1$
        } else {
          a[j][i] = processor.getResult((ReversePolishNotationOperand<DoubleNumber,DoubleMatrix>)system);
        }
      }
    }

    return a;
  }

  /**
   * 状態のタグを返します。
   * 
   * @param numberOfSubsystem サブシステムの数
   * @return 状態のタグを返します。
   */
  private List<String> getStateTags(final int numberOfSubsystem) {
    final int inputNodeSize = this.inputNodes.size();
    final List<String> stateTags = new ArrayList<>();

    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + numberOfSubsystem + i;
      final int column = inputNodeSize + i;
      final DoubleSystemOperator system = this.elements[row][column];
      if ((system instanceof DoubleIntegratorSystem) == false && (system instanceof DoubleUnitDelaySystem) == false) {
        throw new RuntimeException(Messages.getString("AdjacencyMatrix.13")); //$NON-NLS-1$
      }

      if (system instanceof DoubleIntegratorSystem) {
        stateTags.add(((DoubleIntegratorSystem)system).getTag());
      }
      if (system instanceof DoubleUnitDelaySystem) {
        stateTags.add(((DoubleUnitDelaySystem)system).getTag());
      }
    }

    return stateTags;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Bを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @param inputSize 全システムの入力の数
   * @return 線形動的システムのシステム行列B
   */
  private DoubleMatrix createNumericalB(final int[] stateSizes, final int stateSize, final int inputSize) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final DoubleMatrix b = new DoubleMatrix(stateSize, inputSize);

    int offsetColumn = 1;
    for (int i = 0; i < inputNodeSize; i++) {
      final int row = i;
      int offsetRow = 1;
      int k2 = 0;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final DoubleMatrix bij = ((DoubleConstantSystem)system).getGain();
        final int bijRowSize = bij.getRowSize();
        final int bijColumnSize = bij.getColumnSize();
        if (bij.isEmpty() == false) {
          b.setSubMatrix(offsetRow, offsetRow + bijRowSize - 1, offsetColumn, offsetColumn + bijColumnSize - 1, bij);
        }
        offsetRow += bijRowSize;

        k2++;
      }
      offsetColumn += getNodeSignalSize(row + 1);
    }

    return b;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Bの式を生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param processor プロセッサ
   * @return 線形動的システムのシステム行列Bの式
   */
  private String[][] createSymbolicB(final int[] stateSizes, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final String[][] b = new String[numberOfSubsystem][inputNodeSize];

    for (int i = 0; i < inputNodeSize; i++) {
      final int row = i;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          b[j][i] = "0"; //$NON-NLS-1$
        } else {
          b[j][i] = processor.getResult((ReversePolishNotationOperand<DoubleNumber,DoubleMatrix>)system);
        }
      }
    }

    return b;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Cを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @param outputSize 全システムの出力の数
   * @return 線形動的システムのシステム行列C
   */
  private DoubleMatrix createNumericalC(final int[] stateSizes, final int stateSize, final int outputSize) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();
    final DoubleMatrix c = new DoubleMatrix(outputSize, stateSize);

    int offsetColumn = 1;
    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + i;
      int offsetRow = 1;
      for (int j = 0; j < outputNodeSize; j++) {
        final int column = getColumnSize() - outputNodeSize + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          offsetRow += getNodeSignalSize(column + 1);
          continue;
        }
        final DoubleMatrix cij = ((DoubleConstantSystem)system).getGain();
        final int cijRowSize = cij.getRowSize();
        final int cijColumnSize = cij.getColumnSize();
        if (cij.isEmpty() == false) {
          c.setSubMatrix(offsetRow, offsetRow + cijRowSize - 1, offsetColumn, offsetColumn + cijColumnSize - 1, cij);
        }
        offsetRow += cijRowSize;
      }
      offsetColumn += getNodeSignalSize(row + 1);
    }

    return c;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Cの式を生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param processor プロセッサー
   * @return 線形動的システムのシステム行列Cの式
   */
  private String[][] createSymbolicC(final int[] stateSizes, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();
    final String[][] c = new String[outputNodeSize][numberOfSubsystem];

    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + i;
      for (int j = 0; j < outputNodeSize; j++) {
        final int column = getColumnSize() - outputNodeSize + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          c[j][i] = "0"; //$NON-NLS-1$
        } else {
          c[j][i] = processor.getResult((ReversePolishNotationOperand<DoubleNumber,DoubleMatrix>)system);
        }
      }
    }

    return c;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Dを生成します。
   * 
   * @param inputSize 全システムの入力の数
   * @param outputSize 全システムの出力の数
   * @return 線形動的システムのシステム行列D
   */
  private DoubleMatrix createNumericalD(final int inputSize, final int outputSize) {
    final DoubleMatrix d = new DoubleMatrix(outputSize, inputSize);
    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();

    int offsetColumn = 1;
    for (int i = 0; i < inputNodeSize; i++) {
      final int row = i;
      int offsetRow = 1;
      for (int j = 0; j < outputNodeSize; j++) {
        final int column = getColumnSize() - outputNodeSize + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          offsetRow += getNodeSignalSize(column + 1);
          continue;
        }
        final DoubleMatrix dij = ((DoubleConstantSystem)system).getGain();
        final int dijRowSize = dij.getRowSize();
        final int dijColumnSize = dij.getColumnSize();
        if (dij.isEmpty() == false) {
          d.setSubMatrix(offsetRow, offsetRow + dijRowSize - 1, offsetColumn, offsetColumn + dijColumnSize - 1, dij);
        }
        offsetRow += dijRowSize;
      }
      offsetColumn += getNodeSignalSize(row + 1);
    }

    return d;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Dの式を生成します。
   * 
   * @param processor プロセッサー
   * 
   * @return 線形動的システムのシステム行列Dの式
   */
  private String[][] createSymbolicD(final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final int inputNodeSize = this.inputNodes.size();
    final int outputNodeSize = this.outputNodes.size();
    final String[][] d = new String[outputNodeSize][inputNodeSize];

    for (int i = 0; i < inputNodeSize; i++) {
      final int row = i;
      for (int j = 0; j < outputNodeSize; j++) {
        final int column = getColumnSize() - outputNodeSize + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          d[j][i] = "0"; //$NON-NLS-1$
        } else {
          d[j][i] = processor.getResult((ReversePolishNotationOperand<DoubleNumber,DoubleMatrix>)system);
        }
      }
    }

    return d;
  }

  /**
   * 指定されたノードを含む部分グラフを返します。
   * 
   * @param nodes 部分グラフを形成するノードの番号のリスト
   * @return 指定されたノードを含む部分グラフ
   */
  public DoubleAdjacencyMatrix getSubgraph(final List<Integer> nodes) {
    final int size = nodes.size();
    final IntMatrix index = new IntMatrix(1, size);

    int i = 1;
    for (final int node : nodes) {
      index.setElement(1, i, node);
      i++;
    }

    return getSubMatrix(index, index);
  }

  /**
   * trueで指定されたノードのみを含む隣接行列を返します。
   * 
   * @param candidates 候補
   * @return trueで指定されたノードのみを含む隣接行列
   */
  public DoubleAdjacencyMatrix getSubgraph(final BooleanMatrix candidates) {
    final DoubleAdjacencyMatrix result = new DoubleAdjacencyMatrix(getRowSize(), getColumnSize());
    for (int row = 1; row <= getRowSize(); row++) {
      for (int column = 1; column <= getColumnSize(); column++) {
        if (candidates.getElement(row, column) == true) {
          result.setElement(row, column, getElement(row, column));
        }
      }
    }

    return result;
  }

  /**
   * システムの出力数の-1倍(符号反転)を重みとするグラフの隣接行列を返します。
   * 
   * @return システムの出力数の-1倍(符号反転)を重みとするグラフの隣接行列
   */
  public DoubleMatrix getMinusOutputSizeMatrix() {
    final DoubleMatrix minusOutputSizeMatrix = new DoubleMatrix(getRowSize(), getColumnSize());

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        minusOutputSizeMatrix.setElement(row + 1, column + 1, -system.getOutputSize());
      }
    }

    return minusOutputSizeMatrix;
  }

  /**
   * システムの入力数を重みとするグラフの隣接行列を返します。
   * 
   * @return システムの入力数を重みとするグラフの隣接行列
   */
  public DoubleMatrix getInputSizeMatrix() {
    final DoubleMatrix inputSizeMatrix = new DoubleMatrix(getRowSize(), getColumnSize());

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          continue;
        }
        inputSizeMatrix.setElement(row + 1, column + 1, system.getInputSize());
      }
    }

    return inputSizeMatrix;
  }

  /**
   * @see org.mklab.nfc.matrix.BaseArray#hashCode()
   */
  @Override
  public int hashCode() {
    final int PRIME = 31;
    int result = super.hashCode();
    result = PRIME * result + (this.requiringLinearSystem ? 1231 : 1237);
    return result;
  }

  /**
   * @see org.mklab.nfc.matrix.BaseArray#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!super.equals(obj)) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    final DoubleAdjacencyMatrix other = (DoubleAdjacencyMatrix)obj;
    if (this.requiringLinearSystem != other.requiringLinearSystem) {
      return false;
    }
    return true;
  }

  /**
   * ノードの大きさ(信号の数)を返します。
   * 
   * @param nodeNumber ノードの番号
   * @return ノードの大きさ(信号の数)
   */
  public int getNodeSignalSize(final int nodeNumber) {
    for (int column = 0; column < getColumnSize(); column++) {
      if (this.elements[nodeNumber - 1][column] != DoubleZeroSystem.getInstance()) {
        return (this.elements[nodeNumber - 1][column]).getInputSize();
      }
    }

    for (int row = 0; row < getRowSize(); row++) {
      if (this.elements[row][nodeNumber - 1] != DoubleZeroSystem.getInstance()) {
        return (this.elements[row][nodeNumber - 1]).getOutputSize();
      }
    }
    return 0;
  }

  /**
   * 入力の数を返します。
   * 
   * @return 入力ノードの入力の数
   */
  public int getInputSize() {
    int inputSize = 0;

    for (final int inputNode : this.inputNodes) {
      inputSize += getNodeSignalSize(inputNode);
    }

    return inputSize;
  }

  /**
   * 出力の数を返します。
   * 
   * @return 出力ノードの出力の数
   */
  public int getOutputSize() {
    int outputSize = 0;

    for (final int outputNode : this.outputNodes) {
      outputSize += getNodeSignalSize(outputNode);
    }

    return outputSize;
  }

  /**
   * 隣接行列の要素であるシステムオペレータのIDを設定します。
   */
  void setupSystemIDForElements() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator system = getElement(row + 1, column + 1);
        if (system == null) {
          continue;
        }
        system.setID("" + row + "," + column); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  /**
   * 入力ポートのノード番号(番号は1から始まります)とタグのマップを返します。
   * 
   * @return 入力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  public Map<Integer, String> getInputPortTags() {
    return this.inputPortTags;
  }

  /**
   * 入力ポートのノード番号(番号は1から始まります)とタグのマップを設定します。
   * 
   * @param inputPortTags 入力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  public void setInputPortTags(Map<Integer, String> inputPortTags) {
    this.inputPortTags = inputPortTags;
  }

  /**
   * 出力ポートのノード番号(番号は1から始まります)とタグのマップを返します。
   * 
   * @return 出力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  public Map<Integer, String> getOutputPortTags() {
    return this.outputPortTags;
  }

  /**
   * 出力ポートのノード番号(番号は1から始まります)とタグのマップを設定します。
   * 
   * @param outputPortTags 出力ポートのノード番号(番号は1から始まります)とタグのマップ
   */
  public void setOutputPortTags(Map<Integer, String> outputPortTags) {
    this.outputPortTags = outputPortTags;
  }

  /**
   * @see org.mklab.nfc.matrix.BaseArray#clone()
   */
  @Override
  public Object clone() {
    final DoubleAdjacencyMatrix inst = (DoubleAdjacencyMatrix)super.clone();
    inst.requiringLinearSystem = this.requiringLinearSystem;
    inst.requiringDescriptor = this.requiringDescriptor;
    inst.requiringPrimitiveExpression = this.requiringPrimitiveExpression;

    if (this.inputNodes == null) {
      inst.inputNodes = null;
    } else {
      final Integer[] inNodes = new Integer[this.inputNodes.size()];
      inst.inputNodes = new ArrayList<>(Arrays.asList(this.inputNodes.toArray(inNodes)));
    }

    if (this.sourceNodes == null) {
      inst.sourceNodes = null;
    } else {
      final Integer[] inNodes = new Integer[this.sourceNodes.size()];
      inst.sourceNodes = new ArrayList<>(Arrays.asList(this.sourceNodes.toArray(inNodes)));
    }

    if (this.outputNodes == null) {
      inst.outputNodes = null;
    } else {
      final Integer[] outNodes = new Integer[this.outputNodes.size()];
      inst.outputNodes = new ArrayList<>(Arrays.asList(this.outputNodes.toArray(outNodes)));
    }

    if (this.sinkNodes == null) {
      inst.sinkNodes = null;
    } else {
      final Integer[] outNodes = new Integer[this.sinkNodes.size()];
      inst.sinkNodes = new ArrayList<>(Arrays.asList(this.sinkNodes.toArray(outNodes)));
    }

    if (this.inputPortTags == null) {
      inst.inputPortTags = null;
    } else {
      inst.inputPortTags = new TreeMap<>(this.inputPortTags);
    }

    if (this.inputPortTags == null) {
      inst.outputPortTags = null;
    } else {
      inst.outputPortTags = new TreeMap<>(this.outputPortTags);
    }

    return inst;
  }

  /**
   * elementsの要素を持つ隣接文字列行列を生成します。
   * 
   * @return elementsの要素を持つ隣接文字列行列
   */
  DoubleAdjacencyConstantMatrix createAdjacencyConstantMatrix() {
    return new DoubleAdjacencyConstantMatrix(this.elements);
  }

  /**
   * 単位システムが存在する要素をtrueとするbooleanマトリックスを求めます.
   * 
   * @return 単位システムが存在する要素をtrueとするbooleanMatrix
   */
  private BooleanMatrix getBooleanMatrixForUnit() {
    final BooleanMatrix mat = new BooleanMatrix(getRowSize(), getColumnSize());
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final DoubleSystemOperator systemOperator = this.elements[row][column];
        if (systemOperator != DoubleZeroSystem.getInstance()) {
          if (systemOperator instanceof DoubleUnitSystem) {
            mat.setElement(row + 1, column + 1, true);
          }
        }
      }
    }
    return mat;
  }

  /**
   * 一つの列に単位行列が唯一存在する列を求めます.
   * 
   * @return 一つの列に単位行列が唯一存在する列のリスト
   */
  private List<Integer> getOnlyUnitColumnList() {
    final List<Integer> onlyUnitColumnList = new ArrayList<>();
    final int localRowSize = this.elements.length;
    final int localColumnSize = localRowSize == 0 ? 0 : this.elements[0].length;
    final BooleanMatrix mat = getBooleanMatrixForUnit();
    for (int column = 0; column < localColumnSize; column++) {
      int numberOfUnit = 0;
      for (int row = 0; row < localRowSize; row++) {
        if (mat.getElement(row + 1, column + 1) == true) {
          numberOfUnit++;
        }
      }
      if (numberOfUnit == 1) {
        onlyUnitColumnList.add(Integer.valueOf(column));
      }
    }
    return onlyUnitColumnList;
  }

  /**
   * SystemOperatorの中身を出力します。
   * 
   * <p> 一時的なものです。
   * 
   * @param matrix 隣接行列
   * @param debugMode デーバッグするならばtrue
   * @param message メッセージ
   */
  private void showMatrix(final DoubleSystemOperator[][] matrix, boolean debugMode, final String message) {
    if (debugMode) {
      System.out.println(message);
      DoubleAdjacencyMatrix.showMatrix(matrix);
    }
  }

  /**
   * 隣接行列から線形動的システムのディスクリプタ行列Eを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @return 線形動的システムのディスクリプタ行列D
   */
  private DoubleMatrix createNumericalE(int[] stateSizes, final int stateSize) {
    final DoubleMatrix e = new DoubleMatrix(stateSize, stateSize);
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();

    int offsetColumn = 1;
    int k1 = 0;
    for (int i = 0; i < numberOfSubsystem; i++) {

      final int row = inputNodeSize + numberOfSubsystem + i;
      int offsetRow = 1;
      int k2 = 0;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];
        if (system == DoubleZeroSystem.getInstance()) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final DoubleMatrix aij = ((DoubleConstantSystem)system).getGain();
        final int aijRowSize = aij.getRowSize();
        final int aijColumnSize = aij.getColumnSize();
        e.setSubMatrix(offsetRow, offsetRow + aijRowSize - 1, offsetColumn, offsetColumn + aijColumnSize - 1, aij);
        offsetRow += aijRowSize;

        k2++;
      }
      offsetColumn += stateSizes[k1++];
    }
    return e.unaryMinus().add(e.createUnit(stateSize));
  }

  /**
   * 隣接行列から線形動的システムのディスクリプター行列Eの式を生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param processor プロセッサー
   * @return 線形動的システムのディスクリプター行列Eの式
   */
  private String[][] createSymbolicE(final int[] stateSizes, final ReversePolishNotationProcessor<DoubleNumber,DoubleMatrix> processor) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final String[][] e = new String[numberOfSubsystem][numberOfSubsystem];

    this.transformEMatrix();

    for (int i = 0; i < numberOfSubsystem; i++) {
      final int row = inputNodeSize + numberOfSubsystem + i;
      for (int j = 0; j < numberOfSubsystem; j++) {
        final int column = inputNodeSize + numberOfSubsystem + j;
        final DoubleSystemOperator system = this.elements[row][column];

        if (system == DoubleZeroSystem.getInstance()) {
          e[j][i] = "0"; //$NON-NLS-1$
        } else {
          e[j][i] = processor.getResult((ReversePolishNotationOperand<DoubleNumber,DoubleMatrix>)system);
        }
      }
    }

    return e;
  }

  /**
   * SystemOperatorの中身を出力します。
   * 
   * @param matrix システム行列
   */
  public static void showMatrix(DoubleSystemOperator[][] matrix) {
    String lineseparator = System.getProperty("line.separator"); //$NON-NLS-1$
    StringBuffer stringBuffer = new StringBuffer();
    final String separator = ","; //$NON-NLS-1$
    stringBuffer.append(" " + separator); //$NON-NLS-1$
    for (int row = 0; row < matrix.length; row++) {
      stringBuffer.append(row % 10 + separator);
    }
    stringBuffer.append(lineseparator);

    for (int row = 0; row < matrix.length; row++) {
      stringBuffer.append(row % 10 + separator);
      for (int col = 0; col < matrix.length; col++) {
        if (matrix[row][col] instanceof DoubleZeroSystem) {
          stringBuffer.append("0" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleUnitSystem) {
          stringBuffer.append("P" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleNegativeUnitSystem) {
          stringBuffer.append("N" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleIntegratorSystem) {
          stringBuffer.append("q" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleUnitDelaySystem) {
          stringBuffer.append("q" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleContinuousLinearDynamicSystem) {
          stringBuffer.append("L" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof DoubleConstantSystem) {
          stringBuffer.append(new WithoutCancellationExpressionProcessor<DoubleNumber,DoubleMatrix>().getResult(((DoubleConstantSystem)matrix[row][col])) + separator);
        } else if (matrix[row][col] instanceof DoubleConstantSystem) {
          stringBuffer.append("C" + separator); //$NON-NLS-1$
        }
      }
      stringBuffer.append(lineseparator);
    }
    stringBuffer.append(lineseparator);

    //  writeCSVfile(stringBuffer, "adjacencyMatrix"+new Date().getTime()+".csv"); //$NON-NLS-1$ //$NON-NLS-2$

    System.out.println(stringBuffer);
  }

  /**
   * AdjacencyMatrixの中身を出力します。
   * 
   * @param adjacencyMatrix 隣接行列
   */
  public static void showMatrix(DoubleAdjacencyMatrix adjacencyMatrix) {
    final DoubleSystemOperator[][] matrix = adjacencyMatrix.getElements();
    final String lineseparator = System.getProperty("line.separator"); //$NON-NLS-1$
    final StringBuffer stringBuffer = new StringBuffer();
    final String separator = ","; //$NON-NLS-1$
    stringBuffer.append(" " + separator); //$NON-NLS-1$
    for (int row = 0; row < matrix.length; row++) {
      stringBuffer.append(row % 10 + separator);
    }
    stringBuffer.append(lineseparator);
    for (int row = 0; row < matrix.length; row++) {
      stringBuffer.append(row % 10 + separator);
      for (int col = 0; col < matrix.length; col++) {
        if (matrix[row][col] instanceof DoubleZeroSystem) stringBuffer.append("0" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleUnitSystem) stringBuffer.append("P" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleNegativeUnitSystem) stringBuffer.append("N" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleIntegratorSystem) stringBuffer.append("q" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleUnitDelaySystem) stringBuffer.append("q" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleContinuousLinearDynamicSystem) stringBuffer.append("L" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof DoubleConstantSystem) stringBuffer.append(new ExpressionProcessor<DoubleNumber,DoubleMatrix>().getResult(((DoubleConstantSystem)matrix[row][col])) + separator);
      }
      stringBuffer.append(lineseparator);
    }
    stringBuffer.append(lineseparator);

    //  writeCSVfile(stringBuffer, "adjacencyMatrix"+new Date().getTime()+".csv"); //$NON-NLS-1$ //$NON-NLS-2$

    System.out.println(stringBuffer);
  }

  /**
   * 次数の拡張が必要な場合に適当にノードを増やし,積分器を追加します.
   * 
   * @return 隣接行列
   */
  private DoubleAdjacencyMatrix expandDegree() {
    final DoubleAdjacencyMatrix oldAdjMatrix = this;

    int loopNode = 0;
    int srcColumnSize = oldAdjMatrix.getColumnSize();
    for (int i = 1; i < srcColumnSize; i++) {
      if (oldAdjMatrix.getElement(i, i).equals(DoubleZeroSystem.getInstance())) {
        continue;
      }
      loopNode = i;
    }
    if (loopNode == 0) {
      return oldAdjMatrix;
    }

    int integratorOrUnitDelaySystemSize = oldAdjMatrix.getIntegratorOrUnitDelaySystemSize();
    int inputSize = oldAdjMatrix.inputNodes.size();
    int outputSize = oldAdjMatrix.outputNodes.size();
    if ((srcColumnSize - (integratorOrUnitDelaySystemSize * 2 + inputSize + outputSize) <= 0)) {
      return oldAdjMatrix;
    }

    final DoubleAdjacencyMatrix newAdjMatrix = new DoubleAdjacencyMatrix(getRowSize() + 1, getColumnSize() + 1);
    newAdjMatrix.setSubMatrix(1, loopNode - 1, 1, loopNode - 1, oldAdjMatrix.getSubMatrix(1, loopNode - 1, 1, loopNode - 1));
    newAdjMatrix.setSubMatrix(1, loopNode - 1, loopNode + 2, newAdjMatrix.getColumnSize(), oldAdjMatrix.getSubMatrix(1, loopNode - 1, loopNode + 1, srcColumnSize));
    newAdjMatrix.setSubMatrix(loopNode + 1, newAdjMatrix.getRowSize(), 1, loopNode - 1, oldAdjMatrix.getSubMatrix(loopNode, oldAdjMatrix.getRowSize(), 1, loopNode - 1));
    newAdjMatrix.setSubMatrix(loopNode + 1, newAdjMatrix.getRowSize(), loopNode + 2, newAdjMatrix.getColumnSize(),
        oldAdjMatrix.getSubMatrix(loopNode, oldAdjMatrix.getRowSize(), loopNode + 1, srcColumnSize));
    newAdjMatrix.setSubMatrix(1, loopNode - 1, loopNode, loopNode, oldAdjMatrix.getSubMatrix(1, loopNode - 1, loopNode, loopNode));
    newAdjMatrix.setSubMatrix(loopNode + 2, newAdjMatrix.getRowSize(), loopNode, loopNode, oldAdjMatrix.getSubMatrix(loopNode + 1, oldAdjMatrix.getRowSize(), loopNode, loopNode));

    DoubleSystemOperator loopsystem = oldAdjMatrix.getElement(loopNode, loopNode);

    final DoubleIntegratorSystem integratorSystem = new DoubleIntegratorSystem(loopsystem.getInputSize());
    integratorSystem.setStateNumber(integratorOrUnitDelaySystemSize + 1);
    //integratorSystem.setTag("z_" + integratorSystem.getTag()); //$NON-NLS-1$
    integratorSystem.setTag("x_" + integratorSystem.getStateNumber()); //$NON-NLS-1$
    newAdjMatrix.elements[loopNode - 1][loopNode] = integratorSystem;
    newAdjMatrix.elements[loopNode - 1][loopNode - 1] = new DoubleUnitSystem(loopsystem.getInputSize());
    final DoubleConstantSystem sys = ((DoubleConstantSystem)loopsystem).subtract(new DoubleUnitSystem(loopsystem.getOutputSize(), loopsystem.getInputSize()));

    newAdjMatrix.elements[loopNode][loopNode - 1] = (sys);

    newAdjMatrix.modifyID();
    newAdjMatrix.setInputNodes(this.inputNodes);
    newAdjMatrix.setSourceNodes(this.sourceNodes);
    newAdjMatrix.setOutputNodes(this.outputNodes);
    newAdjMatrix.setSinkNodes(this.sinkNodes);
    newAdjMatrix.setInputPortTags(this.inputPortTags);
    newAdjMatrix.setOutputPortTags(this.outputPortTags);
    newAdjMatrix.setRequiringLinearSystem(this.requiringLinearSystem);
    newAdjMatrix.setRequiringDescriptor(this.requiringDescriptor);
    newAdjMatrix.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    newAdjMatrix.shiftInputNodeByInserting(new int[] {loopNode});
    newAdjMatrix.shiftSourceNodeByInserting(new int[] {loopNode});
    newAdjMatrix.shiftOutputNodeByInserting(new int[] {loopNode});
    newAdjMatrix.shiftSinkNodeByInserting(new int[] {loopNode});
    return newAdjMatrix;
  }

  /**
   * @param loopColumn ループが存在する列番号
   * @param column 列番号
   * @param targetNode 対象となるノード
   */
  private void moveElement(int loopColumn, int column, int targetNode) {
    final DoubleAdjacencyMatrix ans = this;
    if (ans.getElement(targetNode, column) == DoubleZeroSystem.getInstance()) {
      ans.setElement(targetNode, column, ans.getElement(loopColumn, column));
    } else {
      ans.setElement(targetNode, column, ((DoubleConstantSystem)ans.getElement(targetNode, column)).add((DoubleConstantSystem)ans.getElement(loopColumn, column)));
    }
    ans.setElement(loopColumn, column, DoubleZeroSystem.getInstance());
  }

  /**
   * 代数ループを含む冗長なノードが存在する場合,適当なノードを削除します.
   * 
   * @return 変更があったかどうか
   */
  @SuppressWarnings("boxing")
  private boolean removeRedundantNodes() {
    final DoubleAdjacencyMatrix ans = this;
    boolean changed = false;

    final List<Integer> loopColumnList = new ArrayList<>();
    for (int i = 1; i < ans.getColumnSize(); i++) {
      if (DoubleAdjacencyMatrixUtil.hasIntegratorOrUnitDelayInRow(ans.getElements(), i - 1)) {
        continue;
      }

      DoubleSystemOperator element = ans.getElement(i, i);
      if (element != DoubleZeroSystem.getInstance() && element.isLinear()) {
        if (element.getLinearSystem().getD().isZero() == false) {
          loopColumnList.add(i);
        } else if (((DoubleConstantSystem)element).isVariable() && ((DoubleConstantSystem)element).getExpression().isEmpty() == false) {
          loopColumnList.add(i);
        }
      }
    }

    if (loopColumnList.isEmpty()) {
      return false;
    }

    final Map<Integer, Integer> targetMap = new HashMap<>();

    for (int loopColumn : loopColumnList) {
      for (int column = 1; column < ans.getColumnSize(); column++) {
        if (ans.getColumnVector(loopColumn).compareColumnVectors(ans.getColumnVector(column))) {
          if (loopColumnList.contains(column) == false) {
            targetMap.put(loopColumn, column);
          }
        }
      }
    }

    for (int loopColumn : loopColumnList) {
      for (int column = 1; column < ans.getColumnSize(); column++) {
        if (loopColumn != column && ans.getColumnVector(loopColumn).compareColumnVectors(ans.getColumnVector(column))) {
          ans.moveElement(loopColumn, column, targetMap.get(loopColumn));
          changed = true;
        }
      }
    }

    if (changed == true) {
      for (int loopColumn : loopColumnList) {
        for (int i = 1; i < ans.getColumnVector(loopColumn).getRowSize(); i++) {
          if (targetMap.get(loopColumn) != null) {
            ans.setElement(i, loopColumn, DoubleZeroSystem.getInstance());
          }
        }
      }
    }

    return changed;
  }

  /**
   * E行列に当たる部分の符号を反転し単位行列を加えます.
   */
  private void transformEMatrix() {
    final DoubleAdjacencyMatrix allSystem = this;
    final int numberOfIntegrator = allSystem.getIntegratorOrUnitDelaySystemSize();
    final int inputSize = allSystem.inputNodes.size();
    final int outputSize = allSystem.outputNodes.size();
    for (int i = numberOfIntegrator + inputSize + 1; i < allSystem.getColumnSize() + 1 - outputSize; i++) {
      DoubleSystemOperator element = allSystem.getElement(i, i);
      if (element == DoubleZeroSystem.getInstance()) {
        allSystem.setElement(i, i, new DoubleUnitSystem(allSystem.getElement(i, i - numberOfIntegrator).getStateSize()));
      } else {
        final DoubleConstantSystem system = ((DoubleConstantSystem)element).unaryMinus().add(new DoubleUnitSystem(element.getInputSize()));
        system.setExpression(new ExpressionProcessor<DoubleNumber,DoubleMatrix>().getResult(system));
        allSystem.setElement(i, i, system);
      }
    }
  }

  /**
   * ディスクリプタ形式として求めるかを設定します.
   * 
   * @param requiringDescriptor ディスクリプタ形式として求めるならばtrue
   */
  public void setRequiringDescriptor(boolean requiringDescriptor) {
    this.requiringDescriptor = requiringDescriptor;
  }

  /**
   * 伝達関数を数式形式で求めます.
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * @return 伝達関数を表す隣接定数行列
   */
  public DoubleAdjacencyConstantMatrix getSymbolicTransferFunction(boolean requiringReachableSubSystem) {
    final DoubleAdjacencyMatrix allSystem = getLinearSystemFromInputToOutput(requiringReachableSubSystem);

    final int[] stateSizes = allSystem.getStateSizes();

    final DoubleAdjacencyConstantMatrix adjacencyConstantMatrix = new DoubleAdjacencyConstantMatrix(allSystem.getElements());
    final DoubleAdjacencyConstantMatrix A = adjacencyConstantMatrix.createA(stateSizes, allSystem.inputNodes.size());
    final DoubleAdjacencyConstantMatrix B = adjacencyConstantMatrix.createB(stateSizes, allSystem.inputNodes.size());
    final DoubleAdjacencyConstantMatrix C = adjacencyConstantMatrix.createC(stateSizes, allSystem.inputNodes.size(), allSystem.outputNodes.size());
    final DoubleAdjacencyConstantMatrix D = adjacencyConstantMatrix.createD(allSystem.inputNodes.size(), allSystem.outputNodes.size());
    final DoubleAdjacencyConstantMatrix E = adjacencyConstantMatrix.createE(stateSizes, allSystem.inputNodes.size());
    final DoubleConstantSystem s = new DoubleConstantSystem(new DoubleMatrix(new double[] {20000}));
    s.setExpression("s"); //$NON-NLS-1$
    s.setVariable(true);
    final int eSize = E.getColumnSize();
    final DoubleSystemOperator[][] sS = new DoubleSystemOperator[eSize][eSize];
    for (int i = 0; i < eSize; i++) {
      sS[i][i] = s;
    }
    final DoubleAdjacencyConstantMatrix S = new DoubleAdjacencyConstantMatrix(sS);

    final DoubleAdjacencyConstantMatrix pencileInverse = (S.multiply(E).subtract(A)).inverse();
    final DoubleAdjacencyConstantMatrix multiply = C.multiply(pencileInverse);
    final DoubleAdjacencyConstantMatrix symbolicTransferFunction = multiply.multiply(B).add(D);
    return symbolicTransferFunction;
  }

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

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

}