AdjacencyMatrix.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.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.GridUtil;
import org.mklab.nfc.matrix.IntMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.ode.PiecewiseContinuousAlgebraicSystem;
import org.mklab.nfc.ode.PiecewiseContinuousDiscreteAlgebraicSystem;
import org.mklab.nfc.ode.PiecewiseDifferentialDifferenceSystem;
import org.mklab.nfc.ode.PiecewiseDifferentialSystem;
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.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.tool.control.AbstractLinearSystem;
import org.mklab.tool.control.ImproperLinearSystem;
import org.mklab.tool.control.LinearSystem;
import org.mklab.tool.control.LinearSystemFactory;
import org.mklab.tool.control.TimeDomainType;
import org.mklab.tool.control.system.continuous.BlockContinuousExplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.BlockContinuousImplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.BlockContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.BlockPiecewiseContinuousDynamicSystem;
import org.mklab.tool.control.system.continuous.BlockPiecewiseContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.ContinuousDynamicSystem;
import org.mklab.tool.control.system.continuous.ContinuousImplicitDynamicSystem;
import org.mklab.tool.control.system.continuous.ContinuousLinearDynamicSystem;
import org.mklab.tool.control.system.continuous.ContinuousStaticSystem;
import org.mklab.tool.control.system.continuous.IntegratorSystem;
import org.mklab.tool.control.system.discrete.BlockDiscreteDynamicSystem;
import org.mklab.tool.control.system.discrete.BlockDiscreteStaticSystem;
import org.mklab.tool.control.system.discrete.DiscreteDynamicSystem;
import org.mklab.tool.control.system.discrete.DiscreteStaticSystem;
import org.mklab.tool.control.system.discrete.HoldSystem;
import org.mklab.tool.control.system.discrete.UnitDelaySystem;
import org.mklab.tool.control.system.graph.ConnectionMatrix;
import org.mklab.tool.control.system.graph.CycleMatrix;
import org.mklab.tool.control.system.graph.ReachableMatrix;
import org.mklab.tool.control.system.math.ConstantSystem;
import org.mklab.tool.control.system.math.DeMultiplexer;
import org.mklab.tool.control.system.math.Multiplexer;
import org.mklab.tool.control.system.math.NegativeUnitSystem;
import org.mklab.tool.control.system.math.UnitSystem;
import org.mklab.tool.control.system.sampled.BlockPiecewiseSampledDataDynamicSystem;
import org.mklab.tool.control.system.sampled.BlockPiecewiseSampledDataStaticSystem;
import org.mklab.tool.control.system.sampled.BlockSampledDataDynamicSystem;
import org.mklab.tool.control.system.sampled.BlockSampledDataStaticSystem;
import org.mklab.tool.control.system.sampled.Sampler;
import org.mklab.tool.control.system.sink.ContinuousSink;
import org.mklab.tool.control.system.sink.Exporter;
import org.mklab.tool.control.system.sink.OutputPort;
import org.mklab.tool.control.system.source.ContinuousSource;
import org.mklab.tool.control.system.source.Importer;
import org.mklab.tool.control.system.source.InputPort;


/**
 * 隣接行列(システムオペレータを成分とする行列)を表わすクラスです。
 * 
 * @author Koga Laboratory
 * @version $Revision: 1.115 $
 * @param <RS> type of real scalar
 * @param <RM> type of real matrix
 * @param <CS> type of complex scalar
 * @param <CM> type of complex matrix
 */
