General information:

Step 0: Update the build and Foward strain:

a. Update the build:

  • Must determine whether the build is in the right build or not?

b. Foward strain:

#!/bin/sh

cd /home/w/wxu/calcium/Hamilton/Rob_hamilton_consortium_data/Post_QC

# skipped this part since I do not have the manifest for this particular chip.

# According to the recors the original chip is in platform HumanOmniExpressExome-8v1 with 585,384 markers (none of the manifests available at illumina match this)


gunzip -c HumanOmniExpressExome-8-v1-2-B.csv.gz | wc -l #964225

#Number of markers in chip (loci count):
gunzip -c HumanOmniExpressExome-8-v1-2-B.csv.gz | head #964193

# 964202=964193+8+1
gunzip -c HumanOmniExpressExome-8-v1-2-B.csv.gz | awk -F"," '(NR>7 && NR<964202){print}' | head

gunzip -c HumanOmniExpressExome-8-v1-2-B.csv.gz | awk -F"," '(NR>8 && NR<964202 && $21=="-"){print $2}' > CO20_negstrand.txt


PL="/home/w/wxu/oespinga/software/plink/plink-1.07-x86_64"
FILE_DIR="$HOME/HumanOmniExpressExome_201506"

$PL/plink --noweb 
--bfile $FILE_DIR/CO20_Cutoff_TOP_20150409_FinalReport_QC 
--flip CO20_negstrand.txt 
--make-bed --out $FILE_DIR/CO20_Cutoff_TOP_20150409_FinalReport_QC_FWD

Step 1: Preparation of the files:

a. Submit Script:

  • Program used: PLINK, gtools (convert the IMPUTE2 format into PED/MAP format) and SHAPEIT

Set up paths for tools and directories:

#! /bin/bash

export PL= /path/to/PLINK
export GTL= /path/to/gtools
export SHP= /path/to/Shapit

# Possible changes to locations:
#FILE_DIR, LOG_D, GTOOL_FDIR, SCRIPT_DIR, FILE_NAME
# Navigate to the directory where the setup files are stored

export LOCAL_DIR= /path/to/local/file
export FILE_DIR= /path/to/postQC file

cd $LOCAL_DIR 

# Set the log directory for all output and error files downstream 
LOG_D=logs
HAPS_FDIR=haplotypes

mkdir -p $LOG_D
mkdir -p $HAPS_FDIR

export LOG_DIR=${LOCAL_DIR}/${LOG_D}
export OUTPUT_DIR=${LOCAL_DIR}/${HAPS_FDIR}
export SCRIPT_DIR= /path/to/the/code
export FILE_NAME=Rob_Hamilton_final #The filtered file (cleaned + forward aligned)

export REFERENCE_PATH= /path/to/reference panel 1000 genomes
export GMAP_DIR= /path/to/GMAGMAP

######################################################################
# Iterate over all chromosomes and submit jobs through the script file: 1a_run_gtool_CO20.sh

export START_CHR=1
export END_CHR=22

Submit script for job using qsub:

echo "prepare files for shapeit"
JOB_NAME= /job_name
qsub -o $LOG_DIR/${JOB_NAME}.out \
-e $LOG_DIR/${JOB_NAME}.err \
-v START_CHR,END_CHR,LOCAL_DIR,FILE_DIR,LOG_DIR,FILE_NAME,OUTPUT_DIR,REFERENCE_PATH,GMAP_DIR,PL,GTL,SHP \
-q debug \
-N $JOB_NAME \
$SCRIPT_DIR/1a_run_preparefiles.sh

# Note
# qsub submit a batch job to Grid Engine 
# -N the name of the job 
# -o the path used for the standard output stream of the job (default job_name.ojob_id format)
# -e the path used for the standard error stream of the job (defaul job_name.ejob_id format)
  • JOB_NAME: Specifies a name for the job submission
  • qsub command: Submits the job using the qsub command in a batch environment (typically used in high-performance computing environments)
    • -o $LOG_DIR/${JOB_NAME}.out: Sets the output log file path
    • -e $LOG_DIR/${JOB_NAME}.err: Sets the error log file path
    • -v: Passes a list of variables to the job, including chromosome range (START_CHR to END_CHR), file directories, tool paths, and reference paths
    • -q debug: Specifies the queue for the job (in this case, the debug queue, typically used for quick or test runs)
    • -N $JOB_NAME: Sets the job name
    • $SCRIPT_DIR/1a_run_preparefiles.sh: Calls the specific script (1a_run_preparefiles.sh) to process the files

b. Run Script:

Script header and resource allocation

#!/bin/bash
#PBS -l nodes=1:ppn=8
#PBS -l walltime=1:00:00
  • #!/bin/bash: indicates the script shoudl run in the Bash shell
  • #PBS -l nodes=1:ppn=8: specifies to the job scheduler to use 1 node with 8 processors per node
  • #PBS -l walltime=1:00:00: Sets a maximum run time of 1 hour

Loading Modules and setting up Directories:

module load gnu-parallel

echo Local Directory: $LOCAL_DIR
echo File Directory: $FILE_DIR
echo File Name: $FILE_NAME
echo Output Directory: $OUTPUT_DIR
echo Reference Directory: $REFERENCE_PATH
cd $OUTPUT_DIR

echo Starting Directory: $OUTPUT_DIR
  • module load gnu-parallel: loads GNU parallel, which allows for parallel execution of commands
  • echo: The script then prints out environment variables to confirm their values

Function to split file by chromosome:

function PLINK_chr {
    CHR=$1
    $PL/plink 
    --noweb 
    --bfile $OUTPUT_DIR/${FILE_NAME}_noduppos 
    --chr ${CHR} 
    --make-bed --recode --out $OUTPUT_DIR/${FILE_NAME}_chr${CHR}
}
export -f PLINK_chr
  • Define the function PLINK_chr to separate genetic data by chromosome (chr) and convert it to PLINK binary format (--make-bed).
  • export -f PLINK_chr: Exports the function so it can be used.

Remove duplicated Positions:

echo "Remove duplicated positions"

1. Identify duplicates by Chromosome Position:

#duplicated positions
awk '{print $1":"$4}' $FILE_DIR/$FILE_NAME.bim | sort | uniq -c | awk '$1>1{print $2}' > duppos.txt
  • Extracts and counts chromosome positions in the .bim file, saving duplicates to duppos.txt

2. Separate Duplicates by SNP ID Prefix (rs or non-rs):

#duplicated positions with snp id not starting with rs
join duppos.txt <(awk '{print $1":"$4,$2}' $FILE_DIR/$FILE_NAME.bim | sort -k 1) | awk '{print $2,$1}' | grep -v -e '^rs' > duppos_wo_rs.txt

#duplicated positions with snp id starting with rs
join duppos.txt <(awk '{print $1":"$4,$2}' $FILE_DIR/$FILE_NAME.bim | sort -k 1) | awk '{print $2,$1}' | grep -e '^rs' > duppos_w_rs.txt
  • Save duplicate positions without the rs prefix to duppos_wo_rs.txt
  • Save duplicate positions with the rs prefix to duppos_w_rs.txt

3. Filter out duplicate SNPs by certain criteria:

## remove snps with duplicated positions and the duplicated ones have rs id
join -2 2 <(join -2 2 duppos.txt duppos_w_rs.txt | awk '!seen[$1]++') duppos_wo_rs.txt | awk '{print $3}' | uniq > duppos_snpids_toremove.txt

## remove snps with duplicated positions and are not duplicated with SNPs that have rs id
join -2 2 <(join -v1 -2 2 duppos.txt duppos_w_rs.txt) duppos_wo_rs.txt | awk 'seen[$1]++{print $2}' >> duppos_snpids_toremove.txt

#the complement of above in case ever needed
#join -2 2 <(join -v1 -2 2 duppos.txt duppos_w_rs.txt) duppos_wo_rs.txt | awk '!seen[$1]++' | wc -l
  • duppos_snpids_toremove.txt accumulates IDs to exclude from further processing. Here, non-rs duplicates are prioritized for removal.

4. Removal additional duplicats if necessary:

#in case duplicated positions exist within the snp ids starting with rs
extra_dups=`awk '{print $2}' duppos_w_rs.txt | uniq -c | awk '$1>1{print $2}' | wc -l`
if [ $extra_dups -gt 0 ]
then
##if more than one duplicated position appear here, keep one
join <(awk '{print $2}' duppos_w_rs.txt | uniq -c | awk '$1>1{print $2}' | sort) <(awk '{print $1":"$4,$2}' $FILE_DIR/$FILE_NAME.bim | sort -k 1) | awk '{print $2,$1}' | grep -e '^rs' | awk 'seen[$2]++{print $1}' >> duppos_snpids_toremove.txt
fi
  • Checks if more than one duplicate SNP has an rs prefix (extra_dups), keeping one instance and removing others.

Strand Alignment Check with ShapeIt

function Strand_alig {
    CHR=$1

    $SHP/shapeit -check -B $OUTPUT_DIR/${FILE_NAME}_chr${CHR} --input-ref $REFERENCE_PATH/1000GP_Phase3_chr${CHR}.hap.gz $REFERENCE_PATH/1000GP_Phase3_chr${CHR}.legend.gz $REFERENCE_PATH/1000GP_Phase3.sample --output-log $OUTPUT_DIR/chr${CHR}.alignments

    nstrand=`awk '$1=="Strand" && $7==1{print $4}' $OUTPUT_DIR/chr${CHR}.alignments.snp.strand | wc -l`

    if [ $nstrand -gt 0 ]
        then
        $PL/plink --noweb --bfile $OUTPUT_DIR/${FILE_NAME}_chr${CHR} --flip  <(awk '$1=="Strand" && $7==1{print $4}' $OUTPUT_DIR/chr${CHR}.alignments.snp.strand) --make-bed --out $OUTPUT_DIR/${FILE_NAME}_chr${CHR}

        $SHP/shapeit -check -B $OUTPUT_DIR/${FILE_NAME}_chr${CHR} --input-ref $REFERENCE_PATH/1000GP_Phase3_chr${CHR}.hap.gz $REFERENCE_PATH/1000GP_Phase3_chr${CHR}.legend.gz $REFERENCE_PATH/1000GP_Phase3.sample --output-log $OUTPUT_DIR/chr${CHR}.alignments

#This may not be necessary since the markers that are excluded in the original .strand.exlude file are removed later anyways.
#awk '$1=="Strand" && $7==1{print $3}' $OUTPUT_DIR/chr${CHR}.alignments.snp.strand > $OUTPUT_DIR/chr${CHR}.alignments.snp.strand.exclude
    fi

    if [ ! -f $OUTPUT_DIR/chr${CHR}.alignments.snp.strand.exclude ]
        then
        touch $OUTPUT_DIR/chr${CHR}.alignments.snp.strand.exclude
    fi
}

export -f Strand_alig
  • The Strand_alig function checks strand alignment by comparing the chromosome data with reference data from 1000 Genomes Project
  • If SNPs are found with strand mismatches, they are corrected by flipping them with PLINK

Run ShapeIt in Parallel for Strand Alignment

echo "Checking missaligned alleles"
seq $START_CHR $END_CHR | parallel -j 8 --joblog $LOG_DIR/1_prephase_shapeit_align.ljob Strand_alig {}

echo "Finished Checking missaligned alleles"
  • Runs the Strand_align function for each chromosome in parallel to check and correct strand alignment issues across all specified chromosomes.

Step 2: Pre-phasing:

# Step 1: Submit Pre-phase
# Set environment variables
PL=/path/to/plink
GTL=/path/to/gtool
SHP=/path/to/shapeit
LOCAL_DIR=/path/to/local_dir
FILE_DIR=/path/to/file_dir
LOG_D=/path/to/logs
HAPS_FDIR=/path/to/haplotypes

Create Necessary Directories

# Create directories for logs and haplotypes
mkdir -p $LOG_D
mkdir -p $HAPS_FDIR
# -p option ensures no error is thrown if the directory already existst
  • Creates directories for logs and haplotypes if they don’t already exist (-p option prevents errors if they already do).

Loop through chromosomes and submit jobs:

# Loop through chromosomes
for CHR in {1,2,6}; do
    qsub -o $LOG_D/2a_run_prephase_chr${CHR}.out \
         -e $LOG_D/2a_run_prephase_chr${CHR}.err \
         -q batch -l nodes=1:ppn=8,walltime=5:00:00 \
         -N 2a_run_prephase_chr${CHR} \
         2a_run_prephase.sh $CHR
done
  • Submit job for each chromosome:
    • -o and -e Directs output and error logs to the specified log directory
    • -q batch Runs the job in the batch queue
    • -l nodes=1:ppn=8,walltime=5:00:00 Requests 1 node, 8 processors, and 5 hours of runtime
    • -N Names the job with a chromosome-specific identifier
    • 2a_run_prephase.sh $CHR Runs the 2a_run_prephase.sh script with the chromosome as an argument.

b. Run Script:

Set up environment variable:

echo "Using PLINK at: $PL"
echo "Using GTOOL at: $GTL"
echo "Using SHAPEIT at: $SHP"
  • Displays the paths to the key tools making it easier to confirm that the correct paths ar set for this run.

Prephase Data using ShapeIt:

# Prephase data using SHAPEIT
shapeit -B ${FILE_DIR}/chr${CHR} \
        -M ${FILE_DIR}/genetic_map_chr${CHR}.txt \
        -O ${HAPS_FDIR}/chr${CHR}.haps \
        --exclude-snp ${FILE_DIR}/exclude_snp.txt \
        --thread 16
  • Runs ShapeIT for haplotype phasing on the specified chromosome ${CHR}
    • -B ${FILE_DIR}/chr${CHR}: Specifies the input PLINK binary file for the chromosome
    • -M ${FILE_DIR}/genetic_map_chr${CHR}.txt: Uses the genetic map file for recombination rates for the chromosome
    • -O ${HAPS_FDIR}/chr${CHR}.haps: Set the output files for haplotype
    • --exclude-snp ${FILE_DIR}/exclude_snp.txt: Excludes specified SNPs from phasing based on this file
    • --thread 16 Runs the phasing process on 16 threads for speed

Check if .haps file was created:

# Check if the .haps file was created
if [[ -s ${HAPS_FDIR}/chr${CHR}.haps ]]; then
    echo "Prephasing for chromosome ${CHR} completed successfully."
else
    echo "Error: Prephasing for chromosome ${CHR} failed."
fi
  • Verifies that the .haps file for the chromosome was created successfully
    • -s checks if the file exists and is non-empty
    • If the files exists, it prints a success message
    • If the file is missing or empty, it displays an error message indicating that prephasing failed for the chromosome

Step 3: Imputation:

a. Submit Script:

Define directories/paths/variables:

#! /bin/bash
export IMP2= path/to/Impute2

#Possbile changes to the locations:
# FILE_DIR, OUT_NEW_DIR, SCRIPT_DIR, CHR_END_FILE, LOG_DIR, FILE_NAME

#The home reference directory
export LOCAL_DIR= path/to/local_file
cd $LOCAL_DIR

#The directory with the haplotype files
export FILE_DIR= path/to/local_file/haplotype

#The log directory for all output and error files downstream
export LOG_DIR=$LOCAL_DIR/"logs"

#Directory to put imputed files
OUT_NEW_DIR='imputed_files_hap'
mkdir -p $OUT_NEW_DIR
export OUTPUT_DIR=$LOCAL_DIR/$OUT_NEW_DIR
SCRIPT_DIR= path/to/script

#Imputation panel reference paths (haplotype, genetic map and legend files)
export REFERENCE_PATH= /path/to/reference_1000_genome 
#export LEGEND_REFERENCE_PATH=$REFERENCE_PATH/legend_files
export LEGEND_REFERENCE_PATH="$REFERENCE_PATH"
CHR_END_FILE=chr_endfile_hap.txt


#Filename (without chromosome suffixes) of the gtool output files
export FILE_NAME=FILE_NAME_final

#Chromosome end positions for modifying the intervals
### Define some parameters for looping
  • Sets the path to the Impute2 software used for genotype imputation
  • Sets the base directory path LOCAL_DIR and navigates to it
  • Sets paths for FILE_DIR, where haplotype files are stored, and LOG_DIR, where log files will be saved
  • Creates the OUTPUT_DIR for imputed files, making the directory if it doesn’t already exist
  • Sets the paths for the SCRIPT_DIR and REFERENCE_PATH, where reference genome files are located
  • Sets the filename prefix FILE_NAME for the output files and the size of intervals (in base pairs) for splitting data for imputation

Set the loop of chromosome:

# Define analysis interval in kb (i.e. base pairs)
let interval_size=500000
export interval_size

# Iterate through each chromosome (chromosome 22 for testing purposes)
let start_chr=16 end_chr=16

# Number of processes per run
export R=8 #6

# Loop through and submit batches
for chr in 2 16 #`seq $start_chr $end_chr`
do
    export chr
    export start_interval=1
    let CHR_LENGTH=`tail -n 1 <(gunzip -c $LEGEND_REFERENCE_PATH/1000GP_Phase3_chr${chr}.legend.gz) | awk '{print $2}'`
    let end_interval="$CHR_LENGTH / $interval_size + 1"
    export end_interval
    echo $LEGEND_REFERENCE_PATH/1000GP_Phase3_chr${chr}.legend.gz
    echo $chr $CHR_LENGTH $end_interval $interval_size
    echo $chr $CHR_LENGTH $end_interval $interval_size >> $LOCAL_DIR/$CHR_END_FILE
#echo chr: $chr interval $interval out of $end_interval # prints the current chromosome
  • Specifies chromosomes for testing (start_chr=16, end_chr=16) and the number of processes per run R
  • Loops over specific chromosomes (chr values 2 and 16 for testing)
  • Calculates chromosome length CHR_LENGTH using the last entry in the chromosome legend file
  • Divides chromosome length by interval_size to get end_interval (total intervals needed for this chromosome)
  • Logs chromosome end positions and interval details to CHR_END_FILE for tracking

Submit Imputation jobs:

JOB_NAME=3_impute2_chr${chr}
    qsub -o $LOG_DIR/$JOB_NAME.out -e $LOG_DIR/$JOB_NAME.err -N $JOB_NAME \
    -v LOCAL_DIR,FILE_DIR,OUTPUT_DIR,LOG_DIR,REFERENCE_PATH,LEGEND_REFERENCE_PATH,chr,interval_size,FILE_NAME,IMP2,start_interval,end_interval,R \
    -q batch -l nodes=1:m32g:ppn=8,walltime=48:00:00 \
    $SCRIPT_DIR/3a_run_imputation.sh
done

#Change the queue to which you are submitting : -q debug / -q batch
#Change the amount of resources requested  nodes=1:ppn=8,walltime=0:30:00
  • Submits a batch job for each chromosome (qsub command) with output/error files specified in LOG_DIR
  • Job parameters like memory, processors, and wall time are defined in -l nodes=1:m32g:ppn=8,walltime=48:00:00

b. Run Script:

Set up environment and directory:

#! /bin/bash

module load gnu-parallel

echo Local Directory: $LOCAL_DIR
echo File Directory: $FILE_DIR
echo Output Directory: $OUTPUT_DIR
echo Imputation Panel Reference Path: $REFERENCE_PATH
echo Imputation Panel Legend Files Reference Path: $LEGEND_REFERENCE_PATH
echo Chromosome Number: $chr
echo Interval Size Set to: $interval_size
echo Input Filename: $FILE_NAME

cd $LOCAL_DIR

export int_sze=$interval_size

export GENETIC_MAP_NAME="genetic_map_chr${chr}_combined_b37.txt"
#HAPLOTYPE_FILENAME="ALL_1000G_phase1integrated_v3_chr${chr}_impute.hap.gz"
export HAPLOTYPE_FILENAME="1000GP_Phase3_chr${chr}.hap.gz"
#LEGEND_FILENAME="ALL_1000G_phase1_chr${chr}_impute.legend"
export LEGEND_FILENAME="1000GP_Phase3_chr${chr}.legend.gz"

mkdir /dev/shm/workdir
mkdir -p $OUTPUT_DIR/chr${chr}
  • Loads gnu-parallel to manage parallel job execution across intervals
  • Displays key directory and file paths, chromosome number, interval size, and filename for logging purposes
  • Changes the working directory to LOCAL_DIR, where imputation-related files and directories are based
  • Exports the interval size variable for defining the genomic range to process per interval
  • Sets the file names of the genetic map, haplotype, and legend files specific to each chromosome, based on the reference panel
  • Creates a temporary work directory in shared memory (/dev/shm/workdir) to speed up I/O operations during imputation, and a chromosome-specific output directory in OUTPUT_DIR.

Run Imputation in Parallel using Impute2:

seq $start_interval $end_interval | parallel -j $R --joblog $LOG_DIR/impute2_chr${chr}.ljob '$IMP2/impute2 -use_prephased_g \
    -known_haps_g $FILE_DIR/${FILE_NAME}_chr${chr}.phased.haps \
    -m $REFERENCE_PATH/$GENETIC_MAP_NAME \
    -h $REFERENCE_PATH/$HAPLOTYPE_FILENAME \
    -l $LEGEND_REFERENCE_PATH/$LEGEND_FILENAME \
    -int $(( int_sze*({}-1)+1 )) $(( int_sze*{} )) \
    -Ne 20000 -buffer 250 \
    -o /dev/shm/workdir/${FILE_NAME}_chr${chr}_{}_imputed \
    -o_gz; \
    mv /dev/shm/workdir/${FILE_NAME}_chr${chr}_{}_imputed* $OUTPUT_DIR/chr${chr} '

#sleep 5s
#mv /dev/shm/workdir/* $OUTPUT_DIR/
  • Sequence Setup: seq $start_interval $end_interval generates intervals across which Impute2 will run
  • Parallel Execution: GNU Parallel runs each interval as a separate job, using -j $R to specify the maximum concurrent jobs.
  • Impute2 Command: Executes the Impute2 command with several arguments:
    • -use_prephased_g: Uses pre-phased genotype data
    • -known_haps_g: Specifies the pre-phased haplotype file for this chromosome
    • -m, -h, -l: Specifies paths to the genetic map, haplotype, and legend files, respectively
    • -int: Defines the genomic interval for this run, calculated based on int_sze and interval number ({}).
    • -Ne: Sets the effective population size for the model.
    • -buffer: Adds a buffer region around each interval for imputation accuracy
    • -o: Specifies the output path for the imputed file within the temporary directory /dev/shm/workdir.
    • Move Output: Moves the completed imputed files from /dev/shm/workdir to the chromosome-specific output directory in OUTPUT_DIR.
  • Last commented code would move all files from /dev/shm/workdir to OUTPUT_DIR, for debugging purposes.

Step 5: Quality Control post-Imputation:

a. Submit Script:

Set up directory and load necessary modules:

#!/bin/bash
#PBS -l nodes=1:ppn=8
#PBS -l walltime=2:00:00

module load gnu-parallel

export IMP_DIR= path/to/Imputation
export SCRIPT_DIR= path/to/code
export OUTPUT_DIR=$IMP_DIR/"imputed_plink"
export LOG_DIR=$IMP_DIR/"logs"
export HAPS_DIR=$IMP_DIR/"imputed_files_hap" #where the imputed files are located
#*** Submit this script as:
# qsub -o $LOG_DIR/5_perform_qc.out -e $LOG_DIR/5_qc.err -q batch -N 5_qc_imp $SCRIPT_DIR/5_perform_qc.sh
export PL= path/to/PLINK
export G_FILE="Rob_Hamilton_imp_chr" #output name of the plink files
export FILES_PREFIX="Hamilton"
#create file with info and imputation info
  • module load gnu-parallel: Load the gnu-parallel module, which allows parallel execution of jobs for improved performance
    • PBS directives is what?
  • Define all the paths to directories and variables
    • IMP_DIR is the main directory where all imputation-related data is stored
    • OUTPUT_DIR and LOG_DIR are sub-directories for storing outputs and logs
    • PL specifies the path to PLINK tool
    • G_FILE and FILES_PREFIX set the naming format for output files to see the results
  • Use qsub to submit the jobs
    • -o and -e options define where to save the standard output and error logs
    • -N assigns a name to the job for tracking in the queue system
    • Name the job 5_qc_imp

Create Header for the Imputation Summary File:

#create header for file
head_file=`ls -lh $HAPS_DIR/chr1/*_info | head -1 | awk '{print $9}'`
echo `awk 'NR==1{print "chr",$0}' $head_file | cut -d" " -f1,3-` | gzip > $IMP_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz
  • The file will compile imputation info for all chromosomes
  • This step extracts the header from the first _info file in the directory, adds a chr (chromosome) column, and saves it as the header of the summary file
    • ls -lh $HAPS_DIR/chr1/*_info | head -1 | awk '{print $9}' gets the name of the first _info file for chromosome 1
    • awk 'NR==1{print "chr",$0}' adds “chr” as a new first column to the header
    • cut -d" " -f1,3- removes any unnecessary columns, retaining only essential data
    • gzip > $IMP_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz saves the modified header as a compressed file.

Append Imputation Data for each Chromosome:

#loop over all _info files generated by impute2
for i in `seq 1 22`
    do
    for INT_FILE in `ls $HAPS_DIR/chr${i}/*chr${i}_*_info | sort -V`
        do
        awk -v chr=$i 'NR>1{print chr,$0}' $INT_FILE | cut -d" " -f1,3- | gzip >> $IMP_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz
    done
done
  • This loop gather data for chromosome 1 to 22 from the _info file
    • It appends imputation data for each chromosome to the summary file (_imputation_loci_info.txt.gz), excluding the header row from each _info file
    • for i in '\seq 1 22' iterates through chromosome numbers 1 to 22.
    • ls $HAPS_DIR/chr${i}/*chr${i}_*_info | sort -V lists and sorts all _info files for each chromosome
    • awk -v chr=$i 'NR>1{print chr,$0}' skips the first row (header) of each _info file and appends chromosome number as the first column
    • gzip >> $IMP_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz appends this processed data to the summary file in compressed format

Perform Quality Control (QC) in Parallel and Completed Message:

cd $OUTPUT_DIR

seq 1 22 | parallel -j 4 --joblog $LOG_DIR/5_qc.ljob $SCRIPT_DIR/5a_perform_qc_script.sh {}

#::: 7 8 16

echo "Finished..."
  • This steps use gnu_parallel to run a QC script (5a_perform_qc_script.sh) on each chromosome file in parallel, processing up to 4 chromosomes at once
    • cd $OUTPUT_DIR changes the directory to where QC outputs will be saved
    • seq 1 22 generates a sequence of numbers from 1 to 22, representing chromosomes
    • parallel -j 4 --joblog $LOG_DIR/5_qc.ljob executes 5a_perform_qc_script.sh with up to 4 jobs in parallel and logs job activity
    • $SCRIPT_DIR/5a_perform_qc_script.sh {} runs the QC script for each chromosome, with {} replaced by chromosome numbers
  • echo "Finished..."print the finish message and confirm all the tasks been executed

b. Run Script:

Set the Variable and Directory:

#!/bin/bash
let CHR=$1
cd $OUTPUT_DIR
  • let CHR=$1 assigns the first argument passed to the script to the variable CHR, representing the chromosome number
  • cd $OUTPUT_DIR changes the working directory to where the QC files will be saved

Count initial SNPs and Identify missing IDs in the BIM file:

echo "$CHR Initial_no_duplicates" `wc -l $G_FILE${CHR}_clean.bim` > Counts_${CHR}.txt
echo "$CHR SNPs_with_dot" `awk '$2=="."{print $2}' ${G_FILE}${CHR}_clean.bim | wc -l` >> Counts_${CHR}.txt
  • wc -l $G_FILE${CHR}_clean.bim counts the number of SNPs in the cleaned BIM file for the chromosome, and the result is labeled and saved to Counts_${CHR}.txt
  • The second command counts SNPs where the identifier is just a dot ("."), which may indicate missing or problematic SNP identifiers, appending the count to Counts_${CHR}.txt

Filter SNPs based on Info and Certainty:

join <(gunzip -c ../${FILES_PREFIX}_imputation_loci_info.txt.gz | awk -v chr=${CHR} '$5>.9 && $6>.9 && $1==chr {print $2}' | sort) <(awk '$2!="."{print $2}' ${G_FILE}${CHR}_clean.bim | sort) > snp_list_${CHR}.txt

echo "$CHR After_info_and_certainty" `wc -l snp_list_${CHR}.txt` >> Counts_${CHR}.txt
  • gunzip -c ../${FILES_PREFIX}_imputation_loci_info.txt.gz | awk -v chr=${CHR} '$5>.9 && $6>.9 && $1==chr {print $2}' | sort filters SNPs from the imputation info file based on info score ($5) and certainty ($6) values above 0.9, for the specified chromosome
  • join matches these SNPs with those in the BIM file (that do not have . as their ID), creating a list of SNPs that meet both criteria in snp_list_${CHR}.txt
  • wc -l snp_list_${CHR}.txt counts these SNPs and logs the result in Counts_${CHR}.txt

Filter SNPs based on Missing Rate:

$PL/plink --noweb --bfile $G_FILE${CHR}_clean --extract snp_list_${CHR}.txt --geno 0.05 --make-bed --out $G_FILE${CHR}_clean_qc

echo "$CHR After_missing_rate" `wc -l $G_FILE${CHR}_clean_qc.bim` >> Counts_${CHR}.txt
  • $PL/plink --bfile $G_FILE${CHR}_clean --extract snp_list_${CHR}.txt --geno 0.05 --make-bed --out $G_FILE${CHR}_clean_qc uses PLINK to filter SNPs based on a missing rate of 5% (--geno 0.05) and generates a new binary file format (--make-bed) with this updated SNP list
  • wc -l $G_FILE${CHR}_clean_qc.bim counts the remaining SNPs after applying the missing rate filter and logs it in Counts_${CHR}.txt

Filter SNPs Based on Minor Allele Frequency (MAF):

$PL/plink --noweb --bfile $G_FILE${CHR}_clean_qc --maf 0.01 --make-bed --out $G_FILE${CHR}_clean_qc

echo "$CHR After_maf_filter" `wc -l $G_FILE${CHR}_clean_qc.bim` >> Counts_${CHR}.txt
  • $PL/plink --bfile $G_FILE${CHR}_clean_qc --maf 0.01 --make-bed --out $G_FILE${CHR}_clean_qc applies a minor allele frequency (MAF) filter of 0.01, removing rare SNPs from the dataset.
  • wc -l $G_FILE${CHR}_clean_qc.bim counts SNPs after applying the MAF filter and appends this count to Counts_${CHR}.txt

Clean up Temporacy Files:

rm snp_list_${CHR}.txt
  • rm snp_list_${CHR}.txt deletes the temporary file snp_list_${CHR}.txt, which is no longer needed after QC processing

Step 6: Count the Info file:

Set up directories and Filenames:

#!/bin/bash
#PBS -l nodes=1:ppn=8
module load extras

##submit as qsub -q batch -l walltime=2:00:00 ./6_counts_imputation_info_files.sh

OUTPUT_DIR= /path/to/output

LOCAL_DIR=$OUTPUT_DIR/path/to/haplo_file #where the imputed files are located
LOCAL1_DIR=$OUTPUT_DIR/path/to/Imputed/file
FILES_PREFIX="Hamilton"

OUTFILE=${FILES_PREFIX}"_summary.txt"

cd $OUTPUT_DIR

#type=0 imputed loci
#type=2 hard genotype (from panel 2) but with imputed info from type 0
#type=3 hard genotype (from panel 2) but no imputation done from reference panel
  • OUTPUT_DIR sets the main output directory
  • LOCAL_DIR and LOCAL1_DIR define paths for imputed haplotype files and PLINK-processed files, respectively
  • FILES_PREFIX sets the prefix for the output filenames
  • OUTFILE specifies the output file for QC summary counts

Calculate Total Imputed Markers:

#raw counts of file (i.e. all attempted imputed loci)
echo -n "Total number of markers imputed: " > $OUTFILE
pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz  | wc -l >> $OUTFILE
echo >> $OUTFILE
  • Navigates to the output directory

  • Counts all markers in the imputation into file and writes to OUTFILE

  • cd $OUTPUT_DIR: Changes the current directory to OUTPUT_DIR

  • echo -n "Total number of markers imputed: " > $OUTFILE: Writes the specified text to OUTFILE without a newline (-n flag suppresses newline).

  • pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz | wc -l:

    • pigz -dc: Decompresses a gzipped file (.txt.gz) without fully extracting it to disk
    • ${FILES_PREFIX}_imputation_loci_info.txt.gz: Refers to the compressed file containing imputation locus information.
    • | wc -l: Counts lines, representing the number of markers.
  • >> $OUTFILE: Appends the marker count to OUTFILE.

Calculate High-Quality Markers:

#high info and certainty loci (based on info and certainty fields in imputation -cols 5 and 6-)
echo -n "Number of markers high info and certainty: " >> $OUTFILE
pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz  | awk '$5>.9 && $6>.9 && NR>1{print $0}' | wc -l >> $OUTFILE
echo >> $OUTFILE
  • Filters markers with high information and certainty scores (both >0.9) and appends the count to OUTFILE

  • echo -n "Number of markers high info and certainty: " >> $OUTFILE: Appends a description line to OUTFILE.

  • pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz | awk '$5>.9 && $6>.9 && NR>1{print $0}' | wc -l:

    • awk '$5>.9 && $6>.9 && NR>1{print $0}': Filters lines where columns 5 (info score) and 6 (certainty score) are both greater than 0.9. NR>1 skips the header row
    • | wc -l: Counts the filtered lines (markers meeting high-quality thresholds) >> $OUTFILE: Appends the count to OUTFILE

Count Imputed Markers Per Chromosome:

####Perform counts from imputed, imputed after removing duplicates and qc'd loci

echo "Counts from imputation (Impute2 output): " >> $OUTFILE
count_tot=0
for i in `seq 1 22`
do
    count=`pigz -dc $LOCAL_DIR/chr${i}/*.gz | wc -l`
    echo $count chr${i} >> $OUTFILE
    count_tot=$((count_tot + count))
done
echo $count_tot" total" >> $OUTFILE
echo >> $OUTFILE
  • Iterates through chromosomes (1-22), counts markers in each chromosome’s imputed files, and calculates a total count

  • echo "Counts from imputation (impute2 output): " >> $OUTFILE: Adds a heading to OUTFILE for the imputed marker counts by chromosome

  • `for i in 22``: Loops through chromosome numbers 1-22.

  • pigz -dc $LOCAL_DIR/chr${i}/*.gz | wc -l:

    • pigz -dc $LOCAL_DIR/chr${i}/*.gz: Decompresses all imputed files for chromosome i
    • | wc -l: Counts the total markers
  • echo $count chr${i} >> $OUTFILE: Writes the count for each chromosome to OUTFILE

  • count_tot=$((count_tot + count)): Adds each chromosome’s count to count_tot for a grand total

  • echo $count_tot" total" >> $OUTFILE: Writes the total count to OUTFILE

Count Cleaned Genotypes:

echo "Counts from called genotypes with gtool .9 threshold after removing duplicates if any (plink conversion): " >> $OUTFILE
a=($(wc -l $LOCAL1_DIR/*_chr*_clean.bim))
#length of the array ${#a[@]}
for k in `seq 0 $((${#a[@]}-1))`
do
if [ $(($k % 2)) -eq 0 ]; then
    echo -n "${a[$k]} " >> $OUTFILE
else
    echo ${a[$k]} >> $OUTFILE
fi
done
echo >> $OUTFILE
  • Counts loci in cleaned .bim files for each chromosome, with duplicates removed, then appends counts to OUTFILE

  • echo "Counts from called genotypes ...": Adds a heading for genotype counts post-duplicate removal to OUTFILE`

  • a=($(wc -l $LOCAL1_DIR/*_chr*_clean.bim)): Stores line counts of each .bim file into array a

  • for k in \seq 0 $((${#a[@]}-1)): Loops through indices of array a

  • if [ $(($k % 2)) -eq 0 ]: Checks if the index is even (i.e., file path and count are grouped)

    • echo -n "${a[$k]} " >> $OUTFILE: Writes the count for each chromosome on the same line
    • else echo ${a[$k]} >> $OUTFILE: Prints each filename to a new line
    • echo >> $OUTFILE: Adds a blank line

Count Markers After QC Filtering:

echo "Counts from imputation, after QC (Info & certainty >.9, MAF>1% and genotype missing rates<5%): " >> $OUTFILE
#of loci after qc (info & certainty, maf, geno rates) and cleaning
b=($(wc -l $LOCAL1_DIR/*_chr*_clean_qc.bim))
#length of the array ${#a[@]}
for k in `seq 0 $((${#a[@]}-1))`
do
if [ $(($k % 2)) -eq 0 ]; then
    echo -n "${b[$k]} " >> $OUTFILE
else
    echo ${b[$k]} >> $OUTFILE
fi
done
echo >> $OUTFILE

#tar -zcvf $LOCAL1_DIR/CO20_imputed_clean_qc.tar.gz $LOCAL1_DIR/*_clean_qc.*
  • After QC filtering for minor allele frequency, missing rate, and high info and certainty, counts markers for each chromosome

  • Count lines in each QC-passed .bim file

    • wc -l $LOCAL1_DIR/*_chr*_clean_qc.bim: Runs wc -l on each .bim file that passed QC in the directory $LOCAL1_DIR (expected filenames include _chr*_clean_qc.bim)
    • b=($(...)): Stores the output of wc -l in an array, b, where each element in the array contains a line count for a corresponding .bim file.
  • Loop through the Array to Print Chromosome Counts:

    • for k in \seq 0 $((${#b[@]}-1)): Iterates over the indices of the array b, starting from 0 up to the last index (${#b[@]}-1')
    • if [ $(($k % 2)) -eq 0 ]: Checks if the index k is even
      • echo -n "${b[$k]} " >> $OUTFILE: For even indices, writes the line count to OUTFILE on the same line, followed by a space. This count corresponds to the number of markers in each chromosome
      • else echo ${b[$k]} >> $OUTFILE: For odd indices, appends the chromosome identifier (e.g., chr1, chr2) to OUTFILE, followed by a newline

Create Concordance Summary:

#bring concordance info from files
grep "0.0-0.1" $LOCAL_DIR/chr*/*_summary | awk '{print $1,$9}' | pigz > $OUTPUT_DIR/${FILES_PREFIX}_imputation_concordance.txt.gz


#Summaries of concordance, info and certainty
pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_concordance.txt.gz | awk '{print $2}' | awk '{for(i=1;i<=NF;i++) {sum[i] += $i; sumsq[i] += ($i)^2}} END {for (i=1;i<=NF;i++) {print "Concordance stats: ", sum[i]/NR, sqrt((sumsq[i]-sum[i]^2/NR)/(NR-1))}}' >> $OUTFILE

pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz | awk 'NR>1 {print $5}' | awk '{for(i=1;i<=NF;i++) {sum[i] += $i; sumsq[i] += ($i)^2}} END {for (i=1;i<=NF;i++) {print "Info stats: ", sum[i]/NR, sqrt((sumsq[i]-sum[i]^2/NR)/(NR-1))}}' >> $OUTFILE

pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_loci_info.txt.gz | awk 'NR>1 {print $6}' | awk '{for(i=1;i<=NF;i++) {sum[i] += $i; sumsq[i] += ($i)^2}} END {for (i=1;i<=NF;i++) {print "Certainty stats: ", sum[i]/NR, sqrt((sumsq[i]-sum[i]^2/NR)/(NR-1))}}' >> $OUTFILE
  • Searches for specific concordance info from imputation summaries and compresses the output for later statistical analysis

  • Search for the specific concordance info from imputation

    • grep "0.0-0.1" $LOCAL_DIR/chr*/*_summary: Searches for lines in _summary files that mention a concordance range 0.0-0.1
    • awk '{print $1,$9}': Extracts columns 1 and 9
    • pigz > $OUTPUT_DIR/${FILES_PREFIX}_imputation_concordance.txt.gz: Compresses the extracted information into imputation_concordance.txt.gz
  • Calculate the Concordance (Column 2)

    • pigz -dc $OUTPUT_DIR/${FILES_PREFIX}_imputation_concordance.txt.gz: Decompresses the concordance file
    • awk '{print $2}': Extracts the second column
    • The subsequent awk statement calculates the mean and standard deviation:
      • for(i=1;i<=NF;i++) {sum[i] += $i; sumsq[i] += ($i)^2}: Sums values and squares for standard deviation
      • print "Concordance stats: ", sum[i]/NR, sqrt((sumsq[i]-sum[i]^2/NR)/(NR-1)): Prints mean and standard deviation for each column
  • Similar for column 5 (info score) and column 6 (certainty)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8c3R5bGU+DQogIGgxIHsgY29sb3I6ICMxRTkwRkY7IH0gICAvKiBGb3IgSDEgaGVhZGluZ3MgKi8NCiAgaDIgeyBjb2xvcjogI0ZGNjM0NzsgfSAgICAvKiBGb3IgSDIgaGVhZGluZ3MgKi8NCiAgaDMgeyBjb2xvcjogIzMyQ0QzMjsgfSAgICAvKiBGb3IgSDMgaGVhZGluZ3MgKi8NCjwvc3R5bGU+DQoNCiMgR2VuZXJhbCBpbmZvcm1hdGlvbjogDQotICoqU3VibWl0IFNjcmlwdCoqOiBUaGlzIHNjcmlwdCBzZXJ2ZXMgYXMgdGhlIGNvbnRyb2xsZXIuIEl0IHNjaGVkdWxlcyBqb2JzLCBzcGVjaWZpZXMgcmVzb3VyY2UgYWxsb2NhdGlvbnMgKGUuZy4sIENQVSwgbWVtb3J5LCB3YWxsdGltZSksIGFzc2lnbnMgam9iIG5hbWVzLCBzZXRzIGxvZ2dpbmcgcGF0aHMsIGFuZCBkZWZpbmVzIGRlcGVuZGVuY2llcyBpZiBuZWVkZWQuDQotICoqUnVuIFNjcmlwdCoqOiBFYWNoIGluZGl2aWR1YWwgcnVuIHNjcmlwdCBjb250YWlucyB0aGUgYWN0dWFsIGNvbW1hbmRzIHRvIGV4ZWN1dGUgc3BlY2lmaWMgdGFza3MuIFRoaXMgbW9kdWxhciBhcHByb2FjaCBtYWtlcyBpdCBlYXN5IHRvIHJ1biBkaWZmZXJlbnQgc2NyaXB0cyBiYXNlZCBvbiBzcGVjaWZpYyB0YXNrcyAoZS5nLiwgcHJlcGhhc2luZywgaW1wdXRhdGlvbiwgZGF0YSBjb252ZXJzaW9uKSB3aXRob3V0IGR1cGxpY2F0aW5nIHJlc291cmNlIGFsbG9jYXRpb24gYW5kIGpvYiBzdWJtaXNzaW9uIGxvZ2ljIGVhY2ggdGltZS4NCg0KIyBTdGVwIDA6IFVwZGF0ZSB0aGUgYnVpbGQgYW5kIEZvd2FyZCBzdHJhaW46IA0KIyMgYS4gVXBkYXRlIHRoZSBidWlsZDoNCi0gTXVzdCBkZXRlcm1pbmUgd2hldGhlciB0aGUgYnVpbGQgaXMgaW4gdGhlIHJpZ2h0IGJ1aWxkIG9yIG5vdD8gDQoNCiMjIyBDb252ZXJ0IFBMSU5LIEJJTSBmaWxlIGNvb3JkaW5hdGVzIHRvIEJFRCBmb3JtYXQgZm9yIExpZnRPdmVyDQpgYGB7YmFzaH0NCiMhL2Jpbi9zaA0KDQpESVI9L3BhdGgvdG8vZGlyZWN0b3J5DQpXRD0kU0NSQVRDSC9wcm9qZWN0DQpQTD0vcGF0aC90by9QTElOSw0KTE89L3BhdGgvdG8vTGlmdE92ZXINCg0KIyMjIyMgVGhpcyB1cGRhdGUgdG8gYnVpbGQgMzcgKGhnMTkpIGlzIE5PVCBuZWVkZWQgaW4gdGhpcyBjYXNlIHNpbmNlIHRoZSBjaGlwIGlzIGFscmVhZHkgaW4gdGhlIGdlbm9taWMgYnVpbGQNCg0KI3VwZGF0ZSB0byBidWlsZCAzNyAoaHR0cDovL2dlbm9tZS5zcGgudW1pY2guZWR1L3dpa2kvTGlmdE92ZXIjTGlmdF9NZXJsaW4uMkZQTElOS19mb3JtYXQpDQojR2VuZXJhdGUgQkVEIGZpbGVzIGZyb20gcGxpbmsgYmltIGZpbGVzDQokTE8vbGlmdE92ZXIgPChhd2sgJ3twb3MwPSQ0LTE7IHByaW50ICJjaHIiJDEscG9zMCwkNCwkMn0nICRESVIvUHJvamVjdF9maW5hbC5iaW0pICRMTy9oZzM4VG9IZzE5Lm92ZXIuY2hhaW4uZ3ogJERJUi9vdXRwdXQuQkVEICRESVIvdW5saWZ0ZWQuQkVEDQpgYGANCg0KLSBgYXdrICd7cG9zMD0kNC0xOyBwcmludCAiY2hyIiQxLHBvczAsJDQsJDJ9JyAkRElSL1Byb2plY3RfZmluYWwuYmltYDogIFVzZXMgYGF3a2AgdG8gcmVmb3JtYXQgdGhlIFBMSU5LIC5iaW0gZmlsZSwgb3V0cHV0dGluZzoNCiAgICAtIENocm9tb3NvbWUgd2l0aCBhICJjaHIiIHByZWZpeCAoZS5nLiwgImNocjEiKQ0KICAgIC0gU3RhcnQgcG9zaXRpb24gKGluIHplcm8tYmFzZWQgZm9ybWF0LCBzbyB3ZSBzdWJ0cmFjdCAxIGZyb20gdGhlIDR0aCBjb2x1bW4pDQogICAgLSBFbmQgcG9zaXRpb24gKHNhbWUgYXMgc3RhcnQgcG9zaXRpb24gc2luY2UgU05QcyBhcmUgc2luZ2xlLXBvaW50IGxvY2kpDQogICAgLSBTTlAgaWRlbnRpZmllciAoMm5kIGNvbHVtbiBvZiAuYmltIGZpbGUpDQotIGAkTE8vbGlmdE92ZXIgLi4uYDogQ29udmVydHMgdGhlIGJ1aWxkIDM4IGNvb3JkaW5hdGVzIHRvIGJ1aWxkIDM3IHVzaW5nIGEgY2hhaW4gZmlsZSAoaGczOFRvSGcxOSkNCi0gYCRESVIvb3V0cHV0LkJFRGAgaXMgdGhlIHJlc3VsdCBhZnRlciB0aGUgZ2Vub21lIGJ1aWxkIGNvbnZlcnNpb24gKHN0b3JlcyBzdWNjZXNzZnVsbHkgY29udmVydGVkIGxvY2kgY29vcmRpbmF0ZXMgaW4gYnVpbGQgMzcgZm9ybWF0KQ0KLSBgJERJUi91bmxpZnRlZC5CRURgIGNvbnRhaW5zIGFueSBsb2NpIHRoYXQgY291bGRu4oCZdCBiZSBtYXBwZWQgKHN0b3JlcyBsb2NpIHRoYXQgY291bGRuJ3QgYmUgY29udmVydGVkLCB1c2VmdWwgZm9yIHRyYWNraW5nIG1pc3NpbmcgZGF0YSkNCiAgICANCiMjIyBVcGRhdGUgdGhlIFBMSU5LIGZpbGUgd2l0aCBuZXcgZ2Vub21lIGJ1aWxkIGNvb3JkaW5hdGVzOg0KYGBge2Jhc2h9DQokUEwvcGxpbmsgLS1ub3dlYiANCiAgLS1hbGxvdy1uby1zZXggDQogIC0tYmZpbGUgJFdEL1Byb2plY3RfZmluYWwgDQogIC0tdXBkYXRlLW1hcCA8KGF3ayAne2dzdWIoImNociIsIiIsJDEpOyBwcmludCAkNCwkMX0nICRESVIvb3V0cHV0LkJFRCkgDQogIC0tdXBkYXRlLWNociAtLW1ha2UtYmVkIC0tb3V0ICRESVIvUHJvamVjdF9maW5hbF9iMzcNCmBgYA0KDQotIGAtLW5vd2ViYDogU3VwcHJlc3Mgd2ViLWJhc2VkIG5vdGlmaWNhdGlvbnMgb3IgdXBkYXRlcyAobm8gbG9uZ2VyIG5lZWQgZm9yIG5ld2VyIHZlcnNpb24pDQotIGAtLWFsbG93LW5vLXNleGA6IEFsbG93cyBQTElOSyB0byBwcm9jZXNzIGZpbGVzIHdoZXJlIHNvbWUgaW5kaXZpZHVhbHMgaGF2ZSB1bmRlZmluZWQgc2V4IChpbmRpY2F0ZWQgYnkgMCBpbiB0aGUgYC5mYW1gIGZpbGUpLiBXaXRob3V0IHRoaXMgZmxhZywgUExJTksgbWF5IHRlcm1pbmF0ZSB3aXRoIGFuZXJyb3IgaWYgaXQgZW5jb3VudGVycyBpbmRpdmlkdWFsIHdpdGggdW5kZWZpbmVkIHNleA0KLSBgLS1iZmlsZSAkV0QvUHJvamVjdF9maW5hbGA6IFNwZWNpZmllcyB0aGUgYmFzZSBmaWxlIGluIFBMSU5LIGJpbmFyeSBmb3JtYXQgKC5iZWQvLmJpbS8uZmFtKQ0KLSBgLS11cGRhdGUtbWFwIDwoLi4uKWA6IFVwZGF0ZXMgdGhlIFNOUCBwb3NpdGlvbiBjb29yZGluYXRlcyBpbiB0aGUgLmJpbSBmaWxlIHVzaW5nOg0KICAgIC0gYGdzdWIoImNociIsIiIsJDEpYDogUmVtb3ZlcyAiY2hyIiBmcm9tIHRoZSBjaHJvbW9zb21lIGNvbHVtbiBpbiBgb3V0cHV0LkJFRGANCiAgICAtIGBwcmludCAkNCwkMWA6IE91dHB1dHMgU05QIHBvc2l0aW9uIHdpdGggY2hyb21vc29tZSBudW1iZXIsIGZvcm1hdHRlZCBmb3IgUExJTksncyBgLS11cGRhdGUtbWFwYCBvcHRpb24NCi0gYC0tdXBkYXRlLWNocmA6IFVwZGF0ZXMgY2hyb21vc29tZSBudW1iZXJzIGFzIHdlbGwgKHJlcXVpcmVkIHNpbmNlIHBvc2l0aW9uIHVwZGF0ZXMgYWxvbmUgbWF5IG5vdCBhbGlnbiBjaHJvbW9zb21lcykNCi0gYC0tbWFrZS1iZWRgOiBTYXZlcyB0aGUgcmVzdWx0IGluIFBMSU5LIGJpbmFyeSBmb3JtYXQNCi0gYCRESVIvUHJvamVjdF9maW5hbF9iMzdgOiBTcGVjaWZpZXMgdGhlIG91dHB1dCBmaWxlIHByZWZpeCwgbm93IGluIGJ1aWxkIDM3DQoNCiMjIyBSZWZpbmUgUExJTksgZmlsZSB3aXRoIGFkZGl0aW9uYWwgcG9zaXRpb24gdXBkYXRlcyAoaWYgbmVjZXNzYXJ5KQ0KYGBge2Jhc2h9DQokUEwvcGxpbmsgDQogIC0tbm93ZWIgDQogIC0tYWxsb3ctbm8tc2V4IA0KICAtLWJmaWxlICRESVIvUHJvamVjdF9maW5hbF9iMzcgDQogIC0tdXBkYXRlLW1hcCA8KGF3ayAne3ByaW50ICQ0LCQzfScgJFdEL291dHB1dC5CRUQpIA0KICAtLW1ha2UtYmVkIC0tb3V0ICRESVIvUHJvamVjdF9maW5hbF9iMzdfYjM3DQpgYGANCg0KLSBgLS1ub3dlYmA6IFN1cHByZXNzIHdlYi1iYXNlZCBub3RpZmljYXRpb25zIG9yIHVwZGF0ZXMgKG5vIGxvbmdlciBuZWVkIGZvciBuZXdlciB2ZXJzaW9uKQ0KLSBgLS1hbGxvdy1uby1zZXhgOiBBbGxvd3MgUExJTksgdG8gcHJvY2VzcyBmaWxlcyB3aGVyZSBzb21lIGluZGl2aWR1YWxzIGhhdmUgdW5kZWZpbmVkIHNleCAoaW5kaWNhdGVkIGJ5IDAgaW4gdGhlIGAuZmFtYCBmaWxlKS4gV2l0aG91dCB0aGlzIGZsYWcsIFBMSU5LIG1heSB0ZXJtaW5hdGUgd2l0aCBhbmVycm9yIGlmIGl0IGVuY291bnRlcnMgaW5kaXZpZHVhbCB3aXRoIHVuZGVmaW5lZCBzZXgNCi0gYC0tYmZpbGUgJERJUi9Qcm9qZWN0X2ZpbmFsX2IzN2A6IFVzZXMgYW4gYWxyZWFkeSBjb252ZXJ0ZWQgUExJTksgZmlsZSBmcm9tIFN0ZXAgMw0KLSBgLS11cGRhdGUtbWFwIDwoYXdrICd7cHJpbnQgJDQsJDN9JyAkV0Qvb3V0cHV0LkJFRClgOiBVcGRhdGVzIHRoZSBwb3NpdGlvbnMgdXNpbmc6DQogICAgLSBgJDRgOiBUaGUgb3JpZ2luYWwgU05QIHBvc2l0aW9uDQogICAgLSBgJDNgOiBUaGUgbmV3bHkgbWFwcGVkIHBvc2l0aW9uIGZyb20gYnVpbGQgMzcNCi0gYC0tbWFrZS1iZWRgOiBTYXZlcyB0aGUgb3V0cHV0IGluIGJpbmFyeSBQTElOSyBmb3JtYXQNCi0gYCRESVIvUHJvamVjdF9maW5hbF9iMzdfYjM3YDogU3BlY2lmaWVzIHRoZSBmaW5hbCBvdXRwdXQgZmlsZSBwcmVmaXgNCg0KIyMgYi4gRm93YXJkIHN0cmFpbjogDQpgYGB7YmFzaH0NCiMhL2Jpbi9zaA0KDQpjZCAvaG9tZS93L3d4dS9jYWxjaXVtL0hhbWlsdG9uL1JvYl9oYW1pbHRvbl9jb25zb3J0aXVtX2RhdGEvUG9zdF9RQw0KDQojIHNraXBwZWQgdGhpcyBwYXJ0IHNpbmNlIEkgZG8gbm90IGhhdmUgdGhlIG1hbmlmZXN0IGZvciB0aGlzIHBhcnRpY3VsYXIgY2hpcC4NCg0KIyBBY2NvcmRpbmcgdG8gdGhlIHJlY29ycyB0aGUgb3JpZ2luYWwgY2hpcCBpcyBpbiBwbGF0Zm9ybSBIdW1hbk9tbmlFeHByZXNzRXhvbWUtOHYxIHdpdGggNTg1LDM4NCBtYXJrZXJzIChub25lIG9mIHRoZSBtYW5pZmVzdHMgYXZhaWxhYmxlIGF0IGlsbHVtaW5hIG1hdGNoIHRoaXMpDQoNCg0KZ3VuemlwIC1jIEh1bWFuT21uaUV4cHJlc3NFeG9tZS04LXYxLTItQi5jc3YuZ3ogfCB3YyAtbCAjOTY0MjI1DQoNCiNOdW1iZXIgb2YgbWFya2VycyBpbiBjaGlwIChsb2NpIGNvdW50KToNCmd1bnppcCAtYyBIdW1hbk9tbmlFeHByZXNzRXhvbWUtOC12MS0yLUIuY3N2Lmd6IHwgaGVhZCAjOTY0MTkzDQoNCiMgOTY0MjAyPTk2NDE5Mys4KzENCmd1bnppcCAtYyBIdW1hbk9tbmlFeHByZXNzRXhvbWUtOC12MS0yLUIuY3N2Lmd6IHwgYXdrIC1GIiwiICcoTlI+NyAmJiBOUjw5NjQyMDIpe3ByaW50fScgfCBoZWFkDQoNCmd1bnppcCAtYyBIdW1hbk9tbmlFeHByZXNzRXhvbWUtOC12MS0yLUIuY3N2Lmd6IHwgYXdrIC1GIiwiICcoTlI+OCAmJiBOUjw5NjQyMDIgJiYgJDIxPT0iLSIpe3ByaW50ICQyfScgPiBDTzIwX25lZ3N0cmFuZC50eHQNCg0KDQpQTD0iL2hvbWUvdy93eHUvb2VzcGluZ2Evc29mdHdhcmUvcGxpbmsvcGxpbmstMS4wNy14ODZfNjQiDQpGSUxFX0RJUj0iJEhPTUUvSHVtYW5PbW5pRXhwcmVzc0V4b21lXzIwMTUwNiINCg0KJFBML3BsaW5rIC0tbm93ZWIgDQotLWJmaWxlICRGSUxFX0RJUi9DTzIwX0N1dG9mZl9UT1BfMjAxNTA0MDlfRmluYWxSZXBvcnRfUUMgDQotLWZsaXAgQ08yMF9uZWdzdHJhbmQudHh0IA0KLS1tYWtlLWJlZCAtLW91dCAkRklMRV9ESVIvQ08yMF9DdXRvZmZfVE9QXzIwMTUwNDA5X0ZpbmFsUmVwb3J0X1FDX0ZXRA0KYGBgDQoNCg0KIyBTdGVwIDE6IFByZXBhcmF0aW9uIG9mIHRoZSBmaWxlczogDQojIyBhLiBTdWJtaXQgU2NyaXB0OiANCi0gUHJvZ3JhbSB1c2VkOiBQTElOSywgZ3Rvb2xzIChjb252ZXJ0IHRoZSBJTVBVVEUyIGZvcm1hdCBpbnRvIFBFRC9NQVAgZm9ybWF0KSBhbmQgU0hBUEVJVA0KDQojIyMgU2V0IHVwIHBhdGhzIGZvciB0b29scyBhbmQgZGlyZWN0b3JpZXM6IA0KYGBge2Jhc2h9DQojISAvYmluL2Jhc2gNCg0KZXhwb3J0IFBMPSAvcGF0aC90by9QTElOSw0KZXhwb3J0IEdUTD0gL3BhdGgvdG8vZ3Rvb2xzDQpleHBvcnQgU0hQPSAvcGF0aC90by9TaGFwaXQNCg0KIyBQb3NzaWJsZSBjaGFuZ2VzIHRvIGxvY2F0aW9uczoNCiNGSUxFX0RJUiwgTE9HX0QsIEdUT09MX0ZESVIsIFNDUklQVF9ESVIsIEZJTEVfTkFNRQ0KIyBOYXZpZ2F0ZSB0byB0aGUgZGlyZWN0b3J5IHdoZXJlIHRoZSBzZXR1cCBmaWxlcyBhcmUgc3RvcmVkDQoNCmV4cG9ydCBMT0NBTF9ESVI9IC9wYXRoL3RvL2xvY2FsL2ZpbGUNCmV4cG9ydCBGSUxFX0RJUj0gL3BhdGgvdG8vcG9zdFFDIGZpbGUNCg0KY2QgJExPQ0FMX0RJUiANCg0KIyBTZXQgdGhlIGxvZyBkaXJlY3RvcnkgZm9yIGFsbCBvdXRwdXQgYW5kIGVycm9yIGZpbGVzIGRvd25zdHJlYW0gDQpMT0dfRD1sb2dzDQpIQVBTX0ZESVI9aGFwbG90eXBlcw0KDQpta2RpciAtcCAkTE9HX0QNCm1rZGlyIC1wICRIQVBTX0ZESVINCg0KZXhwb3J0IExPR19ESVI9JHtMT0NBTF9ESVJ9LyR7TE9HX0R9DQpleHBvcnQgT1VUUFVUX0RJUj0ke0xPQ0FMX0RJUn0vJHtIQVBTX0ZESVJ9DQpleHBvcnQgU0NSSVBUX0RJUj0gL3BhdGgvdG8vdGhlL2NvZGUNCmV4cG9ydCBGSUxFX05BTUU9Um9iX0hhbWlsdG9uX2ZpbmFsICNUaGUgZmlsdGVyZWQgZmlsZSAoY2xlYW5lZCArIGZvcndhcmQgYWxpZ25lZCkNCg0KZXhwb3J0IFJFRkVSRU5DRV9QQVRIPSAvcGF0aC90by9yZWZlcmVuY2UgcGFuZWwgMTAwMCBnZW5vbWVzDQpleHBvcnQgR01BUF9ESVI9IC9wYXRoL3RvL0dNQUdNQVANCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBJdGVyYXRlIG92ZXIgYWxsIGNocm9tb3NvbWVzIGFuZCBzdWJtaXQgam9icyB0aHJvdWdoIHRoZSBzY3JpcHQgZmlsZTogMWFfcnVuX2d0b29sX0NPMjAuc2gNCg0KZXhwb3J0IFNUQVJUX0NIUj0xDQpleHBvcnQgRU5EX0NIUj0yMg0KYGBgDQoNCiMjIyBTdWJtaXQgc2NyaXB0IGZvciBqb2IgdXNpbmcgYHFzdWJgOiANCmBgYHtiYXNofQ0KZWNobyAicHJlcGFyZSBmaWxlcyBmb3Igc2hhcGVpdCINCkpPQl9OQU1FPSAvam9iX25hbWUNCnFzdWIgLW8gJExPR19ESVIvJHtKT0JfTkFNRX0ub3V0IFwNCi1lICRMT0dfRElSLyR7Sk9CX05BTUV9LmVyciBcDQotdiBTVEFSVF9DSFIsRU5EX0NIUixMT0NBTF9ESVIsRklMRV9ESVIsTE9HX0RJUixGSUxFX05BTUUsT1VUUFVUX0RJUixSRUZFUkVOQ0VfUEFUSCxHTUFQX0RJUixQTCxHVEwsU0hQIFwNCi1xIGRlYnVnIFwNCi1OICRKT0JfTkFNRSBcDQokU0NSSVBUX0RJUi8xYV9ydW5fcHJlcGFyZWZpbGVzLnNoDQoNCiMgTm90ZQ0KIyBxc3ViIHN1Ym1pdCBhIGJhdGNoIGpvYiB0byBHcmlkIEVuZ2luZSANCiMgLU4gdGhlIG5hbWUgb2YgdGhlIGpvYiANCiMgLW8gdGhlIHBhdGggdXNlZCBmb3IgdGhlIHN0YW5kYXJkIG91dHB1dCBzdHJlYW0gb2YgdGhlIGpvYiAoZGVmYXVsdCBqb2JfbmFtZS5vam9iX2lkIGZvcm1hdCkNCiMgLWUgdGhlIHBhdGggdXNlZCBmb3IgdGhlIHN0YW5kYXJkIGVycm9yIHN0cmVhbSBvZiB0aGUgam9iIChkZWZhdWwgam9iX25hbWUuZWpvYl9pZCBmb3JtYXQpDQpgYGANCg0KLSBgSk9CX05BTUVgOiBTcGVjaWZpZXMgYSBuYW1lIGZvciB0aGUgam9iIHN1Ym1pc3Npb24NCi0gYHFzdWJgIGNvbW1hbmQ6IFN1Ym1pdHMgdGhlIGpvYiB1c2luZyB0aGUgYHFzdWJgIGNvbW1hbmQgaW4gYSBiYXRjaCBlbnZpcm9ubWVudCAodHlwaWNhbGx5IHVzZWQgaW4gaGlnaC1wZXJmb3JtYW5jZSBjb21wdXRpbmcgZW52aXJvbm1lbnRzKQ0KICAgIC0gYC1vICRMT0dfRElSLyR7Sk9CX05BTUV9Lm91dGA6IFNldHMgdGhlIG91dHB1dCBsb2cgZmlsZSBwYXRoDQogICAgLSBgLWUgJExPR19ESVIvJHtKT0JfTkFNRX0uZXJyYDogU2V0cyB0aGUgZXJyb3IgbG9nIGZpbGUgcGF0aA0KICAgIC0gYC12YDogUGFzc2VzIGEgbGlzdCBvZiB2YXJpYWJsZXMgdG8gdGhlIGpvYiwgaW5jbHVkaW5nIGNocm9tb3NvbWUgcmFuZ2UgKGBTVEFSVF9DSFJgIHRvIGBFTkRfQ0hSYCksIGZpbGUgZGlyZWN0b3JpZXMsIHRvb2wgcGF0aHMsIGFuZCByZWZlcmVuY2UgcGF0aHMNCiAgICAtIGAtcSBkZWJ1Z2A6IFNwZWNpZmllcyB0aGUgcXVldWUgZm9yIHRoZSBqb2IgKGluIHRoaXMgY2FzZSwgdGhlIGRlYnVnIHF1ZXVlLCB0eXBpY2FsbHkgdXNlZCBmb3IgcXVpY2sgb3IgdGVzdCBydW5zKQ0KICAgIC0gYC1OICRKT0JfTkFNRWA6IFNldHMgdGhlIGpvYiBuYW1lDQogICAgLSBgJFNDUklQVF9ESVIvMWFfcnVuX3ByZXBhcmVmaWxlcy5zaGA6IENhbGxzIHRoZSBzcGVjaWZpYyBzY3JpcHQgKGAxYV9ydW5fcHJlcGFyZWZpbGVzLnNoYCkgdG8gcHJvY2VzcyB0aGUgZmlsZXMNCg0KIyMgYi4gUnVuIFNjcmlwdDogDQojIyMgU2NyaXB0IGhlYWRlciBhbmQgcmVzb3VyY2UgYWxsb2NhdGlvbg0KYGBge2Jhc2h9DQojIS9iaW4vYmFzaA0KI1BCUyAtbCBub2Rlcz0xOnBwbj04DQojUEJTIC1sIHdhbGx0aW1lPTE6MDA6MDANCmBgYA0KDQotIGAjIS9iaW4vYmFzaGA6IGluZGljYXRlcyB0aGUgc2NyaXB0IHNob3VkbCBydW4gaW4gdGhlIEJhc2ggc2hlbGwNCi0gYCNQQlMgLWwgbm9kZXM9MTpwcG49OGA6IHNwZWNpZmllcyB0byB0aGUgam9iIHNjaGVkdWxlciB0byB1c2UgMSBub2RlIHdpdGggOCBwcm9jZXNzb3JzIHBlciBub2RlDQotIGAjUEJTIC1sIHdhbGx0aW1lPTE6MDA6MDBgOiBTZXRzIGEgbWF4aW11bSBydW4gdGltZSBvZiAxIGhvdXINCg0KIyMjIExvYWRpbmcgTW9kdWxlcyBhbmQgc2V0dGluZyB1cCBEaXJlY3RvcmllczogDQpgYGB7YmFzaH0NCm1vZHVsZSBsb2FkIGdudS1wYXJhbGxlbA0KDQplY2hvIExvY2FsIERpcmVjdG9yeTogJExPQ0FMX0RJUg0KZWNobyBGaWxlIERpcmVjdG9yeTogJEZJTEVfRElSDQplY2hvIEZpbGUgTmFtZTogJEZJTEVfTkFNRQ0KZWNobyBPdXRwdXQgRGlyZWN0b3J5OiAkT1VUUFVUX0RJUg0KZWNobyBSZWZlcmVuY2UgRGlyZWN0b3J5OiAkUkVGRVJFTkNFX1BBVEgNCmNkICRPVVRQVVRfRElSDQoNCmVjaG8gU3RhcnRpbmcgRGlyZWN0b3J5OiAkT1VUUFVUX0RJUg0KYGBgDQoNCi0gYG1vZHVsZSBsb2FkIGdudS1wYXJhbGxlbGA6IGxvYWRzIEdOVSBwYXJhbGxlbCwgd2hpY2ggYWxsb3dzIGZvciBwYXJhbGxlbCBleGVjdXRpb24gb2YgY29tbWFuZHMNCi0gYGVjaG9gOiBUaGUgc2NyaXB0IHRoZW4gcHJpbnRzIG91dCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdG8gY29uZmlybSB0aGVpciB2YWx1ZXMNCg0KIyMjIEZ1bmN0aW9uIHRvIHNwbGl0IGZpbGUgYnkgY2hyb21vc29tZToNCmBgYHtiYXNofQ0KZnVuY3Rpb24gUExJTktfY2hyIHsNCiAgICBDSFI9JDENCiAgICAkUEwvcGxpbmsgDQogICAgLS1ub3dlYiANCiAgICAtLWJmaWxlICRPVVRQVVRfRElSLyR7RklMRV9OQU1FfV9ub2R1cHBvcyANCiAgICAtLWNociAke0NIUn0gDQogICAgLS1tYWtlLWJlZCAtLXJlY29kZSAtLW91dCAkT1VUUFVUX0RJUi8ke0ZJTEVfTkFNRX1fY2hyJHtDSFJ9DQp9DQpleHBvcnQgLWYgUExJTktfY2hyDQpgYGANCg0KLSBEZWZpbmUgdGhlIGZ1bmN0aW9uIGBQTElOS19jaHJgIHRvIHNlcGFyYXRlIGdlbmV0aWMgZGF0YSBieSBjaHJvbW9zb21lIChgY2hyYCkgYW5kIGNvbnZlcnQgaXQgdG8gUExJTksgYmluYXJ5IGZvcm1hdCAoYC0tbWFrZS1iZWRgKS4NCi0gYGV4cG9ydCAtZiBQTElOS19jaHJgOiBFeHBvcnRzIHRoZSBmdW5jdGlvbiBzbyBpdCBjYW4gYmUgdXNlZC4NCg0KIyMjIFJlbW92ZSBkdXBsaWNhdGVkIFBvc2l0aW9uczogDQpgYGB7YmFzaH0NCmVjaG8gIlJlbW92ZSBkdXBsaWNhdGVkIHBvc2l0aW9ucyINCmBgYA0KKioxLiBJZGVudGlmeSBkdXBsaWNhdGVzIGJ5IENocm9tb3NvbWUgUG9zaXRpb246KioNCmBgYHtiYXNofQ0KI2R1cGxpY2F0ZWQgcG9zaXRpb25zDQphd2sgJ3twcmludCAkMSI6IiQ0fScgJEZJTEVfRElSLyRGSUxFX05BTUUuYmltIHwgc29ydCB8IHVuaXEgLWMgfCBhd2sgJyQxPjF7cHJpbnQgJDJ9JyA+IGR1cHBvcy50eHQNCmBgYA0KLSBFeHRyYWN0cyBhbmQgY291bnRzIGNocm9tb3NvbWUgcG9zaXRpb25zIGluIHRoZSBgLmJpbWAgZmlsZSwgc2F2aW5nIGR1cGxpY2F0ZXMgdG8gYGR1cHBvcy50eHRgDQoNCioqMi4gU2VwYXJhdGUgRHVwbGljYXRlcyBieSBTTlAgSUQgUHJlZml4IChycyBvciBub24tcnMpOioqDQpgYGB7YmFzaH0NCiNkdXBsaWNhdGVkIHBvc2l0aW9ucyB3aXRoIHNucCBpZCBub3Qgc3RhcnRpbmcgd2l0aCBycw0Kam9pbiBkdXBwb3MudHh0IDwoYXdrICd7cHJpbnQgJDEiOiIkNCwkMn0nICRGSUxFX0RJUi8kRklMRV9OQU1FLmJpbSB8IHNvcnQgLWsgMSkgfCBhd2sgJ3twcmludCAkMiwkMX0nIHwgZ3JlcCAtdiAtZSAnXnJzJyA+IGR1cHBvc193b19ycy50eHQNCg0KI2R1cGxpY2F0ZWQgcG9zaXRpb25zIHdpdGggc25wIGlkIHN0YXJ0aW5nIHdpdGggcnMNCmpvaW4gZHVwcG9zLnR4dCA8KGF3ayAne3ByaW50ICQxIjoiJDQsJDJ9JyAkRklMRV9ESVIvJEZJTEVfTkFNRS5iaW0gfCBzb3J0IC1rIDEpIHwgYXdrICd7cHJpbnQgJDIsJDF9JyB8IGdyZXAgLWUgJ15ycycgPiBkdXBwb3Nfd19ycy50eHQNCmBgYA0KLSBTYXZlIGR1cGxpY2F0ZSBwb3NpdGlvbnMgd2l0aG91dCB0aGUgYHJzYCBwcmVmaXggdG8gYGR1cHBvc193b19ycy50eHRgDQotIFNhdmUgZHVwbGljYXRlIHBvc2l0aW9ucyB3aXRoIHRoZSBgcnNgIHByZWZpeCB0byBgZHVwcG9zX3dfcnMudHh0YA0KICAgICAgICANCioqMy4gRmlsdGVyIG91dCBkdXBsaWNhdGUgU05QcyBieSBjZXJ0YWluIGNyaXRlcmlhOioqDQpgYGB7YmFzaH0NCiMjIHJlbW92ZSBzbnBzIHdpdGggZHVwbGljYXRlZCBwb3NpdGlvbnMgYW5kIHRoZSBkdXBsaWNhdGVkIG9uZXMgaGF2ZSBycyBpZA0Kam9pbiAtMiAyIDwoam9pbiAtMiAyIGR1cHBvcy50eHQgZHVwcG9zX3dfcnMudHh0IHwgYXdrICchc2VlblskMV0rKycpIGR1cHBvc193b19ycy50eHQgfCBhd2sgJ3twcmludCAkM30nIHwgdW5pcSA+IGR1cHBvc19zbnBpZHNfdG9yZW1vdmUudHh0DQoNCiMjIHJlbW92ZSBzbnBzIHdpdGggZHVwbGljYXRlZCBwb3NpdGlvbnMgYW5kIGFyZSBub3QgZHVwbGljYXRlZCB3aXRoIFNOUHMgdGhhdCBoYXZlIHJzIGlkDQpqb2luIC0yIDIgPChqb2luIC12MSAtMiAyIGR1cHBvcy50eHQgZHVwcG9zX3dfcnMudHh0KSBkdXBwb3Nfd29fcnMudHh0IHwgYXdrICdzZWVuWyQxXSsre3ByaW50ICQyfScgPj4gZHVwcG9zX3NucGlkc190b3JlbW92ZS50eHQNCg0KI3RoZSBjb21wbGVtZW50IG9mIGFib3ZlIGluIGNhc2UgZXZlciBuZWVkZWQNCiNqb2luIC0yIDIgPChqb2luIC12MSAtMiAyIGR1cHBvcy50eHQgZHVwcG9zX3dfcnMudHh0KSBkdXBwb3Nfd29fcnMudHh0IHwgYXdrICchc2VlblskMV0rKycgfCB3YyAtbA0KYGBgDQotIGBkdXBwb3Nfc25waWRzX3RvcmVtb3ZlLnR4dGAgYWNjdW11bGF0ZXMgSURzIHRvIGV4Y2x1ZGUgZnJvbSBmdXJ0aGVyIHByb2Nlc3NpbmcuIEhlcmUsIG5vbi1gcnNgIGR1cGxpY2F0ZXMgYXJlIHByaW9yaXRpemVkIGZvciByZW1vdmFsLg0KDQoqKjQuIFJlbW92YWwgYWRkaXRpb25hbCBkdXBsaWNhdHMgaWYgbmVjZXNzYXJ5OioqDQpgYGB7YmFzaH0NCiNpbiBjYXNlIGR1cGxpY2F0ZWQgcG9zaXRpb25zIGV4aXN0IHdpdGhpbiB0aGUgc25wIGlkcyBzdGFydGluZyB3aXRoIHJzDQpleHRyYV9kdXBzPWBhd2sgJ3twcmludCAkMn0nIGR1cHBvc193X3JzLnR4dCB8IHVuaXEgLWMgfCBhd2sgJyQxPjF7cHJpbnQgJDJ9JyB8IHdjIC1sYA0KaWYgWyAkZXh0cmFfZHVwcyAtZ3QgMCBdDQp0aGVuDQojI2lmIG1vcmUgdGhhbiBvbmUgZHVwbGljYXRlZCBwb3NpdGlvbiBhcHBlYXIgaGVyZSwga2VlcCBvbmUNCmpvaW4gPChhd2sgJ3twcmludCAkMn0nIGR1cHBvc193X3JzLnR4dCB8IHVuaXEgLWMgfCBhd2sgJyQxPjF7cHJpbnQgJDJ9JyB8IHNvcnQpIDwoYXdrICd7cHJpbnQgJDEiOiIkNCwkMn0nICRGSUxFX0RJUi8kRklMRV9OQU1FLmJpbSB8IHNvcnQgLWsgMSkgfCBhd2sgJ3twcmludCAkMiwkMX0nIHwgZ3JlcCAtZSAnXnJzJyB8IGF3ayAnc2VlblskMl0rK3twcmludCAkMX0nID4+IGR1cHBvc19zbnBpZHNfdG9yZW1vdmUudHh0DQpmaQ0KYGBgDQotIENoZWNrcyBpZiBtb3JlIHRoYW4gb25lIGR1cGxpY2F0ZSBTTlAgaGFzIGFuIGByc2AgcHJlZml4IChgZXh0cmFfZHVwc2ApLCBrZWVwaW5nIG9uZSBpbnN0YW5jZSBhbmQgcmVtb3Zpbmcgb3RoZXJzLg0KICAgIA0KDQojIyMgUmVtb3ZpbmcgRHVwbGljYXRlcyBpbiBQTElOSw0KYGBge2Jhc2h9DQokUEwvcGxpbmsgLS1ub3dlYiAtLWJmaWxlICRGSUxFX0RJUi8kRklMRV9OQU1FIC0tZXhjbHVkZSBkdXBwb3Nfc25waWRzX3RvcmVtb3ZlLnR4dCAtLW1ha2UtYmVkIC0tb3V0ICRPVVRQVVRfRElSLyR7RklMRV9OQU1FfV9ub2R1cHBvcw0KDQokUEwvcGxpbmsgLS1ub3dlYiAtLWJmaWxlICRPVVRQVVRfRElSLyR7RklMRV9OQU1FfV9ub2R1cHBvcyAtLXVwZGF0ZS1pZHMgPChhd2sgJ3twcmludCAkMSwkMiwkMSJfIiQyLCQxIl8iJDJ9JyAkT1VUUFVUX0RJUi8ke0ZJTEVfTkFNRX0uZmFtKSAtLW1ha2UtYmVkIC0tb3V0ICRPVVRQVVRfRElSLyR7RklMRV9OQU1FfV9ub2R1cHBvcw0KYGBgDQoNCi0gVXNlcyBQTElOSyB0byBleGNsdWRlIFNOUHMgbGlzdGVkIGluIGBkdXBwb3Nfc25waWRzX3RvcmVtb3ZlLnR4dGAgc2F2aW5nIHRoZSBmaWx0ZXJlZCBkYXRhIHRvIGAke0ZJTEVfTkFNRX1fbm9kdXBwb3NgDQoNCiMjIyBSdW4gUExJTksgZm9yIENocm9tb3NvbWUtU3BlY2lmaWMgRmlsZXMNCmBgYHtiYXNofQ0KZWNobyAiUnVuIFBMSU5LIHRvIHNlcGFyYXRlIGZpbGVzIHBlciBjaHJvbW9zb21lcyINCg0Kc2VxICRTVEFSVF9DSFIgJEVORF9DSFIgfCBwYXJhbGxlbCAtaiA4IC0tam9ibG9nICRMT0dfRElSLzFfcHJlcGhhc2VfcGxpbmtfZHVwcy5sam9iIFBMSU5LX2NociB7fQ0KYGBgDQoNCi0gUnVucyB0aGUgYFBMSU5LX2NocmAgZnVuY3Rpb24gaW4gcGFyYWxsZWwgb3ZlciBhIHJhbmdlIGlmIGNocm9tb3NvbWVzIGZyb20gYCRTVEFSVF9DSFJgIHRvIGAkRU5EX0NIUmAsIHVzaW5nIHVwIHRvIDggam9icyBjb25jdXJyZW50bHkuDQoNCiMjIyBTdHJhbmQgQWxpZ25tZW50IENoZWNrIHdpdGggU2hhcGVJdA0KYGBge2Jhc2h9DQpmdW5jdGlvbiBTdHJhbmRfYWxpZyB7DQogICAgQ0hSPSQxDQoNCiAgICAkU0hQL3NoYXBlaXQgLWNoZWNrIC1CICRPVVRQVVRfRElSLyR7RklMRV9OQU1FfV9jaHIke0NIUn0gLS1pbnB1dC1yZWYgJFJFRkVSRU5DRV9QQVRILzEwMDBHUF9QaGFzZTNfY2hyJHtDSFJ9LmhhcC5neiAkUkVGRVJFTkNFX1BBVEgvMTAwMEdQX1BoYXNlM19jaHIke0NIUn0ubGVnZW5kLmd6ICRSRUZFUkVOQ0VfUEFUSC8xMDAwR1BfUGhhc2UzLnNhbXBsZSAtLW91dHB1dC1sb2cgJE9VVFBVVF9ESVIvY2hyJHtDSFJ9LmFsaWdubWVudHMNCg0KICAgIG5zdHJhbmQ9YGF3ayAnJDE9PSJTdHJhbmQiICYmICQ3PT0xe3ByaW50ICQ0fScgJE9VVFBVVF9ESVIvY2hyJHtDSFJ9LmFsaWdubWVudHMuc25wLnN0cmFuZCB8IHdjIC1sYA0KDQogICAgaWYgWyAkbnN0cmFuZCAtZ3QgMCBdDQogICAgICAgIHRoZW4NCiAgICAgICAgJFBML3BsaW5rIC0tbm93ZWIgLS1iZmlsZSAkT1VUUFVUX0RJUi8ke0ZJTEVfTkFNRX1fY2hyJHtDSFJ9IC0tZmxpcCAgPChhd2sgJyQxPT0iU3RyYW5kIiAmJiAkNz09MXtwcmludCAkNH0nICRPVVRQVVRfRElSL2NociR7Q0hSfS5hbGlnbm1lbnRzLnNucC5zdHJhbmQpIC0tbWFrZS1iZWQgLS1vdXQgJE9VVFBVVF9ESVIvJHtGSUxFX05BTUV9X2NociR7Q0hSfQ0KDQogICAgICAgICRTSFAvc2hhcGVpdCAtY2hlY2sgLUIgJE9VVFBVVF9ESVIvJHtGSUxFX05BTUV9X2NociR7Q0hSfSAtLWlucHV0LXJlZiAkUkVGRVJFTkNFX1BBVEgvMTAwMEdQX1BoYXNlM19jaHIke0NIUn0uaGFwLmd6ICRSRUZFUkVOQ0VfUEFUSC8xMDAwR1BfUGhhc2UzX2NociR7Q0hSfS5sZWdlbmQuZ3ogJFJFRkVSRU5DRV9QQVRILzEwMDBHUF9QaGFzZTMuc2FtcGxlIC0tb3V0cHV0LWxvZyAkT1VUUFVUX0RJUi9jaHIke0NIUn0uYWxpZ25tZW50cw0KDQojVGhpcyBtYXkgbm90IGJlIG5lY2Vzc2FyeSBzaW5jZSB0aGUgbWFya2VycyB0aGF0IGFyZSBleGNsdWRlZCBpbiB0aGUgb3JpZ2luYWwgLnN0cmFuZC5leGx1ZGUgZmlsZSBhcmUgcmVtb3ZlZCBsYXRlciBhbnl3YXlzLg0KI2F3ayAnJDE9PSJTdHJhbmQiICYmICQ3PT0xe3ByaW50ICQzfScgJE9VVFBVVF9ESVIvY2hyJHtDSFJ9LmFsaWdubWVudHMuc25wLnN0cmFuZCA+ICRPVVRQVVRfRElSL2NociR7Q0hSfS5hbGlnbm1lbnRzLnNucC5zdHJhbmQuZXhjbHVkZQ0KICAgIGZpDQoNCiAgICBpZiBbICEgLWYgJE9VVFBVVF9ESVIvY2hyJHtDSFJ9LmFsaWdubWVudHMuc25wLnN0cmFuZC5leGNsdWRlIF0NCiAgICAgICAgdGhlbg0KICAgICAgICB0b3VjaCAkT1VUUFVUX0RJUi9jaHIke0NIUn0uYWxpZ25tZW50cy5zbnAuc3RyYW5kLmV4Y2x1ZGUNCiAgICBmaQ0KfQ0KDQpleHBvcnQgLWYgU3RyYW5kX2FsaWcNCmBgYA0KDQotIFRoZSBgU3RyYW5kX2FsaWdgIGZ1bmN0aW9uIGNoZWNrcyBzdHJhbmQgYWxpZ25tZW50IGJ5IGNvbXBhcmluZyB0aGUgY2hyb21vc29tZSBkYXRhIHdpdGggcmVmZXJlbmNlIGRhdGEgZnJvbSAxMDAwIEdlbm9tZXMgUHJvamVjdA0KLSBJZiBTTlBzIGFyZSBmb3VuZCB3aXRoIHN0cmFuZCBtaXNtYXRjaGVzLCB0aGV5IGFyZSBjb3JyZWN0ZWQgYnkgZmxpcHBpbmcgdGhlbSB3aXRoIFBMSU5LDQoNCiMjIyBSdW4gU2hhcGVJdCBpbiBQYXJhbGxlbCBmb3IgU3RyYW5kIEFsaWdubWVudA0KYGBge2Jhc2h9DQplY2hvICJDaGVja2luZyBtaXNzYWxpZ25lZCBhbGxlbGVzIg0Kc2VxICRTVEFSVF9DSFIgJEVORF9DSFIgfCBwYXJhbGxlbCAtaiA4IC0tam9ibG9nICRMT0dfRElSLzFfcHJlcGhhc2Vfc2hhcGVpdF9hbGlnbi5sam9iIFN0cmFuZF9hbGlnIHt9DQoNCmVjaG8gIkZpbmlzaGVkIENoZWNraW5nIG1pc3NhbGlnbmVkIGFsbGVsZXMiDQpgYGANCg0KLSBSdW5zIHRoZSBgU3RyYW5kX2FsaWduYCBmdW5jdGlvbiBmb3IgZWFjaCBjaHJvbW9zb21lIGluIHBhcmFsbGVsIHRvIGNoZWNrIGFuZCBjb3JyZWN0IHN0cmFuZCBhbGlnbm1lbnQgaXNzdWVzIGFjcm9zcyBhbGwgc3BlY2lmaWVkIGNocm9tb3NvbWVzLg0KDQojIFN0ZXAgMjogUHJlLXBoYXNpbmc6DQotICoqUGhhc2luZyoqOiBhbHNvIGtub3duIGFzIGhhcGxvdHlwZSBlc3RpbWF0aW9uLCBpcyBhIHN0YXRpc3RpY2FsIHByb2Nlc3MgdGhhdCBzZXBhcmF0ZXMgdGhlIG1hdGVybmFsbHkgYW5kIHBhdGVybmFsbHkgaW5oZXJpdGVkIGNvcGllcyBvZiBlYWNoIGNocm9tb3NvbWUgdG8gaWRlbnRpZnkgYWxsZWxlcyBvbiBib3RoIGNocm9tb3NvbWVzIA0KIyMgYS4gU3VibWl0IFNjcmlwdDogDQojIyMgU2V0IEVudmlyb25tZW50IHZhcmlhYmxlcw0KYGBge2Jhc2h9DQojIFN0ZXAgMTogU3VibWl0IFByZS1waGFzZQ0KIyBTZXQgZW52aXJvbm1lbnQgdmFyaWFibGVzDQpQTD0vcGF0aC90by9wbGluaw0KR1RMPS9wYXRoL3RvL2d0b29sDQpTSFA9L3BhdGgvdG8vc2hhcGVpdA0KTE9DQUxfRElSPS9wYXRoL3RvL2xvY2FsX2Rpcg0KRklMRV9ESVI9L3BhdGgvdG8vZmlsZV9kaXINCkxPR19EPS9wYXRoL3RvL2xvZ3MNCkhBUFNfRkRJUj0vcGF0aC90by9oYXBsb3R5cGVzDQpgYGANCg0KLSBBc3NpZ25zIHBhdGhzIHRvIGtleSB0b29scyBhbmQgZGlyZWN0b3JpZXMuDQoNCiMjIyBDcmVhdGUgTmVjZXNzYXJ5IERpcmVjdG9yaWVzDQpgYGB7YmFzaH0NCiMgQ3JlYXRlIGRpcmVjdG9yaWVzIGZvciBsb2dzIGFuZCBoYXBsb3R5cGVzDQpta2RpciAtcCAkTE9HX0QNCm1rZGlyIC1wICRIQVBTX0ZESVINCiMgLXAgb3B0aW9uIGVuc3VyZXMgbm8gZXJyb3IgaXMgdGhyb3duIGlmIHRoZSBkaXJlY3RvcnkgYWxyZWFkeSBleGlzdHN0DQpgYGANCg0KLSBDcmVhdGVzIGRpcmVjdG9yaWVzIGZvciBsb2dzIGFuZCBoYXBsb3R5cGVzIGlmIHRoZXkgZG9u4oCZdCBhbHJlYWR5IGV4aXN0IChgLXBgIG9wdGlvbiBwcmV2ZW50cyBlcnJvcnMgaWYgdGhleSBhbHJlYWR5IGRvKS4NCg0KIyMjIExvb3AgdGhyb3VnaCBjaHJvbW9zb21lcyBhbmQgc3VibWl0IGpvYnM6DQpgYGB7YmFzaH0NCiMgTG9vcCB0aHJvdWdoIGNocm9tb3NvbWVzDQpmb3IgQ0hSIGluIHsxLDIsNn07IGRvDQogICAgcXN1YiAtbyAkTE9HX0QvMmFfcnVuX3ByZXBoYXNlX2NociR7Q0hSfS5vdXQgXA0KICAgICAgICAgLWUgJExPR19ELzJhX3J1bl9wcmVwaGFzZV9jaHIke0NIUn0uZXJyIFwNCiAgICAgICAgIC1xIGJhdGNoIC1sIG5vZGVzPTE6cHBuPTgsd2FsbHRpbWU9NTowMDowMCBcDQogICAgICAgICAtTiAyYV9ydW5fcHJlcGhhc2VfY2hyJHtDSFJ9IFwNCiAgICAgICAgIDJhX3J1bl9wcmVwaGFzZS5zaCAkQ0hSDQpkb25lDQpgYGANCg0KLSBTdWJtaXQgam9iIGZvciBlYWNoIGNocm9tb3NvbWU6IA0KICAgIC0gYC1vYCBhbmQgYC1lYCBEaXJlY3RzIG91dHB1dCBhbmQgZXJyb3IgbG9ncyB0byB0aGUgc3BlY2lmaWVkIGxvZyBkaXJlY3RvcnkNCiAgICAtIGAtcSBiYXRjaGAgUnVucyB0aGUgam9iIGluIHRoZSBiYXRjaCBxdWV1ZQ0KICAgIC0gYC1sIG5vZGVzPTE6cHBuPTgsd2FsbHRpbWU9NTowMDowMGAgUmVxdWVzdHMgMSBub2RlLCA4IHByb2Nlc3NvcnMsIGFuZCA1IGhvdXJzIG9mIHJ1bnRpbWUNCiAgICAtIGAtTmAgTmFtZXMgdGhlIGpvYiB3aXRoIGEgY2hyb21vc29tZS1zcGVjaWZpYyBpZGVudGlmaWVyDQogICAgLSBgMmFfcnVuX3ByZXBoYXNlLnNoICRDSFJgIFJ1bnMgdGhlIGAyYV9ydW5fcHJlcGhhc2Uuc2hgIHNjcmlwdCB3aXRoIHRoZSBjaHJvbW9zb21lIGFzIGFuIGFyZ3VtZW50Lg0KDQojIyBiLiBSdW4gU2NyaXB0OiANCiMjIyBTZXQgdXAgZW52aXJvbm1lbnQgdmFyaWFibGU6IA0KYGBge2Jhc2h9DQplY2hvICJVc2luZyBQTElOSyBhdDogJFBMIg0KZWNobyAiVXNpbmcgR1RPT0wgYXQ6ICRHVEwiDQplY2hvICJVc2luZyBTSEFQRUlUIGF0OiAkU0hQIg0KYGBgDQoNCi0gRGlzcGxheXMgdGhlIHBhdGhzIHRvIHRoZSBrZXkgdG9vbHMgbWFraW5nIGl0IGVhc2llciB0byBjb25maXJtIHRoYXQgdGhlIGNvcnJlY3QgcGF0aHMgYXIgc2V0IGZvciB0aGlzIHJ1bi4gDQoNCiMjIyBQcmVwaGFzZSBEYXRhIHVzaW5nIFNoYXBlSXQ6DQpgYGB7YmFzaH0NCiMgUHJlcGhhc2UgZGF0YSB1c2luZyBTSEFQRUlUDQpzaGFwZWl0IC1CICR7RklMRV9ESVJ9L2NociR7Q0hSfSBcDQogICAgICAgIC1NICR7RklMRV9ESVJ9L2dlbmV0aWNfbWFwX2NociR7Q0hSfS50eHQgXA0KICAgICAgICAtTyAke0hBUFNfRkRJUn0vY2hyJHtDSFJ9LmhhcHMgXA0KICAgICAgICAtLWV4Y2x1ZGUtc25wICR7RklMRV9ESVJ9L2V4Y2x1ZGVfc25wLnR4dCBcDQogICAgICAgIC0tdGhyZWFkIDE2DQpgYGANCg0KLSBSdW5zIFNoYXBlSVQgZm9yIGhhcGxvdHlwZSBwaGFzaW5nIG9uIHRoZSBzcGVjaWZpZWQgY2hyb21vc29tZSBgJHtDSFJ9YA0KICAgIC0gYC1CICR7RklMRV9ESVJ9L2NociR7Q0hSfWA6IFNwZWNpZmllcyB0aGUgaW5wdXQgUExJTksgYmluYXJ5IGZpbGUgZm9yIHRoZSBjaHJvbW9zb21lDQogICAgLSBgLU0gJHtGSUxFX0RJUn0vZ2VuZXRpY19tYXBfY2hyJHtDSFJ9LnR4dGA6IFVzZXMgdGhlIGdlbmV0aWMgbWFwIGZpbGUgZm9yIHJlY29tYmluYXRpb24gcmF0ZXMgZm9yIHRoZSBjaHJvbW9zb21lDQogICAgLSBgLU8gJHtIQVBTX0ZESVJ9L2NociR7Q0hSfS5oYXBzYDogU2V0IHRoZSBvdXRwdXQgZmlsZXMgZm9yIGhhcGxvdHlwZQ0KICAgIC0gYC0tZXhjbHVkZS1zbnAgJHtGSUxFX0RJUn0vZXhjbHVkZV9zbnAudHh0YDogRXhjbHVkZXMgc3BlY2lmaWVkIFNOUHMgZnJvbSBwaGFzaW5nIGJhc2VkIG9uIHRoaXMgZmlsZQ0KICAgIC0gYC0tdGhyZWFkIDE2YCBSdW5zIHRoZSBwaGFzaW5nIHByb2Nlc3Mgb24gMTYgdGhyZWFkcyBmb3Igc3BlZWQNCiAgICANCiMjIyBDaGVjayBpZiBgLmhhcHNgIGZpbGUgd2FzIGNyZWF0ZWQ6IA0KYGBge2Jhc2h9DQojIENoZWNrIGlmIHRoZSAuaGFwcyBmaWxlIHdhcyBjcmVhdGVkDQppZiBbWyAtcyAke0hBUFNfRkRJUn0vY2hyJHtDSFJ9LmhhcHMgXV07IHRoZW4NCiAgICBlY2hvICJQcmVwaGFzaW5nIGZvciBjaHJvbW9zb21lICR7Q0hSfSBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5LiINCmVsc2UNCiAgICBlY2hvICJFcnJvcjogUHJlcGhhc2luZyBmb3IgY2hyb21vc29tZSAke0NIUn0gZmFpbGVkLiINCmZpDQpgYGANCg0KLSBWZXJpZmllcyB0aGF0IHRoZSBgLmhhcHNgIGZpbGUgZm9yIHRoZSBjaHJvbW9zb21lIHdhcyBjcmVhdGVkIHN1Y2Nlc3NmdWxseQ0KICAgIC0gYC1zYCBjaGVja3MgaWYgdGhlIGZpbGUgZXhpc3RzIGFuZCBpcyBub24tZW1wdHkgDQogICAgLSBJZiB0aGUgZmlsZXMgZXhpc3RzLCBpdCBwcmludHMgYSBzdWNjZXNzIG1lc3NhZ2UNCiAgICAtIElmIHRoZSBmaWxlIGlzIG1pc3Npbmcgb3IgZW1wdHksIGl0IGRpc3BsYXlzIGFuIGVycm9yIG1lc3NhZ2UgaW5kaWNhdGluZyB0aGF0IHByZXBoYXNpbmcgZmFpbGVkIGZvciB0aGUgY2hyb21vc29tZQ0KDQojIFN0ZXAgMzogSW1wdXRhdGlvbjogDQojIyBhLiBTdWJtaXQgU2NyaXB0OiANCiMjIyBEZWZpbmUgZGlyZWN0b3JpZXMvcGF0aHMvdmFyaWFibGVzOg0KYGBge2Jhc2h9DQojISAvYmluL2Jhc2gNCmV4cG9ydCBJTVAyPSBwYXRoL3RvL0ltcHV0ZTINCg0KI1Bvc3NiaWxlIGNoYW5nZXMgdG8gdGhlIGxvY2F0aW9uczoNCiMgRklMRV9ESVIsIE9VVF9ORVdfRElSLCBTQ1JJUFRfRElSLCBDSFJfRU5EX0ZJTEUsIExPR19ESVIsIEZJTEVfTkFNRQ0KDQojVGhlIGhvbWUgcmVmZXJlbmNlIGRpcmVjdG9yeQ0KZXhwb3J0IExPQ0FMX0RJUj0gcGF0aC90by9sb2NhbF9maWxlDQpjZCAkTE9DQUxfRElSDQoNCiNUaGUgZGlyZWN0b3J5IHdpdGggdGhlIGhhcGxvdHlwZSBmaWxlcw0KZXhwb3J0IEZJTEVfRElSPSBwYXRoL3RvL2xvY2FsX2ZpbGUvaGFwbG90eXBlDQoNCiNUaGUgbG9nIGRpcmVjdG9yeSBmb3IgYWxsIG91dHB1dCBhbmQgZXJyb3IgZmlsZXMgZG93bnN0cmVhbQ0KZXhwb3J0IExPR19ESVI9JExPQ0FMX0RJUi8ibG9ncyINCg0KI0RpcmVjdG9yeSB0byBwdXQgaW1wdXRlZCBmaWxlcw0KT1VUX05FV19ESVI9J2ltcHV0ZWRfZmlsZXNfaGFwJw0KbWtkaXIgLXAgJE9VVF9ORVdfRElSDQpleHBvcnQgT1VUUFVUX0RJUj0kTE9DQUxfRElSLyRPVVRfTkVXX0RJUg0KU0NSSVBUX0RJUj0gcGF0aC90by9zY3JpcHQNCg0KI0ltcHV0YXRpb24gcGFuZWwgcmVmZXJlbmNlIHBhdGhzIChoYXBsb3R5cGUsIGdlbmV0aWMgbWFwIGFuZCBsZWdlbmQgZmlsZXMpDQpleHBvcnQgUkVGRVJFTkNFX1BBVEg9IC9wYXRoL3RvL3JlZmVyZW5jZV8xMDAwX2dlbm9tZSANCiNleHBvcnQgTEVHRU5EX1JFRkVSRU5DRV9QQVRIPSRSRUZFUkVOQ0VfUEFUSC9sZWdlbmRfZmlsZXMNCmV4cG9ydCBMRUdFTkRfUkVGRVJFTkNFX1BBVEg9IiRSRUZFUkVOQ0VfUEFUSCINCkNIUl9FTkRfRklMRT1jaHJfZW5kZmlsZV9oYXAudHh0DQoNCg0KI0ZpbGVuYW1lICh3aXRob3V0IGNocm9tb3NvbWUgc3VmZml4ZXMpIG9mIHRoZSBndG9vbCBvdXRwdXQgZmlsZXMNCmV4cG9ydCBGSUxFX05BTUU9RklMRV9OQU1FX2ZpbmFsDQoNCiNDaHJvbW9zb21lIGVuZCBwb3NpdGlvbnMgZm9yIG1vZGlmeWluZyB0aGUgaW50ZXJ2YWxzDQojIyMgRGVmaW5lIHNvbWUgcGFyYW1ldGVycyBmb3IgbG9vcGluZw0KYGBgDQoNCi0gU2V0cyB0aGUgcGF0aCB0byB0aGUgYEltcHV0ZTJgIHNvZnR3YXJlIHVzZWQgZm9yIGdlbm90eXBlIGltcHV0YXRpb24NCi0gU2V0cyB0aGUgYmFzZSBkaXJlY3RvcnkgcGF0aCBgTE9DQUxfRElSYCBhbmQgbmF2aWdhdGVzIHRvIGl0DQotIFNldHMgcGF0aHMgZm9yIGBGSUxFX0RJUmAsIHdoZXJlIGhhcGxvdHlwZSBmaWxlcyBhcmUgc3RvcmVkLCBhbmQgYExPR19ESVJgLCB3aGVyZSBsb2cgZmlsZXMgd2lsbCBiZSBzYXZlZA0KLSBDcmVhdGVzIHRoZSBgT1VUUFVUX0RJUmAgZm9yIGltcHV0ZWQgZmlsZXMsIG1ha2luZyB0aGUgZGlyZWN0b3J5IGlmIGl0IGRvZXNu4oCZdCBhbHJlYWR5IGV4aXN0DQotIFNldHMgdGhlIHBhdGhzIGZvciB0aGUgYFNDUklQVF9ESVJgIGFuZCBgUkVGRVJFTkNFX1BBVEhgLCB3aGVyZSByZWZlcmVuY2UgZ2Vub21lIGZpbGVzIGFyZSBsb2NhdGVkDQotIFNldHMgdGhlIGZpbGVuYW1lIHByZWZpeCBgRklMRV9OQU1FYCBmb3IgdGhlIG91dHB1dCBmaWxlcyBhbmQgdGhlIHNpemUgb2YgaW50ZXJ2YWxzIChpbiBiYXNlIHBhaXJzKSBmb3Igc3BsaXR0aW5nIGRhdGEgZm9yIGltcHV0YXRpb24NCg0KIyMjIFNldCB0aGUgbG9vcCBvZiBjaHJvbW9zb21lOiANCmBgYHtiYXNofQ0KIyBEZWZpbmUgYW5hbHlzaXMgaW50ZXJ2YWwgaW4ga2IgKGkuZS4gYmFzZSBwYWlycykNCmxldCBpbnRlcnZhbF9zaXplPTUwMDAwMA0KZXhwb3J0IGludGVydmFsX3NpemUNCg0KIyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjaHJvbW9zb21lIChjaHJvbW9zb21lIDIyIGZvciB0ZXN0aW5nIHB1cnBvc2VzKQ0KbGV0IHN0YXJ0X2Nocj0xNiBlbmRfY2hyPTE2DQoNCiMgTnVtYmVyIG9mIHByb2Nlc3NlcyBwZXIgcnVuDQpleHBvcnQgUj04ICM2DQoNCiMgTG9vcCB0aHJvdWdoIGFuZCBzdWJtaXQgYmF0Y2hlcw0KZm9yIGNociBpbiAyIDE2ICNgc2VxICRzdGFydF9jaHIgJGVuZF9jaHJgDQpkbw0KCWV4cG9ydCBjaHINCglleHBvcnQgc3RhcnRfaW50ZXJ2YWw9MQ0KCWxldCBDSFJfTEVOR1RIPWB0YWlsIC1uIDEgPChndW56aXAgLWMgJExFR0VORF9SRUZFUkVOQ0VfUEFUSC8xMDAwR1BfUGhhc2UzX2NociR7Y2hyfS5sZWdlbmQuZ3opIHwgYXdrICd7cHJpbnQgJDJ9J2ANCglsZXQgZW5kX2ludGVydmFsPSIkQ0hSX0xFTkdUSCAvICRpbnRlcnZhbF9zaXplICsgMSINCiAgICBleHBvcnQgZW5kX2ludGVydmFsDQogICAgZWNobyAkTEVHRU5EX1JFRkVSRU5DRV9QQVRILzEwMDBHUF9QaGFzZTNfY2hyJHtjaHJ9LmxlZ2VuZC5neg0KICAgIGVjaG8gJGNociAkQ0hSX0xFTkdUSCAkZW5kX2ludGVydmFsICRpbnRlcnZhbF9zaXplDQogICAgZWNobyAkY2hyICRDSFJfTEVOR1RIICRlbmRfaW50ZXJ2YWwgJGludGVydmFsX3NpemUgPj4gJExPQ0FMX0RJUi8kQ0hSX0VORF9GSUxFDQojZWNobyBjaHI6ICRjaHIgaW50ZXJ2YWwgJGludGVydmFsIG91dCBvZiAkZW5kX2ludGVydmFsICMgcHJpbnRzIHRoZSBjdXJyZW50IGNocm9tb3NvbWUNCmBgYA0KDQotIFNwZWNpZmllcyBjaHJvbW9zb21lcyBmb3IgdGVzdGluZyAoYHN0YXJ0X2Nocj0xNmAsIGBlbmRfY2hyPTE2YCkgYW5kIHRoZSBudW1iZXIgb2YgcHJvY2Vzc2VzIHBlciBydW4gYFJgDQotIExvb3BzIG92ZXIgc3BlY2lmaWMgY2hyb21vc29tZXMgKGBjaHJgIHZhbHVlcyAyIGFuZCAxNiBmb3IgdGVzdGluZykNCi0gQ2FsY3VsYXRlcyBjaHJvbW9zb21lIGxlbmd0aCBgQ0hSX0xFTkdUSGAgdXNpbmcgdGhlIGxhc3QgZW50cnkgaW4gdGhlIGNocm9tb3NvbWUgbGVnZW5kIGZpbGUNCi0gRGl2aWRlcyBjaHJvbW9zb21lIGxlbmd0aCBieSBgaW50ZXJ2YWxfc2l6ZWAgdG8gZ2V0IGBlbmRfaW50ZXJ2YWxgICh0b3RhbCBpbnRlcnZhbHMgbmVlZGVkIGZvciB0aGlzIGNocm9tb3NvbWUpDQotIExvZ3MgY2hyb21vc29tZSBlbmQgcG9zaXRpb25zIGFuZCBpbnRlcnZhbCBkZXRhaWxzIHRvIGBDSFJfRU5EX0ZJTEVgIGZvciB0cmFja2luZw0KDQojIyMgU3VibWl0IEltcHV0YXRpb24gam9iczoNCmBgYHtiYXNofQ0KSk9CX05BTUU9M19pbXB1dGUyX2NociR7Y2hyfQ0KICAgIHFzdWIgLW8gJExPR19ESVIvJEpPQl9OQU1FLm91dCAtZSAkTE9HX0RJUi8kSk9CX05BTUUuZXJyIC1OICRKT0JfTkFNRSBcDQoJLXYgTE9DQUxfRElSLEZJTEVfRElSLE9VVFBVVF9ESVIsTE9HX0RJUixSRUZFUkVOQ0VfUEFUSCxMRUdFTkRfUkVGRVJFTkNFX1BBVEgsY2hyLGludGVydmFsX3NpemUsRklMRV9OQU1FLElNUDIsc3RhcnRfaW50ZXJ2YWwsZW5kX2ludGVydmFsLFIgXA0KICAgIC1xIGJhdGNoIC1sIG5vZGVzPTE6bTMyZzpwcG49OCx3YWxsdGltZT00ODowMDowMCBcDQogICAgJFNDUklQVF9ESVIvM2FfcnVuX2ltcHV0YXRpb24uc2gNCmRvbmUNCg0KI0NoYW5nZSB0aGUgcXVldWUgdG8gd2hpY2ggeW91IGFyZSBzdWJtaXR0aW5nIDogLXEgZGVidWcgLyAtcSBiYXRjaA0KI0NoYW5nZSB0aGUgYW1vdW50IG9mIHJlc291cmNlcyByZXF1ZXN0ZWQgIG5vZGVzPTE6cHBuPTgsd2FsbHRpbWU9MDozMDowMA0KYGBgDQoNCi0gU3VibWl0cyBhIGJhdGNoIGpvYiBmb3IgZWFjaCBjaHJvbW9zb21lIChgcXN1YmAgY29tbWFuZCkgd2l0aCBvdXRwdXQvZXJyb3IgZmlsZXMgc3BlY2lmaWVkIGluIGBMT0dfRElSYA0KLSBKb2IgcGFyYW1ldGVycyBsaWtlIG1lbW9yeSwgcHJvY2Vzc29ycywgYW5kIHdhbGwgdGltZSBhcmUgZGVmaW5lZCBpbiBgLWwgbm9kZXM9MTptMzJnOnBwbj04LHdhbGx0aW1lPTQ4OjAwOjAwYA0KDQojIyBiLiBSdW4gU2NyaXB0Og0KIyMjIFNldCB1cCBlbnZpcm9ubWVudCBhbmQgZGlyZWN0b3J5Og0KYGBge2Jhc2h9DQojISAvYmluL2Jhc2gNCg0KbW9kdWxlIGxvYWQgZ251LXBhcmFsbGVsDQoNCmVjaG8gTG9jYWwgRGlyZWN0b3J5OiAkTE9DQUxfRElSDQplY2hvIEZpbGUgRGlyZWN0b3J5OiAkRklMRV9ESVINCmVjaG8gT3V0cHV0IERpcmVjdG9yeTogJE9VVFBVVF9ESVINCmVjaG8gSW1wdXRhdGlvbiBQYW5lbCBSZWZlcmVuY2UgUGF0aDogJFJFRkVSRU5DRV9QQVRIDQplY2hvIEltcHV0YXRpb24gUGFuZWwgTGVnZW5kIEZpbGVzIFJlZmVyZW5jZSBQYXRoOiAkTEVHRU5EX1JFRkVSRU5DRV9QQVRIDQplY2hvIENocm9tb3NvbWUgTnVtYmVyOiAkY2hyDQplY2hvIEludGVydmFsIFNpemUgU2V0IHRvOiAkaW50ZXJ2YWxfc2l6ZQ0KZWNobyBJbnB1dCBGaWxlbmFtZTogJEZJTEVfTkFNRQ0KDQpjZCAkTE9DQUxfRElSDQoNCmV4cG9ydCBpbnRfc3plPSRpbnRlcnZhbF9zaXplDQoNCmV4cG9ydCBHRU5FVElDX01BUF9OQU1FPSJnZW5ldGljX21hcF9jaHIke2Nocn1fY29tYmluZWRfYjM3LnR4dCINCiNIQVBMT1RZUEVfRklMRU5BTUU9IkFMTF8xMDAwR19waGFzZTFpbnRlZ3JhdGVkX3YzX2NociR7Y2hyfV9pbXB1dGUuaGFwLmd6Ig0KZXhwb3J0IEhBUExPVFlQRV9GSUxFTkFNRT0iMTAwMEdQX1BoYXNlM19jaHIke2Nocn0uaGFwLmd6Ig0KI0xFR0VORF9GSUxFTkFNRT0iQUxMXzEwMDBHX3BoYXNlMV9jaHIke2Nocn1faW1wdXRlLmxlZ2VuZCINCmV4cG9ydCBMRUdFTkRfRklMRU5BTUU9IjEwMDBHUF9QaGFzZTNfY2hyJHtjaHJ9LmxlZ2VuZC5neiINCg0KbWtkaXIgL2Rldi9zaG0vd29ya2Rpcg0KbWtkaXIgLXAgJE9VVFBVVF9ESVIvY2hyJHtjaHJ9DQpgYGANCg0KLSBMb2FkcyBgZ251LXBhcmFsbGVsYCB0byBtYW5hZ2UgcGFyYWxsZWwgam9iIGV4ZWN1dGlvbiBhY3Jvc3MgaW50ZXJ2YWxzDQotIERpc3BsYXlzIGtleSBkaXJlY3RvcnkgYW5kIGZpbGUgcGF0aHMsIGNocm9tb3NvbWUgbnVtYmVyLCBpbnRlcnZhbCBzaXplLCBhbmQgZmlsZW5hbWUgZm9yIGxvZ2dpbmcgcHVycG9zZXMNCi0gQ2hhbmdlcyB0aGUgd29ya2luZyBkaXJlY3RvcnkgdG8gYExPQ0FMX0RJUmAsIHdoZXJlIGltcHV0YXRpb24tcmVsYXRlZCBmaWxlcyBhbmQgZGlyZWN0b3JpZXMgYXJlIGJhc2VkDQotIEV4cG9ydHMgdGhlIGludGVydmFsIHNpemUgdmFyaWFibGUgZm9yIGRlZmluaW5nIHRoZSBnZW5vbWljIHJhbmdlIHRvIHByb2Nlc3MgcGVyIGludGVydmFsDQotIFNldHMgdGhlIGZpbGUgbmFtZXMgb2YgdGhlIGdlbmV0aWMgbWFwLCBoYXBsb3R5cGUsIGFuZCBsZWdlbmQgZmlsZXMgc3BlY2lmaWMgdG8gZWFjaCBjaHJvbW9zb21lLCBiYXNlZCBvbiB0aGUgcmVmZXJlbmNlIHBhbmVsDQotIENyZWF0ZXMgYSB0ZW1wb3Jhcnkgd29yayBkaXJlY3RvcnkgaW4gc2hhcmVkIG1lbW9yeSAoYC9kZXYvc2htL3dvcmtkaXJgKSB0byBzcGVlZCB1cCBJL08gb3BlcmF0aW9ucyBkdXJpbmcgaW1wdXRhdGlvbiwgYW5kIGEgY2hyb21vc29tZS1zcGVjaWZpYyBvdXRwdXQgZGlyZWN0b3J5IGluIGBPVVRQVVRfRElSYC4NCg0KIyMjIFJ1biBJbXB1dGF0aW9uIGluIFBhcmFsbGVsIHVzaW5nIEltcHV0ZTI6DQpgYGB7YmFzaH0NCnNlcSAkc3RhcnRfaW50ZXJ2YWwgJGVuZF9pbnRlcnZhbCB8IHBhcmFsbGVsIC1qICRSIC0tam9ibG9nICRMT0dfRElSL2ltcHV0ZTJfY2hyJHtjaHJ9Lmxqb2IgJyRJTVAyL2ltcHV0ZTIgLXVzZV9wcmVwaGFzZWRfZyBcDQoJLWtub3duX2hhcHNfZyAkRklMRV9ESVIvJHtGSUxFX05BTUV9X2NociR7Y2hyfS5waGFzZWQuaGFwcyBcDQogICAgLW0gJFJFRkVSRU5DRV9QQVRILyRHRU5FVElDX01BUF9OQU1FIFwNCiAgICAtaCAkUkVGRVJFTkNFX1BBVEgvJEhBUExPVFlQRV9GSUxFTkFNRSBcDQogICAgLWwgJExFR0VORF9SRUZFUkVOQ0VfUEFUSC8kTEVHRU5EX0ZJTEVOQU1FIFwNCiAgICAtaW50ICQoKCBpbnRfc3plKih7fS0xKSsxICkpICQoKCBpbnRfc3plKnt9ICkpIFwNCiAgICAtTmUgMjAwMDAgLWJ1ZmZlciAyNTAgXA0KICAgIC1vIC9kZXYvc2htL3dvcmtkaXIvJHtGSUxFX05BTUV9X2NociR7Y2hyfV97fV9pbXB1dGVkIFwNCiAgICAtb19nejsgXA0KICAgIG12IC9kZXYvc2htL3dvcmtkaXIvJHtGSUxFX05BTUV9X2NociR7Y2hyfV97fV9pbXB1dGVkKiAkT1VUUFVUX0RJUi9jaHIke2Nocn0gJw0KDQojc2xlZXAgNXMNCiNtdiAvZGV2L3NobS93b3JrZGlyLyogJE9VVFBVVF9ESVIvDQpgYGANCg0KLSAqKlNlcXVlbmNlIFNldHVwKio6IGBzZXEgJHN0YXJ0X2ludGVydmFsICRlbmRfaW50ZXJ2YWxgIGdlbmVyYXRlcyBpbnRlcnZhbHMgYWNyb3NzIHdoaWNoIEltcHV0ZTIgd2lsbCBydW4NCi0gKipQYXJhbGxlbCBFeGVjdXRpb24qKjogR05VIFBhcmFsbGVsIHJ1bnMgZWFjaCBpbnRlcnZhbCBhcyBhIHNlcGFyYXRlIGpvYiwgdXNpbmcgYC1qICRSYCB0byBzcGVjaWZ5IHRoZSBtYXhpbXVtIGNvbmN1cnJlbnQgam9icy4NCi0gKipJbXB1dGUyIENvbW1hbmQqKjogRXhlY3V0ZXMgdGhlIGBJbXB1dGUyYCBjb21tYW5kIHdpdGggc2V2ZXJhbCBhcmd1bWVudHM6DQogICAgLSBgLXVzZV9wcmVwaGFzZWRfZ2A6IFVzZXMgcHJlLXBoYXNlZCBnZW5vdHlwZSBkYXRhDQogICAgLSBgLWtub3duX2hhcHNfZ2A6IFNwZWNpZmllcyB0aGUgcHJlLXBoYXNlZCBoYXBsb3R5cGUgZmlsZSBmb3IgdGhpcyBjaHJvbW9zb21lDQogICAgLSBgLW1gLCBgLWhgLCBgLWxgOiBTcGVjaWZpZXMgcGF0aHMgdG8gdGhlIGdlbmV0aWMgbWFwLCBoYXBsb3R5cGUsIGFuZCBsZWdlbmQgZmlsZXMsIHJlc3BlY3RpdmVseQ0KICAgIC0gYC1pbnRgOiBEZWZpbmVzIHRoZSBnZW5vbWljIGludGVydmFsIGZvciB0aGlzIHJ1biwgY2FsY3VsYXRlZCBiYXNlZCBvbiBgaW50X3N6ZWAgYW5kIGludGVydmFsIG51bWJlciBgKHt9KWAuDQogICAgLSBgLU5lYDogU2V0cyB0aGUgZWZmZWN0aXZlIHBvcHVsYXRpb24gc2l6ZSBmb3IgdGhlIG1vZGVsLg0KICAgIC0gYC1idWZmZXJgOiBBZGRzIGEgYnVmZmVyIHJlZ2lvbiBhcm91bmQgZWFjaCBpbnRlcnZhbCBmb3IgaW1wdXRhdGlvbiBhY2N1cmFjeQ0KICAgIC0gYC1vYDogU3BlY2lmaWVzIHRoZSBvdXRwdXQgcGF0aCBmb3IgdGhlIGltcHV0ZWQgZmlsZSB3aXRoaW4gdGhlIHRlbXBvcmFyeSBkaXJlY3RvcnkgYC9kZXYvc2htL3dvcmtkaXJgLg0KICAgIC0gKipNb3ZlIE91dHB1dCoqOiBNb3ZlcyB0aGUgY29tcGxldGVkIGltcHV0ZWQgZmlsZXMgZnJvbSBgL2Rldi9zaG0vd29ya2RpcmAgdG8gdGhlIGNocm9tb3NvbWUtc3BlY2lmaWMgb3V0cHV0IGRpcmVjdG9yeSBpbiBgT1VUUFVUX0RJUmAuDQotIExhc3QgY29tbWVudGVkIGNvZGUgd291bGQgbW92ZSBhbGwgZmlsZXMgZnJvbSBgL2Rldi9zaG0vd29ya2RpcmAgdG8gYE9VVFBVVF9ESVJgLCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLg0KDQojIFN0ZXAgNDogQ29udmVydCBJbXB1dGVkIERhdGEgdG8gUExJTksgRm9ybWF0OiANCiMjIGEuIFN1Ym1pdCBTY3JpcHQ6IA0KIyMjIFNldCB1cCBFbnZpcm9ubWVudCBWYXJpYWJsZXMgYW5kIERpcmVjdG9yaWVzOg0KYGBge2Jhc2h9DQojIS9iaW4vYmFzaA0KbW9kdWxlIGxvYWQgZ251LXBhcmFsbGVsDQoNClNPVVJDRV9ESVI9Ii9zY3JhdGNoL3cvd3h1L29lc3BpbmdhL0hhbWlsdG9uX2ltcHV0YXRpb24iDQoNCmV4cG9ydCBTQ1JJUFRfRElSPSIvaG9tZS93L3d4dS9vZXNwaW5nYS9IYW1pbHRvbl9pbXB1dGF0aW9uL2NvZGUiDQpleHBvcnQgTE9DQUxfRElSPSRTT1VSQ0VfRElSLyJpbXB1dGVkX2ZpbGVzX2hhcCIgI3doZXJlIHRoZSBpbXB1dGVkIGZpbGVzIGFyZSBsb2NhdGVkDQpleHBvcnQgT1VUUFVUX0RJUj0kU09VUkNFX0RJUi8iaW1wdXRlZF9wbGluayINCmV4cG9ydCBMT0dfRElSPSRTT1VSQ0VfRElSLyJsb2dzIg0KZXhwb3J0IFNBTVBMRV9ESVI9JFNPVVJDRV9ESVIvImhhcGxvdHlwZXMiDQoNCiMqKiogU3VibWl0IHRoaXMgc2NyaXB0IGFzOg0KIyAuLzRfaW1wdXRhdGlvbl90b19wbGluay5zaA0KDQpleHBvcnQgUEw9Ii9ob21lL3cvd3h1L29lc3BpbmdhL3NvZnR3YXJlL3BsaW5rL3BsaW5rLTEuMDcteDg2XzY0Ig0KZXhwb3J0IEdUTD0iL2hvbWUvdy93eHUva3NoZXN0b3AvaW1wdXRhdGlvbl9waXBlbGluZS9JbXB1dGF0aW9uX1Byb2dyYW1zL2d0b29sX3YwLjcuNSINCg0KZXhwb3J0IEhBUF9GSUxFPSJSb2JfSGFtaWx0b25fZmluYWwiICNvdXRwdXQgbmFtZSBvZiB0aGUgaGFwbG90eXBlcw0KZXhwb3J0IEdfRklMRT0iUm9iX0hhbWlsdG9uX2ltcF9jaHIiICNvdXRwdXQgbmFtZSBvZiB0aGUgcGxpbmsgZmlsZXMNCg0KY2QgJExPQ0FMX0RJUg0KbWtkaXIgLXAgJE9VVFBVVF9ESVINCmBgYA0KDQotIGBtb2R1bGUgbG9hZCBnbnUtcGFyYWxsZWxgOiBMb2FkIHRoZSBgZ251LXBhcmFsbGVsYCBtb2R1bGUsIHdoaWNoIGFsbG93cyBwYXJhbGxlbCBleGVjdXRpb24gb2Ygam9icyBmb3IgaW1wcm92ZWQgcGVyZm9ybWFuY2UNCi0gRGVmaW5lIGBTT1VSQ0VfRElSYCBhcyB0aGUgYmFzZSBkaXJlY3RvcnkgZm9yIHRoZSBjb2Rpbmcgb2YgdGhlIHByb2plY3QuDQotIERlZmluZSBvdGhlcnMgZW52aXJvbm1lbnQgdmFyaWFibGVzIChgU0NSSVBUX0RJUmAsIGBMT0NBTF9ESVJgLCBgT1VUUFVUX0RJUmAsIGBMT0dfRElSYCwgYW5kIGBTQU1QTEVfRElSYCkNCi0gU2V0IGVudmlyb25tZW50IHZhcmlhYmxlcyBmb3IgdGhlIHBhdGhzIHRvIGBQTElOS2AgYW5kIGBndG9vbHNgIHRvb2xzDQotIERlZmluZSBvdXRwdXQgZmlsZSBuYW1lOiBgSEFQX0ZJTEVgIChuYW1lIGZvciB0aGUgb3V0cHV0IGhhcGxvdHlwZSBmaWxlcykgYW5kIGBHX0ZJTEVgIChuYW1lIHByZWZpeCBmb3IgdGhlIG91dHB1dCBQTElOSyBmaWxlcykNCi0gQ2hhbmdpbmcgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHRvIGAkTE9DQUxfRElSYCwgd2hlcmUgdGhlIGltcHV0ZWQgZmlsZXMgYXJlIHN0b3JlZA0KLSBVc2UgYG1rZGlyIC1wYCB0byBjcmVhdGUgdGhlIG91dHB1dCBkaXJlY3RvcnkgKGAkT1VUUFVUX0RJUmApIGlmIGl0IGRvZXNuJ3QgZXhpc3QgeWV0DQoNCiMjIyBTdWJtaXQgQ29udmVyc2lvbnMgSm9icyBmb3IgRWFjaCBDaHJvbW9zb21lOiANCmBgYHtiYXNofQ0KZm9yIENIUiBpbiBgc2VxIDEgODsgc2VxIDEwIDEyYA0KZG8NCiAgICBleHBvcnQgQ0hSDQogICAgSk9CX05BTUU9NF90b3BsaW5rX2NociR7Q0hSfQ0KICAgIHFzdWIgLW8gJExPR19ESVIvJEpPQl9OQU1FLm91dCAtZSAkTE9HX0RJUi8kSk9CX05BTUUuZXJyIC1OICRKT0JfTkFNRSBcDQogICAgLXYgTE9DQUxfRElSLE9VVFBVVF9ESVIsTE9HX0RJUixTQU1QTEVfRElSLENIUixQTCxHVEwsR19GSUxFLEhBUF9GSUxFIFwNCiAgICAtcSBzYW5keSAtbCBub2Rlcz0xOm02NGc6cHBuPTE2LHdhbGx0aW1lPTI0OjAwOjAwIFwNCiAgICAkU0NSSVBUX0RJUi80YV9iYWNrX3RvX3BsaW5rX3NjcmlwdC5zaA0KIy1xIGJhdGNoIC1sIG5vZGVzPTE6bTMyZzpwcG49OCx3YWxsdGltZT0xMDowMDowMA0KZG9uZQ0KDQplY2hvICJqb2JzIHN1Ym1pdHRlZC4uLiINCmBgYA0KDQotIExvb3AgdGhyb3VnaCBlYWNoIGNocm9tb3NvbWUgKDEgdG8gOCwgMTAgdG8gMTIpIHRvIHByb2Nlc3MgZGF0YSBmb3IgZWFjaCBjaHJvbW9zb21lIHNlcGFyYXRlbHkuDQotIEV4cG9ydCB0aGUgY3VycmVudCBjaHJvbW9zb21lIChgQ0hSYCkgdG8gbWFrZSBpdCBhY2Nlc3NpYmxlIGluIHRoZSBqb2Igc2NyaXB0DQotIERlZmluZSBgSk9CX05BTUVgIHdpdGggdGhlIGNocm9tb3NvbWUgbnVtYmVyIGZvciB1bmlxdWUgam9iIG5hbWVzIChlLmcuLCBgNF90b3BsaW5rX2NocjFgKQ0KLSBTdWJtaXQgZWFjaCBqb2IgdG8gdGhlIHF1ZXVlIHVzaW5nIGBxc3ViYCwgc3BlY2lmeWluZzoNCiAgICAtICoqT3V0cHV0IGFuZCBFcnJvciBGaWxlcyoqOiBEaXJlY3QgYHN0ZG91dGAgYW5kIGBzdGRlcnJgIHRvIGZpbGVzIGluIGAkTE9HX0RJUmAgKGUuZy4sIGAkSk9CX05BTUUub3V0YCBhbmQgYCRKT0JfTkFNRS5lcnJgKS4NCiAgICAtICoqSm9iIE5hbWUqKjogVXNlIGAtTiAkSk9CX05BTUVgIHRvIHNldCB0aGUgam9iIG5hbWUuDQogICAgLSAqKkVudmlyb25tZW50IFZhcmlhYmxlcyoqOiBQYXNzIHZhcmlhYmxlcyBuZWVkZWQgYnkgdGhlIGpvYiBzY3JpcHQgKGBMT0NBTF9ESVJgLCBgT1VUUFVUX0RJUmAsIGBMT0dfRElSYCwgYFNBTVBMRV9ESVJgLCBgQ0hSYCwgYFBMYCwgYEdUTGAsIGBHX0ZJTEVgLCBgSEFQX0ZJTEVgKSB1c2luZyBmbGFnYC12YCAoTGlzdCBhbGwgdmFyaWFibGVzKQ0KICAgIC0gKipRdWV1ZSBhbmQgUmVzb3VyY2UgUmVxdWlyZW1lbnRzKio6IFJlcXVlc3QgMSBub2RlIHdpdGggNjQgR0IgbWVtb3J5LCAxNiBwcm9jZXNzb3JzLCBhbmQgYSB3YWxsdGltZSBvZiAyNCBob3Vycw0KICAgIC0gKipKb2IgU2NyaXB0Kio6IFJ1biB0aGUgYDRhX2JhY2tfdG9fcGxpbmtfc2NyaXB0LnNoYCBzY3JpcHQgZnJvbSBgJFNDUklQVF9ESVJgDQotIFByaW50IGNvbmZpcm1hdGlvbiBtZXNzYWdlIHRvIGVuZCB0aGUgam9icw0KDQojIyBiLiBSdW4gU2NyaXB0OiANCiMjIyBTZXQgdXAgZGlyZWN0b3JpZXMgYW5kIGluaXRpYXRsaXplOg0KYGBge2Jhc2h9DQojIS9iaW4vYmFzaA0KbW9kdWxlIGxvYWQgZXh0cmFzDQoNCmNkICRMT0NBTF9ESVIvY2hyJHtDSFJ9DQoNCmNocj1gbHMgKmNociR7Q0hSfV8qLmd6IHwgc29ydCAtVmANCmNhdCAkY2hyIHwgcGlneiAtZGMgfCBjdXQgLWQiICIgLWYyLSB8IGF3ayAne2lmKGxlbmd0aCgkMSk+MTAwKSAkMT1zdWJzdHIoJDEsMSwxMDApOyBwcmludCAkMSwkMH0nID4gZmlsZSR7Q0hSfS5nZW4NCmBgYA0KDQotIEluaXRpYXRlIHRoZSBFbnZpcm9ubWVudDogTG9hZCB0aGUgYGV4dHJhc2AgbW9kdWxlIGZvciB0aGlzIGFuYWx5c2lzDQotIE5hdmlnYXRlIHRvIENocm9tb3NvbWUgRGlyZWN0b3J5OiBNb3ZlIHRvIHRoZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYXRhIGZvciB0aGUgc3BlY2lmaWVkIGNocm9tb3NvbWUgKGBDSFJgKQ0KLSBQcm9jZXNzIGFuZCBDb25jYXRlbmF0ZSBDaHJvbW9zb21lIERhdGEgRmlsZXM6IA0KICAgIC0gTGlzdCBhbGwgYC5nemAgZmlsZXMgY29udGFpbmluZyB0aGUgY2hyb21vc29tZSBkYXRhLCBzb3J0IHRoZW0sIGFuZCBjb25jYXRlbmF0ZSB1c2luZyBgY2F0YA0KICAgIC0gRGVjb21wcmVzcyBmaWxlIHdpdGggYHBpZ3ogLWRjYCwgcmVtb3ZlIHRoZSBmaXJzdCBjb2x1bW4gd2l0aCBgY3V0IC1kIiAiIC1mMi1gLCBhbmQgdHJ1bmNhdGUgYW55IHZhbHVlIGluIHRoZSBmaXJzdCBjb2x1bW4gZXhjZWVkaW5nIDEwMCBjaGFyYWN0ZXJzDQogICAgLSBTYXZlIHRoZSBwcm9jZXNzZWQgb3V0cHV0IHRvIGBmaWxlJHtDSFJ9LmdlbmANCg0KIyMjIEdlbmVyYXRlIERvc2FnZXMgYW5kIENvbnZlcnQgRm9ybWF0Og0KYGBge2Jhc2h9DQojc2F2ZSB0byBkb3NhZ2VzICh0cmFuc3Bvc2VkLCBpLmUuIGluZGl2aWR1YWxzIGluIGNvbHVtcykNCmF3ayAtdiBDSFI9JENIUiAne3ByaW50ZiBDSFIiICIkMiIgIiQzIiAiJDQiICIkNTsgZm9yKGk9NjsgaTxORjsgaSs9Myl7aWYgKCQoaSswKT09MCAmJiAkKGkrMSk9PTAgJiYgJChpKzIpPT0wKSBwcmludGYgIiAtOSI7IGVsc2UgcHJpbnRmICIgIiQoaSswKSowKyQoaSsxKSoxKyQoaSsyKSoyfTsgcHJpbnRmICJcbiJ9JyBmaWxlJHtDSFJ9LmdlbiB8IHBpZ3ogPiBkb3NhZ2VzJHtDSFJ9Lmd6DQoNCiMkR1RML2d0b29sIC1HIC0tZyBmaWxlJHtDSFJ9LmdlbiAtLXMgPChhd2sgJ3twcmludCAkMSwkMiIgMCAiJDQsJDV9JyAkU0FNUExFX0RJUi8ke0hBUF9GSUxFfV9jaHIke0NIUn1fY2hyJHtDSFJ9LnBoYXNlZC5zYW1wbGUpIC0tdGhyZXNob2xkIDAuOSAtLXBoZW5vdHlwZSBwbGlua19waGVubyAtLXNleCBzZXggLS1jaHIgJHtDSFJ9IC0tcGVkICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfS5wZWQgLS1tYXAgJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9Lm1hcA0KDQokR1RML2d0b29sIC1HIC0tZyBmaWxlJHtDSFJ9LmdlbiAtLXMgJFNBTVBMRV9ESVIvJHtIQVBfRklMRX1fY2hyJHtDSFJ9LnBoYXNlZC5zYW1wbGUgLS10aHJlc2hvbGQgMC45IC0tcGhlbm90eXBlIHBsaW5rX3BoZW5vIC0tc2V4IHNleCAtLWNociAke0NIUn0gLS1wZWQgJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9LnBlZCAtLW1hcCAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn0ubWFwDQoNCmBgYA0KDQotIEdlbmVyYXRlIGRvc2FnZXMgZmlsZToNCiAgICAtIENvbnZlcnQgZ2Vub3R5cGVzIGluIGBmaWxlJHtDSFJ9LmdlbmAgdG8gZG9zYWdlcyBmb3JtYXQsIHdoZXJlIGluZGl2aWR1YWxzIGFyZSByZXByZXNlbnRlZCBpbiBjb2x1bW5zDQogICAgLSBDYWxjdWxhdGUgZG9zYWdlIHZhbHVlcyBiYXNlZCBvbiBhbGxlbGUgdmFsdWVzIGZvciBlYWNoIHBvc2l0aW9uLCBhc3NpZ25pbmcgYC05YCBmb3IgbWlzc2luZyBkYXRhDQogICAgLSBDb21wcmVzcyB0aGUgb3V0cHV0IHVzaW5nIGBwaWd6YCBhbmQgc2F2ZSBpdCBhcyBgZG9zYWdlcyR7Q0hSfS5nemANCi0gQ29udmVydCBHZW5vdHlwZSBmaWxlIHRvIFBMSU5LIFBFRCBhbmQgTUFQIGZvcm1hdDogDQogICAgLSBVc2UgYGd0b29sYCB0byBjb252ZXJ0IHRoZSBgLmdlbmAgZmlsZSB0byBQTElOSydzIGAucGVkYCBhbmQgYC5tYXBgIGZvcm1hdHMNCiAgICAtIEdlbm90eXBlIGZpbGUgYCgtLWcpYDogYGZpbGUke0NIUn0uZ2VuYA0KICAgIC0gU2FtcGxlIGZpbGUgYCgtLXMpYDogdGhlIHBoYXNlZCBzYW1wbGUgZmlsZSBmb3IgdGhlIGNocm9tb3NvbWUNCiAgICAtIE90aGVyIHBhcmFtZXRlcnM6IGAtLXRocmVzaG9sZGAgZm9yIHF1YWxpdHksIHBoZW5vdHlwZSBhbmQgc2V4IGNvbHVtbnMsIGNocm9tb3NvbWUgSUQgKGBDSFJgKSwgYW5kIG91dHB1dCBmaWxlIHBhdGhzIGZvciBgLnBlZGAgYW5kIGAubWFwYA0KDQojIyMgQ29udmVydCB0byBQTElOSyBCaW5hcnkgZm9ybWF0IGFuZCBEZXRlY3QgRHVwbGljYXRlczoNCmBgYHtiYXNofQ0KJFBML3BsaW5rIC0tbm93ZWIgLS1maWxlICRPVVRQVVRfRElSLyRHX0ZJTEUke0NIUn0gLS1tYWtlLWJlZCAtLW91dCAkT1VUUFVUX0RJUi8kR19GSUxFJHtDSFJ9DQoNCnJtIC1mICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfS5wZWQgJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9Lm1hcCAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn0ubm9zZXgNCg0KYXdrICd7cHJpbnQgJDJ9JyAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn0uYmltIHwgc29ydCB8IHVuaXEgLWQgPiAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn1fZHVwLnNucHMNCg0KbGV0IERVUF9TTlBTPWB3YyAtbCAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn1fZHVwLnNucHMgfCBjdXQgLWQgIiAiIC1mIDFgDQoNCmlmIFsgJERVUF9TTlBTIC1ndCAwIF0NCnRoZW4NCiRQTC9wbGluayAtLW5vd2ViIC0tYmZpbGUgICRPVVRQVVRfRElSLyRHX0ZJTEUke0NIUn0gLS1leGNsdWRlICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfV9kdXAuc25wcyAtLW1ha2UtYmVkIC0tb3V0ICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfV9jbGVhbg0KZWxzZQ0KY3AgJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9LmJlZCAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn1fY2xlYW4uYmVkDQpjcCAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn0uYmltICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfV9jbGVhbi5iaW0NCmNwICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfS5mYW0gJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9X2NsZWFuLmZhbQ0KZmkNCmBgYA0KDQotIENvbnZlcnQgUEVEL01BUCBmaWxlcyB0byBCaW5hcnkgUExJTksgRm9ybWF0OiBSdW4gUExJTksgdG8gY29udmVydCB0aGUgYC5wZWRgIGFuZCBgLm1hcGAgZmlsZXMgaW50byBQTElOSydzIGJpbmFyeSBmb3JtYXQgKGAuYmVkYCxgLmJpbWAsYC5mYW1gKQ0KLSBSZW1vdmUgdGVtcG9yYXJ5IGZpbGVzOiBkZWxldGUgdGhlIGAucGVkYCxgLm1hcGAsIGFuZCBgLm5vc2V4YCBmaWxlcyB0byBjbGVhbiB1cCBhZnRlciBjb252ZXJzaW9uIHRvIGJpbmFyeSBmb3JtYXQNCi0gSWRlbnRpZnkgRHVwbGljYXRlIFNOUHM6IEZpbmQgZHVwbGljYXRlIFNOUHMgaW4gdGhlIGAuYmltYCBmaWxlIGJ5IGV4dHJhY3RpbmcgdGhlIHNlY29uZCBjb2x1bW4sIHNvcnRpbmcsIGFuZCBzYXZpbmcgZHVwbGljYXRlcyB0IGEgZmlsZSBuYW1lZCBgJHtHX0ZJTEV9JHtDSFJ9X2R1cC5zbnBzYA0KLSBDb3VudCBkdXBsaWNhdGUgU05QczogQ291bnQgdGhlIG51bWJlciBvZiBkdXBsaWNhdGUgU05QcyBpZGVudGlmaWVkIGFuZCBzdG9yZSBpbiBgRFVQX1NOUFNgDQotIEhhbmRsZSBEdXBsaWNhdGUgU05QcyAoQ29uZGl0aW9uYWwgUHJvY2Vzc2luZykNCiAgICAtIElmIGR1cGxpY2F0ZXMgYXJlIGZvdW5kIChgRFVQX1NOUFMgPiAwYCksIHJlcnVuIFBMSU5LIHRvIGV4Y2x1ZGUgdGhlbSBhbmQgZ2VuZXJhdGUgYSAiY2xlYW4iIGJpbmFyeSBkYXRzZXQNCiAgICAtIElmIG5vIGR1cGxpY2F0ZXMgYXJlIGZvdW5kLCBjb3B5IHRoZSBgLmJlZGAsIGAuYmltYCwgYW5kIGAuZmFtYCBmaWxlcyBkaXJlY3RseSBhcyB0aGUgImNsZWFuIiBmaWxlcw0KDQojIyMgQ2xlYW4gdXA6DQpgYGB7YmFzaH0NCnJtIC1mIGZpbGUke0NIUn0uZ2VuICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfS5iZWQgJE9VVFBVVF9ESVIvJHtHX0ZJTEV9JHtDSFJ9LmJpbSAkT1VUUFVUX0RJUi8ke0dfRklMRX0ke0NIUn0uZmFtICRPVVRQVVRfRElSLyR7R19GSUxFfSR7Q0hSfV9kdXAuc25wcw0KYGBgDQoNCi0gRGVsZXRlIHRlbXBvcmFyeSBmaWxlcywgaW5jbHVkaW5nIHRoZSBgLmdlbmAgZmlsZSwgdGhlIG9yaWdpbmFsIFBMSU5LIGJpbmFyeSBmaWxlcywgYW5kIHRoZSBkdXBsaWNhdGUgU05QIGZpbGUuIFRoaXMgZW5zdXJlcyBvbmx5IHRoZSBjbGVhbmVkLCBmaW5hbCBvdXRwdXQgZmlsZXMgYXJlIGtlcHQuDQoNCiMgU3RlcCA1OiBRdWFsaXR5IENvbnRyb2wgcG9zdC1JbXB1dGF0aW9uOg0KIyMgYS4gU3VibWl0IFNjcmlwdDogDQojIyMgU2V0IHVwIGRpcmVjdG9yeSBhbmQgbG9hZCBuZWNlc3NhcnkgbW9kdWxlczoNCmBgYHtiYXNofQ0KIyEvYmluL2Jhc2gNCiNQQlMgLWwgbm9kZXM9MTpwcG49OA0KI1BCUyAtbCB3YWxsdGltZT0yOjAwOjAwDQoNCm1vZHVsZSBsb2FkIGdudS1wYXJhbGxlbA0KDQpleHBvcnQgSU1QX0RJUj0gcGF0aC90by9JbXB1dGF0aW9uDQpleHBvcnQgU0NSSVBUX0RJUj0gcGF0aC90by9jb2RlDQpleHBvcnQgT1VUUFVUX0RJUj0kSU1QX0RJUi8iaW1wdXRlZF9wbGluayINCmV4cG9ydCBMT0dfRElSPSRJTVBfRElSLyJsb2dzIg0KZXhwb3J0IEhBUFNfRElSPSRJTVBfRElSLyJpbXB1dGVkX2ZpbGVzX2hhcCIgI3doZXJlIHRoZSBpbXB1dGVkIGZpbGVzIGFyZSBsb2NhdGVkDQojKioqIFN1Ym1pdCB0aGlzIHNjcmlwdCBhczoNCiMgcXN1YiAtbyAkTE9HX0RJUi81X3BlcmZvcm1fcWMub3V0IC1lICRMT0dfRElSLzVfcWMuZXJyIC1xIGJhdGNoIC1OIDVfcWNfaW1wICRTQ1JJUFRfRElSLzVfcGVyZm9ybV9xYy5zaA0KZXhwb3J0IFBMPSBwYXRoL3RvL1BMSU5LDQpleHBvcnQgR19GSUxFPSJSb2JfSGFtaWx0b25faW1wX2NociIgI291dHB1dCBuYW1lIG9mIHRoZSBwbGluayBmaWxlcw0KZXhwb3J0IEZJTEVTX1BSRUZJWD0iSGFtaWx0b24iDQojY3JlYXRlIGZpbGUgd2l0aCBpbmZvIGFuZCBpbXB1dGF0aW9uIGluZm8NCmBgYA0KDQotIGBtb2R1bGUgbG9hZCBnbnUtcGFyYWxsZWxgOiBMb2FkIHRoZSBgZ251LXBhcmFsbGVsYCBtb2R1bGUsIHdoaWNoIGFsbG93cyBwYXJhbGxlbCBleGVjdXRpb24gb2Ygam9icyBmb3IgaW1wcm92ZWQgcGVyZm9ybWFuY2UNCiAgICAtIGBQQlNgIGRpcmVjdGl2ZXMgaXMgd2hhdD8NCi0gRGVmaW5lIGFsbCB0aGUgcGF0aHMgdG8gZGlyZWN0b3JpZXMgYW5kIHZhcmlhYmxlcw0KICAgIC0gYElNUF9ESVJgIGlzIHRoZSBtYWluIGRpcmVjdG9yeSB3aGVyZSBhbGwgaW1wdXRhdGlvbi1yZWxhdGVkIGRhdGEgaXMgc3RvcmVkDQogICAgLSBgT1VUUFVUX0RJUmAgYW5kIGBMT0dfRElSYCBhcmUgc3ViLWRpcmVjdG9yaWVzIGZvciBzdG9yaW5nIG91dHB1dHMgYW5kIGxvZ3MNCiAgICAtIGBQTGAgc3BlY2lmaWVzIHRoZSBwYXRoIHRvIFBMSU5LIHRvb2wNCiAgICAtIGBHX0ZJTEVgIGFuZCBgRklMRVNfUFJFRklYYCBzZXQgdGhlIG5hbWluZyBmb3JtYXQgZm9yIG91dHB1dCBmaWxlcyB0byBzZWUgdGhlIHJlc3VsdHMNCi0gVXNlIGBxc3ViYCB0byBzdWJtaXQgdGhlIGpvYnMgDQogICAgLSBgLW9gIGFuZCBgLWVgIG9wdGlvbnMgZGVmaW5lIHdoZXJlIHRvIHNhdmUgdGhlIHN0YW5kYXJkIG91dHB1dCBhbmQgZXJyb3IgbG9ncw0KICAgIC0gYC1OYCBhc3NpZ25zIGEgbmFtZSB0byB0aGUgam9iIGZvciB0cmFja2luZyBpbiB0aGUgcXVldWUgc3lzdGVtDQogICAgLSBOYW1lIHRoZSBqb2IgYDVfcWNfaW1wYA0KDQojIyMgQ3JlYXRlIEhlYWRlciBmb3IgdGhlIEltcHV0YXRpb24gU3VtbWFyeSBGaWxlOg0KYGBge2Jhc2h9DQojY3JlYXRlIGhlYWRlciBmb3IgZmlsZQ0KaGVhZF9maWxlPWBscyAtbGggJEhBUFNfRElSL2NocjEvKl9pbmZvIHwgaGVhZCAtMSB8IGF3ayAne3ByaW50ICQ5fSdgDQplY2hvIGBhd2sgJ05SPT0xe3ByaW50ICJjaHIiLCQwfScgJGhlYWRfZmlsZSB8IGN1dCAtZCIgIiAtZjEsMy1gIHwgZ3ppcCA+ICRJTVBfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3oNCmBgYA0KLSBUaGUgZmlsZSB3aWxsIGNvbXBpbGUgaW1wdXRhdGlvbiBpbmZvIGZvciBhbGwgY2hyb21vc29tZXMNCi0gVGhpcyBzdGVwIGV4dHJhY3RzIHRoZSBoZWFkZXIgZnJvbSB0aGUgZmlyc3QgYF9pbmZvYCBmaWxlIGluIHRoZSBkaXJlY3RvcnksIGFkZHMgYSBgY2hyYCAoY2hyb21vc29tZSkgY29sdW1uLCBhbmQgc2F2ZXMgaXQgYXMgdGhlIGhlYWRlciBvZiB0aGUgc3VtbWFyeSBmaWxlDQogICAgLSBgbHMgLWxoICRIQVBTX0RJUi9jaHIxLypfaW5mbyB8IGhlYWQgLTEgfCBhd2sgJ3twcmludCAkOX0nYCBnZXRzIHRoZSBuYW1lIG9mIHRoZSBmaXJzdCBgX2luZm9gIGZpbGUgZm9yIGNocm9tb3NvbWUgMQ0KICAgIC0gYGF3ayAnTlI9PTF7cHJpbnQgImNociIsJDB9J2AgYWRkcyAiY2hyIiBhcyBhIG5ldyBmaXJzdCBjb2x1bW4gdG8gdGhlIGhlYWRlcg0KICAgIC0gYGN1dCAtZCIgIiAtZjEsMy1gIHJlbW92ZXMgYW55IHVubmVjZXNzYXJ5IGNvbHVtbnMsIHJldGFpbmluZyBvbmx5IGVzc2VudGlhbCBkYXRhDQogICAgLSBgZ3ppcCA+ICRJTVBfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3pgIHNhdmVzIHRoZSBtb2RpZmllZCBoZWFkZXIgYXMgYSBjb21wcmVzc2VkIGZpbGUuDQoNCiMjIyBBcHBlbmQgSW1wdXRhdGlvbiBEYXRhIGZvciBlYWNoIENocm9tb3NvbWU6IA0KYGBge2Jhc2h9DQojbG9vcCBvdmVyIGFsbCBfaW5mbyBmaWxlcyBnZW5lcmF0ZWQgYnkgaW1wdXRlMg0KZm9yIGkgaW4gYHNlcSAxIDIyYA0KICAgIGRvDQogICAgZm9yIElOVF9GSUxFIGluIGBscyAkSEFQU19ESVIvY2hyJHtpfS8qY2hyJHtpfV8qX2luZm8gfCBzb3J0IC1WYA0KICAgICAgICBkbw0KICAgICAgICBhd2sgLXYgY2hyPSRpICdOUj4xe3ByaW50IGNociwkMH0nICRJTlRfRklMRSB8IGN1dCAtZCIgIiAtZjEsMy0gfCBnemlwID4+ICRJTVBfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3oNCiAgICBkb25lDQpkb25lDQpgYGANCg0KLSBUaGlzIGxvb3AgZ2F0aGVyIGRhdGEgZm9yIGNocm9tb3NvbWUgMSB0byAyMiBmcm9tIHRoZSBgX2luZm9gIGZpbGUgDQogICAgLSBJdCBhcHBlbmRzIGltcHV0YXRpb24gZGF0YSBmb3IgZWFjaCBjaHJvbW9zb21lIHRvIHRoZSBzdW1tYXJ5IGZpbGUgKGBfaW1wdXRhdGlvbl9sb2NpX2luZm8udHh0Lmd6YCksIGV4Y2x1ZGluZyB0aGUgaGVhZGVyIHJvdyBmcm9tIGVhY2ggYF9pbmZvYCBmaWxlDQogICAgLSBgZm9yIGkgaW4gJ1xzZXEgMSAyMidgIGl0ZXJhdGVzIHRocm91Z2ggY2hyb21vc29tZSBudW1iZXJzIDEgdG8gMjIuDQogICAgLSBgbHMgJEhBUFNfRElSL2NociR7aX0vKmNociR7aX1fKl9pbmZvIHwgc29ydCAtVmAgbGlzdHMgYW5kIHNvcnRzIGFsbCBgX2luZm9gIGZpbGVzIGZvciBlYWNoIGNocm9tb3NvbWUNCiAgICAtIGBhd2sgLXYgY2hyPSRpICdOUj4xe3ByaW50IGNociwkMH0nYCBza2lwcyB0aGUgZmlyc3Qgcm93IChoZWFkZXIpIG9mIGVhY2ggYF9pbmZvYCBmaWxlIGFuZCBhcHBlbmRzIGNocm9tb3NvbWUgbnVtYmVyIGFzIHRoZSBmaXJzdCBjb2x1bW4NCiAgICAtIGBnemlwID4+ICRJTVBfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3pgIGFwcGVuZHMgdGhpcyBwcm9jZXNzZWQgZGF0YSB0byB0aGUgc3VtbWFyeSBmaWxlIGluIGNvbXByZXNzZWQgZm9ybWF0DQoNCg0KIyMjIFBlcmZvcm0gUXVhbGl0eSBDb250cm9sIChRQykgaW4gUGFyYWxsZWwgYW5kIENvbXBsZXRlZCBNZXNzYWdlOg0KYGBge2Jhc2h9DQpjZCAkT1VUUFVUX0RJUg0KDQpzZXEgMSAyMiB8IHBhcmFsbGVsIC1qIDQgLS1qb2Jsb2cgJExPR19ESVIvNV9xYy5sam9iICRTQ1JJUFRfRElSLzVhX3BlcmZvcm1fcWNfc2NyaXB0LnNoIHt9DQoNCiM6OjogNyA4IDE2DQoNCmVjaG8gIkZpbmlzaGVkLi4uIg0KYGBgDQotIFRoaXMgc3RlcHMgdXNlIGBnbnVfcGFyYWxsZWxgIHRvIHJ1biBhIFFDIHNjcmlwdCAoYDVhX3BlcmZvcm1fcWNfc2NyaXB0LnNoYCkgb24gZWFjaCBjaHJvbW9zb21lIGZpbGUgaW4gcGFyYWxsZWwsIHByb2Nlc3NpbmcgdXAgdG8gNCBjaHJvbW9zb21lcyBhdCBvbmNlDQogICAgLSBgY2QgJE9VVFBVVF9ESVJgIGNoYW5nZXMgdGhlIGRpcmVjdG9yeSB0byB3aGVyZSBRQyBvdXRwdXRzIHdpbGwgYmUgc2F2ZWQNCiAgICAtIGBzZXEgMSAyMmAgZ2VuZXJhdGVzIGEgc2VxdWVuY2Ugb2YgbnVtYmVycyBmcm9tIDEgdG8gMjIsIHJlcHJlc2VudGluZyBjaHJvbW9zb21lcw0KICAgIC0gYHBhcmFsbGVsIC1qIDQgLS1qb2Jsb2cgJExPR19ESVIvNV9xYy5sam9iYCBleGVjdXRlcyBgNWFfcGVyZm9ybV9xY19zY3JpcHQuc2hgIHdpdGggdXAgdG8gNCBqb2JzIGluIHBhcmFsbGVsIGFuZCBsb2dzIGpvYiBhY3Rpdml0eQ0KICAgIC0gYCRTQ1JJUFRfRElSLzVhX3BlcmZvcm1fcWNfc2NyaXB0LnNoIHt9YCBydW5zIHRoZSBRQyBzY3JpcHQgZm9yIGVhY2ggY2hyb21vc29tZSwgd2l0aCBge31gIHJlcGxhY2VkIGJ5IGNocm9tb3NvbWUgbnVtYmVycw0KLSBgZWNobyAiRmluaXNoZWQuLi4iYHByaW50IHRoZSBmaW5pc2ggbWVzc2FnZSBhbmQgY29uZmlybSBhbGwgdGhlIHRhc2tzIGJlZW4gZXhlY3V0ZWQgDQoNCiMjIGIuIFJ1biBTY3JpcHQ6IA0KIyMjIFNldCB0aGUgVmFyaWFibGUgYW5kIERpcmVjdG9yeToNCmBgYHtiYXNofQ0KIyEvYmluL2Jhc2gNCmxldCBDSFI9JDENCmNkICRPVVRQVVRfRElSDQpgYGANCg0KLSBgbGV0IENIUj0kMWAgYXNzaWducyB0aGUgZmlyc3QgYXJndW1lbnQgcGFzc2VkIHRvIHRoZSBzY3JpcHQgdG8gdGhlIHZhcmlhYmxlIGBDSFJgLCByZXByZXNlbnRpbmcgdGhlIGNocm9tb3NvbWUgbnVtYmVyDQotIGBjZCAkT1VUUFVUX0RJUmAgY2hhbmdlcyB0aGUgd29ya2luZyBkaXJlY3RvcnkgdG8gd2hlcmUgdGhlIFFDIGZpbGVzIHdpbGwgYmUgc2F2ZWQNCg0KIyMjIENvdW50IGluaXRpYWwgU05QcyBhbmQgSWRlbnRpZnkgbWlzc2luZyBJRHMgaW4gdGhlIEJJTSBmaWxlOiANCmBgYHtiYXNofQ0KZWNobyAiJENIUiBJbml0aWFsX25vX2R1cGxpY2F0ZXMiIGB3YyAtbCAkR19GSUxFJHtDSFJ9X2NsZWFuLmJpbWAgPiBDb3VudHNfJHtDSFJ9LnR4dA0KZWNobyAiJENIUiBTTlBzX3dpdGhfZG90IiBgYXdrICckMj09Ii4ie3ByaW50ICQyfScgJHtHX0ZJTEV9JHtDSFJ9X2NsZWFuLmJpbSB8IHdjIC1sYCA+PiBDb3VudHNfJHtDSFJ9LnR4dA0KYGBgDQoNCi0gYHdjIC1sICRHX0ZJTEUke0NIUn1fY2xlYW4uYmltYCBjb3VudHMgdGhlIG51bWJlciBvZiBTTlBzIGluIHRoZSBjbGVhbmVkIEJJTSBmaWxlIGZvciB0aGUgY2hyb21vc29tZSwgYW5kIHRoZSByZXN1bHQgaXMgbGFiZWxlZCBhbmQgc2F2ZWQgdG8gYENvdW50c18ke0NIUn0udHh0YA0KLSBUaGUgc2Vjb25kIGNvbW1hbmQgY291bnRzIFNOUHMgd2hlcmUgdGhlIGlkZW50aWZpZXIgaXMganVzdCBhIGRvdCAoYCIuImApLCB3aGljaCBtYXkgaW5kaWNhdGUgbWlzc2luZyBvciBwcm9ibGVtYXRpYyBTTlAgaWRlbnRpZmllcnMsIGFwcGVuZGluZyB0aGUgY291bnQgdG8gYENvdW50c18ke0NIUn0udHh0YA0KDQojIyMgRmlsdGVyIFNOUHMgYmFzZWQgb24gSW5mbyBhbmQgQ2VydGFpbnR5Og0KYGBge2Jhc2h9DQpqb2luIDwoZ3VuemlwIC1jIC4uLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3ogfCBhd2sgLXYgY2hyPSR7Q0hSfSAnJDU+LjkgJiYgJDY+LjkgJiYgJDE9PWNociB7cHJpbnQgJDJ9JyB8IHNvcnQpIDwoYXdrICckMiE9Ii4ie3ByaW50ICQyfScgJHtHX0ZJTEV9JHtDSFJ9X2NsZWFuLmJpbSB8IHNvcnQpID4gc25wX2xpc3RfJHtDSFJ9LnR4dA0KDQplY2hvICIkQ0hSIEFmdGVyX2luZm9fYW5kX2NlcnRhaW50eSIgYHdjIC1sIHNucF9saXN0XyR7Q0hSfS50eHRgID4+IENvdW50c18ke0NIUn0udHh0DQpgYGANCg0KLSBgZ3VuemlwIC1jIC4uLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3ogfCBhd2sgLXYgY2hyPSR7Q0hSfSAnJDU+LjkgJiYgJDY+LjkgJiYgJDE9PWNociB7cHJpbnQgJDJ9JyB8IHNvcnRgIGZpbHRlcnMgU05QcyBmcm9tIHRoZSBpbXB1dGF0aW9uIGluZm8gZmlsZSBiYXNlZCBvbiBpbmZvIHNjb3JlIChgJDVgKSBhbmQgY2VydGFpbnR5IChgJDZgKSB2YWx1ZXMgYWJvdmUgMC45LCBmb3IgdGhlIHNwZWNpZmllZCBjaHJvbW9zb21lDQotIGBqb2luYCBtYXRjaGVzIHRoZXNlIFNOUHMgd2l0aCB0aG9zZSBpbiB0aGUgQklNIGZpbGUgKHRoYXQgZG8gbm90IGhhdmUgYC5gIGFzIHRoZWlyIElEKSwgY3JlYXRpbmcgYSBsaXN0IG9mIFNOUHMgdGhhdCBtZWV0IGJvdGggY3JpdGVyaWEgaW4gYHNucF9saXN0XyR7Q0hSfS50eHRgDQotIGB3YyAtbCBzbnBfbGlzdF8ke0NIUn0udHh0YCBjb3VudHMgdGhlc2UgU05QcyBhbmQgbG9ncyB0aGUgcmVzdWx0IGluIGBDb3VudHNfJHtDSFJ9LnR4dGANCg0KIyMjIEZpbHRlciBTTlBzIGJhc2VkIG9uIE1pc3NpbmcgUmF0ZTogDQpgYGB7YmFzaH0NCiRQTC9wbGluayAtLW5vd2ViIC0tYmZpbGUgJEdfRklMRSR7Q0hSfV9jbGVhbiAtLWV4dHJhY3Qgc25wX2xpc3RfJHtDSFJ9LnR4dCAtLWdlbm8gMC4wNSAtLW1ha2UtYmVkIC0tb3V0ICRHX0ZJTEUke0NIUn1fY2xlYW5fcWMNCg0KZWNobyAiJENIUiBBZnRlcl9taXNzaW5nX3JhdGUiIGB3YyAtbCAkR19GSUxFJHtDSFJ9X2NsZWFuX3FjLmJpbWAgPj4gQ291bnRzXyR7Q0hSfS50eHQNCmBgYA0KDQotIGAkUEwvcGxpbmsgLS1iZmlsZSAkR19GSUxFJHtDSFJ9X2NsZWFuIC0tZXh0cmFjdCBzbnBfbGlzdF8ke0NIUn0udHh0IC0tZ2VubyAwLjA1IC0tbWFrZS1iZWQgLS1vdXQgJEdfRklMRSR7Q0hSfV9jbGVhbl9xY2AgdXNlcyBQTElOSyB0byBmaWx0ZXIgU05QcyBiYXNlZCBvbiBhIG1pc3NpbmcgcmF0ZSBvZiA1JSAoYC0tZ2VubyAwLjA1YCkgYW5kIGdlbmVyYXRlcyBhIG5ldyBiaW5hcnkgZmlsZSBmb3JtYXQgKGAtLW1ha2UtYmVkYCkgd2l0aCB0aGlzIHVwZGF0ZWQgU05QIGxpc3QNCi0gYHdjIC1sICRHX0ZJTEUke0NIUn1fY2xlYW5fcWMuYmltYCBjb3VudHMgdGhlIHJlbWFpbmluZyBTTlBzIGFmdGVyIGFwcGx5aW5nIHRoZSBtaXNzaW5nIHJhdGUgZmlsdGVyIGFuZCBsb2dzIGl0IGluIGBDb3VudHNfJHtDSFJ9LnR4dGANCg0KIyMjIEZpbHRlciBTTlBzIEJhc2VkIG9uIE1pbm9yIEFsbGVsZSBGcmVxdWVuY3kgKE1BRik6IA0KYGBge2Jhc2h9DQokUEwvcGxpbmsgLS1ub3dlYiAtLWJmaWxlICRHX0ZJTEUke0NIUn1fY2xlYW5fcWMgLS1tYWYgMC4wMSAtLW1ha2UtYmVkIC0tb3V0ICRHX0ZJTEUke0NIUn1fY2xlYW5fcWMNCg0KZWNobyAiJENIUiBBZnRlcl9tYWZfZmlsdGVyIiBgd2MgLWwgJEdfRklMRSR7Q0hSfV9jbGVhbl9xYy5iaW1gID4+IENvdW50c18ke0NIUn0udHh0DQpgYGANCg0KLSBgJFBML3BsaW5rIC0tYmZpbGUgJEdfRklMRSR7Q0hSfV9jbGVhbl9xYyAtLW1hZiAwLjAxIC0tbWFrZS1iZWQgLS1vdXQgJEdfRklMRSR7Q0hSfV9jbGVhbl9xY2AgYXBwbGllcyBhIG1pbm9yIGFsbGVsZSBmcmVxdWVuY3kgKE1BRikgZmlsdGVyIG9mIDAuMDEsIHJlbW92aW5nIHJhcmUgU05QcyBmcm9tIHRoZSBkYXRhc2V0Lg0KLSBgd2MgLWwgJEdfRklMRSR7Q0hSfV9jbGVhbl9xYy5iaW1gIGNvdW50cyBTTlBzIGFmdGVyIGFwcGx5aW5nIHRoZSBNQUYgZmlsdGVyIGFuZCBhcHBlbmRzIHRoaXMgY291bnQgdG8gYENvdW50c18ke0NIUn0udHh0YA0KDQojIyMgQ2xlYW4gdXAgVGVtcG9yYWN5IEZpbGVzOiANCmBgYHtiYXNofQ0Kcm0gc25wX2xpc3RfJHtDSFJ9LnR4dA0KYGBgDQoNCi0gYHJtIHNucF9saXN0XyR7Q0hSfS50eHRgIGRlbGV0ZXMgdGhlIHRlbXBvcmFyeSBmaWxlIGBzbnBfbGlzdF8ke0NIUn0udHh0YCwgd2hpY2ggaXMgbm8gbG9uZ2VyIG5lZWRlZCBhZnRlciBRQyBwcm9jZXNzaW5nDQoNCiMgU3RlcCA2OiBDb3VudCB0aGUgSW5mbyBmaWxlOiANCiMjIyBTZXQgdXAgZGlyZWN0b3JpZXMgYW5kIEZpbGVuYW1lczoNCmBgYHtiYXNofQ0KIyEvYmluL2Jhc2gNCiNQQlMgLWwgbm9kZXM9MTpwcG49OA0KbW9kdWxlIGxvYWQgZXh0cmFzDQoNCiMjc3VibWl0IGFzIHFzdWIgLXEgYmF0Y2ggLWwgd2FsbHRpbWU9MjowMDowMCAuLzZfY291bnRzX2ltcHV0YXRpb25faW5mb19maWxlcy5zaA0KDQpPVVRQVVRfRElSPSAvcGF0aC90by9vdXRwdXQNCg0KTE9DQUxfRElSPSRPVVRQVVRfRElSL3BhdGgvdG8vaGFwbG9fZmlsZSAjd2hlcmUgdGhlIGltcHV0ZWQgZmlsZXMgYXJlIGxvY2F0ZWQNCkxPQ0FMMV9ESVI9JE9VVFBVVF9ESVIvcGF0aC90by9JbXB1dGVkL2ZpbGUNCkZJTEVTX1BSRUZJWD0iSGFtaWx0b24iDQoNCk9VVEZJTEU9JHtGSUxFU19QUkVGSVh9Il9zdW1tYXJ5LnR4dCINCg0KY2QgJE9VVFBVVF9ESVINCg0KI3R5cGU9MCBpbXB1dGVkIGxvY2kNCiN0eXBlPTIgaGFyZCBnZW5vdHlwZSAoZnJvbSBwYW5lbCAyKSBidXQgd2l0aCBpbXB1dGVkIGluZm8gZnJvbSB0eXBlIDANCiN0eXBlPTMgaGFyZCBnZW5vdHlwZSAoZnJvbSBwYW5lbCAyKSBidXQgbm8gaW1wdXRhdGlvbiBkb25lIGZyb20gcmVmZXJlbmNlIHBhbmVsDQpgYGANCg0KLSBgT1VUUFVUX0RJUmAgc2V0cyB0aGUgbWFpbiBvdXRwdXQgZGlyZWN0b3J5DQotIGBMT0NBTF9ESVJgIGFuZCBgTE9DQUwxX0RJUmAgZGVmaW5lIHBhdGhzIGZvciBpbXB1dGVkIGhhcGxvdHlwZSBmaWxlcyBhbmQgUExJTkstcHJvY2Vzc2VkIGZpbGVzLCByZXNwZWN0aXZlbHkNCi0gYEZJTEVTX1BSRUZJWGAgc2V0cyB0aGUgcHJlZml4IGZvciB0aGUgb3V0cHV0IGZpbGVuYW1lcw0KLSBgT1VURklMRWAgc3BlY2lmaWVzIHRoZSBvdXRwdXQgZmlsZSBmb3IgUUMgc3VtbWFyeSBjb3VudHMNCg0KIyMjIENhbGN1bGF0ZSBUb3RhbCBJbXB1dGVkIE1hcmtlcnM6DQpgYGB7YmFzaH0NCiNyYXcgY291bnRzIG9mIGZpbGUgKGkuZS4gYWxsIGF0dGVtcHRlZCBpbXB1dGVkIGxvY2kpDQplY2hvIC1uICJUb3RhbCBudW1iZXIgb2YgbWFya2VycyBpbXB1dGVkOiAiID4gJE9VVEZJTEUNCnBpZ3ogLWRjICRPVVRQVVRfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3ogIHwgd2MgLWwgPj4gJE9VVEZJTEUNCmVjaG8gPj4gJE9VVEZJTEUNCmBgYA0KDQotIE5hdmlnYXRlcyB0byB0aGUgb3V0cHV0IGRpcmVjdG9yeSANCi0gQ291bnRzIGFsbCBtYXJrZXJzIGluIHRoZSBpbXB1dGF0aW9uIGludG8gZmlsZSBhbmQgd3JpdGVzIHRvIGBPVVRGSUxFYA0KDQotIGBjZCAkT1VUUFVUX0RJUmA6IENoYW5nZXMgdGhlIGN1cnJlbnQgZGlyZWN0b3J5IHRvIGBPVVRQVVRfRElSYA0KLSBgZWNobyAtbiAiVG90YWwgbnVtYmVyIG9mIG1hcmtlcnMgaW1wdXRlZDogIiA+ICRPVVRGSUxFYDogV3JpdGVzIHRoZSBzcGVjaWZpZWQgdGV4dCB0byBgT1VURklMRWAgd2l0aG91dCBhIG5ld2xpbmUgKGAtbmAgZmxhZyBzdXBwcmVzc2VzIG5ld2xpbmUpLg0KLSBgcGlneiAtZGMgJE9VVFBVVF9ESVIvJHtGSUxFU19QUkVGSVh9X2ltcHV0YXRpb25fbG9jaV9pbmZvLnR4dC5neiB8IHdjIC1sYDoNCiAgICAtIGBwaWd6IC1kY2A6IERlY29tcHJlc3NlcyBhIGd6aXBwZWQgZmlsZSAoYC50eHQuZ3pgKSB3aXRob3V0IGZ1bGx5IGV4dHJhY3RpbmcgaXQgdG8gZGlzaw0KICAgIC0gYCR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3pgOiBSZWZlcnMgdG8gdGhlIGNvbXByZXNzZWQgZmlsZSBjb250YWluaW5nIGltcHV0YXRpb24gbG9jdXMgaW5mb3JtYXRpb24uDQogICAgLSBgfCB3YyAtbGA6IENvdW50cyBsaW5lcywgcmVwcmVzZW50aW5nIHRoZSBudW1iZXIgb2YgbWFya2Vycy4NCi0gYD4+ICRPVVRGSUxFYDogQXBwZW5kcyB0aGUgbWFya2VyIGNvdW50IHRvIE9VVEZJTEUuDQoNCiMjIyBDYWxjdWxhdGUgSGlnaC1RdWFsaXR5IE1hcmtlcnM6DQpgYGB7YmFzaH0NCiNoaWdoIGluZm8gYW5kIGNlcnRhaW50eSBsb2NpIChiYXNlZCBvbiBpbmZvIGFuZCBjZXJ0YWludHkgZmllbGRzIGluIGltcHV0YXRpb24gLWNvbHMgNSBhbmQgNi0pDQplY2hvIC1uICJOdW1iZXIgb2YgbWFya2VycyBoaWdoIGluZm8gYW5kIGNlcnRhaW50eTogIiA+PiAkT1VURklMRQ0KcGlneiAtZGMgJE9VVFBVVF9ESVIvJHtGSUxFU19QUkVGSVh9X2ltcHV0YXRpb25fbG9jaV9pbmZvLnR4dC5neiAgfCBhd2sgJyQ1Pi45ICYmICQ2Pi45ICYmIE5SPjF7cHJpbnQgJDB9JyB8IHdjIC1sID4+ICRPVVRGSUxFDQplY2hvID4+ICRPVVRGSUxFDQpgYGANCg0KLSBGaWx0ZXJzIG1hcmtlcnMgd2l0aCBoaWdoIGluZm9ybWF0aW9uIGFuZCBjZXJ0YWludHkgc2NvcmVzIChib3RoID4wLjkpIGFuZCBhcHBlbmRzIHRoZSBjb3VudCB0byBgT1VURklMRWANCg0KLSBgZWNobyAtbiAiTnVtYmVyIG9mIG1hcmtlcnMgaGlnaCBpbmZvIGFuZCBjZXJ0YWludHk6ICIgPj4gJE9VVEZJTEVgOiBBcHBlbmRzIGEgZGVzY3JpcHRpb24gbGluZSB0byBPVVRGSUxFLg0KLSBgcGlneiAtZGMgJE9VVFBVVF9ESVIvJHtGSUxFU19QUkVGSVh9X2ltcHV0YXRpb25fbG9jaV9pbmZvLnR4dC5neiB8IGF3ayAnJDU+LjkgJiYgJDY+LjkgJiYgTlI+MXtwcmludCAkMH0nIHwgd2MgLWxgOg0KICAgIC0gYGF3ayAnJDU+LjkgJiYgJDY+LjkgJiYgTlI+MXtwcmludCAkMH0nYDogRmlsdGVycyBsaW5lcyB3aGVyZSBjb2x1bW5zIDUgKGluZm8gc2NvcmUpIGFuZCA2IChjZXJ0YWludHkgc2NvcmUpIGFyZSBib3RoIGdyZWF0ZXIgdGhhbiAwLjkuIGBOUj4xYCBza2lwcyB0aGUgaGVhZGVyIHJvdw0KICAgIC0gYHwgd2MgLWxgOiBDb3VudHMgdGhlIGZpbHRlcmVkIGxpbmVzIChtYXJrZXJzIG1lZXRpbmcgaGlnaC1xdWFsaXR5IHRocmVzaG9sZHMpDQo+PiBgJE9VVEZJTEVgOiBBcHBlbmRzIHRoZSBjb3VudCB0byBgT1VURklMRWANCg0KIyMjIENvdW50IEltcHV0ZWQgTWFya2VycyBQZXIgQ2hyb21vc29tZToNCmBgYHtiYXNofQ0KIyMjI1BlcmZvcm0gY291bnRzIGZyb20gaW1wdXRlZCwgaW1wdXRlZCBhZnRlciByZW1vdmluZyBkdXBsaWNhdGVzIGFuZCBxYydkIGxvY2kNCg0KZWNobyAiQ291bnRzIGZyb20gaW1wdXRhdGlvbiAoSW1wdXRlMiBvdXRwdXQpOiAiID4+ICRPVVRGSUxFDQpjb3VudF90b3Q9MA0KZm9yIGkgaW4gYHNlcSAxIDIyYA0KZG8NCiAgICBjb3VudD1gcGlneiAtZGMgJExPQ0FMX0RJUi9jaHIke2l9LyouZ3ogfCB3YyAtbGANCiAgICBlY2hvICRjb3VudCBjaHIke2l9ID4+ICRPVVRGSUxFDQogICAgY291bnRfdG90PSQoKGNvdW50X3RvdCArIGNvdW50KSkNCmRvbmUNCmVjaG8gJGNvdW50X3RvdCIgdG90YWwiID4+ICRPVVRGSUxFDQplY2hvID4+ICRPVVRGSUxFDQpgYGANCg0KLSBJdGVyYXRlcyB0aHJvdWdoIGNocm9tb3NvbWVzICgxLTIyKSwgY291bnRzIG1hcmtlcnMgaW4gZWFjaCBjaHJvbW9zb21lJ3MgaW1wdXRlZCBmaWxlcywgYW5kIGNhbGN1bGF0ZXMgYSB0b3RhbCBjb3VudA0KDQotIGBlY2hvICJDb3VudHMgZnJvbSBpbXB1dGF0aW9uIChpbXB1dGUyIG91dHB1dCk6ICIgPj4gJE9VVEZJTEVgOiBBZGRzIGEgaGVhZGluZyB0byBgT1VURklMRWAgZm9yIHRoZSBpbXB1dGVkIG1hcmtlciBjb3VudHMgYnkgY2hyb21vc29tZQ0KLSBgZm9yIGkgaW4gXHNlcSAxIDIyYGA6IExvb3BzIHRocm91Z2ggY2hyb21vc29tZSBudW1iZXJzIDEtMjIuDQotIGBwaWd6IC1kYyAkTE9DQUxfRElSL2NociR7aX0vKi5neiB8IHdjIC1sYDoNCiAgICAtIGBwaWd6IC1kYyAkTE9DQUxfRElSL2NociR7aX0vKi5nemA6IERlY29tcHJlc3NlcyBhbGwgaW1wdXRlZCBmaWxlcyBmb3IgY2hyb21vc29tZSBgaWANCiAgICAtIGB8IHdjIC1sYDogQ291bnRzIHRoZSB0b3RhbCBtYXJrZXJzDQotIGBlY2hvICRjb3VudCBjaHIke2l9ID4+ICRPVVRGSUxFYDogV3JpdGVzIHRoZSBjb3VudCBmb3IgZWFjaCBjaHJvbW9zb21lIHRvIGBPVVRGSUxFYA0KLSBgY291bnRfdG90PSQoKGNvdW50X3RvdCArIGNvdW50KSlgOiBBZGRzIGVhY2ggY2hyb21vc29tZeKAmXMgY291bnQgdG8gYGNvdW50X3RvdGAgZm9yIGEgZ3JhbmQgdG90YWwNCi0gYGVjaG8gJGNvdW50X3RvdCIgdG90YWwiID4+ICRPVVRGSUxFYDogV3JpdGVzIHRoZSB0b3RhbCBjb3VudCB0byBgT1VURklMRWANCg0KIyMjIENvdW50IENsZWFuZWQgR2Vub3R5cGVzOiANCmBgYHtiYXNofQ0KZWNobyAiQ291bnRzIGZyb20gY2FsbGVkIGdlbm90eXBlcyB3aXRoIGd0b29sIC45IHRocmVzaG9sZCBhZnRlciByZW1vdmluZyBkdXBsaWNhdGVzIGlmIGFueSAocGxpbmsgY29udmVyc2lvbik6ICIgPj4gJE9VVEZJTEUNCmE9KCQod2MgLWwgJExPQ0FMMV9ESVIvKl9jaHIqX2NsZWFuLmJpbSkpDQojbGVuZ3RoIG9mIHRoZSBhcnJheSAkeyNhW0BdfQ0KZm9yIGsgaW4gYHNlcSAwICQoKCR7I2FbQF19LTEpKWANCmRvDQppZiBbICQoKCRrICUgMikpIC1lcSAwIF07IHRoZW4NCiAgICBlY2hvIC1uICIke2FbJGtdfSAiID4+ICRPVVRGSUxFDQplbHNlDQogICAgZWNobyAke2FbJGtdfSA+PiAkT1VURklMRQ0KZmkNCmRvbmUNCmVjaG8gPj4gJE9VVEZJTEUNCmBgYA0KDQotIENvdW50cyBsb2NpIGluIGNsZWFuZWQgYC5iaW1gIGZpbGVzIGZvciBlYWNoIGNocm9tb3NvbWUsIHdpdGggZHVwbGljYXRlcyByZW1vdmVkLCB0aGVuIGFwcGVuZHMgY291bnRzIHRvIGBPVVRGSUxFYA0KDQotIGBlY2hvICJDb3VudHMgZnJvbSBjYWxsZWQgZ2Vub3R5cGVzIC4uLiJgOiBBZGRzIGEgaGVhZGluZyBmb3IgZ2Vub3R5cGUgY291bnRzIHBvc3QtZHVwbGljYXRlIHJlbW92YWwgdG8gT1VURklMRWANCi0gYGE9KCQod2MgLWwgJExPQ0FMMV9ESVIvKl9jaHIqX2NsZWFuLmJpbSkpYDogU3RvcmVzIGxpbmUgY291bnRzIG9mIGVhY2ggYC5iaW1gIGZpbGUgaW50byBhcnJheSBgYWANCi0gYGZvciBrIGluIFxzZXEgMCAkKCgkeyNhW0BdfS0xKSlgOiBMb29wcyB0aHJvdWdoIGluZGljZXMgb2YgYXJyYXkgYGFgDQotIGBpZiBbICQoKCRrICUgMikpIC1lcSAwIF1gOiBDaGVja3MgaWYgdGhlIGluZGV4IGlzIGV2ZW4gKGkuZS4sIGZpbGUgcGF0aCBhbmQgY291bnQgYXJlIGdyb3VwZWQpDQogICAgLSBgZWNobyAtbiAiJHthWyRrXX0gIiA+PiAkT1VURklMRWA6IFdyaXRlcyB0aGUgY291bnQgZm9yIGVhY2ggY2hyb21vc29tZSBvbiB0aGUgc2FtZSBsaW5lDQogICAgLSBgZWxzZSBlY2hvICR7YVska119ID4+ICRPVVRGSUxFYDogUHJpbnRzIGVhY2ggZmlsZW5hbWUgdG8gYSBuZXcgbGluZQ0KICAgIC0gYGVjaG8gPj4gJE9VVEZJTEVgOiBBZGRzIGEgYmxhbmsgbGluZQ0KDQojIyMgQ291bnQgTWFya2VycyBBZnRlciBRQyBGaWx0ZXJpbmc6IA0KYGBge2Jhc2h9DQplY2hvICJDb3VudHMgZnJvbSBpbXB1dGF0aW9uLCBhZnRlciBRQyAoSW5mbyAmIGNlcnRhaW50eSA+LjksIE1BRj4xJSBhbmQgZ2Vub3R5cGUgbWlzc2luZyByYXRlczw1JSk6ICIgPj4gJE9VVEZJTEUNCiNvZiBsb2NpIGFmdGVyIHFjIChpbmZvICYgY2VydGFpbnR5LCBtYWYsIGdlbm8gcmF0ZXMpIGFuZCBjbGVhbmluZw0KYj0oJCh3YyAtbCAkTE9DQUwxX0RJUi8qX2NocipfY2xlYW5fcWMuYmltKSkNCiNsZW5ndGggb2YgdGhlIGFycmF5ICR7I2FbQF19DQpmb3IgayBpbiBgc2VxIDAgJCgoJHsjYVtAXX0tMSkpYA0KZG8NCmlmIFsgJCgoJGsgJSAyKSkgLWVxIDAgXTsgdGhlbg0KICAgIGVjaG8gLW4gIiR7Ylska119ICIgPj4gJE9VVEZJTEUNCmVsc2UNCiAgICBlY2hvICR7Ylska119ID4+ICRPVVRGSUxFDQpmaQ0KZG9uZQ0KZWNobyA+PiAkT1VURklMRQ0KDQojdGFyIC16Y3ZmICRMT0NBTDFfRElSL0NPMjBfaW1wdXRlZF9jbGVhbl9xYy50YXIuZ3ogJExPQ0FMMV9ESVIvKl9jbGVhbl9xYy4qDQpgYGANCg0KLSBBZnRlciBRQyBmaWx0ZXJpbmcgZm9yIG1pbm9yIGFsbGVsZSBmcmVxdWVuY3ksIG1pc3NpbmcgcmF0ZSwgYW5kIGhpZ2ggaW5mbyBhbmQgY2VydGFpbnR5LCBjb3VudHMgbWFya2VycyBmb3IgZWFjaCBjaHJvbW9zb21lDQoNCi0gQ291bnQgbGluZXMgaW4gZWFjaCBRQy1wYXNzZWQgYC5iaW1gIGZpbGUNCiAgICAtIGB3YyAtbCAkTE9DQUwxX0RJUi8qX2NocipfY2xlYW5fcWMuYmltYDogUnVucyBgd2MgLWxgIG9uIGVhY2ggYC5iaW1gIGZpbGUgdGhhdCBwYXNzZWQgUUMgaW4gdGhlIGRpcmVjdG9yeSBgJExPQ0FMMV9ESVJgIChleHBlY3RlZCBmaWxlbmFtZXMgaW5jbHVkZSBgX2NocipfY2xlYW5fcWMuYmltYCkNCiAgICAtIGBiPSgkKC4uLikpYDogU3RvcmVzIHRoZSBvdXRwdXQgb2YgYHdjIC1sYCBpbiBhbiBhcnJheSwgYGJgLCB3aGVyZSBlYWNoIGVsZW1lbnQgaW4gdGhlIGFycmF5IGNvbnRhaW5zIGEgbGluZSBjb3VudCBmb3IgYSBjb3JyZXNwb25kaW5nIGAuYmltYCBmaWxlLg0KDQotIExvb3AgdGhyb3VnaCB0aGUgQXJyYXkgdG8gUHJpbnQgQ2hyb21vc29tZSBDb3VudHM6IA0KICAgIC0gYGZvciBrIGluIFxzZXEgMCAkKCgkeyNiW0BdfS0xKSlgOiBJdGVyYXRlcyBvdmVyIHRoZSBpbmRpY2VzIG9mIHRoZSBhcnJheSBgYmAsIHN0YXJ0aW5nIGZyb20gYDBgIHVwIHRvIHRoZSBsYXN0IGluZGV4IGAoJHsjYltAXX0tMScpYA0KICAgIC0gYGlmIFsgJCgoJGsgJSAyKSkgLWVxIDAgXWA6IENoZWNrcyBpZiB0aGUgaW5kZXggYGtgIGlzIGV2ZW4NCiAgICAgICAgLSBgZWNobyAtbiAiJHtiWyRrXX0gIiA+PiAkT1VURklMRWA6IEZvciBldmVuIGluZGljZXMsIHdyaXRlcyB0aGUgbGluZSBjb3VudCB0byBgT1VURklMRWAgb24gdGhlIHNhbWUgbGluZSwgZm9sbG93ZWQgYnkgYSBzcGFjZS4gVGhpcyBjb3VudCBjb3JyZXNwb25kcyB0byB0aGUgbnVtYmVyIG9mIG1hcmtlcnMgaW4gZWFjaCBjaHJvbW9zb21lDQogICAgICAgIC0gYGVsc2UgZWNobyAke2JbJGtdfSA+PiAkT1VURklMRWA6IEZvciBvZGQgaW5kaWNlcywgYXBwZW5kcyB0aGUgY2hyb21vc29tZSBpZGVudGlmaWVyIChlLmcuLCBgY2hyMWAsIGBjaHIyYCkgdG8gYE9VVEZJTEVgLCBmb2xsb3dlZCBieSBhIG5ld2xpbmUNCg0KIyMjIENyZWF0ZSBDb25jb3JkYW5jZSBTdW1tYXJ5OiANCmBgYHtiYXNofQ0KI2JyaW5nIGNvbmNvcmRhbmNlIGluZm8gZnJvbSBmaWxlcw0KZ3JlcCAiMC4wLTAuMSIgJExPQ0FMX0RJUi9jaHIqLypfc3VtbWFyeSB8IGF3ayAne3ByaW50ICQxLCQ5fScgfCBwaWd6ID4gJE9VVFBVVF9ESVIvJHtGSUxFU19QUkVGSVh9X2ltcHV0YXRpb25fY29uY29yZGFuY2UudHh0Lmd6DQoNCg0KI1N1bW1hcmllcyBvZiBjb25jb3JkYW5jZSwgaW5mbyBhbmQgY2VydGFpbnR5DQpwaWd6IC1kYyAkT1VUUFVUX0RJUi8ke0ZJTEVTX1BSRUZJWH1faW1wdXRhdGlvbl9jb25jb3JkYW5jZS50eHQuZ3ogfCBhd2sgJ3twcmludCAkMn0nIHwgYXdrICd7Zm9yKGk9MTtpPD1ORjtpKyspIHtzdW1baV0gKz0gJGk7IHN1bXNxW2ldICs9ICgkaSleMn19IEVORCB7Zm9yIChpPTE7aTw9TkY7aSsrKSB7cHJpbnQgIkNvbmNvcmRhbmNlIHN0YXRzOiAiLCBzdW1baV0vTlIsIHNxcnQoKHN1bXNxW2ldLXN1bVtpXV4yL05SKS8oTlItMSkpfX0nID4+ICRPVVRGSUxFDQoNCnBpZ3ogLWRjICRPVVRQVVRfRElSLyR7RklMRVNfUFJFRklYfV9pbXB1dGF0aW9uX2xvY2lfaW5mby50eHQuZ3ogfCBhd2sgJ05SPjEge3ByaW50ICQ1fScgfCBhd2sgJ3tmb3IoaT0xO2k8PU5GO2krKykge3N1bVtpXSArPSAkaTsgc3Vtc3FbaV0gKz0gKCRpKV4yfX0gRU5EIHtmb3IgKGk9MTtpPD1ORjtpKyspIHtwcmludCAiSW5mbyBzdGF0czogIiwgc3VtW2ldL05SLCBzcXJ0KChzdW1zcVtpXS1zdW1baV1eMi9OUikvKE5SLTEpKX19JyA+PiAkT1VURklMRQ0KDQpwaWd6IC1kYyAkT1VUUFVUX0RJUi8ke0ZJTEVTX1BSRUZJWH1faW1wdXRhdGlvbl9sb2NpX2luZm8udHh0Lmd6IHwgYXdrICdOUj4xIHtwcmludCAkNn0nIHwgYXdrICd7Zm9yKGk9MTtpPD1ORjtpKyspIHtzdW1baV0gKz0gJGk7IHN1bXNxW2ldICs9ICgkaSleMn19IEVORCB7Zm9yIChpPTE7aTw9TkY7aSsrKSB7cHJpbnQgIkNlcnRhaW50eSBzdGF0czogIiwgc3VtW2ldL05SLCBzcXJ0KChzdW1zcVtpXS1zdW1baV1eMi9OUikvKE5SLTEpKX19JyA+PiAkT1VURklMRQ0KYGBgDQoNCi0gU2VhcmNoZXMgZm9yIHNwZWNpZmljIGNvbmNvcmRhbmNlIGluZm8gZnJvbSBpbXB1dGF0aW9uIHN1bW1hcmllcyBhbmQgY29tcHJlc3NlcyB0aGUgb3V0cHV0IGZvciBsYXRlciBzdGF0aXN0aWNhbCBhbmFseXNpcw0KDQotIFNlYXJjaCBmb3IgdGhlIHNwZWNpZmljIGNvbmNvcmRhbmNlIGluZm8gZnJvbSBpbXB1dGF0aW9uDQogICAgLSBgZ3JlcCAiMC4wLTAuMSIgJExPQ0FMX0RJUi9jaHIqLypfc3VtbWFyeWA6IFNlYXJjaGVzIGZvciBsaW5lcyBpbiBgX3N1bW1hcnlgIGZpbGVzIHRoYXQgbWVudGlvbiBhIGNvbmNvcmRhbmNlIHJhbmdlIGAwLjAtMC4xYA0KICAgIC0gYGF3ayAne3ByaW50ICQxLCQ5fSdgOiBFeHRyYWN0cyBjb2x1bW5zIDEgYW5kIDkNCiAgICAtIGBwaWd6ID4gJE9VVFBVVF9ESVIvJHtGSUxFU19QUkVGSVh9X2ltcHV0YXRpb25fY29uY29yZGFuY2UudHh0Lmd6YDogQ29tcHJlc3NlcyB0aGUgZXh0cmFjdGVkIGluZm9ybWF0aW9uIGludG8gYGltcHV0YXRpb25fY29uY29yZGFuY2UudHh0Lmd6YA0KICAgIA0KLSBDYWxjdWxhdGUgdGhlIENvbmNvcmRhbmNlIChDb2x1bW4gMikNCiAgICAtIGBwaWd6IC1kYyAkT1VUUFVUX0RJUi8ke0ZJTEVTX1BSRUZJWH1faW1wdXRhdGlvbl9jb25jb3JkYW5jZS50eHQuZ3pgOiBEZWNvbXByZXNzZXMgdGhlIGNvbmNvcmRhbmNlIGZpbGUNCiAgICAtIGBhd2sgJ3twcmludCAkMn0nYDogRXh0cmFjdHMgdGhlIHNlY29uZCBjb2x1bW4NCiAgICAtIFRoZSBzdWJzZXF1ZW50IGBhd2tgIHN0YXRlbWVudCBjYWxjdWxhdGVzIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb246DQogICAgICAgIC0gYGZvcihpPTE7aTw9TkY7aSsrKSB7c3VtW2ldICs9ICRpOyBzdW1zcVtpXSArPSAoJGkpXjJ9YDogU3VtcyB2YWx1ZXMgYW5kIHNxdWFyZXMgZm9yIHN0YW5kYXJkIGRldmlhdGlvbg0KICAgICAgICAtIGBwcmludCAiQ29uY29yZGFuY2Ugc3RhdHM6ICIsIHN1bVtpXS9OUiwgc3FydCgoc3Vtc3FbaV0tc3VtW2ldXjIvTlIpLyhOUi0xKSlgOiBQcmludHMgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIGNvbHVtbg0KDQotIFNpbWlsYXIgZm9yIGNvbHVtbiA1IChpbmZvIHNjb3JlKSBhbmQgY29sdW1uIDYgKGNlcnRhaW50eSkNCg==