1. Introduction

Lead (Pb) is the second most toxic metal after arsenic (As), comprising 0.002% of the earth’s crust.

Lead, like other heavy metals, does not biodegrade or disappear from the environment over time. Although lead levels in the food supply declined dramatically between the 1970s and 1990s, low levels of lead continue to be detected in some foods due to the continued presence in the environment. Lead from the soil can be absorbed by plants grown to obtain fruit or vegetables or plants used as ingredients in food or animal feed.

Lead is poisonous to humans and can affect people of any age or health. Lead is especially harmful to vulnerable populations, including babies, young children, pregnant women and their fetuses and others with chronic health conditions. High levels of lead exposure can seriously damage the health and development of children, specifically the brain and nervous system. The neurological effects of high levels of lead exposure during early childhood include learning disabilities, behavioral difficulties and reduced IQ. Because lead can accumulate in the body, even low-level chronic exposure can be dangerous over time.

It is estimated that exposure to lead was responsible, in 2004, for 143 thousand deaths and 0.6% of the global disease burden, taking into account mild mental under development and cardiovascular outcomes

1.1 Purpose

This work focus on:

1- investigate the content of Lead in foods using the R language as a technique for filtering, analyzing and modeling distributions according to the varieties of food consumed, countries, analytical conditions and dates.

2- Find out where and which foods are most contaminated and create possible hypotheses of cause.

2. Initial data and database ingestion:

The information was taken from the “GEMSFOODS” database of the World Health Organization (WHO) available at:

https://extranet.who.int/gemsfood/

All calculations and graphs were made in ** Rstudio version 1.3.959 ** and ** R base 3.6.3 ** and using the following libraries:

library(readr)
library(ggplot2)
library(tidyverse)
library(knitr)
library(RColorBrewer)
library(ggalluvial)
library(treemapify)
library(gganimate)
library(ggrepel)
library(DescTools)
library(ggthemes)

The codes used to perform that work will be shown. All processing is based on more modern standards in reproducibility [ref] in research where individual data is not manipulated and the entire process (from database recognition to the final report) is done in an automated way and can be audited by the code used.

We started by consolidating the database, the files were downloaded in groups to facilitate the transfering:

GEMS2000 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(1980-2000).csv", dec=",")
GEMS2010 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(2000-2010).csv", dec=",")
GEMS2014 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(2010-2014).csv", dec=",")
GEMS2016 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(2014-2016).csv", dec=",")
GEMS2018 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(2016-2018).csv", dec=",")
GEMS2020 <- read.csv("/mnt/DADOS/Data Science/GEMFoods/Gemsfood(2018-2020).csv", dec=",")
GEMS <- rbind(GEMS2000,GEMS2010,GEMS2014,GEMS2016,GEMS2018,GEMS2020)

3. Main variables:

Checking the information available:

names(GEMS)
 [1] "RecordType"              "RegionName"              "CountryName"            
 [4] "ContaminantName"         "FoodCategory"            "FoodName"               
 [7] "FoodCode"                "LocalFoodName"           "FoodStateName"          
[10] "ResultValue"             "ResultText"              "UnitName"               
[13] "LOD"                     "LOQ"                     "LODMin"                 
[16] "LODMax"                  "LOQMin"                  "LOQMax"                 
[19] "Mean"                    "MeanLower"               "MeanUpper"              
[22] "Median"                  "NinetiethPercentile"     "StandardDeviation"      
[25] "SamplingPeriodStart"     "SamplingPeriodStartText" "SamplingPeriodEnd"      
[28] "SamplingPeriodEndText"   "RepresentativenessName"  "LabCount"               
[31] "LabNumber"               "FoodOriginName"          "AnalyticalQAName"       
[34] "SampleCount"             "SamplesBelowLOQ"         "RangeMin"               
[37] "RangeMax"                "ResultBasisName"         "PortionTypeName"        
[40] "SerialNumber"            "RowNum"                 

The data used will be: “CountryName” the country of origin of the analytical data. “FoodCategory” The type of food, that is, the analytical matrix such as “Vegetables” or “Animal Feed”. “FoodOriginName”, which describes the origin as Domestic or imported. “LOQ” the limit of quantification. “ResultValue”, the value obtained in the analysis when quantified. “AnalyticalQAName”, which describes the conditions of the analysis, if made in an accredited laboratory or with use of internal quality control only. “FoodName” which is the exact description of the food as “Potato” or “Rice”.

Before, the evaluation of the units of the Lead content should be made:

summary(GEMS$UnitName)
 mg/kg  ug/kg 
123819 240956 

When quantified, the result was reported in two different units, mg/kg and ug/kg.

All units will be changed to mg/kg and the values adjusted for the same unit, where 1ug/kg is equivalent to 0.001mg/kg:

Sys.time()
GEMSx <-GEMS
  GEMS1_14 <- GEMS[,14]
  GEMS1_10 <- GEMS[,10]
  GEMS1_12 <- GEMS[,12]
 for(i in 1:nrow(GEMS)){
  if(GEMS1_12[i]=="ug/kg"){
    GEMS1_14[i] <- GEMS1_14[i]/1000
    GEMS1_10[i] <- GEMS1_10[i]/1000
    GEMS1_12[i] <- "mg/kg"
  } else {
NULL
  }
 }
GEMSx[,14]<- GEMS1_14
GEMSx[,10]<- GEMS1_10
GEMSx[,12]<- GEMS1_12

Sys.time()

NOTE: Due to long processing time, the code chunk above was previously executed and saved as a RDS file. Which will be read and stored in a new object(GEMS1).

GEMS1 <- readRDS("/mnt/DADOS/Data Science/GEMFoods/GEMS1.rds")

Confirming the change:

summary(GEMS1$UnitName)
 mg/kg  ug/kg 
364775      0 

The Limit of Quantification has also been changed (Column 14)

4. Exploratory analysis and search for the biggest contaminations:

4.1 Distribution of the data source

Let’s see in a practical way which countries contributed the most to the WHO database using the CountryName variable and how many times a particular country appears:

GEMS1 %>% count(CountryName,sort = TRUE) %>% top_n(20) %>% ggplot()+geom_col(aes(n,reorder(CountryName,n,max),fill=CountryName),show.legend = FALSE)+theme_economist()+xlab("Country Name")+ylab("Number of samples")
Selecting by n

ggplot(GEMS1, aes(x=reorder(CountryName,CountryName,function(x)+length(x)), fill=CountryName, ))+geom_bar(show.legend= FALSE)+ coord_flip()+xlab("Country Name")+ylab("Number of samples")+theme_economist()

Figure 1. Number of samples per database contributor in the period from 1995 to 2020.

“WHO European Region”, “China”, “Canada”, “United States of America”, “Brazil” and “Thailand” are the main contributors. WHO/Europe have 54% of contributions and Canada in second with 13%.

From now on, a sample will be used containing only the 6 countries with the most contributions (GEMS2)

To look for any possible change in the quality of the tests according to the time, we will convert all dates to year.

GEMS2$SamplingPeriodEnd <- as.POSIXct(GEMS2$SamplingPeriodEnd, format = "%m/%d/%Y %H:%M:%S")
GEMS2$SamplingPeriodEnd <- format(GEMS2$SamplingPeriodEnd, format="%Y")

4.2 Analytical Quality of Tests

Evaluating the available information on which tools used for the quality of the tests, the variable “AnalyticalQAName” will be used:

ggplot(GEMS)+geom_bar(aes(reorder(AnalyticalQAName,AnalyticalQAName,function(x)+length(x))
                        ,fill=AnalyticalQAName), show.legend= FALSE, stat = "count")+
  coord_flip()+
  xlab("AnalyticalQAName")+xlab("Quality control Tool")+ylab("Number of samples")

Figure 2. Type of analytical quality control tool used in relation to the number of samples in the database.

We see that the equivalent of approximately 48% (175,133) of the tests were carried out in accredited laboratories and 16% (51139) had some internal quality control or proficiency test.

We now we can investigate whether the origin of the analytical data (whether or not to use an accredited laboratory) interferes with the mean/spread within the data for each country. First, we calculate the average among all samples but taking into account all quantified results, first a summary of the total data:

total average:

GEMS2 %>% group_by(CountryName) %>%  summarise(mean=mean(ResultValue,na.rm=TRUE), n=n()) %>% kable()
CountryName mean n
Canada 0.0184340 48909
China 0.1594830 26633
United States of America 0.0039717 18378
Brazil 0.0487711 16166
Thailand 0.0977092 10624
WHO European Region 0.2078260 198592

Plotting:

GEMS2 %>% 
  group_by(CountryName) %>%  
  summarise(mean=mean(ResultValue,na.rm=TRUE), n=n())%>%
  arrange(desc(n),na.rm=TRUE)%>% 
  ggplot(aes(mean,CountryName ,fill=CountryName))+
  geom_col(show.legend = FALSE)+
  geom_text(aes(label = format(round(mean, 3), nsmall = 2)), vjust = 0.5,hjust=0)+xlim(c(0,0.25))+ylab("Country Name")+xlab("Mean Value of Pb (mg/Kg)")

Figure 3. Average Lead value (mg/kg) in samples according to the country of origin.

Average using only quantified samples (we adopt values only above 0.001mg/Kg):

GEMS2 %>% group_by(CountryName) %>%  summarise(mean=mean(ResultValue>=0.001,na.rm=TRUE), n=n())%>%arrange(desc(n),na.rm=TRUE)%>% kable()
CountryName mean n
WHO European Region 0.4118924 198592
Canada 0.5294337 48909
China 0.6740885 26633
United States of America 0.2584834 18378
Brazil 0.2769097 16166
Thailand 0.3493351 10624

In the form of a bar graph now:

GEMS2 %>% 
  group_by(CountryName) %>%  
  summarise(mean=mean(ResultValue>=0.001,na.rm=TRUE), n=n())%>%
  arrange(desc(n),na.rm=TRUE)%>% 
  ggplot(aes(mean,CountryName ,fill=CountryName))+
  geom_col(show.legend = FALSE)+
  geom_text(aes(label = format(round(mean, 3), nsmall = 2)), vjust = 0.5,hjust=0)+xlim(c(0,0.70))+ylab("Country Name")+xlab("Mean Value of Pb (mg/Kg)")

Figure 4. Average lead value (mg/kg) in quantified samples (above 0.001 mg/kg) according to the country of origin.

Initially, European values appear to be higher, specifically 4x greater than Brazilians, 10x greater than Canadians and 50x greater than those of the USA. However, when only the quantified values are taken into account, this average decreases considerably. One theory would be that countries such as the USA and Canada would have a larger number of undetected samples and thus the average would be reduced.

To check if the source of the difference may be the use of non-accredited laboratories, we will exclude this data in the “GEMS3” dataframe

GEMS3=subset(GEMS2,GEMS2$AnalyticalQAName=="Officially accredited")

Now repeating the test to see if there was a change in the averages:

GEMS3 %>% group_by(CountryName) %>%  summarise(mean=mean(ResultValue>=0.001,na.rm=TRUE), n=n())%>%arrange(desc(n),na.rm=TRUE)%>% ggplot(aes(mean,CountryName ,fill=CountryName))+geom_col(show.legend = FALSE)+geom_text(aes(label = format(round(mean, 3), nsmall = 2)), vjust = 0.5,hjust=0)+xlim(c(0,0.60))+ylab("Country Name")+xlab("Mean Value of Pb (mg/Kg)")

Figure 4. Average Lead value (mg/kg) in quantified samples (above 0.001 mg/kg) and that used accredited laboratories, separated by the country of origin.

Yes, most of the average values have decreased and in particular China, which showed an over-quantification of Lead in non-accredited laboratories. While European laboratories reported much lower figures.

When evaluating data only from quantified samples and accredited laboratories, Europe and Canada have very similar averages.

4.3 Distribution according to matrix (FoodCategory)

We will now analyze the contents of each matrix (Variable “FoodCategory” in GEMS). Below are all the results quantified (considering the 6 main contributing countries) in Lead per mg/kg (x-axis) for each type of food (y-axis):

ggplot(GEMS2, aes(x=FoodCategory,y=ResultValue))+
    geom_boxplot(show.legend = FALSE)+geom_hline(yintercept=0.1, linetype="solid", color = "red")+
    scale_y_log10()+
    scale_x_discrete(breaks=unique(GEMS$FoodCategory),labels = function(x) str_wrap(str_replace_all(x, "gjgj" , " "), width = 80))+
    coord_flip()+theme(axis.text.x = element_text(angle = 90,size =10))+xlab("Food Category")+ylab("Pb (mg/Kg)")

Figure 5: Boxplot of Lead content in mg/Kg in food according to the matrix. The boxes represent 50% of the data, the horizontal bars the other 50%. Dots beyond the bars represent outliers.

The red line represents the limit of 0.1 mg mg/kg of Pb adopted as a standard in several legislation [2]. Only 4 matrices (food) have their median at or above the limit: Herbs and spices, stimulating drinks, animal feed and products for special use. NOTE: Scale of the Y axis is in log10 to facilitate the visualization of the data.

Figure 6: Same as Figure 4 but showing the passage of time between the years 1995 and 2020

During the evolution of the years, a change in the averages is likely to be caused by seasonality.

We will now visualize the distribution in Boxplot form of each food (categorical data “FoodCategory”) separated in the six main contributing countries:

ggplot(GEMS2, aes(ResultValue,FoodCategory,  color=CountryName))+
      geom_boxplot(show.legend = FALSE)+scale_x_log10()+theme(axis.text.y = element_blank())+geom_vline(xintercept=0.1, linetype="solid", color = "red")+facet_wrap(~CountryName)+ylab("Food Category")+xlab("Pb (mg/Kg)")

Figure 7: Boxplot of Lead content in mg/Kg in food according to the matrix (Y axis).

The red line represents the limit of 0.1mg/kg, routinely adopted as the limit for Lead in foods internationally [4].

There is a much larger number of outliers in the European Union than in the second largest contributor (Canada). These outliers include very high concentrations (even 100mg / Kg!) Causing a lever and “pulling” the total averages upwards.

4.4. LOQ behaviour

The LOQ is a important tool to assess the analysis

summary(GEMS2$LOQ)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   0.00    0.01    0.02    0.06    0.05   66.67   45661 

So, from all 319302 samples, 25% have a LOQ below 0.01 mg/Kg, the median is on 0.02 mg/kg and 75% of samples have a LOQ bellow 0.05mg/kg. We found that 14% (45661) of samples didn’t report the LOQ.

Let’s graphically show this information using the ResulValue of Pb (mg/Kg) as the X variable.

ggplot(GEMS2,aes(x=ResultValue, fill=LOQ<=0.01))+
    geom_boxplot()+scale_x_log10()+geom_vline(xintercept=0.1, linetype="dashed", color = "red")+
  geom_vline(xintercept=1.21, linetype="dashed", color = "blue")

Boxplot from quantified samples according to LOQ, a value of 0.01mg/Kg were chosen since is 1/10 of the limit of 0.1mg/Kg. As seen, Lower detection limits have a lower mean value of quantified samples. Higher LOQ than 0.1mg/Kg of Pb have a mean quantified sample higher. Samples that didn’t report the LOQ have a mean quantified value between the two. This can be seen even more clearly on histograms:

ggplot(GEMS2,aes(x=ResultValue, fill=LOQ<=0.01))+
    geom_histogram()+scale_x_log10()+geom_vline(xintercept=0.1, linetype="dashed", color = "red")+
  geom_vline(xintercept=1.21, linetype="dashed", color = "blue")

It’s important to notice that only quantified samples appear on the graph above, and 56% of samples are not quantified.

prop.table(table(GEMS2$ResultValue==0.00000))

    FALSE      TRUE 
0.4366943 0.5633057 

But, are the quantified results with unreported LOQ equivalent to the ones with reported results?

First, the confirmation if the data is normal:

GEMS2res<-sample_n(GEMS2,5000)

shapiro.test(subset(GEMS2res$ResultValue,GEMS2res$LOQ>=0.01))

    Shapiro-Wilk normality test

data:  subset(GEMS2res$ResultValue, GEMS2res$LOQ >= 0.01)
W = 0.045942, p-value < 2.2e-16
shapiro.test(subset(GEMS2res$ResultValue,GEMS2res$LOQ<=0.01))

    Shapiro-Wilk normality test

data:  subset(GEMS2res$ResultValue, GEMS2res$LOQ <= 0.01)
W = 0.097989, p-value < 2.2e-16
shapiro.test(subset(GEMS2res$ResultValue,is.na(GEMS2res$LOQ)))

    Shapiro-Wilk normality test

data:  subset(GEMS2res$ResultValue, is.na(GEMS2res$LOQ))
W = 0.13748, p-value < 2.2e-16
GEMS2res<-GEMS2

All three sets of the ResultValue variable with LOQ lower than 0.01mg/Kg, higher than 0.01mg/Kg and non reported(NA) are normal.

Now, grouping each data according to the LOQ:

GEMS2res1 <- subset(GEMS2res,GEMS2res$LOQ>=0.01)
GEMS2res1$group <- "Higher than 0.01"
GEMS2res2 <- subset(GEMS2res,GEMS2res$LOQ<=0.01)
GEMS2res2$group <- "Lower than 0.01"
GEMS2res3 <- subset(GEMS2res,is.na(GEMS2res$LOQ))
GEMS2res3$group <- "NA"
GEMS2res_1<-rbind(GEMS2res1,GEMS2res2,GEMS2res3)

And performing ANOVA.

anova_GEMS2res_1 <- aov(ResultValue ~ group, GEMS2res_1)
summary(anova_GEMS2res_1)
PostHocTest(anova_GEMS2res_1, method = "hsd")

  Posthoc multiple comparisons of means : Tukey HSD 
    95% family-wise confidence level

$group
                                        diff      lwr.ci    upr.ci   pval    
Lower than 0.01-Higher than 0.01 -0.02801841 -0.16578416 0.1097473 0.8823    
NA-Higher than 0.01               0.26261832  0.08765773 0.4375789 0.0013 ** 
NA-Lower than 0.01                0.29063673  0.09536069 0.4859128 0.0014 ** 

---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

We can note that there’s a different mean between the quantified samples with below and above the LOQ of 0.01mg/Kg of Pb but NOT between these groups and the unreported results.

Now, evaluating according to year of analysis:

ggplot(GEMS2res_1,aes(x=SamplingPeriodEnd,y=length(RowNum), fill=group))+
  geom_bar(position="stack", stat="identity")+ theme(axis.text.x = element_text(angle = 90),)+ggtitle("Number of samples analysed according to LOQ group")


GEMS2res_1 %>% group_by(SamplingPeriodEnd) %>% count(group,sort=TRUE) %>% ggplot()+geom_col(aes(SamplingPeriodEnd,n, fill=group))+ theme(axis.text.x = element_text(angle = 90),)+ggtitle("Number of samples analysed according to LOQ group")

After plotting the LOQ groups according to the Year we note that the unreported data probably come from years we this information wasn’t required.

We decided to keep the quantified data even if the LOQ wasn’t reported.

5. A deeper study of Lead levels in the European Union region

Since the WHO / Europe region has a large amount of data and many outliers/values above what is allowed, it was chosen to evaluate the matrices and their distribution over the years in more detail.

We will now examine the quantified samples (above 0.001mg/kg) in the “WHO European Region” in the form of a heatplot:

 subset(GEMS2, GEMS2$CountryName=="WHO European Region" & GEMS2$ResultValue) %>% 
  ggplot(aes(SamplingPeriodEndText,y=reorder(FoodCategory, ResultValue,function(x)+mean(x)),  fill = ResultValue))+   
  geom_tile(color = "white") +
  scale_fill_gradient(limits=c(0.001, 2),low = "blue", high ="red", trans="log10")+ 
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90),)+ylab("FoodCategory")+
  scale_y_discrete(labels = function(x) str_wrap(str_replace_all(x, "gjgj" , " "), width = 60))+
  xlab("Year")+ylab("Food Category")

