Premise

This .html document refers to a fully reproducible R markdown document that can be downloaded form the code button in the top-right of this document. The source file is also available here

This file that can be used to go through the analysis associated with the Blue Paper 10: “A Global Habitat Strategy Framework” section.

In this supplementary, we assume some basic knowledge of the R programming language. If instruction are followed, this should be fully reproducible using R studio. For further comments on the results of this analysis please refer to the main paper, or please find Contacts to get in touch with the authors to report comments, bugs or problems.

Once R and R-studio are installed on the system, you can download the full supplementary data from: https://github.com/Fabbiologia/Blue-Paper-10.git

Beware that while all data used are open source, specific permission to reuse and publish them are needed from data providers. Credit for the use of those data should also go to the proper source listed in the table in the Methods in brief section.

The R code (v.3.6.0) was written using R-studio IDE (v.1.2.1511), as well as this document, using the following packages that can be installed in R or through R-studio using the following commands:

the package clValid available to download at https://cran.r-project.org/src/contrib/clValid_0.6-6.tar.gz and needs to be installed from source: install.packages(path_to_file, repos = NULL, type="source")

After all the packages are installed they can be loaded in R using the following commands:

## loading needed libraries after are installed
library(tidyverse)
library(readxl)
library(xlsx)
library(cluster)
library(factoextra)
library(vegan)
library(scales)
library(DT)
library(clValid)
library(cowplot)
library(ggpubr)

Methods in brief

Habitat data

To understand habitat area within each Exclusive Economic Zone (EEZ) and the proportion of each habitat that is within an MPA, we collected spatially referenced habitat data for coastal and oceanic ecosystems between February and April 2019. Data for thirty different habitats were found spanning oceanic features digitized from bathymetric data to satellite derived area estimates for many of the coastal habitats. We used a dataset that combined both EEZ area and land that was created by Sala et al., (2018) and intersected it with each habitat using ArcGIS 10.5 Desktop.

Next we calculated the area for each habitat by dissolving the resulting layer by Country and projecting it into the World Cylindrical Equal Area projection, and using the “Calculate Geometry” tool. To extract habitats that are of high conservation importance and the focus of our analysis we selected habitats that comprised of less than 0.5% of the cumulative habitat area. Seamounts did not fall under this classification, but due to their ecological importance were combined with guyots in our analysis (Rogers 1994). We used 6 habitats closely associated with the coast and 6 more closely associated with open ocean, for a total of 12 habitats. The table below lists these habitats and datasource used in the analysis. Losses and gains past the dates the data describes were not accounted for.

The February 2019 World Database of Protected Areas (UNEP-WCMC and IUCN (2019) was used to calculate the area or the number of reported locations of each habitat inside of an MPA. It needs to be clarified that being inside a MPA does not mean the habitat is protected, since the MPA objective and regulamentation might not involve the habitat at all. However, we consider that being inside an environmentally managed area should provide at least some indirect benefits to the habitat conservation.

The dataset was filtered by MPAs whose status was either designated, inscribed, adopted or established, thus removing not reported and proposed categories. The same methodology for the area calculation within each EEZ except the intersection included the filtered MPA dataset. The results were then compiled and compared to ensure that the maximum value of percent protection was 1 for each country.

Habitat Date of Data Data Type Source
Estuaries 2003 Polygon Alder (2003)
Mangroves 1997 - 2000 Polygon Giri, et al. (2011)
Saltmarsh 1973 - 2015 Points McOwen, et al. (2017)
Seagrasses 1934 - 2015 Polygon UNEP-WCMC, Short FT (2017)
Coral Reefs 1954 - 2018 Polygon UNEP-WCMC, WorldFish Centre, WRI, TNC (2018)
Kelp NA Point Jorge Assis (submitted for publication)
Cold Corals 1915 - 2014 Point Freiwald A (2017)
Sills 1950-2009 Polygon Harris et al. (2014)
Seamounts/Guyots 1950-2009 Polygon Harris et al. (2014)
Bridges 1950-2009 Polygon Harris et al. (2014)
Rift Valleys 1950-2009 Polygon Harris et al. (2014)
Hydrothermal Vents 1994-2019 Point Beaulieu, S.E., Szafranski, K. (2019)

The Global Habitat Strategy Framework

In this section, we applied a global marketing strategy to conservation. This strategy is based on the segmentation of a complex group of objects characterized by some attributes. Here, objects are countries’ Exclusive Economic Zones (EEZs), and the attributes are reported in the following table:

! Categories Attributes Coded as Description Source
Population Population growth pop_growth Population growth (annual %) derived from total population. World Bank Open data: https://data.worldbank.org/
! Population Population living less than 5m above sea level pop_coastal Population living in areas where elevation is below 5 meters (% of total population) World Bank Open data: https://data.worldbank.org/
Population Poverty poverty Percent of population living under $1.90 World Bank Open data: https://data.worldbank.org
Consumption Greenhouse gasses emissions emissions Total greenhouse gas emissions (kt of CO2 equivalent) World Bank Open data: https://data.worldbank.org/
Consumption Gross Domestic Product per capita GDP_capita GDP per capita (current USD) World Bank Open data: https://data.worldbank.org/
Consumption Gross Domestic Product total GDP_tot GDP per capita (current USD) World Bank Open data: https://data.worldbank.org/
Environment Pressures on the Marine Environment pressures The ecological and social factors that decrease health status Ocean Health Index: http://www.oceanhealthindex.org/
Environment Amount of area protected protected Overlap between protected area and the target habitat This paper
Government Corruption corruption Corruption score index for each country Worldwide governance indicators: https://datacatalog.worldbank.org
Government Regulatory quality reg_qual Regulatory Quality captures perceptions of the ability of the government to formulate and implement sound policies and regulations Worldwide governance indicators: https://datacatalog.worldbank.org
Government International cooperation int_coop Number of international treaties signed by a country This paper

We used the attributes in the table above to create an ecosocial dataset that can be reproduced using the code below in the Data wrangling section.

The segmentation can be fully reproduced and is further commented in the Segmentation process section.

To achieve the segmentation we used an unsupervised clustering technique. These analysis are designed to categorise observations into a number of different groups (“clusters”), with each being relatively similar based on their values for a range of different factors.

First we scaled our attributes, then we calculated an euclidean distance matrix. Then we used the Ward’s hierarchical cluster, that minimise the total variance between samples within each cluster. Then merge clusters successively so as to minimise the increase in the Ward’s distance. The process continues until there’s just one cluster containing all the observations.

Since we had no a priori hypothesis in on how many segments (clusters) countries should be separated in, the advantage of the unsupervised technique is that it is independent to a previous choice of the number of clusters. Therefore, the choice of how many segments countries will be separated can be based upon the usability and usefulness of the corresponding groupings.

Nevertheless, we tested with different cluster validation techniques, which method and number of clusters better represent data. This process can be reproduced in the Cluster validation section.

Then, we grouped countries according to their segments and calculated the average score for each attribute and plotted it in a bar-plot that are represented in Figure 1 and 2 in main text. These can be fully reproduced in the Results visualization section.

Data wrangling

Filtering data

Two filters were created for landlocked countries that due to an error in eez layer were included and disputed areas also were filtered out.

landlocked <- c("Afghanistan", "Andorra", "Armenia", "Austria", 
                "Azerbaijan", "Belarus", "Bhutan", "Bolivia", 
                "Botswana", "Burkina Faso", "Burundi", 
                "Central African Republic", "Chad", "Czech Republic", 
                "Ethiopia", "Hungary", "Kazakhstan", "Kosovo", "Kyrgyzstan", 
                "Laos", "Lesotho", "Liechtenstein", "Luxembourg", "Macedonia", 
                "Malawi", "Mali", "Moldova", "Mongolia", "Nepal", "Niger", "Paraguay", 
                "Rwanda", "San Marino", "Serbia", "Slovakia", "South Sudan", 
                "Swaziland", "Switzerland", "Tajikistan", "Turkmenistan", "Uganda", 
                "Uzbekistan", "West Bank", "Vatican City", "Zambia", "Zimbabwe", 
                "Gaza Strip")

disputed <- c("Area en controversia (disputed - Peruvian point of view)", 
              "Area of overlap Australia/Indonesia",
              "Conflict zone China/Japan/Taiwan", 
              "Conflict zone Japan/Russia", 
              "Conflict zone Japan/South Korea",
              "Disputed Barbados/Trinidad & Tobago", 
              "Joint regime Colombia/Jamaica", 
              "Joint regime Japan/Korea", 
              "Joint regime Nigeria/Sao Tome and Principe",  
              "Joint development area Australia/East Timor",
              "Protected zone Australia/Papua New Guinea", 
              "Disputed Kenya/Somalia",
              "Disputed Western Sahara/Mauritania")

Loading data files

All data sources are stored in the data/sources/ folder, some have been manually modified from the original downloaded version, that can be checked in the folder data/sources/raw. The final dataset that is going to be used in further analysis is the data/BP-10-Dataset.xlsx file.

EEZ and habitat data

### EEZ----
eez <- read.csv("data/sources/EEZ_marine_area.csv") %>% 
      select(Country, EEZ_area=Area)%>% 
      filter(! Country %in% disputed) %>% 
      filter(! Country %in% landlocked)


## Habitat data ----

hab <- read_excel("data/sources/Area of Habitats by Country.xlsx") %>% 
      filter(! Country %in% disputed) %>% 
      filter(! Country %in% landlocked)


eez_hab <- merge(eez, hab, all.x = T) %>% 
      replace(is.na(.), 0)


## Saving file ----

write.xlsx(eez_hab, "data/BP-10-Dataset.xlsx", sheetName = "eez_hab", 
           col.names = TRUE, row.names = F, append = FALSE)

Ecosocial data

## World Bank Data ----
social <- readxl::read_excel("data/sources/WorldBankData.xlsx")

### OHI----

OHI <- read_excel("data/sources/OHI/hab_pressures_output.xls") %>% 
      select(-c("OBJECTID", "FREQUENCY")) %>% 
      group_by(Country) %>% 
      gather("year", "pressures", SUM_w2012:SUM_w2018) %>% 
      summarise(pressures_hab = mean(pressures))

OHI <- OHI %>% 
      filter(! Country %in% disputed) %>% 
      filter(! Country %in% landlocked)


### WGI indicators ----

WGI <- read_excel("data/sources/WGI_estimate.xlsx") %>% 
      group_by(Country) %>% 
      spread(Indicator, WGI) %>% 
      select(Country, Regulatory_quality, Corruption)


### Internationa agreements edited ----

ICS <- read_excel("data/sources/International_coop.xlsx")

Now it is possible to merge all these datasets. The independent file loading was a choice made to clarify the process and link directly to data sources that can be worked and evaluated independently.

df <- merge(eez, OHI, all.x = T)
df <- merge(df, WGI, all.x = T)
df <- merge(df, social, all.x=T)
df <- merge(df, ICS, all.x = T)

df <- cbind(df, eez_hab[16:ncol(eez_hab)])
names(df)
##  [1] "Country"                  "EEZ_area"                
##  [3] "pressures_hab"            "Regulatory_quality"      
##  [5] "Corruption"               "Pop_growth"              
##  [7] "GDP_capita"               "GDP_tot"                 
##  [9] "Poverty"                  "Pop_coastal"             
## [11] "Greenhouse_gas"           "International_coop"      
## [13] "Coral_Reefs_Parea"        "Estuaries_Parea"         
## [15] "Mangroves_Parea"          "Seagrasses_Parea"        
## [17] "Kelp_Parea"               "Saltmarsh_Parea"         
## [19] "Hydrothermal_vents_Parea" "Cold_Coral_Parea"        
## [21] "Bridges_Parea"            "Sills_Parea"             
## [23] "Rift_Valleys_Parea"       "Seamounts_Guyots_Parea"
names(df) <- c("Country", "EEZ_area", 
               "pressures",
               "reg_qual",
               "corruption",
               "pop_growth",
               "GDP_cap",
               "GDP_tot",
               "poverty",
               "pop_coast",
               "emissions",
               "int_coop",
               "Coral_Reefs_Parea",
               "Estuaries_Parea",
               "Mangroves_Parea",
               "Seagrasses_Parea", 
               "Kelp_Parea", 
               "Saltmarsh_Parea",
               "Hydrothermal_vents_Parea",
               "Cold_Coral_Parea",
               "Bridges_Parea", 
               "Sills_Parea",
               "Rift_Valleys_Parea",
               "Seamounts_Guyots_Parea")

write.xlsx(df, "data/BP-10-Dataset.xlsx",
           sheetName="ecosocial", append=TRUE, row.names = F)

Note that the last code append data into the excel sheet “ecosocial”.


Segmentation process

The segmentation process, here applied to conservation, is based on a marketing strategy to simplify the complexity of target audience to which a product need to be targeted. The world is very heterogeneous, therefore global corporations realized that to adapt to this complexity they needed to divide and conquer different types of markets.

In marketing, segmentations is a process that helps marketers narrow their focus on the most promising groups within the universe they are targeting. There is no single correct way to segment a market. Defining a target consumer base can be performed using a variety of segmentation methods. One can apply a combination of these methods to provide greater insight to their target markets. Corporations usually start creating geo-clusters, which combine geographic, demographic and behavioural data.

In this context, conservation might aim at doing the same thing. International conservation strategies can benefit from this approach to create new strategies to apply to this complex world.

In the following sections we present an fully reproducible example of the segmentation process using the data available.

In the previous Data wrangling section we showed how the dataset was created. Here we focus on describing the process of the segmentation.


First we load the habitat data and we assign the name df that stand for data.frame.

# loading data
df <- read_excel("data/BP-10-Dataset.xlsx")

Then we upload the ecosocial data from the same xlsx file but in the other sheet (note the sheet argument in the read_excel function).

ecosocial <- read_excel("data/BP-10-Dataset.xlsx", sheet = "ecosocial")

From the original ecosocial data we want to change two things. First we invert the Corruption value. In the original format, corruption is represented with positive values when it is low, while with negative values when it is high. This was counter-intuitive for the way we wanted to interpret data, so we multiplied it *-1.

ecosocial$corruption <- ecosocial$corruption*-1 #this inverts Corruption values

Relative habitat importance index

Here we calculate the relative importance index following this formula:

\(\frac{(habitat-area-in-a-country / Total-habitat-area)*100}{(Country-EEZ-area / World-EEZ-area)*100}\)

That can be calculated with the following code:

## Area standardization x EEZ ----

## formula to calculate percentage
perc <- function(x){
   (x/sum(x))*100
}

framework <- df %>% 
   select(Country:Seamounts_Guyots) %>% 
   mutate_at(vars(EEZ_area:Seamounts_Guyots), perc) %>% ## percentage contribution
   mutate_at(vars(MPA:Seamounts_Guyots), funs(./EEZ_area)) ## EEZ area standardization
   

write.xlsx(as.data.frame(framework), "data/BP-10-framework.xlsx", row.names = F)

Example dataset

Now that all variables needed are in place, we choose the habitat target. Here we present a study case with Mangroves and the code to reproduce Figure 2 in main text for Coral Reefs.

First we want to extract countries that have mangroves, merge this with ecosocial variables, and create the dataset we will use for the segmentation.

## Dataset creation 

MG <- framework %>% 
      select(Country, Mangroves) %>% 
      arrange(-Mangroves) %>% 
      mutate(Mangroves = round(Mangroves, 2)) %>%
      filter(Mangroves > 0) %>% 
      merge(., ecosocial) %>% 
      select(Country:int_coop, protected=Mangroves_Parea) %>% 
      mutate_if(is.numeric, round, 2)

This create the following dataset, the first columns called “Mangroves” refers to the Relative habitat importance index calculated for this habitat". Using this interactive table countries can be ordered by its value:

It is important also to account for correlation among variables. Correlated variables are not necessarily a problem for cluster analyisis. However, this can be used to reduce the amount of variables used if wanted.

### Correlation between variables ----

df_corr <- Hmisc::rcorr(as.matrix(select(MG, -Country)))

# Insignificant correlation are crossed
corrplot::corrplot.mixed(df_corr$r, tl.pos = "lt",
         p.mat = df_corr$P, sig.level = 0.05, insig = "pch", pch.cex = 2, pch.col = "grey90")

Here some of the governance data are correlated as expected. Protection of the habitat however is strongly correlated only with EEZ area. No other ecosocial attribute has a significant correlation with protection. A low but unsignificant correlation is found with international cooperations, suggesting that the effects of treaties countries signed had some, even if low, effect on the amount of the habitat protected.

Clustering

To segment countries, we used unsupervised clustering techniques. These analysis are designed to categorise observations into a number of different groups (“clusters”), with each being relatively similar based on their values for a range of different factors. Some form of distance measure are used to determine how close or far apart are different samples (countries, regions, sites etc.) are based on their attributes.

There are many flavours of clustering methods depending upon the distance measure chosen and which characteristic one wants to highlight in the data.

For example, Ward’s distance minimise the total variance between samples within each cluster. Then merge clusters successively so as to minimise the increase in the Ward’s distance. The process continues until there’s just one cluster containing all the observations.

There is a subjective element in clustering techniques. By choosing different pre-transformations of the data, different variables, different distance matrices, the results can be very different. If this from one side can be seen as a weakness, when properly applied can be a powerful exploration technique that can be applied to many situations, scales and questions to be answered. At the end of the clustering process one can review the data and identify what the members of each cluster have in common or not by summarising the characteristics of each cluster and potentially visualising these summarises as a means of comparing them.

Another source of subjectivity is the choice of the number of clusters. The number of cluster (k) can be determined by the user, with the aid of some statistical techniques called “cluster validations”. However, at the end of the day, depending on the research question, a good grouping is determined based upon the usability and usefulness of the corresponding groupings. Once the segmentation is complete, and further knowledge is obtained, other predictive statistical techniques can be used to test the results of the strategy.

Data scaling

In our study case, the first thing we need to do is scaling the variables, this is done to account for the different units, and the different origins all these variables have. If accounted raw, some variables would have much more weight than others only because have different scale and not because of a true effect of that particular variable.

# Scaling

df_scl <- scale(MG[4:ncol(MG)]) 
# the part in parenthesis is only to exclude non numerical columns


# this creates new raw names for the dataframe and 
#the vegan::makecepnames is to shorten some country names
rownames(df_scl) <- make.cepnames(MG$Country) 

This is how the dataset looks:

datatable(df_scl)

There are many NAs in this dataset. This is because we are using Exclusive Economic Zones, while most of the available ecosocial variables are at a country level. Further, for some countries the indicators are simply not available. Unfortunately, we need to exclude those rows with NAs from our analysis. Clustering can deal with some NAs, however, a good amount of countries have a complete data set, so we decided to exclude them.

df_scl <- na.omit(df_scl) ## excluding NAs

Then the distance metric is calculated. As mentioned earlier there are a plethora of distance metrics that can be applied. These can highlight some aspect of the data and should be chosen depending on the hypothesis and the types of variables available. Here we chose the Euclidean distance. Mainly because is the default measure for many software, also because it is relatively simple to interpret and observation with high values of variables are usually clustered together as for observation with low values. This characteristic is something that makes sense when dealing with ecosocial data to create country segments.

The following code calculate the euclidean distance matrix:

dist.eucl <- dist(df_scl, method = "euclidean")

Cluster validation

If one is struggling with the many clustering options, using the clValid package, it is possible to have an aid to choose the best algorithm and also a number of cluster suggestion for each method.

This compares clustering algorithms using two cluster validation measures:

  1. Internal measures, which uses intrinsic information in the data like connectivity, silhouette coefficient and the Dunn index

these are calculated with the following code:

# this selects the methods to be tested
clmethods <- c("hierarchical","kmeans","pam") 

intern <- clValid(na.omit(df_scl), nClust = 2:10, 
                  clMethods = clmethods, validation = "internal")
summary(intern)
## 
## Clustering Methods:
##  hierarchical kmeans pam 
## 
## Cluster sizes:
##  2 3 4 5 6 7 8 9 10 
## 
## Validation Measures:
##                                  2       3       4       5       6       7       8       9      10
##                                                                                                   
## hierarchical Connectivity   2.9290  5.8579  8.9869 13.8361 19.8730 22.2063 27.2754 39.5115 40.8810
##              Dunn           0.9152  0.7288  0.4791  0.3906  0.2552  0.2552  0.2552  0.2280  0.2306
##              Silhouette     0.6778  0.6009  0.4558  0.4104  0.3554  0.3315  0.2468  0.2974  0.2757
## kmeans       Connectivity   4.8579 12.1361 13.1361 21.3504 33.5464 34.2464 44.8226 47.1560 44.4730
##              Dunn           0.7188  0.3222  0.3906  0.2071  0.1378  0.1900  0.1445  0.1521  0.1942
##              Silhouette     0.6511  0.4346  0.4332  0.2877  0.2898  0.2974  0.2570  0.2492  0.2523
## pam          Connectivity  30.0393 39.6226 40.4929 41.7135 43.6079 43.7508 46.1798 52.0063 53.0258
##              Dunn           0.0729  0.0729  0.0729  0.0999  0.1146  0.1146  0.1826  0.1831  0.1901
##              Silhouette     0.1886  0.1335  0.1565  0.1855  0.1847  0.2218  0.2271  0.2158  0.2249
## 
## Optimal Scores:
## 
##              Score  Method       Clusters
## Connectivity 2.9290 hierarchical 2       
## Dunn         0.9152 hierarchical 2       
## Silhouette   0.6778 hierarchical 2

The results of the optimal scores suggests that the best methods is the hierarchical cluster with 2-3 groups.

  1. Stability measures, are a special version of internal measures which evaluate the consistency of a clustering result by comparing it with the clusters obtained after each column is removed, one at a time.

This method include various measures:

  • The average proportion of non-overlap APN measures the average proportion of observations not placed in the same cluster by clustering based on the full data and clustering based on the data with a single column removed.
  • The average distance AD measures the average distance between observations placed in the same cluster under both cases (full data set and removal of one column).
  • The average distance between means ADM measures the average distance between cluster centres for observations placed in the same cluster under both cases.
  • The figure of merit FOM measures the average intra-cluster variance of the deleted column, where the clustering is based on the remaining (undeleted) columns.

These are calculated with the following code:

clmethods <- c("hierarchical","kmeans","pam")
stab <- clValid(na.omit(df_scl), nClust = 2:6, clMethods = clmethods, 
                validation = "stability")
# Display only optimal Scores
optimalScores(stab)
##           Score       Method Clusters
## APN 0.002884477 hierarchical        2
## AD  2.303623538          pam        6
## ADM 0.040850499 hierarchical        2
## FOM 0.666398620 hierarchical        6

In a similar way as before the stability measures suggest the use of hierarchical methods. However, the analyst might also explore a kmeans or pam algorithm with a higher number of cluster.

Again, these should be taken as a guide that the analyst can follow with common-sense.

In our example, we chose to go along with the hierarchical clusters and in particular we use a common method called “Ward” clustering. This can be calculated and visualised with the following code:

res.ward <- hclust(dist.eucl, method = "ward.D2")

fviz_dend(res.ward, k = 4, # Cut in four k = 3 groups
          cex = 0.8,   # label size
          k_colors = "aaas"  # colour palette
)

Note the k=4 argument in the fvis_dend() function. This refers to the number of cluster the analyst can decide to represent the data with.
The advantage of the unsupervised cluster, relative to supervised ones (e.g. kmeans), is that the final cluster tree results are independent on the number of cluster chosen. Therefore, here assigning a number of clusters can be done at different levels, deciding where to “cut” the dendogram tree.

If one wants to be sure about choice of clusters, can use the following methods to further decide the “best” way to groups the objects:

p1 <- fviz_nbclust(df_scl, hcut, method = "wss") +
      geom_vline(xintercept = 4, linetype = 2)+
      labs(subtitle = "Elbow method")

# Silhouette method
p2 <- fviz_nbclust(df_scl, hcut, method = "silhouette")+
      labs(subtitle = "Silhouette method")

# Gap statistic

set.seed(123)
p3 <- fviz_nbclust(na.omit(df_scl), hcut, nstart = 25,  method = "gap_stat", nboot = 500)+
      labs(subtitle = "Gap statistic method")

plot_grid(p1,p2,p3)

These tests, confirm that the best number of groups is between 2 to 4.

Results visualization

Now that we defined our country segments, we can extract the values for each country that define those segments.

To do that we can use the following code:

# this creates cluster classes data 
clusterclass<- cutree(res.ward, k=4) 
# this create factor levels according to the groups
clusterclass_levels<- levels(factor(clusterclass)) 
# this tranforme it to a R data.frame
clusterclass <- as.data.frame(clusterclass) 
# this creates a "Country" column 
clusterclass$Country <- rownames(clusterclass) 
# this merge the cluster groups with the variables used
mangrove_clusters <- cbind(as.data.frame(clusterclass), df_scl) 

# this create a factor for the clusters
mangrove_clusters$clusterclass <- as.factor(mangrove_clusters$clusterclass) 

For the final visualization we follow these steps:

  1. we create the final cluster graph final_cluster;
  2. we create three bar plots for each cluster: mc1, mc2 and mc3;
  3. we create a final image joining all these plots using the ggarrange() function from the package ggarrange.
# final cluster
final_cluster <- fviz_dend(res.ward, 
          k = 4, # Cut in four k = 4 groups
          k_colors  = "aaas", 
          horiz = TRUE,
          cex = 0.7,
          sub = "")

# cluster 1
mc1 <- mangrove_clusters %>% 
      select(clusterclass, pressures:protected) %>%  # selecting variables
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% #rescaling
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% # changing sign
      gather("Variable", "Value", pressures:protected) %>%  # gathering 
      group_by(clusterclass, Variable) %>% ## grouping
      summarise(Value=mean(Value, na.rm=T)) %>%  # summarising 
      filter(clusterclass == 1) %>% # this filters cluster one
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) +  # plot starts
      geom_col(col="black", fill = "#EE0000FF", alpha=0.8)+ 
      ylim(-1,1)+ # this set the scale for the plot
      theme_minimal()+ # this set the graphic theme
      labs(x='', y='', title="C-3")+  
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
       coord_flip()# these are graphical options

