///////////////////////////////////////////////////////////////////////////////
// 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.biolingua;

import edu.cmu.tetrad.study.gene.tetrad.gene.algorithm.util.BasicGraph;
import edu.cmu.tetrad.study.gene.tetrad.gene.algorithm.util.MatrixF;

import java.io.IOException;

/**
 * Simple implementation of a directed Graph.  edges are just represented by
 * float values (a zero == no edge) stored in a matrix.
 * <p>
 * Two edges of different orientation can exist between two nodes, but no more than
 * one edge of a given orientation can exist between two nodes.
 *
 * @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 class Digraph extends BasicGraph {

    /**
     * Edge values
     */
    private MatrixF edges;

    /**
     * Number of parents of each node
     */
    private int[] nParents;

    /**
     * Creates a OldDigraph with <code>gName</code> name, and <code>n</code>
     * nodes.
     */
    public Digraph(String gName, int nNodes) {
        super(gName, nNodes);
    }

    /**
     * Creates a OldDigraph reading it from file <code>fname</code>.
     */
    public Digraph(String fname) throws IOException {
        super(fname);
    }

    /**
     * Copy constructor.
     */
    public Digraph(Digraph digraph) {
        this("Clone_of_[" + digraph + "]", digraph.nNodes);
        for (int i = 0; i < digraph.nNodes; i++) {
            this.nodeNames[i] = digraph.nodeNames[i];
            for (int j = 0; j < i; j++) {
                this.setEdge(i, j, digraph.getEdge(i, j));
            }
        }
    }

    /**
     * Returns a clone of this graph
     */
    public Object clone() {
        Digraph g2 =
                new Digraph("Clone_of_[" + this.graphName + "]", this.nNodes);
        for (int i = 0; i < this.nNodes; i++) {
            g2.nodeNames[i] = this.nodeNames[i];
            for (int j = 0; j < i; j++) {
                g2.setEdge(i, j, this.getEdge(i, j));
            }
        }
        return g2;
    }

    protected void initializeEdges() {
        this.edges = new MatrixF("EdgeMatrix_" + this.graphName, this.nNodes);
        this.nParents = new int[this.nNodes];
    }

    /**
     * Sets a value of edge between nodes i and j
     */
    public void setEdge(int i, int j, double value) {
        double e = this.getEdges().getDoubleValue(i, j);
        this.getEdges().setDoubleValue(i, j, value);
        if ((e == 0.0) && (value != 0.0)) {
            this.nEdges++;
            this.nParents[j]++;
        } else {
            if ((e != 0.0) && (value == 0.0)) {
                this.nEdges--;
                this.nParents[j]--;
            }
        }
    }

    /**
     * Returns the value of edge between nodes i and j
     */
    public double getEdge(int i, int j) {
        return this.getEdges().getDoubleValue(i, j);
    }

    /**
     * Returns a string representation of the set of edges in this graph
     */
    public String EdgesToString() {
        String s = "";
        int ne = 0;
        for (int i = 0; i < this.nNodes; i++) {
            for (int j = 0; j < this.nNodes; j++) {
                double e = this.getEdges().getDoubleValue(i, j);
                if (e != 0.0) {
                    s = s + i + "  " + j + " \t" + e +
                            "\n";  //+"\t// # "+ne+"\n";
                    ne++;
                }
            }
        }
        return s;
    }

    /**
     * Returns the number of parents of node i
     */
    public int getNumParents(int i) {
        if ((i < 0) || (i >= this.getSize())) {
            this.badNodeIndex(i);
        }
        return this.nParents[i];
    }

    /**
     * Returns an array with the indexes of the parents of node i. If node i has
     * no parents it returns an array of size 0 (e.g. not null)
     */
    public int[] getParents(int j) {
        if ((j < 0) || (j >= this.nNodes)) {
            this.badNodeIndex(j);
        }
        int[] ap = new int[this.nParents[j]];
        int np = 0;
        for (int i = 0; i < this.nNodes; i++) {
            if (this.getEdges().getDoubleValue(i, j) != 0.0) {
                ap[np] = i;
                np++;
            }
        }
        return ap;
    }

    public MatrixF getEdges() {
        return this.edges;
    }

}




