//*******************************************************************************************************************************************
// E-Z Reader, ver. 10b; (c) 2025, Erik D. Reichle, Ph.D.
//
// Notes: 
// (1) Release date: 23 January 2025
// (2) This program implements the version of E-Z Reader described by both Reichle (2021, Computational Models of Reading: A Handbook) 
//     and Veldre et al. (2021, Journal of Experimental Psychology: General). 
//*******************************************************************************************************************************************

package ezreader10;

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

public class EZReader10 {
 
    // I/O file names:
    static String corpusFileName = "SRC98-ELP"; // file containing the sentence corpus   
    static String outputFileName = "SimulationResults"; // simulation results wil be written to this file
    static String targetFileName = "SRC98Targets"; // file containing a list of within-sentence locations of target words
    
    // Model free parameters:
    static double A = 25.0; // attention shift latency
    static double Alpha1 = 124.0; // lexical processing rate intercept
    static double Alpha2 = 11.1; // lexical processing rate word-frequency slope
    static double Alpha3 = 76.0; // lexical processing rate word-predictability slope
    static double Delta = 1.68; // L2 vs. L1 proportional difference
    static double Epsilon1 = 0.1; // degradation due to acuity
    static double Epsilon2 = 0.5; // masking from medial letters
    static double Epsilon3 = 1.0; // masking from lateral letters
    static double Eta1 = 0.5; // saccade random error intercept
    static double Eta2 = 0.1; // saccade random error slope
    static double I = 50.0; // post-lexical integration latency
    static double ITarget = 50.0; // post-lexical integration latency (of target words)
    static double Lambda = 0.25; // automatic refixation probability slope
    static double M1 = 150.0; //labile saccadic programming latency
    static double M2 = 25.0; // non-labile saccadic programming latency
    static double Omega1 = 6.0; // systematic range error intercept
    static double Omega2 = 3.0; // systematic range error slope
    static double pF = 0.01; // probability of integration failure
    static double pFTarget = 0.01; // probability of integration failure (for target words)
    static double Psi = 7.0; // preferred saccade length
    static double S = 25.0; // saccade latency
    static double SigmaGamma = 20.0; // controls standard deviation gamma distribution
    static double V = 60.0; // pre-attentive visual processig (i.e., eye-mind latency)
    static double Xi = 0.5; // proportion of labile programming related to target selection
    
    // Fixed simulation parameters:
    static int maxLength = 16; // i.e., number letters + black space to the left of the word
    static int maxSentenceLength = 50; // maximum number of words per sentence
    static double minValue = 0.00001; // value to prevent using zero values
    static int NFrequencyClasses = 8; // number of frequency classes
    static int NSentences = 48; // number of sentences in corpus
    static int NSubjects = 1000; // number of simulated readers
    
    // Simulation toggles:
    static boolean includeRegressionTrials = false; // include trials with inter-word regressions?
    static boolean includeTargetWords = false; // include specific targets words?
       
    // Display toggles:
    static boolean displayCorpus = false; // IVs for each word in corpus
    static boolean displayFrequencyClassMeans = false; // frequency-class means
    static boolean displayMeanDistributions = false; // display mean distributions for each word length
    static boolean displayWordStatistics = false; // mean DVs for each word
    static boolean displayParameters = true; // model parameter values
    static boolean displayPrRegression = false; // proportion of trials having inter-word regressions
    static boolean displayRMSD = false; // goodness-of-fit metric
    static boolean displayStates = false; // E-Z Reader's internal states
    static boolean displayTraces = false; // traces showing individual fixations
    
