/********************************************************
* Speaker Verification Implemented Security             *
* CA4 Project                                           *
* Written by: Ronan Crowley (97084603)                  *
*        and  Paul Connolly (97307599)                  *
********************************************************/


// This class calculates the threshold value for a user. the threshold is calculated
// from 5 anti users. This class is given the log likelihood sum values from
// 5 antispeakers. It calculates the mean and standard deviation of the anti speaker
// samples. the threshold is calculated as the mean of the anti speakers plus 1.2.....
// times the standard deviation.

/*      sample use
        calcThreshold thresholdobject = new calcThreshold();
        double threshold = thresholdobject.getThreshold(username, zvalue);
*/


import java.io.*;
import java.util.*;
import java.lang.*;

public class calcThreshold
{
    private static int datasize =25;
    
    /** Empty Constructor **/
    public void calcThreshold()
    {
    }

    /** Method that is geiven a username and accuracy required vale as argument. It reads 25 values
        from an antithreshold file and 5 values from a threshold file. It returns a threshold value.
    **/
    static public double getThreshold(String username, double accuracy)
    {
        // Local Variables !
        String theLine;
        double[] data;
        data = new double[datasize];
        double[] data1;
        data1 = new double[5];
        double mean = 0.0;
        double mean1 = 0.0;
        double stddev=0.0;
        double stddev1=0.0;
        double probRejectUser=0.0;
        double probAcceptWrongUser=accuracy;
        
        try {   
            // Read Strings from antithreshold.txt and convert them to doubles !
            // DEPRECATED:
            //     DataInputStream input = new DataInputStream(new FileInputStream("users\\"+username+"\\antithreshold.txt"));
            BufferedReader input = new BufferedReader(new FileReader("users\\"+username+"\\antithreshold.txt"));
 
            int i=0;

            while ((theLine = input.readLine()) != null) {
                //Create 25 doubles.....
                try {
                    data[i] = Double.valueOf(theLine.trim()).doubleValue();
                } catch (NumberFormatException nfe) {
                    System.out.println("NumberFormatException: " + nfe.getMessage());
                }
                i++;
            }
            input.close();

            // Read Strings from threshold.txt and convert them to doubles !
            // DEPRECATED:
            //     DataInputStream input1 = new DataInputStream(new FileInputStream("users\\"+username+"\\threshold.txt"));
            BufferedReader input1 = new BufferedReader(new FileReader("users\\"+username+"\\threshold.txt"));
            i=0;

            while ((theLine = input1.readLine()) != null) {
                //Create 5 doubles.....
                try {
                    data1[i] = Double.valueOf(theLine.trim()).doubleValue();
                } catch (NumberFormatException nfe) {
                    System.out.println("NumberFormatException: " + nfe.getMessage());
                }
                i++;
            }
   
            input1.close();
        }
        catch (IOException e) {
           System.err.println("IOException: " + e);
        }
        
        //Calculate mean for antithreshold values
        mean =mean(data);
        System.out.println("MEAN OF INCORRECT: "+mean);
        //calculate standard deviation for antithreshold values
        stddev=std(data,mean);
        
        System.out.println("STD of INCORRECT: "+stddev);        
        //Calculate mean for threshold values
        mean1 =mean1(data1);
        System.out.println("MEAN OF CORRECT: "+mean1);
        //calculate standard deviation of threshold values
        stddev1=std1(data1,mean1);
        System.out.println("STD of CORRECT: "+stddev1);     


        //Calculate threshold
        double threshold = mean+(accuracy*stddev);
        System.out.println("THRESHOLD "+threshold);

        //calculate prob of rejecting the user
        probRejectUser = ((threshold-mean1)/stddev1);

        System.out.println("PROB REJECTING USER in z form: "+probRejectUser);

        //convert to 5 format
        probRejectUser=zToPercent(probRejectUser);

        System.out.println("PROB REJECTING USER in % form: "+probRejectUser);

        System.out.println("PROB Accpet anti user z form: "+probAcceptWrongUser);
        
        //convert to % format
        probAcceptWrongUser=zToPercent(probAcceptWrongUser);
        System.out.println("PROB Accpet anti user z form: "+probAcceptWrongUser);


        //Write Threshold value to t.txt in user's directory
        try
        {
            PrintWriter tout = new PrintWriter( new FileWriter( "users\\"+username+"\\t.txt" ) );
            tout.println(threshold);                //Line 1 = Threshold
            tout.println(accuracy);                 //Line 2 = Accuracy Reference (initially 1.2817 = 10%)
            tout.println(probAcceptWrongUser);      //Line 3 = % Chance of accepting incorrect user
            tout.println(probRejectUser);           //Line 4 = % Chance of rejecting correct user
            tout.close();
        }
        catch(IOException ioe)
        {
            ioe.printStackTrace();
        }

        return threshold;
    }

