/*
 *   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 3 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, see <http://www.gnu.org/licenses/>.
 */

/*
 * ParentSet.java
 * Copyright (C) 2001-2012 University of Waikato, Hamilton, New Zealand
 * 
 */
package weka.classifiers.bayes.net;

import java.io.Serializable;

import weka.core.Instances;
import weka.core.RevisionHandler;
import weka.core.RevisionUtils;

/**
 * Helper class for Bayes Network classifiers. Provides datastructures to
 * represent a set of parents in a graph.
 * 
 * @author Remco Bouckaert (rrb@xm.co.nz)
 * @version $Revision: 8034 $
 */
public class ParentSet 
  implements Serializable, RevisionHandler {
  
  /** for serialization */
  static final long serialVersionUID = 4155021284407181838L;

  /**
   * Holds indexes of parents
   */
  private int[] m_nParents;

  /**
   * returns index parent of parent specified by index
   * 
   * @param iParent Index of parent
   * @return index of parent
   */
  public int getParent(int iParent) {
    return m_nParents[iParent];
  } 
  public int [] getParents() {return m_nParents;}

  /**
   * sets index parent of parent specified by index
   * 
   * @param iParent Index of parent
   * @param nNode index of the node that becomes parent
   */
  public void SetParent(int iParent, int nNode) {
	m_nParents[iParent] = nNode;
  } // SetParent


  /**
   * Holds number of parents
   */
  private int m_nNrOfParents = 0;

  /**
   * returns number of parents
   * @return number of parents
   */
  public int getNrOfParents() {
    return m_nNrOfParents;
  } 

  /**
   * test if node is contained in parent set
   * @param iNode node to test for
   * @return number of parents
   */
	public boolean contains(int iNode) {
		for (int iParent = 0; iParent < m_nNrOfParents; iParent++) {
			if (m_nParents[iParent] == iNode) {
				return true;
			}
		}
		return false;
	}
  /**
   * Holds cardinality  of parents (= number of instantiations the parents can take)
   */
  private int m_nCardinalityOfParents = 1;

  /**
   * returns cardinality of parents
   * 
   * @return the cardinality
   */
  public int getCardinalityOfParents() {
    return m_nCardinalityOfParents;
  } 

  /**
   * returns cardinality of parents after recalculation
   * 
   * @return the cardinality
   */
  public int getFreshCardinalityOfParents(Instances _Instances) {
	  m_nCardinalityOfParents = 1;
	  for (int iParent = 0; iParent < m_nNrOfParents; iParent++) {
		m_nCardinalityOfParents *= _Instances.attribute(m_nParents[iParent]).numValues();
	  }
      return m_nCardinalityOfParents;
  }
  /**
   * default constructor
   */
  public ParentSet() {
    m_nParents = new int[10];
    m_nNrOfParents = 0;
    m_nCardinalityOfParents = 1;
  }    // ParentSet

  /**
   * constructor
   * @param nMaxNrOfParents upper bound on nr of parents
   */
  public ParentSet(int nMaxNrOfParents) {
    m_nParents = new int[nMaxNrOfParents];
    m_nNrOfParents = 0;
    m_nCardinalityOfParents = 1;
  }    // ParentSet

  /**
   * copy constructor
   * @param other other parent set
   */
  public ParentSet(ParentSet other) {
    m_nNrOfParents = other.m_nNrOfParents;
    m_nCardinalityOfParents = other.m_nCardinalityOfParents;
    m_nParents = new int[m_nNrOfParents];

    for (int iParent = 0; iParent < m_nNrOfParents; iParent++) {
      m_nParents[iParent] = other.m_nParents[iParent];
    } 
  }    // ParentSet

  /**
   * reserve memory for parent set
   * 
   * @param nSize maximum size of parent set to reserver memory for
   */
  public void maxParentSetSize(int nSize) {
    m_nParents = new int[nSize];
  }    // MaxParentSetSize
 
  /**
   * Add parent to parent set and update internals (specifically the cardinality of the parent set)
   * 
   * @param nParent parent to add
   * @param _Instances used for updating the internals
   */
  public void addParent(int nParent, Instances _Instances) {
    if (m_nNrOfParents == m_nParents.length) { // 10) {
	// reserve more memory
      int [] nParents = new int[2 * m_nParents.length]; // 50];
        for (int i = 0; i < m_nNrOfParents; i++) {
            nParents[i] = m_nParents[i];
        }
        m_nParents = nParents;
   }
    m_nParents[m_nNrOfParents] = nParent;
    m_nNrOfParents++;
    m_nCardinalityOfParents *= _Instances.attribute(nParent).numValues();
  }    // AddParent

  /**
   * Add parent to parent set at specific location 
   * and update internals (specifically the cardinality of the parent set)
   * 
   * @param nParent parent to add
   * @param iParent location to add parent in parent set
   * @param _Instances used for updating the internals
   */
  public void addParent(int nParent, int iParent, Instances _Instances) {
    if (m_nNrOfParents == m_nParents.length) { // 10) {
	// reserve more memory
      int [] nParents = new int[2 * m_nParents.length]; // 50];
		for (int i = 0; i < m_nNrOfParents; i++) {
			nParents[i] = m_nParents[i];
		}
		m_nParents = nParents;
   }
	for (int iParent2 = m_nNrOfParents; iParent2 > iParent; iParent2--) {
		m_nParents[iParent2] = m_nParents[iParent2 - 1];		
	}
	m_nParents[iParent] = nParent;
	m_nNrOfParents++;
	m_nCardinalityOfParents *= _Instances.attribute(nParent).numValues();
  } // AddParent

  /** delete node from parent set
   * @param nParent node number of the parent to delete
   * @param _Instances data set
   * @return location of the parent in the parent set. This information can be 
   * used to restore the parent set using the addParent method.
   */
  public int deleteParent(int nParent, Instances _Instances) {
      int iParent = 0;
      while ((m_nParents[iParent] != nParent) && (iParent < m_nNrOfParents)) {
          iParent++;
      }
      int iParent2 = -1;
      if (iParent < m_nNrOfParents) {
      	iParent2 = iParent;
      }
      if (iParent < m_nNrOfParents) {
        while (iParent < m_nNrOfParents - 1) {
            m_nParents[iParent] = m_nParents[iParent + 1];
            iParent++;
        }
      	m_nNrOfParents--;
      	m_nCardinalityOfParents /= _Instances.attribute(nParent).numValues();
      }
      return iParent2;
  } // DeleteParent
  
  /**
   * Delete last added parent from parent set and update internals (specifically the cardinality of the parent set)
   * 
   * @param _Instances used for updating the internals
   */
  public void deleteLastParent(Instances _Instances) {
    m_nNrOfParents--;
    m_nCardinalityOfParents = 
      m_nCardinalityOfParents 
      / _Instances.attribute(m_nParents[m_nNrOfParents]).numValues();
  }    // DeleteLastParent

  /** Copy makes current parents set equal to other parent set
   * 
   * @param other : parent set to make a copy from
   */
  public void copy(ParentSet other) {
    m_nCardinalityOfParents = other.m_nCardinalityOfParents;
    m_nNrOfParents = other.m_nNrOfParents;
    for (int iParent = 0; iParent < m_nNrOfParents; iParent++) {
      m_nParents[iParent] = other.m_nParents[iParent];
    }
  } // Copy

  /**
   * Returns the revision string.
   * 
   * @return		the revision
   */
  public String getRevision() {
    return RevisionUtils.extract("$Revision: 8034 $");
  }
 
}      // class ParentSet