    // Initialize Schilling et al. (1998) observed means and standard deviations [SD = p (1 - p); see the Appendix of Reichle et al., 1998]
    // using frequency norms from the English Lexicon Project (Balota et al., 2007):
    static double[] obsMeanFFD = new double[] { 274, 237, 243, 246, 238, 242, 224, 205 }; // first-fixation durations
    static double[] obsSDFFD = new double[] { 42, 20, 20, 37, 36, 37, 74, 84 };
    static double[] obsMeanGD = new double[] { 336, 284, 304, 301, 266, 260, 235, 209 }; // gaze durations
    static double[] obsSDGD = new double[] { 60, 26, 53, 65, 45, 52, 82, 86 }; 
    static double[] obsMeanSFD = new double[] { 281, 257, 253, 260, 246, 246, 228, 204 }; // single-fixation durations
    static double[] obsSDSFD = new double[] { 52, 21, 26, 45, 37, 39, 82, 84 };                   
    static double[] obsMeanPr1 = new double[] { 0.725, 0.655, 0.695, 0.669, 0.714, 0.575, 0.373, 0.311 }; // probability of making single fixation
    static double[] obsSDPr1 = new double[] { 0.446, 0.475, 0.460, 0.471, 0.452, 0.494, 0.484, 0.463 };       
    static double[] obsMeanPr2 = new double[] { 0.203, 0.177, 0.254, 0.223, 0.128, 0.053, 0.025, 0.008 }; // probability of making 2+ fixations
    static double[] obsSDPr2 = new double[] { 0.402, 0.382, 0.435, 0.416, 0.334, 0.224, 0.157, 0.088 };        
    static double[] obsMeanPrS = new double[] { 0.071, 0.167, 0.051, 0.108, 0.158, 0.372, 0.601, 0.681 }; // probability of skipping
    static double[] obsSDPrS = new double[] { 0.258, 0.373, 0.220, 0.311, 0.364, 0.483, 0.490, 0.466 }; 
    
    //**************************************************************************
    
    public static void main(String[] args) 
            throws FileNotFoundException {
        
        // Initialize classes:
        Display display = new Display();
        EZReader ezreader = new EZReader();
        Stats stats = new Stats();
        
        // Initialize target words:
        int targets[] = new int[NSentences];
        if (includeTargetWords == true) {
            Scanner diskScanner1 = new Scanner(new File(targetFileName + ".txt"));
            int k = 0;            
            while (diskScanner1.hasNext()) {
                targets[k] = diskScanner1.nextInt();
                //System.out.printf(" target = %d\n", targets[k]);
                k++;
            }
            System.out.printf(" Targets initialized.\n");
        }
        else for (int i = 0; i < NSentences; i++) targets[i] = 0;
        
        // Initialize sentence corpus:
        Scanner diskScanner2 = new Scanner(new File(corpusFileName + ".txt")); 
        ArrayList<Sentence> text = new ArrayList<>();
        Corpus corpus = new Corpus();
        corpus.initialize(diskScanner2, text, targets);
        System.out.printf(" Corpus initialized.\n");
        
        // Initialize output file:
        PrintStream diskWriter = new PrintStream(new File(outputFileName + ".txt")); 
        System.out.printf(" Output file initialized.\n");
        System.out.printf(" ------------------------\n\n");
        
        // Display model's parameters and properties of words in sentence corpus:
        if (EZReader10.displayParameters == true) display.parameters(diskWriter);
        if (displayCorpus == true) display.corpus(diskWriter, text);
                
        // Execute grid-search of model's parameter space:
        int run = 0;
        double var1, var2, var3, var4; 
        // Note: The starting, stopping, and increment values can be set to define the domains of 1-4 model parameter.
        for (var1 = 0; var1 < 0.01; var1 += 1.0) { 
            for (var2 = 0; var2 < 0.01; var2 += 1.0) { 
                for (var3 = 0; var3 < 0.01; var3 += 1.0) {  
                    for (var4 = 0; var4 < 0.01; var4 += 1.0) {   
                        //L1 = var1; // Note: De-comment and set 1-4 parameters equal to these values (example provided).
                        // = var2;
                        // = var3;
                        // = var4;
                        //System.out.printf(" %1d %1.2f %1.2f %1.2f %1.2f ", run++, var1, var2, var3, var4);

                        // Run model:
                        corpus.zeroWordDVs(text);
                        for (int subject = 0; subject < NSubjects; subject++) {
                            
                            // Run model:
                            ezreader.run(diskWriter, stats, text);  
                        } 
                        
                        // Calculate statistics:
                        stats.analyses(diskWriter, text);
                    }
                }
            }
        }
        System.out.printf("\n ------------------------\n");
        System.out.printf(" Simulation complete.\n\n");        
    }
}

//*******************************************************************************************************************************************
