public class Consts {

    // GENERALS
	public static final String SPACE = " ";
    public static final String TAB = "\t";
    public static final String COMMA_NOTE = ",";
	public static final String CLASSIFICATION_SPLITTER = "-";
	public static final String COLON_SPLITTER = ":";
    public static final String PARAMS_SPLITTER = ";";
	public static final String NEW_LINE = "line.separator";
    public static final int ERROR_NUMBER = -1;

    // DUMMY
    public static final int MIN_GAP_START_DUMMY = 3;
    public static final int MIN_GAP_END_DUMMY = 3;

    // VOWEL DURATION
    public static final int MIN_VOWEL = 9;  //min training set: 45 frames
	public static final int MAX_VOWEL = 92; //max training set: 459 frames
    public static final int MIN_GAP_START = 10;
    public static final int MIN_GAP_END = 10;

    public static final double MEAN_VOWEL_LENGTH = 41.787; //pre calculated from the training set
    public static final double STD_VOWEL_LENGTH = 12.918; //pre calculated from the training set
    public static final double MAX_VOWEL_LENGTH = 38; //pre calculated from the training set

    // MODELS NUMBER OF PARAMETERS
    public static final int PA_PARAMS_SIZE = 1; //The number params 4 PA
    public static final int SVM_PARAMS_SIZE = 2; //The number params 4 SVM
    public static final int DL_PARAMS_SIZE = 2; //The number params 4 Direct Loss
    public static final int CRF_PARAMS_SIZE = 2; //The number params 4 CRF
    public static final int RL_PARAMS_SIZE = 2; //The number params 4 Ramp Loss
    public static final int PL_PARAMS_SIZE = 6; //The number params 4 Probit Loss
    public static final int SP_PARAMS_SIZE = 0; //The number params 0 Perceptron
    public static final int ORBIT_PARAMS_SIZE = 2; //The number params 4 orbit
    public static final int MULTICLASS_REGECT_SIZE = 4; //The number params 4 multiclass regect

    // KERNEL DEFAULT PARAMETERS
    public static double SIGMA = 1;

