1 Extracting Behavioral Data from E-Prime

  1. Run E-Merge
  2. Navigate to the folder with the task data
  3. Select Merge
  4. Select Recursive Merge
  5. Only merge: All specified files regardless of merge status
  6. Save target file (filename.emrg2)
  7. Double click the newly saved file (filename.emrg2) to open it in E-DataAid
  8. Go to File, Export
  9. Make sure SPSS is selected and that the Unicode option is unselected: https://mattkmiecik.com/posts/post-Stop-Using-Excel-to-Preprocess-E-Prime-Data/post-Stop-Using-Excel-to-Preprocess-E-Prime-Data.html (archived at https://perma.cc/N734-4W9X)
  10. Save the file as: filename.txt

2 ERP Processing Pipeline using HAPPE

  1. Download MATLAB: https://its.uiowa.edu/matlab
    • Click How to Install MatLab and follow the relevant instructions
    • Create a MatLab folder in your local directory. You will keep all of your MatLab related files in this folder.
  2. Install the HAPPE pipeline: https://github.com/PINE-Lab/HAPPE
    • Clone the HAPPE repository to your GitHub account
    • Open the HAPPE folder
    • Open the HAPPE User Guide document
    • Read through the HAPPE User Guide
    • Navigate to the Setting up HAPPE section in the user guide and follow the instructions for setting up the HAPPE pipeline, including installation of add-ons and eeglab
  3. Install EP Toolkit: https://sourceforge.net/projects/erppcatoolkit/
    • After downloading, copy the EP_Toolkit folder to your Matlab folder (in your local directory)
    • In the EP_toolkit folder:
      • Open EP_Toolkit
      • Open Documentation
      • Open tutorial
    • In the tutorial document, navigate to the Set Up section and follow the instructions for installing and setting up EP Toolkit and FieldTrip. Do NOT follow instructions for setting up EEGLAB. You have already set up your path to EEGLAB when you set up the HAPPE pipeline.
    • You should have the following subfolders in your MatLab folder:
      • EP_Toolkit
      • Fieldtrip-[version number]
  4. Open the HAPPE pipeline V4 script in MATLAB
    • Follow the User Guide instructions for the HAPPE-ER pipeline, using the user inputs described below.
    • Alternatively, open the MATLAB processing file that runs the HAPPE-ER pipeline and deals with the output.
      • e.g., /Data Processing/6. MATLAB EEG Pipeline/eegProcessingOddball.m

2.1 Oddball Task

  1. User Inputs
    • Enter the path to the folder containing the dataset.
    • Select raw
    • Load pre-existing set of input parameter:
      • N if this is your first time running data through the pipeline.
      • Y if you have decided on a set of parameters. Enter the path to the folder containing the input parameters.
    • Low density data: N
    • Data type: task
    • Performing event-related potential (ERP) analysis: Y
    • Enter the task onset tags
      • Target: tgt+
      • Frequent: frq+
      • done
    • Do multiple onset tags belong to a single condition? N
    • File format: 5
    • Acquisition layout type: 2
    • Number of channels: 128
    • Do you have additional type fields besides “code”? N
    • Select channels of interest: all
    • Frequency of electrical noise in Hz: 60
    • Are there any additional frequencies, (e.g., harmonics) to reduce? N
    • Line Noise reduction method: notch
      • Low cutoff: 59
      • high cutoff: 61
    • Resample: N
    • Filter
      • Low Pass Cutoff: 30
      • High Pass Cutoff: .1
    • Choose a filter: fir
    • Bad Channel Detection: Y
      • after wavelet thresholding
    • ECGone: N
    • Wavelet Thresholding
      • default
      • Threshold rule: hard
    • MuscIL: N
    • Segmentation: Y
      • Starting parameter for stimulus: -200
      • Ending parameter for stimulus: 1000
      • Task offset: 2
    • Baseline Correction: Y
      • Baseline Correction start: -200
      • Baseline Correction end: 0
    • Interpolation: Y
    • Segment Rejection: Y
      • Segment Rejection Method: amplitude
        • minimum segment rejection threshold: -150
        • maximum segment rejection threshold: 150
        • segment rejection based on all channels or ROI: all
    • Re-referencing: Y
      • Does your data contain a flatline or all zero reference channel? N
      • re-referencing method: average
    • Save format: 1
    • Visualizations: N
    • Parameter file save name: default

2.2 Fish/Shark

  1. User Inputs
    • Enter the path to the folder containing the dataset.
    • Select raw
    • Load pre-existing set of input parameter:
      • N if this is your first time running data through the pipeline.
      • Y if you have decided on a set of parameters. Enter the path to the folder containing the input parameters.
    • Low density data: N
    • Data type: task
    • Performing event-related potential (ERP) analysis: Y
    • Enter the task onset tags
      • Correct Go: cGo++
      • Incorrect Go: xGo++
      • Correct NoGo: cNoGo++
      • Incorrect NoGo: xNoGo++
      • done
    • Do multiple onset tags belong to a single condition? N
    • File format: 5
    • Acquisition layout type: 2
    • Number of channels: 128
    • Do you have additional type fields besides “code”? N
    • Select channels of interest: all
    • Frequency of electrical noice in Hz: 60
    • Are there any additional frequencies, (e.g., harmonics) to reduce? N
    • Line Noise reduction method: notch
      • Low cutoff: 59
      • high cutoff: 61
    • Resample: N
    • Filter
      • Low Pass Cutoff: 30
      • High Pass Cutoff: .1
    • Choose a filter: fir
    • Bad Channel Detection: Y
      • after wavelet thresholding
    • ECGone: N
    • Wavelet Thresholding
      • default
      • Threshold rule: hard
    • MuscIL: N
    • Segmentation: Y
      • Starting parameter for stimulus: -200
      • Ending parameter for stimulus: 1000
      • Task offset: 17
    • Baseline Correction: Y
      • Baseline Correction start: -200
      • Baseline Correction end: 0
    • Interpolation: Y
    • Segment Rejection: Y
      • Segment Rejection Method: amplitude
        • minimum segment rejection threshold: -150
        • maximum segment rejection threshold: 150
        • segment rejection based on all channels or ROI: all
    • Re-referencing: Y
      • Does your data contain a flatline or all zero reference channel? N
      • re-referencing method: average
    • Save format: 1
    • Visualizations: N
    • Parameter file save name: default

2.3 Stop-Signal

3 MATLAB Scripts to Manage HAPPE Files

We have scripts for each task that can prepare files for the HAPPE Pipeline and/or manage the files outputted from HAPPE. These actions can be done manually as well, but the MATLAB scripts make the process more efficient. The scripts will also generate a “log” of all of the files processed through HAPPE to facilitate tracking of EEG data processing. The sections below detail the code used to perform these actions as well as the instructions for using the current scripts.

Note: Before using the scripts/code detailed below, ensure that all filepaths used are in your MATLAB path collection. These may include:

  • The location where the automatic script is stored (for our lab, this is under /Data Processing/6. MATLAB EEG Pipeline)
  • The location where the HAPPE pre-processing script is stored
  • The location of the raw data (to be processed)
  • The location(s) of any intermediate files for processing (e.g., the updated .mff files that contain accuracy information in FishShark)
  • The location(s) for any files outputted by HAPPE and/or places you wish to use the script to move them to

3.1 Oddball (Pre-HAPPE)

  1. Update all thresholds and filepaths in script file (must be done BEFORE running the script)
    • In the second section of our script file, we set our “threshold” for the minimum number of trials that need to be retained after pre-processing for a subject’s data to be eligible for PCA. Additional thresholds can also be set for things like number of channels retained, but these are not currently in use.
    % Set quality threshold parameters
    trialCutoff = 10;
    • We also set environment variables with all of the filepaths that are relevant for managing HAPPE output files and tracking processed data. The following paths should be checked and updated as necessary to reflect the organization of processing on your computer.
      • passPath is the location you wish to have files that meet or exceed the above-defined thresholds to be saved
      • allPath is the location you wish to have ALL files outputted from HAPPE saved to (regardless of whether threshold is met or not)
      • failPath is the location you wish to have files that do not meet the above-defined thresholds to be copied to
        • .mff files that do not meet threshold will be copied here as an indication that they should be processed manually to see if they meet threshold afterward
      • summPath is the location you wish to save the file that lists all files processed through HAPPE in the current batch
        • We currently use this to save the “processing log” to a location that all team members/computers have access to so it is easier to determine which files require processing when EEG data are not stored on a shared server
    % Set paths for file sorting
    passPath = 'V:\Processing-Repo\Folder Structure\3 - Files for PCA'; %location for .txt output files
    allPath = 'V:\Processing-Repo\Folder Structure\2 - Processed Files'; %location for all processed files to end up
    failPath = 'V:\Processing-Repo\Folder Structure\1b - Manual Processing'; %location to copy unsuccessful .mff to for manual process
    
    % Set path for processing summary
    summPath = 'Z:\Shared Server\Study Folder\Data Processing\6. MATLAB EEG Pipeline\Processed Data Logs';
  2. Run the HAPPE Pipeline
    • This first section is designed to rely on user input. Press “Run” on the MATLAB editor window with the file open to begin the process.
    • A message will appear in the console prompting you to enter the filepath to the location of the HAPPE pre-processing file you wish to run
    • Once the path is entered, MATLAB will run the file
    % Set path to HAPPE pre-processing script
    happeRun = input('Enter the full path to the HAPPE pre-processing file:\n> ','s') ;
    
    % Call and run HAPPE pre-processing script
    run(happeRun);
  3. Enter HAPPE inputs
    • See the above section for HAPPE user inputs for Oddball

At this point, no more user interaction is required for the script to do its job. The HAPPE pipeline will run, and the remaining MATLAB code in the script file will evaluate the files outputted by HAPPE and move them to the appropriate locations based on this evaluation. See General Post-Happe Steps for examples of the MATLAB code and an explanation of what it does.

3.2 FishSharks (Pre-HAPPE)

  1. Update all thresholds and filepaths in script file (must be done BEFORE running the script)
    • In the fifth section of our script file, we set our “threshold” for the minimum number of trials that need to be retained after pre-processing for a subject’s data to be eligible for PCA. Additional thresholds can also be set for things like number of channels retained, but these are not currently in use.
    • Note that setting these parameters occurs later in the FishSharks script than the one for Oddball; this is because FishSharks files require additional processing before they are ready for HAPPE (see step 2 for details)
    % Set quality threshold parameters
    trialCutoff = 10;
    • We also set environment variables with all of the filepaths that are relevant for managing HAPPE output files and tracking processed data. The following paths should be checked and updated as necessary to reflect the organization of processing on your computer.
      • passPath is the location you wish to have files that meet or exceed the above-defined thresholds to be saved
      • allPath is the location you wish to have ALL files outputted from HAPPE saved to (regardless of whether threshold is met or not)
      • failPath is the location you wish to have files that do not meet the above-defined thresholds to be copied to
        • .mff files that do not meet threshold will be copied here as an indication that they should be processed manually to see if they meet threshold afterward
      • summPath is the location you wish to save the file that lists all files processed through HAPPE in the current batch
        • We currently use this to save the “processing log” to a location that all team members/computers have access to so it is easier to determine which files require processing when EEG data are not stored on a shared server
    % Set paths for file sorting
    passPath = 'V:\Processing-Repo\Folder Structure\3 - Files for PCA'; %location for .txt output files
    allPath = 'V:\Processing-Repo\Folder Structure\2 - Processed Files'; %location for all processed files to end up
    failPath = 'V:\Processing-Repo\Folder Structure\1b - Manual Processing'; %location to copy unsuccessful .mff to for manual process
    
    % Set path for processing summary
    summPath = 'Z:\Shared Server\Study Folder\Data Processing\6. MATLAB EEG Pipeline\Processed Data Logs';
  2. Update raw (.mff) files’ condition tags with accuracy information
    • Unlike the Passive Oddball task, FishSharks trials can be either correct or incorrect. Whether or not a trial was “responded to” correctly is relevant to the nature of the extracted ERP. Because the event tags in our .mff files do not inherently contain information about whether each trial was responded to correctly, we need to add it ourselves. This process can be done manually in NetStation, but it can also be automated using the MATLAB code detailed below.
    • NOTE: This code requires eeglab. Before running the code, open eeglab in your MATLAB session by typing eeglab into the console. You can close it as soon as it opens, but this step ensures that eeglab is loaded into your current session and helps prevent the subsequent code from erroring out.
    1. Set the filepaths for raw and updated .mff files

      • This section is set to rely on user inputs. Press “Run” in the MATLAB editor to start the processing.
      • A message will appear in the console prompting you to enter two filepaths: the first is the location of the raw .mff files, and the second is the location you would like the updated files to save in.
      % User input for location of raw files
      pathRaw = input('Enter the full path to the folder containing the raw files:\n> ','s');
      
      % User input for destination of subsetted files
      pathSub = input('Enter the full path to the folder in which to save the subsetted files:\n> ','s');

      At this point, there will be no user input/actions necessary until all of the .mff files in the “pathRaw” directory have been updated and saved into the “pathSub” directory. The code that asks the user for the path to HAPPE will run when that process has finished. The following section will describe the code used to automate the process of updated .mff event tags to include accuracy information at the trial level. Move on to Step 3 when the process has completed.

    2. Gather and manage information from the directory housing the raw (.mff) files

      % Have MATLAB gather a list of raw files housed in specified location (pathRaw)
      dirInfo = struct2table(dir(pathRaw));
      
      % Remove blank rows
      noName = strcmp(dirInfo.name, '.') | strcmp(dirInfo.name, '..');
      dirInfo(noName, :) = [];
    3. Generate variables necessary for managing raw and updated files

      • This code will generate the full filepaths (including file name) necessary for reading in and saving each .mff file and its updated counterpart
      • It will also generate an ID variable for joining purposes based on the expected location of the subject name in the name of each file
      % Generate ID variable
      dataFiles = dirInfo(:, "name");
      
      % Add ID variable to file data
      dataFiles.ID = extractBefore(dataFiles.name, 8);
      
      % Generate path to read raw data
      rawPaths = dataFiles;
      rawPaths.path = strcat({pathRaw}, "/", dirInfo.name);
      rawPaths = rawPaths(:, ["ID", "path"]);
      
      % Generate path to save updated versions of the data (containing accuracy info at trial level)
      subPaths = dataFiles;
      subPaths.path = strcat({pathSub}, "/", subPaths.ID, "_sub_fishshark.mff");
      subPaths = subPaths(:, ["ID", "path"]);
      
      % Join filepath datatables
      mergePaths = join(rawPaths, subPaths, 'Keys', {'ID'})
    4. Use a loop to update the event tags in each .mff file to reflect accuracy of response

      • For every file included in the “mergePaths” dataset, MATLAB will perform the following actions:
        • Set environment variables representing the path to read in the original .mff file and save its updated counterpart (updates with each iteration of the loop)
        • Read in the .mff file
        • Extract the “event” information from the .mff file
        • Evaluate whether there is usable data in the file
          • This step prevents the code from erroring out if a subject did not make it past the practice trials
          • If the evaluation determines that there is NOT usable data in the present file, the loop will jump to the next file
        • Create a table containing response information (response vs no response and reaction time) and condition information (go vs no-go) at the trial level
        • Evaluate each trial to determine whether the response was correct or incorrect
          • For No-Go trials, responses are CORRECT if there was no response (incorrect if subject did respond)
          • For Go trials, responses are CORRECT if there was a response AND subject’s reaction time was at least 200 ms (incorrect if subject did not respond OR if subject responded too quickly for response to be considered “valid”)
        • Update the event tags in the .mff file to contain a “c” for correct trials and an “x” for incorrect trials
          • The c/x indicator will be appended to the front of the existing event tag (e.g., Go++ will become cGo++)
        • Export an updated version of the .mff file with accuracy information to the specified location (“pathSub”)
      for row = 1:height(mergePaths)
          % Specify paths
          rawFolder = mergePaths{row, "path_rawPaths"}
          subFolder = mergePaths{row, "path_subPaths"}
      
          % Read in EEG data
          EEGraw = pop_mffimport(char(rawFolder), 'code')
      
          % Create table from "event" field of raw data
          EEGevent = struct2table(EEGraw.event)
      
          % Check for the existence of usable rows
          checkVars = strcmp(EEGevent.Properties.VariableNames, 'mffkey_cel')
      
          % Skip files without necessary variables
          if max(checkVars) == 0
              continue
          end
      
          % Create table without practice/training trials
          keepRows = strcmp(EEGevent.mffkey_cel, '4')
          EEGsub  = EEGevent(keepRows, :)
      
          % Check for the existence of usable rows
          checkRows = max(keepRows)
      
          % Skip files with no usable rows
          if checkRows == 0
              continue
          end
      
          % Get response info at trial level
          EEGresp = table(EEGsub.mffkey_obs, EEGsub.mffkey_eval, EEGsub.mffkey_rtim)
          EEGresp = rmmissing(EEGresp)
          EEGresp = renamevars(EEGresp, ["Var1", "Var2", "Var3"], ["Trial", "Eval", "RTime"])
      
          % Get condition info at trial level
          EEGconds = table(EEGsub.mffkey_obs, EEGsub.type)
          EEGconds = renamevars(EEGconds, ["Var1", "Var2"], ["Trial", "Cond"])
          keepConds = strcmp(EEGconds.Cond, 'Go++') | strcmp(EEGconds.Cond, 'NG++')
          EEGcond = EEGconds(keepConds, :)
      
          % Merge datasets
          EEGtrials = join(EEGcond, EEGresp)
      
          EEGtrials.RTime = cellfun(@str2num, EEGtrials.RTime)
          % Evaluate trials for correct-ness of response
          correct = strcmp(EEGtrials.Cond, 'Go++') & strcmp(EEGtrials.Eval, '1') & EEGtrials.RTime > 200 | strcmp(EEGtrials.Cond, 'NG++') & strcmp(EEGtrials.Eval, '0')
          EEGtrials.Acc = correct
      
      
          % Create new code tags including accuracy information
          EEGtrials.newCode(EEGtrials.Acc & strcmp(EEGtrials.Cond, 'Go++')) = {'cGo++'}
          EEGtrials.newCode(~EEGtrials.Acc & strcmp(EEGtrials.Cond, 'Go++')) = {'xGo++'}
          EEGtrials.newCode(EEGtrials.Acc & strcmp(EEGtrials.Cond, 'NG++')) = {'cNG++'}
          EEGtrials.newCode(~EEGtrials.Acc & strcmp(EEGtrials.Cond, 'NG++')) = {'xNG++'}
      
          % Subset information for merge
          EEGmerge = EEGtrials(:, {'Trial', 'Cond', 'newCode'})
      
          % Prep key in original data
          EEGevent.key = strcat(EEGevent.mffkey_obs, EEGevent.type)
      
          % Prep key in merge data
          EEGmerge.key = strcat(EEGmerge.Trial, EEGmerge.Cond)
          EEGmerge = EEGmerge(:, {'key', 'newCode'})
      
          % Merge new codes with event table
          EEGnew = outerjoin(EEGevent, EEGmerge)
      
          % Replace codes where new code is needed
          EEGnew.code(~strcmp(EEGnew.newCode, '')) = EEGnew.newCode(~strcmp(EEGnew.newCode, ''))
          EEGnew.type(~strcmp(EEGnew.newCode, '')) = EEGnew.newCode(~strcmp(EEGnew.newCode, ''))
          EEGnew = table2struct(EEGnew(:, 1:28))
      
          % Replace event table in original struct
          EEGraw.event = EEGnew
      
          % Export updated file
          pop_mffexport(EEGraw, char(subFolder))
      end 
  3. Run the HAPPE Pipeline
    • This section is designed to rely on user input.
    • When the prior steps have finsihed, a message will appear in the console prompting you to enter the filepath to the location of the HAPPE pre-processing file you wish to run
    • Once the path is entered, MATLAB will run the file
    % Set path to HAPPE pre-processing script
    happeRun = input('Enter the full path to the HAPPE pre-processing file:\n> ','s') ;
    
    % Call and run HAPPE pre-processing script
    run(happeRun);
  4. Enter HAPPE inputs
    • See the above section for HAPPE user inputs for FishSharks

At this point, no more user interaction is required for the script to do its job. The HAPPE pipeline will run, and the remaining MATLAB code in the script file will evaluate the files outputted by HAPPE and move them to the appropriate locations based on this evaluation. See General Post-Happe Steps for examples of the MATLAB code and an explanation of what it does.

3.3 Stop-Signal (Pre-HAPPE)

