/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * 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, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.compiler.wrapper;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.StringTokenizer;
import java.util.Vector;

import edu.udo.cs.miningmart.compiler.utils.DrawSample;
import edu.udo.cs.miningmart.db.CompilerDatabaseService;
import edu.udo.cs.miningmart.db.DB;
import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.RelationalDatatypes;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * This abstract class is the super class for both SVM_CL and SVM_RG. It implements
 * all their methods. The subclass SVM_CL is used to apply the SVM for classification.
 * The subclass SVM_RG is used for regression. The attribute "forClassification" in this class
 * distinguishes which mode is used; it is set by the subclasses in their
 * constructors.
 *
 * @see edu.udo.cs.miningmart.m4.core.operator.SVM_CL
 * @see edu.udo.cs.miningmart.m4.core.operator.SVM_RG
 * 
 * @author Timm Euler
 * @version $Id: SupportVectorMachine.java,v 1.5 2006/04/11 14:10:17 euler Exp $
 */
abstract class SupportVectorMachine extends SVM_Wrapper
{
    private String svmCommand;        // command line to call external algo
    private String tempDir;		      // a directory name for the temporary model file
    protected String svmOutput;       // what mySVM outputs

    /**
     * Constructor. The connections to the databases are needed because the
     * SVM needs to read metadata as well as data and create an
     * intermediate table in the database.
     *
     * @param databaseObj the DB object to be used for database connections
     * @param printObj the Print object to be used for log messages
     * @param nameForDatabaseUse Prefix for all functions, view and tables that
     * 	      this wrapper creates in the database
     * @param nameOfDatabaseSchema Name of the database schema that holds the business 
     *        views and tables
     * @param stepId Id of the Step
     * @param sampleSize the maximum number of examples to be used for training;
     * 		  they are selected randomly from the database
     *
     * @throws M4CompilerError A simple exception object with an error message.
     */
    public SupportVectorMachine( CompilerDatabaseService databaseObj, 
    							 Print printObj, 
    							 String nameForDatabaseUse,
    							 String nameOfDatabaseSchema,
    							 long stepId, 
    							 long sampleSize)
    	throws M4CompilerError
    {
    	super(databaseObj, printObj, nameForDatabaseUse, nameOfDatabaseSchema, stepId);
    	
    	this.sampleSize = sampleSize;
    	
        // Get working directory from environment variable:
	    String path = System.getProperty("ML_HOME");

        // svmParametersFileName = path + "/etc/mySVM_kernel_parameters.txt";

		String osName = System.getProperty("os.name");
		if (osName.substring(0, 3).equalsIgnoreCase("win")) {
			osName ="Windows";
		}
        if (osName.equals("SunOS") || osName.equals("Linux") || osName.equals("Windows"))
	    {
	    	String s = File.separator;
		    this.svmCommand = path + "bin" + s + osName + s + "mysvm";
		    this.tempDir = System.getProperty("TEMP_DIR");
	    }
        else
        {
           throw new M4CompilerError(
				 "SVM-Wrapper: current implementation works only on SunOS. Install\n" +
				 "mySVM for your Operating System and change the constructor in\n" +
				 "'SupportVectorMachine.java' (set the mySVM-command path) to adapt\n" +
				 "the implementation.\n" +
				 "Your operating system is: " + System.getProperty("os.name")
				 );
        }
    } // end constructor