    //OCR
    public static final String END_NOTE = "$";
} public interface Reader {
    ArrayList<ArrayList<String>> readFile(String path, String spliter);
    InstancesContainer readData(String path, String dataSpliter, String valueSpliter);
} public class InstancesContainer {

    ArrayList<Example> instances;
    ArrayList<ArrayList<String>> paths;
    int size;

    //C'tor
    public InstancesContainer() {
        instances = new ArrayList<Example>();
        paths = new ArrayList<ArrayList<String>>();
        size = 0;
    }

    //get the requested example
    public Example getInstance(int index)
    {
        try{
            return instances.get(index);
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    //======Setters and getters=====//
    public ArrayList<Example> getInstances() {
        return instances;
    }

    public void setInstances(ArrayList<Example> instances) {
        this.instances = instances;
        this.size = instances.size();
    }

    public ArrayList<ArrayList<String>> getPaths() {
        return paths;
    }

    public void setPaths(ArrayList<ArrayList<String>> paths) {
        this.paths = paths;
        this.size = paths.size();
    }


    public int getSize() {
        if(this.size != 0)
            return this.size;
        this.size = (instances.size() != 0) ? instances.size() : paths.size();
        return this.size;
    }
} public class Logger {

    private static final String directory = "log";
    private static final String log_file = "files.log";

    /**
     * Write the message into the standard output
     * @param message - the message to be written
     */
    public static void info(String message){
        System.out.print(System.getProperty(Consts.NEW_LINE) + message);
    }

    /**
     * Write the message into the standard output without new line at the end
     * @param message - the message to be written
     */
    public static void info_no_new_line(String message){
        System.out.print(message);
    }

    /**
     * Write the message into the standard output with the data and time attached
     * @param message - the message to be written
     */
    public static void infoTime(String message){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        if(message.isEmpty() || message.equals(""))
            message = "None,";
        System.out.print(System.getProperty(Consts.NEW_LINE) + message + " " + dateFormat.format(date));
    }

    /**
     * Writes only the data and time to the standard output
     */
    public static void time(){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        System.out.print(System.getProperty(Consts.NEW_LINE)  + dateFormat.format(date));
    }

    /**
     * Write the message into the standard output with the data and time attached
     * @param msg - the message to be written
     * @param example - a running id
     */
    public static void timeExampleStandard(String msg, int example){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        System.out.print(System.getProperty(Consts.NEW_LINE) + msg+example+", "+dateFormat.format(date));
    }

    /**
     * Write the message into the standard output as a progress bar
     * @param msg - the message to be written
     */
    public static void progressMessage(String msg){
        System.out.print("\r" + msg + " ");
    }

    /**
     * Write the message into the standard error
     * @param message - the message to be written
     */
    public static void error(String message){
        System.err.println("Error: "+message);
    }



    /**
     * Write the message into the files.log in the lod directory(if doesn't exists the program creates the dir)
     * @param message - tha message to ve written
     * @throws IOException
     */
    public static void log2File(String message) throws IOException
    {
        // create the log directory if does noe exists
        File dir = new File(directory);
        if(!dir.exists())
            dir.mkdir();

        // Create file stream
        File file = new File(directory+"/"+log_file);
        FileWriter fstream = new FileWriter(file,true);
        BufferedWriter out = new BufferedWriter(fstream);
        //writes the message
        out.write(message);
        out.write(System.getProperty(Consts.NEW_LINE));
        //close all the file descriptors
        out.close();
        fstream.close();
    }

    /**
     * clean the log directory
     */
    public static void clean(){
        File dir = new File(directory);
        if(dir.exists() && dir.isDirectory()) {
            String[]entries = dir.list();
            for(String s: entries){
                File currentFile = new File(dir.getPath(),s);
                currentFile.delete();
            }
            dir.delete();
        }

        dir.mkdir();
    }
} public class Vector extends HashMap<Integer, Double> implements Serializable{

    private static final long serialVersionUID = 1L;
    private int maxIndex;
    public Vector(){this.maxIndex = -1;}
    public Vector(Vector v){
        super(v);
    }

    @Override
    public Double put(Integer key, Double value){
        super.put(key, value);
        if(maxIndex < key) maxIndex = key;
        return value;
    }

    // getter
    public int getMaxIndex() {
        return maxIndex;
    }
    public void resetMaxIndex(){
        this.maxIndex = -1;
    }
} public class FeatureFunctionsVowelDuration implements IFeatureFunctions {

    int sizeOfVector = 42;
    final int offset_10 = 2;
    final int offset_20 = 4;

    final int win_size_5 = 1;
    final int win_size_10 = 2;
    final int win_size_15 = 3;
    final int win_size_20 = 4;
    final int win_size_25 = 5;
    final int win_size_30 = 6;
    final int win_size_40 = 8;
    final int win_size_50 = 10;
    final double NORMALIZE = 0.05;

    final int SHORT_TERM_ENERGY = 0;
    final int TOTAL_ENERGY = 1;
    final int LOW_ENERGY = 2;
    final int HIGH_ENERGY = 3;
    final int WIENER_ENTROPY = 4;
    final int AUTO_CORRELATION = 5;
    final int PITCH = 6;
    final int VOICING = 7;
    final int ZERO_CROSSING = 8;
    final int IS_VOWEL = 9;
    final int IS_NAZAL = 10;
    final int SUM_VOWELS = 13;
    final int MFCC_1 = 16;
    final int MFCC_2 = 17;
    final int MFCC_3 = 18;
    final int MFCC_4 = 19;

    @Override
    //return null on error
    public Example convert(Example example, String label, IKernel kernel) {

        try{
            Example newExample = Factory.getExample(0);

            String labelValues[] = label.split(Consts.CLASSIFICATION_SPLITTER);
            Vector phiFeatures = new Vector();

            //convert full vector
            if(ConverterHelplers.tryParseInt(labelValues[0])) {

                int start = Integer.parseInt(labelValues[0]);
                int end = Integer.parseInt(labelValues[1]);

                //=================calculate the features=================//
                //=========Difference 5 and 10 frames from location=======//
                int loc=0;

                //====Short Term Energy====//
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, SHORT_TERM_ENERGY));//short term energy, start location - 20 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_20, start, SHORT_TERM_ENERGY));//short term energy, start location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_25, start, SHORT_TERM_ENERGY));//short term energy, start location - 30 window
                loc++;

                phiFeatures.put(loc, calculateDiff(example, win_size_15, end, SHORT_TERM_ENERGY));//short term energy, end location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_20, end, SHORT_TERM_ENERGY));//short term energy, end location - 30 window
                loc++;

                //====Total Energy====//
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start, TOTAL_ENERGY)));//total energy, start location - 20 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start, TOTAL_ENERGY)));//total energy, start location - 30 window
                loc++;
                // offsets
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start - offset_10, TOTAL_ENERGY)));//total energy, start location - 20 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start - offset_20, TOTAL_ENERGY)));//total energy, start location - 30 window
                loc++;

                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, end, TOTAL_ENERGY)));//total energy, start location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, end, TOTAL_ENERGY)));//total energy, start location - 30 window
                loc++;


                //====Low Energy====//
                phiFeatures.put(loc, calculateDiff(example, win_size_30, start, LOW_ENERGY));//low energy, start location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, LOW_ENERGY));//low energy, start location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, LOW_ENERGY));//low energy, start location - 40 window
                loc++;
                // offsets
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start - offset_10, LOW_ENERGY)));//total energy, start location - 20 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start - offset_20, LOW_ENERGY)));//total energy, start location - 30 window
                loc++;

                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, LOW_ENERGY));//low energy, end location - 20 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, LOW_ENERGY));//low energy, end location - 30 window
                loc++;

                //====High Energy====//
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start, HIGH_ENERGY)));//high energy, end location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start, HIGH_ENERGY)));//high energy, end location - 30 window
                loc++;
                // offsets
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start - offset_10, HIGH_ENERGY)));//total energy, start location - 20 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start - offset_20, HIGH_ENERGY)));//total energy, start location - 30 window
                loc++;

                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, end, HIGH_ENERGY)));//high energy, end location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, end, HIGH_ENERGY)));//high energy, end location - 30 window
                loc++;

                //====Wiener Entropy====//
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, start, WIENER_ENTROPY)));//wiener entropy, start location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, start, WIENER_ENTROPY)));//wiener entropy, start location - 30 window
                loc++;

                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, end, WIENER_ENTROPY)));//wiener entropy, start location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, end, WIENER_ENTROPY)));//wiener entropy, start location - 30 window
                loc++;

                //===========Auto Correlation==========//
                phiFeatures.put(loc, calculateDiff(example, win_size_5, start, AUTO_CORRELATION));//auto correlation, start location - 5 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_10, start, AUTO_CORRELATION));//auto correlation, start location - 10 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, AUTO_CORRELATION));//auto correlation, start location - 15 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_20, start, AUTO_CORRELATION));//auto correlation, start location - 15 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_25, start, AUTO_CORRELATION));//auto correlation, start location - 15 window
                loc++;

                //==========Difference 5 and 15 frames from start=========//
                //====Pitch====//
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, PITCH));//pitch, start location - 20 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, PITCH));//pitch, start location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, PITCH));//pitch, start location - 20 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, PITCH));//pitch, start location - 30 window
                loc++;

                //====Voicing====//
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, VOICING));//voicing, start location - 15 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, VOICING));//voicing, start location - 20 window
                loc++;

                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, VOICING));//voicing, end location - 30 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, VOICING));//voicing, end location - 30 window
                loc++;

                //====Zero-Crossing====//
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, ZERO_CROSSING));//zero-crossing, start location - 20 window
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, ZERO_CROSSING));//zero-crossing, start location - 30 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_40, end, ZERO_CROSSING)));//zero-crossing, end location - 20 window
                loc++;
                phiFeatures.put(loc, Math.abs(calculateDiff(example, win_size_50, end, ZERO_CROSSING)));//zero-crossing, end location - 30 window
                loc++;

                //=================Phoneme-Classifier==================//
                //====VOWELS - INDICATOR====//
                phiFeatures.put(loc, calculateDiff(example, win_size_30, start, IS_VOWEL));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, IS_VOWEL));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, IS_VOWEL));
                loc++;


                phiFeatures.put(loc, calculateDiff(example, win_size_30, end, IS_VOWEL));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, IS_VOWEL));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, IS_VOWEL));
                loc++;

                //====NASAL - INDICATOR====//
                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, IS_NAZAL));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, IS_NAZAL));
                loc++;

                //====VOWELS - SUM DIVIDE BY SUM ALL====//
                phiFeatures.put(loc, calculateDiff(example, win_size_30, start, SUM_VOWELS));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, start, SUM_VOWELS));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, SUM_VOWELS));
                loc++;

                phiFeatures.put(loc, calculateDiff(example, win_size_30, end, SUM_VOWELS));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_40, end, SUM_VOWELS));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, SUM_VOWELS));
                loc++;

                //==== DELTA ====//
                // MFCC_1
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, MFCC_1));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, MFCC_1));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_15, end, MFCC_1));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, MFCC_1));
                loc++;

                // MFCC_2
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, MFCC_2));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, MFCC_2));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_15, end, MFCC_2));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, MFCC_2));
                loc++;

                // MFCC_3
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, MFCC_3));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, MFCC_3));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_15, end, MFCC_3));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, MFCC_3));
                loc++;

                // MFCC_4
                phiFeatures.put(loc, calculateDiff(example, win_size_15, start, MFCC_4));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, start, MFCC_4));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_15, end, MFCC_4));
                loc++;
                phiFeatures.put(loc, calculateDiff(example, win_size_50, end, MFCC_4));
                loc++;

                //==============Mean value from start to end==============//
                //true means prev the start point, false means after the end point
                phiFeatures.put(loc, calculateMean(example, start, end, SHORT_TERM_ENERGY, win_size_50, true));//Mean of short-term energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, SHORT_TERM_ENERGY, win_size_50, false));//Mean of short-term energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, TOTAL_ENERGY, win_size_50, true));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, TOTAL_ENERGY, win_size_50, false));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, HIGH_ENERGY, win_size_50, true));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, HIGH_ENERGY, win_size_50, false));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, LOW_ENERGY, win_size_50, true));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, LOW_ENERGY, win_size_50, false));//Mean of low energy
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, VOICING, win_size_50, true));//Mean of voicing
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, VOICING, win_size_50, false));//Mean of voicing
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, ZERO_CROSSING, win_size_50, true));//Mean of zero-crossing
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, ZERO_CROSSING, win_size_50, false));//Mean of zero-crossing
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, SUM_VOWELS, win_size_50, true));
                loc++;
                phiFeatures.put(loc, calculateMean(example, start, end, SUM_VOWELS, win_size_50, false));
                loc++;

                // ==== MAX FUNCTION ==== //
                phiFeatures.put(loc, example.getFeatures2D().get(start).get(TOTAL_ENERGY));
                loc++;
                phiFeatures.put(loc, example.getFeatures2D().get(start).get(LOW_ENERGY));
                loc++;
                phiFeatures.put(loc, example.getFeatures2D().get(start).get(HIGH_ENERGY));
                loc++;
                phiFeatures.put(loc, example.getFeatures2D().get(start).get(AUTO_CORRELATION));
                loc++;

                //============== Delta MFCC Feature Function==============//
                //======================= START ==========================//
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(start).get(MFCC_1));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(start).get(MFCC_2));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(start).get(MFCC_3));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(start).get(MFCC_4));
                loc++;

                //=======================END=======================//
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(end).get(MFCC_1));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(end).get(MFCC_2));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(end).get(MFCC_3));
                loc++;
                phiFeatures.put(loc, NORMALIZE *example.getFeatures2D().get(end).get(MFCC_4));
                loc++;

                //===============Gamma Distribution Over The Vowel Length==============//
                //shape = mean^2/var
                //scale = var/mean
                double variance = Math.pow(Consts.STD_VOWEL_LENGTH,2);
                double shape = Math.pow(Consts.MEAN_VOWEL_LENGTH,2)/variance;
                double scale = variance/Consts.MEAN_VOWEL_LENGTH;
                Gamma gamma = new Gamma(shape, scale);
                double vowelLength = end - start;
                phiFeatures.put(loc,gamma.pdf(vowelLength)/gamma.pdf(Consts.MAX_VOWEL_LENGTH));
                loc++;

                //===============Gaussian Distribution Over The Vowel Length==============//
                double numerator = -Math.pow((vowelLength - Consts.MEAN_VOWEL_LENGTH),2);
                double denominator = 2*Math.pow(Consts.STD_VOWEL_LENGTH,2);
                phiFeatures.put(loc,Math.exp(numerator/denominator));

                newExample.setFeatures(phiFeatures);
                return newExample;
            }
            return null;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public int getSizeOfVector() {
        return this.sizeOfVector;
    }


    //**********************************FEATURE FUNCTIONS***************************************//
    //******************************************************************************************//
    //calculate the average difference of featureNumber win_size before and after location
    private double calculateDiff(Example example, int win_size, int location, int featureNumber)
    {
        try {
            double preVal;
            //get the cumulative values of featureNumber
            double startVal = CacheVowelData.getCumulativeValue(example, location - win_size, featureNumber);
            double endVal = CacheVowelData.getCumulativeValue(example, location, featureNumber);

            preVal = endVal - startVal;
            preVal /= win_size;


            double afterVal;
            //get the cumulative values of featureNumber
            startVal = endVal;
            endVal = CacheVowelData.getCumulativeValue(example, location + win_size, featureNumber);

            afterVal = endVal - startVal;
            afterVal /= win_size;

            Double value = afterVal - preVal;
            if(value.isNaN())
                value = 0.0;

            //return the diff
            return value;

        } catch (Exception e){
            Logger.error("Error in function: calculateDiff, feature number: " + featureNumber + ", example: " + example.path);
            return 0;
        }
    }

    //calculate the avg value from start till end of featureNumber
    //if isPrev equals true then create mean difference from start
    //else create mean difference from end
    private double calculateMean(Example example, int start, int end, int featureNumber, int win_size, boolean isPrev)
    {
        try {
            double avg;
            int counter = end - start;

            //get the cumulative values of featureNumber
            double startVal = CacheVowelData.getCumulativeValue(example,start,featureNumber);
            double endVal = CacheVowelData.getCumulativeValue(example,end,featureNumber);

            //computer the mean
            avg = endVal-startVal;

            avg /= counter;

            double val;
            if(isPrev) {
                //get the cumulative values of featureNumber
                startVal = CacheVowelData.getCumulativeValue(example, start - win_size, featureNumber);
                endVal = CacheVowelData.getCumulativeValue(example, start, featureNumber);

                val = endVal - startVal;
                val /= win_size;
            } else {
                //get the cumulative values of featureNumber
                startVal = CacheVowelData.getCumulativeValue(example, end, featureNumber);
                endVal = CacheVowelData.getCumulativeValue(example, end + win_size, featureNumber);

                val = startVal - endVal;
                val /= win_size;
            }

            Double value = MathHelpers.sigmoid(avg - val);
            if(value.isNaN())
                value = 0.0;

            return value;

        } catch (Exception e){
            Logger.error("Error in function: calculateMean, feature number: "+featureNumber+", example: "+example.path);
            return 0;
        }
    }
    //******************************************************************************************//
} public class StructEDModel implements Serializable{

    // data members
    private Classifier classifier;
    private Vector W;
    private double cumulative_loss = 0;
    private boolean isShuffle;

    // C'tor
    public StructEDModel(Vector init_weights, IUpdateRule updateRule, ITaskLoss taskLoss, IInference inference,
                         IKernel kernel, IFeatureFunctions phi, ArrayList<Double> args){
        classifier = new Classifier();
        classifier.classifierData = new ClassifierData();
        updateRule.init(args);
        classifier.classifierData.updateRule = updateRule;
        classifier.classifierData.taskLoss = taskLoss;
        classifier.classifierData.inference = inference;
        classifier.classifierData.kernel = kernel;
        classifier.classifierData.phi = phi;
        W = init_weights;
        this.isShuffle = true;
    }
    // override constructor to support enable/disable shuffling
    public StructEDModel(Vector init_weights, IUpdateRule updateRule, ITaskLoss taskLoss, IInference inference,
                         IKernel kernel, IFeatureFunctions phi, ArrayList<Double> args, boolean isShuffle){
        classifier = new Classifier();
        classifier.classifierData = new ClassifierData();
        updateRule.init(args);
        classifier.classifierData.updateRule = updateRule;
        classifier.classifierData.taskLoss = taskLoss;
        classifier.classifierData.inference = inference;
        classifier.classifierData.kernel = kernel;
        classifier.classifierData.phi = phi;
        W = init_weights;
        this.isShuffle = isShuffle;
    }

    /**
     * Train the model on the train instances
     * @param epoch - the number of epochs to run over the data
     * @param trainInstances - the training instances(train set)
     * @param task_loss_params - the task loss parameters, if there are no parameters set this to null
     * @param developInstances - the develop instances(dev set), set this to null if there is no validation set
     * @param isAvg - an indicator whether or not to average the weights
     * @throws Exception
     */
    public void train(InstancesContainer trainInstances, ArrayList<Double> task_loss_params, InstancesContainer developInstances, int epoch, int isAvg, boolean verbose) throws Exception {
        //#############################################################################//
        //================================TRAIN========================================//
        //#############################################################################//
        //===================================//
        //print the start time of the program//
        Logger.infoTime("Start Training:");
        Logger.time();
        Logger.info("");
        long startTime = System.currentTimeMillis();
        //===================================//

        //loop over the training set epoch time
        for(int size = 0 ; size<epoch ; size++)
        {
            //===================================//
            //print the start time of the program//
            Logger.info("");
            Logger.info("==================================");
            Logger.timeExampleStandard("Epoch: ", (size + 1));
            Logger.info("==================================");
            Logger.info("");
            //===================================//
            if(isShuffle)
                //preform random shuffle for the next epoch
                trainInstances = ModelHandler.randomShuffle(trainInstances);

            //train the algorithm
            if (classifier == null) throw new AssertionError();
            W = classifier.train(W, trainInstances, task_loss_params, developInstances, isAvg, verbose);
            if(W == null)
                return;
        }

        if(isAvg == 1)
            this.W = classifier.getAvgWeights();
        //===================================//
        //print the start time of the program//
        Logger.time();
        long endTime   = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        long seconds = TimeUnit.MILLISECONDS.toSeconds(totalTime);
        Logger.info("Running time in seconds: " + seconds);
        //===================================//
    }

    /**
     * predict based on the model and return the scores of the best matches
     * @param instances - the test set
     * @param task_loss_params - the parameters for the task loss function, set to null if there are no task loss parameters
     * @param numPredictions2Return - the number of examples to return in the scores
     * @return ArrayList<PredictedLabels> - return the predicted labels as array
     */
    public ArrayList<PredictedLabels> predict(InstancesContainer instances, ArrayList<Double> task_loss_params, int numPredictions2Return, boolean verbose){
        Logger.info("");
        Logger.infoTime("Start Predicting:");
        Logger.time();
        Logger.info("");
        cumulative_loss = 0;
        ArrayList<PredictedLabels> scores = new ArrayList<PredictedLabels>();
        for(int i=0 ; i<instances.getSize() ; i++) {
            Example x = instances.getInstance(i);
            if (x == null)
                continue;
            PredictedLabels score = classifier.test(W, x, numPredictions2Return);

            String y_hat = score.firstKey();
            String y = x.getLabel();
            cumulative_loss += classifier.classifierData.taskLoss.computeTaskLoss(y, y_hat, task_loss_params);
            scores.add(score);
            if(verbose)
                Logger.info("file = "+x.path+", Y = "+y+", Y_Hat = "+y_hat);
        }
        cumulative_loss /= (double) (instances.getSize());
        Logger.info("Cumulative task loss: " + (cumulative_loss));
        Logger.info("");
        return scores;
    }

    /**
     * Plotting the error graph for the validation set
     * @param save flag which indicates whether to write the image or not
     */
    public void plotValidationError(boolean save){
        if(classifier.validationCumulativeLoss != null && classifier.validationCumulativeLoss.size() != 0) {
            Graph graph = new Graph();
            graph.drawGraph(classifier.validationCumulativeLoss, save);
        }
    }

    /**
     * save the model, notice: we save only the weight vector, the rest of the configuration should be predefined
     * @param path - the path to save the model
     * @throws IOException
     */
    public void saveModel(String path) throws IOException {
        FileOutputStream fout = new FileOutputStream(path);
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        oos.writeObject(W);
        oos.close();
    }

    /**
     * load the model, notice: we load only the weight vector, the rest of the configuration should be predefined
     * @param path - the model's path
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void loadModel(String path) throws IOException, ClassNotFoundException {
        FileInputStream fin = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fin);
        W = (Vector) ois.readObject();
        ois.close();
    }

    // Setters and Getters
    public Vector getWeights(){
        return this.W;
    }
    public double getCumulative_loss() {return cumulative_loss;}

    public void setUpdateRule(IUpdateRule updateRule){
        this.classifier.classifierData.updateRule = updateRule;
    }
    public void setTaskLoss(ITaskLoss taskLoss){
        this.classifier.classifierData.taskLoss = taskLoss;
    }
    public void setKernel(IKernel kernel){
        this.classifier.classifierData.kernel = kernel;
    }
    public void setInference(IInference inference){
        this.classifier.classifierData.inference = inference;
    }
    public void setFeatureFunctions(IFeatureFunctions phi){ this.classifier.classifierData.phi = phi; }
    public void setReShuffle(boolean isShuffle) { this.isShuffle = isShuffle; }
} public class InferenceVowelDurationData implements IInference {

    //predict function
    //argmax(yS,yE) (W*Phi(Xi,yS,yE)) + Task Loss
    //this function assumes that the argument vector has already been converted to phi vector
    //return null on error
    public PredictedLabels predictForTrain(Example vector, Vector W, String realClass, ClassifierData classifierData, double epsilonArgMax)
    {
        try{
            double maxVal = 0;
            String maxLabel = "";
            boolean isFirst = true;

            //validation
            if(vector.sizeOfVector<=0)
            {
                Logger.error(ErrorConstants.PHI_VECTOR_DATA);
                return null;
            }

            //loop over all the classifications of this specific example
            for(int i=Consts.MIN_GAP_START ; i<vector.sizeOfVector-(Consts.MIN_GAP_END) ; i++)
            {
                for(int j=i+Consts.MIN_VOWEL ; j<i+Consts.MAX_VOWEL ; j++)
                {
                    if(j>vector.sizeOfVector-(Consts.MIN_GAP_END))
                        break;

                    Example phiData = classifierData.phi.convert(vector,(i+1)+Consts.CLASSIFICATION_SPLITTER+(j+1),classifierData.kernel);
                    //multiple the vectors
                    double tmp = MathHelpers.multipleVectors(W, phiData.getFeatures());

                    if(epsilonArgMax != 0){
                        //add the task loss
                        tmp += epsilonArgMax*classifierData.taskLoss.computeTaskLoss((i+1)+Consts.CLASSIFICATION_SPLITTER+(j+1), realClass, classifierData.arguments);
                    }

                    if(isFirst) {
                        maxLabel = (i + 1) + Consts.CLASSIFICATION_SPLITTER + (j + 1);
                        maxVal = tmp;
                        isFirst = false;
                    }
                    else if(tmp > maxVal) {
                        maxLabel = (i + 1) + Consts.CLASSIFICATION_SPLITTER + (j + 1);
                        maxVal = tmp;
                    }
                }
            }

            PredictedLabels result = new PredictedLabels();
            result.put(maxLabel, maxVal);

            return result;

        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    public PredictedLabels predictForTest(Example vector, Vector W, String realClass, ClassifierData classifierData, int returnAll) {
        switch (returnAll) {
            case 0:
                return predictForTrain(vector, W, realClass, classifierData, 0.0);
            case 1:
                try {
                    TreeMap<String, Double> tree = new TreeMap<String, Double>();

                    //validation
                    if (vector.sizeOfVector <= 0) {
                        Logger.error(ErrorConstants.PHI_VECTOR_DATA);
                        return null;
                    }

                    //loop over all the classifications of this specific example
                    for (int i = Consts.MIN_GAP_START; i < vector.sizeOfVector - (Consts.MIN_GAP_END); i++) {
                        for (int j = i + Consts.MIN_VOWEL; j < i + Consts.MAX_VOWEL; j++) {
                            if (j > vector.sizeOfVector - (Consts.MIN_GAP_END))
                                break;

                            Example phiData = classifierData.phi.convert(vector, (i + 1) + Consts.CLASSIFICATION_SPLITTER + (j + 1), classifierData.kernel);
                            //multiple the vectors
                            double tmp = MathHelpers.multipleVectors(W, phiData.getFeatures());
                            tree.put((i + 1) + Consts.CLASSIFICATION_SPLITTER + (j + 1), tmp);
                        }
                    }

                    MapValueComparatorDescending vc = new MapValueComparatorDescending(tree);
                    PredictedLabels result = new PredictedLabels(vc);
                    result.putAll(tree);

                    return result;

                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            default:
                return predictForTrain(vector, W, realClass, classifierData, 0.0);
        }
    }
} public class TaskLossVowelDuration implements ITaskLoss {

	@Override
    //max{0, |ys - ys'| - epsilon} + max{0, |ye - ye'| - epsilon}
	public double computeTaskLoss(String predictClass, String actualClass, List<Double> params) {
        try {
            Double epsilon_onset = params.get(0);
            Double epsilon_offset = params.get(1);

            String predictValues[] = predictClass.split(Consts.CLASSIFICATION_SPLITTER);
            String actualClassValues[] = actualClass.split(Consts.CLASSIFICATION_SPLITTER);

            double predictResStart = Double.parseDouble(predictValues[0]);
            double actualResStart = Double.parseDouble(actualClassValues[0]);

            double predictResEnd = Double.parseDouble(predictValues[1]);
            double actualResEnd = Double.parseDouble(actualClassValues[1]);

            double diffStart = Math.abs(predictResStart - actualResStart);
            double diffEnd = Math.abs(predictResEnd - actualResEnd);

            //subtract the epsilon
            double absRes = 0;
            if(diffStart >=  epsilon_onset)
                absRes += diffStart;
            if(diffEnd >= epsilon_offset)
                absRes += diffEnd;

            //get the max from the absolute result minus epsilon and 0
            return absRes;

        } catch (Exception e){
            e.printStackTrace();
            return 0;
        }
	}
} public static Reader getReader(int type){
       switch(type){
           case 0:
	        return new StandardReader();
           case 1:
               return new RankReader();
           case 2:
               return new LazyReader();
           case 3:
               return new OcrReader();
           default:
               return new StandardReader();
       }
}
import com.structed.data.entities.Vector;
import com.structed.data.featurefunctions.FeatureFunctionsVowelDuration;
import com.structed.models.StructEDModel;
import com.structed.models.algorithms.*;
import com.structed.models.inference.InferenceVowelDurationData;
import com.structed.models.loss.TaskLossVowelDuration;
import java.io.File;
import java.util.ArrayList;
import static com.structed.data.Factory.getReader;
import com.structed.constants.Consts;
import com.structed.dal.Reader;
import com.structed.data.InstancesContainer;
import com.structed.data.Logger;
/*
 * The MIT License (MIT)
 *
 * StructED - Machine Learning Package for Structured Prediction
 *
 * Copyright (c) 2015 Yossi Adi, E-Mail: yossiadidrum@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.structed.tutorials;




/**
 * Created by yossiadi on 6/29/15.
 * Tutorial about the vowel duration data
 *
 * Compering all the models on the vowel duration measurement task (using the same setup of training)
 * All the models will be saved to the models directory
 *
 * Results: ========================
 *         MODEL    COST    RUN-TIME
 *         SSVM     14.45   13s
 *         PA       13.8    12s
 *         CRF      14.05   31s
 *         DLM      12.7    24s
 *         RLM      12.9    25s
 *         OL       11.7    13s
 *         PL      ~12.9    617s
 */

public class VowelDurationTutorial {
    public static void main(String[] args) throws Exception {
        // ============================ VOWEL DURATION DATA ============================ //
        Logger.info("Vowel Duration data example.");
        String trainPath = "tutorials-code/vowel/data/vowel/train.vowel.txt";
        String testPath = "tutorials-code/vowel/data/vowel/test.vowel.txt";

        int epochNum = 1;
        int readerType = 2;
        int isAvg = 1;
        int numExamples2Display = 1;
        Reader reader = getReader(readerType);

        // load the data
        InstancesContainer vowelTrainInstances = reader.readData(trainPath, Consts.SPACE, Consts.COLON_SPLITTER);