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

Format Lifted Genes

# use liftoff gff and create GRanges
lift_genes <- gffToGRanges("liftoff_out/mm39_lift_to_Maur2.gff", filter = "gene")

# change to genbank names
lift_genes <- renameSeqlevels(lift_genes,
                              seqlevels_ncbi2genbank)
# make dataframe of lift_genes
lift_genes_df <- lift_genes %>%
  data.frame() %>%
  # relocate(gene_id, .after = "strand") %>%
  dplyr::select(!(model_evidence:last_col())) %>% 
  filter(gene_biotype == "protein_coding" & type == "gene") %>% 
  mutate(gene_id = map_chr(Dbxref,
                           ~ pluck(.x, 1) %>% 
           str_remove(., "GeneID:"))) %>% 
  relocate(gene_id, .after = "strand")

#make granges from dataframe
lift_genes_filter <- makeGRangesFromDataFrame(lift_genes_df,
                                              keep.extra.columns = TRUE)

seqlevels(lift_genes_filter) = df_genome$genbankAccession
seqlengths(lift_genes_filter) = df_genome$length

#EU660218.1 5310-6854 

genome(lift_genes_filter) <- "GCF_017639785.1"
genome(genome) <- "GCF_017639785.1"

lift_genes_extend <- extendTSS(lift_genes_filter,
                               extend_from = "gene",
                               seqlengths = seqlengths(lift_genes_filter),
                               mode = "basalPlusExt",
                        basal_upstream = 5000,      
                        basal_downstream = 5000,
                               extension = 0,
                               gene_id_type = "ENTREZ")
genome(lift_genes_extend) <- "GCF_017639785.1"

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)

Transform Data to VST

Transform data to stabilize the variance across the mean using the variance stabilizing transformation (VST) for negative binomial data with a dispersion-mean trend.

vsd <- vst(dds, blind = FALSE)
head(assay(vsd), 3)

Transform Data to rlog

Transform data to stabilize the variance across the mean using the regularized-logarithm transformation or rlog

rld <- rlog(dds, blind = FALSE)
head(assay(rld), 3)

Compare Transformations

dds <- estimateSizeFactors(dds)

df <- bind_rows(
  as.data.frame(log2(counts(dds, normalized=TRUE)[, 1:2]+1)) %>%
         mutate(transformation = "log2(x + 1)"),
  as.data.frame(assay(vsd)[, 1:2]) %>% mutate(transformation = "vst"),
  as.data.frame(assay(rld)[, 1:2]) %>% mutate(transformation = "rlog"))
  
colnames(df)[1:2] <- c("x", "y")  

lvls <- c("log2(x + 1)", "vst", "rlog")
df$transformation <- factor(df$transformation, levels=lvls)

ggplot(df, aes(x = x, y = y)) + geom_hex(bins = 80) +
  coord_fixed() + facet_grid( . ~ transformation)  

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==