2.1 Workflow Introduction

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code. The online version displays the results for one Patient. You can visit our main page at bigcat-um.github.io/IMD-PUPY/

2.2 Clinical Biomarker Data

Loading patient specific data

Note: this step requires input from the user, to select data from another patient as the example (QTOF_KV_184).

## Load data for all patients
patientdata <- read.csv("Data/Data_PuPyMS_QTOF_KV_BIGCAT.csv")

##Select Patient ID here (options: A through T)
patientID <- "I" ## patients B, C, P and Q do not have data on the AA panel
#if value for age is not numeric (e.g. "newborn", sample: QTOF_KV_112, don't perform as.numeric.)
if(grepl("^[A-Za-z]+$", patientdata[1,patientID], perl = T)){
  print("Warning: Non-numeric age value is part of data")
  if(patientdata[1,patientID] == "newborn"){
    age <- as.numeric(0)
  }
  else{print("Warning: name for age value not recognized")}
}else{
    age <- as.numeric(patientdata[1,patientID])
  }
## Note there is a difference in the age categories between the two assays

##PUPY: (age in years)
if(age >= 0 && age < 12){
  ref_age_PUPY = c(5,6)  #age_0to1y
  agerange <- "0 to 1 years"
  }else if(age >= 12 && age < 60){
  ref_age_PUPY  = c(7,8) #age_1to5y
  agerange <- "1 to 5 years"
  }else if(age >= 60 && age < 192){
  ref_age_PUPY  = c(9,10) #age_5to16y
  agerange <- "5 to 16 years"
  }else if(age >= 192){
  ref_age_PUPY = c(11,12) #age_16yandUp
  agerange <- "16+ years"
  }else{
    ref_age_PUPY = c(13,14) #age_0to16
    agerange <- "0 to 16 years"
    }

##Urea: (age in months = m, in years = y)
if(age >= 0 && age < 1){
  ref_age_urea = c(4,5) #age_0to1m
  }else if(age >= 1 && age < 6){
  ref_age_urea = c(6,7) #age_1to6m
  }else if(age >= 6 && age < 12){
  ref_age_urea = c(8,9) #age_6mto1y
  }else if(age >= 12 && age < 24){
  ref_age_urea = c(10,11) #age_1to2y
  }else if(age >= 24 && age < 48){
  ref_age_urea = c(12,13) #age_2-4y
  }else if(age >= 48 && age < 120){
  ref_age_urea = c(14,15) #age_4-10y
  }else if(age >= 120 && age < 216){
  ref_age_urea = c(16,17) #age_10-18y
  }else{
    ref_age_urea = c(18,19) #age_18yandUp
    }

print(paste("Selected Patient ID is: " , patientID, ", age is between: ", agerange, " old"))
[1] "Selected Patient ID is:  I , age is between:  0 to 1 years  old"

Combine Reference + Patient data for each assay seperatly

#Load reference data (includes annotations for biomarkers)
refvalues_PUPY <- read.delim("Data/Reference_values_PUPY_noNAs.txt")
refvalues_urea <- read.delim("Data/Reference_values_Urea.txt")

# Select column based on patientID:
patient.column <- as.integer(match(patientID,names(patientdata)))

##Select Referencedata for one patient 
patientID_PUPY <- as.data.frame(patientdata[4:37, c(1,patient.column)],  drop=false) #Selecting data for PUPY only
patientID_urea <- as.data.frame(patientdata[42:95, c(1,patient.column)],  drop=false) #Selecting data for Urea Acids (including Amino Acids) only
patientID_treatment <- as.data.frame(patientdata[97, c(1,patient.column)],  drop=false) #Selecting data for Urea Acids (including Amino Acids) only

## Choose correct reference value column for further data visualization
age_patientID_PUPY <- as.data.frame(refvalues_PUPY[ , c(1:3, ref_age_PUPY[1], ref_age_PUPY[2], 15 )],  drop=false) #Selecting reference data for PUPY, age: 0 to 1 year
remove(refvalues_PUPY,ref_age_PUPY)
age_patientID_urea <- as.data.frame(refvalues_urea[ , c(1:3, ref_age_urea[1], ref_age_urea[2], 20 )],  drop=false) #Selecting reference data for Urea, age: 0 to 1 month
remove(refvalues_urea,ref_age_urea)
remove(patient.column)

print(paste("Relevant age data is selected for patient: " , patientID))
[1] "Relevant age data is selected for patient:  I"

2.3 Pathway Models

Retrieve Metabolite, Protein and Disease Data from Pathway Models


if(!"SPARQL" %in% installed.packages()){
  install.packages("SPARQL")
}
library(SPARQL)

##Connect to Endpoint WikiPathways
endpointwp <- "https://sparql.wikipathways.org/sparql"

## 1. Query metadata:

queryMetadata <-
"SELECT DISTINCT ?dataset (str(?titleLit) as ?title) ?date ?license 
WHERE {
   ?dataset a void:Dataset ;
   dcterms:title ?titleLit ;
   dcterms:license ?license ;
   pav:createdOn ?date .
 }"

resultsMetadata <- SPARQL(endpointwp,queryMetadata,curl_args=list(useragent=R.version.string))
showresultsMetadata <- resultsMetadata$results
remove(queryMetadata, resultsMetadata)

## 2. Query Pathway data:

##Pathway Models IDs of interest:
pathways_of_Interest <- paste('"WP4224"', '"WP4225"', '"WP4571"', '"WP4595"', '"WP4583"', '"WP4584"')

queryAllContent_1 <-
"
PREFIX wp:      <http://vocabularies.wikipathways.org/wp#>
PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dcterms: <http://purl.org/dc/terms/>

#Variable selection
SELECT DISTINCT (str(?title) as ?pathwayName) ?PWID 
(count(distinct ?geneProduct) AS ?GenesInPWs)
(count(distinct ?protein) AS ?ProteinsInPWs) 
(count(distinct ?metaboliteNode) AS ?MetabolitesInPWs) 
(count(distinct ?interactionID) AS ?RheaInPWs) 
(count(distinct ?interactionMissing) AS ?NoRheaInPWs)
(count(distinct ?omim) as ?diseaseIDs)

WHERE {
  
  VALUES ?PWID {
"

queryAllContent_2 <-
"
}
    ?pathway dcterms:identifier ?PWID. #Obtain the ID
    #?pathway wp:ontologyTag cur:IEM .
    ?pathway wp:isAbout ?gpmlRDF_ID . #find the corresponding GPML link     
    ?pathway dc:title ?title . #Obtain the title   

      {
    ?geneProduct dcterms:isPartOf ?pathway . #Only those part of PW             
    ?geneProduct a wp:GeneProduct . #Filter for Protein DataNodes    
    } 
    UNION
    {
    ?protein dcterms:isPartOf ?pathway . #Only those part of PW             
    ?protein a wp:Protein . #Filter for Protein DataNodes    
    } 
    UNION
    { 
    ?metaboliteNode a wp:Metabolite . #Filter for Metabolite DataNodes
    ?metaboliteNode dcterms:isPartOf ?pathway . #Only those part of PW
    }
    UNION 
    { 
    OPTIONAL{?interaction wp:bdbRhea ?interactionID . #Find interactions with Rhea
    ?interaction dcterms:isPartOf ?pathway .} #Only those part of PW
    }
    UNION
    {
    OPTIONAL{?interactionMissing dcterms:isPartOf ?pathway . #Additional interactions
    ?interactionMissing rdf:type wp:Conversion . #Type 'metabolic conversion'
    FILTER NOT EXISTS {?interactionMissing wp:bdbRhea ?interactionID . }#No Rhea   
    }  
    }
    UNION {
    ?diseaseNode dcterms:isPartOf ?gpmlRDF_ID . #Only check for matching pathways 
    ?diseaseNode rdf:type gpml:Label . #Only include textLabels
    ?diseaseNode gpml:href ?omim . #That have an href attribute  
    FILTER regex(?omim, \"omim.org\", \"i\") #Only keep hrefs if they contain 'omim.org'
    }
      
} ORDER BY ASC(?pathway)
"
queryAllContent <- paste(queryAllContent_1,pathways_of_Interest,queryAllContent_2)
resultsAllContent <- SPARQL(endpointwp,queryAllContent,curl_args=list(useragent=R.version.string))
showresultsAllContent <- resultsAllContent$results

remove(queryAllContent, resultsAllContent)

#Print results Table
print(showresultsMetadata)
print(showresultsAllContent)

remove(pathways_of_Interest, queryAllContent_1, queryAllContent_2)

2.4 Selection of Relevant Biomarkers

First, the Patient data is connected to the reference data (including identifier mapping). Second, the annotated data is checked for missing values (NAs) and unification to ChEBI.

## First, match the names of all biomarkers to an identifier (from reference value data, these mappings have been checked manually)
library(dplyr) #needed for left_join() operation
#Note: the matching below is case sensitive (left_join from dplyr doesn't have an option for case insensitive matching)
annotated_PUPY <-
left_join(patientID_PUPY, age_patientID_PUPY, by=c("Dutch.name")) %>%
  rowwise()  
  colnames(annotated_PUPY)[5] <- "low.Ref"
  colnames(annotated_PUPY)[6] <- "high.Ref"

annotated_urea <-
left_join(patientID_urea, age_patientID_urea, by=c("Dutch.name")) %>%
  rowwise()  
  colnames(annotated_urea)[5] <- "low.Ref"
  colnames(annotated_urea)[6] <- "high.Ref"

annotated_both <- rbind(annotated_PUPY, annotated_urea) #Combining data for PUPY and urea biomarkers in one dataframe

#Remove rows which are for treatment values (Oxypurinol and allopurinol) or not measured by assays anymore(Argininosuccinic acid anhydride) by name of compound
annotated_both <- annotated_both [(!(annotated_both$Dutch.name=="oxypurinol") & !(annotated_both$Dutch.name=="allopurinol") & !(annotated_both$Dutch.name=="ASA_anhydride")),]

MissingMappings <- list()
counter = 1
for (i in 1:nrow(annotated_both)){
  if(annotated_both[i,4] != ""){next}
  else{
    MissingMappings[counter] <- (annotated_both[i,1])
    counter <- counter + 1
   }
}
remove(i,counter)

CHEBIMissing <- list()
counter = 1
for (i in 1:nrow(annotated_both)){
  if(!grepl('CHEBI', annotated_both[i,4]) && annotated_both[i,4] != ""){
    CHEBIMissing[counter] <- (annotated_both[i,1])
    counter <- counter + 1
    }
  else{next}
}
remove(i,counter)
remove(patientID_PUPY,patientID_urea,annotated_PUPY,annotated_urea,age_patientID_PUPY,age_patientID_urea)
print(paste("These biomarkers do not have an identfier mapping:" , paste(c(MissingMappings), collapse=', ' )))
[1] "These biomarkers do not have an identfier mapping: cyshcys"
print(paste("These biomarkers are not annotated with a ChEBI ID:" , paste(c(CHEBIMissing), collapse=', ' )))
[1] "These biomarkers are not annotated with a ChEBI ID: 2,8-dihydroxyadenine"
  

Second, query which biomarkers measured in the assays are not in any pathway model:

allBiomarkers <- annotated_both$ID ##Take the ID column for all data
allBiomarkers <- allBiomarkers[grepl("^CHEBI:", allBiomarkers)] #remove Is not starting with 'CHEBI:' for consistency
library(stringr)
cleaned_CHEBI_all <- str_replace(allBiomarkers, "CHEBI:", "ch:") #update IDs to work with SPARQL query
#supercleaned_CHEBI <- str_replace(vector_CHEBI, "CHEBI:", "") #update IDs to work with SPARQL query
string_CHEBI_all <- paste(c(cleaned_CHEBI_all), collapse=' ' )
remove (cleaned_CHEBI_all)

item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
SELECT DISTINCT ?chebiMetabolite WHERE {
  VALUES ?chebiMetabolite {"
item2 = "}
  ?pathwayRes  a wp:Pathway ;
                wp:organismName \'Homo sapiens\' .
  
  ?metabolite   a wp:Metabolite ;
                dcterms:identifier ?id ;
                dcterms:isPartOf ?pathwayRes .

  ?metabolite wp:bdbChEBI ?chebiMetabolite.
}"

queryMissingBiomarkers_all <- paste(item1,string_CHEBI_all,item2)
remove(item1,item2)

resultsMissingBiomarkers_all <- SPARQL(endpointwp,queryMissingBiomarkers_all,curl_args=list(useragent=R.version.string))
listMissingBiomarkers_all <- c(resultsMissingBiomarkers_all$results) #safe results as list for comparison.
remove(queryMissingBiomarkers_all,resultsMissingBiomarkers_all)

CHEBI_inPWs_all <- gsub("[<https://identifiers.org/chebi/CHEBI:>]", "", listMissingBiomarkers_all) #ChEBI IDs Results IRI cleanup
CHEBI_inQuery_all <- gsub("CHEBI:", "", allBiomarkers) #ChEBI IDs Query cleanup
intersectingCHEBI_all <- setdiff(CHEBI_inQuery_all,CHEBI_inPWs_all)
string_intersectingCHEBI_all <- paste(c(intersectingCHEBI_all), collapse=', ' )
#count_biomarkers <- sapply(strsplit(string_CHEBI, " "), length)

#Find names for missing Biomarkers based on ChEBI ID (to help with data curation)
CHEBI_intersectingCHEBI_all <- paste0("CHEBI:", intersectingCHEBI_all) #update IDs to match back to data again
missingNames_all <- list()
for (j in 1:length(CHEBI_intersectingCHEBI_all)){
  for (i in 1:nrow(annotated_both)){
    if(annotated_both[i,4] == CHEBI_intersectingCHEBI_all[j]){
       missingNames_all[j] <- annotated_both[i,3]
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
string_missingNames_all <- do.call(paste, c(as.list(missingNames_all), sep = ", "))

#Print relevant information:
if(length(intersectingCHEBI_all) == 0 ){print("All biomarkers on the assay(s) are in a pathway!")} else{
  print(paste0("These biomarkers (as ChEBI IDs) are not in any pathway: " , string_intersectingCHEBI_all, "; with the following Database names: ", string_missingNames_all))}
[1] "These biomarkers (as ChEBI IDs) are not in any pathway: 35621, 70744, 89698, 27596, 50599, 86498, 17261, 49015, 61511; with the following Database names: AABA, Gly-pro, Homocystine, 3-Methyl-histidine, 1-Methyl-histidine, Hydroxylysine, N-Aspartylglucosamine, Piperideine carboxylic acid, a-Aminoadipate semialdehyde"
remove(allBiomarkers, CHEBI_inPWs_all, CHEBI_inQuery_all, CHEBI_intersectingCHEBI_all, intersectingCHEBI_all, string_CHEBI_all, string_intersectingCHEBI_all, string_missingNames_all, missingNames_all, CHEBIMissing)

Third, comparing patient data to reference data to calculate the log(2) change value and data cleanup.

# First, convert the Patient Data column to a type "double" iso "character" (empty values will become "NA")
names(annotated_both)[names(annotated_both) == patientID] <- "i.patientData" 
annotated_both$i.patientData <- as.integer(annotated_both$i.patientData)

# Second, compare the Patient Data to the reference Values (if available), and calculate an additional column called "Change".
annotated_both <- annotated_both%>%mutate(Change = case_when(
  i.patientData == 0 ~ NA_real_,                    # Patient data is equal to zero
  high.Ref == 0 ~ NA_real_,                    # If highRef is equal to zero (to avoid dividing by zero)
  is.na(i.patientData) ~ NA_real_,                  # No patient data available
  is.na(high.Ref) & is.na(low.Ref) & i.patientData != 0 ~ NA_real_,   # If no highRef and lowRef value is available (NA), and there is PatientData, change is NA (unable to determine)
 i.patientData >= low.Ref & i.patientData <= high.Ref ~ 1,  #Patient data between reference values
 i.patientData < low.Ref ~ i.patientData/low.Ref, #Patient data lower than lowRef value (downregulated) 
 i.patientData > high.Ref ~ i.patientData/high.Ref   #Patient data higher than highRef value (upregulated)
))

# Third, remove all rows in the dataframe where the Change is NA .
annotated_both <- annotated_both[complete.cases(annotated_both$Change),]

# Add patient ID to column again:
names(annotated_both)[names(annotated_both) == "i.patientData"] <- patientID #Rename column to actual Patient ID for visualization
# Convert "Change column to log(2) scale
annotated_both$log.Change <- log(annotated_both$Change, 2)
#Convert Change column from scientific notation to 2 digits.
is.num <- sapply(annotated_both, is.numeric)
annotated_both[is.num] <- lapply(annotated_both[is.num], round, 2)
remove(is.num)

#Copy data for further cleanup (sort based on highest log-change value, no zero values etc.):
annotated_both_nozero <-annotated_both
#Data cleanup
annotated_both_nozero <- annotated_both_nozero [(!(annotated_both_nozero$log.Change==0)),] #Remove rows with zero's :
annotated_both_nozero <- annotated_both_nozero[order(-annotated_both_nozero$log.Change),]  #Sort on log2(change)
#Print final dataset of patient before visualisation (only relevant columns):
print(annotated_both_nozero[,c(1,3,4,9)], digits = 2)
NA

Fourth, find which relevant biomarkers are in a pathway model.

##Add all IDs in a list
CHEBI_IDs <- list()
counter = 1
for (i in 1:nrow(annotated_both_nozero)){
  if(grepl('CHEBI', annotated_both_nozero[i,4]) && annotated_both_nozero[i,4] != "" && annotated_both_nozero[i,9] != 0){
    CHEBI_IDs[counter] <- (annotated_both_nozero[i,4])
    counter <- counter + 1
    }
  else{next}
}
remove(i,counter)
vector_CHEBI <- unlist(CHEBI_IDs) #convert list to array, for traversing the data to a SPARQL query later on
library(stringr)
cleaned_CHEBI <- str_replace(vector_CHEBI, "CHEBI:", "ch:") #update IDs to work with SPARQL query
supercleaned_CHEBI <- str_replace(vector_CHEBI, "CHEBI:", "") #update IDs to work with SPARQL query
string_CHEBI <- paste(c(cleaned_CHEBI), collapse=' ' )
remove (CHEBI_IDs, vector_CHEBI, cleaned_CHEBI)

##Connect to Endpoint WikiPathways (if step 2.3 is skipped)
endpointwp <- "https://sparql.wikipathways.org/sparql"

##For now, split up the query in sections, to add the string_CHEBI as VALUES.
item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
select distinct ?pathwayRes (str(?wpid) as ?pathway) (str(?title) as ?pathwayTitle) (count(distinct ?chebiMetabolite) AS ?CHEBIsInPWs)
where {
VALUES ?chebiMetabolite {"
item2 = " }
 
 ?datanode  a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetabolite ;
            dcterms:isPartOf ?pathwayRes .

 ?pathwayRes a wp:Pathway ;
             wp:organismName \'Homo sapiens\' ; 
            dcterms:identifier ?wpid ;
            dc:title ?title .
}

ORDER BY DESC(?CHEBIsInPWs)"

query_RelevantPWs <- paste(item1,string_CHEBI,item2)
remove(item1,item2)

results_RelevantPWs <- SPARQL(endpointwp,query_RelevantPWs,curl_args=list(useragent=R.version.string))
showresults_RelevantPWs <- results_RelevantPWs$results
remove(query_RelevantPWs,results_RelevantPWs)

print(paste0("There are ", nrow(annotated_both_nozero) ," biomarkers relevant for patient " , patientID, ", with ChEBI-IDs: ", string_CHEBI, "."))
[1] "There are 7 biomarkers relevant for patient I, with ChEBI-IDs: ch:58443 ch:17821 ch:17261 ch:17568 ch:16964 ch:70744 ch:57731."

Fifth, find which patient specific biomarkers are not in any pathway.

##Find Missing Biomarkers (not part of any Human pathway model)
item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
SELECT DISTINCT ?chebiMetabolite WHERE {
  VALUES ?chebiMetabolite {"
item2 = "}
  ?pathwayRes  a wp:Pathway ;
                wp:organismName 'Homo sapiens' .
  
  ?metabolite   a wp:Metabolite ;
                dcterms:identifier ?id ;
                dcterms:isPartOf ?pathwayRes .

  ?metabolite wp:bdbChEBI ?chebiMetabolite.
}"

queryMissingBiomarkers <- paste(item1,string_CHEBI,item2)
remove(item1,item2)

resultsMissingBiomarkers <- SPARQL(endpointwp,queryMissingBiomarkers,curl_args=list(useragent=R.version.string))
listMissingBiomarkers <- c(resultsMissingBiomarkers$results) #safe results as list for comparison.
remove(queryMissingBiomarkers,resultsMissingBiomarkers)

CHEBI_inPWs <- gsub("[<https://identifiers.org/chebi/CHEBI:>]", "", listMissingBiomarkers) #ChEBI IDs IRI cleanup
intersectingCHEBI <- setdiff(supercleaned_CHEBI,CHEBI_inPWs)
string_intersectingCHEBI <- paste(c(intersectingCHEBI), collapse=', ' )
count_biomarkers <- sapply(strsplit(string_CHEBI, " "), length)

#Find names for missing Biomarkers based on ChEBi ID (to help with data curation)
CHEBI_intersectingCHEBI <- paste0("CHEBI:", intersectingCHEBI) #update IDs to match back to data again
missingNames <- list()
for (j in 1:length(CHEBI_intersectingCHEBI)){
  for (i in 1:nrow(annotated_both_nozero)){
    if(annotated_both_nozero[i,4] == CHEBI_intersectingCHEBI[j]){
       missingNames[j] <- annotated_both_nozero[i,3]
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
string_missingNames <- do.call(paste, c(as.list(missingNames), sep = ", "))

#Print relevant information:
if(length(intersectingCHEBI) == 0 ){print("All relevant biomarkers are in a pathway!")} else{
  print(paste0("These biomarkers (as ChEBI IDs) are not in a pathway: " , string_intersectingCHEBI, "; with the following Database names: ", string_missingNames))}
[1] "These biomarkers (as ChEBI IDs) are not in a pathway: 17261, 70744; with the following Database names: N-Aspartylglucosamine, Gly-pro"

2.5 Theoretical Biomarker Data

Read in information from IEMBase (collected manually) and select only data relevant to sample matrix (urine) and age category (from patient)

biomarkers_purine_all <- read.table(file = 'Data/IEM_database_info_Purine.txt', sep = '\t', header = TRUE)
biomarkers_pyrimidine_all <- read.table(file = 'Data/IEM_database_info_Pyrimidine.txt', sep = '\t', header = TRUE)
biomarkers_urea_all <- read.table(file = 'Data/IEM_database_info_Urea.txt', sep = '\t', header = TRUE)

#Summarize Purine disorders
list_diseases_purine <- unique(na.omit(biomarkers_purine_all[c(17)])) #list all unique disease protein names,  #Remove NA values
#Summarize Pyrimidine disorders
list_diseases_pyrimidine <- unique(na.omit(biomarkers_pyrimidine_all[c(17)])) #list all unique disease protein names #Remove NA values
#Summarize Urea Cycle disorders
list_diseases_urea <- unique(na.omit(biomarkers_urea_all[c(17)])) #list all unique disease protein names #Remove NA values

total_list_diseases <- c(list_diseases_purine[[1]], list_diseases_pyrimidine[[1]], list_diseases_urea[[1]])

## Print amount of unique diseases for each category:
paste0("In total, there were ", length(list_diseases_purine[[1]]) ," unique purine, ", length(list_diseases_pyrimidine[[1]]), " pyrimidine and ", length(list_diseases_urea[[1]]), " urea cycle IMDs (total of ", length(total_list_diseases) ," disorders).")
[1] "In total, there were 17 unique purine, 10 pyrimidine and 8 urea cycle IMDs (total of 35 disorders)."
##PUPY: (age in years)
if(age >= 0 && age < 1){
  ref_age_markers = c(10)  #age_0to1m, neonatal
  }else if(age >= 1 && age < 18){
  ref_age_markers  = c(11) #age_1mto18m, infancy
  }else if(age >= 18 && age < 132){
  ref_age_markers  = c(12) #age_18mto11y, childhood
  }else if(age >= 131 && age < 192){
  ref_age_markers = c(13) #age_11to16y, adolescence
  }else{
    ref_age_markers = c(14) #age_16<, adulthood
  }

biomarkers_purine_age <- as.data.frame(biomarkers_purine_all[, c(1,17,5:7,ref_age_markers,16)],  drop=false) 
biomarkers_purine_age$Category <- 1
biomarkers_pyrimidine_age <- as.data.frame(biomarkers_pyrimidine_all[, c(1,17,5:7,ref_age_markers,16)],  drop=false) 
biomarkers_pyrimidine_age$Category <- 2
biomarkers_urea_age <- as.data.frame(biomarkers_urea_all[, c(1,17,5:7,ref_age_markers,16)],  drop=false) 
biomarkers_urea_age$Category <- 3

biomarkers_three <- rbind(biomarkers_purine_age, biomarkers_pyrimidine_age, biomarkers_urea_age) #Combining data theoretical biomarkers in one dataframe
names(biomarkers_three)[6] <- "UpDown"
remove(biomarkers_purine_all, biomarkers_purine_age, biomarkers_pyrimidine_all, biomarkers_pyrimidine_age, biomarkers_urea_all, biomarkers_urea_age)

# Third, clean up the data, by selecting only entries with a HMDB ID, measured in urine, and converting non-numerical entries for up/down regulation.
biomarkers_three <- biomarkers_three  [(!(is.na(biomarkers_three$HMDB))),]
biomarkers_three <- biomarkers_three  [(!(is.na(biomarkers_three$Measured.in))) & ((biomarkers_three$Measured.in=="U") | (biomarkers_three$Measured.in=="Urine")), ]

#Replace non-numerical characters with values
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "-2 to -1"] <- ("-1.5")) 
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "-2 to n"] <- ("-1"))
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "-1 to n"] <- ("-0.5"))
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "n to 2"] <- ("1"))
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "n to 1"] <- ("0.5"))
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "n"] <- ("0"))
biomarkers_three <- within(biomarkers_three, UpDown[UpDown == "+-"] <- ("0"))

biomarkers_three$UpDown <- as.numeric(biomarkers_three$UpDown)

##Convert old HMD's to new IDs:
for (i in 1:length((biomarkers_three$HMDB))) {
  if(nchar(biomarkers_three$HMDB[i])<= 9){
    biomarkers_three$HMDB[i] <- str_replace(biomarkers_three$HMDB[i], "HMDB", "HMDB00")
  }
  else{next}
}
remove(i)

## Print amount of unique diseases and biomarkers:
paste0("For these three classes of IMDs, ", nrow(unique(na.omit(biomarkers_three[c(5)]))) ," unique biomarkers were relevant for the sample matrix urine, representing ",  nrow(unique(na.omit(biomarkers_three[c(2)]))) , " unique disorders; " , sum(is.na(biomarkers_three[c(2)])), " disorder is missing a protein name.")
[1] "For these three classes of IMDs, 26 unique biomarkers were relevant for the sample matrix urine, representing 26 unique disorders; 1 disorder is missing a protein name."
##Dynamic caption info:
df_figure_caption_biomarkers <- biomarkers_three[,c(3,5)] # Combines biomarkers_three$Symptoms with biomarkers_three$HMDB
df_figure_caption_biomarkers <- unique(df_figure_caption_biomarkers) #Only retain unique metabolite names and HMDB IDs.
df_figure_caption_biomarkers <- na.omit(df_figure_caption_biomarkers) #remove any rows with NA-values
df_figure_caption_disorders <- biomarkers_three[,c(2,1)] # Combines biomarkers_three$Protein_HGNC_name with biomarkers_three$Disorder
df_figure_caption_disorders <- unique(df_figure_caption_disorders) #Only retain unique disorder names + HGNC names
df_figure_caption_disorders <- na.omit(df_figure_caption_disorders) #remove any rows with NA-values

relevant <- biomarkers_three[,c(2,5,6,8)]

##Remove rows with NA values anywhere
relevant <- relevant[complete.cases(relevant), ]
##Remove rows with zero values anywhere
relevant <- relevant[apply(relevant!=0, 1, all),]

#Keep unique diseases and category names
diseases_categories <- relevant[,c(1,4)]
unique_disease_categories <- aggregate(diseases_categories[-1], list(diseases_categories$Protein_HGNC_name), FUN = mean, na.rm = TRUE) #Sorted alphabetically

# Load required packages for cast() function (data transformation based on certain column input.)
if (!require("reshape2")) {
   install.packages("reshape2", dependencies = TRUE)
   library(reshape2)
   }
Loading required package: reshape2
if (!require("reshape")) {
   install.packages("reshaper", dependencies = TRUE)
   library(reshape)
   }
Loading required package: reshape

Attaching package: ‘reshape’

The following objects are masked from ‘package:reshape2’:

    colsplit, melt, recast

The following object is masked from ‘package:dplyr’:

    rename
# Transform the data, using protein HGNC names as row names, HMDB-IDs as columns, and UpDown-regulation values to create matrix
protein.cast <- cast(relevant, Protein_HGNC_name~HMDB, value="UpDown", sum)
column_start = (ncol(protein.cast)+1)
for (i in 1:nrow(protein.cast)) {
  if(protein.cast[i,1] == unique_disease_categories[i,1]){
    protein.cast[i,column_start] <- unique_disease_categories[i,2]
  }
}
remove(i)
names(protein.cast)[column_start] <- "Category"
protein.cast_ordered <- protein.cast[order(protein.cast$Category),]

lines_Category_Purine <- nrow(protein.cast_ordered[protein.cast_ordered$Category == "1",])
lines_Category_Pyrimidine <- nrow(protein.cast_ordered[protein.cast_ordered$Category == "2",])
lines_Category_Urea <- nrow(protein.cast_ordered[protein.cast_ordered$Category == "3",])

##Remove categories column again, so it will not disturb the correlation calculation
protein.cast_ordered <- subset(protein.cast_ordered, select=-c(Category))
rnames <- protein.cast_ordered[,1] #Store the protein names to use as row values in matrix later
matrix_protein.cast <- data.matrix(protein.cast_ordered) #convert the dataframe to a matrix (only including numeric values)
rownames(matrix_protein.cast) <- rnames #Add the saved protein names as rowLabels
matrix_protein.cast <- matrix_protein.cast[,-1] #Remove the first column, including count of rows from dataframe. #Sorted alphabetically

remove(biomarkers_three, diseases_categories, protein.cast, protein.cast_ordered, relevant, unique_disease_categories)

not_covered_IMDs <- setdiff(total_list_diseases,rnames)
not_covered_IMDs <- paste(not_covered_IMDs, collapse = ', ')
amount_biomarkers_matrixData <- ncol(matrix_protein.cast)
amount_diseases_matrixData <- nrow(matrix_protein.cast)

##Add disorders not covered with zero's for all columns:
#MatrixB <- rbind(MatrixA, c(10,11,12))  

