Data Retrieval

This project is focused on analyzing the TCGA-THCA data from a multiomics approach. Specifically, we will be analyzing mRNA, miRNA, lncRNA, and methylation data to describe the trends and relationships between the multiomic profiles of patients and their prognostic characteristics (e.g. survival and/or recurrence).

To start off, we need to import the relevant data we will be using. This data is sourced from TCGA (The Cancer Genome Atlas), although the mRNA, miRNA, methylation, and clinical data came from Firehose (from the Broad Institute), and the lncRNA data came from TANRIC.

Let’s first import the datasets we will be working with, and assign them to variables.

library(readxl)
Warning: package ‘readxl’ was built under R version 4.2.2
#Inputting the data into variables that we can manipulate
clinical <- read_excel("THCA_Clinical_Data.xlsx")
lncRNA <- read_excel("THCA_lncRNA_Data.xlsx")
#methylation <- read_excel("THCA_Methylation_Data.xlsx")
miRNA <- read_excel("THCA_miRNA_Data_Final.xlsx")
mRNA <- read_excel("THCA_mRNA_Data_Final.xlsx")

Let’s look at the head of these tables to understand their structure.

#Accessing the first few rows of each dataset
head(clinical)
head(lncRNA)
#head(methylation)
head(miRNA)
head(mRNA)

Data Preprocessing

After looking at the table heads, there are a couple of issues that we can see:

1) The sample labels aren't identical - some have extra elements at the beginning or end, which         need to be standardized

2) The number of samples in each file aren't the same - for example, miRNA only has 541 samples,        and lncRNA has 556. This also needs to be standardized to make sure only samples with all           data are used.

Let’s start with step 1.

The general format of the data should look something like this: ‘TCGA-XX-XXXX-XX’, so let’s look at each file to see how we can fix it.

lncRNA seems to have extra labels at the start which we can remove, as well as underscores which we can convert to hyphens. However, what’s also important here is that we have normal and tumor samples explictly labeled, whereas other files use the ‘01’ marking for tumors and ‘11’ marking for normal samples. We need to adapt our lncRNA data to follow this structure.

#We are going through each column in the lncRNA dataset
for (i in 1:ncol(lncRNA)) {
  #If the column name - which is the sample ID - contains Normal, we'll add a -11 and format the name to remove the beginning and convert all underscores to hyphens.
  
  if (grepl("Normal", colnames(lncRNA)[i], fixed = TRUE) == TRUE) {
    colnames(lncRNA)[i] <- sub(".*-TC", "TC", colnames(lncRNA)[i])
    colnames(lncRNA)[i] <- gsub("_", "-", colnames(lncRNA)[i])
    colnames(lncRNA)[i] <- paste(colnames(lncRNA)[i], '-11', sep="")
  } else {
    #Otherwise, we'll still format the name, but we'll add a -01 at the end instead.
    colnames(lncRNA)[i] <- sub(".*-TC", "TC", colnames(lncRNA)[i])
    colnames(lncRNA)[i] <- gsub("_", "-", colnames(lncRNA)[i])
    colnames(lncRNA)[i] <- paste(colnames(lncRNA)[i], '-01', sep="")
  }
    
}

#We'll convert the first column to the standard name Gene_ID and remove the extra marker in the Ensemble ID after the decimal point.
colnames(lncRNA)[1] <- "Gene_ID"
lncRNA$Gene_ID <- sub("\\..*", "", lncRNA$Gene_ID)
head(lncRNA)

That looks more in line with the standard sample notation.

Both the methylation and the mRNA file have underscores that we can convert to hyphens.

#We'll iterate over each column and convert the underscores to dashes, for both datasets. Then, we'll convert the first column to the Gene_ID label.
for (i in 1:ncol(mRNA)) {
  colnames(mRNA)[i] <- gsub("_", "-", colnames(mRNA)[i])
}
colnames(mRNA)[1] <- "Gene_ID"
head(mRNA)

for (i in 1:ncol(methylation)) {
  colnames(methylation)[i] <- gsub("_", "-", colnames(methylation)[i])
}
colnames(methylation)[1] <- "Gene_ID"
head(methylation)

That seems to be fixed as well.

For miRNA data, there is a lot of extraneous data at the end about sample and vials, which we don’t need, so let’s remove that.

#We'll iterate over the column names and remove any extraneous information after our normal or tumor sample labels, and then create the Gene_ID column.

for (i in 1:ncol(miRNA)) {
  colnames(miRNA)[i] <- sub("_01.*", "_01", colnames(miRNA)[i])
  colnames(miRNA)[i] <- sub("_01.*", "_01", colnames(miRNA)[i])
  colnames(miRNA)[i] <- gsub("_", "-", colnames(miRNA)[i])
}
colnames(miRNA)[1] <- "Gene_ID"
head(miRNA)

Done!

For clinical data, we have our ‘Sample ID’ column with appropriate notation, so we don’t need to change that.

Now, for step 2, we have to identify which samples are missing from any of the files, as we only want data points present in each file. To accomplish this, I am first going to access all the sample IDs from each file. I’m then going to add them together to form a vector, and use the table function to see how many times each value appears in the vector. Given that we have 5 files of data, a sample present in all files should appear at least 5 times. Thus, we will isolate any values that appear less than 5 times.

#Here we access the specific sample names through indexing of the data (which starts from column 2).
clin_samples <- clinical$`Sample ID`
lnc_samples <- colnames(lncRNA)[2:557]
meth_samples <- colnames(methylation)[5:571]
miR_samples <- colnames(miRNA)[2:570]
mRNA_samples <- colnames(mRNA)[2:569]

#We concatenate all the sample data into a vector.
temp <- c(lnc_samples, meth_samples, miR_samples, mRNA_samples)
#Next, we count the occurrence of each unique sample ID in our vector using the table function - if a sample is present in all 4 datasets, it'll have a count of 4.
temp_table = table(temp)
#Now, we just want to keep the samples that have an occurrence of 4.
keep1 <- names(temp_table[temp_table >= 4])
keep1 <- c("Gene_ID", keep1)

Now, we will remove their columns/rows from each data file in a sequential manner.

#Next, we just subset the datasets to only keep columns that match a value in our vector of accepted samples - using the subset and select functions.
miRNA_new <- subset(miRNA, select=c(keep1))
mRNA_new <- subset(mRNA, select=c(keep1))
methylation_new <- subset(methylation, select=c(keep1))
lncRNA_new <- subset(lncRNA, select=c(keep1))

One additional step we need to take is to make sure that all our tumor samples have clinical data available. Thus, I’m going to compare our tumor samples (with the -01 ending) to our clinical sample IDs to find and also remove any samples that don’t overlap, using a similar method.

#Now, since clinical data only contains tumor samples, we need to separate out our tumor samples by searching for all samples with the -01 tag.
tumor_samples <- grep("-01", keep1, value=TRUE)
normal_samples <- grep("-11", keep1, value=TRUE)

#Same as above - vector, count, keep anything occurring twice since we are comparing 2 datasets.
temp2 <- c(tumor_samples, clin_samples)
temp_table_2 = table(temp2)
keep2 <- names(temp_table_2[temp_table_2 >= 2])
keep2 <- c(keep2)

#We then delete any rows that don't correspond to a sample in our list, by using the in function to only keep rows with values in our vector.

clin_new <- clinical[clinical$`Sample ID` %in% keep2,]
clin_new <- data.frame(clin_new)

clin_new$'Metastasis_Group' <- with(clin_new, ifelse(clin_new$American.Joint.Committee.on.Cancer.Metastasis.Stage.Code == "M1", 1, 2))

clin_new$'Recurrence_Group' <- with(clin_new, ifelse(clin_new$Disease.Free.Status == "1:Recurred/Progressed", 1, 2))

clin_new

Now, all our data formatting is done - each of our datasets is composed of 542 samples, with 492 tumor samples (as the clinical data suggests) and 50 normal samples. Before we move into our actual analysis, we need to make sure all our samples have data points for the RNAs being analyzed. Since a lot of the files have ‘NA’ values in them, we can’t work with this, so we’ll just eliminate all rows containing an NA.

library(dplyr)
library(readr)
library(edgeR)
#For each dataset, we are going to remove any rows with NA in them, and then convert it to a data frame to work with our DESeq2 functions.
miRNA_new[miRNA_new == "NA"] <- NA
miRNA_final <- miRNA_new[complete.cases(miRNA_new), ]
miRNA_final = data.frame(miRNA_final)

mRNA_new[mRNA_new == "NA"] <- NA
mRNA_final <- mRNA_new[complete.cases(mRNA_new), ]
mRNA_final = data.frame(mRNA_final)

lncRNA_new[lncRNA_new == "NA"] <- NA
lncRNA_final <- lncRNA_new[complete.cases(lncRNA_new), ]
lncRNA_final = data.frame(lncRNA_final)

methylation_new[methylation_new == "NA"] <- NA 
methylation_final <- methylation_new[complete.cases(methylation_new), ]
methylation_final = data.frame(methylation_final)

Differential Gene Expression

We finally have our final datasets that we can work with. Let’s begin with analysis.

We first should create a proper metadata variable to begin analysis, by considering tumor and normal samples. However, there are different levels of analysis we can conduct, which means we’ll create a couple of metadata files based on clinical data.

First, let’s create a file with a simple distribution of group 0 for normal tissue and group 1 for tumor tissue.

library(DESeq2)
library(ggplot2)

#Access sample IDs to form column 1, then check for the .01 tag to assign groups as 1 or 0 for tumor or normal.
metadata <- data.frame("Sample_ID"=c(colnames(lncRNA_final)[2:543]))
metadata$'Group' <- ifelse(grepl(".01", metadata$Sample_ID, fixed = TRUE) == TRUE, 1, 0)
metadata$Group <- as.factor(metadata$Group)

for (i in 1:542) {
  sample <- metadata[i, 1]
  sample2 <- gsub("\\.", "-", sample)
  row1 <- clin_new[which(clin_new$Sample.ID == sample2), ]
  group1 <- row1$Overall.Survival..Months.
  if (grepl(".11", sample, fixed=TRUE) == TRUE) {
    metadata[i, c("days_to_death")] <- 10000
  } else{
    metadata[i, c("days_to_death")] <- as.numeric(group1) * 30
  }
}

Next, let’s create a metadata file that adds in recurrent samples as being group 2, and primary samples as being group 1.

#Access sample IDs to form column 1, then if the tumor is normal, assign group 0. If the tumor is not normal, reference clinical data for disease free status, and assign group 2 to recurred samples.
metadata_recur <- data.frame("Sample_ID"=c(colnames(lncRNA_final)[2:543]))
for (i in 1:542) {
  sample <- metadata_recur[i, 1]
  sample2 <- gsub("\\.", "-", sample)
  row1 <- clin_new[which(clin_new$Sample.ID == sample2), ]
  group1 <- row1$Recurrence_Group
  if (grepl(".11", sample, fixed=TRUE) == TRUE) {
    metadata_recur[i, c("Group")] <- 0
    metadata_recur[i, c("days_to_death")] <- 10000
  } else {
    if (group1 == 1) {
      metadata_recur[i, c("Group")] <- 1
    } else{
      metadata_recur[i, c("Group")] <- 2
    }
    metadata_recur[i, c("days_to_death")] <- as.numeric(row1$Overall.Survival..Months.) * 30
  }
}

metadata_recur$Group <- as.factor(metadata_recur$Group)

We can also create a metadata file to evaluate metastatic vs. non-metastatic samples, which can form group 1 and 2 respectively.

#Access sample IDs to form column 1, then if the tumor is normal, assign group 0. If the tumor is not normal, reference clinical data for metastatic status, and assign group 1 to metastatic samples.
metadata_metas <- data.frame("Sample_ID"=c(colnames(lncRNA_final)[2:543]))

for (i in 1:542) {
  sample <- metadata_metas[i, 1]
  sample2 <- gsub("\\.", "-", sample)
  row1 <- clin_new[which(clin_new$Sample.ID == sample2), ]
  group1 <- row1$Metastasis_Group
  if (grepl(".11", sample, fixed=TRUE) == TRUE) {
    metadata_metas[i, c("Group")] <- 0
    metadata_metas[i, c("days_to_death")] <- 10000
  } else {
    if (group1 == 1) {
      metadata_metas[i, c("Group")] <- 1
    } else{
      metadata_metas[i, c("Group")] <- 2
    }
    metadata_metas[i, c("days_to_death")] <- as.numeric(row1$Overall.Survival..Months.)*30
  }
}