    /**
     * This is the wrapper. This method is used to call the external mySVM
     * algorithm for training with the specified parameters. After successful
     * execution, an SQL-Function exists in the database that implements the
     * learned SVM model. The name for this function is given in the parameter
     * 'nameForDatabaseUse'.
     *
     * @param inputColumnset ColumnSet in the database
     *        that belongs to the input Concept of the
     *        operator that uses this wrapper. The Columns that the SVM learns
     *        from belong to this ColumnSet.
     * @param targetColumn the Column in the database that
     *        belongs to the target attribute of the operator that uses this
     *        wrapper.
     * @param conceptId Unique M4 Id of the input Concept of the operator that
     *        uses this wrapper.
     * @param sampleSize Maximum number of training examples to be read from
     *        the database.
     * @param c The parameter C for the support vector machine.
     * @param kernelType One of "dot", "polynomial", "radial", "neural" or "anova".
     * @param epsilon The parameter epsilon for the support vector machine.
     * @param thePredictingColumns A Vector with the Columns in the database that the support
     *        vector machine will use as learning attributes.
     * @param nameForDatabaseUse This String will be extended by this wrapper
     *        with "_T" to get a name for the database table that the learned SVM
     *        model needs. Further, to get a name for the SQL function implementing
     *        the learned model, this String is used with the extension "_FCT".
     * @return TRUE if a table was created in the DB, FALSE if not. A function
     *          is always created if there was no error.
     * @throws M4CompilerError A simple exception object with an error message.
     */
    public void callSVM( Columnset inputColumnset,
			             Column targetColumn,
		                 long conceptId,
	                     String c,
			             String kernelType,
			             String epsilon,
			             Vector thePredictingColumns
			           ) 
		  throws M4CompilerError
    {
        // The wrapper has the following tasks.

        // Step 1. To be able to read the training data, 
        //         check and convert datatypes where the learning algo requires it.

        // add columns which need to be converted to a Vector:

        // final Vector convertThisColumn = new Vector();
        
        checkForConversion( inputColumnset,
                            thePredictingColumns,
                            targetColumn);
        
        String columnsToSelect = this.getCompleteSelectString();
        
        if (columnsToSelect == null)
	    {   throw new M4CompilerError("Error in SVM Wrapper: no columns to select from business data.");  }
	    
	    // Draw a sample of the data:
		final String ttable = SAMPLE_TABLE_PREFIX + this.myStepId;
		
		// construct Vector and String of list of column names:
		Vector selectedCols = new Vector();
	    for (int i = 0; i < this.theColumnInfos.length; i++) {
		   	String cstring = this.theColumnInfos[i].getSelectString();
		   	selectedCols.add(cstring.toUpperCase());
		}			
        
        // write sample of selected attributes to a new table:
		new DrawSample( inputColumnset,
			 			selectedCols,
						ttable,
			       		TEMP_TABLE_PREFIX + this.myStepId,
						null,
						sampleSize,
						null,
						this.getDatabaseObj()
			           );

		this.inputTableName = ttable;
		
		// Query to copy the sample from the temporary table to a flat file.
		// We do not have to rename the target attribute, even if it is encapsulated
		// by conversion functions, because all we want to check is that it is not null.
		String	sql_getData = "SELECT " + columnsToSelect
						  + " FROM " + ttable
						  + " WHERE " + targetColumn.getName() + " IS NOT NULL";

        // execute the query to get the data:
        ResultSet rs = null;
        try {
	        try {
			    rs = this.getDatabaseObj().executeBusinessSqlRead(sql_getData);
	    	}
	        catch (SQLException sqle)
		    {   throw new M4CompilerError("Error in SVM Wrapper when reading business data: " + sqle.getMessage());   }

        // Step 3. Call external learning algo, here: 'mySVM', with customized input.

	        OutputStream streamForDataInput;
    	    Process svmProcess = null;
			this.getPrint().doPrint(Print.OPERATOR, "Starting external algorithm (mySVM) (command: '" + svmCommand + "')...");
	        try
			{   
				String[] args = new String[] { this.svmCommand };
				svmProcess = Runtime.getRuntime().exec(args, null, new File(this.tempDir));
			}
			catch (IOException ioe)
			{
		    	if (svmProcess != null) {  svmProcess.destroy();  }
	            throw new M4CompilerError("Error accessing mySVM binary: " + ioe.getMessage());
		    }
			streamForDataInput = svmProcess.getOutputStream();
		
			try {
            	createSVM_Input(rs, kernelType, c, epsilon, streamForDataInput);
        	}
			catch (M4CompilerError mce) {
				svmProcess.destroy();
        	    throw mce;
	    	}
        
	        this.svmOutput = readStream(svmProcess.getInputStream());
    	    if ((this.svmOutput == null) || this.svmOutput.equals(""))
		    {  throw new M4CompilerError("Error trying to read mySVM-Output!");  }
    	    if (this.svmOutput.indexOf("* Error") > -1)
	    	{  throw new M4CompilerError("Error in SVM: " + this.svmOutput);  }
	        String svmErrors = readStream(svmProcess.getErrorStream());
    	    if ( ! svmErrors.equals(""))
        	{  throw new M4CompilerError("An error occurred in mySVM: " + svmErrors);  }

			this.getPrint().doPrint(Print.MIN, "\n--- SVM output ---");
			this.getPrint().doPrint(Print.MIN, this.svmOutput);
			this.getPrint().doPrint(Print.MIN, "------------------\n");
        }
        finally { // In any case:
			DB.closeResultSet(rs); // 1. Close the ResultSet!
			try { // 2. Remove the table temporarily holding the data sample!
				CompilerDatabaseService db = this.getDatabaseObj();
		    	db.dropBusinessTable(ttable);
			}
			catch (M4Exception m4e) {}
	    }
		
        // Step 4. Read output of external learning algo.        
        try {
            processSVMResult();
        }
        catch (Exception e)
	    {   throw new M4CompilerError("Wrapper for SVM: Error processing result of mySVM: " + e.getMessage());  }

		if (this.forClassification)   // no XiAlpha for regression
		{   this.extractXiAlpha();  }
		
        // Step 5. Create an SQL-function for the learned SVM decision function
        // and insert it into database under the prespecified name

        createDecisionFunctionAsSQL_Function(kernelType);
    } // end public void callSVM()