# cluster 2
mc2 <- mangrove_clusters %>% 
   select(clusterclass, pressures:protected) %>% 
   mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% 
   mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
   gather("Variable", "Value", pressures:protected) %>% 
   group_by(clusterclass, Variable) %>% 
   summarise(Value=mean(Value, na.rm=T)) %>% 
   filter(clusterclass == 2) %>% 
   ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) + 
   geom_col(col="black", fill = "#008B45FF", alpha=0.8)+ 
   ylim(-1,1)+
   theme_minimal()+
   labs(x='', y='Score', title="C-2")+
   theme(axis.text.y = element_text(size=16), 
         axis.text.x = element_text(color = "black"),
         text=element_text(family="serif", face="bold"), 
         panel.grid = element_line(colour="gray90"), 
         legend.position = "")+
      coord_flip()

# cluster 3
mc3 <- mangrove_clusters %>% 
      select(clusterclass, pressures:protected) %>% 
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% 
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
      gather("Variable", "Value", pressures:protected) %>% 
      group_by(clusterclass, Variable) %>% 
      summarise(Value=mean(Value, na.rm=T)) %>% 
      filter(clusterclass == 3) %>% 
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) + 
      geom_col(col="black", fill = "#631879FF", alpha=0.8)+ 
      ylim(-1,1)+
      theme_minimal()+
      labs(x='', y='', title="C-1")+
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
      coord_flip()

# cluster 4
mc4 <- mangrove_clusters %>% 
      select(clusterclass, pressures:protected) %>% 
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% 
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
      gather("Variable", "Value", pressures:protected) %>% 
      group_by(clusterclass, Variable) %>% 
      summarise(Value=mean(Value, na.rm=T)) %>% 
      filter(clusterclass == 4) %>% 
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) + 
      geom_col(col="black", fill = "#3B4992FF", alpha=0.8)+ 
      ylim(-1,1)+
      theme_minimal()+
      labs(x='', y='', title="C-4")+
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
      coord_flip()

# here we create the panel: 

ggarrange(
  final_cluster,  # First row with line plot
  # Second row with box and dot plots
  ggarrange(mc3, mc2, mc1, mc4, ncol = 1), 
  nrow = 1,
  ncol = 2
  ) 

*note that the cluster numbers are changed for the visualization, so that are in the same order in the Figure in main text. This is only for visual representation and do not change the results. Names of some countries were also shortened to save space.

We can save the figure using:

ggsave("figs/Figure1.png", dpi=300)

Coral Reefs

For Coral reefs we show the code below. We followed the same methodology and framework as for Mangroves.

## Coral Reefs dataset creation 

CR <- framework %>% 
   select(Country, Coral_Reefs) %>% 
   arrange(-Coral_Reefs) %>% 
   mutate(Coral_Reefs = round(Coral_Reefs, 2)) %>% 
   filter(Coral_Reefs > 0) %>% 
   merge(., ecosocial) %>% 
   select(Country:emissions, protected=Coral_Reefs_Parea)

This create the following dataset:

datatable(CR)

Now if we want to order countries according to the relative habitat importance index here are the results:

datatable(CR %>% select(Country, Coral_Reefs) %>% arrange(-Coral_Reefs))
### Correlation between variables ----

df_corr <- Hmisc::rcorr(as.matrix(select(CR, -Country)))

# Insignificant correlation are crossed
corrplot::corrplot.mixed(df_corr$r, tl.pos = "lt",
         p.mat = df_corr$P, sig.level = 0.05, 
         insig = "pch", pch.cex = 2, pch.col = "grey90")

Interestingly, in this case, protection is negativley correlated with corruption. Even if significance is low.

Coral reefs Clustering

Data scaling

# Scaling
# the part in parenthesis is only to exclude non numerical columns
df_scl <- scale(CR[4:ncol(CR)]) 

# this creates new raw names for the dataframe and 
#the vegan::makecepnames is to shorten some country names

rownames(df_scl) <- make.cepnames(CR$Country) 


df_scl <- na.omit(df_scl) ## excluding NAs

datatable(df_scl)

Distance calculation

dist.eucl <- dist(df_scl, method = "euclidean")

Coral reefs cluster validation

# this selects the methods to be tested
clmethods <- c("hierarchical","kmeans","pam") 

intern <- clValid(na.omit(df_scl), nClust = 2:10, 
                  clMethods = clmethods, validation = "internal")
summary(intern)
## 
## Clustering Methods:
##  hierarchical kmeans pam 
## 
## Cluster sizes:
##  2 3 4 5 6 7 8 9 10 
## 
## Validation Measures:
##                                  2       3       4       5       6       7       8       9      10
##                                                                                                   
## hierarchical Connectivity   2.9290  5.8579  8.7869 11.7159 14.8115 17.8516 20.7806 32.3925 37.4266
##              Dunn           0.8245  0.6826  0.8172  0.6569  0.5455  0.4598  0.4598  0.1850  0.1871
##              Silhouette     0.6864  0.6570  0.5867  0.4501  0.3778  0.2540  0.1604  0.2278  0.2311
## kmeans       Connectivity   9.7159 10.2159 10.7159 19.4302 26.7488 29.6341 38.8218 37.3746 40.1437
##              Dunn           0.3063  0.4902  0.6569  0.2222  0.1389  0.2452  0.1578  0.1827  0.1827
##              Silhouette     0.6163  0.5957  0.5829  0.2223  0.2659  0.1725  0.2506  0.2820  0.2556
## pam          Connectivity   2.9290  5.8579 25.0147 30.6159 32.3615 47.4492 37.6365 43.8460 50.0980
##              Dunn           0.8245  0.6826  0.0928  0.0847  0.1283  0.1130  0.1451  0.2128  0.1817
##              Silhouette     0.6864  0.6570  0.2128  0.1933  0.2109  0.1597  0.2491  0.2377  0.2015
## 
## Optimal Scores:
## 
##              Score  Method       Clusters
## Connectivity 2.9290 hierarchical 2       
## Dunn         0.8245 hierarchical 2       
## Silhouette   0.6864 hierarchical 2
clmethods <- c("hierarchical","kmeans","pam")
stab <- clValid(na.omit(df_scl), nClust = 2:6, clMethods = clmethods, 
                validation = "stability")
# Display only optimal Scores
optimalScores(stab)
##           Score       Method Clusters
## APN 0.004162812 hierarchical        2
## AD  2.166699805          pam        6
## ADM 0.068919641 hierarchical        2
## FOM 0.791351867 hierarchical        5
res.ward <- hclust(dist.eucl, method = "ward.D2")

fviz_dend(res.ward, k = 4, # Cut in k = 4 groups
          cex = 0.5,   # label size
          k_colors = "aaas"  # colour palette
)

In this case, pruning in 4 segments, create a cluster with only one country (Australia). This is were, for management pourposes, the analyst might choose to join those countries in one segment and treat them accordingly.

p1 <- fviz_nbclust(df_scl, hcut, method = "wss") +
      geom_vline(xintercept = 4, linetype = 2)+
      labs(subtitle = "Elbow method")

# Silhouette method
p2 <- fviz_nbclust(df_scl, hcut, method = "silhouette")+
      labs(subtitle = "Silhouette method")

# Gap statistic

set.seed(123)
p3 <- fviz_nbclust(na.omit(df_scl), hcut, nstart = 25,  method = "gap_stat", nboot = 500)+
      labs(subtitle = "Gap statistic method")

plot_grid(p1,p2,p3)

In this case, while the best clustering is suggested to be 2 or 4, we can see from the dendogram that 4 main groups can be managed in this framework and can be interesting to separate them further. The group formed by Australia, China, Japan and the USA is grouped arificially, since cutting the dendogram would split them further. However, in a management perspective, it make sense that these should be considered toghether.

Coral reefs results visualization

# this creates cluster classes data 
clusterclass<- cutree(res.ward, k=4) 
# this create factor levels according to the groups
clusterclass_levels<- levels(factor(clusterclass)) 
# this tranforme it to a R data.frame
clusterclass <- as.data.frame(clusterclass) 
# this creates a "Country" column 
clusterclass$Country <- rownames(clusterclass) 
# this merge the cluster groups with the variables used
coralreefs_clusters <- cbind(as.data.frame(clusterclass), df_scl) 
# this create a factor for the clusters
coralreefs_clusters$clusterclass <- as.factor(coralreefs_clusters$clusterclass) 

For the final visualization we follow these steps:

  1. we create the final cluster graph final_cluster;
  2. we create three bar plots for each cluster: mc1, mc2 and mc3;
  3. we create a final image joining all these plots using the ggarrange() function from the package ggarrange.
# final cluster
final_cluster <- fviz_dend(res.ward, 
          k = 4, # Cut in four k = 4 groups
          k_colors  = "aaas", 
          horiz = TRUE,
          cex = 0.7,
          sub = "")

# cluster 1
mc1 <-  coralreefs_clusters %>% 
      select(clusterclass, pressures:protected) %>%  #selecting variables
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% # rescaling
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
      gather("Variable", "Value", pressures:protected) %>% # reshape
      group_by(clusterclass, Variable) %>% ## grouping
      summarise(Value=mean(Value, na.rm=T)) %>%  # summarising 
      filter(clusterclass %in% c(1,4)) %>% 
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) +  
      geom_col(aes(fill = clusterclass), col="black",  alpha=0.8, position = "dodge2")+ #
      scale_fill_manual(values=c("#008B45FF", "#631879FF"))+
      ylim(-1,1)+ 
      theme_minimal()+ 
      labs(x='', y='', title="C-2")+ 
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
       coord_flip() 


# cluster 2
mc2 <- coralreefs_clusters %>% 
      select(clusterclass, pressures:protected) %>%  
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>% 
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
      gather("Variable", "Value", pressures:protected) %>% 
      group_by(clusterclass, Variable) %>% 
      summarise(Value=mean(Value, na.rm=T)) %>%
      filter(clusterclass == 2 ) %>% 
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) +  
      geom_col(col="black", fill = "#3B4992FF", alpha=0.8)+ 
      ylim(-1,1)+ 
      theme_minimal()+
      labs(x='', y='', title="C-3")+ 
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
       coord_flip()

# cluster 3
mc3 <- coralreefs_clusters %>% 
      select(clusterclass, pressures:protected) %>% 
      mutate_if(is.numeric, rescale, na.rm=T, c(0,1)) %>%
      mutate(pressures=pressures*-1, corruption=corruption*-1, 
             pop_growth=pop_growth*-1, poverty=poverty*-1,
             pop_coast=pop_coast*-1, emissions=emissions*-1) %>% 
      gather("Variable", "Value", pressures:protected) %>%
      group_by(clusterclass, Variable) %>%
      summarise(Value=mean(Value, na.rm=T)) %>%
      filter(clusterclass == 3) %>%
      ggplot(aes(x=reorder(Variable, Value), y=Value, group=1)) +
      geom_col(col="black", fill = "#EE0000FF", alpha=0.8)+ 
      ylim(-1,1)+ 
      theme_minimal()+
      labs(x='', y='', title="C-2")+
      theme(axis.text.y = element_text(size=16),
            axis.text.x = element_text(color = "black"),
            text=element_text(family="serif", face="bold"), 
            panel.grid = element_line(colour="gray90"), 
            legend.position = "")+
       coord_flip()

# here we create the panel: 

ggarrange(
  final_cluster,  # First row with line plot
  # Second row with box and dot plots
  ggarrange(mc1, mc3, mc2, ncol = 1), 
  nrow = 1,
  ncol = 2
  ) 

*note that the cluster numbers are changed for the visualization, so that are in the same order in the Figure in main text. This is only for visual representation and do not change the results.

We can save the figure using:

ggsave("figs/Figure2.png", dpi=300)

References

Sala, E., Mayorga, J., Costello, C., Kroodsma, D., Palomares, M. L. D., Pauly, D., […] Zeller, D. (2018). The economics of fishing the high seas. Science Advances, 4(6), eaat2504. https://doi.org/10.1126/sciadv.aat2504

Habitat data references from the table

  • Estuaries: Alder J (2003). Putting the coast in the “Sea Around Us”. The Sea Around Us Newsletter 15: 1-2. URL: http://seaaroundus.org/newsletter/Issue15.pdf; http://data.unep-wcmc.org/datasets/23 (version 2.0)

  • Mangroves: Giri, C., E. Ochieng, L. L. Tieszen, Z. Zhu, A. Singh, T. Loveland, J. Masek, and N. Duke. (2011). “Status and Distribution of Mangrove Forests of the World Using Earth Observation Satellite Data: Status and Distributions of Global Mangroves.” Global Ecology and Biogeography 20 (1): 154–59. https://doi.org/10.1111/j.1466-8238.2010.00584.x.

  • Saltmarsh: Mcowen C, Weatherdon LV, Bochove J, Sullivan E, Blyth S, Zockler C, Stanwell-Smith D, Kingston N, Martin CS, Spalding M, Fletcher S (2017). A global map of saltmarshes. Biodiversity Data Journal 5: e11764. Paper DOI: https://doi.org/10.3897/BDJ.5.e11764; Data URL: http://data.unep-wcmc.org/datasets/43 (v.6)

  • Seagrasses:UNEP-WCMC, Short FT (2018). Global distribution of seagrasses (version 6.0). Sixth update to the data layer used in Green and Short (2003). Cambridge (UK): UN Environment World Conservation Monitoring Centre. URL: http://data.unep-wcmc.org/datasets/7

  • Coral Reefs: UNEP-WCMC, WorldFish Centre, WRI, TNC (2018). Global distribution of warm-water coral reefs, compiled from multiple sources including the Millennium Coral Reef Mapping Project. Version 4.0. Includes contributions from IMaRS-USF and IRD (2005), IMaRS-USF (2005) and Spalding et al. (2001). Cambridge (UK): UN Environment World Conservation Monitoring Centre. URL: http://data.unep-wcmc.org/datasets/1

  • Kelp: Jorge Assis (submitted for publication)

  • Coldcorals: Freiwald A, Rogers A, Hall-Spencer J, Guinotte JM, Davies AJ, Yesson C, Martin CS, Weatherdon LV (2017). Global distribution of cold-water corals (version 5.0). Fifth update to the dataset in Freiwald et al. (2004) by UNEP-WCMC, in collaboration with Andre Freiwald and John Guinotte. Cambridge (UK): UN Environment World Conservation Monitoring Centre. URL: http://data.unep-wcmc.org/datasets/3

  • Sills-Rift Valleys: Harris, P. T., Macmillan-Lawler, M., Rupp, J., & Baker, E. K. (2014). Geomorphology of the oceans. Marine Geology, 352, 4–24. https://doi.org/10.1016/j.margeo.2014.01.011 Hydrothermal vents: Beaulieu, S.E., Szafranski, K. (2018) InterRidge Global Database of Active Submarine Hydrothermal Vent Fields, Version 3.4. World Wide Web electronic publication available from http://vents-data.interridge.org Accessed 2019-02-20.

Contacts