3.4 General Post-HAPPE Steps

  1. Remove files that don’t have any output data from the dataset that will be used to assess file quality

    • This step is important because “empty” files don’t play nicely with the code used to evaluate files that have some data in them (even if the data do not meet threshold)
    • This code relies on HAPPE’s quality data that remains in the MATLAB environment after the pipeline has finished.
    % Create a list of files that received some kind of error message
    noTags = any(strcmp(dataQC, 'NO_TAGS'), 2);
    allRej = any(strcmp(dataQC, 'ALL_SEG_REJ'), 2);
    error = any(strcmp(dataQC, 'ERROR'), 2);
    loadFail = any(strcmp(dataQC, 'LOAD_FAIL'), 2);
    
    % Combine filenames with quality data (for some reason, they are not automatically connected by HAPPE)
    dataQCNew = [FileNames', dataQC];
    
    % Remove all files in the above lists (those receiving errors) from the quality data
    dataQCNew(noTags | allRej | error | loadFail, :) = [];
    
    % Create list of variable names for quality data
    dataQCnamesNew = ["File", dataQCnames];
    
    % Save the data as a table for ease of use in subsequent steps
    qcTable = cell2table(dataQCNew, 'VariableNames', dataQCnamesNew);
    
    % Subset to ID and threshold information
    testInfo = qcTable(:, ["idWave", "File", "Test"]);
  2. Identify the files that meet (or don’t meet) the threshold

    % Create a list of files (i.e., rows in the table) that meet threshold
    thresholdTest = qcTable.("Number_tgt+_Segs_Post-Seg_Rej") >= trialCutoff & qcTable.("Number_frq+_Segs_Post-Seg_Rej") >= trialCutoff;
    
    % Add a variable to the quality data table that include whether or not the file meet threshold
    qcTable.Test = thresholdTest;
  3. Add an identifying variable to be used for data joining down the line

    • This variable is generated using its expected location in the file name (i.e., how many text characters “in” it is)
    % Generate IDs based on File variable
    idWaveQC = extractBefore(qcTable.File, 8);
    
    % Append ID variable to quality data
    qcTable.idWave = idWaveQC;
  4. Generate a list of files outputted by HAPPE

    % Generate path for HAPPE pre-processing output (using the HAPPE environment variable from user's input of location of raw data for processing)
    inputPath = strcat(srcDir, "\5 - processed");
    
    % Read in list of files outputted from HAPPE 
    preprocessingOutput = dir(inputPath);
    
    % Remove "empty" rows 
    preprocessingOutput = preprocessingOutput(~ismember({preprocessingOutput.name}, {'.', '..'}));
    
    % Save data as a table for ease of later use
    preprocessingOutput = struct2table(preprocessingOutput);
    
    % Subset to file info
    fileInfo = preprocessingOutput(:, ["name", "folder"]);
  5. Select only desired files to be moved/copied

    • Currently, we don’t do anything with the “Individual Trial” files outputted by HAPPE. These files are quite large and take a long time to move, so it is more efficient to just remove them from the data and not worry about moving them anywhere.
    % Subset to desired files (AveOverTrial)
    fileSubset = fileInfo(contains(fileInfo.name, "AveOverTrials"), :);
  6. Add condition, ID, and threshold-related variables to the file data

    % Generate list of IDs based on file name variable
    idWaveFS = extractBefore(fileSubset.name, 8);
    
    % Add ID list to file data
    fileSubset.idWave = idWaveFS;
    
    % Generate list of files belonging to each condition based on file name variable
    target = contains(fileSubset.name, "tgt+");
    frequent = contains(fileSubset.name, "frq+");
    
    % Create empty variable for condition
    fileSubset.cond = cell(size(fileSubset, 1), 1);
    
    % Fill in condition variable based on the lists generated above
    fileSubset.cond(target) = {'Target'};
    fileSubset.cond(frequent) = {'Frequent'};
    fileSubset.cond(~target & ~frequent) = {'All'};
    
    % Join threshold test information
    fileTest = join(fileSubset, testInfo);
  7. Prepare data table with information about files that met the threshold

    • The data generated here are preparing to copy the .txt files outputted by HAPPE into a folder containing all files that are suitable for PCA
    % Create a separate table for only files that meet threshold
    movingInfo = fileTest(fileTest.Test, :);
    
    % Create empty columns for filepath variables
    movingInfo.destination = cell(size(movingInfo, 1), 1);
    movingInfo.origin = cell(size(movingInfo, 1), 1);
    movingInfo.processedTo = cell(size(movingInfo, 1), 1);
    movingInfo.processedFrom = cell(size(movingInfo, 1), 1);
    
    % Generate file paths based on condition 
    movingInfo.destination = strcat({passPath}, "\", movingInfo.cond, "\", movingInfo.name);
    movingInfo.origin = strcat(movingInfo.folder, "\", movingInfo.name);
    movingInfo.processedTo = strcat({allPath}, "\", movingInfo.name);
    movingInfo.processedFrom = strcat(movingInfo.folder, "\", movingInfo.name);
  8. Prepare data table with information about files that do NOT meet the threshold

    • The data generated here are preparing to copy .mff files from the location of the raw files into a folder indicating the need for manual processing
    % Create a separate table for only files that did not meet threshold
    failFiles = fileTest(~fileTest.Test, ["File", "folder", "name"]);
    
    % Create empty columns for filepath variables
    failFiles.destination = cell(size(failFiles, 1), 1);
    failFiles.origin = cell(size(failFiles, 1), 1);
    failFiles.processedTo = cell(size(failFiles, 1), 1);
    failFiles.processedFrom = cell(size(failFiles, 1), 1);
    
    % Generate filepaths based on ID and task
    failFiles.destination = strcat({failPath}, "\", failFiles.File);
    failFiles.origin = strcat({srcDir}, "\", failFiles.File);
    failFiles.processedFrom = strcat(failFiles.folder, "\", failFiles.name);
    failFiles.processedTo = strcat({allPath}, "\", failFiles.name);
    
  9. Generate environment variables that correspond to the column index of relevant variables for file sorting

    • Note that the very last line of this code defines the varaible(s) to exclude from the HAPPE outputted files. This variable must be stripped from the data before saving them, because the presence of the extra variable makes the file incompatible with EP Toolkit’s PCA process.
    % Define column locations for each filepath variable
    
    % For files that meet threshold:
    toCol = find(strcmp(movingInfo.Properties.VariableNames, "destination"));
    fromCol = find(strcmp(movingInfo.Properties.VariableNames, "origin"));
    procColto = find(strcmp(movingInfo.Properties.VariableNames, "processedTo"));
    procColfrom = find(strcmp(movingInfo.Properties.VariableNames, "processedFrom"));
    
    % For files that do not meet threshold
    rawCol = find(strcmp(failFiles.Properties.VariableNames, "origin"));
    manCol = find(strcmp(failFiles.Properties.VariableNames, "destination"));
    failProcColto = find(strcmp(failFiles.Properties.VariableNames, "processedTo"));
    failProcColFrom = find(strcmp(failFiles.Properties.VariableNames, "processedFrom"));
    
    % Define variable to exclude
    extraVar = 'Time';
  10. Use a loop to process all files that met threshold

    • For each row in the “movingInfo” dataset, the loop will:
      • Identify the origin and destination paths
      • Read in the HAPPE output file
      • Remove the extra variable
      • Save the “cleaned” data in the appropriate folder (without variable names, as required by EP Toolkit)
    for row = 1:height(movingInfo)
        % Specify path info
        pathFrom = movingInfo{row, fromCol};
        pathTo = movingInfo{row, toCol};
        % Read in the data
        rawTable = readtable(pathFrom);
        % Remove extra column (Time)
        cleanTable = rawTable{:, ~strcmp(rawTable.Properties.VariableNames, extraVar)};
        % Save without headers
        writematrix(cleanTable, pathTo, 'Delimiter', '\t')
    end
    
  11. Use a loop to copy raw (.mff) files into a location that stores files requiring manual processing

    for row = 1:height(failFiles)
        % Specify path info
        pathFrom = failFiles{row, rawCol};
        pathTo = failFiles{row, manCol};
        % Copy file 
        copyfile(pathFrom, pathTo)
    end
  12. Use a set of loops to copy all HAPPE output files into a folder intended to house all output (whether threshold is met or not)

    for row = 1:height(movingInfo)
        % Specify path info
        pathFrom = movingInfo{row, procColfrom};
        pathTo = movingInfo{row, procColto};
        % Copy file
        copyfile(pathFrom, pathTo);
    end
    
    for row = 1:height(failFiles)
        % Specify path info
        pathFrom = failFiles{row, failProcColFrom};
        pathTo = failFiles{row, failProcColto};
        % Copy file
        copyfile(pathFrom, pathTo);
    end
  13. Generate a .txt file listing all processed .mff files

    • This file will contain a list of all raw files (e.g., 1111_22_oddball.mff) and save the list to the specified location (“summPath”)
    • The file will have the current date and time appended to the end so that it will be distinguishable from past logs
    • The list of processed files is generated using an environment variable that the HAPPE pipeline creates that lists all files inputted to the pipeline
    % Create a table from HAPPE FileNames cell array
    processedList = cell2table(FileNames(:));
    
    % Rename file variable from default
    processedList = renamevars(processedList, {'Var1'}, {'File'});
    
    % Save current date as a string variable 
    today = string(date());
    
    % Save time as a string variable, replacing ":" with "_" so that file can be written 
    time = strrep(datestr(now, 'HH:MM:SS:FFF'), ':', "_");
    
    % Generate file name to include current date and time 
    listFile = strcat("\oddballProcessed_", today, "_", time);
    
    % Generate full path including file name
    summPathFull = strcat(summPath, listFile);
    
    % Write table to specified location
    writetable(processedList, summPathFull);

4 ERP PCA (EP) Toolkit

4.1 Reading Text Files into EP Toolkit

  1. Open MATLAB with “Run as Administrator”
  2. Open ERP PCA Toolkit in MATLAB
    • Type ep in command prompt
  3. Click Read to import files
  4. Use the following options
    • Format = text (.txt)
    • Type = average
    • Mont = Adult Hydrocel 128-channel 1.0
  5. Select Single File Mode.
    • Single file mode will use the filename to assign the task condition and participant ID for each file.
      • Note: R can rename files in batches Thus, it is critical to use a standard naming convention to name the files. For example, an oddball file could be named:
      • frq_1001_36 and tgt_1001_36
    • A FishSharks file meanwhile could be named: cgo_1001_36 and cng_1001_36
  6. In the Single File Mode menu use the Subject field to denote which characters in the filename name will determine the participant ID.
    • For the above example 1:7 would correspond to 1001_36
  7. Next, In the Single File Mode menu use the Cell field to denote which characters in the file name will determine the task condition.
    • For the below example 41:43 would correspond to frq or tgt.
    • For FishSharks files, it might be 47:49 that correspond to cgo or cng.
    • image
      image
  8. Select Read and select the 2_9AverageNet128 when prompted
  9. The new file will have the participant ID and will combine the conditions for each participant.
    • Subject Names:
      • image
        image
    • Task Conditions
      • image
        image

4.2 Update File with Experiment Information

  1. Go to Main and click Edit
  2. Click on the file you imported
  3. In Overview, add the following information:
    • Experiment Name: Oddball, FishShark, or StopSignal
    • Reference Type: change to average reference
    • Prestimulus period: change to 200
    • Nominal sampling rate: change to 1000
  4. Click Done
  5. Go to Main and click Save
    • Save the combined file as an .ept file in the 4-EPT Averages folder using the following naming convention: “task_condition_age”. For example, if you were working on the target condition of oddball for all age groups, you would save the file as ob_tgt_all

4.3 Generating Grand Average Waveforms

  1. If the EPT average file (e.g., ob_tgt_all) is not already in the working environment, read it in using the steps below
    • Go to Read
    • Format = EP (.ept)
    • Click Read
    • Navigate to the 4 - EPT Averages folder and select desired file(s)
    • Click Open in the browser window to read the file(s)
    • Click Main to return to main menu
  2. Select Edit
  3. When the editor window opens, navigate to the Subjects pane
  4. Select All from among the many options along the lefthand pane of the editor
    • This will select all of the subjects included in the file and assign them a weight of 1
  5. Confirm that all subjects have been selected (look for a checked box in the subject row) and that all weights have been set to 1
  6. Click Add
  7. A new “subject” should have now been added to the bottom of the subjects list
    • This subject is called gave and represents the grand average across all subjects
  8. Click Done to exit the editor window, then Main to return to the EP Toolkit home

4.4 Temporal PCA

  1. Go to Main and click PCA
  2. Input the following:
    • Mode: temporal
    • Rotation: promax
    • Factors: 0
    • Title: tPCA_experimentname (example: tPCA_ob_tgt_all)
  3. Click the appropriate file (e.g., ob_tgt_all)
  4. Determine how many factors to retain using the scree plot (keep the number of factors where the blue line is above the red line)
  5. Determine the percent variance accounted for by the number of factors retained by changing the “minimum % age accounted for criterion”. Record the number of factors retained and % variance accounted for by that number of factors.
  6. Re-run the temporal PCA using the above inputs, but change the number of factors to the number of factors retained from the above step
  7. Return to Main and click Save. Save the tPCA file in the 5-PCA folder

4.5 Spatial PCA

  1. Go to Main and click PCA
  2. Change the PCA type, using the following inputs:
    • Mode: spatial
    • Rotation: infomax
    • Factors: 0
    • Title: sPCA_experimentname (e.g., sPCA_ob_tgt_all)
  3. Click the appropriate file (e.g., ob_tgt_all)
  4. Determine how many factors to retain using the scree plot (keep the number of factors where the blue line is above the red line)
  5. Determine the percent variance accounted for by the number of factors retained by changing the “minimum % age accounted for criterion”. Record the number of factors retained and % variance accounted for by that number of factors.
  6. Re-run the spatial PCA using the above inputs, but change the number of factors to the number of factors retained from the above step
  7. Return to Main and click Save. Save the sPCA file in the 5-PCA folder

4.6 Temporospatial PCA

  1. Go to Main and click PCA
  2. Change the PCA type, using the following inputs:
    • Mode: spatial
    • Rotation: infomax
    • Factors: 0
    • Title: tsPCA_experimentname (e.g., tsPCA_ob_tgt_all)
  3. Click the tPCA file (created in the previous step)
  4. Determine how many factors to retain using the scree plot (keep the number of factors where the blue line is above the red line)
  5. Determine the percent variance accounted for by the number of factors retained by changing the “minimum % age accounted for criterion”. Record the number of factors retained and % variance accounted for by that number of factors.
  6. Re-run the spatial PCA using the above inputs, but change the number of factors to the number of factors retained from the above step
  7. Return to Main and click Save. Save the tsPCA file in the 5-PCA folder.

4.7 PCA Component Selection

Here, the goal is to select the PCA component that corresponds to the ERP component of interest, and the extraction that supports the intended interpretability of the component.

  1. Go to View to begin the process of selecting the PCA component that corresponds to the ERP of interest.
    • Iteratively select and view each temporospatial PCA component to identify the PCA component (“factor”) that corresponds to the ERP of interest (e.g., N2 or P3). Select the temporospatial PCA component that corresponds to the ERP of interest based on the timing, spatial location, morphology, and (as relevant) any condition- or age-related differences of the component based on prior work.
  2. Generate tsPCA components. Go to Window and input the following:
    • select the tsPCA file
    • select among mean, maxPeak, or other options. (According to Joe Dien), when using amplitudes from PCA components, it does not matter which option you select—all the different methods result in comparable p-values when dealing with PCA components (it would be good to verify this). So, select a method that makes sense for the story you want to tell. The methods will yield different results when dealing with the raw waveforms.
    • select AutoPCA or Window to select channels. If the peak amplitude is where you expect temporally and spatially, then use the autoPCA function, and if it is not, then window to where you expect it to be. This will allow you to report results that are more interpretable. As Joe Dien described, the way that PCA data are stored internally in the toolkit are as factor scores (i.e., component scores). When you extract amplitudes from a PCA component, you are extracting the factor scores multiplied by a constant (some scaling factor, representing the electrode where you extract it from). Thus, according to Joe Dien, the p-values should be the same regardless of whether you use AutoPCA, or extract from a single electrode or multiple electrodes (it would be good to verify this). What is changing is merely the scaling factor (i.e., the constant that is multiplied by all factor scores). When you select multiple electrodes, it is computing the PCA-estimated amplitude at each electrode and performing a simple average across those electrodes. The AutoPCA extracts the PCA-estimated amplitude at the peak channel and the peak timepoint. If the waveform is negative-going at the peak channel, and you are interested in the positive-going dipole, you would select the peak positive channel to identify the PCA-estimated amplitude of the positive-going waveform on that PCA component. Nevertheless, even though you are selecting the PCA-estimated amplitude for a given channel at a given electrode, there are now “virtual channels”; the estimates include the contributions of all channels and all timepoints to the extent that they load onto the PCA component of interest. Thus, even if you select to window a PCA component from only 1 channel at 1 timepoint, it is using ALL channels and timepoints in the estimation—this is not the case if windowing the raw ERP waveforms.
    • Save the files generated from the AutoPCA in the 6-PCA Components folder using the following naming convention: “task_condition_age” (e.g.,ob_tgt_all).
  3. To view all of the tsPCA components, click View and input the following
    • select the appropriate file (e.g., ob_tgt_all)
    • select gave
    • select none
    • click Waves
  4. It is good practice to check to make sure that components are comparable across different age ranges
    • You can check this in one of two ways:
      • Visually examine grand averages between age ranges
      • Apply the PCA from one age group and apply it to another age group and examine whether the results hold up using cross-validation in EPToolkit

4.8 Identifying Electrodes that Load Onto PCA Component

  1. Go to Window
  2. Select the PCA file of interest (e.g., tsPCA-ob_tgt_all)
  3. Click the Channels button (about halfway down the Window window)
  4. Click Factor
    • From the dropdown, select the PCA file of interest (e.g., `tsPCA_ob_tgt_all)
    • Enter the threshold in the space below (e.g., 0.5)
      • This sets the minumum factor loading value for an electrode to be “included” in the component-related cluster
    • Depending on whenther you are interested in positive or negative factor loadings, select the appropriate sign (+, -, or +/-)
    • A popup window with PCA factors will appear. Select the component(s) you wish to identify spatially (e.g., TF01SF01)
  5. When prompted, give the electrode cluster a name
  6. The channels that change color are those which load onto the selected component at or above the threshold value

4.9 Exporting Grand Average Data

If you are only interested in the grand average data and not individual subjects, these instructions will allow you to export a .txt file containing only the grand average data.

  1. From EP Toolkit home (Main screen), select Edit
  2. Select the .ept averages file (e.g., ob_tgt_all) that contains a “subject” representing the grand average
    • If the file does NOT contain a grand average subject, follow the steps in the above section to generate it
  3. Rename the file
    • For example, ob_tgt_all could be renamed to ob_tgt_gav
    • Renaming the file will prompt EP Toolkit to ask whether you want to generate a new file with this new name, or overwrite the existing datafile once your changes are complete
  4. Select Subjects from the options at the top of the editor window
  5. Click All from among the options on the lefthand side of the Subjects window
    • This will select all of the subjects
  6. Scroll to the bottom of the list of subjects and deselect the subject labeled grand average
    • Essentially, the goal here is to create a dataset that includes ONLY the grand average information, rather than each individual subject
  7. Once everything EXCEPT for the grand average subject is selected, click Delete on the lefthand side of the editor window
    • This will remove the individual subject data from the dataset and leave the grand average information
  8. Click Done
  9. If you renamed the datafile, EP Toolkit should generate a popup message asking whether you would like to rename your dataset OR generate a new dataset using the new name (leaving the original dataset untouched). From the options presented, clikc New to generate a new file and preserve the original
  10. The editor window should close, returning you to the EP Toolkit pane that asks you to select a dataset to edit. From here, click Main to return to EP Toolkit “home”
  11. Once in the main window, clikc Save
    • Set the save format to Text (.txt)
    • Click the grand average data (e.g., ob_tgt_gav) to save it
    • A file explorer window should open, prompting you to select the appropriate save location and give your file a name

5 Visualizations in R

5.1 R Code for Grand Average Waveform Plot

  1. Read in the grand average waveform data exported from EP Toolkit.

    • We currently process the conditions within a given task separately, so each condition should have its own grand average file.
    obTgt <- read.table("V:/SRS-ERP-Oddball/Hard/All/4 - EPT Averages/2024-11-05/gave/ob_tgt_gav.txt")
    obFrq <- read.table("V:/SRS-ERP-Oddball/Hard/All/4 - EPT Averages/2024-11-05/gave/ob_frq_gav.txt")
  2. Create a subset of data that only includes those electrodes that are part of the clusters identified in EP Toolkit.

    • The grand average data does not have row or column labels, but the columns represent the EEG net channels in numerical order (1-129). We can therefore use their column index values to select the desired electrodes; so, the list containing the channel numbers should include ONLY numbers. The code that selects these channels out of the full dataset will rely on numerical input.
    # Set electrode clusters
    obElectrodes <- c(58, 59, 64, 65, 66, 67, 69, 70, 71, 72, 73, 74, 75, 76, 77, 81, 82, 83, 84, 89, 80, 91, 95, 96, 101)
    
    # Subset to desired electrodes
    obTgt_sub <- obTgt[, obElectrodes]
    obFrq_sub <- obFrq[, obElectrodes]
  3. Compute averages and create labels for conditions

    • Once the data have been subsetted down to include only the electrode channels of interest, all that remains is to compute the average amplitude across all of those channels
    • Adding a condition label will allow us to combine the two condition-specific datasets into one that can be used for visualizations
      • For ease of plotting, name the conditions the way that you would like them to appear on the figure (i.e., “Target” instead of “tgt”)
    # Compute averages 
    obTgt_sub$amplitude <- rowMeans(obTgt_sub)
    obFrq_sub$amplitude <- rowMeans(obFrq_sub)
    
    # Remove raw values and add condition labels
    obTgt_amps <- obTgt_sub %>% select(amplitude) %>% mutate(condition = "Target")
    obFrq_amps <- obFrq_sub %>% select(amplitude) %>% mutate(condition = "Frequent")
  4. Add timing-related information to the data

    • EP Toolkit exports ERP data without timestamps, but arranges it in order of timing
    • We can create a template with the appropriate timestamps and append this column to the amplitude data
    # Create template
    erpTemplate <- data.frame(
      time = -199:1000
    )
    
    # Merge template with amplitude data
    obTgtTimes <- cbind(erpTemplate, obTgt_amps)
    obFrqTimes <- cbind(erpTemplate, obFrq_amps)
  5. Combine all conditions into a single data object to be used for plotting

    oddball <- rbind(obTgtTimes, obFrqTimes) %>% 
               select(time, condition, amplitude) %>% 
               arrange(time)
  6. Generate the waveform figures

    ggplot(
      data = oddball,
      aes(
        x = time,
        y = amplitude,
        group = condition,
        color = condition
      )
    ) +
      geom_line(linewidth = 1.5) +
      scale_x_continuous(
        name = "Time Relative to Stimulus Onset (ms)",
        limits = c(-200, 1000),
        breaks = seq(from = -200, to = 1000, by = 200)) +
      scale_y_continuous(
        name = "Voltage (microvolts)",
        limits = c(-4, 10),
        breaks = seq(from = -10, to = 15, by = 2)) +
      scale_color_viridis_d()+
      theme_classic(base_size = 18) +
      theme(
        legend.position = c(.7, .9),
        legend.title = element_blank())

6 Appendix

6.1 Troubleshooting

  1. Running out of space on EP Toolkit? You can navigate to your folder (maybe under Documents/MATLAB/EPwork) and delete everything except for EPprefs to refresh your workspace. NOTE: This will delete everything stored in EP Toolkit, so remember to back up files that you need to save.
  2. If you get an error or a warning from Git when trying to commit and/or push ERP files up to the repo, you may need to initialize Git LFS. The instructions for initializing Git LFS can be found here.
    • The filetypes that commonly cause such errors in our current ERP processing pipeline are:
      • .txt files
      • .ept files
      • .set files

6.2 To-do

  • Better describe the missingness for files
    • We need a systematic way to identify new ways to process the missingness
    • Find a way to best describe and report the ways of missingness
  • Go through the maxmem edits on the clean_rawData question. We want a standardized value on the machines
  • Look at the warning messages for the automatic script updates
    • automatic cleaning of files problems
  • Integrate ERPLAB with our existing EEGLab Functions including:
    • Adding an event list:
      • Currently, some code for this is updated in the script on the lab drive
      • Documentation is here
    • Figure out how to average epochs and export to the EP Toolkit
  • Evaluate the semi-automated pipelines from:

6.3 EEGLab Processing Steps

https://eeglab.org/tutorials/

  1. Filtering
  2. Average Referencing
  3. Artifact Rejection
    1. Automated artifact rejection (save intermediate file)
    2. Manual selection of bad channels
    3. Manual selection of bad time periods (save intermediate file)
    4. Removal of manually selected bad channels
    5. Removal of manually selected bad time periods (save intermediate file)
    6. Independent Component Analysis (ICA)
      1. Run ICA
      2. Automated removal of bad ICA components
      3. Re-run ICA (save intermediate file)
      4. Manual selection of bad ICA components (save intermediate file)
      5. Removal of manually selected bad ICA components
  4. Interpolation of Bad Channels
  5. Average Referencing
  6. Segmentation
  7. Baseline Correction (save final file)

6.4 EEGLAB

6.4.1 Install Plugins

MFFMatlabIO plugin

6.4.2 Import Data

File → Import Data → Using EEGLAB functions and plugins → Import Magstim/EGI .mff file

EEG = pop_mffimport({'\\\\lc-rs-store24.hpc.uiowa.edu\\lss_itpetersen\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\0-Raw Data (mff)\\1613_90_oddball.mff'},{'code'},0,0);

[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'overwrite','on','gui','off');

Select .mff file(s)

Event type field (may select multiple): code

6.4.3 Filter Data

Filter → Basic FIR Filter

0.1 – 30 Hz

save as new name

EEG = pop_eegfiltnew(EEG, 'locutoff',0.1,'hicutoff',30,'plotfreqz',1);

6.4.4 Average Referencing

Tools → Re-reference the data → Compute average reference

EEG = eeg_checkset( EEG );
EEG = pop_reref( EEG, []);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'setname','1613_90_oddball_fil_ref','overwrite','on','gui','off'); 
eeglab redraw;

6.4.5 Artifact Rejection

6.4.5.1 Automated Artifact Rejection

Tools → Reject data using Clean Rawdata and ASR

EEG = eeg_checkset( EEG );
EEG = pop_clean_rawdata(EEG, 'FlatlineCriterion',5,'ChannelCriterion',0.8,'LineNoiseCriterion',4,'Highpass','off','BurstCriterion',20,'WindowCriterion',0.25,'BurstRejection','on','Distance','Euclidian','WindowCriterionTolerances',[-Inf 7] );
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'gui','off'); 
eeglab redraw;

Currently, we run into a bug that yields the following error:

Not enough memory, This is not a bug (Error occurred in function asr_process() at line 132)

As a workaround, type the following code in MATLAB to edit the function:

edit clean_artifacts

Then, change the number in the following line to a larger number (e.g., 256) and click save:

{'max_mem','MaxMem'}, 256, ...

6.4.5.2 Selection of Bad Channels

View data to identify bad channels to reject.

Edit → Select data

Specify channels to reject

Specify “on -> remove these”

EEG = eeg_checkset( EEG );
EEG = pop_select( EEG, 'nochannel',{'E44','E56','E57','E113'});
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'setname','1613_90_oddball_fil_ref_chn','overwrite','on','gui','off'); 
eeglab redraw;

6.4.5.3 Selection of Bad Time Periods

Plot → Channel data (scroll)

Change voltage scale to 50

Settings → Time range to display

Change to 10 seconds

Settings → Number of channels to display

Change to number of channels to view at one time (e.g., 64)

To erase a selected portion of the data, first drag the mouse (holding down the left mouse button) horizontally across the time region of interest to mark it for rejection. To deselect a portion of the data, simply click on the selected region.

After marking some portions of the data for rejection, press REJECT and a new data set will be created with the rejected data omitted. A new dataset will be created with the marked regions removed. Your goal is to reject non-stereotypic artifacts. Do not reject blinks/saccades, because independent component analysis will remove those.

Clicking “Stack” stacks all electrodes on top of each other to more easily identify noisy data.

Click “REJECT” to remove the bad time periods from the data file.

6.4.5.4 Independent Component Analysis

https://eeglab.org/tutorials/06_RejectArtifacts/RunICA.html (archived at https://perma.cc/AEU9-GB3B)

https://socialsci.libretexts.org/Bookshelves/Psychology/Book%3A_Applied_Event-Related_Potential_Data_Analysis_(Luck)/14%3A_Appendix_3%3A_Example_Processing_Pipeline (archived at https://perma.cc/9QYQ-BNFE)

The component order returned by runica.m is in decreasing order of the EEG variance accounted for by each component.

Tools → Decompose data by ICA

EEG = eeg_checkset( EEG );
EEG = pop_runica(EEG, 'icatype', 'runica', 'extended',1,'interrupt','on');
[ALLEEG EEG] = eeg_store(ALLEEG, EEG, CURRENTSET);
eeglab redraw;

Plot → Component maps → 2D

Visually identify independent components to remove

Tools → Inspect/label components by map

Example ICA Artifact Components

ICA Eye Blink Artifact ICA Lateral Eye Movement Artifact ICA Muscle Artifact ICA Cardiac Artifact ICA Bad Channel

Example ICA Brain Components ICA Brain Activity Example 1 ICA Brain Activity Example 2 ICA Brain Activity Example 3

Overview of ICA Components ICA Overview

Toggle the “Accept” button to reject an independent component, press “OK” to specify it for rejection

Automated detection of artifactual ICA components:

https://eeglab.org/tutorials/06_RejectArtifacts/RunICA.html#automated-detection-of-artifactual-ica-components (archived at https://perma.cc/5RQ7-9WBT)

EEG = eeg_checkset( EEG );
EEG = pop_iclabel(EEG, 'default');
[ALLEEG EEG] = eeg_store(ALLEEG, EEG, CURRENTSET);
EEG = eeg_checkset( EEG );
EEG = pop_icflag(EEG, [NaN NaN;0.9 1;0.9 1;NaN NaN;NaN NaN;NaN NaN;NaN NaN]);
[ALLEEG EEG] = eeg_store(ALLEEG, EEG, CURRENTSET);
eeglab redraw;

There are six categories of components: Brain, Muscle, Eye, Heart, Line Noise, Channel Noise, and Other

Our goal is to keep the brain components and to remove everything else (i.e., artifacts).

Tools → Classify components using ICLabel → Label components

Tools → Classify components using ICLabel → Flag components as artifacts

Substracting rejected ICA components:

https://eeglab.org/tutorials/06_RejectArtifacts/RunICA.html#subtracting-ica-components-from-data (archived at https://perma.cc/HVH4-Z4SA)

Tools → Remove components

EEG = pop_subcomp( EEG, [1    2    6    7   10   13   21   24   26   31   32   33   36   43   44   51   54   55   59   61   67   68   74   83   90   91   93   99  103  112  113  116  118  121], 0);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 6,'gui','off'); 
eeglab redraw;

6.4.6 Interpolate Bad Channels

https://sccn.ucsd.edu/pipermail/eeglablist/2016/011199.html (archived at https://perma.cc/97NH-8LAR)

To interpolate channels you would load up one file that has only the good channels, then load up a second file that has the full channel list, and then run the channel interpolation function from the eeglab gui.

Tools → Interpolate Electrodes → Use all channels (or specific channels?) of other dataset

Using all channels of other dataset:

Important Note: Interpolating files will re-reference the data. Average reference the data after interpolating channels.

EEG = eeg_checkset( EEG );
EEG = pop_interp(EEG, ALLEEG(3).chanlocs, 'spherical');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 4,'gui','off'); 
eeglab redraw;

Using specific channels of other dataset:

EEG = eeg_checkset( EEG );
EEG = pop_interp(EEG, ALLEEG(3).chanlocs([44   56   57  113]), 'spherical');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'gui','off'); 
eeglab redraw;

Removed channels:

EEG = eeg_checkset( EEG );
EEG = pop_interp(EEG, EEG.chaninfo.nodatchans([44  56  57  113]), 'spherical');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 3,'gui','off'); 
EEG = eeg_checkset( EEG );
eeglab redraw;

Data channels:

EEG = eeg_checkset( EEG );
EEG = pop_interp(EEG, [44  56  57  113], 'spherical');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'gui','off'); 
eeglab redraw;

6.4.7 Average Referencing

Tools → Re-reference the data → Compute average reference

EEG = eeg_checkset( EEG );
EEG = pop_reref( EEG, []);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'setname','1613_90_oddball_fil_ref','overwrite','on','gui','off'); 
eeglab redraw;

6.4.8 Segmentation

Tools → Extract Epochs

EEG = eeg_checkset( EEG );
EEG = pop_epoch( EEG, {  'frq+'  'tgt+'  }, [-0.2           1], 'newname', '1613_90_oddball_fil_ref epochs', 'epochinfo', 'yes');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'overwrite','on','gui','off'); 

6.4.9 Baseline Correction

EEG = eeg_checkset( EEG );
EEG = pop_rmbase( EEG, [-200 0] ,[]);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 1,'overwrite','on','gui','off'); 
eeglab redraw;

6.5 Automatic Script example

% Starting EEG Lab 
[ALLEEG EEG CURRENTSET ALLCOM] = eeglab;

%% Helpful documentation is located here
% https://eeglab.org/tutorials/11_Scripting/Using_EEGLAB_history.html (archived at https://perma.cc/Y687-5GKE)
% https://eeglab.org/tutorials/ConceptsGuide/Data_Structures.html (archived at https://perma.cc/5F39-5S32)

%Loading in the Dataset
EEG = pop_mffimport({'R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\0-Raw Data (mff)\\1613_90_oddball.mff'},{'code'},0,0);
EEG.setname='raw';
EEG = eeg_checkset( EEG );
%Storing the current dataset
[ALLEEG, EEG] = eeg_store( ALLEEG, EEG, 1);
% refreshing the graphical interface
eeglab redraw;

%Filter the data
EEG = pop_eegfiltnew(ALLEEG(1), 'locutoff',0.1,'hicutoff',30,'plotfreqz',1);
%Save the Filtered dataset 
%ALLEEG EEG CURRENTSET seems to just be a name for all of the current data
%sets 
%pop_newset seems to save the dataset in both memory and in the toolkit
[ALLEEG, EEG, CURRENTSET] = pop_newset(ALLEEG, EEG, 2,'setname','fil','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\1-Filtering (fil)\\tcid_wave.set','gui','off'); 
eeglab redraw;

% Average referencing
%EEG = eeg_checkset( EEG );
EEG = pop_reref( ALLEEG(2), []);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 3,'setname','Avg Ref','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\9-Average Reference (ref)\\tcid_wave_avg.set','gui','off');
eeglab redraw;

% Rejecting the artifacts
% testing bumping the 
EEG = pop_clean_rawdata(ALLEEG(3), 'FlatlineCriterion',5,'ChannelCriterion',0.8,'LineNoiseCriterion',4,'Highpass','off','BurstCriterion',30,'WindowCriterion',0.25,'BurstRejection','on','Distance','Euclidian','WindowCriterionTolerances',[-Inf 7] );
%Saving cleaned dataset
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 4,'setname','clean data','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\10-Clean Artificats (clean)\\tcid_wave_clean.set','gui','off'); 
eeglab redraw;

% Placeholder to manually remove bad channels


% ICA components
EEG = pop_runica(ALLEEG(4), 'icatype', 'runica', 'extended', 1,'interrupt','on');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 5,'setname','ICA test','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\11-ICA\\tcid_wave_ICA.set','gui','off'); 
eeglab redraw;

%Manually reject ICA components
EEG = pop_subcomp( EEG, [1    2    6    7   10   13   21   24   26   31   32   33   36   43   44   51   54   55   59   61   67   68   74   83   90   91   93   99  103  112  113  116  118  121], 0);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 6,'gui','off'); 
eeglab redraw;

% Place holder to remind to manually remove the channels that we would
% like to reject
% taking the EEG channel lock of the first (raw) dataset
EEG = eeg_checkset( EEG );
EEG = pop_interp(ALLEEG(5), ALLEEG(1).chanlocs, 'spherical');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 6,'setname','Interpolated','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\12-Interpolate\\tcid_wave_Interpolate.set','gui','off'); 
eeglab redraw;

% Segmenting the Data
EEG = eeg_checkset( EEG );
EEG = pop_epoch( ALLEEG(6), {  'frq+'  'tgt+'  }, [-0.2 1], 'newname', 'tcid_wave_segmented', 'epochinfo', 'yes');
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 7,'setname','Segmented','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\13-Segment\\tcid_wave_Segment.set','gui','off'); 
eeglab redraw;

% Baseline Correcting the Data
EEG = eeg_checkset( EEG );
EEG = pop_rmbase( ALLEEG(7), [-200 0] ,[]);
[ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, 8,'setname','Baseline Correct','savenew','R:\\Lab\\Studies\\School Readiness Study\\Data\\LV2\\ERP\\Oddball\\MATLAB\\14-Baseline-Correct\\tcid_wave_baseline-correct.set','gui','off'); 
eeglab redraw;

6.6 Automatic script that loops files

6.6.1 Warning messages with script

Warning messages appeared when using the automatic cleaning of data. We may have to send a dataset to someone so they can check on it. Some documentation is found here:

6.6.2 Filtering, average referencing, and automatically cleaning the data.

This script batch reads in files, filters them, average references, and automatically cleans them. After that, this script reads in the cleaned files for manually processing to remove bad time periods and bad channels

%10/20/22 Working script that reads everything into matlab


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Location of Raw oddball files
rawOddballFiles = '\\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data\LV2\ERP\Oddball\MATLAB\0-Raw\';
%Location of path to save cleaned files
cleanAutoPath = '\\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data\LV2\ERP\Oddball\MATLAB\10-Clean Artificats (clean)\';

%all oddball files in the directory to be processed
filesInDirectory = dir(fullfile(rawOddballFiles, '*.mff')); %Reading the files in the directory

%Listing the number of subjects for the number of times to loop
numberOfSubject = height(filesInDirectory);

%Names of all the files in the directory
listOfSubjects={filesInDirectory.name}; 

%splitting the filename up
filenameSplit = regexp(listOfSubjects, '_', 'split');  
id = cellfun(@(x) x{1},filenameSplit,'un',0);  %getting the id's
wave = cellfun(@(x) x{2},filenameSplit,'un',0); %getting the waves



[ALLEEG EEG CURRENTSET ALLCOM] = eeglab;

for i=1:numberOfSubject

    %%%% Importing Data
    path = [rawOddballFiles,filesInDirectory(i).name];
    tcid = char(strcat(id(i),'_', wave(i))); %combining the TCID and wave

    EEG = pop_mffimport({path},{'code'},0,0);
    % Saving the data in memory
   [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'setname', tcid,'gui','off');

    % Filtering the data
    EEG = pop_eegfiltnew(ALLEEG(i), 'locutoff',0.1,'hicutoff',30,'plotfreqz',1);
    [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'overwrite','on','gui','off');
    
    % Average Referencing
    EEG = pop_reref( ALLEEG(i), []);
    [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'overwrite','on','gui','off');

    % They strongly advise importing channel locations before using this
    % function.
    %Cleaning the Data
    EEG = pop_clean_rawdata(ALLEEG(i), 'FlatlineCriterion',5,'ChannelCriterion',0.8,'LineNoiseCriterion',4,'Highpass','off','BurstCriterion',30,'WindowCriterion',0.25,'BurstRejection','on','Distance','Euclidian','WindowCriterionTolerances',[-Inf 7]);
    nameClean = [tcid,'_autoClean.set'];
    savePathClean = [cleanAutoPath,nameClean];
    [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'setname', nameClean, 'savenew', savePathClean, 'gui','off', 'overwrite', 'on');


end
eeglab redraw:

%% Reading in automatically cleaned datasets

%%%%%%%%%%%%%%
% Clear Data becasue to overwrite anything in memory
%%%%%%%%%%%%%%
ALLEEG = [];
EEG = [];

% Adding the path for cleaned files
cleanAutoPath = '\\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data\LV2\ERP\Oddball\MATLAB\10-Clean Artificats (clean)\';

%Starting EEGLAB
[ALLEEG EEG CURRENTSET ALLCOM] = eeglab;


%Location of cleaned oddball data
filesInDirectoryClean = dir(fullfile(cleanAutoPath, '*.set')); %Reading the files in the directory


%Listing the number of subjects for the number of times to loop
numberOfSubjectClean = height(filesInDirectoryClean);

%Names of all the files in the directory
listOfSubjectsClean = {filesInDirectoryClean.name}; 

%splitting the filename up
filenameSplitClean = regexp(listOfSubjectsClean, '_', 'split');  
id = cellfun(@(x) x{1},filenameSplitClean,'un',0);  %getting the id's
wave = cellfun(@(x) x{2},filenameSplitClean,'un',0); %getting the waves


for i=1:numberOfSubjectClean

    %%%% Importing Data
    tcidClean = char(strcat(id(i),'_', wave(i), '_autoClean.set')); %combining the TCID and wave to name the file
    EEG = pop_loadset('filename', tcidClean, 'filepath', cleanAutoPath); 
    [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'setname', tcidClean,'gui','off');

end
eeglab redraw;

6.6.3 Removing bad time periods and channels

  1. In the now open EEGLAB interface, select a dataset. Go to Tools > Inspect/reject data by eye
visually reject artifacts
visually reject artifacts
  1. Go through the raw signal and manually reject bad time periods
visually reject artifacts
visually reject artifacts
  1. Select REJECT and overwrite the file in memory
    • select Overwrite it in memory (set=yes; unset=create a new dataset)
  2. Visually inspect the data and select any bad channels, and write them down
  3. Next, manually reject the channels by selecting Edit then Select Data
visually reject artifacts
visually reject artifacts
  1. Manually enter the channels to be removed in the Channel range field and select the checkbox under on->remove these and select Ok
  2. Save the file as tcid_wave_manualClean.set in the following drive path
    • \\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data\LV2\ERP\Oddball\MATLAB\11-Manually Cleaned
    • This screenshot represents removing channels 23 56 57 97 visually reject artifacts
  3. Repeat step for each cleaned dataset

6.6.4 Running the ICA

This script runs the ICA. We’ll want to think about how to automatically reject the components here. Once this runs, see the ICA section to reject components. Tools → Inspect/label components by map is how to reject components.

% Running the ICA

%% Reading in the manually cleaned datasets

%%%%%%%%%%%%%%
% Clear Data becasue to overwrite anything in memory
%%%%%%%%%%%%%%
ALLEEG = [];
EEG = [];

% Adding the path for cleaned files
manualCleanPath = '\\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data\LV2\ERP\Oddball\MATLAB\11-Manually Cleaned\';

%Starting EEGLAB
[ALLEEG EEG CURRENTSET ALLCOM] = eeglab;


%Location of cleaned oddball data
filesInDirectoryManualClean = dir(fullfile(manualCleanPath, '*.set')); %Reading the files in the directory


%Listing the number of subjects for the number of times to loop
numberOfSubjectManualClean = height(filesInDirectoryManualClean);

%Names of all the files in the directory
listOfSubjectsManualClean = {filesInDirectoryManualClean.name}; 

%splitting the filename up
filenameSplitManualClean = regexp(listOfSubjectsManualClean, '_', 'split');  
id = cellfun(@(x) x{1},filenameSplitManualClean,'un',0);  %getting the id's
wave = cellfun(@(x) x{2},filenameSplitManualClean,'un',0); %getting the waves


for i=1:numberOfSubjectManualClean

    %%%% Importing Data
    tcidClean = char(strcat(id(i),'_', wave(i), '_manualClean.set')); %combining the TCID and wave to name the file
    EEG = pop_loadset('filename', tcidClean, 'filepath', manualCleanPath); 
    [ALLEEG EEG CURRENTSET] = pop_newset(ALLEEG, EEG, i,'setname', tcidClean,'gui','off');

    %%% Running the ICA
    EEG = eeg_checkset( EEG );
    EEG = pop_runica(EEG, 'icatype', 'runica', 'extended',1,'interrupt','on');
    [ALLEEG EEG] = eeg_store(ALLEEG, EEG, CURRENTSET);
    eeglab redraw;

end
eeglab redraw;

6.7 Notes

6.7.1 Mount/Cap Type

Options that say “AverageNet128” are suitable for our 128 channel cap when selecting the template option when reading in text files. These are age-normed coordinates provided by EGI. So, “2_9” is age-normed for 2 to 9 year olds.

LS0tCnRpdGxlOiAiRUVHIGFuZCBFUlAgUHJvY2Vzc2luZyBhbmQgQW5hbHlzaXMiCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLAogIGVycm9yID0gVFJVRSwKICBjb21tZW50ID0gIiIpCmBgYAoKIyBFeHRyYWN0aW5nIEJlaGF2aW9yYWwgRGF0YSBmcm9tIGBFLVByaW1lYAoKMS4gUnVuIEUtTWVyZ2UKMS4gTmF2aWdhdGUgdG8gdGhlIGZvbGRlciB3aXRoIHRoZSB0YXNrIGRhdGEKMS4gU2VsZWN0IGBNZXJnZWAKMS4gU2VsZWN0IGBSZWN1cnNpdmUgTWVyZ2VgCjEuIE9ubHkgbWVyZ2U6IGBBbGwgc3BlY2lmaWVkIGZpbGVzIHJlZ2FyZGxlc3Mgb2YgbWVyZ2Ugc3RhdHVzYAoxLiBTYXZlIHRhcmdldCBmaWxlIChgZmlsZW5hbWUuZW1yZzJgKQoxLiBEb3VibGUgY2xpY2sgdGhlIG5ld2x5IHNhdmVkIGZpbGUgKGBmaWxlbmFtZS5lbXJnMmApIHRvIG9wZW4gaXQgaW4gRS1EYXRhQWlkCjEuIEdvIHRvIGBGaWxlYCwgYEV4cG9ydGAKMS4gTWFrZSBzdXJlIGBTUFNTYCBpcyBzZWxlY3RlZCBhbmQgdGhhdCB0aGUgYFVuaWNvZGVgIG9wdGlvbiBpcyAqKnVuc2VsZWN0ZWQqKjogaHR0cHM6Ly9tYXR0a21pZWNpay5jb20vcG9zdHMvcG9zdC1TdG9wLVVzaW5nLUV4Y2VsLXRvLVByZXByb2Nlc3MtRS1QcmltZS1EYXRhL3Bvc3QtU3RvcC1Vc2luZy1FeGNlbC10by1QcmVwcm9jZXNzLUUtUHJpbWUtRGF0YS5odG1sIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL043MzQtNFc5WCkKMS4gU2F2ZSB0aGUgZmlsZSBhczogYGZpbGVuYW1lLnR4dGAKCiMgRVJQIFByb2Nlc3NpbmcgUGlwZWxpbmUgdXNpbmcgSEFQUEUKCjEuIERvd25sb2FkIE1BVExBQjogaHR0cHM6Ly9pdHMudWlvd2EuZWR1L21hdGxhYgogICAgLSBDbGljayBgSG93IHRvIEluc3RhbGwgTWF0TGFiYCBhbmQgZm9sbG93IHRoZSByZWxldmFudCBpbnN0cnVjdGlvbnMKICAgIC0gQ3JlYXRlIGEgYE1hdExhYmAgZm9sZGVyIGluIHlvdXIgbG9jYWwgZGlyZWN0b3J5LgogICAgWW91IHdpbGwga2VlcCBhbGwgb2YgeW91ciBNYXRMYWIgcmVsYXRlZCBmaWxlcyBpbiB0aGlzIGZvbGRlci4KMS4gSW5zdGFsbCB0aGUgSEFQUEUgcGlwZWxpbmU6IGh0dHBzOi8vZ2l0aHViLmNvbS9QSU5FLUxhYi9IQVBQRQogICAgLSBDbG9uZSB0aGUgSEFQUEUgcmVwb3NpdG9yeSB0byB5b3VyIGBHaXRIdWJgIGFjY291bnQKICAgIC0gT3BlbiB0aGUgYEhBUFBFYCBmb2xkZXIKICAgIC0gT3BlbiB0aGUgYEhBUFBFIFVzZXIgR3VpZGVgIGRvY3VtZW50CiAgICAtIFJlYWQgdGhyb3VnaCB0aGUgYEhBUFBFIFVzZXIgR3VpZGVgCiAgICAtIE5hdmlnYXRlIHRvIHRoZSBgU2V0dGluZyB1cCBIQVBQRWAgc2VjdGlvbiBpbiB0aGUgdXNlciBndWlkZSBhbmQgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgZm9yIHNldHRpbmcgdXAgdGhlIEhBUFBFIHBpcGVsaW5lLCBpbmNsdWRpbmcgaW5zdGFsbGF0aW9uIG9mIGFkZC1vbnMgYW5kIGVlZ2xhYgoxLiBJbnN0YWxsIEVQIFRvb2xraXQ6IGh0dHBzOi8vc291cmNlZm9yZ2UubmV0L3Byb2plY3RzL2VycHBjYXRvb2xraXQvCiAgICAtIEFmdGVyIGRvd25sb2FkaW5nLCBjb3B5IHRoZSBgRVBfVG9vbGtpdGAgZm9sZGVyIHRvIHlvdXIgYE1hdGxhYmAgZm9sZGVyIChpbiB5b3VyIGxvY2FsIGRpcmVjdG9yeSkKICAgIC0gSW4gdGhlIGBFUF90b29sa2l0YCBmb2xkZXI6IAogICAgICAgIC0gT3BlbiBgRVBfVG9vbGtpdGAKICAgICAgICAtIE9wZW4gYERvY3VtZW50YXRpb25gCiAgICAgICAgLSBPcGVuIGB0dXRvcmlhbGAKICAgIC0gSW4gdGhlIGB0dXRvcmlhbGAgZG9jdW1lbnQsIG5hdmlnYXRlIHRvIHRoZSBgU2V0IFVwYCBzZWN0aW9uIGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBmb3IgaW5zdGFsbGluZyBhbmQgc2V0dGluZyB1cCBFUCBUb29sa2l0IGFuZCBGaWVsZFRyaXAuCiAgICBEbyAqKk5PVCoqIGZvbGxvdyBpbnN0cnVjdGlvbnMgZm9yIHNldHRpbmcgdXAgRUVHTEFCLgogICAgWW91IGhhdmUgYWxyZWFkeSBzZXQgdXAgeW91ciBwYXRoIHRvIEVFR0xBQiB3aGVuIHlvdSBzZXQgdXAgdGhlIEhBUFBFIHBpcGVsaW5lLgogICAgLSBZb3Ugc2hvdWxkIGhhdmUgdGhlIGZvbGxvd2luZyBzdWJmb2xkZXJzIGluIHlvdXIgYE1hdExhYmAgZm9sZGVyOgogICAgICAgIC0gYEVQX1Rvb2xraXRgCiAgICAgICAgLSBgRmllbGR0cmlwLVt2ZXJzaW9uIG51bWJlcl1gCjEuIE9wZW4gdGhlIEhBUFBFIHBpcGVsaW5lIFY0IHNjcmlwdCBpbiBNQVRMQUIKICAgIC0gRm9sbG93IHRoZSBVc2VyIEd1aWRlIGluc3RydWN0aW9ucyBmb3IgdGhlIEhBUFBFLUVSIHBpcGVsaW5lLCB1c2luZyB0aGUgdXNlciBpbnB1dHMgZGVzY3JpYmVkIGJlbG93LgogICAgLSBBbHRlcm5hdGl2ZWx5LCBvcGVuIHRoZSBbTUFUTEFCIHByb2Nlc3NpbmcgZmlsZV0oI21hdGxhYlNjcmlwdHMpIHRoYXQgcnVucyB0aGUgSEFQUEUtRVIgcGlwZWxpbmUgYW5kIGRlYWxzIHdpdGggdGhlIG91dHB1dC4gCiAgICAgICAtIGUuZy4sIGAvRGF0YSBQcm9jZXNzaW5nLzYuIE1BVExBQiBFRUcgUGlwZWxpbmUvZWVnUHJvY2Vzc2luZ09kZGJhbGwubWAKCiMjIE9kZGJhbGwgVGFzawoKMS4gVXNlciBJbnB1dHMKICAgIC0gRW50ZXIgdGhlIHBhdGggdG8gdGhlIGZvbGRlciBjb250YWluaW5nIHRoZSBkYXRhc2V0LgogICAgLSBTZWxlY3QgYHJhd2AKICAgIC0gTG9hZCBwcmUtZXhpc3Rpbmcgc2V0IG9mIGlucHV0IHBhcmFtZXRlcjogCiAgICAgICAgLSBgTmAgaWYgdGhpcyBpcyB5b3VyIGZpcnN0IHRpbWUgcnVubmluZyBkYXRhIHRocm91Z2ggdGhlIHBpcGVsaW5lLgogICAgICAgIC0gYFlgIGlmIHlvdSBoYXZlIGRlY2lkZWQgb24gYSBzZXQgb2YgcGFyYW1ldGVycy4KICAgICAgICBFbnRlciB0aGUgcGF0aCB0byB0aGUgZm9sZGVyIGNvbnRhaW5pbmcgdGhlIGlucHV0IHBhcmFtZXRlcnMuCiAgICAtIExvdyBkZW5zaXR5IGRhdGE6IGBOYAogICAgLSBEYXRhIHR5cGU6IGB0YXNrYAogICAgLSBQZXJmb3JtaW5nIGV2ZW50LXJlbGF0ZWQgcG90ZW50aWFsIChFUlApIGFuYWx5c2lzOiBgWWAKICAgIC0gRW50ZXIgdGhlIHRhc2sgb25zZXQgdGFncwogICAgICAgIC0gVGFyZ2V0OiBgdGd0K2AKICAgICAgICAtIEZyZXF1ZW50OiBgZnJxK2AKICAgICAgICAtIGBkb25lYAogICAgLSBEbyBtdWx0aXBsZSBvbnNldCB0YWdzIGJlbG9uZyB0byBhIHNpbmdsZSBjb25kaXRpb24/IGBOYAogICAgLSBGaWxlIGZvcm1hdDogYDVgCiAgICAtIEFjcXVpc2l0aW9uIGxheW91dCB0eXBlOiBgMmAKICAgIC0gTnVtYmVyIG9mIGNoYW5uZWxzOiBgMTI4YAogICAgLSBEbyB5b3UgaGF2ZSBhZGRpdGlvbmFsIHR5cGUgZmllbGRzIGJlc2lkZXMgImNvZGUiPyBgTmAKICAgIC0gU2VsZWN0IGNoYW5uZWxzIG9mIGludGVyZXN0OiBgYWxsYAogICAgLSBGcmVxdWVuY3kgb2YgZWxlY3RyaWNhbCBub2lzZSBpbiBIejogYDYwYAogICAgLSBBcmUgdGhlcmUgYW55IGFkZGl0aW9uYWwgZnJlcXVlbmNpZXMsIChlLmcuLCBoYXJtb25pY3MpIHRvIHJlZHVjZT8gYE5gCiAgICAtIExpbmUgTm9pc2UgcmVkdWN0aW9uIG1ldGhvZDogYG5vdGNoYAogICAgICAgIC0gTG93IGN1dG9mZjogYDU5YAogICAgICAgIC0gaGlnaCBjdXRvZmY6IGA2MWAKICAgIC0gUmVzYW1wbGU6IGBOYAogICAgLSBGaWx0ZXIKICAgICAgICAtIExvdyBQYXNzIEN1dG9mZjogYDMwYAogICAgICAgIC0gSGlnaCBQYXNzIEN1dG9mZjogYC4xYAogICAgLSBDaG9vc2UgYSBmaWx0ZXI6IGBmaXJgCiAgICAtIEJhZCBDaGFubmVsIERldGVjdGlvbjogYFlgCiAgICAgICAgLSBgYWZ0ZXJgIHdhdmVsZXQgdGhyZXNob2xkaW5nCiAgICAtIEVDR29uZTogYE5gCiAgICAtIFdhdmVsZXQgVGhyZXNob2xkaW5nCiAgICAgICAgLSBgZGVmYXVsdGAKICAgICAgICAtIFRocmVzaG9sZCBydWxlOiBgaGFyZGAKICAgIC0gTXVzY0lMOiBgTmAKICAgIC0gU2VnbWVudGF0aW9uOiBgWWAKICAgICAgICAtIFN0YXJ0aW5nIHBhcmFtZXRlciBmb3Igc3RpbXVsdXM6IGAtMjAwYAogICAgICAgIC0gRW5kaW5nIHBhcmFtZXRlciBmb3Igc3RpbXVsdXM6IGAxMDAwYAogICAgICAgIC0gVGFzayBvZmZzZXQ6IGAyYAogICAgLSBCYXNlbGluZSBDb3JyZWN0aW9uOiBgWWAKICAgICAgICAtIEJhc2VsaW5lIENvcnJlY3Rpb24gc3RhcnQ6IGAtMjAwYAogICAgICAgIC0gQmFzZWxpbmUgQ29ycmVjdGlvbiBlbmQ6IGAwYAogICAgLSBJbnRlcnBvbGF0aW9uOiBgWWAKICAgIC0gU2VnbWVudCBSZWplY3Rpb246IGBZYAogICAgICAgIC0gU2VnbWVudCBSZWplY3Rpb24gTWV0aG9kOiBgYW1wbGl0dWRlYAogICAgICAgICAgICAtIG1pbmltdW0gc2VnbWVudCByZWplY3Rpb24gdGhyZXNob2xkOiBgLTE1MGAKICAgICAgICAgICAgLSBtYXhpbXVtIHNlZ21lbnQgcmVqZWN0aW9uIHRocmVzaG9sZDogYDE1MGAKICAgICAgICAgICAgLSBzZWdtZW50IHJlamVjdGlvbiBiYXNlZCBvbiBhbGwgY2hhbm5lbHMgb3IgUk9JOiBgYWxsYAogICAgLSBSZS1yZWZlcmVuY2luZzogYFlgCiAgICAgICAgLSBEb2VzIHlvdXIgZGF0YSBjb250YWluIGEgZmxhdGxpbmUgb3IgYWxsIHplcm8gcmVmZXJlbmNlIGNoYW5uZWw/IGBOYAogICAgICAgIC0gcmUtcmVmZXJlbmNpbmcgbWV0aG9kOiBgYXZlcmFnZWAKICAgIC0gU2F2ZSBmb3JtYXQ6IGAxYAogICAgLSBWaXN1YWxpemF0aW9uczogYE5gCiAgICAtIFBhcmFtZXRlciBmaWxlIHNhdmUgbmFtZTogYGRlZmF1bHRgCgojIyBGaXNoL1NoYXJrCgoxLiBVc2VyIElucHV0cwogICAgLSBFbnRlciB0aGUgcGF0aCB0byB0aGUgZm9sZGVyIGNvbnRhaW5pbmcgdGhlIGRhdGFzZXQuCiAgICAtIFNlbGVjdCBgcmF3YAogICAgLSBMb2FkIHByZS1leGlzdGluZyBzZXQgb2YgaW5wdXQgcGFyYW1ldGVyOiAKICAgICAgICAtIGBOYCBpZiB0aGlzIGlzIHlvdXIgZmlyc3QgdGltZSBydW5uaW5nIGRhdGEgdGhyb3VnaCB0aGUgcGlwZWxpbmUuCiAgICAgICAgLSBgWWAgaWYgeW91IGhhdmUgZGVjaWRlZCBvbiBhIHNldCBvZiBwYXJhbWV0ZXJzLgogICAgICAgIEVudGVyIHRoZSBwYXRoIHRvIHRoZSBmb2xkZXIgY29udGFpbmluZyB0aGUgaW5wdXQgcGFyYW1ldGVycy4KICAgIC0gTG93IGRlbnNpdHkgZGF0YTogYE5gCiAgICAtIERhdGEgdHlwZTogYHRhc2tgCiAgICAtIFBlcmZvcm1pbmcgZXZlbnQtcmVsYXRlZCBwb3RlbnRpYWwgKEVSUCkgYW5hbHlzaXM6IGBZYAogICAgLSBFbnRlciB0aGUgdGFzayBvbnNldCB0YWdzCiAgICAgICAgLSBDb3JyZWN0IEdvOiBgY0dvKytgCiAgICAgICAgLSBJbmNvcnJlY3QgR286IGB4R28rK2AKICAgICAgICAtIENvcnJlY3QgTm9HbzogYGNOb0dvKytgCiAgICAgICAgLSBJbmNvcnJlY3QgTm9HbzogYHhOb0dvKytgCiAgICAgICAgLSBgZG9uZWAKICAgIC0gRG8gbXVsdGlwbGUgb25zZXQgdGFncyBiZWxvbmcgdG8gYSBzaW5nbGUgY29uZGl0aW9uPyBgTmAKICAgIC0gRmlsZSBmb3JtYXQ6IGA1YAogICAgLSBBY3F1aXNpdGlvbiBsYXlvdXQgdHlwZTogYDJgCiAgICAtIE51bWJlciBvZiBjaGFubmVsczogYDEyOGAKICAgIC0gRG8geW91IGhhdmUgYWRkaXRpb25hbCB0eXBlIGZpZWxkcyBiZXNpZGVzICJjb2RlIj8gYE5gCiAgICAtIFNlbGVjdCBjaGFubmVscyBvZiBpbnRlcmVzdDogYGFsbGAKICAgIC0gRnJlcXVlbmN5IG9mIGVsZWN0cmljYWwgbm9pY2UgaW4gSHo6IGA2MGAKICAgIC0gQXJlIHRoZXJlIGFueSBhZGRpdGlvbmFsIGZyZXF1ZW5jaWVzLCAoZS5nLiwgaGFybW9uaWNzKSB0byByZWR1Y2U/IGBOYAogICAgLSBMaW5lIE5vaXNlIHJlZHVjdGlvbiBtZXRob2Q6IGBub3RjaGAKICAgICAgICAtIExvdyBjdXRvZmY6IGA1OWAKICAgICAgICAtIGhpZ2ggY3V0b2ZmOiBgNjFgCiAgICAtIFJlc2FtcGxlOiBgTmAKICAgIC0gRmlsdGVyCiAgICAgICAgLSBMb3cgUGFzcyBDdXRvZmY6IGAzMGAKICAgICAgICAtIEhpZ2ggUGFzcyBDdXRvZmY6IGAuMWAKICAgIC0gQ2hvb3NlIGEgZmlsdGVyOiBgZmlyYAogICAgLSBCYWQgQ2hhbm5lbCBEZXRlY3Rpb246IGBZYAogICAgICAgIC0gYGFmdGVyYCB3YXZlbGV0IHRocmVzaG9sZGluZwogICAgLSBFQ0dvbmU6IGBOYAogICAgLSBXYXZlbGV0IFRocmVzaG9sZGluZwogICAgICAgIC0gYGRlZmF1bHRgCiAgICAgICAgLSBUaHJlc2hvbGQgcnVsZTogYGhhcmRgCiAgICAtIE11c2NJTDogYE5gCiAgICAtIFNlZ21lbnRhdGlvbjogYFlgCiAgICAgICAgLSBTdGFydGluZyBwYXJhbWV0ZXIgZm9yIHN0aW11bHVzOiBgLTIwMGAKICAgICAgICAtIEVuZGluZyBwYXJhbWV0ZXIgZm9yIHN0aW11bHVzOiBgMTAwMGAKICAgICAgICAtIFRhc2sgb2Zmc2V0OiBgMTdgCiAgICAtIEJhc2VsaW5lIENvcnJlY3Rpb246IGBZYAogICAgICAgIC0gQmFzZWxpbmUgQ29ycmVjdGlvbiBzdGFydDogYC0yMDBgCiAgICAgICAgLSBCYXNlbGluZSBDb3JyZWN0aW9uIGVuZDogYDBgCiAgICAtIEludGVycG9sYXRpb246IGBZYAogICAgLSBTZWdtZW50IFJlamVjdGlvbjogYFlgCiAgICAgICAgLSBTZWdtZW50IFJlamVjdGlvbiBNZXRob2Q6IGBhbXBsaXR1ZGVgCiAgICAgICAgICAgIC0gbWluaW11bSBzZWdtZW50IHJlamVjdGlvbiB0aHJlc2hvbGQ6IGAtMTUwYAogICAgICAgICAgICAtIG1heGltdW0gc2VnbWVudCByZWplY3Rpb24gdGhyZXNob2xkOiBgMTUwYAogICAgICAgICAgICAtIHNlZ21lbnQgcmVqZWN0aW9uIGJhc2VkIG9uIGFsbCBjaGFubmVscyBvciBST0k6IGBhbGxgCiAgICAtIFJlLXJlZmVyZW5jaW5nOiBgWWAKICAgICAgICAtIERvZXMgeW91ciBkYXRhIGNvbnRhaW4gYSBmbGF0bGluZSBvciBhbGwgemVybyByZWZlcmVuY2UgY2hhbm5lbD8gYE5gCiAgICAgICAgLSByZS1yZWZlcmVuY2luZyBtZXRob2Q6IGBhdmVyYWdlYAogICAgLSBTYXZlIGZvcm1hdDogYDFgCiAgICAtIFZpc3VhbGl6YXRpb25zOiBgTmAKICAgIC0gUGFyYW1ldGVyIGZpbGUgc2F2ZSBuYW1lOiBgZGVmYXVsdGAKCiMjIFN0b3AtU2lnbmFsCgojIE1BVExBQiBTY3JpcHRzIHRvIE1hbmFnZSBIQVBQRSBGaWxlcyB7I21hdGxhYlNjcmlwdHN9CgpXZSBoYXZlIHNjcmlwdHMgZm9yIGVhY2ggdGFzayB0aGF0IGNhbiBwcmVwYXJlIGZpbGVzIGZvciB0aGUgSEFQUEUgUGlwZWxpbmUgYW5kL29yIG1hbmFnZSB0aGUgZmlsZXMgb3V0cHV0dGVkIGZyb20gSEFQUEUuClRoZXNlIGFjdGlvbnMgY2FuIGJlIGRvbmUgbWFudWFsbHkgYXMgd2VsbCwgYnV0IHRoZSBNQVRMQUIgc2NyaXB0cyBtYWtlIHRoZSBwcm9jZXNzIG1vcmUgZWZmaWNpZW50LgpUaGUgc2NyaXB0cyB3aWxsIGFsc28gZ2VuZXJhdGUgYSAibG9nIiBvZiBhbGwgb2YgdGhlIGZpbGVzIHByb2Nlc3NlZCB0aHJvdWdoIEhBUFBFIHRvIGZhY2lsaXRhdGUgdHJhY2tpbmcgb2YgRUVHIGRhdGEgcHJvY2Vzc2luZy4KVGhlIHNlY3Rpb25zIGJlbG93IGRldGFpbCB0aGUgY29kZSB1c2VkIHRvIHBlcmZvcm0gdGhlc2UgYWN0aW9ucyBhcyB3ZWxsIGFzIHRoZSBpbnN0cnVjdGlvbnMgZm9yIHVzaW5nIHRoZSBjdXJyZW50IHNjcmlwdHMuCgpOb3RlOiBCZWZvcmUgdXNpbmcgdGhlIHNjcmlwdHMvY29kZSBkZXRhaWxlZCBiZWxvdywgZW5zdXJlIHRoYXQgYWxsIGZpbGVwYXRocyB1c2VkIGFyZSBpbiB5b3VyIE1BVExBQiBwYXRoIGNvbGxlY3Rpb24uClRoZXNlIG1heSBpbmNsdWRlOgogICAKICAgLSBUaGUgbG9jYXRpb24gd2hlcmUgdGhlIGF1dG9tYXRpYyBzY3JpcHQgaXMgc3RvcmVkIChmb3Igb3VyIGxhYiwgdGhpcyBpcyB1bmRlciBgL0RhdGEgUHJvY2Vzc2luZy82LiBNQVRMQUIgRUVHIFBpcGVsaW5lYCkKICAgLSBUaGUgbG9jYXRpb24gd2hlcmUgdGhlIEhBUFBFIHByZS1wcm9jZXNzaW5nIHNjcmlwdCBpcyBzdG9yZWQKICAgLSBUaGUgbG9jYXRpb24gb2YgdGhlIHJhdyBkYXRhICh0byBiZSBwcm9jZXNzZWQpCiAgIC0gVGhlIGxvY2F0aW9uKHMpIG9mIGFueSBpbnRlcm1lZGlhdGUgZmlsZXMgZm9yIHByb2Nlc3NpbmcgKGUuZy4sIHRoZSB1cGRhdGVkIC5tZmYgZmlsZXMgdGhhdCBjb250YWluIGFjY3VyYWN5IGluZm9ybWF0aW9uIGluIEZpc2hTaGFyaykKICAgLSBUaGUgbG9jYXRpb24ocykgZm9yIGFueSBmaWxlcyBvdXRwdXR0ZWQgYnkgSEFQUEUgYW5kL29yIHBsYWNlcyB5b3Ugd2lzaCB0byB1c2UgdGhlIHNjcmlwdCB0byBtb3ZlIHRoZW0gdG8KCiMjIE9kZGJhbGwgKFByZS1IQVBQRSkgCjEuIFVwZGF0ZSBhbGwgdGhyZXNob2xkcyBhbmQgZmlsZXBhdGhzIGluIHNjcmlwdCBmaWxlIChtdXN0IGJlIGRvbmUgQkVGT1JFIHJ1bm5pbmcgdGhlIHNjcmlwdCkKICAgLSBJbiB0aGUgc2Vjb25kIHNlY3Rpb24gb2Ygb3VyIHNjcmlwdCBmaWxlLCB3ZSBzZXQgb3VyICJ0aHJlc2hvbGQiIGZvciB0aGUgbWluaW11bSBudW1iZXIgb2YgdHJpYWxzIHRoYXQgbmVlZCB0byBiZSByZXRhaW5lZCBhZnRlciBwcmUtcHJvY2Vzc2luZyBmb3IgYSBzdWJqZWN0J3MgZGF0YSB0byBiZSBlbGlnaWJsZSBmb3IgUENBLgogICBBZGRpdGlvbmFsIHRocmVzaG9sZHMgY2FuIGFsc28gYmUgc2V0IGZvciB0aGluZ3MgbGlrZSBudW1iZXIgb2YgY2hhbm5lbHMgcmV0YWluZWQsIGJ1dCB0aGVzZSBhcmUgbm90IGN1cnJlbnRseSBpbiB1c2UuCiAgIAogICBgYGAKICAgJSBTZXQgcXVhbGl0eSB0aHJlc2hvbGQgcGFyYW1ldGVycwogICB0cmlhbEN1dG9mZiA9IDEwOwogICBgYGAKICAgLSBXZSBhbHNvIHNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgd2l0aCBhbGwgb2YgdGhlIGZpbGVwYXRocyB0aGF0IGFyZSByZWxldmFudCBmb3IgbWFuYWdpbmcgSEFQUEUgb3V0cHV0IGZpbGVzIGFuZCB0cmFja2luZyBwcm9jZXNzZWQgZGF0YS4KICAgVGhlIGZvbGxvd2luZyBwYXRocyBzaG91bGQgYmUgY2hlY2tlZCBhbmQgdXBkYXRlZCBhcyBuZWNlc3NhcnkgdG8gcmVmbGVjdCB0aGUgb3JnYW5pemF0aW9uIG9mIHByb2Nlc3Npbmcgb24geW91ciBjb21wdXRlci4KICAgICAgLSBgcGFzc1BhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBoYXZlIGZpbGVzIHRoYXQgbWVldCBvciBleGNlZWQgdGhlIGFib3ZlLWRlZmluZWQgdGhyZXNob2xkcyB0byBiZSBzYXZlZAogICAgICAtIGBhbGxQYXRoYCBpcyB0aGUgbG9jYXRpb24geW91IHdpc2ggdG8gaGF2ZSBBTEwgZmlsZXMgb3V0cHV0dGVkIGZyb20gSEFQUEUgc2F2ZWQgdG8gKHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aHJlc2hvbGQgaXMgbWV0IG9yIG5vdCkKICAgICAgLSBgZmFpbFBhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBoYXZlIGZpbGVzIHRoYXQgZG8gbm90IG1lZXQgdGhlIGFib3ZlLWRlZmluZWQgdGhyZXNob2xkcyB0byBiZSBjb3BpZWQgdG8KICAgICAgICAgLSAubWZmIGZpbGVzIHRoYXQgZG8gbm90IG1lZXQgdGhyZXNob2xkIHdpbGwgYmUgY29waWVkIGhlcmUgYXMgYW4gaW5kaWNhdGlvbiB0aGF0IHRoZXkgc2hvdWxkIGJlIHByb2Nlc3NlZCBtYW51YWxseSB0byBzZWUgaWYgdGhleSBtZWV0IHRocmVzaG9sZCBhZnRlcndhcmQKICAgICAgLSBgc3VtbVBhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBzYXZlIHRoZSBmaWxlIHRoYXQgbGlzdHMgYWxsIGZpbGVzIHByb2Nlc3NlZCB0aHJvdWdoIEhBUFBFIGluIHRoZSBjdXJyZW50IGJhdGNoCiAgICAgICAgIC0gV2UgY3VycmVudGx5IHVzZSB0aGlzIHRvIHNhdmUgdGhlICJwcm9jZXNzaW5nIGxvZyIgdG8gYSBsb2NhdGlvbiB0aGF0IGFsbCB0ZWFtIG1lbWJlcnMvY29tcHV0ZXJzIGhhdmUgYWNjZXNzIHRvIHNvIGl0IGlzIGVhc2llciB0byBkZXRlcm1pbmUgd2hpY2ggZmlsZXMgcmVxdWlyZSBwcm9jZXNzaW5nIHdoZW4gRUVHIGRhdGEgYXJlIG5vdCBzdG9yZWQgb24gYSBzaGFyZWQgc2VydmVyCiAgICAKICAgIGBgYAogICAgJSBTZXQgcGF0aHMgZm9yIGZpbGUgc29ydGluZwogICAgcGFzc1BhdGggPSAnVjpcUHJvY2Vzc2luZy1SZXBvXEZvbGRlciBTdHJ1Y3R1cmVcMyAtIEZpbGVzIGZvciBQQ0EnOyAlbG9jYXRpb24gZm9yIC50eHQgb3V0cHV0IGZpbGVzCiAgICBhbGxQYXRoID0gJ1Y6XFByb2Nlc3NpbmctUmVwb1xGb2xkZXIgU3RydWN0dXJlXDIgLSBQcm9jZXNzZWQgRmlsZXMnOyAlbG9jYXRpb24gZm9yIGFsbCBwcm9jZXNzZWQgZmlsZXMgdG8gZW5kIHVwCiAgICBmYWlsUGF0aCA9ICdWOlxQcm9jZXNzaW5nLVJlcG9cRm9sZGVyIFN0cnVjdHVyZVwxYiAtIE1hbnVhbCBQcm9jZXNzaW5nJzsgJWxvY2F0aW9uIHRvIGNvcHkgdW5zdWNjZXNzZnVsIC5tZmYgdG8gZm9yIG1hbnVhbCBwcm9jZXNzCgogICAgJSBTZXQgcGF0aCBmb3IgcHJvY2Vzc2luZyBzdW1tYXJ5CiAgICBzdW1tUGF0aCA9ICdaOlxTaGFyZWQgU2VydmVyXFN0dWR5IEZvbGRlclxEYXRhIFByb2Nlc3NpbmdcNi4gTUFUTEFCIEVFRyBQaXBlbGluZVxQcm9jZXNzZWQgRGF0YSBMb2dzJzsKICAgIGBgYAoxLiBSdW4gdGhlIEhBUFBFIFBpcGVsaW5lCiAgIC0gVGhpcyBmaXJzdCBzZWN0aW9uIGlzIGRlc2lnbmVkIHRvIHJlbHkgb24gdXNlciBpbnB1dC4KICAgUHJlc3MgIlJ1biIgb24gdGhlIE1BVExBQiBlZGl0b3Igd2luZG93IHdpdGggdGhlIGZpbGUgb3BlbiB0byBiZWdpbiB0aGUgcHJvY2Vzcy4KICAgLSBBIG1lc3NhZ2Ugd2lsbCBhcHBlYXIgaW4gdGhlIGNvbnNvbGUgcHJvbXB0aW5nIHlvdSB0byBlbnRlciB0aGUgZmlsZXBhdGggdG8gdGhlIGxvY2F0aW9uIG9mIHRoZSBIQVBQRSBwcmUtcHJvY2Vzc2luZyBmaWxlIHlvdSB3aXNoIHRvIHJ1bgogICAtIE9uY2UgdGhlIHBhdGggaXMgZW50ZXJlZCwgTUFUTEFCIHdpbGwgcnVuIHRoZSBmaWxlCgogICBgYGAKICAgJSBTZXQgcGF0aCB0byBIQVBQRSBwcmUtcHJvY2Vzc2luZyBzY3JpcHQKICAgaGFwcGVSdW4gPSBpbnB1dCgnRW50ZXIgdGhlIGZ1bGwgcGF0aCB0byB0aGUgSEFQUEUgcHJlLXByb2Nlc3NpbmcgZmlsZTpcbj4gJywncycpIDsKCiAgICUgQ2FsbCBhbmQgcnVuIEhBUFBFIHByZS1wcm9jZXNzaW5nIHNjcmlwdAogICBydW4oaGFwcGVSdW4pOwogICBgYGAKMS4gRW50ZXIgSEFQUEUgaW5wdXRzCiAgIC0gU2VlIHRoZSBhYm92ZSBzZWN0aW9uIGZvciBIQVBQRSB1c2VyIGlucHV0cyBmb3IgT2RkYmFsbAoKQXQgdGhpcyBwb2ludCwgbm8gbW9yZSB1c2VyIGludGVyYWN0aW9uIGlzIHJlcXVpcmVkIGZvciB0aGUgc2NyaXB0IHRvIGRvIGl0cyBqb2IuClRoZSBIQVBQRSBwaXBlbGluZSB3aWxsIHJ1biwgYW5kIHRoZSByZW1haW5pbmcgTUFUTEFCIGNvZGUgaW4gdGhlIHNjcmlwdCBmaWxlIHdpbGwgZXZhbHVhdGUgdGhlIGZpbGVzIG91dHB1dHRlZCBieSBIQVBQRSBhbmQgbW92ZSB0aGVtIHRvIHRoZSBhcHByb3ByaWF0ZSBsb2NhdGlvbnMgYmFzZWQgb24gdGhpcyBldmFsdWF0aW9uLgpTZWUgW0dlbmVyYWwgUG9zdC1IYXBwZSBTdGVwc10oI2dlbmVyYWxQb3N0KSBmb3IgZXhhbXBsZXMgb2YgdGhlIE1BVExBQiBjb2RlIGFuZCBhbiBleHBsYW5hdGlvbiBvZiB3aGF0IGl0IGRvZXMuCgojIyBGaXNoU2hhcmtzIChQcmUtSEFQUEUpCgoxLiBVcGRhdGUgYWxsIHRocmVzaG9sZHMgYW5kIGZpbGVwYXRocyBpbiBzY3JpcHQgZmlsZSAobXVzdCBiZSBkb25lIEJFRk9SRSBydW5uaW5nIHRoZSBzY3JpcHQpCiAgIC0gSW4gdGhlIGZpZnRoIHNlY3Rpb24gb2Ygb3VyIHNjcmlwdCBmaWxlLCB3ZSBzZXQgb3VyICJ0aHJlc2hvbGQiIGZvciB0aGUgbWluaW11bSBudW1iZXIgb2YgdHJpYWxzIHRoYXQgbmVlZCB0byBiZSByZXRhaW5lZCBhZnRlciBwcmUtcHJvY2Vzc2luZyBmb3IgYSBzdWJqZWN0J3MgZGF0YSB0byBiZSBlbGlnaWJsZSBmb3IgUENBLgogICBBZGRpdGlvbmFsIHRocmVzaG9sZHMgY2FuIGFsc28gYmUgc2V0IGZvciB0aGluZ3MgbGlrZSBudW1iZXIgb2YgY2hhbm5lbHMgcmV0YWluZWQsIGJ1dCB0aGVzZSBhcmUgbm90IGN1cnJlbnRseSBpbiB1c2UuCiAgIC0gTm90ZSB0aGF0IHNldHRpbmcgdGhlc2UgcGFyYW1ldGVycyBvY2N1cnMgbGF0ZXIgaW4gdGhlIEZpc2hTaGFya3Mgc2NyaXB0IHRoYW4gdGhlIG9uZSBmb3IgT2RkYmFsbDsgCiAgIHRoaXMgaXMgYmVjYXVzZSBGaXNoU2hhcmtzIGZpbGVzIHJlcXVpcmUgYWRkaXRpb25hbCBwcm9jZXNzaW5nIGJlZm9yZSB0aGV5IGFyZSByZWFkeSBmb3IgSEFQUEUgKHNlZSBzdGVwIDIgZm9yIGRldGFpbHMpCiAgIAogICBgYGAKICAgJSBTZXQgcXVhbGl0eSB0aHJlc2hvbGQgcGFyYW1ldGVycwogICB0cmlhbEN1dG9mZiA9IDEwOwogICBgYGAKICAgLSBXZSBhbHNvIHNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgd2l0aCBhbGwgb2YgdGhlIGZpbGVwYXRocyB0aGF0IGFyZSByZWxldmFudCBmb3IgbWFuYWdpbmcgSEFQUEUgb3V0cHV0IGZpbGVzIGFuZCB0cmFja2luZyBwcm9jZXNzZWQgZGF0YS4KICAgVGhlIGZvbGxvd2luZyBwYXRocyBzaG91bGQgYmUgY2hlY2tlZCBhbmQgdXBkYXRlZCBhcyBuZWNlc3NhcnkgdG8gcmVmbGVjdCB0aGUgb3JnYW5pemF0aW9uIG9mIHByb2Nlc3Npbmcgb24geW91ciBjb21wdXRlci4KICAgICAgLSBgcGFzc1BhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBoYXZlIGZpbGVzIHRoYXQgbWVldCBvciBleGNlZWQgdGhlIGFib3ZlLWRlZmluZWQgdGhyZXNob2xkcyB0byBiZSBzYXZlZAogICAgICAtIGBhbGxQYXRoYCBpcyB0aGUgbG9jYXRpb24geW91IHdpc2ggdG8gaGF2ZSBBTEwgZmlsZXMgb3V0cHV0dGVkIGZyb20gSEFQUEUgc2F2ZWQgdG8gKHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aHJlc2hvbGQgaXMgbWV0IG9yIG5vdCkKICAgICAgLSBgZmFpbFBhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBoYXZlIGZpbGVzIHRoYXQgZG8gbm90IG1lZXQgdGhlIGFib3ZlLWRlZmluZWQgdGhyZXNob2xkcyB0byBiZSBjb3BpZWQgdG8KICAgICAgICAgLSAubWZmIGZpbGVzIHRoYXQgZG8gbm90IG1lZXQgdGhyZXNob2xkIHdpbGwgYmUgY29waWVkIGhlcmUgYXMgYW4gaW5kaWNhdGlvbiB0aGF0IHRoZXkgc2hvdWxkIGJlIHByb2Nlc3NlZCBtYW51YWxseSB0byBzZWUgaWYgdGhleSBtZWV0IHRocmVzaG9sZCBhZnRlcndhcmQKICAgICAgLSBgc3VtbVBhdGhgIGlzIHRoZSBsb2NhdGlvbiB5b3Ugd2lzaCB0byBzYXZlIHRoZSBmaWxlIHRoYXQgbGlzdHMgYWxsIGZpbGVzIHByb2Nlc3NlZCB0aHJvdWdoIEhBUFBFIGluIHRoZSBjdXJyZW50IGJhdGNoCiAgICAgICAgIC0gV2UgY3VycmVudGx5IHVzZSB0aGlzIHRvIHNhdmUgdGhlICJwcm9jZXNzaW5nIGxvZyIgdG8gYSBsb2NhdGlvbiB0aGF0IGFsbCB0ZWFtIG1lbWJlcnMvY29tcHV0ZXJzIGhhdmUgYWNjZXNzIHRvIHNvIGl0IGlzIGVhc2llciB0byBkZXRlcm1pbmUgd2hpY2ggZmlsZXMgcmVxdWlyZSBwcm9jZXNzaW5nIHdoZW4gRUVHIGRhdGEgYXJlIG5vdCBzdG9yZWQgb24gYSBzaGFyZWQgc2VydmVyCiAgICAKICAgIGBgYAogICAgJSBTZXQgcGF0aHMgZm9yIGZpbGUgc29ydGluZwogICAgcGFzc1BhdGggPSAnVjpcUHJvY2Vzc2luZy1SZXBvXEZvbGRlciBTdHJ1Y3R1cmVcMyAtIEZpbGVzIGZvciBQQ0EnOyAlbG9jYXRpb24gZm9yIC50eHQgb3V0cHV0IGZpbGVzCiAgICBhbGxQYXRoID0gJ1Y6XFByb2Nlc3NpbmctUmVwb1xGb2xkZXIgU3RydWN0dXJlXDIgLSBQcm9jZXNzZWQgRmlsZXMnOyAlbG9jYXRpb24gZm9yIGFsbCBwcm9jZXNzZWQgZmlsZXMgdG8gZW5kIHVwCiAgICBmYWlsUGF0aCA9ICdWOlxQcm9jZXNzaW5nLVJlcG9cRm9sZGVyIFN0cnVjdHVyZVwxYiAtIE1hbnVhbCBQcm9jZXNzaW5nJzsgJWxvY2F0aW9uIHRvIGNvcHkgdW5zdWNjZXNzZnVsIC5tZmYgdG8gZm9yIG1hbnVhbCBwcm9jZXNzCgogICAgJSBTZXQgcGF0aCBmb3IgcHJvY2Vzc2luZyBzdW1tYXJ5CiAgICBzdW1tUGF0aCA9ICdaOlxTaGFyZWQgU2VydmVyXFN0dWR5IEZvbGRlclxEYXRhIFByb2Nlc3NpbmdcNi4gTUFUTEFCIEVFRyBQaXBlbGluZVxQcm9jZXNzZWQgRGF0YSBMb2dzJzsKICAgIGBgYAoxLiBVcGRhdGUgcmF3ICgubWZmKSBmaWxlcycgY29uZGl0aW9uIHRhZ3Mgd2l0aCBhY2N1cmFjeSBpbmZvcm1hdGlvbgogICAtIFVubGlrZSB0aGUgUGFzc2l2ZSBPZGRiYWxsIHRhc2ssIEZpc2hTaGFya3MgdHJpYWxzIGNhbiBiZSBlaXRoZXIgY29ycmVjdCBvciBpbmNvcnJlY3QuIAogICBXaGV0aGVyIG9yIG5vdCBhIHRyaWFsIHdhcyAicmVzcG9uZGVkIHRvIiBjb3JyZWN0bHkgaXMgcmVsZXZhbnQgdG8gdGhlIG5hdHVyZSBvZiB0aGUgZXh0cmFjdGVkIEVSUC4KICAgQmVjYXVzZSB0aGUgZXZlbnQgdGFncyBpbiBvdXIgLm1mZiBmaWxlcyBkbyBub3QgaW5oZXJlbnRseSBjb250YWluIGluZm9ybWF0aW9uIGFib3V0IHdoZXRoZXIgZWFjaCB0cmlhbCB3YXMgcmVzcG9uZGVkIHRvIGNvcnJlY3RseSwgd2UgbmVlZCB0byBhZGQgaXQgb3Vyc2VsdmVzLgogICBUaGlzIHByb2Nlc3MgY2FuIGJlIGRvbmUgbWFudWFsbHkgaW4gTmV0U3RhdGlvbiwgYnV0IGl0IGNhbiBhbHNvIGJlIGF1dG9tYXRlZCB1c2luZyB0aGUgTUFUTEFCIGNvZGUgZGV0YWlsZWQgYmVsb3cuCiAgIC0gTk9URTogVGhpcyBjb2RlIHJlcXVpcmVzIGVlZ2xhYi4KICAgQmVmb3JlIHJ1bm5pbmcgdGhlIGNvZGUsIG9wZW4gZWVnbGFiIGluIHlvdXIgTUFUTEFCIHNlc3Npb24gYnkgdHlwaW5nIGBlZWdsYWJgIGludG8gdGhlIGNvbnNvbGUuCiAgIFlvdSBjYW4gY2xvc2UgaXQgYXMgc29vbiBhcyBpdCBvcGVucywgYnV0IHRoaXMgc3RlcCBlbnN1cmVzIHRoYXQgZWVnbGFiIGlzIGxvYWRlZCBpbnRvIHlvdXIgY3VycmVudCBzZXNzaW9uIGFuZCBoZWxwcyBwcmV2ZW50IHRoZSBzdWJzZXF1ZW50IGNvZGUgZnJvbSBlcnJvcmluZyBvdXQuCiAgIAogICAxLiBTZXQgdGhlIGZpbGVwYXRocyBmb3IgcmF3IGFuZCB1cGRhdGVkIC5tZmYgZmlsZXMKICAgICAgLSBUaGlzIHNlY3Rpb24gaXMgc2V0IHRvIHJlbHkgb24gdXNlciBpbnB1dHMuCiAgICAgIFByZXNzICJSdW4iIGluIHRoZSBNQVRMQUIgZWRpdG9yIHRvIHN0YXJ0IHRoZSBwcm9jZXNzaW5nLgogICAgICAtIEEgbWVzc2FnZSB3aWxsIGFwcGVhciBpbiB0aGUgY29uc29sZSBwcm9tcHRpbmcgeW91IHRvIGVudGVyIHR3byBmaWxlcGF0aHM6IAogICAgICB0aGUgZmlyc3QgaXMgdGhlIGxvY2F0aW9uIG9mIHRoZSByYXcgLm1mZiBmaWxlcywgYW5kIHRoZSBzZWNvbmQgaXMgdGhlIGxvY2F0aW9uIHlvdSB3b3VsZCBsaWtlIHRoZSB1cGRhdGVkIGZpbGVzIHRvIHNhdmUgaW4uCgogICAgICBgYGAKICAgICAgJSBVc2VyIGlucHV0IGZvciBsb2NhdGlvbiBvZiByYXcgZmlsZXMKICAgICAgcGF0aFJhdyA9IGlucHV0KCdFbnRlciB0aGUgZnVsbCBwYXRoIHRvIHRoZSBmb2xkZXIgY29udGFpbmluZyB0aGUgcmF3IGZpbGVzOlxuPiAnLCdzJyk7CiAgICAgIAogICAgICAlIFVzZXIgaW5wdXQgZm9yIGRlc3RpbmF0aW9uIG9mIHN1YnNldHRlZCBmaWxlcwogICAgICBwYXRoU3ViID0gaW5wdXQoJ0VudGVyIHRoZSBmdWxsIHBhdGggdG8gdGhlIGZvbGRlciBpbiB3aGljaCB0byBzYXZlIHRoZSBzdWJzZXR0ZWQgZmlsZXM6XG4+ICcsJ3MnKTsKICAgICAgYGBgCiAgICAgICBBdCB0aGlzIHBvaW50LCB0aGVyZSB3aWxsIGJlIG5vIHVzZXIgaW5wdXQvYWN0aW9ucyBuZWNlc3NhcnkgdW50aWwgYWxsIG9mIHRoZSAubWZmIGZpbGVzIGluIHRoZSAicGF0aFJhdyIgZGlyZWN0b3J5IGhhdmUgYmVlbiB1cGRhdGVkIGFuZCBzYXZlZCBpbnRvIHRoZSAicGF0aFN1YiIgZGlyZWN0b3J5LgogICAgICAgVGhlIGNvZGUgdGhhdCBhc2tzIHRoZSB1c2VyIGZvciB0aGUgcGF0aCB0byBIQVBQRSB3aWxsIHJ1biB3aGVuIHRoYXQgcHJvY2VzcyBoYXMgZmluaXNoZWQuCiAgICAgICBUaGUgZm9sbG93aW5nIHNlY3Rpb24gd2lsbCBkZXNjcmliZSB0aGUgY29kZSB1c2VkIHRvIGF1dG9tYXRlIHRoZSBwcm9jZXNzIG9mIHVwZGF0ZWQgLm1mZiBldmVudCB0YWdzIHRvIGluY2x1ZGUgYWNjdXJhY3kgaW5mb3JtYXRpb24gYXQgdGhlIHRyaWFsIGxldmVsLgogICAgICAgTW92ZSBvbiB0byBTdGVwIDMgd2hlbiB0aGUgcHJvY2VzcyBoYXMgY29tcGxldGVkLgoKICAgMS4gR2F0aGVyIGFuZCBtYW5hZ2UgaW5mb3JtYXRpb24gZnJvbSB0aGUgZGlyZWN0b3J5IGhvdXNpbmcgdGhlIHJhdyAoLm1mZikgZmlsZXMKICAgICAgCiAgICAgIGBgYAogICAgICAlIEhhdmUgTUFUTEFCIGdhdGhlciBhIGxpc3Qgb2YgcmF3IGZpbGVzIGhvdXNlZCBpbiBzcGVjaWZpZWQgbG9jYXRpb24gKHBhdGhSYXcpCiAgICAgIGRpckluZm8gPSBzdHJ1Y3QydGFibGUoZGlyKHBhdGhSYXcpKTsKCiAgICAgICUgUmVtb3ZlIGJsYW5rIHJvd3MKICAgICAgbm9OYW1lID0gc3RyY21wKGRpckluZm8ubmFtZSwgJy4nKSB8IHN0cmNtcChkaXJJbmZvLm5hbWUsICcuLicpOwogICAgICBkaXJJbmZvKG5vTmFtZSwgOikgPSBbXTsKICAgICAgYGBgCiAgIDEuIEdlbmVyYXRlIHZhcmlhYmxlcyBuZWNlc3NhcnkgZm9yIG1hbmFnaW5nIHJhdyBhbmQgdXBkYXRlZCBmaWxlcwogICAgICAtIFRoaXMgY29kZSB3aWxsIGdlbmVyYXRlIHRoZSBmdWxsIGZpbGVwYXRocyAoaW5jbHVkaW5nIGZpbGUgbmFtZSkgbmVjZXNzYXJ5IGZvciByZWFkaW5nIGluIGFuZCBzYXZpbmcgZWFjaCAubWZmIGZpbGUgYW5kIGl0cyB1cGRhdGVkIGNvdW50ZXJwYXJ0CiAgICAgIC0gSXQgd2lsbCBhbHNvIGdlbmVyYXRlIGFuIElEIHZhcmlhYmxlIGZvciBqb2luaW5nIHB1cnBvc2VzIGJhc2VkIG9uIHRoZSBleHBlY3RlZCBsb2NhdGlvbiBvZiB0aGUgc3ViamVjdCBuYW1lIGluIHRoZSBuYW1lIG9mIGVhY2ggZmlsZSAKCiAgICAgIGBgYAogICAgICAlIEdlbmVyYXRlIElEIHZhcmlhYmxlCiAgICAgIGRhdGFGaWxlcyA9IGRpckluZm8oOiwgIm5hbWUiKTsKICAgICAgCiAgICAgICUgQWRkIElEIHZhcmlhYmxlIHRvIGZpbGUgZGF0YQogICAgICBkYXRhRmlsZXMuSUQgPSBleHRyYWN0QmVmb3JlKGRhdGFGaWxlcy5uYW1lLCA4KTsKICAgICAgCiAgICAgICUgR2VuZXJhdGUgcGF0aCB0byByZWFkIHJhdyBkYXRhCiAgICAgIHJhd1BhdGhzID0gZGF0YUZpbGVzOwogICAgICByYXdQYXRocy5wYXRoID0gc3RyY2F0KHtwYXRoUmF3fSwgIi8iLCBkaXJJbmZvLm5hbWUpOwogICAgICByYXdQYXRocyA9IHJhd1BhdGhzKDosIFsiSUQiLCAicGF0aCJdKTsKICAgICAgCiAgICAgICUgR2VuZXJhdGUgcGF0aCB0byBzYXZlIHVwZGF0ZWQgdmVyc2lvbnMgb2YgdGhlIGRhdGEgKGNvbnRhaW5pbmcgYWNjdXJhY3kgaW5mbyBhdCB0cmlhbCBsZXZlbCkKICAgICAgc3ViUGF0aHMgPSBkYXRhRmlsZXM7CiAgICAgIHN1YlBhdGhzLnBhdGggPSBzdHJjYXQoe3BhdGhTdWJ9LCAiLyIsIHN1YlBhdGhzLklELCAiX3N1Yl9maXNoc2hhcmsubWZmIik7CiAgICAgIHN1YlBhdGhzID0gc3ViUGF0aHMoOiwgWyJJRCIsICJwYXRoIl0pOwogICAgICAKICAgICAgJSBKb2luIGZpbGVwYXRoIGRhdGF0YWJsZXMKICAgICAgbWVyZ2VQYXRocyA9IGpvaW4ocmF3UGF0aHMsIHN1YlBhdGhzLCAnS2V5cycsIHsnSUQnfSkKICAgICAgYGBgCiAgICAxLiBVc2UgYSBsb29wIHRvIHVwZGF0ZSB0aGUgZXZlbnQgdGFncyBpbiBlYWNoIC5tZmYgZmlsZSB0byByZWZsZWN0IGFjY3VyYWN5IG9mIHJlc3BvbnNlCiAgICAgICAtIEZvciBldmVyeSBmaWxlIGluY2x1ZGVkIGluIHRoZSAibWVyZ2VQYXRocyIgZGF0YXNldCwgTUFUTEFCIHdpbGwgcGVyZm9ybSB0aGUgZm9sbG93aW5nIGFjdGlvbnM6CiAgICAgICAgICAtIFNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgcmVwcmVzZW50aW5nIHRoZSBwYXRoIHRvIHJlYWQgaW4gdGhlIG9yaWdpbmFsIC5tZmYgZmlsZSBhbmQgc2F2ZSBpdHMgdXBkYXRlZCBjb3VudGVycGFydCAodXBkYXRlcyB3aXRoIGVhY2ggaXRlcmF0aW9uIG9mIHRoZSBsb29wKQogICAgICAgICAgLSBSZWFkIGluIHRoZSAubWZmIGZpbGUKICAgICAgICAgIC0gRXh0cmFjdCB0aGUgImV2ZW50IiBpbmZvcm1hdGlvbiBmcm9tIHRoZSAubWZmIGZpbGUgCiAgICAgICAgICAtIEV2YWx1YXRlIHdoZXRoZXIgdGhlcmUgaXMgdXNhYmxlIGRhdGEgaW4gdGhlIGZpbGUgCiAgICAgICAgICAgICAtIFRoaXMgc3RlcCBwcmV2ZW50cyB0aGUgY29kZSBmcm9tIGVycm9yaW5nIG91dCBpZiBhIHN1YmplY3QgZGlkIG5vdCBtYWtlIGl0IHBhc3QgdGhlIHByYWN0aWNlIHRyaWFscwogICAgICAgICAgICAgLSBJZiB0aGUgZXZhbHVhdGlvbiBkZXRlcm1pbmVzIHRoYXQgdGhlcmUgaXMgTk9UIHVzYWJsZSBkYXRhIGluIHRoZSBwcmVzZW50IGZpbGUsIHRoZSBsb29wIHdpbGwganVtcCB0byB0aGUgbmV4dCBmaWxlCiAgICAgICAgICAtIENyZWF0ZSBhIHRhYmxlIGNvbnRhaW5pbmcgcmVzcG9uc2UgaW5mb3JtYXRpb24gKHJlc3BvbnNlIHZzIG5vIHJlc3BvbnNlIGFuZCByZWFjdGlvbiB0aW1lKSBhbmQgY29uZGl0aW9uIGluZm9ybWF0aW9uIChnbyB2cyBuby1nbykgYXQgdGhlIHRyaWFsIGxldmVsCiAgICAgICAgICAtIEV2YWx1YXRlIGVhY2ggdHJpYWwgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIHJlc3BvbnNlIHdhcyBjb3JyZWN0IG9yIGluY29ycmVjdAogICAgICAgICAgICAgLSBGb3IgTm8tR28gdHJpYWxzLCByZXNwb25zZXMgYXJlIENPUlJFQ1QgaWYgdGhlcmUgd2FzIG5vIHJlc3BvbnNlIChpbmNvcnJlY3QgaWYgc3ViamVjdCBkaWQgcmVzcG9uZCkKICAgICAgICAgICAgIC0gRm9yIEdvIHRyaWFscywgcmVzcG9uc2VzIGFyZSBDT1JSRUNUIGlmIHRoZXJlIHdhcyBhIHJlc3BvbnNlIEFORCBzdWJqZWN0J3MgcmVhY3Rpb24gdGltZSB3YXMgYXQgbGVhc3QgMjAwIG1zIChpbmNvcnJlY3QgaWYgc3ViamVjdCBkaWQgbm90IHJlc3BvbmQgT1IgaWYgc3ViamVjdCByZXNwb25kZWQgdG9vIHF1aWNrbHkgZm9yIHJlc3BvbnNlIHRvIGJlIGNvbnNpZGVyZWQgInZhbGlkIikKICAgICAgICAgIC0gVXBkYXRlIHRoZSBldmVudCB0YWdzIGluIHRoZSAubWZmIGZpbGUgdG8gY29udGFpbiBhICJjIiBmb3IgY29ycmVjdCB0cmlhbHMgYW5kIGFuICJ4IiBmb3IgaW5jb3JyZWN0IHRyaWFscyAKICAgICAgICAgICAgIC0gVGhlIGMveCBpbmRpY2F0b3Igd2lsbCBiZSBhcHBlbmRlZCB0byB0aGUgZnJvbnQgb2YgdGhlIGV4aXN0aW5nIGV2ZW50IHRhZyAoZS5nLiwgYEdvKytgIHdpbGwgYmVjb21lIGBjR28rK2ApCiAgICAgICAgICAtIEV4cG9ydCBhbiB1cGRhdGVkIHZlcnNpb24gb2YgdGhlIC5tZmYgZmlsZSB3aXRoIGFjY3VyYWN5IGluZm9ybWF0aW9uIHRvIHRoZSBzcGVjaWZpZWQgbG9jYXRpb24gKCJwYXRoU3ViIikKCiAgICAgICBgYGAKICAgICAgIGZvciByb3cgPSAxOmhlaWdodChtZXJnZVBhdGhzKQogICAgICAgICAgICUgU3BlY2lmeSBwYXRocwogICAgICAgICAgIHJhd0ZvbGRlciA9IG1lcmdlUGF0aHN7cm93LCAicGF0aF9yYXdQYXRocyJ9CiAgICAgICAgICAgc3ViRm9sZGVyID0gbWVyZ2VQYXRoc3tyb3csICJwYXRoX3N1YlBhdGhzIn0KICAgIAogICAgICAgICAgICUgUmVhZCBpbiBFRUcgZGF0YQogICAgICAgICAgIEVFR3JhdyA9IHBvcF9tZmZpbXBvcnQoY2hhcihyYXdGb2xkZXIpLCAnY29kZScpCgogICAgICAgICAgICUgQ3JlYXRlIHRhYmxlIGZyb20gImV2ZW50IiBmaWVsZCBvZiByYXcgZGF0YQogICAgICAgICAgIEVFR2V2ZW50ID0gc3RydWN0MnRhYmxlKEVFR3Jhdy5ldmVudCkKCiAgICAgICAgICAgJSBDaGVjayBmb3IgdGhlIGV4aXN0ZW5jZSBvZiB1c2FibGUgcm93cwogICAgICAgICAgIGNoZWNrVmFycyA9IHN0cmNtcChFRUdldmVudC5Qcm9wZXJ0aWVzLlZhcmlhYmxlTmFtZXMsICdtZmZrZXlfY2VsJykKCiAgICAgICAgICAgJSBTa2lwIGZpbGVzIHdpdGhvdXQgbmVjZXNzYXJ5IHZhcmlhYmxlcwogICAgICAgICAgIGlmIG1heChjaGVja1ZhcnMpID09IDAKICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICBlbmQKCiAgICAgICAgICAgJSBDcmVhdGUgdGFibGUgd2l0aG91dCBwcmFjdGljZS90cmFpbmluZyB0cmlhbHMKICAgICAgICAgICBrZWVwUm93cyA9IHN0cmNtcChFRUdldmVudC5tZmZrZXlfY2VsLCAnNCcpCiAgICAgICAgICAgRUVHc3ViICA9IEVFR2V2ZW50KGtlZXBSb3dzLCA6KQoKICAgICAgICAgICAlIENoZWNrIGZvciB0aGUgZXhpc3RlbmNlIG9mIHVzYWJsZSByb3dzCiAgICAgICAgICAgY2hlY2tSb3dzID0gbWF4KGtlZXBSb3dzKQoKICAgICAgICAgICAlIFNraXAgZmlsZXMgd2l0aCBubyB1c2FibGUgcm93cwogICAgICAgICAgIGlmIGNoZWNrUm93cyA9PSAwCiAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgZW5kCgogICAgICAgICAgICUgR2V0IHJlc3BvbnNlIGluZm8gYXQgdHJpYWwgbGV2ZWwKICAgICAgICAgICBFRUdyZXNwID0gdGFibGUoRUVHc3ViLm1mZmtleV9vYnMsIEVFR3N1Yi5tZmZrZXlfZXZhbCwgRUVHc3ViLm1mZmtleV9ydGltKQogICAgICAgICAgIEVFR3Jlc3AgPSBybW1pc3NpbmcoRUVHcmVzcCkKICAgICAgICAgICBFRUdyZXNwID0gcmVuYW1ldmFycyhFRUdyZXNwLCBbIlZhcjEiLCAiVmFyMiIsICJWYXIzIl0sIFsiVHJpYWwiLCAiRXZhbCIsICJSVGltZSJdKQoKICAgICAgICAgICAlIEdldCBjb25kaXRpb24gaW5mbyBhdCB0cmlhbCBsZXZlbAogICAgICAgICAgIEVFR2NvbmRzID0gdGFibGUoRUVHc3ViLm1mZmtleV9vYnMsIEVFR3N1Yi50eXBlKQogICAgICAgICAgIEVFR2NvbmRzID0gcmVuYW1ldmFycyhFRUdjb25kcywgWyJWYXIxIiwgIlZhcjIiXSwgWyJUcmlhbCIsICJDb25kIl0pCiAgICAgICAgICAga2VlcENvbmRzID0gc3RyY21wKEVFR2NvbmRzLkNvbmQsICdHbysrJykgfCBzdHJjbXAoRUVHY29uZHMuQ29uZCwgJ05HKysnKQogICAgICAgICAgIEVFR2NvbmQgPSBFRUdjb25kcyhrZWVwQ29uZHMsIDopCgogICAgICAgICAgICUgTWVyZ2UgZGF0YXNldHMKICAgICAgICAgICBFRUd0cmlhbHMgPSBqb2luKEVFR2NvbmQsIEVFR3Jlc3ApCgogICAgICAgICAgIEVFR3RyaWFscy5SVGltZSA9IGNlbGxmdW4oQHN0cjJudW0sIEVFR3RyaWFscy5SVGltZSkKICAgICAgICAgICAlIEV2YWx1YXRlIHRyaWFscyBmb3IgY29ycmVjdC1uZXNzIG9mIHJlc3BvbnNlCiAgICAgICAgICAgY29ycmVjdCA9IHN0cmNtcChFRUd0cmlhbHMuQ29uZCwgJ0dvKysnKSAmIHN0cmNtcChFRUd0cmlhbHMuRXZhbCwgJzEnKSAmIEVFR3RyaWFscy5SVGltZSA+IDIwMCB8IHN0cmNtcChFRUd0cmlhbHMuQ29uZCwgJ05HKysnKSAmIHN0cmNtcChFRUd0cmlhbHMuRXZhbCwgJzAnKQogICAgICAgICAgIEVFR3RyaWFscy5BY2MgPSBjb3JyZWN0CgoKICAgICAgICAgICAlIENyZWF0ZSBuZXcgY29kZSB0YWdzIGluY2x1ZGluZyBhY2N1cmFjeSBpbmZvcm1hdGlvbgogICAgICAgICAgIEVFR3RyaWFscy5uZXdDb2RlKEVFR3RyaWFscy5BY2MgJiBzdHJjbXAoRUVHdHJpYWxzLkNvbmQsICdHbysrJykpID0geydjR28rKyd9CiAgICAgICAgICAgRUVHdHJpYWxzLm5ld0NvZGUofkVFR3RyaWFscy5BY2MgJiBzdHJjbXAoRUVHdHJpYWxzLkNvbmQsICdHbysrJykpID0geyd4R28rKyd9CiAgICAgICAgICAgRUVHdHJpYWxzLm5ld0NvZGUoRUVHdHJpYWxzLkFjYyAmIHN0cmNtcChFRUd0cmlhbHMuQ29uZCwgJ05HKysnKSkgPSB7J2NORysrJ30KICAgICAgICAgICBFRUd0cmlhbHMubmV3Q29kZSh+RUVHdHJpYWxzLkFjYyAmIHN0cmNtcChFRUd0cmlhbHMuQ29uZCwgJ05HKysnKSkgPSB7J3hORysrJ30KCiAgICAgICAgICAgJSBTdWJzZXQgaW5mb3JtYXRpb24gZm9yIG1lcmdlCiAgICAgICAgICAgRUVHbWVyZ2UgPSBFRUd0cmlhbHMoOiwgeydUcmlhbCcsICdDb25kJywgJ25ld0NvZGUnfSkKCiAgICAgICAgICAgJSBQcmVwIGtleSBpbiBvcmlnaW5hbCBkYXRhCiAgICAgICAgICAgRUVHZXZlbnQua2V5ID0gc3RyY2F0KEVFR2V2ZW50Lm1mZmtleV9vYnMsIEVFR2V2ZW50LnR5cGUpCgogICAgICAgICAgICUgUHJlcCBrZXkgaW4gbWVyZ2UgZGF0YQogICAgICAgICAgIEVFR21lcmdlLmtleSA9IHN0cmNhdChFRUdtZXJnZS5UcmlhbCwgRUVHbWVyZ2UuQ29uZCkKICAgICAgICAgICBFRUdtZXJnZSA9IEVFR21lcmdlKDosIHsna2V5JywgJ25ld0NvZGUnfSkKCiAgICAgICAgICAgJSBNZXJnZSBuZXcgY29kZXMgd2l0aCBldmVudCB0YWJsZQogICAgICAgICAgIEVFR25ldyA9IG91dGVyam9pbihFRUdldmVudCwgRUVHbWVyZ2UpCgogICAgICAgICAgICUgUmVwbGFjZSBjb2RlcyB3aGVyZSBuZXcgY29kZSBpcyBuZWVkZWQKICAgICAgICAgICBFRUduZXcuY29kZSh+c3RyY21wKEVFR25ldy5uZXdDb2RlLCAnJykpID0gRUVHbmV3Lm5ld0NvZGUofnN0cmNtcChFRUduZXcubmV3Q29kZSwgJycpKQogICAgICAgICAgIEVFR25ldy50eXBlKH5zdHJjbXAoRUVHbmV3Lm5ld0NvZGUsICcnKSkgPSBFRUduZXcubmV3Q29kZSh+c3RyY21wKEVFR25ldy5uZXdDb2RlLCAnJykpCiAgICAgICAgICAgRUVHbmV3ID0gdGFibGUyc3RydWN0KEVFR25ldyg6LCAxOjI4KSkKCiAgICAgICAgICAgJSBSZXBsYWNlIGV2ZW50IHRhYmxlIGluIG9yaWdpbmFsIHN0cnVjdAogICAgICAgICAgIEVFR3Jhdy5ldmVudCA9IEVFR25ldwoKICAgICAgICAgICAlIEV4cG9ydCB1cGRhdGVkIGZpbGUKICAgICAgICAgICBwb3BfbWZmZXhwb3J0KEVFR3JhdywgY2hhcihzdWJGb2xkZXIpKQogICAgICAgZW5kIAogICAgICAgYGBgCgogICAgCjEuIFJ1biB0aGUgSEFQUEUgUGlwZWxpbmUKICAgLSBUaGlzIHNlY3Rpb24gaXMgZGVzaWduZWQgdG8gcmVseSBvbiB1c2VyIGlucHV0LgogICAtIFdoZW4gdGhlIHByaW9yIHN0ZXBzIGhhdmUgZmluc2loZWQsIGEgbWVzc2FnZSB3aWxsIGFwcGVhciBpbiB0aGUgY29uc29sZSBwcm9tcHRpbmcgeW91IHRvIGVudGVyIHRoZSBmaWxlcGF0aCB0byB0aGUgbG9jYXRpb24gb2YgdGhlIEhBUFBFIHByZS1wcm9jZXNzaW5nIGZpbGUgeW91IHdpc2ggdG8gcnVuCiAgIC0gT25jZSB0aGUgcGF0aCBpcyBlbnRlcmVkLCBNQVRMQUIgd2lsbCBydW4gdGhlIGZpbGUKCiAgIGBgYAogICAlIFNldCBwYXRoIHRvIEhBUFBFIHByZS1wcm9jZXNzaW5nIHNjcmlwdAogICBoYXBwZVJ1biA9IGlucHV0KCdFbnRlciB0aGUgZnVsbCBwYXRoIHRvIHRoZSBIQVBQRSBwcmUtcHJvY2Vzc2luZyBmaWxlOlxuPiAnLCdzJykgOwoKICAgJSBDYWxsIGFuZCBydW4gSEFQUEUgcHJlLXByb2Nlc3Npbmcgc2NyaXB0CiAgIHJ1bihoYXBwZVJ1bik7CiAgIGBgYAoxLiBFbnRlciBIQVBQRSBpbnB1dHMKICAgLSBTZWUgdGhlIGFib3ZlIHNlY3Rpb24gZm9yIEhBUFBFIHVzZXIgaW5wdXRzIGZvciBGaXNoU2hhcmtzCgpBdCB0aGlzIHBvaW50LCBubyBtb3JlIHVzZXIgaW50ZXJhY3Rpb24gaXMgcmVxdWlyZWQgZm9yIHRoZSBzY3JpcHQgdG8gZG8gaXRzIGpvYi4KVGhlIEhBUFBFIHBpcGVsaW5lIHdpbGwgcnVuLCBhbmQgdGhlIHJlbWFpbmluZyBNQVRMQUIgY29kZSBpbiB0aGUgc2NyaXB0IGZpbGUgd2lsbCBldmFsdWF0ZSB0aGUgZmlsZXMgb3V0cHV0dGVkIGJ5IEhBUFBFIGFuZCBtb3ZlIHRoZW0gdG8gdGhlIGFwcHJvcHJpYXRlIGxvY2F0aW9ucyBiYXNlZCBvbiB0aGlzIGV2YWx1YXRpb24uClNlZSBbR2VuZXJhbCBQb3N0LUhhcHBlIFN0ZXBzXSgjZ2VuZXJhbFBvc3QpIGZvciBleGFtcGxlcyBvZiB0aGUgTUFUTEFCIGNvZGUgYW5kIGFuIGV4cGxhbmF0aW9uIG9mIHdoYXQgaXQgZG9lcy4KCgojIyBTdG9wLVNpZ25hbCAoUHJlLUhBUFBFKSAKCiMjIEdlbmVyYWwgUG9zdC1IQVBQRSBTdGVwcyB7I2dlbmVyYWxQb3N0fQoxLiBSZW1vdmUgZmlsZXMgdGhhdCBkb24ndCBoYXZlIGFueSBvdXRwdXQgZGF0YSBmcm9tIHRoZSBkYXRhc2V0IHRoYXQgd2lsbCBiZSB1c2VkIHRvIGFzc2VzcyBmaWxlIHF1YWxpdHkKICAgLSBUaGlzIHN0ZXAgaXMgaW1wb3J0YW50IGJlY2F1c2UgImVtcHR5IiBmaWxlcyBkb24ndCBwbGF5IG5pY2VseSB3aXRoIHRoZSBjb2RlIHVzZWQgdG8gZXZhbHVhdGUgZmlsZXMgdGhhdCBoYXZlIHNvbWUgZGF0YSBpbiB0aGVtIChldmVuIGlmIHRoZSBkYXRhIGRvIG5vdCBtZWV0IHRocmVzaG9sZCkKICAgLSBUaGlzIGNvZGUgcmVsaWVzIG9uIEhBUFBFJ3MgcXVhbGl0eSBkYXRhIHRoYXQgcmVtYWlucyBpbiB0aGUgTUFUTEFCIGVudmlyb25tZW50IGFmdGVyIHRoZSBwaXBlbGluZSBoYXMgZmluaXNoZWQuCgogICBgYGAKICAgJSBDcmVhdGUgYSBsaXN0IG9mIGZpbGVzIHRoYXQgcmVjZWl2ZWQgc29tZSBraW5kIG9mIGVycm9yIG1lc3NhZ2UKICAgbm9UYWdzID0gYW55KHN0cmNtcChkYXRhUUMsICdOT19UQUdTJyksIDIpOwogICBhbGxSZWogPSBhbnkoc3RyY21wKGRhdGFRQywgJ0FMTF9TRUdfUkVKJyksIDIpOwogICBlcnJvciA9IGFueShzdHJjbXAoZGF0YVFDLCAnRVJST1InKSwgMik7CiAgIGxvYWRGYWlsID0gYW55KHN0cmNtcChkYXRhUUMsICdMT0FEX0ZBSUwnKSwgMik7CgogICAlIENvbWJpbmUgZmlsZW5hbWVzIHdpdGggcXVhbGl0eSBkYXRhIChmb3Igc29tZSByZWFzb24sIHRoZXkgYXJlIG5vdCBhdXRvbWF0aWNhbGx5IGNvbm5lY3RlZCBieSBIQVBQRSkKICAgZGF0YVFDTmV3ID0gW0ZpbGVOYW1lcycsIGRhdGFRQ107CgogICAlIFJlbW92ZSBhbGwgZmlsZXMgaW4gdGhlIGFib3ZlIGxpc3RzICh0aG9zZSByZWNlaXZpbmcgZXJyb3JzKSBmcm9tIHRoZSBxdWFsaXR5IGRhdGEKICAgZGF0YVFDTmV3KG5vVGFncyB8IGFsbFJlaiB8IGVycm9yIHwgbG9hZEZhaWwsIDopID0gW107CgogICAlIENyZWF0ZSBsaXN0IG9mIHZhcmlhYmxlIG5hbWVzIGZvciBxdWFsaXR5IGRhdGEKICAgZGF0YVFDbmFtZXNOZXcgPSBbIkZpbGUiLCBkYXRhUUNuYW1lc107CiAgIAogICAlIFNhdmUgdGhlIGRhdGEgYXMgYSB0YWJsZSBmb3IgZWFzZSBvZiB1c2UgaW4gc3Vic2VxdWVudCBzdGVwcwogICBxY1RhYmxlID0gY2VsbDJ0YWJsZShkYXRhUUNOZXcsICdWYXJpYWJsZU5hbWVzJywgZGF0YVFDbmFtZXNOZXcpOwoKICAgJSBTdWJzZXQgdG8gSUQgYW5kIHRocmVzaG9sZCBpbmZvcm1hdGlvbgogICB0ZXN0SW5mbyA9IHFjVGFibGUoOiwgWyJpZFdhdmUiLCAiRmlsZSIsICJUZXN0Il0pOwogICBgYGAKCjIuIElkZW50aWZ5IHRoZSBmaWxlcyB0aGF0IG1lZXQgKG9yIGRvbid0IG1lZXQpIHRoZSB0aHJlc2hvbGQKCiAgIGBgYAogICAlIENyZWF0ZSBhIGxpc3Qgb2YgZmlsZXMgKGkuZS4sIHJvd3MgaW4gdGhlIHRhYmxlKSB0aGF0IG1lZXQgdGhyZXNob2xkCiAgIHRocmVzaG9sZFRlc3QgPSBxY1RhYmxlLigiTnVtYmVyX3RndCtfU2Vnc19Qb3N0LVNlZ19SZWoiKSA+PSB0cmlhbEN1dG9mZiAmIHFjVGFibGUuKCJOdW1iZXJfZnJxK19TZWdzX1Bvc3QtU2VnX1JlaiIpID49IHRyaWFsQ3V0b2ZmOwoKICAgJSBBZGQgYSB2YXJpYWJsZSB0byB0aGUgcXVhbGl0eSBkYXRhIHRhYmxlIHRoYXQgaW5jbHVkZSB3aGV0aGVyIG9yIG5vdCB0aGUgZmlsZSBtZWV0IHRocmVzaG9sZAogICBxY1RhYmxlLlRlc3QgPSB0aHJlc2hvbGRUZXN0OwogICBgYGAKCjEuIEFkZCBhbiBpZGVudGlmeWluZyB2YXJpYWJsZSB0byBiZSB1c2VkIGZvciBkYXRhIGpvaW5pbmcgZG93biB0aGUgbGluZQogICAtIFRoaXMgdmFyaWFibGUgaXMgZ2VuZXJhdGVkIHVzaW5nIGl0cyBleHBlY3RlZCBsb2NhdGlvbiBpbiB0aGUgZmlsZSBuYW1lIChpLmUuLCBob3cgbWFueSB0ZXh0IGNoYXJhY3RlcnMgImluIiBpdCBpcykKCiAgIGBgYAogICAlIEdlbmVyYXRlIElEcyBiYXNlZCBvbiBGaWxlIHZhcmlhYmxlCiAgIGlkV2F2ZVFDID0gZXh0cmFjdEJlZm9yZShxY1RhYmxlLkZpbGUsIDgpOwoKICAgJSBBcHBlbmQgSUQgdmFyaWFibGUgdG8gcXVhbGl0eSBkYXRhCiAgIHFjVGFibGUuaWRXYXZlID0gaWRXYXZlUUM7CiAgIGBgYAoKMS4gR2VuZXJhdGUgYSBsaXN0IG9mIGZpbGVzIG91dHB1dHRlZCBieSBIQVBQRQoKICAgYGBgCiAgICUgR2VuZXJhdGUgcGF0aCBmb3IgSEFQUEUgcHJlLXByb2Nlc3Npbmcgb3V0cHV0ICh1c2luZyB0aGUgSEFQUEUgZW52aXJvbm1lbnQgdmFyaWFibGUgZnJvbSB1c2VyJ3MgaW5wdXQgb2YgbG9jYXRpb24gb2YgcmF3IGRhdGEgZm9yIHByb2Nlc3NpbmcpCiAgIGlucHV0UGF0aCA9IHN0cmNhdChzcmNEaXIsICJcNSAtIHByb2Nlc3NlZCIpOwoKICAgJSBSZWFkIGluIGxpc3Qgb2YgZmlsZXMgb3V0cHV0dGVkIGZyb20gSEFQUEUgCiAgIHByZXByb2Nlc3NpbmdPdXRwdXQgPSBkaXIoaW5wdXRQYXRoKTsKICAgCiAgICUgUmVtb3ZlICJlbXB0eSIgcm93cyAKICAgcHJlcHJvY2Vzc2luZ091dHB1dCA9IHByZXByb2Nlc3NpbmdPdXRwdXQofmlzbWVtYmVyKHtwcmVwcm9jZXNzaW5nT3V0cHV0Lm5hbWV9LCB7Jy4nLCAnLi4nfSkpOwogICAKICAgJSBTYXZlIGRhdGEgYXMgYSB0YWJsZSBmb3IgZWFzZSBvZiBsYXRlciB1c2UKICAgcHJlcHJvY2Vzc2luZ091dHB1dCA9IHN0cnVjdDJ0YWJsZShwcmVwcm9jZXNzaW5nT3V0cHV0KTsKICAgCiAgICUgU3Vic2V0IHRvIGZpbGUgaW5mbwogICBmaWxlSW5mbyA9IHByZXByb2Nlc3NpbmdPdXRwdXQoOiwgWyJuYW1lIiwgImZvbGRlciJdKTsKICAgYGBgCjEuIFNlbGVjdCBvbmx5IGRlc2lyZWQgZmlsZXMgdG8gYmUgbW92ZWQvY29waWVkCiAgIC0gQ3VycmVudGx5LCB3ZSBkb24ndCBkbyBhbnl0aGluZyB3aXRoIHRoZSAiSW5kaXZpZHVhbCBUcmlhbCIgZmlsZXMgb3V0cHV0dGVkIGJ5IEhBUFBFLgogICBUaGVzZSBmaWxlcyBhcmUgcXVpdGUgbGFyZ2UgYW5kIHRha2UgYSBsb25nIHRpbWUgdG8gbW92ZSwgc28gaXQgaXMgbW9yZSBlZmZpY2llbnQgdG8ganVzdCByZW1vdmUgdGhlbSBmcm9tIHRoZSBkYXRhIGFuZCBub3Qgd29ycnkgYWJvdXQgbW92aW5nIHRoZW0gYW55d2hlcmUuCgogICBgYGAKICAgJSBTdWJzZXQgdG8gZGVzaXJlZCBmaWxlcyAoQXZlT3ZlclRyaWFsKQogICBmaWxlU3Vic2V0ID0gZmlsZUluZm8oY29udGFpbnMoZmlsZUluZm8ubmFtZSwgIkF2ZU92ZXJUcmlhbHMiKSwgOik7CiAgIGBgYAoxLiBBZGQgY29uZGl0aW9uLCBJRCwgYW5kIHRocmVzaG9sZC1yZWxhdGVkIHZhcmlhYmxlcyB0byB0aGUgZmlsZSBkYXRhCgogICBgYGAKICAgJSBHZW5lcmF0ZSBsaXN0IG9mIElEcyBiYXNlZCBvbiBmaWxlIG5hbWUgdmFyaWFibGUKICAgaWRXYXZlRlMgPSBleHRyYWN0QmVmb3JlKGZpbGVTdWJzZXQubmFtZSwgOCk7CiAgIAogICAlIEFkZCBJRCBsaXN0IHRvIGZpbGUgZGF0YQogICBmaWxlU3Vic2V0LmlkV2F2ZSA9IGlkV2F2ZUZTOwogICAKICAgJSBHZW5lcmF0ZSBsaXN0IG9mIGZpbGVzIGJlbG9uZ2luZyB0byBlYWNoIGNvbmRpdGlvbiBiYXNlZCBvbiBmaWxlIG5hbWUgdmFyaWFibGUKICAgdGFyZ2V0ID0gY29udGFpbnMoZmlsZVN1YnNldC5uYW1lLCAidGd0KyIpOwogICBmcmVxdWVudCA9IGNvbnRhaW5zKGZpbGVTdWJzZXQubmFtZSwgImZycSsiKTsKCiAgICUgQ3JlYXRlIGVtcHR5IHZhcmlhYmxlIGZvciBjb25kaXRpb24KICAgZmlsZVN1YnNldC5jb25kID0gY2VsbChzaXplKGZpbGVTdWJzZXQsIDEpLCAxKTsKCiAgICUgRmlsbCBpbiBjb25kaXRpb24gdmFyaWFibGUgYmFzZWQgb24gdGhlIGxpc3RzIGdlbmVyYXRlZCBhYm92ZQogICBmaWxlU3Vic2V0LmNvbmQodGFyZ2V0KSA9IHsnVGFyZ2V0J307CiAgIGZpbGVTdWJzZXQuY29uZChmcmVxdWVudCkgPSB7J0ZyZXF1ZW50J307CiAgIGZpbGVTdWJzZXQuY29uZCh+dGFyZ2V0ICYgfmZyZXF1ZW50KSA9IHsnQWxsJ307CgogICAlIEpvaW4gdGhyZXNob2xkIHRlc3QgaW5mb3JtYXRpb24KICAgZmlsZVRlc3QgPSBqb2luKGZpbGVTdWJzZXQsIHRlc3RJbmZvKTsKICAgYGBgCjEuIFByZXBhcmUgZGF0YSB0YWJsZSB3aXRoIGluZm9ybWF0aW9uIGFib3V0IGZpbGVzIHRoYXQgbWV0IHRoZSB0aHJlc2hvbGQKICAgLSBUaGUgZGF0YSBnZW5lcmF0ZWQgaGVyZSBhcmUgcHJlcGFyaW5nIHRvIGNvcHkgdGhlIC50eHQgZmlsZXMgb3V0cHV0dGVkIGJ5IEhBUFBFIGludG8gYSBmb2xkZXIgY29udGFpbmluZyBhbGwgZmlsZXMgdGhhdCBhcmUgc3VpdGFibGUgZm9yIFBDQQoKICAgYGBgCiAgICUgQ3JlYXRlIGEgc2VwYXJhdGUgdGFibGUgZm9yIG9ubHkgZmlsZXMgdGhhdCBtZWV0IHRocmVzaG9sZAogICBtb3ZpbmdJbmZvID0gZmlsZVRlc3QoZmlsZVRlc3QuVGVzdCwgOik7CgogICAlIENyZWF0ZSBlbXB0eSBjb2x1bW5zIGZvciBmaWxlcGF0aCB2YXJpYWJsZXMKICAgbW92aW5nSW5mby5kZXN0aW5hdGlvbiA9IGNlbGwoc2l6ZShtb3ZpbmdJbmZvLCAxKSwgMSk7CiAgIG1vdmluZ0luZm8ub3JpZ2luID0gY2VsbChzaXplKG1vdmluZ0luZm8sIDEpLCAxKTsKICAgbW92aW5nSW5mby5wcm9jZXNzZWRUbyA9IGNlbGwoc2l6ZShtb3ZpbmdJbmZvLCAxKSwgMSk7CiAgIG1vdmluZ0luZm8ucHJvY2Vzc2VkRnJvbSA9IGNlbGwoc2l6ZShtb3ZpbmdJbmZvLCAxKSwgMSk7CgogICAlIEdlbmVyYXRlIGZpbGUgcGF0aHMgYmFzZWQgb24gY29uZGl0aW9uIAogICBtb3ZpbmdJbmZvLmRlc3RpbmF0aW9uID0gc3RyY2F0KHtwYXNzUGF0aH0sICJcIiwgbW92aW5nSW5mby5jb25kLCAiXCIsIG1vdmluZ0luZm8ubmFtZSk7CiAgIG1vdmluZ0luZm8ub3JpZ2luID0gc3RyY2F0KG1vdmluZ0luZm8uZm9sZGVyLCAiXCIsIG1vdmluZ0luZm8ubmFtZSk7CiAgIG1vdmluZ0luZm8ucHJvY2Vzc2VkVG8gPSBzdHJjYXQoe2FsbFBhdGh9LCAiXCIsIG1vdmluZ0luZm8ubmFtZSk7CiAgIG1vdmluZ0luZm8ucHJvY2Vzc2VkRnJvbSA9IHN0cmNhdChtb3ZpbmdJbmZvLmZvbGRlciwgIlwiLCBtb3ZpbmdJbmZvLm5hbWUpOwogICBgYGAKCjEuIFByZXBhcmUgZGF0YSB0YWJsZSB3aXRoIGluZm9ybWF0aW9uIGFib3V0IGZpbGVzIHRoYXQgZG8gTk9UIG1lZXQgdGhlIHRocmVzaG9sZAogICAtIFRoZSBkYXRhIGdlbmVyYXRlZCBoZXJlIGFyZSBwcmVwYXJpbmcgdG8gY29weSAubWZmIGZpbGVzIGZyb20gdGhlIGxvY2F0aW9uIG9mIHRoZSByYXcgZmlsZXMgaW50byBhIGZvbGRlciBpbmRpY2F0aW5nIHRoZSBuZWVkIGZvciBtYW51YWwgcHJvY2Vzc2luZwoKICAgYGBgCiAgICUgQ3JlYXRlIGEgc2VwYXJhdGUgdGFibGUgZm9yIG9ubHkgZmlsZXMgdGhhdCBkaWQgbm90IG1lZXQgdGhyZXNob2xkCiAgIGZhaWxGaWxlcyA9IGZpbGVUZXN0KH5maWxlVGVzdC5UZXN0LCBbIkZpbGUiLCAiZm9sZGVyIiwgIm5hbWUiXSk7CgogICAlIENyZWF0ZSBlbXB0eSBjb2x1bW5zIGZvciBmaWxlcGF0aCB2YXJpYWJsZXMKICAgZmFpbEZpbGVzLmRlc3RpbmF0aW9uID0gY2VsbChzaXplKGZhaWxGaWxlcywgMSksIDEpOwogICBmYWlsRmlsZXMub3JpZ2luID0gY2VsbChzaXplKGZhaWxGaWxlcywgMSksIDEpOwogICBmYWlsRmlsZXMucHJvY2Vzc2VkVG8gPSBjZWxsKHNpemUoZmFpbEZpbGVzLCAxKSwgMSk7CiAgIGZhaWxGaWxlcy5wcm9jZXNzZWRGcm9tID0gY2VsbChzaXplKGZhaWxGaWxlcywgMSksIDEpOwoKICAgJSBHZW5lcmF0ZSBmaWxlcGF0aHMgYmFzZWQgb24gSUQgYW5kIHRhc2sKICAgZmFpbEZpbGVzLmRlc3RpbmF0aW9uID0gc3RyY2F0KHtmYWlsUGF0aH0sICJcIiwgZmFpbEZpbGVzLkZpbGUpOwogICBmYWlsRmlsZXMub3JpZ2luID0gc3RyY2F0KHtzcmNEaXJ9LCAiXCIsIGZhaWxGaWxlcy5GaWxlKTsKICAgZmFpbEZpbGVzLnByb2Nlc3NlZEZyb20gPSBzdHJjYXQoZmFpbEZpbGVzLmZvbGRlciwgIlwiLCBmYWlsRmlsZXMubmFtZSk7CiAgIGZhaWxGaWxlcy5wcm9jZXNzZWRUbyA9IHN0cmNhdCh7YWxsUGF0aH0sICJcIiwgZmFpbEZpbGVzLm5hbWUpOwoKICAgYGBgCgoxLiBHZW5lcmF0ZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdGhhdCBjb3JyZXNwb25kIHRvIHRoZSBjb2x1bW4gaW5kZXggb2YgcmVsZXZhbnQgdmFyaWFibGVzIGZvciBmaWxlIHNvcnRpbmcKICAgLSBOb3RlIHRoYXQgdGhlIHZlcnkgbGFzdCBsaW5lIG9mIHRoaXMgY29kZSBkZWZpbmVzIHRoZSB2YXJhaWJsZShzKSB0byBleGNsdWRlIGZyb20gdGhlIEhBUFBFIG91dHB1dHRlZCBmaWxlcy4KICAgVGhpcyB2YXJpYWJsZSBtdXN0IGJlIHN0cmlwcGVkIGZyb20gdGhlIGRhdGEgYmVmb3JlIHNhdmluZyB0aGVtLCBiZWNhdXNlIHRoZSBwcmVzZW5jZSBvZiB0aGUgZXh0cmEgdmFyaWFibGUgbWFrZXMgdGhlIGZpbGUgaW5jb21wYXRpYmxlIHdpdGggRVAgVG9vbGtpdCdzIFBDQSBwcm9jZXNzLgoKICAgYGBgCiAgICUgRGVmaW5lIGNvbHVtbiBsb2NhdGlvbnMgZm9yIGVhY2ggZmlsZXBhdGggdmFyaWFibGUKCiAgICUgRm9yIGZpbGVzIHRoYXQgbWVldCB0aHJlc2hvbGQ6CiAgIHRvQ29sID0gZmluZChzdHJjbXAobW92aW5nSW5mby5Qcm9wZXJ0aWVzLlZhcmlhYmxlTmFtZXMsICJkZXN0aW5hdGlvbiIpKTsKICAgZnJvbUNvbCA9IGZpbmQoc3RyY21wKG1vdmluZ0luZm8uUHJvcGVydGllcy5WYXJpYWJsZU5hbWVzLCAib3JpZ2luIikpOwogICBwcm9jQ29sdG8gPSBmaW5kKHN0cmNtcChtb3ZpbmdJbmZvLlByb3BlcnRpZXMuVmFyaWFibGVOYW1lcywgInByb2Nlc3NlZFRvIikpOwogICBwcm9jQ29sZnJvbSA9IGZpbmQoc3RyY21wKG1vdmluZ0luZm8uUHJvcGVydGllcy5WYXJpYWJsZU5hbWVzLCAicHJvY2Vzc2VkRnJvbSIpKTsKICAgCiAgICUgRm9yIGZpbGVzIHRoYXQgZG8gbm90IG1lZXQgdGhyZXNob2xkCiAgIHJhd0NvbCA9IGZpbmQoc3RyY21wKGZhaWxGaWxlcy5Qcm9wZXJ0aWVzLlZhcmlhYmxlTmFtZXMsICJvcmlnaW4iKSk7CiAgIG1hbkNvbCA9IGZpbmQoc3RyY21wKGZhaWxGaWxlcy5Qcm9wZXJ0aWVzLlZhcmlhYmxlTmFtZXMsICJkZXN0aW5hdGlvbiIpKTsKICAgZmFpbFByb2NDb2x0byA9IGZpbmQoc3RyY21wKGZhaWxGaWxlcy5Qcm9wZXJ0aWVzLlZhcmlhYmxlTmFtZXMsICJwcm9jZXNzZWRUbyIpKTsKICAgZmFpbFByb2NDb2xGcm9tID0gZmluZChzdHJjbXAoZmFpbEZpbGVzLlByb3BlcnRpZXMuVmFyaWFibGVOYW1lcywgInByb2Nlc3NlZEZyb20iKSk7CiAgIAogICAlIERlZmluZSB2YXJpYWJsZSB0byBleGNsdWRlCiAgIGV4dHJhVmFyID0gJ1RpbWUnOwogICBgYGAKMS4gVXNlIGEgbG9vcCB0byBwcm9jZXNzIGFsbCBmaWxlcyB0aGF0IG1ldCB0aHJlc2hvbGQKICAgLSBGb3IgZWFjaCByb3cgaW4gdGhlICJtb3ZpbmdJbmZvIiBkYXRhc2V0LCB0aGUgbG9vcCB3aWxsOgogICAgICAtIElkZW50aWZ5IHRoZSBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIHBhdGhzCiAgICAgIC0gUmVhZCBpbiB0aGUgSEFQUEUgb3V0cHV0IGZpbGUKICAgICAgLSBSZW1vdmUgdGhlIGV4dHJhIHZhcmlhYmxlCiAgICAgIC0gU2F2ZSB0aGUgImNsZWFuZWQiIGRhdGEgaW4gdGhlIGFwcHJvcHJpYXRlIGZvbGRlciAod2l0aG91dCB2YXJpYWJsZSBuYW1lcywgYXMgcmVxdWlyZWQgYnkgRVAgVG9vbGtpdCkgCgogICBgYGAKICAgZm9yIHJvdyA9IDE6aGVpZ2h0KG1vdmluZ0luZm8pCiAgICAgICAlIFNwZWNpZnkgcGF0aCBpbmZvCiAgICAgICBwYXRoRnJvbSA9IG1vdmluZ0luZm97cm93LCBmcm9tQ29sfTsKICAgICAgIHBhdGhUbyA9IG1vdmluZ0luZm97cm93LCB0b0NvbH07CiAgICAgICAlIFJlYWQgaW4gdGhlIGRhdGEKICAgICAgIHJhd1RhYmxlID0gcmVhZHRhYmxlKHBhdGhGcm9tKTsKICAgICAgICUgUmVtb3ZlIGV4dHJhIGNvbHVtbiAoVGltZSkKICAgICAgIGNsZWFuVGFibGUgPSByYXdUYWJsZXs6LCB+c3RyY21wKHJhd1RhYmxlLlByb3BlcnRpZXMuVmFyaWFibGVOYW1lcywgZXh0cmFWYXIpfTsKICAgICAgICUgU2F2ZSB3aXRob3V0IGhlYWRlcnMKICAgICAgIHdyaXRlbWF0cml4KGNsZWFuVGFibGUsIHBhdGhUbywgJ0RlbGltaXRlcicsICdcdCcpCiAgIGVuZAoKICAgYGBgCjEuIFVzZSBhIGxvb3AgdG8gY29weSByYXcgKC5tZmYpIGZpbGVzIGludG8gYSBsb2NhdGlvbiB0aGF0IHN0b3JlcyBmaWxlcyByZXF1aXJpbmcgbWFudWFsIHByb2Nlc3NpbmcKCiAgIGBgYAogICBmb3Igcm93ID0gMTpoZWlnaHQoZmFpbEZpbGVzKQogICAgICAgJSBTcGVjaWZ5IHBhdGggaW5mbwogICAgICAgcGF0aEZyb20gPSBmYWlsRmlsZXN7cm93LCByYXdDb2x9OwogICAgICAgcGF0aFRvID0gZmFpbEZpbGVze3JvdywgbWFuQ29sfTsKICAgICAgICUgQ29weSBmaWxlIAogICAgICAgY29weWZpbGUocGF0aEZyb20sIHBhdGhUbykKICAgZW5kCiAgIGBgYAoKMS4gVXNlIGEgc2V0IG9mIGxvb3BzIHRvIGNvcHkgYWxsIEhBUFBFIG91dHB1dCBmaWxlcyBpbnRvIGEgZm9sZGVyIGludGVuZGVkIHRvIGhvdXNlIGFsbCBvdXRwdXQgKHdoZXRoZXIgdGhyZXNob2xkIGlzIG1ldCBvciBub3QpCgogICBgYGAKICAgZm9yIHJvdyA9IDE6aGVpZ2h0KG1vdmluZ0luZm8pCiAgICAgICAlIFNwZWNpZnkgcGF0aCBpbmZvCiAgICAgICBwYXRoRnJvbSA9IG1vdmluZ0luZm97cm93LCBwcm9jQ29sZnJvbX07CiAgICAgICBwYXRoVG8gPSBtb3ZpbmdJbmZve3JvdywgcHJvY0NvbHRvfTsKICAgICAgICUgQ29weSBmaWxlCiAgICAgICBjb3B5ZmlsZShwYXRoRnJvbSwgcGF0aFRvKTsKICAgZW5kCgogICBmb3Igcm93ID0gMTpoZWlnaHQoZmFpbEZpbGVzKQogICAgICAgJSBTcGVjaWZ5IHBhdGggaW5mbwogICAgICAgcGF0aEZyb20gPSBmYWlsRmlsZXN7cm93LCBmYWlsUHJvY0NvbEZyb219OwogICAgICAgcGF0aFRvID0gZmFpbEZpbGVze3JvdywgZmFpbFByb2NDb2x0b307CiAgICAgICAlIENvcHkgZmlsZQogICAgICAgY29weWZpbGUocGF0aEZyb20sIHBhdGhUbyk7CiAgIGVuZAogICBgYGAKMS4gR2VuZXJhdGUgYSAudHh0IGZpbGUgbGlzdGluZyBhbGwgcHJvY2Vzc2VkIC5tZmYgZmlsZXMKICAgLSBUaGlzIGZpbGUgd2lsbCBjb250YWluIGEgbGlzdCBvZiBhbGwgcmF3IGZpbGVzIChlLmcuLCBgMTExMV8yMl9vZGRiYWxsLm1mZmApIGFuZCBzYXZlIHRoZSBsaXN0IHRvIHRoZSBzcGVjaWZpZWQgbG9jYXRpb24gKCJzdW1tUGF0aCIpCiAgIC0gVGhlIGZpbGUgd2lsbCBoYXZlIHRoZSBjdXJyZW50IGRhdGUgYW5kIHRpbWUgYXBwZW5kZWQgdG8gdGhlIGVuZCBzbyB0aGF0IGl0IHdpbGwgYmUgZGlzdGluZ3Vpc2hhYmxlIGZyb20gcGFzdCBsb2dzIAogICAtIFRoZSBsaXN0IG9mIHByb2Nlc3NlZCBmaWxlcyBpcyBnZW5lcmF0ZWQgdXNpbmcgYW4gZW52aXJvbm1lbnQgdmFyaWFibGUgdGhhdCB0aGUgSEFQUEUgcGlwZWxpbmUgY3JlYXRlcyB0aGF0IGxpc3RzIGFsbCBmaWxlcyBpbnB1dHRlZCB0byB0aGUgcGlwZWxpbmUKCiAgIGBgYAogICAlIENyZWF0ZSBhIHRhYmxlIGZyb20gSEFQUEUgRmlsZU5hbWVzIGNlbGwgYXJyYXkKICAgcHJvY2Vzc2VkTGlzdCA9IGNlbGwydGFibGUoRmlsZU5hbWVzKDopKTsKCiAgICUgUmVuYW1lIGZpbGUgdmFyaWFibGUgZnJvbSBkZWZhdWx0CiAgIHByb2Nlc3NlZExpc3QgPSByZW5hbWV2YXJzKHByb2Nlc3NlZExpc3QsIHsnVmFyMSd9LCB7J0ZpbGUnfSk7CgogICAlIFNhdmUgY3VycmVudCBkYXRlIGFzIGEgc3RyaW5nIHZhcmlhYmxlIAogICB0b2RheSA9IHN0cmluZyhkYXRlKCkpOwoKICAgJSBTYXZlIHRpbWUgYXMgYSBzdHJpbmcgdmFyaWFibGUsIHJlcGxhY2luZyAiOiIgd2l0aCAiXyIgc28gdGhhdCBmaWxlIGNhbiBiZSB3cml0dGVuIAogICB0aW1lID0gc3RycmVwKGRhdGVzdHIobm93LCAnSEg6TU06U1M6RkZGJyksICc6JywgIl8iKTsKCiAgICUgR2VuZXJhdGUgZmlsZSBuYW1lIHRvIGluY2x1ZGUgY3VycmVudCBkYXRlIGFuZCB0aW1lIAogICBsaXN0RmlsZSA9IHN0cmNhdCgiXG9kZGJhbGxQcm9jZXNzZWRfIiwgdG9kYXksICJfIiwgdGltZSk7CgogICAlIEdlbmVyYXRlIGZ1bGwgcGF0aCBpbmNsdWRpbmcgZmlsZSBuYW1lCiAgIHN1bW1QYXRoRnVsbCA9IHN0cmNhdChzdW1tUGF0aCwgbGlzdEZpbGUpOwoKICAgJSBXcml0ZSB0YWJsZSB0byBzcGVjaWZpZWQgbG9jYXRpb24KICAgd3JpdGV0YWJsZShwcm9jZXNzZWRMaXN0LCBzdW1tUGF0aEZ1bGwpOwogICBgYGAKCiMgRVJQIFBDQSAoRVApIFRvb2xraXQKCiMjIFJlYWRpbmcgVGV4dCBGaWxlcyBpbnRvIEVQIFRvb2xraXQKCjEuIE9wZW4gTUFUTEFCIHdpdGggIlJ1biBhcyBBZG1pbmlzdHJhdG9yIgoxLiBPcGVuIEVSUCBQQ0EgVG9vbGtpdCBpbiBNQVRMQUIKICAgIC0gVHlwZSBgZXBgIGluIGNvbW1hbmQgcHJvbXB0CjEuIENsaWNrIGBSZWFkYCB0byBpbXBvcnQgZmlsZXMKMS4gVXNlIHRoZSBmb2xsb3dpbmcgb3B0aW9ucwogICAgLSBGb3JtYXQgPSBgdGV4dCAoLnR4dClgCiAgICAtIFR5cGUgPSBgYXZlcmFnZWAKICAgIC0gTW9udCA9IGBBZHVsdCBIeWRyb2NlbCAxMjgtY2hhbm5lbCAxLjBgCjEuIFNlbGVjdCBgU2luZ2xlIEZpbGUgTW9kZWAuCiAgICAtIFNpbmdsZSBmaWxlIG1vZGUgd2lsbCB1c2UgdGhlIGZpbGVuYW1lIHRvIGFzc2lnbiB0aGUgdGFzayBjb25kaXRpb24gYW5kIHBhcnRpY2lwYW50IElEIGZvciBlYWNoIGZpbGUuCiAgICAgICAgLSBOb3RlOiBgUmAgY2FuIHJlbmFtZSBmaWxlcyBpbiBiYXRjaGVzCiAgICBUaHVzLCAqKml0IGlzIGNyaXRpY2FsIHRvIHVzZSBhIHN0YW5kYXJkIG5hbWluZyBjb252ZW50aW9uIHRvIG5hbWUgdGhlIGZpbGVzKiouCiAgICBGb3IgZXhhbXBsZSwgYW4gb2RkYmFsbCBmaWxlIGNvdWxkIGJlIG5hbWVkOiAKICAgICAgICAtIGBmcnFfMTAwMV8zNmAgYW5kIGB0Z3RfMTAwMV8zNmAKICAgIC0gQSBGaXNoU2hhcmtzIGZpbGUgbWVhbndoaWxlIGNvdWxkIGJlIG5hbWVkOiBgY2dvXzEwMDFfMzZgIGFuZCBgY25nXzEwMDFfMzZgCjEuIEluIHRoZSBgU2luZ2xlIEZpbGUgTW9kZWAgbWVudSB1c2UgdGhlIGBTdWJqZWN0YCBmaWVsZCB0byBkZW5vdGUgd2hpY2ggY2hhcmFjdGVycyBpbiB0aGUgZmlsZW5hbWUgbmFtZSB3aWxsIGRldGVybWluZSB0aGUgcGFydGljaXBhbnQgSUQuCiAgICAtIEZvciB0aGUgYWJvdmUgZXhhbXBsZSBgMTo3YCB3b3VsZCBjb3JyZXNwb25kIHRvIGAxMDAxXzM2YAoxLiBOZXh0LCBJbiB0aGUgYFNpbmdsZSBGaWxlIE1vZGVgIG1lbnUgdXNlIHRoZSBgQ2VsbGAgZmllbGQgdG8gZGVub3RlIHdoaWNoIGNoYXJhY3RlcnMgaW4gdGhlIGZpbGUgbmFtZSB3aWxsIGRldGVybWluZSB0aGUgdGFzayBjb25kaXRpb24uCiAgICAtIEZvciB0aGUgYmVsb3cgZXhhbXBsZSBgNDE6NDNgIHdvdWxkIGNvcnJlc3BvbmQgdG8gYGZycWAgb3IgYHRndGAuIAogICAgLSBGb3IgRmlzaFNoYXJrcyBmaWxlcywgaXQgbWlnaHQgYmUgYDQ3OjQ5YCB0aGF0IGNvcnJlc3BvbmQgdG8gYGNnb2Agb3IgYGNuZ2AuCiAgICAtICFbaW1hZ2VdKGltYWdlcy9yZWFkVGV4dEZpbGUyMDI0LnBuZykKMS4gU2VsZWN0IGBSZWFkYCBhbmQgc2VsZWN0IHRoZSBgMl85QXZlcmFnZU5ldDEyOGAgd2hlbiBwcm9tcHRlZAoxLiBUaGUgbmV3IGZpbGUgd2lsbCBoYXZlIHRoZSBwYXJ0aWNpcGFudCBJRCBhbmQgd2lsbCBjb21iaW5lIHRoZSBjb25kaXRpb25zIGZvciBlYWNoIHBhcnRpY2lwYW50LgogICAgLSBTdWJqZWN0IE5hbWVzOiAKICAgICAgICAtICFbaW1hZ2VdKGltYWdlcy9zdWJqZWN0Q29tYmluZWROYW1lcy5wbmcpCiAgICAtIFRhc2sgQ29uZGl0aW9ucwogICAgICAgIC0gIVtpbWFnZV0oaW1hZ2VzL2NvbmRpdGlvbkNvbWJpbmVkTmFtZXMucG5nKQoKIyMgVXBkYXRlIEZpbGUgd2l0aCBFeHBlcmltZW50IEluZm9ybWF0aW9uCgoxLiBHbyB0byBgTWFpbmAgYW5kIGNsaWNrIGBFZGl0YAoxLiBDbGljayBvbiB0aGUgZmlsZSB5b3UgaW1wb3J0ZWQKMS4gSW4gYE92ZXJ2aWV3YCwgYWRkIHRoZSBmb2xsb3dpbmcgaW5mb3JtYXRpb246CiAgICAtIEV4cGVyaW1lbnQgTmFtZTogYE9kZGJhbGxgLCBgRmlzaFNoYXJrYCwgb3IgYFN0b3BTaWduYWxgCiAgICAtIFJlZmVyZW5jZSBUeXBlOiBjaGFuZ2UgdG8gYGF2ZXJhZ2UgcmVmZXJlbmNlYAogICAgLSBQcmVzdGltdWx1cyBwZXJpb2Q6IGNoYW5nZSB0byBgMjAwYAogICAgLSBOb21pbmFsIHNhbXBsaW5nIHJhdGU6IGNoYW5nZSB0byBgMTAwMGAKMS4gQ2xpY2sgYERvbmVgCjEuIEdvIHRvIGBNYWluYCBhbmQgY2xpY2sgYFNhdmVgCiAgICAtIFNhdmUgdGhlIGNvbWJpbmVkIGZpbGUgYXMgYW4gYC5lcHRgIGZpbGUgaW4gdGhlIGA0LUVQVCBBdmVyYWdlc2AgZm9sZGVyIHVzaW5nIHRoZSBmb2xsb3dpbmcgbmFtaW5nIGNvbnZlbnRpb246ICJ0YXNrX2NvbmRpdGlvbl9hZ2UiLgogICAgRm9yIGV4YW1wbGUsIGlmIHlvdSB3ZXJlIHdvcmtpbmcgb24gdGhlIHRhcmdldCBjb25kaXRpb24gb2Ygb2RkYmFsbCBmb3IgYWxsIGFnZSBncm91cHMsIHlvdSB3b3VsZCBzYXZlIHRoZSBmaWxlIGFzIGBvYl90Z3RfYWxsYAoKIyMgR2VuZXJhdGluZyBHcmFuZCBBdmVyYWdlIFdhdmVmb3JtcwoxLiBJZiB0aGUgRVBUIGF2ZXJhZ2UgZmlsZSAoZS5nLiwgYG9iX3RndF9hbGxgKSBpcyBub3QgYWxyZWFkeSBpbiB0aGUgd29ya2luZyBlbnZpcm9ubWVudCwgcmVhZCBpdCBpbiB1c2luZyB0aGUgc3RlcHMgYmVsb3cKICAgLSBHbyB0byBgUmVhZGAKICAgLSBGb3JtYXQgPSBgRVAgKC5lcHQpYAogICAtIENsaWNrIGBSZWFkYAogICAtIE5hdmlnYXRlIHRvIHRoZSBgNCAtIEVQVCBBdmVyYWdlc2AgZm9sZGVyIGFuZCBzZWxlY3QgZGVzaXJlZCBmaWxlKHMpCiAgIC0gQ2xpY2sgYE9wZW5gIGluIHRoZSBicm93c2VyIHdpbmRvdyB0byByZWFkIHRoZSBmaWxlKHMpCiAgIC0gQ2xpY2sgYE1haW5gIHRvIHJldHVybiB0byBtYWluIG1lbnUKMS4gU2VsZWN0IGBFZGl0YAoxLiBXaGVuIHRoZSBlZGl0b3Igd2luZG93IG9wZW5zLCBuYXZpZ2F0ZSB0byB0aGUgYFN1YmplY3RzYCBwYW5lCjEuIFNlbGVjdCBgQWxsYCBmcm9tIGFtb25nIHRoZSBtYW55IG9wdGlvbnMgYWxvbmcgdGhlIGxlZnRoYW5kIHBhbmUgb2YgdGhlIGVkaXRvcgogICAtIFRoaXMgd2lsbCBzZWxlY3QgYWxsIG9mIHRoZSBzdWJqZWN0cyBpbmNsdWRlZCBpbiB0aGUgZmlsZSBhbmQgYXNzaWduIHRoZW0gYSB3ZWlnaHQgb2YgMQoxLiBDb25maXJtIHRoYXQgYWxsIHN1YmplY3RzIGhhdmUgYmVlbiBzZWxlY3RlZCAobG9vayBmb3IgYSBjaGVja2VkIGJveCBpbiB0aGUgc3ViamVjdCByb3cpIGFuZCB0aGF0IGFsbCB3ZWlnaHRzIGhhdmUgYmVlbiBzZXQgdG8gMQoxLiBDbGljayBgQWRkYAoxLiBBIG5ldyAic3ViamVjdCIgc2hvdWxkIGhhdmUgbm93IGJlZW4gYWRkZWQgdG8gdGhlIGJvdHRvbSBvZiB0aGUgc3ViamVjdHMgbGlzdAogICAtIFRoaXMgc3ViamVjdCBpcyBjYWxsZWQgYGdhdmVgIGFuZCByZXByZXNlbnRzIHRoZSBncmFuZCBhdmVyYWdlIGFjcm9zcyBhbGwgc3ViamVjdHMKMS4gQ2xpY2sgYERvbmVgIHRvIGV4aXQgdGhlIGVkaXRvciB3aW5kb3csIHRoZW4gYE1haW5gIHRvIHJldHVybiB0byB0aGUgRVAgVG9vbGtpdCBob21lCgojIyBUZW1wb3JhbCBQQ0EKCjEuIEdvIHRvIGBNYWluYCBhbmQgY2xpY2sgYFBDQWAKMS4gSW5wdXQgdGhlIGZvbGxvd2luZzoKICAgIC0gTW9kZTogYHRlbXBvcmFsYAogICAgLSBSb3RhdGlvbjogYHByb21heGAKICAgIC0gRmFjdG9yczogYDBgCiAgICAtIFRpdGxlOiB0UENBX2V4cGVyaW1lbnRuYW1lIChleGFtcGxlOiBgdFBDQV9vYl90Z3RfYWxsYCkKMS4gQ2xpY2sgdGhlIGFwcHJvcHJpYXRlIGZpbGUgKGUuZy4sIGBvYl90Z3RfYWxsYCkKMS4gRGV0ZXJtaW5lIGhvdyBtYW55IGZhY3RvcnMgdG8gcmV0YWluIHVzaW5nIHRoZSBzY3JlZSBwbG90IChrZWVwIHRoZSBudW1iZXIgb2YgZmFjdG9ycyB3aGVyZSB0aGUgYmx1ZSBsaW5lIGlzIGFib3ZlIHRoZSByZWQgbGluZSkKMS4gRGV0ZXJtaW5lIHRoZSBwZXJjZW50IHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgdGhlIG51bWJlciBvZiBmYWN0b3JzIHJldGFpbmVkIGJ5IGNoYW5naW5nIHRoZSAibWluaW11bSAlIGFnZSBhY2NvdW50ZWQgZm9yIGNyaXRlcmlvbiIuClJlY29yZCB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgcmV0YWluZWQgYW5kICUgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBieSB0aGF0IG51bWJlciBvZiBmYWN0b3JzLgoxLiBSZS1ydW4gdGhlIHRlbXBvcmFsIFBDQSB1c2luZyB0aGUgYWJvdmUgaW5wdXRzLCAqKmJ1dCBjaGFuZ2UgdGhlIG51bWJlciBvZiBmYWN0b3JzIHRvIHRoZSBudW1iZXIgb2YgZmFjdG9ycyByZXRhaW5lZCBmcm9tIHRoZSBhYm92ZSBzdGVwKioKMS4gUmV0dXJuIHRvIGBNYWluYCBhbmQgY2xpY2sgYFNhdmVgLiBTYXZlIHRoZSB0UENBIGZpbGUgaW4gdGhlIGA1LVBDQWAgZm9sZGVyCgojIyBTcGF0aWFsIFBDQQoKMS4gR28gdG8gYE1haW5gIGFuZCBjbGljayBgUENBYAoxLiBDaGFuZ2UgdGhlIFBDQSB0eXBlLCB1c2luZyB0aGUgZm9sbG93aW5nIGlucHV0czoKICAgIC0gTW9kZTogYHNwYXRpYWxgCiAgICAtIFJvdGF0aW9uOiBgaW5mb21heGAKICAgIC0gRmFjdG9yczogYDBgCiAgICAtIFRpdGxlOiBzUENBX2V4cGVyaW1lbnRuYW1lIChlLmcuLCBgc1BDQV9vYl90Z3RfYWxsYCkKMS4gQ2xpY2sgdGhlIGFwcHJvcHJpYXRlIGZpbGUgKGUuZy4sIGBvYl90Z3RfYWxsYCkKMS4gRGV0ZXJtaW5lIGhvdyBtYW55IGZhY3RvcnMgdG8gcmV0YWluIHVzaW5nIHRoZSBzY3JlZSBwbG90IChrZWVwIHRoZSBudW1iZXIgb2YgZmFjdG9ycyB3aGVyZSB0aGUgYmx1ZSBsaW5lIGlzIGFib3ZlIHRoZSByZWQgbGluZSkKMS4gRGV0ZXJtaW5lIHRoZSBwZXJjZW50IHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgdGhlIG51bWJlciBvZiBmYWN0b3JzIHJldGFpbmVkIGJ5IGNoYW5naW5nIHRoZSAibWluaW11bSAlIGFnZSBhY2NvdW50ZWQgZm9yIGNyaXRlcmlvbiIuClJlY29yZCB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgcmV0YWluZWQgYW5kICUgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBieSB0aGF0IG51bWJlciBvZiBmYWN0b3JzLgoxLiBSZS1ydW4gdGhlIHNwYXRpYWwgUENBIHVzaW5nIHRoZSBhYm92ZSBpbnB1dHMsICoqYnV0IGNoYW5nZSB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgdG8gdGhlIG51bWJlciBvZiBmYWN0b3JzIHJldGFpbmVkIGZyb20gdGhlIGFib3ZlIHN0ZXAqKgoxLiBSZXR1cm4gdG8gYE1haW5gIGFuZCBjbGljayBgU2F2ZWAuClNhdmUgdGhlIHNQQ0EgZmlsZSBpbiB0aGUgYDUtUENBYCBmb2xkZXIKCiMjIFRlbXBvcm9zcGF0aWFsIFBDQQoKMS4gR28gdG8gYE1haW5gIGFuZCBjbGljayBgUENBYAoxLiBDaGFuZ2UgdGhlIFBDQSB0eXBlLCB1c2luZyB0aGUgZm9sbG93aW5nIGlucHV0czoKICAgIC0gTW9kZTogYHNwYXRpYWxgCiAgICAtIFJvdGF0aW9uOiBgaW5mb21heGAKICAgIC0gRmFjdG9yczogYDBgCiAgICAtIFRpdGxlOiB0c1BDQV9leHBlcmltZW50bmFtZSAoZS5nLiwgYHRzUENBX29iX3RndF9hbGxgKQoxLiBDbGljayB0aGUgYHRQQ0FgIGZpbGUgKGNyZWF0ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXApCjEuIERldGVybWluZSBob3cgbWFueSBmYWN0b3JzIHRvIHJldGFpbiB1c2luZyB0aGUgc2NyZWUgcGxvdCAoa2VlcCB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgd2hlcmUgdGhlIGJsdWUgbGluZSBpcyBhYm92ZSB0aGUgcmVkIGxpbmUpCjEuIERldGVybWluZSB0aGUgcGVyY2VudCB2YXJpYW5jZSBhY2NvdW50ZWQgZm9yIGJ5IHRoZSBudW1iZXIgb2YgZmFjdG9ycyByZXRhaW5lZCBieSBjaGFuZ2luZyB0aGUgIm1pbmltdW0gJSBhZ2UgYWNjb3VudGVkIGZvciBjcml0ZXJpb24iLgpSZWNvcmQgdGhlIG51bWJlciBvZiBmYWN0b3JzIHJldGFpbmVkIGFuZCAlIHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgdGhhdCBudW1iZXIgb2YgZmFjdG9ycy4KMS4gUmUtcnVuIHRoZSBzcGF0aWFsIFBDQSB1c2luZyB0aGUgYWJvdmUgaW5wdXRzLCAqKmJ1dCBjaGFuZ2UgdGhlIG51bWJlciBvZiBmYWN0b3JzIHRvIHRoZSBudW1iZXIgb2YgZmFjdG9ycyByZXRhaW5lZCBmcm9tIHRoZSBhYm92ZSBzdGVwKioKMS4gUmV0dXJuIHRvIGBNYWluYCBhbmQgY2xpY2sgYFNhdmVgLgpTYXZlIHRoZSB0c1BDQSBmaWxlIGluIHRoZSBgNS1QQ0FgIGZvbGRlci4KCiMjIFBDQSBDb21wb25lbnQgU2VsZWN0aW9uCgpIZXJlLCB0aGUgZ29hbCBpcyB0byBzZWxlY3QgdGhlIFBDQSBjb21wb25lbnQgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgRVJQIGNvbXBvbmVudCBvZiBpbnRlcmVzdCwgYW5kIHRoZSBleHRyYWN0aW9uIHRoYXQgc3VwcG9ydHMgdGhlIGludGVuZGVkIGludGVycHJldGFiaWxpdHkgb2YgdGhlIGNvbXBvbmVudC4KCjEuIEdvIHRvIGBWaWV3YCB0byBiZWdpbiB0aGUgcHJvY2VzcyBvZiBzZWxlY3RpbmcgdGhlIFBDQSBjb21wb25lbnQgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgRVJQIG9mIGludGVyZXN0LgogICAgLSBJdGVyYXRpdmVseSBzZWxlY3QgYW5kIHZpZXcgZWFjaCB0ZW1wb3Jvc3BhdGlhbCBQQ0EgY29tcG9uZW50IHRvIGlkZW50aWZ5IHRoZSBQQ0EgY29tcG9uZW50ICgiZmFjdG9yIikgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgRVJQIG9mIGludGVyZXN0IChlLmcuLCBOMiBvciBQMykuCiAgICBTZWxlY3QgdGhlIHRlbXBvcm9zcGF0aWFsIFBDQSBjb21wb25lbnQgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgRVJQIG9mIGludGVyZXN0IGJhc2VkIG9uIHRoZSB0aW1pbmcsIHNwYXRpYWwgbG9jYXRpb24sIG1vcnBob2xvZ3ksIGFuZCAoYXMgcmVsZXZhbnQpIGFueSBjb25kaXRpb24tIG9yIGFnZS1yZWxhdGVkIGRpZmZlcmVuY2VzIG9mIHRoZSBjb21wb25lbnQgYmFzZWQgb24gcHJpb3Igd29yay4KMS4gR2VuZXJhdGUgdHNQQ0EgY29tcG9uZW50cy4KR28gdG8gYFdpbmRvd2AgYW5kIGlucHV0IHRoZSBmb2xsb3dpbmc6CiAgICAtIHNlbGVjdCB0aGUgdHNQQ0EgZmlsZQogICAgLSBzZWxlY3QgYW1vbmcgYG1lYW5gLCBgbWF4UGVha2AsIG9yIG90aGVyIG9wdGlvbnMuCiAgICAgIChBY2NvcmRpbmcgdG8gSm9lIERpZW4pLCB3aGVuIHVzaW5nIGFtcGxpdHVkZXMgZnJvbSBQQ0EgY29tcG9uZW50cywgaXQgZG9lcyBub3QgbWF0dGVyIHdoaWNoIG9wdGlvbiB5b3Ugc2VsZWN04oCUYWxsIHRoZSBkaWZmZXJlbnQgbWV0aG9kcyByZXN1bHQgaW4gY29tcGFyYWJsZSAqcCotdmFsdWVzIHdoZW4gZGVhbGluZyB3aXRoIFBDQSBjb21wb25lbnRzIChpdCB3b3VsZCBiZSBnb29kIHRvIHZlcmlmeSB0aGlzKS4KICAgICAgU28sIHNlbGVjdCBhIG1ldGhvZCB0aGF0IG1ha2VzIHNlbnNlIGZvciB0aGUgc3RvcnkgeW91IHdhbnQgdG8gdGVsbC4KICAgICAgVGhlIG1ldGhvZHMgd2lsbCB5aWVsZCBkaWZmZXJlbnQgcmVzdWx0cyB3aGVuIGRlYWxpbmcgd2l0aCB0aGUgcmF3IHdhdmVmb3Jtcy4KICAgIC0gc2VsZWN0IGBBdXRvUENBYCBvciBgV2luZG93YCB0byBzZWxlY3QgY2hhbm5lbHMuCiAgICAgIElmIHRoZSBwZWFrIGFtcGxpdHVkZSBpcyB3aGVyZSB5b3UgZXhwZWN0IHRlbXBvcmFsbHkgYW5kIHNwYXRpYWxseSwgdGhlbiB1c2UgdGhlIGF1dG9QQ0EgZnVuY3Rpb24sIGFuZCBpZiBpdCBpcyBub3QsIHRoZW4gd2luZG93IHRvIHdoZXJlIHlvdSBleHBlY3QgaXQgdG8gYmUuCiAgICAgIFRoaXMgd2lsbCBhbGxvdyB5b3UgdG8gcmVwb3J0IHJlc3VsdHMgdGhhdCBhcmUgbW9yZSBpbnRlcnByZXRhYmxlLgogICAgICBBcyBKb2UgRGllbiBkZXNjcmliZWQsIHRoZSB3YXkgdGhhdCBQQ0EgZGF0YSBhcmUgc3RvcmVkIGludGVybmFsbHkgaW4gdGhlIHRvb2xraXQgYXJlIGFzIGZhY3RvciBzY29yZXMgKGkuZS4sIGNvbXBvbmVudCBzY29yZXMpLgogICAgICBXaGVuIHlvdSBleHRyYWN0IGFtcGxpdHVkZXMgZnJvbSBhIFBDQSBjb21wb25lbnQsIHlvdSBhcmUgZXh0cmFjdGluZyB0aGUgZmFjdG9yIHNjb3JlcyBtdWx0aXBsaWVkIGJ5IGEgY29uc3RhbnQgKHNvbWUgc2NhbGluZyBmYWN0b3IsIHJlcHJlc2VudGluZyB0aGUgZWxlY3Ryb2RlIHdoZXJlIHlvdSBleHRyYWN0IGl0IGZyb20pLgogICAgICBUaHVzLCBhY2NvcmRpbmcgdG8gSm9lIERpZW4sIHRoZSAqcCotdmFsdWVzIHNob3VsZCBiZSB0aGUgc2FtZSByZWdhcmRsZXNzIG9mIHdoZXRoZXIgeW91IHVzZSBBdXRvUENBLCBvciBleHRyYWN0IGZyb20gYSBzaW5nbGUgZWxlY3Ryb2RlIG9yIG11bHRpcGxlIGVsZWN0cm9kZXMgKGl0IHdvdWxkIGJlIGdvb2QgdG8gdmVyaWZ5IHRoaXMpLgogICAgICBXaGF0IGlzIGNoYW5naW5nIGlzIG1lcmVseSB0aGUgc2NhbGluZyBmYWN0b3IgKGkuZS4sIHRoZSBjb25zdGFudCB0aGF0IGlzIG11bHRpcGxpZWQgYnkgYWxsIGZhY3RvciBzY29yZXMpLgogICAgICBXaGVuIHlvdSBzZWxlY3QgbXVsdGlwbGUgZWxlY3Ryb2RlcywgaXQgaXMgY29tcHV0aW5nIHRoZSBQQ0EtZXN0aW1hdGVkIGFtcGxpdHVkZSBhdCBlYWNoIGVsZWN0cm9kZSBhbmQgcGVyZm9ybWluZyBhIHNpbXBsZSBhdmVyYWdlIGFjcm9zcyB0aG9zZSBlbGVjdHJvZGVzLgogICAgICBUaGUgQXV0b1BDQSBleHRyYWN0cyB0aGUgUENBLWVzdGltYXRlZCBhbXBsaXR1ZGUgYXQgdGhlIHBlYWsgY2hhbm5lbCBhbmQgdGhlIHBlYWsgdGltZXBvaW50LgogICAgICBJZiB0aGUgd2F2ZWZvcm0gaXMgbmVnYXRpdmUtZ29pbmcgYXQgdGhlIHBlYWsgY2hhbm5lbCwgYW5kIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgcG9zaXRpdmUtZ29pbmcgZGlwb2xlLCB5b3Ugd291bGQgc2VsZWN0IHRoZSBwZWFrIHBvc2l0aXZlIGNoYW5uZWwgdG8gaWRlbnRpZnkgdGhlIFBDQS1lc3RpbWF0ZWQgYW1wbGl0dWRlIG9mIHRoZSBwb3NpdGl2ZS1nb2luZyB3YXZlZm9ybSBvbiB0aGF0IFBDQSBjb21wb25lbnQuCiAgICAgIE5ldmVydGhlbGVzcywgZXZlbiB0aG91Z2ggeW91IGFyZSBzZWxlY3RpbmcgdGhlIFBDQS1lc3RpbWF0ZWQgYW1wbGl0dWRlIGZvciBhIGdpdmVuIGNoYW5uZWwgYXQgYSBnaXZlbiBlbGVjdHJvZGUsIHRoZXJlIGFyZSBub3cgInZpcnR1YWwgY2hhbm5lbHMiOyB0aGUgZXN0aW1hdGVzIGluY2x1ZGUgdGhlIGNvbnRyaWJ1dGlvbnMgb2YgYWxsIGNoYW5uZWxzIGFuZCBhbGwgdGltZXBvaW50cyA8dT50byB0aGUgZXh0ZW50IHRoYXQ8L3U+IHRoZXkgbG9hZCBvbnRvIHRoZSBQQ0EgY29tcG9uZW50IG9mIGludGVyZXN0LgogICAgICBUaHVzLCBldmVuIGlmIHlvdSBzZWxlY3QgdG8gd2luZG93IGEgUENBIGNvbXBvbmVudCBmcm9tIG9ubHkgMSBjaGFubmVsIGF0IDEgdGltZXBvaW50LCBpdCBpcyB1c2luZyBBTEwgY2hhbm5lbHMgYW5kIHRpbWVwb2ludHMgaW4gdGhlIGVzdGltYXRpb27igJR0aGlzIGlzIG5vdCB0aGUgY2FzZSBpZiB3aW5kb3dpbmcgdGhlIHJhdyBFUlAgd2F2ZWZvcm1zLgogICAgLSBTYXZlIHRoZSBmaWxlcyBnZW5lcmF0ZWQgZnJvbSB0aGUgQXV0b1BDQSBpbiB0aGUgYDYtUENBIENvbXBvbmVudHNgIGZvbGRlciB1c2luZyB0aGUgZm9sbG93aW5nIG5hbWluZyBjb252ZW50aW9uOiAidGFza19jb25kaXRpb25fYWdlIiAoZS5nLixgb2JfdGd0X2FsbGApLgoxLiBUbyB2aWV3IGFsbCBvZiB0aGUgdHNQQ0EgY29tcG9uZW50cywgY2xpY2sgYFZpZXdgIGFuZCBpbnB1dCB0aGUgZm9sbG93aW5nCiAgICAtIHNlbGVjdCB0aGUgYXBwcm9wcmlhdGUgZmlsZSAoZS5nLiwgYG9iX3RndF9hbGxgKQogICAgLSBzZWxlY3QgYGdhdmVgCiAgICAtIHNlbGVjdCBgbm9uZWAKICAgIC0gY2xpY2sgYFdhdmVzYAoxLiBJdCBpcyBnb29kIHByYWN0aWNlIHRvIGNoZWNrIHRvIG1ha2Ugc3VyZSB0aGF0IGNvbXBvbmVudHMgYXJlIGNvbXBhcmFibGUgYWNyb3NzIGRpZmZlcmVudCBhZ2UgcmFuZ2VzCiAgICAtIFlvdSBjYW4gY2hlY2sgdGhpcyBpbiBvbmUgb2YgdHdvIHdheXM6CiAgICAgIC0gVmlzdWFsbHkgZXhhbWluZSBncmFuZCBhdmVyYWdlcyBiZXR3ZWVuIGFnZSByYW5nZXMKICAgICAgLSBBcHBseSB0aGUgUENBIGZyb20gb25lIGFnZSBncm91cCBhbmQgYXBwbHkgaXQgdG8gYW5vdGhlciBhZ2UgZ3JvdXAgYW5kIGV4YW1pbmUgd2hldGhlciB0aGUgcmVzdWx0cyBob2xkIHVwIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24gaW4gRVBUb29sa2l0CgojIyBJZGVudGlmeWluZyBFbGVjdHJvZGVzIHRoYXQgTG9hZCBPbnRvIFBDQSBDb21wb25lbnQKMS4gR28gdG8gYFdpbmRvd2AKMS4gU2VsZWN0IHRoZSBQQ0EgZmlsZSBvZiBpbnRlcmVzdCAoZS5nLiwgYHRzUENBLW9iX3RndF9hbGxgKQoxLiBDbGljayB0aGUgYENoYW5uZWxzYCBidXR0b24gKGFib3V0IGhhbGZ3YXkgZG93biB0aGUgYFdpbmRvd2Agd2luZG93KQoxLiBDbGljayBgRmFjdG9yYCAKICAgLSBGcm9tIHRoZSBkcm9wZG93biwgc2VsZWN0IHRoZSBQQ0EgZmlsZSBvZiBpbnRlcmVzdCAoZS5nLiwgYHRzUENBX29iX3RndF9hbGwpCiAgIC0gRW50ZXIgdGhlIHRocmVzaG9sZCBpbiB0aGUgc3BhY2UgYmVsb3cgKGUuZy4sIDAuNSkKICAgICAgLSBUaGlzIHNldHMgdGhlIG1pbnVtdW0gZmFjdG9yIGxvYWRpbmcgdmFsdWUgZm9yIGFuIGVsZWN0cm9kZSB0byBiZSAiaW5jbHVkZWQiIGluIHRoZSBjb21wb25lbnQtcmVsYXRlZCBjbHVzdGVyCiAgICAtIERlcGVuZGluZyBvbiB3aGVudGhlciB5b3UgYXJlIGludGVyZXN0ZWQgaW4gcG9zaXRpdmUgb3IgbmVnYXRpdmUgZmFjdG9yIGxvYWRpbmdzLCBzZWxlY3QgdGhlIGFwcHJvcHJpYXRlIHNpZ24gKGArYCwgYC1gLCBvciBgKy8tYCkKICAgIC0gQSBwb3B1cCB3aW5kb3cgd2l0aCBQQ0EgZmFjdG9ycyB3aWxsIGFwcGVhci4KICAgIFNlbGVjdCB0aGUgY29tcG9uZW50KHMpIHlvdSB3aXNoIHRvIGlkZW50aWZ5IHNwYXRpYWxseSAoZS5nLiwgYFRGMDFTRjAxYCkKMS4gV2hlbiBwcm9tcHRlZCwgZ2l2ZSB0aGUgZWxlY3Ryb2RlIGNsdXN0ZXIgYSBuYW1lCjEuIFRoZSBjaGFubmVscyB0aGF0IGNoYW5nZSBjb2xvciBhcmUgdGhvc2Ugd2hpY2ggbG9hZCBvbnRvIHRoZSBzZWxlY3RlZCBjb21wb25lbnQgYXQgb3IgYWJvdmUgdGhlIHRocmVzaG9sZCB2YWx1ZQoKIyMgRXhwb3J0aW5nIEdyYW5kIEF2ZXJhZ2UgRGF0YQoKSWYgeW91IGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gdGhlIGdyYW5kIGF2ZXJhZ2UgZGF0YSBhbmQgbm90IGluZGl2aWR1YWwgc3ViamVjdHMsIHRoZXNlIGluc3RydWN0aW9ucyB3aWxsIGFsbG93IHlvdSB0byBleHBvcnQgYSAudHh0IGZpbGUgY29udGFpbmluZyBvbmx5IHRoZSBncmFuZCBhdmVyYWdlIGRhdGEuCgoxLiBGcm9tIEVQIFRvb2xraXQgaG9tZSAoYE1haW5gIHNjcmVlbiksIHNlbGVjdCBgRWRpdGAKMS4gU2VsZWN0IHRoZSAuZXB0IGF2ZXJhZ2VzIGZpbGUgKGUuZy4sIGBvYl90Z3RfYWxsYCkgdGhhdCBjb250YWlucyBhICJzdWJqZWN0IiByZXByZXNlbnRpbmcgdGhlIGdyYW5kIGF2ZXJhZ2UKICAgLSBJZiB0aGUgZmlsZSBkb2VzIE5PVCBjb250YWluIGEgZ3JhbmQgYXZlcmFnZSBzdWJqZWN0LCBmb2xsb3cgdGhlIHN0ZXBzIGluIHRoZSBhYm92ZSBzZWN0aW9uIHRvIGdlbmVyYXRlIGl0CjEuIFJlbmFtZSB0aGUgZmlsZSAKICAgLSBGb3IgZXhhbXBsZSwgYG9iX3RndF9hbGxgIGNvdWxkIGJlIHJlbmFtZWQgdG8gYG9iX3RndF9nYXZgCiAgIC0gUmVuYW1pbmcgdGhlIGZpbGUgd2lsbCBwcm9tcHQgRVAgVG9vbGtpdCB0byBhc2sgd2hldGhlciB5b3Ugd2FudCB0byBnZW5lcmF0ZSBhIG5ldyBmaWxlIHdpdGggdGhpcyBuZXcgbmFtZSwgb3Igb3ZlcndyaXRlIHRoZSBleGlzdGluZyBkYXRhZmlsZSBvbmNlIHlvdXIgY2hhbmdlcyBhcmUgY29tcGxldGUKMS4gU2VsZWN0IGBTdWJqZWN0c2AgZnJvbSB0aGUgb3B0aW9ucyBhdCB0aGUgdG9wIG9mIHRoZSBlZGl0b3Igd2luZG93CjEuIENsaWNrIGBBbGxgIGZyb20gYW1vbmcgdGhlIG9wdGlvbnMgb24gdGhlIGxlZnRoYW5kIHNpZGUgb2YgdGhlIGBTdWJqZWN0c2Agd2luZG93CiAgIC0gVGhpcyB3aWxsIHNlbGVjdCBhbGwgb2YgdGhlIHN1YmplY3RzCjEuIFNjcm9sbCB0byB0aGUgYm90dG9tIG9mIHRoZSBsaXN0IG9mIHN1YmplY3RzIGFuZCAqKmRlc2VsZWN0KiogdGhlIHN1YmplY3QgbGFiZWxlZCBgZ3JhbmQgYXZlcmFnZWAKICAgLSBFc3NlbnRpYWxseSwgdGhlIGdvYWwgaGVyZSBpcyB0byBjcmVhdGUgYSBkYXRhc2V0IHRoYXQgaW5jbHVkZXMgT05MWSB0aGUgZ3JhbmQgYXZlcmFnZSBpbmZvcm1hdGlvbiwgcmF0aGVyIHRoYW4gZWFjaCBpbmRpdmlkdWFsIHN1YmplY3QKMS4gT25jZSBldmVyeXRoaW5nIEVYQ0VQVCBmb3IgdGhlIGdyYW5kIGF2ZXJhZ2Ugc3ViamVjdCBpcyBzZWxlY3RlZCwgY2xpY2sgYERlbGV0ZWAgb24gdGhlIGxlZnRoYW5kIHNpZGUgb2YgdGhlIGVkaXRvciB3aW5kb3cKICAgLSBUaGlzIHdpbGwgcmVtb3ZlIHRoZSBpbmRpdmlkdWFsIHN1YmplY3QgZGF0YSBmcm9tIHRoZSBkYXRhc2V0IGFuZCBsZWF2ZSB0aGUgZ3JhbmQgYXZlcmFnZSBpbmZvcm1hdGlvbiAKMS4gQ2xpY2sgYERvbmVgCjEuIElmIHlvdSByZW5hbWVkIHRoZSBkYXRhZmlsZSwgRVAgVG9vbGtpdCBzaG91bGQgZ2VuZXJhdGUgYSBwb3B1cCBtZXNzYWdlIGFza2luZyB3aGV0aGVyIHlvdSB3b3VsZCBsaWtlIHRvIHJlbmFtZSB5b3VyIGRhdGFzZXQgT1IgZ2VuZXJhdGUgYSBuZXcgZGF0YXNldCB1c2luZyB0aGUgbmV3IG5hbWUgKGxlYXZpbmcgdGhlIG9yaWdpbmFsIGRhdGFzZXQgdW50b3VjaGVkKS4KRnJvbSB0aGUgb3B0aW9ucyBwcmVzZW50ZWQsIGNsaWtjIGBOZXdgIHRvIGdlbmVyYXRlIGEgbmV3IGZpbGUgYW5kIHByZXNlcnZlIHRoZSBvcmlnaW5hbAoxLiBUaGUgZWRpdG9yIHdpbmRvdyBzaG91bGQgY2xvc2UsIHJldHVybmluZyB5b3UgdG8gdGhlIEVQIFRvb2xraXQgcGFuZSB0aGF0IGFza3MgeW91IHRvIHNlbGVjdCBhIGRhdGFzZXQgdG8gZWRpdC4gCkZyb20gaGVyZSwgY2xpY2sgYE1haW5gIHRvIHJldHVybiB0byBFUCBUb29sa2l0ICJob21lIgoxLiBPbmNlIGluIHRoZSBtYWluIHdpbmRvdywgY2xpa2MgYFNhdmVgCiAgIC0gU2V0IHRoZSBzYXZlIGZvcm1hdCB0byBgVGV4dCAoLnR4dClgCiAgIC0gQ2xpY2sgdGhlIGdyYW5kIGF2ZXJhZ2UgZGF0YSAoZS5nLiwgYG9iX3RndF9nYXZgKSB0byBzYXZlIGl0CiAgIC0gQSBmaWxlIGV4cGxvcmVyIHdpbmRvdyBzaG91bGQgb3BlbiwgcHJvbXB0aW5nIHlvdSB0byBzZWxlY3QgdGhlIGFwcHJvcHJpYXRlIHNhdmUgbG9jYXRpb24gYW5kIGdpdmUgeW91ciBmaWxlIGEgbmFtZSAKCiMgVmlzdWFsaXphdGlvbnMgaW4gUiAKCiMjIFIgQ29kZSBmb3IgR3JhbmQgQXZlcmFnZSBXYXZlZm9ybSBQbG90CgoxLiBSZWFkIGluIHRoZSBncmFuZCBhdmVyYWdlIHdhdmVmb3JtIGRhdGEgZXhwb3J0ZWQgZnJvbSBFUCBUb29sa2l0LgogICAtIFdlIGN1cnJlbnRseSBwcm9jZXNzIHRoZSBjb25kaXRpb25zIHdpdGhpbiBhIGdpdmVuIHRhc2sgc2VwYXJhdGVseSwgc28gZWFjaCBjb25kaXRpb24gc2hvdWxkIGhhdmUgaXRzIG93biBncmFuZCBhdmVyYWdlIGZpbGUuCiAgIGBgYAogICBvYlRndCA8LSByZWFkLnRhYmxlKCJWOi9TUlMtRVJQLU9kZGJhbGwvSGFyZC9BbGwvNCAtIEVQVCBBdmVyYWdlcy8yMDI0LTExLTA1L2dhdmUvb2JfdGd0X2dhdi50eHQiKQogICBvYkZycSA8LSByZWFkLnRhYmxlKCJWOi9TUlMtRVJQLU9kZGJhbGwvSGFyZC9BbGwvNCAtIEVQVCBBdmVyYWdlcy8yMDI0LTExLTA1L2dhdmUvb2JfZnJxX2dhdi50eHQiKQogICBgYGAKCjEuIENyZWF0ZSBhIHN1YnNldCBvZiBkYXRhIHRoYXQgb25seSBpbmNsdWRlcyB0aG9zZSBlbGVjdHJvZGVzIHRoYXQgYXJlIHBhcnQgb2YgdGhlIGNsdXN0ZXJzIGlkZW50aWZpZWQgaW4gRVAgVG9vbGtpdC4KICAgLSBUaGUgZ3JhbmQgYXZlcmFnZSBkYXRhIGRvZXMgbm90IGhhdmUgcm93IG9yIGNvbHVtbiBsYWJlbHMsIGJ1dCB0aGUgY29sdW1ucyByZXByZXNlbnQgdGhlIEVFRyBuZXQgY2hhbm5lbHMgaW4gbnVtZXJpY2FsIG9yZGVyICgxLTEyOSkuCiAgIFdlIGNhbiB0aGVyZWZvcmUgdXNlIHRoZWlyIGNvbHVtbiBpbmRleCB2YWx1ZXMgdG8gc2VsZWN0IHRoZSBkZXNpcmVkIGVsZWN0cm9kZXM7IHNvLCB0aGUgbGlzdCBjb250YWluaW5nIHRoZSBjaGFubmVsIG51bWJlcnMgc2hvdWxkIGluY2x1ZGUgT05MWSBudW1iZXJzLgogICBUaGUgY29kZSB0aGF0IHNlbGVjdHMgdGhlc2UgY2hhbm5lbHMgb3V0IG9mIHRoZSBmdWxsIGRhdGFzZXQgd2lsbCByZWx5IG9uIG51bWVyaWNhbCBpbnB1dC4gIAogICBgYGAKICAgIyBTZXQgZWxlY3Ryb2RlIGNsdXN0ZXJzCiAgIG9iRWxlY3Ryb2RlcyA8LSBjKDU4LCA1OSwgNjQsIDY1LCA2NiwgNjcsIDY5LCA3MCwgNzEsIDcyLCA3MywgNzQsIDc1LCA3NiwgNzcsIDgxLCA4MiwgODMsIDg0LCA4OSwgODAsIDkxLCA5NSwgOTYsIDEwMSkKCiAgICMgU3Vic2V0IHRvIGRlc2lyZWQgZWxlY3Ryb2RlcwogICBvYlRndF9zdWIgPC0gb2JUZ3RbLCBvYkVsZWN0cm9kZXNdCiAgIG9iRnJxX3N1YiA8LSBvYkZycVssIG9iRWxlY3Ryb2Rlc10KICAgYGBgCjEuIENvbXB1dGUgYXZlcmFnZXMgYW5kIGNyZWF0ZSBsYWJlbHMgZm9yIGNvbmRpdGlvbnMKICAgLSBPbmNlIHRoZSBkYXRhIGhhdmUgYmVlbiBzdWJzZXR0ZWQgZG93biB0byBpbmNsdWRlIG9ubHkgdGhlIGVsZWN0cm9kZSBjaGFubmVscyBvZiBpbnRlcmVzdCwgYWxsIHRoYXQgcmVtYWlucyBpcyB0byBjb21wdXRlIHRoZSBhdmVyYWdlIGFtcGxpdHVkZSBhY3Jvc3MgYWxsIG9mIHRob3NlIGNoYW5uZWxzCiAgIC0gQWRkaW5nIGEgY29uZGl0aW9uIGxhYmVsIHdpbGwgYWxsb3cgdXMgdG8gY29tYmluZSB0aGUgdHdvIGNvbmRpdGlvbi1zcGVjaWZpYyBkYXRhc2V0cyBpbnRvIG9uZSB0aGF0IGNhbiBiZSB1c2VkIGZvciB2aXN1YWxpemF0aW9ucyAKICAgICAgLSBGb3IgZWFzZSBvZiBwbG90dGluZywgbmFtZSB0aGUgY29uZGl0aW9ucyB0aGUgd2F5IHRoYXQgeW91IHdvdWxkIGxpa2UgdGhlbSB0byBhcHBlYXIgb24gdGhlIGZpZ3VyZSAoaS5lLiwgIlRhcmdldCIgaW5zdGVhZCBvZiAidGd0IikKICAgCiAgIGBgYAogICAjIENvbXB1dGUgYXZlcmFnZXMgCiAgIG9iVGd0X3N1YiRhbXBsaXR1ZGUgPC0gcm93TWVhbnMob2JUZ3Rfc3ViKQogICBvYkZycV9zdWIkYW1wbGl0dWRlIDwtIHJvd01lYW5zKG9iRnJxX3N1YikKCiAgICMgUmVtb3ZlIHJhdyB2YWx1ZXMgYW5kIGFkZCBjb25kaXRpb24gbGFiZWxzCiAgIG9iVGd0X2FtcHMgPC0gb2JUZ3Rfc3ViICU+JSBzZWxlY3QoYW1wbGl0dWRlKSAlPiUgbXV0YXRlKGNvbmRpdGlvbiA9ICJUYXJnZXQiKQogICBvYkZycV9hbXBzIDwtIG9iRnJxX3N1YiAlPiUgc2VsZWN0KGFtcGxpdHVkZSkgJT4lIG11dGF0ZShjb25kaXRpb24gPSAiRnJlcXVlbnQiKQogICBgYGAKCjEuIEFkZCB0aW1pbmctcmVsYXRlZCBpbmZvcm1hdGlvbiB0byB0aGUgZGF0YQogICAtIEVQIFRvb2xraXQgZXhwb3J0cyBFUlAgZGF0YSB3aXRob3V0IHRpbWVzdGFtcHMsIGJ1dCBhcnJhbmdlcyBpdCBpbiBvcmRlciBvZiB0aW1pbmcKICAgLSBXZSBjYW4gY3JlYXRlIGEgdGVtcGxhdGUgd2l0aCB0aGUgYXBwcm9wcmlhdGUgdGltZXN0YW1wcyBhbmQgYXBwZW5kIHRoaXMgY29sdW1uIHRvIHRoZSBhbXBsaXR1ZGUgZGF0YQogICBgYGAKICAgIyBDcmVhdGUgdGVtcGxhdGUKICAgZXJwVGVtcGxhdGUgPC0gZGF0YS5mcmFtZSgKICAgICB0aW1lID0gLTE5OToxMDAwCiAgICkKCiAgICMgTWVyZ2UgdGVtcGxhdGUgd2l0aCBhbXBsaXR1ZGUgZGF0YQogICBvYlRndFRpbWVzIDwtIGNiaW5kKGVycFRlbXBsYXRlLCBvYlRndF9hbXBzKQogICBvYkZycVRpbWVzIDwtIGNiaW5kKGVycFRlbXBsYXRlLCBvYkZycV9hbXBzKQogICBgYGAKMS4gQ29tYmluZSBhbGwgY29uZGl0aW9ucyBpbnRvIGEgc2luZ2xlIGRhdGEgb2JqZWN0IHRvIGJlIHVzZWQgZm9yIHBsb3R0aW5nIAogICBgYGAKICAgb2RkYmFsbCA8LSByYmluZChvYlRndFRpbWVzLCBvYkZycVRpbWVzKSAlPiUgCiAgICAgICAgICAgICAgc2VsZWN0KHRpbWUsIGNvbmRpdGlvbiwgYW1wbGl0dWRlKSAlPiUgCiAgICAgICAgICAgICAgYXJyYW5nZSh0aW1lKQogICBgYGAKCjEuIEdlbmVyYXRlIHRoZSB3YXZlZm9ybSBmaWd1cmVzIAogICBgYGAKICAgZ2dwbG90KAogICAgIGRhdGEgPSBvZGRiYWxsLAogICAgIGFlcygKICAgICAgIHggPSB0aW1lLAogICAgICAgeSA9IGFtcGxpdHVkZSwKICAgICAgIGdyb3VwID0gY29uZGl0aW9uLAogICAgICAgY29sb3IgPSBjb25kaXRpb24KICAgICApCiAgICkgKwogICAgIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjUpICsKICAgICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICAgICBuYW1lID0gIlRpbWUgUmVsYXRpdmUgdG8gU3RpbXVsdXMgT25zZXQgKG1zKSIsCiAgICAgICBsaW1pdHMgPSBjKC0yMDAsIDEwMDApLAogICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAtMjAwLCB0byA9IDEwMDAsIGJ5ID0gMjAwKSkgKwogICAgIHNjYWxlX3lfY29udGludW91cygKICAgICAgIG5hbWUgPSAiVm9sdGFnZSAobWljcm92b2x0cykiLAogICAgICAgbGltaXRzID0gYygtNCwgMTApLAogICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAtMTAsIHRvID0gMTUsIGJ5ID0gMikpICsKICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoKSsKICAgICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE4KSArCiAgICAgdGhlbWUoCiAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKC43LCAuOSksCiAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgIGBgYAoKIyBBcHBlbmRpeCAKCiMjIFRyb3VibGVzaG9vdGluZwoxLiBSdW5uaW5nIG91dCBvZiBzcGFjZSBvbiBFUCBUb29sa2l0PyAKWW91IGNhbiBuYXZpZ2F0ZSB0byB5b3VyIGZvbGRlciAobWF5YmUgdW5kZXIgRG9jdW1lbnRzL01BVExBQi9FUHdvcmspIGFuZCBkZWxldGUgZXZlcnl0aGluZyBleGNlcHQgZm9yIEVQcHJlZnMgdG8gcmVmcmVzaCB5b3VyIHdvcmtzcGFjZS4gCk5PVEU6IFRoaXMgd2lsbCBkZWxldGUgZXZlcnl0aGluZyBzdG9yZWQgaW4gRVAgVG9vbGtpdCwgc28gcmVtZW1iZXIgdG8gYmFjayB1cCBmaWxlcyB0aGF0IHlvdSBuZWVkIHRvIHNhdmUuCjEuIElmIHlvdSBnZXQgYW4gZXJyb3Igb3IgYSB3YXJuaW5nIGZyb20gR2l0IHdoZW4gdHJ5aW5nIHRvIGNvbW1pdCBhbmQvb3IgcHVzaCBFUlAgZmlsZXMgdXAgdG8gdGhlIHJlcG8sIHlvdSBtYXkgbmVlZCB0byBpbml0aWFsaXplIEdpdCBMRlMuClRoZSBpbnN0cnVjdGlvbnMgZm9yIGluaXRpYWxpemluZyBHaXQgTEZTIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9kZXZwc3lsYWIuZ2l0aHViLmlvL0RhdGFBbmFseXNpcy9naXQuaHRtbCNnaXRMZnMpLgogICAtIFRoZSBmaWxldHlwZXMgdGhhdCBjb21tb25seSBjYXVzZSBzdWNoIGVycm9ycyBpbiBvdXIgY3VycmVudCBFUlAgcHJvY2Vzc2luZyBwaXBlbGluZSBhcmU6CiAgICAgIC0gYC50eHRgIGZpbGVzCiAgICAgIC0gYC5lcHRgIGZpbGVzCiAgICAgIC0gYC5zZXRgIGZpbGVzCgojIyBUby1kbwoKLSBCZXR0ZXIgZGVzY3JpYmUgdGhlIG1pc3NpbmduZXNzIGZvciBmaWxlcwogICAgIC0gV2UgbmVlZCBhIHN5c3RlbWF0aWMgd2F5IHRvIGlkZW50aWZ5IG5ldyB3YXlzIHRvIHByb2Nlc3MgdGhlIG1pc3NpbmduZXNzCiAgICAgLSBGaW5kIGEgd2F5IHRvIGJlc3QgZGVzY3JpYmUgYW5kIHJlcG9ydCB0aGUgd2F5cyBvZiBtaXNzaW5nbmVzcwotIEdvIHRocm91Z2ggdGhlIG1heG1lbSBlZGl0cyBvbiB0aGUgY2xlYW5fcmF3RGF0YSBxdWVzdGlvbi4KV2Ugd2FudCBhIHN0YW5kYXJkaXplZCB2YWx1ZSBvbiB0aGUgbWFjaGluZXMKLSBMb29rIGF0IHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGZvciB0aGUgYXV0b21hdGljIHNjcmlwdCB1cGRhdGVzCiAgICAgLSBhdXRvbWF0aWMgY2xlYW5pbmcgb2YgZmlsZXMgcHJvYmxlbXMKLSBJbnRlZ3JhdGUgRVJQTEFCIHdpdGggb3VyIGV4aXN0aW5nIEVFR0xhYiBGdW5jdGlvbnMgaW5jbHVkaW5nOiAKICAgIC0gQWRkaW5nIGFuIGV2ZW50IGxpc3Q6IAogICAgICAgICAtIEN1cnJlbnRseSwgc29tZSBjb2RlIGZvciB0aGlzIGlzIHVwZGF0ZWQgaW4gdGhlIHNjcmlwdCBvbiB0aGUgbGFiIGRyaXZlCiAgICAgICAgIC0gRG9jdW1lbnRhdGlvbiBpcyBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2x1Y2tsYWIvZXJwbGFiL3dpa2kvQ3JlYXRpbmctYW4tRXZlbnRMaXN0Oi1FUlBMQUItRnVuY3Rpb25zOi1UdXRvcmlhbCkKICAgIC0gRmlndXJlIG91dCBob3cgdG8gYXZlcmFnZSBlcG9jaHMgYW5kIGV4cG9ydCB0byB0aGUgRVAgVG9vbGtpdAogLSBFdmFsdWF0ZSB0aGUgc2VtaS1hdXRvbWF0ZWQgcGlwZWxpbmVzIGZyb206CiAgICAtIFtEZWJuYXRoIGV0IGFsLiAoMjAyMCldKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Z1bGwvMTAuMTExMS9wc3lwLjEzNTgwKQogICAgLSBbRGVzamFyZGlucyBldCBhbC4gKDIwMjEpXShodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbS9zY2llbmNlL2FydGljbGUvcGlpL1MwMTY1MDI3MDIwMzAzODQ4KQogICAgLSBbRmxvIGV0IGFsLiAoMjAyMildKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzE4Nzg5MjkzMjIwMDAyMTQpCiAgICAtIFtHYWJhci1EdXJuYW0gZXQgYWwuICgyMDE4KV0oaHR0cHM6Ly93d3cuZnJvbnRpZXJzaW4ub3JnL2FydGljbGVzLzEwLjMzODkvZm5pbnMuMjAxOC4wMDA5Ny9mdWxsKQogICAgLSBbSGFyZXNpZ24gZXQgYWwuICgyMDIxKV0oaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMTg3ODkyOTMyMTAwMTE0NikKICAgIC0gW0t1bWFyYXZlbCBldCBhbC4gKDIwMjIpXShodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbS9zY2llbmNlL2FydGljbGUvcGlpL1MxODc4OTI5MzIyMDAwMTIzKQoKIyMgRUVHTGFiIFByb2Nlc3NpbmcgU3RlcHMKCmh0dHBzOi8vZWVnbGFiLm9yZy90dXRvcmlhbHMvCgoxLiBGaWx0ZXJpbmcKMS4gQXZlcmFnZSBSZWZlcmVuY2luZwoxLiBBcnRpZmFjdCBSZWplY3Rpb24KICAgIDEuIEF1dG9tYXRlZCBhcnRpZmFjdCByZWplY3Rpb24gKHNhdmUgaW50ZXJtZWRpYXRlIGZpbGUpCiAgICAxLiBNYW51YWwgc2VsZWN0aW9uIG9mIGJhZCBjaGFubmVscwogICAgMS4gTWFudWFsIHNlbGVjdGlvbiBvZiBiYWQgdGltZSBwZXJpb2RzIChzYXZlIGludGVybWVkaWF0ZSBmaWxlKQogICAgMS4gUmVtb3ZhbCBvZiBtYW51YWxseSBzZWxlY3RlZCBiYWQgY2hhbm5lbHMKICAgIDEuIFJlbW92YWwgb2YgbWFudWFsbHkgc2VsZWN0ZWQgYmFkIHRpbWUgcGVyaW9kcyAoc2F2ZSBpbnRlcm1lZGlhdGUgZmlsZSkKICAgIDEuIFtJbmRlcGVuZGVudCBDb21wb25lbnQgQW5hbHlzaXNdKCNpY2EpIChJQ0EpCiAgICAgICAgMS4gUnVuIElDQQogICAgICAgIDEuIEF1dG9tYXRlZCByZW1vdmFsIG9mIGJhZCBJQ0EgY29tcG9uZW50cwogICAgICAgIDEuIFJlLXJ1biBJQ0EgKHNhdmUgaW50ZXJtZWRpYXRlIGZpbGUpCiAgICAgICAgMS4gTWFudWFsIHNlbGVjdGlvbiBvZiBiYWQgSUNBIGNvbXBvbmVudHMgKHNhdmUgaW50ZXJtZWRpYXRlIGZpbGUpCiAgICAgICAgMS4gUmVtb3ZhbCBvZiBtYW51YWxseSBzZWxlY3RlZCBiYWQgSUNBIGNvbXBvbmVudHMKMS4gSW50ZXJwb2xhdGlvbiBvZiBCYWQgQ2hhbm5lbHMKMS4gQXZlcmFnZSBSZWZlcmVuY2luZwoxLiBTZWdtZW50YXRpb24KMS4gQmFzZWxpbmUgQ29ycmVjdGlvbiAoc2F2ZSBmaW5hbCBmaWxlKQoKIyMgRUVHTEFCCgojIyMgSW5zdGFsbCBQbHVnaW5zCgpNRkZNYXRsYWJJTyBwbHVnaW4KCiMjIyBJbXBvcnQgRGF0YQoKRmlsZSDihpIgSW1wb3J0IERhdGEg4oaSIFVzaW5nIEVFR0xBQiBmdW5jdGlvbnMgYW5kIHBsdWdpbnMg4oaSIEltcG9ydCBNYWdzdGltL0VHSSAubWZmIGZpbGUKCmBgYG1hdGxhYgpFRUcgPSBwb3BfbWZmaW1wb3J0KHsnXFxcXGxjLXJzLXN0b3JlMjQuaHBjLnVpb3dhLmVkdVxcbHNzX2l0cGV0ZXJzZW5cXExhYlxcU3R1ZGllc1xcU2Nob29sIFJlYWRpbmVzcyBTdHVkeVxcRGF0YVxcTFYyXFxFUlBcXE9kZGJhbGxcXDAtUmF3IERhdGEgKG1mZilcXDE2MTNfOTBfb2RkYmFsbC5tZmYnfSx7J2NvZGUnfSwwLDApOwoKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCAxLCdvdmVyd3JpdGUnLCdvbicsJ2d1aScsJ29mZicpOwpgYGAKClNlbGVjdCBgLm1mZmAgZmlsZShzKQoKRXZlbnQgdHlwZSBmaWVsZCAobWF5IHNlbGVjdCBtdWx0aXBsZSk6IGBjb2RlYAoKIyMjIEZpbHRlciBEYXRhCgpGaWx0ZXIg4oaSIEJhc2ljIEZJUiBGaWx0ZXIKCjAuMSAtLSAzMCBIegoKc2F2ZSBhcyBuZXcgbmFtZQoKYGBgbWF0bGFiCkVFRyA9IHBvcF9lZWdmaWx0bmV3KEVFRywgJ2xvY3V0b2ZmJywwLjEsJ2hpY3V0b2ZmJywzMCwncGxvdGZyZXF6JywxKTsKYGBgCgojIyMgQXZlcmFnZSBSZWZlcmVuY2luZwoKVG9vbHMg4oaSIFJlLXJlZmVyZW5jZSB0aGUgZGF0YSDihpIgQ29tcHV0ZSBhdmVyYWdlIHJlZmVyZW5jZQoKYGBgbWF0bGFiCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9yZXJlZiggRUVHLCBbXSk7CltBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgMSwnc2V0bmFtZScsJzE2MTNfOTBfb2RkYmFsbF9maWxfcmVmJywnb3ZlcndyaXRlJywnb24nLCdndWknLCdvZmYnKTsgCmVlZ2xhYiByZWRyYXc7CmBgYAoKIyMjIEFydGlmYWN0IFJlamVjdGlvbgoKIyMjIyBBdXRvbWF0ZWQgQXJ0aWZhY3QgUmVqZWN0aW9uCgpUb29scyDihpIgUmVqZWN0IGRhdGEgdXNpbmcgQ2xlYW4gUmF3ZGF0YSBhbmQgQVNSCgpgYGBtYXRsYWIKRUVHID0gZWVnX2NoZWNrc2V0KCBFRUcgKTsKRUVHID0gcG9wX2NsZWFuX3Jhd2RhdGEoRUVHLCAnRmxhdGxpbmVDcml0ZXJpb24nLDUsJ0NoYW5uZWxDcml0ZXJpb24nLDAuOCwnTGluZU5vaXNlQ3JpdGVyaW9uJyw0LCdIaWdocGFzcycsJ29mZicsJ0J1cnN0Q3JpdGVyaW9uJywyMCwnV2luZG93Q3JpdGVyaW9uJywwLjI1LCdCdXJzdFJlamVjdGlvbicsJ29uJywnRGlzdGFuY2UnLCdFdWNsaWRpYW4nLCdXaW5kb3dDcml0ZXJpb25Ub2xlcmFuY2VzJyxbLUluZiA3XSApOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDEsJ2d1aScsJ29mZicpOyAKZWVnbGFiIHJlZHJhdzsKYGBgCgpDdXJyZW50bHksIHdlIHJ1biBpbnRvIGEgYnVnIHRoYXQgeWllbGRzIHRoZSBmb2xsb3dpbmcgZXJyb3I6CgpgYGAKTm90IGVub3VnaCBtZW1vcnksIFRoaXMgaXMgbm90IGEgYnVnIChFcnJvciBvY2N1cnJlZCBpbiBmdW5jdGlvbiBhc3JfcHJvY2VzcygpIGF0IGxpbmUgMTMyKQpgYGAKCkFzIGEgd29ya2Fyb3VuZCwgdHlwZSB0aGUgZm9sbG93aW5nIGNvZGUgaW4gTUFUTEFCIHRvIGVkaXQgdGhlIGZ1bmN0aW9uOgoKYGBgbWF0bGFiCmVkaXQgY2xlYW5fYXJ0aWZhY3RzCmBgYAoKVGhlbiwgY2hhbmdlIHRoZSBudW1iZXIgaW4gdGhlIGZvbGxvd2luZyBsaW5lIHRvIGEgbGFyZ2VyIG51bWJlciAoZS5nLiwgMjU2KSBhbmQgY2xpY2sgc2F2ZToKCmBgYG1hdGxhYgp7J21heF9tZW0nLCdNYXhNZW0nfSwgMjU2LCAuLi4KYGBgCgojIyMjIFNlbGVjdGlvbiBvZiBCYWQgQ2hhbm5lbHMKClZpZXcgZGF0YSB0byBpZGVudGlmeSBiYWQgY2hhbm5lbHMgdG8gcmVqZWN0LgoKRWRpdCDihpIgU2VsZWN0IGRhdGEKClNwZWNpZnkgY2hhbm5lbHMgdG8gcmVqZWN0CgpTcGVjaWZ5ICJvbiAtPiByZW1vdmUgdGhlc2UiCgpgYGBtYXRsYWIKRUVHID0gZWVnX2NoZWNrc2V0KCBFRUcgKTsKRUVHID0gcG9wX3NlbGVjdCggRUVHLCAnbm9jaGFubmVsJyx7J0U0NCcsJ0U1NicsJ0U1NycsJ0UxMTMnfSk7CltBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgMSwnc2V0bmFtZScsJzE2MTNfOTBfb2RkYmFsbF9maWxfcmVmX2NobicsJ292ZXJ3cml0ZScsJ29uJywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwpgYGAKCiMjIyMgU2VsZWN0aW9uIG9mIEJhZCBUaW1lIFBlcmlvZHMKClBsb3Qg4oaSIENoYW5uZWwgZGF0YSAoc2Nyb2xsKQoKQ2hhbmdlIHZvbHRhZ2Ugc2NhbGUgdG8gNTAKClNldHRpbmdzIOKGkiBUaW1lIHJhbmdlIHRvIGRpc3BsYXkKCkNoYW5nZSB0byAxMCBzZWNvbmRzCgpTZXR0aW5ncyDihpIgTnVtYmVyIG9mIGNoYW5uZWxzIHRvIGRpc3BsYXkKCkNoYW5nZSB0byBudW1iZXIgb2YgY2hhbm5lbHMgdG8gdmlldyBhdCBvbmUgdGltZSAoZS5nLiwgNjQpCgpUbyBlcmFzZSBhIHNlbGVjdGVkIHBvcnRpb24gb2YgdGhlIGRhdGEsIGZpcnN0IGRyYWcgdGhlIG1vdXNlIChob2xkaW5nIGRvd24gdGhlIGxlZnQgbW91c2UgYnV0dG9uKSBob3Jpem9udGFsbHkgYWNyb3NzIHRoZSB0aW1lIHJlZ2lvbiBvZiBpbnRlcmVzdCB0byBtYXJrIGl0IGZvciByZWplY3Rpb24uClRvIGRlc2VsZWN0IGEgcG9ydGlvbiBvZiB0aGUgZGF0YSwgc2ltcGx5IGNsaWNrIG9uIHRoZSBzZWxlY3RlZCByZWdpb24uCgpBZnRlciBtYXJraW5nIHNvbWUgcG9ydGlvbnMgb2YgdGhlIGRhdGEgZm9yIHJlamVjdGlvbiwgcHJlc3MgUkVKRUNUIGFuZCBhIG5ldyBkYXRhIHNldCB3aWxsIGJlIGNyZWF0ZWQgd2l0aCB0aGUgcmVqZWN0ZWQgZGF0YSBvbWl0dGVkLgpBIG5ldyBkYXRhc2V0IHdpbGwgYmUgY3JlYXRlZCB3aXRoIHRoZSBtYXJrZWQgcmVnaW9ucyByZW1vdmVkLgpZb3VyIGdvYWwgaXMgdG8gcmVqZWN0IG5vbi1zdGVyZW90eXBpYyBhcnRpZmFjdHMuCkRvIG5vdCByZWplY3QgYmxpbmtzL3NhY2NhZGVzLCBiZWNhdXNlIFtpbmRlcGVuZGVudCBjb21wb25lbnQgYW5hbHlzaXNdKCNpY2EpIHdpbGwgcmVtb3ZlIHRob3NlLgoKQ2xpY2tpbmcgIlN0YWNrIiBzdGFja3MgYWxsIGVsZWN0cm9kZXMgb24gdG9wIG9mIGVhY2ggb3RoZXIgdG8gbW9yZSBlYXNpbHkgaWRlbnRpZnkgbm9pc3kgZGF0YS4KCkNsaWNrICJSRUpFQ1QiIHRvIHJlbW92ZSB0aGUgYmFkIHRpbWUgcGVyaW9kcyBmcm9tIHRoZSBkYXRhIGZpbGUuCgojIyMjIEluZGVwZW5kZW50IENvbXBvbmVudCBBbmFseXNpcyB7I2ljYX0KCmh0dHBzOi8vZWVnbGFiLm9yZy90dXRvcmlhbHMvMDZfUmVqZWN0QXJ0aWZhY3RzL1J1bklDQS5odG1sIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL0FFVTktR0IzQikKCmh0dHBzOi8vc29jaWFsc2NpLmxpYnJldGV4dHMub3JnL0Jvb2tzaGVsdmVzL1BzeWNob2xvZ3kvQm9vayUzQV9BcHBsaWVkX0V2ZW50LVJlbGF0ZWRfUG90ZW50aWFsX0RhdGFfQW5hbHlzaXNfKEx1Y2spLzE0JTNBX0FwcGVuZGl4XzMlM0FfRXhhbXBsZV9Qcm9jZXNzaW5nX1BpcGVsaW5lIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjLzlRWVEtQk5GRSkKClRoZSBjb21wb25lbnQgb3JkZXIgcmV0dXJuZWQgYnkgYHJ1bmljYS5tYCBpcyBpbiBkZWNyZWFzaW5nIG9yZGVyIG9mIHRoZSBFRUcgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBieSBlYWNoIGNvbXBvbmVudC4KClRvb2xzIOKGkiBEZWNvbXBvc2UgZGF0YSBieSBJQ0EKCmBgYG1hdGxhYgpFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfcnVuaWNhKEVFRywgJ2ljYXR5cGUnLCAncnVuaWNhJywgJ2V4dGVuZGVkJywxLCdpbnRlcnJ1cHQnLCdvbicpOwpbQUxMRUVHIEVFR10gPSBlZWdfc3RvcmUoQUxMRUVHLCBFRUcsIENVUlJFTlRTRVQpOwplZWdsYWIgcmVkcmF3OwpgYGAKClBsb3Qg4oaSIENvbXBvbmVudCBtYXBzIOKGkiAyRAoKVmlzdWFsbHkgaWRlbnRpZnkgaW5kZXBlbmRlbnQgY29tcG9uZW50cyB0byByZW1vdmUKClRvb2xzIOKGkiBJbnNwZWN0L2xhYmVsIGNvbXBvbmVudHMgYnkgbWFwCgpFeGFtcGxlIElDQSBBcnRpZmFjdCBDb21wb25lbnRzCgohW0lDQSBFeWUgQmxpbmsgQXJ0aWZhY3RdKGltYWdlcy9JQ0FfRXllQmxpbmsucG5nKQohW0lDQSBMYXRlcmFsIEV5ZSBNb3ZlbWVudCBBcnRpZmFjdF0oaW1hZ2VzL0lDQV9MYXRlcmFsRXllTW92ZW1lbnQucG5nKQohW0lDQSBNdXNjbGUgQXJ0aWZhY3RdKGltYWdlcy9JQ0FfTXVzY2xlQXJ0aWZhY3QucG5nKQohW0lDQSBDYXJkaWFjIEFydGlmYWN0XShpbWFnZXMvSUNBX0NhcmRpYWNBcnRpZmFjdC5wbmcpCiFbSUNBIEJhZCBDaGFubmVsXShpbWFnZXMvSUNBX0JhZENoYW5uZWwucG5nKQoKRXhhbXBsZSBJQ0EgQnJhaW4gQ29tcG9uZW50cwohW0lDQSBCcmFpbiBBY3Rpdml0eSBFeGFtcGxlIDFdKGltYWdlcy9JQ0FfQnJhaW5BY3Rpdml0eUV4MS5wbmcpCiFbSUNBIEJyYWluIEFjdGl2aXR5IEV4YW1wbGUgMl0oaW1hZ2VzL0lDQV9CcmFpbkFjdGl2aXR5RXgyLnBuZykKIVtJQ0EgQnJhaW4gQWN0aXZpdHkgRXhhbXBsZSAzXShpbWFnZXMvSUNBX0JyYWluQWN0aXZpdHlFeDMucG5nKQoKT3ZlcnZpZXcgb2YgSUNBIENvbXBvbmVudHMKIVtJQ0EgT3ZlcnZpZXddKGltYWdlcy9JQ0FfT3ZlcnZpZXcucG5nKQoKVG9nZ2xlIHRoZSAiQWNjZXB0IiBidXR0b24gdG8gcmVqZWN0IGFuIGluZGVwZW5kZW50IGNvbXBvbmVudCwgcHJlc3MgIk9LIiB0byBzcGVjaWZ5IGl0IGZvciByZWplY3Rpb24KCkF1dG9tYXRlZCBkZXRlY3Rpb24gb2YgYXJ0aWZhY3R1YWwgSUNBIGNvbXBvbmVudHM6CgpodHRwczovL2VlZ2xhYi5vcmcvdHV0b3JpYWxzLzA2X1JlamVjdEFydGlmYWN0cy9SdW5JQ0EuaHRtbCNhdXRvbWF0ZWQtZGV0ZWN0aW9uLW9mLWFydGlmYWN0dWFsLWljYS1jb21wb25lbnRzIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjLzVSUTctOVdCVCkKCmBgYG1hdGxhYgpFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfaWNsYWJlbChFRUcsICdkZWZhdWx0Jyk7CltBTExFRUcgRUVHXSA9IGVlZ19zdG9yZShBTExFRUcsIEVFRywgQ1VSUkVOVFNFVCk7CkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9pY2ZsYWcoRUVHLCBbTmFOIE5hTjswLjkgMTswLjkgMTtOYU4gTmFOO05hTiBOYU47TmFOIE5hTjtOYU4gTmFOXSk7CltBTExFRUcgRUVHXSA9IGVlZ19zdG9yZShBTExFRUcsIEVFRywgQ1VSUkVOVFNFVCk7CmVlZ2xhYiByZWRyYXc7CmBgYAoKVGhlcmUgYXJlIHNpeCBjYXRlZ29yaWVzIG9mIGNvbXBvbmVudHM6IEJyYWluLCBNdXNjbGUsIEV5ZSwgSGVhcnQsIExpbmUgTm9pc2UsIENoYW5uZWwgTm9pc2UsIGFuZCBPdGhlcgoKT3VyIGdvYWwgaXMgdG8ga2VlcCB0aGUgYnJhaW4gY29tcG9uZW50cyBhbmQgdG8gcmVtb3ZlIGV2ZXJ5dGhpbmcgZWxzZSAoaS5lLiwgYXJ0aWZhY3RzKS4KClRvb2xzIOKGkiBDbGFzc2lmeSBjb21wb25lbnRzIHVzaW5nIElDTGFiZWwg4oaSIExhYmVsIGNvbXBvbmVudHMKClRvb2xzIOKGkiBDbGFzc2lmeSBjb21wb25lbnRzIHVzaW5nIElDTGFiZWwg4oaSIEZsYWcgY29tcG9uZW50cyBhcyBhcnRpZmFjdHMKClN1YnN0cmFjdGluZyByZWplY3RlZCBJQ0EgY29tcG9uZW50czoKCmh0dHBzOi8vZWVnbGFiLm9yZy90dXRvcmlhbHMvMDZfUmVqZWN0QXJ0aWZhY3RzL1J1bklDQS5odG1sI3N1YnRyYWN0aW5nLWljYS1jb21wb25lbnRzLWZyb20tZGF0YSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9IVkg0LVo0U0EpCgpUb29scyDihpIgUmVtb3ZlIGNvbXBvbmVudHMKCmBgYG1hdGxhYgpFRUcgPSBwb3Bfc3ViY29tcCggRUVHLCBbMSAgICAyICAgIDYgICAgNyAgIDEwICAgMTMgICAyMSAgIDI0ICAgMjYgICAzMSAgIDMyICAgMzMgICAzNiAgIDQzICAgNDQgICA1MSAgIDU0ICAgNTUgICA1OSAgIDYxICAgNjcgICA2OCAgIDc0ICAgODMgICA5MCAgIDkxICAgOTMgICA5OSAgMTAzICAxMTIgIDExMyAgMTE2ICAxMTggIDEyMV0sIDApOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDYsJ2d1aScsJ29mZicpOyAKZWVnbGFiIHJlZHJhdzsKYGBgCgojIyMgSW50ZXJwb2xhdGUgQmFkIENoYW5uZWxzCgpodHRwczovL3NjY24udWNzZC5lZHUvcGlwZXJtYWlsL2VlZ2xhYmxpc3QvMjAxNi8wMTExOTkuaHRtbCAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy85N05ILThMQVIpCgpUbyBpbnRlcnBvbGF0ZSBjaGFubmVscyB5b3Ugd291bGQgbG9hZCB1cCBvbmUgZmlsZSB0aGF0IGhhcyBvbmx5IHRoZSBnb29kIGNoYW5uZWxzLCB0aGVuIGxvYWQgdXAgYSBzZWNvbmQgZmlsZSB0aGF0IGhhcyB0aGUgZnVsbCBjaGFubmVsIGxpc3QsIGFuZCB0aGVuIHJ1biB0aGUgY2hhbm5lbCBpbnRlcnBvbGF0aW9uIGZ1bmN0aW9uIGZyb20gdGhlIGVlZ2xhYiBndWkuCgpUb29scyDihpIgSW50ZXJwb2xhdGUgRWxlY3Ryb2RlcyDihpIgVXNlIGFsbCBjaGFubmVscyAob3Igc3BlY2lmaWMgY2hhbm5lbHM/KSBvZiBvdGhlciBkYXRhc2V0CgpVc2luZyBhbGwgY2hhbm5lbHMgb2Ygb3RoZXIgZGF0YXNldDoKCioqSW1wb3J0YW50IE5vdGU6KiogSW50ZXJwb2xhdGluZyBmaWxlcyB3aWxsIHJlLXJlZmVyZW5jZSB0aGUgZGF0YS4KQXZlcmFnZSByZWZlcmVuY2UgdGhlIGRhdGEgYWZ0ZXIgaW50ZXJwb2xhdGluZyBjaGFubmVscy4KCmBgYG1hdGxhYgpFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfaW50ZXJwKEVFRywgQUxMRUVHKDMpLmNoYW5sb2NzLCAnc3BoZXJpY2FsJyk7CltBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgNCwnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwpgYGAKClVzaW5nIHNwZWNpZmljIGNoYW5uZWxzIG9mIG90aGVyIGRhdGFzZXQ6CgpgYGBtYXRsYWIKRUVHID0gZWVnX2NoZWNrc2V0KCBFRUcgKTsKRUVHID0gcG9wX2ludGVycChFRUcsIEFMTEVFRygzKS5jaGFubG9jcyhbNDQgICA1NiAgIDU3ICAxMTNdKSwgJ3NwaGVyaWNhbCcpOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDEsJ2d1aScsJ29mZicpOyAKZWVnbGFiIHJlZHJhdzsKYGBgCgpSZW1vdmVkIGNoYW5uZWxzOgoKYGBgbWF0bGFiCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9pbnRlcnAoRUVHLCBFRUcuY2hhbmluZm8ubm9kYXRjaGFucyhbNDQgIDU2ICA1NyAgMTEzXSksICdzcGhlcmljYWwnKTsKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCAzLCdndWknLCdvZmYnKTsgCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CmVlZ2xhYiByZWRyYXc7CmBgYAoKRGF0YSBjaGFubmVsczoKCmBgYG1hdGxhYgpFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfaW50ZXJwKEVFRywgWzQ0ICA1NiAgNTcgIDExM10sICdzcGhlcmljYWwnKTsKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCAxLCdndWknLCdvZmYnKTsgCmVlZ2xhYiByZWRyYXc7CmBgYAoKIyMjIEF2ZXJhZ2UgUmVmZXJlbmNpbmcKClRvb2xzIOKGkiBSZS1yZWZlcmVuY2UgdGhlIGRhdGEg4oaSIENvbXB1dGUgYXZlcmFnZSByZWZlcmVuY2UKCmBgYG1hdGxhYgpFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfcmVyZWYoIEVFRywgW10pOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDEsJ3NldG5hbWUnLCcxNjEzXzkwX29kZGJhbGxfZmlsX3JlZicsJ292ZXJ3cml0ZScsJ29uJywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwpgYGAKCiMjIyBTZWdtZW50YXRpb24KClRvb2xzIOKGkiBFeHRyYWN0IEVwb2NocwoKYGBgbWF0bGFiCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9lcG9jaCggRUVHLCB7ICAnZnJxKycgICd0Z3QrJyAgfSwgWy0wLjIgICAgICAgICAgIDFdLCAnbmV3bmFtZScsICcxNjEzXzkwX29kZGJhbGxfZmlsX3JlZiBlcG9jaHMnLCAnZXBvY2hpbmZvJywgJ3llcycpOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDEsJ292ZXJ3cml0ZScsJ29uJywnZ3VpJywnb2ZmJyk7IApgYGAKCiMjIyBCYXNlbGluZSBDb3JyZWN0aW9uCgpgYGBtYXRsYWIKRUVHID0gZWVnX2NoZWNrc2V0KCBFRUcgKTsKRUVHID0gcG9wX3JtYmFzZSggRUVHLCBbLTIwMCAwXSAsW10pOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDEsJ292ZXJ3cml0ZScsJ29uJywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwpgYGAKCiMjIEF1dG9tYXRpYyBTY3JpcHQgZXhhbXBsZSAKCmBgYG1hdGxhYgolIFN0YXJ0aW5nIEVFRyBMYWIgCltBTExFRUcgRUVHIENVUlJFTlRTRVQgQUxMQ09NXSA9IGVlZ2xhYjsKCiUlIEhlbHBmdWwgZG9jdW1lbnRhdGlvbiBpcyBsb2NhdGVkIGhlcmUKJSBodHRwczovL2VlZ2xhYi5vcmcvdHV0b3JpYWxzLzExX1NjcmlwdGluZy9Vc2luZ19FRUdMQUJfaGlzdG9yeS5odG1sIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL1k2ODctNUdLRSkKJSBodHRwczovL2VlZ2xhYi5vcmcvdHV0b3JpYWxzL0NvbmNlcHRzR3VpZGUvRGF0YV9TdHJ1Y3R1cmVzLmh0bWwgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvNUYzOS01UzMyKQoKJUxvYWRpbmcgaW4gdGhlIERhdGFzZXQKRUVHID0gcG9wX21mZmltcG9ydCh7J1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFwwLVJhdyBEYXRhIChtZmYpXFwxNjEzXzkwX29kZGJhbGwubWZmJ30seydjb2RlJ30sMCwwKTsKRUVHLnNldG5hbWU9J3Jhdyc7CkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CiVTdG9yaW5nIHRoZSBjdXJyZW50IGRhdGFzZXQKW0FMTEVFRywgRUVHXSA9IGVlZ19zdG9yZSggQUxMRUVHLCBFRUcsIDEpOwolIHJlZnJlc2hpbmcgdGhlIGdyYXBoaWNhbCBpbnRlcmZhY2UKZWVnbGFiIHJlZHJhdzsKCiVGaWx0ZXIgdGhlIGRhdGEKRUVHID0gcG9wX2VlZ2ZpbHRuZXcoQUxMRUVHKDEpLCAnbG9jdXRvZmYnLDAuMSwnaGljdXRvZmYnLDMwLCdwbG90ZnJlcXonLDEpOwolU2F2ZSB0aGUgRmlsdGVyZWQgZGF0YXNldCAKJUFMTEVFRyBFRUcgQ1VSUkVOVFNFVCBzZWVtcyB0byBqdXN0IGJlIGEgbmFtZSBmb3IgYWxsIG9mIHRoZSBjdXJyZW50IGRhdGEKJXNldHMgCiVwb3BfbmV3c2V0IHNlZW1zIHRvIHNhdmUgdGhlIGRhdGFzZXQgaW4gYm90aCBtZW1vcnkgYW5kIGluIHRoZSB0b29sa2l0CltBTExFRUcsIEVFRywgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCAyLCdzZXRuYW1lJywnZmlsJywnc2F2ZW5ldycsJ1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFxNQVRMQUJcXDEtRmlsdGVyaW5nIChmaWwpXFx0Y2lkX3dhdmUuc2V0JywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwoKJSBBdmVyYWdlIHJlZmVyZW5jaW5nCiVFRUcgPSBlZWdfY2hlY2tzZXQoIEVFRyApOwpFRUcgPSBwb3BfcmVyZWYoIEFMTEVFRygyKSwgW10pOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDMsJ3NldG5hbWUnLCdBdmcgUmVmJywnc2F2ZW5ldycsJ1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFxNQVRMQUJcXDktQXZlcmFnZSBSZWZlcmVuY2UgKHJlZilcXHRjaWRfd2F2ZV9hdmcuc2V0JywnZ3VpJywnb2ZmJyk7CmVlZ2xhYiByZWRyYXc7CgolIFJlamVjdGluZyB0aGUgYXJ0aWZhY3RzCiUgdGVzdGluZyBidW1waW5nIHRoZSAKRUVHID0gcG9wX2NsZWFuX3Jhd2RhdGEoQUxMRUVHKDMpLCAnRmxhdGxpbmVDcml0ZXJpb24nLDUsJ0NoYW5uZWxDcml0ZXJpb24nLDAuOCwnTGluZU5vaXNlQ3JpdGVyaW9uJyw0LCdIaWdocGFzcycsJ29mZicsJ0J1cnN0Q3JpdGVyaW9uJywzMCwnV2luZG93Q3JpdGVyaW9uJywwLjI1LCdCdXJzdFJlamVjdGlvbicsJ29uJywnRGlzdGFuY2UnLCdFdWNsaWRpYW4nLCdXaW5kb3dDcml0ZXJpb25Ub2xlcmFuY2VzJyxbLUluZiA3XSApOwolU2F2aW5nIGNsZWFuZWQgZGF0YXNldApbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDQsJ3NldG5hbWUnLCdjbGVhbiBkYXRhJywnc2F2ZW5ldycsJ1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFxNQVRMQUJcXDEwLUNsZWFuIEFydGlmaWNhdHMgKGNsZWFuKVxcdGNpZF93YXZlX2NsZWFuLnNldCcsJ2d1aScsJ29mZicpOyAKZWVnbGFiIHJlZHJhdzsKCiUgUGxhY2Vob2xkZXIgdG8gbWFudWFsbHkgcmVtb3ZlIGJhZCBjaGFubmVscwoKCiUgSUNBIGNvbXBvbmVudHMKRUVHID0gcG9wX3J1bmljYShBTExFRUcoNCksICdpY2F0eXBlJywgJ3J1bmljYScsICdleHRlbmRlZCcsIDEsJ2ludGVycnVwdCcsJ29uJyk7CltBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgNSwnc2V0bmFtZScsJ0lDQSB0ZXN0Jywnc2F2ZW5ldycsJ1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFxNQVRMQUJcXDExLUlDQVxcdGNpZF93YXZlX0lDQS5zZXQnLCdndWknLCdvZmYnKTsgCmVlZ2xhYiByZWRyYXc7CgolTWFudWFsbHkgcmVqZWN0IElDQSBjb21wb25lbnRzCkVFRyA9IHBvcF9zdWJjb21wKCBFRUcsIFsxICAgIDIgICAgNiAgICA3ICAgMTAgICAxMyAgIDIxICAgMjQgICAyNiAgIDMxICAgMzIgICAzMyAgIDM2ICAgNDMgICA0NCAgIDUxICAgNTQgICA1NSAgIDU5ICAgNjEgICA2NyAgIDY4ICAgNzQgICA4MyAgIDkwICAgOTEgICA5MyAgIDk5ICAxMDMgIDExMiAgMTEzICAxMTYgIDExOCAgMTIxXSwgMCk7CltBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgNiwnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwoKJSBQbGFjZSBob2xkZXIgdG8gcmVtaW5kIHRvIG1hbnVhbGx5IHJlbW92ZSB0aGUgY2hhbm5lbHMgdGhhdCB3ZSB3b3VsZAolIGxpa2UgdG8gcmVqZWN0CiUgdGFraW5nIHRoZSBFRUcgY2hhbm5lbCBsb2NrIG9mIHRoZSBmaXJzdCAocmF3KSBkYXRhc2V0CkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9pbnRlcnAoQUxMRUVHKDUpLCBBTExFRUcoMSkuY2hhbmxvY3MsICdzcGhlcmljYWwnKTsKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCA2LCdzZXRuYW1lJywnSW50ZXJwb2xhdGVkJywnc2F2ZW5ldycsJ1I6XFxMYWJcXFN0dWRpZXNcXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcXERhdGFcXExWMlxcRVJQXFxPZGRiYWxsXFxNQVRMQUJcXDEyLUludGVycG9sYXRlXFx0Y2lkX3dhdmVfSW50ZXJwb2xhdGUuc2V0JywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwoKJSBTZWdtZW50aW5nIHRoZSBEYXRhCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9lcG9jaCggQUxMRUVHKDYpLCB7ICAnZnJxKycgICd0Z3QrJyAgfSwgWy0wLjIgMV0sICduZXduYW1lJywgJ3RjaWRfd2F2ZV9zZWdtZW50ZWQnLCAnZXBvY2hpbmZvJywgJ3llcycpOwpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIDcsJ3NldG5hbWUnLCdTZWdtZW50ZWQnLCdzYXZlbmV3JywnUjpcXExhYlxcU3R1ZGllc1xcU2Nob29sIFJlYWRpbmVzcyBTdHVkeVxcRGF0YVxcTFYyXFxFUlBcXE9kZGJhbGxcXE1BVExBQlxcMTMtU2VnbWVudFxcdGNpZF93YXZlX1NlZ21lbnQuc2V0JywnZ3VpJywnb2ZmJyk7IAplZWdsYWIgcmVkcmF3OwoKJSBCYXNlbGluZSBDb3JyZWN0aW5nIHRoZSBEYXRhCkVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CkVFRyA9IHBvcF9ybWJhc2UoIEFMTEVFRyg3KSwgWy0yMDAgMF0gLFtdKTsKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCA4LCdzZXRuYW1lJywnQmFzZWxpbmUgQ29ycmVjdCcsJ3NhdmVuZXcnLCdSOlxcTGFiXFxTdHVkaWVzXFxTY2hvb2wgUmVhZGluZXNzIFN0dWR5XFxEYXRhXFxMVjJcXEVSUFxcT2RkYmFsbFxcTUFUTEFCXFwxNC1CYXNlbGluZS1Db3JyZWN0XFx0Y2lkX3dhdmVfYmFzZWxpbmUtY29ycmVjdC5zZXQnLCdndWknLCdvZmYnKTsgCmVlZ2xhYiByZWRyYXc7CgpgYGAKCiMjIEF1dG9tYXRpYyBzY3JpcHQgdGhhdCBsb29wcyBmaWxlcwoKIyMjIFdhcm5pbmcgbWVzc2FnZXMgd2l0aCBzY3JpcHQKCldhcm5pbmcgbWVzc2FnZXMgYXBwZWFyZWQgd2hlbiB1c2luZyB0aGUgYXV0b21hdGljIGNsZWFuaW5nIG9mIGRhdGEuCldlIG1heSBoYXZlIHRvIHNlbmQgYSBkYXRhc2V0IHRvIHNvbWVvbmUgc28gdGhleSBjYW4gY2hlY2sgb24gaXQuClNvbWUgZG9jdW1lbnRhdGlvbiBpcyBmb3VuZCBoZXJlOgoKLSBodHRwczovL3NjY24udWNzZC5lZHUvcGlwZXJtYWlsL2VlZ2xhYmxpc3QvMjAyMS8wMTYyMjIuaHRtbCAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy85U0RHLU5HWEQpCi0gaHR0cHM6Ly9zY2NuLnVjc2QuZWR1L3dpa2kvRUVHTEFCX2J1ZzE5NzEgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvSDdQQS1UUFRaKQoKIyMjIEZpbHRlcmluZywgYXZlcmFnZSByZWZlcmVuY2luZywgYW5kIGF1dG9tYXRpY2FsbHkgY2xlYW5pbmcgdGhlIGRhdGEuCgpUaGlzIHNjcmlwdCBiYXRjaCByZWFkcyBpbiBmaWxlcywgZmlsdGVycyB0aGVtLCBhdmVyYWdlIHJlZmVyZW5jZXMsIGFuZCBhdXRvbWF0aWNhbGx5IGNsZWFucyB0aGVtLgpBZnRlciB0aGF0LCB0aGlzIHNjcmlwdCByZWFkcyBpbiB0aGUgY2xlYW5lZCBmaWxlcyBmb3IgbWFudWFsbHkgcHJvY2Vzc2luZyB0byByZW1vdmUgYmFkIHRpbWUgcGVyaW9kcyBhbmQgYmFkIGNoYW5uZWxzCgpgYGBtYXRsYWIKJTEwLzIwLzIyIFdvcmtpbmcgc2NyaXB0IHRoYXQgcmVhZHMgZXZlcnl0aGluZyBpbnRvIG1hdGxhYgoKCiUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUKJUxvY2F0aW9uIG9mIFJhdyBvZGRiYWxsIGZpbGVzCnJhd09kZGJhbGxGaWxlcyA9ICdcXGxjLXJzLXN0b3JlMjQuaHBjLnVpb3dhLmVkdVxsc3NfaXRwZXRlcnNlblxMYWJcU3R1ZGllc1xTY2hvb2wgUmVhZGluZXNzIFN0dWR5XERhdGFcTFYyXEVSUFxPZGRiYWxsXE1BVExBQlwwLVJhd1wnOwolTG9jYXRpb24gb2YgcGF0aCB0byBzYXZlIGNsZWFuZWQgZmlsZXMKY2xlYW5BdXRvUGF0aCA9ICdcXGxjLXJzLXN0b3JlMjQuaHBjLnVpb3dhLmVkdVxsc3NfaXRwZXRlcnNlblxMYWJcU3R1ZGllc1xTY2hvb2wgUmVhZGluZXNzIFN0dWR5XERhdGFcTFYyXEVSUFxPZGRiYWxsXE1BVExBQlwxMC1DbGVhbiBBcnRpZmljYXRzIChjbGVhbilcJzsKCiVhbGwgb2RkYmFsbCBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5IHRvIGJlIHByb2Nlc3NlZApmaWxlc0luRGlyZWN0b3J5ID0gZGlyKGZ1bGxmaWxlKHJhd09kZGJhbGxGaWxlcywgJyoubWZmJykpOyAlUmVhZGluZyB0aGUgZmlsZXMgaW4gdGhlIGRpcmVjdG9yeQoKJUxpc3RpbmcgdGhlIG51bWJlciBvZiBzdWJqZWN0cyBmb3IgdGhlIG51bWJlciBvZiB0aW1lcyB0byBsb29wCm51bWJlck9mU3ViamVjdCA9IGhlaWdodChmaWxlc0luRGlyZWN0b3J5KTsKCiVOYW1lcyBvZiBhbGwgdGhlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkKbGlzdE9mU3ViamVjdHM9e2ZpbGVzSW5EaXJlY3RvcnkubmFtZX07IAoKJXNwbGl0dGluZyB0aGUgZmlsZW5hbWUgdXAKZmlsZW5hbWVTcGxpdCA9IHJlZ2V4cChsaXN0T2ZTdWJqZWN0cywgJ18nLCAnc3BsaXQnKTsgIAppZCA9IGNlbGxmdW4oQCh4KSB4ezF9LGZpbGVuYW1lU3BsaXQsJ3VuJywwKTsgICVnZXR0aW5nIHRoZSBpZCdzCndhdmUgPSBjZWxsZnVuKEAoeCkgeHsyfSxmaWxlbmFtZVNwbGl0LCd1bicsMCk7ICVnZXR0aW5nIHRoZSB3YXZlcwoKCgpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUIEFMTENPTV0gPSBlZWdsYWI7Cgpmb3IgaT0xOm51bWJlck9mU3ViamVjdAoKICAgICUlJSUgSW1wb3J0aW5nIERhdGEKICAgIHBhdGggPSBbcmF3T2RkYmFsbEZpbGVzLGZpbGVzSW5EaXJlY3RvcnkoaSkubmFtZV07CiAgICB0Y2lkID0gY2hhcihzdHJjYXQoaWQoaSksJ18nLCB3YXZlKGkpKSk7ICVjb21iaW5pbmcgdGhlIFRDSUQgYW5kIHdhdmUKCiAgICBFRUcgPSBwb3BfbWZmaW1wb3J0KHtwYXRofSx7J2NvZGUnfSwwLDApOwogICAgJSBTYXZpbmcgdGhlIGRhdGEgaW4gbWVtb3J5CiAgIFtBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgaSwnc2V0bmFtZScsIHRjaWQsJ2d1aScsJ29mZicpOwoKICAgICUgRmlsdGVyaW5nIHRoZSBkYXRhCiAgICBFRUcgPSBwb3BfZWVnZmlsdG5ldyhBTExFRUcoaSksICdsb2N1dG9mZicsMC4xLCdoaWN1dG9mZicsMzAsJ3Bsb3RmcmVxeicsMSk7CiAgICBbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIGksJ292ZXJ3cml0ZScsJ29uJywnZ3VpJywnb2ZmJyk7CiAgICAKICAgICUgQXZlcmFnZSBSZWZlcmVuY2luZwogICAgRUVHID0gcG9wX3JlcmVmKCBBTExFRUcoaSksIFtdKTsKICAgIFtBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgaSwnb3ZlcndyaXRlJywnb24nLCdndWknLCdvZmYnKTsKCiAgICAlIFRoZXkgc3Ryb25nbHkgYWR2aXNlIGltcG9ydGluZyBjaGFubmVsIGxvY2F0aW9ucyBiZWZvcmUgdXNpbmcgdGhpcwogICAgJSBmdW5jdGlvbi4KICAgICVDbGVhbmluZyB0aGUgRGF0YQogICAgRUVHID0gcG9wX2NsZWFuX3Jhd2RhdGEoQUxMRUVHKGkpLCAnRmxhdGxpbmVDcml0ZXJpb24nLDUsJ0NoYW5uZWxDcml0ZXJpb24nLDAuOCwnTGluZU5vaXNlQ3JpdGVyaW9uJyw0LCdIaWdocGFzcycsJ29mZicsJ0J1cnN0Q3JpdGVyaW9uJywzMCwnV2luZG93Q3JpdGVyaW9uJywwLjI1LCdCdXJzdFJlamVjdGlvbicsJ29uJywnRGlzdGFuY2UnLCdFdWNsaWRpYW4nLCdXaW5kb3dDcml0ZXJpb25Ub2xlcmFuY2VzJyxbLUluZiA3XSk7CiAgICBuYW1lQ2xlYW4gPSBbdGNpZCwnX2F1dG9DbGVhbi5zZXQnXTsKICAgIHNhdmVQYXRoQ2xlYW4gPSBbY2xlYW5BdXRvUGF0aCxuYW1lQ2xlYW5dOwogICAgW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVF0gPSBwb3BfbmV3c2V0KEFMTEVFRywgRUVHLCBpLCdzZXRuYW1lJywgbmFtZUNsZWFuLCAnc2F2ZW5ldycsIHNhdmVQYXRoQ2xlYW4sICdndWknLCdvZmYnLCAnb3ZlcndyaXRlJywgJ29uJyk7CgoKZW5kCmVlZ2xhYiByZWRyYXc6CgolJSBSZWFkaW5nIGluIGF1dG9tYXRpY2FsbHkgY2xlYW5lZCBkYXRhc2V0cwoKJSUlJSUlJSUlJSUlJSUKJSBDbGVhciBEYXRhIGJlY2FzdWUgdG8gb3ZlcndyaXRlIGFueXRoaW5nIGluIG1lbW9yeQolJSUlJSUlJSUlJSUlJQpBTExFRUcgPSBbXTsKRUVHID0gW107CgolIEFkZGluZyB0aGUgcGF0aCBmb3IgY2xlYW5lZCBmaWxlcwpjbGVhbkF1dG9QYXRoID0gJ1xcbGMtcnMtc3RvcmUyNC5ocGMudWlvd2EuZWR1XGxzc19pdHBldGVyc2VuXExhYlxTdHVkaWVzXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcRGF0YVxMVjJcRVJQXE9kZGJhbGxcTUFUTEFCXDEwLUNsZWFuIEFydGlmaWNhdHMgKGNsZWFuKVwnOwoKJVN0YXJ0aW5nIEVFR0xBQgpbQUxMRUVHIEVFRyBDVVJSRU5UU0VUIEFMTENPTV0gPSBlZWdsYWI7CgoKJUxvY2F0aW9uIG9mIGNsZWFuZWQgb2RkYmFsbCBkYXRhCmZpbGVzSW5EaXJlY3RvcnlDbGVhbiA9IGRpcihmdWxsZmlsZShjbGVhbkF1dG9QYXRoLCAnKi5zZXQnKSk7ICVSZWFkaW5nIHRoZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5CgoKJUxpc3RpbmcgdGhlIG51bWJlciBvZiBzdWJqZWN0cyBmb3IgdGhlIG51bWJlciBvZiB0aW1lcyB0byBsb29wCm51bWJlck9mU3ViamVjdENsZWFuID0gaGVpZ2h0KGZpbGVzSW5EaXJlY3RvcnlDbGVhbik7CgolTmFtZXMgb2YgYWxsIHRoZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5Cmxpc3RPZlN1YmplY3RzQ2xlYW4gPSB7ZmlsZXNJbkRpcmVjdG9yeUNsZWFuLm5hbWV9OyAKCiVzcGxpdHRpbmcgdGhlIGZpbGVuYW1lIHVwCmZpbGVuYW1lU3BsaXRDbGVhbiA9IHJlZ2V4cChsaXN0T2ZTdWJqZWN0c0NsZWFuLCAnXycsICdzcGxpdCcpOyAgCmlkID0gY2VsbGZ1bihAKHgpIHh7MX0sZmlsZW5hbWVTcGxpdENsZWFuLCd1bicsMCk7ICAlZ2V0dGluZyB0aGUgaWQncwp3YXZlID0gY2VsbGZ1bihAKHgpIHh7Mn0sZmlsZW5hbWVTcGxpdENsZWFuLCd1bicsMCk7ICVnZXR0aW5nIHRoZSB3YXZlcwoKCmZvciBpPTE6bnVtYmVyT2ZTdWJqZWN0Q2xlYW4KCiAgICAlJSUlIEltcG9ydGluZyBEYXRhCiAgICB0Y2lkQ2xlYW4gPSBjaGFyKHN0cmNhdChpZChpKSwnXycsIHdhdmUoaSksICdfYXV0b0NsZWFuLnNldCcpKTsgJWNvbWJpbmluZyB0aGUgVENJRCBhbmQgd2F2ZSB0byBuYW1lIHRoZSBmaWxlCiAgICBFRUcgPSBwb3BfbG9hZHNldCgnZmlsZW5hbWUnLCB0Y2lkQ2xlYW4sICdmaWxlcGF0aCcsIGNsZWFuQXV0b1BhdGgpOyAKICAgIFtBTExFRUcgRUVHIENVUlJFTlRTRVRdID0gcG9wX25ld3NldChBTExFRUcsIEVFRywgaSwnc2V0bmFtZScsIHRjaWRDbGVhbiwnZ3VpJywnb2ZmJyk7CgplbmQKZWVnbGFiIHJlZHJhdzsKYGBgCgojIyMgUmVtb3ZpbmcgYmFkIHRpbWUgcGVyaW9kcyBhbmQgY2hhbm5lbHMKCjEuIEluIHRoZSBub3cgb3BlbiBFRUdMQUIgaW50ZXJmYWNlLCBzZWxlY3QgYSBkYXRhc2V0LgpHbyB0byBgVG9vbHNgID4gYEluc3BlY3QvcmVqZWN0IGRhdGEgYnkgZXllYCAKCiFbdmlzdWFsbHkgcmVqZWN0IGFydGlmYWN0c10oaW1hZ2VzL3Zpc3VhbF9yZWplY3RfYXJ0aWZhY3RzLnBuZykKCjEuIEdvIHRocm91Z2ggdGhlIHJhdyBzaWduYWwgYW5kIG1hbnVhbGx5IHJlamVjdCBiYWQgdGltZSBwZXJpb2RzCgohW3Zpc3VhbGx5IHJlamVjdCBhcnRpZmFjdHNdKGltYWdlcy9yZWplY3RfdGltZV9wZXJpb2RzLnBuZykKCjEuIFNlbGVjdCBgUkVKRUNUYCBhbmQgb3ZlcndyaXRlIHRoZSBmaWxlIGluIG1lbW9yeQogICAtIHNlbGVjdCBgT3ZlcndyaXRlIGl0IGluIG1lbW9yeSAoc2V0PXllczsgdW5zZXQ9Y3JlYXRlIGEgbmV3IGRhdGFzZXQpYAoxLiBWaXN1YWxseSBpbnNwZWN0IHRoZSBkYXRhIGFuZCBzZWxlY3QgYW55IGJhZCBjaGFubmVscywgYW5kIHdyaXRlIHRoZW0gZG93bgoxLiBOZXh0LCBtYW51YWxseSByZWplY3QgdGhlIGNoYW5uZWxzIGJ5IHNlbGVjdGluZyBgRWRpdGAgdGhlbiBgU2VsZWN0IERhdGFgCgohW3Zpc3VhbGx5IHJlamVjdCBhcnRpZmFjdHNdKGltYWdlcy9kcm9wX2NoYW5uZWxzLnBuZykKCjEuIE1hbnVhbGx5IGVudGVyIHRoZSBjaGFubmVscyB0byBiZSByZW1vdmVkIGluIHRoZSBgQ2hhbm5lbCByYW5nZWAgZmllbGQgYW5kIHNlbGVjdCB0aGUgY2hlY2tib3ggdW5kZXIgYG9uLT5yZW1vdmUgdGhlc2VgIGFuZCBzZWxlY3QgYE9rYAoxLiBTYXZlIHRoZSBmaWxlIGFzIGB0Y2lkX3dhdmVfbWFudWFsQ2xlYW4uc2V0YCBpbiB0aGUgZm9sbG93aW5nIGRyaXZlIHBhdGggCiAgICAtIGBcXGxjLXJzLXN0b3JlMjQuaHBjLnVpb3dhLmVkdVxsc3NfaXRwZXRlcnNlblxMYWJcU3R1ZGllc1xTY2hvb2wgUmVhZGluZXNzIFN0dWR5XERhdGFcTFYyXEVSUFxPZGRiYWxsXE1BVExBQlwxMS1NYW51YWxseSBDbGVhbmVkYAogICAgLSBUaGlzIHNjcmVlbnNob3QgcmVwcmVzZW50cyByZW1vdmluZyBjaGFubmVscyBgMjMgNTYgNTcgOTdgCiFbdmlzdWFsbHkgcmVqZWN0IGFydGlmYWN0c10oaW1hZ2VzL3JlbW92ZV9jaGFubmVsc19zcGVjaWZpYy5wbmcpCjEuIFJlcGVhdCBzdGVwIGZvciBlYWNoIGNsZWFuZWQgZGF0YXNldAoKIyMjIFJ1bm5pbmcgdGhlIElDQQoKVGhpcyBzY3JpcHQgcnVucyB0aGUgSUNBLgpXZSdsbCB3YW50IHRvIHRoaW5rIGFib3V0IGhvdyB0byBhdXRvbWF0aWNhbGx5IHJlamVjdCB0aGUgY29tcG9uZW50cyBoZXJlLgpPbmNlIHRoaXMgcnVucywgc2VlIHRoZSBbSUNBIHNlY3Rpb25dKCNpY2EpIHRvIHJlamVjdCBjb21wb25lbnRzLgpUb29scyDihpIgSW5zcGVjdC9sYWJlbCBjb21wb25lbnRzIGJ5IG1hcCBpcyBob3cgdG8gcmVqZWN0IGNvbXBvbmVudHMuCgpgYGBtYXRsYWIKJSBSdW5uaW5nIHRoZSBJQ0EKCiUlIFJlYWRpbmcgaW4gdGhlIG1hbnVhbGx5IGNsZWFuZWQgZGF0YXNldHMKCiUlJSUlJSUlJSUlJSUlCiUgQ2xlYXIgRGF0YSBiZWNhc3VlIHRvIG92ZXJ3cml0ZSBhbnl0aGluZyBpbiBtZW1vcnkKJSUlJSUlJSUlJSUlJSUKQUxMRUVHID0gW107CkVFRyA9IFtdOwoKJSBBZGRpbmcgdGhlIHBhdGggZm9yIGNsZWFuZWQgZmlsZXMKbWFudWFsQ2xlYW5QYXRoID0gJ1xcbGMtcnMtc3RvcmUyNC5ocGMudWlvd2EuZWR1XGxzc19pdHBldGVyc2VuXExhYlxTdHVkaWVzXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcRGF0YVxMVjJcRVJQXE9kZGJhbGxcTUFUTEFCXDExLU1hbnVhbGx5IENsZWFuZWRcJzsKCiVTdGFydGluZyBFRUdMQUIKW0FMTEVFRyBFRUcgQ1VSUkVOVFNFVCBBTExDT01dID0gZWVnbGFiOwoKCiVMb2NhdGlvbiBvZiBjbGVhbmVkIG9kZGJhbGwgZGF0YQpmaWxlc0luRGlyZWN0b3J5TWFudWFsQ2xlYW4gPSBkaXIoZnVsbGZpbGUobWFudWFsQ2xlYW5QYXRoLCAnKi5zZXQnKSk7ICVSZWFkaW5nIHRoZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5CgoKJUxpc3RpbmcgdGhlIG51bWJlciBvZiBzdWJqZWN0cyBmb3IgdGhlIG51bWJlciBvZiB0aW1lcyB0byBsb29wCm51bWJlck9mU3ViamVjdE1hbnVhbENsZWFuID0gaGVpZ2h0KGZpbGVzSW5EaXJlY3RvcnlNYW51YWxDbGVhbik7CgolTmFtZXMgb2YgYWxsIHRoZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5Cmxpc3RPZlN1YmplY3RzTWFudWFsQ2xlYW4gPSB7ZmlsZXNJbkRpcmVjdG9yeU1hbnVhbENsZWFuLm5hbWV9OyAKCiVzcGxpdHRpbmcgdGhlIGZpbGVuYW1lIHVwCmZpbGVuYW1lU3BsaXRNYW51YWxDbGVhbiA9IHJlZ2V4cChsaXN0T2ZTdWJqZWN0c01hbnVhbENsZWFuLCAnXycsICdzcGxpdCcpOyAgCmlkID0gY2VsbGZ1bihAKHgpIHh7MX0sZmlsZW5hbWVTcGxpdE1hbnVhbENsZWFuLCd1bicsMCk7ICAlZ2V0dGluZyB0aGUgaWQncwp3YXZlID0gY2VsbGZ1bihAKHgpIHh7Mn0sZmlsZW5hbWVTcGxpdE1hbnVhbENsZWFuLCd1bicsMCk7ICVnZXR0aW5nIHRoZSB3YXZlcwoKCmZvciBpPTE6bnVtYmVyT2ZTdWJqZWN0TWFudWFsQ2xlYW4KCiAgICAlJSUlIEltcG9ydGluZyBEYXRhCiAgICB0Y2lkQ2xlYW4gPSBjaGFyKHN0cmNhdChpZChpKSwnXycsIHdhdmUoaSksICdfbWFudWFsQ2xlYW4uc2V0JykpOyAlY29tYmluaW5nIHRoZSBUQ0lEIGFuZCB3YXZlIHRvIG5hbWUgdGhlIGZpbGUKICAgIEVFRyA9IHBvcF9sb2Fkc2V0KCdmaWxlbmFtZScsIHRjaWRDbGVhbiwgJ2ZpbGVwYXRoJywgbWFudWFsQ2xlYW5QYXRoKTsgCiAgICBbQUxMRUVHIEVFRyBDVVJSRU5UU0VUXSA9IHBvcF9uZXdzZXQoQUxMRUVHLCBFRUcsIGksJ3NldG5hbWUnLCB0Y2lkQ2xlYW4sJ2d1aScsJ29mZicpOwoKICAgICUlJSBSdW5uaW5nIHRoZSBJQ0EKICAgIEVFRyA9IGVlZ19jaGVja3NldCggRUVHICk7CiAgICBFRUcgPSBwb3BfcnVuaWNhKEVFRywgJ2ljYXR5cGUnLCAncnVuaWNhJywgJ2V4dGVuZGVkJywxLCdpbnRlcnJ1cHQnLCdvbicpOwogICAgW0FMTEVFRyBFRUddID0gZWVnX3N0b3JlKEFMTEVFRywgRUVHLCBDVVJSRU5UU0VUKTsKICAgIGVlZ2xhYiByZWRyYXc7CgplbmQKZWVnbGFiIHJlZHJhdzsKYGBgCgojIyBOb3RlcwoKIyMjIE1vdW50L0NhcCBUeXBlCgpPcHRpb25zIHRoYXQgc2F5ICJBdmVyYWdlTmV0MTI4IiBhcmUgc3VpdGFibGUgZm9yIG91ciAxMjggY2hhbm5lbCBjYXAgd2hlbiBzZWxlY3RpbmcgdGhlIHRlbXBsYXRlIG9wdGlvbiB3aGVuIHJlYWRpbmcgaW4gdGV4dCBmaWxlcy4KVGhlc2UgYXJlIGFnZS1ub3JtZWQgY29vcmRpbmF0ZXMgcHJvdmlkZWQgYnkgRUdJLgpTbywgIjJfOSIgaXMgYWdlLW5vcm1lZCBmb3IgMiB0byA5IHllYXIgb2xkcy4=



Developmental Psychopathology Lab