    // This method goes through the result file that is returned by mySVM and
    // enters its values into the database.
    private void processSVMResult()
				throws M4CompilerError
    {
        int numberOfLearnAttribs = this.theColumnInfos.length - 1;
        String[] xValues = new String[numberOfLearnAttribs];
	    
        String label, alpha;
        StringTokenizer tok;
        boolean tableExisted = true;
        int numberOfSupportVectors = 0;
        try
	    {
			// read mySVM result file:
			LineNumberReader lnr;
        	lnr = new LineNumberReader(new FileReader(this.tempDir + "mysvm.svm"));
			String line = lnr.readLine().trim();
			// skip parameter lines, but read b, the additive constant for the
			// decision function, into the global variable:
			while ((line != null) 
			       && 
			       ( ! 
			             ( Character.isDigit(line.charAt(0)) )
			             ||
			             ( (line.charAt(0) == '-') && (Character.isDigit(line.charAt(1))) )
			       ))
		    {
				if (line.startsWith("b "))
			    {
					try
				    {  this.b = Double.parseDouble(line.substring(1).trim());  }
					catch (NumberFormatException nfe)
				    {   throw new M4CompilerError("Error reading b from mySVM-Output: tried to convert " +
											    line.substring(1).trim() + " into double.");	 }				
					catch (IndexOutOfBoundsException ioube)
					{  throw new M4CompilerError("Error reading b from mySVM-Output: tried to read line " + line);  }
				}
				line = lnr.readLine();
		    }
		
			// try to drop the table to make sure it does not exist
			try {
				tableExisted = this.getDatabaseObj().dropBusinessTable(this.getModelTableName());			
			}
			catch (M4Exception m4e) {
				throw new M4CompilerError(m4e.getMessage());
			}
			
			// create the table
			String sql_createTable = "CREATE TABLE " + this.getModelTableName() + " (";
			// get the database specific NUMBER datatype
			String numbertype = this.getDBMS_Datatype(RelationalDatatypes.RELATIONAL_DATATYPE_NUMBER, null);
			for (int i = 0; i < numberOfLearnAttribs - 1; i++) {	
				sql_createTable += this.theColumnInfos[i].getColumnName() + " " + numbertype + ", ";
	    	}
			sql_createTable += this.theColumnInfos[numberOfLearnAttribs - 1].getColumnName() + 
			                   " " + numbertype + ",  Alpha " + numbertype + ")";

			try {
				this.getDatabaseObj().executeBusinessSqlWrite(sql_createTable);
		    }
			catch (SQLException sqle)
	    	{
				throw new M4CompilerError("Error trying to create the intermediate table, under name '" +
									      this.getModelTableName() + "': " + sqle.getMessage());
			}
	    	// add table to trash:
	    	this.getDatabaseObj().addTableToTrash(this.getModelTableName(), this.schema, this.myStepId);

			// scan through the SVM-resultfile and read those lines whose alpha-value is
			// not zero -> they are the support vectors
			String sql_insert;
			// int insertResult;
			while (line != null)
		    {
				tok = new StringTokenizer(line);
				// read the vector's x-values:
				for (int i = 0; i < numberOfLearnAttribs; i++)
			    {
					if ( ! tok.hasMoreTokens())
				    {   throw new M4CompilerError("Error reading the support vectors: wrong dimension!");  }
					xValues[i] = tok.nextToken();
					if ((xValues[i] = checkDouble(xValues[i])) == null)
				    {   throw new M4CompilerError("Error reading mySVM-Resultfile (reading an x-Value): expected double, found " + xValues[i]);  }
			    } 
				// read the vector's label:
				if ( ! tok.hasMoreTokens())
			    {   throw new M4CompilerError("Error reading the support vectors: wrong dimension!");  }
				label = tok.nextToken();
				if ((label = checkDouble(label)) == null)
			    {   throw new M4CompilerError("Error reading mySVM-Resultfile (reading a label): expected double, found " + label); }
				// read the vector's alpha-value:
				if ( ! tok.hasMoreTokens())
			    {   throw new M4CompilerError("Error reading the support vectors: wrong dimension!"); }
				alpha = tok.nextToken();
				if ((alpha = checkDouble(alpha)) == null)
			    {   throw new M4CompilerError("Error reading mySVM-Resultfile (reading an alpha): expected double, found " + alpha); }
		
				// if alpha is not zero, it is a support vector:
				if (Double.parseDouble(alpha) != 0.0)
			    {
					// enter xValues, label and alpha into database table
					sql_insert = "insert into " + this.getModelTableName() + " values (";
					for (int i = 0; i < numberOfLearnAttribs; i++)
				    {  sql_insert += xValues[i] + ", ";  }
				    
				    // don't need the label!:
					// sql_insert += label + ", " + alpha + ")";
					sql_insert += alpha + ")";
					
					try
				    {   this.getDatabaseObj().executeBusinessSqlWrite(sql_insert); }
					catch (SQLException sqle)
				    {   throw new M4CompilerError("Error trying to insert values into intermediate table '" + this.getModelTableName() + "': " +
											    sqle.getMessage()); }
					numberOfSupportVectors++;
			    } // end if (alpha not zero)
				line = lnr.readLine(); // next example vector
		    } // end while (scan through SVM-result)
		
	    }
        catch (IOException ioe)
	    {   throw new M4CompilerError("Error: Could not access SVM-file after training: " + ioe.getMessage());  }

		File f = new File(this.tempDir + "mysvm.svm");
		f.delete();

		this.noSV = numberOfSupportVectors;

        // commit insertions to database table
		try
		{   this.getDatabaseObj().commitBusinessTransactions();  }
		catch (SQLException sqle)
		{   throw new M4CompilerError("Error trying to commit the transactions for the intermediate table: " + sqle.getMessage());  }

		String message = "Found " + numberOfSupportVectors +
		    			 " support vectors, entered them into intermediate table in database.";
		if (tableExisted)
	    {   message += " Replaced ";   }
		else { message += " Created "; }
		message += "intermediate database table '" + this.getModelTableName() + "'.";
		this.getPrint().doPrint(Print.OPERATOR, message);	    
    } // end private void processSVMResult 