    /** Method to calculate the mean of the data (5 values from threshold.txt). 
        @param data = The array of doubles whose mean is being calculated.
    **/
    static public double mean1(double[] data)
    {
        double sum=0.0;
        for (int i=0;i<5;i++ )
        {
            sum+=data[i];
        }
        return (sum/datasize);
    }

    /** Method to calculate the mean of the data (25 values from antithreshold.txt). 
        @param data = The array of doubles whose mean is being calculated.
    **/
    static public double mean(double[] data)
    {
        double sum=0.0;
        for (int i=0;i<datasize;i++ )
        {
            sum+=data[i];
        }
        return (sum/datasize);
    }

    /** Method to calculate Standard Deviation 
        @param data = The array of doubles whose Standard Deviation is being calculated. 
        @param mean = The mean of the values int the array of doubles also passed in.
    **/
    static public double std(double[] data, double mean)
    {
        double totaldev= 0.0; //sum of deviations

        // Sum the deviations
        for(int i=0; i < datasize; i++){
            totaldev += Math.pow((data[i] - mean),2);
        }
        //Divide the total deviations by n-1 and get square root
        return Math.sqrt(totaldev/24);  
    }

    /** Method to calculate Standard Deviation 
        @param data = The array of doubles whose Standard Deviation is being calculated. 
        @param mean = The mean of the values int the array of doubles also passed in.
    **/
    static public double std1(double[] data, double mean)
    {
        double totaldev= 0.0; //sum of deviations

        // Sum the deviations
        for(int i=0; i < 5; i++){
            totaldev += Math.pow((data[i] - mean),2);
        }
        //Divide the total deviations by n-1 and get square root
        return Math.sqrt(totaldev/4);   
    }





    /** method which when given a z values, returns the percentage this z value represents **/
    public static double zToPercent(double zvalue)
    {
            //enure zvalue is in absolute format
            if (zvalue<0){
                zvalue=zvalue * (-1);
            }
            
            if (zvalue>=3.7191){
                return 0.0001;
            }
            else if (zvalue>=3.2908){
                return 0.0005;
            }
            else if (zvalue>=3.0905){
                return 0.001;
            }
            else if (zvalue>=2.5762){
                return 0.005;
            }
            else if (zvalue>=2.3268){
                return 0.01;
            }
            else if (zvalue>=2.0542){
                return 0.02;
            }
            else if (zvalue>=1.6452){
                return 0.05;
            }
            else if (zvalue>=1.2817){
                return 0.10;
            }
            else if (zvalue>=1.0364){
                return 0.15;
            }
            else if (zvalue>=0.8415){
                return 0.20;
            }
            else if (zvalue>=0.6742){
                return 0.25;
            }
    
            else
                return 0.0;
    }

    
    /** Method which when given a percentage required, returns the closest z value.**/
    public static double percentToZ(double percent)
    {
            if (percent>=.5)
                return 0.0000;
            else if (percent>=0.25)
                return 0.6742;
            else if (percent>=0.20)
                return 0.8415;
            else if (percent>=0.15)
                return 1.0364;
            else if (percent>=0.10)
                return 1.2817;
            else if (percent>=0.05)
                return 1.6452;
            else if (percent>=0.02)
                return 2.0542;
            else if (percent>=0.01)
                return 2.3268;
            else if (percent>=0.001)
                return 3.0905;
            else if (percent>=0.0005)
                return 3.2908;
            else if (percent>=0.0001)
                return 3.7101;
            else return 0.0;
    }

    /** Main method to allow for standalone testing ! **/
    public static void main(String args[])
    {
        //double[] data={-1727,-4697,-4360,-7713,-6470,-2871,-3668,-3016,-1728,-2697,-2525,-2903,-3444,-4171,-4349,-3107,-5266,-3974,-8256,-7455,-2380,-4238,-4479,-4189,-5149};

        double threshold;
        threshold=getThreshold(args[0], 1.2817);
    }
}