LS0tDQp0aXRsZTogJ0JQLTEwOkdIU0Ygc3VwcGxlbWVudGFyeSBhbmFseXNpcycNCmF1dGhvcjogIkZhYmlvIEZhdm9yZXR0bywgSm95IEt1bWFnYWksIE9jdGF2aW8gQWJ1cnRvLCBBbGV4IFJvZ2VycyINCmRhdGU6ICJMYXN0IHVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KPHN0eWxlPg0KYm9keSB7DQp0ZXh0LWFsaWduOiBqdXN0aWZ5fQ0KPC9zdHlsZT4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQoJZWNobyA9IFRSVUUsDQoJbWVzc2FnZSA9IEZBTFNFLA0KCXdhcm5pbmcgPSBGQUxTRQ0KKQ0KYGBgDQoNCg0KIyBQcmVtaXNlDQoNClRoaXMgLmh0bWwgZG9jdW1lbnQgcmVmZXJzIHRvIGEgZnVsbHkgcmVwcm9kdWNpYmxlIFIgbWFya2Rvd24gZG9jdW1lbnQgdGhhdCBjYW4gYmUgZG93bmxvYWRlZCBmb3JtIHRoZSBgY29kZWAgYnV0dG9uIGluIHRoZSB0b3AtcmlnaHQgb2YgdGhpcyBkb2N1bWVudC4gVGhlIHNvdXJjZSBmaWxlIGlzIGFsc28gYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vRmFiYmlvbG9naWEvQmx1ZS1QYXBlci0xMC9ibG9iL21hc3Rlci9TdXBwbGVtZW50YXJ5X0FfR2xvYmFsX0hhYml0YXRfU3RyYXRlZ3lfRnJhbWV3b3JrLlJtZCkNCg0KDQpUaGlzIGZpbGUgdGhhdCBjYW4gYmUgdXNlZCB0byBnbyB0aHJvdWdoIHRoZSBhbmFseXNpcyBhc3NvY2lhdGVkIHdpdGggdGhlIEJsdWUgUGFwZXIgMTA6ICoqIkEgR2xvYmFsIEhhYml0YXQgU3RyYXRlZ3kgRnJhbWV3b3JrIioqIHNlY3Rpb24uIA0KDQpJbiB0aGlzIHN1cHBsZW1lbnRhcnksIHdlIGFzc3VtZSBzb21lIGJhc2ljIGtub3dsZWRnZSBvZiB0aGUgUiBwcm9ncmFtbWluZyBsYW5ndWFnZS4gSWYgaW5zdHJ1Y3Rpb24gYXJlIGZvbGxvd2VkLCB0aGlzIHNob3VsZCBiZSBmdWxseSByZXByb2R1Y2libGUgdXNpbmcgUiBzdHVkaW8uIEZvciBmdXJ0aGVyIGNvbW1lbnRzIG9uIHRoZSByZXN1bHRzIG9mIHRoaXMgYW5hbHlzaXMgcGxlYXNlIHJlZmVyIHRvIHRoZSBtYWluIHBhcGVyLCBvciBwbGVhc2UgZmluZCBbQ29udGFjdHNdW0NvbnRhY3RzXSB0byBnZXQgaW4gdG91Y2ggd2l0aCB0aGUgYXV0aG9ycyB0byByZXBvcnQgY29tbWVudHMsIGJ1Z3Mgb3IgcHJvYmxlbXMuIA0KDQotIFRvIGRvd25sb2FkIGFuZCBpbnN0YWxsIFI6IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2Jpbi93aW5kb3dzL2Jhc2UvDQotIFRvIGRvd25sb2FkIGFuZCBpbnN0YWxsIFItc3R1ZGlvOiBodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLw0KDQpPbmNlIFIgYW5kIFItc3R1ZGlvIGFyZSBpbnN0YWxsZWQgb24gdGhlIHN5c3RlbSwgeW91IGNhbiBkb3dubG9hZCB0aGUgZnVsbCBzdXBwbGVtZW50YXJ5IGRhdGEgZnJvbTogaHR0cHM6Ly9naXRodWIuY29tL0ZhYmJpb2xvZ2lhL0JsdWUtUGFwZXItMTAuZ2l0IA0KDQpCZXdhcmUgdGhhdCB3aGlsZSBhbGwgZGF0YSB1c2VkIGFyZSBvcGVuIHNvdXJjZSwgc3BlY2lmaWMgcGVybWlzc2lvbiB0byByZXVzZSBhbmQgcHVibGlzaCB0aGVtIGFyZSBuZWVkZWQgZnJvbSBkYXRhIHByb3ZpZGVycy4gQ3JlZGl0IGZvciB0aGUgdXNlIG9mIHRob3NlIGRhdGEgc2hvdWxkIGFsc28gZ28gdG8gdGhlIHByb3BlciBzb3VyY2UgbGlzdGVkIGluIHRoZSB0YWJsZSBpbiB0aGUgW01ldGhvZHMgaW4gYnJpZWZdW01ldGhvZHMgaW4gYnJpZWZdIHNlY3Rpb24uIA0KDQpUaGUgUiBjb2RlICh2LjMuNi4wKSB3YXMgd3JpdHRlbiB1c2luZyBSLXN0dWRpbyBJREUgKHYuMS4yLjE1MTEpLCBhcyB3ZWxsIGFzIHRoaXMgZG9jdW1lbnQsIHVzaW5nIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgdGhhdCBjYW4gYmUgaW5zdGFsbGVkIGluIFIgb3IgdGhyb3VnaCBSLXN0dWRpbyB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOiANCg0KLSBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIilgDQotIGBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKWANCi0gYGluc3RhbGwucGFja2FnZXMoInhsc3giKWANCi0gYGluc3RhbGwucGFja2FnZXMoY2x1c3RlcilgDQotIGBpbnN0YWxsLnBhY2thZ2VzKGZhY3RvZXh0cmEpYA0KLSBgaW5zdGFsbC5wYWNrYWdlcyh2ZWdhbilgDQotIGBpbnN0YWxsLnBhY2thZ2VzKHNjYWxlcylgDQotIGBpbnN0YWxsLnBhY2thZ2VzKERUKWANCi0gYGluc3RhbGwucGFja2FnZXMoY293cGxvdClgDQotIGBpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiKWANCg0KdGhlIHBhY2thZ2UgYGNsVmFsaWRgIGF2YWlsYWJsZSB0byBkb3dubG9hZCBhdCBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9zcmMvY29udHJpYi9jbFZhbGlkXzAuNi02LnRhci5neg0KYW5kIG5lZWRzIHRvIGJlIGluc3RhbGxlZCBmcm9tIHNvdXJjZTogYGluc3RhbGwucGFja2FnZXMocGF0aF90b19maWxlLCByZXBvcyA9IE5VTEwsIHR5cGU9InNvdXJjZSIpYA0KDQoNCkFmdGVyIGFsbCB0aGUgcGFja2FnZXMgYXJlIGluc3RhbGxlZCB0aGV5IGNhbiBiZSBsb2FkZWQgaW4gUiB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOiANCmBgYHtyfQ0KIyMgbG9hZGluZyBuZWVkZWQgbGlicmFyaWVzIGFmdGVyIGFyZSBpbnN0YWxsZWQNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHhsc3gpDQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KHZlZ2FuKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShjbFZhbGlkKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShnZ3B1YnIpDQpgYGANCg0KLS0tDQoNCiMgTWV0aG9kcyBpbiBicmllZg0KDQojIyBIYWJpdGF0IGRhdGENCg0KVG8gdW5kZXJzdGFuZCBoYWJpdGF0IGFyZWEgd2l0aGluIGVhY2ggRXhjbHVzaXZlIEVjb25vbWljIFpvbmUgKEVFWikgYW5kIHRoZSBwcm9wb3J0aW9uIG9mIGVhY2ggaGFiaXRhdCB0aGF0IGlzIHdpdGhpbiBhbiBNUEEsIHdlIGNvbGxlY3RlZCBzcGF0aWFsbHkgcmVmZXJlbmNlZCBoYWJpdGF0IGRhdGEgZm9yIGNvYXN0YWwgYW5kIG9jZWFuaWMgZWNvc3lzdGVtcyBiZXR3ZWVuIEZlYnJ1YXJ5IGFuZCBBcHJpbCAyMDE5LiBEYXRhIGZvciB0aGlydHkgZGlmZmVyZW50IGhhYml0YXRzIHdlcmUgZm91bmQgc3Bhbm5pbmcgb2NlYW5pYyBmZWF0dXJlcyBkaWdpdGl6ZWQgZnJvbSBiYXRoeW1ldHJpYyBkYXRhIHRvIHNhdGVsbGl0ZSBkZXJpdmVkIGFyZWEgZXN0aW1hdGVzIGZvciBtYW55IG9mIHRoZSBjb2FzdGFsIGhhYml0YXRzLiBXZSB1c2VkIGEgZGF0YXNldCB0aGF0IGNvbWJpbmVkIGJvdGggRUVaIGFyZWEgYW5kIGxhbmQgdGhhdCB3YXMgY3JlYXRlZCBieSBTYWxhIGV0IGFsLiwgKDIwMTgpIGFuZCBpbnRlcnNlY3RlZCBpdCB3aXRoIGVhY2ggaGFiaXRhdCB1c2luZyBBcmNHSVMgMTAuNSBEZXNrdG9wLiANCg0KTmV4dCB3ZSBjYWxjdWxhdGVkIHRoZSBhcmVhIGZvciBlYWNoIGhhYml0YXQgYnkgZGlzc29sdmluZyB0aGUgcmVzdWx0aW5nIGxheWVyIGJ5IENvdW50cnkgYW5kIHByb2plY3RpbmcgaXQgaW50byB0aGUgV29ybGQgQ3lsaW5kcmljYWwgRXF1YWwgQXJlYSBwcm9qZWN0aW9uLCBhbmQgdXNpbmcgdGhlICJDYWxjdWxhdGUgR2VvbWV0cnkiIHRvb2wuIFRvIGV4dHJhY3QgaGFiaXRhdHMgdGhhdCBhcmUgb2YgaGlnaCBjb25zZXJ2YXRpb24gaW1wb3J0YW5jZSBhbmQgdGhlIGZvY3VzIG9mIG91ciBhbmFseXNpcyB3ZSBzZWxlY3RlZCBoYWJpdGF0cyB0aGF0IGNvbXByaXNlZCBvZiBsZXNzIHRoYW4gMC41JSBvZiB0aGUgY3VtdWxhdGl2ZSBoYWJpdGF0IGFyZWEuIFNlYW1vdW50cyBkaWQgbm90IGZhbGwgdW5kZXIgdGhpcyBjbGFzc2lmaWNhdGlvbiwgYnV0IGR1ZSB0byB0aGVpciBlY29sb2dpY2FsIGltcG9ydGFuY2Ugd2VyZSBjb21iaW5lZCB3aXRoIGd1eW90cyBpbiBvdXIgYW5hbHlzaXMgKFJvZ2VycyAxOTk0KS4gV2UgdXNlZCA2IGhhYml0YXRzIGNsb3NlbHkgYXNzb2NpYXRlZCB3aXRoIHRoZSBjb2FzdCBhbmQgNiBtb3JlIGNsb3NlbHkgYXNzb2NpYXRlZCB3aXRoIG9wZW4gb2NlYW4sIGZvciBhIHRvdGFsIG9mIDEyIGhhYml0YXRzLiBUaGUgdGFibGUgYmVsb3cgbGlzdHMgdGhlc2UgaGFiaXRhdHMgYW5kIGRhdGFzb3VyY2UgdXNlZCBpbiB0aGUgYW5hbHlzaXMuIExvc3NlcyBhbmQgZ2FpbnMgcGFzdCB0aGUgZGF0ZXMgdGhlIGRhdGEgZGVzY3JpYmVzIHdlcmUgbm90IGFjY291bnRlZCBmb3IuIA0KDQpUaGUgRmVicnVhcnkgMjAxOSBXb3JsZCBEYXRhYmFzZSBvZiBQcm90ZWN0ZWQgQXJlYXMgKFVORVAtV0NNQyBhbmQgSVVDTiAoMjAxOSkgd2FzIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBhcmVhIG9yIHRoZSBudW1iZXIgb2YgcmVwb3J0ZWQgbG9jYXRpb25zIG9mIGVhY2ggaGFiaXRhdCBpbnNpZGUgb2YgYW4gTVBBLiBJdCBuZWVkcyB0byBiZSBjbGFyaWZpZWQgdGhhdCBiZWluZyBpbnNpZGUgYSBNUEEgZG9lcyBub3QgbWVhbiB0aGUgaGFiaXRhdCBpcyBwcm90ZWN0ZWQsIHNpbmNlIHRoZSBNUEEgb2JqZWN0aXZlIGFuZCByZWd1bGFtZW50YXRpb24gbWlnaHQgbm90IGludm9sdmUgdGhlIGhhYml0YXQgYXQgYWxsLiBIb3dldmVyLCB3ZSBjb25zaWRlciB0aGF0IGJlaW5nIGluc2lkZSBhbiBlbnZpcm9ubWVudGFsbHkgbWFuYWdlZCBhcmVhIHNob3VsZCBwcm92aWRlIGF0IGxlYXN0IHNvbWUgaW5kaXJlY3QgYmVuZWZpdHMgdG8gdGhlIGhhYml0YXQgY29uc2VydmF0aW9uLg0KDQpUaGUgZGF0YXNldCB3YXMgZmlsdGVyZWQgYnkgTVBBcyB3aG9zZSBzdGF0dXMgd2FzIGVpdGhlciBkZXNpZ25hdGVkLCBpbnNjcmliZWQsIGFkb3B0ZWQgb3IgZXN0YWJsaXNoZWQsIHRodXMgcmVtb3Zpbmcgbm90IHJlcG9ydGVkIGFuZCBwcm9wb3NlZCBjYXRlZ29yaWVzLiBUaGUgc2FtZSBtZXRob2RvbG9neSBmb3IgdGhlIGFyZWEgY2FsY3VsYXRpb24gd2l0aGluIGVhY2ggRUVaIGV4Y2VwdCB0aGUgaW50ZXJzZWN0aW9uIGluY2x1ZGVkIHRoZSBmaWx0ZXJlZCBNUEEgZGF0YXNldC4gVGhlIHJlc3VsdHMgd2VyZSB0aGVuIGNvbXBpbGVkIGFuZCBjb21wYXJlZCB0byBlbnN1cmUgdGhhdCB0aGUgbWF4aW11bSB2YWx1ZSBvZiBwZXJjZW50IHByb3RlY3Rpb24gd2FzIDEgZm9yIGVhY2ggY291bnRyeS4gDQoNCnwgSGFiaXRhdCB8ICBEYXRlIG9mIERhdGEgfCBEYXRhIFR5cGUgfCBTb3VyY2UgfA0KfCA6LS0tICAgIHwgICAgIDotLS0tOiAgICB8ICAgOi0tLS06ICB8ICAgLS0tOiB8DQp8IEVzdHVhcmllcyB8IDIwMDMgfCBQb2x5Z29uIHwgQWxkZXIgKDIwMDMpIHwNCnwgTWFuZ3JvdmVzCXwgMTk5NyAtIDIwMDAgfCBQb2x5Z29uIHwgR2lyaSwgZXQgYWwuICgyMDExKSB8IA0KfCBTYWx0bWFyc2gJfCAxOTczIC0gMjAxNSB8IFBvaW50cyB8IE1jT3dlbiwgZXQgYWwuICgyMDE3KSB8DQp8IFNlYWdyYXNzZXMgfCAxOTM0IC0gMjAxNSB8IFBvbHlnb24gfCBVTkVQLVdDTUMsIFNob3J0IEZUICgyMDE3KSB8DQp8IENvcmFsIFJlZWZzIHwgMTk1NCAtIDIwMTggfAlQb2x5Z29uIHwgVU5FUC1XQ01DLCBXb3JsZEZpc2ggQ2VudHJlLCBXUkksIFROQyAoMjAxOCkgfA0KfCBLZWxwIHwgTkEJfCBQb2ludCB8IEpvcmdlIEFzc2lzIChzdWJtaXR0ZWQgZm9yIHB1YmxpY2F0aW9uKSB8DQp8IENvbGQgQ29yYWxzIHwgMTkxNSAtIDIwMTQgfAlQb2ludAl8IEZyZWl3YWxkIEEgKDIwMTcpIHwNCnwgU2lsbHMgfCAxOTUwLTIwMDkgfCBQb2x5Z29uIHwgSGFycmlzIGV0IGFsLiAoMjAxNCkgfA0KfCBTZWFtb3VudHMvR3V5b3RzIHwgMTk1MC0yMDA5IHwgUG9seWdvbiB8IEhhcnJpcyBldCBhbC4gKDIwMTQpIHwNCnwgQnJpZGdlcyB8IDE5NTAtMjAwOSB8IFBvbHlnb24gfCBIYXJyaXMgZXQgYWwuICgyMDE0KSB8DQp8IFJpZnQgVmFsbGV5cyB8IDE5NTAtMjAwOSB8IFBvbHlnb24gfCBIYXJyaXMgZXQgYWwuICgyMDE0KSB8DQp8IEh5ZHJvdGhlcm1hbCBWZW50cyB8IDE5OTQtMjAxOSB8IFBvaW50IHwgQmVhdWxpZXUsIFMuRS4sIFN6YWZyYW5za2ksIEsuICgyMDE5KSB8DQoNCg0KDQojIyBUaGUgR2xvYmFsIEhhYml0YXQgU3RyYXRlZ3kgRnJhbWV3b3JrDQoNCkluIHRoaXMgc2VjdGlvbiwgd2UgYXBwbGllZCBhIGdsb2JhbCBtYXJrZXRpbmcgc3RyYXRlZ3kgdG8gY29uc2VydmF0aW9uLiBUaGlzIHN0cmF0ZWd5IGlzIGJhc2VkIG9uIHRoZSBzZWdtZW50YXRpb24gb2YgYSBjb21wbGV4IGdyb3VwIG9mIG9iamVjdHMgY2hhcmFjdGVyaXplZCBieSBzb21lIGF0dHJpYnV0ZXMuIEhlcmUsIG9iamVjdHMgYXJlIGNvdW50cmllcycgRXhjbHVzaXZlIEVjb25vbWljIFpvbmVzIChFRVpzKSwgYW5kIHRoZSBhdHRyaWJ1dGVzIGFyZSByZXBvcnRlZCBpbiB0aGUgZm9sbG93aW5nIHRhYmxlOiANCg0KISBDYXRlZ29yaWVzIHwgQXR0cmlidXRlcyAgICAgIHwgQ29kZWQgYXMgfCBEZXNjcmlwdGlvbiB8IFNvdXJjZSAgICAgfA0KfCA6LS0tICAgICAgIHwgOi0tLSAgICAgICAgfCAgICA6LS0tLTogICB8IDotLS0tOiB8ICAgICAgICAgLS0tOiB8DQp8IFBvcHVsYXRpb24gfCBQb3B1bGF0aW9uIGdyb3d0aCAgICAgIHwgIHBvcF9ncm93dGggICAgICB8IFBvcHVsYXRpb24gZ3Jvd3RoIChhbm51YWwgJSkgZGVyaXZlZCBmcm9tIHRvdGFsIHBvcHVsYXRpb24uIHwgV29ybGQgQmFuayBPcGVuIGRhdGE6IGh0dHBzOi8vZGF0YS53b3JsZGJhbmsub3JnLyB8DQohIFBvcHVsYXRpb24gfCBQb3B1bGF0aW9uIGxpdmluZyBsZXNzIHRoYW4gNW0gYWJvdmUgc2VhIGxldmVsICAgfCAgIHBvcF9jb2FzdGFsICAgICAgfCBQb3B1bGF0aW9uIGxpdmluZyBpbiBhcmVhcyB3aGVyZSBlbGV2YXRpb24gaXMgYmVsb3cgNSBtZXRlcnMgKCUgb2YgdG90YWwgcG9wdWxhdGlvbikgfCAgIFdvcmxkIEJhbmsgT3BlbiBkYXRhOiBodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy8gIHwNCnwgUG9wdWxhdGlvbiB8IFBvdmVydHkgfCBwb3ZlcnR5IHwgUGVyY2VudCBvZiBwb3B1bGF0aW9uIGxpdmluZyB1bmRlciAkMS45MCB8IFdvcmxkIEJhbmsgT3BlbiBkYXRhOiBodHRwczovL2RhdGEud29ybGRiYW5rLm9yZyB8DQp8IENvbnN1bXB0aW9uIHwgR3JlZW5ob3VzZSBnYXNzZXMgZW1pc3Npb25zICB8ICBlbWlzc2lvbnMgICB8IFRvdGFsIGdyZWVuaG91c2UgZ2FzIGVtaXNzaW9ucyAoa3Qgb2YgQ08yIGVxdWl2YWxlbnQpIHwgV29ybGQgQmFuayBPcGVuIGRhdGE6IGh0dHBzOi8vZGF0YS53b3JsZGJhbmsub3JnLyAgIHwNCnwgQ29uc3VtcHRpb24gfCBHcm9zcyBEb21lc3RpYyBQcm9kdWN0IHBlciBjYXBpdGEgfCBHRFBfY2FwaXRhIHwgR0RQIHBlciBjYXBpdGEgKGN1cnJlbnQgVVNEKSB8IFdvcmxkIEJhbmsgT3BlbiBkYXRhOiBodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy8gfCANCnwgQ29uc3VtcHRpb24gfCBHcm9zcyBEb21lc3RpYyBQcm9kdWN0IHRvdGFsIHwgR0RQX3RvdCB8IEdEUCBwZXIgY2FwaXRhIChjdXJyZW50IFVTRCkgfCBXb3JsZCBCYW5rIE9wZW4gZGF0YTogaHR0cHM6Ly9kYXRhLndvcmxkYmFuay5vcmcvIHwNCnwgRW52aXJvbm1lbnQgfCBQcmVzc3VyZXMgb24gdGhlIE1hcmluZSBFbnZpcm9ubWVudCB8IHByZXNzdXJlcyB8IFRoZSBlY29sb2dpY2FsIGFuZCBzb2NpYWwgZmFjdG9ycyB0aGF0IGRlY3JlYXNlIGhlYWx0aCBzdGF0dXMgfCBPY2VhbiBIZWFsdGggSW5kZXg6IGh0dHA6Ly93d3cub2NlYW5oZWFsdGhpbmRleC5vcmcvIHwgDQp8IEVudmlyb25tZW50IHwgQW1vdW50IG9mIGFyZWEgcHJvdGVjdGVkIHwgcHJvdGVjdGVkIHwgT3ZlcmxhcCBiZXR3ZWVuIHByb3RlY3RlZCBhcmVhIGFuZCB0aGUgdGFyZ2V0IGhhYml0YXQgfCBUaGlzIHBhcGVyIHwNCnwgR292ZXJubWVudCB8IENvcnJ1cHRpb24gfCBjb3JydXB0aW9uIHwgQ29ycnVwdGlvbiBzY29yZSBpbmRleCBmb3IgZWFjaCBjb3VudHJ5IHwgV29ybGR3aWRlIGdvdmVybmFuY2UgaW5kaWNhdG9yczogaHR0cHM6Ly9kYXRhY2F0YWxvZy53b3JsZGJhbmsub3JnIHwNCnwgR292ZXJubWVudCB8IFJlZ3VsYXRvcnkgcXVhbGl0eSB8IHJlZ19xdWFsIHwgUmVndWxhdG9yeSBRdWFsaXR5IGNhcHR1cmVzIHBlcmNlcHRpb25zIG9mIHRoZSBhYmlsaXR5IG9mIHRoZSBnb3Zlcm5tZW50IHRvIGZvcm11bGF0ZSBhbmQgaW1wbGVtZW50IHNvdW5kIHBvbGljaWVzIGFuZCByZWd1bGF0aW9ucyB8IFdvcmxkd2lkZSBnb3Zlcm5hbmNlIGluZGljYXRvcnM6IGh0dHBzOi8vZGF0YWNhdGFsb2cud29ybGRiYW5rLm9yZyB8DQp8IEdvdmVybm1lbnQgfCBJbnRlcm5hdGlvbmFsIGNvb3BlcmF0aW9uIHwgaW50X2Nvb3AgfCBOdW1iZXIgb2YgaW50ZXJuYXRpb25hbCB0cmVhdGllcyBzaWduZWQgYnkgYSBjb3VudHJ5IHwgVGhpcyBwYXBlciB8DQoNCi0tLQ0KDQoNCldlIHVzZWQgdGhlIGF0dHJpYnV0ZXMgaW4gdGhlIHRhYmxlIGFib3ZlIHRvIGNyZWF0ZSBhbiBlY29zb2NpYWwgZGF0YXNldCB0aGF0IGNhbiBiZSByZXByb2R1Y2VkIHVzaW5nIHRoZSBjb2RlIGJlbG93IGluIHRoZSBbRGF0YSB3cmFuZ2xpbmddW0RhdGEgd3JhbmdsaW5nXSBzZWN0aW9uLiANCg0KVGhlIHNlZ21lbnRhdGlvbiBjYW4gYmUgZnVsbHkgcmVwcm9kdWNlZCBhbmQgaXMgZnVydGhlciBjb21tZW50ZWQgaW4gdGhlIFtTZWdtZW50YXRpb24gcHJvY2Vzc11bU2VnbWVudGF0aW9uIHByb2Nlc3NdIHNlY3Rpb24uIA0KDQpUbyBhY2hpZXZlIHRoZSBzZWdtZW50YXRpb24gd2UgdXNlZCBhbiB1bnN1cGVydmlzZWQgY2x1c3RlcmluZyB0ZWNobmlxdWUuIFRoZXNlIGFuYWx5c2lzIGFyZSBkZXNpZ25lZCB0byBjYXRlZ29yaXNlIG9ic2VydmF0aW9ucyBpbnRvIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBncm91cHMgKCJjbHVzdGVycyIpLCB3aXRoIGVhY2ggYmVpbmcgcmVsYXRpdmVseSBzaW1pbGFyIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyBmb3IgYSByYW5nZSBvZiBkaWZmZXJlbnQgZmFjdG9ycy4gDQoNCkZpcnN0IHdlIHNjYWxlZCBvdXIgYXR0cmlidXRlcywgdGhlbiB3ZSBjYWxjdWxhdGVkIGFuIGV1Y2xpZGVhbiBkaXN0YW5jZSBtYXRyaXguIFRoZW4gd2UgdXNlZCB0aGUgV2FyZCdzIGhpZXJhcmNoaWNhbCBjbHVzdGVyLCB0aGF0IG1pbmltaXNlIHRoZSB0b3RhbCB2YXJpYW5jZSBiZXR3ZWVuIHNhbXBsZXMgd2l0aGluIGVhY2ggY2x1c3Rlci4gVGhlbiBtZXJnZSBjbHVzdGVycyBzdWNjZXNzaXZlbHkgc28gYXMgdG8gbWluaW1pc2UgdGhlIGluY3JlYXNlIGluIHRoZSBXYXJkJ3MgZGlzdGFuY2UuIFRoZSBwcm9jZXNzIGNvbnRpbnVlcyB1bnRpbCB0aGVyZSdzIGp1c3Qgb25lIGNsdXN0ZXIgY29udGFpbmluZyBhbGwgdGhlIG9ic2VydmF0aW9ucy4gDQoNClNpbmNlIHdlIGhhZCBubyAqYSBwcmlvcmkqIGh5cG90aGVzaXMgaW4gb24gaG93IG1hbnkgc2VnbWVudHMgKGNsdXN0ZXJzKSBjb3VudHJpZXMgc2hvdWxkIGJlIHNlcGFyYXRlZCBpbiwgdGhlIGFkdmFudGFnZSBvZiB0aGUgdW5zdXBlcnZpc2VkIHRlY2huaXF1ZSBpcyB0aGF0IGl0IGlzIGluZGVwZW5kZW50IHRvIGEgcHJldmlvdXMgY2hvaWNlIG9mIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIFRoZXJlZm9yZSwgdGhlIGNob2ljZSBvZiBob3cgbWFueSBzZWdtZW50cyBjb3VudHJpZXMgd2lsbCBiZSBzZXBhcmF0ZWQgY2FuIGJlIGJhc2VkIHVwb24gdGhlIHVzYWJpbGl0eSBhbmQgdXNlZnVsbmVzcyBvZiB0aGUgY29ycmVzcG9uZGluZyBncm91cGluZ3MuIA0KDQpOZXZlcnRoZWxlc3MsIHdlIHRlc3RlZCB3aXRoIGRpZmZlcmVudCBjbHVzdGVyIHZhbGlkYXRpb24gdGVjaG5pcXVlcywgd2hpY2ggbWV0aG9kIGFuZCBudW1iZXIgb2YgY2x1c3RlcnMgYmV0dGVyIHJlcHJlc2VudCBkYXRhLiBUaGlzIHByb2Nlc3MgY2FuIGJlIHJlcHJvZHVjZWQgaW4gdGhlIFtDbHVzdGVyIHZhbGlkYXRpb25dW0NsdXN0ZXIgdmFsaWRhdGlvbl0gc2VjdGlvbi4gDQoNClRoZW4sIHdlIGdyb3VwZWQgY291bnRyaWVzIGFjY29yZGluZyB0byB0aGVpciBzZWdtZW50cyBhbmQgY2FsY3VsYXRlZCB0aGUgYXZlcmFnZSBzY29yZSBmb3IgZWFjaCBhdHRyaWJ1dGUgYW5kIHBsb3R0ZWQgaXQgaW4gYSBiYXItcGxvdCB0aGF0IGFyZSByZXByZXNlbnRlZCBpbiBGaWd1cmUgMSBhbmQgMiBpbiBtYWluIHRleHQuIFRoZXNlIGNhbiBiZSBmdWxseSByZXByb2R1Y2VkIGluIHRoZSBbUmVzdWx0cyB2aXN1YWxpemF0aW9uXVtSZXN1bHRzIFZpc3VhbGl6YXRpb25dIHNlY3Rpb24uIA0KDQoNCiMgRGF0YSB3cmFuZ2xpbmcNCg0KIyMgRmlsdGVyaW5nIGRhdGENCg0KVHdvIGZpbHRlcnMgd2VyZSBjcmVhdGVkIGZvciBsYW5kbG9ja2VkIGNvdW50cmllcyB0aGF0IGR1ZSB0byBhbiBlcnJvciBpbiBlZXogbGF5ZXIgd2VyZSBpbmNsdWRlZCBhbmQgZGlzcHV0ZWQgYXJlYXMgYWxzbyB3ZXJlIGZpbHRlcmVkIG91dC4NCg0KDQpgYGB7cn0NCmxhbmRsb2NrZWQgPC0gYygiQWZnaGFuaXN0YW4iLCAiQW5kb3JyYSIsICJBcm1lbmlhIiwgIkF1c3RyaWEiLCANCiAgICAgICAgICAgICAgICAiQXplcmJhaWphbiIsICJCZWxhcnVzIiwgIkJodXRhbiIsICJCb2xpdmlhIiwgDQogICAgICAgICAgICAgICAgIkJvdHN3YW5hIiwgIkJ1cmtpbmEgRmFzbyIsICJCdXJ1bmRpIiwgDQogICAgICAgICAgICAgICAgIkNlbnRyYWwgQWZyaWNhbiBSZXB1YmxpYyIsICJDaGFkIiwgIkN6ZWNoIFJlcHVibGljIiwgDQogICAgICAgICAgICAgICAgIkV0aGlvcGlhIiwgIkh1bmdhcnkiLCAiS2F6YWtoc3RhbiIsICJLb3Nvdm8iLCAiS3lyZ3l6c3RhbiIsIA0KICAgICAgICAgICAgICAgICJMYW9zIiwgIkxlc290aG8iLCAiTGllY2h0ZW5zdGVpbiIsICJMdXhlbWJvdXJnIiwgIk1hY2Vkb25pYSIsIA0KICAgICAgICAgICAgICAgICJNYWxhd2kiLCAiTWFsaSIsICJNb2xkb3ZhIiwgIk1vbmdvbGlhIiwgIk5lcGFsIiwgIk5pZ2VyIiwgIlBhcmFndWF5IiwgDQogICAgICAgICAgICAgICAgIlJ3YW5kYSIsICJTYW4gTWFyaW5vIiwgIlNlcmJpYSIsICJTbG92YWtpYSIsICJTb3V0aCBTdWRhbiIsIA0KICAgICAgICAgICAgICAgICJTd2F6aWxhbmQiLCAiU3dpdHplcmxhbmQiLCAiVGFqaWtpc3RhbiIsICJUdXJrbWVuaXN0YW4iLCAiVWdhbmRhIiwgDQogICAgICAgICAgICAgICAgIlV6YmVraXN0YW4iLCAiV2VzdCBCYW5rIiwgIlZhdGljYW4gQ2l0eSIsICJaYW1iaWEiLCAiWmltYmFid2UiLCANCiAgICAgICAgICAgICAgICAiR2F6YSBTdHJpcCIpDQoNCmRpc3B1dGVkIDwtIGMoIkFyZWEgZW4gY29udHJvdmVyc2lhIChkaXNwdXRlZCAtIFBlcnV2aWFuIHBvaW50IG9mIHZpZXcpIiwgDQogICAgICAgICAgICAgICJBcmVhIG9mIG92ZXJsYXAgQXVzdHJhbGlhL0luZG9uZXNpYSIsDQogICAgICAgICAgICAgICJDb25mbGljdCB6b25lIENoaW5hL0phcGFuL1RhaXdhbiIsIA0KICAgICAgICAgICAgICAiQ29uZmxpY3Qgem9uZSBKYXBhbi9SdXNzaWEiLCANCiAgICAgICAgICAgICAgIkNvbmZsaWN0IHpvbmUgSmFwYW4vU291dGggS29yZWEiLA0KICAgICAgICAgICAgICAiRGlzcHV0ZWQgQmFyYmFkb3MvVHJpbmlkYWQgJiBUb2JhZ28iLCANCiAgICAgICAgICAgICAgIkpvaW50IHJlZ2ltZSBDb2xvbWJpYS9KYW1haWNhIiwgDQogICAgICAgICAgICAgICJKb2ludCByZWdpbWUgSmFwYW4vS29yZWEiLCANCiAgICAgICAgICAgICAgIkpvaW50IHJlZ2ltZSBOaWdlcmlhL1NhbyBUb21lIGFuZCBQcmluY2lwZSIsICANCiAgICAgICAgICAgICAgIkpvaW50IGRldmVsb3BtZW50IGFyZWEgQXVzdHJhbGlhL0Vhc3QgVGltb3IiLA0KICAgICAgICAgICAgICAiUHJvdGVjdGVkIHpvbmUgQXVzdHJhbGlhL1BhcHVhIE5ldyBHdWluZWEiLCANCiAgICAgICAgICAgICAgIkRpc3B1dGVkIEtlbnlhL1NvbWFsaWEiLA0KICAgICAgICAgICAgICAiRGlzcHV0ZWQgV2VzdGVybiBTYWhhcmEvTWF1cml0YW5pYSIpDQpgYGANCg0KDQojIyBMb2FkaW5nIGRhdGEgZmlsZXMNCg0KQWxsIGRhdGEgc291cmNlcyBhcmUgc3RvcmVkIGluIHRoZSBgZGF0YS9zb3VyY2VzL2AgZm9sZGVyLCBzb21lIGhhdmUgYmVlbiBtYW51YWxseSBtb2RpZmllZCBmcm9tIHRoZSBvcmlnaW5hbCBkb3dubG9hZGVkIHZlcnNpb24sIHRoYXQgY2FuIGJlIGNoZWNrZWQgaW4gdGhlIGZvbGRlciBgZGF0YS9zb3VyY2VzL3Jhd2AuDQpUaGUgZmluYWwgZGF0YXNldCB0aGF0IGlzIGdvaW5nIHRvIGJlIHVzZWQgaW4gZnVydGhlciBhbmFseXNpcyBpcyB0aGUgYGRhdGEvQlAtMTAtRGF0YXNldC54bHN4YCBmaWxlLiANCg0KDQojIyMgRUVaIGFuZCBoYWJpdGF0IGRhdGENCg0KYGBge3J9DQojIyMgRUVaLS0tLQ0KZWV6IDwtIHJlYWQuY3N2KCJkYXRhL3NvdXJjZXMvRUVaX21hcmluZV9hcmVhLmNzdiIpICU+JSANCiAgICAgIHNlbGVjdChDb3VudHJ5LCBFRVpfYXJlYT1BcmVhKSU+JSANCiAgICAgIGZpbHRlcighIENvdW50cnkgJWluJSBkaXNwdXRlZCkgJT4lIA0KICAgICAgZmlsdGVyKCEgQ291bnRyeSAlaW4lIGxhbmRsb2NrZWQpDQoNCg0KIyMgSGFiaXRhdCBkYXRhIC0tLS0NCg0KaGFiIDwtIHJlYWRfZXhjZWwoImRhdGEvc291cmNlcy9BcmVhIG9mIEhhYml0YXRzIGJ5IENvdW50cnkueGxzeCIpICU+JSANCiAgICAgIGZpbHRlcighIENvdW50cnkgJWluJSBkaXNwdXRlZCkgJT4lIA0KICAgICAgZmlsdGVyKCEgQ291bnRyeSAlaW4lIGxhbmRsb2NrZWQpDQoNCg0KZWV6X2hhYiA8LSBtZXJnZShlZXosIGhhYiwgYWxsLnggPSBUKSAlPiUgDQogICAgICByZXBsYWNlKGlzLm5hKC4pLCAwKQ0KDQoNCiMjIFNhdmluZyBmaWxlIC0tLS0NCg0Kd3JpdGUueGxzeChlZXpfaGFiLCAiZGF0YS9CUC0xMC1EYXRhc2V0Lnhsc3giLCBzaGVldE5hbWUgPSAiZWV6X2hhYiIsIA0KICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGLCBhcHBlbmQgPSBGQUxTRSkNCg0KYGBgDQoNCiMjIyBFY29zb2NpYWwgZGF0YSANCg0KYGBge3J9DQojIyBXb3JsZCBCYW5rIERhdGEgLS0tLQ0Kc29jaWFsIDwtIHJlYWR4bDo6cmVhZF9leGNlbCgiZGF0YS9zb3VyY2VzL1dvcmxkQmFua0RhdGEueGxzeCIpDQoNCiMjIyBPSEktLS0tDQoNCk9ISSA8LSByZWFkX2V4Y2VsKCJkYXRhL3NvdXJjZXMvT0hJL2hhYl9wcmVzc3VyZXNfb3V0cHV0LnhscyIpICU+JSANCiAgICAgIHNlbGVjdCgtYygiT0JKRUNUSUQiLCAiRlJFUVVFTkNZIikpICU+JSANCiAgICAgIGdyb3VwX2J5KENvdW50cnkpICU+JSANCiAgICAgIGdhdGhlcigieWVhciIsICJwcmVzc3VyZXMiLCBTVU1fdzIwMTI6U1VNX3cyMDE4KSAlPiUgDQogICAgICBzdW1tYXJpc2UocHJlc3N1cmVzX2hhYiA9IG1lYW4ocHJlc3N1cmVzKSkNCg0KT0hJIDwtIE9ISSAlPiUgDQogICAgICBmaWx0ZXIoISBDb3VudHJ5ICVpbiUgZGlzcHV0ZWQpICU+JSANCiAgICAgIGZpbHRlcighIENvdW50cnkgJWluJSBsYW5kbG9ja2VkKQ0KDQoNCiMjIyBXR0kgaW5kaWNhdG9ycyAtLS0tDQoNCldHSSA8LSByZWFkX2V4Y2VsKCJkYXRhL3NvdXJjZXMvV0dJX2VzdGltYXRlLnhsc3giKSAlPiUgDQogICAgICBncm91cF9ieShDb3VudHJ5KSAlPiUgDQogICAgICBzcHJlYWQoSW5kaWNhdG9yLCBXR0kpICU+JSANCiAgICAgIHNlbGVjdChDb3VudHJ5LCBSZWd1bGF0b3J5X3F1YWxpdHksIENvcnJ1cHRpb24pDQoNCg0KIyMjIEludGVybmF0aW9uYSBhZ3JlZW1lbnRzIGVkaXRlZCAtLS0tDQoNCklDUyA8LSByZWFkX2V4Y2VsKCJkYXRhL3NvdXJjZXMvSW50ZXJuYXRpb25hbF9jb29wLnhsc3giKQ0KDQoNCmBgYA0KDQpOb3cgaXQgaXMgcG9zc2libGUgdG8gbWVyZ2UgYWxsIHRoZXNlIGRhdGFzZXRzLiANClRoZSBpbmRlcGVuZGVudCBmaWxlIGxvYWRpbmcgd2FzIGEgY2hvaWNlIG1hZGUgdG8gY2xhcmlmeSB0aGUgcHJvY2VzcyBhbmQgbGluayBkaXJlY3RseSB0byBkYXRhIHNvdXJjZXMgdGhhdCBjYW4gYmUgd29ya2VkIGFuZCBldmFsdWF0ZWQgaW5kZXBlbmRlbnRseS4gDQoNCg0KYGBge3J9DQoNCmRmIDwtIG1lcmdlKGVleiwgT0hJLCBhbGwueCA9IFQpDQpkZiA8LSBtZXJnZShkZiwgV0dJLCBhbGwueCA9IFQpDQpkZiA8LSBtZXJnZShkZiwgc29jaWFsLCBhbGwueD1UKQ0KZGYgPC0gbWVyZ2UoZGYsIElDUywgYWxsLnggPSBUKQ0KDQpkZiA8LSBjYmluZChkZiwgZWV6X2hhYlsxNjpuY29sKGVlel9oYWIpXSkNCm5hbWVzKGRmKQ0KbmFtZXMoZGYpIDwtIGMoIkNvdW50cnkiLCAiRUVaX2FyZWEiLCANCiAgICAgICAgICAgICAgICJwcmVzc3VyZXMiLA0KICAgICAgICAgICAgICAgInJlZ19xdWFsIiwNCiAgICAgICAgICAgICAgICJjb3JydXB0aW9uIiwNCiAgICAgICAgICAgICAgICJwb3BfZ3Jvd3RoIiwNCiAgICAgICAgICAgICAgICJHRFBfY2FwIiwNCiAgICAgICAgICAgICAgICJHRFBfdG90IiwNCiAgICAgICAgICAgICAgICJwb3ZlcnR5IiwNCiAgICAgICAgICAgICAgICJwb3BfY29hc3QiLA0KICAgICAgICAgICAgICAgImVtaXNzaW9ucyIsDQogICAgICAgICAgICAgICAiaW50X2Nvb3AiLA0KICAgICAgICAgICAgICAgIkNvcmFsX1JlZWZzX1BhcmVhIiwNCiAgICAgICAgICAgICAgICJFc3R1YXJpZXNfUGFyZWEiLA0KICAgICAgICAgICAgICAgIk1hbmdyb3Zlc19QYXJlYSIsDQogICAgICAgICAgICAgICAiU2VhZ3Jhc3Nlc19QYXJlYSIsIA0KICAgICAgICAgICAgICAgIktlbHBfUGFyZWEiLCANCiAgICAgICAgICAgICAgICJTYWx0bWFyc2hfUGFyZWEiLA0KICAgICAgICAgICAgICAgIkh5ZHJvdGhlcm1hbF92ZW50c19QYXJlYSIsDQogICAgICAgICAgICAgICAiQ29sZF9Db3JhbF9QYXJlYSIsDQogICAgICAgICAgICAgICAiQnJpZGdlc19QYXJlYSIsIA0KICAgICAgICAgICAgICAgIlNpbGxzX1BhcmVhIiwNCiAgICAgICAgICAgICAgICJSaWZ0X1ZhbGxleXNfUGFyZWEiLA0KICAgICAgICAgICAgICAgIlNlYW1vdW50c19HdXlvdHNfUGFyZWEiKQ0KDQp3cml0ZS54bHN4KGRmLCAiZGF0YS9CUC0xMC1EYXRhc2V0Lnhsc3giLA0KICAgICAgICAgICBzaGVldE5hbWU9ImVjb3NvY2lhbCIsIGFwcGVuZD1UUlVFLCByb3cubmFtZXMgPSBGKQ0KYGBgDQoNCg0KTm90ZSB0aGF0IHRoZSBsYXN0IGNvZGUgYXBwZW5kIGRhdGEgaW50byB0aGUgZXhjZWwgc2hlZXQgImVjb3NvY2lhbCIuIA0KDQotLS0NCg0KIyBTZWdtZW50YXRpb24gcHJvY2Vzcw0KDQpUaGUgc2VnbWVudGF0aW9uIHByb2Nlc3MsIGhlcmUgYXBwbGllZCB0byBjb25zZXJ2YXRpb24sIGlzIGJhc2VkIG9uIGEgbWFya2V0aW5nIHN0cmF0ZWd5IHRvIHNpbXBsaWZ5IHRoZSBjb21wbGV4aXR5IG9mIHRhcmdldCBhdWRpZW5jZSB0byB3aGljaCBhIHByb2R1Y3QgbmVlZCB0byBiZSB0YXJnZXRlZC4gVGhlIHdvcmxkIGlzIHZlcnkgaGV0ZXJvZ2VuZW91cywgdGhlcmVmb3JlIGdsb2JhbCBjb3Jwb3JhdGlvbnMgcmVhbGl6ZWQgdGhhdCB0byBhZGFwdCB0byB0aGlzIGNvbXBsZXhpdHkgdGhleSBuZWVkZWQgdG8gZGl2aWRlIGFuZCBjb25xdWVyIGRpZmZlcmVudCB0eXBlcyBvZiBtYXJrZXRzLiANCg0KSW4gbWFya2V0aW5nLCBzZWdtZW50YXRpb25zIGlzIGEgcHJvY2VzcyB0aGF0IGhlbHBzIG1hcmtldGVycyBuYXJyb3cgdGhlaXIgZm9jdXMgb24gdGhlIG1vc3QgcHJvbWlzaW5nIGdyb3VwcyB3aXRoaW4gdGhlIHVuaXZlcnNlIHRoZXkgYXJlIHRhcmdldGluZy4gVGhlcmUgaXMgbm8gc2luZ2xlIGNvcnJlY3Qgd2F5IHRvIHNlZ21lbnQgYSBtYXJrZXQuIERlZmluaW5nIGEgdGFyZ2V0IGNvbnN1bWVyIGJhc2UgY2FuIGJlIHBlcmZvcm1lZCB1c2luZyBhIHZhcmlldHkgb2Ygc2VnbWVudGF0aW9uIG1ldGhvZHMuIE9uZSBjYW4gYXBwbHkgYSBjb21iaW5hdGlvbiBvZiB0aGVzZSBtZXRob2RzIHRvIHByb3ZpZGUgZ3JlYXRlciBpbnNpZ2h0IHRvIHRoZWlyIHRhcmdldCBtYXJrZXRzLiBDb3Jwb3JhdGlvbnMgdXN1YWxseSBzdGFydCBjcmVhdGluZyBnZW8tY2x1c3RlcnMsIHdoaWNoIGNvbWJpbmUgZ2VvZ3JhcGhpYywgZGVtb2dyYXBoaWMgYW5kIGJlaGF2aW91cmFsIGRhdGEuIA0KDQpJbiB0aGlzIGNvbnRleHQsIGNvbnNlcnZhdGlvbiBtaWdodCBhaW0gYXQgZG9pbmcgdGhlIHNhbWUgdGhpbmcuIEludGVybmF0aW9uYWwgY29uc2VydmF0aW9uIHN0cmF0ZWdpZXMgY2FuIGJlbmVmaXQgZnJvbSB0aGlzIGFwcHJvYWNoIHRvIGNyZWF0ZSBuZXcgc3RyYXRlZ2llcyB0byBhcHBseSB0byB0aGlzIGNvbXBsZXggd29ybGQuIA0KDQpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIHdlIHByZXNlbnQgYW4gZnVsbHkgcmVwcm9kdWNpYmxlIGV4YW1wbGUgb2YgdGhlIHNlZ21lbnRhdGlvbiBwcm9jZXNzIHVzaW5nIHRoZSBkYXRhIGF2YWlsYWJsZS4gDQoNCkluIHRoZSBwcmV2aW91cyBbRGF0YSB3cmFuZ2xpbmddW0RhdGEgd3JhbmdsaW5nXSBzZWN0aW9uIHdlIHNob3dlZCBob3cgdGhlIGRhdGFzZXQgd2FzIGNyZWF0ZWQuIEhlcmUgd2UgZm9jdXMgb24gZGVzY3JpYmluZyB0aGUgcHJvY2VzcyBvZiB0aGUgc2VnbWVudGF0aW9uLiANCg0KLS0tDQoNCkZpcnN0IHdlIGxvYWQgdGhlIGhhYml0YXQgZGF0YSBhbmQgd2UgYXNzaWduIHRoZSBuYW1lIGBkZmAgdGhhdCBzdGFuZCBmb3IgYGRhdGEuZnJhbWVgLg0KDQpgYGB7cn0NCiMgbG9hZGluZyBkYXRhDQpkZiA8LSByZWFkX2V4Y2VsKCJkYXRhL0JQLTEwLURhdGFzZXQueGxzeCIpDQoNCmBgYA0KDQpUaGVuIHdlIHVwbG9hZCB0aGUgZWNvc29jaWFsIGRhdGEgZnJvbSB0aGUgc2FtZSBgeGxzeGAgZmlsZSBidXQgaW4gdGhlIG90aGVyIHNoZWV0IChub3RlIHRoZSBgc2hlZXRgIGFyZ3VtZW50IGluIHRoZSBgcmVhZF9leGNlbGAgZnVuY3Rpb24pLg0KDQpgYGB7cn0NCmVjb3NvY2lhbCA8LSByZWFkX2V4Y2VsKCJkYXRhL0JQLTEwLURhdGFzZXQueGxzeCIsIHNoZWV0ID0gImVjb3NvY2lhbCIpDQpgYGANCg0KRnJvbSB0aGUgb3JpZ2luYWwgZWNvc29jaWFsIGRhdGEgd2Ugd2FudCB0byBjaGFuZ2UgdHdvIHRoaW5ncy4gRmlyc3Qgd2UgaW52ZXJ0IHRoZSBDb3JydXB0aW9uIHZhbHVlLiBJbiB0aGUgb3JpZ2luYWwgZm9ybWF0LCBjb3JydXB0aW9uIGlzIHJlcHJlc2VudGVkIHdpdGggcG9zaXRpdmUgdmFsdWVzIHdoZW4gaXQgaXMgbG93LCB3aGlsZSB3aXRoIG5lZ2F0aXZlIHZhbHVlcyB3aGVuIGl0IGlzIGhpZ2guIFRoaXMgd2FzIGNvdW50ZXItaW50dWl0aXZlIGZvciB0aGUgd2F5IHdlIHdhbnRlZCB0byBpbnRlcnByZXQgZGF0YSwgc28gd2UgbXVsdGlwbGllZCBpdCBgKi0xYC4NCg0KDQpgYGB7cn0NCmVjb3NvY2lhbCRjb3JydXB0aW9uIDwtIGVjb3NvY2lhbCRjb3JydXB0aW9uKi0xICN0aGlzIGludmVydHMgQ29ycnVwdGlvbiB2YWx1ZXMNCg0KYGBgDQoNCg0KIyMjIFJlbGF0aXZlIGhhYml0YXQgaW1wb3J0YW5jZSBpbmRleA0KDQpIZXJlIHdlIGNhbGN1bGF0ZSB0aGUgcmVsYXRpdmUgaW1wb3J0YW5jZSBpbmRleCBmb2xsb3dpbmcgdGhpcyBmb3JtdWxhOg0KDQokXGZyYWN7KGhhYml0YXQtYXJlYS1pbi1hLWNvdW50cnkgLyBUb3RhbC1oYWJpdGF0LWFyZWEpKjEwMH17KENvdW50cnktRUVaLWFyZWEgLyBXb3JsZC1FRVotYXJlYSkqMTAwfSQNCg0KVGhhdCBjYW4gYmUgY2FsY3VsYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZTogDQpgYGB7cn0NCiMjIEFyZWEgc3RhbmRhcmRpemF0aW9uIHggRUVaIC0tLS0NCg0KIyMgZm9ybXVsYSB0byBjYWxjdWxhdGUgcGVyY2VudGFnZQ0KcGVyYyA8LSBmdW5jdGlvbih4KXsNCiAgICh4L3N1bSh4KSkqMTAwDQp9DQoNCmZyYW1ld29yayA8LSBkZiAlPiUgDQogICBzZWxlY3QoQ291bnRyeTpTZWFtb3VudHNfR3V5b3RzKSAlPiUgDQogICBtdXRhdGVfYXQodmFycyhFRVpfYXJlYTpTZWFtb3VudHNfR3V5b3RzKSwgcGVyYykgJT4lICMjIHBlcmNlbnRhZ2UgY29udHJpYnV0aW9uDQogICBtdXRhdGVfYXQodmFycyhNUEE6U2VhbW91bnRzX0d1eW90cyksIGZ1bnMoLi9FRVpfYXJlYSkpICMjIEVFWiBhcmVhIHN0YW5kYXJkaXphdGlvbg0KICAgDQoNCndyaXRlLnhsc3goYXMuZGF0YS5mcmFtZShmcmFtZXdvcmspLCAiZGF0YS9CUC0xMC1mcmFtZXdvcmsueGxzeCIsIHJvdy5uYW1lcyA9IEYpDQoNCmBgYA0KDQotLS0NCg0KIyMgRXhhbXBsZSBkYXRhc2V0DQoNCk5vdyB0aGF0IGFsbCB2YXJpYWJsZXMgbmVlZGVkIGFyZSBpbiBwbGFjZSwgd2UgY2hvb3NlIHRoZSBoYWJpdGF0IHRhcmdldC4gSGVyZSB3ZSBwcmVzZW50IGEgc3R1ZHkgY2FzZSB3aXRoIE1hbmdyb3ZlcyBhbmQgdGhlIGNvZGUgdG8gcmVwcm9kdWNlIEZpZ3VyZSAyIGluIG1haW4gdGV4dCBmb3IgW0NvcmFsIFJlZWZzXVtDb3JhbCBSZWVmc10uIA0KDQpGaXJzdCB3ZSB3YW50IHRvIGV4dHJhY3QgY291bnRyaWVzIHRoYXQgaGF2ZSBtYW5ncm92ZXMsIG1lcmdlIHRoaXMgd2l0aCBlY29zb2NpYWwgdmFyaWFibGVzLCBhbmQgY3JlYXRlIHRoZSBkYXRhc2V0IHdlIHdpbGwgdXNlIGZvciB0aGUgc2VnbWVudGF0aW9uLiAgDQoNCmBgYHtyfQ0KIyMgRGF0YXNldCBjcmVhdGlvbiANCg0KTUcgPC0gZnJhbWV3b3JrICU+JSANCiAgICAgIHNlbGVjdChDb3VudHJ5LCBNYW5ncm92ZXMpICU+JSANCiAgICAgIGFycmFuZ2UoLU1hbmdyb3ZlcykgJT4lIA0KICAgICAgbXV0YXRlKE1hbmdyb3ZlcyA9IHJvdW5kKE1hbmdyb3ZlcywgMikpICU+JQ0KICAgICAgZmlsdGVyKE1hbmdyb3ZlcyA+IDApICU+JSANCiAgICAgIG1lcmdlKC4sIGVjb3NvY2lhbCkgJT4lIA0KICAgICAgc2VsZWN0KENvdW50cnk6aW50X2Nvb3AsIHByb3RlY3RlZD1NYW5ncm92ZXNfUGFyZWEpICU+JSANCiAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgMikNCg0KYGBgDQoNClRoaXMgY3JlYXRlIHRoZSBmb2xsb3dpbmcgZGF0YXNldCwgdGhlIGZpcnN0IGNvbHVtbnMgY2FsbGVkICJNYW5ncm92ZXMiIHJlZmVycyB0byB0aGUgW1JlbGF0aXZlIGhhYml0YXQgaW1wb3J0YW5jZSBpbmRleF1bUmVsYXRpdmUgaGFiaXRhdCBpbXBvcnRhbmNlIGluZGV4XSBjYWxjdWxhdGVkIGZvciB0aGlzIGhhYml0YXQiLiBVc2luZyB0aGlzIGludGVyYWN0aXZlIHRhYmxlIGNvdW50cmllcyBjYW4gYmUgb3JkZXJlZCBieSBpdHMgdmFsdWU6DQoNCkl0IGlzIGltcG9ydGFudCBhbHNvIHRvIGFjY291bnQgZm9yIGNvcnJlbGF0aW9uIGFtb25nIHZhcmlhYmxlcy4gQ29ycmVsYXRlZCB2YXJpYWJsZXMgYXJlIG5vdCBuZWNlc3NhcmlseSBhIHByb2JsZW0gZm9yIGNsdXN0ZXIgYW5hbHlpc2lzLiBIb3dldmVyLCB0aGlzIGNhbiBiZSB1c2VkIHRvIHJlZHVjZSB0aGUgYW1vdW50IG9mIHZhcmlhYmxlcyB1c2VkIGlmIHdhbnRlZC4gDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQojIyMgQ29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMgLS0tLQ0KDQpkZl9jb3JyIDwtIEhtaXNjOjpyY29ycihhcy5tYXRyaXgoc2VsZWN0KE1HLCAtQ291bnRyeSkpKQ0KDQojIEluc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gYXJlIGNyb3NzZWQNCmNvcnJwbG90Ojpjb3JycGxvdC5taXhlZChkZl9jb3JyJHIsIHRsLnBvcyA9ICJsdCIsDQogICAgICAgICBwLm1hdCA9IGRmX2NvcnIkUCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAicGNoIiwgcGNoLmNleCA9IDIsIHBjaC5jb2wgPSAiZ3JleTkwIikNCg0KYGBgDQoNCkhlcmUgc29tZSBvZiB0aGUgZ292ZXJuYW5jZSBkYXRhIGFyZSBjb3JyZWxhdGVkIGFzIGV4cGVjdGVkLiBQcm90ZWN0aW9uIG9mIHRoZSBoYWJpdGF0IGhvd2V2ZXIgaXMgc3Ryb25nbHkgY29ycmVsYXRlZCBvbmx5IHdpdGggRUVaIGFyZWEuIE5vIG90aGVyIGVjb3NvY2lhbCBhdHRyaWJ1dGUgaGFzIGEgc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gd2l0aCBwcm90ZWN0aW9uLiBBIGxvdyBidXQgdW5zaWduaWZpY2FudCBjb3JyZWxhdGlvbiBpcyBmb3VuZCB3aXRoIGludGVybmF0aW9uYWwgY29vcGVyYXRpb25zLCBzdWdnZXN0aW5nIHRoYXQgdGhlIGVmZmVjdHMgb2YgdHJlYXRpZXMgY291bnRyaWVzIHNpZ25lZCBoYWQgc29tZSwgZXZlbiBpZiBsb3csIGVmZmVjdCBvbiB0aGUgYW1vdW50IG9mIHRoZSBoYWJpdGF0IHByb3RlY3RlZC4gDQoNCg0KIyMjIENsdXN0ZXJpbmcNCg0KVG8gc2VnbWVudCBjb3VudHJpZXMsIHdlIHVzZWQgdW5zdXBlcnZpc2VkIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcy4gVGhlc2UgYW5hbHlzaXMgYXJlIGRlc2lnbmVkIHRvIGNhdGVnb3Jpc2Ugb2JzZXJ2YXRpb25zIGludG8gYSBudW1iZXIgb2YgZGlmZmVyZW50IGdyb3VwcyAoImNsdXN0ZXJzIiksIHdpdGggZWFjaCBiZWluZyByZWxhdGl2ZWx5IHNpbWlsYXIgYmFzZWQgb24gdGhlaXIgdmFsdWVzIGZvciBhIHJhbmdlIG9mIGRpZmZlcmVudCBmYWN0b3JzLiBTb21lIGZvcm0gb2YgZGlzdGFuY2UgbWVhc3VyZSBhcmUgdXNlZCB0byBkZXRlcm1pbmUgaG93IGNsb3NlIG9yIGZhciBhcGFydCBhcmUgZGlmZmVyZW50IHNhbXBsZXMgKGNvdW50cmllcywgcmVnaW9ucywgc2l0ZXMgZXRjLikgYXJlIGJhc2VkIG9uIHRoZWlyIGF0dHJpYnV0ZXMuIA0KDQpUaGVyZSBhcmUgbWFueSBmbGF2b3VycyBvZiBjbHVzdGVyaW5nIG1ldGhvZHMgZGVwZW5kaW5nIHVwb24gdGhlIGRpc3RhbmNlIG1lYXN1cmUgY2hvc2VuIGFuZCB3aGljaCBjaGFyYWN0ZXJpc3RpYyBvbmUgd2FudHMgdG8gaGlnaGxpZ2h0IGluIHRoZSBkYXRhLiANCg0KRm9yIGV4YW1wbGUsIFdhcmQncyBkaXN0YW5jZSBtaW5pbWlzZSB0aGUgdG90YWwgdmFyaWFuY2UgYmV0d2VlbiBzYW1wbGVzIHdpdGhpbiBlYWNoIGNsdXN0ZXIuIFRoZW4gbWVyZ2UgY2x1c3RlcnMgc3VjY2Vzc2l2ZWx5IHNvIGFzIHRvIG1pbmltaXNlIHRoZSBpbmNyZWFzZSBpbiB0aGUgV2FyZCdzIGRpc3RhbmNlLiBUaGUgcHJvY2VzcyBjb250aW51ZXMgdW50aWwgdGhlcmUncyBqdXN0IG9uZSBjbHVzdGVyIGNvbnRhaW5pbmcgYWxsIHRoZSBvYnNlcnZhdGlvbnMuIA0KDQpUaGVyZSBpcyBhIHN1YmplY3RpdmUgZWxlbWVudCBpbiBjbHVzdGVyaW5nIHRlY2huaXF1ZXMuIEJ5IGNob29zaW5nIGRpZmZlcmVudCBwcmUtdHJhbnNmb3JtYXRpb25zIG9mIHRoZSBkYXRhLCBkaWZmZXJlbnQgdmFyaWFibGVzLCBkaWZmZXJlbnQgZGlzdGFuY2UgbWF0cmljZXMsIHRoZSByZXN1bHRzIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudC4gSWYgdGhpcyBmcm9tIG9uZSBzaWRlIGNhbiBiZSBzZWVuIGFzIGEgd2Vha25lc3MsIHdoZW4gcHJvcGVybHkgYXBwbGllZCBjYW4gYmUgYSBwb3dlcmZ1bCBleHBsb3JhdGlvbiB0ZWNobmlxdWUgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBtYW55IHNpdHVhdGlvbnMsIHNjYWxlcyBhbmQgcXVlc3Rpb25zIHRvIGJlIGFuc3dlcmVkLiANCkF0IHRoZSBlbmQgb2YgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBvbmUgY2FuIHJldmlldyB0aGUgZGF0YSBhbmQgaWRlbnRpZnkgd2hhdCB0aGUgbWVtYmVycyBvZiBlYWNoIGNsdXN0ZXIgaGF2ZSBpbiBjb21tb24gb3Igbm90IGJ5IHN1bW1hcmlzaW5nIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgZWFjaCBjbHVzdGVyIGFuZCBwb3RlbnRpYWxseSB2aXN1YWxpc2luZyB0aGVzZSBzdW1tYXJpc2VzIGFzIGEgbWVhbnMgb2YgY29tcGFyaW5nIHRoZW0uDQoNCkFub3RoZXIgc291cmNlIG9mIHN1YmplY3Rpdml0eSBpcyB0aGUgY2hvaWNlIG9mIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIFRoZSBudW1iZXIgb2YgY2x1c3RlciAoaykgY2FuIGJlIGRldGVybWluZWQgYnkgdGhlIHVzZXIsIHdpdGggdGhlIGFpZCBvZiBzb21lIHN0YXRpc3RpY2FsIHRlY2huaXF1ZXMgY2FsbGVkICJjbHVzdGVyIHZhbGlkYXRpb25zIi4gSG93ZXZlciwgYXQgdGhlIGVuZCBvZiB0aGUgZGF5LCBkZXBlbmRpbmcgb24gdGhlIHJlc2VhcmNoIHF1ZXN0aW9uLCBhIGdvb2QgZ3JvdXBpbmcgaXMgZGV0ZXJtaW5lZCBiYXNlZCB1cG9uIHRoZSB1c2FiaWxpdHkgYW5kIHVzZWZ1bG5lc3Mgb2YgdGhlIGNvcnJlc3BvbmRpbmcgZ3JvdXBpbmdzLiBPbmNlIHRoZSBzZWdtZW50YXRpb24gaXMgY29tcGxldGUsIGFuZCBmdXJ0aGVyIGtub3dsZWRnZSBpcyBvYnRhaW5lZCwgb3RoZXIgcHJlZGljdGl2ZSBzdGF0aXN0aWNhbCB0ZWNobmlxdWVzIGNhbiBiZSB1c2VkIHRvIHRlc3QgdGhlIHJlc3VsdHMgb2YgdGhlIHN0cmF0ZWd5LiANCg0KIyMjIyBEYXRhIHNjYWxpbmcNCg0KSW4gb3VyIHN0dWR5IGNhc2UsIHRoZSBmaXJzdCB0aGluZyB3ZSBuZWVkIHRvIGRvIGlzIHNjYWxpbmcgdGhlIHZhcmlhYmxlcywgdGhpcyBpcyBkb25lIHRvIGFjY291bnQgZm9yIHRoZSBkaWZmZXJlbnQgdW5pdHMsIGFuZCB0aGUgZGlmZmVyZW50IG9yaWdpbnMgYWxsIHRoZXNlIHZhcmlhYmxlcyBoYXZlLiBJZiBhY2NvdW50ZWQgcmF3LCBzb21lIHZhcmlhYmxlcyB3b3VsZCBoYXZlIG11Y2ggbW9yZSB3ZWlnaHQgdGhhbiBvdGhlcnMgb25seSBiZWNhdXNlIGhhdmUgZGlmZmVyZW50IHNjYWxlIGFuZCBub3QgYmVjYXVzZSBvZiBhIHRydWUgZWZmZWN0IG9mIHRoYXQgcGFydGljdWxhciB2YXJpYWJsZS4gDQoNCmBgYHtyfQ0KIyBTY2FsaW5nDQoNCmRmX3NjbCA8LSBzY2FsZShNR1s0Om5jb2woTUcpXSkgDQojIHRoZSBwYXJ0IGluIHBhcmVudGhlc2lzIGlzIG9ubHkgdG8gZXhjbHVkZSBub24gbnVtZXJpY2FsIGNvbHVtbnMNCg0KDQojIHRoaXMgY3JlYXRlcyBuZXcgcmF3IG5hbWVzIGZvciB0aGUgZGF0YWZyYW1lIGFuZCANCiN0aGUgdmVnYW46Om1ha2VjZXBuYW1lcyBpcyB0byBzaG9ydGVuIHNvbWUgY291bnRyeSBuYW1lcw0Kcm93bmFtZXMoZGZfc2NsKSA8LSBtYWtlLmNlcG5hbWVzKE1HJENvdW50cnkpIA0KDQoNCg0KYGBgDQoNCg0KVGhpcyBpcyBob3cgdGhlIGRhdGFzZXQgbG9va3M6IA0KDQoNCmBgYHtyfQ0KDQpkYXRhdGFibGUoZGZfc2NsKQ0KDQpgYGANClRoZXJlIGFyZSBtYW55IE5BcyBpbiB0aGlzIGRhdGFzZXQuIA0KVGhpcyBpcyBiZWNhdXNlIHdlIGFyZSB1c2luZyBFeGNsdXNpdmUgRWNvbm9taWMgWm9uZXMsIHdoaWxlIG1vc3Qgb2YgdGhlIGF2YWlsYWJsZSBlY29zb2NpYWwgdmFyaWFibGVzIGFyZSBhdCBhIGNvdW50cnkgbGV2ZWwuIEZ1cnRoZXIsIGZvciBzb21lIGNvdW50cmllcyB0aGUgaW5kaWNhdG9ycyBhcmUgc2ltcGx5IG5vdCBhdmFpbGFibGUuIA0KVW5mb3J0dW5hdGVseSwgd2UgbmVlZCB0byBleGNsdWRlIHRob3NlIHJvd3Mgd2l0aCBOQXMgZnJvbSBvdXIgYW5hbHlzaXMuIA0KQ2x1c3RlcmluZyBjYW4gZGVhbCB3aXRoIHNvbWUgTkFzLCBob3dldmVyLCBhIGdvb2QgYW1vdW50IG9mIGNvdW50cmllcyBoYXZlIGEgY29tcGxldGUgZGF0YSBzZXQsIHNvIHdlIGRlY2lkZWQgdG8gZXhjbHVkZSB0aGVtLiANCg0KYGBge3J9DQoNCmRmX3NjbCA8LSBuYS5vbWl0KGRmX3NjbCkgIyMgZXhjbHVkaW5nIE5Bcw0KDQpgYGANCg0KVGhlbiB0aGUgZGlzdGFuY2UgbWV0cmljIGlzIGNhbGN1bGF0ZWQuIA0KQXMgbWVudGlvbmVkIGVhcmxpZXIgdGhlcmUgYXJlIGEgcGxldGhvcmEgb2YgZGlzdGFuY2UgbWV0cmljcyB0aGF0IGNhbiBiZSBhcHBsaWVkLiBUaGVzZSBjYW4gaGlnaGxpZ2h0IHNvbWUgYXNwZWN0IG9mIHRoZSBkYXRhIGFuZCBzaG91bGQgYmUgY2hvc2VuIGRlcGVuZGluZyBvbiB0aGUgaHlwb3RoZXNpcyBhbmQgdGhlIHR5cGVzIG9mIHZhcmlhYmxlcyBhdmFpbGFibGUuIA0KSGVyZSB3ZSBjaG9zZSB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlLiBNYWlubHkgYmVjYXVzZSBpcyB0aGUgZGVmYXVsdCBtZWFzdXJlIGZvciBtYW55IHNvZnR3YXJlLCBhbHNvIGJlY2F1c2UgaXQgaXMgcmVsYXRpdmVseSBzaW1wbGUgdG8gaW50ZXJwcmV0IGFuZCBvYnNlcnZhdGlvbiB3aXRoIGhpZ2ggdmFsdWVzIG9mIHZhcmlhYmxlcyBhcmUgdXN1YWxseSBjbHVzdGVyZWQgdG9nZXRoZXIgYXMgZm9yIG9ic2VydmF0aW9uIHdpdGggbG93IHZhbHVlcy4gVGhpcyBjaGFyYWN0ZXJpc3RpYyBpcyBzb21ldGhpbmcgdGhhdCBtYWtlcyBzZW5zZSB3aGVuIGRlYWxpbmcgd2l0aCBlY29zb2NpYWwgZGF0YSB0byBjcmVhdGUgY291bnRyeSBzZWdtZW50cy4gDQoNClRoZSBmb2xsb3dpbmcgY29kZSBjYWxjdWxhdGUgdGhlIGV1Y2xpZGVhbiBkaXN0YW5jZSBtYXRyaXg6IA0KDQpgYGB7cn0NCmRpc3QuZXVjbCA8LSBkaXN0KGRmX3NjbCwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQoNCmBgYA0KDQoNCiMjIyBDbHVzdGVyIHZhbGlkYXRpb24NCg0KSWYgb25lIGlzIHN0cnVnZ2xpbmcgd2l0aCB0aGUgbWFueSBjbHVzdGVyaW5nIG9wdGlvbnMsIHVzaW5nIHRoZSBgY2xWYWxpZGAgcGFja2FnZSwgaXQgaXMgcG9zc2libGUgdG8gaGF2ZSBhbiBhaWQgdG8gY2hvb3NlIHRoZSBiZXN0IGFsZ29yaXRobSBhbmQgYWxzbyBhIG51bWJlciBvZiBjbHVzdGVyIHN1Z2dlc3Rpb24gZm9yIGVhY2ggbWV0aG9kLiANCg0KVGhpcyBjb21wYXJlcyBjbHVzdGVyaW5nIGFsZ29yaXRobXMgdXNpbmcgdHdvIGNsdXN0ZXIgdmFsaWRhdGlvbiBtZWFzdXJlczogDQoNCjEuICpJbnRlcm5hbCBtZWFzdXJlcyosIHdoaWNoIHVzZXMgaW50cmluc2ljIGluZm9ybWF0aW9uIGluIHRoZSBkYXRhIGxpa2UgY29ubmVjdGl2aXR5LCBzaWxob3VldHRlIGNvZWZmaWNpZW50IGFuZCB0aGUgRHVubiBpbmRleA0KDQp0aGVzZSBhcmUgY2FsY3VsYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZTogDQpgYGB7cn0NCg0KIyB0aGlzIHNlbGVjdHMgdGhlIG1ldGhvZHMgdG8gYmUgdGVzdGVkDQpjbG1ldGhvZHMgPC0gYygiaGllcmFyY2hpY2FsIiwia21lYW5zIiwicGFtIikgDQoNCmludGVybiA8LSBjbFZhbGlkKG5hLm9taXQoZGZfc2NsKSwgbkNsdXN0ID0gMjoxMCwgDQogICAgICAgICAgICAgICAgICBjbE1ldGhvZHMgPSBjbG1ldGhvZHMsIHZhbGlkYXRpb24gPSAiaW50ZXJuYWwiKQ0Kc3VtbWFyeShpbnRlcm4pDQpgYGANCg0KVGhlIHJlc3VsdHMgb2YgdGhlIG9wdGltYWwgc2NvcmVzIHN1Z2dlc3RzIHRoYXQgdGhlIGJlc3QgbWV0aG9kcyBpcyB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXIgd2l0aCAyLTMgZ3JvdXBzLiANCg0KDQoyLiAqU3RhYmlsaXR5IG1lYXN1cmVzKiwgYXJlIGEgc3BlY2lhbCB2ZXJzaW9uIG9mIGludGVybmFsIG1lYXN1cmVzIHdoaWNoIGV2YWx1YXRlIHRoZSBjb25zaXN0ZW5jeSBvZiBhIGNsdXN0ZXJpbmcgcmVzdWx0IGJ5IGNvbXBhcmluZyBpdCB3aXRoIHRoZSBjbHVzdGVycyBvYnRhaW5lZCBhZnRlciBlYWNoIGNvbHVtbiBpcyByZW1vdmVkLCBvbmUgYXQgYSB0aW1lLiANCg0KVGhpcyBtZXRob2QgaW5jbHVkZSB2YXJpb3VzIG1lYXN1cmVzOiANCg0KLSBUaGUgIGF2ZXJhZ2UgIHByb3BvcnRpb24gb2Ygbm9uLW92ZXJsYXAgKipBUE4qKiBtZWFzdXJlcyB0aGUgYXZlcmFnZSBwcm9wb3J0aW9uIG9mIG9ic2VydmF0aW9ucyBub3QgcGxhY2VkIGluIHRoZSBzYW1lIGNsdXN0ZXIgYnkgY2x1c3RlcmluZyBiYXNlZCBvbiB0aGUgZnVsbCBkYXRhIGFuZCBjbHVzdGVyaW5nIGJhc2VkIG9uIHRoZSBkYXRhIHdpdGggYSBzaW5nbGUgY29sdW1uIHJlbW92ZWQuDQotIFRoZSBhdmVyYWdlIGRpc3RhbmNlICoqQUQqKiBtZWFzdXJlcyB0aGUgYXZlcmFnZSBkaXN0YW5jZSBiZXR3ZWVuIG9ic2VydmF0aW9ucyBwbGFjZWQgaW4gdGhlIHNhbWUgY2x1c3RlciB1bmRlciBib3RoIGNhc2VzIChmdWxsIGRhdGEgc2V0IGFuZCByZW1vdmFsIG9mIG9uZSBjb2x1bW4pLg0KLSBUaGUgYXZlcmFnZSBkaXN0YW5jZSBiZXR3ZWVuIG1lYW5zICoqQURNKiogbWVhc3VyZXMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgYmV0d2VlbiBjbHVzdGVyIGNlbnRyZXMgZm9yIG9ic2VydmF0aW9ucyBwbGFjZWQgaW4gdGhlIHNhbWUgY2x1c3RlciB1bmRlciBib3RoIGNhc2VzLg0KLSBUaGUgZmlndXJlIG9mIG1lcml0ICoqRk9NKiogbWVhc3VyZXMgdGhlIGF2ZXJhZ2UgaW50cmEtY2x1c3RlciB2YXJpYW5jZSBvZiB0aGUgZGVsZXRlZCBjb2x1bW4sIHdoZXJlIHRoZSBjbHVzdGVyaW5nIGlzIGJhc2VkIG9uIHRoZSByZW1haW5pbmcgKHVuZGVsZXRlZCkgY29sdW1ucy4NCg0KVGhlc2UgYXJlIGNhbGN1bGF0ZWQgd2l0aCB0aGUgZm9sbG93aW5nIGNvZGU6IA0KDQpgYGB7cn0NCmNsbWV0aG9kcyA8LSBjKCJoaWVyYXJjaGljYWwiLCJrbWVhbnMiLCJwYW0iKQ0Kc3RhYiA8LSBjbFZhbGlkKG5hLm9taXQoZGZfc2NsKSwgbkNsdXN0ID0gMjo2LCBjbE1ldGhvZHMgPSBjbG1ldGhvZHMsIA0KICAgICAgICAgICAgICAgIHZhbGlkYXRpb24gPSAic3RhYmlsaXR5IikNCiMgRGlzcGxheSBvbmx5IG9wdGltYWwgU2NvcmVzDQpvcHRpbWFsU2NvcmVzKHN0YWIpDQoNCmBgYA0KDQpJbiBhIHNpbWlsYXIgd2F5IGFzIGJlZm9yZSB0aGUgc3RhYmlsaXR5IG1lYXN1cmVzIHN1Z2dlc3QgdGhlIHVzZSBvZiBoaWVyYXJjaGljYWwgbWV0aG9kcy4gSG93ZXZlciwgdGhlIGFuYWx5c3QgbWlnaHQgYWxzbyBleHBsb3JlIGEga21lYW5zIG9yIHBhbSBhbGdvcml0aG0gd2l0aCBhIGhpZ2hlciBudW1iZXIgb2YgY2x1c3Rlci4gDQoNCkFnYWluLCB0aGVzZSBzaG91bGQgYmUgdGFrZW4gYXMgYSBndWlkZSB0aGF0IHRoZSBhbmFseXN0IGNhbiBmb2xsb3cgd2l0aCBjb21tb24tc2Vuc2UuIA0KDQoNCkluIG91ciBleGFtcGxlLCB3ZSBjaG9zZSB0byBnbyBhbG9uZyB3aXRoIHRoZSBoaWVyYXJjaGljYWwgY2x1c3RlcnMgYW5kIGluIHBhcnRpY3VsYXIgd2UgdXNlIGEgY29tbW9uIG1ldGhvZCBjYWxsZWQgIldhcmQiIGNsdXN0ZXJpbmcuIA0KVGhpcyBjYW4gYmUgY2FsY3VsYXRlZCBhbmQgdmlzdWFsaXNlZCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZTogDQoNCmBgYHtyIGZpZy53aWR0aD0xMH0NCnJlcy53YXJkIDwtIGhjbHVzdChkaXN0LmV1Y2wsIG1ldGhvZCA9ICJ3YXJkLkQyIikNCg0KZnZpel9kZW5kKHJlcy53YXJkLCBrID0gNCwgIyBDdXQgaW4gZm91ciBrID0gMyBncm91cHMNCiAgICAgICAgICBjZXggPSAwLjgsICAgIyBsYWJlbCBzaXplDQogICAgICAgICAga19jb2xvcnMgPSAiYWFhcyIgICMgY29sb3VyIHBhbGV0dGUNCikNCg0KYGBgDQoNCk5vdGUgdGhlIGBrPTRgIGFyZ3VtZW50IGluIHRoZSBgZnZpc19kZW5kKClgIGZ1bmN0aW9uLiBUaGlzIHJlZmVycyB0byB0aGUgbnVtYmVyIG9mIGNsdXN0ZXIgdGhlIGFuYWx5c3QgY2FuIGRlY2lkZSB0byByZXByZXNlbnQgdGhlIGRhdGEgd2l0aC4gIA0KVGhlIGFkdmFudGFnZSBvZiB0aGUgdW5zdXBlcnZpc2VkIGNsdXN0ZXIsIHJlbGF0aXZlIHRvIHN1cGVydmlzZWQgb25lcyAoZS5nLiBrbWVhbnMpLCBpcyB0aGF0IHRoZSBmaW5hbCBjbHVzdGVyIHRyZWUgcmVzdWx0cyBhcmUgaW5kZXBlbmRlbnQgb24gdGhlIG51bWJlciBvZiBjbHVzdGVyIGNob3Nlbi4gVGhlcmVmb3JlLCBoZXJlIGFzc2lnbmluZyBhIG51bWJlciBvZiBjbHVzdGVycyBjYW4gYmUgZG9uZSBhdCBkaWZmZXJlbnQgbGV2ZWxzLCBkZWNpZGluZyB3aGVyZSB0byAiY3V0IiB0aGUgZGVuZG9ncmFtIHRyZWUuIA0KDQpJZiBvbmUgd2FudHMgdG8gYmUgc3VyZSBhYm91dCBjaG9pY2Ugb2YgY2x1c3RlcnMsIGNhbiB1c2UgdGhlIGZvbGxvd2luZyBtZXRob2RzIHRvIGZ1cnRoZXIgZGVjaWRlIHRoZSAiYmVzdCIgd2F5IHRvIGdyb3VwcyB0aGUgb2JqZWN0czogDQoNCmBgYHtyfQ0KcDEgPC0gZnZpel9uYmNsdXN0KGRmX3NjbCwgaGN1dCwgbWV0aG9kID0gIndzcyIpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGxpbmV0eXBlID0gMikrDQogICAgICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpDQoNCiMgU2lsaG91ZXR0ZSBtZXRob2QNCnAyIDwtIGZ2aXpfbmJjbHVzdChkZl9zY2wsIGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIikrDQogICAgICBsYWJzKHN1YnRpdGxlID0gIlNpbGhvdWV0dGUgbWV0aG9kIikNCg0KIyBHYXAgc3RhdGlzdGljDQoNCnNldC5zZWVkKDEyMykNCnAzIDwtIGZ2aXpfbmJjbHVzdChuYS5vbWl0KGRmX3NjbCksIGhjdXQsIG5zdGFydCA9IDI1LCAgbWV0aG9kID0gImdhcF9zdGF0IiwgbmJvb3QgPSA1MDApKw0KICAgICAgbGFicyhzdWJ0aXRsZSA9ICJHYXAgc3RhdGlzdGljIG1ldGhvZCIpDQoNCnBsb3RfZ3JpZChwMSxwMixwMykNCmBgYA0KDQoqKlRoZXNlIHRlc3RzLCBjb25maXJtIHRoYXQgdGhlIGJlc3QgbnVtYmVyIG9mIGdyb3VwcyBpcyBiZXR3ZWVuIDIgdG8gNC4qKg0KDQojIFJlc3VsdHMgdmlzdWFsaXphdGlvbg0KDQpOb3cgdGhhdCB3ZSBkZWZpbmVkIG91ciBjb3VudHJ5IHNlZ21lbnRzLCB3ZSBjYW4gZXh0cmFjdCB0aGUgdmFsdWVzIGZvciBlYWNoIGNvdW50cnkgdGhhdCBkZWZpbmUgdGhvc2Ugc2VnbWVudHMuDQoNClRvIGRvIHRoYXQgd2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvZGU6DQoNCmBgYHtyfQ0KIyB0aGlzIGNyZWF0ZXMgY2x1c3RlciBjbGFzc2VzIGRhdGEgDQpjbHVzdGVyY2xhc3M8LSBjdXRyZWUocmVzLndhcmQsIGs9NCkgDQojIHRoaXMgY3JlYXRlIGZhY3RvciBsZXZlbHMgYWNjb3JkaW5nIHRvIHRoZSBncm91cHMNCmNsdXN0ZXJjbGFzc19sZXZlbHM8LSBsZXZlbHMoZmFjdG9yKGNsdXN0ZXJjbGFzcykpIA0KIyB0aGlzIHRyYW5mb3JtZSBpdCB0byBhIFIgZGF0YS5mcmFtZQ0KY2x1c3RlcmNsYXNzIDwtIGFzLmRhdGEuZnJhbWUoY2x1c3RlcmNsYXNzKSANCiMgdGhpcyBjcmVhdGVzIGEgIkNvdW50cnkiIGNvbHVtbiANCmNsdXN0ZXJjbGFzcyRDb3VudHJ5IDwtIHJvd25hbWVzKGNsdXN0ZXJjbGFzcykgDQojIHRoaXMgbWVyZ2UgdGhlIGNsdXN0ZXIgZ3JvdXBzIHdpdGggdGhlIHZhcmlhYmxlcyB1c2VkDQptYW5ncm92ZV9jbHVzdGVycyA8LSBjYmluZChhcy5kYXRhLmZyYW1lKGNsdXN0ZXJjbGFzcyksIGRmX3NjbCkgDQoNCiMgdGhpcyBjcmVhdGUgYSBmYWN0b3IgZm9yIHRoZSBjbHVzdGVycw0KbWFuZ3JvdmVfY2x1c3RlcnMkY2x1c3RlcmNsYXNzIDwtIGFzLmZhY3RvcihtYW5ncm92ZV9jbHVzdGVycyRjbHVzdGVyY2xhc3MpIA0KYGBgDQoNCkZvciB0aGUgZmluYWwgdmlzdWFsaXphdGlvbiB3ZSBmb2xsb3cgdGhlc2Ugc3RlcHM6IA0KDQoxLiB3ZSBjcmVhdGUgdGhlIGZpbmFsIGNsdXN0ZXIgZ3JhcGggYGZpbmFsX2NsdXN0ZXJgOw0KMi4gd2UgY3JlYXRlIHRocmVlIFtiYXIgcGxvdHNdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vYmFycGxvdC8pIGZvciBlYWNoIGNsdXN0ZXI6IGBtYzFgLCBgbWMyYCBhbmQgYG1jM2A7DQozLiB3ZSBjcmVhdGUgYSBmaW5hbCBpbWFnZSBqb2luaW5nIGFsbCB0aGVzZSBwbG90cyB1c2luZyB0aGUgYGdnYXJyYW5nZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBwYWNrYWdlIGBnZ2FycmFuZ2VgLg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTB9DQojIGZpbmFsIGNsdXN0ZXINCmZpbmFsX2NsdXN0ZXIgPC0gZnZpel9kZW5kKHJlcy53YXJkLCANCiAgICAgICAgICBrID0gNCwgIyBDdXQgaW4gZm91ciBrID0gNCBncm91cHMNCiAgICAgICAgICBrX2NvbG9ycyAgPSAiYWFhcyIsIA0KICAgICAgICAgIGhvcml6ID0gVFJVRSwNCiAgICAgICAgICBjZXggPSAwLjcsDQogICAgICAgICAgc3ViID0gIiIpDQoNCiMgY2x1c3RlciAxDQptYzEgPC0gbWFuZ3JvdmVfY2x1c3RlcnMgJT4lIA0KICAgICAgc2VsZWN0KGNsdXN0ZXJjbGFzcywgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lICAjIHNlbGVjdGluZyB2YXJpYWJsZXMNCiAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCByZXNjYWxlLCBuYS5ybT1ULCBjKDAsMSkpICU+JSAjcmVzY2FsaW5nDQogICAgICBtdXRhdGUocHJlc3N1cmVzPXByZXNzdXJlcyotMSwgY29ycnVwdGlvbj1jb3JydXB0aW9uKi0xLCANCiAgICAgICAgICAgICBwb3BfZ3Jvd3RoPXBvcF9ncm93dGgqLTEsIHBvdmVydHk9cG92ZXJ0eSotMSwNCiAgICAgICAgICAgICBwb3BfY29hc3Q9cG9wX2NvYXN0Ki0xLCBlbWlzc2lvbnM9ZW1pc3Npb25zKi0xKSAlPiUgIyBjaGFuZ2luZyBzaWduDQogICAgICBnYXRoZXIoIlZhcmlhYmxlIiwgIlZhbHVlIiwgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lICAjIGdhdGhlcmluZyANCiAgICAgIGdyb3VwX2J5KGNsdXN0ZXJjbGFzcywgVmFyaWFibGUpICU+JSAjIyBncm91cGluZw0KICAgICAgc3VtbWFyaXNlKFZhbHVlPW1lYW4oVmFsdWUsIG5hLnJtPVQpKSAlPiUgICMgc3VtbWFyaXNpbmcgDQogICAgICBmaWx0ZXIoY2x1c3RlcmNsYXNzID09IDEpICU+JSAjIHRoaXMgZmlsdGVycyBjbHVzdGVyIG9uZQ0KICAgICAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoVmFyaWFibGUsIFZhbHVlKSwgeT1WYWx1ZSwgZ3JvdXA9MSkpICsgICMgcGxvdCBzdGFydHMNCiAgICAgIGdlb21fY29sKGNvbD0iYmxhY2siLCBmaWxsID0gIiNFRTAwMDBGRiIsIGFscGhhPTAuOCkrIA0KICAgICAgeWxpbSgtMSwxKSsgIyB0aGlzIHNldCB0aGUgc2NhbGUgZm9yIHRoZSBwbG90DQogICAgICB0aGVtZV9taW5pbWFsKCkrICMgdGhpcyBzZXQgdGhlIGdyYXBoaWMgdGhlbWUNCiAgICAgIGxhYnMoeD0nJywgeT0nJywgdGl0bGU9IkMtMyIpKyAgDQogICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIiwgZmFjZT0iYm9sZCIpLCANCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJncmF5OTAiKSwgDQogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiIikrDQogICAgICAgY29vcmRfZmxpcCgpIyB0aGVzZSBhcmUgZ3JhcGhpY2FsIG9wdGlvbnMNCg0KIyBjbHVzdGVyIDINCm1jMiA8LSBtYW5ncm92ZV9jbHVzdGVycyAlPiUgDQogICBzZWxlY3QoY2x1c3RlcmNsYXNzLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgDQogICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcmVzY2FsZSwgbmEucm09VCwgYygwLDEpKSAlPiUgDQogICBtdXRhdGUocHJlc3N1cmVzPXByZXNzdXJlcyotMSwgY29ycnVwdGlvbj1jb3JydXB0aW9uKi0xLCANCiAgICAgICAgICAgICBwb3BfZ3Jvd3RoPXBvcF9ncm93dGgqLTEsIHBvdmVydHk9cG92ZXJ0eSotMSwNCiAgICAgICAgICAgICBwb3BfY29hc3Q9cG9wX2NvYXN0Ki0xLCBlbWlzc2lvbnM9ZW1pc3Npb25zKi0xKSAlPiUgDQogICBnYXRoZXIoIlZhcmlhYmxlIiwgIlZhbHVlIiwgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lIA0KICAgZ3JvdXBfYnkoY2x1c3RlcmNsYXNzLCBWYXJpYWJsZSkgJT4lIA0KICAgc3VtbWFyaXNlKFZhbHVlPW1lYW4oVmFsdWUsIG5hLnJtPVQpKSAlPiUgDQogICBmaWx0ZXIoY2x1c3RlcmNsYXNzID09IDIpICU+JSANCiAgIGdncGxvdChhZXMoeD1yZW9yZGVyKFZhcmlhYmxlLCBWYWx1ZSksIHk9VmFsdWUsIGdyb3VwPTEpKSArIA0KICAgZ2VvbV9jb2woY29sPSJibGFjayIsIGZpbGwgPSAiIzAwOEI0NUZGIiwgYWxwaGE9MC44KSsgDQogICB5bGltKC0xLDEpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgbGFicyh4PScnLCB5PSdTY29yZScsIHRpdGxlPSJDLTIiKSsNCiAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTYpLCANCiAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIiwgZmFjZT0iYm9sZCIpLCANCiAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJncmF5OTAiKSwgDQogICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiIikrDQogICAgICBjb29yZF9mbGlwKCkNCg0KIyBjbHVzdGVyIDMNCm1jMyA8LSBtYW5ncm92ZV9jbHVzdGVycyAlPiUgDQogICAgICBzZWxlY3QoY2x1c3RlcmNsYXNzLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgDQogICAgICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcmVzY2FsZSwgbmEucm09VCwgYygwLDEpKSAlPiUgDQogICAgICBtdXRhdGUocHJlc3N1cmVzPXByZXNzdXJlcyotMSwgY29ycnVwdGlvbj1jb3JydXB0aW9uKi0xLCANCiAgICAgICAgICAgICBwb3BfZ3Jvd3RoPXBvcF9ncm93dGgqLTEsIHBvdmVydHk9cG92ZXJ0eSotMSwNCiAgICAgICAgICAgICBwb3BfY29hc3Q9cG9wX2NvYXN0Ki0xLCBlbWlzc2lvbnM9ZW1pc3Npb25zKi0xKSAlPiUgDQogICAgICBnYXRoZXIoIlZhcmlhYmxlIiwgIlZhbHVlIiwgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lIA0KICAgICAgZ3JvdXBfYnkoY2x1c3RlcmNsYXNzLCBWYXJpYWJsZSkgJT4lIA0KICAgICAgc3VtbWFyaXNlKFZhbHVlPW1lYW4oVmFsdWUsIG5hLnJtPVQpKSAlPiUgDQogICAgICBmaWx0ZXIoY2x1c3RlcmNsYXNzID09IDMpICU+JSANCiAgICAgIGdncGxvdChhZXMoeD1yZW9yZGVyKFZhcmlhYmxlLCBWYWx1ZSksIHk9VmFsdWUsIGdyb3VwPTEpKSArIA0KICAgICAgZ2VvbV9jb2woY29sPSJibGFjayIsIGZpbGwgPSAiIzYzMTg3OUZGIiwgYWxwaGE9MC44KSsgDQogICAgICB5bGltKC0xLDEpKw0KICAgICAgdGhlbWVfbWluaW1hbCgpKw0KICAgICAgbGFicyh4PScnLCB5PScnLCB0aXRsZT0iQy0xIikrDQogICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIiwgZmFjZT0iYm9sZCIpLCANCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJncmF5OTAiKSwgDQogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiIikrDQogICAgICBjb29yZF9mbGlwKCkNCg0KIyBjbHVzdGVyIDQNCm1jNCA8LSBtYW5ncm92ZV9jbHVzdGVycyAlPiUgDQogICAgICBzZWxlY3QoY2x1c3RlcmNsYXNzLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgDQogICAgICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcmVzY2FsZSwgbmEucm09VCwgYygwLDEpKSAlPiUgDQogICAgICBtdXRhdGUocHJlc3N1cmVzPXByZXNzdXJlcyotMSwgY29ycnVwdGlvbj1jb3JydXB0aW9uKi0xLCANCiAgICAgICAgICAgICBwb3BfZ3Jvd3RoPXBvcF9ncm93dGgqLTEsIHBvdmVydHk9cG92ZXJ0eSotMSwNCiAgICAgICAgICAgICBwb3BfY29hc3Q9cG9wX2NvYXN0Ki0xLCBlbWlzc2lvbnM9ZW1pc3Npb25zKi0xKSAlPiUgDQogICAgICBnYXRoZXIoIlZhcmlhYmxlIiwgIlZhbHVlIiwgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lIA0KICAgICAgZ3JvdXBfYnkoY2x1c3RlcmNsYXNzLCBWYXJpYWJsZSkgJT4lIA0KICAgICAgc3VtbWFyaXNlKFZhbHVlPW1lYW4oVmFsdWUsIG5hLnJtPVQpKSAlPiUgDQogICAgICBmaWx0ZXIoY2x1c3RlcmNsYXNzID09IDQpICU+JSANCiAgICAgIGdncGxvdChhZXMoeD1yZW9yZGVyKFZhcmlhYmxlLCBWYWx1ZSksIHk9VmFsdWUsIGdyb3VwPTEpKSArIA0KICAgICAgZ2VvbV9jb2woY29sPSJibGFjayIsIGZpbGwgPSAiIzNCNDk5MkZGIiwgYWxwaGE9MC44KSsgDQogICAgICB5bGltKC0xLDEpKw0KICAgICAgdGhlbWVfbWluaW1hbCgpKw0KICAgICAgbGFicyh4PScnLCB5PScnLCB0aXRsZT0iQy00IikrDQogICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIiwgZmFjZT0iYm9sZCIpLCANCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJncmF5OTAiKSwgDQogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiIikrDQogICAgICBjb29yZF9mbGlwKCkNCg0KIyBoZXJlIHdlIGNyZWF0ZSB0aGUgcGFuZWw6IA0KDQpnZ2FycmFuZ2UoDQogIGZpbmFsX2NsdXN0ZXIsICAjIEZpcnN0IHJvdyB3aXRoIGxpbmUgcGxvdA0KICAjIFNlY29uZCByb3cgd2l0aCBib3ggYW5kIGRvdCBwbG90cw0KICBnZ2FycmFuZ2UobWMzLCBtYzIsIG1jMSwgbWM0LCBuY29sID0gMSksIA0KICBucm93ID0gMSwNCiAgbmNvbCA9IDINCiAgKSANCg0KYGBgDQoNCipub3RlIHRoYXQgdGhlIGNsdXN0ZXIgbnVtYmVycyBhcmUgY2hhbmdlZCBmb3IgdGhlIHZpc3VhbGl6YXRpb24sIHNvIHRoYXQgYXJlIGluIHRoZSBzYW1lIG9yZGVyIGluIHRoZSBGaWd1cmUgaW4gbWFpbiB0ZXh0LiBUaGlzIGlzIG9ubHkgZm9yIHZpc3VhbCByZXByZXNlbnRhdGlvbiBhbmQgZG8gbm90IGNoYW5nZSB0aGUgcmVzdWx0cy4NCk5hbWVzIG9mIHNvbWUgY291bnRyaWVzIHdlcmUgYWxzbyBzaG9ydGVuZWQgdG8gc2F2ZSBzcGFjZS4gDQoNCg0KV2UgY2FuIHNhdmUgdGhlIGZpZ3VyZSB1c2luZzogDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMH0NCmdnc2F2ZSgiZmlncy9GaWd1cmUxLnBuZyIsIGRwaT0zMDApDQpgYGANCg0KLS0tIA0KDQoNCg0KIyBDb3JhbCBSZWVmcw0KDQpGb3IgQ29yYWwgcmVlZnMgd2Ugc2hvdyB0aGUgY29kZSBiZWxvdy4gV2UgZm9sbG93ZWQgdGhlIHNhbWUgbWV0aG9kb2xvZ3kgYW5kIGZyYW1ld29yayBhcyBmb3IgTWFuZ3JvdmVzLiANCg0KYGBge3J9DQojIyBDb3JhbCBSZWVmcyBkYXRhc2V0IGNyZWF0aW9uIA0KDQpDUiA8LSBmcmFtZXdvcmsgJT4lIA0KICAgc2VsZWN0KENvdW50cnksIENvcmFsX1JlZWZzKSAlPiUgDQogICBhcnJhbmdlKC1Db3JhbF9SZWVmcykgJT4lIA0KICAgbXV0YXRlKENvcmFsX1JlZWZzID0gcm91bmQoQ29yYWxfUmVlZnMsIDIpKSAlPiUgDQogICBmaWx0ZXIoQ29yYWxfUmVlZnMgPiAwKSAlPiUgDQogICBtZXJnZSguLCBlY29zb2NpYWwpICU+JSANCiAgIHNlbGVjdChDb3VudHJ5OmVtaXNzaW9ucywgcHJvdGVjdGVkPUNvcmFsX1JlZWZzX1BhcmVhKQ0KDQpgYGANCg0KVGhpcyBjcmVhdGUgdGhlIGZvbGxvd2luZyBkYXRhc2V0Og0KDQpgYGB7cn0NCg0KZGF0YXRhYmxlKENSKQ0KDQpgYGANCg0KTm93IGlmIHdlIHdhbnQgdG8gb3JkZXIgY291bnRyaWVzIGFjY29yZGluZyB0byB0aGUgcmVsYXRpdmUgaGFiaXRhdCBpbXBvcnRhbmNlIGluZGV4IGhlcmUgYXJlIHRoZSByZXN1bHRzOiANCg0KYGBge3J9DQoNCmRhdGF0YWJsZShDUiAlPiUgc2VsZWN0KENvdW50cnksIENvcmFsX1JlZWZzKSAlPiUgYXJyYW5nZSgtQ29yYWxfUmVlZnMpKQ0KDQpgYGANCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCiMjIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyAtLS0tDQoNCmRmX2NvcnIgPC0gSG1pc2M6OnJjb3JyKGFzLm1hdHJpeChzZWxlY3QoQ1IsIC1Db3VudHJ5KSkpDQoNCiMgSW5zaWduaWZpY2FudCBjb3JyZWxhdGlvbiBhcmUgY3Jvc3NlZA0KY29ycnBsb3Q6OmNvcnJwbG90Lm1peGVkKGRmX2NvcnIkciwgdGwucG9zID0gImx0IiwNCiAgICAgICAgIHAubWF0ID0gZGZfY29yciRQLCBzaWcubGV2ZWwgPSAwLjA1LCANCiAgICAgICAgIGluc2lnID0gInBjaCIsIHBjaC5jZXggPSAyLCBwY2guY29sID0gImdyZXk5MCIpDQoNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCBpbiB0aGlzIGNhc2UsIHByb3RlY3Rpb24gaXMgbmVnYXRpdmxleSBjb3JyZWxhdGVkIHdpdGggY29ycnVwdGlvbi4gRXZlbiBpZiBzaWduaWZpY2FuY2UgaXMgbG93LiANCg0KDQojIyMgQ29yYWwgcmVlZnMgQ2x1c3RlcmluZw0KDQoNCiMjIyMgRGF0YSBzY2FsaW5nDQoNCg0KYGBge3J9DQojIFNjYWxpbmcNCiMgdGhlIHBhcnQgaW4gcGFyZW50aGVzaXMgaXMgb25seSB0byBleGNsdWRlIG5vbiBudW1lcmljYWwgY29sdW1ucw0KZGZfc2NsIDwtIHNjYWxlKENSWzQ6bmNvbChDUildKSANCg0KIyB0aGlzIGNyZWF0ZXMgbmV3IHJhdyBuYW1lcyBmb3IgdGhlIGRhdGFmcmFtZSBhbmQgDQojdGhlIHZlZ2FuOjptYWtlY2VwbmFtZXMgaXMgdG8gc2hvcnRlbiBzb21lIGNvdW50cnkgbmFtZXMNCg0Kcm93bmFtZXMoZGZfc2NsKSA8LSBtYWtlLmNlcG5hbWVzKENSJENvdW50cnkpIA0KDQoNCmRmX3NjbCA8LSBuYS5vbWl0KGRmX3NjbCkgIyMgZXhjbHVkaW5nIE5Bcw0KDQpkYXRhdGFibGUoZGZfc2NsKQ0KDQpgYGANCg0KDQpEaXN0YW5jZSBjYWxjdWxhdGlvbiANCg0KYGBge3J9DQpkaXN0LmV1Y2wgPC0gZGlzdChkZl9zY2wsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KDQpgYGANCg0KDQojIyMgQ29yYWwgcmVlZnMgY2x1c3RlciB2YWxpZGF0aW9uDQoNCmBgYHtyfQ0KIyB0aGlzIHNlbGVjdHMgdGhlIG1ldGhvZHMgdG8gYmUgdGVzdGVkDQpjbG1ldGhvZHMgPC0gYygiaGllcmFyY2hpY2FsIiwia21lYW5zIiwicGFtIikgDQoNCmludGVybiA8LSBjbFZhbGlkKG5hLm9taXQoZGZfc2NsKSwgbkNsdXN0ID0gMjoxMCwgDQogICAgICAgICAgICAgICAgICBjbE1ldGhvZHMgPSBjbG1ldGhvZHMsIHZhbGlkYXRpb24gPSAiaW50ZXJuYWwiKQ0Kc3VtbWFyeShpbnRlcm4pDQpgYGANCg0KDQpgYGB7cn0NCmNsbWV0aG9kcyA8LSBjKCJoaWVyYXJjaGljYWwiLCJrbWVhbnMiLCJwYW0iKQ0Kc3RhYiA8LSBjbFZhbGlkKG5hLm9taXQoZGZfc2NsKSwgbkNsdXN0ID0gMjo2LCBjbE1ldGhvZHMgPSBjbG1ldGhvZHMsIA0KICAgICAgICAgICAgICAgIHZhbGlkYXRpb24gPSAic3RhYmlsaXR5IikNCiMgRGlzcGxheSBvbmx5IG9wdGltYWwgU2NvcmVzDQpvcHRpbWFsU2NvcmVzKHN0YWIpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpyZXMud2FyZCA8LSBoY2x1c3QoZGlzdC5ldWNsLCBtZXRob2QgPSAid2FyZC5EMiIpDQoNCmZ2aXpfZGVuZChyZXMud2FyZCwgayA9IDQsICMgQ3V0IGluIGsgPSA0IGdyb3Vwcw0KICAgICAgICAgIGNleCA9IDAuNSwgICAjIGxhYmVsIHNpemUNCiAgICAgICAgICBrX2NvbG9ycyA9ICJhYWFzIiAgIyBjb2xvdXIgcGFsZXR0ZQ0KKQ0KDQpgYGANCkluIHRoaXMgY2FzZSwgcHJ1bmluZyBpbiA0IHNlZ21lbnRzLCBjcmVhdGUgYSBjbHVzdGVyIHdpdGggb25seSBvbmUgY291bnRyeSAoQXVzdHJhbGlhKS4gVGhpcyBpcyB3ZXJlLCBmb3IgbWFuYWdlbWVudCBwb3VycG9zZXMsIHRoZSBhbmFseXN0IG1pZ2h0IGNob29zZSB0byBqb2luIHRob3NlIGNvdW50cmllcyBpbiBvbmUgc2VnbWVudCBhbmQgdHJlYXQgdGhlbSBhY2NvcmRpbmdseS4gDQoNCmBgYHtyfQ0KcDEgPC0gZnZpel9uYmNsdXN0KGRmX3NjbCwgaGN1dCwgbWV0aG9kID0gIndzcyIpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGxpbmV0eXBlID0gMikrDQogICAgICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpDQoNCiMgU2lsaG91ZXR0ZSBtZXRob2QNCnAyIDwtIGZ2aXpfbmJjbHVzdChkZl9zY2wsIGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIikrDQogICAgICBsYWJzKHN1YnRpdGxlID0gIlNpbGhvdWV0dGUgbWV0aG9kIikNCg0KIyBHYXAgc3RhdGlzdGljDQoNCnNldC5zZWVkKDEyMykNCnAzIDwtIGZ2aXpfbmJjbHVzdChuYS5vbWl0KGRmX3NjbCksIGhjdXQsIG5zdGFydCA9IDI1LCAgbWV0aG9kID0gImdhcF9zdGF0IiwgbmJvb3QgPSA1MDApKw0KICAgICAgbGFicyhzdWJ0aXRsZSA9ICJHYXAgc3RhdGlzdGljIG1ldGhvZCIpDQoNCnBsb3RfZ3JpZChwMSxwMixwMykNCmBgYA0KDQpJbiB0aGlzIGNhc2UsIHdoaWxlIHRoZSBiZXN0IGNsdXN0ZXJpbmcgaXMgc3VnZ2VzdGVkIHRvIGJlIDIgb3IgNCwgd2UgY2FuIHNlZSBmcm9tIHRoZSBkZW5kb2dyYW0gdGhhdCA0IG1haW4gZ3JvdXBzIGNhbiBiZSBtYW5hZ2VkIGluIHRoaXMgZnJhbWV3b3JrIGFuZCBjYW4gYmUgaW50ZXJlc3RpbmcgdG8gc2VwYXJhdGUgdGhlbSBmdXJ0aGVyLiBUaGUgZ3JvdXAgZm9ybWVkIGJ5IEF1c3RyYWxpYSwgQ2hpbmEsIEphcGFuIGFuZCB0aGUgVVNBIGlzIGdyb3VwZWQgYXJpZmljaWFsbHksIHNpbmNlIGN1dHRpbmcgdGhlIGRlbmRvZ3JhbSB3b3VsZCBzcGxpdCB0aGVtIGZ1cnRoZXIuIEhvd2V2ZXIsIGluIGEgbWFuYWdlbWVudCBwZXJzcGVjdGl2ZSwgaXQgbWFrZSBzZW5zZSB0aGF0IHRoZXNlIHNob3VsZCBiZSBjb25zaWRlcmVkIHRvZ2hldGhlci4gDQoNCg0KIyBDb3JhbCByZWVmcyByZXN1bHRzIHZpc3VhbGl6YXRpb24NCg0KYGBge3J9DQojIHRoaXMgY3JlYXRlcyBjbHVzdGVyIGNsYXNzZXMgZGF0YSANCmNsdXN0ZXJjbGFzczwtIGN1dHJlZShyZXMud2FyZCwgaz00KSANCiMgdGhpcyBjcmVhdGUgZmFjdG9yIGxldmVscyBhY2NvcmRpbmcgdG8gdGhlIGdyb3Vwcw0KY2x1c3RlcmNsYXNzX2xldmVsczwtIGxldmVscyhmYWN0b3IoY2x1c3RlcmNsYXNzKSkgDQojIHRoaXMgdHJhbmZvcm1lIGl0IHRvIGEgUiBkYXRhLmZyYW1lDQpjbHVzdGVyY2xhc3MgPC0gYXMuZGF0YS5mcmFtZShjbHVzdGVyY2xhc3MpIA0KIyB0aGlzIGNyZWF0ZXMgYSAiQ291bnRyeSIgY29sdW1uIA0KY2x1c3RlcmNsYXNzJENvdW50cnkgPC0gcm93bmFtZXMoY2x1c3RlcmNsYXNzKSANCiMgdGhpcyBtZXJnZSB0aGUgY2x1c3RlciBncm91cHMgd2l0aCB0aGUgdmFyaWFibGVzIHVzZWQNCmNvcmFscmVlZnNfY2x1c3RlcnMgPC0gY2JpbmQoYXMuZGF0YS5mcmFtZShjbHVzdGVyY2xhc3MpLCBkZl9zY2wpIA0KIyB0aGlzIGNyZWF0ZSBhIGZhY3RvciBmb3IgdGhlIGNsdXN0ZXJzDQpjb3JhbHJlZWZzX2NsdXN0ZXJzJGNsdXN0ZXJjbGFzcyA8LSBhcy5mYWN0b3IoY29yYWxyZWVmc19jbHVzdGVycyRjbHVzdGVyY2xhc3MpIA0KDQpgYGANCg0KRm9yIHRoZSBmaW5hbCB2aXN1YWxpemF0aW9uIHdlIGZvbGxvdyB0aGVzZSBzdGVwczogDQoNCjEuIHdlIGNyZWF0ZSB0aGUgZmluYWwgY2x1c3RlciBncmFwaCBgZmluYWxfY2x1c3RlcmA7DQoyLiB3ZSBjcmVhdGUgdGhyZWUgW2JhciBwbG90c10oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9iYXJwbG90LykgZm9yIGVhY2ggY2x1c3RlcjogYG1jMWAsIGBtYzJgIGFuZCBgbWMzYDsNCjMuIHdlIGNyZWF0ZSBhIGZpbmFsIGltYWdlIGpvaW5pbmcgYWxsIHRoZXNlIHBsb3RzIHVzaW5nIHRoZSBgZ2dhcnJhbmdlKClgIGZ1bmN0aW9uIGZyb20gdGhlIHBhY2thZ2UgYGdnYXJyYW5nZWAuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMH0NCiMgZmluYWwgY2x1c3Rlcg0KZmluYWxfY2x1c3RlciA8LSBmdml6X2RlbmQocmVzLndhcmQsIA0KICAgICAgICAgIGsgPSA0LCAjIEN1dCBpbiBmb3VyIGsgPSA0IGdyb3Vwcw0KICAgICAgICAgIGtfY29sb3JzICA9ICJhYWFzIiwgDQogICAgICAgICAgaG9yaXogPSBUUlVFLA0KICAgICAgICAgIGNleCA9IDAuNywNCiAgICAgICAgICBzdWIgPSAiIikNCg0KIyBjbHVzdGVyIDENCm1jMSA8LSAgY29yYWxyZWVmc19jbHVzdGVycyAlPiUgDQogICAgICBzZWxlY3QoY2x1c3RlcmNsYXNzLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgICNzZWxlY3RpbmcgdmFyaWFibGVzDQogICAgICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcmVzY2FsZSwgbmEucm09VCwgYygwLDEpKSAlPiUgIyByZXNjYWxpbmcNCiAgICAgIG11dGF0ZShwcmVzc3VyZXM9cHJlc3N1cmVzKi0xLCBjb3JydXB0aW9uPWNvcnJ1cHRpb24qLTEsIA0KICAgICAgICAgICAgIHBvcF9ncm93dGg9cG9wX2dyb3d0aCotMSwgcG92ZXJ0eT1wb3ZlcnR5Ki0xLA0KICAgICAgICAgICAgIHBvcF9jb2FzdD1wb3BfY29hc3QqLTEsIGVtaXNzaW9ucz1lbWlzc2lvbnMqLTEpICU+JSANCiAgICAgIGdhdGhlcigiVmFyaWFibGUiLCAiVmFsdWUiLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgIyByZXNoYXBlDQogICAgICBncm91cF9ieShjbHVzdGVyY2xhc3MsIFZhcmlhYmxlKSAlPiUgIyMgZ3JvdXBpbmcNCiAgICAgIHN1bW1hcmlzZShWYWx1ZT1tZWFuKFZhbHVlLCBuYS5ybT1UKSkgJT4lICAjIHN1bW1hcmlzaW5nIA0KICAgICAgZmlsdGVyKGNsdXN0ZXJjbGFzcyAlaW4lIGMoMSw0KSkgJT4lIA0KICAgICAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoVmFyaWFibGUsIFZhbHVlKSwgeT1WYWx1ZSwgZ3JvdXA9MSkpICsgIA0KICAgICAgZ2VvbV9jb2woYWVzKGZpbGwgPSBjbHVzdGVyY2xhc3MpLCBjb2w9ImJsYWNrIiwgIGFscGhhPTAuOCwgcG9zaXRpb24gPSAiZG9kZ2UyIikrICMNCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjMDA4QjQ1RkYiLCAiIzYzMTg3OUZGIikpKw0KICAgICAgeWxpbSgtMSwxKSsgDQogICAgICB0aGVtZV9taW5pbWFsKCkrIA0KICAgICAgbGFicyh4PScnLCB5PScnLCB0aXRsZT0iQy0yIikrIA0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksDQogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpLA0KICAgICAgICAgICAgdGV4dD1lbGVtZW50X3RleHQoZmFtaWx5PSJzZXJpZiIsIGZhY2U9ImJvbGQiKSwgDQogICAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9saW5lKGNvbG91cj0iZ3JheTkwIiksIA0KICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIiIpKw0KICAgICAgIGNvb3JkX2ZsaXAoKSANCg0KDQojIGNsdXN0ZXIgMg0KbWMyIDwtIGNvcmFscmVlZnNfY2x1c3RlcnMgJT4lIA0KICAgICAgc2VsZWN0KGNsdXN0ZXJjbGFzcywgcHJlc3N1cmVzOnByb3RlY3RlZCkgJT4lICANCiAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCByZXNjYWxlLCBuYS5ybT1ULCBjKDAsMSkpICU+JSANCiAgICAgIG11dGF0ZShwcmVzc3VyZXM9cHJlc3N1cmVzKi0xLCBjb3JydXB0aW9uPWNvcnJ1cHRpb24qLTEsIA0KICAgICAgICAgICAgIHBvcF9ncm93dGg9cG9wX2dyb3d0aCotMSwgcG92ZXJ0eT1wb3ZlcnR5Ki0xLA0KICAgICAgICAgICAgIHBvcF9jb2FzdD1wb3BfY29hc3QqLTEsIGVtaXNzaW9ucz1lbWlzc2lvbnMqLTEpICU+JSANCiAgICAgIGdhdGhlcigiVmFyaWFibGUiLCAiVmFsdWUiLCBwcmVzc3VyZXM6cHJvdGVjdGVkKSAlPiUgDQogICAgICBncm91cF9ieShjbHVzdGVyY2xhc3MsIFZhcmlhYmxlKSAlPiUgDQogICAgICBzdW1tYXJpc2UoVmFsdWU9bWVhbihWYWx1ZSwgbmEucm09VCkpICU+JQ0KICAgICAgZmlsdGVyKGNsdXN0ZXJjbGFzcyA9PSAyICkgJT4lIA0KICAgICAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoVmFyaWFibGUsIFZhbHVlKSwgeT1WYWx1ZSwgZ3JvdXA9MSkpICsgIA0KICAgICAgZ2VvbV9jb2woY29sPSJibGFjayIsIGZpbGwgPSAiIzNCNDk5MkZGIiwgYWxwaGE9MC44KSsgDQogICAgICB5bGltKC0xLDEpKyANCiAgICAgIHRoZW1lX21pbmltYWwoKSsNCiAgICAgIGxhYnMoeD0nJywgeT0nJywgdGl0bGU9IkMtMyIpKyANCiAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTYpLA0KICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgICAgIHRleHQ9ZWxlbWVudF90ZXh0KGZhbWlseT0ic2VyaWYiLCBmYWNlPSJib2xkIiksIA0KICAgICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvdXI9ImdyYXk5MCIpLCANCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICIiKSsNCiAgICAgICBjb29yZF9mbGlwKCkNCg0KIyBjbHVzdGVyIDMNCm1jMyA8LSBjb3JhbHJlZWZzX2NsdXN0ZXJzICU+JSANCiAgICAgIHNlbGVjdChjbHVzdGVyY2xhc3MsIHByZXNzdXJlczpwcm90ZWN0ZWQpICU+JSANCiAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCByZXNjYWxlLCBuYS5ybT1ULCBjKDAsMSkpICU+JQ0KICAgICAgbXV0YXRlKHByZXNzdXJlcz1wcmVzc3VyZXMqLTEsIGNvcnJ1cHRpb249Y29ycnVwdGlvbiotMSwgDQogICAgICAgICAgICAgcG9wX2dyb3d0aD1wb3BfZ3Jvd3RoKi0xLCBwb3ZlcnR5PXBvdmVydHkqLTEsDQogICAgICAgICAgICAgcG9wX2NvYXN0PXBvcF9jb2FzdCotMSwgZW1pc3Npb25zPWVtaXNzaW9ucyotMSkgJT4lIA0KICAgICAgZ2F0aGVyKCJWYXJpYWJsZSIsICJWYWx1ZSIsIHByZXNzdXJlczpwcm90ZWN0ZWQpICU+JQ0KICAgICAgZ3JvdXBfYnkoY2x1c3RlcmNsYXNzLCBWYXJpYWJsZSkgJT4lDQogICAgICBzdW1tYXJpc2UoVmFsdWU9bWVhbihWYWx1ZSwgbmEucm09VCkpICU+JQ0KICAgICAgZmlsdGVyKGNsdXN0ZXJjbGFzcyA9PSAzKSAlPiUNCiAgICAgIGdncGxvdChhZXMoeD1yZW9yZGVyKFZhcmlhYmxlLCBWYWx1ZSksIHk9VmFsdWUsIGdyb3VwPTEpKSArDQogICAgICBnZW9tX2NvbChjb2w9ImJsYWNrIiwgZmlsbCA9ICIjRUUwMDAwRkYiLCBhbHBoYT0wLjgpKyANCiAgICAgIHlsaW0oLTEsMSkrIA0KICAgICAgdGhlbWVfbWluaW1hbCgpKw0KICAgICAgbGFicyh4PScnLCB5PScnLCB0aXRsZT0iQy0yIikrDQogICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIiwgZmFjZT0iYm9sZCIpLCANCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJncmF5OTAiKSwgDQogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiIikrDQogICAgICAgY29vcmRfZmxpcCgpDQoNCiMgaGVyZSB3ZSBjcmVhdGUgdGhlIHBhbmVsOiANCg0KZ2dhcnJhbmdlKA0KICBmaW5hbF9jbHVzdGVyLCAgIyBGaXJzdCByb3cgd2l0aCBsaW5lIHBsb3QNCiAgIyBTZWNvbmQgcm93IHdpdGggYm94IGFuZCBkb3QgcGxvdHMNCiAgZ2dhcnJhbmdlKG1jMSwgbWMzLCBtYzIsIG5jb2wgPSAxKSwgDQogIG5yb3cgPSAxLA0KICBuY29sID0gMg0KICApIA0KDQpgYGANCg0KKm5vdGUgdGhhdCB0aGUgY2x1c3RlciBudW1iZXJzIGFyZSBjaGFuZ2VkIGZvciB0aGUgdmlzdWFsaXphdGlvbiwgc28gdGhhdCBhcmUgaW4gdGhlIHNhbWUgb3JkZXIgaW4gdGhlIEZpZ3VyZSBpbiBtYWluIHRleHQuIFRoaXMgaXMgb25seSBmb3IgdmlzdWFsIHJlcHJlc2VudGF0aW9uIGFuZCBkbyBub3QgY2hhbmdlIHRoZSByZXN1bHRzLg0KDQpXZSBjYW4gc2F2ZSB0aGUgZmlndXJlIHVzaW5nOiANCg0KYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEwfQ0KZ2dzYXZlKCJmaWdzL0ZpZ3VyZTIucG5nIiwgZHBpPTMwMCkNCmBgYA0KDQoNCiMgUmVmZXJlbmNlcyANCg0KDQpTYWxhLCBFLiwgTWF5b3JnYSwgSi4sIENvc3RlbGxvLCBDLiwgS3Jvb2RzbWEsIEQuLCBQYWxvbWFyZXMsIE0uIEwuIEQuLCBQYXVseSwgRC4sIFvigKZdIFplbGxlciwgRC4gDQooMjAxOCkuIFRoZSBlY29ub21pY3Mgb2YgZmlzaGluZyB0aGUgaGlnaCBzZWFzLiBTY2llbmNlIEFkdmFuY2VzLCA0KDYpLCBlYWF0MjUwNC4gaHR0cHM6Ly9kb2kub3JnLzEwLjExMjYvc2NpYWR2LmFhdDI1MDQNCg0KIyMgSGFiaXRhdCBkYXRhIHJlZmVyZW5jZXMgZnJvbSB0aGUgdGFibGUNCg0KLSBFc3R1YXJpZXM6IEFsZGVyIEogKDIwMDMpLiBQdXR0aW5nIHRoZSBjb2FzdCBpbiB0aGUg4oCcU2VhIEFyb3VuZCBVc+KAnS4gVGhlIFNlYSBBcm91bmQgVXMgTmV3c2xldHRlciAxNTogMS0yLiBVUkw6IGh0dHA6Ly9zZWFhcm91bmR1cy5vcmcvbmV3c2xldHRlci9Jc3N1ZTE1LnBkZjsgaHR0cDovL2RhdGEudW5lcC13Y21jLm9yZy9kYXRhc2V0cy8yMyAodmVyc2lvbiAyLjApIA0KDQotIE1hbmdyb3ZlczogR2lyaSwgQy4sIEUuIE9jaGllbmcsIEwuIEwuIFRpZXN6ZW4sIFouIFpodSwgQS4gU2luZ2gsIFQuIExvdmVsYW5kLCBKLiBNYXNlaywgYW5kIE4uIER1a2UuICgyMDExKS4g4oCcU3RhdHVzIGFuZCBEaXN0cmlidXRpb24gb2YgTWFuZ3JvdmUgRm9yZXN0cyBvZiB0aGUgV29ybGQgVXNpbmcgRWFydGggT2JzZXJ2YXRpb24gU2F0ZWxsaXRlIERhdGE6IFN0YXR1cyBhbmQgRGlzdHJpYnV0aW9ucyBvZiBHbG9iYWwgTWFuZ3JvdmVzLuKAnSBHbG9iYWwgRWNvbG9neSBhbmQgQmlvZ2VvZ3JhcGh5IDIwICgxKTogMTU04oCTNTkuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMTExL2ouMTQ2Ni04MjM4LjIwMTAuMDA1ODQueC4NCg0KLSBTYWx0bWFyc2g6IE1jb3dlbiBDLCBXZWF0aGVyZG9uIExWLCBCb2Nob3ZlIEosIFN1bGxpdmFuIEUsIEJseXRoIFMsIFpvY2tsZXIgQywgU3RhbndlbGwtU21pdGggRCwgS2luZ3N0b24gTiwgTWFydGluIENTLCBTcGFsZGluZyBNLCBGbGV0Y2hlciBTICgyMDE3KS4gQSBnbG9iYWwgbWFwIG9mIHNhbHRtYXJzaGVzLiBCaW9kaXZlcnNpdHkgRGF0YSBKb3VybmFsIDU6IGUxMTc2NC4gUGFwZXIgRE9JOiBodHRwczovL2RvaS5vcmcvMTAuMzg5Ny9CREouNS5lMTE3NjQ7IERhdGEgVVJMOiBodHRwOi8vZGF0YS51bmVwLXdjbWMub3JnL2RhdGFzZXRzLzQzICh2LjYpDQoNCi0gU2VhZ3Jhc3NlczpVTkVQLVdDTUMsIFNob3J0IEZUICgyMDE4KS4gR2xvYmFsIGRpc3RyaWJ1dGlvbiBvZiBzZWFncmFzc2VzICh2ZXJzaW9uIDYuMCkuIFNpeHRoIHVwZGF0ZSB0byB0aGUgZGF0YSBsYXllciB1c2VkIGluIEdyZWVuIGFuZCBTaG9ydCAoMjAwMykuIENhbWJyaWRnZSAoVUspOiBVTiBFbnZpcm9ubWVudCBXb3JsZCBDb25zZXJ2YXRpb24gTW9uaXRvcmluZyBDZW50cmUuIFVSTDogaHR0cDovL2RhdGEudW5lcC13Y21jLm9yZy9kYXRhc2V0cy83DQoNCi0gQ29yYWwgUmVlZnM6IFVORVAtV0NNQywgV29ybGRGaXNoIENlbnRyZSwgV1JJLCBUTkMgKDIwMTgpLiBHbG9iYWwgZGlzdHJpYnV0aW9uIG9mIHdhcm0td2F0ZXIgY29yYWwgcmVlZnMsIGNvbXBpbGVkIGZyb20gbXVsdGlwbGUgc291cmNlcyBpbmNsdWRpbmcgdGhlIE1pbGxlbm5pdW0gQ29yYWwgUmVlZiBNYXBwaW5nIFByb2plY3QuIFZlcnNpb24gNC4wLiBJbmNsdWRlcyBjb250cmlidXRpb25zIGZyb20gSU1hUlMtVVNGIGFuZCBJUkQgKDIwMDUpLCBJTWFSUy1VU0YgKDIwMDUpIGFuZCBTcGFsZGluZyBldCBhbC4gKDIwMDEpLiBDYW1icmlkZ2UgKFVLKTogVU4gRW52aXJvbm1lbnQgV29ybGQgQ29uc2VydmF0aW9uIE1vbml0b3JpbmcgQ2VudHJlLiBVUkw6IGh0dHA6Ly9kYXRhLnVuZXAtd2NtYy5vcmcvZGF0YXNldHMvMQ0KDQotIEtlbHA6IEpvcmdlIEFzc2lzIChzdWJtaXR0ZWQgZm9yIHB1YmxpY2F0aW9uKSANCg0KLSBDb2xkY29yYWxzOiBGcmVpd2FsZCBBLCBSb2dlcnMgQSwgSGFsbC1TcGVuY2VyIEosIEd1aW5vdHRlIEpNLCBEYXZpZXMgQUosIFllc3NvbiBDLCBNYXJ0aW4gQ1MsIFdlYXRoZXJkb24gTFYgKDIwMTcpLiBHbG9iYWwgZGlzdHJpYnV0aW9uIG9mIGNvbGQtd2F0ZXIgY29yYWxzICh2ZXJzaW9uIDUuMCkuIEZpZnRoIHVwZGF0ZSB0byB0aGUgZGF0YXNldCBpbiBGcmVpd2FsZCBldCBhbC4gKDIwMDQpIGJ5IFVORVAtV0NNQywgaW4gY29sbGFib3JhdGlvbiB3aXRoIEFuZHJlIEZyZWl3YWxkIGFuZCBKb2huIEd1aW5vdHRlLiBDYW1icmlkZ2UgKFVLKTogVU4gRW52aXJvbm1lbnQgV29ybGQgQ29uc2VydmF0aW9uIE1vbml0b3JpbmcgQ2VudHJlLiBVUkw6IGh0dHA6Ly9kYXRhLnVuZXAtd2NtYy5vcmcvZGF0YXNldHMvMw0KDQotIFNpbGxzLVJpZnQgVmFsbGV5czogSGFycmlzLCBQLiBULiwgTWFjbWlsbGFuLUxhd2xlciwgTS4sIFJ1cHAsIEouLCAmIEJha2VyLCBFLiBLLiAoMjAxNCkuIEdlb21vcnBob2xvZ3kgb2YgdGhlIG9jZWFucy4gTWFyaW5lIEdlb2xvZ3ksIDM1MiwgNOKAkzI0LiBodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLm1hcmdlby4yMDE0LjAxLjAxMQ0KSHlkcm90aGVybWFsIHZlbnRzOiBCZWF1bGlldSwgUy5FLiwgU3phZnJhbnNraSwgSy4gKDIwMTgpIEludGVyUmlkZ2UgR2xvYmFsIERhdGFiYXNlIG9mIEFjdGl2ZSBTdWJtYXJpbmUgSHlkcm90aGVybWFsIFZlbnQgRmllbGRzLCBWZXJzaW9uIDMuNC4gV29ybGQgV2lkZSBXZWIgZWxlY3Ryb25pYyBwdWJsaWNhdGlvbiBhdmFpbGFibGUgZnJvbSBodHRwOi8vdmVudHMtZGF0YS5pbnRlcnJpZGdlLm9yZyBBY2Nlc3NlZCAyMDE5LTAyLTIwLg0KDQoNCiMgQ29udGFjdHMNCg0KLSBGYWJpbyBGYXZvcmV0dG86IGZhdm9yZXR0by5mYWJpb0BnbWFpbC5jb20NCg0KLSBKb3kgS3VtYWdhaTogamt1bWFnYWk5NkBnbWFpbC5jb20=