	/* implements the superclass method
	 */
	protected void extractNumberSV() 
	{   // the number of support vectors is already extracted
	}

    private void createSVM_Input( ResultSet rs,
                                  String kernelType,
                                  String c,
                                  String epsilon,
                                  OutputStream whereTo) throws M4CompilerError
    {
        // check if all parameters have suitable format:
        c = checkDouble(c);
        if (c == null) { throw new M4CompilerError("Wrapper for SVM: Parameter 'C' must be a real number!"); }
        if ( ! this.forClassification)
        {
	        this.lossPos = checkDouble(this.lossPos);
	        if (this.lossPos == null) { throw new M4CompilerError("Wrapper for SVM: Parameter 'LossFunctionPos' must be a real number!"); }
	        this.lossNeg = checkDouble(this.lossNeg);
	        if (this.lossNeg == null) { throw new M4CompilerError("Wrapper for SVM: Parameter 'LossFunctionNeg' must be a real number!"); }
        }
        if (epsilon != null)
	    {   epsilon = checkDouble(epsilon);
            if (epsilon == null) { throw new M4CompilerError("Wrapper for SVM: Parameter 'Epsilon' must be a real number!"); }
	    }
        checkKernel(kernelType);

        PrintStream output = new PrintStream(whereTo);

        output.println("\n@parameters");
        if (forClassification)
	    {   output.println("pattern");  }
        else
	    {   output.println("regression");
            output.println("epsilon " + epsilon);
	    }
        output.println("C " + c);
        if ( ! this.forClassification)
        {
	        output.println("L+ " + lossPos);
	        output.println("L- " + lossNeg);
        }

        output.println("\n@kernel");
        output.println("type " + kernelType.toLowerCase());
        
        addKernelParameters(kernelType, output);  

        output.println("\n@examples");
        output.println("format xy\n");

        // find out, for the columns that need to be converted, how this is to
        // be done:
        /*
		// ResultSetMetaData rsmd = rs.getMetaData();
		// numberOfColumns = rsmd.getColumnCount();
		// rsmd = null;
		numberOfColumns = convertColumn.size();
		// symbolsForBooleanValues = new String[2][numberOfColumns];
		String colId, posValue, negValue;
		for (int i = 0; i < numberOfColumns; i++)
		{
			if ((colId = (String) convertColumn.get(i)) != null)
			{
			    int colid = 0;
			    try
			    {  colid = Integer.parseInt(colId);  }
			    catch (NumberFormatException nfe)
			    {  throw new M4CompilerError("SVM-Wrapper: String '" + colId + "' was expected to be integer!"); }
			    		
			    Value[] vs = this.getDatabaseObj().getDistinctElements(colid);
			    if ((vs == null) || (vs.length != 2))
			    {  throw new M4CompilerError("SVM-Wrapper: Column with Id " + colId + 
			    		                         " is of type BINARY, but does not contain exactly two different values!");  }
					
				negValue = vs[0].getValue();
				posValue = vs[1].getValue();
				if (i == numberOfColumns - 1) // target column 
				{
					if ( ! posValue.equalsIgnoreCase(this.getPositiveTargetValue()))
					{   String a = posValue;
						posValue = negValue;
						negValue = a;
					}
					if ( ! posValue.equalsIgnoreCase(this.getPositiveTargetValue()))
					{  throw new M4CompilerError("SVM: Positive Target Value '" +
											     this.getPositiveTargetValue() +
											     "' not found in target attribute!");  }
				}
				symbolsForBooleanValues[0][i] = negValue;
				symbolsForBooleanValues[1][i] = posValue;						
			}
			else
			{
				// no conversion needed
				symbolsForBooleanValues[0][i] = "-1";
				symbolsForBooleanValues[1][i] = "1";
			}
		}// end for (loop through the columns)
		*/
		int noOfWrittenRows = 0;
		try	{
			String line, entry;
			while (rs.next()) {
				line = "";
				for (int i = 1; i <= this.theColumnInfos.length; i++) { // last column is target -> format xy
					entry = rs.getString(i);
					if (this.theColumnInfos[i - 1].mappingIsUsed()) {
						entry = this.theColumnInfos[i - 1].getMappedValue(entry);
					}
					if (entry != null) {
						line += entry + " ";
					}
					else {  
						throw new M4CompilerError("Error producing SVM-input: found unknown or NULL entry in column " +
                                               	  "with col_id = " + this.theColumnInfos[i - 1].getColumn().getId() + ": " + entry);
					}
				}
				output.println(line);
				noOfWrittenRows++;
			}
		}
		catch (SQLException sqle) {
			throw new M4CompilerError("Error accessing database query result: " + sqle.getMessage());
		}
		output.flush();
		output.close();
		if (noOfWrittenRows == 0) {
			throw new M4CompilerError("SVM wrapper: empty training set!");
		}
		this.getPrint().doPrint(Print.OPERATOR, "SVM read " + noOfWrittenRows + " examples.");
    } //end private void createSVM_Input

