package yacs.solver.fdsolver.search;

import yacs.net.*;
import yacs.parser.*;

import java.util.*;

import org.apache.log4j.*;

/**
 * <p>Generiert auf Basis des "Fail-First"-Prinzips (FF) und der
 * "Maximum-Degree-Ordering"-Heuristik (MDO) eine Reihenfolge für die Belegung
 * von Variablen während einer Lösungssuche (vgl. Bessière und Régin 1996). Das
 * Prinzip dieser Heuristik ist, dass Variablen, deren Verhältnis von Größe des
 * Wertebereichs (<tt>dom</tt>) zum Vernetzungsgrad (<tt>deg</tt>) innerhalb
 * des Constraint-Problems am geringsten ist, zuerst mit einem Wert belegt
 * werden (<tt>dom/deg</tt>).</p>
 *
 * <p>Copyright (C) 2005  Wolfgang Runte
 * <br><br>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <br><br>
 * This library 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
 * Lesser General Public License for more details.
 * <br><br>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA</p>
 *
 * @author Wolfgang Runte (woru@tzi.org)
 * @version YACS 0.1.1
 */
public class DomDegRatioVariableOrdering {

// ********** private attributs **********

  /** Logger aktivieren: */
  private static Logger logger = Logger.getLogger("yacs.solver.fdsolver.search.DomDegRationVariableOrdering");

  /**
   * Eine <code>TreeMap</code>, welche das Verhältnis von der Größe des
   * Wertebereichs (dom) zum Vernetzungsgrad (deg) einer Variablen innerhalb des
   * Constraint-Problems als "Key" (<code>float</code>-Wert) und die Variable
   * selbst als "Value" enthält (Instanz der Klasse <code>String</code>).
   */
  private TreeMap ratioMap;

// ********** public attributs **********

// ********** private methods **********

// ********** public methods **********

  /**
   * Generiert eine <tt>dom/deg</tt>-Ordnung für die Variablen des übergebenen
   * Constraint-Netzes. Liefert eine entsprechend geordnete Liste mit
   * Variablennamen (<code>Strings</code>)
   * @param constraintNet ConstraintNet
   * @return List
   */
  public List getOrdering(ConstraintNet constraintNet) {
    int dom, deg;
    FloatSorter floatSorter;
    String variableName;
    this.ratioMap = new TreeMap();
    Expression expr = constraintNet.getSubproblem();
    Iterator it = constraintNet.getVariables().iterator();
    while (it.hasNext()) {
      variableName = (String)it.next();
      this.logger.debug("variableName: " + variableName);
      dom = constraintNet.getDomain(variableName).size();
      this.logger.debug("dom: " + dom);
      deg = expr.count(variableName);
      this.logger.debug("deg: " + deg);
      floatSorter = new FloatSorter(new Float(new Integer(dom).floatValue()/new Integer(deg).floatValue()));
      this.logger.debug("floatSorter: " + floatSorter);
      this.ratioMap.put(floatSorter, variableName);
      this.logger.debug("this.ratioMap: " + this.ratioMap);
    }
    return new LinkedList(this.ratioMap.values());
  }

  /**
   * Einfache, interne Hilfsklasse ("inner class"), die es ermöglicht zwei
   * <code>Float</code>-Objekte mit gleichem Float-Wert als "Key" innerhalb
   * einer <code>TreeMap</code> zu verwenden.
   */
  private class FloatSorter implements Comparable {

    public float floatValue;

    public FloatSorter(Float floatObject) {
      this.floatValue = floatObject.floatValue();
    }

    public int compareTo(Object param) {
      if (this.floatValue >= ((FloatSorter)param).floatValue) {
        return 1;
      }
      return -1;
    }

    public String toString() {
      return "[FloatSorter:" + this.floatValue + "]";
    }

  }

  public static void main(String[] args) {
    Map map = new TreeMap();
    map.put(new Float(0.1), "A");
    map.put(new Float(0.2), "B");
    map.put(new Float(0.3), "C");
    map.put(new Float(0.4), "D");
    map.put(new Float(0.5), "E");
    System.out.println("map: " + map);
    System.out.println("map.values(): " + map.values());
    System.out.println("Float(1/2): " + new Float(1/2));
    System.out.println("Float(new Integer(1).floatValue()/ new Integer(2).floatValue())): " + new Float(new Integer(1).floatValue()/ new Integer(2).floatValue()));
  }

}