Figure 8. Heatplot from the 15-year period of analysis of Lead content in food at WHO/Europe. Red represents the content of 1mg / kg, blue 0.001mg/kg. It is noted that over the years samples of Herbs and spices, stimulating drinks and animal feed, continuously show higher results than other matrices. As already seen in the boxplot in figure 7.

The upper part of the Heatplot contains the foods with the highest average values over the years and the lowest, with the smallest.

We can now define the frequency of the highest 1%, 2% and 10% (in Pb concentration) in the total database:

quantile(subset(GEMS2$ResultValue,GEMS2$CountryName=="WHO European Region"),probs=(0.95),na.rm=TRUE)
  95% 
0.201 

95% of the quantified samples are below 0.2mg/Kg.

quantile(subset(GEMS2$ResultValue,GEMS2$CountryName=="WHO European Region"),probs=(0.98),na.rm=TRUE)
 98% 
0.58 

98% of the quantified samples are below 0.58mg/Kg.

quantile(subset(GEMS2$ResultValue,GEMS2$CountryName=="WHO European Region"),probs=(0.99),na.rm=TRUE)
 99% 
1.21 

99% of the quantified samples are below 1.21mg/Kg.

Observing these limits in the frequency histogram:

subset(GEMS3, GEMS3$CountryName=="WHO European Region" & GEMS3$ResultValue) %>%
  ggplot(aes(ResultValue, fill=FoodCategory))+
  geom_histogram( show.legend= FALSE)+
  geom_vline(xintercept=0.1, linetype="dashed", color = "red")+
  geom_vline(xintercept=1.21, linetype="dashed", color = "blue")+
  scale_x_log10()+xlab("Pb (mg/kg)")+ylab("Number of samples")

Figure 9: Frequency histogram of data referring to the European Union, x-axis on the logarithmic scale. The red line represents the value of 0.1mg/kg a very common limit allowed in the legislation and blue line, the range that covers 99% of the quantified samples (1.21mg/Kg).

Checking for normality.

shapiro.test(sample(subset(GEMS3$ResultValue, GEMS3$CountryName=="WHO European Region"),5000))

    Shapiro-Wilk normality test

data:  sample(subset(GEMS3$ResultValue, GEMS3$CountryName == "WHO European Region"),     5000)
W = 0.060034, p-value < 2.2e-16
t.test(subset(GEMS3$ResultValue, GEMS3$CountryName=="WHO European Region"))

    One Sample t-test

data:  subset(GEMS3$ResultValue, GEMS3$CountryName == "WHO European Region")
t = 4.2531, df = 97261, p-value = 2.11e-05
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 0.08983044 0.24338904
sample estimates:
mean of x 
0.1666097 

Although figure 9 shows the distribution of data with the logarithmic transformation, a normality test and a t test for a sample show that the set is normal and that all European samples have an average of 0.16mg / kg ± 0.08 mg / kg, 95 % confidence and p-value = 2.11e-05.

5.1 Dynamic relationship between analysis quality and sample origin

We now know that the vast majority of samples (60%) are below the limit of quantification and for simplicity is reported as 0.000. A minimum number is quantified and is below the limit of the legislation (0.1 mg/kg). The rest (40%) is quantified and 99% of the time it is below 1.22mg / Kg.

We will now try to visualize how this information relates to each other using the Sankey graph. This graph is similar to that of stacked bars, however, with lines that show the relationships between variables.

Let’s list the Origin of the samples (Domestic or imported), the quality control tool and the quantified lead content:

GEMS2res_1 %>%  group_by(group,AnalyticalQAName,FoodOriginName, ResultValue=ResultValue>=0.10) %>%  summarise(Samples=length(group)) -> Sankey4
ggplot(as.data.frame(Sankey4), aes(y = Samples, axis1 = group, axis2 = AnalyticalQAName, axis3 = FoodOriginName, axis4=ResultValue)) +
    geom_alluvium(aes(fill = group=="NA"), width = 1/6) +
  geom_stratum(width = 1/5, fill = "white", color = "grey50") +
  scale_x_continuous(breaks = 1:4, labels = c("LOQ", "Quality Control", "Sample source", "Above 0.10\ mg/Kg")) +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +  theme(legend.position = "none")+ylab("Number of samples")

Figure 10: Sankey chart showing the relationship between the origin, the quality control technique used and the result in mg / Kg. It can be noted that either by origin, quality control or result there is no trend. It is also noted that between domestic and imported samples, they present the same ratio between quantified above 0.1 mg / kg and below the limit of quantification.

5.2 In search of matrices with higher levels

We will investigate only samples with higher levels of Lead: Herbs and spices, animal feed, stimulating drinks and Products for special nutritional use. First, how they relate to the time and amount of samples tested and quantified:

GEMS4=subset(GEMS2, GEMS2$CountryName=="WHO European Region" & GEMS2$FoodCategory=="ANIMAL FEED" | GEMS2$FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products" | GEMS2$FoodCategory=="Herbs, spices and condiments"  | GEMS2$FoodCategory=="Products for special nutritional use")
GEMS5=subset(GEMS2, GEMS2$CountryName=="WHO European Region")

GEMS4 %>% 
  group_by(SamplingPeriodEnd,FoodCategory) %>%
  count(more_than_1ppb=ResultValue<=0.001,sort=TRUE) %>%
  ggplot()+geom_col(aes(SamplingPeriodEnd,n,fill=more_than_1ppb))+ theme(axis.text.x = element_text(angle = 90),)+
  facet_wrap(~str_wrap(FoodCategory,width = 40))+ggtitle("Number of samples analysed according to matrix")



ggplot(GEMS4,aes(x=SamplingPeriodEnd,y=length(RowNum), fill=ResultValue<=0.001))+
  geom_bar(position="stack", stat="identity")+ theme(axis.text.x = element_text(angle = 90),)+
  facet_wrap(~str_wrap(FoodCategory,width = 40))+ggtitle("Number of samples analysed according to matrix")

Figure 11. Bar graphs grouping the 4 main classes with the highest average values of Lead in the period of 25 years. The number of quantified samples is show by the color difference (Quantified Red, Blue below the LOQ).


GEMS4 %>% 
  group_by(SamplingPeriodEnd,FoodCategory) %>%
  count(more_than_1ppb=ResultValue<=0.001,sort=TRUE) %>%
  ggplot()+geom_col(aes(SamplingPeriodEnd,n,fill=more_than_1ppb),position="fill")+ theme(axis.text.x = element_text(angle = 90),)+
  facet_wrap(~str_wrap(FoodCategory,width = 40))+ggtitle("Number of samples analysed according to matrix")


ggplot(GEMS4,aes(x=SamplingPeriodEnd,y=(RowNum), fill=ResultValue<=0.001))+
  geom_bar(position="fill", stat="identity")+ theme(axis.text.x = element_text(angle = 90),)+
  facet_wrap(~str_wrap(FoodCategory,width = 40))+ylab("ResultValue")+ggtitle("Ratio of quantified samples according to matrix")

Figure 12. Bar graphs grouping the 4 main classes with the highest average values of Lead in the period of 25 years. The values were standardized and each column corresponds to 100% of the values and the ratio between quantified and unquantified samples is demonstrated by the color difference (Quantified Red, Blue below the LOQ).

It is noted that samples of Herbs and spices are routinely quantified for a period of more than 10 years and even with variation in the number of samples, the quantified ratio is very similar along the years.

We know so far that these four food groups are often quantified. However, it is not clear whether some samples have very high levels and “drag” the average to higher mean values.

For this we will evaluate a boxplot with 99% of the data (to avoid outliers) and see where the quartiles are located:

ggplot(GEMS5, aes(x=FoodCategory,y=ResultValue))+
    geom_boxplot(show.legend = FALSE,alpha=1/10)+geom_hline(yintercept=0.1, linetype="solid", color = "red")+
        scale_x_discrete(breaks=unique(GEMS$FoodCategory),labels = function(x) str_wrap(str_replace_all(x, "gjgj" , " "), width = 80))+ylim(c(0,1.22))+
    coord_flip()+theme(axis.text.x = element_text(angle = 90,size =10))+xlab("Food Category")+ylab("Pb (mg/Kg)")+annotate("rect",ymin = 0,ymax=0.88, xmin = "Starchy roots and tubers",xmax="Sugar and confectionary (including cocoa products)", alpha=0.3, color="blue", fill="blue")

Figure 13. Boxplot with linear axis covering 99% of the samples in the range of 0.001 to 1.22mg/kg of Pb.

We can see that the worst case is that from stimulating drinks that include coffees and teas. The median is well within the limit of 0.1mg/kg and the third quartile (50% to 75%) stretches to more than 0.25mg/kg.

Through the cumulative distribution, we see that the problem is not outliers but a large amount of samples in different concentrations that is quantified in this class:

ggplot(GEMS5, aes(x=ResultValue, color=FoodCategory, label=FoodCategory),show.legend = FALSE)+
    stat_ecdf(geom = "line", aes(ResultValue),show.legend = FALSE) +xlab("Pb (mg/Kg)")+geom_vline(xintercept=0.1, linetype="solid", color = "red")+geom_vline(xintercept=1.22, linetype="solid", color = "blue")+xlim(c(0,1.22))+annotate("text", x = 0.52, y = 0.6, 
label = c("Stimulant beverages") , color="purple",size=5 )+annotate("segment", x = 0.30, xend = 0.25, y = 0.6, yend = 0.65, colour = "purple", size=1, alpha=0.9, arrow=arrow())+ylab("Cumulative distribution")

Figure 14. Graph with cumulative distribution of Lead content in food samples in the European Union. Red line represents the concentration of 0.1mg/kg of Pb and blue line 1.22mg/kg which is the content that comprises 99% of the samples.

We “investigated” whether within the class of stimulant drinks, which would be the worst matrix.

ggplot(subset(GEMS5,GEMS5$FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products") , aes(x=ResultValue, color=FoodName, label=FoodName))+
    stat_ecdf(geom = "line", aes(ResultValue)) +xlab("Pb (mg/Kg)")+geom_vline(xintercept=0.1, linetype="solid", color = "red")+geom_vline(xintercept=1.22, linetype="solid", color = "blue")+xlim(c(0,1.22))+ylab("Cumulative distribution")+ theme(legend.position = c(0.7, 0.3))

  
  #theme(  plot.background = element_rect(fill = "lightcyan2"), panel.background= element_rect(fill = "lightcyan2"))

Figure 15. Graph with cumulative distribution of Lead content in food samples in the European Union within the category “Stimulant drinks”. Red line represents the concentration of 0.1mg/kg of Pb and blue line 1.22mg/kg which is the content that includes 99% of the samples.

The worst case is the tea infusions, where more than 50% of the samples had levels above 0.25mg/kg of Lead or 2.5x more than the limit.

When filtering for “Tea (infusion)” only we can see it have the highest distribution of all.

95% of samples are below 2.48mg/kg. Ten times higher than the mean values for all other samples in EU Union.

5.3 Looking back to the worst case

Since we found the TEA as the sample with higher constant level food contaminated with Lead. Let’s visualize how this sample behaves with these 4 variable selected: Result, Quality tool, Origin and LOQ:

GEMS2res_1 %>% filter(CountryName=="WHO European Region") %>%
  filter(FoodName=="Tea (Infusion)") %>%
  group_by(ResultValue=ResultValue>=0.1,AnalyticalQAName,FoodOriginName,group) %>%
  summarise(Samples=length(AnalyticalQAName)) -> Sankey4
ggplot(as.data.frame(Sankey4), aes(y = Samples, axis1 = ResultValue, axis2 = AnalyticalQAName, axis3 = FoodOriginName, axis4=group)) +
  geom_alluvium(aes(fill = ResultValue), width = 1/6)+
  scale_x_discrete(expand = c(0.1,0.1))+
  geom_stratum(width = 1/5, fill = "white", color = "grey50") +
  scale_x_continuous(breaks = 1:4, labels = c( "Above 0.10\ mg/Kg?", "Quality Control", "Sample source","LOQ"))+
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +  theme(legend.position = "none")+ylab("Number of samples") 

Figure 15. Alluvial plot from Tea Samples in EU region. The vast majority of samples (more than 75%) are higher than 0.10mg/Kg and didn’t report the LOQ. Since most of non reported LOQ data are from old data, it’s interesting check if it all comes from previous years.

GEMS2res_1 %>% filter(CountryName=="WHO European Region") %>%
  filter(FoodName=="Tea (Infusion)") %>%
  group_by(ResultValue=ResultValue>=0.1,AnalyticalQAName,FoodOriginName, group, SamplingPeriodEnd) %>%
  summarise(Samples=length(group)) -> Sankey4
ggplot(as.data.frame(Sankey4), aes(y = Samples, axis1 = ResultValue, axis2 = AnalyticalQAName, axis3 = FoodOriginName, axis4=group, axis5=SamplingPeriodEnd)) +
  geom_alluvium(aes(fill = ResultValue), width = 1/5) +scale_x_discrete(expand = c(.1, .1))+
  geom_stratum(width = 1/5, fill = "white", color = "grey50") +
  scale_x_continuous(breaks = 1:5, labels = c("Above 0.10\ mg/Kg?", "Quality Control", "Sample source","LOQ" , "Year")) +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +  theme(legend.position = "none")+ylab("Number of samples") 

Figure 16. Same as figure 15 except with year included(as SamplingPeriodEnd). No, we can not say that “Old samples (pre-2011) used to have more Lead”. To confirm we can normalize the content of ResultValue variable and plot it along the years.

GEMS2res_1 %>% filter(CountryName=="WHO European Region") %>%
  filter(FoodName=="Tea (Infusion)") %>%
ggplot(aes(x=SamplingPeriodEnd,y=(RowNum), fill=ResultValue>=0.1))+
  geom_bar(position="fill", stat="identity")+ theme(axis.text.x = element_text(angle = 90),)+
  facet_wrap(~str_wrap(FoodName,width = 40))+ylab("ResultValue")+ggtitle("Ratio of quantified samples according to matrix")

Figure 17. Stacked bar graph showing normalized number of samples above 0.1mg/Kg of Lead per year. No, the proportion of samples above 0.1 are not decreasing with time. To be sure, lets analyze using boxplot and linear regression, with the median of Result as the central line in each box.

GEMS2res_1 %>% filter(CountryName=="WHO European Region") %>% group_by(SamplingPeriodEnd) %>%
  filter(FoodName=="Tea (Infusion)") %>%
ggplot(aes(x=SamplingPeriodEnd,y=ResultValue))+scale_y_log10()+
  geom_boxplot(na.rm=TRUE)+ theme(axis.text.x = element_text(angle = 90),)+ylab("ResultValue")+geom_smooth(method = "lm", se=FALSE, color="blue", aes(SamplingPeriodEnd, ResultValue))+geom_smooth(method = "lm", color="blue",aes(group=1), na.rm=TRUE)

Figure 18. Boxplot from Tea samples in Eu Region by Year. No trend, mean content of Lead in food is stable for the period of 10 years.

6. Conclusion

Most foods with high lead values have a very limited intake: “ANIMAL FEED” (obviously), “Stimulant beverages” (occasional use), “Herbs, spices and condiments” (only as ingredients and in small quantities). Only “Products for special nutritional use” are critical as they may result in a higher intake for longer periods.

There are no indications that imported samples (to the European Union) are quantified more frequently than domestic ones, indicating contamination at source.

In the European Union (the region with more data available), 91% of all samples are below the 0,1mg/kg international limit and 99% are below 1.21mg/kg.

Stimulant Beverages, specially tea, present a high continuous source of Lead in the diet. Just to address how much, 50% of tea infusion samples are higher than 0,2mg/kg while 95% of other samples fall on this range.

In the 10 years with most data from European Union and the the most studies sample (Tea (Infusion)), no difference was noted in the mean value of contamination.

Some technical questions remain: were the tea samples (Stimulant beverages) digested from the dry mass? Were the samples identified as “infusion” analyzed as “ready for consumption”, ie direct reading? What is the real possibility of misplacement of units in the results such as ug/kg reported as mg/kg? Should they be treated and investigated as outliers or as transcription errors?

7. References:

https://www.fda.gov/food/metals-and-your-food/lead-food-foodwares-and-dietary-supplements

Other…

Tea investigation

How many tea samples we have under Tea (Infusion)

GEMS5 %>% filter(FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products") %>% group_by(LocalFoodName) %>% summarise(total=length(LocalFoodName)) %>% ggplot()+geom_col(aes(total,reorder(LocalFoodName,total,max),fill=LocalFoodName,),show.legend = FALSE)
`summarise()` ungrouping output (override with `.groups` argument)
Warning messages:
1: In doTryCatch(return(expr), name, parentenv, handler) :
  display list redraw incomplete
2: In doTryCatch(return(expr), name, parentenv, handler) :
  invalid graphics state
3: In doTryCatch(return(expr), name, parentenv, handler) :
  invalid graphics state

Diferença médias: concentração Doméstico e Importada

Primeiro, vamos separar os dados de chá analizados na União Europáia:

GEMS_EU<-subset(GEMS2, GEMS2$CountryName=="WHO European Region" & FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products")

GEMS_EU %>% group_by(FoodOriginName, Quantificadas=ResultValue>=0.001) %>% summarise(Numero=length(ResultValue)) %>% ggplot() +geom_col(aes(FoodOriginName,Numero, fill=Quantificadas))
`summarise()` regrouping output by 'FoodOriginName' (override with `.groups` argument)

Temos a proporções de quantificadas entre amostras domesticas mais alta seguida pelas importadas e desconhecidas.

Abaixo podemos ver esses valores

GEMS_EU %>% group_by(FoodOriginName, Quantificadas=ResultValue>=0.001) %>% summarise(Numero=length(ResultValue))
`summarise()` regrouping output by 'FoodOriginName' (override with `.groups` argument)

Apesar de uma maior frequencia de quantificação, os valores médios são consideravelmente próximos, mediana e o quartil de 95% levemente acima para amostras domesticas:


GEMS_EU %>% group_by(FoodOriginName) %>% summarise(ResultValue=mean(ResultValue))

GEMS_EU %>% group_by(FoodOriginName) %>% summarise(ResultValue=median(ResultValue))

GEMS_EU %>% group_by(FoodOriginName) %>% summarise(ResultValue=quantile(ResultValue,0.95))

GEMS_EU %>% filter(ResultValue>0.0001) %>%  group_by(FoodOriginName) %>% ggplot()+ geom_boxplot(aes(FoodOriginName,ResultValue))+ylim(c(-0.001,10))+scale_y_log10()

GEMS_EU %>% filter(ResultValue>0.0001) %>%  group_by(FoodOriginName) %>% summarise(ResultValue=mean(ResultValue))

Não há diferença entre as infusões e o sólido?

GEMS_EU %>% filter(FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products") %>% ggplot()+ geom_boxplot(aes(ResultValue,FoodName))+scale_x_log10()

Média ResultValue, separando por país:

GEMS1 %>% filter(FoodName=="TEAS AND HERBAL TEAS (SOLID)") %>% group_by(CountryName) %>% summarise(ResultValue=mean(ResultValue))
`summarise()` ungrouping output (override with `.groups` argument)
options(scipen=10000)
GEMS1 %>% filter(FoodCategory=="Stimulant beverages, dried and diluted excluding cocoa products") %>% group_by(CountryName)%>%ggplot()+geom_boxplot(aes(ResultValue,CountryName))+scale_x_log10()+facet_wrap(~FoodName,scales="free")

The infusions appear to be expressed in dry mass, or at least converted after extraction on some countries like Singapure and EU. Data on USA, France, Hong Kong, Australia and New Zealand cluster close together on a range that is 100 times lower.

Let’s now investigate what would be the single most high level of lead in the GEMS database.

Worst Food Name (mean value):

  GEMS1 %>%
  group_by(FoodName) %>%
  summarise(ResultValue= mean(ResultValue,na.rm=TRUE)) %>% 
  arrange(desc(ResultValue)) %>% 
  top_n(15) %>% 
  ggplot()+ geom_col(aes(ResultValue,reorder(FoodName,ResultValue,max),fill=FoodName),show.legend = FALSE)
`summarise()` ungrouping output (override with `.groups` argument)
Selecting by ResultValue

Worst Local Food Name:

  GEMS1 %>%
  group_by(LocalFoodName) %>%
  summarise(ResultValue= mean(ResultValue,na.rm=TRUE)) %>% 
  arrange(desc(ResultValue)) %>% 
  top_n(25) %>% 
    ggplot()+ geom_col(aes(ResultValue,reorder(LocalFoodName,ResultValue,mean),fill=LocalFoodName),show.legend=FALSE)+scale_x_log10()
`summarise()` ungrouping output (override with `.groups` argument)
Selecting by ResultValue

2 Teas presentations appear as higher levels on FoodName, however data can be skewed due to lower samples and more presence of outliers.

When considering the final form on LocalFoodName, Tea forms appear 8 times.

Filtering samples with more than 5000 analyses performed. Summarizing with mean, sd, n, RSD, 95% percentil:

GEMS1 %>%
  group_by(FoodName) %>%
  summarise(mean= mean(ResultValue,na.rm=TRUE),
            median= median(ResultValue,na.rm=TRUE),
            n=n(),
            sd=sd(ResultValue,na.rm=TRUE),
            RSD=sd(ResultValue,na.rm=TRUE)*100/mean(ResultValue,na.rm=TRUE),
            percentil_95=quantile(ResultValue,0.95,na.rm=TRUE),
            ratio_ND=prop.table(table(ResultText=="ND"))[2]) %>%
  filter(n>=100)%>%
  arrange(desc(median))-> worst_case
`summarise()` ungrouping output (override with `.groups` argument)
kable(worst_case)
FoodName mean median n sd RSD percentil_95 ratio_ND
Tea, green, black (black, fermented and dried) 0.6850643 0.3300 5553 4.5257447 660.6307 2.200000 0.0628489
TEAS AND HERBAL TEAS (SOLID) 1.0469433 0.3100 5354 15.6918662 1498.8267 2.190000 0.1016063
Feed 1.2395738 0.0700 9349 22.3002025 1799.0218 2.186000 0.3076265
Mushrooms 0.0963046 0.0130 10005 0.3352460 348.1101 0.420000 0.3828086
Wine 0.0110209 0.0060 11079 0.0191751 173.9891 0.035000 0.2176189
Meat and meat products NES 0.7130358 0.0038 9764 37.5743209 5269.6262 0.270000 0.4909873
Cattle meat 0.0252862 0.0000 6686 1.2131144 4797.5382 0.034000 0.8232127
Cattle milk 0.0058318 0.0000 7861 0.0171696 294.4150 0.032650 0.7013103
Cereals and cereal-based products NES 0.0121510 0.0000 5193 0.0305157 251.1373 0.060000 0.5875217
FISHES 0.0115102 0.0000 10260 0.0533181 463.2267 0.060000 0.7422027
Fruit juice 0.0074039 0.0000 5543 0.0270615 365.5047 0.030000 0.5933610
Pig liver 0.0303456 0.0000 7610 0.1361239 448.5790 0.145000 0.5328515
Pig meat 0.0037852 0.0000 7018 0.0175946 464.8234 0.022000 0.8412653
Rice 0.0339760 0.0000 5162 0.1038827 305.7531 0.160850 0.5951182
Pig kidney 0.0274744 0.0000 9910 0.1514991 551.4194 0.120808 0.5821393

Worst food boxplot:

  GEMS1 %>%
  filter(FoodName %in% worst_case$FoodName)%>%
  group_by(FoodName) %>% 
   ggplot()+
    geom_boxplot(aes(ResultValue,FoodName))+
    geom_point(data=worst_case,aes(percentil_95,FoodName),color="red")+
    geom_point(data=worst_case,aes(mean,FoodName),color="blue")+xlim(-0.1,1)+scale_x_log10()
Scale for 'x' is already present. Adding another scale for 'x', which will
replace the existing scale.

Same situation, although Feed also appear to show high lead contamination and have the same 95% quantile (above 2mg/kg). The median is much low due to higher presence of ND samples (about 30%)

LS0tCnRpdGxlOiAnKElOIFBST0dSRVNTISEpMjUgeWVhcnMgb2YgTGVhZCBpbiBmb29kOicKYXV0aG9yOiAiRmFiaWFubyBTaWx2YSAoZHJodGNsQGdtYWlsLmNvbSkiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2NfZmxvYXQ6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCnN1YnRpdGxlOiBBIHN0dWR5IGZyb20gdGhlIHdvcmxkIGRpc3RyaWJ1dGlvbiBvbiBMZWFkIGNvbnRhbWluYXRpb24gb24gZm9vZCB1c2luZwogIHRoZSBHRU1TRk9PRFMoV0hPKSBkYXRhYmFzZSBkdXJpbmcgMTk5NS0yMDIwCi0tLQpgYGB7Y3NzIGluY2x1ZGU9RkFMU0V9Ci5ibHVlQ29kZSB7CmJhY2tncm91bmQtY29sb3I6ICNlNmYwZmYKfQpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGNvbGxhcHNlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJywgY2xhc3Muc291cmNlPSJiZy1zdWNjZXNzIiwgZXJyb3IgPSBUUlVFKQpgYGAKCgoKIyAxLiBJbnRyb2R1Y3Rpb24KCkxlYWQgKFBiKSBpcyB0aGUgc2Vjb25kIG1vc3QgdG94aWMgbWV0YWwgYWZ0ZXIgYXJzZW5pYyAoQXMpLCBjb21wcmlzaW5nIDAuMDAyJSBvZiB0aGUgZWFydGgncyBjcnVzdC4KCkxlYWQsIGxpa2Ugb3RoZXIgaGVhdnkgbWV0YWxzLCBkb2VzIG5vdCBiaW9kZWdyYWRlIG9yIGRpc2FwcGVhciBmcm9tIHRoZSBlbnZpcm9ubWVudCBvdmVyIHRpbWUuIEFsdGhvdWdoIGxlYWQgbGV2ZWxzIGluIHRoZSBmb29kIHN1cHBseSBkZWNsaW5lZCBkcmFtYXRpY2FsbHkgYmV0d2VlbiB0aGUgMTk3MHMgYW5kIDE5OTBzLCBsb3cgbGV2ZWxzIG9mIGxlYWQgY29udGludWUgdG8gYmUgZGV0ZWN0ZWQgaW4gc29tZSBmb29kcyBkdWUgdG8gdGhlIGNvbnRpbnVlZCBwcmVzZW5jZSBpbiB0aGUgZW52aXJvbm1lbnQuIExlYWQgZnJvbSB0aGUgc29pbCBjYW4gYmUgYWJzb3JiZWQgYnkgcGxhbnRzIGdyb3duIHRvIG9idGFpbiBmcnVpdCBvciB2ZWdldGFibGVzIG9yIHBsYW50cyB1c2VkIGFzIGluZ3JlZGllbnRzIGluIGZvb2Qgb3IgYW5pbWFsIGZlZWQuCgpMZWFkIGlzIHBvaXNvbm91cyB0byBodW1hbnMgYW5kIGNhbiBhZmZlY3QgcGVvcGxlIG9mIGFueSBhZ2Ugb3IgaGVhbHRoLiBMZWFkIGlzIGVzcGVjaWFsbHkgaGFybWZ1bCB0byB2dWxuZXJhYmxlIHBvcHVsYXRpb25zLCBpbmNsdWRpbmcgYmFiaWVzLCB5b3VuZyBjaGlsZHJlbiwgcHJlZ25hbnQgd29tZW4gYW5kIHRoZWlyIGZldHVzZXMgYW5kIG90aGVycyB3aXRoIGNocm9uaWMgaGVhbHRoIGNvbmRpdGlvbnMuIEhpZ2ggbGV2ZWxzIG9mIGxlYWQgZXhwb3N1cmUgY2FuIHNlcmlvdXNseSBkYW1hZ2UgdGhlIGhlYWx0aCBhbmQgZGV2ZWxvcG1lbnQgb2YgY2hpbGRyZW4sIHNwZWNpZmljYWxseSB0aGUgYnJhaW4gYW5kIG5lcnZvdXMgc3lzdGVtLiBUaGUgbmV1cm9sb2dpY2FsIGVmZmVjdHMgb2YgaGlnaCBsZXZlbHMgb2YgbGVhZCBleHBvc3VyZSBkdXJpbmcgZWFybHkgY2hpbGRob29kIGluY2x1ZGUgbGVhcm5pbmcgZGlzYWJpbGl0aWVzLCBiZWhhdmlvcmFsIGRpZmZpY3VsdGllcyBhbmQgcmVkdWNlZCBJUS4gQmVjYXVzZSBsZWFkIGNhbiBhY2N1bXVsYXRlIGluIHRoZSBib2R5LCBldmVuIGxvdy1sZXZlbCBjaHJvbmljIGV4cG9zdXJlIGNhbiBiZSBkYW5nZXJvdXMgb3ZlciB0aW1lLgoKSXQgaXMgZXN0aW1hdGVkIHRoYXQgZXhwb3N1cmUgdG8gbGVhZCB3YXMgcmVzcG9uc2libGUsIGluIDIwMDQsIGZvciAxNDMgdGhvdXNhbmQgZGVhdGhzCmFuZCAwLjYlIG9mIHRoZSBnbG9iYWwgZGlzZWFzZSBidXJkZW4sIHRha2luZyBpbnRvIGFjY291bnQgbWlsZCBtZW50YWwgdW5kZXIgZGV2ZWxvcG1lbnQgYW5kIGNhcmRpb3Zhc2N1bGFyIG91dGNvbWVzCgojIDEuMSBQdXJwb3NlCgpUaGlzIHdvcmsgZm9jdXMgb246CgoxLSBpbnZlc3RpZ2F0ZSB0aGUgY29udGVudCBvZiBMZWFkIGluIGZvb2RzIHVzaW5nIHRoZSBSIGxhbmd1YWdlIGFzIGEgdGVjaG5pcXVlIGZvciBmaWx0ZXJpbmcsIGFuYWx5emluZyBhbmQgbW9kZWxpbmcgZGlzdHJpYnV0aW9ucyBhY2NvcmRpbmcgdG8gdGhlIHZhcmlldGllcyBvZiBmb29kIGNvbnN1bWVkLCBjb3VudHJpZXMsIGFuYWx5dGljYWwgY29uZGl0aW9ucyBhbmQgZGF0ZXMuCgoyLSBGaW5kIG91dCB3aGVyZSBhbmQgd2hpY2ggZm9vZHMgYXJlIG1vc3QgY29udGFtaW5hdGVkIGFuZCBjcmVhdGUgcG9zc2libGUgaHlwb3RoZXNlcyBvZiBjYXVzZS4KCiMgMi4gSW5pdGlhbCBkYXRhIGFuZCBkYXRhYmFzZSBpbmdlc3Rpb246CgpUaGUgaW5mb3JtYXRpb24gd2FzIHRha2VuIGZyb20gdGhlICJHRU1TRk9PRFMiIGRhdGFiYXNlIG9mIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIChXSE8pIGF2YWlsYWJsZSBhdDoKCmh0dHBzOi8vZXh0cmFuZXQud2hvLmludC9nZW1zZm9vZC8KCgpBbGwgY2FsY3VsYXRpb25zIGFuZCBncmFwaHMgd2VyZSBtYWRlIGluICoqIFJzdHVkaW8gdmVyc2lvbiAxLjMuOTU5ICoqIGFuZCAqKiBSIGJhc2UgMy42LjMgKiogYW5kIHVzaW5nIHRoZSBmb2xsb3dpbmcgbGlicmFyaWVzOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxjbGFzcy5zb3VyY2U9ImJnLXN1Y2Nlc3MifQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ2FsbHV2aWFsKQpsaWJyYXJ5KHRyZWVtYXBpZnkpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoRGVzY1Rvb2xzKQpsaWJyYXJ5KGdndGhlbWVzKQpgYGAKClRoZSBjb2RlcyB1c2VkIHRvIHBlcmZvcm0gdGhhdCB3b3JrIHdpbGwgYmUgc2hvd24uIEFsbCBwcm9jZXNzaW5nIGlzIGJhc2VkIG9uIG1vcmUgbW9kZXJuIHN0YW5kYXJkcyBpbiByZXByb2R1Y2liaWxpdHkgW3JlZl0gaW4gcmVzZWFyY2ggd2hlcmUgaW5kaXZpZHVhbCBkYXRhIGlzIG5vdCBtYW5pcHVsYXRlZCBhbmQgdGhlIGVudGlyZSBwcm9jZXNzIChmcm9tIGRhdGFiYXNlIHJlY29nbml0aW9uIHRvIHRoZSBmaW5hbCByZXBvcnQpIGlzIGRvbmUgaW4gYW4gYXV0b21hdGVkIHdheSBhbmQgY2FuIGJlIGF1ZGl0ZWQgYnkgdGhlIGNvZGUgdXNlZC4KCldlIHN0YXJ0ZWQgYnkgY29uc29saWRhdGluZyB0aGUgZGF0YWJhc2UsIHRoZSBmaWxlcyB3ZXJlIGRvd25sb2FkZWQgaW4gZ3JvdXBzIHRvIGZhY2lsaXRhdGUgdGhlIHRyYW5zZmVyaW5nOgoKYGBge3IgIG1lc3NhZ2U9RkFMU0UsIGNsYXNzLnNvdXJjZT0iYmctc3VjY2VzcyJ9CnNldHdkKCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcyIpCkdFTVMyMDAwIDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgxOTgwLTIwMDApLmNzdiIsIGRlYz0iLCIpCkdFTVMyMDEwIDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgyMDAwLTIwMTApLmNzdiIsIGRlYz0iLCIpCkdFTVMyMDE0IDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgyMDEwLTIwMTQpLmNzdiIsIGRlYz0iLCIpCkdFTVMyMDE2IDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgyMDE0LTIwMTYpLmNzdiIsIGRlYz0iLCIpCkdFTVMyMDE4IDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgyMDE2LTIwMTgpLmNzdiIsIGRlYz0iLCIpCkdFTVMyMDIwIDwtIHJlYWQuY3N2KCIvbW50L0RBRE9TL0RhdGEgU2NpZW5jZS9HRU1Gb29kcy9HZW1zZm9vZCgyMDE4LTIwMjApLmNzdiIsIGRlYz0iLCIpCkdFTVMgPC0gcmJpbmQoR0VNUzIwMDAsR0VNUzIwMTAsR0VNUzIwMTQsR0VNUzIwMTYsR0VNUzIwMTgsR0VNUzIwMjApCmBgYAoKCiMjIDMuIE1haW4gdmFyaWFibGVzOgoKQ2hlY2tpbmcgdGhlIGluZm9ybWF0aW9uIGF2YWlsYWJsZToKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQpuYW1lcyhHRU1TKQpgYGAKClRoZSBkYXRhIHVzZWQgd2lsbCBiZTogCiJDb3VudHJ5TmFtZSIgdGhlIGNvdW50cnkgb2Ygb3JpZ2luIG9mIHRoZSBhbmFseXRpY2FsIGRhdGEuCiJGb29kQ2F0ZWdvcnkiIFRoZSB0eXBlIG9mIGZvb2QsIHRoYXQgaXMsIHRoZSBhbmFseXRpY2FsIG1hdHJpeCBzdWNoIGFzICJWZWdldGFibGVzIiBvciAiQW5pbWFsIEZlZWQiLgoiRm9vZE9yaWdpbk5hbWUiLCB3aGljaCBkZXNjcmliZXMgdGhlIG9yaWdpbiBhcyBEb21lc3RpYyBvciBpbXBvcnRlZC4KIkxPUSIgdGhlIGxpbWl0IG9mIHF1YW50aWZpY2F0aW9uLgoiUmVzdWx0VmFsdWUiLCB0aGUgdmFsdWUgb2J0YWluZWQgaW4gdGhlIGFuYWx5c2lzIHdoZW4gcXVhbnRpZmllZC4KIkFuYWx5dGljYWxRQU5hbWUiLCB3aGljaCBkZXNjcmliZXMgdGhlIGNvbmRpdGlvbnMgb2YgdGhlIGFuYWx5c2lzLCBpZiBtYWRlIGluIGFuIGFjY3JlZGl0ZWQgbGFib3JhdG9yeSBvciB3aXRoIHVzZSBvZiBpbnRlcm5hbCBxdWFsaXR5IGNvbnRyb2wgb25seS4KIkZvb2ROYW1lIiB3aGljaCBpcyB0aGUgZXhhY3QgZGVzY3JpcHRpb24gb2YgdGhlIGZvb2QgYXMgIlBvdGF0byIgb3IgIlJpY2UiLgoKQmVmb3JlLCB0aGUgZXZhbHVhdGlvbiBvZiB0aGUgdW5pdHMgb2YgdGhlIExlYWQgY29udGVudCBzaG91bGQgYmUgbWFkZToKCgpgYGB7ciwgY29sbGFwc2U9VFJVRSwgfQpzdW1tYXJ5KEdFTVMkVW5pdE5hbWUpCmBgYAoKCldoZW4gcXVhbnRpZmllZCwgdGhlIHJlc3VsdCB3YXMgcmVwb3J0ZWQgaW4gdHdvIGRpZmZlcmVudCB1bml0cywgbWcva2cgYW5kIHVnL2tnLgoKQWxsIHVuaXRzIHdpbGwgYmUgY2hhbmdlZCB0byBtZy9rZyBhbmQgdGhlIHZhbHVlcyBhZGp1c3RlZCBmb3IgdGhlIHNhbWUgdW5pdCwgd2hlcmUgMXVnL2tnIGlzIGVxdWl2YWxlbnQgdG8gMC4wMDFtZy9rZzoKCmBgYHtyIGNvbGxhcHNlPVRSVUUsIGV2YWw9RkFMU0V9ClN5cy50aW1lKCkKR0VNU3ggPC1HRU1TCiAgR0VNUzFfMTQgPC0gR0VNU1ssMTRdCiAgR0VNUzFfMTAgPC0gR0VNU1ssMTBdCiAgR0VNUzFfMTIgPC0gR0VNU1ssMTJdCiBmb3IoaSBpbiAxOm5yb3coR0VNUykpewogIGlmKEdFTVMxXzEyW2ldPT0idWcva2ciKXsKICAgIEdFTVMxXzE0W2ldIDwtIEdFTVMxXzE0W2ldLzEwMDAKICAgIEdFTVMxXzEwW2ldIDwtIEdFTVMxXzEwW2ldLzEwMDAKICAgIEdFTVMxXzEyW2ldIDwtICJtZy9rZyIKICB9IGVsc2UgewpOVUxMCiAgfQogfQpHRU1TeFssMTRdPC0gR0VNUzFfMTQKR0VNU3hbLDEwXTwtIEdFTVMxXzEwCkdFTVN4WywxMl08LSBHRU1TMV8xMgoKU3lzLnRpbWUoKQpgYGAKCk5PVEU6IER1ZSB0byBsb25nIHByb2Nlc3NpbmcgdGltZSwgdGhlIGNvZGUgY2h1bmsgYWJvdmUgd2FzIHByZXZpb3VzbHkgZXhlY3V0ZWQgYW5kIHNhdmVkIGFzIGEgUkRTIGZpbGUuIFdoaWNoIHdpbGwgYmUgcmVhZCBhbmQgc3RvcmVkIGluIGEgbmV3IG9iamVjdChHRU1TMSkuIAoKYGBge3J9CkdFTVMxIDwtIHJlYWRSRFMoIi9tbnQvREFET1MvRGF0YSBTY2llbmNlL0dFTUZvb2RzL0dFTVMxLnJkcyIpCmBgYAoKQ29uZmlybWluZyB0aGUgY2hhbmdlOgoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CnN1bW1hcnkoR0VNUzEkVW5pdE5hbWUpCmBgYAoKCgpUaGUgTGltaXQgb2YgUXVhbnRpZmljYXRpb24gaGFzIGFsc28gYmVlbiBjaGFuZ2VkIChDb2x1bW4gMTQpCgoKCiMgNC4gRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIHNlYXJjaCBmb3IgdGhlIGJpZ2dlc3QgY29udGFtaW5hdGlvbnM6CgojIyA0LjEgRGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhIHNvdXJjZQoKTGV0J3Mgc2VlIGluIGEgcHJhY3RpY2FsIHdheSB3aGljaCBjb3VudHJpZXMgY29udHJpYnV0ZWQgdGhlIG1vc3QgdG8gdGhlIFdITyBkYXRhYmFzZSB1c2luZyB0aGUgQ291bnRyeU5hbWUgdmFyaWFibGUgYW5kIGhvdyBtYW55IHRpbWVzIGEgcGFydGljdWxhciBjb3VudHJ5IGFwcGVhcnM6CgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9JzEzMCUnfQpHRU1TMSAlPiUgY291bnQoQ291bnRyeU5hbWUsc29ydCA9IFRSVUUpICU+JSB0b3BfbigyMCkgJT4lIGdncGxvdCgpK2dlb21fY29sKGFlcyhuLHJlb3JkZXIoQ291bnRyeU5hbWUsbixtYXgpLGZpbGw9Q291bnRyeU5hbWUpLHNob3cubGVnZW5kID0gRkFMU0UpK3RoZW1lX2Vjb25vbWlzdCgpK3hsYWIoIkNvdW50cnkgTmFtZSIpK3lsYWIoIk51bWJlciBvZiBzYW1wbGVzIikKCmBgYApGaWd1cmUgMS4gTnVtYmVyIG9mIHNhbXBsZXMgcGVyIGRhdGFiYXNlIGNvbnRyaWJ1dG9yIGluIHRoZSBwZXJpb2QgZnJvbSAxOTk1IHRvIDIwMjAuCiAKIldITyBFdXJvcGVhbiBSZWdpb24iLCAiQ2hpbmEiLCAiQ2FuYWRhIiwgIlVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYSIsICJCcmF6aWwiIGFuZCAiVGhhaWxhbmQiIGFyZSB0aGUgbWFpbiBjb250cmlidXRvcnMuIFdITy9FdXJvcGUgaGF2ZSA1NCUgb2YgY29udHJpYnV0aW9ucyBhbmQgQ2FuYWRhIGluIHNlY29uZCB3aXRoIDEzJS4KCkZyb20gbm93IG9uLCBhIHNhbXBsZSB3aWxsIGJlIHVzZWQgY29udGFpbmluZyBvbmx5IHRoZSA2IGNvdW50cmllcyB3aXRoIHRoZSBtb3N0IGNvbnRyaWJ1dGlvbnMgKEdFTVMyKQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KR0VNUzI9c3Vic2V0KEdFTVMxLEdFTVMxJENvdW50cnlOYW1lPT0iV0hPIEV1cm9wZWFuIFJlZ2lvbiJ8IEdFTVMxJENvdW50cnlOYW1lPT0iQ2FuYWRhInxHRU1TMSRDb3VudHJ5TmFtZT09IkNoaW5hIiB8R0VNUzEkQ291bnRyeU5hbWU9PSJVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EiIHwgR0VNUzEkQ291bnRyeU5hbWU9PSJCcmF6aWwifCBHRU1TMSRDb3VudHJ5TmFtZT09IlRoYWlsYW5kIikKYGBgCgpUbyBsb29rIGZvciBhbnkgcG9zc2libGUgY2hhbmdlIGluIHRoZSBxdWFsaXR5IG9mIHRoZSB0ZXN0cyBhY2NvcmRpbmcgdG8gdGhlIHRpbWUsIHdlIHdpbGwgY29udmVydCBhbGwgZGF0ZXMgdG8geWVhci4KCmBgYHtyLGNvbGxhcHNlPVRSVUV9CkdFTVMyJFNhbXBsaW5nUGVyaW9kRW5kIDwtIGFzLlBPU0lYY3QoR0VNUzIkU2FtcGxpbmdQZXJpb2RFbmQsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSDolTTolUyIpCkdFTVMyJFNhbXBsaW5nUGVyaW9kRW5kIDwtIGZvcm1hdChHRU1TMiRTYW1wbGluZ1BlcmlvZEVuZCwgZm9ybWF0PSIlWSIpCmBgYAoKIyMgNC4yIEFuYWx5dGljYWwgUXVhbGl0eSBvZiBUZXN0cwoKRXZhbHVhdGluZyB0aGUgYXZhaWxhYmxlIGluZm9ybWF0aW9uIG9uIHdoaWNoIHRvb2xzIHVzZWQgZm9yIHRoZSBxdWFsaXR5IG9mIHRoZSB0ZXN0cywgdGhlIHZhcmlhYmxlICJBbmFseXRpY2FsUUFOYW1lIiB3aWxsIGJlIHVzZWQ6CgoKYGBge3IsIGNvbGxhcHNlPVRSVUUsZmlnLmFsaWduPSdjZW50ZXInfQpnZ3Bsb3QoR0VNUykrZ2VvbV9iYXIoYWVzKHJlb3JkZXIoQW5hbHl0aWNhbFFBTmFtZSxBbmFseXRpY2FsUUFOYW1lLGZ1bmN0aW9uKHgpK2xlbmd0aCh4KSkKICAgICAgICAgICAgICAgICAgICAgICAgLGZpbGw9QW5hbHl0aWNhbFFBTmFtZSksIHNob3cubGVnZW5kPSBGQUxTRSwgc3RhdCA9ICJjb3VudCIpKwogIGNvb3JkX2ZsaXAoKSsKICB4bGFiKCJBbmFseXRpY2FsUUFOYW1lIikreGxhYigiUXVhbGl0eSBjb250cm9sIFRvb2wiKSt5bGFiKCJOdW1iZXIgb2Ygc2FtcGxlcyIpCmBgYApGaWd1cmUgMi4gVHlwZSBvZiBhbmFseXRpY2FsIHF1YWxpdHkgY29udHJvbCB0b29sIHVzZWQgaW4gcmVsYXRpb24gdG8gdGhlIG51bWJlciBvZiBzYW1wbGVzIGluIHRoZSBkYXRhYmFzZS4KCldlIHNlZSB0aGF0IHRoZSBlcXVpdmFsZW50IG9mIGFwcHJveGltYXRlbHkgNDglICgxNzUsMTMzKSBvZiB0aGUgdGVzdHMgd2VyZSBjYXJyaWVkIG91dCBpbiBhY2NyZWRpdGVkIGxhYm9yYXRvcmllcyBhbmQgMTYlICg1MTEzOSkgaGFkIHNvbWUgaW50ZXJuYWwgcXVhbGl0eSBjb250cm9sIG9yIHByb2ZpY2llbmN5IHRlc3QuCgoKV2Ugbm93IHdlIGNhbiBpbnZlc3RpZ2F0ZSB3aGV0aGVyIHRoZSBvcmlnaW4gb2YgdGhlIGFuYWx5dGljYWwgZGF0YSAod2hldGhlciBvciBub3QgdG8gdXNlIGFuIGFjY3JlZGl0ZWQgbGFib3JhdG9yeSkgaW50ZXJmZXJlcyB3aXRoIHRoZSBtZWFuL3NwcmVhZCB3aXRoaW4gdGhlIGRhdGEgZm9yIGVhY2ggY291bnRyeS4gRmlyc3QsIHdlIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBhbW9uZyBhbGwgc2FtcGxlcyBidXQgdGFraW5nIGludG8gYWNjb3VudCBhbGwgcXVhbnRpZmllZCByZXN1bHRzLCBmaXJzdCBhIHN1bW1hcnkgb2YgdGhlIHRvdGFsIGRhdGE6Cgp0b3RhbCBhdmVyYWdlOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRSwgRVZBTD1UUlVFfQpHRU1TMiAlPiUgZ3JvdXBfYnkoQ291bnRyeU5hbWUpICU+JSAgc3VtbWFyaXNlKG1lYW49bWVhbihSZXN1bHRWYWx1ZSxuYS5ybT1UUlVFKSwgbj1uKCkpICU+JSBrYWJsZSgpCmBgYApQbG90dGluZzoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRSwgRVZBTD1UUlVFfQpHRU1TMiAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeU5hbWUpICU+JSAgCiAgc3VtbWFyaXNlKG1lYW49bWVhbihSZXN1bHRWYWx1ZSxuYS5ybT1UUlVFKSwgbj1uKCkpJT4lCiAgYXJyYW5nZShkZXNjKG4pLG5hLnJtPVRSVUUpJT4lIAogIGdncGxvdChhZXMobWVhbixDb3VudHJ5TmFtZSAsZmlsbD1Db3VudHJ5TmFtZSkpKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQocm91bmQobWVhbiwgMyksIG5zbWFsbCA9IDIpKSwgdmp1c3QgPSAwLjUsaGp1c3Q9MCkreGxpbShjKDAsMC4yNSkpK3lsYWIoIkNvdW50cnkgTmFtZSIpK3hsYWIoIk1lYW4gVmFsdWUgb2YgUGIgKG1nL0tnKSIpCmBgYAoKRmlndXJlIDMuIEF2ZXJhZ2UgTGVhZCB2YWx1ZSAobWcva2cpIGluIHNhbXBsZXMgYWNjb3JkaW5nIHRvIHRoZSBjb3VudHJ5IG9mIG9yaWdpbi4KCkF2ZXJhZ2UgdXNpbmcgb25seSBxdWFudGlmaWVkIHNhbXBsZXMgKHdlIGFkb3B0IHZhbHVlcyBvbmx5IGFib3ZlIDAuMDAxbWcvS2cpOgoKYGBge3IgRVZBTD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRX0KR0VNUzIgJT4lIGdyb3VwX2J5KENvdW50cnlOYW1lKSAlPiUgIHN1bW1hcmlzZShtZWFuPW1lYW4oUmVzdWx0VmFsdWU+PTAuMDAxLG5hLnJtPVRSVUUpLCBuPW4oKSklPiVhcnJhbmdlKGRlc2MobiksbmEucm09VFJVRSkKYGBgCgpJbiB0aGUgZm9ybSBvZiBhIGJhciBncmFwaCBub3c6CgoKYGBge3IgRVZBTD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb2xsYXBzZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CkdFTVMyICU+JSAKICBncm91cF9ieShDb3VudHJ5TmFtZSkgJT4lICAKICBzdW1tYXJpc2UobWVhbj1tZWFuKFJlc3VsdFZhbHVlPj0wLjAwMSxuYS5ybT1UUlVFKSwgbj1uKCkpJT4lCiAgYXJyYW5nZShkZXNjKG4pLG5hLnJtPVRSVUUpJT4lIAogIGdncGxvdChhZXMobWVhbixDb3VudHJ5TmFtZSAsZmlsbD1Db3VudHJ5TmFtZSkpKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQocm91bmQobWVhbiwgMyksIG5zbWFsbCA9IDIpKSwgdmp1c3QgPSAwLjUsaGp1c3Q9MCkreGxpbShjKDAsMC43MCkpK3lsYWIoIkNvdW50cnkgTmFtZSIpK3hsYWIoIk1lYW4gVmFsdWUgb2YgUGIgKG1nL0tnKSIpCmBgYAoKRmlndXJlIDQuIEF2ZXJhZ2UgbGVhZCB2YWx1ZSAobWcva2cpIGluIHF1YW50aWZpZWQgc2FtcGxlcyAoYWJvdmUgMC4wMDEgbWcva2cpIGFjY29yZGluZyB0byB0aGUgY291bnRyeSBvZiBvcmlnaW4uCgpJbml0aWFsbHksIEV1cm9wZWFuIHZhbHVlcyBhcHBlYXIgdG8gYmUgaGlnaGVyLCBzcGVjaWZpY2FsbHkgNHggZ3JlYXRlciB0aGFuIEJyYXppbGlhbnMsIDEweCBncmVhdGVyIHRoYW4gQ2FuYWRpYW5zIGFuZCA1MHggZ3JlYXRlciB0aGFuIHRob3NlIG9mIHRoZSBVU0EuIEhvd2V2ZXIsIHdoZW4gb25seSB0aGUgcXVhbnRpZmllZCB2YWx1ZXMgYXJlIHRha2VuIGludG8gYWNjb3VudCwgdGhpcyBhdmVyYWdlIGRlY3JlYXNlcyBjb25zaWRlcmFibHkuIE9uZSB0aGVvcnkgd291bGQgYmUgdGhhdCBjb3VudHJpZXMgc3VjaCBhcyB0aGUgVVNBIGFuZCBDYW5hZGEgd291bGQgaGF2ZSBhIGxhcmdlciBudW1iZXIgb2YgdW5kZXRlY3RlZCBzYW1wbGVzIGFuZCB0aHVzIHRoZSBhdmVyYWdlIHdvdWxkIGJlIHJlZHVjZWQuCgpUbyBjaGVjayBpZiB0aGUgc291cmNlIG9mIHRoZSBkaWZmZXJlbmNlIG1heSBiZSB0aGUgdXNlIG9mIG5vbi1hY2NyZWRpdGVkIGxhYm9yYXRvcmllcywgd2Ugd2lsbCBleGNsdWRlIHRoaXMgZGF0YSBpbiB0aGUgIkdFTVMzIiBkYXRhZnJhbWUKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQpHRU1TMz1zdWJzZXQoR0VNUzIsR0VNUzIkQW5hbHl0aWNhbFFBTmFtZT09Ik9mZmljaWFsbHkgYWNjcmVkaXRlZCIpCmBgYAoKTm93IHJlcGVhdGluZyB0aGUgdGVzdCB0byBzZWUgaWYgdGhlcmUgd2FzIGEgY2hhbmdlIGluIHRoZSBhdmVyYWdlczoKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb2xsYXBzZT1UUlVFLCBFVkFMPVRSVUUsZmlnLmFsaWduPSdjZW50ZXInfQpHRU1TMyAlPiUgZ3JvdXBfYnkoQ291bnRyeU5hbWUpICU+JSAgc3VtbWFyaXNlKG1lYW49bWVhbihSZXN1bHRWYWx1ZT49MC4wMDEsbmEucm09VFJVRSksIG49bigpKSU+JWFycmFuZ2UoZGVzYyhuKSxuYS5ybT1UUlVFKSU+JSBnZ3Bsb3QoYWVzKG1lYW4sQ291bnRyeU5hbWUgLGZpbGw9Q291bnRyeU5hbWUpKStnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKStnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0KHJvdW5kKG1lYW4sIDMpLCBuc21hbGwgPSAyKSksIHZqdXN0ID0gMC41LGhqdXN0PTApK3hsaW0oYygwLDAuNjApKSt5bGFiKCJDb3VudHJ5IE5hbWUiKSt4bGFiKCJNZWFuIFZhbHVlIG9mIFBiIChtZy9LZykiKQpgYGAKCkZpZ3VyZSA0LiBBdmVyYWdlIExlYWQgdmFsdWUgKG1nL2tnKSBpbiBxdWFudGlmaWVkIHNhbXBsZXMgKGFib3ZlIDAuMDAxIG1nL2tnKSBhbmQgdGhhdCB1c2VkIGFjY3JlZGl0ZWQgbGFib3JhdG9yaWVzLCBzZXBhcmF0ZWQgYnkgdGhlIGNvdW50cnkgb2Ygb3JpZ2luLgoKWWVzLCBtb3N0IG9mIHRoZSBhdmVyYWdlIHZhbHVlcyBoYXZlIGRlY3JlYXNlZCBhbmQgaW4gcGFydGljdWxhciBDaGluYSwgd2hpY2ggc2hvd2VkIGFuIG92ZXItcXVhbnRpZmljYXRpb24gb2YgTGVhZCBpbiBub24tYWNjcmVkaXRlZCBsYWJvcmF0b3JpZXMuIFdoaWxlIEV1cm9wZWFuIGxhYm9yYXRvcmllcyByZXBvcnRlZCBtdWNoIGxvd2VyIGZpZ3VyZXMuCgpXaGVuIGV2YWx1YXRpbmcgZGF0YSBvbmx5IGZyb20gcXVhbnRpZmllZCBzYW1wbGVzIGFuZCBhY2NyZWRpdGVkIGxhYm9yYXRvcmllcywgRXVyb3BlIGFuZCBDYW5hZGEgaGF2ZSB2ZXJ5IHNpbWlsYXIgYXZlcmFnZXMuCgoKIyMgNC4zIERpc3RyaWJ1dGlvbiBhY2NvcmRpbmcgdG8gbWF0cml4IChGb29kQ2F0ZWdvcnkpCgpXZSB3aWxsIG5vdyBhbmFseXplIHRoZSBjb250ZW50cyBvZiBlYWNoIG1hdHJpeCAoVmFyaWFibGUgIkZvb2RDYXRlZ29yeSIgaW4gR0VNUykuCkJlbG93IGFyZSBhbGwgdGhlIHJlc3VsdHMgcXVhbnRpZmllZCAoY29uc2lkZXJpbmcgdGhlIDYgbWFpbiBjb250cmlidXRpbmcgY291bnRyaWVzKSBpbiBMZWFkIHBlciBtZy9rZyAoeC1heGlzKSBmb3IgZWFjaCB0eXBlIG9mIGZvb2QgKHktYXhpcyk6CgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb2xsYXBzZT1UUlVFfQpnZ3Bsb3QoR0VNUzIsIGFlcyh4PUZvb2RDYXRlZ29yeSx5PVJlc3VsdFZhbHVlKSkrCiAgICBnZW9tX2JveHBsb3Qoc2hvdy5sZWdlbmQgPSBGQUxTRSkrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAuMSwgbGluZXR5cGU9InNvbGlkIiwgY29sb3IgPSAicmVkIikrCiAgICBzY2FsZV95X2xvZzEwKCkrCiAgICBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcz11bmlxdWUoR0VNUyRGb29kQ2F0ZWdvcnkpLGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZ2pnaiIgLCAiICIpLCB3aWR0aCA9IDgwKSkrCiAgICBjb29yZF9mbGlwKCkrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCxzaXplID0xMCkpK3hsYWIoIkZvb2QgQ2F0ZWdvcnkiKSt5bGFiKCJQYiAobWcvS2cpIikKYGBgCgoKCkZpZ3VyZSA1OiBCb3hwbG90IG9mIExlYWQgY29udGVudCBpbiBtZy9LZyBpbiBmb29kIGFjY29yZGluZyB0byB0aGUgbWF0cml4LiBUaGUgYm94ZXMgcmVwcmVzZW50IDUwJSBvZiB0aGUgZGF0YSwgdGhlIGhvcml6b250YWwgYmFycyB0aGUgb3RoZXIgNTAlLiBEb3RzIGJleW9uZCB0aGUgYmFycyByZXByZXNlbnQgb3V0bGllcnMuCgpUaGUgcmVkIGxpbmUgcmVwcmVzZW50cyB0aGUgbGltaXQgb2YgMC4xIG1nIG1nL2tnIG9mIFBiIGFkb3B0ZWQgYXMgYSBzdGFuZGFyZCBpbiBzZXZlcmFsIGxlZ2lzbGF0aW9uIFsyXS4gT25seSA0IG1hdHJpY2VzIChmb29kKSBoYXZlIHRoZWlyIG1lZGlhbiBhdCBvciBhYm92ZSB0aGUgbGltaXQ6IEhlcmJzIGFuZCBzcGljZXMsIHN0aW11bGF0aW5nIGRyaW5rcywgYW5pbWFsIGZlZWQgYW5kIHByb2R1Y3RzIGZvciBzcGVjaWFsIHVzZS4gTk9URTogU2NhbGUgb2YgdGhlIFkgYXhpcyBpcyBpbiBsb2cxMCB0byBmYWNpbGl0YXRlIHRoZSB2aXN1YWxpemF0aW9uIG9mIHRoZSBkYXRhLgoKIVtGaWd1cmUgNjogU2FtZSBhcyBGaWd1cmUgNCBidXQgc2hvd2luZyB0aGUgcGFzc2FnZSBvZiB0aW1lIGJldHdlZW4gdGhlIHllYXJzIDE5OTUgYW5kIDIwMjBdKC9tbnQvREFET1MvRGF0YSBTY2llbmNlL2JveHBsb3QuZ2lmKXtmaWcuYWxpZ249J2NlbnRlcid9CgoKRHVyaW5nIHRoZSBldm9sdXRpb24gb2YgdGhlIHllYXJzLCBhIGNoYW5nZSBpbiB0aGUgYXZlcmFnZXMgaXMgbGlrZWx5IHRvIGJlIGNhdXNlZCBieSBzZWFzb25hbGl0eS4KCgpXZSB3aWxsIG5vdyB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBpbiBCb3hwbG90IGZvcm0gb2YgZWFjaCBmb29kIChjYXRlZ29yaWNhbCBkYXRhICJGb29kQ2F0ZWdvcnkiKSBzZXBhcmF0ZWQgaW4gdGhlIHNpeCBtYWluIGNvbnRyaWJ1dGluZyBjb3VudHJpZXM6CgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRSxmaWcuYWxpZ249J2NlbnRlcid9CmdncGxvdChHRU1TMiwgYWVzKFJlc3VsdFZhbHVlLEZvb2RDYXRlZ29yeSwgIGNvbG9yPUNvdW50cnlOYW1lKSkrCiAgICAgIGdlb21fYm94cGxvdChzaG93LmxlZ2VuZCA9IEZBTFNFKStzY2FsZV94X2xvZzEwKCkrdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpK2dlb21fdmxpbmUoeGludGVyY2VwdD0wLjEsIGxpbmV0eXBlPSJzb2xpZCIsIGNvbG9yID0gInJlZCIpK2ZhY2V0X3dyYXAofkNvdW50cnlOYW1lKSt5bGFiKCJGb29kIENhdGVnb3J5IikreGxhYigiUGIgKG1nL0tnKSIpCmBgYApGaWd1cmUgNzogQm94cGxvdCBvZiBMZWFkIGNvbnRlbnQgaW4gbWcvS2cgaW4gZm9vZCBhY2NvcmRpbmcgdG8gdGhlIG1hdHJpeCAoWSBheGlzKS4KClRoZSByZWQgbGluZSByZXByZXNlbnRzIHRoZSBsaW1pdCBvZiAwLjFtZy9rZywgcm91dGluZWx5IGFkb3B0ZWQgYXMgdGhlIGxpbWl0IGZvciBMZWFkIGluIGZvb2RzIGludGVybmF0aW9uYWxseSBbNF0uCgpUaGVyZSBpcyBhIG11Y2ggbGFyZ2VyIG51bWJlciBvZiBvdXRsaWVycyBpbiB0aGUgRXVyb3BlYW4gVW5pb24gdGhhbiBpbiB0aGUgc2Vjb25kIGxhcmdlc3QgY29udHJpYnV0b3IgKENhbmFkYSkuIFRoZXNlIG91dGxpZXJzIGluY2x1ZGUgdmVyeSBoaWdoIGNvbmNlbnRyYXRpb25zIChldmVuIDEwMG1nIC8gS2chKSBDYXVzaW5nIGEgbGV2ZXIgYW5kICJwdWxsaW5nIiB0aGUgdG90YWwgYXZlcmFnZXMgdXB3YXJkcy4KCiMgNC40LiBMT1EgYmVoYXZpb3VyCgpUaGUgTE9RIGlzIGEgaW1wb3J0YW50IHRvb2wgdG8gYXNzZXNzIHRoZSBhbmFseXNpcyAKCmBgYHtyIEVWQUw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNvbGxhcHNlPVRSVUV9CnN1bW1hcnkoR0VNUzIkTE9RKQpgYGAKU28sIGZyb20gYWxsIDMxOTMwMiBzYW1wbGVzLCAyNSUgIGhhdmUgYSBMT1EgYmVsb3cgMC4wMSBtZy9LZywgdGhlIG1lZGlhbiBpcyBvbiAwLjAyIG1nL2tnIGFuZCA3NSUgb2Ygc2FtcGxlcyBoYXZlIGEgTE9RIGJlbGxvdyAwLjA1bWcva2cuIFdlIGZvdW5kIHRoYXQgMTQlICg0NTY2MSkgb2Ygc2FtcGxlcyBkaWRuJ3QgcmVwb3J0IHRoZSBMT1EuCgpMZXQncyBncmFwaGljYWxseSBzaG93IHRoaXMgaW5mb3JtYXRpb24gdXNpbmcgdGhlIFJlc3VsVmFsdWUgb2YgUGIgKG1nL0tnKSBhcyB0aGUgWCB2YXJpYWJsZS4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRX0KZ2dwbG90KEdFTVMyLGFlcyh4PVJlc3VsdFZhbHVlLCBmaWxsPUxPUTw9MC4wMSkpKwogICAgZ2VvbV9ib3hwbG90KCkrc2NhbGVfeF9sb2cxMCgpK2dlb21fdmxpbmUoeGludGVyY2VwdD0wLjEsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MS4yMSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKQpgYGAKQm94cGxvdCBmcm9tIHF1YW50aWZpZWQgc2FtcGxlcyBhY2NvcmRpbmcgdG8gTE9RLCBhIHZhbHVlIG9mIDAuMDFtZy9LZyB3ZXJlIGNob3NlbiBzaW5jZSBpcyAxLzEwIG9mIHRoZSBsaW1pdCBvZiAwLjFtZy9LZy4KQXMgc2VlbiwgTG93ZXIgZGV0ZWN0aW9uIGxpbWl0cyBoYXZlIGEgbG93ZXIgbWVhbiB2YWx1ZSBvZiBxdWFudGlmaWVkIHNhbXBsZXMuIEhpZ2hlciBMT1EgdGhhbiAwLjFtZy9LZyBvZiBQYiBoYXZlIGEgbWVhbiBxdWFudGlmaWVkIHNhbXBsZSBoaWdoZXIuIFNhbXBsZXMgdGhhdCBkaWRuJ3QgcmVwb3J0IHRoZSBMT1EgaGF2ZSBhIG1lYW4gcXVhbnRpZmllZCB2YWx1ZSBiZXR3ZWVuIHRoZSB0d28uIFRoaXMgY2FuIGJlIHNlZW4gZXZlbiBtb3JlIGNsZWFybHkgb24gaGlzdG9ncmFtczoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRX0KZ2dwbG90KEdFTVMyLGFlcyh4PVJlc3VsdFZhbHVlLCBmaWxsPUxPUTw9MC4wMSkpKwogICAgZ2VvbV9oaXN0b2dyYW0oKStzY2FsZV94X2xvZzEwKCkrZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gInJlZCIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0xLjIxLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIpCmBgYAoKSXQncyBpbXBvcnRhbnQgdG8gbm90aWNlIHRoYXQgb25seSBxdWFudGlmaWVkIHNhbXBsZXMgYXBwZWFyIG9uIHRoZSBncmFwaCBhYm92ZSwgYW5kIDU2JSBvZiBzYW1wbGVzIGFyZSBub3QgcXVhbnRpZmllZC4KYGBge3IgRVZBTD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2U9VFJVRX0KcHJvcC50YWJsZSh0YWJsZShHRU1TMiRSZXN1bHRWYWx1ZT09MC4wMDAwMCkpCmBgYAoKQnV0LCBhcmUgdGhlIHF1YW50aWZpZWQgcmVzdWx0cyB3aXRoIHVucmVwb3J0ZWQgTE9RIGVxdWl2YWxlbnQgdG8gdGhlIG9uZXMgd2l0aCByZXBvcnRlZCByZXN1bHRzPwoKRmlyc3QsIHRoZSBjb25maXJtYXRpb24gaWYgdGhlIGRhdGEgaXMgbm9ybWFsOgoKYGBge3IsIGNvbGxhcHNlPVRSVUUsIGNsYXNzLnNvdXJjZT0iYmctc3VjY2VzcyJ9CkdFTVMycmVzPC1zYW1wbGVfbihHRU1TMiw1MDAwKQoKc2hhcGlyby50ZXN0KHN1YnNldChHRU1TMnJlcyRSZXN1bHRWYWx1ZSxHRU1TMnJlcyRMT1E+PTAuMDEpKQpzaGFwaXJvLnRlc3Qoc3Vic2V0KEdFTVMycmVzJFJlc3VsdFZhbHVlLEdFTVMycmVzJExPUTw9MC4wMSkpCnNoYXBpcm8udGVzdChzdWJzZXQoR0VNUzJyZXMkUmVzdWx0VmFsdWUsaXMubmEoR0VNUzJyZXMkTE9RKSkpCkdFTVMycmVzPC1HRU1TMgoKYGBgCkFsbCB0aHJlZSBzZXRzIG9mIHRoZSBSZXN1bHRWYWx1ZSB2YXJpYWJsZSB3aXRoIExPUSBsb3dlciB0aGFuIDAuMDFtZy9LZywgaGlnaGVyIHRoYW4gMC4wMW1nL0tnIGFuZCBub24gcmVwb3J0ZWQoTkEpIGFyZSBub3JtYWwuIAoKTm93LCBncm91cGluZyBlYWNoIGRhdGEgYWNjb3JkaW5nIHRvIHRoZSBMT1E6CgpgYGB7ciwgLCBjb2xsYXBzZT1UUlVFLCBjbGFzcy5zb3VyY2U9ImJnLXN1Y2Nlc3MifQpHRU1TMnJlczEgPC0gc3Vic2V0KEdFTVMycmVzLEdFTVMycmVzJExPUT49MC4wMSkKR0VNUzJyZXMxJGdyb3VwIDwtICJIaWdoZXIgdGhhbiAwLjAxIgpHRU1TMnJlczIgPC0gc3Vic2V0KEdFTVMycmVzLEdFTVMycmVzJExPUTw9MC4wMSkKR0VNUzJyZXMyJGdyb3VwIDwtICJMb3dlciB0aGFuIDAuMDEiCkdFTVMycmVzMyA8LSBzdWJzZXQoR0VNUzJyZXMsaXMubmEoR0VNUzJyZXMkTE9RKSkKR0VNUzJyZXMzJGdyb3VwIDwtICJOQSIKR0VNUzJyZXNfMTwtcmJpbmQoR0VNUzJyZXMxLEdFTVMycmVzMixHRU1TMnJlczMpCmBgYAoKQW5kIHBlcmZvcm1pbmcgQU5PVkEuCgpgYGB7ciwgLCBjb2xsYXBzZT1UUlVFfQphbm92YV9HRU1TMnJlc18xIDwtIGFvdihSZXN1bHRWYWx1ZSB+IGdyb3VwLCBHRU1TMnJlc18xKQpzdW1tYXJ5KGFub3ZhX0dFTVMycmVzXzEpCmBgYAoKYGBge3IsICwgY29sbGFwc2U9VFJVRX0KUG9zdEhvY1Rlc3QoYW5vdmFfR0VNUzJyZXNfMSwgbWV0aG9kID0gImhzZCIpCmBgYApXZSBjYW4gbm90ZSB0aGF0IHRoZXJlJ3MgYSBkaWZmZXJlbnQgbWVhbiBiZXR3ZWVuIHRoZSBxdWFudGlmaWVkIHNhbXBsZXMgd2l0aCBiZWxvdyBhbmQgYWJvdmUgdGhlIExPUSBvZiAwLjAxbWcvS2cgb2YgUGIgYnV0IE5PVCBiZXR3ZWVuIHRoZXNlIGdyb3VwcyBhbmQgdGhlIHVucmVwb3J0ZWQgcmVzdWx0cy4KCk5vdywgZXZhbHVhdGluZyBhY2NvcmRpbmcgdG8geWVhciBvZiBhbmFseXNpczoKCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJ30KZ2dwbG90KEdFTVMycmVzXzEsYWVzKHg9U2FtcGxpbmdQZXJpb2RFbmQseT1sZW5ndGgoUm93TnVtKSwgZmlsbD1ncm91cCkpKwogIGdlb21fYmFyKHBvc2l0aW9uPSJzdGFjayIsIHN0YXQ9ImlkZW50aXR5IikrIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLCkrZ2d0aXRsZSgiTnVtYmVyIG9mIHNhbXBsZXMgYW5hbHlzZWQgYWNjb3JkaW5nIHRvIExPUSBncm91cCIpCgpHRU1TMnJlc18xICU+JSBncm91cF9ieShTYW1wbGluZ1BlcmlvZEVuZCkgJT4lIGNvdW50KGdyb3VwLHNvcnQ9VFJVRSkgJT4lIGdncGxvdCgpK2dlb21fY29sKGFlcyhTYW1wbGluZ1BlcmlvZEVuZCxuLCBmaWxsPWdyb3VwKSkrIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLCkrZ2d0aXRsZSgiTnVtYmVyIG9mIHNhbXBsZXMgYW5hbHlzZWQgYWNjb3JkaW5nIHRvIExPUSBncm91cCIpCgpgYGAKCkFmdGVyIHBsb3R0aW5nIHRoZSBMT1EgZ3JvdXBzIGFjY29yZGluZyB0byB0aGUgWWVhciB3ZSBub3RlIHRoYXQgdGhlIHVucmVwb3J0ZWQgZGF0YSBwcm9iYWJseSBjb21lIGZyb20geWVhcnMgd2UgdGhpcyBpbmZvcm1hdGlvbiB3YXNuJ3QgcmVxdWlyZWQuIAoKV2UgZGVjaWRlZCB0byBrZWVwIHRoZSBxdWFudGlmaWVkIGRhdGEgZXZlbiBpZiB0aGUgTE9RIHdhc24ndCByZXBvcnRlZC4KCiMgNS4gQSBkZWVwZXIgc3R1ZHkgb2YgTGVhZCBsZXZlbHMgaW4gdGhlIEV1cm9wZWFuIFVuaW9uIHJlZ2lvbgoKClNpbmNlIHRoZSBXSE8gLyBFdXJvcGUgcmVnaW9uIGhhcyBhIGxhcmdlIGFtb3VudCBvZiBkYXRhIGFuZCBtYW55IG91dGxpZXJzL3ZhbHVlcyBhYm92ZSB3aGF0IGlzIGFsbG93ZWQsIGl0IHdhcyBjaG9zZW4gdG8gZXZhbHVhdGUgdGhlIG1hdHJpY2VzIGFuZCB0aGVpciBkaXN0cmlidXRpb24gb3ZlciB0aGUgeWVhcnMgaW4gbW9yZSBkZXRhaWwuCgpXZSB3aWxsIG5vdyBleGFtaW5lIHRoZSBxdWFudGlmaWVkIHNhbXBsZXMgKGFib3ZlIDAuMDAxbWcva2cpIGluIHRoZSAiV0hPIEV1cm9wZWFuIFJlZ2lvbiIgaW4gdGhlIGZvcm0gb2YgYSBoZWF0cGxvdDoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiBzdWJzZXQoR0VNUzIsIEdFTVMyJENvdW50cnlOYW1lPT0iV0hPIEV1cm9wZWFuIFJlZ2lvbiIgJiBHRU1TMiRSZXN1bHRWYWx1ZSkgJT4lIAogIGdncGxvdChhZXMoU2FtcGxpbmdQZXJpb2RFbmRUZXh0LHk9cmVvcmRlcihGb29kQ2F0ZWdvcnksIFJlc3VsdFZhbHVlLGZ1bmN0aW9uKHgpK21lYW4oeCkpLCAgZmlsbCA9IFJlc3VsdFZhbHVlKSkrICAgCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobGltaXRzPWMoMC4wMDEsIDIpLGxvdyA9ICJibHVlIiwgaGlnaCA9InJlZCIsIHRyYW5zPSJsb2cxMCIpKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwpK3lsYWIoIkZvb2RDYXRlZ29yeSIpKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJnamdqIiAsICIgIiksIHdpZHRoID0gNjApKSsKICB4bGFiKCJZZWFyIikreWxhYigiRm9vZCBDYXRlZ29yeSIpCmBgYAoKRmlndXJlIDguIEhlYXRwbG90IGZyb20gdGhlIDE1LXllYXIgcGVyaW9kIG9mIGFuYWx5c2lzIG9mIExlYWQgY29udGVudCBpbiBmb29kIGF0IFdITy9FdXJvcGUuIFJlZCByZXByZXNlbnRzIHRoZSBjb250ZW50IG9mIDFtZyAvIGtnLCBibHVlIDAuMDAxbWcva2cuIEl0IGlzIG5vdGVkIHRoYXQgb3ZlciB0aGUgeWVhcnMgc2FtcGxlcyBvZiBIZXJicyBhbmQgc3BpY2VzLCBzdGltdWxhdGluZyBkcmlua3MgYW5kIGFuaW1hbCBmZWVkLCBjb250aW51b3VzbHkgc2hvdyBoaWdoZXIgcmVzdWx0cyB0aGFuIG90aGVyIG1hdHJpY2VzLiBBcyBhbHJlYWR5IHNlZW4gaW4gdGhlIGJveHBsb3QgaW4gZmlndXJlIDcuCgpUaGUgdXBwZXIgcGFydCBvZiB0aGUgSGVhdHBsb3QgY29udGFpbnMgdGhlIGZvb2RzIHdpdGggdGhlIGhpZ2hlc3QgYXZlcmFnZSB2YWx1ZXMgb3ZlciB0aGUgeWVhcnMgYW5kIHRoZSBsb3dlc3QsIHdpdGggdGhlIHNtYWxsZXN0LgoKV2UgY2FuIG5vdyBkZWZpbmUgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgaGlnaGVzdCAxJSwgMiUgYW5kIDEwJSAoaW4gUGIgY29uY2VudHJhdGlvbikgaW4gdGhlIHRvdGFsIGRhdGFiYXNlOgoKYGBge3IsY29sbGFwc2U9VFJVRX0KcXVhbnRpbGUoc3Vic2V0KEdFTVMyJFJlc3VsdFZhbHVlLEdFTVMyJENvdW50cnlOYW1lPT0iV0hPIEV1cm9wZWFuIFJlZ2lvbiIpLHByb2JzPSgwLjk1KSxuYS5ybT1UUlVFKQoKYGBgCgogICAKOTUlIG9mIHRoZSBxdWFudGlmaWVkIHNhbXBsZXMgYXJlIGJlbG93IDAuMm1nL0tnLgoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CnF1YW50aWxlKHN1YnNldChHRU1TMiRSZXN1bHRWYWx1ZSxHRU1TMiRDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iKSxwcm9icz0oMC45OCksbmEucm09VFJVRSkKYGBgCgo5OCUgb2YgdGhlIHF1YW50aWZpZWQgc2FtcGxlcyBhcmUgYmVsb3cgMC41OG1nL0tnLgoKCmBgYHtyLGNvbGxhcHNlPVRSVUUgfQpxdWFudGlsZShzdWJzZXQoR0VNUzIkUmVzdWx0VmFsdWUsR0VNUzIkQ291bnRyeU5hbWU9PSJXSE8gRXVyb3BlYW4gUmVnaW9uIikscHJvYnM9KDAuOTkpLG5hLnJtPVRSVUUpCmBgYAoKOTklIG9mIHRoZSBxdWFudGlmaWVkIHNhbXBsZXMgYXJlIGJlbG93IDEuMjFtZy9LZy4KCgpPYnNlcnZpbmcgdGhlc2UgbGltaXRzIGluIHRoZSBmcmVxdWVuY3kgaGlzdG9ncmFtOgoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInfQpzdWJzZXQoR0VNUzMsIEdFTVMzJENvdW50cnlOYW1lPT0iV0hPIEV1cm9wZWFuIFJlZ2lvbiIgJiBHRU1TMyRSZXN1bHRWYWx1ZSkgJT4lCiAgZ2dwbG90KGFlcyhSZXN1bHRWYWx1ZSwgZmlsbD1Gb29kQ2F0ZWdvcnkpKSsKICBnZW9tX2hpc3RvZ3JhbSggc2hvdy5sZWdlbmQ9IEZBTFNFKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4xLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAicmVkIikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTEuMjEsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikrCiAgc2NhbGVfeF9sb2cxMCgpK3hsYWIoIlBiIChtZy9rZykiKSt5bGFiKCJOdW1iZXIgb2Ygc2FtcGxlcyIpCmBgYAoKRmlndXJlIDk6IEZyZXF1ZW5jeSBoaXN0b2dyYW0gb2YgZGF0YSByZWZlcnJpbmcgdG8gdGhlIEV1cm9wZWFuIFVuaW9uLCB4LWF4aXMgb24gdGhlIGxvZ2FyaXRobWljIHNjYWxlLiBUaGUgcmVkIGxpbmUgcmVwcmVzZW50cyB0aGUgdmFsdWUgb2YgMC4xbWcva2cgYSB2ZXJ5IGNvbW1vbiBsaW1pdCBhbGxvd2VkIGluIHRoZSBsZWdpc2xhdGlvbiBhbmQgYmx1ZSBsaW5lLCB0aGUgcmFuZ2UgdGhhdCBjb3ZlcnMgOTklIG9mIHRoZSBxdWFudGlmaWVkIHNhbXBsZXMgKDEuMjFtZy9LZykuCgpDaGVja2luZyBmb3Igbm9ybWFsaXR5LgoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CnNoYXBpcm8udGVzdChzYW1wbGUoc3Vic2V0KEdFTVMzJFJlc3VsdFZhbHVlLCBHRU1TMyRDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iKSw1MDAwKSkKdC50ZXN0KHN1YnNldChHRU1TMyRSZXN1bHRWYWx1ZSwgR0VNUzMkQ291bnRyeU5hbWU9PSJXSE8gRXVyb3BlYW4gUmVnaW9uIikpCmBgYAoKQWx0aG91Z2ggZmlndXJlIDkgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIHdpdGggdGhlIGxvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uLCBhIG5vcm1hbGl0eSB0ZXN0IGFuZCBhIHQgdGVzdCBmb3IgYSBzYW1wbGUgc2hvdyB0aGF0IHRoZSBzZXQgaXMgbm9ybWFsIGFuZCB0aGF0IGFsbCBFdXJvcGVhbiBzYW1wbGVzIGhhdmUgYW4gYXZlcmFnZSBvZiAwLjE2bWcgLyBrZyDCsSAwLjA4IG1nIC8ga2csIDk1ICUgY29uZmlkZW5jZSBhbmQgcC12YWx1ZSA9IDIuMTFlLTA1LgoKCgojIyA1LjEgRHluYW1pYyByZWxhdGlvbnNoaXAgYmV0d2VlbiBhbmFseXNpcyBxdWFsaXR5IGFuZCBzYW1wbGUgb3JpZ2luCgpXZSBub3cga25vdyB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIHNhbXBsZXMgKDYwJSkgYXJlIGJlbG93IHRoZSBsaW1pdCBvZiBxdWFudGlmaWNhdGlvbiBhbmQgZm9yIHNpbXBsaWNpdHkgaXMgcmVwb3J0ZWQgYXMgMC4wMDAuIEEgbWluaW11bSBudW1iZXIgaXMgcXVhbnRpZmllZCBhbmQgaXMgYmVsb3cgdGhlIGxpbWl0IG9mIHRoZSBsZWdpc2xhdGlvbiAoMC4xIG1nL2tnKS4gVGhlIHJlc3QgKDQwJSkgaXMgcXVhbnRpZmllZCBhbmQgOTklIG9mIHRoZSB0aW1lIGl0IGlzIGJlbG93IDEuMjJtZyAvIEtnLgoKV2Ugd2lsbCBub3cgdHJ5IHRvIHZpc3VhbGl6ZSBob3cgdGhpcyBpbmZvcm1hdGlvbiByZWxhdGVzIHRvIGVhY2ggb3RoZXIgdXNpbmcgdGhlIFNhbmtleSBncmFwaC4gVGhpcyBncmFwaCBpcyBzaW1pbGFyIHRvIHRoYXQgb2Ygc3RhY2tlZCBiYXJzLCBob3dldmVyLCB3aXRoIGxpbmVzIHRoYXQgc2hvdyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcy4KCkxldCdzIGxpc3QgdGhlIE9yaWdpbiBvZiB0aGUgc2FtcGxlcyAoRG9tZXN0aWMgb3IgaW1wb3J0ZWQpLCB0aGUgcXVhbGl0eSBjb250cm9sIHRvb2wgYW5kIHRoZSBxdWFudGlmaWVkIGxlYWQgY29udGVudDoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkdFTVMycmVzXzEgJT4lICBncm91cF9ieShncm91cCxBbmFseXRpY2FsUUFOYW1lLEZvb2RPcmlnaW5OYW1lLCBSZXN1bHRWYWx1ZT1SZXN1bHRWYWx1ZT49MC4xMCkgJT4lICBzdW1tYXJpc2UoU2FtcGxlcz1sZW5ndGgoZ3JvdXApKSAtPiBTYW5rZXk0CmdncGxvdChhcy5kYXRhLmZyYW1lKFNhbmtleTQpLCBhZXMoeSA9IFNhbXBsZXMsIGF4aXMxID0gZ3JvdXAsIGF4aXMyID0gQW5hbHl0aWNhbFFBTmFtZSwgYXhpczMgPSBGb29kT3JpZ2luTmFtZSwgYXhpczQ9UmVzdWx0VmFsdWUpKSArCiAgICBnZW9tX2FsbHV2aXVtKGFlcyhmaWxsID0gZ3JvdXA9PSJOQSIpLCB3aWR0aCA9IDEvNikgKwogIGdlb21fc3RyYXR1bSh3aWR0aCA9IDEvNSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImdyZXk1MCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo0LCBsYWJlbHMgPSBjKCJMT1EiLCAiUXVhbGl0eSBDb250cm9sIiwgIlNhbXBsZSBzb3VyY2UiLCAiQWJvdmUgMC4xMFwgbWcvS2ciKSkgKwogIGdlb21fbGFiZWwoc3RhdCA9ICJzdHJhdHVtIiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChzdHJhdHVtKSkpICsgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikreWxhYigiTnVtYmVyIG9mIHNhbXBsZXMiKQpgYGAKCkZpZ3VyZSAxMDogU2Fua2V5IGNoYXJ0IHNob3dpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvcmlnaW4sIHRoZSBxdWFsaXR5IGNvbnRyb2wgdGVjaG5pcXVlIHVzZWQgYW5kIHRoZSByZXN1bHQgaW4gbWcgLyBLZy4KSXQgY2FuIGJlIG5vdGVkIHRoYXQgZWl0aGVyIGJ5IG9yaWdpbiwgcXVhbGl0eSBjb250cm9sIG9yIHJlc3VsdCB0aGVyZSBpcyBubyB0cmVuZC4KSXQgaXMgYWxzbyBub3RlZCB0aGF0IGJldHdlZW4gZG9tZXN0aWMgYW5kIGltcG9ydGVkIHNhbXBsZXMsIHRoZXkgcHJlc2VudCB0aGUgc2FtZSByYXRpbyBiZXR3ZWVuIHF1YW50aWZpZWQgYWJvdmUgMC4xIG1nIC8ga2cgYW5kIGJlbG93IHRoZSBsaW1pdCBvZiBxdWFudGlmaWNhdGlvbi4KCgojIyA1LjIgSW4gc2VhcmNoIG9mIG1hdHJpY2VzIHdpdGggaGlnaGVyIGxldmVscwoKV2Ugd2lsbCBpbnZlc3RpZ2F0ZSBvbmx5IHNhbXBsZXMgd2l0aCBoaWdoZXIgbGV2ZWxzIG9mIExlYWQ6IEhlcmJzIGFuZCBzcGljZXMsIGFuaW1hbCBmZWVkLCBzdGltdWxhdGluZyBkcmlua3MgYW5kIFByb2R1Y3RzIGZvciBzcGVjaWFsIG51dHJpdGlvbmFsIHVzZS4gRmlyc3QsIGhvdyB0aGV5IHJlbGF0ZSB0byB0aGUgdGltZSBhbmQgYW1vdW50IG9mIHNhbXBsZXMgdGVzdGVkIGFuZCBxdWFudGlmaWVkOgoKYGBge3J9CkdFTVM0PXN1YnNldChHRU1TMiwgR0VNUzIkQ291bnRyeU5hbWU9PSJXSE8gRXVyb3BlYW4gUmVnaW9uIiAmIEdFTVMyJEZvb2RDYXRlZ29yeT09IkFOSU1BTCBGRUVEIiB8IEdFTVMyJEZvb2RDYXRlZ29yeT09IlN0aW11bGFudCBiZXZlcmFnZXMsIGRyaWVkIGFuZCBkaWx1dGVkIGV4Y2x1ZGluZyBjb2NvYSBwcm9kdWN0cyIgfCBHRU1TMiRGb29kQ2F0ZWdvcnk9PSJIZXJicywgc3BpY2VzIGFuZCBjb25kaW1lbnRzIiAgfCBHRU1TMiRGb29kQ2F0ZWdvcnk9PSJQcm9kdWN0cyBmb3Igc3BlY2lhbCBudXRyaXRpb25hbCB1c2UiKQpgYGAKCgpgYGB7cn0KR0VNUzU9c3Vic2V0KEdFTVMyLCBHRU1TMiRDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iKQpgYGAKCgpgYGB7cixmaWcuYWxpZ249J2NlbnRlcid9CgpHRU1TNCAlPiUgCiAgZ3JvdXBfYnkoU2FtcGxpbmdQZXJpb2RFbmQsRm9vZENhdGVnb3J5KSAlPiUKICBjb3VudChtb3JlX3RoYW5fMXBwYj1SZXN1bHRWYWx1ZTw9MC4wMDEsc29ydD1UUlVFKSAlPiUKICBnZ3Bsb3QoKStnZW9tX2NvbChhZXMoU2FtcGxpbmdQZXJpb2RFbmQsbixmaWxsPW1vcmVfdGhhbl8xcHBiKSkrIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLCkrCiAgZmFjZXRfd3JhcCh+c3RyX3dyYXAoRm9vZENhdGVnb3J5LHdpZHRoID0gNDApKStnZ3RpdGxlKCJOdW1iZXIgb2Ygc2FtcGxlcyBhbmFseXNlZCBhY2NvcmRpbmcgdG8gbWF0cml4IikKCgpnZ3Bsb3QoR0VNUzQsYWVzKHg9U2FtcGxpbmdQZXJpb2RFbmQseT1sZW5ndGgoUm93TnVtKSwgZmlsbD1SZXN1bHRWYWx1ZTw9MC4wMDEpKSsKICBnZW9tX2Jhcihwb3NpdGlvbj0ic3RhY2siLCBzdGF0PSJpZGVudGl0eSIpKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwpKwogIGZhY2V0X3dyYXAofnN0cl93cmFwKEZvb2RDYXRlZ29yeSx3aWR0aCA9IDQwKSkrZ2d0aXRsZSgiTnVtYmVyIG9mIHNhbXBsZXMgYW5hbHlzZWQgYWNjb3JkaW5nIHRvIG1hdHJpeCIpCmBgYAoKCkZpZ3VyZSAxMS4gQmFyIGdyYXBocyBncm91cGluZyB0aGUgNCBtYWluIGNsYXNzZXMgd2l0aCB0aGUgaGlnaGVzdCBhdmVyYWdlIHZhbHVlcyBvZiBMZWFkIGluIHRoZSBwZXJpb2Qgb2YgMjUgeWVhcnMuIFRoZSBudW1iZXIgb2YgcXVhbnRpZmllZCBzYW1wbGVzIGlzIHNob3cgYnkgdGhlIGNvbG9yIGRpZmZlcmVuY2UgKFF1YW50aWZpZWQgUmVkLCBCbHVlIGJlbG93IHRoZSBMT1EpLgoKYGBge3IsZmlnLmFsaWduPSdjZW50ZXInfQoKR0VNUzQgJT4lIAogIGdyb3VwX2J5KFNhbXBsaW5nUGVyaW9kRW5kLEZvb2RDYXRlZ29yeSkgJT4lCiAgY291bnQobW9yZV90aGFuXzFwcGI9UmVzdWx0VmFsdWU8PTAuMDAxLHNvcnQ9VFJVRSkgJT4lCiAgZ2dwbG90KCkrZ2VvbV9jb2woYWVzKFNhbXBsaW5nUGVyaW9kRW5kLG4sZmlsbD1tb3JlX3RoYW5fMXBwYikscG9zaXRpb249ImZpbGwiKSsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksKSsKICBmYWNldF93cmFwKH5zdHJfd3JhcChGb29kQ2F0ZWdvcnksd2lkdGggPSA0MCkpK2dndGl0bGUoIk51bWJlciBvZiBzYW1wbGVzIGFuYWx5c2VkIGFjY29yZGluZyB0byBtYXRyaXgiKQoKZ2dwbG90KEdFTVM0LGFlcyh4PVNhbXBsaW5nUGVyaW9kRW5kLHk9KFJvd051bSksIGZpbGw9UmVzdWx0VmFsdWU8PTAuMDAxKSkrCiAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLCBzdGF0PSJpZGVudGl0eSIpKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwpKwogIGZhY2V0X3dyYXAofnN0cl93cmFwKEZvb2RDYXRlZ29yeSx3aWR0aCA9IDQwKSkreWxhYigiUmVzdWx0VmFsdWUiKStnZ3RpdGxlKCJSYXRpbyBvZiBxdWFudGlmaWVkIHNhbXBsZXMgYWNjb3JkaW5nIHRvIG1hdHJpeCIpCgpgYGAKCkZpZ3VyZSAxMi4gQmFyIGdyYXBocyBncm91cGluZyB0aGUgNCBtYWluIGNsYXNzZXMgd2l0aCB0aGUgaGlnaGVzdCBhdmVyYWdlIHZhbHVlcyBvZiBMZWFkIGluIHRoZSBwZXJpb2Qgb2YgMjUgeWVhcnMuIFRoZSB2YWx1ZXMgd2VyZSBzdGFuZGFyZGl6ZWQgYW5kIGVhY2ggY29sdW1uIGNvcnJlc3BvbmRzIHRvIDEwMCUgb2YgdGhlIHZhbHVlcyBhbmQgdGhlIHJhdGlvIGJldHdlZW4gcXVhbnRpZmllZCBhbmQgdW5xdWFudGlmaWVkIHNhbXBsZXMgaXMgZGVtb25zdHJhdGVkIGJ5IHRoZSBjb2xvciBkaWZmZXJlbmNlIChRdWFudGlmaWVkIFJlZCwgQmx1ZSBiZWxvdyB0aGUgTE9RKS4KCkl0IGlzIG5vdGVkIHRoYXQgc2FtcGxlcyBvZiBIZXJicyBhbmQgc3BpY2VzIGFyZSByb3V0aW5lbHkgcXVhbnRpZmllZCBmb3IgYSBwZXJpb2Qgb2YgbW9yZSB0aGFuIDEwIHllYXJzIGFuZCBldmVuIHdpdGggdmFyaWF0aW9uIGluIHRoZSBudW1iZXIgb2Ygc2FtcGxlcywgdGhlIHF1YW50aWZpZWQgcmF0aW8gaXMgdmVyeSBzaW1pbGFyIGFsb25nIHRoZSB5ZWFycy4gCgoKV2Uga25vdyBzbyBmYXIgdGhhdCB0aGVzZSBmb3VyIGZvb2QgZ3JvdXBzIGFyZSBvZnRlbiBxdWFudGlmaWVkLiBIb3dldmVyLCBpdCBpcyBub3QgY2xlYXIgd2hldGhlciBzb21lIHNhbXBsZXMgaGF2ZSB2ZXJ5IGhpZ2ggbGV2ZWxzIGFuZCAiZHJhZyIgdGhlIGF2ZXJhZ2UgdG8gaGlnaGVyIG1lYW4gdmFsdWVzLgoKRm9yIHRoaXMgd2Ugd2lsbCBldmFsdWF0ZSBhIGJveHBsb3Qgd2l0aCA5OSUgb2YgdGhlIGRhdGEgKHRvIGF2b2lkIG91dGxpZXJzKSBhbmQgc2VlIHdoZXJlIHRoZSBxdWFydGlsZXMgYXJlIGxvY2F0ZWQ6CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb2xsYXBzZT1UUlVFLGZpZy5hbGlnbj0nY2VudGVyJ30KZ2dwbG90KEdFTVM1LCBhZXMoeD1Gb29kQ2F0ZWdvcnkseT1SZXN1bHRWYWx1ZSkpKwogICAgZ2VvbV9ib3hwbG90KHNob3cubGVnZW5kID0gRkFMU0UsYWxwaGE9MS8xMCkrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAuMSwgbGluZXR5cGU9InNvbGlkIiwgY29sb3IgPSAicmVkIikrCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3M9dW5pcXVlKEdFTVMkRm9vZENhdGVnb3J5KSxsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImdqZ2oiICwgIiAiKSwgd2lkdGggPSA4MCkpK3lsaW0oYygwLDEuMjIpKSsKICAgIGNvb3JkX2ZsaXAoKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHNpemUgPTEwKSkreGxhYigiRm9vZCBDYXRlZ29yeSIpK3lsYWIoIlBiIChtZy9LZykiKSthbm5vdGF0ZSgicmVjdCIseW1pbiA9IDAseW1heD0wLjg4LCB4bWluID0gIlN0YXJjaHkgcm9vdHMgYW5kIHR1YmVycyIseG1heD0iU3VnYXIgYW5kIGNvbmZlY3Rpb25hcnkgKGluY2x1ZGluZyBjb2NvYSBwcm9kdWN0cykiLCBhbHBoYT0wLjMsIGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIpCgpgYGAKCkZpZ3VyZSAxMy4gQm94cGxvdCB3aXRoIGxpbmVhciBheGlzIGNvdmVyaW5nIDk5JSBvZiB0aGUgc2FtcGxlcyBpbiB0aGUgcmFuZ2Ugb2YgMC4wMDEgdG8gMS4yMm1nL2tnIG9mIFBiLgoKV2UgY2FuIHNlZSB0aGF0IHRoZSB3b3JzdCBjYXNlIGlzIHRoYXQgZnJvbSBzdGltdWxhdGluZyBkcmlua3MgdGhhdCBpbmNsdWRlIGNvZmZlZXMgYW5kIHRlYXMuIFRoZSBtZWRpYW4gaXMgd2VsbCB3aXRoaW4gdGhlIGxpbWl0IG9mIDAuMW1nL2tnIGFuZCB0aGUgdGhpcmQgcXVhcnRpbGUgKDUwJSB0byA3NSUpIHN0cmV0Y2hlcyB0byBtb3JlIHRoYW4gMC4yNW1nL2tnLgoKVGhyb3VnaCB0aGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24sIHdlIHNlZSB0aGF0IHRoZSBwcm9ibGVtIGlzIG5vdCBvdXRsaWVycyBidXQgYSBsYXJnZSBhbW91bnQgb2Ygc2FtcGxlcyBpbiBkaWZmZXJlbnQgY29uY2VudHJhdGlvbnMgdGhhdCBpcyBxdWFudGlmaWVkIGluIHRoaXMgY2xhc3M6CgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KEdFTVM1LCBhZXMoeD1SZXN1bHRWYWx1ZSwgY29sb3I9Rm9vZENhdGVnb3J5LCBsYWJlbD1Gb29kQ2F0ZWdvcnkpLHNob3cubGVnZW5kID0gRkFMU0UpKwogICAgc3RhdF9lY2RmKGdlb20gPSAibGluZSIsIGFlcyhSZXN1bHRWYWx1ZSksc2hvdy5sZWdlbmQgPSBGQUxTRSkgK3hsYWIoIlBiIChtZy9LZykiKStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4xLCBsaW5ldHlwZT0ic29saWQiLCBjb2xvciA9ICJyZWQiKStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MS4yMiwgbGluZXR5cGU9InNvbGlkIiwgY29sb3IgPSAiYmx1ZSIpK3hsaW0oYygwLDEuMjIpKSthbm5vdGF0ZSgidGV4dCIsIHggPSAwLjUyLCB5ID0gMC42LCAKbGFiZWwgPSBjKCJTdGltdWxhbnQgYmV2ZXJhZ2VzIikgLCBjb2xvcj0icHVycGxlIixzaXplPTUgKSthbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjMwLCB4ZW5kID0gMC4yNSwgeSA9IDAuNiwgeWVuZCA9IDAuNjUsIGNvbG91ciA9ICJwdXJwbGUiLCBzaXplPTEsIGFscGhhPTAuOSwgYXJyb3c9YXJyb3coKSkreWxhYigiQ3VtdWxhdGl2ZSBkaXN0cmlidXRpb24iKQoKYGBgCgpGaWd1cmUgMTQuIEdyYXBoIHdpdGggY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gb2YgTGVhZCBjb250ZW50IGluIGZvb2Qgc2FtcGxlcyBpbiB0aGUgRXVyb3BlYW4gVW5pb24uIFJlZCBsaW5lIHJlcHJlc2VudHMgdGhlIGNvbmNlbnRyYXRpb24gb2YgMC4xbWcva2cgb2YgUGIgYW5kIGJsdWUgbGluZSAxLjIybWcva2cgd2hpY2ggaXMgdGhlIGNvbnRlbnQgdGhhdCBjb21wcmlzZXMgOTklIG9mIHRoZSBzYW1wbGVzLgoKV2UgImludmVzdGlnYXRlZCIgd2hldGhlciB3aXRoaW4gdGhlIGNsYXNzIG9mIHN0aW11bGFudCBkcmlua3MsIHdoaWNoIHdvdWxkIGJlIHRoZSB3b3JzdCBtYXRyaXguCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KHN1YnNldChHRU1TNSxHRU1TNSRGb29kQ2F0ZWdvcnk9PSJTdGltdWxhbnQgYmV2ZXJhZ2VzLCBkcmllZCBhbmQgZGlsdXRlZCBleGNsdWRpbmcgY29jb2EgcHJvZHVjdHMiKSAsIGFlcyh4PVJlc3VsdFZhbHVlLCBjb2xvcj1Gb29kTmFtZSwgbGFiZWw9Rm9vZE5hbWUpKSsKICAgIHN0YXRfZWNkZihnZW9tID0gImxpbmUiLCBhZXMoUmVzdWx0VmFsdWUpKSAreGxhYigiUGIgKG1nL0tnKSIpK2dlb21fdmxpbmUoeGludGVyY2VwdD0wLjEsIGxpbmV0eXBlPSJzb2xpZCIsIGNvbG9yID0gInJlZCIpK2dlb21fdmxpbmUoeGludGVyY2VwdD0xLjIyLCBsaW5ldHlwZT0ic29saWQiLCBjb2xvciA9ICJibHVlIikreGxpbShjKDAsMS4yMikpK3lsYWIoIkN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43LCAwLjMpKQogIAogICN0aGVtZSggIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Y3lhbjIiKSwgcGFuZWwuYmFja2dyb3VuZD0gZWxlbWVudF9yZWN0KGZpbGwgPSAibGlnaHRjeWFuMiIpKQoKYGBgCgoKRmlndXJlIDE1LiBHcmFwaCB3aXRoIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIG9mIExlYWQgY29udGVudCBpbiBmb29kIHNhbXBsZXMgaW4gdGhlIEV1cm9wZWFuIFVuaW9uIHdpdGhpbiB0aGUgY2F0ZWdvcnkgIlN0aW11bGFudCBkcmlua3MiLiBSZWQgbGluZSByZXByZXNlbnRzIHRoZSBjb25jZW50cmF0aW9uIG9mIDAuMW1nL2tnIG9mIFBiIGFuZCBibHVlIGxpbmUgMS4yMm1nL2tnIHdoaWNoIGlzIHRoZSBjb250ZW50IHRoYXQgaW5jbHVkZXMgOTklIG9mIHRoZSBzYW1wbGVzLgoKVGhlIHdvcnN0IGNhc2UgaXMgdGhlIHRlYSBpbmZ1c2lvbnMsIHdoZXJlIG1vcmUgdGhhbiA1MCUgb2YgdGhlIHNhbXBsZXMgaGFkIGxldmVscyBhYm92ZSAwLjI1bWcva2cgb2YgTGVhZCBvciAyLjV4IG1vcmUgdGhhbiB0aGUgbGltaXQuCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KHN1YnNldChHRU1TNSxHRU1TNSRGb29kQ2F0ZWdvcnk9PSJTdGltdWxhbnQgYmV2ZXJhZ2VzLCBkcmllZCBhbmQgZGlsdXRlZCBleGNsdWRpbmcgY29jb2EgcHJvZHVjdHMiICYgR0VNUzUkRm9vZE5hbWU9PSJUZWEgKEluZnVzaW9uKSIgKSAsIGFlcyh4PVJlc3VsdFZhbHVlLCBjb2xvcj1Mb2NhbEZvb2ROYW1lKSkrCiAgICBzdGF0X2VjZGYoZ2VvbSA9ICJsaW5lIiwgYWVzKFJlc3VsdFZhbHVlKSkgK3hsYWIoIlBiIChtZy9LZykiKStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4xLCBsaW5ldHlwZT0ic29saWQiLCBjb2xvciA9ICJyZWQiKStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MS4yMiwgbGluZXR5cGU9InNvbGlkIiwgY29sb3IgPSAiYmx1ZSIpK3hsaW0oYygwLDEuMjIpKSt5bGFiKCJDdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiIpKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuNywgMC4zKSkKICAKICAjdGhlbWUoICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJsaWdodGN5YW4yIiksIHBhbmVsLmJhY2tncm91bmQ9IGVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Y3lhbjIiKSkKCmBgYAoKV2hlbiBmaWx0ZXJpbmcgZm9yICJUZWEgKGluZnVzaW9uKSIgb25seSB3ZSBjYW4gc2VlIGl0IGhhdmUgdGhlIGhpZ2hlc3QgZGlzdHJpYnV0aW9uIG9mIGFsbC4KCmBgYHtyfQoKR0VNU19FVSAlPiUgZmlsdGVyKEdFTVNfRVUkRm9vZENhdGVnb3J5PT0iU3RpbXVsYW50IGJldmVyYWdlcywgZHJpZWQgYW5kIGRpbHV0ZWQgZXhjbHVkaW5nIGNvY29hIHByb2R1Y3RzIiAmIEdFTVNfRVUkRm9vZE5hbWU9PSJUZWEgKEluZnVzaW9uKSIgKSAlPiUgc3VtbWFyaXplKHF1YW50aWxlKFJlc3VsdFZhbHVlLDAuOTUpKSAKYGBgCjk1JSBvZiBzYW1wbGVzIGFyZSBiZWxvdyAyLjQ4bWcva2cuIFRlbiB0aW1lcyBoaWdoZXIgdGhhbiB0aGUgbWVhbiB2YWx1ZXMgZm9yIGFsbCBvdGhlciBzYW1wbGVzIGluIEVVICBVbmlvbi4KCgojIyA1LjMgTG9va2luZyBiYWNrIHRvIHRoZSB3b3JzdCBjYXNlCgpTaW5jZSB3ZSBmb3VuZCB0aGUgVEVBIGFzIHRoZSBzYW1wbGUgd2l0aCBoaWdoZXIgY29uc3RhbnQgbGV2ZWwgZm9vZCBjb250YW1pbmF0ZWQgd2l0aCBMZWFkLgpMZXQncyB2aXN1YWxpemUgaG93IHRoaXMgc2FtcGxlIGJlaGF2ZXMgd2l0aCB0aGVzZSA0IHZhcmlhYmxlIHNlbGVjdGVkOiBSZXN1bHQsIFF1YWxpdHkgdG9vbCwgT3JpZ2luIGFuZCBMT1E6IAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkdFTVMycmVzXzEgJT4lIGZpbHRlcihDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iKSAlPiUKICBmaWx0ZXIoRm9vZE5hbWU9PSJUZWEgKEluZnVzaW9uKSIpICU+JQogIGdyb3VwX2J5KFJlc3VsdFZhbHVlPVJlc3VsdFZhbHVlPj0wLjEsQW5hbHl0aWNhbFFBTmFtZSxGb29kT3JpZ2luTmFtZSxncm91cCkgJT4lCiAgc3VtbWFyaXNlKFNhbXBsZXM9bGVuZ3RoKEFuYWx5dGljYWxRQU5hbWUpKSAtPiBTYW5rZXk0CmdncGxvdChhcy5kYXRhLmZyYW1lKFNhbmtleTQpLCBhZXMoeSA9IFNhbXBsZXMsIGF4aXMxID0gUmVzdWx0VmFsdWUsIGF4aXMyID0gQW5hbHl0aWNhbFFBTmFtZSwgYXhpczMgPSBGb29kT3JpZ2luTmFtZSwgYXhpczQ9Z3JvdXApKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbCA9IFJlc3VsdFZhbHVlKSwgd2lkdGggPSAxLzYpKwogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYygwLjEsMC4xKSkrCiAgZ2VvbV9zdHJhdHVtKHdpZHRoID0gMS81LCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiZ3JleTUwIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjQsIGxhYmVscyA9IGMoICJBYm92ZSAwLjEwXCBtZy9LZz8iLCAiUXVhbGl0eSBDb250cm9sIiwgIlNhbXBsZSBzb3VyY2UiLCJMT1EiKSkrCiAgZ2VvbV9sYWJlbChzdGF0ID0gInN0cmF0dW0iLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KHN0cmF0dW0pKSkgKyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSt5bGFiKCJOdW1iZXIgb2Ygc2FtcGxlcyIpIApgYGAKCkZpZ3VyZSAxNS4gQWxsdXZpYWwgcGxvdCBmcm9tIFRlYSBTYW1wbGVzIGluIEVVIHJlZ2lvbi4gVGhlIHZhc3QgbWFqb3JpdHkgb2Ygc2FtcGxlcyAobW9yZSB0aGFuIDc1JSkgYXJlIGhpZ2hlciB0aGFuIDAuMTBtZy9LZyBhbmQgZGlkbid0IHJlcG9ydCB0aGUgTE9RLiAKU2luY2UgbW9zdCBvZiBub24gcmVwb3J0ZWQgTE9RIGRhdGEgYXJlIGZyb20gb2xkIGRhdGEsIGl0J3MgaW50ZXJlc3RpbmcgY2hlY2sgaWYgaXQgYWxsIGNvbWVzIGZyb20gcHJldmlvdXMgeWVhcnMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpHRU1TMnJlc18xICU+JSAKICBmaWx0ZXIoQ291bnRyeU5hbWU9PSJXSE8gRXVyb3BlYW4gUmVnaW9uIikgJT4lCiAgZmlsdGVyKEZvb2ROYW1lPT0iVGVhIChJbmZ1c2lvbikiKSAlPiUKICBncm91cF9ieShSZXN1bHRWYWx1ZT1SZXN1bHRWYWx1ZT49MC4xLEFuYWx5dGljYWxRQU5hbWUsRm9vZE9yaWdpbk5hbWUsIGdyb3VwLCBTYW1wbGluZ1BlcmlvZEVuZCkgJT4lCiAgc3VtbWFyaXNlKFNhbXBsZXM9bGVuZ3RoKGdyb3VwKSkgLT4gU2Fua2V5NApnZ3Bsb3QoYXMuZGF0YS5mcmFtZShTYW5rZXk0KSwgYWVzKHkgPSBTYW1wbGVzLCBheGlzMSA9IFJlc3VsdFZhbHVlLCBheGlzMiA9IEFuYWx5dGljYWxRQU5hbWUsIGF4aXMzID0gRm9vZE9yaWdpbk5hbWUsIGF4aXM0PWdyb3VwLCBheGlzNT1TYW1wbGluZ1BlcmlvZEVuZCkpICsKICBnZW9tX2FsbHV2aXVtKGFlcyhmaWxsID0gUmVzdWx0VmFsdWUpLCB3aWR0aCA9IDEvNSkgK3NjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYyguMSwgLjEpKSsKICBnZW9tX3N0cmF0dW0od2lkdGggPSAxLzUsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJncmV5NTAiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6NSwgbGFiZWxzID0gYygiQWJvdmUgMC4xMFwgbWcvS2c/IiwgIlF1YWxpdHkgQ29udHJvbCIsICJTYW1wbGUgc291cmNlIiwiTE9RIiAsICJZZWFyIikpICsKICBnZW9tX2xhYmVsKHN0YXQgPSAic3RyYXR1bSIsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoc3RyYXR1bSkpKSArICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpK3lsYWIoIk51bWJlciBvZiBzYW1wbGVzIikgCgpgYGAKRmlndXJlIDE2LiBTYW1lIGFzIGZpZ3VyZSAxNSBleGNlcHQgd2l0aCB5ZWFyIGluY2x1ZGVkKGFzIFNhbXBsaW5nUGVyaW9kRW5kKS4gTm8sIHdlIGNhbiBub3Qgc2F5IHRoYXQgIk9sZCBzYW1wbGVzIChwcmUtMjAxMSkgdXNlZCB0byBoYXZlIG1vcmUgTGVhZCIuIFRvIGNvbmZpcm0gd2UgY2FuIG5vcm1hbGl6ZSB0aGUgY29udGVudCBvZiBSZXN1bHRWYWx1ZSB2YXJpYWJsZSBhbmQgcGxvdCBpdCBhbG9uZyB0aGUgeWVhcnMuCiAKYGBge3J9CkdFTVMycmVzXzEgJT4lIGZpbHRlcihDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iKSAlPiUKICBmaWx0ZXIoRm9vZE5hbWU9PSJUZWEgKEluZnVzaW9uKSIpICU+JQpnZ3Bsb3QoYWVzKHg9U2FtcGxpbmdQZXJpb2RFbmQseT0oUm93TnVtKSwgZmlsbD1SZXN1bHRWYWx1ZT49MC4xKSkrCiAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLCBzdGF0PSJpZGVudGl0eSIpKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwpKwogIGZhY2V0X3dyYXAofnN0cl93cmFwKEZvb2ROYW1lLHdpZHRoID0gNDApKSt5bGFiKCJSZXN1bHRWYWx1ZSIpK2dndGl0bGUoIlJhdGlvIG9mIHF1YW50aWZpZWQgc2FtcGxlcyBhY2NvcmRpbmcgdG8gbWF0cml4IikKYGBgCiAKIApGaWd1cmUgMTcuIFN0YWNrZWQgYmFyIGdyYXBoIHNob3dpbmcgbm9ybWFsaXplZCBudW1iZXIgb2Ygc2FtcGxlcyBhYm92ZSAwLjFtZy9LZyBvZiBMZWFkIHBlciB5ZWFyLiAKTm8sIHRoZSBwcm9wb3J0aW9uIG9mIHNhbXBsZXMgYWJvdmUgMC4xIGFyZSBub3QgZGVjcmVhc2luZyB3aXRoIHRpbWUuIFRvIGJlIHN1cmUsIGxldHMgYW5hbHl6ZSB1c2luZyBib3hwbG90IGFuZCBsaW5lYXIgcmVncmVzc2lvbiwgd2l0aCB0aGUgbWVkaWFuIG9mIFJlc3VsdCBhcyB0aGUgY2VudHJhbCBsaW5lIGluIGVhY2ggYm94LiAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpHRU1TMnJlc18xICU+JSBmaWx0ZXIoQ291bnRyeU5hbWU9PSJXSE8gRXVyb3BlYW4gUmVnaW9uIikgJT4lIGdyb3VwX2J5KFNhbXBsaW5nUGVyaW9kRW5kKSAlPiUKICBmaWx0ZXIoRm9vZE5hbWU9PSJUZWEgKEluZnVzaW9uKSIpICU+JQpnZ3Bsb3QoYWVzKHg9U2FtcGxpbmdQZXJpb2RFbmQseT1SZXN1bHRWYWx1ZSkpK3NjYWxlX3lfbG9nMTAoKSsKICBnZW9tX2JveHBsb3QobmEucm09VFJVRSkrIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLCkreWxhYigiUmVzdWx0VmFsdWUiKStnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsdWUiLCBhZXMoU2FtcGxpbmdQZXJpb2RFbmQsIFJlc3VsdFZhbHVlKSkrZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3I9ImJsdWUiLGFlcyhncm91cD0xKSwgbmEucm09VFJVRSkKYGBgCkZpZ3VyZSAxOC4gQm94cGxvdCBmcm9tIFRlYSBzYW1wbGVzIGluIEV1IFJlZ2lvbiBieSBZZWFyLiBObyB0cmVuZCwgbWVhbiBjb250ZW50IG9mIExlYWQgaW4gZm9vZCBpcyBzdGFibGUgZm9yIHRoZSBwZXJpb2Qgb2YgMTAgeWVhcnMuCgoKCgojIDYuIENvbmNsdXNpb24KCk1vc3QgZm9vZHMgd2l0aCBoaWdoIGxlYWQgdmFsdWVzIGhhdmUgYSB2ZXJ5IGxpbWl0ZWQgaW50YWtlOgoiQU5JTUFMIEZFRUQiIChvYnZpb3VzbHkpLCAiU3RpbXVsYW50IGJldmVyYWdlcyIgKG9jY2FzaW9uYWwgdXNlKSwgIkhlcmJzLCBzcGljZXMgYW5kIGNvbmRpbWVudHMiIChvbmx5IGFzIGluZ3JlZGllbnRzIGFuZCBpbiBzbWFsbCBxdWFudGl0aWVzKS4gT25seSAiUHJvZHVjdHMgZm9yIHNwZWNpYWwgbnV0cml0aW9uYWwgdXNlIiBhcmUgY3JpdGljYWwgYXMgdGhleSBtYXkgcmVzdWx0IGluIGEgaGlnaGVyIGludGFrZSBmb3IgbG9uZ2VyIHBlcmlvZHMuCgpUaGVyZSBhcmUgbm8gaW5kaWNhdGlvbnMgdGhhdCBpbXBvcnRlZCBzYW1wbGVzICh0byB0aGUgRXVyb3BlYW4gVW5pb24pIGFyZSBxdWFudGlmaWVkIG1vcmUgZnJlcXVlbnRseSB0aGFuIGRvbWVzdGljIG9uZXMsIGluZGljYXRpbmcgY29udGFtaW5hdGlvbiBhdCBzb3VyY2UuCgpJbiB0aGUgRXVyb3BlYW4gVW5pb24gKHRoZSByZWdpb24gd2l0aCBtb3JlIGRhdGEgYXZhaWxhYmxlKSwgOTElIG9mIGFsbCBzYW1wbGVzIGFyZSBiZWxvdyB0aGUgMCwxbWcva2cgaW50ZXJuYXRpb25hbCBsaW1pdCBhbmQgOTklIGFyZSBiZWxvdyAxLjIxbWcva2cuCgpTdGltdWxhbnQgQmV2ZXJhZ2VzLCBzcGVjaWFsbHkgdGVhLCBwcmVzZW50IGEgaGlnaCBjb250aW51b3VzIHNvdXJjZSBvZiBMZWFkIGluIHRoZSBkaWV0LiBKdXN0IHRvIGFkZHJlc3MgaG93IG11Y2gsIDUwJSBvZiB0ZWEgaW5mdXNpb24gc2FtcGxlcyBhcmUgaGlnaGVyIHRoYW4gMCwybWcva2cgd2hpbGUgOTUlIG9mIG90aGVyIHNhbXBsZXMgZmFsbCBvbiB0aGlzIHJhbmdlLgoKSW4gdGhlIDEwIHllYXJzIHdpdGggbW9zdCBkYXRhIGZyb20gRXVyb3BlYW4gVW5pb24gYW5kIHRoZSB0aGUgbW9zdCBzdHVkaWVzIHNhbXBsZSAoVGVhIChJbmZ1c2lvbikpLCBubyBkaWZmZXJlbmNlIHdhcyBub3RlZCBpbiB0aGUgbWVhbiB2YWx1ZSBvZiBjb250YW1pbmF0aW9uLgoKU29tZSB0ZWNobmljYWwgcXVlc3Rpb25zIHJlbWFpbjogd2VyZSB0aGUgdGVhIHNhbXBsZXMgKFN0aW11bGFudCBiZXZlcmFnZXMpIGRpZ2VzdGVkIGZyb20gdGhlIGRyeSBtYXNzPyBXZXJlIHRoZSBzYW1wbGVzIGlkZW50aWZpZWQgYXMgImluZnVzaW9uIiBhbmFseXplZCBhcyAicmVhZHkgZm9yIGNvbnN1bXB0aW9uIiwgaWUgZGlyZWN0IHJlYWRpbmc/CldoYXQgaXMgdGhlIHJlYWwgcG9zc2liaWxpdHkgb2YgbWlzcGxhY2VtZW50IG9mIHVuaXRzIGluIHRoZSByZXN1bHRzIHN1Y2ggYXMgdWcva2cgcmVwb3J0ZWQgYXMgbWcva2c/IFNob3VsZCB0aGV5IGJlIHRyZWF0ZWQgYW5kIGludmVzdGlnYXRlZCBhcyBvdXRsaWVycyBvciBhcyB0cmFuc2NyaXB0aW9uIGVycm9ycz8KCgoKIyA3LiBSZWZlcmVuY2VzOgoKaHR0cHM6Ly93d3cuZmRhLmdvdi9mb29kL21ldGFscy1hbmQteW91ci1mb29kL2xlYWQtZm9vZC1mb29kd2FyZXMtYW5kLWRpZXRhcnktc3VwcGxlbWVudHMKCgoKIyBPdGhlci4uLgoKIyMgVGVhIGludmVzdGlnYXRpb24KCkhvdyBtYW55IHRlYSBzYW1wbGVzIHdlIGhhdmUgdW5kZXIgVGVhIChJbmZ1c2lvbikKYGBge3J9CkdFTVM1ICU+JSBmaWx0ZXIoRm9vZENhdGVnb3J5PT0iU3RpbXVsYW50IGJldmVyYWdlcywgZHJpZWQgYW5kIGRpbHV0ZWQgZXhjbHVkaW5nIGNvY29hIHByb2R1Y3RzIikgJT4lIGdyb3VwX2J5KExvY2FsRm9vZE5hbWUpICU+JSBzdW1tYXJpc2UodG90YWw9bGVuZ3RoKExvY2FsRm9vZE5hbWUpKSAlPiUgZ2dwbG90KCkrZ2VvbV9jb2woYWVzKHRvdGFsLHJlb3JkZXIoTG9jYWxGb29kTmFtZSx0b3RhbCxtYXgpLGZpbGw9TG9jYWxGb29kTmFtZSwpLHNob3cubGVnZW5kID0gRkFMU0UpCmBgYAogCiMjIERpZmVyZW7Dp2EgbcOpZGlhczogY29uY2VudHJhw6fDo28gRG9tw6lzdGljbyBlIEltcG9ydGFkYQoKUHJpbWVpcm8sIHZhbW9zIHNlcGFyYXIgb3MgZGFkb3MgZGUgY2jDoSBhbmFsaXphZG9zIG5hIFVuacOjbyBFdXJvcMOhaWE6CgpgYGB7cn0KR0VNU19FVTwtc3Vic2V0KEdFTVMyLCBHRU1TMiRDb3VudHJ5TmFtZT09IldITyBFdXJvcGVhbiBSZWdpb24iICYgRm9vZENhdGVnb3J5PT0iU3RpbXVsYW50IGJldmVyYWdlcywgZHJpZWQgYW5kIGRpbHV0ZWQgZXhjbHVkaW5nIGNvY29hIHByb2R1Y3RzIikKCkdFTVNfRVUgJT4lIGdyb3VwX2J5KEZvb2RPcmlnaW5OYW1lLCBRdWFudGlmaWNhZGFzPVJlc3VsdFZhbHVlPj0wLjAwMSkgJT4lIHN1bW1hcmlzZShOdW1lcm89bGVuZ3RoKFJlc3VsdFZhbHVlKSkgJT4lIGdncGxvdCgpICtnZW9tX2NvbChhZXMoRm9vZE9yaWdpbk5hbWUsTnVtZXJvLCBmaWxsPVF1YW50aWZpY2FkYXMpKQoKYGBgClRlbW9zIGEgcHJvcG9yw6fDtWVzIGRlIHF1YW50aWZpY2FkYXMgZW50cmUgYW1vc3RyYXMgZG9tZXN0aWNhcyBtYWlzIGFsdGEgc2VndWlkYSBwZWxhcyBpbXBvcnRhZGFzIGUgZGVzY29uaGVjaWRhcy4KCkFiYWl4byBwb2RlbW9zIHZlciBlc3NlcyB2YWxvcmVzCgoKYGBge3J9CkdFTVNfRVUgJT4lIGdyb3VwX2J5KEZvb2RPcmlnaW5OYW1lLCBRdWFudGlmaWNhZGFzPVJlc3VsdFZhbHVlPj0wLjAwMSkgJT4lIHN1bW1hcmlzZShOdW1lcm89bGVuZ3RoKFJlc3VsdFZhbHVlKSkKYGBgCkFwZXNhciBkZSB1bWEgbWFpb3IgZnJlcXVlbmNpYSBkZSBxdWFudGlmaWNhw6fDo28sIG9zIHZhbG9yZXMgbcOpZGlvcyBzw6NvIGNvbnNpZGVyYXZlbG1lbnRlIHByw7N4aW1vcywgbWVkaWFuYSBlIG8gcXVhcnRpbCBkZSA5NSUgIGxldmVtZW50ZSBhY2ltYSBwYXJhIGFtb3N0cmFzIGRvbWVzdGljYXM6CgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCkdFTVNfRVUgJT4lIGdyb3VwX2J5KEZvb2RPcmlnaW5OYW1lKSAlPiUgc3VtbWFyaXNlKG1lYW49bWVhbihSZXN1bHRWYWx1ZSkpCgpHRU1TX0VVICU+JSBncm91cF9ieShGb29kT3JpZ2luTmFtZSkgJT4lIHN1bW1hcmlzZShtZWRpYW49bWVkaWFuKFJlc3VsdFZhbHVlKSkKCkdFTVNfRVUgJT4lIGdyb3VwX2J5KEZvb2RPcmlnaW5OYW1lKSAlPiUgc3VtbWFyaXNlKHF1YW50aWxlPXF1YW50aWxlKFJlc3VsdFZhbHVlLDAuOTUpKQoKR0VNU19FVSAlPiUgZmlsdGVyKFJlc3VsdFZhbHVlPjAuMDAwMSkgJT4lICBncm91cF9ieShGb29kT3JpZ2luTmFtZSkgJT4lIGdncGxvdCgpKyBnZW9tX2JveHBsb3QoYWVzKEZvb2RPcmlnaW5OYW1lLFJlc3VsdFZhbHVlKSkreWxpbShjKC0wLjAwMSwxMCkpK3NjYWxlX3lfbG9nMTAoKQoKYGBgCgoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkdFTVNfRVUgJT4lIGZpbHRlcihSZXN1bHRWYWx1ZT4wLjAwMSkgJT4lICBncm91cF9ieShGb29kT3JpZ2luTmFtZSkgJT4lIHN1bW1hcmlzZShSZXN1bHRWYWx1ZT1tZWFuKFJlc3VsdFZhbHVlKSkKYGBgCgoKTsOjbyBow6EgZGlmZXJlbsOnYSBlbnRyZSBhcyBpbmZ1c8O1ZXMgZSBvIHPDs2xpZG8/CmBgYHtyfQpHRU1TX0VVICU+JSBmaWx0ZXIoRm9vZENhdGVnb3J5PT0iU3RpbXVsYW50IGJldmVyYWdlcywgZHJpZWQgYW5kIGRpbHV0ZWQgZXhjbHVkaW5nIGNvY29hIHByb2R1Y3RzIikgJT4lIGdncGxvdCgpKyBnZW9tX2JveHBsb3QoYWVzKFJlc3VsdFZhbHVlLEZvb2ROYW1lKSkrc2NhbGVfeF9sb2cxMCgpCmBgYAoKCgoKTcOpZGlhIFJlc3VsdFZhbHVlLCBzZXBhcmFuZG8gcG9yIHBhw61zOgoKYGBge3J9CkdFTVMxICU+JSBmaWx0ZXIoRm9vZE5hbWU9PSJURUFTIEFORCBIRVJCQUwgVEVBUyAoU09MSUQpIikgJT4lIGdyb3VwX2J5KENvdW50cnlOYW1lKSAlPiUgc3VtbWFyaXNlKFJlc3VsdFZhbHVlPW1lYW4oUmVzdWx0VmFsdWUpKQpgYGAgCgpgYGB7cn0Kb3B0aW9ucyhzY2lwZW49MTAwMDApCkdFTVMxICU+JSBmaWx0ZXIoRm9vZENhdGVnb3J5PT0iU3RpbXVsYW50IGJldmVyYWdlcywgZHJpZWQgYW5kIGRpbHV0ZWQgZXhjbHVkaW5nIGNvY29hIHByb2R1Y3RzIikgJT4lIGdyb3VwX2J5KENvdW50cnlOYW1lKSU+JWdncGxvdCgpK2dlb21fYm94cGxvdChhZXMoUmVzdWx0VmFsdWUsQ291bnRyeU5hbWUpKStzY2FsZV94X2xvZzEwKCkrZmFjZXRfd3JhcCh+Rm9vZE5hbWUsc2NhbGVzPSJmcmVlIikKYGBgIApUaGUgaW5mdXNpb25zIGFwcGVhciB0byBiZSBleHByZXNzZWQgaW4gZHJ5IG1hc3MsIG9yIGF0IGxlYXN0IGNvbnZlcnRlZCBhZnRlciBleHRyYWN0aW9uIG9uIHNvbWUgY291bnRyaWVzIGxpa2UgU2luZ2FwdXJlIGFuZCBFVS4gRGF0YSBvbiBVU0EsIEZyYW5jZSwgSG9uZyBLb25nLCBBdXN0cmFsaWEgYW5kIE5ldyBaZWFsYW5kIGNsdXN0ZXIgY2xvc2UgdG9nZXRoZXIgb24gYSByYW5nZSB0aGF0IGlzIDEwMCB0aW1lcyBsb3dlci4KCkxldCdzIG5vdyBpbnZlc3RpZ2F0ZSB3aGF0IHdvdWxkIGJlIHRoZSBzaW5nbGUgbW9zdCBoaWdoIGxldmVsIG9mIGxlYWQgaW4gdGhlIEdFTVMgZGF0YWJhc2UuCgojIFdvcnN0IEZvb2QgTmFtZSAobWVhbiB2YWx1ZSk6CgoKYGBge3J9CiAgR0VNUzEgJT4lCiAgZ3JvdXBfYnkoRm9vZE5hbWUpICU+JQogIHN1bW1hcmlzZShSZXN1bHRWYWx1ZT0gbWVhbihSZXN1bHRWYWx1ZSxuYS5ybT1UUlVFKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhSZXN1bHRWYWx1ZSkpICU+JSAKICB0b3BfbigxNSkgJT4lIAogIGdncGxvdCgpKyBnZW9tX2NvbChhZXMoUmVzdWx0VmFsdWUscmVvcmRlcihGb29kTmFtZSxSZXN1bHRWYWx1ZSxtYXgpLGZpbGw9Rm9vZE5hbWUpLHNob3cubGVnZW5kID0gRkFMU0UpCmBgYAoKCgoKV29yc3QgTG9jYWwgRm9vZCBOYW1lOgoKYGBge3J9CiAgR0VNUzEgJT4lCiAgZ3JvdXBfYnkoTG9jYWxGb29kTmFtZSkgJT4lCiAgc3VtbWFyaXNlKFJlc3VsdFZhbHVlPSBtZWFuKFJlc3VsdFZhbHVlLG5hLnJtPVRSVUUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKFJlc3VsdFZhbHVlKSkgJT4lIAogIHRvcF9uKDI1KSAlPiUgCiAgICBnZ3Bsb3QoKSsgZ2VvbV9jb2woYWVzKFJlc3VsdFZhbHVlLHJlb3JkZXIoTG9jYWxGb29kTmFtZSxSZXN1bHRWYWx1ZSxtZWFuKSxmaWxsPUxvY2FsRm9vZE5hbWUpLHNob3cubGVnZW5kPUZBTFNFKStzY2FsZV94X2xvZzEwKCkKYGBgCgoyIFRlYXMgcHJlc2VudGF0aW9ucyBhcHBlYXIgYXMgaGlnaGVyIGxldmVscyBvbiBGb29kTmFtZSwgaG93ZXZlciBkYXRhIGNhbiBiZSBza2V3ZWQgZHVlIHRvIGxvd2VyIHNhbXBsZXMgYW5kIG1vcmUgcHJlc2VuY2Ugb2Ygb3V0bGllcnMuCgpXaGVuIGNvbnNpZGVyaW5nIHRoZSBmaW5hbCBmb3JtIG9uIExvY2FsRm9vZE5hbWUsIFRlYSBmb3JtcyBhcHBlYXIgOCB0aW1lcy4KCkZpbHRlcmluZyBzYW1wbGVzIHdpdGggbW9yZSB0aGFuIDUwMDAgYW5hbHlzZXMgcGVyZm9ybWVkLiBTdW1tYXJpemluZyB3aXRoIG1lYW4sIHNkLCBuLCBSU0QsIDk1JSBwZXJjZW50aWw6CgoKYGBge3J9CkdFTVMxICU+JQogIGdyb3VwX2J5KEZvb2ROYW1lKSAlPiUKICBzdW1tYXJpc2UobWVhbj0gbWVhbihSZXN1bHRWYWx1ZSxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgbWVkaWFuPSBtZWRpYW4oUmVzdWx0VmFsdWUsbmEucm09VFJVRSksCiAgICAgICAgICAgIG49bigpLAogICAgICAgICAgICBzZD1zZChSZXN1bHRWYWx1ZSxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgUlNEPXNkKFJlc3VsdFZhbHVlLG5hLnJtPVRSVUUpKjEwMC9tZWFuKFJlc3VsdFZhbHVlLG5hLnJtPVRSVUUpLAogICAgICAgICAgICBwZXJjZW50aWxfOTU9cXVhbnRpbGUoUmVzdWx0VmFsdWUsMC45NSxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgcmF0aW9fTkQ9cHJvcC50YWJsZSh0YWJsZShSZXN1bHRUZXh0PT0iTkQiKSlbMl0pICU+JQogIGZpbHRlcihuPj0xMDApJT4lCiAgYXJyYW5nZShkZXNjKG1lZGlhbikpLT4gd29yc3RfY2FzZQoKCiU+JQogIHRvcF9uKDE1LG1lZGlhbikKYGBgCmBgYHtyfQprYWJsZSh3b3JzdF9jYXNlKQpgYGAKCgoKIyBXb3JzdCBmb29kIGJveHBsb3Q6CgpgYGB7cn0KICBHRU1TMSAlPiUKICBmaWx0ZXIoRm9vZE5hbWUgJWluJSB3b3JzdF9jYXNlJEZvb2ROYW1lKSU+JQogIGdyb3VwX2J5KEZvb2ROYW1lKSAlPiUgCiAgIGdncGxvdCgpKwogICAgZ2VvbV9ib3hwbG90KGFlcyhSZXN1bHRWYWx1ZSxGb29kTmFtZSkpKwogICAgZ2VvbV9wb2ludChkYXRhPXdvcnN0X2Nhc2UsYWVzKHBlcmNlbnRpbF85NSxGb29kTmFtZSksY29sb3I9InJlZCIpKwogICAgZ2VvbV9wb2ludChkYXRhPXdvcnN0X2Nhc2UsYWVzKG1lYW4sRm9vZE5hbWUpLGNvbG9yPSJibHVlIikreGxpbSgtMC4xLDEpK3NjYWxlX3hfbG9nMTAoKQoKCmBgYAoKCmBgYHtyfQogIEdFTVMxICU+JQogIGZpbHRlcihGb29kTmFtZSAlaW4lIHdvcnN0X2Nhc2UkRm9vZE5hbWUpJT4lCiAgZ3JvdXBfYnkoRm9vZE5hbWUpICU+JSAKICAgZ2dwbG90KCkrCiAgICBnZW9tX2JveHBsb3QoYWVzKFJlc3VsdFZhbHVlLEZvb2ROYW1lKSxhbHBoYT0wLjEpKwogICAgZ2VvbV9wb2ludChkYXRhPXdvcnN0X2Nhc2UsYWVzKHBlcmNlbnRpbF85NSxGb29kTmFtZSksY29sb3I9InJlZCIpKwogICAgZ2VvbV9wb2ludChkYXRhPXdvcnN0X2Nhc2UsYWVzKG1lYW4sRm9vZE5hbWUpLGNvbG9yPSJibHVlIikreGxpbSgtMC4xLDMpCgpgYGAKClNhbWUgc2l0dWF0aW9uLCBhbHRob3VnaCBGZWVkIGFsc28gYXBwZWFyIHRvIHNob3cgaGlnaCBsZWFkIGNvbnRhbWluYXRpb24gYW5kIGhhdmUgdGhlIHNhbWUgOTUlIHF1YW50aWxlIChhYm92ZSAybWcva2cpLiBUaGUgbWVkaWFuIGlzIG11Y2ggbG93IGR1ZSB0byBoaWdoZXIgcHJlc2VuY2Ugb2YgTkQgc2FtcGxlcyAoYWJvdXQgMzAlKQoK