    // Tests if the given String is an allowed kernel type for mySVM.
    protected void checkKernel(String kern) throws M4CompilerError
    {
        if ( ! ( kern.equalsIgnoreCase("dot") ||
                 kern.equalsIgnoreCase("polynomial") ||
                 kern.equalsIgnoreCase("radial") ||
                 kern.equalsIgnoreCase("neural") ||
                 kern.equalsIgnoreCase("anova") ) )
	    {   throw new M4CompilerError("Support Vector Machine: unknown or disallowed kernel type: " + kern + "!");  }
        return;
    } // end private void checkKernel
    
    protected void checkColSetType(String type) throws M4CompilerError
    {  // nothing to be checked for this type of SVM
    }
    
    protected Vector getColNamesInput() {
    	Vector inputNames = new Vector(this.theColumnInfos.length - 1);
    	for (int i = 0; i < this.theColumnInfos.length - 1; i++) {
    		inputNames.add(this.theColumnInfos[i].getColumnName());
    	}
    	inputNames.trimToSize();
    	return inputNames;
    }
    
    protected String getModelTablePlusCondition()
    {
    	return this.getModelTableName();
    }

    // Reads the stream into a string
    private String readStream(InputStream in) throws M4CompilerError
    {
        StringBuffer sb;
        try
	    {
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		sb = new StringBuffer();
		String line = br.readLine();
		while (line != null)
		    {   sb.append(line + "\n");
		    line = br.readLine();
		    }
	    }
        catch (IOException ioe)
	    {   throw new M4CompilerError("SupportVectorMachine: could not read mySVM-Output");  }
        return sb.toString();
    } // end private String readStream