metadata_metas$Group <- as.factor(metadata_metas$Group)

Now, for each of our datasets, we can format them to work with DESeq2, and then run the appropriate analysis/visualizations for them.

lncRNA

#Need to convert the gene names to the actual row names, and remove that column
lnc_DESEQ <- lncRNA_final[,-1]
rownames(lnc_DESEQ) <- lncRNA_final$Gene_ID
Metadata Metastasis
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(lnc_DESEQ), colData=metadata, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 15 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_lnc_metas <- results(dds, tidy=TRUE)
rownames(res_lnc_metas) <- res_lnc_metas[, 1]
res_lnc_metas <- res_lnc_metas %>% 
  arrange(res_lnc_metas$padj)
head(res_lnc_metas)

summary(res_lnc_metas)
     row               baseMean        log2FoldChange       lfcSE            stat        
 Length:12727       Min.   :  0.0000   Min.   :-3.384   Min.   :0.000   Min.   :-14.998  
 Class :character   1st Qu.:  0.0000   1st Qu.:-0.239   1st Qu.:0.270   1st Qu.: -0.520  
 Mode  :character   Median :  0.0000   Median : 0.049   Median :1.316   Median :  0.010  
                    Mean   :  0.3318   Mean   :-0.065   Mean   :2.163   Mean   : -0.397  
                    3rd Qu.:  0.0134   3rd Qu.: 0.084   3rd Qu.:4.674   3rd Qu.:  0.032  
                    Max.   :391.1179   Max.   : 5.322   Max.   :4.674   Max.   : 16.550  
                                       NA's   :7953     NA's   :7953    NA's   :7953     
     pvalue           padj      
 Min.   :0.000   Min.   :0.000  
 1st Qu.:0.106   1st Qu.:0.001  
 Median :0.910   Median :0.080  
 Mean   :0.633   Mean   :0.271  
 3rd Qu.:0.990   3rd Qu.:0.525  
 Max.   :1.000   Max.   :1.000  
 NA's   :7953    NA's   :10655  
#Analyze the distribution of p-values (likely skewed)
hist(res_lnc_metas$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_lnc_metas)[i], intgroup="Group", main=rownames(res_lnc_metas)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_lnc_metas, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_lnc_metas, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_lnc_metas, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Now, let’s use the same methodology to work through the other RNA types we have with us:

miRNA

#Need to convert the gene names to the actual row names, and remove that column
mi_DESEQ <- miRNA_final[,-1]
rownames(mi_DESEQ) <- miRNA_final$Gene_ID
Metadata Normal
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(mi_DESEQ), colData=metadata, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 3 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_miR_main <- results(dds, tidy=TRUE)
rownames(res_miR_main) <- res_miR_main[, 1]
res_miR_main <- res_miR_main %>% 
  arrange(res_miR_main$padj)
head(res_miR_main)

summary(res_miR_main)
     row               baseMean        log2FoldChange          lfcSE        
 Length:139         Min.   :    25.7   Min.   :-2.126281   Min.   :0.05621  
 Class :character   1st Qu.:   196.9   1st Qu.:-0.347177   1st Qu.:0.08113  
 Mode  :character   Median :  1190.2   Median :-0.003324   Median :0.10156  
                    Mean   : 25381.0   Mean   : 0.151709   Mean   :0.11360  
                    3rd Qu.:  6978.9   3rd Qu.: 0.476889   3rd Qu.:0.14036  
                    Max.   :986817.4   Max.   : 5.976988   Max.   :0.27290  
      stat               pvalue               padj          
 Min.   :-12.31630   Min.   :0.0000000   Min.   :0.0000000  
 1st Qu.: -3.46220   1st Qu.:0.0000000   1st Qu.:0.0000000  
 Median : -0.04179   Median :0.0000522   Median :0.0001037  
 Mean   :  1.27820   Mean   :0.1382176   Mean   :0.1532180  
 3rd Qu.:  5.12964   3rd Qu.:0.0981589   3rd Qu.:0.1305359  
 Max.   : 25.71245   Max.   :0.9765655   Max.   :0.9765655  
#Analyze the distribution of p-values (likely skewed)
hist(res_miR_main$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_miR_main)[i], intgroup="Group", main=rownames(res_miR_main)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_miR_main, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_miR_main, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_miR_main, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
Warning: package ‘pheatmap’ was built under R version 4.2.2
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Metadata Recurrence
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(mi_DESEQ), colData=metadata_recur, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 31 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_miR_recur <- results(dds, tidy=TRUE)
rownames(res_miR_recur) <- res_miR_recur[, 1]
res_miR_recur <- res_miR_recur %>% 
  arrange(res_miR_recur$padj)
head(res_miR_recur)

summary(res_miR_recur)
     row               baseMean         log2FoldChange         lfcSE              stat          
 Length:517         Min.   :      0.4   Min.   :-2.41223   Min.   :0.05337   Min.   :-11.53610  
 Class :character   1st Qu.:      2.7   1st Qu.:-0.37298   1st Qu.:0.10972   1st Qu.: -2.52993  
 Mode  :character   Median :     23.1   Median : 0.01108   Median :0.16785   Median :  0.06862  
                    Mean   :  10488.8   Mean   : 0.13365   Mean   :0.18855   Mean   :  0.60866  
                    3rd Qu.:    498.2   3rd Qu.: 0.55130   3rd Qu.:0.24149   3rd Qu.:  3.42082  
                    Max.   :1031530.8   Max.   : 5.63717   Max.   :0.70103   Max.   : 22.40207  
     pvalue              padj          
 Min.   :0.000000   Min.   :0.0000000  
 1st Qu.:0.000001   1st Qu.:0.0000041  
 Median :0.005420   Median :0.0108183  
 Mean   :0.161064   Mean   :0.1858572  
 3rd Qu.:0.210264   3rd Qu.:0.2801715  
 Max.   :0.993476   Max.   :0.9934762  
#Analyze the distribution of p-values (likely skewed)
hist(res_miR_recur$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_miR_recur)[i], intgroup="Group", main=rownames(res_miR_recur)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_miR_recur, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_miR_recur, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_miR_recur, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Metadata Metastasis
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(mi_DESEQ), colData=metadata_metas, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 25 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_miR_metas <- results(dds, tidy=TRUE)
rownames(res_miR_metas) <- res_miR_metas[, 1]
res_miR_metas <- res_miR_metas %>% 
  arrange(res_miR_metas$padj)
head(res_miR_metas)

summary(res_miR_metas)
     row               baseMean         log2FoldChange         lfcSE              stat         
 Length:517         Min.   :      0.4   Min.   :-2.43592   Min.   :0.05318   Min.   :-11.7341  
 Class :character   1st Qu.:      2.7   1st Qu.:-0.37799   1st Qu.:0.10932   1st Qu.: -2.5484  
 Mode  :character   Median :     23.1   Median : 0.01754   Median :0.16730   Median :  0.1431  
                    Mean   :  10488.9   Mean   : 0.14134   Mean   :0.18822   Mean   :  0.6393  
                    3rd Qu.:    498.2   3rd Qu.: 0.55949   3rd Qu.:0.24097   3rd Qu.:  3.5059  
                    Max.   :1031530.8   Max.   : 5.66327   Max.   :0.71200   Max.   : 22.5772  
     pvalue               padj          
 Min.   :0.0000000   Min.   :0.0000000  
 1st Qu.:0.0000004   1st Qu.:0.0000016  
 Median :0.0047254   Median :0.0094085  
 Mean   :0.1587452   Mean   :0.1828219  
 3rd Qu.:0.2185203   3rd Qu.:0.2908206  
 Max.   :0.9995498   Max.   :0.9995498  
#Analyze the distribution of p-values (likely skewed)
hist(res_miR_metas$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_miR_metas)[i], intgroup="Group", main=rownames(res_miR_metas)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_miR_metas, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_miR_metas, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_miR_metas, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

mRNA

#Need to convert the gene names to the actual row names, and remove that column
m_DESEQ <- mRNA_final[,-1]
rownames(m_DESEQ) <- mRNA_final$Gene_ID
Metadata Normal
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(m_DESEQ), colData=metadata, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 1292 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_m_main <- results(dds, tidy=TRUE)
rownames(res_m_main) <- res_m_main[, 1]
res_m_main <- res_m_main %>% 
  arrange(res_m_main$padj)
head(res_m_main)

summary(res_m_main)
     row               baseMean         log2FoldChange        lfcSE             stat         
 Length:20473       Min.   :      0.0   Min.   :-4.7300   Min.   :0.0220   Min.   :-18.4327  
 Class :character   1st Qu.:     24.6   1st Qu.:-0.3090   1st Qu.:0.0593   1st Qu.: -3.1685  
 Mode  :character   Median :    611.7   Median : 0.0325   Median :0.1061   Median :  0.0297  
                    Mean   :   2643.9   Mean   : 0.1126   Mean   :0.3284   Mean   :  0.4885  
                    3rd Qu.:   2355.0   3rd Qu.: 0.3527   3rd Qu.:0.2193   3rd Qu.:  3.6408  
                    Max.   :2153260.9   Max.   : 8.6128   Max.   :4.6740   Max.   : 30.6929  
                                        NA's   :367       NA's   :367      NA's   :367       
     pvalue            padj       
 Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000  
 Median :0.0007   Median :0.0010  
 Mean   :0.1706   Mean   :0.1736  
 3rd Qu.:0.1769   3rd Qu.:0.1977  
 Max.   :0.9999   Max.   :0.9999  
 NA's   :367      NA's   :757     
#Analyze the distribution of p-values (likely skewed)
hist(res_m_main$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_m_main)[i], intgroup="Group", main=rownames(res_m_main)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_m_main, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_m_main, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_m_main, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Metadata Recurrence
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(m_DESEQ), colData=metadata_recur, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 1290 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_m_recur <- results(dds, tidy=TRUE)
rownames(res_m_recur) <- res_m_recur[, 1]
res_m_recur <- res_m_recur %>% 
  arrange(res_m_recur$padj)
head(res_m_recur)

summary(res_m_recur)
     row               baseMean         log2FoldChange        lfcSE             stat              pvalue      
 Length:20531       Min.   :      0.0   Min.   :-4.7194   Min.   :0.0239   Min.   :-16.7630   Min.   :0.0000  
 Class :character   1st Qu.:     24.0   1st Qu.:-0.2854   1st Qu.:0.0641   1st Qu.: -2.8046   1st Qu.:0.0000  
 Mode  :character   Median :    606.9   Median : 0.0488   Median :0.1149   Median :  0.0566   Median :0.0017  
                    Mean   :   2641.9   Mean   : 0.1409   Mean   :0.3587   Mean   :  0.5535   Mean   :0.1811  
                    3rd Qu.:   2347.3   3rd Qu.: 0.3738   3rd Qu.:0.2378   3rd Qu.:  3.4825   3rd Qu.:0.2230  
                    Max.   :2128218.5   Max.   : 8.7262   Max.   :5.0175   Max.   : 27.6647   Max.   :0.9996  
                                        NA's   :373       NA's   :373      NA's   :373        NA's   :373     
      padj       
 Min.   :0.0000  
 1st Qu.:0.0000  
 Median :0.0019  
 Mean   :0.1683  
 3rd Qu.:0.2032  
 Max.   :0.9996  
 NA's   :1154    
#Analyze the distribution of p-values (likely skewed)
hist(res_m_recur$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_m_recur)[i], intgroup="Group", main=rownames(res_m_recur)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_m_recur, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_m_recur, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_m_recur, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Metadata Metastasis
#Create the DESeq data and analyze it
dds <- DESeqDataSetFromMatrix(countData=round(m_DESEQ), colData=metadata_metas, design=~Group)
converting counts to integer mode
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 1136 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
#Plot the dispersion estimates of the differential data
plotDispEsts(dds)


#Access and store the results of DEG
res_m_metas <- results(dds, tidy=TRUE)
rownames(res_m_metas) <- res_m_metas[, 1]
res_m_metas <- res_m_metas %>% 
  arrange(res_m_metas$padj)
head(res_m_metas)

