content: /*
   *    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.
   */
  
  /*
   * Debug.java
   * Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
   */
  
  package weka.core;
  
  import java.io.BufferedWriter;
  import java.io.FileWriter;
  import java.io.PrintWriter;
  import java.io.Serializable;
  import java.io.StringWriter;
  import java.lang.management.ManagementFactory;
  import java.lang.management.ThreadMXBean;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.logging.FileHandler;
  import java.util.logging.Handler;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  import java.util.logging.SimpleFormatter;
  
  /**
   * A helper class for debug output, logging, clocking, etc.
   * 
   * @author  fracpete (fracpete at waikato dot ac dot nz)
   * @version $Revision: 7517 $
   */
  public class Debug
    implements Serializable, RevisionHandler {
  
    /** for serialization */
    private static final long serialVersionUID = 66171861743328020L;
    
    /** the log level All */
    public final static Level ALL = Level.ALL;
    /** the log level Vonfig */
    public final static Level CONFIG = Level.CONFIG;
    /** the log level Fine */
    public final static Level FINE = Level.FINE;
    /** the log level Finer */
    public final static Level FINER = Level.FINER;
    /** the log level Finest */
    public final static Level FINEST = Level.FINEST;
    /** the log level Info */
    public final static Level INFO = Level.INFO;
    /** the log level Off - i.e., no logging */
    public final static Level OFF = Level.OFF;
    /** the log level Severe */
    public final static Level SEVERE = Level.SEVERE;
    /** the log level Warning */
    public final static Level WARNING = Level.WARNING;
  
    /** whether logging is enabled */
    protected boolean m_Enabled = true;
    
    /** for logging */
    protected Log m_Log;
    
    /** for clocking */
    protected Clock m_Clock = new Clock();
    
    /**
     * A little helper class for clocking and outputting times. It measures the
     * CPU time if possible, otherwise it's just based on the system time. In 
     * case one just wants to measure time (e.g., database queries don't take up
     * much CPU time, but still might take a long time to finish), then one can
     * disable the use of CPU time as well.
     *
     * @author FracPete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 7517 $ 
     * @see ThreadMXBean#isThreadCpuTimeEnabled()
     */
    public static class Clock 
      implements Serializable, RevisionHandler {
      
      /** for serialization */
      private static final long serialVersionUID = 4622161807307942201L;
      
      /** the output format in milli-seconds */
      public final static int FORMAT_MILLISECONDS = 0;
      
      /** the output format in seconds, with fraction of msecs */
      public final static int FORMAT_SECONDS = 1;
      
      /** the output format in hours:minutes:seconds, with fraction of msecs */
      public final static int FORMAT_HHMMSS = 2;
      
      /** the output formats */
      public static final Tag[] TAGS_FORMAT = {
        new Tag(FORMAT_MILLISECONDS, "milli-seconds"),
        new Tag(FORMAT_SECONDS, "seconds"),
        new Tag(FORMAT_HHMMSS, "hh:mm:ss")
      };
      
      /** the format of the output */
      public int m_OutputFormat = FORMAT_SECONDS;
      
      /** the start time */
      protected long m_Start;
      
      /** the end time */
      protected long m_Stop;
      
      /** whether the time is still clocked */
      protected boolean m_Running;
      
      /** the thread ID */
      protected long m_ThreadID;
      
      /** whether the system can measure the CPU time */
      protected boolean m_CanMeasureCpuTime;
      
      /** whether to use the CPU time (by default TRUE) */
      protected boolean m_UseCpuTime;
      
      /** the thread monitor, if the system can measure the CPU time */
      protected transient ThreadMXBean m_ThreadMonitor;
      
      /**
       * automatically starts the clock with FORMAT_SECONDS format and CPU
       * time if available
       * 
       * @see		#m_OutputFormat
       */
      public Clock() {
        this(true);
      }
      
      /**
       * automatically starts the clock with the given output format and CPU
       * time if available
       * 
       * @param format	the output format
       * @see		#m_OutputFormat
       */
      public Clock(int format) {
        this(true, format);
      }
      
      /**
       * starts the clock depending on <code>start</code> immediately with the
       * FORMAT_SECONDS output format and CPU time if available
       * 
       * @param start	whether to start the clock immediately
       * @see		#m_OutputFormat
       */
      public Clock(boolean start) {
        this(start, FORMAT_SECONDS);
      }
      
      /**
       * starts the clock depending on <code>start</code> immediately, using
       * CPU time if available
       * 
       * @param start	whether to start the clock immediately
       * @param format	the format
       * @see		#m_OutputFormat
       */
      public Clock(boolean start, int format) {
        m_Running    = false;
        m_Start      = 0;
        m_Stop       = 0;
        m_UseCpuTime = true;
        setOutputFormat(format);
  
        if (start)
  	start();
      }
      
      /**
       * initializes the clocking, ensure to get the correct thread ID.
       */
      protected void init() {
        m_ThreadMonitor = null;
        m_ThreadMonitor = getThreadMonitor();
  
        // can we measure cpu time?
        m_CanMeasureCpuTime = m_ThreadMonitor.isThreadCpuTimeSupported();
      }
      
      /**
       * whether the measurement is based on the msecs returned from the System
       * class or on the more accurate CPU time. Also depends on whether the 
       * usage of the CPU time was disabled or enabled.
       * 
       * @return		true if the more accurate CPU time of the thread is 
       * 			used and the use of CPU time hasn't been disabled
       * @see System#currentTimeMillis()
       * @see ThreadMXBean#isThreadCpuTimeEnabled()
       * @see #getUseCpuTime()
       */
      public boolean isCpuTime() {
        return m_UseCpuTime && m_CanMeasureCpuTime;
      }
  
      /**
       * enables/disables the use of CPU time (if measurement of CPU time is 
       * available). The actual use of CPU time still depends on whether the 
       * system supports it. Resets the current timer, if running.
       * 
       * @param value	if true the CPU time is used (if possible)
       */
      public void setUseCpuTime(boolean value) {
        m_UseCpuTime = value;
        
        // we have to re-initialize the start time, otherwise we get bogus
        // results
        if (m_Running) {
  	stop();
  	start();
        }
      }
  
      /**
       * returns whether the use of CPU is time is enabled/disabled (regardless
       * whether the system supports it or not)
       * 
       * @return		true the CPU time is used (if possible)
       */
      public boolean getUseCpuTime() {
        return m_UseCpuTime;
      }
      
      /**
       * Returns a new thread monitor if the current one is null (e.g., due to
       * serialization) or the currently set one. The thread ID is also updated
       * if necessary.
       * 
       * @return		the thread monitor to use
       */
      protected ThreadMXBean getThreadMonitor() {
        if (m_ThreadMonitor == null) {
  	m_ThreadMonitor = ManagementFactory.getThreadMXBean();
  	if (m_CanMeasureCpuTime && !m_ThreadMonitor.isThreadCpuTimeEnabled())
  	  m_ThreadMonitor.setThreadCpuTimeEnabled(true);
  	m_ThreadID = Thread.currentThread().getId();
        }
        
        return m_ThreadMonitor;
      }
      
      /**
       * returns the current time in msec
       * 
       * @return		the current time
       */
      protected long getCurrentTime() {
        long	result;
        
        if (isCpuTime())
  	result = getThreadMonitor().getThreadUserTime(m_ThreadID) / 1000000;
        else
  	result = System.currentTimeMillis();
        
        return result;
      }
      
      /**
       * saves the current system time (or CPU time) in msec as start time
       * 
       * @see       #m_Start
       */
      public void start() {
        // make sure that we get the right thread ID!
        init();
  
        m_Start   = getCurrentTime();
        m_Stop    = m_Start;
        m_Running = true;
      }
      
      /**
       * saves the current system (or CPU time) in msec as stop time
       * 
       * @see       #m_Stop
       */
      public void stop() {
        m_Stop    = getCurrentTime();
        m_Running = false;
      }
      
      /**
       * returns the start time
       * 
       * @return	the start time
       */
      public long getStart() {
        return m_Start;
      }
      
      /**
       * returns the stop time or, if still running, the current time
       * 
       * @return 	the stop time
       */
      public long getStop() {
        long	result;
        
        if (isRunning())
  	result = getCurrentTime();
        else
  	result = m_Stop;
        
        return result;
      }
      
      /**
       * whether the time is still being clocked
       * 
       * @return		true if the time is still being clocked
       */
      public boolean isRunning() {
        return m_Running;
      }
      
      /**
       * sets the format of the output
       * 
       * @param value       the format of the output
       * @see               #m_OutputFormat
       */
      public void setOutputFormat(int value) {
        if (value == FORMAT_MILLISECONDS)
  	m_OutputFormat = value;
        else if (value == FORMAT_SECONDS)
  	m_OutputFormat = value;
        else if (value == FORMAT_HHMMSS)
  	m_OutputFormat = value;
        else
  	System.out.println("Format '" + value + "' is not recognized!");
      }
      
      /**
       * returns the output format
       * 
       * @return		the output format
       * @see		#m_OutputFormat
       */
      public int getOutputFormat() {
        return m_OutputFormat;
      }
      
      /**
       * returns the elapsed time, getStop() - getStart(), as string
       * 
       * @return	the elapsed time as string
       * @see       #getStart()
       * @see       #getStop()
       */
      public String toString() {
        String    result;
        long      elapsed;
        long      hours;
        long      mins;
        long      secs;
        long      msecs;
        
        result  = "";
        elapsed = getStop() - getStart();
        
        switch (getOutputFormat()) {
  	case FORMAT_HHMMSS:
  	  hours   = elapsed / (3600 * 1000);
  	  elapsed = elapsed % (3600 * 1000);
  	  mins    = elapsed / (60 * 1000);
  	  elapsed = elapsed % (60 * 1000);
  	  secs    = elapsed / 1000;
  	  msecs   = elapsed % 1000;
  	  
  	  if (hours > 0)
  	    result += "" + hours + ":";
  	  
  	  if (mins < 10)
  	    result += "0" + mins + ":";
  	  else
  	    result += ""  + mins + ":";
  	  
  	  if (secs < 10)
  	    result += "0" + secs + ".";
  	  else
  	    result += "" + secs + ".";
  	  
  	  result += Utils.doubleToString(
  	      (double) msecs / (double) 1000, 3).replaceAll(".*\\.", "");
  	  break;
  	  
  	case FORMAT_SECONDS:
  	  result = Utils.doubleToString((double) elapsed / (double) 1000, 3) + "s";
  	  break;
  	  
  	case FORMAT_MILLISECONDS:
  	  result = "" + elapsed + "ms";
  	  break;
  	  
  	default:
  	  result = "<unknown time format>";
        }
        
        return result;
      }
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
    
    /**
     * A class that can be used for timestamps in files, The toString() method
     * simply returns the associated Date object in a timestamp format. For
     * formatting options, see java.text.SimpleDateFormat.
     *
     * @author FracPete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 7517 $ 
     * @see SimpleDateFormat
     */
    public static class Timestamp
      implements Serializable, RevisionHandler {
      
      /** for serialization */
      private static final long serialVersionUID = -6099868388466922753L;
  
      /** the default format */
      public final static String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
      
      /** the actual date */
      protected Date m_Stamp;
      
      /** the format of the timestamp */
      protected String m_Format;
      
      /** handles the format of the output */
      protected SimpleDateFormat m_Formatter;
      
      /**
       * creates a timestamp with the current date and time and the default
       * format.
       */
      public Timestamp() {
        this(DEFAULT_FORMAT);
      }
      
      /**
       * creates a timestamp with the current date and time and the specified
       * format.
       * 
       * @param format	the format of the timestamp
       * @see		SimpleDateFormat
       */
      public Timestamp(String format) {
        this(new Date(), format);
      }
      
      /**
       * creates a timestamp with the given date and the default format.
       * 
       * @param stamp	the associated date/time for the timestamp
       */
      public Timestamp(Date stamp) {
        this(stamp, DEFAULT_FORMAT);
      }
      
      /**
       * creates a timestamp with the given date and format.
       * 
       * @param stamp	the associated date/time for the timestamp
       * @param format	the format of the timestamp
       * @see		SimpleDateFormat
       */
      public Timestamp(Date stamp, String format) {
        super();
        
        m_Stamp = stamp;
        setFormat(format);
      }
      
      /**
       * sets the format for the timestamp
       * 
       * @param value	the format string
       * @see		SimpleDateFormat
       */
      public void setFormat(String value) {
        try {
  	m_Formatter = new SimpleDateFormat(value);
  	m_Format    = value;
        }
        catch (Exception e) {
  	m_Formatter = new SimpleDateFormat(DEFAULT_FORMAT);
  	m_Format    = DEFAULT_FORMAT;
        }
      }
      
      /**
       * returns the current timestamp format
       * 
       * @return		the current format
       */
      public String getFormat() {
        return m_Format;
      }
      
      /**
       * returns the associated date/time
       * 
       * @return		the timestamp value
       */
      public Date getStamp() {
        return m_Stamp;
      }
      
      /**
       * returns the timestamp as string in the specified format
       * 
       * @return		the timestamp as string
       */
      public String toString() {
        return m_Formatter.format(getStamp());
      }
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
    
    /**
     * A little, simple helper class for logging stuff. Uses simple file access
     * and not the java.util.logging stuff (see Log for that). Uses the 
     * writeToFile methods of the Debug class.
     * 
     * @see Debug.Log
     * @see Debug#writeToFile(String, String)
     * @see Debug#writeToFile(String, String, boolean)
     */
    public static class SimpleLog 
      implements Serializable, RevisionHandler {
      
      /** for serialization */
      private static final long serialVersionUID = -2671928223819510830L;
      
      /** the file to write to (if null then only stdout is used) */
      protected String m_Filename = null;
      
      /**
       * default constructor, uses only stdout
       */
      public SimpleLog() {
        this(null);
      }
      
      /**
       * Creates a logger that writes into the specified file. Appends to the 
       * file by default.
       * 
       * @param filename	the file to write to, if null then only stdout is used
       */
      public SimpleLog(String filename) {
        this(filename, true);
      }
      
      /**
       * Creates a logger that writes into the specified file. Appends to the 
       * file by default.
       * 
       * @param filename	the file to write to, if null then only stdout is used
       * @param append	if false, the file will be deleted first
       */
      public SimpleLog(String filename, boolean append) {
        super();
        
        m_Filename = filename;
        
        Debug.writeToFile(m_Filename, "--> Log started", append);
      }
      
      /**
       * returns the filename of the log, can be null
       * 
       * @return		the filename of the log
       */
      public String getFilename() {
        return m_Filename;
      }
      
      /**
       * logs the given message to the file
       * 
       * @param message	the message to log
       */
      public void log(String message) {
        String	log;
        
        log = new Timestamp() + " " + message;
        
        if (getFilename() != null)
  	Debug.writeToFile(getFilename(), log);
  
        System.out.println(log);
      }
      
      /**
       * a convenience method for dumping the current system info in the 
       * log file
       * 
       * @see SystemInfo
       */
      public void logSystemInfo() {
        log("SystemInfo:\n" + new SystemInfo().toString());
      }
      
      /**
       * returns a string representation of the logger
       * 
       * @return		a string representation of the logger
       */
      public String toString() {
        String	result;
        
        result =   "Filename: " + getFilename();
        
        return result;
      }
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
    
    /**
     * A helper class for logging stuff. Uses the java.util.logging
     * package. If this approach seems an "overkill" (it can create quite a few 
     * log files if used in different threads), one can use the 
     * Debug.SimpleLog class.
     *
     * @author FracPete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 7517 $ 
     * @see Debug.SimpleLog
     */
    public static class Log
      implements Serializable, RevisionHandler {
      
      /** for serialization */
      private static final long serialVersionUID = 1458435732111675823L;
  
      /** the actual logger, if null only stdout is used */
      protected transient Logger m_Logger = null;
      
      /** the filename, if any */
      protected String m_Filename = null;
      
      /** the size of the file (in bytes) */
      protected int m_Size;
      
      /** the number of files for rotating the logs */
      protected int m_NumFiles;
  
      /** whether the initialization of the logger failed */
      protected boolean m_LoggerInitFailed = false;
      
      /**
       * default constructor, uses only stdout
       */
      public Log() {
        this(null);
      }
      
      /**
       * creates a logger that logs into the specified file, if null then only
       * stdout is used. It uses 1,000,000 bytes for file size and 1 file.
       * 
       * @param filename	the file to log into
       */
      public Log(String filename) {
        this(filename, 1000000, 1);
      }
      
      /**
       * creates a logger that logs into the specified file, if null then only
       * stdout is used.
       * 
       * @param filename	the file to log into
       * @param size	the size of the files in bytes
       * @param numFiles	the number of files for rotating
       */
      public Log(String filename, int size, int numFiles) {
        m_Filename = filename;
        m_Size     = size;
        m_NumFiles = numFiles;
      }
      
      /**
       * initializes and returns the logger if necessary (e.g., due to 
       * serialization).
       * 
       * @return		the logger, can be null, e.g., if no filename provided
       */
      protected Logger getLogger() {
        if ( (m_Logger == null) && (!m_LoggerInitFailed) ) {
  	if (m_Filename != null) {
  	  m_Logger = Logger.getLogger(m_Filename);
  	  Handler fh = null;
  	  try{	     
  	    fh = new FileHandler(m_Filename, m_Size, m_NumFiles);
  	    fh.setFormatter(new SimpleFormatter());
  	    m_Logger.addHandler(fh);      
  	    m_LoggerInitFailed = false;
  	  }
  	  catch(Exception e) {
  	    System.out.println("Cannot init fileHandler for logger:" + e.toString());
  	    m_Logger = null;
  	    m_LoggerInitFailed = true;
  	  }  
  	}
        }
        
        return m_Logger;
      }
      
      /**
       * turns the string representing a level, e.g., "FINE" or "ALL" into
       * the corresponding level (case-insensitive). The default is ALL.
       * 
       * @param level	the string to return a level for
       * @return		the corresponding level or the default
       */
      public static Level stringToLevel(String level) {
        Level	result;
        
        if (level.equalsIgnoreCase("ALL"))
          result = ALL;
        else if (level.equalsIgnoreCase("CONFIG"))
          result = CONFIG;
        else if (level.equalsIgnoreCase("FINE"))
          result = FINE;
        else if (level.equalsIgnoreCase("FINER"))
          result = FINER;
        else if (level.equalsIgnoreCase("FINEST"))
          result = FINEST;
        else if (level.equalsIgnoreCase("INFO"))
          result = INFO;
        else if (level.equalsIgnoreCase("OFF"))
          result = OFF;
        else if (level.equalsIgnoreCase("SEVERE"))
          result = SEVERE;
        else if (level.equalsIgnoreCase("WARNING"))
          result = WARNING;
        else
          result = ALL;
        
        return result;
      }
      
      /**
       * returns the filename of the log, can be null
       * 
       * @return		the filename of the log
       */
      public String getFilename() {
        return m_Filename;
      }
      
      /**
       * returns the size of the files
       * 
       * @return		the size of a file
       */
      public int getSize() {
        return m_Size;
      }
      
      /**
       * returns the number of files being used
       * 
       * @return		the number of files
       */
      public int getNumFiles() {
        return m_NumFiles;
      }
  
      /**
       * logs the given message
       * 
       * @param level	the level of severity
       * @param message	the message to log
       */
      public void log(Level level, String message) {
        log(level, "", message);
      }
      
      /**
       * prints the given message with the specified level
       * 
       * @param level	the level of logging
       * @param sourceclass	the class that logs the message
       * @param message	the message to print
       */
      public void log(Level level, String sourceclass, String message) {
        log(level, sourceclass, "", message);
      }
      
      /**
       * prints the given message with the specified level
       * 
       * @param level		the level of logging
       * @param sourceclass		the class that logs the message
       * @param sourcemethod	the method that logs the message
       * @param message		the message to print
       */
      public void log(Level level, String sourceclass, String sourcemethod, String message) {
        Logger	logger;
        
        logger = getLogger();
        
        if (logger != null)
          logger.logp(level, sourceclass, sourcemethod, message);
        else
  	System.out.println(message);
      }
      
      /**
       * a convenience method for dumping the current system info in the 
       * log file
       * 
       * @see SystemInfo
       */
      public void logSystemInfo() {
        log(INFO, "SystemInfo:\n" + new SystemInfo().toString());
      }
      
      /**
       * returns a string representation of the logger
       * 
       * @return		a string representation of the logger
       */
      public String toString() {
        String	result;
        
        result =   "Filename: " + getFilename() + ", "
        	       + "Size: " + getSize() + ", "
        	       + "# Files: " + getNumFiles();
        
        return result;
      }
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
  
    /**
     * This extended Random class enables one to print the generated random
     * numbers etc., before they are returned. It can either use stdout (default)
     * for outputting the logging information or a Log object (level is then 
     * INFO).
     *
     * @author  FracPete (fracpete at waikato dot ac dot nz)
     * @version $Revision: 7517 $
     */
    public static class Random
      extends java.util.Random
      implements Serializable, RevisionHandler {
  
      /** for serialization */
      private static final long serialVersionUID = 1256846887618333956L;
  
      /** whether to output debug information */
      protected boolean m_Debug = false;
  
      /** the unique ID for this number generator */
      protected long m_ID;
      
      /** for keeping track of unique IDs */
      protected static long m_CurrentID;
      
      /** the log to use for outputting the data, otherwise just stdout */
      protected Log m_Log = null;
      
      /**
       * Creates a new random number generator. With no debugging.
       */
      public Random() {
        this(false);
      }
  
      /**
       * Creates a new random number generator using a single long seed.
       * With no debugging
       * 
       * @param seed	the seed value
       */
      public Random(long seed) {
        this(seed, false);
      }
  
      /**
       * Creates a new random number generator. With optional debugging.
       * 
       * @param debug	if true, debugging output is enabled
       */
      public Random(boolean debug) {
        super();
        setDebug(debug);
        m_ID = nextID();
        if (getDebug())
          printStackTrace();
      }
  
      /**
       * Creates a new random number generator using a single long seed.
       * With optional debugging
       * 
       * @param seed	the seed value
       * @param debug	if true, debugging output is enabled
       */
      public Random(long seed, boolean debug) {
        super(seed);
        setDebug(debug);
        m_ID = nextID();
        if (getDebug())
          printStackTrace();
      }
  
      /**
       * sets whether to print the generated random values or not
       * 
       * @param value	if true debugging output is enabled
       */
      public void setDebug(boolean value) {
        m_Debug = value;
      }
  
      /**
       * returns whether to print the generated random values or not
       * 
       * @return		true if debugging output is enabled
       */
      public boolean getDebug() {
        return m_Debug;
      }
      
      /**
       * the log to use, if it is null then stdout is used
       * 
       * @param value	the log to use
       */
      public void setLog(Log value) {
        m_Log = value;
      }
      
      /**
       * the currently used log, if null then stdout is used for outputting
       * the debugging information
       * 
       * @return		the log, can be null
       */
      public Log getLog() {
        return m_Log;
      }
  
      /**
       * returns the next unique ID for a number generator
       * 
       * @return		the next unique ID
       */
      protected static long nextID() {
        m_CurrentID++;
        
        return m_CurrentID;
      }
  
      /**
       * returns the unique ID of this number generator
       * 
       * @return		the unique ID of this number generator
       */
      public long getID() {
        return m_ID;
      }
  
      /**
       * prints the given message only if m_Debug is TRUE
       * 
       * @param msg	the message to print
       * @see 		#m_Debug
       */
      protected void println(String msg) {
        if (getDebug()) {
  	if (getLog() != null)
  	  getLog().log(Level.INFO, m_ID + ": " + msg);
  	else
  	  System.out.println(m_ID + ": " + msg);
        }
      }
  
      /**
       * prints the current stacktrace
       */
      public void printStackTrace() {
        Throwable		t;
        StringWriter	writer;
  
        writer = new StringWriter();
        
        // generate stacktrace
        t = new Throwable();
        t.fillInStackTrace();
        t.printStackTrace(new PrintWriter(writer));
  
        println(writer.toString());
      }
  
      /**
       * Returns the next pseudorandom, uniformly distributed boolean value from
       * this random number generator's sequence.
       * 
       * @return		random boolean
       */
      public boolean nextBoolean() {
        boolean result = super.nextBoolean();
        println("nextBoolean=" + result);
        return result;
      }
  
      /**
       * Generates random bytes and places them into a user-supplied byte array.
       * 
       * @param bytes	array to fill with random bytes
       */
      public void nextBytes(byte[] bytes) {
        super.nextBytes(bytes);
        println("nextBytes=" + Utils.arrayToString(bytes));
      }
  
      /**
       * Returns the next pseudorandom, uniformly distributed double value between
       * 0.0 and 1.0 from this random number generator's sequence.
       * 
       * @return		random double
       */
      public double nextDouble() {
        double result = super.nextDouble();
        println("nextDouble=" + result);
        return result;
      }
  
      /**
       * Returns the next pseudorandom, uniformly distributed float  value between
       * 0.0 and 1.0 from this random number generator's sequence.
       * 
       * @return		random float
       */
      public float nextFloat() {
        float result = super.nextFloat();
        println("nextFloat=" + result);
        return result;
      }
  
      /**
       * Returns the next pseudorandom, Gaussian ("normally") distributed double
       * value with mean 0.0 and standard deviation 1.0 from this random number
       * generator's sequence.
       * 
       * @return		random double, gaussian distributed
       */
      public double nextGaussian() {
        double result = super.nextGaussian();
        println("nextGaussian=" + result);
        return result;
      }
  
      /**
       * Returns the next pseudorandom, uniformly distributed int  value from this
       * random number generator's sequence.
       * 
       * @return		random int
       */
      public int nextInt() {
        int result = super.nextInt();
        println("nextInt=" + result);
        return result;
      }
  
      /**
       * Returns a pseudorandom, uniformly distributed int value between 0
       * (inclusive) and the specified value (exclusive), drawn from this random
       * number generator's sequence.
       * 
       * @param n		the upper limit (exclusive)
       * @return		random int
       */
      public int nextInt(int n) {
        int result = super.nextInt(n);
        println("nextInt(" + n + ")=" + result);
        return result;
      }
  
      /**
       * Returns the next pseudorandom, uniformly distributed long  value from this
       * random number generator's sequence.
       * 
       * @return		random long
       */
      public long nextLong() {
        long result = super.nextLong();
        println("nextLong=" + result);
        return result;
      }
  
      /**
       * Sets the seed of this random number generator using a single long seed.
       * 
       * @param seed	the seed value
       */
      public void setSeed(long seed) {
        super.setSeed(seed);
        println("setSeed(" + seed + ")");
      }
  
      /**
       * returns a string representation of this number generator
       * 
       * @return		a string representation
       */
      public String toString() {
        return this.getClass().getName() + ": " + getID();
      }
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
    /**
     * contains debug methods
     *
     * @author Gabi Schmidberger (gabi at cs dot waikato dot ac dot nz)
     * @version $Revision: 7517 $
     */
    public static class DBO 
      implements Serializable, RevisionHandler {
  
      /** for serialization */
      static final long serialVersionUID = -5245628124742606784L;  
  
      /** enables/disables output of debug information */
      public boolean m_verboseOn = false;
  
      /** range of outputtyp */
      public Range m_outputTypes = new Range();
  
      /** 
       * Set the verbose on flag on
       */
      public void setVerboseOn() {
        m_verboseOn = true;
      }
  
      /** 
       * Initialize ranges, upper limit must be set
       * 
       * @param upper upper limit
       */
      public void initializeRanges(int upper) {
        m_outputTypes.setUpper(upper);
      }
  
      /**
       * Return true if the outputtype is set
       * 
       * @param num value that is reserved for a specific outputtype
       * @return return true if the output type is set
       */
      public boolean outputTypeSet(int num) {
        return (m_outputTypes.isInRange(num));
      }
  
       /**
       * Return true if the debug level is set
       * same method as outpuTypeSet but better name
       * 
       * @param num value that is reserved for a specific outputtype
       * @return return true if the debug level is set
       */
      public boolean dl(int num) {
        return (outputTypeSet(num));
      }
  
     /**
       * Switches the outputs on that are requested from the option O
       * 
       * @param list list of integers, all are used for an output type
       */
      public void setOutputTypes(String list) {
        if (list.length() > 0) {
          m_verboseOn = true; 
  
          m_outputTypes.setRanges(list);
          m_outputTypes.setUpper(30);
        }
      }
  
      /**
       * Gets the current output type selection
       *
       * @return a string containing a comma separated list of ranges
       */
      public String getOutputTypes() {
        return m_outputTypes.getRanges();
      }
  
      /**
       * prints out text + endofline if verbose is on.
       * helps to make debug output commands more visible in text
       * 
       * @param text the text to print
       */
      public void dpln(String text) {
        if (m_verboseOn) {
          System.out.println(text);
        }
      } 
  
      /**
       * prints out text + endofline but only if parameter debug type is set.
       * helps to make debug output commands more visible in text
       *
       * @param debugType the type of the output
       * @param text the text to print
       */
      public void dpln(int debugType, String text) {
        if (outputTypeSet(debugType)) {
          System.out.println(text);
        }
      } 
  
       /**
       * prints out text  if verbose is on.
       * helps to make debug output commands more visible in text
       * 
       * @param text the text to print
       */
      public void dp(String text) {
        if (m_verboseOn) {
          System.out.print(text);
        }
      } 
  
     /**
       * prints out text but only if debug level is set.
       * helps to make debug output commands more visible in text
       *
       * @param debugType the type of the output
       * @param text the text to print
       */
      public void dp(int debugType, String text) {
       if (outputTypeSet(debugType)) {
          System.out.print(text);
        }
      } 
  
      /**
       * prints out text + endofline.
       * helps to make debug output commands more visible in text
       * 
       * @param text the text to print
       */
      public static void pln(String text) {
        System.out.println(text);
      } 
  
      /**
       * prints out text.
       * helps to make debug output commands more visible in text
       * 
       * @param text the text to print
       */
      public static void p (String text) {
        System.out.print(text);
      } 
      
      /**
       * Returns the revision string.
       * 
       * @return		the revision
       */
      public String getRevision() {
        return RevisionUtils.extract("$Revision: 7517 $");
      }
    }
    
    /**
     * default constructor, prints only to stdout
     */
    public Debug() {
      this(null);
    }
    
    /**
     * logs the output to the specified file (and stdout). Size is 1,000,000 bytes 
     * and 1 file.
     * 
     * @param filename	the name of the log
     */
    public Debug(String filename) {
      this(filename, 1000000, 1);
    }
    
    /**
     * logs the output
     * 
     * @param filename	the name of the log
     * @param size	the size of the files in bytes
     * @param numFiles	the number of files for rotating
     */
    public Debug(String filename, int size, int numFiles) {
      super();
      
      m_Log = newLog(filename, size, numFiles);
    }
    
    /**
     * turns the string representing a level, e.g., "FINE" or "ALL" into
     * the corresponding level (case-insensitive). The default is ALL.
     * 
     * @param level	the string to return a level for
     * @return		the corresponding level or the default
     */
    public static Level stringToLevel(String level) {
      return Log.stringToLevel(level);
    }
    
    /**
     * returns a new Log instance
     * 
     * @param filename	the name of the log
     * @param size	the size of the files in bytes
     * @param numFiles	the number of files for rotating
     * @return		the log instance
     */
    public static Log newLog(String filename, int size, int numFiles) {
      return new Log(filename, size, numFiles);
    }
    
    /**
     * prints the given message with level INFO
     * 
     * @param message	the message to print
     */
    public void log(String message) {
      log(INFO, message);
    }
    
    /**
     * prints the given message with the specified level and an empty sourceclass
     * 
     * @param level	the level of logging
     * @param message	the message to print
     */
    public void log(Level level, String message) {
      log(level, "", message);
    }
    
    /**
     * prints the given message with the specified level
     * 
     * @param level	the level of logging
     * @param sourceclass	the class that logs the message
     * @param message	the message to print
     */
    public void log(Level level, String sourceclass, String message) {
      log(level, sourceclass, "", message);
    }
    
    /**
     * prints the given message with the specified level
     * 
     * @param level		the level of logging
     * @param sourceclass		the class that logs the message
     * @param sourcemethod	the method that logs the message
     * @param message		the message to print
     */
    public void log(Level level, String sourceclass, String sourcemethod, String message) {
      if (getEnabled())
        m_Log.log(level, sourceclass, sourcemethod, message);
    }
    
    /**
     * sets whether the logging is enabled or not
     * 
     * @param value	if true logging will be enabled
     */
    public void setEnabled(boolean value) {
      m_Enabled = value;
    }
    
    /**
     * returns whether the logging is enabled
     * 
     * @return		true if the logging is enabled
     */
    public boolean getEnabled() {
      return m_Enabled;
    }
    
    /**
     * returns a new instance of a clock
     * 
     * @return		a new instance of a Clock
     */
    public static Clock newClock() {
      return new Clock();
    }
    
    /**
     * returns the instance of the Clock that is internally used
     * 
     * @return		the clock that's being used
     */
    public Clock getClock() {
      return m_Clock;
    }
    
    /**
     * starts the clock
     */
    public void startClock() {
      m_Clock.start();
    }
    
    /**
     * stops the clock and prints the message associated with the time, but only
     * if the logging is enabled.
     * 
     * @param message	the message to print
     * @see		#getEnabled()
     */
    public void stopClock(String message) {
      log(message + ": " + m_Clock);
    }
    
    /**
     * returns a default debug random object, with no particular seed and 
     * debugging enabled.
     * 
     * @return		a new instance of a Random object
     */
    public static java.util.Random newRandom() {
      return new Random(true);
    }
    
    /**
     * returns a debug random object with the specified seed and debugging 
     * enabled.
     * 
     * @param seed	the seed value
     * @return		a new instance of a Random object
     */
    public static java.util.Random newRandom(int seed) {
      return new Random(seed, true);
    }
  
    /**
     * returns a default timestamp for the current date/time
     * 
     * @return		a new timestamp
     */
    public static Timestamp newTimestamp() {
      return new Timestamp();
    }
    
    /**
     * returns the system temp directory
     * 
     * @return		the temp directory
     */
    public static String getTempDir() {
      return System.getProperty("java.io.tmpdir");
    }
    
    /**
     * returns the home directory of the user
     * 
     * @return		the user's home directory
     */
    public static String getHomeDir() {
      return System.getProperty("user.home");
    }
    
    /**
     * returns the current working directory of the user
     * 
     * @return		the user's current working directory
     */
    public static String getCurrentDir() {
      return System.getProperty("user.dir");
    }
    
    /**
     * Writes the given object to the specified file. The string representation
     * of the object is appended to the file.
     * 
     * @param filename	the file to write to
     * @param obj		the object to write to the file
     * @return		true if writing was successful
     */
    public static boolean writeToFile(String filename, Object obj) {
      return writeToFile(filename, obj, true);
    }
    
    /**
     * Writes the given message to the specified file. The message is appended 
     * to the file.
     * 
     * @param filename	the file to write to
     * @param message	the message to write
     * @return		true if writing was successful
     */
    public static boolean writeToFile(String filename, String message) {
      return writeToFile(filename, message, true);
    }
    
    /**
     * Writes the given object to the specified file. The string representation 
     * of the object is either appended or replaces the current content of the 
     * file.
     * 
     * @param filename	the file to write to
     * @param obj		the object to write to the file
     * @param append	whether to append the message or not
     * @return		true if writing was successful
     */
    public static boolean writeToFile(String filename, Object obj, boolean append) {
      return writeToFile(filename, obj.toString(), append);
    }
    
    /**
     * Writes the given message to the specified file. The message is either 
     * appended or replaces the current content of the file.
     * 
     * @param filename	the file to write to
     * @param message	the message to write
     * @param append	whether to append the message or not
     * @return		true if writing was successful
     */
    public static boolean writeToFile(String filename, String message, boolean append) {
      boolean		result;
      BufferedWriter	writer;
      
      try {
        writer = new BufferedWriter(new FileWriter(filename, append));
        writer.write(message);
        writer.newLine();
        writer.flush();
        writer.close();
        result = true;
      }
      catch (Exception e) {
        result = false;
      }
      
      return result;
    }
    
    /**
     * writes the serialized object to the speicified file
     * 
     * @param filename	the file to serialize the object to
     * @param o		the object to serialize
     * @return		true if writing was successful
     */
    public static boolean saveToFile(String filename, Object o) {
      boolean 	result;
      
      if (SerializationHelper.isSerializable(o.getClass())) {
        try {
  	SerializationHelper.write(filename, o);
  	result = true;
        }
        catch (Exception e) {
  	result = false;
        }
      }
      else {
        result = false;
      }
      
      return result;
    }
    
    /**
     * deserializes the content of the file and returns it, null if an error
     * occurred.
     * 
     * @param filename	the name of the file to deserialize
     * @return		the deserialized content, null if problem occurred
     */
    public static Object loadFromFile(String filename) {
      Object	result;
      
      try {
        result = SerializationHelper.read(filename);
      }
      catch (Exception e) {
        result = null;
      }
      
      return result;
    }
    
    /**
     * Returns the revision string.
     * 
     * @return		the revision
     */
    public String getRevision() {
      return RevisionUtils.extract("$Revision: 7517 $");
    }
  }
  