/*
 *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * RuleItem.java
 * Copyright (C) 2004 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.associations;

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

import java.io.Serializable;
import java.util.Hashtable;

/**
 * Class for storing an (class) association rule.
 * The premise and the consequence are stored each as separate item sets.
 * For every rule their expected predictive accuracy and the time of generation is stored.
 * These two measures allow to introduce a sort order for rules.
 *
 * @author Stefan Mutter
 * @version $Revision: 1.5 $
 */
public class RuleItem
  implements Comparable, Serializable, RevisionHandler {

  /** for serialization */
  private static final long serialVersionUID = -3761299128347476534L;

  /** The premise of a rule. */ 
  protected ItemSet m_premise;

  /** The consequence of a rule. */ 
  protected ItemSet m_consequence;

  /** The expected predictive accuracy of a rule. */ 
  protected double m_accuracy;

  /** The generation time of a rule. */ 
  protected int m_genTime;


  /**
   * Constructor for an empty RuleItem
   */    
  public RuleItem(){

  }

  /**
   * Constructor that generates a RuleItem out of a given one
   * @param toCopy RuleItem to copy
   */    
  public RuleItem(RuleItem toCopy){

    m_premise = toCopy.m_premise;
    m_consequence = toCopy.m_consequence;
    m_accuracy = toCopy.m_accuracy;
    m_genTime = toCopy.m_genTime;
  }

  /**
   * Constructor
   * @param premise the premise of the future RuleItem
   * @param consequence the consequence of the future RuleItem
   * @param genTime the time of generation of the future RuleItem
   * @param ruleSupport support of the rule
   * @param m_midPoints the mid poitns of the intervals
   * @param m_priors Hashtable containing the estimated prior probablilities
   */      
  public RuleItem(ItemSet premise, ItemSet consequence, int genTime,int ruleSupport,double [] m_midPoints, Hashtable m_priors){


    m_premise = premise;
    m_consequence = consequence;
    m_accuracy = RuleGeneration.expectation((double)ruleSupport,m_premise.m_counter,m_midPoints,m_priors);
    //overflow, underflow
    if(Double.isNaN(m_accuracy) || m_accuracy < 0){
      m_accuracy = Double.MIN_VALUE;
    }
    m_consequence.m_counter = ruleSupport;
    m_genTime = genTime;
  }

  /**
   * Constructs a new RuleItem if the support of the given rule is above the support threshold.
   * @param premise the premise
   * @param consequence the consequence
   * @param instances the instances
   * @param genTime the time of generation of the current premise and consequence
   * @param minRuleCount the support threshold
   * @param m_midPoints the mid points of the intervals
   * @param m_priors the estimated priori probabilities (in a hashtable)
   * @return a RuleItem if its support is above the threshold, null otherwise
   */      
  public RuleItem generateRuleItem(ItemSet premise, ItemSet consequence, Instances instances,int genTime, int minRuleCount,double[] m_midPoints, Hashtable m_priors){
    ItemSet rule = new ItemSet(instances.numInstances());
    rule.m_items = new int[(consequence.m_items).length];
    System.arraycopy(premise.m_items, 0, rule.m_items, 0, (premise.m_items).length);
    for(int k = 0;k < consequence.m_items.length; k++){
      if(consequence.m_items[k] != -1)
	rule.m_items[k] = consequence.m_items[k];
    }
    for (int i = 0; i < instances.numInstances(); i++) 
      rule.upDateCounter(instances.instance(i));
    int ruleSupport = rule.support();
    if(ruleSupport > minRuleCount){
      RuleItem newRule = new RuleItem(premise,consequence,genTime,ruleSupport,m_midPoints,m_priors);
      return newRule;
    }
    return null;
  }

  //Note: this class has a natural ordering that is inconsistent with equals
  /**
   * compares two RuleItems and allows an ordering concerning
   * expected predictive accuracy and time of generation
   * Note: this class has a natural ordering that is inconsistent with equals
   * @param o RuleItem to compare
   * @return integer indicating the sort oder of the two RuleItems
   */      
  public int compareTo(Object o) {

    if(this.m_accuracy == ((RuleItem)o).m_accuracy){ 
      if((this.m_genTime == ((RuleItem)o).m_genTime))
	return 0;
      if(this.m_genTime > ((RuleItem)o).m_genTime)
	return -1;
      if(this.m_genTime < ((RuleItem)o).m_genTime)
	return 1;
    }
    if(this.m_accuracy < ((RuleItem)o).m_accuracy)
      return -1;
    return 1;
  }

  /**
   * returns whether two RuleItems are equal
   * @param o RuleItem to compare
   * @return true if the rules are equal, false otherwise
   */      
  public  boolean equals(Object o){

    if(o == null)
      return false;
    if(m_premise.equals(((RuleItem)o).m_premise) && m_consequence.equals(((RuleItem)o).m_consequence))
      return true;
    return false;
  }

  /**
   * Gets the expected predictive accuracy of a rule
   * @return the expected predictive accuracy of a rule stored as a RuleItem
   */      
  public double accuracy(){

    return m_accuracy;
  }

  /**
   * Gets the premise of a rule
   * @return the premise of a rule stored as a RuleItem
   */      
  public ItemSet premise(){

    return m_premise;
  }

  /**
   * Gets the consequence of a rule
   * @return the consequence of a rule stored as a RuleItem
   */      
  public ItemSet consequence(){

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