public class AdjacencyMatrix<RS extends RealNumericalScalar<RS, RM, CS, CM>, RM extends RealNumericalMatrix<RS, RM, CS, CM>, CS extends ComplexNumericalScalar<RS, RM, CS, CM>, CM extends ComplexNumericalMatrix<RS, RM, CS, CM>>
    extends BaseArray<SystemOperator<RS, RM, CS, CM>, AdjacencyMatrix<RS, RM, CS, CM>> {

  /** シリアルバージョン */
  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;

  /** unit of scalar */
  protected RS sunit;

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

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

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

  /**
   * 引数を成分とする行列を生成します。
   * 
   * @param elements 成分をもつ配列
   * @param sunit unit of scalar
   */
  @SuppressWarnings("boxing")
  public AdjacencyMatrix(final SystemOperator<RS, RM, CS, CM>[][] elements, RS sunit) {
    super(elements);
    this.sunit = sunit;
    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 SystemOperator<RS, RM, CS, CM> system = this.elements[sourceNode - 1][column];
        if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][sinkNode - 1];
        if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
          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;
    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) {
    print(this.elements, output, maxColumnSize);
  }

  /**
   * 隣接行列が表わすブロックシステムを返します。
   * 
   * @return 隣接行列が表わすブロックシステム
   */
  public BlockSystem<RS, RM, CS, CM> getBlockSystem() {
    if (isPiecewiseContinuous()) {
      if (isContinuousDynamic()) {
        return new BlockPiecewiseContinuousDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
      }

      if (isContinuous()) {
        return new BlockPiecewiseContinuousStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
      }

      if (isDiscreteDynamic()) {
        return new BlockDiscreteDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
      }

      if (isDiscrete()) {
        return new BlockDiscreteStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
      }

      if (isSampledDataDynamic()) {
        return new BlockPiecewiseSampledDataDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
      }

      return new BlockPiecewiseSampledDataStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isContinuousImplicitDynamic()) {
      return new BlockContinuousImplicitDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isContinuousDynamic()) {
      return new BlockContinuousExplicitDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isContinuous()) {
      return new BlockContinuousStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isDiscreteDynamic()) {
      return new BlockDiscreteDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isDiscrete()) {
      return new BlockDiscreteStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    if (isSampledDataDynamic()) {
      return new BlockSampledDataDynamicSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
    }

    return new BlockSampledDataStaticSystem<>((this.elements), this.inputNodes, this.outputNodes, this.sunit);
  }

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

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (continuousImplicitDynamic == false && system instanceof ContinuousImplicitDynamicSystem) {
          continuousImplicitDynamic = true;
        }
        if (system instanceof Sampler) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (continuousDynamic == false && system instanceof ContinuousDynamicSystem) {
          continuousDynamic = true;
        }
        if (system instanceof Sampler) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (discreteDynamic == false && system instanceof DiscreteDynamicSystem) {
          discreteDynamic = true;
        }
        if (system instanceof ContinuousDynamicSystem) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (system instanceof ConstantSystem) {
          continue;
        }

        if (system instanceof DiscreteDynamicSystem) {
          return false;
        }
        if (system instanceof DiscreteStaticSystem) {
          return false;
        }
        if (system instanceof HoldSystem) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (system instanceof PiecewiseDifferentialSystem) {
          return true;
        }
        if (system instanceof PiecewiseContinuousAlgebraicSystem) {
          return true;
        }
        if (system instanceof PiecewiseDifferentialDifferenceSystem) {
          return true;
        }
        if (system instanceof PiecewiseContinuousDiscreteAlgebraicSystem) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (system instanceof ConstantSystem) {
          continue;
        }

        if (system instanceof ContinuousDynamicSystem) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (system instanceof ContinuousDynamicSystem) {
          hasContinuousDynamics = true;
        }
        if (system instanceof Sampler) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

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

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

        if (hasSampler == false && system instanceof Sampler) {
          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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        final boolean notZeroSystem = system != ZeroSystem.getInstance(this.sunit);
        final boolean notInputPort = (system instanceof InputPort) == false;
        final boolean notOutputPort = (system instanceof OutputPort) == false;
        final boolean notLinear = system.isLinear() == false;
        if (notZeroSystem && notInputPort && notOutputPort && notLinear) {
          return false;
        }
      }
    }

    return true;
  }

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

    final SystemOperator<RS, RM, CS, CM>[][] matrix = 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 AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
      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 多重器と分離器を縮約した隣接行列
   */
  AdjacencyMatrix<RS, RM, CS, CM> contractMultiplexerAndDeMultiplexer() {
    boolean contracting = false;

    final SystemOperator<RS, RM, CS, CM>[][] matrix = 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 AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
      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 SystemOperator<RS, RM, CS, CM>[][] 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);
        SystemOperator<RS, RM, CS, CM> system = matrix[inputNode][outputNode];
        if (system != ZeroSystem.getInstance(this.sunit) && (system instanceof ConstantSystem) == false) {
          return;
        }
      }
    }

    for (int i = 1; i <= multiplexer.size(); i++) {
      final int inputNode = multiplexer.get(i);
      Multiplexer<RS, RM, CS, CM> mux = (Multiplexer<RS, RM, CS, CM>)matrix[inputNode][column];
      for (int j = 1; j <= deMultiplexer.size(); j++) {
        final int outputNode = deMultiplexer.get(j);
        DeMultiplexer<RS, RM, CS, CM> dex = (DeMultiplexer<RS, RM, CS, CM>)matrix[column][outputNode];
        ConstantSystem<RS, RM, CS, CM> system = dex.multiply(mux);

        SystemOperator<RS, RM, CS, CM> oldSystem = matrix[inputNode][outputNode];
        if (system.getGain().isZero() == false) {
          if (oldSystem == ZeroSystem.getInstance(this.sunit)) {
            matrix[inputNode][outputNode] = system;
          } else {
            matrix[inputNode][outputNode] = ((ConstantSystem<RS, RM, CS, CM>)oldSystem).add(system);
          }
        }
      }
    }

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

  /**
   * 分離器から多重器へのエッジを短絡します。
   * 
   * @param matrix 隣接行列
   * @param row 短絡する分離器を探す行番号
   * @param multiplexer 多重器の入力ポート番号と入力ポートのノード番号のマップ
   * @param deMultiplexer 分離器の出力ポート番号と出力ポートのノード番号のマップ
   */
  @SuppressWarnings("boxing")
  private void shortDeMultiplexerAndMultiplexer(final SystemOperator<RS, RM, CS, CM>[][] 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);
      DeMultiplexer<RS, RM, CS, CM> dex = (DeMultiplexer<RS, RM, CS, CM>)matrix[row][removingNode];
      for (int j = 1; j <= multiplexer.size(); j++) {
        final int outputNode = multiplexer.get(j);
        Multiplexer<RS, RM, CS, CM> mux = (Multiplexer<RS, RM, CS, CM>)matrix[removingNode][outputNode];
        ConstantSystem<RS, RM, CS, CM> system = mux.multiply(dex);
        if (system.getGain().isZero()) {
          matrix[row][outputNode] = ZeroSystem.getInstance(this.sunit);
        } 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] = ZeroSystem.getInstance(this.sunit);
        matrix[removingNode][outputNode] = ZeroSystem.getInstance(this.sunit);
      }
    }
  }

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

    for (int column = 0; column < getColumnSize(); column++) {
      final SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

      if (system instanceof DeMultiplexer) {
        deMultiplexer.put(((DeMultiplexer<RS, RM, CS, CM>)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 SystemOperator<RS, RM, CS, CM>[][] matrix, final int row) {
    final Map<Integer, Integer> multiplexer = new TreeMap<>();

    for (int column = 0; column < getColumnSize(); column++) {
      final SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

      if (system instanceof Multiplexer) {
        multiplexer.put(((Multiplexer<RS, RM, CS, CM>)system).getInputNumber(), column);
        continue;
      }

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

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

    for (int row = 0; row < getRowSize(); row++) {
      final SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

      if (system instanceof Multiplexer) {
        multiplexer.put(((Multiplexer<RS, RM, CS, CM>)system).getInputNumber(), row);
        continue;
      }

      multiplexer.clear();
    }
    return multiplexer;
  }

  /**
   * 定数システムを除去(定数エッジを縮約)した等価なシステム行列を生成します。
   * 
   * @param onlyUnit 単位システムのみを縮約するならばtrue
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 定数を除去した等価なシステム行列
   */
  AdjacencyMatrix<RS, RM, CS, CM> contractConstantEdge(final boolean onlyUnit, final boolean onlyConstant) {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = createClone(this.elements);
    AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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;
      }

      AdjacencyMatrix<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM>[][] 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 ZeroSystem) {
          continue;
        }
        matrix[row][unit] = matrix[row][target];
        matrix[row][target] = ZeroSystem.getInstance(this.sunit);
      }

      matrix[unit][unit] = matrix[target][target];
      matrix[target][target] = ZeroSystem.getInstance(this.sunit);
      matrix[target][unit] = ZeroSystem.getInstance(this.sunit);

      changed = true;
    }

    return changed;
  }

  /**
   * 自己ループのノードにつながる,かつ,列に唯一の単位行列が存在するノードのリストを得ます.
   * 
   * @return 自己ループのノードにつながる,かつ,列に唯一の単位行列が存在するノードのリスト
   */
  @SuppressWarnings("boxing")
  private List<Integer> getConnectedLoopNodes() {
    final SystemOperator<RS, RM, CS, CM>[][] 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 SystemOperator<RS, RM, CS, CM>[][] 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 IntegratorSystem) {
          connectedIntegrator.add(unit);
        }
      }
    }
    return connectedIntegrator;
  }

  /**
   * 自己ループが発生するノードのリストを得ます.
   * 
   * @return 自己ループノードのリスト
   */
  private List<Integer> getSelfLoopNodes() {
    final SystemOperator<RS, RM, CS, CM>[][] 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] != ZeroSystem.getInstance(this.sunit);
      if (hasSelfLoop) {
        selfLoopNumbers.add(Integer.valueOf(column));
      }
    }
    return selfLoopNumbers;
  }

  /**
   * 列ベクトル要素(ConstantSystem)を比較します.
   * 
   * @param opponent 比較対象
   * @return 一致するならばtrue,不一致ならばfalse
   */
  private boolean compareColumnVectors(AdjacencyMatrix<RS, RM, CS, CM> opponent) {
    for (int i = 1; i < this.getRowSize(); i++) {
      if (this.getElement(i, 1) instanceof ConstantSystem && opponent.getElement(i, 1) instanceof ConstantSystem) {
        final ConstantSystem<RS, RM, CS, CM> element = (ConstantSystem<RS, RM, CS, CM>)this.getElement(i, 1);
        final ConstantSystem<RS, RM, CS, CM> opponentElement = (ConstantSystem<RS, RM, CS, CM>)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 SystemOperator<RS, RM, CS, CM> element = this.getElement(i, 1);
        final SystemOperator<RS, RM, CS, CM> opponentElement = opponent.getElement(i, 1);
        if (element == ZeroSystem.getInstance(this.sunit)) {
          if (opponentElement instanceof ConstantSystem && opponentElement.isLinear() && opponentElement.getLinearSystem().getD().isZero()) {
            continue;
          }
        }
        if (opponentElement == ZeroSystem.getInstance(this.sunit)) {
          if (element instanceof ConstantSystem && element.isLinear() && element.getLinearSystem().getD().isZero()) {
            continue;
          }
        }

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

  /**
   * 代数ループを見つけ解決します。
   * 
   * @return 代数ループを解決すればtrue、そうでなければfalse
   */
  @SuppressWarnings({"boxing"})
  private boolean removeArgebraicloop() {
    SystemOperator<RS, RM, CS, CM>[][] 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] != ZeroSystem.getInstance(this.sunit);
      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++) {
      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 AdjacencyConstantMatrix<RS, RM, CS, CM> allMatrix = new AdjacencyConstantMatrix<>(matrix, this.sunit).transpose().createAdjacencyConstantMatrix();
    final AdjacencyConstantMatrix<RS, RM, CS, CM> baseMatrix = allMatrix.getLoopMatrix((ArrayList<Integer>)selfLoopNumbers);
    final AdjacencyConstantMatrix<RS, RM, CS, CM> subtractedMatrix = baseMatrix.subtractFromUnit();

    final AdjacencyConstantMatrix<RS, RM, CS, CM> 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] = ZeroSystem.getInstance(this.sunit);
      }
    }

    final AdjacencyConstantMatrix<RS, RM, CS, CM> multiplier = (new AdjacencyConstantMatrix<>(matrix, this.sunit)
        .getSubMatrix(1, matrix.length, selfLoopNumbers.get(0) + 1, selfLoopNumbers.get(0) + selfLoopNumbers.size()).transpose()).createAdjacencyConstantMatrix();

    final AdjacencyConstantMatrix<RS, RM, CS, CM> multipliedMatrix = inverseLoopMatrix.multiply(multiplier);
    final AdjacencyConstantMatrix<RS, RM, CS, CM> 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 線形システムまたは定数システムに定数行列を左から掛けた結果
   */
  SystemOperator<RS, RM, CS, CM> leftMultiplyGain(final SystemOperator<RS, RM, CS, CM> element, final RM gain) {
    final SystemOperator<RS, RM, CS, CM> newSystem;
    if (element instanceof ConstantSystem) {
      final RM gain1 = ((ConstantSystem<RS, RM, CS, CM>)element).getGain();
      newSystem = new ConstantSystem<>(gain.multiply(gain1));
    } else {
      newSystem = new ContinuousLinearDynamicSystem<>(element.getLinearSystem().leftMultiply(gain), this.sunit);
    }
    return newSystem;
  }

  /**
   * (列方向に)定数システムを前方のシステムに結合(定数直列エッジを縮約)します。
   * 
   * @param onlyUnit 単位システムのみを縮約するならばtrue
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 縮約したならばtrue、そうでなければfalse
   */
  private boolean contractConstantEdgeForwardColumnWise(final boolean onlyUnit, final boolean onlyConstant) {
    final SystemOperator<RS, RM, CS, CM>[][] 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] != ZeroSystem.getInstance(this.sunit)) {
        hasSelfLoop = true;
      }
      if (matrix[column][column] instanceof ConstantSystem && 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 ConstantSystem)) {
          continue;
        }

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

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

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

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

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

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

        final ConstantSystem<RS, RM, CS, CM> gainSystem = (ConstantSystem<RS, RM, CS, CM>)matrix[row][column];

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

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

          final SystemOperator<RS, RM, CS, CM> systemInColumn = matrix[column][k];

          if (systemInColumn == ZeroSystem.getInstance(this.sunit)) {
            continue;
          }

          final SystemOperator<RS, RM, CS, CM> systemInRow = matrix[row][k];

          if (systemInRow instanceof ConstantSystem && systemInColumn instanceof ConstantSystem) {
            if (onlyConstant) {
              if (((ConstantSystem<RS, RM, CS, CM>)systemInRow).isVariable() || ((ConstantSystem<RS, RM, CS, CM>)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 UnitSystem) {
              matrix[row][k] = ((ConstantSystem<RS, RM, CS, CM>)systemInRow).add(((ConstantSystem<RS, RM, CS, CM>)systemInColumn));
            } else if (gainSystem.isSizeDefined()) {
              matrix[row][k] = ((ConstantSystem<RS, RM, CS, CM>)systemInRow).add(((ConstantSystem<RS, RM, CS, CM>)systemInColumn).multiply(gainSystem));
            } else {
              allChanged = false;
              continue;
            }
          } else {
            assert systemInRow == ZeroSystem.getInstance(this.sunit) : Messages.getString("AdjacencyMatrix.1"); //$NON-NLS-1$
            assert systemInColumn instanceof LinearSystemOperator : Messages.getString("AdjacencyMatrix.2"); //$NON-NLS-1$

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

            setupInputSizeAndOutputSize(systemInColumn, column, k);

            if (gainSystem instanceof UnitSystem) {
              matrix[row][k] = systemInColumn;
            } else if (systemInColumn instanceof UnitSystem) {
              matrix[row][k] = gainSystem;
            } else if (systemInColumn instanceof ConstantSystem) {
              if (onlyConstant && ((ConstantSystem<RS, RM, CS, CM>)systemInColumn).isVariable()) {
                allChanged = false;
                continue;
              }

              matrix[row][k] = ((ConstantSystem<RS, RM, CS, CM>)systemInColumn).multiply(gainSystem);
            } else {
              allChanged = false;
              continue;
            }
          }

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

          if (isAllZeroInColumn(matrix, row, column)) {
            matrix[column][k] = ZeroSystem.getInstance(this.sunit);

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

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

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

    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();) {
      List<Integer> loopPath = iterator.next();
      Integer node = loopPath.get(0);

      ConstantSystem<RS, RM, CS, CM> weight;
      if (loopPath.size() == 1) {
        weight = (ConstantSystem<RS, RM, CS, CM>)matrix[node][node];
      } else {
        weight = contractLoopNode(node, new ArrayList<>(loopPath), null);
      }

      try {
        weight.unaryMinus().add(new UnitSystem<>(weight.getOutputSize(), this.sunit).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 AdjacencyMatrix<RS, RM, CS, CM> tmpAdjMatrix) {
    final List<List<Integer>> localMaximumCycles = new CycleMatrix<>(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 AdjacencyMatrix<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM> targetNode = tmpAdjMatrix.elements[row][node];
          if (targetNode == ZeroSystem.getInstance(this.sunit)) {
            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 SystemOperator<RS, RM, CS, CM>[][] 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(row))) {
            loopStartList.add(Integer.valueOf(node));
          }
        }
      }
    }
    return loopStartList;
  }

  /**
   */
  private void setIntegratorToZeroSystem() {
    final SystemOperator<RS, RM, CS, CM>[][] 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 IntegratorSystem) {
          this.elements[row][column] = ZeroSystem.getInstance(this.sunit);
        }
      }
    }
  }

  /**
   * ループを構成するノードを縮約し,その重みを返します.
   * 
   * @param node 対象のノード
   * @param loopList ループを構成するノードのリスト
   * @param weight 対象の重み
   * @return ConstantSystem ループを構成するノードの重み
   */
  @SuppressWarnings("boxing")
  private ConstantSystem<RS, RM, CS, CM> contractLoopNode(final Integer node, final List<Integer> loopList, final ConstantSystem<RS, RM, CS, CM> weight) {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;
    final int localRowSize = matrix.length;
    final int localColumnSize = localRowSize == 0 ? 0 : matrix[0].length;
    ConstantSystem<RS, RM, CS, CM> 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 ConstantSystem)) {
          continue;
        }

        final ConstantSystem<RS, RM, CS, CM> targetNode = (ConstantSystem<RS, RM, CS, CM>)matrix[node][column];
        if (weight == null) {
          weights = contractLoopNode(column, loopList, targetNode.clone());
        } else {
          loopList.remove(Integer.valueOf(node));
          if (targetNode instanceof UnitSystem) {
            weights = contractLoopNode(column, loopList, weight);
            continue;
          }
          if (targetNode instanceof NegativeUnitSystem) {
            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 SystemOperator<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[node][column];
      if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

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

    for (int row = 0; row < getRowSize(); row++) {
      final SystemOperator<RS, RM, CS, CM> system = this.elements[row][node];
      if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

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

    return -1;
  }

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

    final BooleanMatrix mat = new BooleanMatrix(getRowSize(), getColumnSize());
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        SystemOperator<RS, RM, CS, CM> systemOperator = this.elements[row][column];
        if (systemOperator != ZeroSystem.getInstance(this.sunit)) {
          if (systemOperator instanceof ConstantSystem && 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 AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(newElements, this.sunit);
    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 線形動的システムを状態空間表現による隣接行列に換えて拡張した行列
   */
  AdjacencyMatrix<RS, RM, CS, CM> expandLinearDynamicSystem() {
    final SystemOperator<RS, RM, CS, CM>[][] blockMatrix = replaceLinearDynamicSystemWithBLockSystem(this.elements, this.isRequiringPrimitiveExpression());
    final AdjacencyMatrix<RS, RM, CS, CM> blockSystem = new AdjacencyMatrix<>(blockMatrix, this.sunit);
    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 全てのブロックシステム成分を展開したシステムの隣接行列
   */
  AdjacencyMatrix<RS, RM, CS, CM> expandAllBlockSystem() {
    AdjacencyMatrix<RS, RM, CS, CM> oldSystem = this;
    AdjacencyMatrix<RS, RM, CS, CM> newSystem = oldSystem.expandBlockSystem();
    while (newSystem != oldSystem) {
      oldSystem = newSystem;
      newSystem = oldSystem.expandBlockSystem();
    }

    return newSystem;
  }

  /**
   * ブロックシステム成分を拡張したブロックシステムを生成します。
   * 
   * @return 拡張された場合、ブロックシステム成分を拡張したブロックシステム。拡張されない場合、同一システム。
   */
  private AdjacencyMatrix<RS, RM, CS, CM> expandBlockSystem() {
    final SystemOperator<RS, RM, CS, CM>[][] 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 BlockSystem) {
          final BlockSystem<RS, RM, CS, CM> blockSystem = (BlockSystem<RS, RM, CS, CM>)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 AdjacencyMatrix<RS, RM, CS, CM> createExpandedSystem(final BlockSystem<RS, RM, CS, CM> blockSystem, final int row, final int column) {
    final SystemOperator<RS, RM, CS, CM>[][] 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 SystemOperator<RS, RM, CS, CM>[][] newMatrix = insertRowAndColumn(matrix, min, max);
    final List<List<Integer>> sourceNodesSinkNodes = blockSystem.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 AdjacencyMatrix<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == null || system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }
        system.setID("" + row + "," + column); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  /**
   * {@link AdjacencyMatrix}の内部データである{@link SystemOperator}の2次元配列を返します。
   * 
   * @return 隣接行列({@link SystemOperator}の2次元配列)
   */
  SystemOperator<RS, RM, CS, CM>[][] getSystemEquationArray() {
    return createClone(this.elements);
  }

  /**
   * 積分器を下側へソートします。
   * 
   * @return 積分器を下側へソートした隣接行列
   */
  private AdjacencyMatrix<RS, RM, CS, CM> sortIntegratorLower() {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = createClone(this.elements);
    final AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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 SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;
    final int outputNodeSize = this.outputNodes.size();
    final int numberOfIntegrator = this.getIntegratorOrUnitDelaySystemSize();

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

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

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

  /**
   * 積分器を左側へソートします。
   * 
   * @return 積分器を左側へソートした隣接行列
   */
  private AdjacencyMatrix<RS, RM, CS, CM> sortIntegratorLeft() {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = createClone(this.elements);
    final AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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 SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;
    final int inputNodeSize = this.inputNodes.size();
    final int numberOfIntegrator = this.getIntegratorOrUnitDelaySystemSize();

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

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

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

  /**
   * 積分器が適正な場所に対角に並ぶようソートした隣接行列を生成します。
   * 
   * @return 積分器が適正な場所に対角に並ぶようソートした隣接行列
   */
  private AdjacencyMatrix<RS, RM, CS, CM> sortIntegratorIntoDiagonal() {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = createClone(this.elements);
    final AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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 = findIntegratorOrUnitDelayInRow(matrix, row);
      if (column == -1) {
        throw new IllegalArgumentException(Messages.getString("AdjacencyMatrix.7")); //$NON-NLS-1$
      }
      if (column == inputNodeSize + i) {
        continue;
      }

      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 IntegratorSystem) {
          count++;
        }
        if (this.elements[row][column] instanceof UnitDelaySystem) {
          count++;
        }
      }
    }

    return count;
  }

  /**
   * SourceブロックとSinkブロックにZeroSystemを代入します。
   */
  void setZeroToSourceAndSink() {
    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system instanceof InputPort || system instanceof OutputPort) {
          continue;
        }

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

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

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

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

        int outputSize = 0;
        boolean allInputSizeDefined = true;
        for (Multiplexer<RS, RM, CS, CM> multiplexer : ((Multiplexer<RS, RM, CS, CM>)system).getGroup().getMultiplexers()) {
          int oneInputSize = multiplexer.getInputSize();
          if (oneInputSize == -1) {
            allInputSizeDefined = false;
            break;
          }
          outputSize += oneInputSize;
        }
        if (allInputSizeDefined == false) {
          continue;
        }

        for (Multiplexer<RS, RM, CS, CM> multiplexer : ((Multiplexer<RS, RM, CS, CM>)system).getGroup().getMultiplexers()) {
          multiplexer.setOutputSize(outputSize);
        }
      }
    }

  }

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

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

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

        int inputSize = 0;
        boolean allOutputSizeDefined = true;
        for (DeMultiplexer<RS, RM, CS, CM> deMultiplexer : ((DeMultiplexer<RS, RM, CS, CM>)system).getGroup().getDeMultiplexers()) {
          int oneOutputSize = deMultiplexer.getOutputSize();
          if (oneOutputSize == -1) {
            allOutputSizeDefined = false;
            break;
          }
          inputSize += oneOutputSize;
        }
        if (allOutputSizeDefined == false) {
          continue;
        }

        for (DeMultiplexer<RS, RM, CS, CM> deMultiplexer : ((DeMultiplexer<RS, RM, CS, CM>)system).getGroup().getDeMultiplexers()) {
          deMultiplexer.setInputSize(inputSize);
        }
      }
    }

  }

  /**
   * SourceブロックとSinkブロックの間にあるシステムを返します。
   * 
   * @return SourceブロックとSinkブロックの間にあるシステム
   */
  AdjacencyMatrix<RS, RM, CS, CM> getSystemBetweenSourceAndSink() {
    final List<Integer> inputSourceNodes = new ArrayList<>();
    final List<Integer> outputSinkNodes = new ArrayList<>();

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

    inputSourceNodes.addAll(this.sourceNodes);
    outputSinkNodes.addAll(this.sinkNodes);

    if (inputSourceNodes.size() == 0 || outputSinkNodes.size() == 0) {
      return this;
    }

    final AdjacencyMatrix<RS, RM, CS, CM> selectedSystem = getSystemBetweenNodes(inputSourceNodes, outputSinkNodes, false);
    selectedSystem.modifyID();
    return selectedSystem;
  }

  /**
   * 入力ノードから出力ノードまでの線形システムを返します。
   * 
   * @param requiringReachableSubSystem 可到達なサブシステム(入力ノードから出力ノードまでのパスに対応するシステム)を求めるならばtrue
   * 
   * @return 入力ノードから出力ノードまでの線形システム
   */
  public LinearSystem<RS, RM, CS, CM> getLinearSystem(final boolean requiringReachableSubSystem) {
    final AdjacencyMatrix<RS, RM, CS, CM> 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 RM a = allSystem.createNumericalA(stateSizes, stateSize);
    final RM b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final RM c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final RM d = allSystem.createNumericalD(inputSize, outputSize);
    final RM e = allSystem.createNumericalE(stateSizes, stateSize);

    final LinearSystem<RS, RM, CS, CM> linearSystem = this.requiringDescriptor ? LinearSystemFactory.createLinearSystem(a, b, c, d, e, true)
        : LinearSystemFactory.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 LinearSystem<RS, RM, CS, CM> getLinearSystemForMaxima(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<RS, RM> processor) {
    final AdjacencyMatrix<RS, RM, CS, CM> 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 RM a = allSystem.createNumericalA(stateSizes, stateSize);
    final RM b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final RM c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final RM d = allSystem.createNumericalD(inputSize, outputSize);

    final LinearSystem<RS, RM, CS, CM> linearSystem = LinearSystemFactory.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 LinearSystem<RS, RM, CS, CM> getLinearSystemByProcessor(final boolean requiringReachableSubSystem, final ReversePolishNotationProcessor<RS, RM> processor) {
    final AdjacencyMatrix<RS, RM, CS, CM> 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 RM a = allSystem.createNumericalA(stateSizes, stateSize);
    final RM b = allSystem.createNumericalB(stateSizes, stateSize, inputSize);
    final RM c = allSystem.createNumericalC(stateSizes, stateSize, outputSize);
    final RM d = allSystem.createNumericalD(inputSize, outputSize);
    final RM 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);

    LinearSystem<RS, RM, CS, CM> linearSystem;
    if (this.requiringDescriptor) {
      linearSystem = LinearSystemFactory.createLinearSystem(a, b, c, d, e, true);
    } else {
      linearSystem = LinearSystemFactory.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 入力ノードから出力ノードまでの線形システム
   */
  AdjacencyMatrix<RS, RM, CS, CM> getLinearSystemFromInputToOutput(final boolean requiringReachableSubSystem) {
    setZeroToSourceAndSink();

    AdjacencyMatrix<RS, RM, CS, CM> 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 AdjacencyMatrix<RS, RM, CS, CM> linearDynamicExpandedSystem = selectedSystem.expandLinearDynamicSystem();
    final AdjacencyMatrix<RS, RM, CS, CM> expandedSystem = this.isRequiringPrimitiveExpression() == false ? linearDynamicExpandedSystem
        : linearDynamicExpandedSystem.expandAllConstantAndIntegratorSystem();
    expandedSystem.setRequiringLinearSystem(this.requiringLinearSystem);
    expandedSystem.setRequiringDescriptor(this.requiringDescriptor);
    expandedSystem.setRequiringPrimitiveExpression(this.requiringPrimitiveExpression);

    AdjacencyMatrix<RS, RM, CS, CM> oldSystem = expandedSystem;
    AdjacencyMatrix<RS, RM, CS, CM> contractedSystem = expandedSystem;
    do {
      oldSystem = contractedSystem;
      contractedSystem = oldSystem.contractConstantEdge(false, false);
    } while (contractedSystem != oldSystem);

    final AdjacencyMatrix<RS, RM, CS, CM> lowerSortedSystem = contractedSystem.sortIntegratorLower();
    final AdjacencyMatrix<RS, RM, CS, CM> leftSortedSystem = lowerSortedSystem.sortIntegratorLeft();
    final AdjacencyMatrix<RS, RM, CS, CM> diagonalIntegratorSystem = leftSortedSystem.sortIntegratorIntoDiagonal();
    final AdjacencyMatrix<RS, RM, CS, CM> inputOutputSortedSystem = diagonalIntegratorSystem.sortByInputNodeAndOutputNode();
    final AdjacencyMatrix<RS, RM, CS, CM> allSystem = inputOutputSortedSystem.sortByStateNumber();

    allSystem.setDefaultSizeToUnknownPort();
    return allSystem;
  }

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

    return newSystem;
  }

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

  /**
   * inputノードに繋る定数システムに応じて inputノードの数を拡張します.
   * 
   * @return 拡張されたinputノードを持つ隣接行列
   */
  @SuppressWarnings("boxing")
  private AdjacencyMatrix<RS, RM, CS, CM> expandInput() {
    AdjacencyMatrix<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;
      final int inputNode = input - 1;

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

      final SystemOperator<RS, RM, CS, CM>[][] newMatrix = insertColumn(ans.elements, inputNode + 1, inputNode + expandInputSize);

      ans = new AdjacencyMatrix<>(newMatrix, this.sunit);
      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 SystemOperator<RS, RM, CS, CM>[][] matrix) {
    final int size = matrix[0].length;
    int expandInputSize = 0;
    for (int column = 0; column < size; column++) {
      final SystemOperator<RS, RM, CS, CM> system = matrix[inputNode][column];
      if (system instanceof ZeroSystem) 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(AdjacencyMatrix<RS, RM, CS, CM> 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(AdjacencyMatrix<RS, RM, CS, CM> 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 AdjacencyMatrix<RS, RM, CS, CM> expandOutput() {
    AdjacencyMatrix<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;
      final int size = matrix.length;

      int expandOutputSize = 0;
      for (int row = 0; row < size; row++) {
        final SystemOperator<RS, RM, CS, CM> system = matrix[row][matrix[0].length - (size - outputNode)];
        if (system instanceof ZeroSystem) {
          continue;
        }
        if (system.isSISO()) {
          continue;
        }
        expandOutputSize = (system.getOutputSize() - 1);
        break;
      }

      if (expandOutputSize == 0) {
        continue;
      }

      final SystemOperator<RS, RM, CS, CM>[][] newMatrix = insertRow(ans.elements, matrix.length - expandOutputSize + 1, matrix.length);
      final int[] insertingNodes = createInsertingNodesArray(offset, output, expandOutputSize);

      ans = new AdjacencyMatrix<>(newMatrix, this.sunit);
      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 AdjacencyMatrix<RS, RM, CS, CM> expandConstantAndIntegratorSystem() {
    final SystemOperator<RS, RM, CS, CM>[][] 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 ConstantSystem && false == matrix[row][column] instanceof IntegratorSystem) {
          continue;
        }
        final SystemOperator<RS, RM, CS, CM> 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 ConstantSystem && false == matrix[row][k] instanceof IntegratorSystem) {
            continue;
          }
          return createExpandUnitInRow(system, row);
        }

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

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

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

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

    SystemOperator<RS, RM, CS, CM>[][] newMatrix = 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 ConstantSystem && false == newMatrix[k][column] instanceof IntegratorSystem) {
        continue;
      }
      newMatrix = 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 UnitSystem) {
            newMatrix[i][j] = new UnitSystem<>(1, this.sunit);
            continue;
          }
          newMatrix[i][j] = new NegativeUnitSystem<>(1, this.sunit);
        }
      }
      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 AdjacencyMatrix<RS, RM, CS, CM> ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);

    return ans;
  }

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

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

    SystemOperator<RS, RM, CS, CM>[][] newMatrix = 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 ConstantSystem && false == newMatrix[row][k] instanceof IntegratorSystem) {
        continue;
      }
      newMatrix = 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 UnitSystem) {
            newMatrix[i][j] = new UnitSystem<>(1, this.sunit);
            continue;
          }
          newMatrix[i][j] = new NegativeUnitSystem<>(1, this.sunit);
        }
      }
      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 AdjacencyMatrix<RS, RM, CS, CM> ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);

    return ans;
  }

  /**
   * newMatrixをelementに持ち,基の隣接行列からinsertingRowNodesとinsertingColumnNodesを 用いて入力・出力ポートを拡張した隣接行列を返します.
   * 
   * @param newMatrix 新しいelement要素
   * @param insertingRowNodes 挿入された行ノードの配列
   * @param insertingColumnNodes 挿入された列ノードの配列
   * @return 与えられたelementを持つ拡張された隣接行列
   */
  private AdjacencyMatrix<RS, RM, CS, CM> updateExpandedAdjacencyMatrix(SystemOperator<RS, RM, CS, CM>[][] newMatrix, final int[] insertingRowNodes, final int[] insertingColumnNodes) {
    final AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(newMatrix, this.sunit);
    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 AdjacencyMatrix<RS, RM, CS, CM> createExpandConstantAndIntegratorSystem(final SystemOperator<RS, RM, CS, CM> system, final int row, final int column) {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = this.elements;

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

    SystemOperator<RS, RM, CS, CM>[][] 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 UnitSystem || system instanceof NegativeUnitSystem) {
          if ((i - row + rowOffset) != (j - column)) {
            continue;
          }
          newMatrix[i][j] = new UnitSystem<>(1, this.sunit);
        } else if (system instanceof IntegratorSystem) {
          if ((i - row + rowOffset) != (j - column)) {
            continue;
          }
          newMatrix[i][j] = createPrimitiveIntegratorSystem((IntegratorSystem<RS, RM, CS, CM>)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 AdjacencyMatrix<RS, RM, CS, CM> ans = updateExpandedAdjacencyMatrix(newMatrix, insertingRowNodes, insertingColumnNodes);
    return ans;
  }

  /**
   * 指定された行を基点として最小要素となる積分器システムを作成します.
   * 
   * @param system 展開対象の積分器
   * @param row 展開の基点となる行
   * @param i 展開される行
   * @return 新たにtagを追加された積分器
   */
  private IntegratorSystem<RS, RM, CS, CM> createPrimitiveIntegratorSystem(final IntegratorSystem<RS, RM, CS, CM> system, final int row, int i) {
    final IntegratorSystem<RS, RM, CS, CM> integrator = system;
    final IntegratorSystem<RS, RM, CS, CM> newIntegrator = new IntegratorSystem<>(1, this.sunit);
    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 ConstantSystem<RS, RM, CS, CM> createPrimitiveConstantSystem(final SystemOperator<RS, RM, CS, CM> system, final int row, final int column, int i, int j) {
    final ConstantSystem<RS, RM, CS, CM> constantSystem = (ConstantSystem<RS, RM, CS, CM>)system;
    RS[] ee = this.sunit.createArray(1);
    ee[0] = constantSystem.getGain().transpose().getElement(i - row + 1, j - column + 1);
    final ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(this.sunit.createGrid(ee));
    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 SystemOperator<RS, RM, CS, CM>[][] expandMatrixByInputOutputSize(final SystemOperator<RS, RM, CS, CM>[][] 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;

    SystemOperator<RS, RM, CS, CM>[][] newMatrix = insertRowAndColumn(matrix, rowMin, rowMax, columnMin, columnMax);
    return newMatrix;
  }

  /**
   * 入力ポートと出力ポートが全く接続されていないシステムの隣接行列を返します。
   * 
   * @return 入力ポートと出力ポートが全く接続されていないシステムの隣接行列
   */
  private AdjacencyMatrix<RS, RM, CS, CM> createZeroSystem() {
    SystemOperator<RS, RM, CS, CM>[][] system = new SystemOperator[2][2];
    system[0][0] = ZeroSystem.getInstance(this.sunit);
    system[0][1] = new ConstantSystem<>(this.sunit.createZeroGrid(1, 1));
    system[1][0] = ZeroSystem.getInstance(this.sunit);
    system[1][1] = ZeroSystem.getInstance(this.sunit);

    return new AdjacencyMatrix<>(system, this.sunit);
  }

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

    for (int inputNode : this.inputNodes) {
      for (int outputNode : this.outputNodes) {
        final SystemOperator<RS, RM, CS, CM> system = this.elements[inputNode - 1][outputNode - 1];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }
        if (system instanceof UnitSystem && (system.getInputSize() == -1 || system.getOutputSize() == -1)) {
          system.setInputSize(defaultSize);
          system.setOutputSize(defaultSize);
        }
      }
    }
  }

  /**
   * 線形システムに入力、出力、状態のタグを設定します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param linearSystem 線形システム
   */
  private void setInputOutputStateTagToLinearSystem(final LinearSystem<RS, RM, CS, CM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
      stateSizes[i] = system.getStateSize();
    }
    return stateSizes;
  }

  /**
   * 指定されたノード間に存在するシステムを返します。
   * 
   * @param sources 出発ノード
   * @param destinations 到着ノード
   * @param isBidirectional 双方向に繋がっているパス上にあるシステムを求めるならばtrue、そうでなければfalse
   * @return 指定されたノード間に存在するシステム
   */
  AdjacencyMatrix<RS, RM, CS, CM> getSystemBetweenNodes(final List<Integer> sources, final List<Integer> destinations, final boolean isBidirectional) {
    final BooleanMatrix connection = new ConnectionMatrix<>(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 SystemOperator<RS, RM, CS, CM> system = getElement(row, column);
        if (system == ZeroSystem.getInstance(this.sunit)) {
          connection.setElement(row, column, false);
        }
      }
    }

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

    final AdjacencyMatrix<RS, RM, CS, CM> 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 ReachableMatrix<>(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 ReachableMatrix<>(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 AdjacencyMatrix<RS, RM, CS, CM> sortByInputNodeAndOutputNode() {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = createClone(this.elements);
    final List<Integer> newInputNodes = sortByInputNode(matrix); // this.inputPortTags
    final List<Integer> newOutputNodes = sortByOutputNode(matrix); // this.outputPortTags

    AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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 AdjacencyMatrix<RS, RM, CS, CM> sortByStateNumber() {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = 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 = ((DynamicSystem<RS, RM, CS, CM>)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;

      exchangeRowAndColumn(matrix, row1, row2);

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

      exchangeRowAndColumn(matrix, column1, column2);

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

    final AdjacencyMatrix<RS, RM, CS, CM> ans = new AdjacencyMatrix<>(matrix, this.sunit);
    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 SystemOperator<RS, RM, CS, CM>[][] 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;
      }

      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 SystemOperator<RS, RM, CS, CM>[][] 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;
      }

      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 RM createNumericalA(final int[] stateSizes, final int stateSize) {
    final RM a = this.sunit.createZeroGrid(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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final RM aij = ((ConstantSystem<RS, RM, CS, CM>)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<RS, RM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];

        if (system == ZeroSystem.getInstance(this.sunit)) {
          a[j][i] = "0"; //$NON-NLS-1$
        } else {
          a[j][i] = processor.getResult((ReversePolishNotationOperand<RS, RM>)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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
      if ((system instanceof IntegratorSystem) == false && (system instanceof UnitDelaySystem) == false) {
        throw new RuntimeException(Messages.getString("AdjacencyMatrix.13")); //$NON-NLS-1$
      }

      if (system instanceof IntegratorSystem) {
        stateTags.add(((IntegratorSystem<RS, RM, CS, CM>)system).getTag());
      }
      if (system instanceof UnitDelaySystem) {
        stateTags.add(((UnitDelaySystem<RS, RM, CS, CM>)system).getTag());
      }
    }

    return stateTags;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Bを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @param inputSize 全システムの入力の数
   * @return 線形動的システムのシステム行列B
   */
  private RM createNumericalB(final int[] stateSizes, final int stateSize, final int inputSize) {
    final int numberOfSubsystem = stateSizes.length;
    final int inputNodeSize = this.inputNodes.size();
    final RM b = this.sunit.createZeroGrid(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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final RM bij = ((ConstantSystem<RS, RM, CS, CM>)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<RS, RM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          b[j][i] = "0"; //$NON-NLS-1$
        } else {
          b[j][i] = processor.getResult((ReversePolishNotationOperand<RS, RM>)system);
        }
      }
    }

    return b;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Cを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @param outputSize 全システムの出力の数
   * @return 線形動的システムのシステム行列C
   */
  private RM 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 RM c = this.sunit.createZeroGrid(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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          offsetRow += getNodeSignalSize(column + 1);
          continue;
        }
        final RM cij = ((ConstantSystem<RS, RM, CS, CM>)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<RS, RM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          c[j][i] = "0"; //$NON-NLS-1$
        } else {
          c[j][i] = processor.getResult((ReversePolishNotationOperand<RS, RM>)system);
        }
      }
    }

    return c;
  }

  /**
   * 隣接行列から線形動的システムのシステム行列Dを生成します。
   * 
   * @param inputSize 全システムの入力の数
   * @param outputSize 全システムの出力の数
   * @return 線形動的システムのシステム行列D
   */
  private RM createNumericalD(final int inputSize, final int outputSize) {
    final RM d = this.sunit.createZeroGrid(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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          offsetRow += getNodeSignalSize(column + 1);
          continue;
        }
        final RM dij = ((ConstantSystem<RS, RM, CS, CM>)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<RS, RM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          d[j][i] = "0"; //$NON-NLS-1$
        } else {
          d[j][i] = processor.getResult((ReversePolishNotationOperand<RS, RM>)system);
        }
      }
    }

    return d;
  }

  /**
   * 指定されたノードを含む部分グラフを返します。
   * 
   * @param nodes 部分グラフを形成するノードの番号のリスト
   * @return 指定されたノードを含む部分グラフ
   */
  public AdjacencyMatrix<RS, RM, CS, CM> 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 AdjacencyMatrix<RS, RM, CS, CM> getSubgraph(final BooleanMatrix candidates) {
    final AdjacencyMatrix<RS, RM, CS, CM> result = new AdjacencyMatrix<>(getRowSize(), getColumnSize(), this.sunit);
    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 RM getMinusOutputSizeMatrix() {
    final RM minusOutputSizeMatrix = this.sunit.createZeroGrid(getRowSize(), getColumnSize());

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }
        minusOutputSizeMatrix.setElement(row + 1, column + 1, -system.getOutputSize());
      }
    }

    return minusOutputSizeMatrix;
  }

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

    for (int row = 0; row < getRowSize(); row++) {
      for (int column = 0; column < getColumnSize(); column++) {
        final SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          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 AdjacencyMatrix<RS, RM, CS, CM> other = (AdjacencyMatrix<RS,RM,CS,CM>)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] != ZeroSystem.getInstance(this.sunit)) {
        return (this.elements[nodeNumber - 1][column]).getInputSize();
      }
    }

    for (int row = 0; row < getRowSize(); row++) {
      if (this.elements[row][nodeNumber - 1] != ZeroSystem.getInstance(this.sunit)) {
        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 SystemOperator<RS, RM, CS, CM> 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 AdjacencyMatrix<RS, RM, CS, CM> clone() {
    final AdjacencyMatrix<RS, RM, CS, CM> inst = super.createClone();
    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の要素を持つ隣接文字列行列
   */
  AdjacencyConstantMatrix<RS, RM, CS, CM> createAdjacencyConstantMatrix() {
    return new AdjacencyConstantMatrix<>(this.elements, this.sunit);
  }

  /**
   * 単位システムが存在する要素を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 SystemOperator<RS, RM, CS, CM> systemOperator = this.elements[row][column];
        if (systemOperator != ZeroSystem.getInstance(this.sunit)) {
          if (systemOperator instanceof UnitSystem) {
            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 SystemOperator<RS, RM, CS, CM>[][] matrix, boolean debugMode, final String message) {
    if (debugMode) {
      System.out.println(message);
      showMatrix(matrix);
    }
  }

  /**
   * 隣接行列から線形動的システムのディスクリプタ行列Eを生成します。
   * 
   * @param stateSizes サブシステムの状態の数の配列
   * @param stateSize 全システムの状態の数
   * @return 線形動的システムのディスクリプタ行列D
   */
  private RM createNumericalE(int[] stateSizes, final int stateSize) {
    final RM e = this.sunit.createZeroGrid(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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];
        if (system == ZeroSystem.getInstance(this.sunit)) {
          offsetRow += stateSizes[k2++];
          continue;
        }
        final RM aij = ((ConstantSystem<RS, RM, CS, CM>)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<RS, RM> 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 SystemOperator<RS, RM, CS, CM> system = this.elements[row][column];

        if (system == ZeroSystem.getInstance(this.sunit)) {
          e[j][i] = "0"; //$NON-NLS-1$
        } else {
          e[j][i] = processor.getResult((ReversePolishNotationOperand<RS, RM>)system);
        }
      }
    }

    return e;
  }

  /**
   * SystemOperatorの中身を出力します。
   * 
   * @param matrix システム行列
   */
  public void showMatrix(SystemOperator<RS, RM, CS, CM>[][] 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 ZeroSystem) {
          stringBuffer.append("0" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof UnitSystem) {
          stringBuffer.append("P" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof NegativeUnitSystem) {
          stringBuffer.append("N" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof IntegratorSystem) {
          stringBuffer.append("q" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof UnitDelaySystem) {
          stringBuffer.append("q" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof ContinuousLinearDynamicSystem) {
          stringBuffer.append("L" + separator); //$NON-NLS-1$
        } else if (matrix[row][col] instanceof ConstantSystem) {
          stringBuffer.append(new WithoutCancellationExpressionProcessor<RS,RM>().getResult(((ConstantSystem<RS,RM,CS,CM>)matrix[row][col])) + separator);
        } else if (matrix[row][col] instanceof ConstantSystem) {
          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 void showMatrix(AdjacencyMatrix<RS, RM, CS, CM> adjacencyMatrix) {
    final SystemOperator<RS, RM, CS, CM>[][] 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 ZeroSystem) stringBuffer.append("0" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof UnitSystem) stringBuffer.append("P" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof NegativeUnitSystem) stringBuffer.append("N" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof IntegratorSystem) stringBuffer.append("q" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof UnitDelaySystem) stringBuffer.append("q" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof ContinuousLinearDynamicSystem) stringBuffer.append("L" + separator); //$NON-NLS-1$
        else if (matrix[row][col] instanceof ConstantSystem) stringBuffer.append(new ExpressionProcessor<RS, RM>().getResult(((ConstantSystem<RS, RM, CS, CM>)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 AdjacencyMatrix<RS, RM, CS, CM> expandDegree() {
    final AdjacencyMatrix<RS, RM, CS, CM> oldAdjMatrix = this;

    int loopNode = 0;
    int srcColumnSize = oldAdjMatrix.getColumnSize();
    for (int i = 1; i < srcColumnSize; i++) {
      if (oldAdjMatrix.getElement(i, i).equals(ZeroSystem.getInstance(this.sunit))) {
        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 AdjacencyMatrix<RS, RM, CS, CM> newAdjMatrix = new AdjacencyMatrix<>(getRowSize() + 1, getColumnSize() + 1, this.sunit);
    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));

    SystemOperator<RS, RM, CS, CM> loopsystem = oldAdjMatrix.getElement(loopNode, loopNode);

    final IntegratorSystem<RS, RM, CS, CM> integratorSystem = new IntegratorSystem<>(loopsystem.getInputSize(), this.sunit);
    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 UnitSystem<>(loopsystem.getInputSize(), this.sunit);
    final ConstantSystem<RS, RM, CS, CM> sys = ((ConstantSystem<RS, RM, CS, CM>)loopsystem).subtract(new UnitSystem<>(loopsystem.getOutputSize(), loopsystem.getInputSize(), this.sunit));

    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 AdjacencyMatrix<RS, RM, CS, CM> ans = this;
    if (ans.getElement(targetNode, column) == ZeroSystem.getInstance(this.sunit)) {
      ans.setElement(targetNode, column, ans.getElement(loopColumn, column));
    } else {
      ans.setElement(targetNode, column, ((ConstantSystem<RS, RM, CS, CM>)ans.getElement(targetNode, column)).add((ConstantSystem<RS, RM, CS, CM>)ans.getElement(loopColumn, column)));
    }
    ans.setElement(loopColumn, column, ZeroSystem.getInstance(this.sunit));
  }

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

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

      SystemOperator<RS, RM, CS, CM> element = ans.getElement(i, i);
      if (element != ZeroSystem.getInstance(this.sunit) && element.isLinear()) {
        if (element.getLinearSystem().getD().isZero() == false) {
          loopColumnList.add(i);
        } else if (((ConstantSystem<RS, RM, CS, CM>)element).isVariable() && ((ConstantSystem<RS, RM, CS, CM>)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, ZeroSystem.getInstance(this.sunit));
          }
        }
      }
    }

    return changed;
  }

  /**
   * E行列に当たる部分の符号を反転し単位行列を加えます.
   */
  private void transformEMatrix() {
    final AdjacencyMatrix<RS, RM, CS, CM> 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++) {
      SystemOperator<RS, RM, CS, CM> element = allSystem.getElement(i, i);
      if (element == ZeroSystem.getInstance(this.sunit)) {
        allSystem.setElement(i, i, new UnitSystem<>(allSystem.getElement(i, i - numberOfIntegrator).getStateSize(), this.sunit));
      } else {
        final ConstantSystem<RS, RM, CS, CM> system = ((ConstantSystem<RS, RM, CS, CM>)element).unaryMinus().add(new UnitSystem<>(element.getInputSize(), this.sunit));
        system.setExpression(new ExpressionProcessor<RS, RM>().getResult(system));
        allSystem.setElement(i, i, system);
      }
    }
  }

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

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

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

    final AdjacencyConstantMatrix<RS, RM, CS, CM> adjacencyConstantMatrix = new AdjacencyConstantMatrix<>(allSystem.getElements(), this.sunit);
    final AdjacencyConstantMatrix<RS, RM, CS, CM> A = adjacencyConstantMatrix.createA(stateSizes, allSystem.inputNodes.size());
    final AdjacencyConstantMatrix<RS, RM, CS, CM> B = adjacencyConstantMatrix.createB(stateSizes, allSystem.inputNodes.size());
    final AdjacencyConstantMatrix<RS, RM, CS, CM> C = adjacencyConstantMatrix.createC(stateSizes, allSystem.inputNodes.size(), allSystem.outputNodes.size());
    final AdjacencyConstantMatrix<RS, RM, CS, CM> D = adjacencyConstantMatrix.createD(allSystem.inputNodes.size(), allSystem.outputNodes.size());
    final AdjacencyConstantMatrix<RS, RM, CS, CM> E = adjacencyConstantMatrix.createE(stateSizes, allSystem.inputNodes.size());
    RS[] ss = this.sunit.createArray(1);
    ss[0] = this.sunit.create(20000);
    final ConstantSystem<RS, RM, CS, CM> s = new ConstantSystem<>(this.sunit.createGrid(ss));
    s.setExpression("s"); //$NON-NLS-1$
    s.setVariable(true);
    final int eSize = E.getColumnSize();
    final SystemOperator<RS, RM, CS, CM>[][] sS = new SystemOperator[eSize][eSize];
    for (int i = 0; i < eSize; i++) {
      sS[i][i] = s;
    }
    final AdjacencyConstantMatrix<RS, RM, CS, CM> S = new AdjacencyConstantMatrix<>(sS, this.sunit);

    final AdjacencyConstantMatrix<RS, RM, CS, CM> pencileInverse = (S.multiply(E).subtract(A)).inverse();
    final AdjacencyConstantMatrix<RS, RM, CS, CM> multiply = C.multiply(pencileInverse);
    final AdjacencyConstantMatrix<RS, RM, CS, CM> 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;
  }

  /**
   * 線形動的システムの状態空間表現によるブロックシステムに置き換えます。
   * 
   * @param matrix 隣接行列
   * @param requireringPrimitive 最小の要素まで分解するならばtrue
   * @return 線形動的システムの状態空間表現をブロックシステムに置き換えた隣接行列
   */
  @SuppressWarnings("boxing")
  SystemOperator<RS, RM, CS, CM>[][] replaceLinearDynamicSystemWithBLockSystem(final SystemOperator<RS, RM, CS, CM>[][] matrix, final boolean requireringPrimitive) {
    final SystemOperator<RS, RM, CS, CM>[][] newMatrix = createClone(matrix);

    final int size = newMatrix.length;

    for (int row = 0; row < size; row++) {
      for (int column = 0; column < size; column++) {
        if (newMatrix[row][column] == ZeroSystem.getInstance(this.sunit)) {
          continue;
        }

        if (!newMatrix[row][column].isLinear() || !newMatrix[row][column].isDynamic()) {
          continue;
        }

        final List<Integer> inputNodesLocal = new ArrayList<>(Arrays.asList(1));
        final List<Integer> outputNodesLocal = new ArrayList<>(Arrays.asList(4));

        if ((newMatrix[row][column] instanceof IntegratorSystem) == false && requireringPrimitive) {
          final List<Integer> newOutputNodes = new ArrayList<>(Arrays.asList(newMatrix[row][column].getLinearSystem().getStateSize() * 2 + 2));
          if (((ContinuousLinearDynamicSystem<RS, RM, CS, CM>)newMatrix[row][column]).isTransferFuntion() == true) {
            if (newMatrix[row][column].getLinearSystem().isProper() == false) throw new IllegalArgumentException(Messages.getString("AdjacencyMatrixUtil.0")); //$NON-NLS-1$
            newMatrix[row][column] = new BlockContinuousExplicitDynamicSystem<>(createSymbolicLinearDynamicSystemBlockMatrixForTransferFunction(newMatrix[row][column]), inputNodesLocal,
                newOutputNodes, this.sunit);
            continue;
          }
          newMatrix[row][column] = new BlockContinuousExplicitDynamicSystem<>(createSymbolicLinearDynamicSystemBlockMatrix(newMatrix[row][column]), inputNodesLocal, newOutputNodes, this.sunit);
        } else {
          newMatrix[row][column] = new BlockContinuousExplicitDynamicSystem<>(createLinearDynamicSystemBlockMatrix(newMatrix[row][column]), inputNodesLocal, outputNodesLocal, this.sunit);
        }

      }
    }

    return newMatrix;
  }

  /**
   * 線形動的システム(状態空間表現)の状態空間表現による隣接行列を生成します。
   * 
   * <p> システムを最小要素で隣接行列上に展開します.
   * 
   * @param system 線形動的システム
   * 
   * @return 線形動的システム(状態空間表現)の状態空間表現による隣接行列
   */
  private SystemOperator<RS, RM, CS, CM>[][] createSymbolicLinearDynamicSystemBlockMatrix(final SystemOperator<RS, RM, CS, CM> system) {
    final LinearSystemOperator<RS, RM, CS, CM> linearSystem = (LinearSystemOperator<RS, RM, CS, CM>)system;

    final int aSize = linearSystem.getA().getColumnSize();
    final int inputSize = linearSystem.getB().getColumnSize();
    final int outputSize = linearSystem.getC().getRowSize();

    final SystemOperator<RS, RM, CS, CM>[][] matrix = new SystemOperator[aSize * 2 + 1 + inputSize][aSize * 2 + 1 + outputSize];

    setZeroSystemToNullElement(matrix);

    for (int i = 0; i < aSize; i++) {
      IntegratorSystem<RS, RM, CS, CM> integrator = new IntegratorSystem<>(1, this.sunit);
      integrator.setTag("x_" + linearSystem.getTag() + "_" + (i + 1)); //$NON-NLS-1$ //$NON-NLS-2$
      integrator.setStateNumber(i + 1);
      matrix[aSize + inputSize + i][1 + i] = integrator;
    }

    // B取得
    RM b = linearSystem.getB();
    for (int i = 0; i < b.getRowSize(); i++) {
      for (int j = 0; j < b.getColumnSize(); j++) {
        final ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(b.getSubMatrix(i + 1, i + 1, j + 1, j + 1));
        final int rowIndex = i + 1;
        final int columnIndex = j + 1;
        element.setExpression(linearSystem.getTag() + "_b_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setTag(linearSystem.getTag() + "_b_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setVariable(true);
        matrix[j][aSize + 1 + i] = element;
      }
    }

    // A取得
    final RM a = linearSystem.getA();
    for (int i = 0; i < a.getRowSize(); i++) {
      for (int j = 0; j < a.getColumnSize(); j++) {
        final ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(a.getSubMatrix(i + 1, i + 1, j + 1, j + 1));
        final int rowIndex = i + 1;
        final int columnIndex = j + 1;
        element.setExpression(linearSystem.getTag() + "_a_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setTag(linearSystem.getTag() + "_a_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setVariable(true);
        matrix[inputSize + j][aSize + 1 + i] = element;
      }
    }

    // C取得
    final RM c = linearSystem.getC();
    for (int i = 0; i < c.getRowSize(); i++) {
      for (int j = 0; j < c.getColumnSize(); j++) {
        final ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(c.getSubMatrix(i + 1, i + 1, j + 1, j + 1));
        final int rowIndex = i + 1;
        final int columnIndex = j + 1;
        element.setExpression(linearSystem.getTag() + "_c_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setTag(linearSystem.getTag() + "_c_" + i + 1 + "_" + j + 1); //$NON-NLS-1$ //$NON-NLS-2$
        element.setVariable(true);
        matrix[inputSize + j][aSize * 2 + 1 + i] = element;
      }
    }

    // D取得
    final RM d = linearSystem.getD();
    for (int i = 0; i < d.getRowSize(); i++) {
      for (int j = 0; j < d.getColumnSize(); j++) {
        final ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(d.getSubMatrix(i + 1, i + 1, j + 1, j + 1));
        final int rowIndex = i + 1;
        final int columnIndex = j + 1;
        element.setExpression(linearSystem.getTag() + "_d_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setTag(linearSystem.getTag() + "_d_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
        element.setVariable(true);
        matrix[j][aSize * 2 + 1 + i] = element;
      }
    }

    if (linearSystem.getLinearSystem() instanceof ImproperLinearSystem) {
      // E取得
      RM e = ((AbstractLinearSystem<RS, RM, CS, CM>)linearSystem.getLinearSystem()).getE();
      for (int i = 0; i < e.getRowSize(); i++) {
        for (int j = 0; j < e.getColumnSize(); j++) {
          ConstantSystem<RS, RM, CS, CM> element = new ConstantSystem<>(e.getSubMatrix(i + 1, i + 1, j + 1, j + 1));
          final int rowIndex = i + 1;
          final int columnIndex = j + 1;
          element.setExpression(linearSystem.getTag() + "_e_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
          element.setTag(linearSystem.getTag() + "_e_" + rowIndex + "_" + columnIndex); //$NON-NLS-1$ //$NON-NLS-2$
          element.setVariable(true);
          if (i == j) {
            element = element.unaryMinus().add(new UnitSystem<>(1, this.sunit));
          }
          matrix[inputSize + aSize + j][aSize + 1 + i] = element;
        }
      }
    }

    return matrix;
  }

  /**
   * 線形動的システム(伝達関数)の状態空間表現による隣接行列を生成します。システムを可制御正準型で隣接行列上に展開します.
   * 
   * @param system 線形動的システム
   * 
   * @return 線形動的システム(伝達関数)の状態空間表現による隣接行列
   */
  SystemOperator<RS, RM, CS, CM>[][] createSymbolicLinearDynamicSystemBlockMatrixForTransferFunction(final SystemOperator<RS, RM, CS, CM> system) {
    final LinearSystemOperator<RS, RM, CS, CM> linearSystem = (LinearSystemOperator<RS, RM, CS, CM>)system;

    final int size = linearSystem.getA().getColumnSize();

    final SystemOperator<RS, RM, CS, CM>[][] matrix = new SystemOperator[size * 2 + 2][size * 2 + 2];

    setZeroSystemToNullElement(matrix);

    for (int i = 0; i < size; i++) {
      matrix[size + i + 1][i + 1] = new IntegratorSystem<>(1, this.sunit);
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[size + i + 1][i + 1]).setTag("x_" + linearSystem.getTag() + "_" + (size - i)); //$NON-NLS-1$ //$NON-NLS-2$
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[size + i + 1][i + 1]).setStateNumber(((DynamicSystem<RS, RM, CS, CM>)system).getStateNumber() + size - i);
    }

    // B取得
    matrix[0][size + 1] = new UnitSystem<>(1, this.sunit);

    // A取得
    final RM A = new ConstantSystem<>(linearSystem.getA()).getGain();
    for (int i = 0; i < size; i++) {
      final RM valueMatrix = A.getRowVector(1).getColumnVector(i + 1);
      if (valueMatrix.isZero()) {
        matrix[1 + i][size + 1] = ZeroSystem.getInstance(this.sunit);
        break;
      }
      ConstantSystem<RS, RM, CS, CM> a = new ConstantSystem<>(valueMatrix.unaryMinus()).unaryMinus();
      a.setExpression(linearSystem.getTag() + "_d" + "_" + (size - i - 1)); //$NON-NLS-1$ //$NON-NLS-2$
      a.setTag(linearSystem.getTag() + "_" + (size - i - 1)); //$NON-NLS-1$
      a.setVariable(true);
      matrix[1 + i][size + 1] = a;
    }
    for (int i = 0; i < size - 1; i++) {
      matrix[1 + i][size + 2 + i] = new UnitSystem<>(1, this.sunit);
    }

    // C取得
    final RM C = new ConstantSystem<>(linearSystem.getC()).getGain();
    for (int i = 0; i < size; i++) {
      final RM valueMatrix = C.getRowVector(1).getColumnVector(i + 1);
      if (valueMatrix.isZero()) {
        matrix[1 + i][size * 2 + 1] = ZeroSystem.getInstance(this.sunit);
        continue;
      }
      ConstantSystem<RS, RM, CS, CM> c = new ConstantSystem<>(valueMatrix);
      c.setExpression(linearSystem.getTag() + "_n" + "_" + (size - i - 1)); //$NON-NLS-1$ //$NON-NLS-2$
      c.setTag(linearSystem.getTag() + "_" + (size - i - 1)); //$NON-NLS-1$
      c.setVariable(true);
      matrix[1 + i][size * 2 + 1] = c;
    }

    return matrix;
  }

  /**
   * 線形動的システムの状態空間表現による隣接行列を生成します。
   * 
   * @param system 線形動的システム
   * 
   * @return 線形動的システムの状態空間表現による隣接行列
   */
  SystemOperator<RS, RM, CS, CM>[][] createLinearDynamicSystemBlockMatrix(final SystemOperator<RS, RM, CS, CM> system) {
    final SystemOperator<RS, RM, CS, CM>[][] matrix = new SystemOperator[4][4];

    setZeroSystemToNullElement(matrix);

    final LinearSystemOperator<RS, RM, CS, CM> linearSystem = (LinearSystemOperator<RS,RM,CS,CM>)system;
    final String format = linearSystem.getLinearSystem().getFormat();

    if (system instanceof IntegratorSystem) {
      matrix[1][2] = ZeroSystem.getInstance(this.sunit);
      matrix[0][2] = new UnitSystem<>(linearSystem.getA().getRowSize(), this.sunit);
      matrix[1][3] = new UnitSystem<>(linearSystem.getA().getRowSize(), this.sunit);
      matrix[0][3] = ZeroSystem.getInstance(this.sunit);

      matrix[2][1] = new IntegratorSystem<>(system.getStateSize(), this.sunit);
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setTag(linearSystem.getTag());
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setStateNumber(((DynamicSystem<RS, RM, CS, CM>)system).getStateNumber());

    } else if (system.getLinearSystem().isProper() == false) {

      // A取得
      matrix[1][2] = new ConstantSystem<>(linearSystem.getA());
      if (linearSystem.hasVariableA()) {
        // ((ConstantSystem)matrix[1][2]).setExpression("A_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setExpression(linearSystem.getTag() + "_A"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getA();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setVariable(linearSystem.hasVariableA());

      // B取得
      matrix[0][2] = new ConstantSystem<>(linearSystem.getB());
      if (linearSystem.hasVariableB()) {
        // ((ConstantSystem)matrix[0][2]).setExpression("B_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setExpression(linearSystem.getTag() + "_B"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getB();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setVariable(linearSystem.hasVariableB());

      // C取得
      matrix[1][3] = new ConstantSystem<>(linearSystem.getC());
      if (linearSystem.hasVariableC()) {
        // ((ConstantSystem)matrix[1][3]).setExpression("C_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setExpression(linearSystem.getTag() + "_C"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getC();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setVariable(linearSystem.hasVariableC());

      // D取得
      final RM value = linearSystem.getD();
      if (value.isZero()) {
        matrix[0][3] = ZeroSystem.getInstance(this.sunit);
      } else {
        matrix[0][3] = new ConstantSystem<>(linearSystem.getD());
        if (linearSystem.hasVariableD()) {
          // ((ConstantSystem)matrix[0][3]).setExpression("D_" + linearSystem.getTag());
          // //$NON-NLS-1$
          ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setExpression(linearSystem.getTag() + "_D"); //$NON-NLS-1$
        } else {
          // final DoubleMatrix value = linearSystem.getD();
          ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setExpression(toMmString(value, format));
        }
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setTag(linearSystem.getTag());
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setVariable(linearSystem.hasVariableD());
      }

      // E取得
      final ConstantSystem<RS, RM, CS, CM> e = new ConstantSystem<>(((ImproperLinearSystem<RS, RM, CS, CM>)linearSystem.getLinearSystem()).getE());
      // e.setExpression("E_" + linearSystem.getTag()); //$NON-NLS-1$
      e.setExpression(linearSystem.getTag() + "_E"); //$NON-NLS-1$
      e.setTag(linearSystem.getTag());
      e.setVariable(linearSystem.hasVariableE());
      matrix[2][2] = e.unaryMinus().add(new UnitSystem<>(e.getGain().getRowSize(), this.sunit));

      // matrix[2][1] = new IntegratorSystem(system.getStateSize());
      matrix[2][1] = new IntegratorSystem<>(linearSystem.getA().getColumnSize(), this.sunit);
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setTag("x_" + linearSystem.getTag()); //$NON-NLS-1$
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setStateNumber(((DynamicSystem<RS, RM, CS, CM>)system).getStateNumber());
    } else if (system instanceof UnitDelaySystem) {
      matrix[1][2] = ZeroSystem.getInstance(this.sunit);
      matrix[0][2] = new UnitSystem<>(linearSystem.getA().getRowSize(), this.sunit);
      matrix[1][3] = new UnitSystem<>(linearSystem.getA().getRowSize(), this.sunit);
      matrix[0][3] = ZeroSystem.getInstance(this.sunit);

      matrix[2][1] = new UnitDelaySystem<>(system.getStateSize(), this.sunit);
      ((UnitDelaySystem<RS, RM, CS, CM>)matrix[2][1]).setTag(linearSystem.getTag());
      ((UnitDelaySystem<RS, RM, CS, CM>)matrix[2][1]).setStateNumber(((DynamicSystem<RS, RM, CS, CM>)system).getStateNumber());
    } else {
      // A取得
      matrix[1][2] = new ConstantSystem<>(linearSystem.getA());
      if (linearSystem.hasVariableA()) {
        // ((ConstantSystem)matrix[1][2]).setExpression("A_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setExpression(linearSystem.getTag() + "_A"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getA();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][2]).setVariable(linearSystem.hasVariableA());

      // B取得
      matrix[0][2] = new ConstantSystem<>(linearSystem.getB());
      if (linearSystem.hasVariableB()) {
        // ((ConstantSystem)matrix[0][2]).setExpression("B_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setExpression(linearSystem.getTag() + "_B"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getB();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[0][2]).setVariable(linearSystem.hasVariableB());

      // C取得
      matrix[1][3] = new ConstantSystem<>(linearSystem.getC());
      if (linearSystem.hasVariableC()) {
        // ((ConstantSystem)matrix[1][3]).setExpression("C_" + linearSystem.getTag());
        // //$NON-NLS-1$
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setExpression(linearSystem.getTag() + "_C"); //$NON-NLS-1$
      } else {
        final RM value = linearSystem.getC();
        ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setExpression(toMmString(value, format));
      }
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setTag(linearSystem.getTag());
      ((ConstantSystem<RS, RM, CS, CM>)matrix[1][3]).setVariable(linearSystem.hasVariableC());

      // D取得
      final RM value = linearSystem.getD();
      if (value.isZero()) {
        matrix[0][3] = ZeroSystem.getInstance(this.sunit);
      } else {
        matrix[0][3] = new ConstantSystem<>(linearSystem.getD());
        if (linearSystem.hasVariableD()) {
          // ((ConstantSystem)matrix[0][3]).setExpression("D_" + linearSystem.getTag());
          // //$NON-NLS-1$
          ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setExpression(linearSystem.getTag() + "_D"); //$NON-NLS-1$
        } else {
          // final DoubleMatrix value = linearSystem.getD();
          if (value.isZero()) {
            matrix[0][3] = ZeroSystem.getInstance(this.sunit);
          }
          ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setExpression(toMmString(value, format));
        }
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setTag(linearSystem.getTag());
        ((ConstantSystem<RS, RM, CS, CM>)matrix[0][3]).setVariable(linearSystem.hasVariableD());
      }
      // E取得
      RM E = linearSystem.getE();
      if (!E.equals(E.createUnit())) {
        final ConstantSystem<RS, RM, CS, CM> e = new ConstantSystem<>(E);
        // e.setExpression("E_" + linearSystem.getTag()); //$NON-NLS-1$
        e.setExpression(linearSystem.getTag() + "_E"); //$NON-NLS-1$
        e.setTag(linearSystem.getTag());
        e.setVariable(linearSystem.hasVariableE());
        matrix[2][2] = e.unaryMinus().add(new UnitSystem<>(e.getGain().getRowSize(), this.sunit));
      }
      matrix[2][1] = new IntegratorSystem<>(system.getStateSize(), this.sunit);
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setTag("x_" + linearSystem.getTag()); //$NON-NLS-1$
      ((IntegratorSystem<RS, RM, CS, CM>)matrix[2][1]).setStateNumber(((DynamicSystem<RS, RM, CS, CM>)system).getStateNumber());

    }

    // IngegralSystem
    return matrix;
  }

  /**
   * 行列の値をMMフォーマットで返します。
   * 
   * @param value 行列の値
   * @param format フォーマット
   * @return 行列の値をMMフォーマット
   */
  private String toMmString(final RM value, final String format) {
    final String expression;
    if (value.getRowSize() == 1 && value.getColumnSize() == 1) {
      expression = value.getElement(1, 1).toString(format);
    } else {
      expression = (value).toString(format).replaceAll("\\s*", ""); //$NON-NLS-1$ //$NON-NLS-2$
    }
    return expression;
  }

  /**
   * システムオペレータの配列のクローンを生成します。
   * 
   * <p> 成分の参照がコピーされます。
   * 
   * @param matrix 元の配列
   * @return 生成された配列
   */
  SystemOperator<RS, RM, CS, CM>[][] createClone(final SystemOperator<RS, RM, CS, CM>[][] matrix) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;
    final SystemOperator<RS, RM, CS, CM>[][] newMatrix = new SystemOperator[rowSize][columnSize];
    for (int row = 0; row < rowSize; row++) {
      for (int column = 0; column < columnSize; column++) {
        newMatrix[row][column] = matrix[row][column];
      }
    }

    return newMatrix;
  }

  /**
   * 指定された行と列に零(行・列)ベクトルを挿入した行列を生成します。
   * 
   * @param matrix 隣接行列
   * @param rowMin 挿入開始行番号
   * @param rowMax 挿入終了行番号
   * @param columnMin 挿入開始列番号
   * @param columnMax 挿入終了列番号
   * @return 零(行・列)ベクトルを挿入した行列
   */
  public SystemOperator<RS, RM, CS, CM>[][] insertRowAndColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int rowMin, final int rowMax, final int columnMin, final int columnMax) {
    final SystemOperator<RS, RM, CS, CM>[][] insertedRow = insertRow(matrix, rowMin, rowMax);
    final SystemOperator<RS, RM, CS, CM>[][] insertedColumn = insertColumn(insertedRow, columnMin, columnMax);
    return insertedColumn;
  }

  /**
   * 指定された行と列に零(行・列)ベクトルを挿入した行列を生成します。
   * 
   * @param matrix 隣接行列
   * @param min 挿入開始行番号
   * @param max 挿入終了行番号
   * @return 零(行・列)ベクトルを挿入した行列
   */
  public SystemOperator<RS, RM, CS, CM>[][] insertRowAndColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int min, final int max) {
    final int size = matrix.length;
    final int delta = max - min + 1;
    if (delta == 0) {
      return matrix;
    }

    final int newSize = size + delta;
    SystemOperator<RS, RM, CS, CM>[][] newMatrix = new SystemOperator[newSize][newSize];
    setZeroSystemToNullElement(newMatrix);

    // left upper
    GridUtil.setSubMatrix(newMatrix, 0, min - 1, 0, min - 1, getSubMatrix(matrix, 0, min - 1, 0, min - 1));
    // right upper
    GridUtil.setSubMatrix(newMatrix, 0, min - 1, max + 1, newSize - 1, getSubMatrix(matrix, 0, min - 1, min, size - 1));
    // left lower
    GridUtil.setSubMatrix(newMatrix, max + 1, newSize - 1, 0, min - 1, getSubMatrix(matrix, min, size - 1, 0, min - 1));
    // right lower
    GridUtil.setSubMatrix(newMatrix, max + 1, newSize - 1, max + 1, newSize - 1, getSubMatrix(matrix, min, size - 1, min, size - 1));

    return newMatrix;
  }

  /**
   * ゼロシステムをnull成分に設定します。
   * 
   * @param matrix ゼロシステムを設定する配列
   */
  @SuppressWarnings("null")
  public void setZeroSystemToNullElement(final SystemOperator<RS, RM, CS, CM>[][] matrix) {
    final int rowSize = matrix == null ? 0 : matrix.length;
    final int columnSize = rowSize != 0 ? matrix[0].length : 0;

    for (int row = 0; row < rowSize; row++) {
      for (int column = 0; column < columnSize; column++) {
        if (matrix[row][column] == null) {
          matrix[row][column] = ZeroSystem.getInstance(this.sunit);
        }
      }
    }
  }

  /**
   * 指定された列に零ベクトルを挿入した行列を生成します。
   * 
   * @param matrix 隣接行列
   * @param min 挿入開始行番号
   * @param max 挿入終了行番号
   * @return 零ベクトルを挿入した行列
   */
  public SystemOperator<RS, RM, CS, CM>[][] insertColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int min, final int max) {
    final int rowSize = matrix.length;
    final int columnSize = matrix[0].length;
    final int delta = max - min + 1;
    if (delta == 0) {
      return matrix;
    }

    final int newSize = columnSize + delta;
    SystemOperator<RS, RM, CS, CM>[][] newMatrix = new SystemOperator[rowSize][newSize];
    setZeroSystemToNullElement(newMatrix);

    // left
    GridUtil.setSubMatrix(newMatrix, 0, rowSize - 1, 0, min - 1, getSubMatrix(matrix, 0, rowSize - 1, 0, min - 1));
    // right
    GridUtil.setSubMatrix(newMatrix, 0, rowSize - 1, max + 1, newSize - 1, getSubMatrix(matrix, 0, rowSize - 1, min, columnSize - 1));

    return newMatrix;
  }

  /**
   * 指定された行に零ベクトルを挿入した行列を生成します。
   * 
   * @param matrix 隣接行列
   * @param min 挿入開始行番号
   * @param max 挿入終了行番号
   * @return 零ベクトルを挿入した行列
   */
  public SystemOperator<RS, RM, CS, CM>[][] insertRow(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int min, final int max) {
    final int rowSize = matrix.length;
    final int columnSize = matrix[0].length;
    final int delta = max - min + 1;
    if (delta == 0) {
      return matrix;
    }

    final int newSize = rowSize + delta;
    SystemOperator<RS, RM, CS, CM>[][] newMatrix = new SystemOperator[newSize][columnSize];
    setZeroSystemToNullElement(newMatrix);

    // upper
    GridUtil.setSubMatrix(newMatrix, 0, min - 1, 0, columnSize - 1, getSubMatrix(matrix, 0, min - 1, 0, columnSize - 1));
    // lower
    if (max + 1 >= rowSize) return newMatrix;
    GridUtil.setSubMatrix(newMatrix, max + 1, newSize - 1, 0, columnSize - 1, getSubMatrix(matrix, min, rowSize - 1, 0, columnSize - 1));

    return newMatrix;
  }

  /**
   * 部分グリッドを生成します。
   * 
   * @param grid 対象となるグリッド
   * @param rowMin 始端行の数
   * @param rowMax 終端行の数
   * @param columnMin 始端列の数
   * @param columnMax 終端列の数
   * @return 部分行列
   */
  private final SystemOperator<RS, RM, CS, CM>[][] getSubMatrix(SystemOperator<RS, RM, CS, CM>[][] grid, int rowMin, int rowMax, int columnMin, int columnMax) {
    int subRowSize = rowMax - rowMin + 1;
    int subColSize = columnMax - columnMin + 1;
    SystemOperator<RS, RM, CS, CM>[][] ans = GridUtil.<SystemOperator<RS, RM, CS, CM>> createArray(subRowSize, subColSize, grid);

    for (int i = 0; i < subRowSize; i++) {
      SystemOperator<RS, RM, CS, CM>[] ansi = ans[i];
      SystemOperator<RS, RM, CS, CM>[] gridi = grid[i + rowMin];
      for (int j = 0; j < subColSize; j++) {
        // ansi[j] = (SystemOperator)gridi[j + columnMin].clone();
        ansi[j] = gridi[j + columnMin];
      }
    }
    return ans;
  }

  /**
   * 行の入れ替えと列の入れ替えを行います。
   * 
   * @param matrix 操作対象の行列
   * @param number1 対象(行|列)番号1
   * @param number2 対象(行|列)番号2
   */
  void exchangeRowAndColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int number1, final int number2) {
    if (number1 == number2) {
      return;
    }

    final int size = matrix.length;

    // 行を入れ替える
    for (int column = 0; column < size; column++) {
      final SystemOperator<RS, RM, CS, CM> tmp = matrix[number1][column];
      matrix[number1][column] = matrix[number2][column];
      matrix[number2][column] = tmp;
    }

    // 列を入れ替える
    for (int row = 0; row < size; row++) {
      final SystemOperator<RS, RM, CS, CM> tmp = matrix[row][number1];
      matrix[row][number1] = matrix[row][number2];
      matrix[row][number2] = tmp;
    }
  }

  /**
   * 積分器が配置されるべき列のうち、積分器が存在しない列番号を返します。
   * 
   * @param matrix 隣接行列
   * @param numberOfIntegrator 含まれる積分器の数
   * @param inputNodeSize 入力ノードの数
   * @return 積分器が配置されるべき列のうち、積分器が存在しない列番号
   */
  int findColumnWithoutIntegrator(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int numberOfIntegrator, final int inputNodeSize) {
    for (int i = 0; i < numberOfIntegrator; i++) {
      final int column = inputNodeSize + i;
      if (hasIntegratorOrUnitDelayInColumn(matrix, column) == false) {
        return column;
      }
    }

    return -1;
  }

  /**
   * 列成分に積分器(または1サンプル遅れ器)が存在するか判定します。
   * 
   * @param matrix 隣接行列
   * @param column 調べる列
   * @return 列成分に積分器(または1サンプル遅れ器)が存在するならばtrue、そうでなければfalse
   */
  boolean hasIntegratorOrUnitDelayInColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int column) {
    final int rowSize = matrix.length;

    for (int row = 0; row < rowSize; row++) {
      SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system instanceof IntegratorSystem) {
        return true;
      }
      if (system instanceof UnitDelaySystem) {
        return true;
      }
    }

    return false;
  }

  /**
   * 積分器(または1サンプル遅れ器)が存在する列番号を返します。
   * 
   * @param matrix 隣接行列
   * @param row 調べる行
   * @return 積分器(または1サンプル遅れ器)が存在する列番号、積分器(または1サンプル遅れ器)が存在しない場合、-1
   */
  int findIntegratorOrUnitDelayInRow(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    for (int column = 0; column < columnSize; column++) {
      SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system instanceof IntegratorSystem) {
        return column;
      }
      if (system instanceof UnitDelaySystem) {
        return column;
      }
    }

    return -1;
  }

  /**
   * 積分器が配置されるべき行のうち、積分器が存在しない行番号を返します。
   * 
   * @param matrix 隣接行列
   * @param numberOfIntegrator 含まれる積分器の数
   * @param outputNodeSize 出力ノードの数
   * @return 積分器が配置されるべき行のうち、積分器が存在しない行番号
   */
  int findRowWithoutIntegrator(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int numberOfIntegrator, final int outputNodeSize) {
    final int rowSize = matrix.length;

    for (int i = 0; i < numberOfIntegrator; i++) {
      final int row = rowSize - outputNodeSize - numberOfIntegrator + i;
      if (hasIntegratorOrUnitDelayInRow(matrix, row) == false) {
        return row;
      }
    }

    return -1;
  }

  /**
   * 行成分に積分器(または1サンプル遅れ器)が存在するか判定します。
   * 
   * @param matrix 隣接行列
   * @param row 調べる行
   * @return 行成分に積分器(または1サンプル遅れ器)が存在するならばtrue、そうでなければfalse
   */
  boolean hasIntegratorOrUnitDelayInRow(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    for (int column = 0; column < columnSize; column++) {
      SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system instanceof IntegratorSystem) {
        return true;
      }
      if (system instanceof UnitDelaySystem) {
        return true;
      }
    }

    return false;
  }

  /**
   * ライターに出力します。
   * 
   * @param matrix 対象となる行列
   * @param output ライター
   * @param maxColumnSize 最大列の数
   */
  void print(final SystemOperator<RS, RM, CS, CM>[][] matrix, final Writer output, final int maxColumnSize) {
    final PrintWriter pw = new PrintWriter(output);
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    int remain = columnSize;
    int columnOffset = 0;

    while (remain > 0) {
      int printColumnSize = Math.min(maxColumnSize, remain);

      printColumnNumber(columnOffset, printColumnSize, pw);

      for (int i = 0; i < rowSize; i++) {
        printRowNumber(i, pw);

        SystemOperator<RS, RM, CS, CM>[] elements = new SystemOperator[printColumnSize];
        for (int j = 0; j < printColumnSize; j++) {
          elements[j] = matrix[i][columnOffset + j];
        }

        printElements(elements, pw);
      }

      columnOffset += printColumnSize;
      remain -= printColumnSize;
    }

    pw.flush();
  }

  /**
   * 複数個の成分をプリントライターに出力します。
   * 
   * @param elements 成分の配列
   * @param output 出力先のプリントライター
   */
  private void printElements(final SystemOperator<RS, RM, CS, CM>[] elements, final PrintWriter output) {
    final int size = elements.length;
    for (int j = 0; j < size; j++) {
      output.print(" "); //$NON-NLS-1$
      output.print(elements[j]);
    }
    output.println();
  }

  /**
   * 行番号を出力します。
   * 
   * @param row 行番号
   * @param output 出力先のプリントライター
   */
  private void printRowNumber(final int row, final PrintWriter output) {
    output.print(String.format(" (%3d)", Integer.valueOf(row + 1))); //$NON-NLS-1$
  }

  /**
   * 列番号を出力します。
   * 
   * @param columnOffset 列のオフセット
   * @param printColumnSize 出力する列の数
   * @param output 出力先のプリントライター
   */
  private void printColumnNumber(final int columnOffset, final int printColumnSize, final PrintWriter output) {
    output.print(" "); //$NON-NLS-1$
    output.print("     "); //$NON-NLS-1$
    for (int i = 0; i < printColumnSize; i++) {
      output.print(String.format("  [ (%3d]", Integer.valueOf(columnOffset + i + 1))); //$NON-NLS-1$
    }
    output.println();
  }

  /**
   * 全ての行成分がゼロ、線形システム、定数システムであるか判定します。
   * 
   * @param matrix 隣接行列
   * @param row 調べる行
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 全ての行成分がゼロ、線形システム、定数システムならばtrue、そうでなければfalse
   */
  boolean isAllLinearOrConstnatOrZeroInRow(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row, final boolean onlyConstant) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    for (int column = 0; column < columnSize; column++) {
      SystemOperator<RS, RM, CS, CM> system = matrix[row][column];
      if (system == ZeroSystem.getInstance(this.sunit) || system instanceof LinearSystemOperator) {
        continue;
      }
      if (system instanceof ConstantSystem) {
        if (onlyConstant == false || ((ConstantSystem<RS,RM,CS,CM>)system).isVariable() == false) {
          continue;
        }
      }
      return false;
    }

    return true;
  }

  /**
   * 2行の成分のどちからかゼロシステムであるか、または両方が定数システムであるか判定します。
   * 
   * @param matrix 隣接行列
   * @param row1 対象行1
   * @param row2 対象行2
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return 2行の成分のどちからかゼロシステムであるか、または両方が定数システムならばtrue、そうでなければfalse
   */
  boolean isEitherRowZeroOrBothConstant(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row1, final int row2, final boolean onlyConstant) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    for (int k = 0; k < columnSize; k++) {
      if (matrix[row1][k] == ZeroSystem.getInstance(this.sunit) || matrix[row2][k] == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }

      if (matrix[row1][k] instanceof ConstantSystem) {
        if (onlyConstant == false || ((ConstantSystem<RS,RM,CS,CM>)matrix[row1][k]).isVariable() == false) {
          continue;
        }
      }
      if (matrix[row2][k] instanceof ConstantSystem) {
        if (onlyConstant == false || ((ConstantSystem<RS,RM,CS,CM>)matrix[row2][k]).isVariable() == false) {
          continue;
        }
      }

      return false;
    }

    return true;
  }

  /**
   * (<code>row</code>,<code>column</code>)成分を除く、<code>column</code>列の全ての成分が零であるか判定します。
   * 
   * @param matrix 調べる配列
   * @param row 調べる行番号
   * @param column 調べる列番号
   * @return (<code>row</code>,<code>column</code>)成分を除く、<code>column</code>列の全ての成分が零ならばtrue、そうでなければfalse
   */
  boolean isAllZeroInColumn(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row, final int column) {
    final int rowSize = matrix.length;

    for (int k = 0; k < row; k++) {
      if (matrix[k][column] != ZeroSystem.getInstance(this.sunit)) {
        return false;
      }
    }

    for (int k = row + 1; k < rowSize; k++) {
      if (matrix[k][column] != ZeroSystem.getInstance(this.sunit)) {
        return false;
      }
    }

    return true;
  }

  /**
   * <code>row</code>行の全ての成分が零又は定数システムであるか判定します。
   * 
   * @param matrix 調べる配列
   * @param row 調べる行番号
   * @param onlyConstant 定数のみを縮約するならばtrue
   * @return row行の全ての成分が零又は定数システムならばtrue、そうでなければfalse
   */
  boolean isAllConstantOrZeroInRow(final SystemOperator<RS, RM, CS, CM>[][] matrix, final int row, final boolean onlyConstant) {
    final int rowSize = matrix.length;
    final int columnSize = rowSize == 0 ? 0 : matrix[0].length;

    for (int column = 0; column < columnSize; column++) {
      if (matrix[row][column] == ZeroSystem.getInstance(this.sunit)) {
        continue;
      }
      if (matrix[row][column] instanceof ConstantSystem) {
        if (onlyConstant == false || ((ConstantSystem<RS,RM,CS,CM>)matrix[row][column]).isVariable() == false) {
          continue;
        }
      }
      return false;
    }
    return true;
  }

}