Authors
Lexi E. Frank1,2*, Nicole Flack1,2, Christopher Faulk3, Alyssa J.
Block4, 5, Jason C. Bartz2,4, Peter A. Larsen1,2
1 Department of Veterinary and Biomedical Sciences, University of
Minnesota, St. Paul, MN, U.S. 55108, 2 Minnesota Center for Prion
Research and Outreach, St. Paul, MN, U.S. 55108, 3 Department of Animal
Science, University of Minnesota, St. Paul, MN, U.S. 55108, 4 Department
of Medical Microbiology and Immunology, School of Medicine, Creighton
University, Omaha, NE, U.S., 5 Current Address: Department of
Microbiology, Immunology, and Pathology, Colorado State University, Fort
Collins, CO, U.S. 80523
*Corresponding Author
Lexi E. Frank
fran1464@umn.edu
Department of Veterinary and Biomedical Sciences
1971 Commonwealth Ave. St. Paul, MN 55108
OCRiD: 0009-0006-7736-4146
Published in Acta Neuropathologica
Experiment Details
Samples: DNA extracted from Mesocricetus auratus (syrian
hamsters) brain homogenate.
Extraction method: Qiagen MagAttract kit
Sequencing Instrument: Promethion 2 solo, R.10 Promethion flow
cell
Library Preparation: SQK-LSK114, SQK-NBD114.24, SQK-NBD114.96
The experiment for this study had 3 controls and 3 treated hamsters
per time point. There were 3 time points.
DNA Methylation Analysis
File Manipulation
Sort BAM files using samtools
samtools sort -@ 32 hamster.bam -o hamster.sorted.bam
Index Sorted BAMs
Index file is needed for modkit pileup in the next step.
samtools index -@ 32 hamster.sorted.bam
Create .bed Methyl Files with modkit pileup
You need the sorted BAMs with samtools index files (.bai) in the
folder.
modkit pileup -t 32 --cpg --ref GCF_017639785.1_BCM_Maur_2.0_genomic.fa --combine-strands --only-tabs hamster.sorted.bam hamster.modkit.bed
Awk to Separate 5-hydroxymethylcytosine (5hmC) and 5-methylcytosine
(5mC) Data
This works for all files in a directory.
for FILE in *.modkit.bed ; do
PREFIX=$(echo ${FILE} | sed 's/\.bed//')
awk -F'\t' '($4=="h") {print $0}' ${FILE} > ${PREFIX}.h.bed
awk -F'\t' '($4=="m") {print $0}' ${FILE} > ${PREFIX}.m.bed
done
Global Methylation Calculation
This works to create a .txt file for each individual with the
proportion of canonical Cs, 5mc, and 5hmcs
for FILE in hamster*.modkit.m.bed ; do
PREFIX=$(echo ${FILE} | sed 's/\.bed//')
awk '{can+=$13; mod+=$12; oth+=$14; valid+=$10} END{print (can/valid) " CpG canonical\n" (mod/valid) " 5mCpG modified \n" (oth/valid) " 5hmCpG modified"}' ${FILE} > ${PREFIX}.txt
done
Global Methylation Differences Across Time Points
Calculate if there is a significant difference between global
methylation levels across time points. Requires sample_info
object defined below.
global_methyl_files <- list.files("modkit_out_m", "*modkit.m.txt",
full.names = TRUE)
names(global_methyl_files) <- str_extract(global_methyl_files, "hamster\\d{1,2}")
global_methyl <- map_dfr(as.list(global_methyl_files),
~ read_delim(.x,
delim = " ",
col_names = c("ratio", "modification", "note"),
show_col_types = FALSE),
.id = "ID") %>%
filter(modification != "CpG") %>%
inner_join(sample_info, by = "ID") %>%
group_by(timepoint, modification) %>%
nest() %>%
mutate(ttest = map(data, ~ t.test(ratio ~ group, data = .x))) %>%
mutate(pvalue = map_chr(ttest, ~ pluck(.x, "p.value")))
global_methyl
Differential Methylation Analysis with Methylkit
Use Methylkit for
differential DNA methylation analysis. See methylkit
vignette.
Create Sample Info
Create a file that contains information such as sample and file
names, treatment, time point, etc.
Read in Data
Load in files with methylation data into R studio.
# modkit output column specs
modkit_cols <- list(
fraction = FALSE,
chr.col = 1,
start.col = 2,
end.col = 3,
coverage.col = 5,
strand.col = 6,
freqC.col = 11
)
# use the file names to load the data
mk_obj <- methRead(
as.list(sample_info$file),
sample.id = as.list(sample_info$ID),
assembly = "hamster",
pipeline = modkit_cols,
treatment = sample_info$treatment_binary,
mincov = 10
)
mk_obj
Summery stats
Coverage
# Iterate over the GRangesList to plot all of the samples
map(mk_obj, getCoverageStats, plot = T, both.strands = F)
# Print one sample's coverage data. You can do both.strands = T to check strand bias.
getCoverageStats(mk_obj[[2]], plot = T, both.strands = F)
Methylation Histogram
In a histogram of methylation percentages across a given sample, we
expect a large number of low and high methylation CpGs (peaks on left
and right ends) with less density across the middle.
getMethylationStats(mk_obj[[1]],
plot = T,
both.strands = F
)
# Iterate across all
map(mk_obj, getMethylationStats, plot = T, both.strands = F)
Normalization and Coverage Filtering
Normalizing coverage between samples is useful for avoiding bias when
coverage across samples is uneven. Filtering anomalously high or low
coverage is also helpful.
mk_obj_normal <- filterByCoverage(mk_obj,
lo.count = 10,
hi.perc = 99.9) %>%
normalizeCoverage(.)
Recheck the summary stat plots after normalization and filtering.
Setting both.strands = T can detect strand bias.
# Print one sample's coverage data
getCoverageStats(mk_obj_normal[[2]], plot = T, both.strands = F)
# one sample's methylation stats
getMethylationStats(mk_obj_normal[[2]], plot = T, both.strands = F)
# Iterate over the GRangesList to plot all of the samples
map(mk_obj_normal, getMethylationStats, plot = T, both.strands = F)
# Print one sample's coverage data. You can do both.strands = T to check strand bias.
getCoverageStats(mk_obj_normal[[2]], plot = T, both.strands = F)
Unite
This operation selects CpGs that are covered in all samples.
# Destrand = F allows us to examine DMRs
mk_m_meth <-
methylKit::unite(mk_obj_normal, destrand = F)
Tiling for Differentially Methylated Regions (DMRs)
We choose windows of 1,000 bases for our regional analysis.
DMRs per Time Point
Separate data for each time point. Time point 3 used as an
example.
mk_windows_1000 <- tileMethylCounts(mk_m_meth,win.size=1000,step.size=1000,cov.bases = 3, mc.cores = 10)
## reorganize data to include only timpepoint 3 (do this for all time points)
mk_windows_1000_reorg3 <- reorganize(mk_windows_1000, sample.ids = c("hamster11", "hamster17", "hamster18", "hamster2", "hamster6", "hamster7"), treatment = c(0,0,1,1,0,1), save.db = TRUE)
# diff meth statistics for all regions (do this for all time points)
diff_mkwindows3_1000 <- calculateDiffMeth(mk_windows_1000_reorg3,
mc.cores = 6)
# filter for significant regions (do this for all time points)
delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000,
difference = 10,
qvalue = 0.05)
Get Stats for Methylation
Do this for each time point. Time point 3 used as an example.
# Separate out hyper and hypomethylation
# get hyper-methylated
hyper_delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000, difference=10,qvalue=0.05,type="hyper")
# get hypo-methylated
hypo_delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000, difference=10,qvalue=0.05,type="hypo")
## make it a data frame
delta_mkdfwindows3_1000 <- getData(delta_mkwindows3_1000)
hypo_delta_mkdfwindows3_1000 <- getData(hypo_delta_mkwindows3_1000)
hyper_delta_mkdfwindows3_1000 <- getData(hyper_delta_mkwindows3_1000)
## make the background a dataframe
diff_mkdfwindows3df_1000 <- getData(diff_mkwindows3_1000)
##summary stats, hyper or hypo and how much, genic region enrichment (i.e. exons, promoters)
## summary stats,number and percent average hyper and hypo, and absolute
summarise(delta_mkdfwindows3_1000, mean(abs(meth.diff)))
##summary of count of all
nrow(delta_mkdfwindows3_1000)
## mean of hypermethylated regions
delta_mkdfwindows3_1000 %>%
filter(meth.diff > 0) %>%
summarize(mean(meth.diff))
## calculate count of hypermethylation
delta_mkdfwindows3_1000 %>%
summarize(count = sum(meth.diff > 0))
## mean of hypomethylated regions
delta_mkdfwindows3_1000 %>%
filter(meth.diff < 0) %>%
summarize(mean(meth.diff))
## calculate count of hypomethylation
delta_mkdfwindows3_1000 %>%
summarize(count = sum(meth.diff < 0))
## make a histogram
hist(delta_mkdfwindows3_1000$meth.diff)
## see distinct chr
delta_mkdfwindows3_1000 %>% distinct(chr)
## see shared chr DMRs between timepoint
delta_mkdfwindows3_1000 %>% inner_join(delta_mkdfwindows1_1000, by = c('chr', 'start')) %>% nrow()
Clustering of Individuals
PCA
Setting transpose = FALSE (default is TRUE)
turns this into a “regular” PCA plot, where you’re looking at the
contribution of samples to the variance.
PCASamples(mk_m_meth, transpose = FALSE)
PCA Plot using ggplot2
##create the objects to make the pca
pca.obj = PCASamples(mk_m_meth, transpose = FALSE, obj.return = TRUE)
pca.obj2 <- pca.obj$rotation[, 1:2] %>% as.data.frame()
##ggplot
p1<- ggplot(pca.obj2, aes(PC1, PC2)) +
geom_point(aes(color=sample_info$treatment)) +
geom_text_repel(label=rownames(pca.obj2), max.overlaps = 15, aes(color=sample_info$treatment)) +
ggtitle("Hamster CpG 5mc Methylation PCA plot") +
theme_bw()
p1
Dendrogram
clusterSamples(mk_m_meth)
Scree plot
PCASamples(mk_m_meth, screeplot = TRUE)
Genomation
Use Genomation
for summary and annotation of genomic intervals.
RefSeqAll track downloaded from UCSC Genome Browser on 27 march 2024
as “all fields”. Replace ID column with symbol to retain in annotation
object:
awk 'BEGIN {OFS="\t"} NR>1 {$4=$13; NF=12; print}' BCM_Maur_2.0_Mar2021_refseqall.bed > BCM_Maur_2.0_Mar2021_refseqall_symbol.bed
Create Annotation Object
This can be done by treatment, time point, etc., comparing the group
to the background.
gene_obj <-
genomation::readTranscriptFeatures("~/Desktop/reference_data/BCM_Maur_2.0_Mar2021_refseqall_bed12.bed", remove.unusual = F)
hyper_delta3W_gr <- makeGRangesFromDataFrame(hyper_delta_mkdfwindows3_1000, keep.extra.columns = T)
# export as a bed file
library(rtracklayer)
export.bed(hyper_delta3W_gr, con = 'hyper_delta3W.bed')
## background
diff3W_gr <- makeGRangesFromDataFrame(diff_mkdfwindows3df_1000, keep.extra.columns = T)
# annotate
annot <- annotateWithGeneParts(delta3W_gr, gene_obj, intersect.chr = T)
getTargetAnnotationStats(annot)
plotTargetAnnotation(annot)
## annotate background
annot_null <- annotateWithGeneParts(diff3W_gr, gene_obj, intersect.chr = T)
getTargetAnnotationStats(annot_null)
plotTargetAnnotation(annot_null)
Chi-Square Test
# Generate pie charts for DMRs and background
##par(mfrow = c(1,2))
##plotTargetAnnotation(annot,
##main = "DMRs by Genic Region")
##plotTargetAnnotation(annot_null,
##main = "All CpGs by Genic Region")
# Count DMRs that overlap with target gene features vs. background
dmr3W_stats <- genomation::getTargetAnnotationStats(annot, percentage = F)
null3W_stats <- genomation::getTargetAnnotationStats(annot_null,
percentage = F)
dmr_null_matrix3W <- rbind(dmr3W_stats, null3W_stats)
# 2-way table chisq function
test_annot_matrix <- function(x,y){
matrix <- x %>%
data.frame %>%
rownames_to_column(var = "source") %>%
mutate(other_features = rowSums(across(!c(source, all_of(y))))) %>%
dplyr::select(source, all_of(y), other_features) %>%
column_to_rownames(var = "source") %>%
as.matrix()
test <- chisq.test(matrix)
return(list(matrix = matrix, test = test))
}
# Calculate enrichment in genic regions vs. background
map(list("promoter", "exon", "intron"),
~ test_annot_matrix(dmr_null_matrix3W, .x))
Get Top DMR-Associated Genes
annot_bed <- read_tsv("~/Desktop/reference_data/BCM_Maur_2.0_Mar2021_refseqall.bed", show_col_types = F) %>%
dplyr::select(name, name2)
topgenes <- annot@dist.to.TSS %>%
left_join(annot_bed, by = c("feature.name" = "name")) %>%
group_by(name2) %>%
tally() %>%
arrange(desc(n))
topgenes
## wrtie a tsv of top DMR associated genes
write_tsv(topgenes, file = "topDMRassociatedgenes_tp1.tsv")
Lift Over Genes to Enhance Reference
Use NCBI genome, lift over mm39 (Mus musculus) genes to
hamster due to lack of dedicated gene ontology resources for the
hamster.
Installed LiftOff
version 1.6.3 via conda: conda install -c bioconda liftoff.
Downloaded GFFs from NCBI.
liftoff -g ../GCF_000001635.27_GRCm39_genomic.gff -o mm39_lift_to_Maur2.gff ../GCF_017639785.1_BCM_Maur_2.0_genomic.fna ../GCF_000001635.27_GRCm39_genomic.fna
Get Genome Data
# pull scaffold info and gene IDs for most recent hamster assembly
lt = getGenomeDataFromNCBI("GCF_017639785.1", return_granges = FALSE)
# generate named vector to translate RefSeq scaffold IDs to Genbank
seqlevels_ncbi2genbank = structure(lt$genome$genbankAccession,
names = lt$genome$refseqAccession)
# isolate data frames from list and edit to gene ID column name
df_genome = lt$genome
df_genes = lt$gene %>% dplyr::rename(gene_id = `EntreZ ID`)
# make gene data into GRanges object
genes = makeGRangesFromDataFrame(df_genes,
seqnames.field = "RefSeq Contig Accession",
start.field = "Start",
end.field = "End",
keep.extra.columns = TRUE)
# rename GRanges object seqlevels (chromosome/scaffold IDs)
genes <- renameSeqlevels(genes, seqlevels_ncbi2genbank)
# assign seqlevel and seqlength metadata using genome data
seqlevels(genes) = df_genome$genbankAccession
seqlengths(genes) = df_genome$length
# get rds files
delta3W_gr <- readRDS("delta3W_gr.rds")
# translate NCBI seqnames to GenBank in for all timepoints
deltaW_gr_list <- list(delta1W_gr, delta2W_gr, delta3W_gr)
names(deltaW_gr_list) <- c('delta1W_gr', 'delta2W_gr', 'delta3W_gr')
deltaW_gr_list <- map(deltaW_gr_list,
~ renameSeqlevels(.x, seqlevels_ncbi2genbank))
# genome = lt$genome
# genome = genome[!is.na(genome[, 2]), ]
df_genome$start = 1
df_genome$strand = "+"
genome = GRanges(
seqnames = df_genome[, 2],
ranges = IRanges(df_genome[, 6], df_genome[, 5]),
strand = df_genome[, 4],
gene_id = df_genome[, 5]
)
genome <- renameSeqlevels(genome, seqlevels_ncbi2genbank)
seqlevels(genome) = df_genome$genbankAccession
seqlengths(genome) = df_genome$length
Find Closest Genes to DMRs
Use bedtools
closests to find the intersecting/nearest gene to differentially
methylated regions (in bed file format) from bed file of liftoff
annotation to get a list for enrichment analysis (DAVID). Do this for
all time points data. Time point 3 is used as an example here.
# All data for one time point
delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000, difference=10,qvalue=0.05)
# Separate out hyper and hypo
# get hyper-methylated
hyper_delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000, difference=10,qvalue=0.05,type="hyper")
# get hypo-methylated
hypo_delta_mkwindows3_1000 <- getMethylDiff(diff_mkwindows3_1000, difference=10,qvalue=0.05,type="hypo")
## make it a data frame
delta_mkdfwindows3_1000 <- getData(delta_mkwindows3_1000)
hypo_delta_mkdfwindows3_1000 <- getData(hypo_delta_mkwindows3_1000)
hyper_delta_mkdfwindows3_1000 <- getData(hyper_delta_mkwindows3_1000)
## Make it a granges
hypo_delta3W_gr <- makeGRangesFromDataFrame(hypo_delta_mkdfwindows3_1000, keep.extra.columns = T)
hyper_delta3W_gr <- makeGRangesFromDataFrame(hyper_delta_mkdfwindows3_1000, keep.extra.columns = T)
#export as a bed file
library(rtracklayer)
export.bed(hyper_delta3W_gr, con = 'hyper_delta1W.bed')
export.bed(hypo_delta3W_gr, con = 'hypo_delta1W.bed')
export.bed(delta3W_gr, con = 'delta1W.bed')
# In command line
# make reference bed only contain genes
# awk -F'\t' '{if($8=="gene")print $0}' mm39_lift_to_Maur2.bed > genes_mm39_lift_to_Maur2.bed
# Find closest gene to methylkit DMR output and only print column with gene name information
bedtools closest -a hypo_delta3W.bed -b genes_mm39_lift_to_Maur2.bed | awk '{"\t"; print $16}' > genes_hypo_delta3W.tsv
# Find closest gene to methylkit DMR output and print everything
bedtools closest -a hypo_delta3W.bed -b genes_mm39_lift_to_Maur2.bed > ALLgenes_hypo_delta3W.tsv
# sed to print only lines with no .
sed '/^[[:space:]]*\.[[:space:]]*$/d' genes_delta3W.tsv > cleaned_genes_delta3W.tsv
# awk to only print $2 column separated by ;
awk -F';' '{print $2}' cleaned_genes_delta3W.tsv > 2cleaned_genes_delta3W.tsv
# sed to print only gene name
sed -n 's/.*GeneID:\([0-9]*\),.*/\1/p' 2cleaned_genes_delta3W.tsv > final_genes_delta3w.tsv
Take this list of genes into DAVID.
GO Term Visualization
Visualize DAVID
output with ggplot2.
library(ggplot2)
# load in data
meth_alltp_DAVID_BP <- read.csv("~/Desktop/DAVID/meth_alltp_0.05_DAVID_BP.csv")
subset_meth_alltp_DAVID_BP <- subset(meth_alltp_DAVID_BP, `Benjamini` < 0.002)
## plot
ggplot(data = subset_meth_alltp_DAVID_BP, aes(x = Group, y = Term, color = `Benjamini`, size = Count)) + geom_point() + scale_color_gradient(low = "red", high = "blue", name = "Benjamini-Hochberg\n Adjusted P-Value") + theme_bw() + ylab("Gene Ontology Term") + xlab("") + ggtitle("Gene Ontology Enrichment by Time Point") + theme(axis.text.y = element_text(size=12, colour = "black")) + theme(axis.title.y = element_text(size=18, face="bold", colour = "black")) + theme(plot.title = element_text(size = 20, face = "bold",hjust = 0.5) ) + theme(axis.text.x = element_text(face="bold",size=12, colour = "black"))
## save plot
ggsave("meth_GOenrich_pvalue_0.002.jpeg", plot = last_plot(), path = "/home/Desktop/DAVID", scale = 1, width=17, height=10, units='in', dpi = 300)
RNAseq Analysis
RNAseq data was generated with Dual-indexed libraries using
Takara/Clontech Stranded Total RNA-seq kit v2 - Pico Input Mammalian
reagents.
Fastqc
Use fastqc
v0.12.1 to check quality of raw reads.
gunzip 1_Shield_S2_L003_R1_001.fastq.gz
fastqc 1_Shield_S2_L003_R1_001.fastq --out fastqc_out
Trimming Sequencing Adapters
Use BBduk from BBmap v35.85 to trim
the sequencing adapters.
# bbduk
bbduk.sh in1=read1.fq in2=read2.fq out1=clean1.fq out2=clean2.fq
# bbduk for one set of paired ends
~/Desktop/bbmap/bbduk.sh -Xmx1g ref= ~/Desktop/adapters.fa in1=10_Shield_S9_L003_R1_001.fastq in2=10_Shield_S9_L003_R2_001.fastq out1=10_Shield_S9_L003_R1_001_trim.fastq out2=10_Shield_S9_L003_R2_001_trim.fastq
# bbduk loop for multiple sets of paired end samples
for R1 in *R1*
do
R2=${R1//R1_001.fastq.gz/R2_001.fastq.gz}
R1trim=${R1//.fastq.gz/_trim.fastq.gz}
R2trim=${R2//.fastq.gz/_trim.fastq.gz}
~/Desktop/bbmap/bbduk.sh -Xmx1g ref= ~/Desktop/bbmap/resources/adapters.fa in1=$R1 in2=$R2 out1=$R1trim out2=$R2trim
done
Align to Reference Genome with STAR
Use STAR aligner to
align sequence data to a reference genome.
Generate Genome Index
# for GFF instead of GTF file use --sjdbGTFtagExonParentTranscript instead of --sjdbGTFfile
STAR --runThreadN 30 --runMode genomeGenerate --genomeDir ~/Desktop/rnaseq_hamster/genomeDir --genomeFastaFiles ~/Desktop/rnaseq_hamster/reference_data/GCF_017639785.1_BCM_Maur_2.0_genomic.fa --sjdbGTFfile ~/Desktop/rnaseq_hamster/liftoff_out/mm39_lift_to_Maur2.gff
Align
Align one sample’s paired end files.
mkdir alignments
STAR --runThreadN 30 \
--genomeDir ~/Desktop/rnaseq_hamster/genomeDir \
--readFilesIn 1_Shield_S2_L003_R1_001_trim.fastq.gz 1_Shield_S2_L003_R2_001_trim.fastq.gz \
--readFilesCommand zcat \
--outSAMtype BAM SortedByCoordinate \
--quantMode GeneCounts \
--outFileNamePrefix alignments/1_Shield_S2_L003
Loop to align files R1 and R2 for all samples at once.
# define variables
index=~/Desktop/rnaseq_hamster/genomeDir/
for i in *_001_trim.fastq.gz
do
PREFIX=$(echo ${i} | sed 's/\_R._001_trim.fastq.gz//')
echo $PREFIX
# define R1 fastq filename
R1=~/Desktop/rnaseq_hamster/rnaseq_fastq/trim_bbduk/${PREFIX}_R1_001_trim.fastq.gz
echo $R1
# define R2 fastq filename
R2=~/Desktop/rnaseq_hamster/rnaseq_fastq/trim_bbduk/${PREFIX}_R2_001_trim.fastq.gz
echo $R2
# align with STAR
STAR --runThreadN 30 \
--genomeDir $index \
--readFilesIn ${R1} ${R2} \
--outSAMtype BAM SortedByCoordinate \
--quantMode GeneCounts \
--readFilesCommand zcat
--outFileNamePrefix alignments/${PREFIX}_
done
echo "done!"
Use featureCounts to Count Transcripts
Use featureCounts
v2.0.3 to count mapped reads.
featureCounts -M -p -t exon -F GFF -g "ID" -a ~/Desktop/rnaseq_hamster/liftoff_out/mm39_lift_to_Maur2.gff -o ~/Desktop/rnaseq_hamster/featurecounts_out/1_M_counts.txt ~/Desktop/rnaseq_hamster/rnaseq_fastq/trim_bbduk/alignments/test/1_Shield_S2_L003Aligned.sortedByCoord.out.bam
DESeq2
Use DESeq2
to determine differential methylation.
Import Data
It is VERY important that the rows of coldata are in the same order
as the columns of the featurecounts matrix
# read in metadata file
coldata <- read.csv("sample_data.csv", row.names=1)
# manipulate colData
coldata <- coldata[,c("treatment","timepoint","group")]
coldata$treatment <- factor(coldata$treatment)
coldata$timepoint <- factor(coldata$timepoint)
coldata$group <- factor(coldata$group)
# read in feature counts file
featurecounts <- read.delim("featurecounts_out/featurecounts/all_counts.txt", header = TRUE, skip = 1)
# change the column names to match coldata
colnames(featurecounts)
colnames(featurecounts) <- c("Geneid", "Chr", "Start", "End", "Strand", "Length", "10", "11", "12", "13","14","15","16","17","18","1","20","3","4","5","6","7","8","9")
# make row names the Geneid column
#rownames(featurecounts) <- featurecounts[ , 1]
#featurecounts = as.matrix(featurecounts[ , -1])
# the first column right of Length is where the counts start, until the end of the matrix
column.from = which(colnames(featurecounts) == "Length") + 1
column.end = ncol(featurecounts)
Create DESeq Dataset
# import into DESeq2 matrix
dds <-
DESeqDataSetFromMatrix(countData = featurecounts[, column.from:column.end],
colData = coldata,
design = ~ treatment)
# add gene names as row names to dds
rownames(dds) <- featurecounts$Geneid
dds
# look at assay
assay(dds)
# look at assay names
assayNames(dds)
# examine count sums of all individual samples gene expression
colSums(assay(dds))
Data Exploration
Explore the data following the DESeq2 vignettes.
Pre-filtering for Data Exploration
Perform pre-filtering to keep only rows that have a count of at least
10 for a minimal number of samples. For data exploration only, NOT for
statistical analysis.
nrow(dds)
# pre-filtering to keep only rows that have a count of at least 10 for a minimal number of samples
smallestGroupSize <- 3
keep <- rowSums(counts(dds) >= 10) >= smallestGroupSize
dds_ex <- dds[keep,]
nrow(dds_ex)
Compare Sample Distances
To ensure we have a roughly equal contribution from all genes, we use
it on the VST data. calculate the Euclidean distance between
samples.
sampleDists <- dist(t(assay(vsd)))
sampleDists
# make the heatmap of sample distances using euclidean distances
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste( vsd$treatment, vsd$timepoint, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
clustering_distance_rows = sampleDists,
clustering_distance_cols = sampleDists,
col = colors)
Calculate Sample Distance using Poisson
This measure of dissimilarity between counts takes the inherent
variance structure of counts into consideration when calculating the
distances between samples.
library("PoiClaClu")
poisd <- PoissonDistance(t(counts(dds_ex)))
# Heatmap of sample-to-sample distances using the Poisson Distance.
samplePoisDistMatrix <- as.matrix( poisd$dd )
rownames(samplePoisDistMatrix) <- paste( dds_ex$treatment, dds_ex$timepoint, sep=" - " )
colnames(samplePoisDistMatrix) <- NULL
pheatmap(samplePoisDistMatrix,
clustering_distance_rows = poisd$dd,
clustering_distance_cols = poisd$dd,
col = colors)
PCA
Principle Component Analysis of the VSD transformed data.
plotPCA(vsd, intgroup=c("treatment", "timepoint"))
# PCA but more detailed (using ggplot)
pcaData <- plotPCA(vsd, intgroup=c("treatment", "timepoint"), returnData=TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))
ggplot(pcaData, aes(PC1, PC2, color=treatment, shape=timepoint)) +
geom_point(size=3) +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance")) +
coord_fixed()
Differential Expression Analysis on Raw Data
Treatments versus controls only here, no time series included. DESeq2
uses the Benjamini-Hochberg (BH) adjustment.
dds <- DESeq(dds)
resultsNames(dds)
res <- results(dds)
res
## order results by smallest p-value
resOrdered <- res[order(res$pvalue),]
resOrdered
## Summarize basic tallies
summary(res)
## Summarize how many adjusted p-values were less than 0.1?
sum(res$padj < 0.1, na.rm=TRUE)
# results function, lower the false discovery rate threshold
res10 <- results(dds, alpha=0.10)
res05 <- results(dds, alpha=0.05)
summary(res10)
# Raise the log2 fold change threshold, so that we test for genes that show more substantial changes due to treatment. For example, by specifying lfcThreshold = 1, we test for genes that show significant effects of treatment on gene counts more than doubling or less than halving, because 2^1=2.
resLFC1 <- results(dds, lfcThreshold=.5)
table(resLFC1$padj < 0.1)
# Information about which variables and tests were used can be found by calling the function mcols on the results object.
mcols(res)$description
mcols(res, use.names = TRUE)
# The column log2FoldChange is the effect size estimate. It tells us how much the gene’s expression seems to have changed due to treatment in comparison to untreated samples. This value is reported on a logarithmic scale to base 2.
Volcano Plot
plotMA(res10, ylim=c(-6,6))
Histogram
hist(res$pvalue[res$baseMean > 1], breaks = 0:20/20,
col = "grey50", border = "white")
Heatmap of Counts
library("pheatmap")
select <- order(rowMeans(counts(dds,normalized=TRUE)),
decreasing=TRUE)[1:20]
df <- as.data.frame(colData(dds)[,c("treatment","timepoint")])
pheatmap(assay(dds)[select,], cluster_rows=FALSE, show_rownames=FALSE,
cluster_cols=TRUE, annotation_col=df)
Another Histogram
The ratio of small p values for genes binned by mean normalized
count. The p values are from a test of log2 fold change greater than 1
or less than -1.
qs <- c(0, quantile(resLFC1$baseMean[resLFC1$baseMean > 0], 0:6/6))
bins <- cut(resLFC1$baseMean, qs)
levels(bins) <- paste0("~", round(signif((qs[-1] + qs[-length(qs)])/2, 2)))
fractionSig <- tapply(resLFC1$pvalue, bins, function(p)
mean(p < .05, na.rm = TRUE))
barplot(fractionSig, xlab = "mean normalized count",
ylab = "fraction of small p values")
Time Course DESeq2 Analysis
Separate Out Time Points
Separate out data from each time point using the count data. For
example, here is time point 3.
# Time point 3
dds3 <- DESeqDataSetFromMatrix(countData = featurecounts[, c("11","17","18","20","6","7")], colData = coldata[c("11","17","18","20","6","7"),], ~ treatment)
# add gene names as row names to dds
rownames(dds3) <- featurecounts$Geneid
dds3
# Results
dds3 <- DESeq(dds3)
resultsNames(dds3)
res3 <- results(dds3)
summary(res3)
res3
# results function
res3_05 <- results(dds3, alpha=0.05)
summary(res3_05)
res3_05
#order by smallest pvalue
res3 <- res3[order(res3$padj),]
res3
Export Results to csv
write.csv(res3, file = "~/Desktop/rnaseq_hamster/results/tp1_gene_results.csv")
Volcano plots
plotMA(res3, ylim=c(-6,6))
GO Term Visualization
library(ggplot2)
# load in data
alltp_DAVID_BP <- read.csv("~/Desktop/rnaseq_hamster/DAVID/alltp_0.05_DAVID_BP.csv")
## plot
ggplot(data = alltp_DAVID_BP, aes(x = Group, y = Term, color = `Benjamini`, size = Count)) + geom_point() + scale_color_gradient(low = "red", high = "blue", name = "Benjamini-Hochberg\n Adjusted P-Value") + scale_size(name = "Gene Count") + theme_bw() + ylab("GO Biological Processes Term") + xlab("") + ggtitle("Gene Ontology Enrichment by Time Point") + theme(axis.text.y = element_text(size=14, colour = "black")) + theme(plot.title = element_text(size = 20, face = "bold",hjust = 0.5) ) + theme(axis.title.y = element_text(size=14, face="bold", colour = "black")) + theme(axis.text.x = element_text(face="bold",size=14, colour = "black"))
## save plot
ggsave("rnaseq_GOenrich_all.jpeg", plot = last_plot(), path = "/home/larsenlab/Desktop/rnaseq_hamster/DAVID", scale = 1, width=15, height=10, units='in', dpi = 300)
LS0tCnRpdGxlOiAiRXBpZ2VuZXRpYyBDaGFuZ2VzIEFzc29jaWF0ZWQgd2l0aCB0aGUgUHJvZ3Jlc3Npb24gb2YgUHJpb24gRGlzZWFzZSBpbiBTeXJpYW4gSGFtc3RlcnMgKCpNZXNvY3JpY2V0dXMgYXVyYXR1cyopIC0gQmlvaW5mb3JtYXRpYyBQaXBlbGluZSIKZGF0ZTogIjIwMjUtMTAtMDMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQpldmFsOiBmYWxzZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMjIEF1dGhvcnMKCkxleGkgRS4gRnJhbmsxLDJcKiwgTmljb2xlIEZsYWNrMSwyLCBDaHJpc3RvcGhlciBGYXVsazMsIEFseXNzYSBKLiBCbG9jazQsIDUsIEphc29uIEMuIEJhcnR6Miw0LCBQZXRlciBBLiBMYXJzZW4xLDIKCjEgRGVwYXJ0bWVudCBvZiBWZXRlcmluYXJ5IGFuZCBCaW9tZWRpY2FsIFNjaWVuY2VzLCBVbml2ZXJzaXR5IG9mIE1pbm5lc290YSwgU3QuIFBhdWwsIE1OLCBVLlMuIDU1MTA4LCAyIE1pbm5lc290YSBDZW50ZXIgZm9yIFByaW9uIFJlc2VhcmNoIGFuZCBPdXRyZWFjaCwgU3QuIFBhdWwsIE1OLCBVLlMuIDU1MTA4LCAzIERlcGFydG1lbnQgb2YgQW5pbWFsIFNjaWVuY2UsIFVuaXZlcnNpdHkgb2YgTWlubmVzb3RhLCBTdC4gUGF1bCwgTU4sIFUuUy4gNTUxMDgsIDQgRGVwYXJ0bWVudCBvZiBNZWRpY2FsIE1pY3JvYmlvbG9neSBhbmQgSW1tdW5vbG9neSwgU2Nob29sIG9mIE1lZGljaW5lLCBDcmVpZ2h0b24gVW5pdmVyc2l0eSwgT21haGEsIE5FLCBVLlMuLCA1IEN1cnJlbnQgQWRkcmVzczogRGVwYXJ0bWVudCBvZiBNaWNyb2Jpb2xvZ3ksIEltbXVub2xvZ3ksIGFuZCBQYXRob2xvZ3ksIENvbG9yYWRvIFN0YXRlIFVuaXZlcnNpdHksIEZvcnQgQ29sbGlucywgQ08sIFUuUy4gODA1MjMKClwqQ29ycmVzcG9uZGluZyBBdXRob3IKCkxleGkgRS4gRnJhbmsKCmZyYW4xNDY0XEB1bW4uZWR1CgpEZXBhcnRtZW50IG9mIFZldGVyaW5hcnkgYW5kIEJpb21lZGljYWwgU2NpZW5jZXMKCjE5NzEgQ29tbW9ud2VhbHRoIEF2ZS4gU3QuIFBhdWwsIE1OIDU1MTA4CgpPQ1JpRDogMDAwOS0wMDA2LTc3MzYtNDE0NgoKUHVibGlzaGVkIGluIEFjdGEgTmV1cm9wYXRob2xvZ2ljYQoKIyBFeHBlcmltZW50IERldGFpbHMKClNhbXBsZXM6IEROQSBleHRyYWN0ZWQgZnJvbSAqTWVzb2NyaWNldHVzIGF1cmF0dXMqIChzeXJpYW4gaGFtc3RlcnMpIGJyYWluIGhvbW9nZW5hdGUuCgpFeHRyYWN0aW9uIG1ldGhvZDogUWlhZ2VuIE1hZ0F0dHJhY3Qga2l0CgpTZXF1ZW5jaW5nIEluc3RydW1lbnQ6IFByb21ldGhpb24gMiBzb2xvLCBSLjEwIFByb21ldGhpb24gZmxvdyBjZWxsCgpMaWJyYXJ5IFByZXBhcmF0aW9uOiBTUUstTFNLMTE0LCBTUUstTkJEMTE0LjI0LCBTUUstTkJEMTE0Ljk2CgpUaGUgZXhwZXJpbWVudCBmb3IgdGhpcyBzdHVkeSBoYWQgMyBjb250cm9scyBhbmQgMyB0cmVhdGVkIGhhbXN0ZXJzIHBlciB0aW1lIHBvaW50LiBUaGVyZSB3ZXJlIDMgdGltZSBwb2ludHMuCgojIEROQSBNZXRoeWxhdGlvbiBBbmFseXNpcwoKIyMgRmlsZSBNYW5pcHVsYXRpb24KClNvcnQgQkFNIGZpbGVzIHVzaW5nIFtzYW10b29sc10oaHR0cHM6Ly93d3cuaHRzbGliLm9yZy8pCgpgYGB7YmFzaH0Kc2FtdG9vbHMgc29ydCAtQCAzMiBoYW1zdGVyLmJhbSAtbyBoYW1zdGVyLnNvcnRlZC5iYW0KYGBgCgojIyBJbmRleCBTb3J0ZWQgQkFNcwoKSW5kZXggZmlsZSBpcyBuZWVkZWQgZm9yIG1vZGtpdCBwaWxldXAgaW4gdGhlIG5leHQgc3RlcC4KCmBgYHtiYXNofQpzYW10b29scyBpbmRleCAtQCAzMiBoYW1zdGVyLnNvcnRlZC5iYW0gCmBgYAoKIyMgQ3JlYXRlIC5iZWQgTWV0aHlsIEZpbGVzIHdpdGggbW9ka2l0IHBpbGV1cAoKWW91IG5lZWQgdGhlIHNvcnRlZCBCQU1zIHdpdGggc2FtdG9vbHMgaW5kZXggZmlsZXMgKC5iYWkpIGluIHRoZSBmb2xkZXIuCgpgYGB7YmFzaH0KbW9ka2l0IHBpbGV1cCAtdCAzMiAtLWNwZyAtLXJlZiBHQ0ZfMDE3NjM5Nzg1LjFfQkNNX01hdXJfMi4wX2dlbm9taWMuZmEgLS1jb21iaW5lLXN0cmFuZHMgLS1vbmx5LXRhYnMgaGFtc3Rlci5zb3J0ZWQuYmFtIGhhbXN0ZXIubW9ka2l0LmJlZApgYGAKCiMjIEF3ayB0byBTZXBhcmF0ZSA1LWh5ZHJveHltZXRoeWxjeXRvc2luZSAoNWhtQykgYW5kIDUtbWV0aHlsY3l0b3NpbmUgKDVtQykgRGF0YQoKVGhpcyB3b3JrcyBmb3IgYWxsIGZpbGVzIGluIGEgZGlyZWN0b3J5LgoKYGBge2Jhc2h9CmZvciBGSUxFIGluICoubW9ka2l0LmJlZCA7IGRvCiAgUFJFRklYPSQoZWNobyAke0ZJTEV9IHwgc2VkICdzL1wuYmVkLy8nKQogIGF3ayAtRidcdCcgJygkND09ImgiKSB7cHJpbnQgJDB9JyAke0ZJTEV9ID4gJHtQUkVGSVh9LmguYmVkIAogIGF3ayAtRidcdCcgJygkND09Im0iKSB7cHJpbnQgJDB9JyAke0ZJTEV9ID4gJHtQUkVGSVh9Lm0uYmVkCmRvbmUKYGBgCgojIyBHbG9iYWwgTWV0aHlsYXRpb24gQ2FsY3VsYXRpb24KClRoaXMgd29ya3MgdG8gY3JlYXRlIGEgLnR4dCBmaWxlIGZvciBlYWNoIGluZGl2aWR1YWwgd2l0aCB0aGUgcHJvcG9ydGlvbiBvZiBjYW5vbmljYWwgQ3MsIDVtYywgYW5kIDVobWNzCgpgYGB7YmFzaH0KZm9yIEZJTEUgaW4gaGFtc3RlcioubW9ka2l0Lm0uYmVkIDsgZG8KICBQUkVGSVg9JChlY2hvICR7RklMRX0gfCBzZWQgJ3MvXC5iZWQvLycpCiAgYXdrICd7Y2FuKz0kMTM7IG1vZCs9JDEyOyBvdGgrPSQxNDsgdmFsaWQrPSQxMH0gRU5Ee3ByaW50IChjYW4vdmFsaWQpICIgQ3BHIGNhbm9uaWNhbFxuIiAobW9kL3ZhbGlkKSAiIDVtQ3BHIG1vZGlmaWVkIFxuIiAob3RoL3ZhbGlkKSAiIDVobUNwRyAgIG1vZGlmaWVkIn0nICR7RklMRX0gPiAke1BSRUZJWH0udHh0CmRvbmUKYGBgCgojIyBHbG9iYWwgTWV0aHlsYXRpb24gRGlmZmVyZW5jZXMgQWNyb3NzIFRpbWUgUG9pbnRzCgpDYWxjdWxhdGUgaWYgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gZ2xvYmFsIG1ldGh5bGF0aW9uIGxldmVscyBhY3Jvc3MgdGltZSBwb2ludHMuIFJlcXVpcmVzIGBzYW1wbGVfaW5mb2Agb2JqZWN0IGRlZmluZWQgYmVsb3cuCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpnbG9iYWxfbWV0aHlsX2ZpbGVzIDwtIGxpc3QuZmlsZXMoIm1vZGtpdF9vdXRfbSIsICIqbW9ka2l0Lm0udHh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFKQoKbmFtZXMoZ2xvYmFsX21ldGh5bF9maWxlcykgPC0gc3RyX2V4dHJhY3QoZ2xvYmFsX21ldGh5bF9maWxlcywgImhhbXN0ZXJcXGR7MSwyfSIpCgpnbG9iYWxfbWV0aHlsIDwtIG1hcF9kZnIoYXMubGlzdChnbG9iYWxfbWV0aHlsX2ZpbGVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgIH4gcmVhZF9kZWxpbSgueCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbGltID0gIiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX25hbWVzID0gYygicmF0aW8iLCAibW9kaWZpY2F0aW9uIiwgIm5vdGUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UpLAogICAgICAgICAgICAgICAgICAgICAgICAgLmlkID0gIklEIikgJT4lIAogIGZpbHRlcihtb2RpZmljYXRpb24gIT0gIkNwRyIpICU+JSAKICBpbm5lcl9qb2luKHNhbXBsZV9pbmZvLCBieSA9ICJJRCIpICU+JSAKICBncm91cF9ieSh0aW1lcG9pbnQsIG1vZGlmaWNhdGlvbikgJT4lIAogIG5lc3QoKSAlPiUgCiAgbXV0YXRlKHR0ZXN0ID0gbWFwKGRhdGEsIH4gdC50ZXN0KHJhdGlvIH4gZ3JvdXAsIGRhdGEgPSAueCkpKSAlPiUgCiAgbXV0YXRlKHB2YWx1ZSA9IG1hcF9jaHIodHRlc3QsIH4gcGx1Y2soLngsICJwLnZhbHVlIikpKQoKZ2xvYmFsX21ldGh5bApgYGAKCiMjIERpZmZlcmVudGlhbCBNZXRoeWxhdGlvbiBBbmFseXNpcyB3aXRoIE1ldGh5bGtpdAoKVXNlIFtNZXRoeWxraXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbDJuYS9tZXRoeWxLaXQpIGZvciBkaWZmZXJlbnRpYWwgRE5BIG1ldGh5bGF0aW9uIGFuYWx5c2lzLiBTZWUgW21ldGh5bGtpdCB2aWduZXR0ZV0oaHR0cHM6Ly9naXRodWIuY29tL2FsMm5hL21ldGh5bEtpdC9ibG9iL21hc3Rlci92aWduZXR0ZXMvbWV0aHlsS2l0LlJtZCkuCgojIyMgQ3JlYXRlIFNhbXBsZSBJbmZvCgpDcmVhdGUgYSBmaWxlIHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gc3VjaCBhcyBzYW1wbGUgYW5kIGZpbGUgbmFtZXMsIHRyZWF0bWVudCwgdGltZSBwb2ludCwgZXRjLgoKIyMjIFJlYWQgaW4gRGF0YQoKTG9hZCBpbiBmaWxlcyB3aXRoIG1ldGh5bGF0aW9uIGRhdGEgaW50byBSIHN0dWRpby4KCmBgYHtyfQojIG1vZGtpdCBvdXRwdXQgY29sdW1uIHNwZWNzCm1vZGtpdF9jb2xzIDwtIGxpc3QoCiAgZnJhY3Rpb24gPSBGQUxTRSwKICBjaHIuY29sID0gMSwKICBzdGFydC5jb2wgPSAyLAogIGVuZC5jb2wgPSAzLAogIGNvdmVyYWdlLmNvbCA9IDUsCiAgc3RyYW5kLmNvbCA9IDYsCiAgZnJlcUMuY29sID0gMTEKKQoKIyB1c2UgdGhlIGZpbGUgbmFtZXMgdG8gbG9hZCB0aGUgZGF0YQpta19vYmogPC0gbWV0aFJlYWQoCiAgYXMubGlzdChzYW1wbGVfaW5mbyRmaWxlKSwKICBzYW1wbGUuaWQgPSBhcy5saXN0KHNhbXBsZV9pbmZvJElEKSwKICBhc3NlbWJseSA9ICJoYW1zdGVyIiwKICBwaXBlbGluZSA9IG1vZGtpdF9jb2xzLAogIHRyZWF0bWVudCA9IHNhbXBsZV9pbmZvJHRyZWF0bWVudF9iaW5hcnksCiAgbWluY292ID0gMTAKKQoKbWtfb2JqCgpgYGAKCiMjIyBTdW1tZXJ5IHN0YXRzCgojIyMjIENvdmVyYWdlCgpgYGB7cn0KIyBJdGVyYXRlIG92ZXIgdGhlIEdSYW5nZXNMaXN0IHRvIHBsb3QgYWxsIG9mIHRoZSBzYW1wbGVzCm1hcChta19vYmosIGdldENvdmVyYWdlU3RhdHMsIHBsb3QgPSBULCBib3RoLnN0cmFuZHMgPSBGKQoKIyBQcmludCBvbmUgc2FtcGxlJ3MgY292ZXJhZ2UgZGF0YS4gWW91IGNhbiBkbyBib3RoLnN0cmFuZHMgPSBUIHRvIGNoZWNrIHN0cmFuZCBiaWFzLgpnZXRDb3ZlcmFnZVN0YXRzKG1rX29ialtbMl1dLCBwbG90ID0gVCwgYm90aC5zdHJhbmRzID0gRikKYGBgCgojIyMjIE1ldGh5bGF0aW9uIEhpc3RvZ3JhbQoKSW4gYSBoaXN0b2dyYW0gb2YgbWV0aHlsYXRpb24gcGVyY2VudGFnZXMgYWNyb3NzIGEgZ2l2ZW4gc2FtcGxlLCB3ZSBleHBlY3QgYSBsYXJnZSBudW1iZXIgb2YgbG93IGFuZCBoaWdoIG1ldGh5bGF0aW9uIENwR3MgKHBlYWtzIG9uIGxlZnQgYW5kIHJpZ2h0IGVuZHMpIHdpdGggbGVzcyBkZW5zaXR5IGFjcm9zcyB0aGUgbWlkZGxlLgoKYGBge3J9CmdldE1ldGh5bGF0aW9uU3RhdHMobWtfb2JqW1sxXV0sCiAgICAgICAgICAgICAgICAgICAgcGxvdCA9IFQsCiAgICAgICAgICAgICAgICAgICAgYm90aC5zdHJhbmRzID0gRgogICAgICAgICAgICAgICAgICAgICkKCiMgSXRlcmF0ZSBhY3Jvc3MgYWxsCm1hcChta19vYmosIGdldE1ldGh5bGF0aW9uU3RhdHMsIHBsb3QgPSBULCBib3RoLnN0cmFuZHMgPSBGKQpgYGAKCiMjIyBOb3JtYWxpemF0aW9uIGFuZCBDb3ZlcmFnZSBGaWx0ZXJpbmcKCk5vcm1hbGl6aW5nIGNvdmVyYWdlIGJldHdlZW4gc2FtcGxlcyBpcyB1c2VmdWwgZm9yIGF2b2lkaW5nIGJpYXMgd2hlbiBjb3ZlcmFnZSBhY3Jvc3Mgc2FtcGxlcyBpcyB1bmV2ZW4uIEZpbHRlcmluZyBhbm9tYWxvdXNseSBoaWdoIG9yIGxvdyBjb3ZlcmFnZSBpcyBhbHNvIGhlbHBmdWwuCgpgYGB7cn0KbWtfb2JqX25vcm1hbCA8LSBmaWx0ZXJCeUNvdmVyYWdlKG1rX29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvLmNvdW50ID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaS5wZXJjID0gOTkuOSkgJT4lCiAgbm9ybWFsaXplQ292ZXJhZ2UoLikKYGBgCgpSZWNoZWNrIHRoZSBzdW1tYXJ5IHN0YXQgcGxvdHMgYWZ0ZXIgbm9ybWFsaXphdGlvbiBhbmQgZmlsdGVyaW5nLiBTZXR0aW5nIGBib3RoLnN0cmFuZHMgPSBUYCBjYW4gZGV0ZWN0IHN0cmFuZCBiaWFzLgoKYGBge3J9CiMgUHJpbnQgb25lIHNhbXBsZSdzIGNvdmVyYWdlIGRhdGEKZ2V0Q292ZXJhZ2VTdGF0cyhta19vYmpfbm9ybWFsW1syXV0sIHBsb3QgPSBULCBib3RoLnN0cmFuZHMgPSBGKQoKIyBvbmUgc2FtcGxlJ3MgbWV0aHlsYXRpb24gc3RhdHMKZ2V0TWV0aHlsYXRpb25TdGF0cyhta19vYmpfbm9ybWFsW1syXV0sIHBsb3QgPSBULCBib3RoLnN0cmFuZHMgPSBGKQoKYGBgCgpgYGB7cn0KIyBJdGVyYXRlIG92ZXIgdGhlIEdSYW5nZXNMaXN0IHRvIHBsb3QgYWxsIG9mIHRoZSBzYW1wbGVzCm1hcChta19vYmpfbm9ybWFsLCBnZXRNZXRoeWxhdGlvblN0YXRzLCBwbG90ID0gVCwgYm90aC5zdHJhbmRzID0gRikKCiMgUHJpbnQgb25lIHNhbXBsZSdzIGNvdmVyYWdlIGRhdGEuIFlvdSBjYW4gZG8gYm90aC5zdHJhbmRzID0gVCB0byBjaGVjayBzdHJhbmQgYmlhcy4KZ2V0Q292ZXJhZ2VTdGF0cyhta19vYmpfbm9ybWFsW1syXV0sIHBsb3QgPSBULCBib3RoLnN0cmFuZHMgPSBGKQpgYGAKCiMjIyBVbml0ZQoKVGhpcyBvcGVyYXRpb24gc2VsZWN0cyBDcEdzIHRoYXQgYXJlIGNvdmVyZWQgaW4gYWxsIHNhbXBsZXMuCgpgYGB7cn0KIyBEZXN0cmFuZCA9IEYgYWxsb3dzIHVzIHRvIGV4YW1pbmUgRE1Scwpta19tX21ldGggPC0KICBtZXRoeWxLaXQ6OnVuaXRlKG1rX29ial9ub3JtYWwsIGRlc3RyYW5kID0gRikKCmBgYAoKIyMjIFRpbGluZyBmb3IgRGlmZmVyZW50aWFsbHkgTWV0aHlsYXRlZCBSZWdpb25zIChETVJzKQoKV2UgY2hvb3NlIHdpbmRvd3Mgb2YgMSwwMDAgYmFzZXMgZm9yIG91ciByZWdpb25hbCBhbmFseXNpcy4KCiMjIyMgRE1ScyBwZXIgVGltZSBQb2ludAoKU2VwYXJhdGUgZGF0YSBmb3IgZWFjaCB0aW1lIHBvaW50LiBUaW1lIHBvaW50IDMgdXNlZCBhcyBhbiBleGFtcGxlLgoKYGBge3J9Cm1rX3dpbmRvd3NfMTAwMCA8LSB0aWxlTWV0aHlsQ291bnRzKG1rX21fbWV0aCx3aW4uc2l6ZT0xMDAwLHN0ZXAuc2l6ZT0xMDAwLGNvdi5iYXNlcyA9IDMsIG1jLmNvcmVzID0gMTApCgojIyByZW9yZ2FuaXplIGRhdGEgdG8gaW5jbHVkZSBvbmx5IHRpbXBlcG9pbnQgMyAoZG8gdGhpcyBmb3IgYWxsIHRpbWUgcG9pbnRzKQpta193aW5kb3dzXzEwMDBfcmVvcmczIDwtIHJlb3JnYW5pemUobWtfd2luZG93c18xMDAwLCBzYW1wbGUuaWRzID0gYygiaGFtc3RlcjExIiwgImhhbXN0ZXIxNyIsICJoYW1zdGVyMTgiLCAiaGFtc3RlcjIiLCAiaGFtc3RlcjYiLCAiaGFtc3RlcjciKSwgdHJlYXRtZW50ID0gYygwLDAsMSwxLDAsMSksIHNhdmUuZGIgPSBUUlVFKQoKIAojIGRpZmYgbWV0aCBzdGF0aXN0aWNzIGZvciBhbGwgcmVnaW9ucyAoZG8gdGhpcyBmb3IgYWxsIHRpbWUgcG9pbnRzKQpkaWZmX21rd2luZG93czNfMTAwMCA8LSBjYWxjdWxhdGVEaWZmTWV0aChta193aW5kb3dzXzEwMDBfcmVvcmczLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gNikKCiMgZmlsdGVyIGZvciBzaWduaWZpY2FudCByZWdpb25zIChkbyB0aGlzIGZvciBhbGwgdGltZSBwb2ludHMpCmRlbHRhX21rd2luZG93czNfMTAwMCA8LSBnZXRNZXRoeWxEaWZmKGRpZmZfbWt3aW5kb3dzM18xMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZlcmVuY2UgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWUgPSAwLjA1KQpgYGAKCiMjIyMgR2V0IFN0YXRzIGZvciBNZXRoeWxhdGlvbgoKRG8gdGhpcyBmb3IgZWFjaCB0aW1lIHBvaW50LiBUaW1lIHBvaW50IDMgdXNlZCBhcyBhbiBleGFtcGxlLgoKYGBge3J9CiMgU2VwYXJhdGUgb3V0IGh5cGVyIGFuZCBoeXBvbWV0aHlsYXRpb24KCiMgZ2V0IGh5cGVyLW1ldGh5bGF0ZWQKaHlwZXJfZGVsdGFfbWt3aW5kb3dzM18xMDAwIDwtIGdldE1ldGh5bERpZmYoZGlmZl9ta3dpbmRvd3MzXzEwMDAsIGRpZmZlcmVuY2U9MTAscXZhbHVlPTAuMDUsdHlwZT0iaHlwZXIiKQoKIyBnZXQgaHlwby1tZXRoeWxhdGVkCmh5cG9fZGVsdGFfbWt3aW5kb3dzM18xMDAwIDwtIGdldE1ldGh5bERpZmYoZGlmZl9ta3dpbmRvd3MzXzEwMDAsIGRpZmZlcmVuY2U9MTAscXZhbHVlPTAuMDUsdHlwZT0iaHlwbyIpCgojIyBtYWtlIGl0IGEgZGF0YSBmcmFtZQpkZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGRlbHRhX21rd2luZG93czNfMTAwMCkKaHlwb19kZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGh5cG9fZGVsdGFfbWt3aW5kb3dzM18xMDAwKQpoeXBlcl9kZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGh5cGVyX2RlbHRhX21rd2luZG93czNfMTAwMCkKCiMjIG1ha2UgdGhlIGJhY2tncm91bmQgYSBkYXRhZnJhbWUKZGlmZl9ta2Rmd2luZG93czNkZl8xMDAwIDwtIGdldERhdGEoZGlmZl9ta3dpbmRvd3MzXzEwMDApCgoKIyNzdW1tYXJ5IHN0YXRzLCBoeXBlciBvciBoeXBvIGFuZCBob3cgbXVjaCwgZ2VuaWMgcmVnaW9uIGVucmljaG1lbnQgKGkuZS4gZXhvbnMsIHByb21vdGVycykKCiMjIHN1bW1hcnkgc3RhdHMsbnVtYmVyIGFuZCAgcGVyY2VudCBhdmVyYWdlIGh5cGVyIGFuZCBoeXBvLCBhbmQgYWJzb2x1dGUKc3VtbWFyaXNlKGRlbHRhX21rZGZ3aW5kb3dzM18xMDAwLCBtZWFuKGFicyhtZXRoLmRpZmYpKSkKCiMjc3VtbWFyeSBvZiBjb3VudCBvZiBhbGwKbnJvdyhkZWx0YV9ta2Rmd2luZG93czNfMTAwMCkKCiMjIG1lYW4gb2YgaHlwZXJtZXRoeWxhdGVkIHJlZ2lvbnMKZGVsdGFfbWtkZndpbmRvd3MzXzEwMDAgJT4lIAogIGZpbHRlcihtZXRoLmRpZmYgPiAwKSAlPiUKICBzdW1tYXJpemUobWVhbihtZXRoLmRpZmYpKQoKIyMgY2FsY3VsYXRlIGNvdW50IG9mIGh5cGVybWV0aHlsYXRpb24KZGVsdGFfbWtkZndpbmRvd3MzXzEwMDAgJT4lCiAgc3VtbWFyaXplKGNvdW50ID0gc3VtKG1ldGguZGlmZiA+IDApKQoKIyMgbWVhbiBvZiBoeXBvbWV0aHlsYXRlZCByZWdpb25zCmRlbHRhX21rZGZ3aW5kb3dzM18xMDAwICU+JSAKICBmaWx0ZXIobWV0aC5kaWZmIDwgMCkgJT4lCiAgc3VtbWFyaXplKG1lYW4obWV0aC5kaWZmKSkKCiMjIGNhbGN1bGF0ZSBjb3VudCBvZiBoeXBvbWV0aHlsYXRpb24KZGVsdGFfbWtkZndpbmRvd3MzXzEwMDAgJT4lCiAgc3VtbWFyaXplKGNvdW50ID0gc3VtKG1ldGguZGlmZiA8IDApKQoKIyMgbWFrZSBhIGhpc3RvZ3JhbQpoaXN0KGRlbHRhX21rZGZ3aW5kb3dzM18xMDAwJG1ldGguZGlmZikKCiMjIHNlZSBkaXN0aW5jdCBjaHIgCmRlbHRhX21rZGZ3aW5kb3dzM18xMDAwICU+JSBkaXN0aW5jdChjaHIpCgojIyBzZWUgc2hhcmVkIGNociBETVJzIGJldHdlZW4gdGltZXBvaW50CmRlbHRhX21rZGZ3aW5kb3dzM18xMDAwICU+JSBpbm5lcl9qb2luKGRlbHRhX21rZGZ3aW5kb3dzMV8xMDAwLCBieSA9IGMoJ2NocicsICdzdGFydCcpKSAlPiUgbnJvdygpCmBgYAoKIyMjIENsdXN0ZXJpbmcgb2YgSW5kaXZpZHVhbHMKCiMjIyMgUENBCgpTZXR0aW5nIGB0cmFuc3Bvc2UgPSBGQUxTRWAgKGRlZmF1bHQgaXMgYFRSVUVgKSB0dXJucyB0aGlzIGludG8gYSAicmVndWxhciIgUENBIHBsb3QsIHdoZXJlIHlvdSdyZSBsb29raW5nIGF0IHRoZSBjb250cmlidXRpb24gb2Ygc2FtcGxlcyB0byB0aGUgdmFyaWFuY2UuCgpgYGB7cn0KUENBU2FtcGxlcyhta19tX21ldGgsIHRyYW5zcG9zZSA9IEZBTFNFKQpgYGAKClBDQSBQbG90IHVzaW5nIFtnZ3Bsb3QyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pCgpgYGB7ciBkZXYgPSdwbmcnfQojI2NyZWF0ZSB0aGUgb2JqZWN0cyB0byBtYWtlIHRoZSBwY2EKcGNhLm9iaiA9IFBDQVNhbXBsZXMobWtfbV9tZXRoLCB0cmFuc3Bvc2UgPSBGQUxTRSwgb2JqLnJldHVybiA9IFRSVUUpCnBjYS5vYmoyIDwtIHBjYS5vYmokcm90YXRpb25bLCAxOjJdICU+JSBhcy5kYXRhLmZyYW1lKCkKCiMjZ2dwbG90CnAxPC0gZ2dwbG90KHBjYS5vYmoyLCBhZXMoUEMxLCBQQzIpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yPXNhbXBsZV9pbmZvJHRyZWF0bWVudCkpICsKICBnZW9tX3RleHRfcmVwZWwobGFiZWw9cm93bmFtZXMocGNhLm9iajIpLCBtYXgub3ZlcmxhcHMgPSAxNSwgYWVzKGNvbG9yPXNhbXBsZV9pbmZvJHRyZWF0bWVudCkpICsKICBnZ3RpdGxlKCJIYW1zdGVyIENwRyA1bWMgTWV0aHlsYXRpb24gUENBIHBsb3QiKSArCiAgdGhlbWVfYncoKQpwMQpgYGAKCkRlbmRyb2dyYW0KCmBgYHtyfQpjbHVzdGVyU2FtcGxlcyhta19tX21ldGgpCmBgYAoKU2NyZWUgcGxvdAoKYGBge3J9ClBDQVNhbXBsZXMobWtfbV9tZXRoLCBzY3JlZXBsb3QgPSBUUlVFKQpgYGAKCiMjIEdlbm9tYXRpb24KClVzZSBbR2Vub21hdGlvbl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2dlbm9tYXRpb24uaHRtbCkgZm9yIHN1bW1hcnkgYW5kIGFubm90YXRpb24gb2YgZ2Vub21pYyBpbnRlcnZhbHMuCgpSZWZTZXFBbGwgdHJhY2sgZG93bmxvYWRlZCBmcm9tIFVDU0MgR2Vub21lIEJyb3dzZXIgb24gMjcgbWFyY2ggMjAyNCBhcyAiYWxsIGZpZWxkcyIuIFJlcGxhY2UgSUQgY29sdW1uIHdpdGggc3ltYm9sIHRvIHJldGFpbiBpbiBhbm5vdGF0aW9uIG9iamVjdDoKCmBgYHtiYXNofQphd2sgJ0JFR0lOIHtPRlM9Ilx0In0gTlI+MSB7JDQ9JDEzOyBORj0xMjsgcHJpbnR9JyBCQ01fTWF1cl8yLjBfTWFyMjAyMV9yZWZzZXFhbGwuYmVkID4gQkNNX01hdXJfMi4wX01hcjIwMjFfcmVmc2VxYWxsX3N5bWJvbC5iZWQKYGBgCgojIyMgQ3JlYXRlIEFubm90YXRpb24gT2JqZWN0CgpUaGlzIGNhbiBiZSBkb25lIGJ5IHRyZWF0bWVudCwgdGltZSBwb2ludCwgZXRjLiwgY29tcGFyaW5nIHRoZSBncm91cCB0byB0aGUgYmFja2dyb3VuZC4KCmBgYHtyfQpnZW5lX29iaiA8LQogIGdlbm9tYXRpb246OnJlYWRUcmFuc2NyaXB0RmVhdHVyZXMoIn4vRGVza3RvcC9yZWZlcmVuY2VfZGF0YS9CQ01fTWF1cl8yLjBfTWFyMjAyMV9yZWZzZXFhbGxfYmVkMTIuYmVkIiwgcmVtb3ZlLnVudXN1YWwgPSBGKQoKaHlwZXJfZGVsdGEzV19nciA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoaHlwZXJfZGVsdGFfbWtkZndpbmRvd3MzXzEwMDAsIGtlZXAuZXh0cmEuY29sdW1ucyA9IFQpCgojIGV4cG9ydCBhcyBhIGJlZCBmaWxlCmxpYnJhcnkocnRyYWNrbGF5ZXIpCmV4cG9ydC5iZWQoaHlwZXJfZGVsdGEzV19nciwgY29uID0gJ2h5cGVyX2RlbHRhM1cuYmVkJykKCiMjIGJhY2tncm91bmQKZGlmZjNXX2dyIDwtIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZShkaWZmX21rZGZ3aW5kb3dzM2RmXzEwMDAsIGtlZXAuZXh0cmEuY29sdW1ucyA9IFQpCgojIGFubm90YXRlCmFubm90IDwtIGFubm90YXRlV2l0aEdlbmVQYXJ0cyhkZWx0YTNXX2dyLCBnZW5lX29iaiwgaW50ZXJzZWN0LmNociA9IFQpCmdldFRhcmdldEFubm90YXRpb25TdGF0cyhhbm5vdCkKcGxvdFRhcmdldEFubm90YXRpb24oYW5ub3QpCgojIyBhbm5vdGF0ZSBiYWNrZ3JvdW5kCmFubm90X251bGwgPC0gYW5ub3RhdGVXaXRoR2VuZVBhcnRzKGRpZmYzV19nciwgZ2VuZV9vYmosIGludGVyc2VjdC5jaHIgPSBUKQpnZXRUYXJnZXRBbm5vdGF0aW9uU3RhdHMoYW5ub3RfbnVsbCkKcGxvdFRhcmdldEFubm90YXRpb24oYW5ub3RfbnVsbCkKYGBgCgojIyMgQ2hpLVNxdWFyZSBUZXN0CgpgYGB7cn0KIyBHZW5lcmF0ZSBwaWUgY2hhcnRzIGZvciBETVJzIGFuZCBiYWNrZ3JvdW5kCiMjcGFyKG1mcm93ID0gYygxLDIpKQojI3Bsb3RUYXJnZXRBbm5vdGF0aW9uKGFubm90LAogICAgICAgICAgICAgICAgICAgICAjI21haW4gPSAiRE1ScyBieSBHZW5pYyBSZWdpb24iKQojI3Bsb3RUYXJnZXRBbm5vdGF0aW9uKGFubm90X251bGwsCiAgICAgICAgICAgICAgICAgICAgIyNtYWluID0gIkFsbCBDcEdzIGJ5IEdlbmljIFJlZ2lvbiIpCgojIENvdW50IERNUnMgdGhhdCBvdmVybGFwIHdpdGggdGFyZ2V0IGdlbmUgZmVhdHVyZXMgdnMuIGJhY2tncm91bmQKZG1yM1dfc3RhdHMgPC0gZ2Vub21hdGlvbjo6Z2V0VGFyZ2V0QW5ub3RhdGlvblN0YXRzKGFubm90LCBwZXJjZW50YWdlID0gRikKbnVsbDNXX3N0YXRzIDwtIGdlbm9tYXRpb246OmdldFRhcmdldEFubm90YXRpb25TdGF0cyhhbm5vdF9udWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50YWdlID0gRikgCmRtcl9udWxsX21hdHJpeDNXIDwtIHJiaW5kKGRtcjNXX3N0YXRzLCBudWxsM1dfc3RhdHMpCgojIDItd2F5IHRhYmxlIGNoaXNxIGZ1bmN0aW9uCnRlc3RfYW5ub3RfbWF0cml4IDwtIGZ1bmN0aW9uKHgseSl7CiAgbWF0cml4IDwtIHggJT4lIAogIGRhdGEuZnJhbWUgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJzb3VyY2UiKSAlPiUKICBtdXRhdGUob3RoZXJfZmVhdHVyZXMgPSByb3dTdW1zKGFjcm9zcyghYyhzb3VyY2UsIGFsbF9vZih5KSkpKSkgJT4lIAogIGRwbHlyOjpzZWxlY3Qoc291cmNlLCBhbGxfb2YoeSksIG90aGVyX2ZlYXR1cmVzKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInNvdXJjZSIpICU+JQogIGFzLm1hdHJpeCgpCiAgCiAgdGVzdCA8LSBjaGlzcS50ZXN0KG1hdHJpeCkKICAKICByZXR1cm4obGlzdChtYXRyaXggPSBtYXRyaXgsIHRlc3QgPSB0ZXN0KSkKfQoKIyBDYWxjdWxhdGUgZW5yaWNobWVudCBpbiBnZW5pYyByZWdpb25zIHZzLiBiYWNrZ3JvdW5kCm1hcChsaXN0KCJwcm9tb3RlciIsICJleG9uIiwgImludHJvbiIpLCAKICAgICAgICAgfiB0ZXN0X2Fubm90X21hdHJpeChkbXJfbnVsbF9tYXRyaXgzVywgLngpKQpgYGAKCiMjIyBHZXQgVG9wIERNUi1Bc3NvY2lhdGVkIEdlbmVzCgpgYGB7cn0KYW5ub3RfYmVkIDwtIHJlYWRfdHN2KCJ+L0Rlc2t0b3AvcmVmZXJlbmNlX2RhdGEvQkNNX01hdXJfMi4wX01hcjIwMjFfcmVmc2VxYWxsLmJlZCIsIHNob3dfY29sX3R5cGVzID0gRikgJT4lCiAgZHBseXI6OnNlbGVjdChuYW1lLCBuYW1lMikKCnRvcGdlbmVzIDwtIGFubm90QGRpc3QudG8uVFNTICU+JQogIGxlZnRfam9pbihhbm5vdF9iZWQsIGJ5ID0gYygiZmVhdHVyZS5uYW1lIiA9ICJuYW1lIikpICU+JQogIGdyb3VwX2J5KG5hbWUyKSAlPiUKICB0YWxseSgpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKdG9wZ2VuZXMKCiMjIHdydGllIGEgdHN2IG9mIHRvcCBETVIgYXNzb2NpYXRlZCBnZW5lcwp3cml0ZV90c3YodG9wZ2VuZXMsIGZpbGUgPSAidG9wRE1SYXNzb2NpYXRlZGdlbmVzX3RwMS50c3YiKQpgYGAKCiMjIExpZnQgT3ZlciBHZW5lcyB0byBFbmhhbmNlIFJlZmVyZW5jZQoKVXNlIE5DQkkgZ2Vub21lLCBsaWZ0IG92ZXIgbW0zOSAoKk11cyBtdXNjdWx1cyopIGdlbmVzIHRvIGhhbXN0ZXIgZHVlIHRvIGxhY2sgb2YgZGVkaWNhdGVkIGdlbmUgb250b2xvZ3kgcmVzb3VyY2VzIGZvciB0aGUgaGFtc3Rlci4KCkluc3RhbGxlZCBbTGlmdE9mZl0oaHR0cHM6Ly9naXRodWIuY29tL2Fnc2h1bWF0ZS9MaWZ0b2ZmKSB2ZXJzaW9uIDEuNi4zIHZpYSBjb25kYTogYGNvbmRhIGluc3RhbGwgLWMgYmlvY29uZGEgbGlmdG9mZmAuIERvd25sb2FkZWQgR0ZGcyBmcm9tIE5DQkkuCgpgYGAgeyNiYXNofQpsaWZ0b2ZmIC1nIC4uL0dDRl8wMDAwMDE2MzUuMjdfR1JDbTM5X2dlbm9taWMuZ2ZmIC1vIG1tMzlfbGlmdF90b19NYXVyMi5nZmYgLi4vR0NGXzAxNzYzOTc4NS4xX0JDTV9NYXVyXzIuMF9nZW5vbWljLmZuYSAuLi9HQ0ZfMDAwMDAxNjM1LjI3X0dSQ20zOV9nZW5vbWljLmZuYQpgYGAKCiMjIyBHZXQgR2Vub21lIERhdGEKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIHB1bGwgc2NhZmZvbGQgaW5mbyBhbmQgZ2VuZSBJRHMgZm9yIG1vc3QgcmVjZW50IGhhbXN0ZXIgYXNzZW1ibHkKbHQgPSBnZXRHZW5vbWVEYXRhRnJvbU5DQkkoIkdDRl8wMTc2Mzk3ODUuMSIsIHJldHVybl9ncmFuZ2VzID0gRkFMU0UpCgojIGdlbmVyYXRlIG5hbWVkIHZlY3RvciB0byB0cmFuc2xhdGUgUmVmU2VxIHNjYWZmb2xkIElEcyB0byBHZW5iYW5rCnNlcWxldmVsc19uY2JpMmdlbmJhbmsgPSBzdHJ1Y3R1cmUobHQkZ2Vub21lJGdlbmJhbmtBY2Nlc3Npb24sCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMgPSBsdCRnZW5vbWUkcmVmc2VxQWNjZXNzaW9uKQoKIyBpc29sYXRlIGRhdGEgZnJhbWVzIGZyb20gbGlzdCBhbmQgZWRpdCB0byBnZW5lIElEIGNvbHVtbiBuYW1lCmRmX2dlbm9tZSA9IGx0JGdlbm9tZQpkZl9nZW5lcyA9IGx0JGdlbmUgJT4lIGRwbHlyOjpyZW5hbWUoZ2VuZV9pZCA9IGBFbnRyZVogSURgKQoKIyBtYWtlIGdlbmUgZGF0YSBpbnRvIEdSYW5nZXMgb2JqZWN0CmdlbmVzID0gbWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKGRmX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXFuYW1lcy5maWVsZCA9ICJSZWZTZXEgQ29udGlnIEFjY2Vzc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0LmZpZWxkID0gIlN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kLmZpZWxkID0gIkVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXAuZXh0cmEuY29sdW1ucyA9IFRSVUUpCgojIHJlbmFtZSBHUmFuZ2VzIG9iamVjdCBzZXFsZXZlbHMgKGNocm9tb3NvbWUvc2NhZmZvbGQgSURzKQpnZW5lcyA8LSByZW5hbWVTZXFsZXZlbHMoZ2VuZXMsIHNlcWxldmVsc19uY2JpMmdlbmJhbmspCgojIGFzc2lnbiBzZXFsZXZlbCBhbmQgc2VxbGVuZ3RoIG1ldGFkYXRhIHVzaW5nIGdlbm9tZSBkYXRhCnNlcWxldmVscyhnZW5lcykgPSBkZl9nZW5vbWUkZ2VuYmFua0FjY2Vzc2lvbgpzZXFsZW5ndGhzKGdlbmVzKSA9IGRmX2dlbm9tZSRsZW5ndGgKCiMgZ2V0IHJkcyBmaWxlcwpkZWx0YTNXX2dyIDwtIHJlYWRSRFMoImRlbHRhM1dfZ3IucmRzIikKCiMgdHJhbnNsYXRlIE5DQkkgc2VxbmFtZXMgdG8gR2VuQmFuayBpbiBmb3IgYWxsIHRpbWVwb2ludHMKZGVsdGFXX2dyX2xpc3QgPC0gbGlzdChkZWx0YTFXX2dyLCBkZWx0YTJXX2dyLCBkZWx0YTNXX2dyKQoKbmFtZXMoZGVsdGFXX2dyX2xpc3QpIDwtIGMoJ2RlbHRhMVdfZ3InLCAnZGVsdGEyV19ncicsICdkZWx0YTNXX2dyJykKCmRlbHRhV19ncl9saXN0IDwtIG1hcChkZWx0YVdfZ3JfbGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH4gcmVuYW1lU2VxbGV2ZWxzKC54LCBzZXFsZXZlbHNfbmNiaTJnZW5iYW5rKSkKCgojIGdlbm9tZSA9IGx0JGdlbm9tZQojIGdlbm9tZSA9IGdlbm9tZVshaXMubmEoZ2Vub21lWywgMl0pLCBdCmRmX2dlbm9tZSRzdGFydCA9IDEKZGZfZ2Vub21lJHN0cmFuZCA9ICIrIgpnZW5vbWUgPSBHUmFuZ2VzKAogIHNlcW5hbWVzID0gZGZfZ2Vub21lWywgMl0sCiAgcmFuZ2VzID0gSVJhbmdlcyhkZl9nZW5vbWVbLCA2XSwgZGZfZ2Vub21lWywgNV0pLAogIHN0cmFuZCA9IGRmX2dlbm9tZVssIDRdLAogIGdlbmVfaWQgPSBkZl9nZW5vbWVbLCA1XSAKKQpnZW5vbWUgPC0gcmVuYW1lU2VxbGV2ZWxzKGdlbm9tZSwgc2VxbGV2ZWxzX25jYmkyZ2VuYmFuaykKc2VxbGV2ZWxzKGdlbm9tZSkgPSBkZl9nZW5vbWUkZ2VuYmFua0FjY2Vzc2lvbgpzZXFsZW5ndGhzKGdlbm9tZSkgPSBkZl9nZW5vbWUkbGVuZ3RoCmBgYAoKIyMjIEZvcm1hdCBMaWZ0ZWQgR2VuZXMKCmBgYHtyfQojIHVzZSBsaWZ0b2ZmIGdmZiBhbmQgY3JlYXRlIEdSYW5nZXMKbGlmdF9nZW5lcyA8LSBnZmZUb0dSYW5nZXMoImxpZnRvZmZfb3V0L21tMzlfbGlmdF90b19NYXVyMi5nZmYiLCBmaWx0ZXIgPSAiZ2VuZSIpCgojIGNoYW5nZSB0byBnZW5iYW5rIG5hbWVzCmxpZnRfZ2VuZXMgPC0gcmVuYW1lU2VxbGV2ZWxzKGxpZnRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcWxldmVsc19uY2JpMmdlbmJhbmspCiMgbWFrZSBkYXRhZnJhbWUgb2YgbGlmdF9nZW5lcwpsaWZ0X2dlbmVzX2RmIDwtIGxpZnRfZ2VuZXMgJT4lCiAgZGF0YS5mcmFtZSgpICU+JQogICMgcmVsb2NhdGUoZ2VuZV9pZCwgLmFmdGVyID0gInN0cmFuZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoIShtb2RlbF9ldmlkZW5jZTpsYXN0X2NvbCgpKSkgJT4lIAogIGZpbHRlcihnZW5lX2Jpb3R5cGUgPT0gInByb3RlaW5fY29kaW5nIiAmIHR5cGUgPT0gImdlbmUiKSAlPiUgCiAgbXV0YXRlKGdlbmVfaWQgPSBtYXBfY2hyKERieHJlZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgfiBwbHVjaygueCwgMSkgJT4lIAogICAgICAgICAgIHN0cl9yZW1vdmUoLiwgIkdlbmVJRDoiKSkpICU+JSAKICByZWxvY2F0ZShnZW5lX2lkLCAuYWZ0ZXIgPSAic3RyYW5kIikKCiNtYWtlIGdyYW5nZXMgZnJvbSBkYXRhZnJhbWUKbGlmdF9nZW5lc19maWx0ZXIgPC0gbWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKGxpZnRfZ2VuZXNfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwLmV4dHJhLmNvbHVtbnMgPSBUUlVFKQoKc2VxbGV2ZWxzKGxpZnRfZ2VuZXNfZmlsdGVyKSA9IGRmX2dlbm9tZSRnZW5iYW5rQWNjZXNzaW9uCnNlcWxlbmd0aHMobGlmdF9nZW5lc19maWx0ZXIpID0gZGZfZ2Vub21lJGxlbmd0aAoKI0VVNjYwMjE4LjEgNTMxMC02ODU0IAoKZ2Vub21lKGxpZnRfZ2VuZXNfZmlsdGVyKSA8LSAiR0NGXzAxNzYzOTc4NS4xIgpnZW5vbWUoZ2Vub21lKSA8LSAiR0NGXzAxNzYzOTc4NS4xIgoKbGlmdF9nZW5lc19leHRlbmQgPC0gZXh0ZW5kVFNTKGxpZnRfZ2VuZXNfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZW5kX2Zyb20gPSAiZ2VuZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXFsZW5ndGhzID0gc2VxbGVuZ3RocyhsaWZ0X2dlbmVzX2ZpbHRlciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gImJhc2FsUGx1c0V4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGJhc2FsX3Vwc3RyZWFtID0gNTAwMCwgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgYmFzYWxfZG93bnN0cmVhbSA9IDUwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb24gPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pZF90eXBlID0gIkVOVFJFWiIpCmdlbm9tZShsaWZ0X2dlbmVzX2V4dGVuZCkgPC0gIkdDRl8wMTc2Mzk3ODUuMSIKYGBgCgojIyBGaW5kIENsb3Nlc3QgR2VuZXMgdG8gRE1ScwoKVXNlIFtiZWR0b29scyBjbG9zZXN0c10oaHR0cHM6Ly9iZWR0b29scy5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvY29udGVudC90b29scy9jbG9zZXN0Lmh0bWwpIHRvIGZpbmQgdGhlIGludGVyc2VjdGluZy9uZWFyZXN0IGdlbmUgdG8gZGlmZmVyZW50aWFsbHkgbWV0aHlsYXRlZCByZWdpb25zIChpbiBiZWQgZmlsZSBmb3JtYXQpIGZyb20gYmVkIGZpbGUgb2YgbGlmdG9mZiBhbm5vdGF0aW9uIHRvIGdldCBhIGxpc3QgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMgKERBVklEKS4gRG8gdGhpcyBmb3IgYWxsIHRpbWUgcG9pbnRzIGRhdGEuIFRpbWUgcG9pbnQgMyBpcyB1c2VkIGFzIGFuIGV4YW1wbGUgaGVyZS4KCmBgYHtyfQojIEFsbCBkYXRhIGZvciBvbmUgdGltZSBwb2ludApkZWx0YV9ta3dpbmRvd3MzXzEwMDAgPC0gZ2V0TWV0aHlsRGlmZihkaWZmX21rd2luZG93czNfMTAwMCwgZGlmZmVyZW5jZT0xMCxxdmFsdWU9MC4wNSkKCiMgU2VwYXJhdGUgb3V0IGh5cGVyIGFuZCBoeXBvCiMgZ2V0IGh5cGVyLW1ldGh5bGF0ZWQKaHlwZXJfZGVsdGFfbWt3aW5kb3dzM18xMDAwIDwtIGdldE1ldGh5bERpZmYoZGlmZl9ta3dpbmRvd3MzXzEwMDAsIGRpZmZlcmVuY2U9MTAscXZhbHVlPTAuMDUsdHlwZT0iaHlwZXIiKQoKIyBnZXQgaHlwby1tZXRoeWxhdGVkCmh5cG9fZGVsdGFfbWt3aW5kb3dzM18xMDAwIDwtIGdldE1ldGh5bERpZmYoZGlmZl9ta3dpbmRvd3MzXzEwMDAsIGRpZmZlcmVuY2U9MTAscXZhbHVlPTAuMDUsdHlwZT0iaHlwbyIpCgojIyBtYWtlIGl0IGEgZGF0YSBmcmFtZQpkZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGRlbHRhX21rd2luZG93czNfMTAwMCkKaHlwb19kZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGh5cG9fZGVsdGFfbWt3aW5kb3dzM18xMDAwKQpoeXBlcl9kZWx0YV9ta2Rmd2luZG93czNfMTAwMCA8LSBnZXREYXRhKGh5cGVyX2RlbHRhX21rd2luZG93czNfMTAwMCkKCiMjIE1ha2UgaXQgYSBncmFuZ2VzCmh5cG9fZGVsdGEzV19nciA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoaHlwb19kZWx0YV9ta2Rmd2luZG93czNfMTAwMCwga2VlcC5leHRyYS5jb2x1bW5zID0gVCkKaHlwZXJfZGVsdGEzV19nciA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoaHlwZXJfZGVsdGFfbWtkZndpbmRvd3MzXzEwMDAsIGtlZXAuZXh0cmEuY29sdW1ucyA9IFQpCgojZXhwb3J0IGFzIGEgYmVkIGZpbGUKbGlicmFyeShydHJhY2tsYXllcikKZXhwb3J0LmJlZChoeXBlcl9kZWx0YTNXX2dyLCBjb24gPSAnaHlwZXJfZGVsdGExVy5iZWQnKQpleHBvcnQuYmVkKGh5cG9fZGVsdGEzV19nciwgY29uID0gJ2h5cG9fZGVsdGExVy5iZWQnKQpleHBvcnQuYmVkKGRlbHRhM1dfZ3IsIGNvbiA9ICdkZWx0YTFXLmJlZCcpCmBgYAoKYGBge2Jhc2h9CiMgSW4gY29tbWFuZCBsaW5lCiMgbWFrZSByZWZlcmVuY2UgYmVkIG9ubHkgY29udGFpbiBnZW5lcwojIGF3ayAtRidcdCcgJ3tpZigkOD09ImdlbmUiKXByaW50ICQwfScgbW0zOV9saWZ0X3RvX01hdXIyLmJlZCA+IGdlbmVzX21tMzlfbGlmdF90b19NYXVyMi5iZWQKCiMgRmluZCBjbG9zZXN0IGdlbmUgdG8gbWV0aHlsa2l0IERNUiBvdXRwdXQgYW5kIG9ubHkgcHJpbnQgY29sdW1uIHdpdGggZ2VuZSBuYW1lIGluZm9ybWF0aW9uCmJlZHRvb2xzIGNsb3Nlc3QgLWEgaHlwb19kZWx0YTNXLmJlZCAtYiBnZW5lc19tbTM5X2xpZnRfdG9fTWF1cjIuYmVkIHwgYXdrICd7Ilx0IjsgcHJpbnQgJDE2fScgPiBnZW5lc19oeXBvX2RlbHRhM1cudHN2CgojIEZpbmQgY2xvc2VzdCBnZW5lIHRvIG1ldGh5bGtpdCBETVIgb3V0cHV0IGFuZCBwcmludCBldmVyeXRoaW5nCmJlZHRvb2xzIGNsb3Nlc3QgLWEgaHlwb19kZWx0YTNXLmJlZCAtYiBnZW5lc19tbTM5X2xpZnRfdG9fTWF1cjIuYmVkID4gQUxMZ2VuZXNfaHlwb19kZWx0YTNXLnRzdgoKIyBzZWQgdG8gcHJpbnQgb25seSBsaW5lcyB3aXRoIG5vIC4Kc2VkICcvXltbOnNwYWNlOl1dKlwuW1s6c3BhY2U6XV0qJC9kJyBnZW5lc19kZWx0YTNXLnRzdiA+IGNsZWFuZWRfZ2VuZXNfZGVsdGEzVy50c3YKCiMgYXdrIHRvIG9ubHkgcHJpbnQgJDIgY29sdW1uIHNlcGFyYXRlZCBieSA7CmF3ayAtRic7JyAne3ByaW50ICQyfScgY2xlYW5lZF9nZW5lc19kZWx0YTNXLnRzdiA+IDJjbGVhbmVkX2dlbmVzX2RlbHRhM1cudHN2CgojIHNlZCB0byBwcmludCBvbmx5IGdlbmUgbmFtZQpzZWQgLW4gJ3MvLipHZW5lSUQ6XChbMC05XSpcKSwuKi9cMS9wJyAyY2xlYW5lZF9nZW5lc19kZWx0YTNXLnRzdiA+IGZpbmFsX2dlbmVzX2RlbHRhM3cudHN2CmBgYAoKVGFrZSB0aGlzIGxpc3Qgb2YgZ2VuZXMgaW50byBbREFWSURdKGh0dHBzOi8vZGF2aWRiaW9pbmZvcm1hdGljcy5uaWguZ292LykuCgojIyMgR08gVGVybSBWaXN1YWxpemF0aW9uCgpWaXN1YWxpemUgW0RBVklEXShodHRwczovL2RhdmlkYmlvaW5mb3JtYXRpY3MubmloLmdvdi8pIG91dHB1dCB3aXRoIGdncGxvdDIuCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBsb2FkIGluIGRhdGEKbWV0aF9hbGx0cF9EQVZJRF9CUCA8LSByZWFkLmNzdigifi9EZXNrdG9wL0RBVklEL21ldGhfYWxsdHBfMC4wNV9EQVZJRF9CUC5jc3YiKQoKc3Vic2V0X21ldGhfYWxsdHBfREFWSURfQlAgPC0gc3Vic2V0KG1ldGhfYWxsdHBfREFWSURfQlAsIGBCZW5qYW1pbmlgIDwgMC4wMDIpCgojIyBwbG90CmdncGxvdChkYXRhID0gc3Vic2V0X21ldGhfYWxsdHBfREFWSURfQlAsIGFlcyh4ID0gR3JvdXAsIHkgPSBUZXJtLCBjb2xvciA9IGBCZW5qYW1pbmlgLCBzaXplID0gQ291bnQpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJyZWQiLCBoaWdoID0gImJsdWUiLCBuYW1lID0gIkJlbmphbWluaS1Ib2NoYmVyZ1xuIEFkanVzdGVkIFAtVmFsdWUiKSArIHRoZW1lX2J3KCkgKyB5bGFiKCJHZW5lIE9udG9sb2d5IFRlcm0iKSArIHhsYWIoIiIpICsgZ2d0aXRsZSgiR2VuZSBPbnRvbG9neSBFbnJpY2htZW50IGJ5IFRpbWUgUG9pbnQiKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGNvbG91ciA9ICJibGFjayIpKSArIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4LCBmYWNlPSJib2xkIiwgY29sb3VyID0gImJsYWNrIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIsaGp1c3QgPSAwLjUpICkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIixzaXplPTEyLCBjb2xvdXIgPSAiYmxhY2siKSkKCiMjIHNhdmUgcGxvdApnZ3NhdmUoIm1ldGhfR09lbnJpY2hfcHZhbHVlXzAuMDAyLmpwZWciLCBwbG90ID0gbGFzdF9wbG90KCksIHBhdGggPSAiL2hvbWUvRGVza3RvcC9EQVZJRCIsIHNjYWxlID0gMSwgd2lkdGg9MTcsIGhlaWdodD0xMCwgdW5pdHM9J2luJywgZHBpID0gMzAwKQoKYGBgCgojIFJOQXNlcSBBbmFseXNpcwoKUk5Bc2VxIGRhdGEgd2FzIGdlbmVyYXRlZCB3aXRoIER1YWwtaW5kZXhlZCBsaWJyYXJpZXMgdXNpbmcgVGFrYXJhL0Nsb250ZWNoIFN0cmFuZGVkIFRvdGFsIFJOQS1zZXEga2l0IHYyIC0gUGljbyBJbnB1dCBNYW1tYWxpYW4gcmVhZ2VudHMuCgojIyBGYXN0cWMKClVzZSBbZmFzdHFjXShodHRwczovL3d3dy5iaW9pbmZvcm1hdGljcy5iYWJyYWhhbS5hYy51ay9wcm9qZWN0cy9mYXN0cWMvKSB2MC4xMi4xIHRvIGNoZWNrIHF1YWxpdHkgb2YgcmF3IHJlYWRzLgoKYGBge2Jhc2h9Cmd1bnppcCAxX1NoaWVsZF9TMl9MMDAzX1IxXzAwMS5mYXN0cS5negpmYXN0cWMgMV9TaGllbGRfUzJfTDAwM19SMV8wMDEuZmFzdHEgLS1vdXQgZmFzdHFjX291dApgYGAKCiMjIFRyaW1taW5nIFNlcXVlbmNpbmcgQWRhcHRlcnMKClVzZSBCQmR1ayBmcm9tIFtCQm1hcF0oaHR0cHM6Ly9zb3VyY2Vmb3JnZS5uZXQvcHJvamVjdHMvYmJtYXAvKSB2MzUuODUgdG8gdHJpbSB0aGUgc2VxdWVuY2luZyBhZGFwdGVycy4KCmBgYHtiYXNofQogIyBiYmR1ayAKIGJiZHVrLnNoIGluMT1yZWFkMS5mcSBpbjI9cmVhZDIuZnEgb3V0MT1jbGVhbjEuZnEgb3V0Mj1jbGVhbjIuZnEKIAogIyBiYmR1ayBmb3Igb25lIHNldCBvZiBwYWlyZWQgZW5kcwogfi9EZXNrdG9wL2JibWFwL2JiZHVrLnNoIC1YbXgxZyByZWY9IH4vRGVza3RvcC9hZGFwdGVycy5mYSBpbjE9MTBfU2hpZWxkX1M5X0wwMDNfUjFfMDAxLmZhc3RxIGluMj0xMF9TaGllbGRfUzlfTDAwM19SMl8wMDEuZmFzdHEgb3V0MT0xMF9TaGllbGRfUzlfTDAwM19SMV8wMDFfdHJpbS5mYXN0cSBvdXQyPTEwX1NoaWVsZF9TOV9MMDAzX1IyXzAwMV90cmltLmZhc3RxIAogCiAjIGJiZHVrIGxvb3AgZm9yIG11bHRpcGxlIHNldHMgb2YgcGFpcmVkIGVuZCBzYW1wbGVzCiBmb3IgUjEgaW4gKlIxKgpkbwogIFIyPSR7UjEvL1IxXzAwMS5mYXN0cS5nei9SMl8wMDEuZmFzdHEuZ3p9CiAgUjF0cmltPSR7UjEvLy5mYXN0cS5nei9fdHJpbS5mYXN0cS5nen0KICBSMnRyaW09JHtSMi8vLmZhc3RxLmd6L190cmltLmZhc3RxLmd6fQp+L0Rlc2t0b3AvYmJtYXAvYmJkdWsuc2ggLVhteDFnIHJlZj0gfi9EZXNrdG9wL2JibWFwL3Jlc291cmNlcy9hZGFwdGVycy5mYSBpbjE9JFIxIGluMj0kUjIgb3V0MT0kUjF0cmltIG91dDI9JFIydHJpbQpkb25lCmBgYAoKIyMgQWxpZ24gdG8gUmVmZXJlbmNlIEdlbm9tZSB3aXRoIFNUQVIKClVzZSBbU1RBUl0oaHR0cHM6Ly9naXRodWIuY29tL2FsZXhkb2Jpbi9TVEFSKSBhbGlnbmVyIHRvIGFsaWduIHNlcXVlbmNlIGRhdGEgdG8gYSByZWZlcmVuY2UgZ2Vub21lLgoKIyMjIEdlbmVyYXRlIEdlbm9tZSBJbmRleAoKYGBge2Jhc2h9CgojIGZvciBHRkYgaW5zdGVhZCBvZiBHVEYgZmlsZSB1c2UgLS1zamRiR1RGdGFnRXhvblBhcmVudFRyYW5zY3JpcHQgaW5zdGVhZCBvZiAtLXNqZGJHVEZmaWxlClNUQVIgLS1ydW5UaHJlYWROIDMwIC0tcnVuTW9kZSBnZW5vbWVHZW5lcmF0ZSAtLWdlbm9tZURpciB+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvZ2Vub21lRGlyIC0tZ2Vub21lRmFzdGFGaWxlcyB+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvcmVmZXJlbmNlX2RhdGEvR0NGXzAxNzYzOTc4NS4xX0JDTV9NYXVyXzIuMF9nZW5vbWljLmZhIC0tc2pkYkdURmZpbGUgfi9EZXNrdG9wL3JuYXNlcV9oYW1zdGVyL2xpZnRvZmZfb3V0L21tMzlfbGlmdF90b19NYXVyMi5nZmYgCgpgYGAKCiMjIyBBbGlnbgoKQWxpZ24gb25lIHNhbXBsZSdzIHBhaXJlZCBlbmQgZmlsZXMuCgpgYGB7YmFzaH0KbWtkaXIgYWxpZ25tZW50cwoKU1RBUiAtLXJ1blRocmVhZE4gMzAgXAogICAgICAtLWdlbm9tZURpciB+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvZ2Vub21lRGlyIFwKICAgICAgLS1yZWFkRmlsZXNJbiAxX1NoaWVsZF9TMl9MMDAzX1IxXzAwMV90cmltLmZhc3RxLmd6IDFfU2hpZWxkX1MyX0wwMDNfUjJfMDAxX3RyaW0uZmFzdHEuZ3ogXAogICAgICAtLXJlYWRGaWxlc0NvbW1hbmQgemNhdCBcCiAgICAgIC0tb3V0U0FNdHlwZSBCQU0gU29ydGVkQnlDb29yZGluYXRlIFwKICAgICAgLS1xdWFudE1vZGUgR2VuZUNvdW50cyBcCiAgICAgIC0tb3V0RmlsZU5hbWVQcmVmaXggYWxpZ25tZW50cy8xX1NoaWVsZF9TMl9MMDAzCgpgYGAKCkxvb3AgdG8gYWxpZ24gZmlsZXMgUjEgYW5kIFIyIGZvciBhbGwgc2FtcGxlcyBhdCBvbmNlLgoKYGBge2Jhc2h9CiMgZGVmaW5lIHZhcmlhYmxlcwppbmRleD1+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvZ2Vub21lRGlyLwoKZm9yIGkgaW4gKl8wMDFfdHJpbS5mYXN0cS5negpkbyAKICBQUkVGSVg9JChlY2hvICR7aX0gfCBzZWQgJ3MvXF9SLl8wMDFfdHJpbS5mYXN0cS5nei8vJykKICBlY2hvICRQUkVGSVgKCiAgIyBkZWZpbmUgUjEgZmFzdHEgZmlsZW5hbWUKICBSMT1+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvcm5hc2VxX2Zhc3RxL3RyaW1fYmJkdWsvJHtQUkVGSVh9X1IxXzAwMV90cmltLmZhc3RxLmd6CiAgZWNobyAkUjEKCiAjIGRlZmluZSBSMiBmYXN0cSBmaWxlbmFtZQogIFIyPX4vRGVza3RvcC9ybmFzZXFfaGFtc3Rlci9ybmFzZXFfZmFzdHEvdHJpbV9iYmR1ay8ke1BSRUZJWH1fUjJfMDAxX3RyaW0uZmFzdHEuZ3oKICBlY2hvICRSMgoKICMgYWxpZ24gd2l0aCBTVEFSCiAgU1RBUiAtLXJ1blRocmVhZE4gMzAgXAogICAgLS1nZW5vbWVEaXIgJGluZGV4IFwKICAgIC0tcmVhZEZpbGVzSW4gJHtSMX0gJHtSMn0gXAogICAgLS1vdXRTQU10eXBlIEJBTSAgIFNvcnRlZEJ5Q29vcmRpbmF0ZSBcCiAgICAtLXF1YW50TW9kZSBHZW5lQ291bnRzIFwKICAgIC0tcmVhZEZpbGVzQ29tbWFuZCB6Y2F0IAogICAgLS1vdXRGaWxlTmFtZVByZWZpeCBhbGlnbm1lbnRzLyR7UFJFRklYfV8KZG9uZQoKZWNobyAiZG9uZSEiCmBgYAoKIyMgVXNlIGZlYXR1cmVDb3VudHMgdG8gQ291bnQgVHJhbnNjcmlwdHMKClVzZSBbZmVhdHVyZUNvdW50c10oaHR0cHM6Ly9zdWJyZWFkLnNvdXJjZWZvcmdlLm5ldC9mZWF0dXJlQ291bnRzLmh0bWwpIHYyLjAuMyB0byBjb3VudCBtYXBwZWQgcmVhZHMuCgpgYGB7YmFzaH0KZmVhdHVyZUNvdW50cyAtTSAtcCAtdCBleG9uIC1GIEdGRiAtZyAiSUQiIC1hIH4vRGVza3RvcC9ybmFzZXFfaGFtc3Rlci9saWZ0b2ZmX291dC9tbTM5X2xpZnRfdG9fTWF1cjIuZ2ZmIC1vIH4vRGVza3RvcC9ybmFzZXFfaGFtc3Rlci9mZWF0dXJlY291bnRzX291dC8xX01fY291bnRzLnR4dCB+L0Rlc2t0b3Avcm5hc2VxX2hhbXN0ZXIvcm5hc2VxX2Zhc3RxL3RyaW1fYmJkdWsvYWxpZ25tZW50cy90ZXN0LzFfU2hpZWxkX1MyX0wwMDNBbGlnbmVkLnNvcnRlZEJ5Q29vcmQub3V0LmJhbQpgYGAKCiMjIERFU2VxMgoKVXNlIFtERVNlcTJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9ERVNlcTIuaHRtbCkgdG8gZGV0ZXJtaW5lIGRpZmZlcmVudGlhbCBtZXRoeWxhdGlvbi4KCiMjIyBJbXBvcnQgRGF0YQoKSXQgaXMgVkVSWSBpbXBvcnRhbnQgdGhhdCB0aGUgcm93cyBvZiBjb2xkYXRhIGFyZSBpbiB0aGUgc2FtZSBvcmRlciBhcyB0aGUgY29sdW1ucyBvZiB0aGUgZmVhdHVyZWNvdW50cyBtYXRyaXgKCmBgYHtyfQojIHJlYWQgaW4gbWV0YWRhdGEgZmlsZSAKY29sZGF0YSA8LSByZWFkLmNzdigic2FtcGxlX2RhdGEuY3N2Iiwgcm93Lm5hbWVzPTEpCgojIG1hbmlwdWxhdGUgY29sRGF0YQpjb2xkYXRhIDwtIGNvbGRhdGFbLGMoInRyZWF0bWVudCIsInRpbWVwb2ludCIsImdyb3VwIildCmNvbGRhdGEkdHJlYXRtZW50IDwtIGZhY3Rvcihjb2xkYXRhJHRyZWF0bWVudCkKY29sZGF0YSR0aW1lcG9pbnQgPC0gZmFjdG9yKGNvbGRhdGEkdGltZXBvaW50KQpjb2xkYXRhJGdyb3VwIDwtIGZhY3Rvcihjb2xkYXRhJGdyb3VwKQoKIyByZWFkIGluIGZlYXR1cmUgY291bnRzIGZpbGUKZmVhdHVyZWNvdW50cyA8LSByZWFkLmRlbGltKCJmZWF0dXJlY291bnRzX291dC9mZWF0dXJlY291bnRzL2FsbF9jb3VudHMudHh0IiwgaGVhZGVyID0gVFJVRSwgc2tpcCA9IDEpCgojIGNoYW5nZSB0aGUgY29sdW1uIG5hbWVzIHRvIG1hdGNoIGNvbGRhdGEKY29sbmFtZXMoZmVhdHVyZWNvdW50cykKY29sbmFtZXMoZmVhdHVyZWNvdW50cykgPC0gYygiR2VuZWlkIiwgIkNociIsICJTdGFydCIsICJFbmQiLCAiU3RyYW5kIiwgIkxlbmd0aCIsICIxMCIsICIxMSIsICIxMiIsICIxMyIsIjE0IiwiMTUiLCIxNiIsIjE3IiwiMTgiLCIxIiwiMjAiLCIzIiwiNCIsIjUiLCI2IiwiNyIsIjgiLCI5IikKCiMgbWFrZSByb3cgbmFtZXMgdGhlIEdlbmVpZCBjb2x1bW4KI3Jvd25hbWVzKGZlYXR1cmVjb3VudHMpIDwtIGZlYXR1cmVjb3VudHNbICwgMV0KI2ZlYXR1cmVjb3VudHMgPSBhcy5tYXRyaXgoZmVhdHVyZWNvdW50c1sgLCAtMV0pCgojIHRoZSBmaXJzdCBjb2x1bW4gcmlnaHQgb2YgTGVuZ3RoIGlzIHdoZXJlIHRoZSBjb3VudHMgc3RhcnQsIHVudGlsIHRoZSBlbmQgb2YgdGhlIG1hdHJpeApjb2x1bW4uZnJvbSA9IHdoaWNoKGNvbG5hbWVzKGZlYXR1cmVjb3VudHMpID09ICJMZW5ndGgiKSArIDEKY29sdW1uLmVuZCAgPSBuY29sKGZlYXR1cmVjb3VudHMpCmBgYAoKIyMjIENyZWF0ZSBERVNlcSBEYXRhc2V0CgpgYGB7cn0KIyBpbXBvcnQgaW50byBERVNlcTIgbWF0cml4CmRkcyA8LSAKREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBmZWF0dXJlY291bnRzWywgY29sdW1uLmZyb206Y29sdW1uLmVuZF0sCiAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiB0cmVhdG1lbnQpCiMgYWRkIGdlbmUgbmFtZXMgYXMgcm93IG5hbWVzIHRvIGRkcwpyb3duYW1lcyhkZHMpIDwtIGZlYXR1cmVjb3VudHMkR2VuZWlkCgpkZHMKCiMgbG9vayBhdCBhc3NheQphc3NheShkZHMpCgojIGxvb2sgYXQgYXNzYXkgbmFtZXMKYXNzYXlOYW1lcyhkZHMpCgojIGV4YW1pbmUgY291bnQgc3VtcyBvZiBhbGwgaW5kaXZpZHVhbCBzYW1wbGVzIGdlbmUgZXhwcmVzc2lvbgpjb2xTdW1zKGFzc2F5KGRkcykpCmBgYAoKIyMgRGF0YSBFeHBsb3JhdGlvbgoKRXhwbG9yZSB0aGUgZGF0YSBmb2xsb3dpbmcgdGhlIERFU2VxMiB2aWduZXR0ZXMuCgojIyMgUHJlLWZpbHRlcmluZyBmb3IgRGF0YSBFeHBsb3JhdGlvbgoKUGVyZm9ybSBwcmUtZmlsdGVyaW5nIHRvIGtlZXAgb25seSByb3dzIHRoYXQgaGF2ZSBhIGNvdW50IG9mIGF0IGxlYXN0IDEwIGZvciBhIG1pbmltYWwgbnVtYmVyIG9mIHNhbXBsZXMuIEZvciBkYXRhIGV4cGxvcmF0aW9uIG9ubHksIE5PVCBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMuCgpgYGB7cn0KbnJvdyhkZHMpCiMgcHJlLWZpbHRlcmluZyB0byBrZWVwIG9ubHkgcm93cyB0aGF0IGhhdmUgYSBjb3VudCBvZiBhdCBsZWFzdCAxMCBmb3IgYSBtaW5pbWFsIG51bWJlciBvZiBzYW1wbGVzCnNtYWxsZXN0R3JvdXBTaXplIDwtIDMKa2VlcCA8LSByb3dTdW1zKGNvdW50cyhkZHMpID49IDEwKSA+PSBzbWFsbGVzdEdyb3VwU2l6ZQpkZHNfZXggPC0gZGRzW2tlZXAsXQpucm93KGRkc19leCkKYGBgCgojIyMgVHJhbnNmb3JtIERhdGEgdG8gVlNUCgpUcmFuc2Zvcm0gZGF0YSB0byBzdGFiaWxpemUgdGhlIHZhcmlhbmNlIGFjcm9zcyB0aGUgbWVhbiB1c2luZyB0aGUgdmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24gKFZTVCkgZm9yIG5lZ2F0aXZlIGJpbm9taWFsIGRhdGEgd2l0aCBhIGRpc3BlcnNpb24tbWVhbiB0cmVuZC4KCmBgYHtyfQp2c2QgPC0gdnN0KGRkcywgYmxpbmQgPSBGQUxTRSkKaGVhZChhc3NheSh2c2QpLCAzKQpgYGAKCiMjIyBUcmFuc2Zvcm0gRGF0YSB0byBybG9nCgpUcmFuc2Zvcm0gZGF0YSB0byBzdGFiaWxpemUgdGhlIHZhcmlhbmNlIGFjcm9zcyB0aGUgbWVhbiB1c2luZyB0aGUgcmVndWxhcml6ZWQtbG9nYXJpdGhtIHRyYW5zZm9ybWF0aW9uIG9yIHJsb2cKCmBgYHtyfQpybGQgPC0gcmxvZyhkZHMsIGJsaW5kID0gRkFMU0UpCmhlYWQoYXNzYXkocmxkKSwgMykKYGBgCgojIyMgQ29tcGFyZSBUcmFuc2Zvcm1hdGlvbnMKCmBgYHtyfQpkZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHMpCgpkZiA8LSBiaW5kX3Jvd3MoCiAgYXMuZGF0YS5mcmFtZShsb2cyKGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQ9VFJVRSlbLCAxOjJdKzEpKSAlPiUKICAgICAgICAgbXV0YXRlKHRyYW5zZm9ybWF0aW9uID0gImxvZzIoeCArIDEpIiksCiAgYXMuZGF0YS5mcmFtZShhc3NheSh2c2QpWywgMToyXSkgJT4lIG11dGF0ZSh0cmFuc2Zvcm1hdGlvbiA9ICJ2c3QiKSwKICBhcy5kYXRhLmZyYW1lKGFzc2F5KHJsZClbLCAxOjJdKSAlPiUgbXV0YXRlKHRyYW5zZm9ybWF0aW9uID0gInJsb2ciKSkKICAKY29sbmFtZXMoZGYpWzE6Ml0gPC0gYygieCIsICJ5IikgIAoKbHZscyA8LSBjKCJsb2cyKHggKyAxKSIsICJ2c3QiLCAicmxvZyIpCmRmJHRyYW5zZm9ybWF0aW9uIDwtIGZhY3RvcihkZiR0cmFuc2Zvcm1hdGlvbiwgbGV2ZWxzPWx2bHMpCgpnZ3Bsb3QoZGYsIGFlcyh4ID0geCwgeSA9IHkpKSArIGdlb21faGV4KGJpbnMgPSA4MCkgKwogIGNvb3JkX2ZpeGVkKCkgKyBmYWNldF9ncmlkKCAuIH4gdHJhbnNmb3JtYXRpb24pICAKYGBgCgojIyMgQ29tcGFyZSBTYW1wbGUgRGlzdGFuY2VzCgpUbyBlbnN1cmUgd2UgaGF2ZSBhIHJvdWdobHkgZXF1YWwgY29udHJpYnV0aW9uIGZyb20gYWxsIGdlbmVzLCB3ZSB1c2UgaXQgb24gdGhlIFZTVCBkYXRhLiBjYWxjdWxhdGUgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMuCgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHZzZCkpKQpzYW1wbGVEaXN0cwoKIyBtYWtlIHRoZSBoZWF0bWFwIG9mIHNhbXBsZSBkaXN0YW5jZXMgdXNpbmcgZXVjbGlkZWFuIGRpc3RhbmNlcwpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeCggc2FtcGxlRGlzdHMgKQpyb3duYW1lcyhzYW1wbGVEaXN0TWF0cml4KSA8LSBwYXN0ZSggdnNkJHRyZWF0bWVudCwgdnNkJHRpbWVwb2ludCwgc2VwID0gIiAtICIgKQpjb2xuYW1lcyhzYW1wbGVEaXN0TWF0cml4KSA8LSBOVUxMCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKCByZXYoYnJld2VyLnBhbCg5LCAiQmx1ZXMiKSkgKSgyNTUpCnBoZWF0bWFwKHNhbXBsZURpc3RNYXRyaXgsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IHNhbXBsZURpc3RzLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSBzYW1wbGVEaXN0cywKICAgICAgICAgY29sID0gY29sb3JzKQpgYGAKCiMjIyBDYWxjdWxhdGUgU2FtcGxlIERpc3RhbmNlIHVzaW5nIFBvaXNzb24KClRoaXMgbWVhc3VyZSBvZiBkaXNzaW1pbGFyaXR5IGJldHdlZW4gY291bnRzIHRha2VzIHRoZSBpbmhlcmVudCB2YXJpYW5jZSBzdHJ1Y3R1cmUgb2YgY291bnRzIGludG8gY29uc2lkZXJhdGlvbiB3aGVuIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiBzYW1wbGVzLgoKYGBge3J9CmxpYnJhcnkoIlBvaUNsYUNsdSIpCnBvaXNkIDwtIFBvaXNzb25EaXN0YW5jZSh0KGNvdW50cyhkZHNfZXgpKSkKCiMgSGVhdG1hcCBvZiBzYW1wbGUtdG8tc2FtcGxlIGRpc3RhbmNlcyB1c2luZyB0aGUgUG9pc3NvbiBEaXN0YW5jZS4Kc2FtcGxlUG9pc0Rpc3RNYXRyaXggPC0gYXMubWF0cml4KCBwb2lzZCRkZCApCnJvd25hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBwYXN0ZSggZGRzX2V4JHRyZWF0bWVudCwgZGRzX2V4JHRpbWVwb2ludCwgc2VwPSIgLSAiICkKY29sbmFtZXMoc2FtcGxlUG9pc0Rpc3RNYXRyaXgpIDwtIE5VTEwKcGhlYXRtYXAoc2FtcGxlUG9pc0Rpc3RNYXRyaXgsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IHBvaXNkJGRkLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSBwb2lzZCRkZCwKICAgICAgICAgY29sID0gY29sb3JzKQpgYGAKCiMjIyBQQ0EKClByaW5jaXBsZSBDb21wb25lbnQgQW5hbHlzaXMgb2YgdGhlIFZTRCB0cmFuc2Zvcm1lZCBkYXRhLgoKYGBge3J9CnBsb3RQQ0EodnNkLCBpbnRncm91cD1jKCJ0cmVhdG1lbnQiLCAidGltZXBvaW50IikpCgojIFBDQSBidXQgbW9yZSBkZXRhaWxlZCAodXNpbmcgZ2dwbG90KQpwY2FEYXRhIDwtIHBsb3RQQ0EodnNkLCBpbnRncm91cD1jKCJ0cmVhdG1lbnQiLCAidGltZXBvaW50IiksIHJldHVybkRhdGE9VFJVRSkKcGVyY2VudFZhciA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEsICJwZXJjZW50VmFyIikpCmdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIGNvbG9yPXRyZWF0bWVudCwgc2hhcGU9dGltZXBvaW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkgKyAKICBjb29yZF9maXhlZCgpCmBgYAoKIyMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgb24gUmF3IERhdGEKClRyZWF0bWVudHMgdmVyc3VzIGNvbnRyb2xzIG9ubHkgaGVyZSwgbm8gdGltZSBzZXJpZXMgaW5jbHVkZWQuIERFU2VxMiB1c2VzIHRoZSBCZW5qYW1pbmktSG9jaGJlcmcgKEJIKSBhZGp1c3RtZW50LgoKYGBge3J9CmRkcyA8LSBERVNlcShkZHMpCnJlc3VsdHNOYW1lcyhkZHMpCnJlcyA8LSByZXN1bHRzKGRkcykKcmVzCgojIyBvcmRlciByZXN1bHRzIGJ5IHNtYWxsZXN0IHAtdmFsdWUKcmVzT3JkZXJlZCA8LSByZXNbb3JkZXIocmVzJHB2YWx1ZSksXQpyZXNPcmRlcmVkCgojIyBTdW1tYXJpemUgYmFzaWMgdGFsbGllcwpzdW1tYXJ5KHJlcykKCiMjIFN1bW1hcml6ZSBob3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjE/CnN1bShyZXMkcGFkaiA8IDAuMSwgbmEucm09VFJVRSkKCiMgcmVzdWx0cyBmdW5jdGlvbiwgbG93ZXIgdGhlIGZhbHNlIGRpc2NvdmVyeSByYXRlIHRocmVzaG9sZApyZXMxMCA8LSByZXN1bHRzKGRkcywgYWxwaGE9MC4xMCkKcmVzMDUgPC0gcmVzdWx0cyhkZHMsIGFscGhhPTAuMDUpCnN1bW1hcnkocmVzMTApCgojIFJhaXNlIHRoZSBsb2cyIGZvbGQgY2hhbmdlIHRocmVzaG9sZCwgc28gdGhhdCB3ZSB0ZXN0IGZvciBnZW5lcyB0aGF0IHNob3cgbW9yZSBzdWJzdGFudGlhbCBjaGFuZ2VzIGR1ZSB0byB0cmVhdG1lbnQuIEZvciBleGFtcGxlLCBieSBzcGVjaWZ5aW5nIGxmY1RocmVzaG9sZCA9IDEsIHdlIHRlc3QgZm9yIGdlbmVzIHRoYXQgc2hvdyBzaWduaWZpY2FudCBlZmZlY3RzIG9mIHRyZWF0bWVudCBvbiBnZW5lIGNvdW50cyBtb3JlIHRoYW4gZG91Ymxpbmcgb3IgbGVzcyB0aGFuIGhhbHZpbmcsIGJlY2F1c2UgMl4xPTIuCnJlc0xGQzEgPC0gcmVzdWx0cyhkZHMsIGxmY1RocmVzaG9sZD0uNSkKdGFibGUocmVzTEZDMSRwYWRqIDwgMC4xKQoKIyBJbmZvcm1hdGlvbiBhYm91dCB3aGljaCB2YXJpYWJsZXMgYW5kIHRlc3RzIHdlcmUgdXNlZCBjYW4gYmUgZm91bmQgYnkgY2FsbGluZyB0aGUgZnVuY3Rpb24gbWNvbHMgb24gdGhlIHJlc3VsdHMgb2JqZWN0LgptY29scyhyZXMpJGRlc2NyaXB0aW9uCm1jb2xzKHJlcywgdXNlLm5hbWVzID0gVFJVRSkKCiMgVGhlIGNvbHVtbiBsb2cyRm9sZENoYW5nZSBpcyB0aGUgZWZmZWN0IHNpemUgZXN0aW1hdGUuIEl0IHRlbGxzIHVzIGhvdyBtdWNoIHRoZSBnZW5l4oCZcyBleHByZXNzaW9uIHNlZW1zIHRvIGhhdmUgY2hhbmdlZCBkdWUgdG8gdHJlYXRtZW50IGluIGNvbXBhcmlzb24gdG8gdW50cmVhdGVkIHNhbXBsZXMuIFRoaXMgdmFsdWUgaXMgcmVwb3J0ZWQgb24gYSBsb2dhcml0aG1pYyBzY2FsZSB0byBiYXNlIDIuCgpgYGAKCiMjIyBWb2xjYW5vIFBsb3QKCmBgYHtyfQpwbG90TUEocmVzMTAsIHlsaW09YygtNiw2KSkKYGBgCgojIyMgSGlzdG9ncmFtCgpgYGB7cn0KaGlzdChyZXMkcHZhbHVlW3JlcyRiYXNlTWVhbiA+IDFdLCBicmVha3MgPSAwOjIwLzIwLAogICAgIGNvbCA9ICJncmV5NTAiLCBib3JkZXIgPSAid2hpdGUiKQpgYGAKCiMjIyBIZWF0bWFwIG9mIENvdW50cwoKYGBge3J9CmxpYnJhcnkoInBoZWF0bWFwIikKc2VsZWN0IDwtIG9yZGVyKHJvd01lYW5zKGNvdW50cyhkZHMsbm9ybWFsaXplZD1UUlVFKSksCiAgICAgICAgICAgICAgICBkZWNyZWFzaW5nPVRSVUUpWzE6MjBdCmRmIDwtIGFzLmRhdGEuZnJhbWUoY29sRGF0YShkZHMpWyxjKCJ0cmVhdG1lbnQiLCJ0aW1lcG9pbnQiKV0pCnBoZWF0bWFwKGFzc2F5KGRkcylbc2VsZWN0LF0sIGNsdXN0ZXJfcm93cz1GQUxTRSwgc2hvd19yb3duYW1lcz1GQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzPVRSVUUsIGFubm90YXRpb25fY29sPWRmKQpgYGAKCkFub3RoZXIgSGlzdG9ncmFtCgpUaGUgcmF0aW8gb2Ygc21hbGwgcCB2YWx1ZXMgZm9yIGdlbmVzIGJpbm5lZCBieSBtZWFuIG5vcm1hbGl6ZWQgY291bnQuIFRoZSBwIHZhbHVlcyBhcmUgZnJvbSBhIHRlc3Qgb2YgbG9nMiBmb2xkIGNoYW5nZSBncmVhdGVyIHRoYW4gMSBvciBsZXNzIHRoYW4gLTEuCgpgYGB7cn0KcXMgPC0gYygwLCBxdWFudGlsZShyZXNMRkMxJGJhc2VNZWFuW3Jlc0xGQzEkYmFzZU1lYW4gPiAwXSwgMDo2LzYpKQpiaW5zIDwtIGN1dChyZXNMRkMxJGJhc2VNZWFuLCBxcykKbGV2ZWxzKGJpbnMpIDwtIHBhc3RlMCgifiIsIHJvdW5kKHNpZ25pZigocXNbLTFdICsgcXNbLWxlbmd0aChxcyldKS8yLCAyKSkpCmZyYWN0aW9uU2lnIDwtIHRhcHBseShyZXNMRkMxJHB2YWx1ZSwgYmlucywgZnVuY3Rpb24ocCkKICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHAgPCAuMDUsIG5hLnJtID0gVFJVRSkpCmJhcnBsb3QoZnJhY3Rpb25TaWcsIHhsYWIgPSAibWVhbiBub3JtYWxpemVkIGNvdW50IiwKICAgICAgICAgICAgICAgICAgICAgeWxhYiA9ICJmcmFjdGlvbiBvZiBzbWFsbCBwIHZhbHVlcyIpCmBgYAoKIyMgVGltZSBDb3Vyc2UgREVTZXEyIEFuYWx5c2lzCgojIyMgU2VwYXJhdGUgT3V0IFRpbWUgUG9pbnRzCgpTZXBhcmF0ZSBvdXQgZGF0YSBmcm9tIGVhY2ggdGltZSBwb2ludCB1c2luZyB0aGUgY291bnQgZGF0YS4gRm9yIGV4YW1wbGUsIGhlcmUgaXMgdGltZSBwb2ludCAzLgoKYGBge3J9CiMgVGltZSBwb2ludCAzCmRkczMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBmZWF0dXJlY291bnRzWywgYygiMTEiLCIxNyIsIjE4IiwiMjAiLCI2IiwiNyIpXSwgY29sRGF0YSA9IGNvbGRhdGFbYygiMTEiLCIxNyIsIjE4IiwiMjAiLCI2IiwiNyIpLF0sIH4gdHJlYXRtZW50KQojIGFkZCBnZW5lIG5hbWVzIGFzIHJvdyBuYW1lcyB0byBkZHMKcm93bmFtZXMoZGRzMykgPC0gZmVhdHVyZWNvdW50cyRHZW5laWQKCmRkczMKCiMgUmVzdWx0cwpkZHMzIDwtIERFU2VxKGRkczMpCnJlc3VsdHNOYW1lcyhkZHMzKQpyZXMzIDwtIHJlc3VsdHMoZGRzMykKc3VtbWFyeShyZXMzKQpyZXMzCgojIHJlc3VsdHMgZnVuY3Rpb24KcmVzM18wNSA8LSByZXN1bHRzKGRkczMsIGFscGhhPTAuMDUpCnN1bW1hcnkocmVzM18wNSkKcmVzM18wNQoKI29yZGVyIGJ5IHNtYWxsZXN0IHB2YWx1ZQpyZXMzIDwtIHJlczNbb3JkZXIocmVzMyRwYWRqKSxdCnJlczMKYGBgCgojIyMgRXhwb3J0IFJlc3VsdHMgdG8gY3N2CgpgYGB7cn0Kd3JpdGUuY3N2KHJlczMsIGZpbGUgPSAifi9EZXNrdG9wL3JuYXNlcV9oYW1zdGVyL3Jlc3VsdHMvdHAxX2dlbmVfcmVzdWx0cy5jc3YiKQpgYGAKCiMjIyBWb2xjYW5vIHBsb3RzCgpgYGB7cn0KcGxvdE1BKHJlczMsIHlsaW09YygtNiw2KSkKYGBgCgojIyMgR08gVGVybSBWaXN1YWxpemF0aW9uCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBsb2FkIGluIGRhdGEKYWxsdHBfREFWSURfQlAgPC0gcmVhZC5jc3YoIn4vRGVza3RvcC9ybmFzZXFfaGFtc3Rlci9EQVZJRC9hbGx0cF8wLjA1X0RBVklEX0JQLmNzdiIpCgojIyBwbG90CmdncGxvdChkYXRhID0gYWxsdHBfREFWSURfQlAsIGFlcyh4ID0gR3JvdXAsIHkgPSBUZXJtLCBjb2xvciA9IGBCZW5qYW1pbmlgLCBzaXplID0gQ291bnQpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJyZWQiLCBoaWdoID0gImJsdWUiLCBuYW1lID0gIkJlbmphbWluaS1Ib2NoYmVyZ1xuIEFkanVzdGVkIFAtVmFsdWUiKSArIHNjYWxlX3NpemUobmFtZSA9ICJHZW5lIENvdW50IikgKyB0aGVtZV9idygpICsgeWxhYigiR08gQmlvbG9naWNhbCBQcm9jZXNzZXMgVGVybSIpICsgeGxhYigiIikgKyBnZ3RpdGxlKCJHZW5lIE9udG9sb2d5IEVucmljaG1lbnQgYnkgVGltZSBQb2ludCIpICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgY29sb3VyID0gImJsYWNrIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIsaGp1c3QgPSAwLjUpICkgKyB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFjZT0iYm9sZCIsIGNvbG91ciA9ICJibGFjayIpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLHNpemU9MTQsIGNvbG91ciA9ICJibGFjayIpKSAKCiMjIHNhdmUgcGxvdApnZ3NhdmUoInJuYXNlcV9HT2VucmljaF9hbGwuanBlZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgcGF0aCA9ICIvaG9tZS9sYXJzZW5sYWIvRGVza3RvcC9ybmFzZXFfaGFtc3Rlci9EQVZJRCIsIHNjYWxlID0gMSwgd2lkdGg9MTUsIGhlaWdodD0xMCwgdW5pdHM9J2luJywgZHBpID0gMzAwKQoKYGBgCg==