package javacodebook.core.matrix;

import java.util.Random;

/**
 * Eine Matrix-Klasse mit einfachen Rechnenoperationen
 * @author Mark Donnermeyer
 */
public class Matrix {
    int rows, cols;
    private double[][] cell;
    
    /**
     * Erzeugt eine Matrix der Grösse rows/cols und
     * allen Elementen auf 0 gesetzt
     */
    public Matrix(int rows, int cols) {
        cell = new double[rows][cols];
        for (int i = 0; i < rows; i++) {
            for (int k = 0; k < cols; k++) {
                cell[i][k] = 0;
            }
        }
        this.rows = rows;
        this.cols = cols;
    }
    
    /**
     * Erzeugt eine neue Matrix mit der Grösse und den
     * Werten der übergebenen Matrix.
     */
    public Matrix(Matrix matrix) {
        cell = matrix.getCells();
        rows = matrix.getRows();
        cols = matrix.getCols();
    }
    
    /**
     * Erzeugt eine neue Matrix mit der Grösse und den
     * Werten des übergebenen Arrays.
     */
    public Matrix(double [][]matrix)
    {
        cell = new double[matrix.length][matrix[0].length];
        rows = cell.length;
        cols = cell[0].length;
        for (int i = 0; i < rows; i++) {
            System.arraycopy(matrix[i], 0, cell[i], 0, cols);
        }
    }
    
    /** 
     * Anzahl der Zeilen der Matrix zurückgeben.
     */
    public int getRows() {
        return rows;
    }
    
    /** 
     * Anzahl der Spalter der Matrix zurückgeben.
     */
    public int getCols() {
        return cols;
    }
    
    /**
     * Gibt eine Kopie der Matrix-Elemente zurück
     */
    public double[][] getCells() {
        double copy[][] = new double[rows][cols];
        for (int i = 0; i < rows; i++) {
            System.arraycopy(cell[i], 0, copy[i], 0, cols);
        }
        return copy;
    }
    
    /**
     * Den Wert einer Zelle der Matrix zurückgeben
     */
    public double getValue(int row, int col) {
        return cell[row][col];
    }
    
    /**
     * Den Wert einer Zelle der Matrix neu setzen
     */
    public void setValue(int row, int col, double value) {
        cell[row][col] = value;
    }
    
    /**
     * Testen, ob zwei Matrizen die gleiche Anzahl an Zeilen und Spalten hat
     */
    public boolean sameDimension(Matrix b) {
        return (rows == b.getRows() && cols == b.getCols());
    }
    
    /**
     * Addiert zwei Matrizen miteinander. Die Matrizen
     * müssen hierfür das gleiche Format haben.
     */
    public Matrix add(Matrix b) throws Exception {
        if (!sameDimension(b)) {
            throw new Exception("Dimension mismatch");
        }
        Matrix result = new Matrix(rows, cols);
        double value = 0;
        for (int i = 0; i<rows; i++) {
            for (int j = 0; j <cols; j++) {
                value = getValue(i,j) + b.getValue(i,j);
                result.setValue(i, j, value);
            }
        }
        return result;
    }
    
    /**
     * Multipliziert zwei Matrizen miteinander. Die Matrizen
     * müssen hierfür das richtige Format haben. (Die Anzahl
     * der Zeilen der übergebenen Matrix ist gleich der 
     * Anzahl der Spalten dieser Matrix.
     */
    public Matrix multiply(Matrix b) throws Exception {
        if (cols != b.getRows()) {
            throw new Exception("Dimension mismatch");
        }
        Matrix result = new Matrix(rows, b.getCols());
        double value;
        for (int i=0; i<rows; i++) {
            for (int j=0; j<b.getCols(); j++) {
                for (int k=0; k<cols; k++) {
                    value = result.getValue(i,j);
                    value += (getValue(i, k)*b.getValue(k,j));
                    result.setValue(i,j, value);
                }
            }
        }
        return result;
    }
    
    /**
     * Multipliziert eine Matrize mit einer Zahl.
     */
    public Matrix multiply(double value) {
        Matrix result = new Matrix(rows, cols);
        for (int i=0; i<rows; i++) {
            for (int j=0; j<cols; j++) {
                cell[i][j] = value* cell[i][j];
            }
        }
        return result;
    }        
    
    /**
     * Schreibt die Matrix in einen String
     */
    public String toString() {
        String ret = new String();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                ret += " " + cell[i][j];
            }
            ret += "\n";
        }
        return ret;
    }
}