summary(res_m_metas)
     row               baseMean         log2FoldChange        lfcSE             stat              pvalue      
 Length:20531       Min.   :      0.0   Min.   :-4.7832   Min.   :0.0238   Min.   :-17.0028   Min.   :0.0000  
 Class :character   1st Qu.:     24.0   1st Qu.:-0.2860   1st Qu.:0.0639   1st Qu.: -2.8185   1st Qu.:0.0000  
 Mode  :character   Median :    606.5   Median : 0.0509   Median :0.1145   Median :  0.0575   Median :0.0016  
                    Mean   :   2642.3   Mean   : 0.1421   Mean   :0.3576   Mean   :  0.5661   Mean   :0.1799  
                    3rd Qu.:   2347.3   3rd Qu.: 0.3786   3rd Qu.:0.2372   3rd Qu.:  3.5152   3rd Qu.:0.2163  
                    Max.   :2128218.5   Max.   : 8.6091   Max.   :4.9986   Max.   : 27.8391   Max.   :0.9999  
                                        NA's   :373       NA's   :373      NA's   :373        NA's   :373     
      padj       
 Min.   :0.0000  
 1st Qu.:0.0000  
 Median :0.0024  
 Mean   :0.1858  
 3rd Qu.:0.2398  
 Max.   :0.9999  
 NA's   :764     
#Analyze the distribution of p-values (likely skewed)
hist(res_m_metas$padj)


#Analyze distribution of p-values with LFC threshold of 1.5.
res2 <- results(dds, lfcThreshold=1.5, alpha=0.01)
hist(res2$padj)


for (i in 1:10) {
  plotCounts(dds, gene=rownames(res_m_metas)[i], intgroup="Group", main=rownames(res_m_metas)[i])
}


plotMA(dds, lfcThreshold=1.5, alpha=0.01)
#reset par
par(mfrow=c(1,1))

# Make a basic volcano plot
with(res_m_metas, plot(log2FoldChange, -log10(pvalue), pch=20, main="Volcano plot", xlim=c(-3,3)))

# Add colored points: blue if padj<0.01, red if log2FC>1 and padj<0.05)
with(subset(res_m_metas, padj<.01 ), points(log2FoldChange, -log10(pvalue), pch=20, col="blue"))
with(subset(res_m_metas, padj<.01 & abs(log2FoldChange)>2), points(log2FoldChange, -log10(pvalue), pch=20, col="red"))


library(pheatmap)
vsdata <- varianceStabilizingTransformation(dds, blind=TRUE)
plotPCA(vsdata, intgroup="Group")


rld_mat <- assay(vsdata)
rld_cor = cor(rld_mat)
pheatmap(rld_cor, cluster_rows=TRUE, cluster_cols=TRUE, show_colnames=FALSE, show_rownames = FALSE)

Let’s test out the GDCRNATools package.

