/// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            int N = n / M; 	//number of blocks that can be made from the binary string

            //for each block calculate the proportion of ones
            double sum = 0.0;
            for (int i = 0; i < N; i++) {
                double pi = (double)model.epsilon.GetRange(i * M, M).Sum() / (double)M;   //sum of bits in block / length of the block
                sum += Math.Pow((pi - 0.5), 2);
            }

            //calculate p_value
            double chi_squared = 4.0 * M * sum;
            double p_value = Cephes.igamc(N / 2.0, chi_squared / 2.0);

            if (printResults) {
                Report report = new Report("2: Frequency Test within a Block");
                report.Write("\t\t\tBLOCK FREQUENCY TEST");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\t(a) Chi^2           = " + chi_squared);
                report.Write("\t\t(b) # of substrings = " + N);
                report.Write("\t\t(c) block length    = " + M);
                report.Write("\t\t(d) Note: " + n % M + " bits were discarded.");
                report.Write("\t\t---------------------------------------------");
                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value = " + p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {
            
            double pi = (double)model.epsilon.GetRange(0, n).Sum() / (double)n;    //number of ones in the binary string / length

            //prerequisite for test, if not passed test is not applicable   
            if (Math.Abs(pi - 0.5) > (2.0 / Math.Sqrt(n))) {
                if (printResults) {
                    Report report = new Report("3: Runs Test");
                    report.Write("\t\t\t\tRUNS TEST");
                    report.Write("\t\t------------------------------------------");
                    report.Write("\t\tPI ESTIMATOR CRITERIA NOT MET! PI = " + pi);
                    model.reports.Add(report.title, report);
                }
                return new double[] { 0.0 };
            } else {
                //count number of runs in the binary string (where e(i) = e(i+1) is considered to be a run)
                int V_obs = 1;
                for (int i = 1; i < n; i++) {
                    if (model.epsilon[i] != model.epsilon[i - 1]) {
                        V_obs++;
                    }
                }

                //calculate p_value
                double p_value = Cephes.erfc(Math.Abs(V_obs - 2.0 * n * pi * (1 - pi)) / (2.0 * pi * (1 - pi) * Math.Sqrt(2 * n)));

                if (printResults) {
                    Report report = new Report("3: Runs Test");
                    report.Write("\t\t\t\tRUNS TEST");
                    report.Write("\t\t------------------------------------------");
                    report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                    report.Write("\t\t------------------------------------------");
                    report.Write("\t\t(a) Pi                        = " + pi);
                    report.Write("\t\t(b) V_n_obs (Total # of runs) = " + (int)V_obs);
                    report.Write("\t\t(c) V_n_obs - 2 n pi (1-pi)", false);
                    report.Write("\t\t      2 sqrt(2n) pi (1-pi)");
                    report.Write("\t\t------------------------------------------");
                    if (p_value < 0 || p_value > 1) {
                        report.Write("WARNING:  P_VALUE IS OUT OF RANGE.");
                    }
                    report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS"+"\t\tp_value = "+p_value);
                    model.reports.Add(report.title, report);
                }
                return new double[] { p_value };
            }
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            //calculate the sum of all bits in the string after mapping (0->-1, 1->1)
            double S_n = model.epsilon.GetRange(0, n).Sum(delegate(int i) { return 2 * i - 1; });

            //calculate p_value
            double S_obs = Math.Abs(S_n) / Math.Sqrt(n);
            double p_value = Cephes.erfc(S_obs / Math.Sqrt(2));

            if (printResults) {
                Report report = new Report("1: Frequency (Monobit) Test");
                report.Write("\t\t\t      FREQUENCY TEST");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\t(a) The nth partial sum = " + (int)S_n);
                report.Write("\t\t(b) S_n/n               = " + S_n / n);
                report.Write("\t\t---------------------------------------------");
                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value = " + p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        private double[] run(int[] B, bool printResults) {
            if (n > model.epsilon.Count || n <= 0) {
                throw new ArgumentException("The value of n must be smaller than the size of the input data, and be greater than 0", "Frequency n");
            }

            int[] Wj = new int[N];

            for (int i = 0; i < N; i++) { //for each start position
                for (int j = 0; j < M - B.Length + 1; j++) {    //is there a match for the template
                    bool match = true;
                    for (int k = 0; k < B.Length; k++) {
                        if ((int)B[k] != (int)model.epsilon[i * M + j + k]) {
                            match = false;  
                            break;
                        }
                    }
                    if (match) {
                        Wj[i]++;    //if there was a match, record it
                    }
                }
            }

            //calculate p_value
            double chi2 = 0.0;
            for (int i = 0; i < N; i++) {
                chi2 += Math.Pow(((double)Wj[i] - mu) / Math.Pow(sigma, 0.5), 2);
            }
            double p_value = Cephes.igamc(N / 2.0, chi2 / 2.0);

            if (printResults) {
                Report report = new Report("7: Non-overlapping Template Matching Test");
                report.Write("chi^2   = " + chi2);
                report.Write("p_value = " + p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        /// <exception cref="ArgumentException"/>
        /// <exception cref="FormatException"/>
        /// <exception cref="OverflowException"/>
        /// <exception cref="FileNotFoundException"/>
        /// <exception cref="DirectoryNotFoundException"/>
        /// <exception cref="UnauthorizedAccessException"/>
        /// <exception cref="IOException"/>
        /// <exception cref="OutOfMemoryException"/>
        public override double[] run(bool printResults) {

            if (B != null) { //as we know it is to run on a single test
                mu = (M - B.Length + 1) / Math.Pow(2, B.Length);
                sigma = M * (1.0 / Math.Pow(2.0, B.Length) - (2.0 * B.Length - 1.0) / Math.Pow(2.0, 2.0 * B.Length));
                return run(B, printResults);
            }
            //otherwise on a template of tests

            int[] numOfTemplates = {0, 0, 2, 4, 6, 12, 20, 40, 74, 148, 284, 568, 1116,
						    2232, 4424, 8848, 17622, 35244, 70340, 140680, 281076, 562152};
	        

            //compute predicted probabilities
            int K = 5;
            mu = (M - m + 1) / Math.Pow(2, m);
            sigma = M * (1.0 / Math.Pow(2.0, m) - (2.0 * m - 1.0) / Math.Pow(2.0, 2.0 * m));

            Report report = new Report("7: Non-overlapping Template Matching Test");
            if (printResults) {
                report.Write("\t\t  NONPERIODIC TEMPLATES TEST");
                report.Write("-------------------------------------------------------------------------------------");
                report.Write("\t\t  COMPUTATIONAL INFORMATION");
                report.Write("-------------------------------------------------------------------------------------");
                report.Write("\tLAMBDA = " + mu + "\tM = " + M + "\tN = " + N + "\tm = " + m + "\tn = " + n + "");
                report.Write("-------------------------------------------------------------------------------------");
            }

            double[] p_values = new double[numOfTemplates[m]];
            try {
                using (StreamReader sr = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(TEMPLATE_DIR + ".template" + m))) {
                    for (int i = 0; i < numOfTemplates[m]; i++) {

                        String line = sr.ReadLine();    //load in the template string
                        String[] numbers = Regex.Split(line, TEMPLATESPLITSTR);
                        B = new int[m];
                        for (int k = 0; k < m; k++) {
                            try {
                                B[k] = Convert.ToInt32(numbers[k]); //parse into an array
                            } catch (FormatException) {
                                throw new FormatException("The input data did not consist of a an optional " +
                                        "sign followed by a sequence of digits (0 through 9):\r\n\r\n" + line);
                            } catch (OverflowException) {
                                throw new OverflowException("The input string was not of a number within the program's ranges:\r\n\r\n" + line);
                            }
                        }

                        p_values[i] = this.run(B, false)[0];    //get p_value for this template string

                        if (printResults) {
                            if (p_values[i] < 0 || p_values[i] > 1) {
                                report.Write("\t\tWARNING:  P_VALUE IS OUT OF RANGE.");
                            }
                            report.Write(i + "\t: " + (p_values[i] < ALPHA ? "FAILURE" : "SUCCESS") + "\t\t" + p_values[i]);
                        }

                    }
                }
            } catch (ArgumentException) {
                throw new ArgumentException("The input file path is not a valid one:\r\n\r\n" + TEMPLATE_DIR + "template" + m);
            } catch (FileNotFoundException) {
                throw new FileNotFoundException("The input file does not exist:\r\n\r\n" + TEMPLATE_DIR + "template" + m);
            } catch (DirectoryNotFoundException) {
                throw new DirectoryNotFoundException("The input directory does not exist:\r\n\r\n" + TEMPLATE_DIR + "template" + m);
            } catch (UnauthorizedAccessException) {
                throw new UnauthorizedAccessException("The user does not have the permissions to access this file:\r\n\r\n" + TEMPLATE_DIR + "template" + m);
            } catch (IOException) {
                throw new IOException("Failed to read from file successfully:\r\n\r\n" + TEMPLATE_DIR + "template" + m);
            } catch (OutOfMemoryException) {
                throw new OutOfMemoryException("System out of memory");
            }
            model.reports.Add(report.title, report);
            return p_values;
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {
            
	        double[]    pi = { 0.01047, 0.03125, 0.12500, 0.50000, 0.25000, 0.06250, 0.020833 };

            int N = n/M;    //number of blocks
	       
            double mu = M / 2.0 + (9.0 + Math.Pow(-1, M+1)) / 36.0 - 1.0 / Math.Pow(2, M) * (M / 3.0 + 2.0 / 9.0);

            double[] v = new double[K+1];

	        for ( int i=0; i<N; i++ ) {
                int[] T = new int[M];   //initialize the work arrays
                int[] P = new int[M];
                int[] C = new int[M];
                int[] B = new int[M];
		        
		        int L = 0;
		        int m = -1;
		        int d = 0;
		        C[0] = 1;
		        B[0] = 1;
		
		        //calculate the linear complexity of the block
		        int blockPos = 0;
		        while ( blockPos < M ) {
                    d = (int)model.epsilon[i * M + blockPos];
                    for (int j = 1; j <= L; j++) {
                        d += C[j] * model.epsilon[i * M + blockPos - j];
                    }
                    d = d % 2;
			        if ( d == 1 ) {
                        for (int j = 0; j < M; j++) {
                            T[j] = C[j];
                            P[j] = 0;
                        }
                        for (int j = 0; j < M; j++) {
                            if (B[j] == 1) {
                                P[j + blockPos - m] = 1;
                            }
                        }
                        for (int j = 0; j < M; j++) {
                            C[j] = (C[j] + P[j]) % 2;
                        }
				        if ( L <= blockPos/2 ) {
					        L = blockPos + 1 - L;
					        m = blockPos;
                            for (int j = 0; j < M; j++) {
                                B[j] = T[j];
                            }
				        }
			        }
			        blockPos++;
		        }
                
                double Ti = Math.Pow(-1, M) * (L - mu) + 2.0 / 9.0;
		
                //record result of complexity test
		        if ( Ti <= -2.5 )
			        v[0]++;
		        else if ( Ti > -2.5 && Ti <= -1.5 )
			        v[1]++;
		        else if ( Ti > -1.5 && Ti <= -0.5 )
			        v[2]++;
		        else if ( Ti > -0.5 && Ti <= 0.5 )
			        v[3]++;
		        else if ( Ti > 0.5 && Ti <= 1.5 )
			        v[4]++;
		        else if ( Ti > 1.5 && Ti <= 2.5 )
			        v[5]++;
		        else
			        v[6]++;
	        }

            //calculate p_value
	        double chi_squared = 0;
            for (int i = 0; i < K + 1; i++) {
                chi_squared += Math.Pow(v[i] - N * pi[i], 2) / (N * pi[i]);
            }
	        double p_value = Cephes.igamc(K/2.0, chi_squared/2.0);

            if (printResults) {
                Report report = new Report("10: Linear Complexity Test");
                report.Write("-----------------------------------------------------");
                report.Write("\tL I N E A R  C O M P L E X I T Y");
                report.Write("-----------------------------------------------------");
                report.Write("\tM (substring length)     = {0}" + M);
                report.Write("\tN (number of substrings) = {0}" + N);
                report.Write("-----------------------------------------------------");
                report.Write("        F R E Q U E N C Y                            ");
                report.Write("-----------------------------------------------------");
                report.Write("  C0   C1   C2   C3   C4   C5   C6    CHI2    P-value");
                report.Write("-----------------------------------------------------");
                report.Write("\tNote: " + n % M + " bits were discarded!");
                for (int i = 0; i < K + 1; i++) {
                    report.Write(((int)v[i]).ToString(), false);
                }
                report.Write("");
                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value = " + p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            int[] stateX = { -9, -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

            int J = 0;
            int[] S = new int[n];
            //compute partial sums
            S[0] = 2 * (int)model.epsilon[0] - 1;
            for (int i = 1; i < n; i++) {
                S[i] = S[i - 1] + 2 * model.epsilon[i] - 1;
                if (S[i] == 0) {
                    J++;
                }
            }
            if (S[n - 1] != 0) {
                J++;
            }

            Report report = new Report("15: Random Excursions Variant Test");
            if (printResults) {
                report.Write("\t\t\tRANDOM EXCURSIONS VARIANT TEST");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\t(a) Number Of Cycles (J) = " + J);
                report.Write("\t\t(b) Sequence Length (n)  = " + n);
            }

            double[] p_values = new double[18];
            int constraint = (int)Math.Max(0.005 * Math.Pow(n, 0.5), 500);
            if (J < constraint) {
                if (printResults) {
                    report.Write("\t\tWARNING:  TEST NOT APPLICABLE.  THERE ARE AN");
                    report.Write("\t\t\t  INSUFFICIENT NUMBER OF CYCLES.");
                    report.Write("\t\t---------------------------------------------");
                }
                for (int i = 0; i < 18; i++) {
                    report.Write(0.0.ToString());
                }
            } else {
                for (int p = 0; p < 18; p++) {
                    int x = stateX[p];
                    int count = 0;
                    //count occurences of state x
                    for (int i = 0; i < n; i++) {
                        if (S[i] == x) {
                            count++;
                        }
                    }
                    //compute p_value
                    p_values[p] = Cephes.erfc(Math.Abs(count - J) / (Math.Sqrt(2.0 * J * (4.0 * Math.Abs(x) - 2))));

                    if (printResults) {
                        if (p_values[p] < 0 || p_values[p] > 1) {
                            report.Write("\t\t(b) WARNING: P_VALUE IS OUT OF RANGE.");
                        }
                        report.Write(p_values[p] < ALPHA ? "FAILURE" : "SUCCESS" + "\t\t");
                        report.Write("(x = " + x + ") Total visits = " + count + "; p-value = " + p_values[p]);
                        report.Write(p_values[p].ToString());
                    }
                }
            }
            if (printResults) {
                model.reports.Add(report.title, report);
            }
            return p_values;
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {
	
	        int r = 0;
	        double[] apEn = new double[2];
	        for ( int blockSize=m; blockSize<=m+1; blockSize++ ) {
		        if ( blockSize == 0 ) {
			        apEn[0] = 0;
			        r++;
		        }
		        else {

                    int[] P = new int[(int)Math.Pow(2, blockSize+1)-1];

                    for (int i = 1; i < Math.Pow(2, blockSize + 1) - 1; i++) {
                        P[i] = 0;
                    }
                    //calculate frequency of n overlapping blocks
                    for (int i = 0; i < n; i++) {
                        int k = 1;
                        for (int j = 0; j < blockSize; j++) {
                            k *= 2;
                            if ((int)model.epsilon[(i + j) % n] == 1) {
                                k++;
                            }
                        }
                        P[k - 1]++;
                    }
                    //calculate approximate entropy entry from frequency
			        double sum = 0;
			        int index = (int)Math.Pow(2, blockSize)-1;
			        for ( int i=0; i<(int)Math.Pow(2, blockSize); i++ ) {
                        if (P[index] > 0) {
                            sum += P[index] * Math.Log(P[index] / (double)n);
                        }
				        index++;
			        }
			        apEn[r] = sum / n;
			        r++;
		        }
	        }
	        double approximateEntropy = apEn[0] - apEn[1];
	
            //calculate p_value
	        double chi_squared = 2.0*n*(Math.Log(2) - approximateEntropy);
	        double p_value = Cephes.igamc(Math.Pow(2, m-1), chi_squared/2.0);

            if (printResults) {
                Report report = new Report("12: Approximate Entropy Test");
                report.Write("\t\t\tAPPROXIMATE ENTROPY TEST");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\t(a) m (block length)    = "+ m);
                report.Write("\t\t(b) n (sequence length) = " + n);
                report.Write("\t\t(c) Chi^2               = " + chi_squared);
                report.Write("\t\t(d) Phi(m)	          = " + apEn[0]);
                report.Write("\t\t(e) Phi(m+1)	          = " + apEn[1]);
                report.Write("\t\t(f) ApEn                = " + approximateEntropy);
                report.Write("\t\t--------------------------------------------");
                if (m > (int)(Math.Log(n) / Math.Log(2) - 5)) {
                    report.Write("\t\tNote: The blockSize = "+m+" exceeds recommended value of "+
                        Math.Max(1, (int)(Math.Log(n) / Math.Log(2) - 5)));
                    report.Write("\t\tResults are inaccurate!");
                    report.Write("\t\t--------------------------------------------");
                }
                report.Write((p_value < ALPHA ? "FAILURE" : "SUCCESS")+"\t\tp_value = "+ p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            double[] pi;
            int K, M, V0;
            init(out K, out M, out V0, out pi);  //initialize variables to sensible values

            int[] V = new int[pi.Length];
            int N = n / M;    //number of blocks that can be made from the binary string
            for (int i = 0; i < N; i++) {
                //count the longest run of ones in each block
                int longestRun = 0;
                int currentRun = 0;
                for (int j = 0; j < M; j++) {
                    if (model.epsilon[i * M + j] == 1) {
                        longestRun = Math.Max(longestRun, ++currentRun);
                    } else {
                        currentRun = 0;
                    }
                }
                //record the longest run of ones found in the correct counter
                if (longestRun < V0) {
                    V[0]++;
                } else if (longestRun > V0 + K) {
                    V[K]++;
                } else {
                    V[longestRun - V0]++;
                }
            }

            //calculate p_value
            double chi_squared = 0.0;
            for (int i = 0; i <= K; i++) {
                chi_squared += ((V[i] - N * pi[i]) * (V[i] - N * pi[i])) / (N * pi[i]);
            }
            double p_value = Cephes.igamc((double)(K / 2.0), chi_squared / 2.0);

            if (printResults) {
                Report report = new Report("4: Test for the Longest Run of Ones in a Block");
                report.Write("\t\t\t  LONGEST RUNS OF ONES TEST\n");
                report.Write("\t\t---------------------------------------------\n");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:\n");
                report.Write("\t\t---------------------------------------------\n");
                report.Write("\t\t(a) N (# of substrings)  = "+ N);
                report.Write("\t\t(b) M (Substring Length) = " + M);
                report.Write("\t\t(c) Chi^2                = " + chi_squared);
                report.Write("\t\t---------------------------------------------\n");
                report.Write("\t\t      F R E Q U E N C Y\n");
                report.Write("\t\t---------------------------------------------\n");
                if (K == 3) {
                    report.Write("\t\t  <=1     2     3    >=4   P-value  Assignment");
                    report.Write("\t\t "+V[0]+" "+V[1]+" "+V[2]+" "+V[3]);
                } else if (K == 5) {
                    report.Write("\t\t<=4  5  6  7  8  >=9 P-value  Assignment");
                    report.Write("\t\t "+V[0]+" "+V[1]+" "+V[2]+" "+V[3]+" "+V[4]+" "+V[5]);
                } else {
                    report.Write("\t\t<=10  11  12  13  14  15 >=16 P-value  Assignment");
                    report.Write("\t\t "+V[0]+" "+V[1]+" "+V[2]+" "+V[3]+" "+V[4]+" "+V[5]+" "+V[6]);
                }
                if (p_value < 0 || p_value > 1) {
                    report.Write("WARNING:  P_VALUE IS OUT OF RANGE.\n");
                }
                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS"+"{0}\t\tp_value = "+ p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {
	        
            double[] pi = new double[6];
            double lambda = (double)(M - B.Length + 1) / Math.Pow(2, B.Length);
            double eta = lambda / 2.0;
            double total = 0.0;
            for (int i = 0; i < K; i++) { //compute prior probabilities
                pi[i] = probability(i, eta);
                total += pi[i];
            }
            pi[K] = 1 - total;

            int[] v = new int[K+1];
            for (int i = 0; i < N; i++) {   //search for a match of the template in each block
                int count = 0;
                for (int j = 0; j < M - B.Length + 1; j++) {
                    bool match = true;
                    for (int k = 0; k < B.Length; k++) {
                        if (B[k] != model.epsilon[i * M + j + k]) {
                            match = false;
                        }
                    }
                    if (match) {
                        count++;
                    }
                }
                if (count < K) {   //record the matches found
                    v[count]++;
                } else {
                    v[K]++;
                }
            }

            //compute p_value
	        double sum = 0.0;
	        double chiSquared = 0.0;
            for (int i = 0; i < K + 1; i++) {
                chiSquared += Math.Pow((double)v[i] - (double)N * pi[i], 2) / ((double)N * pi[i]);
                sum += v[i];
            }

            double p_value = Cephes.igamc(5.0 / 2.0, chiSquared / 2.0);

            if (printResults) {
                Report report = new Report("8: Overlapping Template Matching Test");
                report.Write("\t\t    OVERLAPPING TEMPLATE OF ALL ONES TEST");
                report.Write("\t\t-----------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t-----------------------------------------------");
                report.Write("\t\t(a) n (sequence_length)      = " + n);
                report.Write("\t\t(b) m (block length of 1s)   = " + B.Length);
                report.Write("\t\t(c) M (length of substring)  = " + M);
                report.Write("\t\t(d) N (number of substrings) = " + N);
                report.Write("\t\t(e) lambda [(M-m+1)/2^m]     = " + lambda);
                report.Write("\t\t(f) eta                      = " + eta);
                report.Write("\t\t(g) Chi^2                    = " + chiSquared);
                report.Write("\t\t(h) P-value                  = " + p_value);
                report.Write("\t\t-----------------------------------------------");
                report.Write("\t\t   F R E Q U E N C Y");
                report.Write("\t\t", false);
                for (int i = 0; i < K; i++) {
                    report.Write("   " + i + " ");
                }
                report.Write("  >=" + K);
                report.Write("\t\t-----------------------------------------------");
                report.Write("\t\t", false);
                for (int i = 0; i < K + 1; i++) {
                    report.Write(" " + v[i] + " ");
                }
                if (p_value < 0 || p_value > 1) { 
                    report.Write("WARNING:  P_VALUE IS OUT OF RANGE.");
                }
                report.Write(p_value+" "+(p_value < ALPHA ? "FAILURE" : "SUCCESS"));
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public unsafe override double[] run(bool printResults) {
            
            double[] X = new double[n];
            

            for (int i = 0; i < n; i++) {
                X[i] = 2 * (int)model.epsilon[i] - 1; //map all bits in binary string (0->-1,1->1)
            }
            double[] m = new double[n / 2 + 1];
            //generate DFT of the binary string, using the unsafe functions implemented in C
            fixed (double* wsavePtr = &(new double[2 * n])[0]) {
                fixed (int* ifacPtr = &(new int[15])[0]) {
                    fixed (double* mPtr = &m[0]) {
                        fixed (double* XPtr = &X[0]) {
                            FFT.__ogg_fdrffti(n, wsavePtr, ifacPtr);		//init stage for work arrays
                            FFT.__ogg_fdrfftf(n, XPtr, wsavePtr, ifacPtr);  //apply FFT on data
                        }
                    }
                }
            }

            //get magnitude of the DFT produced (to convert complex domain to real domain)
            m[0] = Math.Sqrt(X[0] * X[0]);
            for (int i = 0; i < n / 2; i++) {
                if (2 * i + 2 >= X.Length) {
                    m[i + 1] = Math.Sqrt(Math.Pow(X[2 * i + 1], 2));
                } else {
                    m[i + 1] = Math.Sqrt(Math.Pow(X[2 * i + 1], 2) + Math.Pow(X[2 * i + 2], 2));
                }
            }

	        int N_l = 0;
	        double T = Math.Sqrt(2.995732274*n); //calculate upper bound (T) (the 95% peak height threshold)

            for (int i = 0; i < n / 2; i++) {
                if (m[i] < T) {
                    N_l++;    //count observed number of peaks in |DFT| greater than T
                }
            }
            double N_0 = 0.95 * n / 2.0; //expected number of peaks
            double d = (N_l - N_0) / Math.Sqrt(n / 4.0 * 0.95 * 0.05);

            //calculate p_value
            double p_value = Cephes.erfc(Math.Abs(d) / Math.Sqrt(2.0));

            if (printResults) {
                Report report = new Report("6: Discrete Fourier Transform (Sprectral) Test");
                report.Write("\t\t\t\tFFT TEST");
                report.Write("\t\t-------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t-------------------------------------------");
                report.Write("\t\t(-) Upper Bound= " + T);
                report.Write("\t\t(b) N_l        = " + N_l);
                report.Write("\t\t(c) N_o        = " + N_0);
                report.Write("\t\t(d) d          = " + d);
                report.Write("\t\t-------------------------------------------");

                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value = " + p_value);
                model.reports.Add(report.title, report);
            }
            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            double[] Pm = new double[3];
	        int r = Math.Min(M, Q);		//compute predicted probabilities
		    double product = 1;
            for (int i = 0; i <= r - 1; i++) {
                product *= ((1.0 - Math.Pow(2, i - Q)) * (1.0 - Math.Pow(2, i - M))) / (1.0 - Math.Pow(2, i - r));
            }
            Pm[0] = Math.Pow(2, r * (M + Q - r) - M * Q) * product;
            r--;
		    product = 1;
            for (int i = 0; i <= r - 1; i++) {
                product *= ((1.0 - Math.Pow(2, i - Q)) * (1.0 - Math.Pow(2, i - M))) / (1.0 - Math.Pow(2, i - r));
            }
            Pm[1] = Math.Pow(2, r * (M + Q - r) - M * Q) * product;
            Pm[2] = 1 - (Pm[0] + Pm[1]);

            int N = n / (M * Q); //number of blocks
            int[] Fm = new int[3];
            for (int k = 0; k < N; k++) {
                //construct the matrix of MxQ in size
                int[,] matrix = new int[M, Q];
                for (int i = 0; i < M; i++) {
                    for (int j = 0; j < Q; j++) {
                        matrix[i,j] = model.epsilon[k * (M * Q) + j + i * M];
                    }
                }

                int R = computeRank(M, Q, matrix); //get the rank of the matrix

                if (R == M) {
                    Fm[0]++;	//full rank
                } else if (R == M - 1) {
                    Fm[1]++;    //full rank - 1
                }
            }
            Fm[2] = N - (Fm[0] + Fm[1]); //full rank - 2

            //compute p_value
            double chi_squared = (Math.Pow(Fm[0] - N * Pm[0], 2) / (double)(N * Pm[0]) +
                                  Math.Pow(Fm[1] - N * Pm[1], 2) / (double)(N * Pm[1]) +
                                  Math.Pow(Fm[2] - N * Pm[2], 2) / (double)(N * Pm[2]));
            double p_value = Cephes.igamc(1, chi_squared / 2.0);

            if (printResults) {
                Report report = new Report("2.5 Binary Matrix Rank Test");
                report.Write("\t\t\t\tRANK TEST");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\t(a) Probability P_"+M+" = " + Pm[0]);
                report.Write("\t\t(b)             P_"+(M - 1)+" = " + Pm[1]);
                report.Write("\t\t(c)             P_"+(M - 2)+" = " + Pm[2]);
                report.Write("\t\t(d) Frequency   F_"+M+" = " + Fm[0]);
                report.Write("\t\t(e)             F_"+(M - 1)+" = "+ Fm[1]);
                report.Write("\t\t(f)             F_"+(M - 2)+" = " + Fm[2]);
                report.Write("\t\t(g) # of matrices    = " + N);
                report.Write("\t\t(h) Chi^2            = "+chi_squared);
                report.Write("\t\t(i) NOTE: "+n % (M * Q)+" BITS WERE DISCARDED.");
                report.Write("\t\t---------------------------------------------");
                report.Write(p_value < ALPHA ? "FAILURE" : "SUCCESS"+"\t\tp_value = "+p_value);
                if (p_value < 0 || p_value > 1) {
                    report.Write("WARNING:  P_VALUE IS OUT OF RANGE.");
                }
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

	        double[] expected_value = { 0, 0, 0, 0, 0, 0, 5.2177052, 6.1962507, 7.1836656,
				                        8.1764248, 9.1723243, 10.170032, 11.168765,
				                        12.168070, 13.167693, 14.167488, 15.167379 };
	        double[] variance = { 0, 0, 0, 0, 0, 0, 2.954, 3.125, 3.238, 3.311, 3.356, 3.384,
				                  3.401, 3.410, 3.416, 3.419, 3.421 };
	        
	        int K = (int) (n/L - (double)Q); //number of test blocks
	
	        int p = (int)Math.Pow(2, L);
	        long[] T = new long[p];

            //initialization segment
            for (int i = 1; i <= Q; i++) {
                long decValue = 0;
                for (int j = 0; j < L; j++) {
                    decValue += model.epsilon[(i - 1) * L + j] * (long)Math.Pow(2, L - 1 - j);  //calculate decimal value of segment
                }
                T[decValue] = i;
            }

            //test segment
            double sum = 0;
	        for ( int i=Q+1; i<=Q+K; i++ ) {
		        long decValue = 0;
                for (int j = 0; j < L; j++) {
                    decValue += model.epsilon[(i - 1) * L + j] * (long)Math.Pow(2, L - 1 - j);  //calculate decimal value of segment
                }
		        sum += Math.Log(i - T[decValue])/Math.Log(2);
		        T[decValue] = i;
	        }
            double phi = (double)(sum / (double)K);

            //forumla from 2.9.4(5)
            double c = 0.7 - 0.8 / (double)L + (4 + 32 / (double)L) * Math.Pow(K, -3 / (double)L) / 15;
            double sigma = c * Math.Sqrt(variance[L] / (double)K);

            //calculate p_value
	        double arg = Math.Abs(phi-expected_value[L])/(Math.Sqrt(2) * sigma);
	        double p_value = Cephes.erfc(arg);
            
            if (printResults) {
                Report report = new Report("9: Maurer’s “Universal Statistical” Test");
                report.Write("\t\tUNIVERSAL STATISTICAL TEST");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\t(a) L         = "+ L);
                report.Write("\t\t(b) Q         = " + Q);
                report.Write("\t\t(c) K         = " + K);
                report.Write("\t\t(d) sum       = " + sum);
                report.Write("\t\t(f) variance  = " + variance[L]);
                report.Write("\t\t(g) exp_value = " + expected_value[L]);
                report.Write("\t\t(h) phi       = " + phi);
                report.Write("\t\t(i) WARNING:  "+(n - (Q + K) * L)+" bits were discarded.");
                report.Write("\t\t-----------------------------------------");
                if (p_value < 0 || p_value > 1) {
                    report.Write("\t\tWARNING:  P_VALUE IS OUT OF RANGE");
                }
                report.Write((p_value < ALPHA ? "FAILURE" : "SUCCESS")+"\t\tp_value = "+p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {
            int S = 0;
            int sup = 0;
            int inf = 0;
            int z = 0;
            
            for (int k = 0; k < n; k++) {
                //calculate the partial sum
                if (model.epsilon[k] == 1) {
                    S++;
                } else {
                    S--;
                }
                if (S > sup) {
                    sup++;
                }
                if (S < inf) {
                    inf--;
                }
                //compute the test statistic
                if (mode) {
                    z = (sup > -inf) ? sup : -inf;
                } else {
                    z = (sup - S > S - inf) ? sup - S : S - inf;
                }
            }

            //compute p_value
            double sum1 = 0.0;
            for (int k = (-n / z + 1) / 4; k <= (n / z - 1) / 4; k++) {
                sum1 += Cephes.normal(((4 * k + 1) * z) / Math.Sqrt(n));
                sum1 -= Cephes.normal(((4 * k - 1) * z) / Math.Sqrt(n));
            }
            double sum2 = 0.0;
            for (int k = (-n / z - 3) / 4; k <= (n / z - 1) / 4; k++) {
                sum2 += Cephes.normal(((4 * k + 3) * z) / Math.Sqrt(n));
                sum2 -= Cephes.normal(((4 * k + 1) * z) / Math.Sqrt(n));
            }
            double p_value = 1.0 - sum1 + sum2;

            if (printResults) {
                Report report = new Report("13: Cumulative Sums (Cusum) Test");
                report.Write("\t\t      CUMULATIVE SUMS (" + (mode ? "FORWARD" : "REVERSE") + ") TEST");
                report.Write("\t\t-------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t-------------------------------------------");
                report.Write("\t\t(a) The maximum partial sum = "+ z);
                report.Write("\t\t-------------------------------------------");

                if (p_value < 0 || p_value > 1) {
                    report.Write("\t\tWARNING:  P_VALUE IS OUT OF RANGE");
                }

                report.Write((p_value < ALPHA ? "FAILURE" : "SUCCESS")+"\t\tp_value = "+p_value);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value };
        }
 /// <summary>
 /// Method for running tests selected in the view
 /// </summary>
 /// <remarks>
 /// Gets data from the view object, such as file for binary string file, and which tests to run with which data
 /// </remarks>
 internal void runTests() {
     Model model = new Model(Util.str2ints(Util.loadData(view.getFilePath()), view.getSplitStr()));
     List<Test> tests = new List<Test>();
     if (view.performFrequencyTest()) {
         tests.Add(new Frequency(view.getFrequency_n(), ref model));
     }
     if (view.performBlockFrequencyTest()) {
         tests.Add(new BlockFrequency(view.getBlockFrequency_M(), view.getBlockFrequency_n(), ref model));
     }
     if (view.performRunsTest()) {
         tests.Add(new Runs(view.getRuns_n(), ref model));
     }
     if (view.performLongestRunOfOnesTest()) {
         tests.Add(new LongestRunOfOnes(view.getLongestRunOfOnes_n(), ref model));
     }
     if (view.performMatrixRankTest()) {
         tests.Add(new Rank(view.getMatrixRank_n(), ref model));
     }
     if (view.performDiscreteFourierTransformTest()) {
         tests.Add(new DiscreteFourierTransform(view.getDiscreteFourierTransform_n(), ref model));
     }
     if (view.performNonOverlappingTemplateMatchingTest()) {
         if (view.performSingleNonOverlappingTemplateMatchingTest()) {
             tests.Add(new NonOverlappingTemplateMatching(view.getNonOverlappingTemplateMatching_B(), view.getNonOverlappingTemplateMatching_n(), ref model));
         } else {
             tests.Add(new NonOverlappingTemplateMatching(view.getNonOverlappingTemplateMatching_m(), view.getNonOverlappingTemplateMatching_n(), ref model));
         }
     }
     if (view.performOverlappingTemplateMatchingTest()) {
         tests.Add(new OverlappingTemplateMatching(view.getOverlappingTemplateMatching_B(), view.getOverlappingTemplateMatching_n(), ref model));
     }
     if (view.performUniversalTest()) {
         if (view.performCustomUniversalTest()) {
             tests.Add(new Universal(view.getUniversal_L(), view.getUniversal_Q(), view.getUniversal_n(), ref model));
         } else {
             tests.Add(new Universal(view.getUniversal_n(), ref model));
         }
     }
     if (view.performLinearComplexityTest()) {
         tests.Add(new LinearComplexity(view.getLinearComplexity_M(), view.getLinearComplexity_n(), ref model));
     }
     if (view.performSerialTest()) {
         tests.Add(new Serial(view.getSerial_m(), view.getSerial_n(), ref model));
     }
     if (view.performApproximateEntropyTest()) {
         tests.Add(new ApproximateEntropy(view.getApproximateEntropy_m(), view.getApproximateEntropy_n(), ref model));
     }
     if (view.performCumulativeSumsTest()) {
         tests.Add(new CumulativeSums(view.performForwardCumulativeSumsTest(), view.getCumulativeSums_n(), ref model));
     }
     if (view.performRandomExcursionsTest()) {
         tests.Add(new RandomExcursions(view.getRandomExcursions_n(), ref model));
     }
     if (view.performRandomExcursionsVariantTest()) {
         tests.Add(new RandomExcursionsVariant(view.getRandomExcursionsVariant_n(), ref model));
     }
     view.showProgressBar();
     view.updateProgressBar(10);
     Report full = new Report("All Tests");
     foreach (Test t in tests) {
         t.run(true);
         view.updateProgressBar(90 / tests.Count);
         full.Write(model.reports.Last().Value.body);
     }
     model.reports.Add(full.title, full);
     new ReportsForm(model.reports).Show();
     view.hideProgressBar();
 }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            int[] stateX = { -4, -3, -2, -1, 1, 2, 3, 4 };
  
            double[,] pi = { {0.0000000000, 0.00000000000, 0.00000000000, 0.00000000000, 0.00000000000, 0.0000000000}, 
						     {0.5000000000, 0.25000000000, 0.12500000000, 0.06250000000, 0.03125000000, 0.0312500000},
						     {0.7500000000, 0.06250000000, 0.04687500000, 0.03515625000, 0.02636718750, 0.0791015625},
						     {0.8333333333, 0.02777777778, 0.02314814815, 0.01929012346, 0.01607510288, 0.0803755143},
						     {0.8750000000, 0.01562500000, 0.01367187500, 0.01196289063, 0.01046752930, 0.0732727051} };

            Report report = new Report("14: Random Excursions Test");   

            //determine cycles
            int J = 0;
            int[] S_k = new int[n];
            S_k[0] = 2 * (int)model.epsilon[0] - 1;
            int[] cycle = new int[Math.Max(1000, n / 100)];
            for (int i = 1; i < n; i++) {
                S_k[i] = S_k[i - 1] + 2 * model.epsilon[i] - 1;
                if (S_k[i] == 0) {
                    J++;
                    if (J > Math.Max(1000, n / 100)) {
                        if (printResults) {
                            report.Write("ERROR IN FUNCTION randomExcursions:  EXCEEDING THE MAX NUMBER OF CYCLES EXPECTED.");
                        }
                        return null;
                    }
                    cycle[J] = i;
                }
            }
            if (S_k[n - 1] != 0) {
                J++;
            }
            cycle[J] = n;

            if (printResults) {
                report.Write("\t\t\t  RANDOM EXCURSIONS TEST");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\tCOMPUTATIONAL INFORMATION:");
                report.Write("\t\t--------------------------------------------");
                report.Write("\t\t(a) Number Of Cycles (J) = " + J);
                report.Write("\t\t(b) Sequence Length (n)  = " + n);
            }

            double constraint = Math.Max(0.005 * Math.Pow(n, 0.5), 500);
            double[] p_values = new double[8];
            if (J < constraint) {
                if (printResults) {
                    report.Write("\t\t---------------------------------------------");
                    report.Write("\t\tWARNING:  TEST NOT APPLICABLE.  THERE ARE AN");
                    report.Write("\t\t\t  INSUFFICIENT NUMBER OF CYCLES.");
                    report.Write("\t\t---------------------------------------------");
                    for (int i = 0; i < 8; i++) {
                        report.Write(0.0.ToString());
                    }
                }
            } else {
                if (printResults) {
                    report.Write("\t\t(c) Rejection Constraint = " + constraint);
                    report.Write("\t\t-------------------------------------------");
                }

                double[,] v = new double[6, 8];
                int cycleStart = 0;
                int cycleStop = cycle[1];
                for (int k = 0; k < 6; k++) {
                    for (int i = 0; i < 8; i++) {
                        v[k, i] = 0.0;
                    }
                }
                int[] counter = new int[8];
                //for each cycle compute frequency of x
                for (int j = 1; j <= J; j++) {
                    for (int i = 0; i < 8; i++) {
                        counter[i] = 0;
                    }
                    for (int i = cycleStart; i < cycleStop; i++) {
                        if ((S_k[i] >= 1 && S_k[i] <= 4) || (S_k[i] >= -4 && S_k[i] <= -1)) {
                            int b = S_k[i]<0 ? 4 : 3;
                            counter[S_k[i] + b]++;
                        }
                    }
                    cycleStart = cycle[j] + 1;
                    if (j < J) {
                        cycleStop = cycle[j + 1];
                    }

                    for (int i = 0; i < 8; i++) {
                        if ((counter[i] >= 0) && (counter[i] <= 4)) {
                            v[counter[i], i]++;
                        } else if (counter[i] >= 5) {
                            v[5, i]++;
                        }
                    }
                }

                //calculate p_values
                for (int i = 0; i < 8; i++) {
                    int x = stateX[i];
                    double sum = 0;
                    for (int k = 0; k < 6; k++) {
                        sum += Math.Pow(v[k, i] - J * pi[(int)Math.Abs(x), k], 2) / (J * pi[(int)Math.Abs(x), k]);
                    }
                    p_values[i] = Cephes.igamc(2.5, sum / 2.0);

                    if (printResults) {
                        if (p_values[i] < 0 || p_values[i] > 1) {
                            report.Write("WARNING:  P_VALUE IS OUT OF RANGE.");
                        }

                        report.Write(p_values[i] < ALPHA ? "FAILURE" : "SUCCESS"+"\t\tx = "+x+" chi^2 = "+sum+" p_value = "+p_values[i]);
                    }

                }
            }
            if (printResults) {
                model.reports.Add(report.title, report);
            }
            return p_values;
        }
        /// <summary>
        /// Runs the test
        /// </summary>
        /// <param name="printResults">If true text output will be added to a log, otherwise not</param>
        /// <returns>The p_value(s) of the test based upon the input data</returns>
        public override double[] run(bool printResults) {

            //calculate all psi_squares
            double psim0 = psi2(m, n);
            double psim1 = psi2(m - 1, n);
            double psim2 = psi2(m - 2, n);
            double del1 = psim0 - psim1;
            double del2 = psim0 - 2.0 * psim1 + psim2;

            //calculate p_values
            double p_value1 = Cephes.igamc(Math.Pow(2, m - 1) / 2, del1 / 2.0);
            double p_value2 = Cephes.igamc(Math.Pow(2, m - 2) / 2, del2 / 2.0);

            if (printResults) {
                Report report = new Report("11: Serial Test");
                report.Write("\t\t\t       SERIAL TEST");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\t COMPUTATIONAL INFORMATION:		  ");
                report.Write("\t\t---------------------------------------------");
                report.Write("\t\t(a) Block length    (m) = " + m);
                report.Write("\t\t(b) Sequence length (n) = " + n);
                report.Write("\t\t(c) Psi_m               = " + psim0);
                report.Write("\t\t(d) Psi_m-1             = " + psim1);
                report.Write("\t\t(e) Psi_m-2             = " + psim2);
                report.Write("\t\t(f) Del_1               = " + del1);
                report.Write("\t\t(g) Del_2               = " + del2);
                report.Write("\t\t---------------------------------------------\n");
                report.Write(p_value1 < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value1 = " + p_value1);
                report.Write(p_value2 < ALPHA ? "FAILURE" : "SUCCESS" + "\t\tp_value2 = " + p_value2);
                model.reports.Add(report.title, report);
            }

            return new double[] { p_value1, p_value2 };
        }