## Print amount of unique diseases for each category:
paste0("Taking available reference data into account and availability of a HMDB ID, a maximum of ", amount_biomarkers_matrixData ," unique biomarkers could be linked to ", amount_diseases_matrixData, " IMDs.")
[1] "Taking available reference data into account and availability of a HMDB ID, a maximum of 23 unique biomarkers could be linked to 25 IMDs."
paste0("For the following IMDs, no data was available: ", not_covered_IMDs, ".")
[1] "For the following IMDs, no data was available: TPMT, PRPPS, RRM2B, ITPA, IMPDH1, AMPD1, TYMP, TK2, DHODH."
if (!require("gplots")) {
   install.packages("gplots", dependencies = TRUE)
   library(gplots)
   }
Loading required package: gplots

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
if (!require("RColorBrewer")) {
   install.packages("RColorBrewer", dependencies = TRUE)
   library(RColorBrewer)
   }
Loading required package: RColorBrewer
my_palette <- colorRampPalette(c("blue", "white", "red"))(n = 299)

col_breaks = c(seq(-3,-1,length=100),  # for blue
  seq(-0.99,0.99,length=100),           # for yellow
  seq(1,3,length=100))             # for red

#Create a file name specific for patientID
filename <- paste0("Images/", age, "months_heatmap.png")
title_name <- paste0("Biomarker overlap for three IMD types, \n age category: ", agerange)

# creates a 5 x 5 inch image
png(filename,    # create PNG for the heat map        
  width = 5*300,        # 5 x 300 pixels
  height = 5*300,
  res = 280,            # 300 pixels per inch
  pointsize = 8)        # smaller font size

output <- heatmap.2(matrix_protein.cast,
  #cellnote = matrix_protein.cast,  # same data set for cell labels
  main = title_name, # heat map title
  notecol="black",      # change font color of cell labels to black
  cellnote = ifelse(matrix_protein.cast != 0, matrix_protein.cast, ""), #Visualizing only label if data is not zero
  
  RowSideColors = c(    # grouping row-variables into different categories, e.g.
     rep("gray", lines_Category_Purine),   #  Purine diseases
     rep("blue", lines_Category_Pyrimidine),    # Pyrimidine diseases
     rep("black", lines_Category_Urea)),    # Urea diseases
  
  density.info="none",  # turns off density plot inside color legend
  trace="none",         # turns off trace lines inside the heat map
  margins =c(12,9),     # widens margins around plot
  col=my_palette,       # use on color palette defined earlier
  breaks=col_breaks,    # enable color transition at specified limits
  dendrogram="row",     # only draw a row dendrogram
  Colv="NA")            # turn off column clustering

par(lend = 1)           # square line ends for the color legend
legend("topright",      # location of the legend on the heatmap plot
    legend = c("Purine", "Pyrimidine", "Urea cycle"), # category labels
    col = c("gray", "blue", "black"), # color key
    title = "IMD classes",           # legend title
    lty= 1,             # line style
    lwd = 10            # line width
)
 

dev.off()               # close the PNG device
null device 
          1 
remove(rnames, title_name)

#Show the heatmap as output in RMD file
knitr::include_graphics(filename)


##Obtain the order of the diseases and biomarkers as appearing in the clustered heatmap:
verticalAxis <- rev(rownames(matrix_protein.cast)[output$rowInd])
horizontalAxis <- colnames(matrix_protein.cast)[output$colInd]

##New dynamic Figure captions:
###Diseases
disorderNames <- list()