LS0tDQp0aXRsZTogIlRDR0FfVEhDQSBBbmFseXNpcyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KIyMgKkRhdGEgUmV0cmlldmFsKg0KDQpUaGlzIHByb2plY3QgaXMgZm9jdXNlZCBvbiBhbmFseXppbmcgdGhlIFRDR0EtVEhDQSBkYXRhIGZyb20gYSBtdWx0aW9taWNzIGFwcHJvYWNoLiBTcGVjaWZpY2FsbHksIHdlIHdpbGwgYmUgYW5hbHl6aW5nIG1STkEsIG1pUk5BLCBsbmNSTkEsIGFuZCBtZXRoeWxhdGlvbiBkYXRhIHRvIGRlc2NyaWJlIHRoZSB0cmVuZHMgYW5kIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgbXVsdGlvbWljIHByb2ZpbGVzIG9mIHBhdGllbnRzIGFuZCB0aGVpciBwcm9nbm9zdGljIGNoYXJhY3RlcmlzdGljcyAoZS5nLiBzdXJ2aXZhbCBhbmQvb3IgcmVjdXJyZW5jZSkuDQoNClRvIHN0YXJ0IG9mZiwgd2UgbmVlZCB0byBpbXBvcnQgdGhlIHJlbGV2YW50IGRhdGEgd2Ugd2lsbCBiZSB1c2luZy4gVGhpcyBkYXRhIGlzIHNvdXJjZWQgZnJvbSBUQ0dBIChUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyksIGFsdGhvdWdoIHRoZSBtUk5BLCBtaVJOQSwgbWV0aHlsYXRpb24sIGFuZCBjbGluaWNhbCBkYXRhIGNhbWUgZnJvbSBGaXJlaG9zZSAoZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlKSwgYW5kIHRoZSBsbmNSTkEgZGF0YSBjYW1lIGZyb20gVEFOUklDLg0KDQpMZXQncyBmaXJzdCBpbXBvcnQgdGhlIGRhdGFzZXRzIHdlIHdpbGwgYmUgd29ya2luZyB3aXRoLCBhbmQgYXNzaWduIHRoZW0gdG8gdmFyaWFibGVzLg0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCg0KI0lucHV0dGluZyB0aGUgZGF0YSBpbnRvIHZhcmlhYmxlcyB0aGF0IHdlIGNhbiBtYW5pcHVsYXRlDQpjbGluaWNhbCA8LSByZWFkX2V4Y2VsKCJUSENBX0NsaW5pY2FsX0RhdGEueGxzeCIpDQpsbmNSTkEgPC0gcmVhZF9leGNlbCgiVEhDQV9sbmNSTkFfRGF0YS54bHN4IikNCiNtZXRoeWxhdGlvbiA8LSByZWFkX2V4Y2VsKCJUSENBX01ldGh5bGF0aW9uX0RhdGEueGxzeCIpDQptaVJOQSA8LSByZWFkX2V4Y2VsKCJUSENBX21pUk5BX0RhdGFfRmluYWwueGxzeCIpDQptUk5BIDwtIHJlYWRfZXhjZWwoIlRIQ0FfbVJOQV9EYXRhX0ZpbmFsLnhsc3giKQ0KYGBgDQoNCkxldCdzIGxvb2sgYXQgdGhlIGhlYWQgb2YgdGhlc2UgdGFibGVzIHRvIHVuZGVyc3RhbmQgdGhlaXIgc3RydWN0dXJlLg0KDQpgYGB7cn0NCiNBY2Nlc3NpbmcgdGhlIGZpcnN0IGZldyByb3dzIG9mIGVhY2ggZGF0YXNldA0KaGVhZChjbGluaWNhbCkNCmhlYWQobG5jUk5BKQ0KI2hlYWQobWV0aHlsYXRpb24pDQpoZWFkKG1pUk5BKQ0KaGVhZChtUk5BKQ0KYGBgDQojIyAqRGF0YSBQcmVwcm9jZXNzaW5nKg0KDQpBZnRlciBsb29raW5nIGF0IHRoZSB0YWJsZSBoZWFkcywgdGhlcmUgYXJlIGEgY291cGxlIG9mIGlzc3VlcyB0aGF0IHdlIGNhbiBzZWU6DQoNCiAgICAxKSBUaGUgc2FtcGxlIGxhYmVscyBhcmVuJ3QgaWRlbnRpY2FsIC0gc29tZSBoYXZlIGV4dHJhIGVsZW1lbnRzIGF0IHRoZSBiZWdpbm5pbmcgb3IgZW5kLCB3aGljaCAgICAgICAgIG5lZWQgdG8gYmUgc3RhbmRhcmRpemVkDQogICAgDQogICAgMikgVGhlIG51bWJlciBvZiBzYW1wbGVzIGluIGVhY2ggZmlsZSBhcmVuJ3QgdGhlIHNhbWUgLSBmb3IgZXhhbXBsZSwgbWlSTkEgb25seSBoYXMgNTQxIHNhbXBsZXMsICAgICAgICBhbmQgbG5jUk5BIGhhcyA1NTYuIFRoaXMgYWxzbyBuZWVkcyB0byBiZSBzdGFuZGFyZGl6ZWQgdG8gbWFrZSBzdXJlIG9ubHkgc2FtcGxlcyB3aXRoIGFsbCAgICAgICAgICAgZGF0YSBhcmUgdXNlZC4NCg0KTGV0J3Mgc3RhcnQgd2l0aCBzdGVwIDEuDQoNClRoZSBnZW5lcmFsIGZvcm1hdCBvZiB0aGUgZGF0YSBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOiAnVENHQS1YWC1YWFhYLVhYJywgc28gbGV0J3MgbG9vayBhdCBlYWNoIGZpbGUgdG8gc2VlIGhvdyB3ZSBjYW4gZml4IGl0Lg0KDQpsbmNSTkEgc2VlbXMgdG8gaGF2ZSBleHRyYSBsYWJlbHMgYXQgdGhlIHN0YXJ0IHdoaWNoIHdlIGNhbiByZW1vdmUsIGFzIHdlbGwgYXMgdW5kZXJzY29yZXMgd2hpY2ggd2UgY2FuIGNvbnZlcnQgdG8gaHlwaGVucy4gSG93ZXZlciwgd2hhdCdzIGFsc28gaW1wb3J0YW50IGhlcmUgaXMgdGhhdCB3ZSBoYXZlIG5vcm1hbCBhbmQgdHVtb3Igc2FtcGxlcyBleHBsaWN0bHkgbGFiZWxlZCwgd2hlcmVhcyBvdGhlciBmaWxlcyB1c2UgdGhlICcwMScgbWFya2luZyBmb3IgdHVtb3JzIGFuZCAnMTEnIG1hcmtpbmcgZm9yIG5vcm1hbCBzYW1wbGVzLiBXZSBuZWVkIHRvIGFkYXB0IG91ciBsbmNSTkEgZGF0YSB0byBmb2xsb3cgdGhpcyBzdHJ1Y3R1cmUuDQpgYGB7cn0NCiNXZSBhcmUgZ29pbmcgdGhyb3VnaCBlYWNoIGNvbHVtbiBpbiB0aGUgbG5jUk5BIGRhdGFzZXQNCmZvciAoaSBpbiAxOm5jb2wobG5jUk5BKSkgew0KICAjSWYgdGhlIGNvbHVtbiBuYW1lIC0gd2hpY2ggaXMgdGhlIHNhbXBsZSBJRCAtIGNvbnRhaW5zIE5vcm1hbCwgd2UnbGwgYWRkIGEgLTExIGFuZCBmb3JtYXQgdGhlIG5hbWUgdG8gcmVtb3ZlIHRoZSBiZWdpbm5pbmcgYW5kIGNvbnZlcnQgYWxsIHVuZGVyc2NvcmVzIHRvIGh5cGhlbnMuDQogIA0KICBpZiAoZ3JlcGwoIk5vcm1hbCIsIGNvbG5hbWVzKGxuY1JOQSlbaV0sIGZpeGVkID0gVFJVRSkgPT0gVFJVRSkgew0KICAgIGNvbG5hbWVzKGxuY1JOQSlbaV0gPC0gc3ViKCIuKi1UQyIsICJUQyIsIGNvbG5hbWVzKGxuY1JOQSlbaV0pDQogICAgY29sbmFtZXMobG5jUk5BKVtpXSA8LSBnc3ViKCJfIiwgIi0iLCBjb2xuYW1lcyhsbmNSTkEpW2ldKQ0KICAgIGNvbG5hbWVzKGxuY1JOQSlbaV0gPC0gcGFzdGUoY29sbmFtZXMobG5jUk5BKVtpXSwgJy0xMScsIHNlcD0iIikNCiAgfSBlbHNlIHsNCiAgICAjT3RoZXJ3aXNlLCB3ZSdsbCBzdGlsbCBmb3JtYXQgdGhlIG5hbWUsIGJ1dCB3ZSdsbCBhZGQgYSAtMDEgYXQgdGhlIGVuZCBpbnN0ZWFkLg0KICAgIGNvbG5hbWVzKGxuY1JOQSlbaV0gPC0gc3ViKCIuKi1UQyIsICJUQyIsIGNvbG5hbWVzKGxuY1JOQSlbaV0pDQogICAgY29sbmFtZXMobG5jUk5BKVtpXSA8LSBnc3ViKCJfIiwgIi0iLCBjb2xuYW1lcyhsbmNSTkEpW2ldKQ0KICAgIGNvbG5hbWVzKGxuY1JOQSlbaV0gPC0gcGFzdGUoY29sbmFtZXMobG5jUk5BKVtpXSwgJy0wMScsIHNlcD0iIikNCiAgfQ0KICAgIA0KfQ0KDQojV2UnbGwgY29udmVydCB0aGUgZmlyc3QgY29sdW1uIHRvIHRoZSBzdGFuZGFyZCBuYW1lIEdlbmVfSUQgYW5kIHJlbW92ZSB0aGUgZXh0cmEgbWFya2VyIGluIHRoZSBFbnNlbWJsZSBJRCBhZnRlciB0aGUgZGVjaW1hbCBwb2ludC4NCmNvbG5hbWVzKGxuY1JOQSlbMV0gPC0gIkdlbmVfSUQiDQpsbmNSTkEkR2VuZV9JRCA8LSBzdWIoIlxcLi4qIiwgIiIsIGxuY1JOQSRHZW5lX0lEKQ0KaGVhZChsbmNSTkEpDQpgYGANCg0KVGhhdCBsb29rcyBtb3JlIGluIGxpbmUgd2l0aCB0aGUgc3RhbmRhcmQgc2FtcGxlIG5vdGF0aW9uLg0KDQpCb3RoIHRoZSBtZXRoeWxhdGlvbiBhbmQgdGhlIG1STkEgZmlsZSBoYXZlIHVuZGVyc2NvcmVzIHRoYXQgd2UgY2FuIGNvbnZlcnQgdG8gaHlwaGVucy4NCg0KYGBge3J9DQojV2UnbGwgaXRlcmF0ZSBvdmVyIGVhY2ggY29sdW1uIGFuZCBjb252ZXJ0IHRoZSB1bmRlcnNjb3JlcyB0byBkYXNoZXMsIGZvciBib3RoIGRhdGFzZXRzLiBUaGVuLCB3ZSdsbCBjb252ZXJ0IHRoZSBmaXJzdCBjb2x1bW4gdG8gdGhlIEdlbmVfSUQgbGFiZWwuDQpmb3IgKGkgaW4gMTpuY29sKG1STkEpKSB7DQogIGNvbG5hbWVzKG1STkEpW2ldIDwtIGdzdWIoIl8iLCAiLSIsIGNvbG5hbWVzKG1STkEpW2ldKQ0KfQ0KY29sbmFtZXMobVJOQSlbMV0gPC0gIkdlbmVfSUQiDQpoZWFkKG1STkEpDQoNCiNmb3IgKGkgaW4gMTpuY29sKG1ldGh5bGF0aW9uKSkgew0KIyAgY29sbmFtZXMobWV0aHlsYXRpb24pW2ldIDwtIGdzdWIoIl8iLCAiLSIsIGNvbG5hbWVzKG1ldGh5bGF0aW9uKVtpXSkNCiN9DQojY29sbmFtZXMobWV0aHlsYXRpb24pWzFdIDwtICJHZW5lX0lEIg0KI2hlYWQobWV0aHlsYXRpb24pDQpgYGANClRoYXQgc2VlbXMgdG8gYmUgZml4ZWQgYXMgd2VsbC4NCg0KRm9yIG1pUk5BIGRhdGEsIHRoZXJlIGlzIGEgbG90IG9mIGV4dHJhbmVvdXMgZGF0YSBhdCB0aGUgZW5kIGFib3V0IHNhbXBsZSBhbmQgdmlhbHMsIHdoaWNoIHdlIGRvbid0IG5lZWQsIHNvIGxldCdzIHJlbW92ZSB0aGF0Lg0KDQpgYGB7cn0NCiNXZSdsbCBpdGVyYXRlIG92ZXIgdGhlIGNvbHVtbiBuYW1lcyBhbmQgcmVtb3ZlIGFueSBleHRyYW5lb3VzIGluZm9ybWF0aW9uIGFmdGVyIG91ciBub3JtYWwgb3IgdHVtb3Igc2FtcGxlIGxhYmVscywgYW5kIHRoZW4gY3JlYXRlIHRoZSBHZW5lX0lEIGNvbHVtbi4NCg0KZm9yIChpIGluIDE6bmNvbChtaVJOQSkpIHsNCiAgY29sbmFtZXMobWlSTkEpW2ldIDwtIHN1YigiXzAxLioiLCAiXzAxIiwgY29sbmFtZXMobWlSTkEpW2ldKQ0KICBjb2xuYW1lcyhtaVJOQSlbaV0gPC0gc3ViKCJfMDEuKiIsICJfMDEiLCBjb2xuYW1lcyhtaVJOQSlbaV0pDQogIGNvbG5hbWVzKG1pUk5BKVtpXSA8LSBnc3ViKCJfIiwgIi0iLCBjb2xuYW1lcyhtaVJOQSlbaV0pDQp9DQpjb2xuYW1lcyhtaVJOQSlbMV0gPC0gIkdlbmVfSUQiDQpoZWFkKG1pUk5BKQ0KYGBgDQpEb25lIQ0KDQpGb3IgY2xpbmljYWwgZGF0YSwgd2UgaGF2ZSBvdXIgJ1NhbXBsZSBJRCcgY29sdW1uIHdpdGggYXBwcm9wcmlhdGUgbm90YXRpb24sIHNvIHdlIGRvbid0IG5lZWQgdG8gY2hhbmdlIHRoYXQuDQoNCk5vdywgZm9yIHN0ZXAgMiwgd2UgaGF2ZSB0byBpZGVudGlmeSB3aGljaCBzYW1wbGVzIGFyZSBtaXNzaW5nIGZyb20gYW55IG9mIHRoZSBmaWxlcywgYXMgd2Ugb25seSB3YW50IGRhdGEgcG9pbnRzIHByZXNlbnQgaW4gZWFjaCBmaWxlLiBUbyBhY2NvbXBsaXNoIHRoaXMsIEkgYW0gZmlyc3QgZ29pbmcgdG8gYWNjZXNzIGFsbCB0aGUgc2FtcGxlIElEcyBmcm9tIGVhY2ggZmlsZS4gSSdtIHRoZW4gZ29pbmcgdG8gYWRkIHRoZW0gdG9nZXRoZXIgdG8gZm9ybSBhIHZlY3RvciwgYW5kIHVzZSB0aGUgdGFibGUgZnVuY3Rpb24gdG8gc2VlIGhvdyBtYW55IHRpbWVzIGVhY2ggdmFsdWUgYXBwZWFycyBpbiB0aGUgdmVjdG9yLiBHaXZlbiB0aGF0IHdlIGhhdmUgNSBmaWxlcyBvZiBkYXRhLCBhIHNhbXBsZSBwcmVzZW50IGluIGFsbCBmaWxlcyBzaG91bGQgYXBwZWFyIGF0IGxlYXN0IDUgdGltZXMuIFRodXMsIHdlIHdpbGwgaXNvbGF0ZSBhbnkgdmFsdWVzIHRoYXQgYXBwZWFyIGxlc3MgdGhhbiA1IHRpbWVzLg0KDQpgYGB7cn0NCiNIZXJlIHdlIGFjY2VzcyB0aGUgc3BlY2lmaWMgc2FtcGxlIG5hbWVzIHRocm91Z2ggaW5kZXhpbmcgb2YgdGhlIGRhdGEgKHdoaWNoIHN0YXJ0cyBmcm9tIGNvbHVtbiAyKS4NCmNsaW5fc2FtcGxlcyA8LSBjbGluaWNhbCRgU2FtcGxlIElEYA0KbG5jX3NhbXBsZXMgPC0gY29sbmFtZXMobG5jUk5BKVsyOjU1N10NCm1ldGhfc2FtcGxlcyA8LSBjb2xuYW1lcyhtZXRoeWxhdGlvbilbNTo1NzFdDQptaVJfc2FtcGxlcyA8LSBjb2xuYW1lcyhtaVJOQSlbMjo1NzBdDQptUk5BX3NhbXBsZXMgPC0gY29sbmFtZXMobVJOQSlbMjo1NjldDQoNCiNXZSBjb25jYXRlbmF0ZSBhbGwgdGhlIHNhbXBsZSBkYXRhIGludG8gYSB2ZWN0b3IuDQp0ZW1wIDwtIGMobG5jX3NhbXBsZXMsIG1ldGhfc2FtcGxlcywgbWlSX3NhbXBsZXMsIG1STkFfc2FtcGxlcykNCiNOZXh0LCB3ZSBjb3VudCB0aGUgb2NjdXJyZW5jZSBvZiBlYWNoIHVuaXF1ZSBzYW1wbGUgSUQgaW4gb3VyIHZlY3RvciB1c2luZyB0aGUgdGFibGUgZnVuY3Rpb24gLSBpZiBhIHNhbXBsZSBpcyBwcmVzZW50IGluIGFsbCA0IGRhdGFzZXRzLCBpdCdsbCBoYXZlIGEgY291bnQgb2YgNC4NCnRlbXBfdGFibGUgPSB0YWJsZSh0ZW1wKQ0KI05vdywgd2UganVzdCB3YW50IHRvIGtlZXAgdGhlIHNhbXBsZXMgdGhhdCBoYXZlIGFuIG9jY3VycmVuY2Ugb2YgNC4NCmtlZXAxIDwtIG5hbWVzKHRlbXBfdGFibGVbdGVtcF90YWJsZSA+PSA0XSkNCmtlZXAxIDwtIGMoIkdlbmVfSUQiLCBrZWVwMSkNCg0KYGBgDQoNCk5vdywgd2Ugd2lsbCByZW1vdmUgdGhlaXIgY29sdW1ucy9yb3dzIGZyb20gZWFjaCBkYXRhIGZpbGUgaW4gYSBzZXF1ZW50aWFsIG1hbm5lci4NCg0KYGBge3J9DQojTmV4dCwgd2UganVzdCBzdWJzZXQgdGhlIGRhdGFzZXRzIHRvIG9ubHkga2VlcCBjb2x1bW5zIHRoYXQgbWF0Y2ggYSB2YWx1ZSBpbiBvdXIgdmVjdG9yIG9mIGFjY2VwdGVkIHNhbXBsZXMgLSB1c2luZyB0aGUgc3Vic2V0IGFuZCBzZWxlY3QgZnVuY3Rpb25zLg0KbWlSTkFfbmV3IDwtIHN1YnNldChtaVJOQSwgc2VsZWN0PWMoa2VlcDEpKQ0KbVJOQV9uZXcgPC0gc3Vic2V0KG1STkEsIHNlbGVjdD1jKGtlZXAxKSkNCm1ldGh5bGF0aW9uX25ldyA8LSBzdWJzZXQobWV0aHlsYXRpb24sIHNlbGVjdD1jKGtlZXAxKSkNCmxuY1JOQV9uZXcgPC0gc3Vic2V0KGxuY1JOQSwgc2VsZWN0PWMoa2VlcDEpKQ0KYGBgDQoNCk9uZSBhZGRpdGlvbmFsIHN0ZXAgd2UgbmVlZCB0byB0YWtlIGlzIHRvIG1ha2Ugc3VyZSB0aGF0IGFsbCBvdXIgdHVtb3Igc2FtcGxlcyBoYXZlIGNsaW5pY2FsIGRhdGEgYXZhaWxhYmxlLiBUaHVzLCBJJ20gZ29pbmcgdG8gY29tcGFyZSBvdXIgdHVtb3Igc2FtcGxlcyAod2l0aCB0aGUgLTAxIGVuZGluZykgdG8gb3VyIGNsaW5pY2FsIHNhbXBsZSBJRHMgdG8gZmluZCBhbmQgYWxzbyByZW1vdmUgYW55IHNhbXBsZXMgdGhhdCBkb24ndCBvdmVybGFwLCB1c2luZyBhIHNpbWlsYXIgbWV0aG9kLg0KDQpgYGB7cn0NCiNOb3csIHNpbmNlIGNsaW5pY2FsIGRhdGEgb25seSBjb250YWlucyB0dW1vciBzYW1wbGVzLCB3ZSBuZWVkIHRvIHNlcGFyYXRlIG91dCBvdXIgdHVtb3Igc2FtcGxlcyBieSBzZWFyY2hpbmcgZm9yIGFsbCBzYW1wbGVzIHdpdGggdGhlIC0wMSB0YWcuDQp0dW1vcl9zYW1wbGVzIDwtIGdyZXAoIi0wMSIsIGtlZXAxLCB2YWx1ZT1UUlVFKQ0Kbm9ybWFsX3NhbXBsZXMgPC0gZ3JlcCgiLTExIiwga2VlcDEsIHZhbHVlPVRSVUUpDQoNCiNTYW1lIGFzIGFib3ZlIC0gdmVjdG9yLCBjb3VudCwga2VlcCBhbnl0aGluZyBvY2N1cnJpbmcgdHdpY2Ugc2luY2Ugd2UgYXJlIGNvbXBhcmluZyAyIGRhdGFzZXRzLg0KdGVtcDIgPC0gYyh0dW1vcl9zYW1wbGVzLCBjbGluX3NhbXBsZXMpDQp0ZW1wX3RhYmxlXzIgPSB0YWJsZSh0ZW1wMikNCmtlZXAyIDwtIG5hbWVzKHRlbXBfdGFibGVfMlt0ZW1wX3RhYmxlXzIgPj0gMl0pDQprZWVwMiA8LSBjKGtlZXAyKQ0KDQojV2UgdGhlbiBkZWxldGUgYW55IHJvd3MgdGhhdCBkb24ndCBjb3JyZXNwb25kIHRvIGEgc2FtcGxlIGluIG91ciBsaXN0LCBieSB1c2luZyB0aGUgaW4gZnVuY3Rpb24gdG8gb25seSBrZWVwIHJvd3Mgd2l0aCB2YWx1ZXMgaW4gb3VyIHZlY3Rvci4NCg0KY2xpbl9uZXcgPC0gY2xpbmljYWxbY2xpbmljYWwkYFNhbXBsZSBJRGAgJWluJSBrZWVwMixdDQpjbGluX25ldyA8LSBkYXRhLmZyYW1lKGNsaW5fbmV3KQ0KDQpjbGluX25ldyQnTWV0YXN0YXNpc19Hcm91cCcgPC0gd2l0aChjbGluX25ldywgaWZlbHNlKGNsaW5fbmV3JEFtZXJpY2FuLkpvaW50LkNvbW1pdHRlZS5vbi5DYW5jZXIuTWV0YXN0YXNpcy5TdGFnZS5Db2RlID09ICJNMSIsIDEsIDIpKQ0KDQpjbGluX25ldyQnUmVjdXJyZW5jZV9Hcm91cCcgPC0gd2l0aChjbGluX25ldywgaWZlbHNlKGNsaW5fbmV3JERpc2Vhc2UuRnJlZS5TdGF0dXMgPT0gIjE6UmVjdXJyZWQvUHJvZ3Jlc3NlZCIsIDEsIDIpKQ0KDQpjbGluX25ldw0KYGBgDQoNCk5vdywgYWxsIG91ciBkYXRhIGZvcm1hdHRpbmcgaXMgZG9uZSAtIGVhY2ggb2Ygb3VyIGRhdGFzZXRzIGlzIGNvbXBvc2VkIG9mIDU0MiBzYW1wbGVzLCB3aXRoIDQ5MiB0dW1vciBzYW1wbGVzIChhcyB0aGUgY2xpbmljYWwgZGF0YSBzdWdnZXN0cykgYW5kIDUwIG5vcm1hbCBzYW1wbGVzLiBCZWZvcmUgd2UgbW92ZSBpbnRvIG91ciBhY3R1YWwgYW5hbHlzaXMsIHdlIG5lZWQgdG8gbWFrZSBzdXJlIGFsbCBvdXIgc2FtcGxlcyBoYXZlIGRhdGEgcG9pbnRzIGZvciB0aGUgUk5BcyBiZWluZyBhbmFseXplZC4gU2luY2UgYSBsb3Qgb2YgdGhlIGZpbGVzIGhhdmUgJ05BJyB2YWx1ZXMgaW4gdGhlbSwgd2UgY2FuJ3Qgd29yayB3aXRoIHRoaXMsIHNvIHdlJ2xsIGp1c3QgZWxpbWluYXRlIGFsbCByb3dzIGNvbnRhaW5pbmcgYW4gTkEuDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGVkZ2VSKQ0KI0ZvciBlYWNoIGRhdGFzZXQsIHdlIGFyZSBnb2luZyB0byByZW1vdmUgYW55IHJvd3Mgd2l0aCBOQSBpbiB0aGVtLCBhbmQgdGhlbiBjb252ZXJ0IGl0IHRvIGEgZGF0YSBmcmFtZSB0byB3b3JrIHdpdGggb3VyIERFU2VxMiBmdW5jdGlvbnMuDQptaVJOQV9uZXdbbWlSTkFfbmV3ID09ICJOQSJdIDwtIE5BDQptaVJOQV9maW5hbCA8LSBtaVJOQV9uZXdbY29tcGxldGUuY2FzZXMobWlSTkFfbmV3KSwgXQ0KbWlSTkFfZmluYWwgPSBkYXRhLmZyYW1lKG1pUk5BX2ZpbmFsKQ0KDQptUk5BX25ld1ttUk5BX25ldyA9PSAiTkEiXSA8LSBOQQ0KbVJOQV9maW5hbCA8LSBtUk5BX25ld1tjb21wbGV0ZS5jYXNlcyhtUk5BX25ldyksIF0NCm1STkFfZmluYWwgPSBkYXRhLmZyYW1lKG1STkFfZmluYWwpDQoNCmxuY1JOQV9uZXdbbG5jUk5BX25ldyA9PSAiTkEiXSA8LSBOQQ0KbG5jUk5BX2ZpbmFsIDwtIGxuY1JOQV9uZXdbY29tcGxldGUuY2FzZXMobG5jUk5BX25ldyksIF0NCmxuY1JOQV9maW5hbCA9IGRhdGEuZnJhbWUobG5jUk5BX2ZpbmFsKQ0KDQptZXRoeWxhdGlvbl9uZXdbbWV0aHlsYXRpb25fbmV3ID09ICJOQSJdIDwtIE5BIA0KbWV0aHlsYXRpb25fZmluYWwgPC0gbWV0aHlsYXRpb25fbmV3W2NvbXBsZXRlLmNhc2VzKG1ldGh5bGF0aW9uX25ldyksIF0NCm1ldGh5bGF0aW9uX2ZpbmFsID0gZGF0YS5mcmFtZShtZXRoeWxhdGlvbl9maW5hbCkNCg0KYGBgDQoNCiMjICpEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uKg0KDQoNCldlIGZpbmFsbHkgaGF2ZSBvdXIgZmluYWwgZGF0YXNldHMgdGhhdCB3ZSBjYW4gd29yayB3aXRoLiBMZXQncyBiZWdpbiB3aXRoIGFuYWx5c2lzLiANCg0KV2UgZmlyc3Qgc2hvdWxkIGNyZWF0ZSBhIHByb3BlciBtZXRhZGF0YSB2YXJpYWJsZSB0byBiZWdpbiBhbmFseXNpcywgYnkgY29uc2lkZXJpbmcgdHVtb3IgYW5kIG5vcm1hbCBzYW1wbGVzLiBIb3dldmVyLCB0aGVyZSBhcmUgZGlmZmVyZW50IGxldmVscyBvZiBhbmFseXNpcyB3ZSBjYW4gY29uZHVjdCwgd2hpY2ggbWVhbnMgd2UnbGwgY3JlYXRlIGEgY291cGxlIG9mIG1ldGFkYXRhIGZpbGVzIGJhc2VkIG9uIGNsaW5pY2FsIGRhdGEuDQoNCkZpcnN0LCBsZXQncyBjcmVhdGUgYSBmaWxlIHdpdGggYSBzaW1wbGUgZGlzdHJpYnV0aW9uIG9mIGdyb3VwIDAgZm9yIG5vcm1hbCB0aXNzdWUgYW5kIGdyb3VwIDEgZm9yIHR1bW9yIHRpc3N1ZS4NCg0KYGBge3J9DQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoZ2dwbG90MikNCg0KI0FjY2VzcyBzYW1wbGUgSURzIHRvIGZvcm0gY29sdW1uIDEsIHRoZW4gY2hlY2sgZm9yIHRoZSAuMDEgdGFnIHRvIGFzc2lnbiBncm91cHMgYXMgMSBvciAwIGZvciB0dW1vciBvciBub3JtYWwuDQptZXRhZGF0YSA8LSBkYXRhLmZyYW1lKCJTYW1wbGVfSUQiPWMoY29sbmFtZXMobG5jUk5BX2ZpbmFsKVsyOjU0M10pKQ0KbWV0YWRhdGEkJ0dyb3VwJyA8LSBpZmVsc2UoZ3JlcGwoIi4wMSIsIG1ldGFkYXRhJFNhbXBsZV9JRCwgZml4ZWQgPSBUUlVFKSA9PSBUUlVFLCAxLCAwKQ0KbWV0YWRhdGEkR3JvdXAgPC0gYXMuZmFjdG9yKG1ldGFkYXRhJEdyb3VwKQ0KDQpmb3IgKGkgaW4gMTo1NDIpIHsNCiAgc2FtcGxlIDwtIG1ldGFkYXRhW2ksIDFdDQogIHNhbXBsZTIgPC0gZ3N1YigiXFwuIiwgIi0iLCBzYW1wbGUpDQogIHJvdzEgPC0gY2xpbl9uZXdbd2hpY2goY2xpbl9uZXckU2FtcGxlLklEID09IHNhbXBsZTIpLCBdDQogIGdyb3VwMSA8LSByb3cxJE92ZXJhbGwuU3Vydml2YWwuLk1vbnRocy4NCiAgaWYgKGdyZXBsKCIuMTEiLCBzYW1wbGUsIGZpeGVkPVRSVUUpID09IFRSVUUpIHsNCiAgICBtZXRhZGF0YVtpLCBjKCJkYXlzX3RvX2RlYXRoIildIDwtIDEwMDAwDQogIH0gZWxzZXsNCiAgICBtZXRhZGF0YVtpLCBjKCJkYXlzX3RvX2RlYXRoIildIDwtIGFzLm51bWVyaWMoZ3JvdXAxKSAqIDMwDQogIH0NCn0NCg0KYGBgDQoNCk5leHQsIGxldCdzIGNyZWF0ZSBhIG1ldGFkYXRhIGZpbGUgdGhhdCBhZGRzIGluIHJlY3VycmVudCBzYW1wbGVzIGFzIGJlaW5nIGdyb3VwIDIsIGFuZCBwcmltYXJ5IHNhbXBsZXMgYXMgYmVpbmcgZ3JvdXAgMS4NCg0KYGBge3J9DQojQWNjZXNzIHNhbXBsZSBJRHMgdG8gZm9ybSBjb2x1bW4gMSwgdGhlbiBpZiB0aGUgdHVtb3IgaXMgbm9ybWFsLCBhc3NpZ24gZ3JvdXAgMC4gSWYgdGhlIHR1bW9yIGlzIG5vdCBub3JtYWwsIHJlZmVyZW5jZSBjbGluaWNhbCBkYXRhIGZvciBkaXNlYXNlIGZyZWUgc3RhdHVzLCBhbmQgYXNzaWduIGdyb3VwIDIgdG8gcmVjdXJyZWQgc2FtcGxlcy4NCm1ldGFkYXRhX3JlY3VyIDwtIGRhdGEuZnJhbWUoIlNhbXBsZV9JRCI9Yyhjb2xuYW1lcyhsbmNSTkFfZmluYWwpWzI6NTQzXSkpDQpmb3IgKGkgaW4gMTo1NDIpIHsNCiAgc2FtcGxlIDwtIG1ldGFkYXRhX3JlY3VyW2ksIDFdDQogIHNhbXBsZTIgPC0gZ3N1YigiXFwuIiwgIi0iLCBzYW1wbGUpDQogIHJvdzEgPC0gY2xpbl9uZXdbd2hpY2goY2xpbl9uZXckU2FtcGxlLklEID09IHNhbXBsZTIpLCBdDQogIGdyb3VwMSA8LSByb3cxJFJlY3VycmVuY2VfR3JvdXANCiAgaWYgKGdyZXBsKCIuMTEiLCBzYW1wbGUsIGZpeGVkPVRSVUUpID09IFRSVUUpIHsNCiAgICBtZXRhZGF0YV9yZWN1cltpLCBjKCJHcm91cCIpXSA8LSAwDQogICAgbWV0YWRhdGFfcmVjdXJbaSwgYygiZGF5c190b19kZWF0aCIpXSA8LSAxMDAwMA0KICB9IGVsc2Ugew0KICAgIGlmIChncm91cDEgPT0gMSkgew0KICAgICAgbWV0YWRhdGFfcmVjdXJbaSwgYygiR3JvdXAiKV0gPC0gMQ0KICAgIH0gZWxzZXsNCiAgICAgIG1ldGFkYXRhX3JlY3VyW2ksIGMoIkdyb3VwIildIDwtIDINCiAgICB9DQogICAgbWV0YWRhdGFfcmVjdXJbaSwgYygiZGF5c190b19kZWF0aCIpXSA8LSBhcy5udW1lcmljKHJvdzEkT3ZlcmFsbC5TdXJ2aXZhbC4uTW9udGhzLikgKiAzMA0KICB9DQp9DQoNCm1ldGFkYXRhX3JlY3VyJEdyb3VwIDwtIGFzLmZhY3RvcihtZXRhZGF0YV9yZWN1ciRHcm91cCkNCmBgYA0KDQpXZSBjYW4gYWxzbyBjcmVhdGUgYSBtZXRhZGF0YSBmaWxlIHRvIGV2YWx1YXRlIG1ldGFzdGF0aWMgdnMuIG5vbi1tZXRhc3RhdGljIHNhbXBsZXMsIHdoaWNoIGNhbiBmb3JtIGdyb3VwIDEgYW5kIDIgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7cn0NCiNBY2Nlc3Mgc2FtcGxlIElEcyB0byBmb3JtIGNvbHVtbiAxLCB0aGVuIGlmIHRoZSB0dW1vciBpcyBub3JtYWwsIGFzc2lnbiBncm91cCAwLiBJZiB0aGUgdHVtb3IgaXMgbm90IG5vcm1hbCwgcmVmZXJlbmNlIGNsaW5pY2FsIGRhdGEgZm9yIG1ldGFzdGF0aWMgc3RhdHVzLCBhbmQgYXNzaWduIGdyb3VwIDEgdG8gbWV0YXN0YXRpYyBzYW1wbGVzLg0KbWV0YWRhdGFfbWV0YXMgPC0gZGF0YS5mcmFtZSgiU2FtcGxlX0lEIj1jKGNvbG5hbWVzKGxuY1JOQV9maW5hbClbMjo1NDNdKSkNCg0KZm9yIChpIGluIDE6NTQyKSB7DQogIHNhbXBsZSA8LSBtZXRhZGF0YV9tZXRhc1tpLCAxXQ0KICBzYW1wbGUyIDwtIGdzdWIoIlxcLiIsICItIiwgc2FtcGxlKQ0KICByb3cxIDwtIGNsaW5fbmV3W3doaWNoKGNsaW5fbmV3JFNhbXBsZS5JRCA9PSBzYW1wbGUyKSwgXQ0KICBncm91cDEgPC0gcm93MSRNZXRhc3Rhc2lzX0dyb3VwDQogIGlmIChncmVwbCgiLjExIiwgc2FtcGxlLCBmaXhlZD1UUlVFKSA9PSBUUlVFKSB7DQogICAgbWV0YWRhdGFfbWV0YXNbaSwgYygiR3JvdXAiKV0gPC0gMA0KICAgIG1ldGFkYXRhX21ldGFzW2ksIGMoImRheXNfdG9fZGVhdGgiKV0gPC0gMTAwMDANCiAgfSBlbHNlIHsNCiAgICBpZiAoZ3JvdXAxID09IDEpIHsNCiAgICAgIG1ldGFkYXRhX21ldGFzW2ksIGMoIkdyb3VwIildIDwtIDENCiAgICB9IGVsc2V7DQogICAgICBtZXRhZGF0YV9tZXRhc1tpLCBjKCJHcm91cCIpXSA8LSAyDQogICAgfQ0KICAgIG1ldGFkYXRhX21ldGFzW2ksIGMoImRheXNfdG9fZGVhdGgiKV0gPC0gYXMubnVtZXJpYyhyb3cxJE92ZXJhbGwuU3Vydml2YWwuLk1vbnRocy4pKjMwDQogIH0NCn0NCg0KbWV0YWRhdGFfbWV0YXMkR3JvdXAgPC0gYXMuZmFjdG9yKG1ldGFkYXRhX21ldGFzJEdyb3VwKQ0KYGBgDQoNCk5vdywgZm9yIGVhY2ggb2Ygb3VyIGRhdGFzZXRzLCB3ZSBjYW4gZm9ybWF0IHRoZW0gdG8gd29yayB3aXRoIERFU2VxMiwgYW5kIHRoZW4gcnVuIHRoZSBhcHByb3ByaWF0ZSBhbmFseXNpcy92aXN1YWxpemF0aW9ucyBmb3IgdGhlbS4NCg0KIyMjIyBsbmNSTkENCg0KYGBge3J9DQojTmVlZCB0byBjb252ZXJ0IHRoZSBnZW5lIG5hbWVzIHRvIHRoZSBhY3R1YWwgcm93IG5hbWVzLCBhbmQgcmVtb3ZlIHRoYXQgY29sdW1uDQpsbmNfREVTRVEgPC0gbG5jUk5BX2ZpbmFsWywtMV0NCnJvd25hbWVzKGxuY19ERVNFUSkgPC0gbG5jUk5BX2ZpbmFsJEdlbmVfSUQNCmBgYA0KDQoNCg0KIyMjIyMgTWV0YWRhdGEgTWV0YXN0YXNpcw0KYGBge3J9DQojQ3JlYXRlIHRoZSBERVNlcSBkYXRhIGFuZCBhbmFseXplIGl0DQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGE9cm91bmQobG5jX0RFU0VRKSwgY29sRGF0YT1tZXRhZGF0YSwgZGVzaWduPX5Hcm91cCkNCmRkcyA8LSBERVNlcShkZHMpDQoNCiNQbG90IHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcyBvZiB0aGUgZGlmZmVyZW50aWFsIGRhdGENCnBsb3REaXNwRXN0cyhkZHMpDQoNCiNBY2Nlc3MgYW5kIHN0b3JlIHRoZSByZXN1bHRzIG9mIERFRw0KcmVzX2xuY19tZXRhcyA8LSByZXN1bHRzKGRkcywgdGlkeT1UUlVFKQ0Kcm93bmFtZXMocmVzX2xuY19tZXRhcykgPC0gcmVzX2xuY19tZXRhc1ssIDFdDQpyZXNfbG5jX21ldGFzIDwtIHJlc19sbmNfbWV0YXMgJT4lIA0KICBhcnJhbmdlKHJlc19sbmNfbWV0YXMkcGFkaikNCmhlYWQocmVzX2xuY19tZXRhcykNCg0Kc3VtbWFyeShyZXNfbG5jX21ldGFzKQ0KDQojQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIChsaWtlbHkgc2tld2VkKQ0KaGlzdChyZXNfbG5jX21ldGFzJHBhZGopDQoNCiNBbmFseXplIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcyB3aXRoIExGQyB0aHJlc2hvbGQgb2YgMS41Lg0KcmVzMiA8LSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCmhpc3QocmVzMiRwYWRqKQ0KDQpmb3IgKGkgaW4gMToxMCkgew0KICBwbG90Q291bnRzKGRkcywgZ2VuZT1yb3duYW1lcyhyZXNfbG5jX21ldGFzKVtpXSwgaW50Z3JvdXA9Ikdyb3VwIiwgbWFpbj1yb3duYW1lcyhyZXNfbG5jX21ldGFzKVtpXSkNCn0NCg0KcGxvdE1BKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCiNyZXNldCBwYXINCnBhcihtZnJvdz1jKDEsMSkpDQojIE1ha2UgYSBiYXNpYyB2b2xjYW5vIHBsb3QNCndpdGgocmVzX2xuY19tZXRhcywgcGxvdChsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgbWFpbj0iVm9sY2FubyBwbG90IiwgeGxpbT1jKC0zLDMpKSkNCg0KIyBBZGQgY29sb3JlZCBwb2ludHM6IGJsdWUgaWYgcGFkajwwLjAxLCByZWQgaWYgbG9nMkZDPjEgYW5kIHBhZGo8MC4wNSkNCndpdGgoc3Vic2V0KHJlc19sbmNfbWV0YXMsIHBhZGo8LjAxICksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJibHVlIikpDQp3aXRoKHN1YnNldChyZXNfbG5jX21ldGFzLCBwYWRqPC4wMSAmIGFicyhsb2cyRm9sZENoYW5nZSk+MiksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJyZWQiKSkNCg0KbGlicmFyeShwaGVhdG1hcCkNCnZzZGF0YSA8LSB2YXJpYW5jZVN0YWJpbGl6aW5nVHJhbnNmb3JtYXRpb24oZGRzLCBibGluZD1UUlVFKQ0KcGxvdFBDQSh2c2RhdGEsIGludGdyb3VwPSJHcm91cCIpDQoNCnJsZF9tYXQgPC0gYXNzYXkodnNkYXRhKQ0KcmxkX2NvciA9IGNvcihybGRfbWF0KQ0KcGhlYXRtYXAocmxkX2NvciwgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBzaG93X2NvbG5hbWVzPUZBTFNFLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpDQpgYGANCg0KTm93LCBsZXQncyB1c2UgdGhlIHNhbWUgbWV0aG9kb2xvZ3kgdG8gd29yayB0aHJvdWdoIHRoZSBvdGhlciBSTkEgdHlwZXMgd2UgaGF2ZSB3aXRoIHVzOg0KDQojIyMjIG1pUk5BDQoNCmBgYHtyfQ0KI05lZWQgdG8gY29udmVydCB0aGUgZ2VuZSBuYW1lcyB0byB0aGUgYWN0dWFsIHJvdyBuYW1lcywgYW5kIHJlbW92ZSB0aGF0IGNvbHVtbg0KbWlfREVTRVEgPC0gbWlSTkFfZmluYWxbLC0xXQ0Kcm93bmFtZXMobWlfREVTRVEpIDwtIG1pUk5BX2ZpbmFsJEdlbmVfSUQNCmBgYA0KDQojIyMjIyBNZXRhZGF0YSBOb3JtYWwNCmBgYHtyfQ0KI0NyZWF0ZSB0aGUgREVTZXEgZGF0YSBhbmQgYW5hbHl6ZSBpdA0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhPXJvdW5kKG1pX0RFU0VRKSwgY29sRGF0YT1tZXRhZGF0YSwgZGVzaWduPX5Hcm91cCkNCmRkcyA8LSBERVNlcShkZHMpDQoNCiNQbG90IHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcyBvZiB0aGUgZGlmZmVyZW50aWFsIGRhdGENCnBsb3REaXNwRXN0cyhkZHMpDQoNCiNBY2Nlc3MgYW5kIHN0b3JlIHRoZSByZXN1bHRzIG9mIERFRw0KcmVzX21pUl9tYWluIDwtIHJlc3VsdHMoZGRzLCB0aWR5PVRSVUUpDQpyb3duYW1lcyhyZXNfbWlSX21haW4pIDwtIHJlc19taVJfbWFpblssIDFdDQpyZXNfbWlSX21haW4gPC0gcmVzX21pUl9tYWluICU+JSANCiAgYXJyYW5nZShyZXNfbWlSX21haW4kcGFkaikNCmhlYWQocmVzX21pUl9tYWluKQ0KDQpzdW1tYXJ5KHJlc19taVJfbWFpbikNCg0KI0FuYWx5emUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcyAobGlrZWx5IHNrZXdlZCkNCmhpc3QocmVzX21pUl9tYWluJHBhZGopDQoNCiNBbmFseXplIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcyB3aXRoIExGQyB0aHJlc2hvbGQgb2YgMS41Lg0KcmVzMiA8LSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCmhpc3QocmVzMiRwYWRqKQ0KDQpmb3IgKGkgaW4gMToxMCkgew0KICBwbG90Q291bnRzKGRkcywgZ2VuZT1yb3duYW1lcyhyZXNfbWlSX21haW4pW2ldLCBpbnRncm91cD0iR3JvdXAiLCBtYWluPXJvd25hbWVzKHJlc19taVJfbWFpbilbaV0pDQp9DQoNCnBsb3RNQShkZHMsIGxmY1RocmVzaG9sZD0xLjUsIGFscGhhPTAuMDEpDQojcmVzZXQgcGFyDQpwYXIobWZyb3c9YygxLDEpKQ0KIyBNYWtlIGEgYmFzaWMgdm9sY2FubyBwbG90DQp3aXRoKHJlc19taVJfbWFpbiwgcGxvdChsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgbWFpbj0iVm9sY2FubyBwbG90IiwgeGxpbT1jKC0zLDMpKSkNCg0KIyBBZGQgY29sb3JlZCBwb2ludHM6IGJsdWUgaWYgcGFkajwwLjAxLCByZWQgaWYgbG9nMkZDPjEgYW5kIHBhZGo8MC4wNSkNCndpdGgoc3Vic2V0KHJlc19taVJfbWFpbiwgcGFkajwuMDEgKSwgcG9pbnRzKGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBjb2w9ImJsdWUiKSkNCndpdGgoc3Vic2V0KHJlc19taVJfbWFpbiwgcGFkajwuMDEgJiBhYnMobG9nMkZvbGRDaGFuZ2UpPjIpLCBwb2ludHMobG9nMkZvbGRDaGFuZ2UsIC1sb2cxMChwdmFsdWUpLCBwY2g9MjAsIGNvbD0icmVkIikpDQoNCmxpYnJhcnkocGhlYXRtYXApDQp2c2RhdGEgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRkcywgYmxpbmQ9VFJVRSkNCnBsb3RQQ0EodnNkYXRhLCBpbnRncm91cD0iR3JvdXAiKQ0KDQpybGRfbWF0IDwtIGFzc2F5KHZzZGF0YSkNCnJsZF9jb3IgPSBjb3IocmxkX21hdCkNCnBoZWF0bWFwKHJsZF9jb3IsIGNsdXN0ZXJfcm93cz1UUlVFLCBjbHVzdGVyX2NvbHM9VFJVRSwgc2hvd19jb2xuYW1lcz1GQUxTRSwgc2hvd19yb3duYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIyMjIE1ldGFkYXRhIFJlY3VycmVuY2UNCmBgYHtyfQ0KI0NyZWF0ZSB0aGUgREVTZXEgZGF0YSBhbmQgYW5hbHl6ZSBpdA0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhPXJvdW5kKG1pX0RFU0VRKSwgY29sRGF0YT1tZXRhZGF0YV9yZWN1ciwgZGVzaWduPX5Hcm91cCkNCmRkcyA8LSBERVNlcShkZHMpDQoNCiNQbG90IHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcyBvZiB0aGUgZGlmZmVyZW50aWFsIGRhdGENCnBsb3REaXNwRXN0cyhkZHMpDQoNCiNBY2Nlc3MgYW5kIHN0b3JlIHRoZSByZXN1bHRzIG9mIERFRw0KcmVzX21pUl9yZWN1ciA8LSByZXN1bHRzKGRkcywgdGlkeT1UUlVFKQ0Kcm93bmFtZXMocmVzX21pUl9yZWN1cikgPC0gcmVzX21pUl9yZWN1clssIDFdDQpyZXNfbWlSX3JlY3VyIDwtIHJlc19taVJfcmVjdXIgJT4lIA0KICBhcnJhbmdlKHJlc19taVJfcmVjdXIkcGFkaikNCmhlYWQocmVzX21pUl9yZWN1cikNCg0Kc3VtbWFyeShyZXNfbWlSX3JlY3VyKQ0KDQojQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIChsaWtlbHkgc2tld2VkKQ0KaGlzdChyZXNfbWlSX3JlY3VyJHBhZGopDQoNCiNBbmFseXplIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcyB3aXRoIExGQyB0aHJlc2hvbGQgb2YgMS41Lg0KcmVzMiA8LSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCmhpc3QocmVzMiRwYWRqKQ0KDQpmb3IgKGkgaW4gMToxMCkgew0KICBwbG90Q291bnRzKGRkcywgZ2VuZT1yb3duYW1lcyhyZXNfbWlSX3JlY3VyKVtpXSwgaW50Z3JvdXA9Ikdyb3VwIiwgbWFpbj1yb3duYW1lcyhyZXNfbWlSX3JlY3VyKVtpXSkNCn0NCg0KcGxvdE1BKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCiNyZXNldCBwYXINCnBhcihtZnJvdz1jKDEsMSkpDQojIE1ha2UgYSBiYXNpYyB2b2xjYW5vIHBsb3QNCndpdGgocmVzX21pUl9yZWN1ciwgcGxvdChsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgbWFpbj0iVm9sY2FubyBwbG90IiwgeGxpbT1jKC0zLDMpKSkNCg0KIyBBZGQgY29sb3JlZCBwb2ludHM6IGJsdWUgaWYgcGFkajwwLjAxLCByZWQgaWYgbG9nMkZDPjEgYW5kIHBhZGo8MC4wNSkNCndpdGgoc3Vic2V0KHJlc19taVJfcmVjdXIsIHBhZGo8LjAxICksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJibHVlIikpDQp3aXRoKHN1YnNldChyZXNfbWlSX3JlY3VyLCBwYWRqPC4wMSAmIGFicyhsb2cyRm9sZENoYW5nZSk+MiksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJyZWQiKSkNCg0KbGlicmFyeShwaGVhdG1hcCkNCnZzZGF0YSA8LSB2YXJpYW5jZVN0YWJpbGl6aW5nVHJhbnNmb3JtYXRpb24oZGRzLCBibGluZD1UUlVFKQ0KcGxvdFBDQSh2c2RhdGEsIGludGdyb3VwPSJHcm91cCIpDQoNCnJsZF9tYXQgPC0gYXNzYXkodnNkYXRhKQ0KcmxkX2NvciA9IGNvcihybGRfbWF0KQ0KcGhlYXRtYXAocmxkX2NvciwgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBzaG93X2NvbG5hbWVzPUZBTFNFLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjIyMgTWV0YWRhdGEgTWV0YXN0YXNpcw0KYGBge3J9DQojQ3JlYXRlIHRoZSBERVNlcSBkYXRhIGFuZCBhbmFseXplIGl0DQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGE9cm91bmQobWlfREVTRVEpLCBjb2xEYXRhPW1ldGFkYXRhX21ldGFzLCBkZXNpZ249fkdyb3VwKQ0KZGRzIDwtIERFU2VxKGRkcykNCg0KI1Bsb3QgdGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIG9mIHRoZSBkaWZmZXJlbnRpYWwgZGF0YQ0KcGxvdERpc3BFc3RzKGRkcykNCg0KI0FjY2VzcyBhbmQgc3RvcmUgdGhlIHJlc3VsdHMgb2YgREVHDQpyZXNfbWlSX21ldGFzIDwtIHJlc3VsdHMoZGRzLCB0aWR5PVRSVUUpDQpyb3duYW1lcyhyZXNfbWlSX21ldGFzKSA8LSByZXNfbWlSX21ldGFzWywgMV0NCnJlc19taVJfbWV0YXMgPC0gcmVzX21pUl9tZXRhcyAlPiUgDQogIGFycmFuZ2UocmVzX21pUl9tZXRhcyRwYWRqKQ0KaGVhZChyZXNfbWlSX21ldGFzKQ0KDQpzdW1tYXJ5KHJlc19taVJfbWV0YXMpDQoNCiNBbmFseXplIHRoZSBkaXN0cmlidXRpb24gb2YgcC12YWx1ZXMgKGxpa2VseSBza2V3ZWQpDQpoaXN0KHJlc19taVJfbWV0YXMkcGFkaikNCg0KI0FuYWx5emUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIHdpdGggTEZDIHRocmVzaG9sZCBvZiAxLjUuDQpyZXMyIDwtIHJlc3VsdHMoZGRzLCBsZmNUaHJlc2hvbGQ9MS41LCBhbHBoYT0wLjAxKQ0KaGlzdChyZXMyJHBhZGopDQoNCmZvciAoaSBpbiAxOjEwKSB7DQogIHBsb3RDb3VudHMoZGRzLCBnZW5lPXJvd25hbWVzKHJlc19taVJfbWV0YXMpW2ldLCBpbnRncm91cD0iR3JvdXAiLCBtYWluPXJvd25hbWVzKHJlc19taVJfbWV0YXMpW2ldKQ0KfQ0KDQpwbG90TUEoZGRzLCBsZmNUaHJlc2hvbGQ9MS41LCBhbHBoYT0wLjAxKQ0KI3Jlc2V0IHBhcg0KcGFyKG1mcm93PWMoMSwxKSkNCiMgTWFrZSBhIGJhc2ljIHZvbGNhbm8gcGxvdA0Kd2l0aChyZXNfbWlSX21ldGFzLCBwbG90KGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBtYWluPSJWb2xjYW5vIHBsb3QiLCB4bGltPWMoLTMsMykpKQ0KDQojIEFkZCBjb2xvcmVkIHBvaW50czogYmx1ZSBpZiBwYWRqPDAuMDEsIHJlZCBpZiBsb2cyRkM+MSBhbmQgcGFkajwwLjA1KQ0Kd2l0aChzdWJzZXQocmVzX21pUl9tZXRhcywgcGFkajwuMDEgKSwgcG9pbnRzKGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBjb2w9ImJsdWUiKSkNCndpdGgoc3Vic2V0KHJlc19taVJfbWV0YXMsIHBhZGo8LjAxICYgYWJzKGxvZzJGb2xkQ2hhbmdlKT4yKSwgcG9pbnRzKGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBjb2w9InJlZCIpKQ0KDQpsaWJyYXJ5KHBoZWF0bWFwKQ0KdnNkYXRhIDwtIHZhcmlhbmNlU3RhYmlsaXppbmdUcmFuc2Zvcm1hdGlvbihkZHMsIGJsaW5kPVRSVUUpDQpwbG90UENBKHZzZGF0YSwgaW50Z3JvdXA9Ikdyb3VwIikNCg0KcmxkX21hdCA8LSBhc3NheSh2c2RhdGEpDQpybGRfY29yID0gY29yKHJsZF9tYXQpDQpwaGVhdG1hcChybGRfY29yLCBjbHVzdGVyX3Jvd3M9VFJVRSwgY2x1c3Rlcl9jb2xzPVRSVUUsIHNob3dfY29sbmFtZXM9RkFMU0UsIHNob3dfcm93bmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyMjIG1STkENCg0KYGBge3J9DQojTmVlZCB0byBjb252ZXJ0IHRoZSBnZW5lIG5hbWVzIHRvIHRoZSBhY3R1YWwgcm93IG5hbWVzLCBhbmQgcmVtb3ZlIHRoYXQgY29sdW1uDQptX0RFU0VRIDwtIG1STkFfZmluYWxbLC0xXQ0Kcm93bmFtZXMobV9ERVNFUSkgPC0gbVJOQV9maW5hbCRHZW5lX0lEDQpgYGANCg0KIyMjIyMgTWV0YWRhdGEgTm9ybWFsDQpgYGB7cn0NCiNDcmVhdGUgdGhlIERFU2VxIGRhdGEgYW5kIGFuYWx5emUgaXQNCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YT1yb3VuZChtX0RFU0VRKSwgY29sRGF0YT1tZXRhZGF0YSwgZGVzaWduPX5Hcm91cCkNCmRkcyA8LSBERVNlcShkZHMpDQoNCiNQbG90IHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcyBvZiB0aGUgZGlmZmVyZW50aWFsIGRhdGENCnBsb3REaXNwRXN0cyhkZHMpDQoNCiNBY2Nlc3MgYW5kIHN0b3JlIHRoZSByZXN1bHRzIG9mIERFRw0KcmVzX21fbWFpbiA8LSByZXN1bHRzKGRkcywgdGlkeT1UUlVFKQ0Kcm93bmFtZXMocmVzX21fbWFpbikgPC0gcmVzX21fbWFpblssIDFdDQpyZXNfbV9tYWluIDwtIHJlc19tX21haW4gJT4lIA0KICBhcnJhbmdlKHJlc19tX21haW4kcGFkaikNCmhlYWQocmVzX21fbWFpbikNCg0Kc3VtbWFyeShyZXNfbV9tYWluKQ0KDQojQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIChsaWtlbHkgc2tld2VkKQ0KaGlzdChyZXNfbV9tYWluJHBhZGopDQoNCiNBbmFseXplIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcyB3aXRoIExGQyB0aHJlc2hvbGQgb2YgMS41Lg0KcmVzMiA8LSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCmhpc3QocmVzMiRwYWRqKQ0KDQpmb3IgKGkgaW4gMToxMCkgew0KICBwbG90Q291bnRzKGRkcywgZ2VuZT1yb3duYW1lcyhyZXNfbV9tYWluKVtpXSwgaW50Z3JvdXA9Ikdyb3VwIiwgbWFpbj1yb3duYW1lcyhyZXNfbV9tYWluKVtpXSkNCn0NCg0KcGxvdE1BKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCiNyZXNldCBwYXINCnBhcihtZnJvdz1jKDEsMSkpDQojIE1ha2UgYSBiYXNpYyB2b2xjYW5vIHBsb3QNCndpdGgocmVzX21fbWFpbiwgcGxvdChsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgbWFpbj0iVm9sY2FubyBwbG90IiwgeGxpbT1jKC0zLDMpKSkNCg0KIyBBZGQgY29sb3JlZCBwb2ludHM6IGJsdWUgaWYgcGFkajwwLjAxLCByZWQgaWYgbG9nMkZDPjEgYW5kIHBhZGo8MC4wNSkNCndpdGgoc3Vic2V0KHJlc19tX21haW4sIHBhZGo8LjAxICksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJibHVlIikpDQp3aXRoKHN1YnNldChyZXNfbV9tYWluLCBwYWRqPC4wMSAmIGFicyhsb2cyRm9sZENoYW5nZSk+MiksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJyZWQiKSkNCg0KbGlicmFyeShwaGVhdG1hcCkNCnZzZGF0YSA8LSB2YXJpYW5jZVN0YWJpbGl6aW5nVHJhbnNmb3JtYXRpb24oZGRzLCBibGluZD1UUlVFKQ0KcGxvdFBDQSh2c2RhdGEsIGludGdyb3VwPSJHcm91cCIpDQoNCnJsZF9tYXQgPC0gYXNzYXkodnNkYXRhKQ0KcmxkX2NvciA9IGNvcihybGRfbWF0KQ0KcGhlYXRtYXAocmxkX2NvciwgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBzaG93X2NvbG5hbWVzPUZBTFNFLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjIyMgTWV0YWRhdGEgUmVjdXJyZW5jZQ0KYGBge3J9DQojQ3JlYXRlIHRoZSBERVNlcSBkYXRhIGFuZCBhbmFseXplIGl0DQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGE9cm91bmQobV9ERVNFUSksIGNvbERhdGE9bWV0YWRhdGFfcmVjdXIsIGRlc2lnbj1+R3JvdXApDQpkZHMgPC0gREVTZXEoZGRzKQ0KDQojUGxvdCB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgb2YgdGhlIGRpZmZlcmVudGlhbCBkYXRhDQpwbG90RGlzcEVzdHMoZGRzKQ0KDQojQWNjZXNzIGFuZCBzdG9yZSB0aGUgcmVzdWx0cyBvZiBERUcNCnJlc19tX3JlY3VyIDwtIHJlc3VsdHMoZGRzLCB0aWR5PVRSVUUpDQpyb3duYW1lcyhyZXNfbV9yZWN1cikgPC0gcmVzX21fcmVjdXJbLCAxXQ0KcmVzX21fcmVjdXIgPC0gcmVzX21fcmVjdXIgJT4lIA0KICBhcnJhbmdlKHJlc19tX3JlY3VyJHBhZGopDQpoZWFkKHJlc19tX3JlY3VyKQ0KDQpzdW1tYXJ5KHJlc19tX3JlY3VyKQ0KDQojQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIChsaWtlbHkgc2tld2VkKQ0KaGlzdChyZXNfbV9yZWN1ciRwYWRqKQ0KDQojQW5hbHl6ZSBkaXN0cmlidXRpb24gb2YgcC12YWx1ZXMgd2l0aCBMRkMgdGhyZXNob2xkIG9mIDEuNS4NCnJlczIgPC0gcmVzdWx0cyhkZHMsIGxmY1RocmVzaG9sZD0xLjUsIGFscGhhPTAuMDEpDQpoaXN0KHJlczIkcGFkaikNCg0KZm9yIChpIGluIDE6MTApIHsNCiAgcGxvdENvdW50cyhkZHMsIGdlbmU9cm93bmFtZXMocmVzX21fcmVjdXIpW2ldLCBpbnRncm91cD0iR3JvdXAiLCBtYWluPXJvd25hbWVzKHJlc19tX3JlY3VyKVtpXSkNCn0NCg0KcGxvdE1BKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCiNyZXNldCBwYXINCnBhcihtZnJvdz1jKDEsMSkpDQojIE1ha2UgYSBiYXNpYyB2b2xjYW5vIHBsb3QNCndpdGgocmVzX21fcmVjdXIsIHBsb3QobG9nMkZvbGRDaGFuZ2UsIC1sb2cxMChwdmFsdWUpLCBwY2g9MjAsIG1haW49IlZvbGNhbm8gcGxvdCIsIHhsaW09YygtMywzKSkpDQoNCiMgQWRkIGNvbG9yZWQgcG9pbnRzOiBibHVlIGlmIHBhZGo8MC4wMSwgcmVkIGlmIGxvZzJGQz4xIGFuZCBwYWRqPDAuMDUpDQp3aXRoKHN1YnNldChyZXNfbV9yZWN1ciwgcGFkajwuMDEgKSwgcG9pbnRzKGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBjb2w9ImJsdWUiKSkNCndpdGgoc3Vic2V0KHJlc19tX3JlY3VyLCBwYWRqPC4wMSAmIGFicyhsb2cyRm9sZENoYW5nZSk+MiksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJyZWQiKSkNCg0KbGlicmFyeShwaGVhdG1hcCkNCnZzZGF0YSA8LSB2YXJpYW5jZVN0YWJpbGl6aW5nVHJhbnNmb3JtYXRpb24oZGRzLCBibGluZD1UUlVFKQ0KcGxvdFBDQSh2c2RhdGEsIGludGdyb3VwPSJHcm91cCIpDQoNCnJsZF9tYXQgPC0gYXNzYXkodnNkYXRhKQ0KcmxkX2NvciA9IGNvcihybGRfbWF0KQ0KcGhlYXRtYXAocmxkX2NvciwgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBzaG93X2NvbG5hbWVzPUZBTFNFLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjIyMgTWV0YWRhdGEgTWV0YXN0YXNpcw0KYGBge3J9DQojQ3JlYXRlIHRoZSBERVNlcSBkYXRhIGFuZCBhbmFseXplIGl0DQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGE9cm91bmQobV9ERVNFUSksIGNvbERhdGE9bWV0YWRhdGFfbWV0YXMsIGRlc2lnbj1+R3JvdXApDQpkZHMgPC0gREVTZXEoZGRzKQ0KDQojUGxvdCB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgb2YgdGhlIGRpZmZlcmVudGlhbCBkYXRhDQpwbG90RGlzcEVzdHMoZGRzKQ0KDQojQWNjZXNzIGFuZCBzdG9yZSB0aGUgcmVzdWx0cyBvZiBERUcNCnJlc19tX21ldGFzIDwtIHJlc3VsdHMoZGRzLCB0aWR5PVRSVUUpDQpyb3duYW1lcyhyZXNfbV9tZXRhcykgPC0gcmVzX21fbWV0YXNbLCAxXQ0KcmVzX21fbWV0YXMgPC0gcmVzX21fbWV0YXMgJT4lIA0KICBhcnJhbmdlKHJlc19tX21ldGFzJHBhZGopDQpoZWFkKHJlc19tX21ldGFzKQ0KDQpzdW1tYXJ5KHJlc19tX21ldGFzKQ0KDQojQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIChsaWtlbHkgc2tld2VkKQ0KaGlzdChyZXNfbV9tZXRhcyRwYWRqKQ0KDQojQW5hbHl6ZSBkaXN0cmlidXRpb24gb2YgcC12YWx1ZXMgd2l0aCBMRkMgdGhyZXNob2xkIG9mIDEuNS4NCnJlczIgPC0gcmVzdWx0cyhkZHMsIGxmY1RocmVzaG9sZD0xLjUsIGFscGhhPTAuMDEpDQpoaXN0KHJlczIkcGFkaikNCg0KZm9yIChpIGluIDE6MTApIHsNCiAgcGxvdENvdW50cyhkZHMsIGdlbmU9cm93bmFtZXMocmVzX21fbWV0YXMpW2ldLCBpbnRncm91cD0iR3JvdXAiLCBtYWluPXJvd25hbWVzKHJlc19tX21ldGFzKVtpXSkNCn0NCg0KcGxvdE1BKGRkcywgbGZjVGhyZXNob2xkPTEuNSwgYWxwaGE9MC4wMSkNCiNyZXNldCBwYXINCnBhcihtZnJvdz1jKDEsMSkpDQojIE1ha2UgYSBiYXNpYyB2b2xjYW5vIHBsb3QNCndpdGgocmVzX21fbWV0YXMsIHBsb3QobG9nMkZvbGRDaGFuZ2UsIC1sb2cxMChwdmFsdWUpLCBwY2g9MjAsIG1haW49IlZvbGNhbm8gcGxvdCIsIHhsaW09YygtMywzKSkpDQoNCiMgQWRkIGNvbG9yZWQgcG9pbnRzOiBibHVlIGlmIHBhZGo8MC4wMSwgcmVkIGlmIGxvZzJGQz4xIGFuZCBwYWRqPDAuMDUpDQp3aXRoKHN1YnNldChyZXNfbV9tZXRhcywgcGFkajwuMDEgKSwgcG9pbnRzKGxvZzJGb2xkQ2hhbmdlLCAtbG9nMTAocHZhbHVlKSwgcGNoPTIwLCBjb2w9ImJsdWUiKSkNCndpdGgoc3Vic2V0KHJlc19tX21ldGFzLCBwYWRqPC4wMSAmIGFicyhsb2cyRm9sZENoYW5nZSk+MiksIHBvaW50cyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSksIHBjaD0yMCwgY29sPSJyZWQiKSkNCg0KbGlicmFyeShwaGVhdG1hcCkNCnZzZGF0YSA8LSB2YXJpYW5jZVN0YWJpbGl6aW5nVHJhbnNmb3JtYXRpb24oZGRzLCBibGluZD1UUlVFKQ0KcGxvdFBDQSh2c2RhdGEsIGludGdyb3VwPSJHcm91cCIpDQoNCnJsZF9tYXQgPC0gYXNzYXkodnNkYXRhKQ0KcmxkX2NvciA9IGNvcihybGRfbWF0KQ0KcGhlYXRtYXAocmxkX2NvciwgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBzaG93X2NvbG5hbWVzPUZBTFNFLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpDQpgYGANCg0KDQpMZXQncyB0ZXN0IG91dCB0aGUgR0RDUk5BVG9vbHMgcGFja2FnZS4NCg0KYGBge3J9DQpsaWJyYXJ5KEdEQ1JOQVRvb2xzKQ0KbGlicmFyeSgiQW5ub3RhdGlvbkRiaSIpDQpsaWJyYXJ5KCJvcmcuSHMuZWcuZGIiKQ0KDQptX0RFU0VRIDwtIG1fREVTRVFbMzA6MjA1MzEsXQ0KbV9ERVNFUSQnRU5UUkVaJyA8LSBjKHJvd25hbWVzKG1fREVTRVEpKQ0KbV9ERVNFUSQnRU5UUkVaJyA8LSBnc3ViKCIuKlxcfCIsICIiLCBtX0RFU0VRJCdFTlRSRVonKQ0KbV9ERVNFUSQnRW5zZW1ibGUnIDwtIG1hcElkcyhvcmcuSHMuZWcuZGIsIGtleXM9bV9ERVNFUSQnRU5UUkVaJywga2V5dHlwZT0iRU5UUkVaSUQiLCBjb2x1bW49IkVOU0VNQkwiKQ0Kcm93bmFtZXMobV9ERVNFUSkgPC0gbWFrZS5uYW1lcyhtX0RFU0VRJEVuc2VtYmxlLCB1bmlxdWU9VFJVRSkNCm1fREVTRVEkRW5zZW1ibGUgPC0gTlVMTA0KbV9ERVNFUSRFTlRSRVogPC0gTlVMTA0KbWV0YWRhdGFfbmV3IDwtIGRhdGEuZnJhbWUoYyhtZXRhZGF0YSRTYW1wbGVfSUQpKQ0KbWV0YWRhdGFfbmV3JCdHcm91cCcgPC0gaWZlbHNlKG1ldGFkYXRhJEdyb3VwID09IDAsICdOb3JtYWxUaXNzdWUnLCAnUHJpbWFyeVR1bW9yJykNCg0KREVHQWxsIDwtIGdkY0RFQW5hbHlzaXMoY291bnRzPXJvdW5kKG1fREVTRVEpLCBncm91cD1tZXRhZGF0YV9uZXckR3JvdXAsIGNvbXBhcmlzb249J05vcm1hbFRpc3N1ZS1QcmltYXJ5VHVtb3InLCBtZXRob2Q9J0RFU2VxMicpDQoNCmRlUEMgPC0gZ2RjREVSZXBvcnQoZGVnPURFR0FsbCkNCmhlYWQoZGVQQykNCg0KZ2RjQmFyUGxvdChkZVBDLCBhbmdsZT00NSwgZGF0YS50eXBlPSdSTkFzZXEnKQ0KDQoNCg0KZW5yaWNoT3V0cHV0IDwtIGdkY0VucmljaEFuYWx5c2lzKGdlbmU9cm93bmFtZXMoZGVQQylbMTo1MDBdLCBzaW1wbGlmeT1UUlVFKQ0KZ2RjRW5yaWNoUGxvdChlbnJpY2htZW50PWVucmljaE91dHB1dCwgdHlwZT0nYmFyJywgY2F0ZWdvcnk9J0tFR0cnLCBudW0udGVybXM9NSkNCmBgYA0KDQpgYGB7cn0NCg0KYGBgDQoNCg0K