///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below.       //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,       //
// 2007, 2008, 2009, 2010, 2014, 2015, 2022 by Peter Spirtes, Richard        //
// Scheines, Joseph Ramsey, and Clark Glymour.                               //
//                                                                           //
// This program is free software; you can redistribute it and/or modify      //
// it under the terms of the GNU General Public License as published by      //
// the Free Software Foundation; either version 2 of the License, or         //
// (at your option) any later version.                                       //
//                                                                           //
// This program is distributed in the hope that it will be useful,           //
// but WITHOUT ANY WARRANTY; without even the implied warranty of            //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             //
// GNU General Public License for more details.                              //
//                                                                           //
// You should have received a copy of the GNU General Public License         //
// along with this program; if not, write to the Free Software               //
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA //
///////////////////////////////////////////////////////////////////////////////

package edu.cmu.tetrad.study.gene.tetrad.gene.algorithm.util;


import java.io.*;

/**
 * Basic functionality of a Matrix
 *
 * @author <a href="http://www.eecs.tulane.edu/Saavedra" target="_TOP">Raul Saavedra</a>
 * (<a href="mailto:rsaavedr@ai.uwf.edu">rsaavedr@ai.uwf.edu</A>)
 */
public abstract class BasicMatrix {
    protected String name;
    protected int n;

    /**
     * Maximum short value
     */
    public static final short MAX_SHORT = Short.MAX_VALUE;

    /**
     * Minimum short getValue
     */
    public static final short MIN_SHORT = Short.MIN_VALUE;

    /**
     * Maximum int value
     */
    public static final int MAX_INT = Integer.MAX_VALUE;

    /**
     * Minimum int value
     */
    public static final int MIN_INT = Integer.MIN_VALUE;

    /**
     * Maximum float value
     */
    public static final float MAX_FLOAT = Float.MAX_VALUE;

    /**
     * Minimum float value
     */
    public static final float MIN_FLOAT = -BasicMatrix.MAX_FLOAT;

    /**
     * No parameters constructor, only used within the package
     */
    protected BasicMatrix() {
    }

    /**
     * Creates a matrix with <code>nrows</code> rows, and with name
     * <code>mname</code>.
     */
    public BasicMatrix(String mname, int nrows) {
        this.name = mname;
        if (nrows <= 0) {
            throw new IllegalArgumentException("Invalid # nodes " + nrows);
        }
        this.n = nrows;
        this.initMatrixStorage();
    }

    /**
     * Creates a matrix reading it from a file <code>fname</code>. The file has
     * to be an ascii one and follow a particular format:<p> *MATRIX*  [matrix
     * name] <br> [n]<br> [0, 0 ] [ 0, 1] ...  [ 0, n-1]<br> [1, 0 ] [ 1, 1] ...
     * [ 1, n-1]<br> :<br> [n-1,0] [n-1,1] ...  [n-1,n-1]<p> First token
     * should be a word with "MATRIX" as a substring (case insens.), followed by
     * the name of the matrix (one word). [n] is the number of rows and columns
     * in the matrix, and [i,j] is element at position i,j in the matrix.<br>
     * For example, a 3x3 identity matrix could be represented as follows:<p>
     * MATRIX Identity3x3<br> <br> 3  // # rows and columns<br> <br> //
     * Matrix elements:<br> 1 0 0<br> 0 1 0<br> 0 0 1<p> Notice there can
     * be slash-slash (and also slash-star) style comments anywhere in the file.
     * Numbers can be separated by any number of blank delimiters: tabs, spaces,
     * carriage returns.  In the examples above they appear in different lines
     * for more readability of the file. The file may have less elements than
     * the total needed to fill the matrix.  If it has more elements an illegal
     * argument exception will be generated.
     */
    public BasicMatrix(String fname) throws IOException {
        // Create and prepare stream tokenizer
        File f = new File(fname);
        BufferedReader in = new BufferedReader(new FileReader(f));
        StreamTokenizer strmTok = new StreamTokenizer(in);
        strmTok.slashStarComments(true);
        strmTok.slashSlashComments(true);
        strmTok.parseNumbers();
        strmTok.wordChars('_', '_'); // underscore is a word char

        // Read matrix name
        int nt = strmTok.nextToken();
        if ((strmTok.sval == null) ||
                (!strmTok.sval.toUpperCase().contains("MATRIX"))) {
            throw new IllegalArgumentException(
                    "First token does not contain 'MATRIX': " + strmTok.sval);
        }
        nt = strmTok.nextToken();
        this.name = strmTok.sval;

        // Read from file # of rows in the matrix
        nt = strmTok.nextToken();
        if (nt != StreamTokenizer.TT_NUMBER) {
            throw new IllegalArgumentException(
                    "Error parsing # of rows: " + strmTok.sval);
        }
        int vnrows = (int) strmTok.nval;
        if (vnrows <= 0) {
            throw new IllegalArgumentException("Invalid # rows " + vnrows);
        }
        this.n = vnrows;
        this.initMatrixStorage();

        // Now read elements from the file
        int row = 0;
        int col = 0;
        final int val = 0;
        while (true) {
            try {
                nt = strmTok.nextToken();
            } catch (IOException e) {
                break;
            }
            if (nt == StreamTokenizer.TT_EOF) {
                break;
            }
            if (nt == StreamTokenizer.TT_NUMBER) {
                this.setDoubleValue(row, col, strmTok.nval);
                col++;
                if (col == vnrows) {
                    col = 0;
                    row++;
                }
            } else {
                throw new IllegalArgumentException("Error parsing element [" +
                        row + "," + col + "]: " + strmTok.sval);
            }
        }
        in.close();
    }

    /**
     * Returns # rows ( == # columns) of this matrix
     */
    public int getSize() {
        return this.n;
    }

    /**
     * Sets the name of this matrix
     */
    public void setName(String newName) {
        this.name = newName;
    }

    /**
     * Returns name of this matrix
     */
    public String getName() {
        return this.name;
    }

    /**
     * Returns a specially formatted string with all the contents of this
     * matrix
     */
    public String toString() {
        StringBuilder s = new StringBuilder(this.getClass().getName() + " " + this.name + "\n" + this.n +
                " // <- Total # rows\n");
        for (int r = 0; r < this.n; r++) {
            //s = s + "/* "+r+" */  ";
            for (int c = 0; c < this.n; c++) {
                s.append(this.getDoubleValue(r, c)).append(" ");
            }
            s.append("\n");
        }
        return s.toString();
    }

    protected void badIndexXcp(int r, int c) {
        throw new IllegalArgumentException(
                "Bad index (" + r + "," + c + ") for matrix of size " + this.n);
    }

    /**
     * Initializes the data structure used to hold the contents of the matrix
     */
    protected abstract void initMatrixStorage();

    /**
     * Returns the value stored at element (r,c) as a double
     */
    public abstract double getDoubleValue(int r, int c);

    /**
     * Assigns double value x in matrix element (r, c). Notice the presence
     * of this method does not really force the implementing class to actually
     * store doubles.
     */
    public abstract void setDoubleValue(int r, int c, double x);

    /**
     * Assign zero to all elements in the matrix
     */
    public abstract void setAllValuesToZero();

}