for (j in 1:length(verticalAxis)){
  for (i in 1:nrow(df_figure_caption_disorders)){
    if(df_figure_caption_disorders[i,1] == verticalAxis[j]){
      combineDiseaseHGNC <- paste0(verticalAxis[j], ': ', df_figure_caption_disorders[i,2])
       disorderNames[j] <- combineDiseaseHGNC
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
ordered_disorderNames <- do.call(paste, c(as.list(disorderNames), sep = ", "))

###Biomarkers
biomarkerNames <- list()

for (j in 1:length(horizontalAxis)){
  for (i in 1:nrow(df_figure_caption_biomarkers)){
    if(df_figure_caption_biomarkers[i,2] == horizontalAxis[j]){
      combinebiomarkerHMDB <- paste0(horizontalAxis[j], ': ', df_figure_caption_biomarkers[i,1])
       biomarkerNames[j] <- combinebiomarkerHMDB
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
ordered_biomarkerNames <- do.call(paste, c(as.list(biomarkerNames), sep = ", "))

#df_figure_caption_biomarkers

#Print Figure caption (sorted diseases and unsorted metabolites)
paste("Protein names correspond to the following disorders:", ordered_disorderNames)
[1] "Protein names correspond to the following disorders: SLC25A15: Ornithine transporter deficiency, OTC: Ornithine transcarbamylase deficiency, ADA: Adenosine deaminase deficiency, SLC25A13: Citrin deficiency, ARG1: Arginase deficiency, DGUOK: Deoxyguanosine kinase deficiency, NAGS: N-Acetylglutamate synthase deficiency, CPS1: Carbamoyl phosphate synthetase I deficiency, NT5C3A: Pyrimidine 5’-nucleotidase superactivity, AGXT2: Beta-aminoisobutyrate-pyruvate transaminase deficiency, APRT: Adenine phosphoribosyltransferase deficiency, ADSL: Adenyl- succinate  lyase deficiency, ATIC: AICAr transformylase/IMP cyclohydrolase deficiency, ASL: Argininosuccinic aciduria, UMPS: Orotic aciduria type I, ASS1: Citrullinemia type I, XAN2: Xanthinuria, Type II, XO: Xanthinuria, Type I, PNP: Purine nucleoside phosphorylase deficiency, DPYD: Dihydropyrimidine dehydrogenase deficiency, HPRT1_less: Kelley-Seegmiller syndrome, HPRT1: Lesch-Nyhan syndrome, PRPS1: Phosphoribosyl pyrophosphate synthetase 1 superactivity, DPYS: Dihydropyrimidinase deficiency, UPB1: Beta-ureidopropionase deficiency"
paste(". HMDB IDs resemble these metabolites:", ordered_biomarkerNames)
[1] ". HMDB IDs resemble these metabolites: HMDB0000026: N-Carbamyl-beta-alanine, HMDB0000034: Adenine, HMDB0000052: Argininosuccinate, HMDB0000071: Deoxyinosine, dIno, HMDB0000076: Dihydrouracil, HMDB0000079: Dihydrothymine, HMDB0000085: Purine nucleoside phosphorylase, HMDB0000101: Deoxyadenosine, HMDB0000143: Galactose, HMDB0000157: Hypoxanthine, HMDB0000226: Orotic acid, HMDB0000262: Thymine, HMDB0000289: Uric acid, HMDB0000292: Xanthine, HMDB0000300: Uracil, HMDB0000401: 2,8-Dihydroxyadenine, HMDB0000635: Succinylacetone, HMDB0000679: Homocitrulline, HMDB0000797: SAICA riboside, HMDB0000904: Citrulline, HMDB0000912: Succinyladenosine, HMDB0002299: beta-aminoisobutyrate, HMDB0062179: AICA riboside"
remove(total_list_diseases, amount_biomarkers_matrixData, amount_diseases_matrixData, column_start, ordered_biomarkerNames, ordered_disorderNames, list_diseases_purine, list_diseases_pyrimidine, list_diseases_urea, not_covered_IMDs)

2.6 Relevant Biomarker Overlap

## Obtain patient data
patient_biomarker_data_HMDB <- annotated_both_nozero[,c(4,7,9)]
patient_biomarker_data_HMDB <- patient_biomarker_data_HMDB[!(is.na(patient_biomarker_data_HMDB$HMDB) | patient_biomarker_data_HMDB$HMDB==""), ]
#replace data smaller than 0.05 or -0.05 with NA
patient_biomarker_data_HMDB$log.Change[findInterval(patient_biomarker_data_HMDB$log.Change, c(-0.05,0.05)) == 1L] <- NA
# Remove all rows in the dataframe where the Change is NA .
patient_biomarker_data_HMDB <- patient_biomarker_data_HMDB[complete.cases(patient_biomarker_data_HMDB$log.Change),]

#Dynamic caption for patient specific biomarkers:
df_figure_caption_biomarkers_patientspecific <- annotated_both_nozero[,c(3,7)] # Combines biomarkers_three$Symptoms with biomarkers_three$HMDB
df_figure_caption_biomarkers_patientspecific <- unique(df_figure_caption_biomarkers_patientspecific) #Only retain unique metabolite names and HMDB IDs.
df_figure_caption_biomarkers_patientspecific <- na.omit(df_figure_caption_biomarkers_patientspecific) #remove any rows with NA-values

#Convert log(2)change data to similar numeric scale as theoretical biomarker data (-3 to 3)
patient_biomarker_data_HMDB <- patient_biomarker_data_HMDB%>%mutate(biomarker.Change = case_when(
 log.Change > 3 ~ 3,         # Every log(2) change value larger then 3
 log.Change < -3 ~ -3,       # Every log(2) change value smaller then -3
 TRUE ~ log.Change           # Keep all other values the same
))

#Remove columns which are not needed
patient_biomarker_data_HMDB <- patient_biomarker_data_HMDB[,c(-1,-3)]
#Transpose data
patient_extension = (t(patient_biomarker_data_HMDB[,2]))
colnames(patient_extension) <- patient_biomarker_data_HMDB$HMDB

#Add label "patient' in front of ID, for clearer depiction in heatmap
if(patientID_treatment[,2] == 1){ 
  patientLabel <- paste0("Patient_", patientID, ", treated")
  }else{patientLabel <- paste0("Patient_", patientID, ", untreated")}

rownames(patient_extension) <- patientLabel

#Add patient data to matrix
matrix_protein.cast_patient <- acast(rbind(melt(matrix_protein.cast), melt(patient_extension)), X1~X2, sum) 
Warning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUE
matrix_protein.cast_patient <- matrix_protein.cast_patient[apply(matrix_protein.cast_patient[,-1], 1, function(x) !all(x==0)),] #remove a row if all values are zero.
matrix_protein.cast_patient <- matrix_protein.cast_patient[, colSums(matrix_protein.cast_patient != 0) > 0] #remove a column if all values are zero.

#Create a file name specific for patientID
filename2 <- paste0("Images/", patientID, "_heatmap.png")
title_name2 <- paste0("Biomarker overlap for three IMD types, \n age category: ", agerange, "\n for patient:", patientID)

# creates a 5 x 5 inch image
png(filename2,    # create PNG for the heat map        
  width = 5*400+nrow(patient_biomarker_data_HMDB)*30, # Updated based on high number of biomarkers for patient.
  height = 5*300,
  res = 280,            # 300 pixels per inch
  pointsize = 8)        # smaller font size

output_patient <- heatmap.2(matrix_protein.cast_patient,
  #cellnote = round(matrix_protein.cast_patient,0),  # same data set for cell labels, round off to 1 decimal.
  main = title_name2, # heat map title
  notecol="black",      # change font color of cell labels to black
  cellnote = ifelse(matrix_protein.cast_patient != 0, round(matrix_protein.cast_patient,1), ""), #Visualizing only label if data is not zero
  
  #Color the space between the data points
  sepwidth=c(0.01,0.01),
  sepcolor="lightgray",
  colsep=1:ncol(matrix_protein.cast_patient),
  rowsep=1:nrow(matrix_protein.cast_patient),
  
  RowSideColors = c(    # grouping row-variables into different categories, e.g.
     rep("grey", lines_Category_Purine),   #  Purine diseases
     rep("blue", lines_Category_Pyrimidine),    # Pyrimidine diseases
     rep("black", lines_Category_Urea),    # Urea diseases
     rep("purple", 1)) , #patientData
  
  density.info="none",  # turns off density plot inside color legend
  trace="none",         # turns off trace lines inside the heat map
  margins =c(12,9),     # widens margins around plot
  col=my_palette,       # use on color palette defined earlier
  breaks=col_breaks,    # enable color transition at specified limits
  dendrogram="row",     # only draw a row dendrogram
  Colv="NA") #,            # turn off column clustering
  #na.color = "Grey")   # color NA values specifically --> dOESN'T WORK :(
  
par(lend = 1)           # square line ends for the color legend
legend("topright",      # location of the legend on the heatmap plot
    legend = c("Purine", "Pyrimidine", "Urea cycle", "Patient"), # category labels
    col = c("gray", "blue", "black", "purple"), # color key
    title = "IMD classes",           # legend title
    lty= 1,             # line style
    lwd = 10            # line width
)
 

dev.off()               # close the PNG device
null device 
          1 
#Show the heatmap as output in RMD file
knitr::include_graphics(filename2)


##Print names of metabolites added in heatmap originating from patient.
## Clustering order might differ, so data needs to be reloaded:

##Obtain the order of the diseases and biomarkers as appearing in the clustered heatmap:
verticalAxis_patient <- rev(rownames(matrix_protein.cast_patient)[output_patient$rowInd])
horizontalAxis_patient <- colnames(matrix_protein.cast_patient)[output_patient$colInd]

##New dynamic Figure captions:
###Diseases
disorderNames_patient <- list()

for (j in 1:length(verticalAxis_patient)){
  for (i in 1:nrow(df_figure_caption_disorders)){
    if(df_figure_caption_disorders[i,1] == verticalAxis_patient[j]){
      combineDiseaseHGNC_patient <- paste0(verticalAxis_patient[j], ': ', df_figure_caption_disorders[i,2])
       disorderNames_patient[j] <- combineDiseaseHGNC_patient
      }
    else{
      #noDiseaseID <- paste(verticalAxis_patient[(j-1)])#print message which ID cannot be found
      next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
ordered_disorderNames_patient <- do.call(paste, c(as.list(disorderNames_patient), sep = ", "))
ordered_disorderNames_patient <- str_replace_all(ordered_disorderNames_patient, ' , ', ' ' )

###Theoretical Biomarkers
biomarkerNames_patient <- list()

for (j in 1:length(horizontalAxis_patient)){
  for (i in 1:nrow(df_figure_caption_biomarkers)){
    if(df_figure_caption_biomarkers[i,2] == horizontalAxis_patient[j]){
      combinebiomarkerHMDB_patient <- paste0(horizontalAxis_patient[j], ': ', df_figure_caption_biomarkers[i,1])
       biomarkerNames_patient[j] <- combinebiomarkerHMDB_patient
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
ordered_biomarkerNames_patient <- do.call(paste, c(as.list(biomarkerNames_patient), sep = ", "))

##Patient-specific Biomarkers
compare_patient_to_theoreticalMarkers <- setdiff(horizontalAxis_patient, df_figure_caption_biomarkers[,2])

biomarkerNames_patientspecific <- list()

for (j in 1:length(compare_patient_to_theoreticalMarkers)){
  for (i in 1:nrow(df_figure_caption_biomarkers_patientspecific)){
    if(df_figure_caption_biomarkers_patientspecific[i,2] == compare_patient_to_theoreticalMarkers[j]){
      combinebiomarkerHMDB_patientspecific <- paste0(compare_patient_to_theoreticalMarkers[j], ': ', df_figure_caption_biomarkers_patientspecific[i,1])
       biomarkerNames_patientspecific[j] <- combinebiomarkerHMDB_patientspecific
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
ordered_biomarkerNames_patientspecific <- do.call(paste, c(as.list(biomarkerNames_patientspecific), sep = ", "))

#df_figure_caption_biomarkers

#Print Figure caption (sorted diseases and unsorted metabolites)
paste0("Protein names correspond to the following disorders: ", ordered_disorderNames_patient, ". ")
[1] "Protein names correspond to the following disorders: SLC25A15: Ornithine transporter deficiency, OTC: Ornithine transcarbamylase deficiency, ADA: Adenosine deaminase deficiency, SLC25A13: Citrin deficiency, ARG1: Arginase deficiency, DGUOK: Deoxyguanosine kinase deficiency, NAGS: N-Acetylglutamate synthase deficiency, CPS1: Carbamoyl phosphate synthetase I deficiency, NT5C3A: Pyrimidine 5’-nucleotidase superactivity, AGXT2: Beta-aminoisobutyrate-pyruvate transaminase deficiency, APRT: Adenine phosphoribosyltransferase deficiency, ADSL: Adenyl- succinate  lyase deficiency, ATIC: AICAr transformylase/IMP cyclohydrolase deficiency, ASL: Argininosuccinic aciduria, UMPS: Orotic aciduria type I, ASS1: Citrullinemia type I, XAN2: Xanthinuria, Type II, XO: Xanthinuria, Type I, PNP: Purine nucleoside phosphorylase deficiency, DPYD: Dihydropyrimidine dehydrogenase deficiency, HPRT1_less: Kelley-Seegmiller syndrome, HPRT1: Lesch-Nyhan syndrome, PRPS1: Phosphoribosyl pyrophosphate synthetase 1 superactivity, DPYS: Dihydropyrimidinase deficiency, UPB1: Beta-ureidopropionase deficiency. "
paste0("HMDB IDs resemble these metabolites: ", ordered_biomarkerNames_patient, ". ")
[1] "HMDB IDs resemble these metabolites: HMDB0000026: N-Carbamyl-beta-alanine, HMDB0000034: Adenine, HMDB0000052: Argininosuccinate, HMDB0000071: Deoxyinosine, dIno, HMDB0000076: Dihydrouracil, HMDB0000079: Dihydrothymine, HMDB0000085: Purine nucleoside phosphorylase, HMDB0000101: Deoxyadenosine, HMDB0000143: Galactose, HMDB0000157: Hypoxanthine, HMDB0000226: Orotic acid, HMDB0000262: Thymine, HMDB0000289: Uric acid, HMDB0000292: Xanthine, HMDB0000300: Uracil, HMDB0000401: 2,8-Dihydroxyadenine, HMDB0000635: Succinylacetone, HMDB0000679: Homocitrulline, HMDB0000797: SAICA riboside, HMDB0000904: Citrulline, HMDB0000912: Succinyladenosine, HMDB0002299: beta-aminoisobutyrate, HMDB0062179: AICA riboside. "
paste0("Additional biomarkers relevant for this specific patient (not part of IEMbase data): ", ordered_biomarkerNames_patientspecific, ". ")
[1] "Additional biomarkers relevant for this specific patient (not part of IEMbase data): HMDB0000489: N-Aspartylglucosamine, HMDB0000469: 5-(Hydroxymethyl)uracil, HMDB0000721: Gly-pro, HMDB0002166: (S)-Beta-aminoisobutyrate. "
remove(biomarkerNames, biomarkerNames_patient, biomarkerNames_patientspecific, df_figure_caption_biomarkers, df_figure_caption_disorders, df_figure_caption_biomarkers_patientspecific, disorderNames, disorderNames_patient, horizontalAxis, horizontalAxis_patient, verticalAxis, verticalAxis_patient, ordered_biomarkerNames_patient, ordered_biomarkerNames_patientspecific, ordered_disorderNames_patient, compare_patient_to_theoreticalMarkers, combinebiomarkerHMDB, combinebiomarkerHMDB_patient, combinebiomarkerHMDB_patientspecific, combineDiseaseHGNC, combineDiseaseHGNC_patient)

2.7 Pathway Selection

First, find relevant pathway from WikiPathways (sorted on covering most patient specific biomarkers)

#For now, filter out Reactome PWs due to visualization issues.
item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
PREFIX cur: <http://vocabularies.wikipathways.org/wp#Curation:>
select distinct ?pathwayRes (str(?wpid) as ?pathway) (str(?title) as ?pathwayTitle) (count(distinct ?chebiMetabolite) AS ?CHEBIsInPWs) (GROUP_CONCAT(DISTINCT fn:substring(?hgnc,37);separator=' ') AS ?Proteins) (GROUP_CONCAT(DISTINCT fn:substring(?chebiMetabolite,37);separator=' ') AS ?includedCHEBIs)
where {
VALUES ?chebiMetabolite {"

item2 = "}
 
 ?datanode  a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetabolite ;
            dcterms:isPartOf ?pathwayRes .

 ?pathwayRes a wp:Pathway ;
             wp:organismName 'Homo sapiens' ; 
            dcterms:identifier ?wpid ;
            dc:title ?title .
            
 ?datanode2 wp:bdbHgncSymbol ?hgnc ;
            dcterms:isPartOf ?pathwayRes .
            
  #?pathwayRes wp:ontologyTag cur:Reactome_Approved . 
  ?pathwayRes wp:ontologyTag cur:AnalysisCollection .           
}

ORDER BY DESC(?CHEBIsInPWs) "

query_CombinePWs <- paste(item1,string_CHEBI,item2)
remove(item1, item2)
results_CombinePWs <- SPARQL(endpointwp,query_CombinePWs,curl_args=list(useragent=R.version.string))
showresults_CombinePWs <- results_CombinePWs$results
remove(query_CombinePWs,results_CombinePWs)

#Print table with first 5 relevant pathways (if less than 5 are found, print only those)
if(nrow(showresults_CombinePWs) < 5){
print(showresults_CombinePWs_Second[1:nrow(showresults_CombinePWs_Second),c(2:4,6,5)])
}else{print(showresults_CombinePWs[1:5,c(2:4,6,5)])}

Select most relevant pathways

## Note for user: pick a different number here, if another pathway than the first is deemed most relevant (default is top pathway)
first_selected_pathway = 1

Second, find second pathway covering most unique biomarkers:

#First, obtain all the ChEBI IDs of the first ranked pathway, compare these to the list of remaining markers and only retain the marker which weren't covered yet
catch_chebis_firstPW <- showresults_CombinePWs[first_selected_pathway,6]
split_chebis_firstPW <- as.list(strsplit(catch_chebis_firstPW, ' ')[[1]])
split_string_CHEBI <- as.list(strsplit(string_CHEBI, ' ')[[1]])
cleaned_string_CHEBI <- as.list(str_replace(split_string_CHEBI, "ch:", ""))
compare_FirstSecond_CHEBI <- setdiff(cleaned_string_CHEBI, split_chebis_firstPW)
query_compatible_second <- as.list(paste("ch:", compare_FirstSecond_CHEBI, sep=""))
string_CHEBI_second <- paste(query_compatible_second, collapse = ' ')
remove(catch_chebis_firstPW,split_chebis_firstPW,split_string_CHEBI,cleaned_string_CHEBI,compare_FirstSecond_CHEBI,query_compatible_second)

item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
PREFIX cur: <http://vocabularies.wikipathways.org/wp#Curation:>
select distinct ?pathwayRes (str(?wpid) as ?pathway) (str(?title) as ?pathwayTitle) (count(distinct ?chebiMetabolite) AS ?CHEBIsInPWs) (count(distinct ?chebiMetaboliteSecond) AS ?CHEBIsInPWsSecond) (GROUP_CONCAT(DISTINCT fn:substring(?hgnc,37);separator=' ') AS ?Proteins) (GROUP_CONCAT(DISTINCT fn:substring(?chebiMetabolite,37);separator=' ') AS ?includedCHEBIs)
where {
VALUES ?chebiMetabolite {"

item2 = "}
VALUES ?chebiMetaboliteSecond {"

item3 = "}
 
 ?datanode  a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetabolite ;
            dcterms:isPartOf ?pathwayRes .

 ?pathwayRes a wp:Pathway ;
             wp:organismName 'Homo sapiens' ; 
            dcterms:identifier ?wpid ;
            dc:title ?title .
            
 ?datanode2 wp:bdbHgncSymbol ?hgnc ;
            dcterms:isPartOf ?pathwayRes .
            
  #?pathwayRes wp:ontologyTag cur:Reactome_Approved . 
  ?pathwayRes wp:ontologyTag cur:AnalysisCollection .   
  
  ?datanode a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetaboliteSecond ;
            dcterms:isPartOf ?pathwayRes .
}

ORDER BY DESC(?CHEBIsInPWsSecond) "

query_CombinePWs_Second <- paste(item1,string_CHEBI,item2, string_CHEBI_second, item3)
remove(item1, item2, item3)

results_CombinePWs_Second <- SPARQL(endpointwp,query_CombinePWs_Second,curl_args=list(useragent=R.version.string))
showresults_CombinePWs_Second <- results_CombinePWs_Second$results
remove(query_CombinePWs_Second,results_CombinePWs_Second)

#Print table with second match, first 5 relevant pathways
print("Second best matching pathways are:")
[1] "Second best matching pathways are:"
if(nrow(showresults_CombinePWs_Second) < 5){
print(showresults_CombinePWs_Second[1:nrow(showresults_CombinePWs_Second),c(2,3,4,5,7)])
}else{print(showresults_CombinePWs_Second[1:5,c(2,3,4,5,7)])}

Select 2nd most relevant pathways

## Note for user: pick a different number here, if another pathway than the first is deemed most relevant (default is top pathway)
second_selected_pathway = 1

Third, find third pathway covering most unique biomarkers:

#First, obtain all the ChEBI IDs of the second highest ranked pathway, compare these to the list of markers remaining and only retain the marker which weren't covered yet
catch_chebis_secondPW <- showresults_CombinePWs_Second[second_selected_pathway,7]
split_chebis_secondPW <- as.list(strsplit(catch_chebis_secondPW, ' ')[[1]])
split_string_CHEBI_second <- as.list(strsplit(string_CHEBI_second, ' ')[[1]])
cleaned_string_CHEBI_second <- as.list(str_replace(split_string_CHEBI_second, "ch:", ""))
compare_SecondThird_CHEBI <- setdiff(cleaned_string_CHEBI_second, split_chebis_secondPW)
query_compatible_third <- as.list(paste("ch:", compare_SecondThird_CHEBI, sep=""))
string_CHEBI_third <- paste(query_compatible_third, collapse = ' ')
remove(catch_chebis_secondPW,split_chebis_secondPW,split_string_CHEBI_second,cleaned_string_CHEBI_second,compare_SecondThird_CHEBI,query_compatible_third)

#Removed protein output, query times out.
item1 = "PREFIX ch:<https://identifiers.org/chebi/CHEBI:>
PREFIX cur: <http://vocabularies.wikipathways.org/wp#Curation:>
select distinct ?pathwayRes (str(?wpid) as ?pathway) (str(?title) as ?pathwayTitle) (count(distinct ?chebiMetabolite) AS ?CHEBIsInPWs) (count(distinct ?chebiMetaboliteSecond) AS ?CHEBIsInPWsSecond) (count(distinct ?chebiMetaboliteThird) AS ?CHEBIsInPWsThird) (GROUP_CONCAT(DISTINCT fn:substring(?chebiMetabolite,37);separator=' ') AS ?includedCHEBIs)
where {
VALUES ?chebiMetabolite {"

item2 = "}
VALUES ?chebiMetaboliteSecond {"

item3 = "}
VALUES ?chebiMetaboliteThird {"

item4 = "}
 
 ?datanode  a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetabolite ;
            dcterms:isPartOf ?pathwayRes .

 ?pathwayRes a wp:Pathway ;
             wp:organismName 'Homo sapiens' ; 
            dcterms:identifier ?wpid ;
            dc:title ?title .
            
  #?pathwayRes wp:ontologyTag cur:Reactome_Approved . 
  ?pathwayRes wp:ontologyTag cur:AnalysisCollection .   
  
  ?datanode a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetaboliteSecond ;
            dcterms:isPartOf ?pathwayRes .
            
  ?datanode a wp:Metabolite ;          
            wp:bdbChEBI ?chebiMetaboliteThird ;
            dcterms:isPartOf ?pathwayRes .          
}

ORDER BY DESC(?CHEBIsInPWsThird) "

query_CombinePWs_Third <- paste(item1,string_CHEBI,item2, string_CHEBI_second, item3, string_CHEBI_third, item4)
remove(item1, item2, item3, item4)

results_CombinePWs_Third<- SPARQL(endpointwp,query_CombinePWs_Third,curl_args=list(useragent=R.version.string))
showresults_CombinePWs_Third <- results_CombinePWs_Third$results
remove(query_CombinePWs_Third,results_CombinePWs_Third)

#Print table with third match, first 5 relevant pathways (if there are 5)
##TODO: skip lines below, if there is no relevant third best PW. (patient I for example)
print("Third best matching pathways are:")
[1] "Third best matching pathways are:"
if(nrow(showresults_CombinePWs_Third) < 1){
paste("No more pathway models found")
}else if(nrow(showresults_CombinePWs_Third) < 5){
print(showresults_CombinePWs_Third[1:nrow(showresults_CombinePWs_Third),c(2,3,4:7)])
  }else{print(showresults_CombinePWs_Third[1:5,c(2,3,4:7)])}
NA

Select 3nd most relevant pathways

if(nrow(showresults_CombinePWs_Third) < 1){
  third_selected_pathway = 0
paste("Skipping this step")}else{
third_selected_pathway = 1 ## Note for user: pick a different number here, if another pathway than the first is deemed most relevant (default is top pathway)
}

Fourth, find which biomarkers are not covered by top three (or two).

#First, obtain all the ChEBI IDs of the third highest ranked pathway, compare these to the list of remaining markers and only retain the marker which weren't covered yet
if(third_selected_pathway < 1){
paste("Using IDs from step 2 iso step 3, relevant pathway models are:")
split_string_CHEBI_Third <- as.list(strsplit(string_CHEBI_third, ' ')[[1]])
 cleaned_string_CHEBI_Third <- as.list(str_replace(split_string_CHEBI_Third, "ch:", ""))
##Using IDs from third step, if no third PWM is selected
string_CHEBI_third_final <- paste(cleaned_string_CHEBI_Third, collapse = ', ')

#Find names for IDs which are present in a pathway model (if two pathways were selected):
CHEBI_missingNames_pathways_third <- glue::glue("CHEBI:{cleaned_string_CHEBI_Third}")
missingNames_pathways_third <- list()
for (j in 1:length(CHEBI_missingNames_pathways_third)){
  for (i in 1:nrow(annotated_both_nozero)){
    if(annotated_both_nozero[i,4] == CHEBI_missingNames_pathways_third[j]){
       missingNames_pathways_third[j] <- annotated_both_nozero[i,3]
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
string_missingNames_pathways_third <- do.call(paste, c(as.list(missingNames_pathways_third), sep = ", "))

  }else{
catch_chebis_ThirdPW <- showresults_CombinePWs_Third[third_selected_pathway,7]
split_chebis_ThirdPW <- as.list(strsplit(catch_chebis_ThirdPW, ' ')[[1]])
split_string_CHEBI_Third <- as.list(strsplit(string_CHEBI_third, ' ')[[1]])
cleaned_string_CHEBI_Third <- as.list(str_replace(split_string_CHEBI_Third, "ch:", ""))
##Find IDs still missing after selecting top 3 pathway models.
compare_ThirdFourth_CHEBI <- setdiff(cleaned_string_CHEBI_Third, split_chebis_ThirdPW)
query_compatible_fourth <- as.list(compare_ThirdFourth_CHEBI, sep="")
string_CHEBI_fourth <- paste(query_compatible_fourth, collapse = ', ')

remove(catch_chebis_ThirdPW,split_string_CHEBI_Third,compare_ThirdFourth_CHEBI)

#Find names for IDs which are present in a pathway model (if three pathways were selected):
CHEBI_missingNames_pathways <- glue::glue("CHEBI:{query_compatible_fourth}")
missingNames_pathways <- list()
for (j in 1:length(CHEBI_missingNames_pathways)){
  for (i in 1:nrow(annotated_both_nozero)){
    if(annotated_both_nozero[i,4] == CHEBI_missingNames_pathways[j]){
       missingNames_pathways[j] <- annotated_both_nozero[i,3]
      }
    else{next}
  }
}
remove(i,j)
#Save list on one string for reporting purposes
string_missingNames_pathways <- do.call(paste, c(as.list(missingNames_pathways), sep = ", "))

}

#Print results selected pathways
print(paste0(showresults_CombinePWs[first_selected_pathway,3], " (", showresults_CombinePWs[first_selected_pathway,2], ") [",  showresults_CombinePWs[first_selected_pathway,4], "]"))
[1] "Amino acid metabolism (WP3925) [6]"
print(paste0(showresults_CombinePWs_Second[second_selected_pathway,3], " (", showresults_CombinePWs_Second[second_selected_pathway,2], ") [",  showresults_CombinePWs_Second[second_selected_pathway,4], "]"))
[1] "Biomarkers for pyrimidine metabolism disorders (WP4584) [5]"
#Print results not selected biomarkers
if(third_selected_pathway == 0){
  print(paste0("These identifiers are not captured in the top 2 pathways: ", string_CHEBI_third_final, ", with the following database names: ", string_missingNames_pathways_third))
}else if(third_selected_pathway == 1){
print(paste0(showresults_CombinePWs_Third[third_selected_pathway,3], " (", showresults_CombinePWs_Third[third_selected_pathway,2], ") [",  showresults_CombinePWs_Third[third_selected_pathway,4], "]"))
  print(paste0("These identifiers are not captured in the top 3 pathways: ", string_CHEBI_fourth, ", with the following database names: ", string_missingNames_pathways))
} else{print(paste0(showresults_CombinePWs_Third[third_selected_pathway,3], " (", showresults_CombinePWs_Third[third_selected_pathway,2], ")# [",  showresults_CombinePWs_Third[third_selected_pathway,4], "]"))
  print(paste0("These identifiers are not captured in the top 3 pathways: ", string_CHEBI_fourth, ", with the following database names: ", string_missingNames_pathways))}
[1] "Biochemical pathways: part I (WP3604) [2]"
[1] "These identifiers are not captured in the top 3 pathways: 18237, 57743, 15727, with the following database names: Glutamic acid, Citrulline, Carnosine"

2.8 Data Visuzalization

Preparation

Installing and loading packages

These packages need to be installed (and may take a while to run).

Connect to cytoscape

Open the Cytoscape Program first; this step was tested on version: 3.9.1, running on Java 11.0.11.

# Make sure to have Cytoscape started on your computer (>=3.9.1)
cytoscapePing ()
cytoscapeVersionInfo ()

Install WikiPathways apps

For the opening of pathway models and enhanced visualization steps, the WikiPathways app necessary (version 3.3.10).

if("WikiPathways" %in% commandsHelp("")) print("Success: the WikiPathways app is installed") else print("Warning: WikiPathways app is not installed. Please install the WikiPathways app before proceeding.")
if(!"WikiPathways" %in% commandsHelp("")){
  installApp("WikiPathways")
}

Open pathway from WikiPathways and Visualize patient data through log.Change

Pathway models can be imported from WikiPathways with the WikiPathways apps (as pathway or network). Enabling the visualization of biomarkers scattered over several pathways, requires finding the optimal combination of two (or three) individual pathways, adding the data to these pathways and merging the results.

Determine the scale for fill color, and convert data to dataframe

#Determine highest up/downregulated value:
highest <- max(annotated_both$log.Change)
lowest <- abs(min(annotated_both$log.Change))
if(highest < lowest){colorRange <- lowest} else{colorRange <- highest}

#Change data to dataframe before loading in Cytoscape:
finalData <- as.data.frame(annotated_both)

The 2log transformed data can now be visualized on the nodes in the form of a color gradient from blue(downregulated) to white (0) to red (upregulated), just as the reference data.

#Select highest relevant pathway ID (containing most biomarkers) based on previous step.
wp= commandsRun(paste0('wikipathways import-as-network id=', showresults_CombinePWs[first_selected_pathway,2])) #pick first PW to show data on.

## Linking metabolic data to ChEBI-ID column
loadTableData(finalData, data.key.column = "ID", table.key.column = "ChEBI")

#Set range of data values for visualisation:
data.values = c(-colorRange,-1,0,1,colorRange)
#display.brewer.all(length(data.values), colorblindFriendly=TRUE, type="div") # div,qual,seq,all
node.colors <- c(rev(brewer.pal(length(data.values), "RdBu")))

#Update WikiPathways pallet with Patient Data:
setNodeColorMapping("log.Change", data.values, node.colors, default.color = "#AAAAAA", style.name = "WikiPathways-As-Network")

#Download the Network as a PNG Figure, and store the figure in the Images folder.
imageURL_Name <- paste0("Images/patient_",patientID,"_network_First")
network1 <- exportImage(imageURL_Name,'PNG')
#Show the network as output in RMD file
filenameFirst<- paste0(imageURL_Name, '.png')
knitr::include_graphics(filenameFirst)

Selecting the second pathway of choice

#Select secondly highest ranked relevant PW (manual for now):
wp = commandsRun(paste0('wikipathways import-as-network id=', showresults_CombinePWs_Second[second_selected_pathway,2]))

loadTableData(finalData, data.key.column = "ID", table.key.column = "ChEBI")

#Update WikiPathways pallet with Patient Data:
setNodeColorMapping("log.Change", data.values, node.colors, default.color = "#AAAAAA", style.name = "WikiPathways-As-Network")

#Download the Network as a PNG Figure, and store the figure in the Images folder.
imageURL_Name <- paste0("Images/patient_",patientID,"_network_Second")
network2 <- exportImage(imageURL_Name,'PNG')
#Show the network as output in RMD file
filenameSecond <- paste0(imageURL_Name, '.png')
knitr::include_graphics(filenameSecond)

Selecting the third pathway of choice

if(third_selected_pathway < 1){
  paste("skip this step, no third pathway selected")}else{

#Select third highest ranked relevant PW (manual for now):
wp= commandsRun(paste0('wikipathways import-as-network id=', showresults_CombinePWs_Third[third_selected_pathway,2]))

loadTableData(finalData, data.key.column = "ID", table.key.column = "ChEBI")

#Update WikiPathways pallet with Patient Data:
setNodeColorMapping("log.Change", data.values, node.colors, default.color = "#AAAAAA", style.name = "WikiPathways-As-Network")

#Download the Network as a PNG Figure, and store the figure in the Images folder.
imageURL_Name <- paste0("Images/patient_",patientID,"_network_Third")
network3 <- exportImage(imageURL_Name,'PNG')

#Show the network as output in RMD file
filenameThird <- paste0(imageURL_Name, '.png')
knitr::include_graphics(filenameThird)

}

2.9 Data Interpretation

Finally: Print reporting details

###Print overview of information for each patient
print(paste("Selected Patient ID is: " , patientID, ", age is between: ", agerange, " old"))
remove(age)
print("Relevant biomarkers are:")
print(annotated_both_nozero[,c(1,3,4,8)], digits = 2)

#Print relevant biomarker information matched to pathways:
print(paste("There are", count_biomarkers ,"biomarkers relevant for patient" , patientID, ", the ChEBI-IDs are", string_CHEBI))
if(length(intersectingCHEBI) == 0 ){print("All Biomarkers are in a pathway!")} else{
  print(paste("These biomarkers (as ChEBI IDs) are not in a pathway:" , string_intersectingCHEBI))}

remove(listMissingBiomarkers, CHEBI_inPWs,supercleaned_CHEBI,string_intersectingCHEBI)

#Print table with first five relevant pathways
#print(showresults_CombinePWs[1:5,c(2,3,4,6)])

###Save relevant information for each patient in dataframe (for Tables in publication)
#table6 <- data.frame(matrix(ncol=4,nrow=0, dimnames=list(NULL, c("Primary.ID", "Secondary.ID", "Tertiary.ID", "Not.covered.biomarkers"))))
##Add patient data to dataframe (note: this overwrites existing patient entries, as opposed to using rbind)
#table6[patientID, ] <- c(showresults_CombinePWs[1,2], showresults_CombinePWs_Second[1,2], showresults_CombinePWs_Third[1,2], missing_IDs)
LS0tCnRpdGxlOiAiU3lzdGVtcyBCaW9sb2d5IGZyYW1ld29yayB1c2luZyBtZXRhYm9saWMgbWFya2VycyBhbmQgcGF0aHdheXMgdG8gc3VwcG9ydCB0aGUgZGlhZ25vc2lzIG9mIFB1cmluZSBhbmQgUHlyaW1pZGluZSBJbmhlcml0ZWQgTWV0YWJvbGljIERpc29yZGVycyIKb3V0cHV0OiBodG1sX25vdGVib29rCiMgQXV0aG9ycwpwZXJzb24oIkRlbmlzZSIsICJTbGVudGVyIiwgZW1haWwgPSAiZGVuaXNlLnNsZW50ZXJAbWFhc3RyaWNodHVuaXZlcnNpdHkubmwsIEdpdEh1YjogRGVuaXNlU2wyMiIsCiAgICAgICByb2xlID0gImF1dCIpCnBlcnNvbigiSXJlbmUiLCAiSGVtZWwiLCBlbWFpbCA9ICJpLmhlbWVsQG1hYXN0cmljaHR1bml2ZXJzaXR5Lm5sLCBHaXRIdWI6IElyZW5lSGVtZWwiLAogICAgICAgcm9sZSA9ICJjdGIiKQotLS0KCiMgMi4xIFdvcmtmbG93IEludHJvZHVjdGlvbgoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4KVGhlIG9ubGluZSB2ZXJzaW9uIGRpc3BsYXlzIHRoZSByZXN1bHRzIGZvciBvbmUgUGF0aWVudC4KWW91IGNhbiB2aXNpdCBvdXIgbWFpbiBwYWdlIGF0IFtiaWdjYXQtdW0uZ2l0aHViLmlvL0lNRC1QVVBZL10oaHR0cHM6Ly9iaWdjYXQtdW0uZ2l0aHViLmlvL0lNRC1QVVBZLykKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQppZighInJtYXJrZG93biIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikKfQpsaWJyYXJ5KHJtYXJrZG93bikKaWYoISJkcGx5ciIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQp9CmxpYnJhcnkoZHBseXIpCgojI1NldHRpbmcgd29ya2luZyBkaXJlY3RvcnkgdG8gbG9jYWwgZ2l0aHViIGZpbGUgZm9yIExpbnV4IChub3RlOiB5b3UgcHJvYmFibHkgbmVlZCB0byBjaGFuZ2UgdGhlIGZpbGUgcGF0aCwgaWYgeW91IHJ1biB0aGlzIHdvcmtmbG93IG9uIGEgV2luZG93cyBjb21wdXRlcik6CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKI3NldHdkKCJ+L2dpdC9JTUQiKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICJ+L2dpdC9JTUQtUFVQWSIpCgojI0NoZWNrIGlmIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBpcyB0aGUgdG9wIGZvbGRlciBvZiB0aGUgcmVwb3NpdG9yeSAoY2FsbGVkIElNRCkKZ2V0d2QoKQpgYGAKCiMgMi4yIENsaW5pY2FsIEJpb21hcmtlciBEYXRhCgojIyMgTG9hZGluZyBwYXRpZW50IHNwZWNpZmljIGRhdGEKCk5vdGU6IHRoaXMgc3RlcCByZXF1aXJlcyBpbnB1dCBmcm9tIHRoZSB1c2VyLCB0byBzZWxlY3QgZGF0YSBmcm9tIGFub3RoZXIgcGF0aWVudCBhcyB0aGUgZXhhbXBsZSAoUVRPRl9LVl8xODQpLgoKYGBge3J9CiMjIExvYWQgZGF0YSBmb3IgYWxsIHBhdGllbnRzCnBhdGllbnRkYXRhIDwtIHJlYWQuY3N2KCJEYXRhL0RhdGFfUHVQeU1TX1FUT0ZfS1ZfQklHQ0FULmNzdiIpCgojI1NlbGVjdCBQYXRpZW50IElEIGhlcmUgKG9wdGlvbnM6IEEgdGhyb3VnaCBUKQpwYXRpZW50SUQgPC0gIkkiICMjIHBhdGllbnRzIEIsIEMsIFAgYW5kIFEgZG8gbm90IGhhdmUgZGF0YSBvbiB0aGUgQUEgcGFuZWwKI2lmIHZhbHVlIGZvciBhZ2UgaXMgbm90IG51bWVyaWMgKGUuZy4gIm5ld2Jvcm4iLCBzYW1wbGU6IFFUT0ZfS1ZfMTEyLCBkb24ndCBwZXJmb3JtIGFzLm51bWVyaWMuKQppZihncmVwbCgiXltBLVphLXpdKyQiLCBwYXRpZW50ZGF0YVsxLHBhdGllbnRJRF0sIHBlcmwgPSBUKSl7CiAgcHJpbnQoIldhcm5pbmc6IE5vbi1udW1lcmljIGFnZSB2YWx1ZSBpcyBwYXJ0IG9mIGRhdGEiKQogIGlmKHBhdGllbnRkYXRhWzEscGF0aWVudElEXSA9PSAibmV3Ym9ybiIpewogICAgYWdlIDwtIGFzLm51bWVyaWMoMCkKICB9CiAgZWxzZXtwcmludCgiV2FybmluZzogbmFtZSBmb3IgYWdlIHZhbHVlIG5vdCByZWNvZ25pemVkIil9Cn1lbHNlewogICAgYWdlIDwtIGFzLm51bWVyaWMocGF0aWVudGRhdGFbMSxwYXRpZW50SURdKQogIH0KIyMgTm90ZSB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gdGhlIGFnZSBjYXRlZ29yaWVzIGJldHdlZW4gdGhlIHR3byBhc3NheXMKCiMjUFVQWTogKGFnZSBpbiB5ZWFycykKaWYoYWdlID49IDAgJiYgYWdlIDwgMTIpewogIHJlZl9hZ2VfUFVQWSA9IGMoNSw2KSAgI2FnZV8wdG8xeQogIGFnZXJhbmdlIDwtICIwIHRvIDEgeWVhcnMiCiAgfWVsc2UgaWYoYWdlID49IDEyICYmIGFnZSA8IDYwKXsKICByZWZfYWdlX1BVUFkgID0gYyg3LDgpICNhZ2VfMXRvNXkKICBhZ2VyYW5nZSA8LSAiMSB0byA1IHllYXJzIgogIH1lbHNlIGlmKGFnZSA+PSA2MCAmJiBhZ2UgPCAxOTIpewogIHJlZl9hZ2VfUFVQWSAgPSBjKDksMTApICNhZ2VfNXRvMTZ5CiAgYWdlcmFuZ2UgPC0gIjUgdG8gMTYgeWVhcnMiCiAgfWVsc2UgaWYoYWdlID49IDE5Mil7CiAgcmVmX2FnZV9QVVBZID0gYygxMSwxMikgI2FnZV8xNnlhbmRVcAogIGFnZXJhbmdlIDwtICIxNisgeWVhcnMiCiAgfWVsc2V7CiAgICByZWZfYWdlX1BVUFkgPSBjKDEzLDE0KSAjYWdlXzB0bzE2CiAgICBhZ2VyYW5nZSA8LSAiMCB0byAxNiB5ZWFycyIKICAgIH0KCiMjVXJlYTogKGFnZSBpbiBtb250aHMgPSBtLCBpbiB5ZWFycyA9IHkpCmlmKGFnZSA+PSAwICYmIGFnZSA8IDEpewogIHJlZl9hZ2VfdXJlYSA9IGMoNCw1KSAjYWdlXzB0bzFtCiAgfWVsc2UgaWYoYWdlID49IDEgJiYgYWdlIDwgNil7CiAgcmVmX2FnZV91cmVhID0gYyg2LDcpICNhZ2VfMXRvNm0KICB9ZWxzZSBpZihhZ2UgPj0gNiAmJiBhZ2UgPCAxMil7CiAgcmVmX2FnZV91cmVhID0gYyg4LDkpICNhZ2VfNm10bzF5CiAgfWVsc2UgaWYoYWdlID49IDEyICYmIGFnZSA8IDI0KXsKICByZWZfYWdlX3VyZWEgPSBjKDEwLDExKSAjYWdlXzF0bzJ5CiAgfWVsc2UgaWYoYWdlID49IDI0ICYmIGFnZSA8IDQ4KXsKICByZWZfYWdlX3VyZWEgPSBjKDEyLDEzKSAjYWdlXzItNHkKICB9ZWxzZSBpZihhZ2UgPj0gNDggJiYgYWdlIDwgMTIwKXsKICByZWZfYWdlX3VyZWEgPSBjKDE0LDE1KSAjYWdlXzQtMTB5CiAgfWVsc2UgaWYoYWdlID49IDEyMCAmJiBhZ2UgPCAyMTYpewogIHJlZl9hZ2VfdXJlYSA9IGMoMTYsMTcpICNhZ2VfMTAtMTh5CiAgfWVsc2V7CiAgICByZWZfYWdlX3VyZWEgPSBjKDE4LDE5KSAjYWdlXzE4eWFuZFVwCiAgICB9CgpwcmludChwYXN0ZSgiU2VsZWN0ZWQgUGF0aWVudCBJRCBpczogIiAsIHBhdGllbnRJRCwgIiwgYWdlIGlzIGJldHdlZW46ICIsIGFnZXJhbmdlLCAiIG9sZCIpKQoKYGBgCgojIyMgQ29tYmluZSBSZWZlcmVuY2UgKyBQYXRpZW50IGRhdGEgZm9yIGVhY2ggYXNzYXkgc2VwZXJhdGx5CgpgYGB7cn0KI0xvYWQgcmVmZXJlbmNlIGRhdGEgKGluY2x1ZGVzIGFubm90YXRpb25zIGZvciBiaW9tYXJrZXJzKQpyZWZ2YWx1ZXNfUFVQWSA8LSByZWFkLmRlbGltKCJEYXRhL1JlZmVyZW5jZV92YWx1ZXNfUFVQWV9ub05Bcy50eHQiKQpyZWZ2YWx1ZXNfdXJlYSA8LSByZWFkLmRlbGltKCJEYXRhL1JlZmVyZW5jZV92YWx1ZXNfVXJlYS50eHQiKQoKIyBTZWxlY3QgY29sdW1uIGJhc2VkIG9uIHBhdGllbnRJRDoKcGF0aWVudC5jb2x1bW4gPC0gYXMuaW50ZWdlcihtYXRjaChwYXRpZW50SUQsbmFtZXMocGF0aWVudGRhdGEpKSkKCiMjU2VsZWN0IFJlZmVyZW5jZWRhdGEgZm9yIG9uZSBwYXRpZW50IApwYXRpZW50SURfUFVQWSA8LSBhcy5kYXRhLmZyYW1lKHBhdGllbnRkYXRhWzQ6MzcsIGMoMSxwYXRpZW50LmNvbHVtbildLCAgZHJvcD1mYWxzZSkgI1NlbGVjdGluZyBkYXRhIGZvciBQVVBZIG9ubHkKcGF0aWVudElEX3VyZWEgPC0gYXMuZGF0YS5mcmFtZShwYXRpZW50ZGF0YVs0Mjo5NSwgYygxLHBhdGllbnQuY29sdW1uKV0sICBkcm9wPWZhbHNlKSAjU2VsZWN0aW5nIGRhdGEgZm9yIFVyZWEgQWNpZHMgKGluY2x1ZGluZyBBbWlubyBBY2lkcykgb25seQpwYXRpZW50SURfdHJlYXRtZW50IDwtIGFzLmRhdGEuZnJhbWUocGF0aWVudGRhdGFbOTcsIGMoMSxwYXRpZW50LmNvbHVtbildLCAgZHJvcD1mYWxzZSkgI1NlbGVjdGluZyBkYXRhIGZvciBVcmVhIEFjaWRzIChpbmNsdWRpbmcgQW1pbm8gQWNpZHMpIG9ubHkKCiMjIENob29zZSBjb3JyZWN0IHJlZmVyZW5jZSB2YWx1ZSBjb2x1bW4gZm9yIGZ1cnRoZXIgZGF0YSB2aXN1YWxpemF0aW9uCmFnZV9wYXRpZW50SURfUFVQWSA8LSBhcy5kYXRhLmZyYW1lKHJlZnZhbHVlc19QVVBZWyAsIGMoMTozLCByZWZfYWdlX1BVUFlbMV0sIHJlZl9hZ2VfUFVQWVsyXSwgMTUgKV0sICBkcm9wPWZhbHNlKSAjU2VsZWN0aW5nIHJlZmVyZW5jZSBkYXRhIGZvciBQVVBZLCBhZ2U6IDAgdG8gMSB5ZWFyCnJlbW92ZShyZWZ2YWx1ZXNfUFVQWSxyZWZfYWdlX1BVUFkpCmFnZV9wYXRpZW50SURfdXJlYSA8LSBhcy5kYXRhLmZyYW1lKHJlZnZhbHVlc191cmVhWyAsIGMoMTozLCByZWZfYWdlX3VyZWFbMV0sIHJlZl9hZ2VfdXJlYVsyXSwgMjAgKV0sICBkcm9wPWZhbHNlKSAjU2VsZWN0aW5nIHJlZmVyZW5jZSBkYXRhIGZvciBVcmVhLCBhZ2U6IDAgdG8gMSBtb250aApyZW1vdmUocmVmdmFsdWVzX3VyZWEscmVmX2FnZV91cmVhKQpyZW1vdmUocGF0aWVudC5jb2x1bW4pCgpwcmludChwYXN0ZSgiUmVsZXZhbnQgYWdlIGRhdGEgaXMgc2VsZWN0ZWQgZm9yIHBhdGllbnQ6ICIgLCBwYXRpZW50SUQpKQoKYGBgCgojIDIuMyBQYXRod2F5IE1vZGVscwoKUmV0cmlldmUgTWV0YWJvbGl0ZSwgUHJvdGVpbiBhbmQgRGlzZWFzZSBEYXRhIGZyb20gUGF0aHdheSBNb2RlbHMKCmBgYHtyfQoKaWYoISJTUEFSUUwiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogIGluc3RhbGwucGFja2FnZXMoIlNQQVJRTCIpCn0KbGlicmFyeShTUEFSUUwpCgojI0Nvbm5lY3QgdG8gRW5kcG9pbnQgV2lraVBhdGh3YXlzCmVuZHBvaW50d3AgPC0gImh0dHBzOi8vc3BhcnFsLndpa2lwYXRod2F5cy5vcmcvc3BhcnFsIgoKIyMgMS4gUXVlcnkgbWV0YWRhdGE6CgpxdWVyeU1ldGFkYXRhIDwtCiJTRUxFQ1QgRElTVElOQ1QgP2RhdGFzZXQgKHN0cig/dGl0bGVMaXQpIGFzID90aXRsZSkgP2RhdGUgP2xpY2Vuc2UgCldIRVJFIHsKICAgP2RhdGFzZXQgYSB2b2lkOkRhdGFzZXQgOwogICBkY3Rlcm1zOnRpdGxlID90aXRsZUxpdCA7CiAgIGRjdGVybXM6bGljZW5zZSA/bGljZW5zZSA7CiAgIHBhdjpjcmVhdGVkT24gP2RhdGUgLgogfSIKCnJlc3VsdHNNZXRhZGF0YSA8LSBTUEFSUUwoZW5kcG9pbnR3cCxxdWVyeU1ldGFkYXRhLGN1cmxfYXJncz1saXN0KHVzZXJhZ2VudD1SLnZlcnNpb24uc3RyaW5nKSkKc2hvd3Jlc3VsdHNNZXRhZGF0YSA8LSByZXN1bHRzTWV0YWRhdGEkcmVzdWx0cwpyZW1vdmUocXVlcnlNZXRhZGF0YSwgcmVzdWx0c01ldGFkYXRhKQoKIyMgMi4gUXVlcnkgUGF0aHdheSBkYXRhOgoKIyNQYXRod2F5IE1vZGVscyBJRHMgb2YgaW50ZXJlc3Q6CnBhdGh3YXlzX29mX0ludGVyZXN0IDwtIHBhc3RlKCciV1A0MjI0IicsICciV1A0MjI1IicsICciV1A0NTcxIicsICciV1A0NTk1IicsICciV1A0NTgzIicsICciV1A0NTg0IicpCgpxdWVyeUFsbENvbnRlbnRfMSA8LQoiClBSRUZJWCB3cDogICAgICA8aHR0cDovL3ZvY2FidWxhcmllcy53aWtpcGF0aHdheXMub3JnL3dwIz4KUFJFRklYIHJkZnM6ICAgIDxodHRwOi8vd3d3LnczLm9yZy8yMDAwLzAxL3JkZi1zY2hlbWEjPgpQUkVGSVggZGN0ZXJtczogPGh0dHA6Ly9wdXJsLm9yZy9kYy90ZXJtcy8+CgojVmFyaWFibGUgc2VsZWN0aW9uClNFTEVDVCBESVNUSU5DVCAoc3RyKD90aXRsZSkgYXMgP3BhdGh3YXlOYW1lKSA/UFdJRCAKKGNvdW50KGRpc3RpbmN0ID9nZW5lUHJvZHVjdCkgQVMgP0dlbmVzSW5QV3MpCihjb3VudChkaXN0aW5jdCA/cHJvdGVpbikgQVMgP1Byb3RlaW5zSW5QV3MpIAooY291bnQoZGlzdGluY3QgP21ldGFib2xpdGVOb2RlKSBBUyA/TWV0YWJvbGl0ZXNJblBXcykgCihjb3VudChkaXN0aW5jdCA/aW50ZXJhY3Rpb25JRCkgQVMgP1JoZWFJblBXcykgCihjb3VudChkaXN0aW5jdCA/aW50ZXJhY3Rpb25NaXNzaW5nKSBBUyA/Tm9SaGVhSW5QV3MpCihjb3VudChkaXN0aW5jdCA/b21pbSkgYXMgP2Rpc2Vhc2VJRHMpCgpXSEVSRSB7CiAgCiAgVkFMVUVTID9QV0lEIHsKIgoKcXVlcnlBbGxDb250ZW50XzIgPC0KIgp9CiAgICA/cGF0aHdheSBkY3Rlcm1zOmlkZW50aWZpZXIgP1BXSUQuICNPYnRhaW4gdGhlIElECiAgICAjP3BhdGh3YXkgd3A6b250b2xvZ3lUYWcgY3VyOklFTSAuCiAgICA/cGF0aHdheSB3cDppc0Fib3V0ID9ncG1sUkRGX0lEIC4gI2ZpbmQgdGhlIGNvcnJlc3BvbmRpbmcgR1BNTCBsaW5rICAgICAKICAgID9wYXRod2F5IGRjOnRpdGxlID90aXRsZSAuICNPYnRhaW4gdGhlIHRpdGxlICAgCgogICAgICB7CiAgICA/Z2VuZVByb2R1Y3QgZGN0ZXJtczppc1BhcnRPZiA/cGF0aHdheSAuICNPbmx5IHRob3NlIHBhcnQgb2YgUFcgICAgICAgICAgICAgCiAgICA/Z2VuZVByb2R1Y3QgYSB3cDpHZW5lUHJvZHVjdCAuICNGaWx0ZXIgZm9yIFByb3RlaW4gRGF0YU5vZGVzICAgIAogICAgfSAKICAgIFVOSU9OCiAgICB7CiAgICA/cHJvdGVpbiBkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5IC4gI09ubHkgdGhvc2UgcGFydCBvZiBQVyAgICAgICAgICAgICAKICAgID9wcm90ZWluIGEgd3A6UHJvdGVpbiAuICNGaWx0ZXIgZm9yIFByb3RlaW4gRGF0YU5vZGVzICAgIAogICAgfSAKICAgIFVOSU9OCiAgICB7IAogICAgP21ldGFib2xpdGVOb2RlIGEgd3A6TWV0YWJvbGl0ZSAuICNGaWx0ZXIgZm9yIE1ldGFib2xpdGUgRGF0YU5vZGVzCiAgICA/bWV0YWJvbGl0ZU5vZGUgZGN0ZXJtczppc1BhcnRPZiA/cGF0aHdheSAuICNPbmx5IHRob3NlIHBhcnQgb2YgUFcKICAgIH0KICAgIFVOSU9OIAogICAgeyAKICAgIE9QVElPTkFMez9pbnRlcmFjdGlvbiB3cDpiZGJSaGVhID9pbnRlcmFjdGlvbklEIC4gI0ZpbmQgaW50ZXJhY3Rpb25zIHdpdGggUmhlYQogICAgP2ludGVyYWN0aW9uIGRjdGVybXM6aXNQYXJ0T2YgP3BhdGh3YXkgLn0gI09ubHkgdGhvc2UgcGFydCBvZiBQVwogICAgfQogICAgVU5JT04KICAgIHsKICAgIE9QVElPTkFMez9pbnRlcmFjdGlvbk1pc3NpbmcgZGN0ZXJtczppc1BhcnRPZiA/cGF0aHdheSAuICNBZGRpdGlvbmFsIGludGVyYWN0aW9ucwogICAgP2ludGVyYWN0aW9uTWlzc2luZyByZGY6dHlwZSB3cDpDb252ZXJzaW9uIC4gI1R5cGUgJ21ldGFib2xpYyBjb252ZXJzaW9uJwogICAgRklMVEVSIE5PVCBFWElTVFMgez9pbnRlcmFjdGlvbk1pc3Npbmcgd3A6YmRiUmhlYSA/aW50ZXJhY3Rpb25JRCAuIH0jTm8gUmhlYSAgIAogICAgfSAgCiAgICB9CiAgICBVTklPTiB7CiAgICA/ZGlzZWFzZU5vZGUgZGN0ZXJtczppc1BhcnRPZiA/Z3BtbFJERl9JRCAuICNPbmx5IGNoZWNrIGZvciBtYXRjaGluZyBwYXRod2F5cyAKICAgID9kaXNlYXNlTm9kZSByZGY6dHlwZSBncG1sOkxhYmVsIC4gI09ubHkgaW5jbHVkZSB0ZXh0TGFiZWxzCiAgICA/ZGlzZWFzZU5vZGUgZ3BtbDpocmVmID9vbWltIC4gI1RoYXQgaGF2ZSBhbiBocmVmIGF0dHJpYnV0ZSAgCiAgICBGSUxURVIgcmVnZXgoP29taW0sIFwib21pbS5vcmdcIiwgXCJpXCIpICNPbmx5IGtlZXAgaHJlZnMgaWYgdGhleSBjb250YWluICdvbWltLm9yZycKICAgIH0KICAgICAgCn0gT1JERVIgQlkgQVNDKD9wYXRod2F5KQoiCnF1ZXJ5QWxsQ29udGVudCA8LSBwYXN0ZShxdWVyeUFsbENvbnRlbnRfMSxwYXRod2F5c19vZl9JbnRlcmVzdCxxdWVyeUFsbENvbnRlbnRfMikKcmVzdWx0c0FsbENvbnRlbnQgPC0gU1BBUlFMKGVuZHBvaW50d3AscXVlcnlBbGxDb250ZW50LGN1cmxfYXJncz1saXN0KHVzZXJhZ2VudD1SLnZlcnNpb24uc3RyaW5nKSkKc2hvd3Jlc3VsdHNBbGxDb250ZW50IDwtIHJlc3VsdHNBbGxDb250ZW50JHJlc3VsdHMKCnJlbW92ZShxdWVyeUFsbENvbnRlbnQsIHJlc3VsdHNBbGxDb250ZW50KQoKI1ByaW50IHJlc3VsdHMgVGFibGUKcHJpbnQoc2hvd3Jlc3VsdHNNZXRhZGF0YSkKcHJpbnQoc2hvd3Jlc3VsdHNBbGxDb250ZW50KQoKcmVtb3ZlKHBhdGh3YXlzX29mX0ludGVyZXN0LCBxdWVyeUFsbENvbnRlbnRfMSwgcXVlcnlBbGxDb250ZW50XzIpCmBgYAoKIyAyLjQgU2VsZWN0aW9uIG9mIFJlbGV2YW50IEJpb21hcmtlcnMKCkZpcnN0LCB0aGUgUGF0aWVudCBkYXRhIGlzIGNvbm5lY3RlZCB0byB0aGUgcmVmZXJlbmNlIGRhdGEgKGluY2x1ZGluZyBpZGVudGlmaWVyIG1hcHBpbmcpLiBTZWNvbmQsIHRoZSBhbm5vdGF0ZWQgZGF0YSBpcyBjaGVja2VkIGZvciBtaXNzaW5nIHZhbHVlcyAoTkFzKSBhbmQgdW5pZmljYXRpb24gdG8gQ2hFQkkuCmBgYHtyfQojIyBGaXJzdCwgbWF0Y2ggdGhlIG5hbWVzIG9mIGFsbCBiaW9tYXJrZXJzIHRvIGFuIGlkZW50aWZpZXIgKGZyb20gcmVmZXJlbmNlIHZhbHVlIGRhdGEsIHRoZXNlIG1hcHBpbmdzIGhhdmUgYmVlbiBjaGVja2VkIG1hbnVhbGx5KQpsaWJyYXJ5KGRwbHlyKSAjbmVlZGVkIGZvciBsZWZ0X2pvaW4oKSBvcGVyYXRpb24KI05vdGU6IHRoZSBtYXRjaGluZyBiZWxvdyBpcyBjYXNlIHNlbnNpdGl2ZSAobGVmdF9qb2luIGZyb20gZHBseXIgZG9lc24ndCBoYXZlIGFuIG9wdGlvbiBmb3IgY2FzZSBpbnNlbnNpdGl2ZSBtYXRjaGluZykKYW5ub3RhdGVkX1BVUFkgPC0KbGVmdF9qb2luKHBhdGllbnRJRF9QVVBZLCBhZ2VfcGF0aWVudElEX1BVUFksIGJ5PWMoIkR1dGNoLm5hbWUiKSkgJT4lCiAgcm93d2lzZSgpICAKICBjb2xuYW1lcyhhbm5vdGF0ZWRfUFVQWSlbNV0gPC0gImxvdy5SZWYiCiAgY29sbmFtZXMoYW5ub3RhdGVkX1BVUFkpWzZdIDwtICJoaWdoLlJlZiIKCmFubm90YXRlZF91cmVhIDwtCmxlZnRfam9pbihwYXRpZW50SURfdXJlYSwgYWdlX3BhdGllbnRJRF91cmVhLCBieT1jKCJEdXRjaC5uYW1lIikpICU+JQogIHJvd3dpc2UoKSAgCiAgY29sbmFtZXMoYW5ub3RhdGVkX3VyZWEpWzVdIDwtICJsb3cuUmVmIgogIGNvbG5hbWVzKGFubm90YXRlZF91cmVhKVs2XSA8LSAiaGlnaC5SZWYiCgphbm5vdGF0ZWRfYm90aCA8LSByYmluZChhbm5vdGF0ZWRfUFVQWSwgYW5ub3RhdGVkX3VyZWEpICNDb21iaW5pbmcgZGF0YSBmb3IgUFVQWSBhbmQgdXJlYSBiaW9tYXJrZXJzIGluIG9uZSBkYXRhZnJhbWUKCiNSZW1vdmUgcm93cyB3aGljaCBhcmUgZm9yIHRyZWF0bWVudCB2YWx1ZXMgKE94eXB1cmlub2wgYW5kIGFsbG9wdXJpbm9sKSBvciBub3QgbWVhc3VyZWQgYnkgYXNzYXlzIGFueW1vcmUoQXJnaW5pbm9zdWNjaW5pYyBhY2lkIGFuaHlkcmlkZSkgYnkgbmFtZSBvZiBjb21wb3VuZAphbm5vdGF0ZWRfYm90aCA8LSBhbm5vdGF0ZWRfYm90aCBbKCEoYW5ub3RhdGVkX2JvdGgkRHV0Y2gubmFtZT09Im94eXB1cmlub2wiKSAmICEoYW5ub3RhdGVkX2JvdGgkRHV0Y2gubmFtZT09ImFsbG9wdXJpbm9sIikgJiAhKGFubm90YXRlZF9ib3RoJER1dGNoLm5hbWU9PSJBU0FfYW5oeWRyaWRlIikpLF0KCk1pc3NpbmdNYXBwaW5ncyA8LSBsaXN0KCkKY291bnRlciA9IDEKZm9yIChpIGluIDE6bnJvdyhhbm5vdGF0ZWRfYm90aCkpewogIGlmKGFubm90YXRlZF9ib3RoW2ksNF0gIT0gIiIpe25leHR9CiAgZWxzZXsKICAgIE1pc3NpbmdNYXBwaW5nc1tjb3VudGVyXSA8LSAoYW5ub3RhdGVkX2JvdGhbaSwxXSkKICAgIGNvdW50ZXIgPC0gY291bnRlciArIDEKICAgfQp9CnJlbW92ZShpLGNvdW50ZXIpCgpDSEVCSU1pc3NpbmcgPC0gbGlzdCgpCmNvdW50ZXIgPSAxCmZvciAoaSBpbiAxOm5yb3coYW5ub3RhdGVkX2JvdGgpKXsKICBpZighZ3JlcGwoJ0NIRUJJJywgYW5ub3RhdGVkX2JvdGhbaSw0XSkgJiYgYW5ub3RhdGVkX2JvdGhbaSw0XSAhPSAiIil7CiAgICBDSEVCSU1pc3NpbmdbY291bnRlcl0gPC0gKGFubm90YXRlZF9ib3RoW2ksMV0pCiAgICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxCiAgICB9CiAgZWxzZXtuZXh0fQp9CnJlbW92ZShpLGNvdW50ZXIpCnJlbW92ZShwYXRpZW50SURfUFVQWSxwYXRpZW50SURfdXJlYSxhbm5vdGF0ZWRfUFVQWSxhbm5vdGF0ZWRfdXJlYSxhZ2VfcGF0aWVudElEX1BVUFksYWdlX3BhdGllbnRJRF91cmVhKQpwcmludChwYXN0ZSgiVGhlc2UgYmlvbWFya2VycyBkbyBub3QgaGF2ZSBhbiBpZGVudGZpZXIgbWFwcGluZzoiICwgcGFzdGUoYyhNaXNzaW5nTWFwcGluZ3MpLCBjb2xsYXBzZT0nLCAnICkpKQpwcmludChwYXN0ZSgiVGhlc2UgYmlvbWFya2VycyBhcmUgbm90IGFubm90YXRlZCB3aXRoIGEgQ2hFQkkgSUQ6IiAsIHBhc3RlKGMoQ0hFQklNaXNzaW5nKSwgY29sbGFwc2U9JywgJyApKSkKICAKYGBgCgpTZWNvbmQsIHF1ZXJ5IHdoaWNoIGJpb21hcmtlcnMgbWVhc3VyZWQgaW4gdGhlIGFzc2F5cyBhcmUgbm90IGluIGFueSBwYXRod2F5IG1vZGVsOgpgYGB7cn0KYWxsQmlvbWFya2VycyA8LSBhbm5vdGF0ZWRfYm90aCRJRCAjI1Rha2UgdGhlIElEIGNvbHVtbiBmb3IgYWxsIGRhdGEKYWxsQmlvbWFya2VycyA8LSBhbGxCaW9tYXJrZXJzW2dyZXBsKCJeQ0hFQkk6IiwgYWxsQmlvbWFya2VycyldICNyZW1vdmUgSXMgbm90IHN0YXJ0aW5nIHdpdGggJ0NIRUJJOicgZm9yIGNvbnNpc3RlbmN5CmxpYnJhcnkoc3RyaW5ncikKY2xlYW5lZF9DSEVCSV9hbGwgPC0gc3RyX3JlcGxhY2UoYWxsQmlvbWFya2VycywgIkNIRUJJOiIsICJjaDoiKSAjdXBkYXRlIElEcyB0byB3b3JrIHdpdGggU1BBUlFMIHF1ZXJ5CiNzdXBlcmNsZWFuZWRfQ0hFQkkgPC0gc3RyX3JlcGxhY2UodmVjdG9yX0NIRUJJLCAiQ0hFQkk6IiwgIiIpICN1cGRhdGUgSURzIHRvIHdvcmsgd2l0aCBTUEFSUUwgcXVlcnkKc3RyaW5nX0NIRUJJX2FsbCA8LSBwYXN0ZShjKGNsZWFuZWRfQ0hFQklfYWxsKSwgY29sbGFwc2U9JyAnICkKcmVtb3ZlIChjbGVhbmVkX0NIRUJJX2FsbCkKCml0ZW0xID0gIlBSRUZJWCBjaDo8aHR0cHM6Ly9pZGVudGlmaWVycy5vcmcvY2hlYmkvQ0hFQkk6PgpTRUxFQ1QgRElTVElOQ1QgP2NoZWJpTWV0YWJvbGl0ZSBXSEVSRSB7CiAgVkFMVUVTID9jaGViaU1ldGFib2xpdGUgeyIKaXRlbTIgPSAifQogID9wYXRod2F5UmVzICBhIHdwOlBhdGh3YXkgOwogICAgICAgICAgICAgCXdwOm9yZ2FuaXNtTmFtZSBcJ0hvbW8gc2FwaWVuc1wnIC4KICAKICA/bWV0YWJvbGl0ZSAJYSB3cDpNZXRhYm9saXRlIDsKICAgICAgICAgICAgICAgIGRjdGVybXM6aWRlbnRpZmllciA/aWQgOwogICAgICAgICAgICAgICAgZGN0ZXJtczppc1BhcnRPZiA/cGF0aHdheVJlcyAuCgogID9tZXRhYm9saXRlIHdwOmJkYkNoRUJJID9jaGViaU1ldGFib2xpdGUuCn0iCgpxdWVyeU1pc3NpbmdCaW9tYXJrZXJzX2FsbCA8LSBwYXN0ZShpdGVtMSxzdHJpbmdfQ0hFQklfYWxsLGl0ZW0yKQpyZW1vdmUoaXRlbTEsaXRlbTIpCgpyZXN1bHRzTWlzc2luZ0Jpb21hcmtlcnNfYWxsIDwtIFNQQVJRTChlbmRwb2ludHdwLHF1ZXJ5TWlzc2luZ0Jpb21hcmtlcnNfYWxsLGN1cmxfYXJncz1saXN0KHVzZXJhZ2VudD1SLnZlcnNpb24uc3RyaW5nKSkKbGlzdE1pc3NpbmdCaW9tYXJrZXJzX2FsbCA8LSBjKHJlc3VsdHNNaXNzaW5nQmlvbWFya2Vyc19hbGwkcmVzdWx0cykgI3NhZmUgcmVzdWx0cyBhcyBsaXN0IGZvciBjb21wYXJpc29uLgpyZW1vdmUocXVlcnlNaXNzaW5nQmlvbWFya2Vyc19hbGwscmVzdWx0c01pc3NpbmdCaW9tYXJrZXJzX2FsbCkKCkNIRUJJX2luUFdzX2FsbCA8LSBnc3ViKCJbPGh0dHBzOi8vaWRlbnRpZmllcnMub3JnL2NoZWJpL0NIRUJJOj5dIiwgIiIsIGxpc3RNaXNzaW5nQmlvbWFya2Vyc19hbGwpICNDaEVCSSBJRHMgUmVzdWx0cyBJUkkgY2xlYW51cApDSEVCSV9pblF1ZXJ5X2FsbCA8LSBnc3ViKCJDSEVCSToiLCAiIiwgYWxsQmlvbWFya2VycykgI0NoRUJJIElEcyBRdWVyeSBjbGVhbnVwCmludGVyc2VjdGluZ0NIRUJJX2FsbCA8LSBzZXRkaWZmKENIRUJJX2luUXVlcnlfYWxsLENIRUJJX2luUFdzX2FsbCkKc3RyaW5nX2ludGVyc2VjdGluZ0NIRUJJX2FsbCA8LSBwYXN0ZShjKGludGVyc2VjdGluZ0NIRUJJX2FsbCksIGNvbGxhcHNlPScsICcgKQojY291bnRfYmlvbWFya2VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoc3RyaW5nX0NIRUJJLCAiICIpLCBsZW5ndGgpCgojRmluZCBuYW1lcyBmb3IgbWlzc2luZyBCaW9tYXJrZXJzIGJhc2VkIG9uIENoRUJJIElEICh0byBoZWxwIHdpdGggZGF0YSBjdXJhdGlvbikKQ0hFQklfaW50ZXJzZWN0aW5nQ0hFQklfYWxsIDwtIHBhc3RlMCgiQ0hFQkk6IiwgaW50ZXJzZWN0aW5nQ0hFQklfYWxsKSAjdXBkYXRlIElEcyB0byBtYXRjaCBiYWNrIHRvIGRhdGEgYWdhaW4KbWlzc2luZ05hbWVzX2FsbCA8LSBsaXN0KCkKZm9yIChqIGluIDE6bGVuZ3RoKENIRUJJX2ludGVyc2VjdGluZ0NIRUJJX2FsbCkpewogIGZvciAoaSBpbiAxOm5yb3coYW5ub3RhdGVkX2JvdGgpKXsKICAgIGlmKGFubm90YXRlZF9ib3RoW2ksNF0gPT0gQ0hFQklfaW50ZXJzZWN0aW5nQ0hFQklfYWxsW2pdKXsKICAgICAgIG1pc3NpbmdOYW1lc19hbGxbal0gPC0gYW5ub3RhdGVkX2JvdGhbaSwzXQogICAgICB9CiAgICBlbHNle25leHR9CiAgfQp9CnJlbW92ZShpLGopCiNTYXZlIGxpc3Qgb24gb25lIHN0cmluZyBmb3IgcmVwb3J0aW5nIHB1cnBvc2VzCnN0cmluZ19taXNzaW5nTmFtZXNfYWxsIDwtIGRvLmNhbGwocGFzdGUsIGMoYXMubGlzdChtaXNzaW5nTmFtZXNfYWxsKSwgc2VwID0gIiwgIikpCgojUHJpbnQgcmVsZXZhbnQgaW5mb3JtYXRpb246CmlmKGxlbmd0aChpbnRlcnNlY3RpbmdDSEVCSV9hbGwpID09IDAgKXtwcmludCgiQWxsIGJpb21hcmtlcnMgb24gdGhlIGFzc2F5KHMpIGFyZSBpbiBhIHBhdGh3YXkhIil9IGVsc2V7CiAgcHJpbnQocGFzdGUwKCJUaGVzZSBiaW9tYXJrZXJzIChhcyBDaEVCSSBJRHMpIGFyZSBub3QgaW4gYW55IHBhdGh3YXk6ICIgLCBzdHJpbmdfaW50ZXJzZWN0aW5nQ0hFQklfYWxsLCAiOyB3aXRoIHRoZSBmb2xsb3dpbmcgRGF0YWJhc2UgbmFtZXM6ICIsIHN0cmluZ19taXNzaW5nTmFtZXNfYWxsKSl9CgpyZW1vdmUoYWxsQmlvbWFya2VycywgQ0hFQklfaW5QV3NfYWxsLCBDSEVCSV9pblF1ZXJ5X2FsbCwgQ0hFQklfaW50ZXJzZWN0aW5nQ0hFQklfYWxsLCBpbnRlcnNlY3RpbmdDSEVCSV9hbGwsIHN0cmluZ19DSEVCSV9hbGwsIHN0cmluZ19pbnRlcnNlY3RpbmdDSEVCSV9hbGwsIHN0cmluZ19taXNzaW5nTmFtZXNfYWxsLCBtaXNzaW5nTmFtZXNfYWxsLCBDSEVCSU1pc3NpbmcpCmBgYAoKVGhpcmQsIGNvbXBhcmluZyBwYXRpZW50IGRhdGEgdG8gcmVmZXJlbmNlIGRhdGEgdG8gY2FsY3VsYXRlIHRoZSBsb2coMikgY2hhbmdlIHZhbHVlIGFuZCBkYXRhIGNsZWFudXAuCmBgYHtyfQojIEZpcnN0LCBjb252ZXJ0IHRoZSBQYXRpZW50IERhdGEgY29sdW1uIHRvIGEgdHlwZSAiZG91YmxlIiBpc28gImNoYXJhY3RlciIgKGVtcHR5IHZhbHVlcyB3aWxsIGJlY29tZSAiTkEiKQpuYW1lcyhhbm5vdGF0ZWRfYm90aClbbmFtZXMoYW5ub3RhdGVkX2JvdGgpID09IHBhdGllbnRJRF0gPC0gImkucGF0aWVudERhdGEiIAphbm5vdGF0ZWRfYm90aCRpLnBhdGllbnREYXRhIDwtIGFzLmludGVnZXIoYW5ub3RhdGVkX2JvdGgkaS5wYXRpZW50RGF0YSkKCiMgU2Vjb25kLCBjb21wYXJlIHRoZSBQYXRpZW50IERhdGEgdG8gdGhlIHJlZmVyZW5jZSBWYWx1ZXMgKGlmIGF2YWlsYWJsZSksIGFuZCBjYWxjdWxhdGUgYW4gYWRkaXRpb25hbCBjb2x1bW4gY2FsbGVkICJDaGFuZ2UiLgphbm5vdGF0ZWRfYm90aCA8LSBhbm5vdGF0ZWRfYm90aCU+JW11dGF0ZShDaGFuZ2UgPSBjYXNlX3doZW4oCiAgaS5wYXRpZW50RGF0YSA9PSAwIH4gTkFfcmVhbF8sICAgICAgICAgICAgICAgICAgICAjIFBhdGllbnQgZGF0YSBpcyBlcXVhbCB0byB6ZXJvCiAgaGlnaC5SZWYgPT0gMCB+IE5BX3JlYWxfLCAgICAgICAgICAgICAgICAgICAgIyBJZiBoaWdoUmVmIGlzIGVxdWFsIHRvIHplcm8gKHRvIGF2b2lkIGRpdmlkaW5nIGJ5IHplcm8pCiAgaXMubmEoaS5wYXRpZW50RGF0YSkgfiBOQV9yZWFsXywgICAgICAgICAgICAgICAgICAjIE5vIHBhdGllbnQgZGF0YSBhdmFpbGFibGUKICBpcy5uYShoaWdoLlJlZikgJiBpcy5uYShsb3cuUmVmKSAmIGkucGF0aWVudERhdGEgIT0gMCB+IE5BX3JlYWxfLCAgICMgSWYgbm8gaGlnaFJlZiBhbmQgbG93UmVmIHZhbHVlIGlzIGF2YWlsYWJsZSAoTkEpLCBhbmQgdGhlcmUgaXMgUGF0aWVudERhdGEsIGNoYW5nZSBpcyBOQSAodW5hYmxlIHRvIGRldGVybWluZSkKIGkucGF0aWVudERhdGEgPj0gbG93LlJlZiAmIGkucGF0aWVudERhdGEgPD0gaGlnaC5SZWYgfiAxLCAgI1BhdGllbnQgZGF0YSBiZXR3ZWVuIHJlZmVyZW5jZSB2YWx1ZXMKIGkucGF0aWVudERhdGEgPCBsb3cuUmVmIH4gaS5wYXRpZW50RGF0YS9sb3cuUmVmLCAjUGF0aWVudCBkYXRhIGxvd2VyIHRoYW4gbG93UmVmIHZhbHVlIChkb3ducmVndWxhdGVkKSAKIGkucGF0aWVudERhdGEgPiBoaWdoLlJlZiB+IGkucGF0aWVudERhdGEvaGlnaC5SZWYgICAjUGF0aWVudCBkYXRhIGhpZ2hlciB0aGFuIGhpZ2hSZWYgdmFsdWUgKHVwcmVndWxhdGVkKQopKQoKIyBUaGlyZCwgcmVtb3ZlIGFsbCByb3dzIGluIHRoZSBkYXRhZnJhbWUgd2hlcmUgdGhlIENoYW5nZSBpcyBOQSAuCmFubm90YXRlZF9ib3RoIDwtIGFubm90YXRlZF9ib3RoW2NvbXBsZXRlLmNhc2VzKGFubm90YXRlZF9ib3RoJENoYW5nZSksXQoKIyBBZGQgcGF0aWVudCBJRCB0byBjb2x1bW4gYWdhaW46Cm5hbWVzKGFubm90YXRlZF9ib3RoKVtuYW1lcyhhbm5vdGF0ZWRfYm90aCkgPT0gImkucGF0aWVudERhdGEiXSA8LSBwYXRpZW50SUQgI1JlbmFtZSBjb2x1bW4gdG8gYWN0dWFsIFBhdGllbnQgSUQgZm9yIHZpc3VhbGl6YXRpb24KIyBDb252ZXJ0ICJDaGFuZ2UgY29sdW1uIHRvIGxvZygyKSBzY2FsZQphbm5vdGF0ZWRfYm90aCRsb2cuQ2hhbmdlIDwtIGxvZyhhbm5vdGF0ZWRfYm90aCRDaGFuZ2UsIDIpCiNDb252ZXJ0IENoYW5nZSBjb2x1bW4gZnJvbSBzY2llbnRpZmljIG5vdGF0aW9uIHRvIDIgZGlnaXRzLgppcy5udW0gPC0gc2FwcGx5KGFubm90YXRlZF9ib3RoLCBpcy5udW1lcmljKQphbm5vdGF0ZWRfYm90aFtpcy5udW1dIDwtIGxhcHBseShhbm5vdGF0ZWRfYm90aFtpcy5udW1dLCByb3VuZCwgMikKcmVtb3ZlKGlzLm51bSkKCiNDb3B5IGRhdGEgZm9yIGZ1cnRoZXIgY2xlYW51cCAoc29ydCBiYXNlZCBvbiBoaWdoZXN0IGxvZy1jaGFuZ2UgdmFsdWUsIG5vIHplcm8gdmFsdWVzIGV0Yy4pOgphbm5vdGF0ZWRfYm90aF9ub3plcm8gPC1hbm5vdGF0ZWRfYm90aAojRGF0YSBjbGVhbnVwCmFubm90YXRlZF9ib3RoX25vemVybyA8LSBhbm5vdGF0ZWRfYm90aF9ub3plcm8gWyghKGFubm90YXRlZF9ib3RoX25vemVybyRsb2cuQ2hhbmdlPT0wKSksXSAjUmVtb3ZlIHJvd3Mgd2l0aCB6ZXJvJ3MgOgphbm5vdGF0ZWRfYm90aF9ub3plcm8gPC0gYW5ub3RhdGVkX2JvdGhfbm96ZXJvW29yZGVyKC1hbm5vdGF0ZWRfYm90aF9ub3plcm8kbG9nLkNoYW5nZSksXSAgI1NvcnQgb24gbG9nMihjaGFuZ2UpCiNQcmludCBmaW5hbCBkYXRhc2V0IG9mIHBhdGllbnQgYmVmb3JlIHZpc3VhbGlzYXRpb24gKG9ubHkgcmVsZXZhbnQgY29sdW1ucyk6CnByaW50KGFubm90YXRlZF9ib3RoX25vemVyb1ssYygxLDMsNCw5KV0sIGRpZ2l0cyA9IDIpCgpgYGAKCkZvdXJ0aCwgZmluZCB3aGljaCByZWxldmFudCBiaW9tYXJrZXJzIGFyZSBpbiBhIHBhdGh3YXkgbW9kZWwuCmBgYHtyfQojI0FkZCBhbGwgSURzIGluIGEgbGlzdApDSEVCSV9JRHMgPC0gbGlzdCgpCmNvdW50ZXIgPSAxCmZvciAoaSBpbiAxOm5yb3coYW5ub3RhdGVkX2JvdGhfbm96ZXJvKSl7CiAgaWYoZ3JlcGwoJ0NIRUJJJywgYW5ub3RhdGVkX2JvdGhfbm96ZXJvW2ksNF0pICYmIGFubm90YXRlZF9ib3RoX25vemVyb1tpLDRdICE9ICIiICYmIGFubm90YXRlZF9ib3RoX25vemVyb1tpLDldICE9IDApewogICAgQ0hFQklfSURzW2NvdW50ZXJdIDwtIChhbm5vdGF0ZWRfYm90aF9ub3plcm9baSw0XSkKICAgIGNvdW50ZXIgPC0gY291bnRlciArIDEKICAgIH0KICBlbHNle25leHR9Cn0KcmVtb3ZlKGksY291bnRlcikKdmVjdG9yX0NIRUJJIDwtIHVubGlzdChDSEVCSV9JRHMpICNjb252ZXJ0IGxpc3QgdG8gYXJyYXksIGZvciB0cmF2ZXJzaW5nIHRoZSBkYXRhIHRvIGEgU1BBUlFMIHF1ZXJ5IGxhdGVyIG9uCmxpYnJhcnkoc3RyaW5ncikKY2xlYW5lZF9DSEVCSSA8LSBzdHJfcmVwbGFjZSh2ZWN0b3JfQ0hFQkksICJDSEVCSToiLCAiY2g6IikgI3VwZGF0ZSBJRHMgdG8gd29yayB3aXRoIFNQQVJRTCBxdWVyeQpzdXBlcmNsZWFuZWRfQ0hFQkkgPC0gc3RyX3JlcGxhY2UodmVjdG9yX0NIRUJJLCAiQ0hFQkk6IiwgIiIpICN1cGRhdGUgSURzIHRvIHdvcmsgd2l0aCBTUEFSUUwgcXVlcnkKc3RyaW5nX0NIRUJJIDwtIHBhc3RlKGMoY2xlYW5lZF9DSEVCSSksIGNvbGxhcHNlPScgJyApCnJlbW92ZSAoQ0hFQklfSURzLCB2ZWN0b3JfQ0hFQkksIGNsZWFuZWRfQ0hFQkkpCgojI0Nvbm5lY3QgdG8gRW5kcG9pbnQgV2lraVBhdGh3YXlzIChpZiBzdGVwIDIuMyBpcyBza2lwcGVkKQplbmRwb2ludHdwIDwtICJodHRwczovL3NwYXJxbC53aWtpcGF0aHdheXMub3JnL3NwYXJxbCIKCiMjRm9yIG5vdywgc3BsaXQgdXAgdGhlIHF1ZXJ5IGluIHNlY3Rpb25zLCB0byBhZGQgdGhlIHN0cmluZ19DSEVCSSBhcyBWQUxVRVMuCml0ZW0xID0gIlBSRUZJWCBjaDo8aHR0cHM6Ly9pZGVudGlmaWVycy5vcmcvY2hlYmkvQ0hFQkk6PgpzZWxlY3QgZGlzdGluY3QgP3BhdGh3YXlSZXMgKHN0cig/d3BpZCkgYXMgP3BhdGh3YXkpIChzdHIoP3RpdGxlKSBhcyA/cGF0aHdheVRpdGxlKSAoY291bnQoZGlzdGluY3QgP2NoZWJpTWV0YWJvbGl0ZSkgQVMgP0NIRUJJc0luUFdzKQp3aGVyZSB7ClZBTFVFUyA/Y2hlYmlNZXRhYm9saXRlIHsiCml0ZW0yID0gIiB9CiAKID9kYXRhbm9kZQlhIHdwOk1ldGFib2xpdGUgOyAgICAgICAgICAKICAgICAgICAgICAJd3A6YmRiQ2hFQkkgP2NoZWJpTWV0YWJvbGl0ZSA7CiAgICAJCWRjdGVybXM6aXNQYXJ0T2YgP3BhdGh3YXlSZXMgLgoKID9wYXRod2F5UmVzIGEgd3A6UGF0aHdheSA7CiAgICAgICAgICAgICB3cDpvcmdhbmlzbU5hbWUgXCdIb21vIHNhcGllbnNcJyA7IAogICAgCQlkY3Rlcm1zOmlkZW50aWZpZXIgP3dwaWQgOwogICAgCQlkYzp0aXRsZSA/dGl0bGUgLgp9CgpPUkRFUiBCWSBERVNDKD9DSEVCSXNJblBXcykiCgpxdWVyeV9SZWxldmFudFBXcyA8LSBwYXN0ZShpdGVtMSxzdHJpbmdfQ0hFQkksaXRlbTIpCnJlbW92ZShpdGVtMSxpdGVtMikKCnJlc3VsdHNfUmVsZXZhbnRQV3MgPC0gU1BBUlFMKGVuZHBvaW50d3AscXVlcnlfUmVsZXZhbnRQV3MsY3VybF9hcmdzPWxpc3QodXNlcmFnZW50PVIudmVyc2lvbi5zdHJpbmcpKQpzaG93cmVzdWx0c19SZWxldmFudFBXcyA8LSByZXN1bHRzX1JlbGV2YW50UFdzJHJlc3VsdHMKcmVtb3ZlKHF1ZXJ5X1JlbGV2YW50UFdzLHJlc3VsdHNfUmVsZXZhbnRQV3MpCgpwcmludChwYXN0ZTAoIlRoZXJlIGFyZSAiLCBucm93KGFubm90YXRlZF9ib3RoX25vemVybykgLCIgYmlvbWFya2VycyByZWxldmFudCBmb3IgcGF0aWVudCAiICwgcGF0aWVudElELCAiLCB3aXRoIENoRUJJLUlEczogIiwgc3RyaW5nX0NIRUJJLCAiLiIpKQoKYGBgCgpGaWZ0aCwgZmluZCB3aGljaCBwYXRpZW50IHNwZWNpZmljIGJpb21hcmtlcnMgYXJlIG5vdCBpbiBhbnkgcGF0aHdheS4KYGBge3J9CiMjRmluZCBNaXNzaW5nIEJpb21hcmtlcnMgKG5vdCBwYXJ0IG9mIGFueSBIdW1hbiBwYXRod2F5IG1vZGVsKQppdGVtMSA9ICJQUkVGSVggY2g6PGh0dHBzOi8vaWRlbnRpZmllcnMub3JnL2NoZWJpL0NIRUJJOj4KU0VMRUNUIERJU1RJTkNUID9jaGViaU1ldGFib2xpdGUgV0hFUkUgewogIFZBTFVFUyA/Y2hlYmlNZXRhYm9saXRlIHsiCml0ZW0yID0gIn0KICA/cGF0aHdheVJlcyAgYSB3cDpQYXRod2F5IDsKICAgICAgICAgICAgIAl3cDpvcmdhbmlzbU5hbWUgJ0hvbW8gc2FwaWVucycgLgogIAogID9tZXRhYm9saXRlIAlhIHdwOk1ldGFib2xpdGUgOwogICAgICAgICAgICAgICAgZGN0ZXJtczppZGVudGlmaWVyID9pZCA7CiAgICAgICAgICAgICAgICBkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4KCiAgP21ldGFib2xpdGUgd3A6YmRiQ2hFQkkgP2NoZWJpTWV0YWJvbGl0ZS4KfSIKCnF1ZXJ5TWlzc2luZ0Jpb21hcmtlcnMgPC0gcGFzdGUoaXRlbTEsc3RyaW5nX0NIRUJJLGl0ZW0yKQpyZW1vdmUoaXRlbTEsaXRlbTIpCgpyZXN1bHRzTWlzc2luZ0Jpb21hcmtlcnMgPC0gU1BBUlFMKGVuZHBvaW50d3AscXVlcnlNaXNzaW5nQmlvbWFya2VycyxjdXJsX2FyZ3M9bGlzdCh1c2VyYWdlbnQ9Ui52ZXJzaW9uLnN0cmluZykpCmxpc3RNaXNzaW5nQmlvbWFya2VycyA8LSBjKHJlc3VsdHNNaXNzaW5nQmlvbWFya2VycyRyZXN1bHRzKSAjc2FmZSByZXN1bHRzIGFzIGxpc3QgZm9yIGNvbXBhcmlzb24uCnJlbW92ZShxdWVyeU1pc3NpbmdCaW9tYXJrZXJzLHJlc3VsdHNNaXNzaW5nQmlvbWFya2VycykKCkNIRUJJX2luUFdzIDwtIGdzdWIoIls8aHR0cHM6Ly9pZGVudGlmaWVycy5vcmcvY2hlYmkvQ0hFQkk6Pl0iLCAiIiwgbGlzdE1pc3NpbmdCaW9tYXJrZXJzKSAjQ2hFQkkgSURzIElSSSBjbGVhbnVwCmludGVyc2VjdGluZ0NIRUJJIDwtIHNldGRpZmYoc3VwZXJjbGVhbmVkX0NIRUJJLENIRUJJX2luUFdzKQpzdHJpbmdfaW50ZXJzZWN0aW5nQ0hFQkkgPC0gcGFzdGUoYyhpbnRlcnNlY3RpbmdDSEVCSSksIGNvbGxhcHNlPScsICcgKQpjb3VudF9iaW9tYXJrZXJzIDwtIHNhcHBseShzdHJzcGxpdChzdHJpbmdfQ0hFQkksICIgIiksIGxlbmd0aCkKCiNGaW5kIG5hbWVzIGZvciBtaXNzaW5nIEJpb21hcmtlcnMgYmFzZWQgb24gQ2hFQmkgSUQgKHRvIGhlbHAgd2l0aCBkYXRhIGN1cmF0aW9uKQpDSEVCSV9pbnRlcnNlY3RpbmdDSEVCSSA8LSBwYXN0ZTAoIkNIRUJJOiIsIGludGVyc2VjdGluZ0NIRUJJKSAjdXBkYXRlIElEcyB0byBtYXRjaCBiYWNrIHRvIGRhdGEgYWdhaW4KbWlzc2luZ05hbWVzIDwtIGxpc3QoKQpmb3IgKGogaW4gMTpsZW5ndGgoQ0hFQklfaW50ZXJzZWN0aW5nQ0hFQkkpKXsKICBmb3IgKGkgaW4gMTpucm93KGFubm90YXRlZF9ib3RoX25vemVybykpewogICAgaWYoYW5ub3RhdGVkX2JvdGhfbm96ZXJvW2ksNF0gPT0gQ0hFQklfaW50ZXJzZWN0aW5nQ0hFQklbal0pewogICAgICAgbWlzc2luZ05hbWVzW2pdIDwtIGFubm90YXRlZF9ib3RoX25vemVyb1tpLDNdCiAgICAgIH0KICAgIGVsc2V7bmV4dH0KICB9Cn0KcmVtb3ZlKGksaikKI1NhdmUgbGlzdCBvbiBvbmUgc3RyaW5nIGZvciByZXBvcnRpbmcgcHVycG9zZXMKc3RyaW5nX21pc3NpbmdOYW1lcyA8LSBkby5jYWxsKHBhc3RlLCBjKGFzLmxpc3QobWlzc2luZ05hbWVzKSwgc2VwID0gIiwgIikpCgojUHJpbnQgcmVsZXZhbnQgaW5mb3JtYXRpb246CmlmKGxlbmd0aChpbnRlcnNlY3RpbmdDSEVCSSkgPT0gMCApe3ByaW50KCJBbGwgcmVsZXZhbnQgYmlvbWFya2VycyBhcmUgaW4gYSBwYXRod2F5ISIpfSBlbHNlewogIHByaW50KHBhc3RlMCgiVGhlc2UgYmlvbWFya2VycyAoYXMgQ2hFQkkgSURzKSBhcmUgbm90IGluIGEgcGF0aHdheTogIiAsIHN0cmluZ19pbnRlcnNlY3RpbmdDSEVCSSwgIjsgd2l0aCB0aGUgZm9sbG93aW5nIERhdGFiYXNlIG5hbWVzOiAiLCBzdHJpbmdfbWlzc2luZ05hbWVzKSl9CmBgYAoKIyAyLjUgVGhlb3JldGljYWwgQmlvbWFya2VyIERhdGEKClJlYWQgaW4gaW5mb3JtYXRpb24gZnJvbSBJRU1CYXNlIChjb2xsZWN0ZWQgbWFudWFsbHkpIGFuZCBzZWxlY3Qgb25seSBkYXRhIHJlbGV2YW50IHRvIHNhbXBsZSBtYXRyaXggKHVyaW5lKSBhbmQgYWdlIGNhdGVnb3J5IChmcm9tIHBhdGllbnQpCgpgYGB7cn0KYmlvbWFya2Vyc19wdXJpbmVfYWxsIDwtIHJlYWQudGFibGUoZmlsZSA9ICdEYXRhL0lFTV9kYXRhYmFzZV9pbmZvX1B1cmluZS50eHQnLCBzZXAgPSAnXHQnLCBoZWFkZXIgPSBUUlVFKQpiaW9tYXJrZXJzX3B5cmltaWRpbmVfYWxsIDwtIHJlYWQudGFibGUoZmlsZSA9ICdEYXRhL0lFTV9kYXRhYmFzZV9pbmZvX1B5cmltaWRpbmUudHh0Jywgc2VwID0gJ1x0JywgaGVhZGVyID0gVFJVRSkKYmlvbWFya2Vyc191cmVhX2FsbCA8LSByZWFkLnRhYmxlKGZpbGUgPSAnRGF0YS9JRU1fZGF0YWJhc2VfaW5mb19VcmVhLnR4dCcsIHNlcCA9ICdcdCcsIGhlYWRlciA9IFRSVUUpCgojU3VtbWFyaXplIFB1cmluZSBkaXNvcmRlcnMKbGlzdF9kaXNlYXNlc19wdXJpbmUgPC0gdW5pcXVlKG5hLm9taXQoYmlvbWFya2Vyc19wdXJpbmVfYWxsW2MoMTcpXSkpICNsaXN0IGFsbCB1bmlxdWUgZGlzZWFzZSBwcm90ZWluIG5hbWVzLCAgI1JlbW92ZSBOQSB2YWx1ZXMKI1N1bW1hcml6ZSBQeXJpbWlkaW5lIGRpc29yZGVycwpsaXN0X2Rpc2Vhc2VzX3B5cmltaWRpbmUgPC0gdW5pcXVlKG5hLm9taXQoYmlvbWFya2Vyc19weXJpbWlkaW5lX2FsbFtjKDE3KV0pKSAjbGlzdCBhbGwgdW5pcXVlIGRpc2Vhc2UgcHJvdGVpbiBuYW1lcyAjUmVtb3ZlIE5BIHZhbHVlcwojU3VtbWFyaXplIFVyZWEgQ3ljbGUgZGlzb3JkZXJzCmxpc3RfZGlzZWFzZXNfdXJlYSA8LSB1bmlxdWUobmEub21pdChiaW9tYXJrZXJzX3VyZWFfYWxsW2MoMTcpXSkpICNsaXN0IGFsbCB1bmlxdWUgZGlzZWFzZSBwcm90ZWluIG5hbWVzICNSZW1vdmUgTkEgdmFsdWVzCgp0b3RhbF9saXN0X2Rpc2Vhc2VzIDwtIGMobGlzdF9kaXNlYXNlc19wdXJpbmVbWzFdXSwgbGlzdF9kaXNlYXNlc19weXJpbWlkaW5lW1sxXV0sIGxpc3RfZGlzZWFzZXNfdXJlYVtbMV1dKQoKIyMgUHJpbnQgYW1vdW50IG9mIHVuaXF1ZSBkaXNlYXNlcyBmb3IgZWFjaCBjYXRlZ29yeToKcGFzdGUwKCJJbiB0b3RhbCwgdGhlcmUgd2VyZSAiLCBsZW5ndGgobGlzdF9kaXNlYXNlc19wdXJpbmVbWzFdXSkgLCIgdW5pcXVlIHB1cmluZSwgIiwgbGVuZ3RoKGxpc3RfZGlzZWFzZXNfcHlyaW1pZGluZVtbMV1dKSwgIiBweXJpbWlkaW5lIGFuZCAiLCBsZW5ndGgobGlzdF9kaXNlYXNlc191cmVhW1sxXV0pLCAiIHVyZWEgY3ljbGUgSU1EcyAodG90YWwgb2YgIiwgbGVuZ3RoKHRvdGFsX2xpc3RfZGlzZWFzZXMpICwiIGRpc29yZGVycykuIikKCiMjUFVQWTogKGFnZSBpbiB5ZWFycykKaWYoYWdlID49IDAgJiYgYWdlIDwgMSl7CiAgcmVmX2FnZV9tYXJrZXJzID0gYygxMCkgICNhZ2VfMHRvMW0sIG5lb25hdGFsCiAgfWVsc2UgaWYoYWdlID49IDEgJiYgYWdlIDwgMTgpewogIHJlZl9hZ2VfbWFya2VycyAgPSBjKDExKSAjYWdlXzFtdG8xOG0sIGluZmFuY3kKICB9ZWxzZSBpZihhZ2UgPj0gMTggJiYgYWdlIDwgMTMyKXsKICByZWZfYWdlX21hcmtlcnMgID0gYygxMikgI2FnZV8xOG10bzExeSwgY2hpbGRob29kCiAgfWVsc2UgaWYoYWdlID49IDEzMSAmJiBhZ2UgPCAxOTIpewogIHJlZl9hZ2VfbWFya2VycyA9IGMoMTMpICNhZ2VfMTF0bzE2eSwgYWRvbGVzY2VuY2UKICB9ZWxzZXsKICAgIHJlZl9hZ2VfbWFya2VycyA9IGMoMTQpICNhZ2VfMTY8LCBhZHVsdGhvb2QKICB9CgpiaW9tYXJrZXJzX3B1cmluZV9hZ2UgPC0gYXMuZGF0YS5mcmFtZShiaW9tYXJrZXJzX3B1cmluZV9hbGxbLCBjKDEsMTcsNTo3LHJlZl9hZ2VfbWFya2VycywxNildLCAgZHJvcD1mYWxzZSkgCmJpb21hcmtlcnNfcHVyaW5lX2FnZSRDYXRlZ29yeSA8LSAxCmJpb21hcmtlcnNfcHlyaW1pZGluZV9hZ2UgPC0gYXMuZGF0YS5mcmFtZShiaW9tYXJrZXJzX3B5cmltaWRpbmVfYWxsWywgYygxLDE3LDU6NyxyZWZfYWdlX21hcmtlcnMsMTYpXSwgIGRyb3A9ZmFsc2UpIApiaW9tYXJrZXJzX3B5cmltaWRpbmVfYWdlJENhdGVnb3J5IDwtIDIKYmlvbWFya2Vyc191cmVhX2FnZSA8LSBhcy5kYXRhLmZyYW1lKGJpb21hcmtlcnNfdXJlYV9hbGxbLCBjKDEsMTcsNTo3LHJlZl9hZ2VfbWFya2VycywxNildLCAgZHJvcD1mYWxzZSkgCmJpb21hcmtlcnNfdXJlYV9hZ2UkQ2F0ZWdvcnkgPC0gMwoKYmlvbWFya2Vyc190aHJlZSA8LSByYmluZChiaW9tYXJrZXJzX3B1cmluZV9hZ2UsIGJpb21hcmtlcnNfcHlyaW1pZGluZV9hZ2UsIGJpb21hcmtlcnNfdXJlYV9hZ2UpICNDb21iaW5pbmcgZGF0YSB0aGVvcmV0aWNhbCBiaW9tYXJrZXJzIGluIG9uZSBkYXRhZnJhbWUKbmFtZXMoYmlvbWFya2Vyc190aHJlZSlbNl0gPC0gIlVwRG93biIKcmVtb3ZlKGJpb21hcmtlcnNfcHVyaW5lX2FsbCwgYmlvbWFya2Vyc19wdXJpbmVfYWdlLCBiaW9tYXJrZXJzX3B5cmltaWRpbmVfYWxsLCBiaW9tYXJrZXJzX3B5cmltaWRpbmVfYWdlLCBiaW9tYXJrZXJzX3VyZWFfYWxsLCBiaW9tYXJrZXJzX3VyZWFfYWdlKQoKIyBUaGlyZCwgY2xlYW4gdXAgdGhlIGRhdGEsIGJ5IHNlbGVjdGluZyBvbmx5IGVudHJpZXMgd2l0aCBhIEhNREIgSUQsIG1lYXN1cmVkIGluIHVyaW5lLCBhbmQgY29udmVydGluZyBub24tbnVtZXJpY2FsIGVudHJpZXMgZm9yIHVwL2Rvd24gcmVndWxhdGlvbi4KYmlvbWFya2Vyc190aHJlZSA8LSBiaW9tYXJrZXJzX3RocmVlICBbKCEoaXMubmEoYmlvbWFya2Vyc190aHJlZSRITURCKSkpLF0KYmlvbWFya2Vyc190aHJlZSA8LSBiaW9tYXJrZXJzX3RocmVlICBbKCEoaXMubmEoYmlvbWFya2Vyc190aHJlZSRNZWFzdXJlZC5pbikpKSAmICgoYmlvbWFya2Vyc190aHJlZSRNZWFzdXJlZC5pbj09IlUiKSB8IChiaW9tYXJrZXJzX3RocmVlJE1lYXN1cmVkLmluPT0iVXJpbmUiKSksIF0KCiNSZXBsYWNlIG5vbi1udW1lcmljYWwgY2hhcmFjdGVycyB3aXRoIHZhbHVlcwpiaW9tYXJrZXJzX3RocmVlIDwtIHdpdGhpbihiaW9tYXJrZXJzX3RocmVlLCBVcERvd25bVXBEb3duID09ICItMiB0byAtMSJdIDwtICgiLTEuNSIpKSAKYmlvbWFya2Vyc190aHJlZSA8LSB3aXRoaW4oYmlvbWFya2Vyc190aHJlZSwgVXBEb3duW1VwRG93biA9PSAiLTIgdG8gbiJdIDwtICgiLTEiKSkKYmlvbWFya2Vyc190aHJlZSA8LSB3aXRoaW4oYmlvbWFya2Vyc190aHJlZSwgVXBEb3duW1VwRG93biA9PSAiLTEgdG8gbiJdIDwtICgiLTAuNSIpKQpiaW9tYXJrZXJzX3RocmVlIDwtIHdpdGhpbihiaW9tYXJrZXJzX3RocmVlLCBVcERvd25bVXBEb3duID09ICJuIHRvIDIiXSA8LSAoIjEiKSkKYmlvbWFya2Vyc190aHJlZSA8LSB3aXRoaW4oYmlvbWFya2Vyc190aHJlZSwgVXBEb3duW1VwRG93biA9PSAibiB0byAxIl0gPC0gKCIwLjUiKSkKYmlvbWFya2Vyc190aHJlZSA8LSB3aXRoaW4oYmlvbWFya2Vyc190aHJlZSwgVXBEb3duW1VwRG93biA9PSAibiJdIDwtICgiMCIpKQpiaW9tYXJrZXJzX3RocmVlIDwtIHdpdGhpbihiaW9tYXJrZXJzX3RocmVlLCBVcERvd25bVXBEb3duID09ICIrLSJdIDwtICgiMCIpKQoKYmlvbWFya2Vyc190aHJlZSRVcERvd24gPC0gYXMubnVtZXJpYyhiaW9tYXJrZXJzX3RocmVlJFVwRG93bikKCiMjQ29udmVydCBvbGQgSE1EJ3MgdG8gbmV3IElEczoKZm9yIChpIGluIDE6bGVuZ3RoKChiaW9tYXJrZXJzX3RocmVlJEhNREIpKSkgewogIGlmKG5jaGFyKGJpb21hcmtlcnNfdGhyZWUkSE1EQltpXSk8PSA5KXsKICAgIGJpb21hcmtlcnNfdGhyZWUkSE1EQltpXSA8LSBzdHJfcmVwbGFjZShiaW9tYXJrZXJzX3RocmVlJEhNREJbaV0sICJITURCIiwgIkhNREIwMCIpCiAgfQogIGVsc2V7bmV4dH0KfQpyZW1vdmUoaSkKCiMjIFByaW50IGFtb3VudCBvZiB1bmlxdWUgZGlzZWFzZXMgYW5kIGJpb21hcmtlcnM6CnBhc3RlMCgiRm9yIHRoZXNlIHRocmVlIGNsYXNzZXMgb2YgSU1EcywgIiwgbnJvdyh1bmlxdWUobmEub21pdChiaW9tYXJrZXJzX3RocmVlW2MoNSldKSkpICwiIHVuaXF1ZSBiaW9tYXJrZXJzIHdlcmUgcmVsZXZhbnQgZm9yIHRoZSBzYW1wbGUgbWF0cml4IHVyaW5lLCByZXByZXNlbnRpbmcgIiwgIG5yb3codW5pcXVlKG5hLm9taXQoYmlvbWFya2Vyc190aHJlZVtjKDIpXSkpKSAsICIgdW5pcXVlIGRpc29yZGVyczsgIiAsIHN1bShpcy5uYShiaW9tYXJrZXJzX3RocmVlW2MoMildKSksICIgZGlzb3JkZXIgaXMgbWlzc2luZyBhIHByb3RlaW4gbmFtZS4iKQoKIyNEeW5hbWljIGNhcHRpb24gaW5mbzoKZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2VycyA8LSBiaW9tYXJrZXJzX3RocmVlWyxjKDMsNSldICMgQ29tYmluZXMgYmlvbWFya2Vyc190aHJlZSRTeW1wdG9tcyB3aXRoIGJpb21hcmtlcnNfdGhyZWUkSE1EQgpkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzIDwtIHVuaXF1ZShkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzKSAjT25seSByZXRhaW4gdW5pcXVlIG1ldGFib2xpdGUgbmFtZXMgYW5kIEhNREIgSURzLgpkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzIDwtIG5hLm9taXQoZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2VycykgI3JlbW92ZSBhbnkgcm93cyB3aXRoIE5BLXZhbHVlcwpkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMgPC0gYmlvbWFya2Vyc190aHJlZVssYygyLDEpXSAjIENvbWJpbmVzIGJpb21hcmtlcnNfdGhyZWUkUHJvdGVpbl9IR05DX25hbWUgd2l0aCBiaW9tYXJrZXJzX3RocmVlJERpc29yZGVyCmRmX2ZpZ3VyZV9jYXB0aW9uX2Rpc29yZGVycyA8LSB1bmlxdWUoZGZfZmlndXJlX2NhcHRpb25fZGlzb3JkZXJzKSAjT25seSByZXRhaW4gdW5pcXVlIGRpc29yZGVyIG5hbWVzICsgSEdOQyBuYW1lcwpkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMgPC0gbmEub21pdChkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMpICNyZW1vdmUgYW55IHJvd3Mgd2l0aCBOQS12YWx1ZXMKCnJlbGV2YW50IDwtIGJpb21hcmtlcnNfdGhyZWVbLGMoMiw1LDYsOCldCgojI1JlbW92ZSByb3dzIHdpdGggTkEgdmFsdWVzIGFueXdoZXJlCnJlbGV2YW50IDwtIHJlbGV2YW50W2NvbXBsZXRlLmNhc2VzKHJlbGV2YW50KSwgXQojI1JlbW92ZSByb3dzIHdpdGggemVybyB2YWx1ZXMgYW55d2hlcmUKcmVsZXZhbnQgPC0gcmVsZXZhbnRbYXBwbHkocmVsZXZhbnQhPTAsIDEsIGFsbCksXQoKI0tlZXAgdW5pcXVlIGRpc2Vhc2VzIGFuZCBjYXRlZ29yeSBuYW1lcwpkaXNlYXNlc19jYXRlZ29yaWVzIDwtIHJlbGV2YW50WyxjKDEsNCldCnVuaXF1ZV9kaXNlYXNlX2NhdGVnb3JpZXMgPC0gYWdncmVnYXRlKGRpc2Vhc2VzX2NhdGVnb3JpZXNbLTFdLCBsaXN0KGRpc2Vhc2VzX2NhdGVnb3JpZXMkUHJvdGVpbl9IR05DX25hbWUpLCBGVU4gPSBtZWFuLCBuYS5ybSA9IFRSVUUpICNTb3J0ZWQgYWxwaGFiZXRpY2FsbHkKCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcyBmb3IgY2FzdCgpIGZ1bmN0aW9uIChkYXRhIHRyYW5zZm9ybWF0aW9uIGJhc2VkIG9uIGNlcnRhaW4gY29sdW1uIGlucHV0LikKaWYgKCFyZXF1aXJlKCJyZXNoYXBlMiIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInJlc2hhcGUyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShyZXNoYXBlMikKICAgfQppZiAoIXJlcXVpcmUoInJlc2hhcGUiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJyZXNoYXBlciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgIGxpYnJhcnkocmVzaGFwZSkKICAgfQoKIyBUcmFuc2Zvcm0gdGhlIGRhdGEsIHVzaW5nIHByb3RlaW4gSEdOQyBuYW1lcyBhcyByb3cgbmFtZXMsIEhNREItSURzIGFzIGNvbHVtbnMsIGFuZCBVcERvd24tcmVndWxhdGlvbiB2YWx1ZXMgdG8gY3JlYXRlIG1hdHJpeApwcm90ZWluLmNhc3QgPC0gY2FzdChyZWxldmFudCwgUHJvdGVpbl9IR05DX25hbWV+SE1EQiwgdmFsdWU9IlVwRG93biIsIHN1bSkKY29sdW1uX3N0YXJ0ID0gKG5jb2wocHJvdGVpbi5jYXN0KSsxKQpmb3IgKGkgaW4gMTpucm93KHByb3RlaW4uY2FzdCkpIHsKICBpZihwcm90ZWluLmNhc3RbaSwxXSA9PSB1bmlxdWVfZGlzZWFzZV9jYXRlZ29yaWVzW2ksMV0pewogICAgcHJvdGVpbi5jYXN0W2ksY29sdW1uX3N0YXJ0XSA8LSB1bmlxdWVfZGlzZWFzZV9jYXRlZ29yaWVzW2ksMl0KICB9Cn0KcmVtb3ZlKGkpCm5hbWVzKHByb3RlaW4uY2FzdClbY29sdW1uX3N0YXJ0XSA8LSAiQ2F0ZWdvcnkiCnByb3RlaW4uY2FzdF9vcmRlcmVkIDwtIHByb3RlaW4uY2FzdFtvcmRlcihwcm90ZWluLmNhc3QkQ2F0ZWdvcnkpLF0KCmxpbmVzX0NhdGVnb3J5X1B1cmluZSA8LSBucm93KHByb3RlaW4uY2FzdF9vcmRlcmVkW3Byb3RlaW4uY2FzdF9vcmRlcmVkJENhdGVnb3J5ID09ICIxIixdKQpsaW5lc19DYXRlZ29yeV9QeXJpbWlkaW5lIDwtIG5yb3cocHJvdGVpbi5jYXN0X29yZGVyZWRbcHJvdGVpbi5jYXN0X29yZGVyZWQkQ2F0ZWdvcnkgPT0gIjIiLF0pCmxpbmVzX0NhdGVnb3J5X1VyZWEgPC0gbnJvdyhwcm90ZWluLmNhc3Rfb3JkZXJlZFtwcm90ZWluLmNhc3Rfb3JkZXJlZCRDYXRlZ29yeSA9PSAiMyIsXSkKCiMjUmVtb3ZlIGNhdGVnb3JpZXMgY29sdW1uIGFnYWluLCBzbyBpdCB3aWxsIG5vdCBkaXN0dXJiIHRoZSBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbgpwcm90ZWluLmNhc3Rfb3JkZXJlZCA8LSBzdWJzZXQocHJvdGVpbi5jYXN0X29yZGVyZWQsIHNlbGVjdD0tYyhDYXRlZ29yeSkpCnJuYW1lcyA8LSBwcm90ZWluLmNhc3Rfb3JkZXJlZFssMV0gI1N0b3JlIHRoZSBwcm90ZWluIG5hbWVzIHRvIHVzZSBhcyByb3cgdmFsdWVzIGluIG1hdHJpeCBsYXRlcgptYXRyaXhfcHJvdGVpbi5jYXN0IDwtIGRhdGEubWF0cml4KHByb3RlaW4uY2FzdF9vcmRlcmVkKSAjY29udmVydCB0aGUgZGF0YWZyYW1lIHRvIGEgbWF0cml4IChvbmx5IGluY2x1ZGluZyBudW1lcmljIHZhbHVlcykKcm93bmFtZXMobWF0cml4X3Byb3RlaW4uY2FzdCkgPC0gcm5hbWVzICNBZGQgdGhlIHNhdmVkIHByb3RlaW4gbmFtZXMgYXMgcm93TGFiZWxzCm1hdHJpeF9wcm90ZWluLmNhc3QgPC0gbWF0cml4X3Byb3RlaW4uY2FzdFssLTFdICNSZW1vdmUgdGhlIGZpcnN0IGNvbHVtbiwgaW5jbHVkaW5nIGNvdW50IG9mIHJvd3MgZnJvbSBkYXRhZnJhbWUuICNTb3J0ZWQgYWxwaGFiZXRpY2FsbHkKCnJlbW92ZShiaW9tYXJrZXJzX3RocmVlLCBkaXNlYXNlc19jYXRlZ29yaWVzLCBwcm90ZWluLmNhc3QsIHByb3RlaW4uY2FzdF9vcmRlcmVkLCByZWxldmFudCwgdW5pcXVlX2Rpc2Vhc2VfY2F0ZWdvcmllcykKCm5vdF9jb3ZlcmVkX0lNRHMgPC0gc2V0ZGlmZih0b3RhbF9saXN0X2Rpc2Vhc2VzLHJuYW1lcykKbm90X2NvdmVyZWRfSU1EcyA8LSBwYXN0ZShub3RfY292ZXJlZF9JTURzLCBjb2xsYXBzZSA9ICcsICcpCmFtb3VudF9iaW9tYXJrZXJzX21hdHJpeERhdGEgPC0gbmNvbChtYXRyaXhfcHJvdGVpbi5jYXN0KQphbW91bnRfZGlzZWFzZXNfbWF0cml4RGF0YSA8LSBucm93KG1hdHJpeF9wcm90ZWluLmNhc3QpCgojI0FkZCBkaXNvcmRlcnMgbm90IGNvdmVyZWQgd2l0aCB6ZXJvJ3MgZm9yIGFsbCBjb2x1bW5zOgojTWF0cml4QiA8LSByYmluZChNYXRyaXhBLCBjKDEwLDExLDEyKSkgIAoKIyMgUHJpbnQgYW1vdW50IG9mIHVuaXF1ZSBkaXNlYXNlcyBmb3IgZWFjaCBjYXRlZ29yeToKcGFzdGUwKCJUYWtpbmcgYXZhaWxhYmxlIHJlZmVyZW5jZSBkYXRhIGludG8gYWNjb3VudCBhbmQgYXZhaWxhYmlsaXR5IG9mIGEgSE1EQiBJRCwgYSBtYXhpbXVtIG9mICIsIGFtb3VudF9iaW9tYXJrZXJzX21hdHJpeERhdGEgLCIgdW5pcXVlIGJpb21hcmtlcnMgY291bGQgYmUgbGlua2VkIHRvICIsIGFtb3VudF9kaXNlYXNlc19tYXRyaXhEYXRhLCAiIElNRHMuIikKcGFzdGUwKCJGb3IgdGhlIGZvbGxvd2luZyBJTURzLCBubyBkYXRhIHdhcyBhdmFpbGFibGU6ICIsIG5vdF9jb3ZlcmVkX0lNRHMsICIuIikKCmlmICghcmVxdWlyZSgiZ3Bsb3RzIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ3Bsb3RzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShncGxvdHMpCiAgIH0KaWYgKCFyZXF1aXJlKCJSQ29sb3JCcmV3ZXIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICBsaWJyYXJ5KFJDb2xvckJyZXdlcikKICAgfQoKbXlfcGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpKG4gPSAyOTkpCgpjb2xfYnJlYWtzID0gYyhzZXEoLTMsLTEsbGVuZ3RoPTEwMCksICAjIGZvciBibHVlCiAgc2VxKC0wLjk5LDAuOTksbGVuZ3RoPTEwMCksICAgICAgICAgICAjIGZvciB5ZWxsb3cKICBzZXEoMSwzLGxlbmd0aD0xMDApKSAgICAgICAgICAgICAjIGZvciByZWQKCiNDcmVhdGUgYSBmaWxlIG5hbWUgc3BlY2lmaWMgZm9yIHBhdGllbnRJRApmaWxlbmFtZSA8LSBwYXN0ZTAoIkltYWdlcy8iLCBhZ2UsICJtb250aHNfaGVhdG1hcC5wbmciKQp0aXRsZV9uYW1lIDwtIHBhc3RlMCgiQmlvbWFya2VyIG92ZXJsYXAgZm9yIHRocmVlIElNRCB0eXBlcywgXG4gYWdlIGNhdGVnb3J5OiAiLCBhZ2VyYW5nZSkKCiMgY3JlYXRlcyBhIDUgeCA1IGluY2ggaW1hZ2UKcG5nKGZpbGVuYW1lLCAgICAjIGNyZWF0ZSBQTkcgZm9yIHRoZSBoZWF0IG1hcCAgICAgICAgCiAgd2lkdGggPSA1KjMwMCwgICAgICAgICMgNSB4IDMwMCBwaXhlbHMKICBoZWlnaHQgPSA1KjMwMCwKICByZXMgPSAyODAsICAgICAgICAgICAgIyAzMDAgcGl4ZWxzIHBlciBpbmNoCiAgcG9pbnRzaXplID0gOCkgICAgICAgICMgc21hbGxlciBmb250IHNpemUKCm91dHB1dCA8LSBoZWF0bWFwLjIobWF0cml4X3Byb3RlaW4uY2FzdCwKICAjY2VsbG5vdGUgPSBtYXRyaXhfcHJvdGVpbi5jYXN0LCAgIyBzYW1lIGRhdGEgc2V0IGZvciBjZWxsIGxhYmVscwogIG1haW4gPSB0aXRsZV9uYW1lLCAjIGhlYXQgbWFwIHRpdGxlCiAgbm90ZWNvbD0iYmxhY2siLCAgICAgICMgY2hhbmdlIGZvbnQgY29sb3Igb2YgY2VsbCBsYWJlbHMgdG8gYmxhY2sKICBjZWxsbm90ZSA9IGlmZWxzZShtYXRyaXhfcHJvdGVpbi5jYXN0ICE9IDAsIG1hdHJpeF9wcm90ZWluLmNhc3QsICIiKSwgI1Zpc3VhbGl6aW5nIG9ubHkgbGFiZWwgaWYgZGF0YSBpcyBub3QgemVybwogIAogIFJvd1NpZGVDb2xvcnMgPSBjKCAgICAjIGdyb3VwaW5nIHJvdy12YXJpYWJsZXMgaW50byBkaWZmZXJlbnQgY2F0ZWdvcmllcywgZS5nLgogICAgIHJlcCgiZ3JheSIsIGxpbmVzX0NhdGVnb3J5X1B1cmluZSksICAgIyAgUHVyaW5lIGRpc2Vhc2VzCiAgICAgcmVwKCJibHVlIiwgbGluZXNfQ2F0ZWdvcnlfUHlyaW1pZGluZSksICAgICMgUHlyaW1pZGluZSBkaXNlYXNlcwogICAgIHJlcCgiYmxhY2siLCBsaW5lc19DYXRlZ29yeV9VcmVhKSksICAgICMgVXJlYSBkaXNlYXNlcwogIAogIGRlbnNpdHkuaW5mbz0ibm9uZSIsICAjIHR1cm5zIG9mZiBkZW5zaXR5IHBsb3QgaW5zaWRlIGNvbG9yIGxlZ2VuZAogIHRyYWNlPSJub25lIiwgICAgICAgICAjIHR1cm5zIG9mZiB0cmFjZSBsaW5lcyBpbnNpZGUgdGhlIGhlYXQgbWFwCiAgbWFyZ2lucyA9YygxMiw5KSwgICAgICMgd2lkZW5zIG1hcmdpbnMgYXJvdW5kIHBsb3QKICBjb2w9bXlfcGFsZXR0ZSwgICAgICAgIyB1c2Ugb24gY29sb3IgcGFsZXR0ZSBkZWZpbmVkIGVhcmxpZXIKICBicmVha3M9Y29sX2JyZWFrcywgICAgIyBlbmFibGUgY29sb3IgdHJhbnNpdGlvbiBhdCBzcGVjaWZpZWQgbGltaXRzCiAgZGVuZHJvZ3JhbT0icm93IiwgICAgICMgb25seSBkcmF3IGEgcm93IGRlbmRyb2dyYW0KICBDb2x2PSJOQSIpICAgICAgICAgICAgIyB0dXJuIG9mZiBjb2x1bW4gY2x1c3RlcmluZwoKcGFyKGxlbmQgPSAxKSAgICAgICAgICAgIyBzcXVhcmUgbGluZSBlbmRzIGZvciB0aGUgY29sb3IgbGVnZW5kCmxlZ2VuZCgidG9wcmlnaHQiLCAgICAgICMgbG9jYXRpb24gb2YgdGhlIGxlZ2VuZCBvbiB0aGUgaGVhdG1hcCBwbG90CiAgICBsZWdlbmQgPSBjKCJQdXJpbmUiLCAiUHlyaW1pZGluZSIsICJVcmVhIGN5Y2xlIiksICMgY2F0ZWdvcnkgbGFiZWxzCiAgICBjb2wgPSBjKCJncmF5IiwgImJsdWUiLCAiYmxhY2siKSwgIyBjb2xvciBrZXkKICAgIHRpdGxlID0gIklNRCBjbGFzc2VzIiwgICAgICAgICAgICMgbGVnZW5kIHRpdGxlCiAgICBsdHk9IDEsICAgICAgICAgICAgICMgbGluZSBzdHlsZQogICAgbHdkID0gMTAgICAgICAgICAgICAjIGxpbmUgd2lkdGgKKQogCgpkZXYub2ZmKCkgICAgICAgICAgICAgICAjIGNsb3NlIHRoZSBQTkcgZGV2aWNlCgpyZW1vdmUocm5hbWVzLCB0aXRsZV9uYW1lKQoKI1Nob3cgdGhlIGhlYXRtYXAgYXMgb3V0cHV0IGluIFJNRCBmaWxlCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGZpbGVuYW1lKQoKIyNPYnRhaW4gdGhlIG9yZGVyIG9mIHRoZSBkaXNlYXNlcyBhbmQgYmlvbWFya2VycyBhcyBhcHBlYXJpbmcgaW4gdGhlIGNsdXN0ZXJlZCBoZWF0bWFwOgp2ZXJ0aWNhbEF4aXMgPC0gcmV2KHJvd25hbWVzKG1hdHJpeF9wcm90ZWluLmNhc3QpW291dHB1dCRyb3dJbmRdKQpob3Jpem9udGFsQXhpcyA8LSBjb2xuYW1lcyhtYXRyaXhfcHJvdGVpbi5jYXN0KVtvdXRwdXQkY29sSW5kXQoKIyNOZXcgZHluYW1pYyBGaWd1cmUgY2FwdGlvbnM6CiMjI0Rpc2Vhc2VzCmRpc29yZGVyTmFtZXMgPC0gbGlzdCgpCgpmb3IgKGogaW4gMTpsZW5ndGgodmVydGljYWxBeGlzKSl7CiAgZm9yIChpIGluIDE6bnJvdyhkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMpKXsKICAgIGlmKGRmX2ZpZ3VyZV9jYXB0aW9uX2Rpc29yZGVyc1tpLDFdID09IHZlcnRpY2FsQXhpc1tqXSl7CiAgICAgIGNvbWJpbmVEaXNlYXNlSEdOQyA8LSBwYXN0ZTAodmVydGljYWxBeGlzW2pdLCAnOiAnLCBkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnNbaSwyXSkKICAgICAgIGRpc29yZGVyTmFtZXNbal0gPC0gY29tYmluZURpc2Vhc2VIR05DCiAgICAgIH0KICAgIGVsc2V7bmV4dH0KICB9Cn0KcmVtb3ZlKGksaikKI1NhdmUgbGlzdCBvbiBvbmUgc3RyaW5nIGZvciByZXBvcnRpbmcgcHVycG9zZXMKb3JkZXJlZF9kaXNvcmRlck5hbWVzIDwtIGRvLmNhbGwocGFzdGUsIGMoYXMubGlzdChkaXNvcmRlck5hbWVzKSwgc2VwID0gIiwgIikpCgojIyNCaW9tYXJrZXJzCmJpb21hcmtlck5hbWVzIDwtIGxpc3QoKQoKZm9yIChqIGluIDE6bGVuZ3RoKGhvcml6b250YWxBeGlzKSl7CiAgZm9yIChpIGluIDE6bnJvdyhkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzKSl7CiAgICBpZihkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzW2ksMl0gPT0gaG9yaXpvbnRhbEF4aXNbal0pewogICAgICBjb21iaW5lYmlvbWFya2VySE1EQiA8LSBwYXN0ZTAoaG9yaXpvbnRhbEF4aXNbal0sICc6ICcsIGRmX2ZpZ3VyZV9jYXB0aW9uX2Jpb21hcmtlcnNbaSwxXSkKICAgICAgIGJpb21hcmtlck5hbWVzW2pdIDwtIGNvbWJpbmViaW9tYXJrZXJITURCCiAgICAgIH0KICAgIGVsc2V7bmV4dH0KICB9Cn0KcmVtb3ZlKGksaikKI1NhdmUgbGlzdCBvbiBvbmUgc3RyaW5nIGZvciByZXBvcnRpbmcgcHVycG9zZXMKb3JkZXJlZF9iaW9tYXJrZXJOYW1lcyA8LSBkby5jYWxsKHBhc3RlLCBjKGFzLmxpc3QoYmlvbWFya2VyTmFtZXMpLCBzZXAgPSAiLCAiKSkKCiNkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzCgojUHJpbnQgRmlndXJlIGNhcHRpb24gKHNvcnRlZCBkaXNlYXNlcyBhbmQgdW5zb3J0ZWQgbWV0YWJvbGl0ZXMpCnBhc3RlKCJQcm90ZWluIG5hbWVzIGNvcnJlc3BvbmQgdG8gdGhlIGZvbGxvd2luZyBkaXNvcmRlcnM6Iiwgb3JkZXJlZF9kaXNvcmRlck5hbWVzKQpwYXN0ZSgiLiBITURCIElEcyByZXNlbWJsZSB0aGVzZSBtZXRhYm9saXRlczoiLCBvcmRlcmVkX2Jpb21hcmtlck5hbWVzKQoKcmVtb3ZlKHRvdGFsX2xpc3RfZGlzZWFzZXMsIGFtb3VudF9iaW9tYXJrZXJzX21hdHJpeERhdGEsIGFtb3VudF9kaXNlYXNlc19tYXRyaXhEYXRhLCBjb2x1bW5fc3RhcnQsIG9yZGVyZWRfYmlvbWFya2VyTmFtZXMsIG9yZGVyZWRfZGlzb3JkZXJOYW1lcywgbGlzdF9kaXNlYXNlc19wdXJpbmUsIGxpc3RfZGlzZWFzZXNfcHlyaW1pZGluZSwgbGlzdF9kaXNlYXNlc191cmVhLCBub3RfY292ZXJlZF9JTURzKQpgYGAKCiMgMi42IFJlbGV2YW50IEJpb21hcmtlciBPdmVybGFwCgpgYGB7cn0KIyMgT2J0YWluIHBhdGllbnQgZGF0YQpwYXRpZW50X2Jpb21hcmtlcl9kYXRhX0hNREIgPC0gYW5ub3RhdGVkX2JvdGhfbm96ZXJvWyxjKDQsNyw5KV0KcGF0aWVudF9iaW9tYXJrZXJfZGF0YV9ITURCIDwtIHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQlshKGlzLm5hKHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQiRITURCKSB8IHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQiRITURCPT0iIiksIF0KI3JlcGxhY2UgZGF0YSBzbWFsbGVyIHRoYW4gMC4wNSBvciAtMC4wNSB3aXRoIE5BCnBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQiRsb2cuQ2hhbmdlW2ZpbmRJbnRlcnZhbChwYXRpZW50X2Jpb21hcmtlcl9kYXRhX0hNREIkbG9nLkNoYW5nZSwgYygtMC4wNSwwLjA1KSkgPT0gMUxdIDwtIE5BCiMgUmVtb3ZlIGFsbCByb3dzIGluIHRoZSBkYXRhZnJhbWUgd2hlcmUgdGhlIENoYW5nZSBpcyBOQSAuCnBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQiA8LSBwYXRpZW50X2Jpb21hcmtlcl9kYXRhX0hNREJbY29tcGxldGUuY2FzZXMocGF0aWVudF9iaW9tYXJrZXJfZGF0YV9ITURCJGxvZy5DaGFuZ2UpLF0KCiNEeW5hbWljIGNhcHRpb24gZm9yIHBhdGllbnQgc3BlY2lmaWMgYmlvbWFya2VyczoKZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2Vyc19wYXRpZW50c3BlY2lmaWMgPC0gYW5ub3RhdGVkX2JvdGhfbm96ZXJvWyxjKDMsNyldICMgQ29tYmluZXMgYmlvbWFya2Vyc190aHJlZSRTeW1wdG9tcyB3aXRoIGJpb21hcmtlcnNfdGhyZWUkSE1EQgpkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzX3BhdGllbnRzcGVjaWZpYyA8LSB1bmlxdWUoZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2Vyc19wYXRpZW50c3BlY2lmaWMpICNPbmx5IHJldGFpbiB1bmlxdWUgbWV0YWJvbGl0ZSBuYW1lcyBhbmQgSE1EQiBJRHMuCmRmX2ZpZ3VyZV9jYXB0aW9uX2Jpb21hcmtlcnNfcGF0aWVudHNwZWNpZmljIDwtIG5hLm9taXQoZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2Vyc19wYXRpZW50c3BlY2lmaWMpICNyZW1vdmUgYW55IHJvd3Mgd2l0aCBOQS12YWx1ZXMKCiNDb252ZXJ0IGxvZygyKWNoYW5nZSBkYXRhIHRvIHNpbWlsYXIgbnVtZXJpYyBzY2FsZSBhcyB0aGVvcmV0aWNhbCBiaW9tYXJrZXIgZGF0YSAoLTMgdG8gMykKcGF0aWVudF9iaW9tYXJrZXJfZGF0YV9ITURCIDwtIHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQiU+JW11dGF0ZShiaW9tYXJrZXIuQ2hhbmdlID0gY2FzZV93aGVuKAogbG9nLkNoYW5nZSA+IDMgfiAzLCAgICAgICAgICMgRXZlcnkgbG9nKDIpIGNoYW5nZSB2YWx1ZSBsYXJnZXIgdGhlbiAzCiBsb2cuQ2hhbmdlIDwgLTMgfiAtMywgICAgICAgIyBFdmVyeSBsb2coMikgY2hhbmdlIHZhbHVlIHNtYWxsZXIgdGhlbiAtMwogVFJVRSB+IGxvZy5DaGFuZ2UgICAgICAgICAgICMgS2VlcCBhbGwgb3RoZXIgdmFsdWVzIHRoZSBzYW1lCikpCgojUmVtb3ZlIGNvbHVtbnMgd2hpY2ggYXJlIG5vdCBuZWVkZWQKcGF0aWVudF9iaW9tYXJrZXJfZGF0YV9ITURCIDwtIHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQlssYygtMSwtMyldCiNUcmFuc3Bvc2UgZGF0YQpwYXRpZW50X2V4dGVuc2lvbiA9ICh0KHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQlssMl0pKQpjb2xuYW1lcyhwYXRpZW50X2V4dGVuc2lvbikgPC0gcGF0aWVudF9iaW9tYXJrZXJfZGF0YV9ITURCJEhNREIKCiNBZGQgbGFiZWwgInBhdGllbnQnIGluIGZyb250IG9mIElELCBmb3IgY2xlYXJlciBkZXBpY3Rpb24gaW4gaGVhdG1hcAppZihwYXRpZW50SURfdHJlYXRtZW50WywyXSA9PSAxKXsgCiAgcGF0aWVudExhYmVsIDwtIHBhc3RlMCgiUGF0aWVudF8iLCBwYXRpZW50SUQsICIsIHRyZWF0ZWQiKQogIH1lbHNle3BhdGllbnRMYWJlbCA8LSBwYXN0ZTAoIlBhdGllbnRfIiwgcGF0aWVudElELCAiLCB1bnRyZWF0ZWQiKX0KCnJvd25hbWVzKHBhdGllbnRfZXh0ZW5zaW9uKSA8LSBwYXRpZW50TGFiZWwKCiNBZGQgcGF0aWVudCBkYXRhIHRvIG1hdHJpeAptYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnQgPC0gYWNhc3QocmJpbmQobWVsdChtYXRyaXhfcHJvdGVpbi5jYXN0KSwgbWVsdChwYXRpZW50X2V4dGVuc2lvbikpLCBYMX5YMiwgc3VtKSAKbWF0cml4X3Byb3RlaW4uY2FzdF9wYXRpZW50IDwtIG1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudFthcHBseShtYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnRbLC0xXSwgMSwgZnVuY3Rpb24oeCkgIWFsbCh4PT0wKSksXSAjcmVtb3ZlIGEgcm93IGlmIGFsbCB2YWx1ZXMgYXJlIHplcm8uCm1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudCA8LSBtYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnRbLCBjb2xTdW1zKG1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudCAhPSAwKSA+IDBdICNyZW1vdmUgYSBjb2x1bW4gaWYgYWxsIHZhbHVlcyBhcmUgemVyby4KCiNDcmVhdGUgYSBmaWxlIG5hbWUgc3BlY2lmaWMgZm9yIHBhdGllbnRJRApmaWxlbmFtZTIgPC0gcGFzdGUwKCJJbWFnZXMvIiwgcGF0aWVudElELCAiX2hlYXRtYXAucG5nIikKdGl0bGVfbmFtZTIgPC0gcGFzdGUwKCJCaW9tYXJrZXIgb3ZlcmxhcCBmb3IgdGhyZWUgSU1EIHR5cGVzLCBcbiBhZ2UgY2F0ZWdvcnk6ICIsIGFnZXJhbmdlLCAiXG4gZm9yIHBhdGllbnQ6IiwgcGF0aWVudElEKQoKIyBjcmVhdGVzIGEgNSB4IDUgaW5jaCBpbWFnZQpwbmcoZmlsZW5hbWUyLCAgICAjIGNyZWF0ZSBQTkcgZm9yIHRoZSBoZWF0IG1hcCAgICAgICAgCiAgd2lkdGggPSA1KjQwMCtucm93KHBhdGllbnRfYmlvbWFya2VyX2RhdGFfSE1EQikqMzAsICMgVXBkYXRlZCBiYXNlZCBvbiBoaWdoIG51bWJlciBvZiBiaW9tYXJrZXJzIGZvciBwYXRpZW50LgogIGhlaWdodCA9IDUqMzAwLAogIHJlcyA9IDI4MCwgICAgICAgICAgICAjIDMwMCBwaXhlbHMgcGVyIGluY2gKICBwb2ludHNpemUgPSA4KSAgICAgICAgIyBzbWFsbGVyIGZvbnQgc2l6ZQoKb3V0cHV0X3BhdGllbnQgPC0gaGVhdG1hcC4yKG1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudCwKICAjY2VsbG5vdGUgPSByb3VuZChtYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnQsMCksICAjIHNhbWUgZGF0YSBzZXQgZm9yIGNlbGwgbGFiZWxzLCByb3VuZCBvZmYgdG8gMSBkZWNpbWFsLgogIG1haW4gPSB0aXRsZV9uYW1lMiwgIyBoZWF0IG1hcCB0aXRsZQogIG5vdGVjb2w9ImJsYWNrIiwgICAgICAjIGNoYW5nZSBmb250IGNvbG9yIG9mIGNlbGwgbGFiZWxzIHRvIGJsYWNrCiAgY2VsbG5vdGUgPSBpZmVsc2UobWF0cml4X3Byb3RlaW4uY2FzdF9wYXRpZW50ICE9IDAsIHJvdW5kKG1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudCwxKSwgIiIpLCAjVmlzdWFsaXppbmcgb25seSBsYWJlbCBpZiBkYXRhIGlzIG5vdCB6ZXJvCiAgCiAgI0NvbG9yIHRoZSBzcGFjZSBiZXR3ZWVuIHRoZSBkYXRhIHBvaW50cwogIHNlcHdpZHRoPWMoMC4wMSwwLjAxKSwKICBzZXBjb2xvcj0ibGlnaHRncmF5IiwKICBjb2xzZXA9MTpuY29sKG1hdHJpeF9wcm90ZWluLmNhc3RfcGF0aWVudCksCiAgcm93c2VwPTE6bnJvdyhtYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnQpLAogIAogIFJvd1NpZGVDb2xvcnMgPSBjKCAgICAjIGdyb3VwaW5nIHJvdy12YXJpYWJsZXMgaW50byBkaWZmZXJlbnQgY2F0ZWdvcmllcywgZS5nLgogICAgIHJlcCgiZ3JleSIsIGxpbmVzX0NhdGVnb3J5X1B1cmluZSksICAgIyAgUHVyaW5lIGRpc2Vhc2VzCiAgICAgcmVwKCJibHVlIiwgbGluZXNfQ2F0ZWdvcnlfUHlyaW1pZGluZSksICAgICMgUHlyaW1pZGluZSBkaXNlYXNlcwogICAgIHJlcCgiYmxhY2siLCBsaW5lc19DYXRlZ29yeV9VcmVhKSwgICAgIyBVcmVhIGRpc2Vhc2VzCiAgICAgcmVwKCJwdXJwbGUiLCAxKSkgLCAjcGF0aWVudERhdGEKICAKICBkZW5zaXR5LmluZm89Im5vbmUiLCAgIyB0dXJucyBvZmYgZGVuc2l0eSBwbG90IGluc2lkZSBjb2xvciBsZWdlbmQKICB0cmFjZT0ibm9uZSIsICAgICAgICAgIyB0dXJucyBvZmYgdHJhY2UgbGluZXMgaW5zaWRlIHRoZSBoZWF0IG1hcAogIG1hcmdpbnMgPWMoMTIsOSksICAgICAjIHdpZGVucyBtYXJnaW5zIGFyb3VuZCBwbG90CiAgY29sPW15X3BhbGV0dGUsICAgICAgICMgdXNlIG9uIGNvbG9yIHBhbGV0dGUgZGVmaW5lZCBlYXJsaWVyCiAgYnJlYWtzPWNvbF9icmVha3MsICAgICMgZW5hYmxlIGNvbG9yIHRyYW5zaXRpb24gYXQgc3BlY2lmaWVkIGxpbWl0cwogIGRlbmRyb2dyYW09InJvdyIsICAgICAjIG9ubHkgZHJhdyBhIHJvdyBkZW5kcm9ncmFtCiAgQ29sdj0iTkEiKSAjLCAgICAgICAgICAgICMgdHVybiBvZmYgY29sdW1uIGNsdXN0ZXJpbmcKICAjbmEuY29sb3IgPSAiR3JleSIpICAgIyBjb2xvciBOQSB2YWx1ZXMgc3BlY2lmaWNhbGx5IC0tPiBkT0VTTidUIFdPUksgOigKICAKcGFyKGxlbmQgPSAxKSAgICAgICAgICAgIyBzcXVhcmUgbGluZSBlbmRzIGZvciB0aGUgY29sb3IgbGVnZW5kCmxlZ2VuZCgidG9wcmlnaHQiLCAgICAgICMgbG9jYXRpb24gb2YgdGhlIGxlZ2VuZCBvbiB0aGUgaGVhdG1hcCBwbG90CiAgICBsZWdlbmQgPSBjKCJQdXJpbmUiLCAiUHlyaW1pZGluZSIsICJVcmVhIGN5Y2xlIiwgIlBhdGllbnQiKSwgIyBjYXRlZ29yeSBsYWJlbHMKICAgIGNvbCA9IGMoImdyYXkiLCAiYmx1ZSIsICJibGFjayIsICJwdXJwbGUiKSwgIyBjb2xvciBrZXkKICAgIHRpdGxlID0gIklNRCBjbGFzc2VzIiwgICAgICAgICAgICMgbGVnZW5kIHRpdGxlCiAgICBsdHk9IDEsICAgICAgICAgICAgICMgbGluZSBzdHlsZQogICAgbHdkID0gMTAgICAgICAgICAgICAjIGxpbmUgd2lkdGgKKQogCgpkZXYub2ZmKCkgICAgICAgICAgICAgICAjIGNsb3NlIHRoZSBQTkcgZGV2aWNlCgojU2hvdyB0aGUgaGVhdG1hcCBhcyBvdXRwdXQgaW4gUk1EIGZpbGUKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoZmlsZW5hbWUyKQoKIyNQcmludCBuYW1lcyBvZiBtZXRhYm9saXRlcyBhZGRlZCBpbiBoZWF0bWFwIG9yaWdpbmF0aW5nIGZyb20gcGF0aWVudC4KIyMgQ2x1c3RlcmluZyBvcmRlciBtaWdodCBkaWZmZXIsIHNvIGRhdGEgbmVlZHMgdG8gYmUgcmVsb2FkZWQ6CgojI09idGFpbiB0aGUgb3JkZXIgb2YgdGhlIGRpc2Vhc2VzIGFuZCBiaW9tYXJrZXJzIGFzIGFwcGVhcmluZyBpbiB0aGUgY2x1c3RlcmVkIGhlYXRtYXA6CnZlcnRpY2FsQXhpc19wYXRpZW50IDwtIHJldihyb3duYW1lcyhtYXRyaXhfcHJvdGVpbi5jYXN0X3BhdGllbnQpW291dHB1dF9wYXRpZW50JHJvd0luZF0pCmhvcml6b250YWxBeGlzX3BhdGllbnQgPC0gY29sbmFtZXMobWF0cml4X3Byb3RlaW4uY2FzdF9wYXRpZW50KVtvdXRwdXRfcGF0aWVudCRjb2xJbmRdCgojI05ldyBkeW5hbWljIEZpZ3VyZSBjYXB0aW9uczoKIyMjRGlzZWFzZXMKZGlzb3JkZXJOYW1lc19wYXRpZW50IDwtIGxpc3QoKQoKZm9yIChqIGluIDE6bGVuZ3RoKHZlcnRpY2FsQXhpc19wYXRpZW50KSl7CiAgZm9yIChpIGluIDE6bnJvdyhkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMpKXsKICAgIGlmKGRmX2ZpZ3VyZV9jYXB0aW9uX2Rpc29yZGVyc1tpLDFdID09IHZlcnRpY2FsQXhpc19wYXRpZW50W2pdKXsKICAgICAgY29tYmluZURpc2Vhc2VIR05DX3BhdGllbnQgPC0gcGFzdGUwKHZlcnRpY2FsQXhpc19wYXRpZW50W2pdLCAnOiAnLCBkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnNbaSwyXSkKICAgICAgIGRpc29yZGVyTmFtZXNfcGF0aWVudFtqXSA8LSBjb21iaW5lRGlzZWFzZUhHTkNfcGF0aWVudAogICAgICB9CiAgICBlbHNlewogICAgICAjbm9EaXNlYXNlSUQgPC0gcGFzdGUodmVydGljYWxBeGlzX3BhdGllbnRbKGotMSldKSNwcmludCBtZXNzYWdlIHdoaWNoIElEIGNhbm5vdCBiZSBmb3VuZAogICAgICBuZXh0fQogIH0KfQpyZW1vdmUoaSxqKQojU2F2ZSBsaXN0IG9uIG9uZSBzdHJpbmcgZm9yIHJlcG9ydGluZyBwdXJwb3NlcwpvcmRlcmVkX2Rpc29yZGVyTmFtZXNfcGF0aWVudCA8LSBkby5jYWxsKHBhc3RlLCBjKGFzLmxpc3QoZGlzb3JkZXJOYW1lc19wYXRpZW50KSwgc2VwID0gIiwgIikpCm9yZGVyZWRfZGlzb3JkZXJOYW1lc19wYXRpZW50IDwtIHN0cl9yZXBsYWNlX2FsbChvcmRlcmVkX2Rpc29yZGVyTmFtZXNfcGF0aWVudCwgJyAsICcsICcgJyApCgojIyNUaGVvcmV0aWNhbCBCaW9tYXJrZXJzCmJpb21hcmtlck5hbWVzX3BhdGllbnQgPC0gbGlzdCgpCgpmb3IgKGogaW4gMTpsZW5ndGgoaG9yaXpvbnRhbEF4aXNfcGF0aWVudCkpewogIGZvciAoaSBpbiAxOm5yb3coZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2VycykpewogICAgaWYoZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2Vyc1tpLDJdID09IGhvcml6b250YWxBeGlzX3BhdGllbnRbal0pewogICAgICBjb21iaW5lYmlvbWFya2VySE1EQl9wYXRpZW50IDwtIHBhc3RlMChob3Jpem9udGFsQXhpc19wYXRpZW50W2pdLCAnOiAnLCBkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzW2ksMV0pCiAgICAgICBiaW9tYXJrZXJOYW1lc19wYXRpZW50W2pdIDwtIGNvbWJpbmViaW9tYXJrZXJITURCX3BhdGllbnQKICAgICAgfQogICAgZWxzZXtuZXh0fQogIH0KfQpyZW1vdmUoaSxqKQojU2F2ZSBsaXN0IG9uIG9uZSBzdHJpbmcgZm9yIHJlcG9ydGluZyBwdXJwb3NlcwpvcmRlcmVkX2Jpb21hcmtlck5hbWVzX3BhdGllbnQgPC0gZG8uY2FsbChwYXN0ZSwgYyhhcy5saXN0KGJpb21hcmtlck5hbWVzX3BhdGllbnQpLCBzZXAgPSAiLCAiKSkKCiMjUGF0aWVudC1zcGVjaWZpYyBCaW9tYXJrZXJzCmNvbXBhcmVfcGF0aWVudF90b190aGVvcmV0aWNhbE1hcmtlcnMgPC0gc2V0ZGlmZihob3Jpem9udGFsQXhpc19wYXRpZW50LCBkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzWywyXSkKCmJpb21hcmtlck5hbWVzX3BhdGllbnRzcGVjaWZpYyA8LSBsaXN0KCkKCmZvciAoaiBpbiAxOmxlbmd0aChjb21wYXJlX3BhdGllbnRfdG9fdGhlb3JldGljYWxNYXJrZXJzKSl7CiAgZm9yIChpIGluIDE6bnJvdyhkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzX3BhdGllbnRzcGVjaWZpYykpewogICAgaWYoZGZfZmlndXJlX2NhcHRpb25fYmlvbWFya2Vyc19wYXRpZW50c3BlY2lmaWNbaSwyXSA9PSBjb21wYXJlX3BhdGllbnRfdG9fdGhlb3JldGljYWxNYXJrZXJzW2pdKXsKICAgICAgY29tYmluZWJpb21hcmtlckhNREJfcGF0aWVudHNwZWNpZmljIDwtIHBhc3RlMChjb21wYXJlX3BhdGllbnRfdG9fdGhlb3JldGljYWxNYXJrZXJzW2pdLCAnOiAnLCBkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzX3BhdGllbnRzcGVjaWZpY1tpLDFdKQogICAgICAgYmlvbWFya2VyTmFtZXNfcGF0aWVudHNwZWNpZmljW2pdIDwtIGNvbWJpbmViaW9tYXJrZXJITURCX3BhdGllbnRzcGVjaWZpYwogICAgICB9CiAgICBlbHNle25leHR9CiAgfQp9CnJlbW92ZShpLGopCiNTYXZlIGxpc3Qgb24gb25lIHN0cmluZyBmb3IgcmVwb3J0aW5nIHB1cnBvc2VzCm9yZGVyZWRfYmlvbWFya2VyTmFtZXNfcGF0aWVudHNwZWNpZmljIDwtIGRvLmNhbGwocGFzdGUsIGMoYXMubGlzdChiaW9tYXJrZXJOYW1lc19wYXRpZW50c3BlY2lmaWMpLCBzZXAgPSAiLCAiKSkKCiNkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzCgojUHJpbnQgRmlndXJlIGNhcHRpb24gKHNvcnRlZCBkaXNlYXNlcyBhbmQgdW5zb3J0ZWQgbWV0YWJvbGl0ZXMpCnBhc3RlMCgiUHJvdGVpbiBuYW1lcyBjb3JyZXNwb25kIHRvIHRoZSBmb2xsb3dpbmcgZGlzb3JkZXJzOiAiLCBvcmRlcmVkX2Rpc29yZGVyTmFtZXNfcGF0aWVudCwgIi4gIikKcGFzdGUwKCJITURCIElEcyByZXNlbWJsZSB0aGVzZSBtZXRhYm9saXRlczogIiwgb3JkZXJlZF9iaW9tYXJrZXJOYW1lc19wYXRpZW50LCAiLiAiKQpwYXN0ZTAoIkFkZGl0aW9uYWwgYmlvbWFya2VycyByZWxldmFudCBmb3IgdGhpcyBzcGVjaWZpYyBwYXRpZW50IChub3QgcGFydCBvZiBJRU1iYXNlIGRhdGEpOiAiLCBvcmRlcmVkX2Jpb21hcmtlck5hbWVzX3BhdGllbnRzcGVjaWZpYywgIi4gIikKCnJlbW92ZShiaW9tYXJrZXJOYW1lcywgYmlvbWFya2VyTmFtZXNfcGF0aWVudCwgYmlvbWFya2VyTmFtZXNfcGF0aWVudHNwZWNpZmljLCBkZl9maWd1cmVfY2FwdGlvbl9iaW9tYXJrZXJzLCBkZl9maWd1cmVfY2FwdGlvbl9kaXNvcmRlcnMsIGRmX2ZpZ3VyZV9jYXB0aW9uX2Jpb21hcmtlcnNfcGF0aWVudHNwZWNpZmljLCBkaXNvcmRlck5hbWVzLCBkaXNvcmRlck5hbWVzX3BhdGllbnQsIGhvcml6b250YWxBeGlzLCBob3Jpem9udGFsQXhpc19wYXRpZW50LCB2ZXJ0aWNhbEF4aXMsIHZlcnRpY2FsQXhpc19wYXRpZW50LCBvcmRlcmVkX2Jpb21hcmtlck5hbWVzX3BhdGllbnQsIG9yZGVyZWRfYmlvbWFya2VyTmFtZXNfcGF0aWVudHNwZWNpZmljLCBvcmRlcmVkX2Rpc29yZGVyTmFtZXNfcGF0aWVudCwgY29tcGFyZV9wYXRpZW50X3RvX3RoZW9yZXRpY2FsTWFya2VycywgY29tYmluZWJpb21hcmtlckhNREIsIGNvbWJpbmViaW9tYXJrZXJITURCX3BhdGllbnQsIGNvbWJpbmViaW9tYXJrZXJITURCX3BhdGllbnRzcGVjaWZpYywgY29tYmluZURpc2Vhc2VIR05DLCBjb21iaW5lRGlzZWFzZUhHTkNfcGF0aWVudCkKCmBgYAoKIyAyLjcgUGF0aHdheSBTZWxlY3Rpb24KCkZpcnN0LCBmaW5kIHJlbGV2YW50IHBhdGh3YXkgZnJvbSBXaWtpUGF0aHdheXMgKHNvcnRlZCBvbiBjb3ZlcmluZyBtb3N0IHBhdGllbnQgc3BlY2lmaWMgYmlvbWFya2VycykKCmBgYHtyfQojRm9yIG5vdywgZmlsdGVyIG91dCBSZWFjdG9tZSBQV3MgZHVlIHRvIHZpc3VhbGl6YXRpb24gaXNzdWVzLgppdGVtMSA9ICJQUkVGSVggY2g6PGh0dHBzOi8vaWRlbnRpZmllcnMub3JnL2NoZWJpL0NIRUJJOj4KUFJFRklYIGN1cjogPGh0dHA6Ly92b2NhYnVsYXJpZXMud2lraXBhdGh3YXlzLm9yZy93cCNDdXJhdGlvbjo+CnNlbGVjdCBkaXN0aW5jdCA/cGF0aHdheVJlcyAoc3RyKD93cGlkKSBhcyA/cGF0aHdheSkgKHN0cig/dGl0bGUpIGFzID9wYXRod2F5VGl0bGUpIChjb3VudChkaXN0aW5jdCA/Y2hlYmlNZXRhYm9saXRlKSBBUyA/Q0hFQklzSW5QV3MpIChHUk9VUF9DT05DQVQoRElTVElOQ1QgZm46c3Vic3RyaW5nKD9oZ25jLDM3KTtzZXBhcmF0b3I9JyAnKSBBUyA/UHJvdGVpbnMpIChHUk9VUF9DT05DQVQoRElTVElOQ1QgZm46c3Vic3RyaW5nKD9jaGViaU1ldGFib2xpdGUsMzcpO3NlcGFyYXRvcj0nICcpIEFTID9pbmNsdWRlZENIRUJJcykKd2hlcmUgewpWQUxVRVMgP2NoZWJpTWV0YWJvbGl0ZSB7IgoKaXRlbTIgPSAifQogCiA/ZGF0YW5vZGUJYSB3cDpNZXRhYm9saXRlIDsgICAgICAgICAgCiAgICAgICAgICAgCXdwOmJkYkNoRUJJID9jaGViaU1ldGFib2xpdGUgOwogICAgCQlkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4KCiA/cGF0aHdheVJlcyBhIHdwOlBhdGh3YXkgOwogICAgICAgICAgICAgd3A6b3JnYW5pc21OYW1lICdIb21vIHNhcGllbnMnIDsgCiAgICAJCWRjdGVybXM6aWRlbnRpZmllciA/d3BpZCA7CiAgICAJCWRjOnRpdGxlID90aXRsZSAuCiAgICAJCQogP2RhdGFub2RlMiB3cDpiZGJIZ25jU3ltYm9sID9oZ25jIDsKICAgIAkJZGN0ZXJtczppc1BhcnRPZiA/cGF0aHdheVJlcyAuCiAgICAJCQogICM/cGF0aHdheVJlcyB3cDpvbnRvbG9neVRhZyBjdXI6UmVhY3RvbWVfQXBwcm92ZWQgLiAKICA/cGF0aHdheVJlcyB3cDpvbnRvbG9neVRhZyBjdXI6QW5hbHlzaXNDb2xsZWN0aW9uIC4gICAJCQp9CgpPUkRFUiBCWSBERVNDKD9DSEVCSXNJblBXcykgIgoKcXVlcnlfQ29tYmluZVBXcyA8LSBwYXN0ZShpdGVtMSxzdHJpbmdfQ0hFQkksaXRlbTIpCnJlbW92ZShpdGVtMSwgaXRlbTIpCnJlc3VsdHNfQ29tYmluZVBXcyA8LSBTUEFSUUwoZW5kcG9pbnR3cCxxdWVyeV9Db21iaW5lUFdzLGN1cmxfYXJncz1saXN0KHVzZXJhZ2VudD1SLnZlcnNpb24uc3RyaW5nKSkKc2hvd3Jlc3VsdHNfQ29tYmluZVBXcyA8LSByZXN1bHRzX0NvbWJpbmVQV3MkcmVzdWx0cwpyZW1vdmUocXVlcnlfQ29tYmluZVBXcyxyZXN1bHRzX0NvbWJpbmVQV3MpCgojUHJpbnQgdGFibGUgd2l0aCBmaXJzdCA1IHJlbGV2YW50IHBhdGh3YXlzIChpZiBsZXNzIHRoYW4gNSBhcmUgZm91bmQsIHByaW50IG9ubHkgdGhvc2UpCmlmKG5yb3coc2hvd3Jlc3VsdHNfQ29tYmluZVBXcykgPCA1KXsKcHJpbnQoc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmRbMTpucm93KHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfU2Vjb25kKSxjKDI6NCw2LDUpXSkKfWVsc2V7cHJpbnQoc2hvd3Jlc3VsdHNfQ29tYmluZVBXc1sxOjUsYygyOjQsNiw1KV0pfQpgYGAKClNlbGVjdCBtb3N0IHJlbGV2YW50IHBhdGh3YXlzCgpgYGB7cn0KIyMgTm90ZSBmb3IgdXNlcjogcGljayBhIGRpZmZlcmVudCBudW1iZXIgaGVyZSwgaWYgYW5vdGhlciBwYXRod2F5IHRoYW4gdGhlIGZpcnN0IGlzIGRlZW1lZCBtb3N0IHJlbGV2YW50IChkZWZhdWx0IGlzIHRvcCBwYXRod2F5KQpmaXJzdF9zZWxlY3RlZF9wYXRod2F5ID0gMQpgYGAKClNlY29uZCwgZmluZCBzZWNvbmQgcGF0aHdheSBjb3ZlcmluZyBtb3N0IHVuaXF1ZSBiaW9tYXJrZXJzOgoKYGBge3J9CiNGaXJzdCwgb2J0YWluIGFsbCB0aGUgQ2hFQkkgSURzIG9mIHRoZSBmaXJzdCByYW5rZWQgcGF0aHdheSwgY29tcGFyZSB0aGVzZSB0byB0aGUgbGlzdCBvZiByZW1haW5pbmcgbWFya2VycyBhbmQgb25seSByZXRhaW4gdGhlIG1hcmtlciB3aGljaCB3ZXJlbid0IGNvdmVyZWQgeWV0CmNhdGNoX2NoZWJpc19maXJzdFBXIDwtIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NbZmlyc3Rfc2VsZWN0ZWRfcGF0aHdheSw2XQpzcGxpdF9jaGViaXNfZmlyc3RQVyA8LSBhcy5saXN0KHN0cnNwbGl0KGNhdGNoX2NoZWJpc19maXJzdFBXLCAnICcpW1sxXV0pCnNwbGl0X3N0cmluZ19DSEVCSSA8LSBhcy5saXN0KHN0cnNwbGl0KHN0cmluZ19DSEVCSSwgJyAnKVtbMV1dKQpjbGVhbmVkX3N0cmluZ19DSEVCSSA8LSBhcy5saXN0KHN0cl9yZXBsYWNlKHNwbGl0X3N0cmluZ19DSEVCSSwgImNoOiIsICIiKSkKY29tcGFyZV9GaXJzdFNlY29uZF9DSEVCSSA8LSBzZXRkaWZmKGNsZWFuZWRfc3RyaW5nX0NIRUJJLCBzcGxpdF9jaGViaXNfZmlyc3RQVykKcXVlcnlfY29tcGF0aWJsZV9zZWNvbmQgPC0gYXMubGlzdChwYXN0ZSgiY2g6IiwgY29tcGFyZV9GaXJzdFNlY29uZF9DSEVCSSwgc2VwPSIiKSkKc3RyaW5nX0NIRUJJX3NlY29uZCA8LSBwYXN0ZShxdWVyeV9jb21wYXRpYmxlX3NlY29uZCwgY29sbGFwc2UgPSAnICcpCnJlbW92ZShjYXRjaF9jaGViaXNfZmlyc3RQVyxzcGxpdF9jaGViaXNfZmlyc3RQVyxzcGxpdF9zdHJpbmdfQ0hFQkksY2xlYW5lZF9zdHJpbmdfQ0hFQkksY29tcGFyZV9GaXJzdFNlY29uZF9DSEVCSSxxdWVyeV9jb21wYXRpYmxlX3NlY29uZCkKCml0ZW0xID0gIlBSRUZJWCBjaDo8aHR0cHM6Ly9pZGVudGlmaWVycy5vcmcvY2hlYmkvQ0hFQkk6PgpQUkVGSVggY3VyOiA8aHR0cDovL3ZvY2FidWxhcmllcy53aWtpcGF0aHdheXMub3JnL3dwI0N1cmF0aW9uOj4Kc2VsZWN0IGRpc3RpbmN0ID9wYXRod2F5UmVzIChzdHIoP3dwaWQpIGFzID9wYXRod2F5KSAoc3RyKD90aXRsZSkgYXMgP3BhdGh3YXlUaXRsZSkgKGNvdW50KGRpc3RpbmN0ID9jaGViaU1ldGFib2xpdGUpIEFTID9DSEVCSXNJblBXcykgKGNvdW50KGRpc3RpbmN0ID9jaGViaU1ldGFib2xpdGVTZWNvbmQpIEFTID9DSEVCSXNJblBXc1NlY29uZCkgKEdST1VQX0NPTkNBVChESVNUSU5DVCBmbjpzdWJzdHJpbmcoP2hnbmMsMzcpO3NlcGFyYXRvcj0nICcpIEFTID9Qcm90ZWlucykgKEdST1VQX0NPTkNBVChESVNUSU5DVCBmbjpzdWJzdHJpbmcoP2NoZWJpTWV0YWJvbGl0ZSwzNyk7c2VwYXJhdG9yPScgJykgQVMgP2luY2x1ZGVkQ0hFQklzKQp3aGVyZSB7ClZBTFVFUyA/Y2hlYmlNZXRhYm9saXRlIHsiCgppdGVtMiA9ICJ9ClZBTFVFUyA/Y2hlYmlNZXRhYm9saXRlU2Vjb25kIHsiCgppdGVtMyA9ICJ9CiAKID9kYXRhbm9kZQlhIHdwOk1ldGFib2xpdGUgOyAgICAgICAgICAKICAgICAgICAgICAJd3A6YmRiQ2hFQkkgP2NoZWJpTWV0YWJvbGl0ZSA7CiAgICAJCWRjdGVybXM6aXNQYXJ0T2YgP3BhdGh3YXlSZXMgLgoKID9wYXRod2F5UmVzIGEgd3A6UGF0aHdheSA7CiAgICAgICAgICAgICB3cDpvcmdhbmlzbU5hbWUgJ0hvbW8gc2FwaWVucycgOyAKICAgIAkJZGN0ZXJtczppZGVudGlmaWVyID93cGlkIDsKICAgIAkJZGM6dGl0bGUgP3RpdGxlIC4KICAgIAkJCiA/ZGF0YW5vZGUyIHdwOmJkYkhnbmNTeW1ib2wgP2hnbmMgOwogICAgCQlkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4KICAgIAkJCiAgIz9wYXRod2F5UmVzIHdwOm9udG9sb2d5VGFnIGN1cjpSZWFjdG9tZV9BcHByb3ZlZCAuIAogID9wYXRod2F5UmVzIHdwOm9udG9sb2d5VGFnIGN1cjpBbmFseXNpc0NvbGxlY3Rpb24gLiAgIAogIAogID9kYXRhbm9kZQlhIHdwOk1ldGFib2xpdGUgOyAgICAgICAgICAKICAgICAgICAgICAJd3A6YmRiQ2hFQkkgP2NoZWJpTWV0YWJvbGl0ZVNlY29uZCA7CiAgICAJCWRjdGVybXM6aXNQYXJ0T2YgP3BhdGh3YXlSZXMgLgp9CgpPUkRFUiBCWSBERVNDKD9DSEVCSXNJblBXc1NlY29uZCkgIgoKcXVlcnlfQ29tYmluZVBXc19TZWNvbmQgPC0gcGFzdGUoaXRlbTEsc3RyaW5nX0NIRUJJLGl0ZW0yLCBzdHJpbmdfQ0hFQklfc2Vjb25kLCBpdGVtMykKcmVtb3ZlKGl0ZW0xLCBpdGVtMiwgaXRlbTMpCgpyZXN1bHRzX0NvbWJpbmVQV3NfU2Vjb25kIDwtIFNQQVJRTChlbmRwb2ludHdwLHF1ZXJ5X0NvbWJpbmVQV3NfU2Vjb25kLGN1cmxfYXJncz1saXN0KHVzZXJhZ2VudD1SLnZlcnNpb24uc3RyaW5nKSkKc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmQgPC0gcmVzdWx0c19Db21iaW5lUFdzX1NlY29uZCRyZXN1bHRzCnJlbW92ZShxdWVyeV9Db21iaW5lUFdzX1NlY29uZCxyZXN1bHRzX0NvbWJpbmVQV3NfU2Vjb25kKQoKI1ByaW50IHRhYmxlIHdpdGggc2Vjb25kIG1hdGNoLCBmaXJzdCA1IHJlbGV2YW50IHBhdGh3YXlzCnByaW50KCJTZWNvbmQgYmVzdCBtYXRjaGluZyBwYXRod2F5cyBhcmU6IikKaWYobnJvdyhzaG93cmVzdWx0c19Db21iaW5lUFdzX1NlY29uZCkgPCA1KXsKcHJpbnQoc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmRbMTpucm93KHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfU2Vjb25kKSxjKDIsMyw0LDUsNyldKQp9ZWxzZXtwcmludChzaG93cmVzdWx0c19Db21iaW5lUFdzX1NlY29uZFsxOjUsYygyLDMsNCw1LDcpXSl9CmBgYAoKU2VsZWN0IDJuZCBtb3N0IHJlbGV2YW50IHBhdGh3YXlzCgpgYGB7cn0KIyMgTm90ZSBmb3IgdXNlcjogcGljayBhIGRpZmZlcmVudCBudW1iZXIgaGVyZSwgaWYgYW5vdGhlciBwYXRod2F5IHRoYW4gdGhlIGZpcnN0IGlzIGRlZW1lZCBtb3N0IHJlbGV2YW50IChkZWZhdWx0IGlzIHRvcCBwYXRod2F5KQpzZWNvbmRfc2VsZWN0ZWRfcGF0aHdheSA9IDEKYGBgCgpUaGlyZCwgZmluZCB0aGlyZCBwYXRod2F5IGNvdmVyaW5nIG1vc3QgdW5pcXVlIGJpb21hcmtlcnM6CgpgYGB7cn0KI0ZpcnN0LCBvYnRhaW4gYWxsIHRoZSBDaEVCSSBJRHMgb2YgdGhlIHNlY29uZCBoaWdoZXN0IHJhbmtlZCBwYXRod2F5LCBjb21wYXJlIHRoZXNlIHRvIHRoZSBsaXN0IG9mIG1hcmtlcnMgcmVtYWluaW5nIGFuZCBvbmx5IHJldGFpbiB0aGUgbWFya2VyIHdoaWNoIHdlcmVuJ3QgY292ZXJlZCB5ZXQKY2F0Y2hfY2hlYmlzX3NlY29uZFBXIDwtIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfU2Vjb25kW3NlY29uZF9zZWxlY3RlZF9wYXRod2F5LDddCnNwbGl0X2NoZWJpc19zZWNvbmRQVyA8LSBhcy5saXN0KHN0cnNwbGl0KGNhdGNoX2NoZWJpc19zZWNvbmRQVywgJyAnKVtbMV1dKQpzcGxpdF9zdHJpbmdfQ0hFQklfc2Vjb25kIDwtIGFzLmxpc3Qoc3Ryc3BsaXQoc3RyaW5nX0NIRUJJX3NlY29uZCwgJyAnKVtbMV1dKQpjbGVhbmVkX3N0cmluZ19DSEVCSV9zZWNvbmQgPC0gYXMubGlzdChzdHJfcmVwbGFjZShzcGxpdF9zdHJpbmdfQ0hFQklfc2Vjb25kLCAiY2g6IiwgIiIpKQpjb21wYXJlX1NlY29uZFRoaXJkX0NIRUJJIDwtIHNldGRpZmYoY2xlYW5lZF9zdHJpbmdfQ0hFQklfc2Vjb25kLCBzcGxpdF9jaGViaXNfc2Vjb25kUFcpCnF1ZXJ5X2NvbXBhdGlibGVfdGhpcmQgPC0gYXMubGlzdChwYXN0ZSgiY2g6IiwgY29tcGFyZV9TZWNvbmRUaGlyZF9DSEVCSSwgc2VwPSIiKSkKc3RyaW5nX0NIRUJJX3RoaXJkIDwtIHBhc3RlKHF1ZXJ5X2NvbXBhdGlibGVfdGhpcmQsIGNvbGxhcHNlID0gJyAnKQpyZW1vdmUoY2F0Y2hfY2hlYmlzX3NlY29uZFBXLHNwbGl0X2NoZWJpc19zZWNvbmRQVyxzcGxpdF9zdHJpbmdfQ0hFQklfc2Vjb25kLGNsZWFuZWRfc3RyaW5nX0NIRUJJX3NlY29uZCxjb21wYXJlX1NlY29uZFRoaXJkX0NIRUJJLHF1ZXJ5X2NvbXBhdGlibGVfdGhpcmQpCgojUmVtb3ZlZCBwcm90ZWluIG91dHB1dCwgcXVlcnkgdGltZXMgb3V0LgppdGVtMSA9ICJQUkVGSVggY2g6PGh0dHBzOi8vaWRlbnRpZmllcnMub3JnL2NoZWJpL0NIRUJJOj4KUFJFRklYIGN1cjogPGh0dHA6Ly92b2NhYnVsYXJpZXMud2lraXBhdGh3YXlzLm9yZy93cCNDdXJhdGlvbjo+CnNlbGVjdCBkaXN0aW5jdCA/cGF0aHdheVJlcyAoc3RyKD93cGlkKSBhcyA/cGF0aHdheSkgKHN0cig/dGl0bGUpIGFzID9wYXRod2F5VGl0bGUpIChjb3VudChkaXN0aW5jdCA/Y2hlYmlNZXRhYm9saXRlKSBBUyA/Q0hFQklzSW5QV3MpIChjb3VudChkaXN0aW5jdCA/Y2hlYmlNZXRhYm9saXRlU2Vjb25kKSBBUyA/Q0hFQklzSW5QV3NTZWNvbmQpIChjb3VudChkaXN0aW5jdCA/Y2hlYmlNZXRhYm9saXRlVGhpcmQpIEFTID9DSEVCSXNJblBXc1RoaXJkKSAoR1JPVVBfQ09OQ0FUKERJU1RJTkNUIGZuOnN1YnN0cmluZyg/Y2hlYmlNZXRhYm9saXRlLDM3KTtzZXBhcmF0b3I9JyAnKSBBUyA/aW5jbHVkZWRDSEVCSXMpCndoZXJlIHsKVkFMVUVTID9jaGViaU1ldGFib2xpdGUgeyIKCml0ZW0yID0gIn0KVkFMVUVTID9jaGViaU1ldGFib2xpdGVTZWNvbmQgeyIKCml0ZW0zID0gIn0KVkFMVUVTID9jaGViaU1ldGFib2xpdGVUaGlyZCB7IgoKaXRlbTQgPSAifQogCiA/ZGF0YW5vZGUJYSB3cDpNZXRhYm9saXRlIDsgICAgICAgICAgCiAgICAgICAgICAgCXdwOmJkYkNoRUJJID9jaGViaU1ldGFib2xpdGUgOwogICAgCQlkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4KCiA/cGF0aHdheVJlcyBhIHdwOlBhdGh3YXkgOwogICAgICAgICAgICAgd3A6b3JnYW5pc21OYW1lICdIb21vIHNhcGllbnMnIDsgCiAgICAJCWRjdGVybXM6aWRlbnRpZmllciA/d3BpZCA7CiAgICAJCWRjOnRpdGxlID90aXRsZSAuCiAgICAJCQogICM/cGF0aHdheVJlcyB3cDpvbnRvbG9neVRhZyBjdXI6UmVhY3RvbWVfQXBwcm92ZWQgLiAKICA/cGF0aHdheVJlcyB3cDpvbnRvbG9neVRhZyBjdXI6QW5hbHlzaXNDb2xsZWN0aW9uIC4gICAKICAKICA/ZGF0YW5vZGUJYSB3cDpNZXRhYm9saXRlIDsgICAgICAgICAgCiAgICAgICAgICAgCXdwOmJkYkNoRUJJID9jaGViaU1ldGFib2xpdGVTZWNvbmQgOwogICAgCQlkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4KICAgIAkJCiAgP2RhdGFub2RlCWEgd3A6TWV0YWJvbGl0ZSA7ICAgICAgICAgIAogICAgICAgICAgIAl3cDpiZGJDaEVCSSA/Y2hlYmlNZXRhYm9saXRlVGhpcmQgOwogICAgCQlkY3Rlcm1zOmlzUGFydE9mID9wYXRod2F5UmVzIC4gIAkJCn0KCk9SREVSIEJZIERFU0MoP0NIRUJJc0luUFdzVGhpcmQpICIKCnF1ZXJ5X0NvbWJpbmVQV3NfVGhpcmQgPC0gcGFzdGUoaXRlbTEsc3RyaW5nX0NIRUJJLGl0ZW0yLCBzdHJpbmdfQ0hFQklfc2Vjb25kLCBpdGVtMywgc3RyaW5nX0NIRUJJX3RoaXJkLCBpdGVtNCkKcmVtb3ZlKGl0ZW0xLCBpdGVtMiwgaXRlbTMsIGl0ZW00KQoKcmVzdWx0c19Db21iaW5lUFdzX1RoaXJkPC0gU1BBUlFMKGVuZHBvaW50d3AscXVlcnlfQ29tYmluZVBXc19UaGlyZCxjdXJsX2FyZ3M9bGlzdCh1c2VyYWdlbnQ9Ui52ZXJzaW9uLnN0cmluZykpCnNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmQgPC0gcmVzdWx0c19Db21iaW5lUFdzX1RoaXJkJHJlc3VsdHMKcmVtb3ZlKHF1ZXJ5X0NvbWJpbmVQV3NfVGhpcmQscmVzdWx0c19Db21iaW5lUFdzX1RoaXJkKQoKI1ByaW50IHRhYmxlIHdpdGggdGhpcmQgbWF0Y2gsIGZpcnN0IDUgcmVsZXZhbnQgcGF0aHdheXMgKGlmIHRoZXJlIGFyZSA1KQojI1RPRE86IHNraXAgbGluZXMgYmVsb3csIGlmIHRoZXJlIGlzIG5vIHJlbGV2YW50IHRoaXJkIGJlc3QgUFcuIChwYXRpZW50IEkgZm9yIGV4YW1wbGUpCnByaW50KCJUaGlyZCBiZXN0IG1hdGNoaW5nIHBhdGh3YXlzIGFyZToiKQppZihucm93KHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmQpIDwgMSl7CnBhc3RlKCJObyBtb3JlIHBhdGh3YXkgbW9kZWxzIGZvdW5kIikKfWVsc2UgaWYobnJvdyhzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkKSA8IDUpewpwcmludChzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkWzE6bnJvdyhzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkKSxjKDIsMyw0OjcpXSkKICB9ZWxzZXtwcmludChzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkWzE6NSxjKDIsMyw0OjcpXSl9CgpgYGAKClNlbGVjdCAzbmQgbW9zdCByZWxldmFudCBwYXRod2F5cwoKYGBge3J9CmlmKG5yb3coc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19UaGlyZCkgPCAxKXsKICB0aGlyZF9zZWxlY3RlZF9wYXRod2F5ID0gMApwYXN0ZSgiU2tpcHBpbmcgdGhpcyBzdGVwIil9ZWxzZXsKdGhpcmRfc2VsZWN0ZWRfcGF0aHdheSA9IDEgIyMgTm90ZSBmb3IgdXNlcjogcGljayBhIGRpZmZlcmVudCBudW1iZXIgaGVyZSwgaWYgYW5vdGhlciBwYXRod2F5IHRoYW4gdGhlIGZpcnN0IGlzIGRlZW1lZCBtb3N0IHJlbGV2YW50IChkZWZhdWx0IGlzIHRvcCBwYXRod2F5KQp9CmBgYAoKRm91cnRoLCBmaW5kIHdoaWNoIGJpb21hcmtlcnMgYXJlIG5vdCBjb3ZlcmVkIGJ5IHRvcCB0aHJlZSAob3IgdHdvKS4KCmBgYHtyfQojRmlyc3QsIG9idGFpbiBhbGwgdGhlIENoRUJJIElEcyBvZiB0aGUgdGhpcmQgaGlnaGVzdCByYW5rZWQgcGF0aHdheSwgY29tcGFyZSB0aGVzZSB0byB0aGUgbGlzdCBvZiByZW1haW5pbmcgbWFya2VycyBhbmQgb25seSByZXRhaW4gdGhlIG1hcmtlciB3aGljaCB3ZXJlbid0IGNvdmVyZWQgeWV0CmlmKHRoaXJkX3NlbGVjdGVkX3BhdGh3YXkgPCAxKXsKcGFzdGUoIlVzaW5nIElEcyBmcm9tIHN0ZXAgMiBpc28gc3RlcCAzLCByZWxldmFudCBwYXRod2F5IG1vZGVscyBhcmU6IikKc3BsaXRfc3RyaW5nX0NIRUJJX1RoaXJkIDwtIGFzLmxpc3Qoc3Ryc3BsaXQoc3RyaW5nX0NIRUJJX3RoaXJkLCAnICcpW1sxXV0pCiBjbGVhbmVkX3N0cmluZ19DSEVCSV9UaGlyZCA8LSBhcy5saXN0KHN0cl9yZXBsYWNlKHNwbGl0X3N0cmluZ19DSEVCSV9UaGlyZCwgImNoOiIsICIiKSkKIyNVc2luZyBJRHMgZnJvbSB0aGlyZCBzdGVwLCBpZiBubyB0aGlyZCBQV00gaXMgc2VsZWN0ZWQKc3RyaW5nX0NIRUJJX3RoaXJkX2ZpbmFsIDwtIHBhc3RlKGNsZWFuZWRfc3RyaW5nX0NIRUJJX1RoaXJkLCBjb2xsYXBzZSA9ICcsICcpCgojRmluZCBuYW1lcyBmb3IgSURzIHdoaWNoIGFyZSBwcmVzZW50IGluIGEgcGF0aHdheSBtb2RlbCAoaWYgdHdvIHBhdGh3YXlzIHdlcmUgc2VsZWN0ZWQpOgpDSEVCSV9taXNzaW5nTmFtZXNfcGF0aHdheXNfdGhpcmQgPC0gZ2x1ZTo6Z2x1ZSgiQ0hFQkk6e2NsZWFuZWRfc3RyaW5nX0NIRUJJX1RoaXJkfSIpCm1pc3NpbmdOYW1lc19wYXRod2F5c190aGlyZCA8LSBsaXN0KCkKZm9yIChqIGluIDE6bGVuZ3RoKENIRUJJX21pc3NpbmdOYW1lc19wYXRod2F5c190aGlyZCkpewogIGZvciAoaSBpbiAxOm5yb3coYW5ub3RhdGVkX2JvdGhfbm96ZXJvKSl7CiAgICBpZihhbm5vdGF0ZWRfYm90aF9ub3plcm9baSw0XSA9PSBDSEVCSV9taXNzaW5nTmFtZXNfcGF0aHdheXNfdGhpcmRbal0pewogICAgICAgbWlzc2luZ05hbWVzX3BhdGh3YXlzX3RoaXJkW2pdIDwtIGFubm90YXRlZF9ib3RoX25vemVyb1tpLDNdCiAgICAgIH0KICAgIGVsc2V7bmV4dH0KICB9Cn0KcmVtb3ZlKGksaikKI1NhdmUgbGlzdCBvbiBvbmUgc3RyaW5nIGZvciByZXBvcnRpbmcgcHVycG9zZXMKc3RyaW5nX21pc3NpbmdOYW1lc19wYXRod2F5c190aGlyZCA8LSBkby5jYWxsKHBhc3RlLCBjKGFzLmxpc3QobWlzc2luZ05hbWVzX3BhdGh3YXlzX3RoaXJkKSwgc2VwID0gIiwgIikpCgogIH1lbHNlewpjYXRjaF9jaGViaXNfVGhpcmRQVyA8LSBzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkW3RoaXJkX3NlbGVjdGVkX3BhdGh3YXksN10Kc3BsaXRfY2hlYmlzX1RoaXJkUFcgPC0gYXMubGlzdChzdHJzcGxpdChjYXRjaF9jaGViaXNfVGhpcmRQVywgJyAnKVtbMV1dKQpzcGxpdF9zdHJpbmdfQ0hFQklfVGhpcmQgPC0gYXMubGlzdChzdHJzcGxpdChzdHJpbmdfQ0hFQklfdGhpcmQsICcgJylbWzFdXSkKY2xlYW5lZF9zdHJpbmdfQ0hFQklfVGhpcmQgPC0gYXMubGlzdChzdHJfcmVwbGFjZShzcGxpdF9zdHJpbmdfQ0hFQklfVGhpcmQsICJjaDoiLCAiIikpCiMjRmluZCBJRHMgc3RpbGwgbWlzc2luZyBhZnRlciBzZWxlY3RpbmcgdG9wIDMgcGF0aHdheSBtb2RlbHMuCmNvbXBhcmVfVGhpcmRGb3VydGhfQ0hFQkkgPC0gc2V0ZGlmZihjbGVhbmVkX3N0cmluZ19DSEVCSV9UaGlyZCwgc3BsaXRfY2hlYmlzX1RoaXJkUFcpCnF1ZXJ5X2NvbXBhdGlibGVfZm91cnRoIDwtIGFzLmxpc3QoY29tcGFyZV9UaGlyZEZvdXJ0aF9DSEVCSSwgc2VwPSIiKQpzdHJpbmdfQ0hFQklfZm91cnRoIDwtIHBhc3RlKHF1ZXJ5X2NvbXBhdGlibGVfZm91cnRoLCBjb2xsYXBzZSA9ICcsICcpCgpyZW1vdmUoY2F0Y2hfY2hlYmlzX1RoaXJkUFcsc3BsaXRfc3RyaW5nX0NIRUJJX1RoaXJkLGNvbXBhcmVfVGhpcmRGb3VydGhfQ0hFQkkpCgojRmluZCBuYW1lcyBmb3IgSURzIHdoaWNoIGFyZSBwcmVzZW50IGluIGEgcGF0aHdheSBtb2RlbCAoaWYgdGhyZWUgcGF0aHdheXMgd2VyZSBzZWxlY3RlZCk6CkNIRUJJX21pc3NpbmdOYW1lc19wYXRod2F5cyA8LSBnbHVlOjpnbHVlKCJDSEVCSTp7cXVlcnlfY29tcGF0aWJsZV9mb3VydGh9IikKbWlzc2luZ05hbWVzX3BhdGh3YXlzIDwtIGxpc3QoKQpmb3IgKGogaW4gMTpsZW5ndGgoQ0hFQklfbWlzc2luZ05hbWVzX3BhdGh3YXlzKSl7CiAgZm9yIChpIGluIDE6bnJvdyhhbm5vdGF0ZWRfYm90aF9ub3plcm8pKXsKICAgIGlmKGFubm90YXRlZF9ib3RoX25vemVyb1tpLDRdID09IENIRUJJX21pc3NpbmdOYW1lc19wYXRod2F5c1tqXSl7CiAgICAgICBtaXNzaW5nTmFtZXNfcGF0aHdheXNbal0gPC0gYW5ub3RhdGVkX2JvdGhfbm96ZXJvW2ksM10KICAgICAgfQogICAgZWxzZXtuZXh0fQogIH0KfQpyZW1vdmUoaSxqKQojU2F2ZSBsaXN0IG9uIG9uZSBzdHJpbmcgZm9yIHJlcG9ydGluZyBwdXJwb3NlcwpzdHJpbmdfbWlzc2luZ05hbWVzX3BhdGh3YXlzIDwtIGRvLmNhbGwocGFzdGUsIGMoYXMubGlzdChtaXNzaW5nTmFtZXNfcGF0aHdheXMpLCBzZXAgPSAiLCAiKSkKCn0KCiNQcmludCByZXN1bHRzIHNlbGVjdGVkIHBhdGh3YXlzCnByaW50KHBhc3RlMChzaG93cmVzdWx0c19Db21iaW5lUFdzW2ZpcnN0X3NlbGVjdGVkX3BhdGh3YXksM10sICIgKCIsIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NbZmlyc3Rfc2VsZWN0ZWRfcGF0aHdheSwyXSwgIikgWyIsICBzaG93cmVzdWx0c19Db21iaW5lUFdzW2ZpcnN0X3NlbGVjdGVkX3BhdGh3YXksNF0sICJdIikpCnByaW50KHBhc3RlMChzaG93cmVzdWx0c19Db21iaW5lUFdzX1NlY29uZFtzZWNvbmRfc2VsZWN0ZWRfcGF0aHdheSwzXSwgIiAoIiwgc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmRbc2Vjb25kX3NlbGVjdGVkX3BhdGh3YXksMl0sICIpIFsiLCAgc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmRbc2Vjb25kX3NlbGVjdGVkX3BhdGh3YXksNF0sICJdIikpCgojUHJpbnQgcmVzdWx0cyBub3Qgc2VsZWN0ZWQgYmlvbWFya2VycwppZih0aGlyZF9zZWxlY3RlZF9wYXRod2F5ID09IDApewogIHByaW50KHBhc3RlMCgiVGhlc2UgaWRlbnRpZmllcnMgYXJlIG5vdCBjYXB0dXJlZCBpbiB0aGUgdG9wIDIgcGF0aHdheXM6ICIsIHN0cmluZ19DSEVCSV90aGlyZF9maW5hbCwgIiwgd2l0aCB0aGUgZm9sbG93aW5nIGRhdGFiYXNlIG5hbWVzOiAiLCBzdHJpbmdfbWlzc2luZ05hbWVzX3BhdGh3YXlzX3RoaXJkKSkKfWVsc2UgaWYodGhpcmRfc2VsZWN0ZWRfcGF0aHdheSA9PSAxKXsKcHJpbnQocGFzdGUwKHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmRbdGhpcmRfc2VsZWN0ZWRfcGF0aHdheSwzXSwgIiAoIiwgc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19UaGlyZFt0aGlyZF9zZWxlY3RlZF9wYXRod2F5LDJdLCAiKSBbIiwgIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmRbdGhpcmRfc2VsZWN0ZWRfcGF0aHdheSw0XSwgIl0iKSkKICBwcmludChwYXN0ZTAoIlRoZXNlIGlkZW50aWZpZXJzIGFyZSBub3QgY2FwdHVyZWQgaW4gdGhlIHRvcCAzIHBhdGh3YXlzOiAiLCBzdHJpbmdfQ0hFQklfZm91cnRoLCAiLCB3aXRoIHRoZSBmb2xsb3dpbmcgZGF0YWJhc2UgbmFtZXM6ICIsIHN0cmluZ19taXNzaW5nTmFtZXNfcGF0aHdheXMpKQp9IGVsc2V7cHJpbnQocGFzdGUwKHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmRbdGhpcmRfc2VsZWN0ZWRfcGF0aHdheSwzXSwgIiAoIiwgc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19UaGlyZFt0aGlyZF9zZWxlY3RlZF9wYXRod2F5LDJdLCAiKSMgWyIsICBzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkW3RoaXJkX3NlbGVjdGVkX3BhdGh3YXksNF0sICJdIikpCiAgcHJpbnQocGFzdGUwKCJUaGVzZSBpZGVudGlmaWVycyBhcmUgbm90IGNhcHR1cmVkIGluIHRoZSB0b3AgMyBwYXRod2F5czogIiwgc3RyaW5nX0NIRUJJX2ZvdXJ0aCwgIiwgd2l0aCB0aGUgZm9sbG93aW5nIGRhdGFiYXNlIG5hbWVzOiAiLCBzdHJpbmdfbWlzc2luZ05hbWVzX3BhdGh3YXlzKSl9CgpgYGAKCiMgMi44IERhdGEgVmlzdXphbGl6YXRpb24KCiMjIFByZXBhcmF0aW9uCgojIyMgSW5zdGFsbGluZyBhbmQgbG9hZGluZyBwYWNrYWdlcwoKVGhlc2UgcGFja2FnZXMgbmVlZCB0byBiZSBpbnN0YWxsZWQgKGFuZCBtYXkgdGFrZSBhIHdoaWxlIHRvIHJ1bikuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQppZighInJXaWtpUGF0aHdheXMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogICAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgICAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCiAgICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgicldpa2lQYXRod2F5cyIsIHVwZGF0ZSA9IEZBTFNFKQp9CmxpYnJhcnkocldpa2lQYXRod2F5cykKCmxvYWQubGlicyA8LSBjKAogICJSQ29sb3JCcmV3ZXIiLAogICJyV2lraVBhdGh3YXlzIiwKICAiUkN5MyIpCm9wdGlvbnMoaW5zdGFsbC5wYWNrYWdlcy5jaGVjay5zb3VyY2UgPSAibm8iKQpvcHRpb25zKGluc3RhbGwucGFja2FnZXMuY29tcGlsZS5mcm9tLnNvdXJjZSA9ICJuZXZlciIpCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpOyBsaWJyYXJ5KHBhY21hbikKcF9sb2FkKGxvYWQubGlicywgdXBkYXRlID0gVFJVRSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQpzdGF0dXMgPC0gc2FwcGx5KGxvYWQubGlicyxyZXF1aXJlLGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKaWYoYWxsKHN0YXR1cykpewogICAgcHJpbnQoIlNVQ0NFU1M6IFlvdSBoYXZlIHN1Y2Nlc3NmdWxseSBpbnN0YWxsZWQgYW5kIGxvYWRlZCBhbGwgcmVxdWlyZWQgbGlicmFyaWVzLiIpCn0gZWxzZXsKICAgIGNhdCgiRVJST1I6IE9uZSBvciBtb3JlIGxpYnJhcmllcyBmYWlsZWQgdG8gaW5zdGFsbCBjb3JyZWN0bHkuIENoZWNrIHRoZSBmb2xsb3dpbmcgbGlzdCBmb3IgRkFMU0UgY2FzZXMgYW5kIHRyeSBhZ2Fpbi4uLlxuXG4iKQogICAgc3RhdHVzCn0KCmBgYAoKIyMjIENvbm5lY3QgdG8gY3l0b3NjYXBlCgpPcGVuIHRoZSBDeXRvc2NhcGUgUHJvZ3JhbSBmaXJzdDsgdGhpcyBzdGVwIHdhcyB0ZXN0ZWQgb24gdmVyc2lvbjogMy45LjEsIHJ1bm5pbmcgb24gSmF2YSAxMS4wLjExLgoKYGBge3J9CiMgTWFrZSBzdXJlIHRvIGhhdmUgQ3l0b3NjYXBlIHN0YXJ0ZWQgb24geW91ciBjb21wdXRlciAoPj0zLjkuMSkKY3l0b3NjYXBlUGluZyAoKQpjeXRvc2NhcGVWZXJzaW9uSW5mbyAoKQpgYGAKCiMjIyBJbnN0YWxsIFdpa2lQYXRod2F5cyBhcHBzCgpGb3IgdGhlIG9wZW5pbmcgb2YgcGF0aHdheSBtb2RlbHMgYW5kIGVuaGFuY2VkIHZpc3VhbGl6YXRpb24gc3RlcHMsIHRoZSBXaWtpUGF0aHdheXMgYXBwIG5lY2Vzc2FyeSAodmVyc2lvbiAzLjMuMTApLgoKYGBge3J9CmlmKCJXaWtpUGF0aHdheXMiICVpbiUgY29tbWFuZHNIZWxwKCIiKSkgcHJpbnQoIlN1Y2Nlc3M6IHRoZSBXaWtpUGF0aHdheXMgYXBwIGlzIGluc3RhbGxlZCIpIGVsc2UgcHJpbnQoIldhcm5pbmc6IFdpa2lQYXRod2F5cyBhcHAgaXMgbm90IGluc3RhbGxlZC4gUGxlYXNlIGluc3RhbGwgdGhlIFdpa2lQYXRod2F5cyBhcHAgYmVmb3JlIHByb2NlZWRpbmcuIikKaWYoISJXaWtpUGF0aHdheXMiICVpbiUgY29tbWFuZHNIZWxwKCIiKSl7CiAgaW5zdGFsbEFwcCgiV2lraVBhdGh3YXlzIikKfQpgYGAKCiMjIyBPcGVuIHBhdGh3YXkgZnJvbSBXaWtpUGF0aHdheXMgYW5kIFZpc3VhbGl6ZSBwYXRpZW50IGRhdGEgdGhyb3VnaCBsb2cuQ2hhbmdlCgpQYXRod2F5IG1vZGVscyBjYW4gYmUgaW1wb3J0ZWQgZnJvbSBXaWtpUGF0aHdheXMgd2l0aCB0aGUgV2lraVBhdGh3YXlzIGFwcHMgKGFzIHBhdGh3YXkgb3IgbmV0d29yaykuIEVuYWJsaW5nIHRoZSB2aXN1YWxpemF0aW9uIG9mIGJpb21hcmtlcnMgc2NhdHRlcmVkIG92ZXIgc2V2ZXJhbCBwYXRod2F5cywgcmVxdWlyZXMgZmluZGluZyB0aGUgb3B0aW1hbCBjb21iaW5hdGlvbiBvZiB0d28gKG9yIHRocmVlKSBpbmRpdmlkdWFsIHBhdGh3YXlzLCBhZGRpbmcgdGhlIGRhdGEgdG8gdGhlc2UgcGF0aHdheXMgYW5kIG1lcmdpbmcgdGhlIHJlc3VsdHMuCgpEZXRlcm1pbmUgdGhlIHNjYWxlIGZvciBmaWxsIGNvbG9yLCBhbmQgY29udmVydCBkYXRhIHRvIGRhdGFmcmFtZQoKYGBge3J9CiNEZXRlcm1pbmUgaGlnaGVzdCB1cC9kb3ducmVndWxhdGVkIHZhbHVlOgpoaWdoZXN0IDwtIG1heChhbm5vdGF0ZWRfYm90aCRsb2cuQ2hhbmdlKQpsb3dlc3QgPC0gYWJzKG1pbihhbm5vdGF0ZWRfYm90aCRsb2cuQ2hhbmdlKSkKaWYoaGlnaGVzdCA8IGxvd2VzdCl7Y29sb3JSYW5nZSA8LSBsb3dlc3R9IGVsc2V7Y29sb3JSYW5nZSA8LSBoaWdoZXN0fQoKI0NoYW5nZSBkYXRhIHRvIGRhdGFmcmFtZSBiZWZvcmUgbG9hZGluZyBpbiBDeXRvc2NhcGU6CmZpbmFsRGF0YSA8LSBhcy5kYXRhLmZyYW1lKGFubm90YXRlZF9ib3RoKQpgYGAKClRoZSAybG9nIHRyYW5zZm9ybWVkIGRhdGEgY2FuIG5vdyBiZSB2aXN1YWxpemVkIG9uIHRoZSBub2RlcyBpbiB0aGUgZm9ybSBvZiBhIGNvbG9yIGdyYWRpZW50IGZyb20gYmx1ZShkb3ducmVndWxhdGVkKSB0byB3aGl0ZSAoMCkgdG8gcmVkICh1cHJlZ3VsYXRlZCksIGp1c3QgYXMgdGhlIHJlZmVyZW5jZSBkYXRhLgoKYGBge3J9CiNTZWxlY3QgaGlnaGVzdCByZWxldmFudCBwYXRod2F5IElEIChjb250YWluaW5nIG1vc3QgYmlvbWFya2VycykgYmFzZWQgb24gcHJldmlvdXMgc3RlcC4Kd3A9IGNvbW1hbmRzUnVuKHBhc3RlMCgnd2lraXBhdGh3YXlzIGltcG9ydC1hcy1uZXR3b3JrIGlkPScsIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NbZmlyc3Rfc2VsZWN0ZWRfcGF0aHdheSwyXSkpICNwaWNrIGZpcnN0IFBXIHRvIHNob3cgZGF0YSBvbi4KCiMjIExpbmtpbmcgbWV0YWJvbGljIGRhdGEgdG8gQ2hFQkktSUQgY29sdW1uCmxvYWRUYWJsZURhdGEoZmluYWxEYXRhLCBkYXRhLmtleS5jb2x1bW4gPSAiSUQiLCB0YWJsZS5rZXkuY29sdW1uID0gIkNoRUJJIikKCiNTZXQgcmFuZ2Ugb2YgZGF0YSB2YWx1ZXMgZm9yIHZpc3VhbGlzYXRpb246CmRhdGEudmFsdWVzID0gYygtY29sb3JSYW5nZSwtMSwwLDEsY29sb3JSYW5nZSkKI2Rpc3BsYXkuYnJld2VyLmFsbChsZW5ndGgoZGF0YS52YWx1ZXMpLCBjb2xvcmJsaW5kRnJpZW5kbHk9VFJVRSwgdHlwZT0iZGl2IikgIyBkaXYscXVhbCxzZXEsYWxsCm5vZGUuY29sb3JzIDwtIGMocmV2KGJyZXdlci5wYWwobGVuZ3RoKGRhdGEudmFsdWVzKSwgIlJkQnUiKSkpCgojVXBkYXRlIFdpa2lQYXRod2F5cyBwYWxsZXQgd2l0aCBQYXRpZW50IERhdGE6CnNldE5vZGVDb2xvck1hcHBpbmcoImxvZy5DaGFuZ2UiLCBkYXRhLnZhbHVlcywgbm9kZS5jb2xvcnMsIGRlZmF1bHQuY29sb3IgPSAiI0FBQUFBQSIsIHN0eWxlLm5hbWUgPSAiV2lraVBhdGh3YXlzLUFzLU5ldHdvcmsiKQoKI0Rvd25sb2FkIHRoZSBOZXR3b3JrIGFzIGEgUE5HIEZpZ3VyZSwgYW5kIHN0b3JlIHRoZSBmaWd1cmUgaW4gdGhlIEltYWdlcyBmb2xkZXIuCmltYWdlVVJMX05hbWUgPC0gcGFzdGUwKCJJbWFnZXMvcGF0aWVudF8iLHBhdGllbnRJRCwiX25ldHdvcmtfRmlyc3QiKQpuZXR3b3JrMSA8LSBleHBvcnRJbWFnZShpbWFnZVVSTF9OYW1lLCdQTkcnKQpgYGAKCmBgYHtyfQojU2hvdyB0aGUgbmV0d29yayBhcyBvdXRwdXQgaW4gUk1EIGZpbGUKZmlsZW5hbWVGaXJzdDwtIHBhc3RlMChpbWFnZVVSTF9OYW1lLCAnLnBuZycpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGZpbGVuYW1lRmlyc3QpCmBgYAojIyMgU2VsZWN0aW5nIHRoZSBzZWNvbmQgcGF0aHdheSBvZiBjaG9pY2UKCmBgYHtyfQojU2VsZWN0IHNlY29uZGx5IGhpZ2hlc3QgcmFua2VkIHJlbGV2YW50IFBXIChtYW51YWwgZm9yIG5vdyk6CndwID0gY29tbWFuZHNSdW4ocGFzdGUwKCd3aWtpcGF0aHdheXMgaW1wb3J0LWFzLW5ldHdvcmsgaWQ9Jywgc2hvd3Jlc3VsdHNfQ29tYmluZVBXc19TZWNvbmRbc2Vjb25kX3NlbGVjdGVkX3BhdGh3YXksMl0pKQoKbG9hZFRhYmxlRGF0YShmaW5hbERhdGEsIGRhdGEua2V5LmNvbHVtbiA9ICJJRCIsIHRhYmxlLmtleS5jb2x1bW4gPSAiQ2hFQkkiKQoKI1VwZGF0ZSBXaWtpUGF0aHdheXMgcGFsbGV0IHdpdGggUGF0aWVudCBEYXRhOgpzZXROb2RlQ29sb3JNYXBwaW5nKCJsb2cuQ2hhbmdlIiwgZGF0YS52YWx1ZXMsIG5vZGUuY29sb3JzLCBkZWZhdWx0LmNvbG9yID0gIiNBQUFBQUEiLCBzdHlsZS5uYW1lID0gIldpa2lQYXRod2F5cy1Bcy1OZXR3b3JrIikKCiNEb3dubG9hZCB0aGUgTmV0d29yayBhcyBhIFBORyBGaWd1cmUsIGFuZCBzdG9yZSB0aGUgZmlndXJlIGluIHRoZSBJbWFnZXMgZm9sZGVyLgppbWFnZVVSTF9OYW1lIDwtIHBhc3RlMCgiSW1hZ2VzL3BhdGllbnRfIixwYXRpZW50SUQsIl9uZXR3b3JrX1NlY29uZCIpCm5ldHdvcmsyIDwtIGV4cG9ydEltYWdlKGltYWdlVVJMX05hbWUsJ1BORycpCgpgYGAKCmBgYHtyfQojU2hvdyB0aGUgbmV0d29yayBhcyBvdXRwdXQgaW4gUk1EIGZpbGUKZmlsZW5hbWVTZWNvbmQgPC0gcGFzdGUwKGltYWdlVVJMX05hbWUsICcucG5nJykKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoZmlsZW5hbWVTZWNvbmQpCmBgYAoKIyMjIFNlbGVjdGluZyB0aGUgdGhpcmQgcGF0aHdheSBvZiBjaG9pY2UKCmBgYHtyfQppZih0aGlyZF9zZWxlY3RlZF9wYXRod2F5IDwgMSl7CiAgcGFzdGUoInNraXAgdGhpcyBzdGVwLCBubyB0aGlyZCBwYXRod2F5IHNlbGVjdGVkIil9ZWxzZXsKCiNTZWxlY3QgdGhpcmQgaGlnaGVzdCByYW5rZWQgcmVsZXZhbnQgUFcgKG1hbnVhbCBmb3Igbm93KToKd3A9IGNvbW1hbmRzUnVuKHBhc3RlMCgnd2lraXBhdGh3YXlzIGltcG9ydC1hcy1uZXR3b3JrIGlkPScsIHNob3dyZXN1bHRzX0NvbWJpbmVQV3NfVGhpcmRbdGhpcmRfc2VsZWN0ZWRfcGF0aHdheSwyXSkpCgpsb2FkVGFibGVEYXRhKGZpbmFsRGF0YSwgZGF0YS5rZXkuY29sdW1uID0gIklEIiwgdGFibGUua2V5LmNvbHVtbiA9ICJDaEVCSSIpCgojVXBkYXRlIFdpa2lQYXRod2F5cyBwYWxsZXQgd2l0aCBQYXRpZW50IERhdGE6CnNldE5vZGVDb2xvck1hcHBpbmcoImxvZy5DaGFuZ2UiLCBkYXRhLnZhbHVlcywgbm9kZS5jb2xvcnMsIGRlZmF1bHQuY29sb3IgPSAiI0FBQUFBQSIsIHN0eWxlLm5hbWUgPSAiV2lraVBhdGh3YXlzLUFzLU5ldHdvcmsiKQoKI0Rvd25sb2FkIHRoZSBOZXR3b3JrIGFzIGEgUE5HIEZpZ3VyZSwgYW5kIHN0b3JlIHRoZSBmaWd1cmUgaW4gdGhlIEltYWdlcyBmb2xkZXIuCmltYWdlVVJMX05hbWUgPC0gcGFzdGUwKCJJbWFnZXMvcGF0aWVudF8iLHBhdGllbnRJRCwiX25ldHdvcmtfVGhpcmQiKQpuZXR3b3JrMyA8LSBleHBvcnRJbWFnZShpbWFnZVVSTF9OYW1lLCdQTkcnKQoKI1Nob3cgdGhlIG5ldHdvcmsgYXMgb3V0cHV0IGluIFJNRCBmaWxlCmZpbGVuYW1lVGhpcmQgPC0gcGFzdGUwKGltYWdlVVJMX05hbWUsICcucG5nJykKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoZmlsZW5hbWVUaGlyZCkKCn0KCmBgYAojIyMgUHJpbnQgbWlzc2luZyBiaW9tYXJrZXJzIGluIHZpc3VhbGl6YXRpb24KCmBgYHtyfQojVE9ETyBwcmludCB3aGljaCBiaW9tYXJrZXJzIGFyZSBub3QgdmlzdWFsaXplZCBhdXRvbWF0aWNhbGx5IChub3cgbWFudWFsbHkgc2VsZWN0ZWQpOyBhbHNvIGFkZCB0byBmaW5hbCByZXBvcnRpbmcgc3RlcCEKbWlzc2luZ19JRHMgIDwtIHBhc3RlMChhbm5vdGF0ZWRfYm90aF9ub3plcm9bNSw0XSwgIiwgIiAsIGFubm90YXRlZF9ib3RoX25vemVyb1sxMCw0XSkKcHJpbnQoYW5ub3RhdGVkX2JvdGhfbm96ZXJvW2MoNSwxMCksYygxLDMsNCw4KV0sIGRpZ2l0cyA9IDIpCmBgYAoKIyAyLjkgRGF0YSBJbnRlcnByZXRhdGlvbgoKIyMjIEZpbmFsbHk6IFByaW50IHJlcG9ydGluZyBkZXRhaWxzCgpgYGB7cn0KIyMjUHJpbnQgb3ZlcnZpZXcgb2YgaW5mb3JtYXRpb24gZm9yIGVhY2ggcGF0aWVudApwcmludChwYXN0ZSgiU2VsZWN0ZWQgUGF0aWVudCBJRCBpczogIiAsIHBhdGllbnRJRCwgIiwgYWdlIGlzIGJldHdlZW46ICIsIGFnZXJhbmdlLCAiIG9sZCIpKQpyZW1vdmUoYWdlKQpwcmludCgiUmVsZXZhbnQgYmlvbWFya2VycyBhcmU6IikKcHJpbnQoYW5ub3RhdGVkX2JvdGhfbm96ZXJvWyxjKDEsMyw0LDgpXSwgZGlnaXRzID0gMikKCiNQcmludCByZWxldmFudCBiaW9tYXJrZXIgaW5mb3JtYXRpb24gbWF0Y2hlZCB0byBwYXRod2F5czoKcHJpbnQocGFzdGUoIlRoZXJlIGFyZSIsIGNvdW50X2Jpb21hcmtlcnMgLCJiaW9tYXJrZXJzIHJlbGV2YW50IGZvciBwYXRpZW50IiAsIHBhdGllbnRJRCwgIiwgdGhlIENoRUJJLUlEcyBhcmUiLCBzdHJpbmdfQ0hFQkkpKQppZihsZW5ndGgoaW50ZXJzZWN0aW5nQ0hFQkkpID09IDAgKXtwcmludCgiQWxsIEJpb21hcmtlcnMgYXJlIGluIGEgcGF0aHdheSEiKX0gZWxzZXsKICBwcmludChwYXN0ZSgiVGhlc2UgYmlvbWFya2VycyAoYXMgQ2hFQkkgSURzKSBhcmUgbm90IGluIGEgcGF0aHdheToiICwgc3RyaW5nX2ludGVyc2VjdGluZ0NIRUJJKSl9CgpyZW1vdmUobGlzdE1pc3NpbmdCaW9tYXJrZXJzLCBDSEVCSV9pblBXcyxzdXBlcmNsZWFuZWRfQ0hFQkksc3RyaW5nX2ludGVyc2VjdGluZ0NIRUJJKQoKI1ByaW50IHRhYmxlIHdpdGggZmlyc3QgZml2ZSByZWxldmFudCBwYXRod2F5cwojcHJpbnQoc2hvd3Jlc3VsdHNfQ29tYmluZVBXc1sxOjUsYygyLDMsNCw2KV0pCgojIyNTYXZlIHJlbGV2YW50IGluZm9ybWF0aW9uIGZvciBlYWNoIHBhdGllbnQgaW4gZGF0YWZyYW1lIChmb3IgVGFibGVzIGluIHB1YmxpY2F0aW9uKQojdGFibGU2IDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2w9NCxucm93PTAsIGRpbW5hbWVzPWxpc3QoTlVMTCwgYygiUHJpbWFyeS5JRCIsICJTZWNvbmRhcnkuSUQiLCAiVGVydGlhcnkuSUQiLCAiTm90LmNvdmVyZWQuYmlvbWFya2VycyIpKSkpCiMjQWRkIHBhdGllbnQgZGF0YSB0byBkYXRhZnJhbWUgKG5vdGU6IHRoaXMgb3ZlcndyaXRlcyBleGlzdGluZyBwYXRpZW50IGVudHJpZXMsIGFzIG9wcG9zZWQgdG8gdXNpbmcgcmJpbmQpCiN0YWJsZTZbcGF0aWVudElELCBdIDwtIGMoc2hvd3Jlc3VsdHNfQ29tYmluZVBXc1sxLDJdLCBzaG93cmVzdWx0c19Db21iaW5lUFdzX1NlY29uZFsxLDJdLCBzaG93cmVzdWx0c19Db21iaW5lUFdzX1RoaXJkWzEsMl0sIG1pc3NpbmdfSURzKQoKYGBgCg==