package jp.ac.takushoku_u.cs;

/**
* Matrixクラスは2次元配列の実際の要素、行名と列名を保持します。<BR>
* @version 1.1
* @author 笠島 裕史(Kasajima Hiroshi)
*/
/*
* 最終更新日 2004年11月24日
*/
public class Matrix {

/**
* 行のサイズ保持変数
*/
    private int matrixRowSize;
	
/**
* 列のサイズ保持変数
*/
	private int matrixColSize;

/**
* 行名格納用配列
*/
	private String[ ] matrixRowName;

/**
* 列名格納用配列
*/
	private String[ ] matrixColName;
	
/**
* データ保存用2次元配列
*/
	private Object[ ][ ] matrix;
	
/**
* 2次元配列のデフォルト値（行サイズ）
*/
	private static final int MATRIX_INIT_ROW_SIZE = 5;
	
/**
* 2次元配列のデフォルト値（列サイズ）
*/
	private static final int MATRIX_INIT_COL_SIZE = 5;


/**
* 設定されているサイズの行名、列名を入れる配列を生成します。<BR>
* 行名と列名は行番号と列番号で設定されます。
*/
	public Matrix(){
		setSizeMatrix(MATRIX_INIT_ROW_SIZE,MATRIX_INIT_COL_SIZE);
		setRowNameNumber();
		setColNameNumber();
	}

/**
* 指定された行サイズと列サイズを元に行名、列名を入れる配列を生成します。<BR>
* 行名と列名は行番号と列番号で設定されます。
* @param rowSize 指定する行サイズ
* @param colSize 指定する列サイズ
* @exception NegativeMatrixSizeException 負のサイズのマトリクスを生成しようとした場合
*/
	public Matrix(int rowSize, int colSize){
		setSizeMatrix(rowSize,colSize);
		setRowNameNumber();
		setColNameNumber();
	}
	
/**
* 指定された行サイズと列サイズを元に行名、列名を入れる配列を生成し、
* 指定された番号から順に行名と列名を初期化します。<BR>
* 行名と列名は nameNumber で指定された番号から順に設定されます。
* @param rowSize 指定する行サイズ
* @param colSize 指定する列サイズ
* @param nameNumber 行名および列名の開始番号
* @exception NegativeMatrixSizeException 負のサイズのマトリクスを生成しようとした場合
*/
	public Matrix(int rowSize, int colSize, int nameNumber){
		setSizeMatrix(rowSize,colSize);
		setRowNameNumber(nameNumber);
		setColNameNumber(nameNumber);
	}

/**
* 指定された行サイズ、列サイズ、行名、列名を元にデータ保存用の2次元配列と行名、列名を入れる配列を生成します。<BR>
* 生成された2次元配列は全て0で初期化されます。
* @param rowSize 指定する行サイズ
* @param colSize 指定する列サイズ
* @param rowName 指定する行名
* @param colName 指定する列名
* @exception NegativeMatrixSizeException 負のサイズのマトリクスが生成しようとした場合
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException  rowName または colName が null の場合
*/
	public Matrix(int rowSize, int colSize, String[] rowName, String[] colName){
		setSizeMatrix(rowSize,colSize);
		
		//行名および列名のコピー
		nameRowCopy(rowName);
		nameColCopy(colName);
	}
	
/**
* 指定された2次元配列を元にデータ保存用の2次元配列と行名、列名を入れる配列を生成します。<BR>
* 行名と列名は行番号と列番号で設定されます。
* @param originalMatrix 指定する2次元配列
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException  originalMatrix が null の場合
*/
	public Matrix(Object[][] originalMatrix){
		setSizeMatrix(originalMatrix.length, originalMatrix[0].length);
		setRowNameNumber();
		setColNameNumber();
		
		//マトリクスのコピー
		for (int i = 0; i < getRowLength(); i++){
			for (int j = 0; j < getColLength(); j++){
				setCell(i, j, originalMatrix[i][j]);
			}
		}
	}

/**
* 指定された2次元配列を元にデータ保存用の2次元配列と行名、列名を入れる配列を生成し、
* 指定された番号から順に行名と列名を初期化します。<BR>
* 行名と列名は nameNumber で指定された番号から順に設定されます。
* @param originalMatrix 指定する2次元配列
* @param nameNumber 行名および列名の開始番号
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException  originalMatrix が null の場合
*/
	public Matrix(Object[][] originalMatrix, int nameNumber){
		setSizeMatrix(originalMatrix.length, originalMatrix[0].length);
		setRowNameNumber(nameNumber);
		setColNameNumber(nameNumber);
		
		//マトリクスのコピー
		for (int i = 0; i < getRowLength(); i++){
			for (int j = 0; j < getColLength(); j++){
				setCell(i, j, originalMatrix[i][j]);
			}
		}
	}

/**
* 指定された2次元配列、行名、列名を元にデータ保存用の2次元配列と行名、列名を入れる配列を生成します。
* @param originalMatrix 指定する2次元配列
* @param rowName 指定する行名
* @param colName 指定する列名
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException  rowName , colName または originalMatrix が null の場合
*/
	public Matrix(Object[][] originalMatrix, String[] rowName, String[] colName){
		setSizeMatrix(originalMatrix.length, originalMatrix[0].length);
		
		//マトリクスのコピー
		for (int i = 0; i < getRowLength(); i++){
			for (int j = 0; j < getColLength(); j++){
				setCell(i, j, originalMatrix[i][j]);
			}
		}
		
		//行名および列名のコピー
		nameRowCopy(rowName);
		nameColCopy(colName);
	}

/**
* 指定した配列を行名としてマトリクスにコピーします。<BR>
* コピー元のマトリクスサイズと一致しない場合はコピー元のマトリクスサイズに変更し、コピーを行います。
* @param rowName コピー元の行名配列
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException rowName が null の場合
*/
	public void nameRowCopy(String rowName[]){
		
		//サイズを確認し、一致しない場合はサイズを変更します
		if (getRowLength() != rowName.length){
				setSizeRow(rowName.length);
		}
		
		//コピーをします
		System.arraycopy(rowName, 0, matrixRowName, 0, rowName.length);
	}
	
/**
* 指定した配列を列名としてマトリクスにコピーします。<BR>
* コピー元のマトリクスサイズと一致しない場合はコピー元のマトリクスサイズに変更し、コピーを行います。
* @param colName コピー元の列名配列
* @exception IndexOutOfBoundsException 配列のコピーによって配列の境界を越えたデータのアクセスが生じた場合
* @exception NullPointerException colName が null の場合
*/
	public void nameColCopy(String colName[]){
		
		//サイズを確認し、一致しない場合はサイズを変更します
		if (getColLength() != colName.length){
			setSizeCol(colName.length);
		}
		
		//コピーをします
		System.arraycopy(colName, 0, matrixColName, 0, colName.length);
	}
	
/**
* マトリクスに他のマトリクスの要素、行名、列名の全てをコピーします。<BR>
* コピー元のマトリクスサイズと一致しない場合はコピー元のマトリクスサイズに変更し、コピーを行います。
* @param originalMatrix コピー元のマトリクス
* @see #matrixCopy(Matrix originalMatrix, int baseRow, int baseCol, int copyRow,int copyCol, int rowLength, int colLength)
*/
	public void matrixCopy(Matrix originalMatrix){
		
		//サイズを確認し、一致しない場合はサイズを変更します
		if ((getRowLength() != originalMatrix.getRowLength()) ||
			(getColLength() != originalMatrix.getColLength())){
				setSizeMatrix(originalMatrix.getRowLength(), originalMatrix.getColLength());
		}
		
		//コピーをします
			for (int i = 0; i < getRowLength(); i++){
				setRowName(i, originalMatrix.getRowName(i));
				for (int j = 0; j < getColLength(); j++){
					if (i == 0){
						setColName(j, originalMatrix.getColName(j));
					}
					setCell(i, j, originalMatrix.getObjectCell(i, j));
				}
			}
	}
	
/**
* 指定位置で開始する指定要素からコピー先のマトリクスの指定位置に要素をコピーします。<BR>
* コピー元の要素( baseRow , baseCol )で指定された要素を開始位置とし、rowLength 行 colLength 列分だけ
* 要素( copyRow , copyCol )から指定される要素から始まる要素にコピーを行います。
* コピー先の範囲に当てはまらない要素と行名、列名は変更されません。
* @param originalMatrix コピー元のマトリクス
* @param baseRow コピー元のマトリクスに対するコピーの行開始位置
* @param baseCol コピー元のマトリクスに対するコピーの列開始位置
* @param copyRow コピーされる行開始位置
* @param copyCol コピーされる列開始位置
* @param rowLength コピーを行う行の要素数
* @param colLength コピーを行う列の要素数
* @exception SetValueOutOfBoundsException コピー先が存在しない範囲にあたる場合
* @see #matrixCopy(Matrix originalMatrix)
*/
	public void matrixCopy(Matrix originalMatrix, int baseRow, int baseCol, int copyRow,
		int copyCol, int rowLength, int colLength){
		
		//コピー先のサイズを確認します
		if ((getRowLength() - copyRow >= rowLength) &&
			(getColLength() - copyCol >= colLength) &&
			(originalMatrix.getRowLength() >= (rowLength + baseRow)) &&
			(originalMatrix.getColLength() >= (colLength + baseCol))){
			for (int i = 0; i < rowLength; i++){
				for (int j = 0; j < colLength; j++){
					setCell(i + copyRow, j + copyCol, 
						originalMatrix.getObjectCell(i + baseRow, j + baseCol));
				}
			}
		}
		else{
			throw new SetValueOutOfBoundsException();
		}
	}
	
/**
* 指定した範囲にある要素を移動します。
* 移動元の要素( baseRow , baseCol )で指定された要素を開始位置とし、rowLength 行 colLength 列分だけ
* 要素( copyRow , copyCol )から指定される要素から始まる要素に移動を行います。
* 行名、列名は変更されません。
* また移動先の要素の値は上書きされ、移動元の要素の値は変更されません。
* @param baseRow 移動元の要素に対する移動行開始位置
* @param baseCol 移動元の要素に対する移動列開始位置
* @param copyRow 移動される要素の行開始位置
* @param copyCol 移動される要素の列開始位置
* @param rowLength 移動を行う行の要素数
* @param colLength 移動を行う列の要素数
* @exception SetValueOutOfBoundsException 移動先が存在しない範囲にあたる場合
*/
	public void matrixMove(int baseRow, int baseCol, int copyRow,
		int copyCol, int rowLength, int colLength){
			
		//コピー先のサイズを確認します
		if ((getRowLength() - copyRow >= rowLength) &&
			(getColLength() - copyCol >= colLength) &&
			(getRowLength() >= (rowLength + baseRow)) &&
			(getColLength() >= (colLength + baseCol))){
				
				//コピー前のオブジェクトの保持
				Object[][] o = new Object[getRowLength()][getColLength()];
				for(int i = 0; i < getRowLength(); i++){
					for(int j = 0; j < getColLength(); j++){
						o[i][j] = getObjectCell(i, j);
					}
				}
				
			for (int i = 0; i < rowLength; i++){
				for (int j = 0; j < colLength; j++){
					setCell(i + copyRow, j + copyCol, o[i + baseRow][j + baseCol]);
				}
			}
		}
		else{
			throw new SetValueOutOfBoundsException();
		}
	}
	
/**
* 指定した範囲にある行名を移動します。
* 移動元の要素 baseRow で指定された要素を開始位置とし、rowLength 行だけ
* 要素 copyRow から指定される要素から始まる要素に移動を行います。
* また移動先の要素の名前は上書きされ、移動元の要素の名前は変更されません。
* @param baseRow 移動元の要素に対する移動行開始位置
* @param copyRow 移動される要素の行開始位置
* @param rowLength 移動を行う行の要素数
* @exception SetValueOutOfBoundsException 移動先が存在しない範囲にあたる場合
*/
	public void rowNameMove(int baseRow, int copyRow, int rowLength){
			
		//コピー先のサイズを確認します
		if ((getRowLength() - copyRow >= rowLength) &&
			(getRowLength() >= (rowLength + baseRow))){
				
			//コピー前のオブジェクトの保持
			String[] s = getRowNameArray();
			for (int i = 0; i < rowLength; i++){
				setRowName(i + copyRow, s[i + baseRow]);
			}
		}
		else{
			throw new SetValueOutOfBoundsException();
		}
	}
	
/**
* 指定した範囲にある列名を移動します。
* 移動元の要素 baseCol で指定された要素を開始位置とし、 colLength 列分だけ
* 要素 copyCol から指定される要素から始まる要素に移動を行います。
* また移動先の要素の列名は上書きされ、移動元の要素の列名は変更されません。
* @param baseCol 移動元の要素に対する移動列開始位置
* @param copyCol 移動される要素の列開始位置
* @param colLength 移動を行う列の要素数
* @exception SetValueOutOfBoundsException 移動先が存在しない範囲にあたる場合
*/
	public void colNameMove(int baseCol, int copyCol, int colLength){
			
		//コピー先のサイズを確認します
		if ((getColLength() - copyCol >= colLength) &&
			(getColLength() >= (colLength + baseCol))){
				
			//コピー前のオブジェクトの保持
			String[] s = getColNameArray();
			for (int j = 0; j < colLength; j++){
				setColName(j + copyCol, s[j + baseCol]);
			}
		}
		else{
			throw new SetValueOutOfBoundsException();
		}
	}
	
/**
* 保持された要素を変更せずにマトリクスの大きさを変更します。
* マトリクスを小さくする場合は指定した要素より大きい部分がなくなります。
* 大きくする場合大きくされた部分は何もされません。
* @param rowLength 行サイズ
* @param colLength 列サイズ
* @exception NegativeMatrixSizeException 負のサイズのマトリクスが生成しようとした場合
*/
	public void reSizeMatrix(int rowLength, int colLength){
		
		
		int r = getRowLength();
		int c = getColLength();
		Object[][] o = getObjectMatrix();
		String[] sr = getRowNameArray();
		String[] sc = getColNameArray();
		setSizeMatrix(rowLength, colLength);
		
		int row;
		int col;
		if(r>=rowLength){
			row = rowLength;
		}
		else{
			row = r;
		}
		if(c>=colLength){
			col = colLength;
		}
		else{
			col = c;
		}
		
		//マトリクスの要素を戻す
		for (int i = 0; i < row; i++){
			setRowName(i, sr[i]);
			for (int j = 0; j < col; j++){
				if(i == 0){
					setColName(j, sc[j]);
				}
				setCell(i, j, o[i][j]);
			}
		}
		
	}
	
/**
* 指定行の行名を付けます。
* @param row 指定行番号
* @param rowName 行名
* @exception NotElementException 指定された行が存在しない場合
* @see #setColName(int col,String colName)
*/
	public void setRowName(int row,String rowName){
		if (isRowNumber(row)){
			matrixRowName[row] = rowName;
		}
		else{
			throw new NotElementException("row("+ row + ")");
		}
	}
	
/**
* 指定列の列名を付けます。
* @param col 指定列番号
* @param colName 行名
* @exception NotElementException 指定された列が存在しない場合
* @see #setRowName(int row,String rowName)
*/
	public void setColName(int col,String colName){
		if (isColNumber(col)){
			matrixColName[col] = colName;
		}
		else{
			throw new NotElementException("col("+ col+")");
		}
	}
	
/**
* マトリクスの行サイズを返します。
* @return 行サイズ
* @see #getColLength()
*/
	public int getRowLength(){
		return matrixRowSize;
	}
	
/**
* マトリクスの列サイズを返します。
* @return 列サイズ
* @see #getRowLength()
*/
	public int getColLength(){
		return matrixColSize;
	}

	
/**
* 指定した行の行名を返します。
* @param  row 指定行
* @return 指定行の行名
* @exception NotElementException 指定された行が存在しない場合
* @see #getColName(int col)
* @see #getRowNameArray()
*/
	public String getRowName(int row){
		if (isRowNumber(row)){
			return matrixRowName[row];
		}
		else{
			throw new NotElementException("row("+ row +")");
		}
	}
	
/**
* マトリクスにある全ての行名を配列で返します。
* @return 行名の入った配列
* @see #getColNameArray()
* @see #getRowName(int row)
*/
	public String[] getRowNameArray(){
		String[] returnArray = new String[getRowLength()];
		for (int i = 0; i < getRowLength(); i++){
			returnArray[i] = getRowName(i);
		}
		return returnArray;
	}
	
/**
* 指定した列の行名を返します。
* @param  col 指定列
* @return 指定行の列名
* @exception NotElementException 指定された列が存在しない場合
* @see #getRowName(int row)
* @see #getColNameArray()
*/
	public String getColName(int col){
		if (isColNumber(col)){
			return matrixColName[col];
		}
		else{
			throw new NotElementException("col("+ col +")");
		}
	}
	
/**
* マトリクスにある全ての列名を配列で返します。
* @return 列名の入った配列
* @see #getRowNameArray()
* @see #getColName(int col)
*/
	public String[] getColNameArray(){
		String[] returnArray = new String[getColLength()];
		for (int i = 0; i < getColLength(); i++){
			returnArray[i] = getColName(i);
		}
		return returnArray;
	}
	
/**
* 指定した行名の行番号を返します。
* @param rowName 指定行名
* @return 指定行の行番号
* @exception NotElementException 指定された行名が存在しない場合
* @see #getColNumber(String colName)
*/
	public int getRowNumber(String rowName){
		for (int i = 0; i < getRowLength(); i++){
			if (rowName.equals(matrixRowName[i])){
				return i;
			}
		}
		
		//存在しない場合
		throw new NotElementException("rowName(" + rowName + ")");
	}
	
/**
* 指定した列名の列番号を返します。
* @param colName 指定列名
* @return 指定行の列番号
* @exception NotElementException 指定した列名が存在しない場合
* @see #getRowNumber(String rowName)
*/
	public int getColNumber(String colName){
		for (int i = 0; i < getColLength(); i++){
			if (colName.equals(matrixColName[i])){
				return i;
			}
		}
		
		//存在しない場合
		throw new NotElementException("colName(" + colName + ")");
	}
	
/**
* マトリクスに指定した要素が存在するかチェックします。
* @param row 指定行番号
* @param col 指定列番号
* @return このマトリクスに指定した要素が存在すればtrueを、存在しなければfalseを返します。
*/
	public boolean isMatrixEntityPoint(int row, int col){
		return ((0 <= row && row < this.getRowLength()) &&
			(0 <= col && col < this.getColLength()));
	}

/**
* マトリクスに指定した行番号が存在するかチェックします。
* @param row 指定行番号
* @return このマトリクスに指定した行番号が存在すればtrueを、存在しなければfalseを返します。
*/
	public boolean isRowNumber(int row){
		return ((0 <= row) && (row < this.getRowLength()));
	}
	
/**
* マトリクスに指定した列番号が存在するかチェックします。
* @param col 指定列番号
* @return このマトリクスに指定した列番号が存在すればtrueを、存在しなければfalseを返します。
*/
	public boolean isColNumber(int col){
		return ((0 <= col) && (col < this.getColLength()));
	}
	
/**
* 2次元要素の大きさを変更します。
* @param rowSize 行サイズ
* @param colSize 列サイズ
* @exception NegativeMatrixSizeException 負のサイズのマトリクスが生成しようとした場合
*/
	private void setSizeMatrix(int rowSize, int colSize){
		
		//サイズは負ではないか
		if((rowSize >= 0) && ( colSize >= 0)){
			setSizeRow(rowSize);
			setSizeCol(colSize);
			matrix = new Object[rowSize][colSize];
		}
		else{
			throw new NegativeMatrixSizeException();
		}
	}
	
/**
* 2次元要素の行の大きさを変更します。
* @param rowSize 行サイズ
* @exception NegativeMatrixSizeException 負のサイズのマトリクスが生成しようとした場合
*/
	private void setSizeRow(int rowSize){
		
		//サイズは負ではないか
		if(rowSize >= 0){
			matrixRowSize = rowSize;		//行サイズの生成
			matrixRowName = new String[matrixRowSize];		//行名配列の生成
		}
		else{
			throw new NegativeMatrixSizeException();
		}
	}
	
/**
* 2次元要素の列の大きさを変更します。
* @param colSize 列サイズ
* @exception NegativeMatrixSizeException 負のサイズのマトリクスを生成しようとした場合
*/
	private void setSizeCol(int colSize){
		
		//サイズは負ではないか
		if(colSize >= 0){
			matrixColSize = colSize;		//列サイズの生成
			matrixColName = new String[matrixColSize];		//列名配列の生成
		}
		else{
			throw new NegativeMatrixSizeException();
		}
	}
	
/**
* 行名を入れる配列に行番号を行名として設定します。<BR>
* 行番号は0から始まるため、0から順に行名が行番号が同じ番号となります。 
* @see #setColNameNumber()
* @see #setRowNameNumber(int start)
*/
	public void setRowNameNumber(){
		for (int i = 0; i < getRowLength(); i++){
			setRowName(i, i +"");
		}
	}
	
/**
* 行名を入れる配列にstartから始まる番号を行名として設定します。
* @param start 最初の行名番号
* @see #setColNameNumber(int start)
* @see #setRowNameNumber()
*/
	public void setRowNameNumber(int start){
		for (int i = 0; i < getRowLength(); i++){
			setRowName(i, i + start +"");
		}
	}
	
/**
* 列名を入れる配列に列番号を列名として設定します。<BR>
* 列番号は0から始まるため、0から順に列名が列番号が同じ番号となります。 
* @see #setRowNameNumber()
* @see #setColNameNumber(int start)
*/
	public void setColNameNumber(){
		for (int i = 0; i < getColLength(); i++){
			setColName(i, i +"");
		}
	}
	
/**
* 列名を入れる配列にstartから始まる番号を列名として設定します。
* @param start 最初の列名番号
* @see #setRowNameNumber(int start)
* @see #setColNameNumber()
*/
	public void setColNameNumber(int start){
		for (int i = 0; i < getColLength(); i++){
			setColName(i, i + start +"");
		}
	}
	
/**
* このマトリクスが正方行列かチェックします。
* @return 正方行列ならばtrueを、違う場合はfalseを返します。
*/
	public boolean isSquare(){
		return getRowLength() == getColLength();
	}
	
/**
* 指定された1点へ値を設定します。
* @param row 指定行番号
* @param col 指定列番号
* @param value 設定値
* @exception NotElementException 指定した点が存在しない場合
*/
	public void setCell(int row,int col, Object value){
		if (isMatrixEntityPoint(row, col)){
			matrix[row][col] = value;
		}
		else{
			throw new NotElementException("Point(" + row + "," + col + ")");
		}
	}
	
/**
* 指定された1点の値を返します。
* @param row 指定行番号
* @param col 指定列番号
* @return 指定した点の値
* @exception NotElementException 指定された点が存在しない場合
*/
	public Object getObjectCell(int row, int col){
		if (isMatrixEntityPoint(row, col)){
			return matrix[row][col];
		}
		else{
			throw new NotElementException("point(" + row + "," + col + ")");
		}
	}
	
/**
* 要素を2次元配列で返します。
* @return 要素の2次元配列
*/
	public Object[][] getObjectMatrix(){
		return matrix;
	}
}