    // Reads the kernel parameters from the default vector and adds them
    // to the input stream for mySVM
    private void addKernelParameters(String kern, PrintStream output) {
        /*
        try
	    {
		    LineNumberReader lnr = new LineNumberReader(new FileReader(svmParametersFileName));
		    String line = lnr.readLine();
		    while ( (line != null) && ( ! line.startsWith("@" + kern.toLowerCase())) )
		    {   line = lnr.readLine();   }
		    if (line == null)
		    {   throw new M4CompilerError("Wrapper for SVM: kernel parameters for kernel type '"+kern+
				                          "' not found in file: " + svmParametersFileName);   }
		    else
		    {
			    line = lnr.readLine();
			    theKernelParams = new Vector();
		    }

		    while ((line != null) && ( ! ( line.trim().equals("") || line.startsWith("#"))))
		    {   output.println(line);
		        theKernelParams.add(line);
		        line = lnr.readLine();
		    }
            theKernelParams.trimToSize();
	    }
        catch (IOException ioe)
	    {   throw new M4CompilerError("Wrapper for SVM: error with file " + svmParametersFileName + ": " + ioe.getMessage());  }
        if (theKernelParams.isEmpty())
	    {   throw new M4CompilerError("Wrapper for SVM: no kernel parameters for kernel type '"+kern+
                                      "' found in file: " + svmParametersFileName);
	    }
        */

        // Implementation without parameter file:

		Vector theKernelParams = this.getKernelParams(kern);
        
        if (theKernelParams == null)
        {   return;   }
        
        for (int i = 0; i < theKernelParams.size(); i++)
        {   output.println((String) theKernelParams.get(i));   }
    } // end private void addKernelParameters    
} // end of class SupportVectorMachine
/*
 * Historie
 * --------
 * 
 * $Log: SupportVectorMachine.java,v $
 * Revision 1.5  2006/04/11 14:10:17  euler
 * Updated license text.
 *
 * Revision 1.4  2006/04/06 16:31:17  euler
 * Prepended license remark.
 *
 * Revision 1.3  2006/03/31 16:41:46  euler
 * *** empty log message ***
 *
 * Revision 1.2  2006/03/29 09:50:47  euler
 * Added installation robustness.
 *
 * Revision 1.1  2006/01/03 09:54:37  hakenjos
 * Initial version!
 *
 */
