Disclaimer: The purpose of the Open Case Studies project is to demonstrate the use of various data science methods, tools, and software in the context of messy, real-world data. A given case study does not cover all aspects of the research process, is not claiming to be the most appropriate way to analyze a given dataset, and should not be used in the context of making policy decisions without external consultation from scientific experts.

This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 (CC BY-NC 3.0) United States License.

To cite this case study please use:

Wright, Carrie and Meng, Qier and Jager, Leah and Taub, Margaret and Hicks, Stephanie. (2020). https://github.com/opencasestudies/ocs-bp-rural-and-urban-obesity. Exploring global patterns of obesity across rural and urban regions (Version v1.0.0).

To access the GitHub Repository for this case study see here: https://github.com/opencasestudies/ocs-bp-rural-and-urban-obesity.

You may also access and download the data using our OCSdata package. To learn more about this package including examples, see this link. Here is how you would install this package:

install.packages("OCSdata")

This case study is part of a series of public health case studies for the Bloomberg American Health Initiative.


The total reading time for this case study is calculated via koRpus and shown below:

Reading Time Method
72 minutes koRpus

Readability Score:

A readability index estimates the reading difficulty level of a particular text. Flesch-Kincaid, FORCAST, and SMOG are three common readability indices that were calculated for this case study via koRpus. These indices provide an estimation of the minimum reading level required to comprehend this case study by grade and age.

Text language: en 
index grade age
Flesch-Kincaid 9 14
FORCAST 10 15
SMOG 11 16

Please help us by filling out our survey.

Motivation

Body mass index (BMI) is often used as a proxy for adiposity (the condition of having excess body fat) and is measured as an individual’s weight in kilograms (kg) or pounds (lbs) divided by the individual’s height in meters (\(m^2\)) squared.

There are different categories of weight defined using different BMI thresholds as defined by the World Health Organization (WHO), the Centers for Disease Control and Prevention, and the National Institutes of Health. For example, these are the categories defined by the WHO:

[source]

The following chart can help you identify your estimate of BMI:

[source]

Recently, an article published in Nature evaluated and compared the BMI of populations in rural and urban communities around the world:

NCD Risk Factor Collaboration (NCD-RisC). Rising rural body-mass index is the main driver of the global obesity epidemic in adults. Nature 569, 260–264 (2019).

The article challenged the widely-held view that increased urbanization was one of the major reasons for increased global obesity rates. This view came about because many countries around the world have shown increased urbanization levels in parallel with increased obesity rates. In this article, however, the NCD-RisC argued that this might not be the case and that in fact for most regions around the world, BMI measurements are increasing in rural populations just as much, if not more so, than urban populations. Furthermore, this study suggested that obesity has particularly increased in female populations in rural regions:

“We noted a persistently higher rural BMI, especially for women.”

In this case study, we will evaluate the data reported in this article to explore regional and gender specific differences in the obesity rates around the world in 1985 and 2017. Most importantly we will test if there is a difference in obesity rates between rural and urban communities and if there has been a change in obesity over time for these regions, particularly for women. To do this we will test if there is a difference between the average BMI for each group.

Main Questions


Our main questions are:

  1. Is there a difference between rural and urban BMI estimates around the world? In particular, what does this difference look like for women?
  2. How have BMI estimates changed from 1985 to 2017? In particular, what does this change over time look like for women?
  3. How do different countries vary in terms of estimates of BMI? In particular, how does the United States compare to the rest of the world?

Learning Objectives


The skills, methods, and concepts that students will be familiar with by the end of this case study are:

Data science Learning Objectives:

  1. Importing data from a PDF (pdftools)
  2. Subsetting and filtering data (dplyr)
  3. Working with character strings (stringr)
  4. Reshaping data into different formats (tidyr)
  5. Applying functions to all columns of a tibble (purrr)
  6. Creating data visualizations (ggplot2) with labels (ggrepel)
  7. Combining multiple plots (cowplot and patchwork)

Statistical Learning Objectives:

  1. Familiarity with the use of Quantile-Quantile plots to assess normality
  2. Define and understand the utility of alpha and the p value
  3. Describe the difference between nonparametric and parametric tests
  4. Be able to identify paired data
  5. Implementation of a paired \(t\)-test
  6. Interpretation of a paired \(t\)-test
  7. Implementation of a Wilcoxon signed-rank test 8.Interpretation of a Wilcoxon signed-rank test
  8. Understanding of the need for multiple testing correction

In this case study, we will walk you through importing data from a pdf, cleaning, wrangling and visualizing the data, and comparing two groups .

We will use well-established and commonly used packages, including stringr, tidyr, dplyr, purrr, and ggplot2 from the tidyverse. Specifically, the tidyverse is

“an opinionated collection of R packages designed for data science. All packages share an underlying philosophy and common APIs”.

Another way of putting it is that it’s a set of packages that are useful specifically for data manipulation, exploration, and visualization with a common philosophy. While some students may be familiar with previous packages from the R programming language, these packages make data science in R especially efficient.

We will begin by loading the packages that we will need with a brief explanation:

library(here)
library(pdftools)
library(stringr)
library(readr)
library(dplyr)
library(tibble)
library(magrittr)
library(glue)
library(purrr)
library(tidyr)
library(ggplot2)
library(ggrepel)
library(cowplot)
library(patchwork)
library(OCSdata)

Packages used in this case study:

Package Use in this case study
here to easily load and save data with relative paths
pdftools to read a text from pdf into R
stringr to manipulate the text data
readr to manipulate the text data within the pdf into individual lines
dplyr to arrange/filter/select subsets of the data
tibble to create data objects that we can manipulate with dplyr/stringr/tidyr/purrr
magrittr to use the %<>% piping operator
glue to paste or combine character strings and data together
purrr to perform functions on all columns of a tibble
tidyr to convert data from ‘wide’ to ‘long’ format
ggplot2 to make visualizations with multiple layers
ggrepel to allow labels in figures not to overlap
cowplot and patchwork to allow plots to be combined
OCSdata to access and download OCS data files

The first time we use a function, we will use the :: to indicate which package we are using (e.g. stringr::str_detect()). Unless we have overlapping function names, this is not necessary, but we will include it here to be informative about where the functions we will use come from.

Context


Previous work has shown higher BMI (>30) is associated with higher rates of all-causes of mortality, as well as increased rates of type 2 diabetes, cancer, heart disease, and stroke.

However, there appears to be caveats associated with the use of a threshold like this to evaluate public health risk in various populations.

According to this report:

…the associations between BMI, percentage of body fat, and body fat distribution differ across populations.

In particular, in some Asian populations a specific BMI reflects a higher percentage of body fat.

Some Pacific populations also have a lower percentage of body fat at a given BMI.

The associations of BMI and comorbidities are probably not stable within populations over time.

Limitations


The measurement of BMI has some limitations that are well recognized, as it does not account for the composition of body mass, the location of body fat, or the contribution of body frame size. However, BMI has been a useful health indicator for risk for many diseases and conditions particularly when combined with other risk factor information.

While gender and sex are not actually binary, the data presented that is used in this analysis only contain data for groups of individuals described as men or women.

What are the data?


We will be using data located within a table of the supplementary material for the NCD-RisC paper referenced above.

This is a pdf that can be found freely available online. Here is a screenshot of the first few rows of this table:

You can see that the data contain average BMI estimates for both men and women in countries at the national level, as well as the average BMI estimates for the rural and urban areas of these countries in both 1985 and 2017.

The data within the parentheses are the 95% credible intervals (CIs) for the average BMI estimates. The authors provide these CIs as a guide to understand how likely the estimate is for the true population mean BMI. A wider range suggests that the estimate is less accurate, as there are more possible values for the true mean with credible evidence. Notice that the mean BMI values are “age-standardised,” which means that the mean values were adjusted for the different age distributions of the various countries so that the countries can be more fairly compared. For example, if one country has a population that is considerably younger, the mean BMI might be quite low (as younger individuals tend to have lower BMI values due to faster metabolic rates). One might falsely conclude that people in that country generally have lower BMI values than people in most other countries, however the lower overall BMI mean might simply be due to the fact that the country has a younger population than those other countries.

Data Import


First let’s download the data from the PDF file. We use file.exists() from base R to check if the file we want to download already exists. If not, then we download it using the function utils::download.file(). We also use the here() function of the here package to look specifically in the raw subdirectory of the data directory of the directory where our open RStudio project file is located. This helps us, as we do not need to write the full path to the document. This makes our code more reproducible for someone else, who may have a different overall file structure on their computer but downloads our case study files from GitHub and therefore has the same file structure specifically for the files included in our GitHub repository. Thus our commands about file locations still work on their local machine.

if(!file.exists(here("data", "raw", "paper_supplement.pdf"))){
  url <- "https://static-content.springer.com/esm/art%3A10.1038%2Fs41586-019-1171-x/MediaObjects/41586_2019_1171_MOESM1_ESM.pdf"
  utils::download.file(url, here("data", "raw", "paper_supplement.pdf"))
}

We will save our data to a directory within our working directory called data. We will create subdirectories within this directory to organize our data. We can use the here function from the here package to make this process easier. The here() function allows us to specify the path or location of the document that we want to import, starting from the directory where an .Rproj file is located after creating a project in RStudio. In this case, we will import our files within subdirectories of a directory called raw of the data directory. (Note the next chunk of code will only work for you if you pull the repository from GitHub and set up your file structure in the same way.) If you had trouble downloading the data from the orginal sources you can download them from our GitHub repository for this case study or use the OCSdata package:

# install.packages("OCSdata")
library(OCSdata)
raw_data("ocs-bp-rural-and-urban-obesity", outpath = getwd())
# This will save the pdf file in a "OCSdata/data/raw/" subfolder 
# in your current working directory

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



Now that we have downloaded the PDF file, we will read it in to R using the pdftools package:

pdf_obesity <- pdftools::pdf_text(here("data", "raw", "paper_supplement.pdf"))

Let’s take a look at the data – the summary() function from base R helps us to look at the structure of R objects.

summary(pdf_obesity)
   Length     Class      Mode 
       63 character character 

We can see that we have 63 elements that are character strings. You may also notice that the original PDF has 63 pages. Let’s take a look at some of these elements and compare them to the pages in the original document.

pdf_obesity[1] # this looks like the first page
[1] "Letter                                                                                  https://doi.org/10.1038/s41586-019-1171-x\n\n\n\n\nRising rural body-mass index is the main driver of\nthe global obesity epidemic in adults\nNCD Risk Factor Collaboration (NCD-RisC)*\n\n\n*A list of authors and their affiliations appears in the online version of the paper.\n\n\n\n\n2 6 0 | N A T U RE | V O L 5 6 9 | 9 M A Y 2 0 1 9\n"
pdf_obesity[2] # this looks like the second page
[1] "Supplementary Information. Statistical model for estimating BMI trends by rural and urban\nplace of residence.\n\n\n\n\n                                            1\n"
str(pdf_obesity[52], nchar.max = 1600)
 chr "                                                                               2                                                             2\n                                      Age-standardised mean BMI in 1985 (kg/m )                     Age-standardised mean BMI in 2017 (kg/m )\n              Country      Sex\n                                 National               Rural                Urban             National               Rural                Urban\n                        Men      20.2 (17.8-22.7)      19.7 (17.2-22.2)     22.4 (20.0-25.0)   22.8 (20.3-25.3)      22.5 (20.0-25.0)     23.6 (21.0-26.1)\nAfghanistan             Women    20.6 (18.4-22.8)      20.1 (17.8-22.4)     23.2 (20.9-25.4)   24.4 (23.3-25.4)      23.6 (22.5-24.8)     26.3 (25.1-27.4)\n                        Men      25.2 (23.9-26.5)      25.0 (23.7-26.4)     25.4 (24.0-26.7)   27.0 (26.0-27.9)      26.9 (25.9-27.9)     27.0 (26.0-28.0)\nAlbania                 Women    26.0 (24.1-27.9)      26.1 (24.1-28.1)     25.9 (23.9-27.8)   26.0 (24.8-27.2)      26.2 (24.8-27.5)     25.9 (24.6-27.2)\n                        Men      22.1 (20.8-23.3)      21.8 (20.5-23.1)     22.3 (21.0-23.6)   25.1 (24.5-25.7)      24.8 (24.1-25.4)     25.2 (24.6-25.9)\nAlgeria                 Women    24.0 (22.2-25.7)      23.3 (21.4-25.1)     24.8 (22.9-26.6)   27.4 (26.7-28.0)      27.0 (26.3-27.8)     27.5 (26.7-28.2)\n                        Men      33.7 (32.7-34.7)      32.6 (31.7-33.5)     34.0 (32.9-35.1)   34.3 (33.0-35.6)      34.6 (33.1-35.9)     34.2 (32.9-35.6)\nAmerican Samoa          Wo"| __truncated__

Yes, this looks like data in the table we want. We are using the str() function to print just a portion of the data. The str() function compactly displays the structure of arbitrary R objects. The nchar.max argument lets us define the maximal number of characters to show. Note print(pdf_obesity[52]) would show the entire contents of this page of the table.

Here is the PDF version again:

We can see that the output looks pretty similar to the pages of the pdf, but the spacing is a bit awkward. Note that the way the data is displayed is partially influenced by the width setting of the RStudio window.

Now, we are interested in a supplementary Table 3, which has multiple pages and includes the same header on each page. We can use that to determine what elements of our pdf_obesity character strings include our table. We will use the str_detect() function from the stringr package to search for the elements that contain text that is consistently in the header. The output of of this function will show which elements of the object (in this case pages of the pdf) include this pattern indicated as a TRUE or FALSE.

stringr::str_detect(pattern = "Age-standardised mean BMI", 
                    string = pdf_obesity)
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[49] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[61]  TRUE FALSE FALSE

We note, the “Age-standardised mean BMI” is part of the header in the table on every page. The code above shows the pages that contain our table of interest (as TRUE values).

Now we will extract just the data for the table now and call it rural_urban. To do this we will use the str_subset() function of the stringr package.

rural_urban <- stringr::str_subset(pattern = "Age-standardised mean BMI", 
                                   string = pdf_obesity)

summary(rural_urban) 
   Length     Class      Mode 
       10 character character 

Here, we see there are 10 pages worth of elements in our rural_urban object.

Let’s check the first and last page:

str(rural_urban[1], nchar.max = 1600)
 chr "                                                                               2                                                             2\n                                      Age-standardised mean BMI in 1985 (kg/m )                     Age-standardised mean BMI in 2017 (kg/m )\n              Country      Sex\n                                 National               Rural                Urban             National               Rural                Urban\n                        Men      20.2 (17.8-22.7)      19.7 (17.2-22.2)     22.4 (20.0-25.0)   22.8 (20.3-25.3)      22.5 (20.0-25.0)     23.6 (21.0-26.1)\nAfghanistan             Women    20.6 (18.4-22.8)      20.1 (17.8-22.4)     23.2 (20.9-25.4)   24.4 (23.3-25.4)      23.6 (22.5-24.8)     26.3 (25.1-27.4)\n                        Men      25.2 (23.9-26.5)      25.0 (23.7-26.4)     25.4 (24.0-26.7)   27.0 (26.0-27.9)      26.9 (25.9-27.9)     27.0 (26.0-28.0)\nAlbania                 Women    26.0 (24.1-27.9)      26.1 (24.1-28.1)     25.9 (23.9-27.8)   26.0 (24.8-27.2)      26.2 (24.8-27.5)     25.9 (24.6-27.2)\n                        Men      22.1 (20.8-23.3)      21.8 (20.5-23.1)     22.3 (21.0-23.6)   25.1 (24.5-25.7)      24.8 (24.1-25.4)     25.2 (24.6-25.9)\nAlgeria                 Women    24.0 (22.2-25.7)      23.3 (21.4-25.1)     24.8 (22.9-26.6)   27.4 (26.7-28.0)      27.0 (26.3-27.8)     27.5 (26.7-28.2)\n                        Men      33.7 (32.7-34.7)      32.6 (31.7-33.5)     34.0 (32.9-35.1)   34.3 (33.0-35.6)      34.6 (33.1-35.9)     34.2 (32.9-35.6)\nAmerican Samoa          Wo"| __truncated__

Using [1] shows just the first page of the 10 pages that contain the table. Also, notice the new line "\n" values that indicate new lines.

This looks the same as the beginning… how about the end?

str(rural_urban[10], nchar.max = 1800)
 chr "                                                                                                             2                                                                  2\n                                                                Age-standardised mean BMI in 1985 (kg/m )                              Age-standardised mean BMI in 2017 (kg/m )\n              Country                      Sex\n                                                           National               Rural                Urban                      National               Rural                Urban\n                                      Men                  20.0 (18.6-21.4)      19.6 (18.1-21.1)     20.7 (19.2-22.0)            22.2 (21.6-22.9)      21.7 (21.0-22.4)     23.0 (22.3-23.7)\nZambia                                Women                21.4 (20.3-22.6)      20.7 (19.5-21.9)     22.5 (21.3-23.7)            23.9 (23.3-24.5)      22.7 (22.1-23.3)     25.5 (24.9-26.2)\n                                      Men                  21.2 (20.2-22.3)      20.9 (19.8-22.1)     22.1 (21.1-23.1)            22.3 (21.7-22.9)      21.9 (21.2-22.5)     23.3 (22.6-23.9)\nZimbabwe                              Women                24.6 (23.5-25.9)      24.0 (22.8-25.4)     26.5 (25.3-27.8)            25.3 (24.6-26.1)      24.5 (23.7-25.3)     27.1 (26.2-27.9)\n*The entire population live in areas classified as urban (Bermuda, Hong Kong (from 1992 onwards), Nauru and Singapore) or rural (Tokelau).\nNumbers in parentheses show 95% credible intervals.\n\n\n\n\n                                                                                                60\n"

Great! Our rural_urban object looks like it contains the entire Supplementary 3 table, as both the beginning and the end include the data we expected.

Now we will save this imported data for later use like so in a subdirectory of the data directory called imported:

save(rural_urban, file = here::here("data", "imported", "imported_data.rda"))

Data Wrangling


If you have been following along but stopped, we could load our imported data from the “data” directory like so:

load(file = here::here("data", "imported", "imported_data.rda"))

If you skipped the data import section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the imported data using the following code:

imported_data("ocs-bp-rural-and-urban-obesity", outpath = getwd())
load(here::here("OCSdata", "data", "imported", "imported_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found here or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called imported within a subdirectory called data to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "imported", "imported_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



At this point we have large strings now for each page of the table, but this is not very convenient to work with. Now, we will wrangle the data into a more usable form. Ideally, we would like our data to be in some sort of tabular form.

Separate the data into lines


First, it would be useful to separate the data that is currently in 10 giant string chunks into individual lines or rows of the table. To do this we can use the read_lines() function of the readr package which will separate the lines based on the "\n" new line characters.

summary(rural_urban)
   Length     Class      Mode 
       10 character character 
rural_urban <- readr::read_lines(rural_urban)
summary(rural_urban) 
   Length     Class      Mode 
      465 character character 
# now we have 461 lines

We can see that each string in the rural_urban object is now a single line from the table.

For example, line 6 shows the data for women in Afghanistan.

rural_urban[6] 
[1] "Afghanistan             Women    20.6 (18.4-22.8)      20.1 (17.8-22.4)     23.2 (20.9-25.4)   24.4 (23.3-25.4)      23.6 (22.5-24.8)     26.3 (25.1-27.4)"

Removing excess white-space


We also have a lot of white-space. Let’s get rid of the excess white spaces using str_squish() also from the stringr package.

rural_urban <- stringr::str_squish(rural_urban) 
head(rural_urban)
[1] "2 2"                                                                                                                    
[2] "Age-standardised mean BMI in 1985 (kg/m ) Age-standardised mean BMI in 2017 (kg/m )"                                    
[3] "Country Sex"                                                                                                            
[4] "National Rural Urban National Rural Urban"                                                                              
[5] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"              
[6] "Afghanistan Women 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"

Now it is much easier to see the data.

If we look at the part of our data that contains the end of the first page of the table and the start of the second we can see that the header information is repeated, as well as a line with the page number and an empty line, and a line that says “2 2”. The “2 2” comes from importing the PDF file, these are the superscript “2”s in the \(m^2\) strings in the table header - no need to worry about this. You may also notice some values that are na* and we will deal with this later.

rural_urban[44:56] # scroll through!
 [1] "Benin Women 20.6 (19.1-22.0) 20.1 (18.5-21.6) 21.8 (20.3-23.3) 24.3 (23.5-25.0) 23.2 (22.5-24.0) 25.5 (24.7-26.3)"  
 [2] "Men 24.3 (21.8-26.8) na* 24.3 (21.8-26.8) 26.7 (24.3-29.2) na* 26.7 (24.3-29.2)"                                    
 [3] "Bermuda Women 25.4 (22.2-28.6) na* 25.4 (22.2-28.6) 28.5 (25.3-31.6) na* 28.5 (25.3-31.6)"                          
 [4] "Men 20.6 (19.1-22.1) 20.3 (18.7-21.8) 23.1 (21.5-24.6) 23.5 (22.8-24.3) 23.1 (22.2-23.9) 24.2 (23.3-25.2)"          
 [5] "Bhutan Women 20.7 (18.4-22.9) 20.3 (18.0-22.6) 23.0 (20.8-25.3) 24.6 (23.7-25.5) 23.8 (22.7-24.9) 25.9 (24.7-27.0)" 
 [6] ""                                                                                                                   
 [7] "51"                                                                                                                 
 [8] "2 2"                                                                                                                
 [9] "Age-standardised mean BMI in 1985 (kg/m ) Age-standardised mean BMI in 2017 (kg/m )"                                
[10] "Country Sex"                                                                                                        
[11] "National Rural Urban National Rural Urban"                                                                          
[12] "Men 23.3 (21.0-25.5) 22.9 (20.6-25.2) 23.6 (21.3-25.9) 26.1 (23.9-28.4) 25.3 (23.1-27.5) 26.5 (24.2-28.8)"          
[13] "Bolivia Women 23.8 (22.3-25.3) 23.0 (21.4-24.5) 24.6 (23.1-26.1) 27.9 (26.5-29.3) 27.0 (25.6-28.5) 28.3 (26.8-29.7)"

Although the header was necessary on all of the pages of the pdf version of the table, we only need that information once in our data.

Removing unnecessary repeated header information


So, let’s remove all the header information and the page number lines from the rural_urban object, then we will make a single line header for the beginning. One way to do this is to find all lines that include either “Women” or “Men” and only keep this data.

First, let’s see how many lines include “Women” or “Men”. We can do this by first identifying the lines that contain these patterns and then check the length of the resulting vector for the line numbers that contain these patterns. Note, the "|" indicates that we are looking for either “Women” or “Men”.

#scroll through the output!
stringr::str_which(string = rural_urban, 
                   pattern = "Women|Men")
  [1]   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22
 [19]  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40
 [37]  41  42  43  44  45  46  47  48  55  56  57  58  59  60  61  62  63  64
 [55]  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82
 [73]  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98 105 106
 [91] 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
[109] 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
[127] 143 144 145 146 147 148 155 156 157 158 159 160 161 162 163 164 165 166
[145] 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
[163] 185 186 187 188 189 190 191 192 193 194 195 196 197 198 205 206 207 208
[181] 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
[199] 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
[217] 245 246 247 248 255 256 257 258 259 260 261 262 263 264 265 266 267 268
[235] 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
[253] 287 288 289 290 291 292 293 294 295 296 297 298 305 306 307 308 309 310
[271] 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
[289] 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
[307] 347 348 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
[325] 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
[343] 389 390 391 392 393 394 395 396 397 398 405 406 407 408 409 410 411 412
[361] 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
[379] 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
[397] 455 456 457 458

length(stringr::str_which(string = rural_urban, 
                          pattern = "Women|Men"))
[1] 400

Note that str_which() is case sensitive, so it would not work to use “women” as the pattern, and using “men” would return the lines that contain “Women” or “Yemen” etc. Try this out with pattern = "women" and pattern = "men" to see the result!

OK, so this looks correct. This includes most lines but there are gaps where the header is located. It looks like out of the original 461 lines, there are 400 lines in our table that aren’t headers.

rural_urban <- str_subset(string = rural_urban, 
                          pattern = "Women|Men")

We can check our data now using either head() from base R or glimpse() from the tibble and dplyr package. (Yup, the same function is available from both packages!) The head() function shows us the first rows or lines of the data, while the glimpse() function provides us information about the total size of the object and shows us the first line or row.

dplyr::glimpse(rural_urban)
 chr [1:400] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)" ...

Now our rural_urban object 400 lines instead of 461, like it did before when it contained the redundant header information.

head(rural_urban)
[1] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"              
[2] "Afghanistan Women 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"
[3] "Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 (25.9-27.9) 27.0 (26.0-28.0)"              
[4] "Albania Women 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)"    
[5] "Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 (24.1-25.4) 25.2 (24.6-25.9)"              
[6] "Algeria Women 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)"    

Great! So now our data looks much better but we need to add back our header and we would like this to only be a single line to make it easy to transform our data into a table or table-like object.

As a reminder, here is what our header used to look like:

We will add a new header after first dealing with some additional spacing and missing data issues.

Dealing with spacing


First let’s try splitting our header-less data into columns based on spaces using the str_split() function, where we specify that we are splitting the data based on the pattern of a space (the space is included in quotes). Referring to str_split() documentation, the simplify argument is set to FALSE by default, where a list of character vectors is returned. If it is set to TRUE as we will be doing here, a character matrix is returned.

Here, we will take a look at just the first 10 lines using the [1:10,] syntax:

#scroll through the output!
str_split(string = rural_urban, 
          pattern = " ", 
          simplify = TRUE)[1:10,] 
      [,1]          [,2]    [,3]          [,4]          [,5]         
 [1,] "Men"         "20.2"  "(17.8-22.7)" "19.7"        "(17.2-22.2)"
 [2,] "Afghanistan" "Women" "20.6"        "(18.4-22.8)" "20.1"       
 [3,] "Men"         "25.2"  "(23.9-26.5)" "25.0"        "(23.7-26.4)"
 [4,] "Albania"     "Women" "26.0"        "(24.1-27.9)" "26.1"       
 [5,] "Men"         "22.1"  "(20.8-23.3)" "21.8"        "(20.5-23.1)"
 [6,] "Algeria"     "Women" "24.0"        "(22.2-25.7)" "23.3"       
 [7,] "Men"         "33.7"  "(32.7-34.7)" "32.6"        "(31.7-33.5)"
 [8,] "American"    "Samoa" "Women"       "34.3"        "(33.1-35.6)"
 [9,] "Men"         "25.0"  "(22.5-27.4)" "25.3"        "(22.8-27.7)"
[10,] "Andorra"     "Women" "25.2"        "(22.0-28.4)" "25.4"       
      [,6]          [,7]          [,8]          [,9]          [,10]        
 [1,] "22.4"        "(20.0-25.0)" "22.8"        "(20.3-25.3)" "22.5"       
 [2,] "(17.8-22.4)" "23.2"        "(20.9-25.4)" "24.4"        "(23.3-25.4)"
 [3,] "25.4"        "(24.0-26.7)" "27.0"        "(26.0-27.9)" "26.9"       
 [4,] "(24.1-28.1)" "25.9"        "(23.9-27.8)" "26.0"        "(24.8-27.2)"
 [5,] "22.3"        "(21.0-23.6)" "25.1"        "(24.5-25.7)" "24.8"       
 [6,] "(21.4-25.1)" "24.8"        "(22.9-26.6)" "27.4"        "(26.7-28.0)"
 [7,] "34.0"        "(32.9-35.1)" "34.3"        "(33.0-35.6)" "34.6"       
 [8,] "34.1"        "(33.0-35.2)" "34.4"        "(33.0-35.8)" "35.3"       
 [9,] "25.0"        "(22.5-27.4)" "26.8"        "(24.4-29.2)" "26.8"       
[10,] "(22.2-28.7)" "25.2"        "(22.0-28.4)" "25.3"        "(22.1-28.6)"
      [,11]         [,12]         [,13]         [,14]         [,15]        
 [1,] "(20.0-25.0)" "23.6"        "(21.0-26.1)" ""            ""           
 [2,] "23.6"        "(22.5-24.8)" "26.3"        "(25.1-27.4)" ""           
 [3,] "(25.9-27.9)" "27.0"        "(26.0-28.0)" ""            ""           
 [4,] "26.2"        "(24.8-27.5)" "25.9"        "(24.6-27.2)" ""           
 [5,] "(24.1-25.4)" "25.2"        "(24.6-25.9)" ""            ""           
 [6,] "27.0"        "(26.3-27.8)" "27.5"        "(26.7-28.2)" ""           
 [7,] "(33.1-35.9)" "34.2"        "(32.9-35.6)" ""            ""           
 [8,] "(33.7-36.9)" "35.0"        "(33.1-36.9)" "35.4"        "(33.7-37.1)"
 [9,] "(24.3-29.2)" "26.8"        "(24.3-29.3)" ""            ""           
[10,] "25.2"        "(21.9-28.5)" "25.3"        "(22.1-28.6)" ""           
      [,16] [,17] [,18]
 [1,] ""    ""    ""   
 [2,] ""    ""    ""   
 [3,] ""    ""    ""   
 [4,] ""    ""    ""   
 [5,] ""    ""    ""   
 [6,] ""    ""    ""   
 [7,] ""    ""    ""   
 [8,] ""    ""    ""   
 [9,] ""    ""    ""   
[10,] ""    ""    ""   

This almost worked, but unfortunately country names that have spaces will be a problem. For example, we can see that “American Samoa” has been divided into two columns and all subsequent columns are shifted.

Let’s try this another way.

As we can see by looking at our rural_urban data, the country information is only present when the sex is Women. Let’s try to extract the country information from the rows that contain data about Women.

head(rural_urban)
[1] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"              
[2] "Afghanistan Women 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"
[3] "Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 (25.9-27.9) 27.0 (26.0-28.0)"              
[4] "Albania Women 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)"    
[5] "Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 (24.1-25.4) 25.2 (24.6-25.9)"              
[6] "Algeria Women 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)"    

To do this, we start by noticing that the sex always starts with either a capital “W” if the sex is female. Note, we need to use a space before the “W” otherwise we will split some of the country names if the names starts with “W”.

Here, we will also introduce the concept of piping, which uses the %>% from the magrittr package, which allows us to pipe the output from one function to the input for another function. This is really useful when we have multiple steps, which we will show soon.

First, we will select just the data for Women and Men separately:

Women <- str_subset(string = rural_urban, 
                    pattern = "Women") 
Men <- str_subset(string = rural_urban, 
                    pattern = "Men")


head(Women)
[1] "Afghanistan Women 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"   
[2] "Albania Women 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)"       
[3] "Algeria Women 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)"       
[4] "American Samoa Women 34.3 (33.1-35.6) 34.1 (33.0-35.2) 34.4 (33.0-35.8) 35.3 (33.7-36.9) 35.0 (33.1-36.9) 35.4 (33.7-37.1)"
[5] "Andorra Women 25.2 (22.0-28.4) 25.4 (22.2-28.7) 25.2 (22.0-28.4) 25.3 (22.1-28.6) 25.2 (21.9-28.5) 25.3 (22.1-28.6)"       
[6] "Angola Women 21.3 (18.0-24.6) 20.9 (17.6-24.3) 22.7 (19.3-26.0) 24.4 (21.2-27.7) 23.3 (20.0-26.7) 25.7 (22.4-29.1)"        
head(Men)
[1] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"
[2] "Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 (25.9-27.9) 27.0 (26.0-28.0)"
[3] "Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 (24.1-25.4) 25.2 (24.6-25.9)"
[4] "Men 33.7 (32.7-34.7) 32.6 (31.7-33.5) 34.0 (32.9-35.1) 34.3 (33.0-35.6) 34.6 (33.1-35.9) 34.2 (32.9-35.6)"
[5] "Men 25.0 (22.5-27.4) 25.3 (22.8-27.7) 25.0 (22.5-27.4) 26.8 (24.4-29.2) 26.8 (24.3-29.2) 26.8 (24.3-29.3)"
[6] "Men 20.5 (17.9-23.1) 20.2 (17.6-22.9) 21.4 (18.8-24.0) 22.6 (20.0-25.1) 22.0 (19.4-24.6) 23.2 (20.6-25.9)"

And then split the Women data based on the pattern pattern = " Women" (Note that the space before the word “Women” is included - so that it is not within the country data). Here we take our Women dataset and pipe it into the first argument of the str_split() function, where then we can split on the pattern " Women". We then pipe through the unlist() function so that the results are a vector instead of a list. We assign this new result to the country_split object.

country_split <- 
  Women %>%
  stringr::str_split(pattern = " Women") %>%
  unlist() 

head(country_split)
[1] "Afghanistan"                                                                                           
[2] " 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"
[3] "Albania"                                                                                               
[4] " 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)"
[5] "Algeria"                                                                                               
[6] " 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)"

However, we can also do the same thing in a single step, where we replace the Women dataset with the code we used to create that dataset. So first, we select the “Women” and then we split the data by " Women" to create a new dataset all within the same chunk of code.

country_split <- 
  str_subset(string = rural_urban,
             pattern = "Women") %>%
  stringr::str_split(pattern= " Women") %>%
  unlist()

head(country_split)
[1] "Afghanistan"                                                                                           
[2] " 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"
[3] "Albania"                                                                                               
[4] " 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)"
[5] "Algeria"                                                                                               
[6] " 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)"

Now, we can see that Country is always the odd rows and the BMI data for Women is the even rows. We can select the odd rows using the Modulus (Remainder from division) operator. This operator looks like this %%. All odd values such as 3, 7, or 15 when divided by 2 would leave a remainder of 1. We will select just these rows using the filter() function of dplyr.

country <- 
  tibble(country_split) %>% 
  dplyr::filter(row_number() %% 2 == 1) 

We can take a look to make sure that all the country names look as expected. You can scroll through the country names if you hover over the data.

country

                       country_split
1                        Afghanistan
2                            Albania
3                            Algeria
4                     American Samoa
5                            Andorra
6                             Angola
7                Antigua and Barbuda
8                          Argentina
9                            Armenia
10                         Australia
11                           Austria
12                        Azerbaijan
13                           Bahamas
14                           Bahrain
15                        Bangladesh
16                          Barbados
17                           Belarus
18                           Belgium
19                            Belize
20                             Benin
21                           Bermuda
22                            Bhutan
23                           Bolivia
24            Bosnia and Herzegovina
25                          Botswana
26                            Brazil
27                 Brunei Darussalam
28                          Bulgaria
29                      Burkina Faso
30                           Burundi
31                        Cabo Verde
32                          Cambodia
33                          Cameroon
34                            Canada
35          Central African Republic
36                              Chad
37                             Chile
38                             China
39             China (Hong Kong SAR)
40                          Colombia
41                           Comoros
42                             Congo
43                      Cook Islands
44                        Costa Rica
45                     Cote d'Ivoire
46                           Croatia
47                              Cuba
48                            Cyprus
49                    Czech Republic
50                           Denmark
51                          Djibouti
52                          Dominica
53                Dominican Republic
54                          DR Congo
55                           Ecuador
56                             Egypt
57                       El Salvador
58                 Equatorial Guinea
59                           Eritrea
60                           Estonia
61                          Ethiopia
62                              Fiji
63                           Finland
64                            France
65                  French Polynesia
66                             Gabon
67                            Gambia
68                           Georgia
69                           Germany
70                             Ghana
71                            Greece
72                         Greenland
73                           Grenada
74                         Guatemala
75                            Guinea
76                     Guinea Bissau
77                            Guyana
78                             Haiti
79                          Honduras
80                           Hungary
81                           Iceland
82                             India
83                         Indonesia
84                              Iran
85                              Iraq
86                           Ireland
87                            Israel
88                             Italy
89                           Jamaica
90                             Japan
91                            Jordan
92                        Kazakhstan
93                             Kenya
94                          Kiribati
95                            Kuwait
96                        Kyrgyzstan
97                           Lao PDR
98                            Latvia
99                           Lebanon
100                          Lesotho
101                          Liberia
102                            Libya
103                        Lithuania
104                       Luxembourg
105                 Macedonia (TFYR)
106                       Madagascar
107                           Malawi
108                         Malaysia
109                         Maldives
110                             Mali
111                            Malta
112                 Marshall Islands
113                       Mauritania
114                        Mauritius
115                           Mexico
116 Micronesia (Federated States of)
117                          Moldova
118                         Mongolia
119                       Montenegro
120                          Morocco
121                       Mozambique
122                          Myanmar
123                          Namibia
124                            Nauru
125                            Nepal
126                      Netherlands
127                      New Zealand
128                        Nicaragua
129                            Niger
130                          Nigeria
131                             Niue
132                      North Korea
133                           Norway
134   Occupied Palestinian Territory
135                             Oman
136                         Pakistan
137                            Palau
138                           Panama
139                 Papua New Guinea
140                         Paraguay
141                             Peru
142                      Philippines
143                           Poland
144                         Portugal
145                      Puerto Rico
146                            Qatar
147                          Romania
148               Russian Federation
149                           Rwanda
150            Saint Kitts and Nevis
151                      Saint Lucia
152 Saint Vincent and the Grenadines
153                            Samoa
154            Sao Tome and Principe
155                     Saudi Arabia
156                          Senegal
157                           Serbia
158                       Seychelles
159                     Sierra Leone
160                        Singapore
161                         Slovakia
162                         Slovenia
163                  Solomon Islands
164                          Somalia
165                     South Africa
166                      South Korea
167                            Spain
168                        Sri Lanka
169                   Sudan (former)
170                         Suriname
171                        Swaziland
172                           Sweden
173                      Switzerland
174             Syrian Arab Republic
175                           Taiwan
176                       Tajikistan
177                         Tanzania
178                         Thailand
179                      Timor-Leste
180                             Togo
181                          Tokelau
182                            Tonga
183              Trinidad and Tobago
184                          Tunisia
185                           Turkey
186                     Turkmenistan
187                           Tuvalu
188                           Uganda
189                          Ukraine
190             United Arab Emirates
191                   United Kingdom
192         United States of America
193                          Uruguay
194                       Uzbekistan
195                          Vanuatu
196                        Venezuela
197                         Viet Nam
198                            Yemen
199                           Zambia
200                         Zimbabwe

Looks good! We have 200 countries represented in our dataset.

Great! Now we have a list of the countries that can be used for both the male and female data.

Remember the even rows are the rows with the BMI data for women. These row values have a remainder of 0 when divided by 2. Let’s select that data too.

Women_BMI <- 
  tibble(country_split) %>% 
  dplyr::filter(row_number() %% 2 == 0)

head(Women_BMI)
# A tibble: 6 x 1
  country_split                                                                 
  <chr>                                                                         
1 " 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (2~
2 " 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (2~
3 " 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (2~
4 " 34.3 (33.1-35.6) 34.1 (33.0-35.2) 34.4 (33.0-35.8) 35.3 (33.7-36.9) 35.0 (3~
5 " 25.2 (22.0-28.4) 25.4 (22.2-28.7) 25.2 (22.0-28.4) 25.3 (22.1-28.6) 25.2 (2~
6 " 21.3 (18.0-24.6) 20.9 (17.6-24.3) 22.7 (19.3-26.0) 24.4 (21.2-27.7) 23.3 (2~

This looks similar to our Men data (although slightly different):

head(Men)
[1] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"
[2] "Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 (25.9-27.9) 27.0 (26.0-28.0)"
[3] "Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 (24.1-25.4) 25.2 (24.6-25.9)"
[4] "Men 33.7 (32.7-34.7) 32.6 (31.7-33.5) 34.0 (32.9-35.1) 34.3 (33.0-35.6) 34.6 (33.1-35.9) 34.2 (32.9-35.6)"
[5] "Men 25.0 (22.5-27.4) 25.3 (22.8-27.7) 25.0 (22.5-27.4) 26.8 (24.4-29.2) 26.8 (24.3-29.2) 26.8 (24.3-29.3)"
[6] "Men 20.5 (17.9-23.1) 20.2 (17.6-22.9) 21.4 (18.8-24.0) 22.6 (20.0-25.1) 22.0 (19.4-24.6) 23.2 (20.6-25.9)"

Notice how the data in each row is in quotes. This is because these are character strings. We can check this by using the base class() function.

class(Men)
[1] "character"

Whereas the Women_BMI data is a tibble.

class(Women_BMI)
[1] "tbl_df"     "tbl"        "data.frame"

Let’s make the Men data into a tibble as well:

Men <- tibble(Men)

It’s always a good idea to check that your data objects are the size you expect when wrangling. We can do so with the dim() function, which shows us the dimensions of data objects.

dim(Women_BMI)
[1] 200   1
dim(Men)
[1] 200   1
head(Women_BMI)
# A tibble: 6 x 1
  country_split                                                                 
  <chr>                                                                         
1 " 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (2~
2 " 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (2~
3 " 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (2~
4 " 34.3 (33.1-35.6) 34.1 (33.0-35.2) 34.4 (33.0-35.8) 35.3 (33.7-36.9) 35.0 (3~
5 " 25.2 (22.0-28.4) 25.4 (22.2-28.7) 25.2 (22.0-28.4) 25.3 (22.1-28.6) 25.2 (2~
6 " 21.3 (18.0-24.6) 20.9 (17.6-24.3) 22.7 (19.3-26.0) 24.4 (21.2-27.7) 23.3 (2~
head(Men)
# A tibble: 6 x 1
  Men                                                                           
  <chr>                                                                         
1 Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 ~
2 Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 ~
3 Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 ~
4 Men 33.7 (32.7-34.7) 32.6 (31.7-33.5) 34.0 (32.9-35.1) 34.3 (33.0-35.6) 34.6 ~
5 Men 25.0 (22.5-27.4) 25.3 (22.8-27.7) 25.0 (22.5-27.4) 26.8 (24.4-29.2) 26.8 ~
6 Men 20.5 (17.9-23.1) 20.2 (17.6-22.9) 21.4 (18.8-24.0) 22.6 (20.0-25.1) 22.0 ~

Great! There are 200 rows of character strings with the BMI data like we expected to match the 200 countries in our data.

We do however need to add the word " Women" back to the Women_BMI data. To do this we will use the glue() function of the glue package.

Here is a simple example of how this function glues two things together.

where <-"the moon"

glue::glue("the cow jumped over {where}")
the cow jumped over the moon

Now let’s add the word “Women” to each row of the Women_BMI data using glue.

First let’s pull the country_split data out as a vector to make this simpler when we glue. To do this we will use the pull() function of the dplyr package. This function will allow us to extract or pull out the single variable called country_split which contains the BMI values for women from within the tibble called Women_BMI which we just created by grabbing only the even rows of our original country_split tibble.

Then, let’s use the first() function of the dplyr package to get a reminder of what just the first row of the country_split column of the Women_BMI data looks like.

women_data <- Women_BMI %>%
  pull(country_split)

dplyr::first(women_data)
[1] " 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)"

Now we will paste the word "Women" in the beginning of each value in the vector using the glue() function.

Note that there will be a space in between. This is because the space is already in front of the first BMI value.

Women <- glue("Women{women_data}")
head(Women)
Women 20.6 (18.4-22.8) 20.1 (17.8-22.4) 23.2 (20.9-25.4) 24.4 (23.3-25.4) 23.6 (22.5-24.8) 26.3 (25.1-27.4)
Women 26.0 (24.1-27.9) 26.1 (24.1-28.1) 25.9 (23.9-27.8) 26.0 (24.8-27.2) 26.2 (24.8-27.5) 25.9 (24.6-27.2)
Women 24.0 (22.2-25.7) 23.3 (21.4-25.1) 24.8 (22.9-26.6) 27.4 (26.7-28.0) 27.0 (26.3-27.8) 27.5 (26.7-28.2)
Women 34.3 (33.1-35.6) 34.1 (33.0-35.2) 34.4 (33.0-35.8) 35.3 (33.7-36.9) 35.0 (33.1-36.9) 35.4 (33.7-37.1)
Women 25.2 (22.0-28.4) 25.4 (22.2-28.7) 25.2 (22.0-28.4) 25.3 (22.1-28.6) 25.2 (21.9-28.5) 25.3 (22.1-28.6)
Women 21.3 (18.0-24.6) 20.9 (17.6-24.3) 22.7 (19.3-26.0) 24.4 (21.2-27.7) 23.3 (20.0-26.7) 25.7 (22.4-29.1)

You could also do all of this in one step but its less obvious what is happening.

Here we can pull the country_split data out of the Women_BMI object and then paste "Women" in front of each string in one command.

Women <- glue("Women{pull(Women_BMI,country_split)}")

If we try splitting our data by space again, will it have the expected number of columns? What about the rows that contain na* values?

Let’s just take the Men data that contain na* values. This column is called rural_urban.

unknown <- 
  Men%>% 
  filter(str_detect(pattern ="na\\*", 
                    string = Men)) 

head(unknown)
# A tibble: 5 x 1
  Men                                                                           
  <chr>                                                                         
1 Men 24.3 (21.8-26.8) na* 24.3 (21.8-26.8) 26.7 (24.3-29.2) na* 26.7 (24.3-29.~
2 Men 22.4 (21.4-23.3) 21.5 (20.2-22.8) 22.4 (21.5-23.4) 24.8 (23.6-26.1) na* 2~
3 Men 32.6 (32.0-33.2) na* 32.6 (32.0-33.2) 32.9 (31.7-34.1) na* 32.9 (31.7-34.~
4 Men 22.8 (22.2-23.3) na* 22.8 (22.2-23.3) 24.4 (23.6-25.1) na* 24.4 (23.6-25.~
5 Men 29.6 (28.1-31.1) 29.6 (28.1-31.1) na* 32.3 (31.4-33.2) 32.3 (31.4-33.2) n~

Now we can try splitting by a space

tibble::as_tibble(str_split(pull(unknown, Men), " ",
                            simplify = TRUE))
# A tibble: 5 x 12
  V1    V2    V3          V4    V5     V6    V7    V8    V9    V10   V11   V12  
  <chr> <chr> <chr>       <chr> <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Men   24.3  (21.8-26.8) na*   24.3   (21.~ 26.7  (24.~ na*   26.7  (24.~ ""   
2 Men   22.4  (21.4-23.3) 21.5  (20.2~ 22.4  (21.~ 24.8  (23.~ na*   24.8  "(23~
3 Men   32.6  (32.0-33.2) na*   32.6   (32.~ 32.9  (31.~ na*   32.9  (31.~ ""   
4 Men   22.8  (22.2-23.3) na*   22.8   (22.~ 24.4  (23.~ na*   24.4  (23.~ ""   
5 Men   29.6  (28.1-31.1) 29.6  (28.1~ na*   32.3  (31.~ 32.3  (31.~ na*   ""   

So close! Notice that the "na*" values have shifted the subsequent values within the columns because typically there is a space between the BMI and the credible intervals. Here we can see this data in our original pdf:

Dealing with NA values


We need to replace our na* values with something that includes a space so that when we separate our data by space, we will have two values instead of one when we have an na*. Therefore, na* na* should work.

So, in order to use the functions within the stringr package, our Men data needs to be of character class, so let’s check that now.

head(Men)
# A tibble: 6 x 1
  Men                                                                           
  <chr>                                                                         
1 Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 ~
2 Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 ~
3 Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 ~
4 Men 33.7 (32.7-34.7) 32.6 (31.7-33.5) 34.0 (32.9-35.1) 34.3 (33.0-35.6) 34.6 ~
5 Men 25.0 (22.5-27.4) 25.3 (22.8-27.7) 25.0 (22.5-27.4) 26.8 (24.4-29.2) 26.8 ~
6 Men 20.5 (17.9-23.1) 20.2 (17.6-22.9) 21.4 (18.8-24.0) 22.6 (20.0-25.1) 22.0 ~

The <chr> indicates that our Men data is already character. But if this is not the case, we could use the as.character() function that allows us to change factor data within to the character class.

We are also changing the structure so that the data is not within a column called Men.

Men <- pull(Men, Men)
head(Men)
[1] "Men 20.2 (17.8-22.7) 19.7 (17.2-22.2) 22.4 (20.0-25.0) 22.8 (20.3-25.3) 22.5 (20.0-25.0) 23.6 (21.0-26.1)"
[2] "Men 25.2 (23.9-26.5) 25.0 (23.7-26.4) 25.4 (24.0-26.7) 27.0 (26.0-27.9) 26.9 (25.9-27.9) 27.0 (26.0-28.0)"
[3] "Men 22.1 (20.8-23.3) 21.8 (20.5-23.1) 22.3 (21.0-23.6) 25.1 (24.5-25.7) 24.8 (24.1-25.4) 25.2 (24.6-25.9)"
[4] "Men 33.7 (32.7-34.7) 32.6 (31.7-33.5) 34.0 (32.9-35.1) 34.3 (33.0-35.6) 34.6 (33.1-35.9) 34.2 (32.9-35.6)"
[5] "Men 25.0 (22.5-27.4) 25.3 (22.8-27.7) 25.0 (22.5-27.4) 26.8 (24.4-29.2) 26.8 (24.3-29.2) 26.8 (24.3-29.3)"
[6] "Men 20.5 (17.9-23.1) 20.2 (17.6-22.9) 21.4 (18.8-24.0) 22.6 (20.0-25.1) 22.0 (19.4-24.6) 23.2 (20.6-25.9)"
Men <- 
  stringr::str_replace_all(string = Men, 
                           pattern = "na\\*", 
                           replacement = "na\\* na\\*") 

# The \\ are necessary because the * is a special character 
# The * would typically indicate any possible value, 
# but here we actually want a "*" instead
# Thus the double backslash does that for us
# Here we are replacing all occurrences of the na* values 
#(thus str_replace_all instead of str_replace) with na* na*. 

Let’s check that it worked…

Men[20:30]
 [1] "Men 20.7 (19.4-22.1) 20.3 (18.9-21.7) 21.7 (20.3-23.1) 22.7 (22.0-23.3) 22.1 (21.3-22.8) 23.4 (22.7-24.1)"
 [2] "Men 24.3 (21.8-26.8) na* na* 24.3 (21.8-26.8) 26.7 (24.3-29.2) na* na* 26.7 (24.3-29.2)"                  
 [3] "Men 20.6 (19.1-22.1) 20.3 (18.7-21.8) 23.1 (21.5-24.6) 23.5 (22.8-24.3) 23.1 (22.2-23.9) 24.2 (23.3-25.2)"
 [4] "Men 23.3 (21.0-25.5) 22.9 (20.6-25.2) 23.6 (21.3-25.9) 26.1 (23.9-28.4) 25.3 (23.1-27.5) 26.5 (24.2-28.8)"
 [5] "Men 24.9 (23.5-26.2) 24.8 (23.4-26.2) 25.0 (23.6-26.4) 26.8 (25.7-27.8) 26.7 (25.7-27.8) 26.8 (25.7-27.9)"
 [6] "Men 20.6 (19.2-22.0) 20.3 (18.8-21.7) 21.4 (20.0-22.9) 22.6 (21.9-23.4) 21.9 (20.9-22.8) 23.2 (22.3-24.0)"
 [7] "Men 23.3 (22.5-24.0) 22.2 (21.4-23.0) 23.7 (22.9-24.5) 26.2 (25.7-26.7) 25.0 (24.3-25.6) 26.4 (25.9-26.9)"
 [8] "Men 24.0 (22.5-25.4) 23.7 (22.1-25.1) 24.2 (22.7-25.7) 27.1 (26.4-27.7) 26.6 (26.0-27.3) 27.2 (26.5-27.9)"
 [9] "Men 25.0 (23.8-26.4) 24.9 (23.5-26.4) 25.1 (23.8-26.5) 27.0 (25.8-28.1) 27.0 (25.7-28.3) 27.0 (25.7-28.2)"
[10] "Men 20.2 (18.8-21.5) 20.0 (18.6-21.4) 21.4 (19.9-22.9) 22.2 (21.4-23.0) 21.8 (21.0-22.6) 23.1 (22.1-24.1)"
[11] "Men 20.2 (17.7-22.6) 20.1 (17.6-22.5) 21.2 (18.7-23.6) 22.1 (19.8-24.5) 22.0 (19.6-24.3) 23.2 (20.8-25.6)"

Great!

Now for the Women data object

class(Women)
[1] "glue"      "character"
# the female data is already character type
Women <- 
  stringr::str_replace_all(string = Women, 
                           pattern = "na\\*", 
                           replacement = "na\\* na\\*") 
Women[20:30]
 [1] "Women 20.6 (19.1-22.0) 20.1 (18.5-21.6) 21.8 (20.3-23.3) 24.3 (23.5-25.0) 23.2 (22.5-24.0) 25.5 (24.7-26.3)"
 [2] "Women 25.4 (22.2-28.6) na* na* 25.4 (22.2-28.6) 28.5 (25.3-31.6) na* na* 28.5 (25.3-31.6)"                  
 [3] "Women 20.7 (18.4-22.9) 20.3 (18.0-22.6) 23.0 (20.8-25.3) 24.6 (23.7-25.5) 23.8 (22.7-24.9) 25.9 (24.7-27.0)"
 [4] "Women 23.8 (22.3-25.3) 23.0 (21.4-24.5) 24.6 (23.1-26.1) 27.9 (26.5-29.3) 27.0 (25.6-28.5) 28.3 (26.8-29.7)"
 [5] "Women 25.5 (23.5-27.4) 25.7 (23.7-27.7) 25.1 (23.1-27.0) 25.7 (24.4-27.1) 26.0 (24.6-27.5) 25.3 (23.9-26.8)"
 [6] "Women 24.2 (22.2-26.2) 23.7 (21.5-25.8) 25.6 (23.5-27.7) 26.2 (25.3-27.1) 25.0 (23.8-26.2) 27.0 (26.0-28.2)"
 [7] "Women 24.0 (23.2-25.0) 23.1 (22.1-24.1) 24.4 (23.5-25.4) 26.9 (26.2-27.5) 26.4 (25.7-27.2) 26.9 (26.3-27.6)"
 [8] "Women 23.6 (21.4-25.6) 22.9 (20.7-25.0) 24.0 (21.8-26.1) 27.4 (26.6-28.1) 27.0 (26.1-27.8) 27.5 (26.7-28.2)"
 [9] "Women 25.2 (23.4-27.1) 25.5 (23.4-27.6) 25.0 (23.1-26.9) 25.6 (24.1-27.1) 26.0 (24.2-27.8) 25.5 (23.9-27.1)"
[10] "Women 19.9 (18.7-21.2) 19.6 (18.4-20.9) 22.0 (20.7-23.3) 22.2 (21.4-23.1) 21.1 (20.2-22.0) 24.7 (23.7-25.7)"
[11] "Women 19.6 (17.5-21.7) 19.5 (17.4-21.6) 21.7 (19.5-23.8) 21.1 (20.3-22.0) 20.7 (19.8-21.6) 24.0 (23.0-24.9)"

Great, now we can split our data by spaces.

Splitting the data


We will split our data finally by spaces using the str_split() function of the stringr package. We can check the rows with NA values by looking at rows 20-30.

Men <- tibble::as_tibble(str_split(Men, " ", 
                                   simplify = TRUE))
Men[20:30,]
# A tibble: 11 x 13
   V1    V2    V3    V4    V5    V6    V7    V8    V9    V10   V11   V12   V13  
   <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Men   20.7  (19.~ 20.3  (18.~ 21.7  (20.~ 22.7  (22.~ 22.1  (21.~ 23.4  (22.~
 2 Men   24.3  (21.~ na*   na*   24.3  (21.~ 26.7  (24.~ na*   na*   26.7  (24.~
 3 Men   20.6  (19.~ 20.3  (18.~ 23.1  (21.~ 23.5  (22.~ 23.1  (22.~ 24.2  (23.~
 4 Men   23.3  (21.~ 22.9  (20.~ 23.6  (21.~ 26.1  (23.~ 25.3  (23.~ 26.5  (24.~
 5 Men   24.9  (23.~ 24.8  (23.~ 25.0  (23.~ 26.8  (25.~ 26.7  (25.~ 26.8  (25.~
 6 Men   20.6  (19.~ 20.3  (18.~ 21.4  (20.~ 22.6  (21.~ 21.9  (20.~ 23.2  (22.~
 7 Men   23.3  (22.~ 22.2  (21.~ 23.7  (22.~ 26.2  (25.~ 25.0  (24.~ 26.4  (25.~
 8 Men   24.0  (22.~ 23.7  (22.~ 24.2  (22.~ 27.1  (26.~ 26.6  (26.~ 27.2  (26.~
 9 Men   25.0  (23.~ 24.9  (23.~ 25.1  (23.~ 27.0  (25.~ 27.0  (25.~ 27.0  (25.~
10 Men   20.2  (18.~ 20.0  (18.~ 21.4  (19.~ 22.2  (21.~ 21.8  (21.~ 23.1  (22.~
11 Men   20.2  (17.~ 20.1  (17.~ 21.2  (18.~ 22.1  (19.~ 22.0  (19.~ 23.2  (20.~

Great! It looks good!

Now for the Women data.

Women <- as_tibble(str_split(Women, " ", 
                             simplify = TRUE))
head(Women)
# A tibble: 6 x 13
  V1    V2    V3     V4    V5    V6    V7    V8    V9    V10   V11   V12   V13  
  <chr> <chr> <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Women 20.6  (18.4~ 20.1  (17.~ 23.2  (20.~ 24.4  (23.~ 23.6  (22.~ 26.3  (25.~
2 Women 26.0  (24.1~ 26.1  (24.~ 25.9  (23.~ 26.0  (24.~ 26.2  (24.~ 25.9  (24.~
3 Women 24.0  (22.2~ 23.3  (21.~ 24.8  (22.~ 27.4  (26.~ 27.0  (26.~ 27.5  (26.~
4 Women 34.3  (33.1~ 34.1  (33.~ 34.4  (33.~ 35.3  (33.~ 35.0  (33.~ 35.4  (33.~
5 Women 25.2  (22.0~ 25.4  (22.~ 25.2  (22.~ 25.3  (22.~ 25.2  (21.~ 25.3  (22.~
6 Women 21.3  (18.0~ 20.9  (17.~ 22.7  (19.~ 24.4  (21.~ 23.3  (20.~ 25.7  (22.~
Women[20:30,] 
# A tibble: 11 x 13
   V1    V2    V3    V4    V5    V6    V7    V8    V9    V10   V11   V12   V13  
   <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 Women 20.6  (19.~ 20.1  (18.~ 21.8  (20.~ 24.3  (23.~ 23.2  (22.~ 25.5  (24.~
 2 Women 25.4  (22.~ na*   na*   25.4  (22.~ 28.5  (25.~ na*   na*   28.5  (25.~
 3 Women 20.7  (18.~ 20.3  (18.~ 23.0  (20.~ 24.6  (23.~ 23.8  (22.~ 25.9  (24.~
 4 Women 23.8  (22.~ 23.0  (21.~ 24.6  (23.~ 27.9  (26.~ 27.0  (25.~ 28.3  (26.~
 5 Women 25.5  (23.~ 25.7  (23.~ 25.1  (23.~ 25.7  (24.~ 26.0  (24.~ 25.3  (23.~
 6 Women 24.2  (22.~ 23.7  (21.~ 25.6  (23.~ 26.2  (25.~ 25.0  (23.~ 27.0  (26.~
 7 Women 24.0  (23.~ 23.1  (22.~ 24.4  (23.~ 26.9  (26.~ 26.4  (25.~ 26.9  (26.~
 8 Women 23.6  (21.~ 22.9  (20.~ 24.0  (21.~ 27.4  (26.~ 27.0  (26.~ 27.5  (26.~
 9 Women 25.2  (23.~ 25.5  (23.~ 25.0  (23.~ 25.6  (24.~ 26.0  (24.~ 25.5  (23.~
10 Women 19.9  (18.~ 19.6  (18.~ 22.0  (20.~ 22.2  (21.~ 21.1  (20.~ 24.7  (23.~
11 Women 19.6  (17.~ 19.5  (17.~ 21.7  (19.~ 21.1  (20.~ 20.7  (19.~ 24.0  (23.~

We can see that our na values look correct.

Looks good!

Adding new header


We can see from our pdf and our object called header what the header was like in the original pdf document. We need to add column names to our data based on the original data.

We’ll wait to add Country to the header until we add the country information to our data.

new_header <- c("Sex","National_BMI_1985", 
                "National_BMI_1985_CI", "Rural_BMI_1985", 
                "Rural_BMI_1985_CI", "Urban_BMI_1985",
                "Urban_BMI_1985_CI", "National_BMI_2017",
                "National_BMI_2017_CI","Rural_BMI_2017",
                "Rural_BMI_2017_CI", "Urban_BMI_2017", 
                "Urban_BMI_2017_CI")

Let’s change the names of our columns of our tibbles to this new header for our Men and Women data

names(Women) <- new_header
names(Men) <- new_header

head(Women)
# A tibble: 6 x 13
  Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985 Rural_BMI_1985_CI
  <chr> <chr>             <chr>                <chr>          <chr>            
1 Women 20.6              (18.4-22.8)          20.1           (17.8-22.4)      
2 Women 26.0              (24.1-27.9)          26.1           (24.1-28.1)      
3 Women 24.0              (22.2-25.7)          23.3           (21.4-25.1)      
4 Women 34.3              (33.1-35.6)          34.1           (33.0-35.2)      
5 Women 25.2              (22.0-28.4)          25.4           (22.2-28.7)      
6 Women 21.3              (18.0-24.6)          20.9           (17.6-24.3)      
# ... with 8 more variables: Urban_BMI_1985 <chr>, Urban_BMI_1985_CI <chr>,
#   National_BMI_2017 <chr>, National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>,
#   Rural_BMI_2017_CI <chr>, Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>
head(Men)
# A tibble: 6 x 13
  Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985 Rural_BMI_1985_CI
  <chr> <chr>             <chr>                <chr>          <chr>            
1 Men   20.2              (17.8-22.7)          19.7           (17.2-22.2)      
2 Men   25.2              (23.9-26.5)          25.0           (23.7-26.4)      
3 Men   22.1              (20.8-23.3)          21.8           (20.5-23.1)      
4 Men   33.7              (32.7-34.7)          32.6           (31.7-33.5)      
5 Men   25.0              (22.5-27.4)          25.3           (22.8-27.7)      
6 Men   20.5              (17.9-23.1)          20.2           (17.6-22.9)      
# ... with 8 more variables: Urban_BMI_1985 <chr>, Urban_BMI_1985_CI <chr>,
#   National_BMI_2017 <chr>, National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>,
#   Rural_BMI_2017_CI <chr>, Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Now we will add our country data to both our Men and Women tibbles using the bind_cols() function. This will add the country as a new column to the Women data object on the left.

Women <- dplyr::bind_cols(country, Women)
head(Women)
# A tibble: 6 x 14
  country_split  Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>          <chr> <chr>             <chr>                <chr>         
1 Afghanistan    Women 20.6              (18.4-22.8)          20.1          
2 Albania        Women 26.0              (24.1-27.9)          26.1          
3 Algeria        Women 24.0              (22.2-25.7)          23.3          
4 American Samoa Women 34.3              (33.1-35.6)          34.1          
5 Andorra        Women 25.2              (22.0-28.4)          25.4          
6 Angola         Women 21.3              (18.0-24.6)          20.9          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Similarly, for the data about Men, this will add the country as a new column to the Men data object on the left.

Men <- dplyr::bind_cols(country, Men) 
head(Men)
# A tibble: 6 x 14
  country_split  Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>          <chr> <chr>             <chr>                <chr>         
1 Afghanistan    Men   20.2              (17.8-22.7)          19.7          
2 Albania        Men   25.2              (23.9-26.5)          25.0          
3 Algeria        Men   22.1              (20.8-23.3)          21.8          
4 American Samoa Men   33.7              (32.7-34.7)          32.6          
5 Andorra        Men   25.0              (22.5-27.4)          25.3          
6 Angola         Men   20.5              (17.9-23.1)          20.2          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Changing variable names


Here, we will change the variable name in the country dataset to country (currently it is called country_split). We will also introduce the concept of the assignment pipe. In this case our pipe operator looks like this %<>%. Using this additional pipe requires another package, called magrittr. The other simpler pipe options from this package are loaded with tidyverse (if you used library(tidyverse) which loads most tidyverse packages), but not this version.

The > portion of the pipe still behaves like a normal pipe, while the < portion of the pipe makes an assignment to whatever the <is pointing to, just like when we use the typical assignment operator <-.

# library(magrittr) 
# We can't use the `%<>%` unless we load the magrittr package
# We have already done this but we include this for illustrative purposes.
# Here we will just use the traditional assignment strategy

Women <- dplyr::rename(Women, Country = country_split) 

Here, we have renamed the country_split variable to be called Country.

Here, we reassign Men using the pipe strategy.

Men %<>% rename(Country = country_split) 

This is equivalent to

Men <- rename(Men, Country = country_split)

We have renamed the country_split variable to to be called Country. We have also reassigned Men to the same data object. which has the Country variable renamed.

Let’s take a look at our data for Men and Women:

head(Women)
# A tibble: 6 x 14
  Country        Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>          <chr> <chr>             <chr>                <chr>         
1 Afghanistan    Women 20.6              (18.4-22.8)          20.1          
2 Albania        Women 26.0              (24.1-27.9)          26.1          
3 Algeria        Women 24.0              (22.2-25.7)          23.3          
4 American Samoa Women 34.3              (33.1-35.6)          34.1          
5 Andorra        Women 25.2              (22.0-28.4)          25.4          
6 Angola         Women 21.3              (18.0-24.6)          20.9          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>
head(Men)
# A tibble: 6 x 14
  Country        Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>          <chr> <chr>             <chr>                <chr>         
1 Afghanistan    Men   20.2              (17.8-22.7)          19.7          
2 Albania        Men   25.2              (23.9-26.5)          25.0          
3 Algeria        Men   22.1              (20.8-23.3)          21.8          
4 American Samoa Men   33.7              (32.7-34.7)          32.6          
5 Andorra        Men   25.0              (22.5-27.4)          25.3          
6 Angola         Men   20.5              (17.9-23.1)          20.2          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Joining the data


Now that both of these tibbles have the same structure, we can combine our Men and Women data using the bind_rows() function of dplyr. This will combine all of the rows based on their similar column structure. All of the male data will be first and all the female data will be second.

BMI <- dplyr::bind_rows(Men, Women)

Let’s check the size of our BMI data… it should have 400 rows (obs). We can see the end of the BMI data using the tail() function:

str(BMI)
tibble [400 x 14] (S3: tbl_df/tbl/data.frame)
 $ Country             : chr [1:400] "Afghanistan" "Albania" "Algeria" "American Samoa" ...
 $ Sex                 : chr [1:400] "Men" "Men" "Men" "Men" ...
 $ National_BMI_1985   : chr [1:400] "20.2" "25.2" "22.1" "33.7" ...
 $ National_BMI_1985_CI: chr [1:400] "(17.8-22.7)" "(23.9-26.5)" "(20.8-23.3)" "(32.7-34.7)" ...
 $ Rural_BMI_1985      : chr [1:400] "19.7" "25.0" "21.8" "32.6" ...
 $ Rural_BMI_1985_CI   : chr [1:400] "(17.2-22.2)" "(23.7-26.4)" "(20.5-23.1)" "(31.7-33.5)" ...
 $ Urban_BMI_1985      : chr [1:400] "22.4" "25.4" "22.3" "34.0" ...
 $ Urban_BMI_1985_CI   : chr [1:400] "(20.0-25.0)" "(24.0-26.7)" "(21.0-23.6)" "(32.9-35.1)" ...
 $ National_BMI_2017   : chr [1:400] "22.8" "27.0" "25.1" "34.3" ...
 $ National_BMI_2017_CI: chr [1:400] "(20.3-25.3)" "(26.0-27.9)" "(24.5-25.7)" "(33.0-35.6)" ...
 $ Rural_BMI_2017      : chr [1:400] "22.5" "26.9" "24.8" "34.6" ...
 $ Rural_BMI_2017_CI   : chr [1:400] "(20.0-25.0)" "(25.9-27.9)" "(24.1-25.4)" "(33.1-35.9)" ...
 $ Urban_BMI_2017      : chr [1:400] "23.6" "27.0" "25.2" "34.2" ...
 $ Urban_BMI_2017_CI   : chr [1:400] "(21.0-26.1)" "(26.0-28.0)" "(24.6-25.9)" "(32.9-35.6)" ...
tail(BMI)
# A tibble: 6 x 14
  Country   Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>     <chr> <chr>             <chr>                <chr>         
1 Vanuatu   Women 24.4              (22.7-26.1)          24.2          
2 Venezuela Women 24.0              (22.2-25.9)          22.6          
3 Viet Nam  Women 18.5              (17.7-19.4)          18.2          
4 Yemen     Women 20.5              (18.9-22.0)          20.0          
5 Zambia    Women 21.4              (20.3-22.6)          20.7          
6 Zimbabwe  Women 24.6              (23.5-25.9)          24.0          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Sorting the data


Now, let’s sort the data alphabetically by Country using the arrange() function from the dplyr package:

BMI <- dplyr::arrange(BMI, Country)
head(BMI)
# A tibble: 6 x 14
  Country     Sex   National_BMI_1985 National_BMI_1985_CI Rural_BMI_1985
  <chr>       <chr> <chr>             <chr>                <chr>         
1 Afghanistan Men   20.2              (17.8-22.7)          19.7          
2 Afghanistan Women 20.6              (18.4-22.8)          20.1          
3 Albania     Men   25.2              (23.9-26.5)          25.0          
4 Albania     Women 26.0              (24.1-27.9)          26.1          
5 Algeria     Men   22.1              (20.8-23.3)          21.8          
6 Algeria     Women 24.0              (22.2-25.7)          23.3          
# ... with 9 more variables: Rural_BMI_1985_CI <chr>, Urban_BMI_1985 <chr>,
#   Urban_BMI_1985_CI <chr>, National_BMI_2017 <chr>,
#   National_BMI_2017_CI <chr>, Rural_BMI_2017 <chr>, Rural_BMI_2017_CI <chr>,
#   Urban_BMI_2017 <chr>, Urban_BMI_2017_CI <chr>

Our data is looking great!

Now, we might want to make sure that our observations for each variable look the way we want. In other words, if we want to make plots about National BMI in 1985 then we would need our values to be numeric. Looking at our BMI data using str(), we can see the type of data for each of our variables listed just after variable name and the ":" colon.

str(BMI)
tibble [400 x 14] (S3: tbl_df/tbl/data.frame)
 $ Country             : chr [1:400] "Afghanistan" "Afghanistan" "Albania" "Albania" ...
 $ Sex                 : chr [1:400] "Men" "Women" "Men" "Women" ...
 $ National_BMI_1985   : chr [1:400] "20.2" "20.6" "25.2" "26.0" ...
 $ National_BMI_1985_CI: chr [1:400] "(17.8-22.7)" "(18.4-22.8)" "(23.9-26.5)" "(24.1-27.9)" ...
 $ Rural_BMI_1985      : chr [1:400] "19.7" "20.1" "25.0" "26.1" ...
 $ Rural_BMI_1985_CI   : chr [1:400] "(17.2-22.2)" "(17.8-22.4)" "(23.7-26.4)" "(24.1-28.1)" ...
 $ Urban_BMI_1985      : chr [1:400] "22.4" "23.2" "25.4" "25.9" ...
 $ Urban_BMI_1985_CI   : chr [1:400] "(20.0-25.0)" "(20.9-25.4)" "(24.0-26.7)" "(23.9-27.8)" ...
 $ National_BMI_2017   : chr [1:400] "22.8" "24.4" "27.0" "26.0" ...
 $ National_BMI_2017_CI: chr [1:400] "(20.3-25.3)" "(23.3-25.4)" "(26.0-27.9)" "(24.8-27.2)" ...
 $ Rural_BMI_2017      : chr [1:400] "22.5" "23.6" "26.9" "26.2" ...
 $ Rural_BMI_2017_CI   : chr [1:400] "(20.0-25.0)" "(22.5-24.8)" "(25.9-27.9)" "(24.8-27.5)" ...
 $ Urban_BMI_2017      : chr [1:400] "23.6" "26.3" "27.0" "25.9" ...
 $ Urban_BMI_2017_CI   : chr [1:400] "(21.0-26.1)" "(25.1-27.4)" "(26.0-28.0)" "(24.6-27.2)" ...

Looks like none of our BMI data is actually numeric (or num), but of the class character (or chr). Let’s change that now.

To start, we could change these values to be numeric with 6 lines of code like this, where the double brackets[[]] and quotes are used to select the specific variable within the BMI tibble. In this case we need to use the brackets instead of pull() because we can’t start a command with pull() to reassign values in a variable.

#pull(BMI, National_BMI_1985)%<>% as.numeric #This will not work
BMI[["National_BMI_1985"]] %<>% as.numeric
BMI[["Rural_BMI_1985"]] %<>% as.numeric
BMI[["Urban_BMI_1985"]] %<>% as.numeric

BMI[["National_BMI_2017"]] %<>% as.numeric
BMI[["Rural_BMI_2017"]] %<>% as.numeric
BMI[["Urban_BMI_2017"]] %<>% as.numeric

And if we did this we would see these variables show as "num" which stands for a numeric type of variable.

str(BMI)
tibble [400 x 14] (S3: tbl_df/tbl/data.frame)
 $ Country             : chr [1:400] "Afghanistan" "Afghanistan" "Albania" "Albania" ...
 $ Sex                 : chr [1:400] "Men" "Women" "Men" "Women" ...
 $ National_BMI_1985   : num [1:400] 20.2 20.6 25.2 26 22.1 24 33.7 34.3 25 25.2 ...
 $ National_BMI_1985_CI: chr [1:400] "(17.8-22.7)" "(18.4-22.8)" "(23.9-26.5)" "(24.1-27.9)" ...
 $ Rural_BMI_1985      : num [1:400] 19.7 20.1 25 26.1 21.8 23.3 32.6 34.1 25.3 25.4 ...
 $ Rural_BMI_1985_CI   : chr [1:400] "(17.2-22.2)" "(17.8-22.4)" "(23.7-26.4)" "(24.1-28.1)" ...
 $ Urban_BMI_1985      : num [1:400] 22.4 23.2 25.4 25.9 22.3 24.8 34 34.4 25 25.2 ...
 $ Urban_BMI_1985_CI   : chr [1:400] "(20.0-25.0)" "(20.9-25.4)" "(24.0-26.7)" "(23.9-27.8)" ...
 $ National_BMI_2017   : num [1:400] 22.8 24.4 27 26 25.1 27.4 34.3 35.3 26.8 25.3 ...
 $ National_BMI_2017_CI: chr [1:400] "(20.3-25.3)" "(23.3-25.4)" "(26.0-27.9)" "(24.8-27.2)" ...
 $ Rural_BMI_2017      : num [1:400] 22.5 23.6 26.9 26.2 24.8 27 34.6 35 26.8 25.2 ...
 $ Rural_BMI_2017_CI   : chr [1:400] "(20.0-25.0)" "(22.5-24.8)" "(25.9-27.9)" "(24.8-27.5)" ...
 $ Urban_BMI_2017      : num [1:400] 23.6 26.3 27 25.9 25.2 27.5 34.2 35.4 26.8 25.3 ...
 $ Urban_BMI_2017_CI   : chr [1:400] "(21.0-26.1)" "(25.1-27.4)" "(26.0-28.0)" "(24.6-27.2)" ...

Or we can use a more automated way with the map() function of the purrr package:

BMI_numeric <- 
  purrr::map((BMI %>% 
                select(-matches("CI|Sex|Country"))),
             as.numeric) %>% 
  as_tibble()

The map() function of the purrr package allows us to apply the as.numeric() function to all the selected columns of BMI dataset. Above, we have selected those that don’t contain CI, Sex, or Country in the column name. If you are familiar with apply(), this function is very similar.

Now we need to add the Country and Sex variables back to our new numeric dataset. To do this we will use the mutate() function from the dplyr package. This function allows us to create and modify variables.

BMI_numeric %<>% 
  mutate(Country = pull(BMI, Country), 
         Sex = pull(BMI, Sex))
head(BMI_numeric)
# A tibble: 6 x 8
  National_BMI_19~ Rural_BMI_1985 Urban_BMI_1985 National_BMI_20~ Rural_BMI_2017
             <dbl>          <dbl>          <dbl>            <dbl>          <dbl>
1             20.2           19.7           22.4             22.8           22.5
2             20.6           20.1           23.2             24.4           23.6
3             25.2           25             25.4             27             26.9
4             26             26.1           25.9             26             26.2
5             22.1           21.8           22.3             25.1           24.8
6             24             23.3           24.8             27.4           27  
# ... with 3 more variables: Urban_BMI_2017 <dbl>, Country <chr>, Sex <chr>

In this case our numeric data is of a class dbl. This means double-precision floating point numbers (with decimals). The alternative numeric option is the integer class.

Transforming to tidy data


It is generally useful to get the data in what is called long format for other analyses, and particularly for plotting.

For a more detailed description about this, please see the Data Visualization section of this case study.

Basically, the long format has more rows and fewer columns. Thus, we can combine some columns together. In our case, we can put all the different BMI data together in one long column. We will create a new column that tells us what each row of the new BMI column represents. In other words, it will tell us what the original column was.

To do this we will use the pivot_longer() function of the tidyr package. The data argument specifies what data we want to work with, the cols argument specifies the names of the columns or variables that we want to combine and change to one long format variable, and the names_to argument specifies the name for the new variable that we are creating. The : is used to indicate that we want to combine the columns or variables between National_BMI_1985 and Urban_BMI_2017.

BMI_long <-
  tidyr::pivot_longer(data = BMI_numeric, 
                      cols = National_BMI_1985:Urban_BMI_2017, 
                      names_to = "class_BMI")

head(BMI_long)
# A tibble: 6 x 4
  Country     Sex   class_BMI         value
  <chr>       <chr> <chr>             <dbl>
1 Afghanistan Men   National_BMI_1985  20.2
2 Afghanistan Men   Rural_BMI_1985     19.7
3 Afghanistan Men   Urban_BMI_1985     22.4
4 Afghanistan Men   National_BMI_2017  22.8
5 Afghanistan Men   Rural_BMI_2017     22.5
6 Afghanistan Men   Urban_BMI_2017     23.6

We really want to promote the idea of working with tidy data. Tidy data is data that is structured in a way that makes it especially usable. One of the rules of tidy data, is that each column or variable should have only one type of data. Currently we have two types of data in the class_BMI variable: the type of BMI measurement ( Rural, Urban, or National), and the year (1985 and 2017). We can separate these two data types of the class_BMI column using the separate() function of the tidyr package to replace the class_BMI column with two new columns.

BMI_long %<>% 
  tidyr::separate(class_BMI, 
                  c("Region", NA, "Year")) 

Here, we separate the class_BMI column of the BMI_long tibble. This is based on the underscores (“_”) and creates two new columns. The first column will be called Region. It will contain the 1st part of the class_BMI data before the 1st underscore. The 2nd column will be called Year. It will contain the 3rd part of the data based on underscores. The NA in our code is for removing the middle part of the column (“BMI”). Thus, this part will not be used to create any new columns.

head(BMI_long)
# A tibble: 6 x 5
  Country     Sex   Region   Year  value
  <chr>       <chr> <chr>    <chr> <dbl>
1 Afghanistan Men   National 1985   20.2
2 Afghanistan Men   Rural    1985   19.7
3 Afghanistan Men   Urban    1985   22.4
4 Afghanistan Men   National 2017   22.8
5 Afghanistan Men   Rural    2017   22.5
6 Afghanistan Men   Urban    2017   23.6

Let’s see how the dimensions of the BMI data have changed in long format:

dim(BMI_numeric)
[1] 400   8
dim(BMI_long)
[1] 2400    5

Let’s rename the value column to something more informative. We can use the rename() function of the dplyr package to do this. Notice that the value we want to replace is listed second after the = sign.

BMI_long %<>% 
  rename(BMI = value)
head(BMI_long)
# A tibble: 6 x 5
  Country     Sex   Region   Year    BMI
  <chr>       <chr> <chr>    <chr> <dbl>
1 Afghanistan Men   National 1985   20.2
2 Afghanistan Men   Rural    1985   19.7
3 Afghanistan Men   Urban    1985   22.4
4 Afghanistan Men   National 2017   22.8
5 Afghanistan Men   Rural    2017   22.5
6 Afghanistan Men   Urban    2017   23.6

Question opportunity: Why exactly are there 2,400 rows now?

Great! Our data is very usable now in this format! Now let’s save our wrangled data for future use. We will save it as an r compatible file (a .rda file), as well as csv files, as these are useful to give to collaborators. We will use the write_csv() function of the readr package to do this.This requires that each tibble be saved one at a time.

save(BMI_numeric, 
     BMI_long, 
     file = here::here("data",  "wrangled", "wrangled_data.rda"))
readr::write_csv(BMI_numeric, 
                 path = here::here("data", "wrangled", "BMI_numeric.csv"))
readr::write_csv(BMI_long, 
                 path = here::here("data", "wrangled", "BMI_long.csv"))

Data Exploration


If you have been following along but stopped, we could load our wrangled data like so:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data wrangling section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data using the following code:

wrangled_rda("ocs-bp-rural-and-urban-obesity", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found here or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



Now that our data is clean and in a format that we can work with, we can start to take a look at the data and how different groups might compare.

Going back to one of our original questions,

Is there a difference between rural and urban BMI estimates around the world?

We want to use a statistical test to identify if there are differences in the average BMI estimates between e.g. rural and urban groups. First, let’s explore our data a bit.

General summary


summary(BMI_numeric)
 National_BMI_1985 Rural_BMI_1985 Urban_BMI_1985  National_BMI_2017
 Min.   :18.20     Min.   :17.7   Min.   :19.30   Min.   :20.20    
 1st Qu.:21.50     1st Qu.:20.9   1st Qu.:22.43   1st Qu.:24.07    
 Median :24.00     Median :23.4   Median :24.30   Median :26.15    
 Mean   :23.68     Mean   :23.3   Mean   :24.24   Mean   :26.00    
 3rd Qu.:25.23     3rd Qu.:25.1   3rd Qu.:25.40   3rd Qu.:27.43    
 Max.   :34.30     Max.   :34.1   Max.   :34.40   Max.   :35.30    
                   NA's   :6      NA's   :2                        
 Rural_BMI_2017  Urban_BMI_2017    Country              Sex           
 Min.   :19.80   Min.   :21.50   Length:400         Length:400        
 1st Qu.:23.30   1st Qu.:24.80   Class :character   Class :character  
 Median :26.00   Median :26.30   Mode  :character   Mode  :character  
 Mean   :25.61   Mean   :26.37                                        
 3rd Qu.:27.30   3rd Qu.:27.60                                        
 Max.   :35.00   Max.   :35.40                                        
 NA's   :8       NA's   :2                                            

We can see that the mean BMI estimates are larger in 2017. However, is this a meaningful increase? This is difficult to determine without a statistical test.

Lets look at the data stratified by Sex:

BMI_numeric %>% 
  filter(Sex == "Women") %>% 
  summary()
 National_BMI_1985 Rural_BMI_1985  Urban_BMI_1985  National_BMI_2017
 Min.   :18.20     Min.   :17.70   Min.   :19.70   Min.   :21.00    
 1st Qu.:22.00     1st Qu.:21.20   1st Qu.:22.85   1st Qu.:24.40    
 Median :24.25     Median :23.90   Median :24.60   Median :26.10    
 Mean   :24.00     Mean   :23.59   Mean   :24.63   Mean   :26.38    
 3rd Qu.:25.50     3rd Qu.:25.40   3rd Qu.:25.60   3rd Qu.:28.00    
 Max.   :34.30     Max.   :34.10   Max.   :34.40   Max.   :35.30    
                   NA's   :3       NA's   :1                        
 Rural_BMI_2017  Urban_BMI_2017    Country              Sex           
 Min.   :20.40   Min.   :21.60   Length:200         Length:200        
 1st Qu.:23.70   1st Qu.:25.30   Class :character   Class :character  
 Median :26.20   Median :26.40   Mode  :character   Mode  :character  
 Mean   :25.98   Mean   :26.84                                        
 3rd Qu.:27.60   3rd Qu.:28.25                                        
 Max.   :35.00   Max.   :35.40                                        
 NA's   :4       NA's   :1                                            
BMI_numeric %>% 
  filter(Sex == "Men") %>% 
  summary()
 National_BMI_1985 Rural_BMI_1985  Urban_BMI_1985  National_BMI_2017
 Min.   :18.50     Min.   :18.40   Min.   :19.30   Min.   :20.20    
 1st Qu.:21.10     1st Qu.:20.80   1st Qu.:22.10   1st Qu.:23.30    
 Median :23.60     Median :23.20   Median :24.10   Median :26.20    
 Mean   :23.36     Mean   :23.01   Mean   :23.84   Mean   :25.61    
 3rd Qu.:25.00     3rd Qu.:24.90   3rd Qu.:25.10   3rd Qu.:27.10    
 Max.   :33.70     Max.   :32.60   Max.   :34.00   Max.   :34.30    
                   NA's   :3       NA's   :1                        
 Rural_BMI_2017  Urban_BMI_2017   Country              Sex           
 Min.   :19.80   Min.   :21.5   Length:200         Length:200        
 1st Qu.:22.60   1st Qu.:23.8   Class :character   Class :character  
 Median :25.75   Median :26.3   Mode  :character   Mode  :character  
 Mean   :25.24   Mean   :25.9                                        
 3rd Qu.:27.00   3rd Qu.:27.2                                        
 Max.   :34.60   Max.   :34.2                                        
 NA's   :4       NA's   :1                                           

It looks like mean BMIs have increased in all regions for both men and women, but let’s look deeper with some statistical tests.

Distribution


In order to apply a statistical test to compare the means, one of the first things to do is to explore the frequency of the different observed values. One way to summarize the frequency of different observed values is the frequency distribution, which can be shown in a table or a plot. See here for more information about distributions.

There are other types of distributions too, such as probability distributions, which describe the probability the occurrence of different possible outcomes. Probability distributions can be defined for discrete data (e.g. flipping a coin as heads or tails) and continuous data (e.g. BMI estimates). If we are talking about discrete data, the relative frequency of the different categories (heads or tails) can be used to define a discrete probability distribution. We can simply assign a probability to each category, e.g. \(P(heads) = 0.5\) and \(P(tails) = 0.5\).

Continuous probability distributions work in a similar way, except instead of assigning a probability to each category or type of observation (e.g. heads or tails), we assign a probability to an interval of continuous values (e.g. BMI between 22 and 26). This is because it’s not useful to construct a distribution that assigns a probability to each possible outcome (e.g. a BMI of 22.11, 22.12, etc) with such high precision. In this case, we would have to assign a very small probability to every possible BMI outcome.

The normal (or Gaussian) distribution is a type of theoretical continuous probability distribution that is frequently used to approximate the distribution of many naturally occurring measurements, such as that of BMI. We say that a random quantity \(X\) (e.g. BMI) is normally distributed with mean \(m\) and standard deviation \(s\) if the proportion of values in a given interval \((a,b)\) (e.g. BMI between 22 and 26) can be computed using this mathematical formula:

\[P(a < x < b) = \int_a^b \frac{1}{\sqrt{2\pi} s} e^{-\frac{1}{2}\big(\frac{x-m}{s}\big)^2} \]

Translating this to our case study, if our BMI data follow a normal distribution with mean of \(m = 24\) and standard deviation of \(s = 1\), then the distribution of different BMI outcomes should be centered around that mean value of 24 and would look something like this:

norm_BMI_ex_data <- 
  tibble(norm_data = rnorm(n = 200, mean = 24, sd = 1))
hist(pull(norm_BMI_ex_data, norm_data))

Here we are simulating 200 observations from a normal distribution with a mean of \(m = 24\) and standard deviation of \(s = 1\) using the rnorm() function.

Alternatively to using base R, we can plot the frequency of simulated observations using the geom_hist() function of the ggplot2 R package. The ggplot2 package creates plots by using layers. Notice in the following code how there is a plus sign between the ggplot() function and the geom_histogram() function. With ggplot2 we select what data we would like to plot using the first function (ggplot()) and then we add on additional layers of complexity (these layers can even involve different data).


Click here for additional information on using ggplot2.

The reasons ggplot2 is generally intuitive for beginners is the use of grammar of graphics or the gg in ggplot2. The idea is that you can construct many sentences by learning just a few nouns, adjectives, and verbs. There are specific “words” that we will introduce, and once you are comfortable with them, you will be able to create (or “write”) hundreds of different plots.

What is the ggplot() function?

As explained by Hadley Wickham:

the grammar tells us that a statistical graphic is a mapping from data to aesthetic attributes (colour, shape, size) of geometric objects (points, lines, bars). The plot may also contain statistical transformations of the data and is drawn on a specific coordinates system.

ggplot2 Terminology

  • ggplot - the main function where you specify the dataset and variables to plot (this is where we define the x and y variable names)
  • geoms - geometric objects
    • e.g. geom_point(), geom_bar(), geom_line(), geom_histogram()
  • aes - aesthetics
    • shape, transparency, color, fill, line types
  • scales - define how your data will be plotted
    • continuous, discrete, log, etc

The function aes() is an aesthetic mapping function inside the ggplot() object. We use this function to specify plot attributes (e.g. x and y variable names) that will not change as we add more layers.

Anything that goes in the ggplot() object becomes a global setting. From there, we use the geom objects to add more layers to the base ggplot() object. These will define what we are interested in illustrating using the data.


  ggplot2::ggplot(norm_BMI_ex_data, aes(x = norm_data)) + 
  ggplot2::geom_histogram() 

Here, we are using the column called norm_data within norm_BMI_ex_data. As we noted, with ggplot2 we must first specify the data to plot with the ggplot() function. Then, the geom_* function specifies what type of plot to create (e.g. geom_histogram() create a histogram).

We could also make this same plot by piping the norm_BMI_ex_data data into the ggplot() function:

norm_BMI_ex_data %>% 
  ggplot2::ggplot(aes(x = norm_data)) + 
  ggplot2::geom_histogram() 

We will see later how we can add many layers to plots with ggplot2.

Now instead of using simulated data from a normal distribution, let’s make a histogram of our BMI data:

BMI_long %>% 
  ggplot(aes(x = BMI)) + 
  geom_histogram() 

Interesting! The data looks like it has one mode, but it does not look completely symmetric. In fact it looks like it might be what is called right skewed. See here for more information about skewed distributions.

To check we can calculate the mean and median of the BMI data using the base summarize() function of the dplyr package. If the mean and median are very similar, then we will know that the data isn’t skewed. Note that it is necessary to filter out the NA values first.

BMI_long %>%
  filter(!is.na(BMI))%>%
  summarize(mean = mean(BMI), median = median(BMI))
# A tibble: 1 x 2
   mean median
  <dbl>  <dbl>
1  24.9   24.9

OK, so they are essentially the same, mean= 24.87 and median = 24.9, so this does not fit the hallmark of right-skewed data where the the mean is greater than the median.

However so far we have made a histogram of the BMI data in the long format which combines BMI estimates for different subgroups of people, so rural women, rural men, urban women, urban men, etc. are all combined. If we want to make group comparisons, we need to look at the distributions for the specific groups that we are evaluating. So, we need to pick a particular population of individuals and graph their BMI, for example just the national estimates for women and for men in 1985.

The easiest way to do this is to use some other functions of the ggplot2 package, particularly the facet_wrap() function, which will allow us to look at differences in the distribution of the BMI estimates stratified by Year, Sex, and Region. We can sequentially divide our plots by deeper levels using multiple variables and the + plus sign within facet_wrap().

BMI_long %>%
  ggplot(aes(x=BMI)) +
  geom_histogram() +
  facet_wrap(~ Sex + Year + Region) 

Some of these plots look pretty similar to a normal distribution, like the Women 2017 Urban plot (although it might be right-skewed, again we could check the mean and median to determine that). However some of the other plots, like the Men 2017 Urban plot, look very different. These plots show what is called a bimodal distribution, meaning that there appear to be two peaks (or modes), or in other words two different values (and values near each of these) that are the most frequent.

Quantile-Quantile Plots


Another useful plot to create when exploring distributions is something called a “quantile-quantile” plot (or Q-Q plot for short). When we talk about a quantile, we are talking about dividing up the distribution of the data into roughly equal portions where roughly the same number of observations fall into each portion. For example, if you divide your data into 100 quantiles, you can think about this as percentiles, but you could also divide your data into 10 quantiles and these would be called deciles.

Why Q-Q plots? This plot allows us to compare the quantiles of two distributions together: (1) quantiles of a known theoretical distribution (like the normal distribution) compared to (2) quantiles of the distribution of our data. If the quantiles from these two distributions line up in the plot, then that is a visual piece of evidence that our data follow that theoretical distribution (like the normal distribution).

How does this work? To do this we will plot the quantiles of our data on the y-axis and the quantiles of the theoretical normal distribution on the x-axis. If the quantiles line up then we can say that our data is fairly normal. See here for more information about Q-Q plots.

Using the stat_qq() function of the ggplot2 package, we can easily create a Q-Q plot for our data randomly sampled from a normal distribution (“sample”) and compare it to the quantiles from a normal distribution (“theoretical”). The default comparison distribution for these functions is the normal distribution, so we don’t need to specify it in our code.

norm_BMI_ex_data %>%
  ggplot(aes(sample = norm_data)) +
  stat_qq() + 
  stat_qq_line()

The stat_qq_line() function is used to add a line (computes the slope an intercept) on the plot. If the points lie on this straight line, this is evidence that the data have a normal distribution, not that the data have a particular normal distribution. Not surprisingly, we see that the points mostly fall on the line, indicating that the quantiles are fairly similar between the observed and theoretical data.

There are some points that are a bit further from the line as we get to the extreme quantiles.

Note that this plot is comparing our norm_BMI_ex_data which was randomly sampled from a normal distribution with mean 24 and standard deviation 1 to a standard normal distribution with mean 0 and standard deviation 1, which is why the values on the x-axis are -3 to 3 instead of 21 to 27.

As expected we see that about half the points are below our mean of 24 on the y-axis.

We can specify a specific mean and standard deviation for our theoretical distribution in our Q-Q plot; here we specify a normal distribution with mean 24 and standard deviation 1 (so that the theoretical distribution looks more like our data):

norm_BMI_ex_data %>%
  ggplot(aes(sample = norm_data)) +
  stat_qq(distribution = qnorm, dparams = c(mean = 24, sd = 1)) + 
  stat_qq_line(distribution = qnorm, dparams = c(mean = 24, sd = 1))

Notice that the plot looks the same except for the values on the x-axis have changed, meaning our interpretation will be the same – it looks like the values are consistent with a normal distribution.

Now, let’s take a look at our actual BMI data:

BMI_long %>%
  ggplot(aes(sample =BMI)) +
  stat_qq() + 
  stat_qq_line() +
  facet_wrap(~ Sex + Year + Region)

This is a little hard to see, some plots suggest those subsets of the data are similar to a normal distribution, but others seem to be skewed. Let’s take a closer look.

For the sake of simplicity, we are going to focus on the subset of data for the women. This is because women were identified in the paper to have fastest increases in BMI over time. If we want to compare two subsets of women (e.g. 1985 versus 2017 or rural versus urban), then we need to look at the distribution of each of these subsets.

BMI_long %>%
  filter(Sex == "Women") %>%
  ggplot(aes(sample = BMI)) +
  stat_qq() + 
  stat_qq_line() +
  facet_wrap(~ Year + Region)

We can see that at the extremes of our quantiles, for most of our data, our tails are not very similar to the theoretical distribution. We can can also see that our data is right- or positive-skewed, because the extreme values are above the line.

The rural data looks more normal than the urban data. You might be asking, why do we care if the data look more normal or not? It turns out if you want to ask questions like “Is there a difference in the average BMI estimates from women in 1985 compared to women in 2017?”, we will need to use some of type of statistical test. The most commonly used statistical tests that can answer that question rely on an assumption about the data you have, namely that your data have a similar shape as a theoretical normal distribution. So, if we want to use those powerful statistical tests that rely on this assumption of “normality”, then we need to check that assumption before using those tests.

We can conclude from these plots that the assumption of normality appears to be violated in our data.

Data Analysis


If you have been following along but stopped, we could load our wrangled data like so:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data wrangling section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data using the following code:

wrangled_rda("ocs-bp-rural-and-urban-obesity", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found here or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



In the previous section, we hinted at the existence of statistical tests to be able to assess if there is a difference in the average BMI estimates between two groups (e.g. national BMI estimates in rural areas and national BMI estimates in urban areas). One such test is the Student’s \(t\)-test, which can be used to determine if two group means are different. However, as mentioned before, this test depends on certain assumption of our data.

In this section, we are going to talk about this test and other tests that do not require the same assumptions about our data.

Hypothesis testing


Let’s remind ourselves of one of our original questions,

Is there a difference between rural and urban BMI estimates around the world?

In hypothesis testing, we are interested in comparing two different hypotheses: a “null” hypothesis (can be thought of like a baseline e.g. the means between two groups are the same) compared to an “alternative” hypothesis (e.g. the means between two groups are different). We are going to ask if there is enough evidence in our data to reject the null hypothesis.

Let’s try to formalize this a bit.

Suppose we want to test whether there is a difference between rural and urban BMI estimates using the data from 1985. In our case, then, we want to test whether the mean BMI estimate among the rural areas is the same as the mean BMI estimate in the urban areas. If we call the true unknown means of the two groups \(\mu_U\) and \(\mu_R\), for the urban and rural areas, respectively, then we can define the null hypothesis that there is no difference in the two means:

\[ H_0: \mu_U = \mu_R \]

In contrast, we also define an alternative hypothesis that there is a difference between the mean BMI estimates:

\[ H_a: \mu_U \neq \mu_R \]

The idea behind a hypothesis test is that we assume the null hypothesis is true and we use our data to help us identify if there is enough evidence to reject the null hypothesis.

This is similar to the idea of assuming that individuals are not guilty until proven otherwise. If there is not enough evidence in the data, then we say we “fail to reject the null hypothesis”.

You might be asking, “But we do not know what the true group means are?” That is correct. Instead, we must estimate these means based on the data that we have. For example, in our data, we can estimate the average BMI estimates for women in rural and urban regions in 1985. Here we will use the summarize() function from the dplyr package again, this time using the argument na.rm = TRUE to remove NA values.

BMI_long %>% 
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Region) %>%
  summarize(avg_bmi = mean(BMI, na.rm = TRUE)) 
# A tibble: 2 x 2
  Region avg_bmi
  <chr>    <dbl>
1 Rural     23.6
2 Urban     24.6

Here we see that that in 1985, the average BMI estimate for women across urban areas is 24.6 compared to 23.6 across rural areas. It looks like BMI estimates are, on average, higher in urban areas compared to rural areas. However, now we want to use a statistical test to assess if this difference is statistically meaningful or a difference of this size could be found due to random chance.

Two-sample \(t\)-test


The two-sample \(t\)-test is a common way to determine if the means of two groups are different. It does this by calculating a observed test-statistic, \(t\), that looks at the difference in the sample means of the two groups divided by an estimate of the variability in the difference in sample means. Here \(s_{pooled}\) is standard deviation of the data when pooling the samples together. \[ t = \frac{\bar{X}_U-\bar{X}_R}{s_{pooled}\sqrt{\frac{1}{n_U} + \frac{1}{n_R}}} \] If the null hypothesis is true, and the means for urban and rural areas are the same, then the sample means in the numerator will be similar and this test statistic will be close to 0. However, if the test statistic is not close to 0, this provides evidence the means for the urban and rural areas are difference, and so we may choose to reject the null hypothesis.

To formally decide whether to reject the null hypothesis, we compare the observed test statistic to the distribution of possible test statistics that could occur if the null hypothesis were true using what we call a p-value. The p-value tells us the probability of observing a test statistic as or more extreme than the one we observed if the null hypothesis is true. If the p-value is small, say less than 0.05, then our observed test statistic is pretty rare if the null hypothesis is true. This would lead us to reject the null hypothesis and conclude that the means of the two groups are different. We will talk more about p-values, and in particular our choice of 0.05 as a comparison for the p-value, a little later.

Here we perform a two-sample \(t\)-test comparing estimate for rural women in 1985 to estimates for urban women in 1985. We use the var.equal = TRUE option here to use the pooled standard deviation \(s_{pooled}\) from the formula above.

t.test(pull(filter(BMI_long, Sex == "Women", 
                   Year == "1985", 
                   Region == "Rural"), BMI), 
       pull(filter(BMI_long, Sex == "Women", 
                   Year == "1985", 
                   Region == "Urban"), BMI), 
       var.equal = TRUE)

    Two Sample t-test

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Urban"), BMI)
t = -3.8952, df = 394, p-value = 0.0001152
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.5744694 -0.5182378
sample estimates:
mean of x mean of y 
 23.58782  24.63417 
# means are different - p value <.05 reject the null: no difference in the means

In this case the p-value for the test is 0.00012, which is quite small. Here would would reject the null hypothesis that the means in the two groups are equal and conclude there are differences in mean BMI estimates for women in urban and rural areas.

But wait! We need to check the assumptions of the statistical test we used to be sure it is valid. The two-sample \(t\)-test relies on several assumptions:

  1. The data for each group is normally distributed.
  2. The variance of both groups is similar.
  3. The observations from the two groups are independent (meaning that observations do not influence each other).
  4. The observations within each group are independent (meaning that observations do not influence each other).

If these assumptions are violated, this doesn’t necessarily mean we can’t perform a statistical test. It just means we may need to consider the following options:

  1. If the data isn’t normally distributed, we may need to consider either a transformation of the data to make it more normally distributed or a nonparametric test which doesn’t rely on the normality assumption.
  2. If the variances of the two groups aren’t similar, we can perform a Welch’s \(t\)-test (also called the unequal variance \(t\)-test) to account for difference variances. This modifies the denominator of the test statistic to no longer use the standard deviation from pooling the samples. We can do this in R by using the var.equal = FALSE option in the t.test() function.
  3. If the data in the two samples are not independent and are instead what we call paired, we should use the Paired \(t\)-test.
  4. If the data within a group is not independent, we would need to consider methods beyond the scope of this case study.

Are these assumptions met in our case? We’ve already seen, using Q-Q plots, that these BMI estimates may not be normally distributed. We haven’t checked the assumption of equal variance yet. But it seems clear that the last assumption (of independent samples) is violated in our case! The BMI data for rural areas comes from the same countries and the BMI data for urban areas, meaning the samples are not independent and are instead what we call paired. We need to account for this because paired values may be more similar to one another because they come from the same country. Note that this would also be true for the male and female measurements from the same country or the values in the same countries from 1985 and later in 2017.

Paired \(t\)-test


When we perform a paired \(t\)-test, instead of looking at the means of the two samples separately, we combine observations in a pair (from the same country in our case) to calculate a difference in values between the two samples. For example, we are really testing the bmi_diff column in the data below:

BMI_long %>% 
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Country) %>%
  summarize(bmi_rural = BMI[Region == "Rural"],
            bmi_urban = BMI[Region == "Urban"],
            bmi_diff = bmi_rural - bmi_urban) 
# A tibble: 200 x 4
   Country             bmi_rural bmi_urban bmi_diff
   <chr>                   <dbl>     <dbl>    <dbl>
 1 Afghanistan              20.1      23.2   -3.10 
 2 Albania                  26.1      25.9    0.200
 3 Algeria                  23.3      24.8   -1.5  
 4 American Samoa           34.1      34.4   -0.300
 5 Andorra                  25.4      25.2    0.200
 6 Angola                   20.9      22.7   -1.8  
 7 Antigua and Barbuda      24        25.4   -1.40 
 8 Argentina                23.3      24.5   -1.2  
 9 Armenia                  25.2      25.5   -0.300
10 Australia                24.7      24.4    0.300
# ... with 190 more rows

This means our hypotheses are slightly different from the two-sample \(t\)-test. In this case we are testing the differences among the pairs of observations and how close these differences are to zero. (A mean difference of zero would mean the BMI estimates are the same in the rural and urban areas.) If we let \(\mu_d\) represent the true mean difference between the paired observations of the two groups, then our null hypothesis is that the mean of the differences is equal to zero: \[ H_0 : \mu_d = 0 \]

And the alternative hypothesis is that the mean of the differences is not equal to zero: \[ H_a : \mu_d \neq 0 \]

We can perform a paired \(t\)-test by adding the option paired = TRUE to the t.test() command. Note that we don’t need to specify anything about equality of variances now, because there is only one variance, the variance of the paired differences!

t.test(pull(filter(BMI_long, Sex == "Women", 
                   Year == "1985", 
                   Region == "Rural"), BMI), 
       pull(filter(BMI_long, Sex == "Women", 
                   Year == "1985", 
                   Region == "Urban"), BMI), 
       paired = TRUE)

    Paired t-test

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Urban"), BMI)
t = -14.095, df = 195, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.1870263 -0.8956268
sample estimates:
mean of the differences 
              -1.041327 
# means are different - p value <.05 reject the null: no difference in the means

Question opportunity: Looking at the t value, was global BMI lower in Rural or Urban areas in 1985?

Let’s come back to the other \(t\)-test assumptions now. With a paired \(t\)-test, we no longer need to assess the assumption about equal variance, since we are really testing the mean of one sample (of paired differences) rather than comparing two samples. However, we still have to check normality and independence of observations. We can rephrase these now to be about the paired differences rather than the two separate samples:

  1. The paired differences are normally distributed.
  2. The paired differences are independent (meaning that observations do not influence each other).

If we assume that measurements between different countries are independent, this second assumption is not violated. Therefore, we only now need to check normality of these paired differences.

We can start by making a Q-Q plot of the paired differences.

BMI_long %>% 
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Country) %>%
  summarize(bmi_rural = BMI[Region == "Rural"],
            bmi_urban = BMI[Region == "Urban"],
            bmi_diff = bmi_rural - bmi_urban)  %>%
  ggplot(aes(sample = bmi_diff)) +
  stat_qq() +
  stat_qq_line()

We see that these paired differences do not appear to be normally distributed because the points do not lie on the straight line. We can also see this by making a histogram of these paired differences:

BMI_long %>% 
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Country) %>%
  summarize(bmi_rural = BMI[Region == "Rural"],
            bmi_urban = BMI[Region == "Urban"],
            bmi_diff = bmi_rural - bmi_urban)  %>%
  ggplot(aes(x = bmi_diff)) +
  geom_histogram()

This histogram appears to show show a bimodal distribution, meaning two groups of countries – most countries have a rural BMI estimates lower than urban BMI estimates (bmi_diff < 0), while a smaller group of countries have urban BMI estimates higher than urban BMI estimates (bmi_diff >0).

First we will try transform our data to make it more normally distributed. One way to do this is to take the logarithm of the data values. Logarithms make extreme values less extreme, so can be helpful when data has a right skew.

# create a new dataset with the paired differences
# create a log version of the difference variable using mutate()
BMI_diff_log <- BMI_long %>%
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Country) %>%
  summarize(bmi_rural = BMI[Region == "Rural"],
            bmi_urban = BMI[Region == "Urban"],
            bmi_diff = bmi_rural - bmi_urban) %>%  
  mutate(log_diff=log(bmi_diff))

Notice the warning message that In log(bmi_diff) : NaNs produced. This is because some of our differences were less than zero, and the logarithm of a negative number is not defined. We can fix this issue by adding the same positive number, say 5, to each difference before taking the logarithm:

BMI_diff_log <- BMI_long %>%
  filter(Year == 1985, Sex == "Women", Region != "National") %>% 
  group_by(Country) %>%
  summarize(bmi_rural = BMI[Region == "Rural"],
            bmi_urban = BMI[Region == "Urban"],
            bmi_diff = bmi_rural - bmi_urban) %>%  
  mutate(log_diff=log(bmi_diff + 5))

Let’s look at a Q-Q plot and histogram of this transformed data:

BMI_diff_log %>% 
  ggplot(aes(sample = log_diff)) +
  stat_qq() +
  stat_qq_line()

BMI_diff_log %>% 
  ggplot(aes(x = log_diff)) +
  geom_histogram()

The transformed data still does not appear to be normally distributed and the bimodal nature of the distribution is still apparent in the histogram. What should we do? The \(t\)-test is fairly robust to violations of the normality assumption if the sample size is relatively large, due to what is called the central limit theorem, which states that as samples get larger, the sample mean has an approximate normal distribution.

With our sample size of 200, we could consider using the paired \(t\)-test even with our non-normal data. However, if we had a smaller sample, we might still need to consider another alternative since the log-transformed data still doesn’t look normal. We might want to consider a non-parametric test in that case.

Nonparametric tests


When the normality assumption of a paired \(t\)-test is violated, we can consider a nonparametric alternative instead. What’s a non-parametric test? Parametric tests are based on assumptions about the distribution of the underlying data, while nonparametric tests do not rely on these distributional assumptions.

See here for more information about the difference between these two classes of tests.

The nonparametric alternative to the paired \(t\)-test is called the Wilcoxon signed-rank test. This test doesn’t require the same normality assumption as the \(t\)-test, so can be considered with the data does not appear to be normally distributed and particularly when the number of samples is low. The null hypothesis for this test is that the median of the paired differences is 0.

To do a Wilcoxon test, we use the wilcox.test() function:

wilcox.test(pull(filter(BMI_long, Sex == "Women", 
                        Year == "1985", 
                        Region == "Rural"), BMI),
           pull(filter(BMI_long, Sex == "Women", 
                       Year == "1985", 
                       Region == "Urban"), BMI),
           paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Urban"), BMI)
V = 1562, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

With this nonparametric test, we still see a small p-value, indicating that BMI estimates between urban and rural areas are different. We would conclude that there’s a difference in the median BMI estimates between these types of regions.

Multiple Testing Correction


Let’s take a look back at the questions we wanted to answer: 1) Is there a difference between rural and urban BMI estimates around the world? In particular, how does this look for females? 2) How have BMI estimates changed from 1985 to 2017? Again, In particular, how does this look for females? 3) How do different countries compare for BMI estimates? In particular, how does the United States compare to the rest of the world?

It’s important to note that ultimately we wanted to test 4 different tests to answer question 1 and 2:

  1. Is there a difference in mean BMI for women between Rural and Urban areas in 1985?
  2. Is there a difference in mean BMI for women between Rural and Urban areas in 2017?
  3. Is there a difference in mean BMI for women between 1985 and 2017 in Rural areas?
  4. Is there a difference in mean BMI for women between 1985 and 2017 in Urban areas?

So far we have only looked at the first test. Let’s perform all 4 of these tests using the non-parametric Wilcoxon signed-rank test:

wilcox.test(pull(filter(BMI_long, Sex == "Women", 
                        Year == "1985", 
                        Region == "Rural"), BMI),
           pull(filter(BMI_long, Sex == "Women", 
                       Year == "1985", 
                       Region == "Urban"), BMI),
           paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Urban"), BMI)
V = 1562, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0
wilcox.test(pull(filter(BMI_long, Sex == "Women", 
                        Year == "2017", 
                        Region == "Rural"), BMI),
           pull(filter(BMI_long, Sex == "Women", 
                       Year == "2017", 
                       Region == "Urban"), BMI),
           paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_long, Sex == "Women", Year == "2017", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "2017", Region == "Urban"), BMI)
V = 2750.5, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0
wilcox.test(pull(filter(BMI_long, Sex == "Women", 
                        Year == "1985", 
                        Region == "Rural"), BMI),
           pull(filter(BMI_long, Sex == "Women", 
                       Year == "2017", 
                       Region == "Rural"), BMI),
           paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Rural"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "2017", Region == "Rural"), BMI)
V = 273, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0
wilcox.test(pull(filter(BMI_long, Sex == "Women", 
                        Year == "1985", 
                        Region == "Urban"), BMI),
           pull(filter(BMI_long, Sex == "Women", 
                       Year == "2017", 
                       Region == "Urban"), BMI),
           paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_long, Sex == "Women", Year == "1985", Region == "Urban"), BMI) and pull(filter(BMI_long, Sex == "Women", Year == "2017", Region == "Urban"), BMI)
V = 189, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

In each of these tests, the very small p-value that would lead us to conclude that there is a difference between the two groups we are comparing. However, when we test multiple questions like this, we need to correct for the multiple tests that we are performing.

The more we test data, the more likely we are to see a significant finding just by random chance even when the finding is in fact not real. It all comes down to probability.

Probability

You may recall that probability is the chance that an event will happen and it ranges from 0 to 1. You can also think of it as a percentage of 0% (probability =0) to 100% (probability = 1) of chance that an event will happen.

[source]

If we consider flipping a coin, the probability of an event happening (heads) is always equal to 1 ( or 100% chance) minus the probability of the event not happening (tails instead of heads) because the total probability can not exceed 100%.

    probability of 1 (100% chance) = the probability of heads (50% chance)
                                     + the probability of tails (50% chance)
                                   
                                 1 = P(heads) + P(tails)
        probability of one outcome = 1 - the probability of the other outcome
                          P(heads) = 1 - P(tails) 
                                    or
                          P(tails) = 1 - P(heads)

If we think about the probability of getting significant or nonsignificant results we can think of it like this:

                                 1 = P(significant results) 
                                      + P(no significant results)
            P(significant results) = 1 - P(no significant results)

Alpha


Alpha is the amount of risk that you are willing to accept that a statistical test result is actually a false positive, in other words we reject the null hypothesis when the null hypothesis is true. This is also called type 1 error. A significance threshold of .05 is customary and it means that we are accepting a 5% chance that our result is actually a false positive.

                             alpha = probability of false significant results 
                                       or
                                 α = P(Making a type 1 error)
                                        

Remember that in our statistical tests if the p-value < alpha it is considered significant and if p-value > alpha it is considered not significant

But what really is the p-value?

p-values


The p in p-value stands for probability - the probability of observing a test statistic (for example the t in our student \(t\)-tests based on the means of our groups of comparison) as or more extreme than what we observe if the null hypothesis is true (continuing with the t test example, the distribution of possible t statistics if there is no difference between groups). Therefore a p-value of 0.02 means that there is a 2 percent chance that our data may also look the way it does if the null hypothesis is true. This does not mean there is a 2% chance that the null hypothesis is true. It only means that there is a 2% chance we would observe data as extreme as ours if there really is no difference between the groups.

So then if alpha is the threshold for the p-value for obtaining false significant results then the probability of not making incorrect conclusions is 1 - alpha:

             P(Not making a type 1 error) = 1 - α
#P(Not making a type 1 error) = 1 - α
1-.05 
[1] 0.95

OK so if we use an alpha of .05 we are accepting that 95% of the time we will not make a Type 1 error and 5 % of the time we will just by random chance.
Taking this one step further:

                 P(Making an error) = 1 - P(Not making an error)
                 P(Making an error) = 1 - (1 - α)
                              

Here we can see that this checks out:

#P(Making an error) = 1 - (1 - α)
1-(1-.05)
[1] 0.05

So what happens if we perform multiple tests?

The probability of not making a type 1 error would remain the same for each test. Therefore we need to multiply the probabilities together each time to determine the over all probability of making an error across multiple tests. See here about why we multiply probabilities together.

    P(Not making an error in m tests) = (1 - α)^m
P(Making at least 1 error in m tests) = 1 - (1 - α)^m

Let’s consider if we performed 10 different statistical tests and if we as usual considered the significance threshold alpha of .05:

So the probability of getting 1 significant result with 10 tests is:

#P(Making at least 1 error in 10 tests) = 1 - ((1 - α)(1 - α)(1 - α)(1 - α)(1 - α)(1 - α)(1 - α)(1 - α)(1 - α)(1 - α))
#this is the same as:
#P(Making at least 1 error in 10 tests) = 1 - (1 - α)^10
 1- (1-.05)^10
[1] 0.4012631

So there is a 40% chance that that there will be a significant finding simply due to random chance alone.

What about 100 tests?

#P(Making at least 1 error in 100 tests) = 1 - (1 - α)^100
 1- (1-.05)^100
[1] 0.9940795

Yikes!! That is almost a 100% chance that there will be a significant finding simply due to chance alone!

Much of this explanation is described in this lecture.

One way to correct for this is multiple testing issue is using the Bonferroni method.

In this method we would divide our significance threshold (generally 0.05) by the number of tests.

.05/4 # 4 tests
[1] 0.0125

Our new significance threshold is now 0.0125. Thus our p-values should be less than this value for us to reject the null that there is no difference in means. In our 4 Wilcoxon tests, our p-values were less than 0.0125. So we see a significant difference in the means of the groups after multiple testing correction for our four different tests.

Data Visualization


If you have been following along but stopped, we could load our wrangled data like so:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data wrangling section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data using the following code:

wrangled_rda("ocs-bp-rural-and-urban-obesity", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found here or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



Again we will utilize ggplot2 to create plots to look at the directional and magnitude of the differences in BMI we are interested in. If you need additional information please see the Data Exploration section earlier in this case study. The top two lines of the code for the following plots, filter the data to only specific values of interest. Then we layer what is called a jitter on top of a box plot. A jitter is essentially a dot plot but with some variation on the location of the points so that they do not line up vertically which can make the individual points difficult to see.

Our first main question was: Is there a difference between rural and urban BMI estimates around the world? Let’s look at the national mean BMI estimates for each of the years:

BMI_long %>% 
  filter(Sex == "Women", 
         Year == "1985", 
         Region != "National") %>%
  ggplot(aes(x = Region, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3) 

# the width determines how wide the points are plotted

BMI_long %>% 
  filter(Sex == "Women", 
         Year == "2017", 
         Region != "National") %>%
  ggplot(aes(x = Region, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3)

Our 2nd main question was: How have BMI estimates changed from 1985 to 2017? Let’s look at the change in rural and urban mean BMI estimates over time:

#this time we will add titles for Rural and Urban using ggtitle()
BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region == "Rural") %>%
  # filtering this way allows us to keep data from both years
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3) +
  ggtitle("Rural")

BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region == "Urban") %>%
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3) +
  ggtitle("Urban")

Let’s put the plots together to see how the change over the years differs between the regions. We will use again use facet_wrap() to do this:

BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region %in% c("Rural", "Urban")) %>%
  # filtering this way allows us to keep data from both years
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3) +
  facet_wrap(~ Region)

# we would also get national data without `Region %in% c("Rural", "Urban"))`

Indeed the change in BMI over time looks bigger in the rural areas!

One third main questions was: How do the different countries compare? Or in other words what do the individual dots represent in our box plots? We will take a look using geom_label():

BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region == "Rural") %>%
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot() +
  geom_jitter(width = .3) +
  geom_label(aes(label = Country))

If we include all country names this is a bit too much… so perhaps we should focus on just the extreme BMI values using filter() function of thedplyr package. We will also use the ggrepel package to have our labels not overlap each other.

Part of the third question was: How does the United States of America compare? So let’s label the data points for the United states.

BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region == "Rural") %>%
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot() +
  geom_jitter(data=BMI_long %>% 
              filter(Sex == "Women", 
                     Year %in% c("1985", "2017"), 
                     Region == "Rural") %>%
              filter(BMI>31 | BMI<19 | Country == "United States of America"), 
              aes(x =Year, y =BMI),
              width = .02) +
  ggrepel::geom_text_repel(data=BMI_long %>% 
              filter(Sex == "Women", 
                     Year %in% c("1985", "2017"), 
                     Region == "Rural") %>%
              filter(BMI>31 | BMI<19 | Country == "United States of America"), 
              aes(x =Year, y =BMI, label = Country))

And let’s fill the box plots with color and outline in black:

BMI_long %>% 
  filter(Sex == "Women", 
         Year %in% c("1985", "2017"), 
         Region == "Rural") %>%
  ggplot(aes(x = Year, y = BMI)) +
  geom_boxplot(outlier.shape = NA, color = "black", aes(fill = Year)) +
  geom_jitter(data=BMI_long %>% 
              filter(Sex == "Women", 
                     Year %in% c("1985", "2017"), 
                     Region == "Rural") %>%
              subset(BMI>31 | BMI<19 | Country == "United States of America"), 
              aes(x =Year, y =BMI),
              width = .02) +
  ggrepel::geom_text_repel(data=BMI_long %>% 
              filter(Sex == "Women", 
                     Year %in% c("1985", "2017"), 
                     Region == "Rural") %>%
              subset(BMI>31 | BMI<19 | Country == "United States of America"), 
              aes(x =Year, y =BMI, label = Country))

Overall differences


Let’s take a look at all the data together, including the data for men:

ggplot(BMI_long, aes(x = Year, y = BMI, col = Region)) +
  geom_boxplot(outlier.shape = NA, color = "black" , aes(fill = Region)) + 
  facet_grid(~ Sex)   

That’s useful, but let’s look at the individual points and include our country labels, to do so lets change our United States of America label to USA:

BMI_long[["Country"]] <-BMI_long[["Country"]] %>%
  str_replace( pattern = "United States of America", replacement = "USA")


ggplot(BMI_long, aes(x = Year, y = BMI, col = Year)) + 
  geom_boxplot(outlier.shape = NA, color = "black" , aes(fill = Sex)) + 
  facet_grid(~ Sex + Region) + 
  geom_hline(yintercept=30, linetype="dashed", color = "red", size =1) + 
  # This will add a horizontal dashed line to indicate the obesity BMI threshold
  geom_jitter(data=subset(BMI_long), 
              aes(x =Year, y =BMI), 
              width = .2, size =2, shape =21, color = "black", fill = "gray") + 
  # This will add the individual country data points
  # The shape 21 allows for a different fill and outline color
  # The width determines how wide the jitter points are plotted
  geom_jitter(data=subset(BMI_long, Country == "USA"), 
              aes(x =Year, y =BMI), 
              width = .02, size =12, shape =21, color = "black", fill = "gray") + 
  # This will add points that are larger for the USA data
  geom_text(data=subset(BMI_long,  Country == "USA"), 
            aes(x =Year, y =BMI,label=Country), 
            color = "black") + 
  # This will add USA labels to the USA points
  theme(legend.position = "none", 
  # This is useful for removing the legend
        axis.text.x = element_text(size = 15,angle = 30), 
        # this changes the size and angle of the x axis point labels 
        axis.text.y = element_text(size = 20), 
        axis.title.y = element_text(size =15), 
        axis.title.x = element_text(size =15), 
        strip.text.x = element_text(size = 15)) +
  # This changes the size of x axis labels for the facet 
        ggtitle( "Differences in BMI Over Time and Across Region Type and Gender") +
  # Add a  plot title
        scale_fill_manual(values=c("dodgerblue", "orchid2"))

Here we can see that overall BMI appears to be increasing globally over time. Additionally we can see that this is occurring not just in urban areas, but also in rural areas. The US is consistently above the median in all strata of the data. In general, the female data shows higher BMI values than the male data. The rural USA BMI appears to be higher than the urban BMI for both men and women. Many countries have average BMI estimates above the obesity threshold of 30. Thus it appears that education and outreach programs for weight management should focus on both rural and urban areas and both genders. Education and assistance for women may be especially helpful.

Differences in rate of change


How does the rate of change in BMI differ between groups? Which group might especially need attention?

First let’s calculate the differences in BMI from 2017 and 1985 and add this to our BMI_long data object. This will create new variables called Rural_difference, Urban_difference, and National_difference.

BMI_numeric  %<>% 
  mutate(Rural_difference = Rural_BMI_2017 - Rural_BMI_1985) %>%
  mutate(Urban_difference = Urban_BMI_2017 - Urban_BMI_1985) %>%
  mutate(National_difference = National_BMI_2017 - National_BMI_1985)

BMI_diff_long <- BMI_numeric %>% 
  select(Country:National_difference) %>% 
  gather(key = Type, value = Difference , Rural_difference:National_difference)


BMI_diff_long <- BMI_numeric %>% 
  select(Country: National_difference) %>% 
  pivot_longer( names_to = "Difference" , cols = Rural_difference:National_difference)


head(BMI_diff_long)
# A tibble: 6 x 4
  Country     Sex   Difference          value
  <chr>       <chr> <chr>               <dbl>
1 Afghanistan Men   Rural_difference     2.8 
2 Afghanistan Men   Urban_difference     1.20
3 Afghanistan Men   National_difference  2.6 
4 Afghanistan Women Rural_difference     3.5 
5 Afghanistan Women Urban_difference     3.1 
6 Afghanistan Women National_difference  3.80

Let’s replace “United states of America” with “USA” and make a plot with this data to compare the change in BMI:

BMI_diff_long[["Country"]] <-BMI_diff_long[["Country"]] %>%
  str_replace( pattern = "United States of America", replacement = "USA")


ggplot(BMI_diff_long, aes(x = Difference, y = value, col = Difference)) + 
  geom_boxplot(outlier.shape = NA, color = "black" , aes(fill = Difference)) + 
  facet_grid(~ Sex) +
  geom_jitter(data=subset(BMI_diff_long), 
              aes(x =Difference, y =value), 
              width = .2, size = 2, shape = 21, color = "black", fill = "gray") + 
  # This will add the individual country data points
  # The shape 21 allows for a different fill and outline color
  # The width determines how wide the jitter points are plotted
  geom_jitter(data=subset(BMI_diff_long, Country == "USA"), 
              aes(x = Difference, y = value), 
              width = .02, size = 12, shape = 21, color = "black", fill = "gray") + 
  # This will add points that are larger for the USA data
  geom_text(data=subset(BMI_diff_long,  Country == "USA"), 
            aes(x =Difference, y = value, label = Country), color = "black") + 
  # This will add USA labels to the USA points
  theme(legend.position = "none", 
  # This is useful for removing the legend
        axis.text.x = element_text(size = 15,angle = 30), 
        # this changes the size and angle of the x axis point labels 
        axis.text.y = element_text(size = 20), 
        axis.title.y = element_text(size = 15), 
        axis.title.x = element_text(size = 15), 
        strip.text.x = element_text(size = 15)) +
        # this changes the size of x axis labels for the facet
        ggtitle( "Change in BMI Over Time and Across Region Type and Gender")

We can now see that the rate of change from 1985 to 2017 appears to be larger in the women compared to the men in all regions. The group with the largest increase in the USA is the women living in rural areas.

Let’s check the difference with some statistical tests. Remember that these rural differences are measured on the same countries as the urban differences, so we will need to use paired tests to make these comparisons. We can either consider the paired \(t\)-test or the Wilcoxon signed-rank test. To decide between them, we’ll look at the normality of the paired differences in change over time.

Let’s compare the change over time in rural communities to the change in urban communities among women and separately among men.

# lets look at the normality of these paired differences
# first we need to calculate the paired differences of change over time
BMI_diff_long %>%
  filter(Difference != "National_difference") %>%
  group_by(Country, Sex) %>%
  summarize(change_rural = value[Difference == "Rural_difference"],
            change_urban = value[Difference == "Urban_difference"],
            change_diff = change_rural - change_urban) %>%
  ggplot(aes(x=change_diff)) +
  geom_histogram() +
  facet_wrap(~ Sex)

# interesting...the spread of the difference in change over time is much 
# broader for women than for men
# for women, the differences look reasonably normal, spread between -1 and +1
# for men there appear to be two groups of countries; one with difference in change over time
# centered around 0 and the other with difference in change over time between 1 and 2.


# Let's look at Q-Q plots:
BMI_diff_long %>%
  filter(Difference != "National_difference") %>%
  group_by(Country, Sex) %>%
  summarize(change_rural = value[Difference == "Rural_difference"],
            change_urban = value[Difference == "Urban_difference"],
            change_diff = change_rural - change_urban) %>%
  ggplot(aes(sample = change_diff)) +
  stat_qq() + # to add the line we need to also include stat_qq_line()
  stat_qq_line() +
  facet_wrap(~ Sex)

# the distribution of differences in change over time does not appear normal
# for either men or women

Based on the non-normality of these paired differences, we will use a Wilcoxon signed-rank test to compare changes over time between urban and rural areas separately for men and women:

wilcox.test(pull(filter(BMI_diff_long, 
                    Sex == "Women",  
                    Difference == "Rural_difference"), value), 
        pull(filter(BMI_diff_long,
                    Sex == "Women",  
                    Difference == "Urban_difference"), value),
        paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_diff_long, Sex == "Women", Difference == "Rural_difference"), value) and pull(filter(BMI_diff_long, Sex == "Women", Difference == "Urban_difference"), value)
V = 11304, p-value = 0.00327
alternative hypothesis: true location shift is not equal to 0
wilcox.test(pull(filter(BMI_diff_long,
                    Sex == "Men",
                    Difference == "Rural_difference"), value), 
        pull(filter(BMI_diff_long,
                    Sex == "Men", 
                    Difference == "Urban_difference"), value),
        paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  pull(filter(BMI_diff_long, Sex == "Men", Difference == "Rural_difference"), value) and pull(filter(BMI_diff_long, Sex == "Men", Difference == "Urban_difference"), value)
V = 9159, p-value = 0.08256
alternative hypothesis: true location shift is not equal to 0

Using a p-value threshold of 0.05, we would conclude there’s a difference in change over time between urban and rural areas for women (p = 0.00327) but not for men (p = 0.08256).

However, now we have performed six comparisons (4 earlier) so we should apply our multiple testing correction and use a new p-value threshold of:

.05/6
[1] 0.008333333

With the threshold adjusted for multiple comparisons, we would still conclude there’s a difference in the change over time between urban and rural areas for women but not for men.

Interestingly, although we don’t see a difference in change over time for men, our histogram of the differences between urban and rural areas for men highlighted two groups of countries – one centered around zero (no difference) and one above 1 (showing a difference):

BMI_diff_long %>%
  filter(Difference != "National_difference") %>%
  group_by(Country, Sex) %>%
  summarize(change_rural = value[Difference == "Rural_difference"],
            change_urban = value[Difference == "Urban_difference"],
            change_diff = change_rural - change_urban) %>%
  ggplot(aes(x=change_diff)) +
  geom_histogram() +
  facet_wrap(~ Sex)

In fact for these countries, the difference between rural and urban areas for men is higher than the difference for women for almost all of the countries!

We can look to see which countries have such a large difference in change over time between rural and urban areas for men:

BMI_diff_long %>%
  filter(Sex == "Men", Difference != "National_difference") %>%
  group_by(Country) %>%
  summarize(change_rural = value[Difference == "Rural_difference"],
            change_urban = value[Difference == "Urban_difference"],
            change_diff = change_rural - change_urban) %>%
  filter(change_diff > 1)
# A tibble: 21 x 4
   Country          change_rural change_urban change_diff
   <chr>                   <dbl>        <dbl>       <dbl>
 1 Afghanistan              2.8         1.20         1.60
 2 American Samoa           2           0.200        1.80
 3 Bangladesh               2.3         0.600        1.70
 4 Bhutan                   2.8         1.10         1.70
 5 Cook Islands             3.10        1.40         1.70
 6 Fiji                     3           1.60         1.40
 7 French Polynesia         2.5         0.800        1.7 
 8 India                    2.6         0.700        1.90
 9 Kiribati                 2.10        0.5          1.60
10 Marshall Islands         2.5         0.900        1.60
# ... with 11 more rows

For women, we did see a difference in the change over time between urban and rural areas. There appear to be specific countries where BMI shows a particular increase especially for women. Which countries are those? How does that compare with the US? Clearly the US is among the countries with the highest differences.

BMI_diff_long %>% 
  filter(Country == "USA")
# A tibble: 6 x 4
  Country Sex   Difference          value
  <chr>   <chr> <chr>               <dbl>
1 USA     Men   Rural_difference     3.10
2 USA     Men   Urban_difference     3.1 
3 USA     Men   National_difference  3.10
4 USA     Women Rural_difference     3.5 
5 USA     Women Urban_difference     3.4 
6 USA     Women National_difference  3.40

In the US, focus should be placed on both urban and rural women to improve this public health issue.

Here we can see the countries that have the largest differences in BMI from 1985-2017:

BMI_diff_long %>%
  filter(value > 4.9) %>%
  arrange(- value)
# A tibble: 6 x 4
  Country     Sex   Difference          value
  <chr>       <chr> <chr>               <dbl>
1 Egypt       Women Rural_difference      5.9
2 Honduras    Women National_difference   5.6
3 Honduras    Women Rural_difference      5.5
4 Egypt       Women National_difference   5.1
5 Saint Lucia Women Rural_difference      5  
6 Honduras    Women Urban_difference      4.9
#here we sorted by the highest to lowest values of BMI Difference

However it is important to see what the mean BMI values were for these countries in 2017. It could be that the average was underweight in 1985… let’s take a look.

BMI_long %>% 
  filter(Country %in% pull(filter(BMI_diff_long, value > 4.9), Country), 
                    Sex %in% pull(filter(BMI_diff_long, value > 4.9), Sex),
                    Year =="2017") %>%
  arrange(- BMI)
# A tibble: 9 x 5
  Country     Sex   Region   Year    BMI
  <chr>       <chr> <chr>    <chr> <dbl>
1 Egypt       Women Urban    2017   32.3
2 Egypt       Women National 2017   31.7
3 Egypt       Women Rural    2017   31.3
4 Saint Lucia Women National 2017   30.5
5 Saint Lucia Women Rural    2017   30.5
6 Saint Lucia Women Urban    2017   30.5
7 Honduras    Women Urban    2017   28.3
8 Honduras    Women National 2017   27.7
9 Honduras    Women Rural    2017   26.9
#Here is the data just for the changes in BMI in Egypt
filter(BMI_diff_long, Country == "Egypt") %>% 
  arrange(- value)
# A tibble: 6 x 4
  Country Sex   Difference          value
  <chr>   <chr> <chr>               <dbl>
1 Egypt   Women Rural_difference     5.9 
2 Egypt   Women National_difference  5.1 
3 Egypt   Women Urban_difference     4.20
4 Egypt   Men   Urban_difference     2.60
5 Egypt   Men   Rural_difference     2.5 
6 Egypt   Men   National_difference  2.5 

Thus rural women in Egypt had the greatest increase in BMI from 1985 to 2017 in this data (5.9) and the average BMI is now over the obesity threshold of 30.

The data suggests that rural women in Egypt and other countries may especially benefit from dietary resources and our interventions and programs to assist with weight management.

Summary


Summary Plot


Now let’s make a plot that summarizes our findings. To do this we will simplify the other plots we made and then combine them together.

# simplified national means plot
Means_plot<-BMI_long %>% 
  filter(Sex %in% c("Men", "Women"), 
         Year %in% c("1985", "2017"), 
         Region == "National") %>%
ggplot(aes(x = Sex, y = BMI)) + 
  geom_boxplot(outlier.shape = NA, color = "black" , aes(fill = Sex)) + 
 scale_fill_manual(values=c("dodgerblue", "orchid2")) +
  facet_grid(~ Year) + 
  geom_hline(yintercept=30, linetype="dashed", color = "red", size =1) + 
  geom_jitter(data=BMI_long %>%
                filter(Sex %in% c("Men", "Women"), 
                       Year %in% c("1985", "2017"), 
                       Region == "National"),
                aes(x =Sex, y =BMI), 
                width = .2, size =2, shape =21, 
                color = "black", fill = "gray") +
  geom_jitter(data=subset(BMI_long %>%
                            filter(Sex %in% c("Men", "Women"), 
                                   Year %in% c("1985", "2017"), 
                                   Region == "National", 
                                   Country == "USA")), 
                             aes(x =Sex, y =BMI), 
                             width = .02, size =12, shape =21, 
                             color = "black", fill = "gray") + 
  # This will add points that are larger for the USA data
  geom_text(data=subset(BMI_long%>%
                          filter(Sex %in% c("Men", "Women"), 
                                 Year %in% c("1985", "2017"), 
                                 Region == "National", Country == "USA")),
                          aes(x =Sex, y =BMI,label=Country), 
                          color = "black") + 
  # This will add USA labels to the USA points
  theme_linedraw() +
  # This will make the background of the plot white and the facet labels black
  theme(legend.position = "none", 
  # This is useful for removing the legend
        axis.text.x = element_text(size = 15,angle = 30, vjust = 0.5), 
        # this changes the size and angle of the x axis point labels 
        axis.text.y = element_text(size = 20), 
        axis.title.y = element_text(size =15), 
        axis.title.x = element_text(size =15), 
        strip.text.x = element_text(size = 15))
   
#Simplified difference plot 
BMI_diff_long[["Difference"]] <-BMI_diff_long[["Difference"]]%>%
  str_replace( pattern = "_difference", replacement = "")

Diff_plot<-BMI_diff_long %>% 
  filter(Sex %in% c("Men", "Women"), 
         Difference != "National") %>%
ggplot( aes(x = Difference, y = value, col = Difference)) + 
  geom_boxplot(outlier.shape = NA, color = "black" , aes(fill = Sex)) + 
  scale_fill_manual(values=c("dodgerblue", "orchid2")) +
  facet_grid(~ Sex) +
  geom_jitter(data=BMI_diff_long %>% 
    filter(Sex %in% c("Men", "Women"), 
           Difference != "National"), 
    aes(x =Difference, y = value), 
    width = .2, size = 2, shape = 21, 
    color = "black", fill = "gray") + 
  geom_jitter(data=subset(BMI_diff_long %>% 
    filter(Sex %in% c("Men", "Women"), 
           Difference != "National", 
           Country == "USA")), 
    aes(x = Difference, y = value), 
    width = .02, size =12, shape =21, 
    color = "black", fill = "gray") + 
  # This will add points that are larger for the USA data
  geom_text(data=subset(BMI_diff_long %>% 
    filter(Sex %in% c("Men", "Women"),
           Difference != "National"), 
           Country == "USA"),
    aes(x = Difference, y = value,label=Country), 
    color = "black") + 
  # This will add USA labels to the USA points
  theme_linedraw() +
    # This will make the background of the plot white and the facet labels black
  theme(legend.position = "none", 
  # This is useful for removing the legend
        axis.text.x = element_text(size = 15,angle = 30, vjust = 0.5), 
        # this changes the size and angle of the x axis point labels 
        axis.text.y = element_text(size = 20), 
        axis.title.y = element_text(size =15), 
        axis.title.x = element_text(size =15), 
        strip.text.x = element_text(size = 15))
  # this changes the size of x axis labels for the facet

#add labels
Diff_plot<-Diff_plot + 
        labs(title = "Change in BMI by region", 
             x = "", 
             y = "Change in BMI \n (1985 to 2017)") +
        theme(title = element_text (size = 12, face = "bold"))

Means_plot <-Means_plot + 
        labs(title = "Mean National BMI over time", 
             x = "", 
             y = "Mean BMI") +
        theme(title = element_text (size = 12, face = "bold"))



obesity_text<-tibble(Year=c(1985),BMI=c(31),Sex=c("Men"),label=c("Obesity"))

Means_plot <-Means_plot + 
             geom_text(data = obesity_text,
                       label=pull(obesity_text,label), 
                       color = "red", 
                       aes( fontface ="bold.italic", size = 13))

cowplot::plot_grid(Means_plot, Diff_plot, labels = c("A", "B"))

We could make a similar plot with the patchwork package:

Means_plot +labs(tag = 'A') + Diff_plot+ labs(tag = 'B') +
  plot_layout(guides = 'collect', ncol = 2)

Great, now we have put two plots together using the plot_grid() function of the cowplot package. This way we can clearly communicate two messages. The first being that BMI has increased over time globally and that many countries including the United States of America are approaching a mean BMI that is above the obesity threshold of 30. We can also see that women on average have larger BMI values than males, but that both genders show increased levels over time. In the second plot we can see that the increase in BMI is not just happening in urban communities, but in both rural and urban communities and particularly in women.

Our plot visually explores all of our main questions: 1) Is there a difference between rural and urban BMI estimates around the world? In particular, how does this look for females? 2) How have BMI estimates changed from 1985 to 2017? Again, In particular, how does this look for females? 3) How do different countries compare for BMI estimates? In particular, how does the United States compare to the rest of the world?

Now let’s save our plot as a png file using the png() function:

png(here::here("img", "main_plot.png"), height = 1500, width = 2300, res = 300)
cowplot::plot_grid(Means_plot, Diff_plot, labels = c("A", "B"))
dev.off()
png 
  2 

You might think that these two plots look a bit odd together, that if you look at the change in the boxplots in part A, that the data for Men shows a bigger change, however, we can’t tell from this plot how the individual countries are changing. Part B of the figure looks at the specific change for each country.

We could check our plot using the following code:

BMI_diff_long %>%
  group_by(Sex, Difference) %>%
  filter(!is.na(value))%>%
  filter(Country == "USA") %>%
  summarize(mean = mean(value), median = median(value))
# A tibble: 6 x 4
# Groups:   Sex [2]
  Sex   Difference  mean median
  <chr> <chr>      <dbl>  <dbl>
1 Men   National    3.10   3.10
2 Men   Rural       3.10   3.10
3 Men   Urban       3.1    3.1 
4 Women National    3.40   3.40
5 Women Rural       3.5    3.5 
6 Women Urban       3.4    3.4 
BMI_diff_long %>%
  group_by(Sex, Difference) %>%
  filter(!is.na(value))%>%
  summarize(mean = mean(value), median = median(value))
# A tibble: 6 x 4
# Groups:   Sex [2]
  Sex   Difference  mean median
  <chr> <chr>      <dbl>  <dbl>
1 Men   National    2.26   2.20
2 Men   Rural       2.23   2.30
3 Men   Urban       2.05   2.10
4 Women National    2.37   2.7 
5 Women Rural       2.38   2.5 
6 Women Urban       2.21   2.60
BMI_long %>%
  group_by(Sex, Year, Region) %>%
  filter(!is.na(BMI))%>%
  filter(Country == "USA") %>%
  summarize(mean = mean(BMI), median = median(BMI))
# A tibble: 12 x 5
# Groups:   Sex, Year [4]
   Sex   Year  Region    mean median
   <chr> <chr> <chr>    <dbl>  <dbl>
 1 Men   1985  National  25.8   25.8
 2 Men   1985  Rural     26.1   26.1
 3 Men   1985  Urban     25.7   25.7
 4 Men   2017  National  28.9   28.9
 5 Men   2017  Rural     29.2   29.2
 6 Men   2017  Urban     28.8   28.8
 7 Women 1985  National  25.7   25.7
 8 Women 1985  Rural     26     26  
 9 Women 1985  Urban     25.6   25.6
10 Women 2017  National  29.1   29.1
11 Women 2017  Rural     29.5   29.5
12 Women 2017  Urban     29     29  
BMI_long %>%
  group_by(Sex, Year, Region) %>%
  filter(!is.na(BMI))%>%
  summarize(mean = mean(BMI), median = median(BMI))
# A tibble: 12 x 5
# Groups:   Sex, Year [4]
   Sex   Year  Region    mean median
   <chr> <chr> <chr>    <dbl>  <dbl>
 1 Men   1985  National  23.4   23.6
 2 Men   1985  Rural     23.0   23.2
 3 Men   1985  Urban     23.8   24.1
 4 Men   2017  National  25.6   26.2
 5 Men   2017  Rural     25.2   25.8
 6 Men   2017  Urban     25.9   26.3
 7 Women 1985  National  24.0   24.2
 8 Women 1985  Rural     23.6   23.9
 9 Women 1985  Urban     24.6   24.6
10 Women 2017  National  26.4   26.1
11 Women 2017  Rural     26.0   26.2
12 Women 2017  Urban     26.8   26.4
BMI_numeric %>%
  group_by(Sex)%>%
  summarize(Nat_mean1985 = mean(National_BMI_1985), 
            Nat_mean2017 = mean(National_BMI_2017))
# A tibble: 2 x 3
  Sex   Nat_mean1985 Nat_mean2017
  <chr>        <dbl>        <dbl>
1 Men           23.4         25.6
2 Women         24.0         26.4
BMI_numeric %>%
  group_by(Sex)%>%
  summarize(Nat_med1985 = median(National_BMI_1985), 
            Nat_med2017 = median(National_BMI_2017))
# A tibble: 2 x 3
  Sex   Nat_med1985 Nat_med2017
  <chr>       <dbl>       <dbl>
1 Men          23.6        26.2
2 Women        24.2        26.1

We can see that the largest changes occur for Women.

Scroll through the output!

BMI_diff_long %>%
  filter(value>3.5) %>%
  arrange(-value) %>%
  # this allows us to show the full output
  print(n = 1e3)
# A tibble: 117 x 4
    Country                          Sex   Difference value
    <chr>                            <chr> <chr>      <dbl>
  1 Egypt                            Women Rural       5.9 
  2 Honduras                         Women National    5.6 
  3 Honduras                         Women Rural       5.5 
  4 Egypt                            Women National    5.1 
  5 Saint Lucia                      Women Rural       5   
  6 Honduras                         Women Urban       4.9 
  7 El Salvador                      Women Rural       4.8 
  8 Mexico                           Women Rural       4.8 
  9 Uzbekistan                       Women Rural       4.8 
 10 Dominican Republic               Women Rural       4.8 
 11 Comoros                          Women Urban       4.7 
 12 Nicaragua                        Women Rural       4.7 
 13 Saint Lucia                      Women National    4.7 
 14 El Salvador                      Women National    4.6 
 15 Haiti                            Women National    4.6 
 16 Kyrgyzstan                       Women Rural       4.6 
 17 Costa Rica                       Women Rural       4.5 
 18 Guatemala                        Women Rural       4.5 
 19 Jamaica                          Women Rural       4.5 
 20 Qatar                            Women Rural       4.40
 21 Bahrain                          Women Rural       4.4 
 22 Dominican Republic               Women National    4.4 
 23 Guatemala                        Women National    4.4 
 24 Malaysia                         Women Rural       4.4 
 25 Nicaragua                        Women National    4.4 
 26 Uzbekistan                       Women National    4.4 
 27 Bangladesh                       Women National    4.3 
 28 Gabon                            Women National    4.3 
 29 Iraq                             Women Rural       4.3 
 30 Mexico                           Women National    4.3 
 31 Saint Kitts and Nevis            Women Rural       4.3 
 32 Belize                           Women Rural       4.2 
 33 Comoros                          Women National    4.2 
 34 Costa Rica                       Women National    4.2 
 35 Kyrgyzstan                       Women National    4.2 
 36 Panama                           Women Rural       4.2 
 37 Tajikistan                       Women Rural       4.2 
 38 Tanzania                         Women Urban       4.2 
 39 Egypt                            Women Urban       4.20
 40 Brunei Darussalam                Women Rural       4.1 
 41 Gambia                           Women National    4.1 
 42 Ghana                            Women National    4.1 
 43 Haiti                            Women Rural       4.1 
 44 Nepal                            Women National    4.1 
 45 Bolivia                          Women National    4.10
 46 Chile                            Women Rural       4.10
 47 Grenada                          Women Rural       4.10
 48 Saint Lucia                      Men   Rural       4.10
 49 Samoa                            Women Rural       4.00
 50 Antigua and Barbuda              Women Rural       4   
 51 Bahamas                          Women Rural       4   
 52 Bangladesh                       Women Rural       4   
 53 Bolivia                          Women Rural       4   
 54 Comoros                          Women Rural       4   
 55 Dominica                         Women Rural       4   
 56 Jamaica                          Women National    4   
 57 Malaysia                         Women National    4   
 58 Mexico                           Women Urban       4   
 59 Nicaragua                        Women Urban       4   
 60 Panama                           Women National    4   
 61 Puerto Rico                      Women Rural       4   
 62 Saint Kitts and Nevis            Women National    4   
 63 Saint Lucia                      Men   National    4   
 64 Saint Lucia                      Women Urban       4   
 65 Suriname                         Women Rural       4   
 66 Turkmenistan                     Women Rural       4   
 67 Bhutan                           Women National    3.90
 68 Gabon                            Women Urban       3.90
 69 Ghana                            Women Urban       3.90
 70 Mali                             Women Urban       3.90
 71 Samoa                            Women Urban       3.90
 72 El Salvador                      Women Urban       3.9 
 73 Georgia                          Women Rural       3.9 
 74 Guyana                           Women Rural       3.9 
 75 Libya                            Women Rural       3.9 
 76 Saint Vincent and the Grenadines Women Rural       3.9 
 77 Samoa                            Women National    3.9 
 78 Grenada                          Women National    3.8 
 79 Nepal                            Women Rural       3.8 
 80 Pakistan                         Women National    3.8 
 81 Yemen                            Women National    3.8 
 82 Afghanistan                      Women National    3.80
 83 Brunei Darussalam                Women National    3.80
 84 Guatemala                        Women Urban       3.80
 85 Paraguay                         Women Rural       3.80
 86 Tajikistan                       Women National    3.80
 87 Dominica                         Women National    3.70
 88 Guyana                           Women National    3.70
 89 Algeria                          Women Rural       3.7 
 90 Argentina                        Women Rural       3.7 
 91 Belize                           Women National    3.7 
 92 Benin                            Women Urban       3.7 
 93 Benin                            Women National    3.7 
 94 Bolivia                          Women Urban       3.7 
 95 Dominican Republic               Women Urban       3.7 
 96 Gambia                           Women Urban       3.7 
 97 Iraq                             Women National    3.7 
 98 Mongolia                         Women Rural       3.7 
 99 Occupied Palestinian Territory   Women Rural       3.7 
100 Pakistan                         Women Rural       3.7 
101 Saint Lucia                      Men   Urban       3.7 
102 Saint Vincent and the Grenadines Women National    3.7 
103 Syrian Arab Republic             Women Rural       3.7 
104 Uzbekistan                       Women Urban       3.7 
105 Yemen                            Women Rural       3.7 
106 Antigua and Barbuda              Women National    3.6 
107 Costa Rica                       Women Urban       3.6 
108 Liberia                          Women Urban       3.6 
109 Malaysia                         Women Urban       3.6 
110 Mali                             Women National    3.6 
111 Papua New Guinea                 Women Rural       3.6 
112 Togo                             Women Urban       3.6 
113 Turkmenistan                     Women National    3.6 
114 Uruguay                          Women Rural       3.6 
115 Ecuador                          Women Rural       3.60
116 Malawi                           Women Urban       3.60
117 Qatar                            Women National    3.60

**Synopsis


We have evaluated BMI average estimates from 200 different countries around the world. To do so we imported data from a pdf using the pdftools package. We used tidyverse packages such as dplyr, stringr, and tidy to clean the data and get it in a workable format. Our statistical analysis focused on evaluating differences in BMI in females around the world across time and between rural and urban areas. We found a significant difference both between years and between the type of community among women globally using paired \(t\)-tests and nonparametric tests. Thus BMI has increased in women since 1985. Although BMI estimates are significantly higher in Urban areas compared to rural areas, BMI estimates have increased in both regions. We learned that there are assumptions and considerations to keep in mind when performing tests that compare two groups.

We learned that the two sample \(t\)-test relies on:

  1. normality of the data for both groups (this is not as much of an issue if the number of observations is relatively large total n>30)
  2. equal variance between the two groups (make sure you do the correct test if the data is not normal)
  3. independent observations

We learned that we can evaluate if our data is normally distributed by plotting the distribution and by creating Q-Q plots.

We learned that if our data is not normally distributed, we can consider these options:

  1. We can still perform a \(t\)-test if our n is large
  2. We can transform the data before performing a \(t\)-test

We learned that if our data has unequal variance, we can consider using the Welch’s \(t\)-test.

Importantly, we learned that when we have paired data (are observations are not independent) and we intend to compare means, we should perform a paired test (paired \(t\)-test or the nonparametric Wilcoxon signed rank test. Our data used in this case study was paired because observations were taken for the same countries for different categories of populations - thus, we wanted to compare these populations within the same country (male vs female, rural vs urban etc.). Other examples would be in a case-control study (if samples are matched for various demographics) or a study with repeated measures (for example, symptom measures from the same individual before and after a treatment). We learned that paired \(t\)-test tests a different hypothesis the two-sample \(t\)-test. In this case we are testing the differences among the pairs of observations and how close these differences are to zero, thus there is only one variance, the variance of the paired differences and therefore, we do not need to worry about the equality of variance assumption like we do with the two-sample \(t\)test. We learned that we can perform a paired \(t\)-test by adding the option paired = TRUE to the t.test() command.

The paired \(t\)-test assumes that:

  1. The paired differences are normally distributed, which we can evaluate using Q-Q plots or plotting the distribution of the differences.
  2. The paired differences are independent (meaning that observations do not influence each other).

We learned that if the normality assumption is violated, we can use the Wilcoxon signed rank test, or if our data is large enough, due to the central limit theorem, the paired \(t\)-test should still be robust to deviations from normality.

Using the ggplot2 package we were able to visualize trends in the data. Importantly, the largest increase appears to be in the rural areas particularly for women. We were also able to identify how the US compared to other countries and which countries showed the largest increase in BMI over time. Analyses like this are important for defining which groups could benefit the most from interventions, education, and policy changes when attempting to mitigate public health challenges. You can see in the article that this data came from that many additional considerations would be involved to perform such an analysis.

Suggested Homework


Students can evaluate the change in BMI over time using the global data available for each year between 2015 and 2017. The data can be found here.

Additional Information


Session info


sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] OCSdata_1.0.2             patchwork_1.1.1          
 [3] cowplot_1.1.1             ggrepel_0.9.1            
 [5] ggplot2_3.3.5             tidyr_1.1.4              
 [7] purrr_0.3.4               glue_1.6.1               
 [9] tibble_3.1.6              dplyr_1.0.7              
[11] readr_2.1.1               stringr_1.4.0            
[13] pdftools_3.1.1            koRpus.lang.en_0.1-4     
[15] koRpus_0.13-8             sylly_0.1-6              
[17] read.so_0.1.1             wordcountaddin_0.3.0.9000
[19] magrittr_2.0.2            knitr_1.37               
[21] here_1.0.1               

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.8        lattice_0.20-45   assertthat_0.2.1  rprojroot_2.0.2  
 [5] digest_0.6.29     utf8_1.2.2        R6_2.5.1          evaluate_0.15    
 [9] httr_1.4.2        highr_0.9         pillar_1.7.0      rlang_1.0.1      
[13] curl_4.3.2        rstudioapi_0.13   data.table_1.14.2 jquerylib_0.1.4  
[17] Matrix_1.3-4      rmarkdown_2.11    qpdf_1.1          labeling_0.4.2   
[21] sylly.en_0.1-3    bit_4.0.4         munsell_0.5.0     compiler_4.1.2   
[25] xfun_0.29         pkgconfig_2.0.3   askpass_1.1       htmltools_0.5.2  
[29] tidyselect_1.1.2  fansi_1.0.2       crayon_1.5.0      tzdb_0.2.0       
[33] withr_2.5.0       grid_4.1.2        jsonlite_1.8.0    gtable_0.3.0     
[37] lifecycle_1.0.1   DBI_1.1.2         scales_1.1.1      vroom_1.5.7      
[41] cli_3.2.0         stringi_1.7.6     farver_2.1.0      fs_1.5.2         
[45] remotes_2.4.2     bslib_0.3.1       ellipsis_0.3.2    generics_0.1.1   
[49] vctrs_0.3.8       tools_4.1.2       bit64_4.0.5       hms_1.1.1        
[53] parallel_4.1.2    fastmap_1.1.0     yaml_2.3.5        colorspace_2.0-2 
[57] usethis_2.1.5     sass_0.4.0       

Estimate of RMarkdown Compilation Time:

About 31 - 41 seconds

This compilation time was measured on a PC machine operating on Windows 10. This range should only be used as an estimate as compilation time will vary with different machines and operating systems.

Acknowledgments


We would like to acknowledge Jessica Fanzo for assisting in framing the major direction of the case study.

We would like to acknowledge Michael Breshock for his contributions to this case study and developing the OCSdata package.

We would also like to acknowledge the Bloomberg American Health Initiative for funding this work.

LS0tDQp0aXRsZTogIk9wZW4gQ2FzZSBTdHVkaWVzOiBFeHBsb3JpbmcgZ2xvYmFsIHBhdHRlcm5zIG9mIG9iZXNpdHkgYWNyb3NzIHJ1cmFsIGFuZCB1cmJhbiByZWdpb25zIg0KY3NzOiBzdHlsZS5jc3MNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBpbmNsdWRlczoNCiAgICAgICBpbl9oZWFkZXI6IEdBX1NjcmlwdC5SaHRtbA0KICAgIHNlbGZfY29udGFpbmVkOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQo8c3R5bGU+DQojVE9DIHsNCiAgYmFja2dyb3VuZDogdXJsKCJodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vaW1nL2ljb24tYmFoaS5wbmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogMjQwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCjwhLS0gT3BlbiBhbGwgbGlua3MgaW4gbmV3IHRhYi0tPiAgDQo8YmFzZSB0YXJnZXQ9Il9ibGFuayIvPiAgDQo8ZGl2IGlkPSJnb29nbGVfdHJhbnNsYXRlX2VsZW1lbnQiPjwvZGl2Pg0KDQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPScvL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV9hL2VsZW1lbnQuanM/Y2I9Z29vZ2xlVHJhbnNsYXRlRWxlbWVudEluaXQnPjwvc2NyaXB0Pg0KDQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+DQpmdW5jdGlvbiBnb29nbGVUcmFuc2xhdGVFbGVtZW50SW5pdCgpIHsNCiAgbmV3IGdvb2dsZS50cmFuc2xhdGUuVHJhbnNsYXRlRWxlbWVudCh7cGFnZUxhbmd1YWdlOiAnZW4nfSwgJ2dvb2dsZV90cmFuc2xhdGVfZWxlbWVudCcpOw0KfQ0KPC9zY3JpcHQ+DQoNCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlID0gVFJVRSwgY29tbWVudCA9IE5BLCBlY2hvID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgb3V0LndpZHRoID0gJzkwJScpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShtYWdyaXR0cikNCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJiZW5tYXJ3aWNrL3dvcmRjb3VudGFkZGluIiwgdHlwZSA9ICJzb3VyY2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImFsaXN0YWlyZTQ3L3JlYWQuc28iKQ0KbGlicmFyeSh3b3JkY291bnRhZGRpbikNCmxpYnJhcnkocmVhZC5zbykNCg0Kcm1hcmtkb3duOjo6cGVyZl90aW1lcl9yZXNldF9hbGwoKQ0Kcm1hcmtkb3duOjo6cGVyZl90aW1lcl9zdGFydCgicmVuZGVyIikNCmBgYA0KDQogDQojIyMjIHsub3V0bGluZSB9DQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAibWFpbl9wbG90LnBuZyIpKQ0KYGBgDQoNCiMjIyMNCg0KIyMjIyB7LmRpc2NsYWltZXJfYmxvY2t9DQoNCioqRGlzY2xhaW1lcioqOiBUaGUgcHVycG9zZSBvZiB0aGUgW09wZW4gQ2FzZSBTdHVkaWVzXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8pe3RhcmdldD0iX2JsYW5rIn0gcHJvamVjdCBpcyAqKnRvIGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgdmFyaW91cyBkYXRhIHNjaWVuY2UgbWV0aG9kcywgdG9vbHMsIGFuZCBzb2Z0d2FyZSBpbiB0aGUgY29udGV4dCBvZiBtZXNzeSwgcmVhbC13b3JsZCBkYXRhKiouIEEgZ2l2ZW4gY2FzZSBzdHVkeSBkb2VzIG5vdCBjb3ZlciBhbGwgYXNwZWN0cyBvZiB0aGUgcmVzZWFyY2ggcHJvY2VzcywgaXMgbm90IGNsYWltaW5nIHRvIGJlIHRoZSBtb3N0IGFwcHJvcHJpYXRlIHdheSB0byBhbmFseXplIGEgZ2l2ZW4gZGF0YXNldCwgYW5kIHNob3VsZCBub3QgYmUgdXNlZCBpbiB0aGUgY29udGV4dCBvZiBtYWtpbmcgcG9saWN5IGRlY2lzaW9ucyB3aXRob3V0IGV4dGVybmFsIGNvbnN1bHRhdGlvbiBmcm9tIHNjaWVudGlmaWMgZXhwZXJ0cy4gDQoNCiMjIyMNCg0KIyMjIyB7LmxpY2Vuc2VfYmxvY2t9DQoNClRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciB0aGUgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1Ob25Db21tZXJjaWFsIDMuMCBbKENDIEJZLU5DIDMuMCldKGh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy8zLjAvdXMvKXt0YXJnZXQ9Il9ibGFuayJ9ICBVbml0ZWQgU3RhdGVzIExpY2Vuc2UuDQoNCiMjIyMNCg0KIyMjIyB7LnJlZmVyZW5jZV9ibG9ja30NCg0KVG8gY2l0ZSB0aGlzIGNhc2Ugc3R1ZHkgcGxlYXNlIHVzZToNCg0KV3JpZ2h0LCBDYXJyaWUgYW5kIE1lbmcsIFFpZXIgYW5kIEphZ2VyLCBMZWFoIGFuZCBUYXViLCBNYXJnYXJldCBhbmQgSGlja3MsIFN0ZXBoYW5pZS4gKDIwMjApLiBodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS4gRXhwbG9yaW5nIGdsb2JhbCBwYXR0ZXJucyBvZiBvYmVzaXR5IGFjcm9zcyBydXJhbCBhbmQgdXJiYW4gcmVnaW9ucyAoVmVyc2lvbiB2MS4wLjApLg0KDQojIyMjDQoNClRvIGFjY2VzcyB0aGUgR2l0SHViIFJlcG9zaXRvcnkgZm9yIHRoaXMgY2FzZSBzdHVkeSBzZWUgaGVyZTogaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkuDQoNCllvdSBtYXkgYWxzbyBhY2Nlc3MgYW5kIGRvd25sb2FkIHRoZSBkYXRhIHVzaW5nIG91ciBgT0NTZGF0YWAgcGFja2FnZS4gVG8gbGVhcm4gbW9yZSBhYm91dCB0aGlzIHBhY2thZ2UgaW5jbHVkaW5nIGV4YW1wbGVzLCBzZWUgdGhpcyBbbGlua10oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9PQ1NkYXRhKS4gSGVyZSBpcyBob3cgeW91IHdvdWxkIGluc3RhbGwgdGhpcyBwYWNrYWdlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoIk9DU2RhdGEiKQ0KYGBgDQoNClRoaXMgY2FzZSBzdHVkeSBpcyBwYXJ0IG9mIGEgc2VyaWVzIG9mIHB1YmxpYyBoZWFsdGggY2FzZSBzdHVkaWVzIGZvciB0aGUgW0Jsb29tYmVyZyBBbWVyaWNhbiBIZWFsdGggIEluaXRpYXRpdmVdKGh0dHBzOi8vYW1lcmljYW5oZWFsdGguamh1LmVkdS9vcGVuLWNhc2Utc3R1ZGllcykuDQoNCioqKg0KDQpUaGUgdG90YWwgcmVhZGluZyB0aW1lIGZvciB0aGlzIGNhc2Ugc3R1ZHkgaXMgY2FsY3VsYXRlZCB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKSBhbmQgc2hvd24gYmVsb3c6IA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnJlYWR0YWJsZSA9IHRleHRfc3RhdHMoImluZGV4LlJtZCIpICMgcHJvZHVjaW5nIHJlYWRpbmcgdGltZSBtYXJrZG93biB0YWJsZQ0KcmVhZHRpbWUgPSByZWFkLnNvOjpyZWFkLm1kKHJlYWR0YWJsZSkgJT4lIGRwbHlyOjpzZWxlY3QoTWV0aG9kLCBrb1JwdXMpICU+JSAjIHJlYWRpbmcgdGFibGUgaW50byBkYXRhZnJhbWUsIHNlbGVjdGluZyByZWxldmFudCBmYWN0b3JzDQogIGRwbHlyOjpmaWx0ZXIoTWV0aG9kID09ICJSZWFkaW5nIHRpbWUiKSAlPiUgIyBkcm9wcGluZyB1bm5lY2Vzc2FyeSByb3dzDQogIGRwbHlyOjptdXRhdGUoa29ScHVzID0gcGFzdGUocm91bmQoYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfc3BsaXQoa29ScHVzLCAiICIpW1sxXV1bMV0pKSwgIm1pbnV0ZXMiKSkgJT4lICMgcm91bmRpbmcgcmVhZGluZyB0aW1lIGVzdGltYXRlDQogIGRwbHlyOjptdXRhdGUoTWV0aG9kID0gImtvUnB1cyIpICU+JSBkcGx5cjo6cmVsb2NhdGUoa29ScHVzLCAuYmVmb3JlID0gTWV0aG9kKSAlPiUgZHBseXI6OnJlbmFtZShgUmVhZGluZyBUaW1lYCA9IGtvUnB1cykgIyByZW9yZ2FuaXppbmcgdGFibGUNCmtuaXRyOjprYWJsZShyZWFkdGltZSwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNCioqUmVhZGFiaWxpdHkgU2NvcmU6ICoqDQoNCkEgcmVhZGFiaWxpdHkgaW5kZXggZXN0aW1hdGVzIHRoZSByZWFkaW5nIGRpZmZpY3VsdHkgbGV2ZWwgb2YgYSBwYXJ0aWN1bGFyIHRleHQuIEZsZXNjaC1LaW5jYWlkLCBGT1JDQVNULCBhbmQgU01PRyBhcmUgdGhyZWUgY29tbW9uIHJlYWRhYmlsaXR5IGluZGljZXMgdGhhdCB3ZXJlIGNhbGN1bGF0ZWQgZm9yIHRoaXMgY2FzZSBzdHVkeSB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKS4gVGhlc2UgaW5kaWNlcyBwcm92aWRlIGFuIGVzdGltYXRpb24gb2YgdGhlIG1pbmltdW0gcmVhZGluZyBsZXZlbCByZXF1aXJlZCB0byBjb21wcmVoZW5kIHRoaXMgY2FzZSBzdHVkeSBieSBncmFkZSBhbmQgYWdlLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpydCA9IHdvcmRjb3VudGFkZGluOjpyZWFkYWJpbGl0eSgiaW5kZXguUm1kIiwgcXVpZXQ9VFJVRSkgIyBwcm9kdWNpbmcgcmVhZGFiaWxpdHkgbWFya2Rvd24gdGFibGUNCmRmID0gcmVhZC5zbzo6cmVhZC5tZChydCkgJT4lIGRwbHlyOjpzZWxlY3QoaW5kZXgsIGdyYWRlLCBhZ2UpICU+JSAgIyByZWFkaW5nIHRhYmxlIGludG8gZGF0YWZyYW1lLCBzZWxlY3RpbmcgcmVsZXZhbnQgZmFjdG9ycw0KICB0aWR5cjo6ZHJvcF9uYSgpICU+JSBkcGx5cjo6bXV0YXRlKGdyYWRlID0gcm91bmQoYXMubnVtZXJpYyhncmFkZSkpLCAjIGRyb3BwaW5nIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcywgcm91bmRpbmcgYWdlIGFuZCBncmFkZSBjb2x1bW5zDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlID0gcm91bmQoYXMubnVtZXJpYyhhZ2UpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCmtuaXRyOjprYWJsZShkZiwgZm9ybWF0PSJtYXJrZG93biIpDQpgYGANCg0KKioqDQoNClBsZWFzZSBoZWxwIHVzIGJ5IGZpbGxpbmcgb3V0IG91ciBzdXJ2ZXkuDQoNCg0KPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsganVzdGlmeS1jb250ZW50OiBjZW50ZXI7Ij48aWZyYW1lIHNyYz0iaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZm9ybXMvZC9lLzFGQUlwUUxTZnBONEZOM0tFTHFCTkVnZjJBdHBpN1d5N05xeTJiZVNrRlFJTkw3WTVzQU1WNV93L3ZpZXdmb3JtP2VtYmVkZGVkPXRydWUiIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcwMCIgZnJhbWVib3JkZXI9IjAiIG1hcmdpbmhlaWdodD0iMCIgbWFyZ2lud2lkdGg9IjAiPkxvYWRpbmfigKY8L2lmcmFtZT48L2Rpdj4NCg0KDQojIyAqKk1vdGl2YXRpb24qKg0KDQpCb2R5IG1hc3MgaW5kZXggKFtCTUldKGh0dHBzOi8vd3d3LmNkYy5nb3YvaGVhbHRoeXdlaWdodC9hc3Nlc3NpbmcvYm1pL2FkdWx0X2JtaS9pbmRleC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9KSBpcyBvZnRlbiB1c2VkIGFzIGEgcHJveHkgZm9yICoqYWRpcG9zaXR5KiogKHRoZSBjb25kaXRpb24gb2YgaGF2aW5nIGV4Y2VzcyBib2R5IGZhdCkgYW5kIGlzIG1lYXN1cmVkIGFzIGFuIGluZGl2aWR1YWwncyB3ZWlnaHQgaW4ga2lsb2dyYW1zIChrZykgb3IgcG91bmRzIChsYnMpIGRpdmlkZWQgYnkgdGhlIGluZGl2aWR1YWwncyBoZWlnaHQgaW4gbWV0ZXJzICgkbV4yJCkgc3F1YXJlZC4NCg0KVGhlcmUgYXJlIGRpZmZlcmVudCBjYXRlZ29yaWVzIG9mIHdlaWdodCBkZWZpbmVkIHVzaW5nIGRpZmZlcmVudCBCTUkgdGhyZXNob2xkcyBhcyBkZWZpbmVkIGJ5IHRoZSBbV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbl0oaHR0cDovL3d3dy5ldXJvLndoby5pbnQvZW4vaGVhbHRoLXRvcGljcy9kaXNlYXNlLXByZXZlbnRpb24vbnV0cml0aW9uL2EtaGVhbHRoeS1saWZlc3R5bGUvYm9keS1tYXNzLWluZGV4LWJtaSl7dGFyZ2V0PSJfYmxhbmsifSAoV0hPKSwgdGhlIFtDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wgYW5kIFByZXZlbnRpb25dKGh0dHBzOi8vd3d3LmNkYy5nb3Yvb2Jlc2l0eS9hZHVsdC9kZWZpbmluZy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LCBhbmQgdGhlIFtOYXRpb25hbCBJbnN0aXR1dGVzIG9mIEhlYWx0aF0oaHR0cHM6Ly93d3cubmhsYmkubmloLmdvdi9zaXRlcy9kZWZhdWx0L2ZpbGVzL21lZGlhL2RvY3Mvb2Jlc2l0eS1ldmlkZW5jZS1yZXZpZXcucGRmKXt0YXJnZXQ9Il9ibGFuayJ9LiANCkZvciBleGFtcGxlLCB0aGVzZSBhcmUgdGhlIGNhdGVnb3JpZXMgZGVmaW5lZCBieSB0aGUgV0hPOiANCg0KYGBge3IsICBlY2hvPUZBTFNFLCBvdXQud2lkdGg9JzEwMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwib2Jlc2l0eV9jbGFzc2VzLmpwZWciKSkNCmBgYA0KDQojIyMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYm9va3MvTkJLNTM1NDU2L2Jpbi9ibWlfV0hPLmpwZyl7dGFyZ2V0PSJfYmxhbmsifV0NCg0KVGhlIGZvbGxvd2luZyBjaGFydCBjYW4gaGVscCB5b3UgaWRlbnRpZnkgeW91ciBlc3RpbWF0ZSBvZiBCTUk6DQpgYGB7ciwgIGVjaG89RkFMU0UsIG91dC53aWR0aD0nMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJCTUljaGFydC5qcGVnIikpDQpgYGANCg0KIyMjIyMjIFtbc291cmNlXShodHRwOi8vZGlldC5tYXlvY2xpbmljLm9yZy9kaWV0L2VhdC93aGF0LWlzLXlvdXItYm1pP3hpZD1ubF9NYXlvQ2xpbmljRGlldF8yMDE2MDQyNil7dGFyZ2V0PSJfYmxhbmsifV0NCg0KDQpSZWNlbnRseSwgYW4gW2FydGljbGVdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0xMTcxLXgucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IHB1Ymxpc2hlZCBpbiBbKk5hdHVyZSpdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20pe3RhcmdldD0iX2JsYW5rIn0gZXZhbHVhdGVkIGFuZCBjb21wYXJlZCB0aGUgQk1JIG9mIHBvcHVsYXRpb25zIGluIHJ1cmFsIGFuZCB1cmJhbiBjb21tdW5pdGllcyBhcm91bmQgdGhlIHdvcmxkOiANCg0KYGBge3IsICBlY2hvPUZBTFNFLCBvdXQud2lkdGg9JzEwMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInBhcGVyLnBuZyIpKQ0KYGBgDQoNCiMjIyMgey5yZWZlcmVuY2VfYmxvY2t9DQpOQ0QgUmlzayBGYWN0b3IgQ29sbGFib3JhdGlvbiAoTkNELVJpc0MpLiBSaXNpbmcgcnVyYWwgYm9keS1tYXNzIGluZGV4IGlzIHRoZSBtYWluIGRyaXZlciBvZiB0aGUgZ2xvYmFsIG9iZXNpdHkgZXBpZGVtaWMgaW4gYWR1bHRzLiAqTmF0dXJlKiAqKjU2OSoqLCAyNjDigJMyNjQgKDIwMTkpLiANCg0KIyMjIw0KDQpUaGUgYXJ0aWNsZSBjaGFsbGVuZ2VkIHRoZSB3aWRlbHktaGVsZCB2aWV3IHRoYXQgaW5jcmVhc2VkIHVyYmFuaXphdGlvbiB3YXMgb25lIG9mIHRoZSBtYWpvciByZWFzb25zIGZvciBpbmNyZWFzZWQgZ2xvYmFsIG9iZXNpdHkgcmF0ZXMuIA0KVGhpcyB2aWV3IGNhbWUgYWJvdXQgYmVjYXVzZSBtYW55IGNvdW50cmllcyBhcm91bmQgdGhlIHdvcmxkIGhhdmUgc2hvd24gaW5jcmVhc2VkIHVyYmFuaXphdGlvbiBsZXZlbHMgaW4gcGFyYWxsZWwgd2l0aCBpbmNyZWFzZWQgb2Jlc2l0eSByYXRlcy4gDQpJbiB0aGlzIGFydGljbGUsIGhvd2V2ZXIsIHRoZSBOQ0QtUmlzQyBhcmd1ZWQgdGhhdCB0aGlzIG1pZ2h0IG5vdCBiZSB0aGUgY2FzZSBhbmQgdGhhdCBpbiBmYWN0IGZvciBtb3N0IHJlZ2lvbnMgYXJvdW5kIHRoZSB3b3JsZCwgQk1JIG1lYXN1cmVtZW50cyBhcmUgaW5jcmVhc2luZyBpbiBydXJhbCBwb3B1bGF0aW9ucyBqdXN0IGFzIG11Y2gsIGlmIG5vdCBtb3JlIHNvLCB0aGFuIHVyYmFuIHBvcHVsYXRpb25zLiANCkZ1cnRoZXJtb3JlLCB0aGlzIHN0dWR5IHN1Z2dlc3RlZCB0aGF0IG9iZXNpdHkgaGFzIHBhcnRpY3VsYXJseSBpbmNyZWFzZWQgaW4gZmVtYWxlIHBvcHVsYXRpb25zIGluIHJ1cmFsIHJlZ2lvbnM6IA0KDQo+ICJXZSBub3RlZCBhIHBlcnNpc3RlbnRseSBoaWdoZXIgcnVyYWwgQk1JLCBlc3BlY2lhbGx5IGZvciB3b21lbi4iDQoNCkluIHRoaXMgY2FzZSBzdHVkeSwgd2Ugd2lsbCBldmFsdWF0ZSB0aGUgZGF0YSByZXBvcnRlZCBpbiB0aGlzIGFydGljbGUgdG8gZXhwbG9yZSByZWdpb25hbCBhbmQgZ2VuZGVyIHNwZWNpZmljIGRpZmZlcmVuY2VzIGluIHRoZSBvYmVzaXR5IHJhdGVzIGFyb3VuZCB0aGUgd29ybGQgaW4gMTk4NSBhbmQgMjAxNy4gDQpNb3N0IGltcG9ydGFudGx5IHdlIHdpbGwgdGVzdCBpZiB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gb2Jlc2l0eSByYXRlcyBiZXR3ZWVuIHJ1cmFsIGFuZCB1cmJhbiBjb21tdW5pdGllcyBhbmQgaWYgdGhlcmUgaGFzIGJlZW4gYSBjaGFuZ2UgaW4gb2Jlc2l0eSBvdmVyIHRpbWUgZm9yIHRoZXNlIHJlZ2lvbnMsIHBhcnRpY3VsYXJseSBmb3Igd29tZW4uIA0KVG8gZG8gdGhpcyB3ZSB3aWxsIHRlc3QgaWYgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGF2ZXJhZ2UgQk1JIGZvciBlYWNoIGdyb3VwLg0KDQoNCiMjICoqTWFpbiBRdWVzdGlvbnMqKg0KKioqDQoNCiMjIyMgey5tYWluX3F1ZXN0aW9uX2Jsb2NrfQ0KDQo8Yj48dT4gT3VyIG1haW4gcXVlc3Rpb25zIGFyZTogPC91PjwvYj4NCg0KMSkgSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIEJNSSBlc3RpbWF0ZXMgYXJvdW5kIHRoZSB3b3JsZD8gSW4gcGFydGljdWxhciwgd2hhdCBkb2VzIHRoaXMgZGlmZmVyZW5jZSBsb29rIGxpa2UgZm9yIHdvbWVuPw0KMikgSG93IGhhdmUgQk1JIGVzdGltYXRlcyBjaGFuZ2VkIGZyb20gMTk4NSB0byAyMDE3PyBJbiBwYXJ0aWN1bGFyLCB3aGF0IGRvZXMgdGhpcyBjaGFuZ2Ugb3ZlciB0aW1lIGxvb2sgbGlrZSBmb3Igd29tZW4/DQozKSBIb3cgZG8gZGlmZmVyZW50IGNvdW50cmllcyB2YXJ5IGluIHRlcm1zIG9mIGVzdGltYXRlcyBvZiBCTUk/IEluIHBhcnRpY3VsYXIsIGhvdyBkb2VzIHRoZSBVbml0ZWQgU3RhdGVzIGNvbXBhcmUgdG8gdGhlIHJlc3Qgb2YgdGhlIHdvcmxkPw0KDQojIyMjDQoNCiMjICoqTGVhcm5pbmcgT2JqZWN0aXZlcyoqIA0KKioqDQoNClRoZSBza2lsbHMsIG1ldGhvZHMsIGFuZCBjb25jZXB0cyB0aGF0IHN0dWRlbnRzIHdpbGwgYmUgZmFtaWxpYXIgd2l0aCBieSB0aGUgZW5kIG9mIHRoaXMgY2FzZSBzdHVkeSBhcmU6DQoNCjx1PioqRGF0YSBzY2llbmNlIExlYXJuaW5nIE9iamVjdGl2ZXM6Kio8L3U+DQoNCjEuIEltcG9ydGluZyBkYXRhIGZyb20gYSBQREYgKGBwZGZ0b29sc2ApICANCjIuIFN1YnNldHRpbmcgYW5kIGZpbHRlcmluZyBkYXRhIChgZHBseXJgKSAgDQozLiBXb3JraW5nIHdpdGggY2hhcmFjdGVyIHN0cmluZ3MgKGBzdHJpbmdyYCkgIA0KNC4gUmVzaGFwaW5nIGRhdGEgaW50byBkaWZmZXJlbnQgZm9ybWF0cyAoYHRpZHlyYCkgIA0KNS4gQXBwbHlpbmcgZnVuY3Rpb25zIHRvIGFsbCBjb2x1bW5zIG9mIGEgdGliYmxlIChgcHVycnJgKSAgDQo2LiBDcmVhdGluZyBkYXRhIHZpc3VhbGl6YXRpb25zIChgZ2dwbG90MmApIHdpdGggbGFiZWxzIChgZ2dyZXBlbGApICANCjcuIENvbWJpbmluZyBtdWx0aXBsZSBwbG90cyAoYGNvd3Bsb3RgIGFuZCBgcGF0Y2h3b3JrYCkgIA0KDQo8dT4qKlN0YXRpc3RpY2FsIExlYXJuaW5nIE9iamVjdGl2ZXM6Kio8L3U+IA0KDQoxLiBGYW1pbGlhcml0eSB3aXRoIHRoZSB1c2Ugb2YgUXVhbnRpbGUtUXVhbnRpbGUgcGxvdHMgdG8gYXNzZXNzIG5vcm1hbGl0eSAgIA0KMi4gRGVmaW5lIGFuZCB1bmRlcnN0YW5kIHRoZSB1dGlsaXR5IG9mIGFscGhhIGFuZCB0aGUgcCB2YWx1ZSAgDQozLiBEZXNjcmliZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIG5vbnBhcmFtZXRyaWMgYW5kIHBhcmFtZXRyaWMgdGVzdHMgIA0KNC4gQmUgYWJsZSB0byBpZGVudGlmeSBwYWlyZWQgZGF0YSAgDQo1LiBJbXBsZW1lbnRhdGlvbiBvZiBhIFtwYWlyZWQgJHQkLXRlc3RdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzU1Nzk0NjUvKXt0YXJnZXQ9Il9ibGFuayJ9ICANCjYuIEludGVycHJldGF0aW9uIG9mIGEgW3BhaXJlZCAkdCQtdGVzdF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNTU3OTQ2NS8pe3RhcmdldD0iX2JsYW5rIn0gIA0KNy4gSW1wbGVtZW50YXRpb24gb2YgYSBbV2lsY294b24gc2lnbmVkLXJhbmsgdGVzdF0oaHR0cDovL3d3dy5iaW9zdGF0aGFuZGJvb2suY29tL3dpbGNveG9uc2lnbmVkcmFuay5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9DQo4LkludGVycHJldGF0aW9uIG9mIGEgW1dpbGNveG9uIHNpZ25lZC1yYW5rIHRlc3RdKGh0dHA6Ly93d3cuYmlvc3RhdGhhbmRib29rLmNvbS93aWxjb3hvbnNpZ25lZHJhbmsuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifQ0KOS4gVW5kZXJzdGFuZGluZyBvZiB0aGUgbmVlZCBmb3IgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uICANCg0KDQpJbiB0aGlzIGNhc2Ugc3R1ZHksIHdlIHdpbGwgd2FsayB5b3UgdGhyb3VnaCBpbXBvcnRpbmcgZGF0YSBmcm9tIGEgcGRmLCBjbGVhbmluZywgd3JhbmdsaW5nIGFuZCB2aXN1YWxpemluZyB0aGUgZGF0YSwgYW5kIDxiPiBjb21wYXJpbmcgdHdvIGdyb3VwcyA8L2I+LiAgDQoNCldlIHdpbGwgdXNlIHdlbGwtZXN0YWJsaXNoZWQgYW5kIGNvbW1vbmx5IHVzZWQgcGFja2FnZXMsIGluY2x1ZGluZyBgc3RyaW5ncmAsIGB0aWR5cmAsIGBkcGx5cmAsIGBwdXJycmAsIGFuZCBgZ2dwbG90MmAgZnJvbSB0aGUgW2B0aWR5dmVyc2VgXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifS4gDQpTcGVjaWZpY2FsbHksIHRoZSB0aWR5dmVyc2UgaXMNCg0KPiAiYW4gb3BpbmlvbmF0ZWQgY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzIGRlc2lnbmVkIGZvciBkYXRhIHNjaWVuY2UuIEFsbCBwYWNrYWdlcyBzaGFyZSBhbiB1bmRlcmx5aW5nIHBoaWxvc29waHkgYW5kIGNvbW1vbiBBUElzIi4NCg0KQW5vdGhlciB3YXkgb2YgcHV0dGluZyBpdCBpcyB0aGF0IGl0J3MgYSBzZXQgb2YgcGFja2FnZXMgdGhhdCBhcmUgdXNlZnVsIHNwZWNpZmljYWxseSBmb3IgZGF0YSBtYW5pcHVsYXRpb24sIGV4cGxvcmF0aW9uLCBhbmQgdmlzdWFsaXphdGlvbiB3aXRoIGEgY29tbW9uIHBoaWxvc29waHkuDQpXaGlsZSBzb21lIHN0dWRlbnRzIG1heSBiZSBmYW1pbGlhciB3aXRoIHByZXZpb3VzIHBhY2thZ2VzIGZyb20gdGhlIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UsIHRoZXNlIHBhY2thZ2VzIG1ha2UgZGF0YSBzY2llbmNlIGluIFIgZXNwZWNpYWxseSBlZmZpY2llbnQuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICIyMCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vdGlkeXZlcnNlLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciKQ0KYGBgDQoNCldlIHdpbGwgYmVnaW4gYnkgbG9hZGluZyB0aGUgcGFja2FnZXMgdGhhdCB3ZSB3aWxsIG5lZWQgd2l0aCBhIGJyaWVmIGV4cGxhbmF0aW9uOg0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkocGRmdG9vbHMpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoZ2x1ZSkNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KE9DU2RhdGEpDQpgYGANCg0KDQo8dT4qKlBhY2thZ2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5OioqIDwvdT4NCg0KIFBhY2thZ2UgICB8IFVzZSBpbiB0aGlzIGNhc2Ugc3R1ZHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tDQpbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgIHwgdG8gZWFzaWx5IGxvYWQgYW5kIHNhdmUgZGF0YSB3aXRoIHJlbGF0aXZlIHBhdGhzDQpbcGRmdG9vbHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9wZGZ0b29scy9wZGZ0b29scy5wZGYpe3RhcmdldD0iX2JsYW5rIn0gICB8IHRvIHJlYWQgYSB0ZXh0IGZyb20gcGRmIGludG8gUiAgIA0KW3N0cmluZ3JdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3N0cmluZ3IuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIG1hbmlwdWxhdGUgdGhlIHRleHQgZGF0YQ0KW3JlYWRyXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBtYW5pcHVsYXRlIHRoZSB0ZXh0IGRhdGEgd2l0aGluIHRoZSBwZGYgaW50byBpbmRpdmlkdWFsIGxpbmVzICANCltkcGx5cl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gYXJyYW5nZS9maWx0ZXIvc2VsZWN0IHN1YnNldHMgb2YgdGhlIGRhdGEgDQpbdGliYmxlXShodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgfCB0byBjcmVhdGUgZGF0YSBvYmplY3RzIHRoYXQgd2UgY2FuIG1hbmlwdWxhdGUgd2l0aCBgZHBseXJgL2BzdHJpbmdyYC9gdGlkeXJgL2BwdXJycmANClttYWdyaXR0cl0oaHR0cHM6Ly9tYWdyaXR0ci50aWR5dmVyc2Uub3JnL2FydGljbGVzL21hZ3JpdHRyLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICB8IHRvIHVzZSB0aGUgYCU8PiVgIHBpcGluZyBvcGVyYXRvcg0KW2dsdWVdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvYmxvZy8yMDE3LzEwL2dsdWUtMS4yLjAvKXt0YXJnZXQ9Il9ibGFuayJ9ICB8IHRvIHBhc3RlIG9yIGNvbWJpbmUgY2hhcmFjdGVyIHN0cmluZ3MgYW5kIGRhdGEgdG9nZXRoZXINCltwdXJycl0oaHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gcGVyZm9ybSBmdW5jdGlvbnMgb24gYWxsIGNvbHVtbnMgb2YgYSB0aWJibGUNClt0aWR5cl0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gY29udmVydCBkYXRhIGZyb20gJ3dpZGUnIHRvICdsb25nJyBmb3JtYXQNCltnZ3Bsb3QyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pICAgIHwgdG8gbWFrZSB2aXN1YWxpemF0aW9ucyB3aXRoIG11bHRpcGxlIGxheWVycw0KW2dncmVwZWxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3JlcGVsL3ZpZ25ldHRlcy9nZ3JlcGVsLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICAgfCB0byBhbGxvdyBsYWJlbHMgaW4gZmlndXJlcyBub3QgdG8gb3ZlcmxhcA0KW2Nvd3Bsb3RdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb3dwbG90L3ZpZ25ldHRlcy9pbnRyb2R1Y3Rpb24uaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW3BhdGNod29ya10oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9wYXRjaHdvcmspe3RhcmdldD0iX2JsYW5rIn0gfCB0byBhbGxvdyBwbG90cyB0byBiZSBjb21iaW5lZCANCltPQ1NkYXRhXShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL09DU2RhdGEpe3RhcmdldD0iX2JsYW5rIn0gfCB0byBhY2Nlc3MgYW5kIGRvd25sb2FkIE9DUyBkYXRhIGZpbGVzDQoNCg0KX19fDQoNClRoZSBmaXJzdCB0aW1lIHdlIHVzZSBhIGZ1bmN0aW9uLCB3ZSB3aWxsIHVzZSB0aGUgYDo6YCB0byBpbmRpY2F0ZSB3aGljaCBwYWNrYWdlIHdlIGFyZSB1c2luZyAoZS5nLiBgc3RyaW5ncjo6c3RyX2RldGVjdCgpYCkuIA0KVW5sZXNzIHdlIGhhdmUgb3ZlcmxhcHBpbmcgZnVuY3Rpb24gbmFtZXMsIHRoaXMgaXMgbm90IG5lY2Vzc2FyeSwgYnV0IHdlIHdpbGwgaW5jbHVkZSBpdCBoZXJlIHRvIGJlIGluZm9ybWF0aXZlIGFib3V0IHdoZXJlIHRoZSBmdW5jdGlvbnMgd2Ugd2lsbCB1c2UgY29tZSBmcm9tLg0KDQojIyAqKkNvbnRleHQqKg0KKioqDQoNClByZXZpb3VzIHdvcmsgaGFzIHNob3duIGhpZ2hlciBCTUkgKD4zMCkgaXMgYXNzb2NpYXRlZCB3aXRoIFtoaWdoZXIgcmF0ZXMgb2YgYWxsLWNhdXNlcyBvZiBtb3J0YWxpdHldKGh0dHBzOi8vd3d3Lm5obGJpLm5paC5nb3Yvc2l0ZXMvZGVmYXVsdC9maWxlcy9tZWRpYS9kb2NzL29iZXNpdHktZXZpZGVuY2UtcmV2aWV3LnBkZil7dGFyZ2V0PSJfYmxhbmsifSwgYXMgd2VsbCBhcyBpbmNyZWFzZWQgcmF0ZXMgb2YgdHlwZSAyIGRpYWJldGVzLCBjYW5jZXIsIGhlYXJ0IGRpc2Vhc2UsIGFuZCBzdHJva2UuIA0KDQpIb3dldmVyLCB0aGVyZSBhcHBlYXJzIHRvIGJlIGNhdmVhdHMgYXNzb2NpYXRlZCB3aXRoIHRoZSB1c2Ugb2YgYSB0aHJlc2hvbGQgbGlrZSB0aGlzIHRvIGV2YWx1YXRlIHB1YmxpYyBoZWFsdGggcmlzayBpbiB2YXJpb3VzIHBvcHVsYXRpb25zLg0KDQpBY2NvcmRpbmcgdG8gdGhpcyBbcmVwb3J0XShodHRwczovL3d3dy53aG8uaW50L251dHJpdGlvbi9wdWJsaWNhdGlvbnMvYm1pX2FzaWFfc3RyYXRlZ2llcy5wZGYpe3RhcmdldD0iX2JsYW5rIn06DQoNCj4uLi50aGUgYXNzb2NpYXRpb25zIGJldHdlZW4gQk1JLCBwZXJjZW50YWdlIG9mIGJvZHkgZmF0LCBhbmQgYm9keSBmYXQgZGlzdHJpYnV0aW9uIGRpZmZlciBhY3Jvc3MgcG9wdWxhdGlvbnMuIA0KDQo+IEluIHBhcnRpY3VsYXIsIGluIHNvbWUgQXNpYW4gcG9wdWxhdGlvbnMgYSBzcGVjaWZpYyBCTUkgcmVmbGVjdHMgYSBoaWdoZXIgcGVyY2VudGFnZSBvZiBib2R5IGZhdC4NCg0KPiBTb21lIFBhY2lmaWMgcG9wdWxhdGlvbnMgYWxzbyBoYXZlIGEgbG93ZXIgcGVyY2VudGFnZSBvZiBib2R5IGZhdCBhdCBhIGdpdmVuIEJNSS4NCg0KPiBUaGUgYXNzb2NpYXRpb25zIG9mIEJNSSBhbmQgY29tb3JiaWRpdGllcyBhcmUgcHJvYmFibHkgbm90IHN0YWJsZSB3aXRoaW4gcG9wdWxhdGlvbnMgb3ZlciB0aW1lLg0KDQojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vd3d3Lndoby5pbnQvbnV0cml0aW9uL3B1YmxpY2F0aW9ucy9ibWlfYXNpYV9zdHJhdGVnaWVzLnBkZikNCg0KIyMgKipMaW1pdGF0aW9ucyoqDQoqKioNCg0KVGhlIG1lYXN1cmVtZW50IG9mIEJNSSBoYXMgc29tZSBbbGltaXRhdGlvbnNdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzQ4OTA4NDEvcGRmL250LTUwLTExNy5wZGYpe3RhcmdldD0iX2JsYW5rIn0gdGhhdCBhcmUgd2VsbCByZWNvZ25pemVkLCBhcyBpdCBkb2VzIG5vdCBhY2NvdW50IGZvciB0aGUgY29tcG9zaXRpb24gb2YgYm9keSBtYXNzLCB0aGUgbG9jYXRpb24gb2YgYm9keSBmYXQsIG9yIHRoZSBjb250cmlidXRpb24gb2YgYm9keSBmcmFtZSBzaXplLiANCkhvd2V2ZXIsIFtCTUkgaGFzIGJlZW4gYSB1c2VmdWwgaGVhbHRoIGluZGljYXRvcl0oaHR0cHM6Ly9qb3VybmFscy5sd3cuY29tL2Fjc20taGVhbHRoZml0bmVzcy9GdWxsdGV4dC8yMDE2LzA3MDAwL1RIRV9CRU5FRklUU19PRl9CT0RZX01BU1NfSU5ERVhfQU5EX1dBSVNULjguYXNweCNwZGYtbGluayl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgcmlzayBmb3IgbWFueSBkaXNlYXNlcyBhbmQgY29uZGl0aW9ucyBwYXJ0aWN1bGFybHkgd2hlbiBjb21iaW5lZCB3aXRoIG90aGVyIHJpc2sgZmFjdG9yIGluZm9ybWF0aW9uLg0KDQpXaGlsZSBbZ2VuZGVyXShodHRwczovL3d3dy5nZW5kZXJzcGVjdHJ1bS5vcmcvcXVpY2stbGlua3MvdW5kZXJzdGFuZGluZy1nZW5kZXIvKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCBbc2V4XShodHRwczovL3d3dy53aG8uaW50L2dlbm9taWNzL2dlbmRlci9lbi9pbmRleDEuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBhcmUgbm90IGFjdHVhbGx5IGJpbmFyeSwgdGhlIGRhdGEgcHJlc2VudGVkIHRoYXQgaXMgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIG9ubHkgY29udGFpbiBkYXRhIGZvciBncm91cHMgb2YgaW5kaXZpZHVhbHMgZGVzY3JpYmVkIGFzIG1lbiBvciB3b21lbi4gDQoNCg0KIyMgKipXaGF0IGFyZSB0aGUgZGF0YT8qKg0KKioqDQpXZSB3aWxsIGJlIHVzaW5nIGRhdGEgbG9jYXRlZCB3aXRoaW4gYSB0YWJsZSBvZiB0aGUgW3N1cHBsZW1lbnRhcnkgbWF0ZXJpYWxdKGh0dHBzOi8vc3RhdGljLWNvbnRlbnQuc3ByaW5nZXIuY29tL2VzbS9hcnQlM0ExMC4xMDM4JTJGczQxNTg2LTAxOS0xMTcxLXgvTWVkaWFPYmplY3RzLzQxNTg2XzIwMTlfMTE3MV9NT0VTTTFfRVNNLnBkZil7dGFyZ2V0PSJfYmxhbmsifSBmb3IgdGhlIE5DRC1SaXNDIHBhcGVyIHJlZmVyZW5jZWQgYWJvdmUuIA0KDQpUaGlzIGlzIGEgcGRmIHRoYXQgY2FuIGJlIGZvdW5kIGZyZWVseSBhdmFpbGFibGUgb25saW5lLiBIZXJlIGlzIGEgc2NyZWVuc2hvdCBvZiB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhpcyB0YWJsZTogDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsImZpcnN0X3BhZ2UucG5nIikpDQpgYGANCg0KWW91IGNhbiBzZWUgdGhhdCB0aGUgZGF0YSBjb250YWluIGF2ZXJhZ2UgQk1JIGVzdGltYXRlcyBmb3IgYm90aCBtZW4gYW5kIHdvbWVuIGluIGNvdW50cmllcyBhdCB0aGUgbmF0aW9uYWwgbGV2ZWwsIGFzIHdlbGwgYXMgdGhlIGF2ZXJhZ2UgQk1JIGVzdGltYXRlcyBmb3IgdGhlIHJ1cmFsIGFuZCB1cmJhbiBhcmVhcyBvZiB0aGVzZSBjb3VudHJpZXMgaW4gYm90aCAxOTg1IGFuZCAyMDE3Lg0KDQpUaGUgZGF0YSB3aXRoaW4gdGhlIHBhcmVudGhlc2VzIGFyZSB0aGUgOTUlIFtjcmVkaWJsZSBpbnRlcnZhbHNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NyZWRpYmxlX2ludGVydmFsKXt0YXJnZXQ9Il9ibGFuayJ9IChDSXMpIGZvciB0aGUgYXZlcmFnZSBCTUkgZXN0aW1hdGVzLiBUaGUgYXV0aG9ycyBwcm92aWRlIHRoZXNlIENJcyBhcyBhIGd1aWRlIHRvIHVuZGVyc3RhbmQgaG93IGxpa2VseSB0aGUgZXN0aW1hdGUgaXMgZm9yIHRoZSB0cnVlIHBvcHVsYXRpb24gbWVhbiBCTUkuIEEgd2lkZXIgcmFuZ2Ugc3VnZ2VzdHMgdGhhdCB0aGUgZXN0aW1hdGUgaXMgbGVzcyBhY2N1cmF0ZSwgYXMgdGhlcmUgYXJlIG1vcmUgcG9zc2libGUgdmFsdWVzIGZvciB0aGUgdHJ1ZSBtZWFuIHdpdGggY3JlZGlibGUgZXZpZGVuY2UuIE5vdGljZSB0aGF0IHRoZSBtZWFuIEJNSSB2YWx1ZXMgYXJlICJhZ2Utc3RhbmRhcmRpc2VkLCIgd2hpY2ggbWVhbnMgdGhhdCB0aGUgbWVhbiB2YWx1ZXMgd2VyZSBhZGp1c3RlZCBmb3IgdGhlIGRpZmZlcmVudCBhZ2UgZGlzdHJpYnV0aW9ucyBvZiB0aGUgdmFyaW91cyBjb3VudHJpZXMgc28gdGhhdCB0aGUgY291bnRyaWVzIGNhbiBiZSBtb3JlIGZhaXJseSBjb21wYXJlZC4gRm9yIGV4YW1wbGUsIGlmIG9uZSBjb3VudHJ5IGhhcyBhIHBvcHVsYXRpb24gdGhhdCBpcyBjb25zaWRlcmFibHkgeW91bmdlciwgdGhlIG1lYW4gQk1JIG1pZ2h0IGJlIHF1aXRlIGxvdyAoYXMgeW91bmdlciBpbmRpdmlkdWFscyB0ZW5kIHRvIGhhdmUgbG93ZXIgQk1JIHZhbHVlcyBkdWUgdG8gZmFzdGVyIG1ldGFib2xpYyByYXRlcykuIE9uZSBtaWdodCBmYWxzZWx5IGNvbmNsdWRlIHRoYXQgcGVvcGxlIGluIHRoYXQgY291bnRyeSBnZW5lcmFsbHkgaGF2ZSBsb3dlciBCTUkgdmFsdWVzIHRoYW4gcGVvcGxlIGluIG1vc3Qgb3RoZXIgY291bnRyaWVzLCBob3dldmVyIHRoZSBsb3dlciBvdmVyYWxsIEJNSSBtZWFuIG1pZ2h0IHNpbXBseSBiZSBkdWUgdG8gdGhlIGZhY3QgdGhhdCB0aGUgY291bnRyeSBoYXMgYSB5b3VuZ2VyIHBvcHVsYXRpb24gdGhhbiB0aG9zZSBvdGhlciBjb3VudHJpZXMuDQoNCg0KIyMgKipEYXRhIEltcG9ydCoqDQoqKioNCkZpcnN0IGxldCdzIGRvd25sb2FkIHRoZSBkYXRhIGZyb20gdGhlIFBERiBmaWxlLiANCldlIHVzZSBgZmlsZS5leGlzdHMoKWAgZnJvbSBiYXNlIFIgdG8gY2hlY2sgaWYgdGhlIGZpbGUgd2Ugd2FudCB0byBkb3dubG9hZCBhbHJlYWR5IGV4aXN0cy4gDQpJZiBub3QsIHRoZW4gd2UgZG93bmxvYWQgaXQgdXNpbmcgdGhlIGZ1bmN0aW9uIGB1dGlsczo6ZG93bmxvYWQuZmlsZSgpYC4gV2UgYWxzbyB1c2UgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIG9mIHRoZSBgaGVyZWAgcGFja2FnZSB0byBsb29rIHNwZWNpZmljYWxseSBpbiB0aGUgYHJhd2Agc3ViZGlyZWN0b3J5IG9mIHRoZSBgZGF0YWAgZGlyZWN0b3J5IG9mIHRoZSBkaXJlY3Rvcnkgd2hlcmUgb3VyIG9wZW4gUlN0dWRpbyBwcm9qZWN0IGZpbGUgaXMgbG9jYXRlZC4gVGhpcyBoZWxwcyB1cywgYXMgd2UgZG8gbm90IG5lZWQgdG8gd3JpdGUgdGhlIGZ1bGwgcGF0aCB0byB0aGUgZG9jdW1lbnQuIFRoaXMgbWFrZXMgb3VyIGNvZGUgbW9yZSByZXByb2R1Y2libGUgZm9yIHNvbWVvbmUgZWxzZSwgd2hvIG1heSBoYXZlIGEgZGlmZmVyZW50IG92ZXJhbGwgZmlsZSBzdHJ1Y3R1cmUgb24gdGhlaXIgY29tcHV0ZXIgYnV0IGRvd25sb2FkcyBvdXIgY2FzZSBzdHVkeSBmaWxlcyBmcm9tIEdpdEh1YiBhbmQgdGhlcmVmb3JlIGhhcyB0aGUgc2FtZSBmaWxlIHN0cnVjdHVyZSBzcGVjaWZpY2FsbHkgZm9yIHRoZSBmaWxlcyBpbmNsdWRlZCBpbiBvdXIgR2l0SHViIHJlcG9zaXRvcnkuIFRodXMgb3VyIGNvbW1hbmRzIGFib3V0IGZpbGUgbG9jYXRpb25zIHN0aWxsIHdvcmsgb24gdGhlaXIgbG9jYWwgbWFjaGluZS4NCg0KYGBge3J9DQppZighZmlsZS5leGlzdHMoaGVyZSgiZGF0YSIsICJyYXciLCAicGFwZXJfc3VwcGxlbWVudC5wZGYiKSkpew0KICB1cmwgPC0gImh0dHBzOi8vc3RhdGljLWNvbnRlbnQuc3ByaW5nZXIuY29tL2VzbS9hcnQlM0ExMC4xMDM4JTJGczQxNTg2LTAxOS0xMTcxLXgvTWVkaWFPYmplY3RzLzQxNTg2XzIwMTlfMTE3MV9NT0VTTTFfRVNNLnBkZiINCiAgdXRpbHM6OmRvd25sb2FkLmZpbGUodXJsLCBoZXJlKCJkYXRhIiwgInJhdyIsICJwYXBlcl9zdXBwbGVtZW50LnBkZiIpKQ0KfQ0KYGBgDQoNCg0KV2Ugd2lsbCBzYXZlIG91ciBkYXRhIHRvIGEgZGlyZWN0b3J5IHdpdGhpbiBvdXIgd29ya2luZyBkaXJlY3RvcnkgY2FsbGVkIGRhdGEuIFdlIHdpbGwgY3JlYXRlIHN1YmRpcmVjdG9yaWVzIHdpdGhpbiB0aGlzIGRpcmVjdG9yeSB0byBvcmdhbml6ZSBvdXIgZGF0YS4gV2UgY2FuIHVzZSB0aGUgYGhlcmVgIGZ1bmN0aW9uIGZyb20gdGhlIGBoZXJlYCBwYWNrYWdlIHRvIG1ha2UgdGhpcyBwcm9jZXNzIGVhc2llci4gVGhlIGBoZXJlKClgIGZ1bmN0aW9uIGFsbG93cyB1cyB0byBzcGVjaWZ5IHRoZSBwYXRoIG9yIGxvY2F0aW9uIG9mIHRoZSBkb2N1bWVudCB0aGF0IHdlIHdhbnQgdG8gaW1wb3J0LCBzdGFydGluZyBmcm9tIHRoZSBkaXJlY3Rvcnkgd2hlcmUgYW4gYC5ScHJvamAgZmlsZSBpcyBsb2NhdGVkIGFmdGVyIGNyZWF0aW5nIGEgcHJvamVjdCBpbiBSU3R1ZGlvLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgaW1wb3J0IG91ciBmaWxlcyB3aXRoaW4gc3ViZGlyZWN0b3JpZXMgb2YgIGEgZGlyZWN0b3J5IGNhbGxlZCBgcmF3YCBvZiB0aGUgYGRhdGFgIGRpcmVjdG9yeS4gKCpOb3RlIHRoZSBuZXh0IGNodW5rIG9mIGNvZGUgd2lsbCBvbmx5IHdvcmsgZm9yIHlvdSBpZiB5b3UgcHVsbCB0aGUgcmVwb3NpdG9yeSBmcm9tIEdpdEh1YiBhbmQgc2V0IHVwIHlvdXIgZmlsZSBzdHJ1Y3R1cmUgaW4gdGhlIHNhbWUgd2F5LiopIElmIHlvdSBoYWQgdHJvdWJsZSBkb3dubG9hZGluZyB0aGUgZGF0YSBmcm9tIHRoZSBvcmdpbmFsIHNvdXJjZXMgeW91IGNhbiBkb3dubG9hZCB0aGVtIGZyb20gb3VyIFtHaXRIdWIgcmVwb3NpdG9yeSBmb3IgdGhpcyBjYXNlIHN0dWR5XShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS90cmVlL21hc3Rlci9kYXRhKSBvciB1c2UgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgaW5zdGFsbC5wYWNrYWdlcygiT0NTZGF0YSIpDQpsaWJyYXJ5KE9DU2RhdGEpDQpyYXdfZGF0YSgib2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5Iiwgb3V0cGF0aCA9IGdldHdkKCkpDQojIFRoaXMgd2lsbCBzYXZlIHRoZSBwZGYgZmlsZSBpbiBhICJPQ1NkYXRhL2RhdGEvcmF3LyIgc3ViZm9sZGVyIA0KIyBpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkNCmBgYA0KDQoqKioNCg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQo8L2RldGFpbHM+DQoNCioqKg0KDQpOb3cgdGhhdCB3ZSBoYXZlIGRvd25sb2FkZWQgdGhlIFBERiBmaWxlLCB3ZSB3aWxsIHJlYWQgaXQgaW4gdG8gUiB1c2luZyB0aGUgYHBkZnRvb2xzYCBwYWNrYWdlOg0KDQpgYGB7cn0NCnBkZl9vYmVzaXR5IDwtIHBkZnRvb2xzOjpwZGZfdGV4dChoZXJlKCJkYXRhIiwgInJhdyIsICJwYXBlcl9zdXBwbGVtZW50LnBkZiIpKQ0KYGBgDQoNCg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEgLS0gdGhlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIGZyb20gYmFzZSBSIGhlbHBzIHVzIHRvIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiBSIG9iamVjdHMuDQoNCmBgYHtyfQ0Kc3VtbWFyeShwZGZfb2Jlc2l0eSkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgd2UgaGF2ZSA2MyBlbGVtZW50cyB0aGF0IGFyZSBjaGFyYWN0ZXIgc3RyaW5ncy4gDQpZb3UgbWF5IGFsc28gbm90aWNlIHRoYXQgdGhlIG9yaWdpbmFsIFBERiBoYXMgNjMgcGFnZXMuIExldCdzIHRha2UgYSBsb29rIGF0IHNvbWUgb2YgdGhlc2UgZWxlbWVudHMgYW5kIGNvbXBhcmUgdGhlbSB0byB0aGUgcGFnZXMgaW4gdGhlIG9yaWdpbmFsIGRvY3VtZW50Lg0KDQpgYGB7cn0NCnBkZl9vYmVzaXR5WzFdICMgdGhpcyBsb29rcyBsaWtlIHRoZSBmaXJzdCBwYWdlDQpwZGZfb2Jlc2l0eVsyXSAjIHRoaXMgbG9va3MgbGlrZSB0aGUgc2Vjb25kIHBhZ2UNCmBgYA0KDQpgYGB7ciwgZXZhbCA9IFRSVUV9DQpzdHIocGRmX29iZXNpdHlbNTJdLCBuY2hhci5tYXggPSAxNjAwKQ0KYGBgDQpZZXMsIHRoaXMgbG9va3MgbGlrZSBkYXRhIGluIHRoZSB0YWJsZSB3ZSB3YW50LiANCldlIGFyZSB1c2luZyB0aGUgYHN0cigpYCBmdW5jdGlvbiB0byBwcmludCBqdXN0IGEgcG9ydGlvbiBvZiB0aGUgZGF0YS4gVGhlIGBzdHIoKWAgZnVuY3Rpb24gY29tcGFjdGx5IGRpc3BsYXlzIHRoZSBzdHJ1Y3R1cmUgb2YgYXJiaXRyYXJ5IFIgb2JqZWN0cy4gDQpUaGUgYG5jaGFyLm1heGAgYXJndW1lbnQgbGV0cyB1cyBkZWZpbmUgdGhlIG1heGltYWwgbnVtYmVyIG9mIGNoYXJhY3RlcnMgdG8gc2hvdy4gDQpOb3RlIGBwcmludChwZGZfb2Jlc2l0eVs1Ml0pYCB3b3VsZCBzaG93IHRoZSBlbnRpcmUgY29udGVudHMgb2YgdGhpcyBwYWdlIG9mIHRoZSB0YWJsZS4gDQoNCkhlcmUgaXMgdGhlIFBERiB2ZXJzaW9uIGFnYWluOg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJmaXJzdF9wYWdlLnBuZyIpKQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgb3V0cHV0IGxvb2tzIHByZXR0eSBzaW1pbGFyIHRvIHRoZSBwYWdlcyBvZiB0aGUgcGRmLCBidXQgdGhlIHNwYWNpbmcgaXMgYSBiaXQgYXdrd2FyZC4gDQpOb3RlIHRoYXQgdGhlIHdheSB0aGUgZGF0YSBpcyBkaXNwbGF5ZWQgaXMgcGFydGlhbGx5IGluZmx1ZW5jZWQgYnkgdGhlIHdpZHRoIHNldHRpbmcgb2YgdGhlIFJTdHVkaW8gd2luZG93LiAgDQoNCk5vdywgd2UgYXJlIGludGVyZXN0ZWQgaW4gYSBzdXBwbGVtZW50YXJ5IFRhYmxlIDMsIHdoaWNoIGhhcyBtdWx0aXBsZSBwYWdlcyBhbmQgaW5jbHVkZXMgdGhlIHNhbWUgaGVhZGVyIG9uIGVhY2ggcGFnZS4gDQpXZSBjYW4gdXNlIHRoYXQgdG8gZGV0ZXJtaW5lIHdoYXQgZWxlbWVudHMgb2Ygb3VyIGBwZGZfb2Jlc2l0eWAgY2hhcmFjdGVyIHN0cmluZ3MgaW5jbHVkZSBvdXIgdGFibGUuIA0KV2Ugd2lsbCB1c2UgdGhlIGBzdHJfZGV0ZWN0KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBzdHJpbmdyYCBwYWNrYWdlIHRvIHNlYXJjaCBmb3IgdGhlIGVsZW1lbnRzIHRoYXQgY29udGFpbiB0ZXh0IHRoYXQgaXMgY29uc2lzdGVudGx5IGluIHRoZSBoZWFkZXIuIA0KVGhlIG91dHB1dCBvZiBvZiB0aGlzIGZ1bmN0aW9uIHdpbGwgc2hvdyB3aGljaCBlbGVtZW50cyBvZiB0aGUgb2JqZWN0IChpbiB0aGlzIGNhc2UgcGFnZXMgb2YgdGhlIHBkZikgaW5jbHVkZSB0aGlzIHBhdHRlcm4gaW5kaWNhdGVkIGFzIGEgYFRSVUVgIG9yIGBGQUxTRWAuDQoNCmBgYHtyfQ0Kc3RyaW5ncjo6c3RyX2RldGVjdChwYXR0ZXJuID0gIkFnZS1zdGFuZGFyZGlzZWQgbWVhbiBCTUkiLCANCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nID0gcGRmX29iZXNpdHkpDQpgYGANCldlIG5vdGUsIHRoZSAiQWdlLXN0YW5kYXJkaXNlZCBtZWFuIEJNSSIgaXMgcGFydCBvZiB0aGUgaGVhZGVyIGluIHRoZSB0YWJsZSBvbiBldmVyeSBwYWdlLg0KVGhlIGNvZGUgYWJvdmUgc2hvd3MgdGhlIHBhZ2VzIHRoYXQgY29udGFpbiBvdXIgdGFibGUgb2YgaW50ZXJlc3QgKGFzIGBUUlVFYCB2YWx1ZXMpLg0KDQpOb3cgd2Ugd2lsbCBleHRyYWN0IGp1c3QgdGhlIGRhdGEgZm9yIHRoZSB0YWJsZSBub3cgYW5kIGNhbGwgaXQgYHJ1cmFsX3VyYmFuYC4gDQpUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgc3RyX3N1YnNldCgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuDQoNCmBgYHtyfQ0KcnVyYWxfdXJiYW4gPC0gc3RyaW5ncjo6c3RyX3N1YnNldChwYXR0ZXJuID0gIkFnZS1zdGFuZGFyZGlzZWQgbWVhbiBCTUkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nID0gcGRmX29iZXNpdHkpDQoNCnN1bW1hcnkocnVyYWxfdXJiYW4pIA0KYGBgDQoNCkhlcmUsIHdlIHNlZSB0aGVyZSBhcmUgMTAgcGFnZXMgd29ydGggb2YgZWxlbWVudHMgaW4gb3VyIGBydXJhbF91cmJhbmAgb2JqZWN0LiANCg0KTGV0J3MgY2hlY2sgdGhlIGZpcnN0IGFuZCBsYXN0IHBhZ2U6DQoNCmBgYHtyLCBldmFsPVRSVUV9DQpzdHIocnVyYWxfdXJiYW5bMV0sIG5jaGFyLm1heCA9IDE2MDApDQpgYGANClVzaW5nIGBbMV1gIHNob3dzIGp1c3QgdGhlIGZpcnN0IHBhZ2Ugb2YgdGhlIDEwIHBhZ2VzIHRoYXQgY29udGFpbiB0aGUgdGFibGUuIA0KQWxzbywgbm90aWNlIHRoZSBuZXcgbGluZSBgIlxuImAgdmFsdWVzIHRoYXQgaW5kaWNhdGUgbmV3IGxpbmVzLiANCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwiZmlyc3RfcGFnZV9pbl9SLnBuZyIpKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsImZpcnN0X3BhZ2UucG5nIikpDQpgYGANCg0KVGhpcyBsb29rcyB0aGUgc2FtZSBhcyB0aGUgYmVnaW5uaW5nLi4uIGhvdyBhYm91dCB0aGUgZW5kPw0KDQpgYGB7cn0NCnN0cihydXJhbF91cmJhblsxMF0sIG5jaGFyLm1heCA9IDE4MDApDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRSB9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJsYXN0X3BhZ2UucG5nIikpDQpgYGANCg0KDQpHcmVhdCEgT3VyIGBydXJhbF91cmJhbmAgb2JqZWN0IGxvb2tzIGxpa2UgaXQgY29udGFpbnMgdGhlIGVudGlyZSBTdXBwbGVtZW50YXJ5IDMgdGFibGUsIGFzIGJvdGggdGhlIGJlZ2lubmluZyBhbmQgdGhlIGVuZCBpbmNsdWRlIHRoZSBkYXRhIHdlIGV4cGVjdGVkLg0KDQpOb3cgd2Ugd2lsbCBzYXZlIHRoaXMgaW1wb3J0ZWQgZGF0YSBmb3IgbGF0ZXIgdXNlIGxpa2Ugc28gaW4gYSBzdWJkaXJlY3Rvcnkgb2YgdGhlIGBkYXRhYCBkaXJlY3RvcnkgY2FsbGVkIGBpbXBvcnRlZGA6DQoNCmBgYHtyfQ0Kc2F2ZShydXJhbF91cmJhbiwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQojIyAqKkRhdGEgV3JhbmdsaW5nKioNCioqKg0KSWYgeW91IGhhdmUgYmVlbiBmb2xsb3dpbmcgYWxvbmcgYnV0IHN0b3BwZWQsIHdlIGNvdWxkIGxvYWQgb3VyIGltcG9ydGVkIGRhdGEgZnJvbSB0aGUgImRhdGEiIGRpcmVjdG9yeSBsaWtlIHNvOg0KDQpgYGB7cn0NCmxvYWQoZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgaW1wb3J0IHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5Pg0KDQpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgT0NTZGF0YWAgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmxpYnJhcnkoT0NTZGF0YSkNCmBgYA0KDQpUaGVuLCB5b3UgbWF5IGxvYWQgdGhlIGltcG9ydGVkIGRhdGEgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmltcG9ydGVkX2RhdGEoIm9jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eSIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQpJZiB0aGUgcGFja2FnZSBkb2VzIG5vdCB3b3JrIGZvciB5b3UsIGFsdGVybmF0aXZlbHksIGFuIFJEQSBmaWxlIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkvbWFzdGVyL2RhdGEvaW1wb3J0ZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5L21hc3Rlci9kYXRhL2ltcG9ydGVkL2ltcG90ZWRfZGF0YS5yZGEpLiBEb3dubG9hZCB0aGlzIGZpbGUgYW5kIHRoZW4gcGxhY2UgaXQgaW4geW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IHdpdGhpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgYGltcG9ydGVkYCB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkIGBkYXRhYCB0byBjb3B5IGFuZCBwYXN0ZSBvdXIgY29kZS4gV2UgdXNlZCBhbiBSU3R1ZGlvIHByb2plY3QgYW5kIHRoZSBbYGhlcmVgIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSkgdG8gbmF2aWdhdGUgdG8gdGhlIGZpbGUgbW9yZSBlYXNpbHkuIA0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJpbXBvcnRlZCIsICJpbXBvcnRlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQo8L2RldGFpbHM+DQoqKioNCg0KQXQgdGhpcyBwb2ludCB3ZSBoYXZlIGxhcmdlIHN0cmluZ3Mgbm93IGZvciBlYWNoIHBhZ2Ugb2YgdGhlIHRhYmxlLCBidXQgdGhpcyBpcyBub3QgdmVyeSBjb252ZW5pZW50IHRvIHdvcmsgd2l0aC4gDQpOb3csIHdlIHdpbGwgd3JhbmdsZSB0aGUgZGF0YSBpbnRvIGEgbW9yZSB1c2FibGUgZm9ybS4gDQpJZGVhbGx5LCB3ZSB3b3VsZCBsaWtlIG91ciBkYXRhIHRvIGJlIGluIHNvbWUgc29ydCBvZiB0YWJ1bGFyIGZvcm0uDQoNCg0KDQojIyMgKipTZXBhcmF0ZSB0aGUgZGF0YSBpbnRvIGxpbmVzKioNCioqKg0KRmlyc3QsIGl0IHdvdWxkIGJlIHVzZWZ1bCB0byBzZXBhcmF0ZSB0aGUgZGF0YSB0aGF0IGlzIGN1cnJlbnRseSBpbiAxMCBnaWFudCBzdHJpbmcgY2h1bmtzIGludG8gaW5kaXZpZHVhbCBsaW5lcyBvciByb3dzIG9mIHRoZSB0YWJsZS4gVG8gZG8gdGhpcyB3ZSBjYW4gdXNlIHRoZSBgcmVhZF9saW5lcygpYCBmdW5jdGlvbiBvZiB0aGUgYHJlYWRyYCBwYWNrYWdlIHdoaWNoIHdpbGwgc2VwYXJhdGUgdGhlIGxpbmVzIGJhc2VkIG9uIHRoZSBgIlxuImAgbmV3IGxpbmUgY2hhcmFjdGVycy4NCmBgYHtyfQ0Kc3VtbWFyeShydXJhbF91cmJhbikNCnJ1cmFsX3VyYmFuIDwtIHJlYWRyOjpyZWFkX2xpbmVzKHJ1cmFsX3VyYmFuKQ0Kc3VtbWFyeShydXJhbF91cmJhbikgDQojIG5vdyB3ZSBoYXZlIDQ2MSBsaW5lcw0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgZWFjaCBzdHJpbmcgaW4gdGhlIGBydXJhbF91cmJhbmAgb2JqZWN0IGlzIG5vdyBhIHNpbmdsZSBsaW5lIGZyb20gdGhlIHRhYmxlLg0KDQpGb3IgZXhhbXBsZSwgbGluZSA2IHNob3dzIHRoZSBkYXRhIGZvciB3b21lbiBpbiBBZmdoYW5pc3Rhbi4NCmBgYHtyfQ0KcnVyYWxfdXJiYW5bNl0gDQpgYGANCg0KDQojIyMgKipSZW1vdmluZyBleGNlc3Mgd2hpdGUtc3BhY2UqKg0KKioqDQoNCldlIGFsc28gaGF2ZSBhIGxvdCBvZiB3aGl0ZS1zcGFjZS4gTGV0J3MgZ2V0IHJpZCBvZiB0aGUgZXhjZXNzIHdoaXRlIHNwYWNlcyB1c2luZyBgc3RyX3NxdWlzaCgpYCBhbHNvIGZyb20gdGhlIGBzdHJpbmdyYCBwYWNrYWdlLg0KDQpgYGB7cn0NCnJ1cmFsX3VyYmFuIDwtIHN0cmluZ3I6OnN0cl9zcXVpc2gocnVyYWxfdXJiYW4pIA0KaGVhZChydXJhbF91cmJhbikNCmBgYA0KDQpOb3cgaXQgaXMgbXVjaCBlYXNpZXIgdG8gc2VlIHRoZSBkYXRhLg0KDQpJZiB3ZSBsb29rIGF0IHRoZSBwYXJ0IG9mIG91ciBkYXRhIHRoYXQgY29udGFpbnMgdGhlIGVuZCBvZiB0aGUgZmlyc3QgcGFnZSBvZiB0aGUgdGFibGUgYW5kIHRoZSBzdGFydCBvZiB0aGUgc2Vjb25kIHdlIGNhbiBzZWUgdGhhdCB0aGUgaGVhZGVyIGluZm9ybWF0aW9uIGlzIHJlcGVhdGVkLCBhcyB3ZWxsIGFzIGEgbGluZSB3aXRoIHRoZSBwYWdlIG51bWJlciBhbmQgYW4gZW1wdHkgbGluZSwgYW5kIGEgbGluZSB0aGF0IHNheXMgIjIgMiIuIFRoZSAiMiAyIiBjb21lcyBmcm9tIGltcG9ydGluZyB0aGUgUERGIGZpbGUsIHRoZXNlIGFyZSB0aGUgc3VwZXJzY3JpcHQgIjIicyBpbiB0aGUgJG1eMiQgc3RyaW5ncyBpbiB0aGUgdGFibGUgaGVhZGVyIC0gbm8gbmVlZCB0byB3b3JyeSBhYm91dCB0aGlzLiBZb3UgbWF5IGFsc28gbm90aWNlIHNvbWUgdmFsdWVzIHRoYXQgYXJlIGBuYSpgIGFuZCB3ZSB3aWxsIGRlYWwgd2l0aCB0aGlzIGxhdGVyLg0KDQojIyMjIHsuc2Nyb2xsYWJsZSB9DQpgYGB7cn0NCnJ1cmFsX3VyYmFuWzQ0OjU2XSAjIHNjcm9sbCB0aHJvdWdoIQ0KYGBgDQojIyMjDQoNCkFsdGhvdWdoIHRoZSBoZWFkZXIgd2FzIG5lY2Vzc2FyeSBvbiBhbGwgb2YgdGhlIHBhZ2VzIG9mIHRoZSBwZGYgdmVyc2lvbiBvZiB0aGUgdGFibGUsIHdlIG9ubHkgbmVlZCB0aGF0IGluZm9ybWF0aW9uIG9uY2UgaW4gb3VyIGRhdGEuDQoNCiMjIyAqKlJlbW92aW5nIHVubmVjZXNzYXJ5IHJlcGVhdGVkIGhlYWRlciBpbmZvcm1hdGlvbioqDQoqKioNClNvLCBsZXQncyByZW1vdmUgYWxsIHRoZSBoZWFkZXIgaW5mb3JtYXRpb24gYW5kIHRoZSBwYWdlIG51bWJlciBsaW5lcyBmcm9tIHRoZSBgcnVyYWxfdXJiYW5gIG9iamVjdCwgdGhlbiB3ZSB3aWxsIG1ha2UgYSBzaW5nbGUgbGluZSBoZWFkZXIgZm9yIHRoZSBiZWdpbm5pbmcuDQpPbmUgd2F5IHRvIGRvIHRoaXMgaXMgdG8gZmluZCBhbGwgbGluZXMgdGhhdCBpbmNsdWRlIGVpdGhlciAiV29tZW4iIG9yICJNZW4iIGFuZCBvbmx5IGtlZXAgdGhpcyBkYXRhLg0KDQpGaXJzdCwgbGV0J3Mgc2VlIGhvdyBtYW55IGxpbmVzIGluY2x1ZGUgIldvbWVuIiBvciAiTWVuIi4gDQpXZSBjYW4gZG8gdGhpcyBieSBmaXJzdCBpZGVudGlmeWluZyB0aGUgbGluZXMgdGhhdCBjb250YWluIHRoZXNlIHBhdHRlcm5zIGFuZCB0aGVuIGNoZWNrIHRoZSBsZW5ndGggb2YgdGhlIHJlc3VsdGluZyB2ZWN0b3IgZm9yIHRoZSBsaW5lIG51bWJlcnMgdGhhdCBjb250YWluIHRoZXNlIHBhdHRlcm5zLiANCk5vdGUsIHRoZSBgInwiYCBpbmRpY2F0ZXMgdGhhdCB3ZSBhcmUgbG9va2luZyBmb3IgZWl0aGVyICJXb21lbiIgb3IgIk1lbiIuDQoNCiMjIyMgey5zY3JvbGxhYmxlIH0NCmBgYHtyfQ0KI3Njcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhDQpzdHJpbmdyOjpzdHJfd2hpY2goc3RyaW5nID0gcnVyYWxfdXJiYW4sIA0KICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiV29tZW58TWVuIikNCmBgYA0KIyMjIw0KDQpgYGB7cn0NCmxlbmd0aChzdHJpbmdyOjpzdHJfd2hpY2goc3RyaW5nID0gcnVyYWxfdXJiYW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIldvbWVufE1lbiIpKQ0KDQpgYGANCg0KTm90ZSB0aGF0IGBzdHJfd2hpY2goKWAgaXMgY2FzZSBzZW5zaXRpdmUsIHNvIGl0IHdvdWxkIG5vdCB3b3JrIHRvIHVzZSAid29tZW4iIGFzIHRoZSBwYXR0ZXJuLCBhbmQgdXNpbmcgIm1lbiIgd291bGQgcmV0dXJuIHRoZSBsaW5lcyB0aGF0IGNvbnRhaW4gIldvKiptZW4qKiIgb3IgIlllKiptZW4qKiIgZXRjLiAgVHJ5IHRoaXMgb3V0IHdpdGggYHBhdHRlcm4gPSAid29tZW4iYCBhbmQgYHBhdHRlcm4gPSAibWVuImAgdG8gc2VlIHRoZSByZXN1bHQhDQoNCk9LLCBzbyB0aGlzIGxvb2tzIGNvcnJlY3QuIA0KVGhpcyBpbmNsdWRlcyBtb3N0IGxpbmVzIGJ1dCB0aGVyZSBhcmUgZ2FwcyB3aGVyZSB0aGUgaGVhZGVyIGlzIGxvY2F0ZWQuIEl0IGxvb2tzIGxpa2Ugb3V0IG9mIHRoZSBvcmlnaW5hbCA0NjEgbGluZXMsIHRoZXJlIGFyZSA0MDAgbGluZXMgaW4gb3VyIHRhYmxlIHRoYXQgYXJlbid0IGhlYWRlcnMuDQoNCmBgYHtyfQ0KcnVyYWxfdXJiYW4gPC0gc3RyX3N1YnNldChzdHJpbmcgPSBydXJhbF91cmJhbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiV29tZW58TWVuIikNCmBgYA0KDQpXZSBjYW4gY2hlY2sgb3VyIGRhdGEgbm93IHVzaW5nIGVpdGhlciBgaGVhZCgpYCBmcm9tIGJhc2UgUiBvciBgZ2xpbXBzZSgpYCBmcm9tIHRoZSBgdGliYmxlYCAgYW5kIGBkcGx5cmAgcGFja2FnZS4gKFl1cCwgdGhlIHNhbWUgZnVuY3Rpb24gaXMgYXZhaWxhYmxlIGZyb20gYm90aCBwYWNrYWdlcyEpICBUaGUgYGhlYWQoKWAgZnVuY3Rpb24gc2hvd3MgdXMgdGhlIGZpcnN0IHJvd3Mgb3IgbGluZXMgb2YgdGhlIGRhdGEsIHdoaWxlIHRoZSBgZ2xpbXBzZSgpYCBmdW5jdGlvbiBwcm92aWRlcyB1cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdG90YWwgc2l6ZSBvZiB0aGUgb2JqZWN0IGFuZCBzaG93cyB1cyB0aGUgZmlyc3QgbGluZSBvciByb3cuDQoNCmBgYHtyfQ0KZHBseXI6OmdsaW1wc2UocnVyYWxfdXJiYW4pDQpgYGANCg0KTm93IG91ciBgcnVyYWxfdXJiYW5gIG9iamVjdCA0MDAgbGluZXMgaW5zdGVhZCBvZiA0NjEsIGxpa2UgaXQgZGlkIGJlZm9yZSB3aGVuIGl0IGNvbnRhaW5lZCB0aGUgcmVkdW5kYW50IGhlYWRlciBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQpoZWFkKHJ1cmFsX3VyYmFuKQ0KYGBgDQoNCkdyZWF0ISBTbyBub3cgb3VyIGRhdGEgbG9va3MgbXVjaCBiZXR0ZXIgYnV0IHdlIG5lZWQgdG8gYWRkIGJhY2sgb3VyIGhlYWRlciBhbmQgd2Ugd291bGQgbGlrZSB0aGlzIHRvIG9ubHkgYmUgYSBzaW5nbGUgbGluZSB0byBtYWtlIGl0IGVhc3kgdG8gdHJhbnNmb3JtIG91ciBkYXRhIGludG8gYSB0YWJsZSBvciB0YWJsZS1saWtlIG9iamVjdC4NCg0KQXMgYSByZW1pbmRlciwgaGVyZSBpcyB3aGF0IG91ciBoZWFkZXIgdXNlZCB0byBsb29rIGxpa2U6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsICBvdXQud2lkdGggPSAnMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJsYXN0X3BhZ2UucG5nIikpDQpgYGANCg0KV2Ugd2lsbCBhZGQgYSBuZXcgaGVhZGVyIGFmdGVyIGZpcnN0IGRlYWxpbmcgd2l0aCBzb21lIGFkZGl0aW9uYWwgc3BhY2luZyBhbmQgbWlzc2luZyBkYXRhIGlzc3Vlcy4NCg0KIyMjICoqRGVhbGluZyB3aXRoIHNwYWNpbmcqKg0KKioqDQpGaXJzdCBsZXQncyB0cnkgc3BsaXR0aW5nIG91ciBoZWFkZXItbGVzcyBkYXRhIGludG8gY29sdW1ucyBiYXNlZCBvbiBzcGFjZXMgdXNpbmcgdGhlIGBzdHJfc3BsaXQoKWAgZnVuY3Rpb24sIHdoZXJlIHdlIHNwZWNpZnkgdGhhdCB3ZSBhcmUgc3BsaXR0aW5nIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSBwYXR0ZXJuIG9mIGEgc3BhY2UgKHRoZSBzcGFjZSBpcyBpbmNsdWRlZCBpbiBxdW90ZXMpLiBSZWZlcnJpbmcgdG8gW3N0cl9zcGxpdCgpIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdHJpbmdyL3ZlcnNpb25zLzEuNC4wL3RvcGljcy9zdHJfc3BsaXQpLCB0aGUgYHNpbXBsaWZ5YCBhcmd1bWVudCBpcyBzZXQgdG8gYEZBTFNFYCBieSBkZWZhdWx0LCB3aGVyZSBhIGxpc3Qgb2YgY2hhcmFjdGVyIHZlY3RvcnMgaXMgcmV0dXJuZWQuIElmIGl0IGlzIHNldCB0byBgVFJVRWAgYXMgd2Ugd2lsbCBiZSBkb2luZyBoZXJlLCBhIGNoYXJhY3RlciBtYXRyaXggaXMgcmV0dXJuZWQuDQoNCkhlcmUsIHdlIHdpbGwgdGFrZSBhIGxvb2sgYXQganVzdCB0aGUgZmlyc3QgMTAgbGluZXMgdXNpbmcgdGhlIGBbMToxMCxdYCBzeW50YXg6IA0KDQoNCiMjIyMgey5zY3JvbGxhYmxlIH0NCmBgYHtyfQ0KI3Njcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhDQpzdHJfc3BsaXQoc3RyaW5nID0gcnVyYWxfdXJiYW4sIA0KICAgICAgICAgIHBhdHRlcm4gPSAiICIsIA0KICAgICAgICAgIHNpbXBsaWZ5ID0gVFJVRSlbMToxMCxdIA0KYGBgDQojIyMjDQoNClRoaXMgYWxtb3N0IHdvcmtlZCwgYnV0IHVuZm9ydHVuYXRlbHkgY291bnRyeSBuYW1lcyB0aGF0IGhhdmUgc3BhY2VzIHdpbGwgYmUgYSBwcm9ibGVtLiANCkZvciBleGFtcGxlLCB3ZSBjYW4gc2VlIHRoYXQgIkFtZXJpY2FuIFNhbW9hIiBoYXMgYmVlbiBkaXZpZGVkIGludG8gdHdvIGNvbHVtbnMgYW5kIGFsbCBzdWJzZXF1ZW50IGNvbHVtbnMgYXJlIHNoaWZ0ZWQuDQoNCkxldCdzIHRyeSB0aGlzIGFub3RoZXIgd2F5LiAgDQoNCkFzIHdlIGNhbiBzZWUgYnkgbG9va2luZyBhdCBvdXIgYHJ1cmFsX3VyYmFuYCBkYXRhLCB0aGUgY291bnRyeSBpbmZvcm1hdGlvbiBpcyBvbmx5IHByZXNlbnQgd2hlbiB0aGUgc2V4IGlzIFdvbWVuLiBMZXQncyB0cnkgdG8gZXh0cmFjdCB0aGUgY291bnRyeSBpbmZvcm1hdGlvbiBmcm9tIHRoZSByb3dzIHRoYXQgY29udGFpbiBkYXRhIGFib3V0IFdvbWVuLg0KDQpgYGB7cn0NCmhlYWQocnVyYWxfdXJiYW4pDQpgYGANClRvIGRvIHRoaXMsIHdlIHN0YXJ0IGJ5IG5vdGljaW5nIHRoYXQgdGhlIHNleCBhbHdheXMgc3RhcnRzIHdpdGggZWl0aGVyIGEgY2FwaXRhbCAiVyIgaWYgdGhlIHNleCBpcyBmZW1hbGUuIE5vdGUsIHdlIG5lZWQgdG8gdXNlIGEgc3BhY2UgYmVmb3JlIHRoZSAiVyIgb3RoZXJ3aXNlIHdlIHdpbGwgc3BsaXQgc29tZSBvZiB0aGUgY291bnRyeSBuYW1lcyBpZiB0aGUgbmFtZXMgc3RhcnRzIHdpdGggIlciLiANCg0KSGVyZSwgd2Ugd2lsbCBhbHNvIGludHJvZHVjZSB0aGUgY29uY2VwdCBvZiBwaXBpbmcsIHdoaWNoIHVzZXMgdGhlIGAlPiVgIGZyb20gdGhlIGBtYWdyaXR0cmAgcGFja2FnZSwgd2hpY2ggYWxsb3dzIHVzIHRvIHBpcGUgdGhlIG91dHB1dCBmcm9tIG9uZSBmdW5jdGlvbiB0byB0aGUgaW5wdXQgZm9yIGFub3RoZXIgZnVuY3Rpb24uDQpUaGlzIGlzIHJlYWxseSB1c2VmdWwgd2hlbiB3ZSBoYXZlIG11bHRpcGxlIHN0ZXBzLCB3aGljaCB3ZSB3aWxsIHNob3cgc29vbi4NCg0KRmlyc3QsIHdlIHdpbGwgc2VsZWN0IGp1c3QgdGhlIGRhdGEgZm9yIFdvbWVuIGFuZCBNZW4gc2VwYXJhdGVseToNCg0KYGBge3J9DQoNCldvbWVuIDwtIHN0cl9zdWJzZXQoc3RyaW5nID0gcnVyYWxfdXJiYW4sIA0KICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIldvbWVuIikgDQpNZW4gPC0gc3RyX3N1YnNldChzdHJpbmcgPSBydXJhbF91cmJhbiwgDQogICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTWVuIikNCg0KDQpoZWFkKFdvbWVuKQ0KaGVhZChNZW4pDQpgYGANCg0KDQpBbmQgdGhlbiBzcGxpdCB0aGUgYFdvbWVuYCBkYXRhIGJhc2VkIG9uIHRoZSBwYXR0ZXJuIGBwYXR0ZXJuID0gIiBXb21lbiJgIChOb3RlIHRoYXQgdGhlIHNwYWNlIGJlZm9yZSB0aGUgd29yZCAiV29tZW4iIGlzIGluY2x1ZGVkIC0gc28gdGhhdCBpdCBpcyBub3Qgd2l0aGluIHRoZSBjb3VudHJ5IGRhdGEpLg0KSGVyZSB3ZSB0YWtlIG91ciBgV29tZW5gIGRhdGFzZXQgYW5kIHBpcGUgaXQgaW50byB0aGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIGBzdHJfc3BsaXQoKWAgZnVuY3Rpb24sIHdoZXJlIHRoZW4gd2UgY2FuIHNwbGl0IG9uIHRoZSBwYXR0ZXJuIGAiIFdvbWVuImAuICBXZSB0aGVuIHBpcGUgdGhyb3VnaCB0aGUgYHVubGlzdCgpYCBmdW5jdGlvbiBzbyB0aGF0IHRoZSByZXN1bHRzIGFyZSBhIHZlY3RvciBpbnN0ZWFkIG9mIGEgbGlzdC4gIFdlIGFzc2lnbiB0aGlzIG5ldyByZXN1bHQgdG8gdGhlIGBjb3VudHJ5X3NwbGl0YCBvYmplY3QuDQoNCmBgYHtyfQ0KY291bnRyeV9zcGxpdCA8LSANCiAgV29tZW4gJT4lDQogIHN0cmluZ3I6OnN0cl9zcGxpdChwYXR0ZXJuID0gIiBXb21lbiIpICU+JQ0KICB1bmxpc3QoKSANCg0KaGVhZChjb3VudHJ5X3NwbGl0KQ0KYGBgDQoNCkhvd2V2ZXIsIHdlIGNhbiBhbHNvIGRvIHRoZSBzYW1lIHRoaW5nIGluIGEgc2luZ2xlIHN0ZXAsIHdoZXJlIHdlIHJlcGxhY2UgdGhlIGBXb21lbmAgZGF0YXNldCB3aXRoIHRoZSBjb2RlIHdlIHVzZWQgdG8gY3JlYXRlIHRoYXQgZGF0YXNldC4gIFNvIGZpcnN0LCB3ZSBzZWxlY3QgdGhlICJXb21lbiIgYW5kIHRoZW4gd2Ugc3BsaXQgdGhlIGRhdGEgYnkgIiBXb21lbiIgdG8gY3JlYXRlIGEgbmV3IGRhdGFzZXQgYWxsIHdpdGhpbiB0aGUgc2FtZSBjaHVuayBvZiBjb2RlLg0KYGBge3J9DQpjb3VudHJ5X3NwbGl0IDwtIA0KICBzdHJfc3Vic2V0KHN0cmluZyA9IHJ1cmFsX3VyYmFuLA0KICAgICAgICAgICAgIHBhdHRlcm4gPSAiV29tZW4iKSAlPiUNCiAgc3RyaW5ncjo6c3RyX3NwbGl0KHBhdHRlcm49ICIgV29tZW4iKSAlPiUNCiAgdW5saXN0KCkNCg0KaGVhZChjb3VudHJ5X3NwbGl0KQ0KYGBgDQoNCk5vdywgd2UgY2FuIHNlZSB0aGF0IGBDb3VudHJ5YCBpcyBhbHdheXMgdGhlIG9kZCByb3dzIGFuZCB0aGUgIEJNSSBkYXRhIGZvciBXb21lbiBpcyB0aGUgZXZlbiByb3dzLiANCldlIGNhbiBzZWxlY3QgdGhlIG9kZCByb3dzIHVzaW5nIHRoZSA8Yj4gTW9kdWx1cyAoUmVtYWluZGVyIGZyb20gZGl2aXNpb24pIG9wZXJhdG9yPC9iPi4gDQpUaGlzIG9wZXJhdG9yIGxvb2tzIGxpa2UgdGhpcyBgJSVgLiANCkFsbCBvZGQgdmFsdWVzIHN1Y2ggYXMgMywgNywgb3IgMTUgd2hlbiBkaXZpZGVkIGJ5IDIgd291bGQgbGVhdmUgYSByZW1haW5kZXIgb2YgMS4gDQpXZSB3aWxsIHNlbGVjdCBqdXN0IHRoZXNlIHJvd3MgdXNpbmcgdGhlIGBmaWx0ZXIoKWAgZnVuY3Rpb24gb2YgYGRwbHlyYC4NCg0KYGBge3J9DQpjb3VudHJ5IDwtIA0KICB0aWJibGUoY291bnRyeV9zcGxpdCkgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHJvd19udW1iZXIoKSAlJSAyID09IDEpIA0KYGBgDQoNCldlIGNhbiB0YWtlIGEgbG9vayB0byBtYWtlIHN1cmUgdGhhdCBhbGwgdGhlIGNvdW50cnkgbmFtZXMgbG9vayBhcyBleHBlY3RlZC4gWW91IGNhbiBzY3JvbGwgdGhyb3VnaCB0aGUgY291bnRyeSBuYW1lcyBpZiB5b3UgaG92ZXIgb3ZlciB0aGUgZGF0YS4NCg0KYGBge3IsIGV2YWw9IEZBTFNFfQ0KY291bnRyeQ0KYGBgDQoNCiMjIyMgey5zY3JvbGxhYmxlIH0NCmBgYHtyLCBlY2hvID0gRkFMU0V9DQphcy5kYXRhLmZyYW1lKGNvdW50cnkpDQpgYGANCiMjIyMNCg0KTG9va3MgZ29vZCEgIFdlIGhhdmUgMjAwIGNvdW50cmllcyByZXByZXNlbnRlZCBpbiBvdXIgZGF0YXNldC4NCg0KR3JlYXQhIE5vdyB3ZSBoYXZlIGEgbGlzdCBvZiB0aGUgY291bnRyaWVzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGJvdGggdGhlIG1hbGUgYW5kIGZlbWFsZSBkYXRhLiANCg0KDQpSZW1lbWJlciB0aGUgZXZlbiByb3dzIGFyZSB0aGUgcm93cyB3aXRoIHRoZSBCTUkgZGF0YSBmb3Igd29tZW4uDQpUaGVzZSByb3cgdmFsdWVzIGhhdmUgYSByZW1haW5kZXIgb2YgMCB3aGVuIGRpdmlkZWQgYnkgMi4NCkxldCdzIHNlbGVjdCB0aGF0IGRhdGEgdG9vLg0KDQpgYGB7cn0NCldvbWVuX0JNSSA8LSANCiAgdGliYmxlKGNvdW50cnlfc3BsaXQpICU+JSANCiAgZHBseXI6OmZpbHRlcihyb3dfbnVtYmVyKCkgJSUgMiA9PSAwKQ0KDQpoZWFkKFdvbWVuX0JNSSkNCmBgYA0KVGhpcyBsb29rcyBzaW1pbGFyIHRvIG91ciBgTWVuYCBkYXRhIChhbHRob3VnaCBzbGlnaHRseSBkaWZmZXJlbnQpOg0KDQpgYGB7cn0NCmhlYWQoTWVuKQ0KYGBgDQoNCk5vdGljZSBob3cgdGhlIGRhdGEgaW4gZWFjaCByb3cgaXMgaW4gcXVvdGVzLiBUaGlzIGlzIGJlY2F1c2UgdGhlc2UgYXJlIGNoYXJhY3RlciBzdHJpbmdzLiBXZSBjYW4gY2hlY2sgdGhpcyBieSB1c2luZyB0aGUgYmFzZSBgY2xhc3MoKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KY2xhc3MoTWVuKQ0KYGBgDQoNCldoZXJlYXMgdGhlIGBXb21lbl9CTUlgIGRhdGEgaXMgYSB0aWJibGUuDQpgYGB7cn0NCmNsYXNzKFdvbWVuX0JNSSkNCmBgYA0KDQpMZXQncyBtYWtlIHRoZSBgTWVuYCBkYXRhIGludG8gYSB0aWJibGUgYXMgd2VsbDoNCmBgYHtyfQ0KTWVuIDwtIHRpYmJsZShNZW4pDQpgYGANCg0KSXQncyBhbHdheXMgYSBnb29kIGlkZWEgdG8gY2hlY2sgdGhhdCB5b3VyIGRhdGEgb2JqZWN0cyBhcmUgdGhlIHNpemUgeW91IGV4cGVjdCB3aGVuIHdyYW5nbGluZy4gDQpXZSBjYW4gZG8gc28gd2l0aCB0aGUgYGRpbSgpYCBmdW5jdGlvbiwgd2hpY2ggc2hvd3MgdXMgdGhlIGRpbWVuc2lvbnMgb2YgZGF0YSBvYmplY3RzLg0KDQpgYGB7cn0NCmRpbShXb21lbl9CTUkpDQpkaW0oTWVuKQ0KaGVhZChXb21lbl9CTUkpDQpoZWFkKE1lbikNCmBgYA0KR3JlYXQhIFRoZXJlIGFyZSAyMDAgcm93cyBvZiBjaGFyYWN0ZXIgc3RyaW5ncyB3aXRoIHRoZSBCTUkgZGF0YSBsaWtlIHdlIGV4cGVjdGVkIHRvIG1hdGNoIHRoZSAyMDAgY291bnRyaWVzIGluIG91ciBkYXRhLg0KDQpXZSBkbyBob3dldmVyIG5lZWQgdG8gYWRkIHRoZSB3b3JkIGAiIFdvbWVuImAgYmFjayB0byB0aGUgYFdvbWVuX0JNSWAgZGF0YS4NClRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIGBnbHVlKClgIGZ1bmN0aW9uIG9mIHRoZSBgZ2x1ZWAgcGFja2FnZS4gDQoNCkhlcmUgaXMgYSBzaW1wbGUgZXhhbXBsZSBvZiBob3cgdGhpcyBmdW5jdGlvbiBnbHVlcyB0d28gdGhpbmdzIHRvZ2V0aGVyLg0KDQpgYGB7cn0NCndoZXJlIDwtInRoZSBtb29uIg0KDQpnbHVlOjpnbHVlKCJ0aGUgY293IGp1bXBlZCBvdmVyIHt3aGVyZX0iKQ0KDQpgYGANCg0KTm93IGxldCdzIGFkZCAgdGhlIHdvcmQgIldvbWVuIiB0byBlYWNoIHJvdyBvZiB0aGUgYFdvbWVuX0JNSWAgZGF0YSB1c2luZyBgZ2x1ZWAuDQoNCkZpcnN0IGxldCdzIHB1bGwgdGhlIGBjb3VudHJ5X3NwbGl0YCBkYXRhIG91dCBhcyBhIHZlY3RvciB0byBtYWtlIHRoaXMgc2ltcGxlciB3aGVuIHdlIGBnbHVlYC4gVG8gZG8gdGhpcyB3ZSB3aWxsIHVzZSB0aGUgYHB1bGwoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4gVGhpcyBmdW5jdGlvbiB3aWxsIGFsbG93IHVzIHRvIGV4dHJhY3Qgb3IgcHVsbCBvdXQgdGhlIHNpbmdsZSB2YXJpYWJsZSBjYWxsZWQgYGNvdW50cnlfc3BsaXRgIHdoaWNoIGNvbnRhaW5zIHRoZSBCTUkgdmFsdWVzIGZvciB3b21lbiBmcm9tIHdpdGhpbiB0aGUgdGliYmxlIGNhbGxlZCBgV29tZW5fQk1JYCB3aGljaCB3ZSBqdXN0IGNyZWF0ZWQgYnkgZ3JhYmJpbmcgb25seSB0aGUgZXZlbiByb3dzIG9mIG91ciBvcmlnaW5hbCBgY291bnRyeV9zcGxpdGAgdGliYmxlLg0KDQpUaGVuLCBsZXQncyB1c2UgdGhlIGBmaXJzdCgpYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlIHRvIGdldCBhIHJlbWluZGVyIG9mIHdoYXQganVzdCB0aGUgZmlyc3Qgcm93IG9mIHRoZSBgY291bnRyeV9zcGxpdGAgY29sdW1uIG9mIHRoZSBgV29tZW5fQk1JYCBkYXRhIGxvb2tzIGxpa2UuDQoNCg0KYGBge3J9DQp3b21lbl9kYXRhIDwtIFdvbWVuX0JNSSAlPiUNCiAgcHVsbChjb3VudHJ5X3NwbGl0KQ0KDQpkcGx5cjo6Zmlyc3Qod29tZW5fZGF0YSkNCmBgYA0KDQpOb3cgd2Ugd2lsbCBwYXN0ZSB0aGUgd29yZCBgIldvbWVuImAgaW4gdGhlIGJlZ2lubmluZyBvZiBlYWNoIHZhbHVlIGluIHRoZSB2ZWN0b3IgdXNpbmcgdGhlIGBnbHVlKClgIGZ1bmN0aW9uLg0KDQpOb3RlIHRoYXQgdGhlcmUgd2lsbCBiZSBhIHNwYWNlIGluIGJldHdlZW4uDQpUaGlzIGlzIGJlY2F1c2UgdGhlIHNwYWNlIGlzIGFscmVhZHkgaW4gZnJvbnQgb2YgdGhlIGZpcnN0IEJNSSB2YWx1ZS4NCg0KYGBge3J9DQpXb21lbiA8LSBnbHVlKCJXb21lbnt3b21lbl9kYXRhfSIpDQpoZWFkKFdvbWVuKQ0KYGBgDQoNCllvdSBjb3VsZCBhbHNvIGRvIGFsbCBvZiB0aGlzIGluIG9uZSBzdGVwIGJ1dCBpdHMgbGVzcyBvYnZpb3VzIHdoYXQgaXMgaGFwcGVuaW5nLg0KDQpIZXJlIHdlIGNhbiBwdWxsIHRoZSBgY291bnRyeV9zcGxpdGAgZGF0YSBvdXQgb2YgdGhlIGBXb21lbl9CTUlgIG9iamVjdCBhbmQgdGhlbiBwYXN0ZSBgIldvbWVuImAgaW4gZnJvbnQgb2YgZWFjaCBzdHJpbmcgaW4gb25lIGNvbW1hbmQuDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KV29tZW4gPC0gZ2x1ZSgiV29tZW57cHVsbChXb21lbl9CTUksY291bnRyeV9zcGxpdCl9IikNCmBgYA0KDQoNCg0KSWYgd2UgdHJ5IHNwbGl0dGluZyBvdXIgZGF0YSBieSBzcGFjZSBhZ2Fpbiwgd2lsbCBpdCBoYXZlIHRoZSBleHBlY3RlZCBudW1iZXIgb2YgY29sdW1ucz8gV2hhdCBhYm91dCB0aGUgcm93cyB0aGF0IGNvbnRhaW4gYG5hKmAgdmFsdWVzPw0KDQpMZXQncyBqdXN0IHRha2UgdGhlIGBNZW5gIGRhdGEgdGhhdCBjb250YWluIGBuYSpgIHZhbHVlcy4gDQpUaGlzIGNvbHVtbiBpcyBjYWxsZWQgYHJ1cmFsX3VyYmFuYC4gDQoNCmBgYHtyfQ0KdW5rbm93biA8LSANCiAgTWVuJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChwYXR0ZXJuID0ibmFcXCoiLCANCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nID0gTWVuKSkgDQoNCmhlYWQodW5rbm93bikNCmBgYA0KDQpOb3cgd2UgY2FuIHRyeSBzcGxpdHRpbmcgYnkgYSBzcGFjZQ0KYGBge3J9DQp0aWJibGU6OmFzX3RpYmJsZShzdHJfc3BsaXQocHVsbCh1bmtub3duLCBNZW4pLCAiICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2ltcGxpZnkgPSBUUlVFKSkNCg0KYGBgDQoNClNvIGNsb3NlISBOb3RpY2UgdGhhdCB0aGUgYCJuYSoiYCB2YWx1ZXMgaGF2ZSBzaGlmdGVkIHRoZSBzdWJzZXF1ZW50IHZhbHVlcyB3aXRoaW4gdGhlIGNvbHVtbnMgYmVjYXVzZSB0eXBpY2FsbHkgdGhlcmUgaXMgYSBzcGFjZSBiZXR3ZWVuIHRoZSBCTUkgYW5kIHRoZSBjcmVkaWJsZSBpbnRlcnZhbHMuDQpIZXJlIHdlIGNhbiBzZWUgdGhpcyBkYXRhIGluIG91ciBvcmlnaW5hbCBwZGY6DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJtaXNzaW5nX3BkZi5wbmciKSkNCmBgYA0KDQoNCiMjIyAqKkRlYWxpbmcgd2l0aCBOQSB2YWx1ZXMqKg0KKioqDQoNCldlIG5lZWQgdG8gcmVwbGFjZSBvdXIgYG5hKmAgdmFsdWVzIHdpdGggc29tZXRoaW5nIHRoYXQgaW5jbHVkZXMgYSBzcGFjZSBzbyB0aGF0IHdoZW4gd2Ugc2VwYXJhdGUgb3VyIGRhdGEgYnkgc3BhY2UsIHdlIHdpbGwgaGF2ZSB0d28gdmFsdWVzIGluc3RlYWQgb2Ygb25lIHdoZW4gd2UgaGF2ZSBhbiBgbmEqYC4gDQpUaGVyZWZvcmUsIGBuYSogbmEqYCBzaG91bGQgd29yay4NCg0KU28sIGluIG9yZGVyIHRvIHVzZSB0aGUgZnVuY3Rpb25zIHdpdGhpbiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UsIG91ciBgTWVuYCBkYXRhIG5lZWRzIHRvIGJlIG9mIGBjaGFyYWN0ZXJgIGNsYXNzLCBzbyBsZXQncyBjaGVjayB0aGF0IG5vdy4NCg0KYGBge3J9DQpoZWFkKE1lbikNCmBgYA0KVGhlIGA8Y2hyPmAgaW5kaWNhdGVzIHRoYXQgb3VyIGBNZW5gIGRhdGEgaXMgYWxyZWFkeSBgY2hhcmFjdGVyYC4gQnV0IGlmIHRoaXMgaXMgbm90IHRoZSBjYXNlLCB3ZSBjb3VsZCB1c2UgdGhlIGAgYXMuY2hhcmFjdGVyKClgIGZ1bmN0aW9uIHRoYXQgYWxsb3dzIHVzIHRvIGNoYW5nZSBmYWN0b3IgZGF0YSB3aXRoaW4gdG8gdGhlIGNoYXJhY3RlciBjbGFzcy4NCg0KV2UgYXJlIGFsc28gY2hhbmdpbmcgdGhlIHN0cnVjdHVyZSBzbyB0aGF0IHRoZSBkYXRhIGlzIG5vdCB3aXRoaW4gYSBjb2x1bW4gY2FsbGVkIGBNZW5gLiANCmBgYHtyfQ0KTWVuIDwtIHB1bGwoTWVuLCBNZW4pDQpoZWFkKE1lbikNCmBgYA0KDQpgYGB7cn0NCk1lbiA8LSANCiAgc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKHN0cmluZyA9IE1lbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIm5hXFwqIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJuYVxcKiBuYVxcKiIpIA0KDQojIFRoZSBcXCBhcmUgbmVjZXNzYXJ5IGJlY2F1c2UgdGhlICogaXMgYSBzcGVjaWFsIGNoYXJhY3RlciANCiMgVGhlICogd291bGQgdHlwaWNhbGx5IGluZGljYXRlIGFueSBwb3NzaWJsZSB2YWx1ZSwgDQojIGJ1dCBoZXJlIHdlIGFjdHVhbGx5IHdhbnQgYSAiKiIgaW5zdGVhZA0KIyBUaHVzIHRoZSBkb3VibGUgYmFja3NsYXNoIGRvZXMgdGhhdCBmb3IgdXMNCiMgSGVyZSB3ZSBhcmUgcmVwbGFjaW5nIGFsbCBvY2N1cnJlbmNlcyBvZiB0aGUgbmEqIHZhbHVlcyANCiModGh1cyBzdHJfcmVwbGFjZV9hbGwgaW5zdGVhZCBvZiBzdHJfcmVwbGFjZSkgd2l0aCBuYSogbmEqLiANCmBgYA0KDQpMZXQncyBjaGVjayB0aGF0IGl0IHdvcmtlZC4uLg0KYGBge3J9DQpNZW5bMjA6MzBdDQpgYGANCg0KR3JlYXQhDQoNCk5vdyBmb3IgdGhlIFdvbWVuIGRhdGEgb2JqZWN0DQpgYGB7cn0NCmNsYXNzKFdvbWVuKQ0KIyB0aGUgZmVtYWxlIGRhdGEgaXMgYWxyZWFkeSBjaGFyYWN0ZXIgdHlwZQ0KV29tZW4gPC0gDQogIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbChzdHJpbmcgPSBXb21lbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIm5hXFwqIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJuYVxcKiBuYVxcKiIpIA0KV29tZW5bMjA6MzBdDQpgYGANCg0KR3JlYXQsIG5vdyB3ZSBjYW4gc3BsaXQgb3VyIGRhdGEgYnkgc3BhY2VzLg0KDQojIyMgKipTcGxpdHRpbmcgdGhlIGRhdGEqKg0KKioqDQpXZSB3aWxsIHNwbGl0IG91ciBkYXRhIGZpbmFsbHkgYnkgc3BhY2VzIHVzaW5nIHRoZSBgc3RyX3NwbGl0KClgIGZ1bmN0aW9uIG9mIHRoZSBgc3RyaW5ncmAgcGFja2FnZS4gV2UgY2FuIGNoZWNrIHRoZSByb3dzIHdpdGggTkEgdmFsdWVzIGJ5IGxvb2tpbmcgYXQgcm93cyAyMC0zMC4NCmBgYHtyfQ0KTWVuIDwtIHRpYmJsZTo6YXNfdGliYmxlKHN0cl9zcGxpdChNZW4sICIgIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBsaWZ5ID0gVFJVRSkpDQpNZW5bMjA6MzAsXQ0KYGBgDQpHcmVhdCEgSXQgbG9va3MgZ29vZCENCg0KTm93IGZvciB0aGUgYFdvbWVuYCBkYXRhLg0KDQpgYGB7cn0NCldvbWVuIDwtIGFzX3RpYmJsZShzdHJfc3BsaXQoV29tZW4sICIgIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBsaWZ5ID0gVFJVRSkpDQpoZWFkKFdvbWVuKQ0KV29tZW5bMjA6MzAsXSANCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgb3VyIGBuYWAgdmFsdWVzIGxvb2sgY29ycmVjdC4gDQoNCkxvb2tzIGdvb2QhDQogDQogDQojIyMgKipBZGRpbmcgbmV3IGhlYWRlcioqDQoqKioNCldlIGNhbiBzZWUgZnJvbSBvdXIgcGRmIGFuZCBvdXIgb2JqZWN0IGNhbGxlZCBgaGVhZGVyYCB3aGF0IHRoZSBoZWFkZXIgd2FzIGxpa2UgaW4gdGhlIG9yaWdpbmFsIHBkZiBkb2N1bWVudC4gV2UgbmVlZCB0byBhZGQgY29sdW1uIG5hbWVzIHRvIG91ciBkYXRhIGJhc2VkIG9uIHRoZSBvcmlnaW5hbCBkYXRhLg0KDQpXZSdsbCB3YWl0IHRvIGFkZCBgQ291bnRyeWAgdG8gdGhlIGhlYWRlciB1bnRpbCB3ZSBhZGQgdGhlIGNvdW50cnkgaW5mb3JtYXRpb24gdG8gb3VyIGRhdGEuDQoNCmBgYHtyfQ0KbmV3X2hlYWRlciA8LSBjKCJTZXgiLCJOYXRpb25hbF9CTUlfMTk4NSIsIA0KICAgICAgICAgICAgICAgICJOYXRpb25hbF9CTUlfMTk4NV9DSSIsICJSdXJhbF9CTUlfMTk4NSIsIA0KICAgICAgICAgICAgICAgICJSdXJhbF9CTUlfMTk4NV9DSSIsICJVcmJhbl9CTUlfMTk4NSIsDQogICAgICAgICAgICAgICAgIlVyYmFuX0JNSV8xOTg1X0NJIiwgIk5hdGlvbmFsX0JNSV8yMDE3IiwNCiAgICAgICAgICAgICAgICAiTmF0aW9uYWxfQk1JXzIwMTdfQ0kiLCJSdXJhbF9CTUlfMjAxNyIsDQogICAgICAgICAgICAgICAgIlJ1cmFsX0JNSV8yMDE3X0NJIiwgIlVyYmFuX0JNSV8yMDE3IiwgDQogICAgICAgICAgICAgICAgIlVyYmFuX0JNSV8yMDE3X0NJIikNCmBgYA0KDQpMZXQncyBjaGFuZ2UgdGhlIG5hbWVzIG9mIG91ciBjb2x1bW5zIG9mIG91ciB0aWJibGVzIHRvIHRoaXMgbmV3IGhlYWRlciBmb3Igb3VyIGBNZW5gIGFuZCBgV29tZW5gIGRhdGENCg0KYGBge3J9DQpuYW1lcyhXb21lbikgPC0gbmV3X2hlYWRlcg0KbmFtZXMoTWVuKSA8LSBuZXdfaGVhZGVyDQoNCmhlYWQoV29tZW4pDQpoZWFkKE1lbikNCmBgYA0KDQpOb3cgd2Ugd2lsbCBhZGQgb3VyIGNvdW50cnkgZGF0YSB0byBib3RoIG91ciBgTWVuYCBhbmQgYFdvbWVuYCB0aWJibGVzIHVzaW5nIHRoZSBgYmluZF9jb2xzKClgIGZ1bmN0aW9uLiBUaGlzIHdpbGwgYWRkIHRoZSBgY291bnRyeWAgYXMgYSBuZXcgY29sdW1uIHRvIHRoZSBgV29tZW5gIGRhdGEgb2JqZWN0IG9uIHRoZSBsZWZ0LiANCg0KYGBge3J9DQpXb21lbiA8LSBkcGx5cjo6YmluZF9jb2xzKGNvdW50cnksIFdvbWVuKQ0KaGVhZChXb21lbikNCmBgYA0KDQoNClNpbWlsYXJseSwgZm9yIHRoZSBkYXRhIGFib3V0IE1lbiwgdGhpcyB3aWxsIGFkZCB0aGUgYGNvdW50cnlgIGFzIGEgbmV3IGNvbHVtbiB0byB0aGUgYE1lbmAgZGF0YSBvYmplY3Qgb24gdGhlIGxlZnQuIA0KYGBge3J9DQpNZW4gPC0gZHBseXI6OmJpbmRfY29scyhjb3VudHJ5LCBNZW4pIA0KaGVhZChNZW4pDQpgYGANCg0KDQojIyMgKipDaGFuZ2luZyB2YXJpYWJsZSBuYW1lcyoqDQoqKioNCkhlcmUsIHdlIHdpbGwgY2hhbmdlIHRoZSB2YXJpYWJsZSBuYW1lIGluIHRoZSBgY291bnRyeWAgZGF0YXNldCB0byBgY291bnRyeWAgKGN1cnJlbnRseSBpdCBpcyBjYWxsZWQgYGNvdW50cnlfc3BsaXRgKS4gDQpXZSB3aWxsIGFsc28gaW50cm9kdWNlIHRoZSBjb25jZXB0IG9mIHRoZSBhc3NpZ25tZW50IHBpcGUuIA0KSW4gdGhpcyBjYXNlIG91ciBwaXBlIG9wZXJhdG9yIGxvb2tzIGxpa2UgdGhpcyBgJTw+JWAuIA0KVXNpbmcgdGhpcyBhZGRpdGlvbmFsIHBpcGUgcmVxdWlyZXMgYW5vdGhlciBwYWNrYWdlLCBjYWxsZWQgYG1hZ3JpdHRyYC4gDQpUaGUgb3RoZXIgc2ltcGxlciBwaXBlIG9wdGlvbnMgZnJvbSB0aGlzIHBhY2thZ2UgYXJlIGxvYWRlZCB3aXRoIGB0aWR5dmVyc2VgIChpZiB5b3UgdXNlZCBgbGlicmFyeSh0aWR5dmVyc2UpYCB3aGljaCBsb2FkcyBtb3N0IHRpZHl2ZXJzZSBwYWNrYWdlcyksIGJ1dCBub3QgdGhpcyB2ZXJzaW9uLiANCg0KVGhlIGA+YCBwb3J0aW9uIG9mIHRoZSBwaXBlIHN0aWxsIGJlaGF2ZXMgbGlrZSBhIG5vcm1hbCBwaXBlLCB3aGlsZSB0aGUgYDxgIHBvcnRpb24gb2YgdGhlIHBpcGUgbWFrZXMgYW4gYXNzaWdubWVudCB0byB3aGF0ZXZlciB0aGUgYDxgaXMgcG9pbnRpbmcgdG8sIGp1c3QgbGlrZSB3aGVuIHdlIHVzZSB0aGUgdHlwaWNhbCBhc3NpZ25tZW50IG9wZXJhdG9yIGA8LWAuIA0KDQpgYGB7cn0NCiMgbGlicmFyeShtYWdyaXR0cikgDQojIFdlIGNhbid0IHVzZSB0aGUgYCU8PiVgIHVubGVzcyB3ZSBsb2FkIHRoZSBtYWdyaXR0ciBwYWNrYWdlDQojIFdlIGhhdmUgYWxyZWFkeSBkb25lIHRoaXMgYnV0IHdlIGluY2x1ZGUgdGhpcyBmb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzLg0KIyBIZXJlIHdlIHdpbGwganVzdCB1c2UgdGhlIHRyYWRpdGlvbmFsIGFzc2lnbm1lbnQgc3RyYXRlZ3kNCg0KV29tZW4gPC0gZHBseXI6OnJlbmFtZShXb21lbiwgQ291bnRyeSA9IGNvdW50cnlfc3BsaXQpIA0KYGBgDQpIZXJlLCB3ZSBoYXZlIHJlbmFtZWQgdGhlIGBjb3VudHJ5X3NwbGl0YCB2YXJpYWJsZSB0byBiZSBjYWxsZWQgYENvdW50cnlgLiANCg0KSGVyZSwgd2UgcmVhc3NpZ24gYE1lbmAgdXNpbmcgdGhlIHBpcGUgc3RyYXRlZ3kuDQpgYGB7cn0NCk1lbiAlPD4lIHJlbmFtZShDb3VudHJ5ID0gY291bnRyeV9zcGxpdCkgDQpgYGANCg0KVGhpcyBpcyBlcXVpdmFsZW50IHRvDQpgYGB7ciwgZXZhbD1GQUxTRX0NCk1lbiA8LSByZW5hbWUoTWVuLCBDb3VudHJ5ID0gY291bnRyeV9zcGxpdCkNCmBgYA0KV2UgaGF2ZSByZW5hbWVkIHRoZSBgY291bnRyeV9zcGxpdGAgdmFyaWFibGUgdG8gdG8gYmUgY2FsbGVkIGBDb3VudHJ5YC4gDQpXZSBoYXZlIGFsc28gcmVhc3NpZ25lZCBgTWVuYCB0byB0aGUgc2FtZSBkYXRhIG9iamVjdC4gd2hpY2ggaGFzIHRoZSBgQ291bnRyeWAgdmFyaWFibGUgcmVuYW1lZC4gDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IG91ciBkYXRhIGZvciBgTWVuYCBhbmQgYFdvbWVuYDoNCg0KYGBge3J9DQpoZWFkKFdvbWVuKQ0KaGVhZChNZW4pDQpgYGANCiMjIyAqKkpvaW5pbmcgdGhlIGRhdGEqKg0KKioqDQpOb3cgdGhhdCBib3RoIG9mIHRoZXNlIHRpYmJsZXMgaGF2ZSB0aGUgc2FtZSBzdHJ1Y3R1cmUsIHdlIGNhbiBjb21iaW5lIG91ciBgTWVuYCBhbmQgYFdvbWVuYCBkYXRhIHVzaW5nIHRoZSBgYmluZF9yb3dzKClgIGZ1bmN0aW9uIG9mIGBkcGx5cmAuIFRoaXMgd2lsbCBjb21iaW5lIGFsbCBvZiB0aGUgcm93cyBiYXNlZCBvbiB0aGVpciBzaW1pbGFyIGNvbHVtbiBzdHJ1Y3R1cmUuDQpBbGwgb2YgdGhlIG1hbGUgZGF0YSAgd2lsbCBiZSBmaXJzdCBhbmQgYWxsIHRoZSBmZW1hbGUgZGF0YSB3aWxsIGJlIHNlY29uZC4NCg0KYGBge3J9DQpCTUkgPC0gZHBseXI6OmJpbmRfcm93cyhNZW4sIFdvbWVuKQ0KYGBgDQoNCkxldCdzIGNoZWNrIHRoZSBzaXplIG9mIG91ciBCTUkgZGF0YS4uLiBpdCBzaG91bGQgaGF2ZSA0MDAgcm93cyAob2JzKS4gV2UgY2FuIHNlZSB0aGUgZW5kIG9mIHRoZSBgQk1JYCBkYXRhIHVzaW5nIHRoZSAgYHRhaWwoKWAgZnVuY3Rpb246DQpgYGB7cn0NCnN0cihCTUkpDQp0YWlsKEJNSSkNCmBgYA0KDQojIyMgKipTb3J0aW5nIHRoZSBkYXRhKioNCioqKg0KDQpOb3csIGxldCdzIHNvcnQgdGhlIGRhdGEgYWxwaGFiZXRpY2FsbHkgYnkgYENvdW50cnlgIHVzaW5nIHRoZSBgYXJyYW5nZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2U6DQoNCmBgYHtyfQ0KQk1JIDwtIGRwbHlyOjphcnJhbmdlKEJNSSwgQ291bnRyeSkNCmhlYWQoQk1JKQ0KYGBgDQoNCk91ciBkYXRhIGlzIGxvb2tpbmcgZ3JlYXQhIA0KDQpOb3csIHdlIG1pZ2h0IHdhbnQgdG8gbWFrZSBzdXJlIHRoYXQgb3VyIG9ic2VydmF0aW9ucyBmb3IgZWFjaCB2YXJpYWJsZSBsb29rIHRoZSB3YXkgd2Ugd2FudC4gDQpJbiBvdGhlciB3b3JkcywgaWYgd2Ugd2FudCB0byBtYWtlIHBsb3RzIGFib3V0IE5hdGlvbmFsIEJNSSBpbiAxOTg1IHRoZW4gd2Ugd291bGQgbmVlZCBvdXIgdmFsdWVzIHRvIGJlIG51bWVyaWMuIA0KTG9va2luZyBhdCBvdXIgQk1JIGRhdGEgdXNpbmcgYHN0cigpYCwgd2UgY2FuIHNlZSB0aGUgdHlwZSBvZiBkYXRhIGZvciBlYWNoIG9mIG91ciB2YXJpYWJsZXMgbGlzdGVkIGp1c3QgYWZ0ZXIgdmFyaWFibGUgbmFtZSBhbmQgdGhlIGAiOiJgIGNvbG9uLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGV2YWx9DQpzdHIoQk1JKQ0KYGBgDQoNCkxvb2tzIGxpa2Ugbm9uZSBvZiBvdXIgQk1JIGRhdGEgaXMgYWN0dWFsbHkgYG51bWVyaWNgIChvciBgbnVtYCksIGJ1dCBvZiB0aGUgY2xhc3MgYGNoYXJhY3RlcmAgKG9yIGBjaHJgKS4gDQpMZXQncyBjaGFuZ2UgdGhhdCBub3cuDQoNClRvIHN0YXJ0LCB3ZSBjb3VsZCBjaGFuZ2UgdGhlc2UgdmFsdWVzIHRvIGJlIG51bWVyaWMgd2l0aCA2IGxpbmVzIG9mIGNvZGUgbGlrZSB0aGlzLCB3aGVyZSB0aGUgZG91YmxlIGJyYWNrZXRzYFtbXV1gIGFuZCBxdW90ZXMgYXJlIHVzZWQgdG8gc2VsZWN0IHRoZSBzcGVjaWZpYyB2YXJpYWJsZSB3aXRoaW4gdGhlIGBCTUlgIHRpYmJsZS4gSW4gdGhpcyBjYXNlIHdlIG5lZWQgdG8gdXNlIHRoZSBicmFja2V0cyBpbnN0ZWFkIG9mIGBwdWxsKClgIGJlY2F1c2Ugd2UgY2FuJ3Qgc3RhcnQgYSBjb21tYW5kIHdpdGggYHB1bGwoKWAgdG8gcmVhc3NpZ24gdmFsdWVzIGluIGEgdmFyaWFibGUuDQoNCmBgYHtyLCBldmFsID0gVFJVRX0NCiNwdWxsKEJNSSwgTmF0aW9uYWxfQk1JXzE5ODUpJTw+JSBhcy5udW1lcmljICNUaGlzIHdpbGwgbm90IHdvcmsNCkJNSVtbIk5hdGlvbmFsX0JNSV8xOTg1Il1dICU8PiUgYXMubnVtZXJpYw0KQk1JW1siUnVyYWxfQk1JXzE5ODUiXV0gJTw+JSBhcy5udW1lcmljDQpCTUlbWyJVcmJhbl9CTUlfMTk4NSJdXSAlPD4lIGFzLm51bWVyaWMNCg0KQk1JW1siTmF0aW9uYWxfQk1JXzIwMTciXV0gJTw+JSBhcy5udW1lcmljDQpCTUlbWyJSdXJhbF9CTUlfMjAxNyJdXSAlPD4lIGFzLm51bWVyaWMNCkJNSVtbIlVyYmFuX0JNSV8yMDE3Il1dICU8PiUgYXMubnVtZXJpYw0KYGBgDQoNCkFuZCBpZiB3ZSBkaWQgdGhpcyB3ZSB3b3VsZCBzZWUgdGhlc2UgdmFyaWFibGVzIHNob3cgYXMgYCJudW0iYCB3aGljaCBzdGFuZHMgZm9yIGEgbnVtZXJpYyB0eXBlIG9mIHZhcmlhYmxlLg0KYGBge3J9DQpzdHIoQk1JKQ0KYGBgDQoNCk9yIHdlIGNhbiB1c2UgYSBtb3JlIGF1dG9tYXRlZCB3YXkgd2l0aCB0aGUgYG1hcCgpYCBmdW5jdGlvbiBvZiB0aGUgYHB1cnJyYCBwYWNrYWdlOg0KYGBge3J9DQpCTUlfbnVtZXJpYyA8LSANCiAgcHVycnI6Om1hcCgoQk1JICU+JSANCiAgICAgICAgICAgICAgICBzZWxlY3QoLW1hdGNoZXMoIkNJfFNleHxDb3VudHJ5IikpKSwNCiAgICAgICAgICAgICBhcy5udW1lcmljKSAlPiUgDQogIGFzX3RpYmJsZSgpDQpgYGANCg0KVGhlIGBtYXAoKWAgZnVuY3Rpb24gb2YgdGhlIGBwdXJycmAgcGFja2FnZSBhbGxvd3MgdXMgdG8gYXBwbHkgdGhlIGBhcy5udW1lcmljKClgIGZ1bmN0aW9uIHRvIGFsbCB0aGUgc2VsZWN0ZWQgY29sdW1ucyBvZiBgQk1JYCBkYXRhc2V0Lg0KQWJvdmUsIHdlIGhhdmUgc2VsZWN0ZWQgdGhvc2UgdGhhdCBkb24ndCBjb250YWluIGBDSWAsIGBTZXhgLCBvciBgQ291bnRyeWAgaW4gdGhlIGNvbHVtbiBuYW1lLiANCklmIHlvdSBhcmUgZmFtaWxpYXIgd2l0aCBgYXBwbHkoKWAsIHRoaXMgZnVuY3Rpb24gaXMgdmVyeSBzaW1pbGFyLg0KDQpOb3cgd2UgbmVlZCB0byBhZGQgdGhlIGBDb3VudHJ5YCBhbmQgYFNleGAgdmFyaWFibGVzIGJhY2sgdG8gb3VyIG5ldyBudW1lcmljIGRhdGFzZXQuIFRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlLiBUaGlzIGZ1bmN0aW9uIGFsbG93cyB1cyB0byBjcmVhdGUgYW5kIG1vZGlmeSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KQk1JX251bWVyaWMgJTw+JSANCiAgbXV0YXRlKENvdW50cnkgPSBwdWxsKEJNSSwgQ291bnRyeSksIA0KICAgICAgICAgU2V4ID0gcHVsbChCTUksIFNleCkpDQpoZWFkKEJNSV9udW1lcmljKQ0KYGBgDQoNCkluIHRoaXMgY2FzZSBvdXIgbnVtZXJpYyBkYXRhIGlzIG9mIGEgY2xhc3MgYGRibGAuIA0KVGhpcyBtZWFucyBkb3VibGUtcHJlY2lzaW9uIGZsb2F0aW5nIHBvaW50IG51bWJlcnMgKHdpdGggZGVjaW1hbHMpLg0KVGhlIGFsdGVybmF0aXZlIG51bWVyaWMgb3B0aW9uIGlzIHRoZSBpbnRlZ2VyIGNsYXNzLiANCg0KIyMjICoqVHJhbnNmb3JtaW5nIHRvIHRpZHkgZGF0YSoqDQoqKioNCg0KSXQgaXMgZ2VuZXJhbGx5IHVzZWZ1bCB0byBnZXQgdGhlIGRhdGEgaW4gd2hhdCBpcyBjYWxsZWQgPGI+bG9uZzwvYj4gZm9ybWF0IGZvciBvdGhlciBhbmFseXNlcywgYW5kIHBhcnRpY3VsYXJseSBmb3IgcGxvdHRpbmcuIA0KDQpGb3IgYSBtb3JlIGRldGFpbGVkIGRlc2NyaXB0aW9uIGFib3V0IHRoaXMsIHBsZWFzZSBzZWUgdGhlIERhdGEgVmlzdWFsaXphdGlvbiBzZWN0aW9uIG9mIHRoaXMgW2Nhc2Ugc3R1ZHldKGh0dHBzOi8vd3d3Lm9wZW5jYXNlc3R1ZGllcy5vcmcvb2NzLWJwLW9waW9pZC1ydXJhbC11cmJhbi8jUnVyYWxfYW5kX1VyYmFuX2FyZWFzLyM6fjp0ZXh0PSUyMHRoZSUyMGRhdGElMjB3YXMlMjBwcmVzZW50ZWQlMjBpbiUyMGElMjBmb3JtYXQlMjB0aGF0JTIwaXMlMjBjYWxsZWQlMjBsb25nJTIwZm9ybWF0Lil7dGFyZ2V0PSJfYmxhbmsifS4NCg0KQmFzaWNhbGx5LCB0aGUgbG9uZyBmb3JtYXQgaGFzIG1vcmUgcm93cyBhbmQgZmV3ZXIgY29sdW1ucy4NClRodXMsIHdlIGNhbiBjb21iaW5lIHNvbWUgY29sdW1ucyB0b2dldGhlci4NCkluIG91ciBjYXNlLCB3ZSBjYW4gcHV0IGFsbCB0aGUgZGlmZmVyZW50IEJNSSBkYXRhIHRvZ2V0aGVyIGluIG9uZSBsb25nIGNvbHVtbi4NCldlIHdpbGwgY3JlYXRlIGEgbmV3IGNvbHVtbiB0aGF0IHRlbGxzIHVzIHdoYXQgZWFjaCByb3cgb2YgdGhlIG5ldyBCTUkgY29sdW1uIHJlcHJlc2VudHMuDQpJbiBvdGhlciB3b3JkcywgaXQgd2lsbCB0ZWxsIHVzIHdoYXQgdGhlIG9yaWdpbmFsIGNvbHVtbiB3YXMuDQoNClRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIGBwaXZvdF9sb25nZXIoKWAgZnVuY3Rpb24gb2YgdGhlIGB0aWR5cmAgcGFja2FnZS4gVGhlIGBkYXRhYCBhcmd1bWVudCBzcGVjaWZpZXMgd2hhdCBkYXRhIHdlIHdhbnQgdG8gd29yayB3aXRoLCB0aGUgYGNvbHNgIGFyZ3VtZW50IHNwZWNpZmllcyB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgb3IgdmFyaWFibGVzIHRoYXQgd2Ugd2FudCB0byBjb21iaW5lIGFuZCBjaGFuZ2UgdG8gb25lIGxvbmcgZm9ybWF0IHZhcmlhYmxlLCBhbmQgdGhlIGBuYW1lc190b2AgYXJndW1lbnQgc3BlY2lmaWVzIHRoZSBuYW1lIGZvciB0aGUgbmV3IHZhcmlhYmxlIHRoYXQgd2UgYXJlIGNyZWF0aW5nLiBUaGUgYDpgIGlzIHVzZWQgdG8gaW5kaWNhdGUgdGhhdCB3ZSB3YW50IHRvIGNvbWJpbmUgdGhlIGNvbHVtbnMgb3IgdmFyaWFibGVzIGJldHdlZW4gYE5hdGlvbmFsX0JNSV8xOTg1YCBhbmQgYFVyYmFuX0JNSV8yMDE3YC4NCg0KYGBge3J9DQpCTUlfbG9uZyA8LQ0KICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGRhdGEgPSBCTUlfbnVtZXJpYywgDQogICAgICAgICAgICAgICAgICAgICAgY29scyA9IE5hdGlvbmFsX0JNSV8xOTg1OlVyYmFuX0JNSV8yMDE3LCANCiAgICAgICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJjbGFzc19CTUkiKQ0KDQpoZWFkKEJNSV9sb25nKQ0KYGBgDQoNCldlIHJlYWxseSB3YW50IHRvIHByb21vdGUgdGhlIGlkZWEgb2Ygd29ya2luZyB3aXRoIFt0aWR5IGRhdGFdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdGlkeS1kYXRhLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uIFRpZHkgZGF0YSBpcyBkYXRhIHRoYXQgaXMgc3RydWN0dXJlZCBpbiBhIHdheSB0aGF0IG1ha2VzIGl0IGVzcGVjaWFsbHkgdXNhYmxlLiBPbmUgb2YgdGhlIHJ1bGVzIG9mIHRpZHkgZGF0YSwgaXMgdGhhdCBlYWNoIGNvbHVtbiBvciB2YXJpYWJsZSBzaG91bGQgaGF2ZSBvbmx5IG9uZSB0eXBlIG9mIGRhdGEuIEN1cnJlbnRseSB3ZSBoYXZlIHR3byB0eXBlcyBvZiBkYXRhIGluIHRoZSBgY2xhc3NfQk1JYCB2YXJpYWJsZTogdGhlIHR5cGUgb2YgQk1JIG1lYXN1cmVtZW50ICggUnVyYWwsIFVyYmFuLCBvciBOYXRpb25hbCksIGFuZCB0aGUgeWVhciAoMTk4NSBhbmQgMjAxNykuICBXZSBjYW4gc2VwYXJhdGUgdGhlc2UgdHdvIGRhdGEgdHlwZXMgb2YgdGhlIGBjbGFzc19CTUlgIGNvbHVtbiB1c2luZyB0aGUgYHNlcGFyYXRlKClgIGZ1bmN0aW9uIG9mIHRoZSBgdGlkeXJgIHBhY2thZ2UgdG8gcmVwbGFjZSB0aGUgYGNsYXNzX0JNSWAgY29sdW1uIHdpdGggdHdvIG5ldyBjb2x1bW5zLg0KDQpgYGB7cn0NCkJNSV9sb25nICU8PiUgDQogIHRpZHlyOjpzZXBhcmF0ZShjbGFzc19CTUksIA0KICAgICAgICAgICAgICAgICAgYygiUmVnaW9uIiwgTkEsICJZZWFyIikpIA0KYGBgDQoNCkhlcmUsIHdlIHNlcGFyYXRlIHRoZSBgY2xhc3NfQk1JYCBjb2x1bW4gb2YgdGhlIGBCTUlfbG9uZ2AgdGliYmxlLiANClRoaXMgaXMgYmFzZWQgb24gdGhlIHVuZGVyc2NvcmVzICjigJxf4oCdKSBhbmQgY3JlYXRlcyB0d28gbmV3IGNvbHVtbnMuIA0KVGhlIGZpcnN0IGNvbHVtbiB3aWxsIGJlIGNhbGxlZCBgUmVnaW9uYC4gDQpJdCB3aWxsIGNvbnRhaW4gdGhlIDFzdCBwYXJ0IG9mIHRoZSBgY2xhc3NfQk1JYCBkYXRhIGJlZm9yZSB0aGUgMXN0IHVuZGVyc2NvcmUuIA0KVGhlIDJuZCBjb2x1bW4gd2lsbCBiZSBjYWxsZWQgYFllYXJgLg0KSXQgd2lsbCBjb250YWluIHRoZSAzcmQgcGFydCBvZiB0aGUgZGF0YSBiYXNlZCBvbiB1bmRlcnNjb3Jlcy4NClRoZSBgTkFgIGluIG91ciBjb2RlIGlzIGZvciByZW1vdmluZyB0aGUgbWlkZGxlIHBhcnQgb2YgdGhlIGNvbHVtbiAo4oCcQk1J4oCdKS4gVGh1cywgdGhpcyBwYXJ0IHdpbGwgbm90IGJlIHVzZWQgdG8gY3JlYXRlIGFueSBuZXcgY29sdW1ucy4NCg0KYGBge3J9DQpoZWFkKEJNSV9sb25nKQ0KYGBgDQoNCkxldCdzIHNlZSBob3cgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGBCTUlgIGRhdGEgaGF2ZSBjaGFuZ2VkIGluIGxvbmcgZm9ybWF0Og0KYGBge3J9DQpkaW0oQk1JX251bWVyaWMpDQpkaW0oQk1JX2xvbmcpDQpgYGANCkxldCdzIHJlbmFtZSB0aGUgYHZhbHVlYCBjb2x1bW4gdG8gc29tZXRoaW5nIG1vcmUgaW5mb3JtYXRpdmUuIFdlIGNhbiB1c2UgdGhlIGByZW5hbWUoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byBkbyB0aGlzLiBOb3RpY2UgdGhhdCB0aGUgdmFsdWUgd2Ugd2FudCB0byByZXBsYWNlIGlzIGxpc3RlZCBzZWNvbmQgYWZ0ZXIgdGhlIGA9YCBzaWduLg0KDQpgYGB7cn0NCg0KQk1JX2xvbmcgJTw+JSANCiAgcmVuYW1lKEJNSSA9IHZhbHVlKQ0KaGVhZChCTUlfbG9uZykNCg0KYGBgDQojIyMjIHsucXVlc3Rpb25fYmxvY2t9DQo8dT5RdWVzdGlvbiBvcHBvcnR1bml0eTo8L3U+IFdoeSBleGFjdGx5IGFyZSB0aGVyZSAyLDQwMCByb3dzIG5vdz8NCg0KIyMjIw0KDQpHcmVhdCEgT3VyIGRhdGEgaXMgdmVyeSB1c2FibGUgbm93IGluIHRoaXMgZm9ybWF0ISBOb3cgbGV0J3Mgc2F2ZSBvdXIgd3JhbmdsZWQgZGF0YSBmb3IgZnV0dXJlIHVzZS4gV2Ugd2lsbCBzYXZlIGl0IGFzIGFuIHIgY29tcGF0aWJsZSBmaWxlIChhIC5yZGEgZmlsZSksIGFzIHdlbGwgYXMgY3N2IGZpbGVzLCBhcyB0aGVzZSBhcmUgdXNlZnVsIHRvIGdpdmUgdG8gY29sbGFib3JhdG9ycy4gV2Ugd2lsbCB1c2UgdGhlIGB3cml0ZV9jc3YoKWAgZnVuY3Rpb24gb2YgdGhlIGByZWFkcmAgcGFja2FnZSB0byBkbyB0aGlzLlRoaXMgcmVxdWlyZXMgdGhhdCBlYWNoIHRpYmJsZSBiZSBzYXZlZCBvbmUgYXQgYSB0aW1lLg0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0Kc2F2ZShCTUlfbnVtZXJpYywgDQogICAgIEJNSV9sb25nLCANCiAgICAgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGEucmRhIikpDQpyZWFkcjo6d3JpdGVfY3N2KEJNSV9udW1lcmljLCANCiAgICAgICAgICAgICAgICAgcGF0aCA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAiQk1JX251bWVyaWMuY3N2IikpDQpyZWFkcjo6d3JpdGVfY3N2KEJNSV9sb25nLCANCiAgICAgICAgICAgICAgICAgcGF0aCA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAiQk1JX2xvbmcuY3N2IikpDQoNCmBgYA0KDQoNCiMjICoqRGF0YSBFeHBsb3JhdGlvbioqDQoqKioNCg0KSWYgeW91IGhhdmUgYmVlbiBmb2xsb3dpbmcgYWxvbmcgYnV0IHN0b3BwZWQsIHdlIGNvdWxkIGxvYWQgb3VyIHdyYW5nbGVkIGRhdGEgbGlrZSBzbzoNCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgd3JhbmdsaW5nIHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5Pg0KDQpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgT0NTZGF0YWAgcGFja2FnZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikNCmxpYnJhcnkoT0NTZGF0YSkNCmBgYA0KDQpUaGVuLCB5b3UgbWF5IGxvYWQgdGhlIHdyYW5nbGVkIGRhdGEgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCndyYW5nbGVkX3JkYSgib2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5Iiwgb3V0cGF0aCA9IGdldHdkKCkpDQpsb2FkKGhlcmU6OmhlcmUoIk9DU2RhdGEiLCAiZGF0YSIsICJ3cmFuZ2xlZCIsICJ3cmFuZ2xlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYWx0ZXJuYXRpdmVseSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS8vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS90cmVlL21hc3Rlci9kYXRhL3dyYW5nbGVkKSBvciBzbGlnaHRseSBtb3JlIGRpcmVjdGx5IFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eS9tYXN0ZXIvZGF0YS93cmFuZ2xlZC93cmFuZ2xlZF9kYXRhLnJkYSkuIERvd25sb2FkIHRoaXMgZmlsZSBhbmQgdGhlbiBwbGFjZSBpdCBpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAid3JhbmdsZWQiIHdpdGhpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgImRhdGEiIHRvIGNvcHkgYW5kIHBhc3RlIG91ciBjb2RlLiBXZSB1c2VkIGFuIFJTdHVkaW8gcHJvamVjdCBhbmQgdGhlIFtgaGVyZWAgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBuYXZpZ2F0ZSB0byB0aGUgZmlsZSBtb3JlIGVhc2lseS4NCg0KYGBge3J9DQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+DQoNCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoNCg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQ0KYGBgDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4NCg0KPC9kZXRhaWxzPg0KKioqDQo8L2RldGFpbHM+DQoqKioNCg0KTm93IHRoYXQgb3VyIGRhdGEgaXMgY2xlYW4gYW5kIGluIGEgZm9ybWF0IHRoYXQgd2UgY2FuIHdvcmsgd2l0aCwgd2UgY2FuIHN0YXJ0IHRvIHRha2UgYSBsb29rIGF0IHRoZSBkYXRhIGFuZCBob3cgZGlmZmVyZW50IGdyb3VwcyBtaWdodCBjb21wYXJlLg0KDQpHb2luZyBiYWNrIHRvIG9uZSBvZiBvdXIgb3JpZ2luYWwgcXVlc3Rpb25zLCANCg0KPiBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgYmV0d2VlbiBydXJhbCBhbmQgdXJiYW4gQk1JIGVzdGltYXRlcyBhcm91bmQgdGhlIHdvcmxkPw0KDQpXZSB3YW50IHRvIHVzZSBhIHN0YXRpc3RpY2FsIHRlc3QgdG8gaWRlbnRpZnkgaWYgdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIHRoZSBhdmVyYWdlIEJNSSBlc3RpbWF0ZXMgYmV0d2VlbiBlLmcuIHJ1cmFsIGFuZCB1cmJhbiBncm91cHMuIA0KRmlyc3QsIGxldCdzIGV4cGxvcmUgb3VyIGRhdGEgYSBiaXQuIA0KDQojIyMgKipHZW5lcmFsIHN1bW1hcnkqKg0KKioqDQoNCg0KYGBge3J9DQpzdW1tYXJ5KEJNSV9udW1lcmljKQ0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgdGhlIG1lYW4gQk1JIGVzdGltYXRlcyBhcmUgbGFyZ2VyIGluIDIwMTcuDQpIb3dldmVyLCBpcyB0aGlzIGEgbWVhbmluZ2Z1bCBpbmNyZWFzZT8NClRoaXMgaXMgZGlmZmljdWx0IHRvIGRldGVybWluZSB3aXRob3V0IGEgc3RhdGlzdGljYWwgdGVzdC4NCg0KTGV0cyBsb29rIGF0IHRoZSBkYXRhIHN0cmF0aWZpZWQgYnkgYFNleGA6DQoNCmBgYHtyfQ0KQk1JX251bWVyaWMgJT4lIA0KICBmaWx0ZXIoU2V4ID09ICJXb21lbiIpICU+JSANCiAgc3VtbWFyeSgpDQpgYGANCg0KYGBge3J9DQpCTUlfbnVtZXJpYyAlPiUgDQogIGZpbHRlcihTZXggPT0gIk1lbiIpICU+JSANCiAgc3VtbWFyeSgpDQpgYGANCg0KSXQgbG9va3MgbGlrZSBtZWFuIEJNSXMgaGF2ZSBpbmNyZWFzZWQgaW4gYWxsIHJlZ2lvbnMgZm9yIGJvdGggbWVuIGFuZCB3b21lbiwgYnV0IGxldCdzIGxvb2sgZGVlcGVyIHdpdGggc29tZSBzdGF0aXN0aWNhbCB0ZXN0cy4NCg0KIyMjICoqRGlzdHJpYnV0aW9uKioNCioqKg0KDQpJbiBvcmRlciB0byBhcHBseSBhIHN0YXRpc3RpY2FsIHRlc3QgdG8gY29tcGFyZSB0aGUgbWVhbnMsIG9uZSBvZiB0aGUgZmlyc3QgdGhpbmdzIHRvIGRvIGlzIHRvIGV4cGxvcmUgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgZGlmZmVyZW50IG9ic2VydmVkIHZhbHVlcy4gDQpPbmUgd2F5IHRvIHN1bW1hcml6ZSB0aGUgZnJlcXVlbmN5IG9mIGRpZmZlcmVudCBvYnNlcnZlZCB2YWx1ZXMgaXMgdGhlIDxiPmZyZXF1ZW5jeSBkaXN0cmlidXRpb248L2I+LCB3aGljaCBjYW4gYmUgc2hvd24gaW4gYSB0YWJsZSBvciBhIHBsb3QuIA0KU2VlIFtoZXJlXShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvaW50cm9kdWN0aW9uL2Rpc3RyaWJ1dGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBkaXN0cmlidXRpb25zLiANCg0KVGhlcmUgYXJlIG90aGVyIHR5cGVzIG9mIGRpc3RyaWJ1dGlvbnMgdG9vLCBzdWNoIGFzIDxiPnByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnM8L2I+LCB3aGljaCBkZXNjcmliZSB0aGUgcHJvYmFiaWxpdHkgdGhlIG9jY3VycmVuY2Ugb2YgZGlmZmVyZW50IHBvc3NpYmxlIG91dGNvbWVzLiANClByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMgY2FuIGJlIGRlZmluZWQgZm9yIGRpc2NyZXRlIGRhdGEgKGUuZy4gZmxpcHBpbmcgYSBjb2luIGFzIGhlYWRzIG9yIHRhaWxzKSBhbmQgY29udGludW91cyBkYXRhIChlLmcuIEJNSSBlc3RpbWF0ZXMpLiANCklmIHdlIGFyZSB0YWxraW5nIGFib3V0IGRpc2NyZXRlIGRhdGEsIHRoZSByZWxhdGl2ZSBmcmVxdWVuY3kgb2YgdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzIChoZWFkcyBvciB0YWlscykgY2FuIGJlIHVzZWQgdG8gZGVmaW5lIGEgPGI+ZGlzY3JldGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uPC9iPi4gDQpXZSBjYW4gc2ltcGx5IGFzc2lnbiBhIHByb2JhYmlsaXR5IHRvIGVhY2ggY2F0ZWdvcnksIGUuZy4gJFAoaGVhZHMpID0gMC41JCBhbmQgJFAodGFpbHMpID0gMC41JC4gDQoNCjxiPkNvbnRpbnVvdXMgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uczwvYj4gd29yayBpbiBhIHNpbWlsYXIgd2F5LCBleGNlcHQgaW5zdGVhZCBvZiBhc3NpZ25pbmcgYSBwcm9iYWJpbGl0eSB0byBlYWNoIGNhdGVnb3J5IG9yIHR5cGUgb2Ygb2JzZXJ2YXRpb24gKGUuZy4gaGVhZHMgb3IgdGFpbHMpLCB3ZSBhc3NpZ24gYSBwcm9iYWJpbGl0eSB0byBhbiBpbnRlcnZhbCBvZiBjb250aW51b3VzIHZhbHVlcyAoZS5nLiBCTUkgYmV0d2VlbiAyMiBhbmQgMjYpLiANClRoaXMgaXMgYmVjYXVzZSBpdCdzIG5vdCB1c2VmdWwgdG8gY29uc3RydWN0IGEgZGlzdHJpYnV0aW9uIHRoYXQgYXNzaWducyBhIHByb2JhYmlsaXR5IHRvIGVhY2ggcG9zc2libGUgb3V0Y29tZSAoZS5nLiBhIEJNSSBvZiAyMi4xMSwgMjIuMTIsIGV0Yykgd2l0aCBzdWNoIGhpZ2ggcHJlY2lzaW9uLiBJbiB0aGlzIGNhc2UsIHdlIHdvdWxkIGhhdmUgdG8gYXNzaWduIGEgdmVyeSBzbWFsbCBwcm9iYWJpbGl0eSB0byBldmVyeSBwb3NzaWJsZSBCTUkgb3V0Y29tZS4gDQoNClRoZSA8Yj5ub3JtYWwgKG9yIEdhdXNzaWFuKSBkaXN0cmlidXRpb248L2I+IGlzIGEgdHlwZSBvZiA8Yj50aGVvcmV0aWNhbCBjb250aW51b3VzIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbjwvYj4gdGhhdCBpcyBmcmVxdWVudGx5IHVzZWQgdG8gYXBwcm94aW1hdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYW55IG5hdHVyYWxseSBvY2N1cnJpbmcgbWVhc3VyZW1lbnRzLCBzdWNoIGFzIHRoYXQgb2YgQk1JLiANCldlIHNheSB0aGF0IGEgcmFuZG9tIHF1YW50aXR5ICRYJCAoZS5nLiBCTUkpIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggbWVhbiAkbSQgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAkcyQgaWYgdGhlIHByb3BvcnRpb24gb2YgdmFsdWVzIGluIGEgZ2l2ZW4gaW50ZXJ2YWwgJChhLGIpJCAoZS5nLiBCTUkgYmV0d2VlbiAyMiBhbmQgMjYpIGNhbiBiZSBjb21wdXRlZCB1c2luZyB0aGlzIG1hdGhlbWF0aWNhbCBmb3JtdWxhOiANCg0KJCRQKGEgPCB4IDwgYikgPSBcaW50X2FeYiBcZnJhY3sxfXtcc3FydHsyXHBpfSBzfSBlXnstXGZyYWN7MX17Mn1cYmlnKFxmcmFje3gtbX17c31cYmlnKV4yfSAgJCQNCg0KVHJhbnNsYXRpbmcgdGhpcyB0byBvdXIgY2FzZSBzdHVkeSwgaWYgb3VyIEJNSSBkYXRhIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIG9mICRtID0gMjQkIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgJHMgPSAxJCwgdGhlbiB0aGUgZGlzdHJpYnV0aW9uIG9mIGRpZmZlcmVudCBCTUkgb3V0Y29tZXMgc2hvdWxkIGJlIGNlbnRlcmVkIGFyb3VuZCB0aGF0IG1lYW4gdmFsdWUgb2YgMjQgYW5kIHdvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoNCg0KYGBge3J9DQpub3JtX0JNSV9leF9kYXRhIDwtIA0KICB0aWJibGUobm9ybV9kYXRhID0gcm5vcm0obiA9IDIwMCwgbWVhbiA9IDI0LCBzZCA9IDEpKQ0KaGlzdChwdWxsKG5vcm1fQk1JX2V4X2RhdGEsIG5vcm1fZGF0YSkpDQpgYGANCg0KSGVyZSB3ZSBhcmUgc2ltdWxhdGluZyAyMDAgb2JzZXJ2YXRpb25zIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggYSBtZWFuIG9mICRtID0gMjQkIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgJHMgPSAxJCB1c2luZyB0aGUgYHJub3JtKClgIGZ1bmN0aW9uLiANCg0KQWx0ZXJuYXRpdmVseSB0byB1c2luZyBiYXNlIFIsIHdlIGNhbiBwbG90IHRoZSBmcmVxdWVuY3kgb2Ygc2ltdWxhdGVkIG9ic2VydmF0aW9ucyB1c2luZyB0aGUgYGdlb21faGlzdCgpYCBmdW5jdGlvbiBvZiB0aGUgYGdncGxvdDJgIFIgcGFja2FnZS4gDQpUaGUgW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSBwYWNrYWdlIGNyZWF0ZXMgcGxvdHMgYnkgdXNpbmcgbGF5ZXJzLg0KTm90aWNlIGluIHRoZSBmb2xsb3dpbmcgY29kZSBob3cgdGhlcmUgaXMgYSBwbHVzIHNpZ24gYmV0d2VlbiB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbiBhbmQgdGhlIGBnZW9tX2hpc3RvZ3JhbSgpYCBmdW5jdGlvbi4gDQpXaXRoIGBnZ3Bsb3QyYCB3ZSBzZWxlY3Qgd2hhdCBkYXRhIHdlIHdvdWxkIGxpa2UgdG8gcGxvdCB1c2luZyB0aGUgZmlyc3QgZnVuY3Rpb24gKGBnZ3Bsb3QoKWApIGFuZCB0aGVuIHdlIGFkZCBvbiBhZGRpdGlvbmFsIGxheWVycyBvZiBjb21wbGV4aXR5ICh0aGVzZSBsYXllcnMgY2FuIGV2ZW4gaW52b2x2ZSBkaWZmZXJlbnQgZGF0YSkuIA0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiB1c2luZyBgZ2dwbG90MmAuPC9zdW1tYXJ5Pg0KDQpUaGUgcmVhc29ucyBbYGdncGxvdDJgXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnKSBpcyBnZW5lcmFsbHkgaW50dWl0aXZlIGZvciBiZWdpbm5lcnMgaXMgdGhlIHVzZSBvZiBbZ3JhbW1hciBvZiBncmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIuaHRtbCkgb3IgdGhlIGBnZ2AgaW4gYGdncGxvdDJgLiANClRoZSBpZGVhIGlzIHRoYXQgeW91IGNhbiBjb25zdHJ1Y3QgbWFueSBzZW50ZW5jZXMgYnkgbGVhcm5pbmcganVzdCBhIGZldyBub3VucywgYWRqZWN0aXZlcywgYW5kIHZlcmJzLiANClRoZXJlIGFyZSBzcGVjaWZpYyAid29yZHMiIHRoYXQgd2Ugd2lsbCBpbnRyb2R1Y2UsIGFuZCBvbmNlIHlvdSBhcmUgY29tZm9ydGFibGUgd2l0aCB0aGVtLCB5b3Ugd2lsbCBiZSBhYmxlIHRvIGNyZWF0ZSAob3IgIndyaXRlIikgaHVuZHJlZHMgb2YgZGlmZmVyZW50IHBsb3RzLg0KDQoqKl9XaGF0IGlzIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uP18qKg0KDQpBcyBleHBsYWluZWQgYnkgSGFkbGV5IFdpY2toYW06IA0KDQo+IHRoZSBncmFtbWFyIHRlbGxzIHVzIHRoYXQgYSBzdGF0aXN0aWNhbCBncmFwaGljIGlzIGEgbWFwcGluZyBmcm9tIGRhdGEgdG8gYWVzdGhldGljIGF0dHJpYnV0ZXMgKGNvbG91ciwgc2hhcGUsIHNpemUpIG9mIGdlb21ldHJpYyBvYmplY3RzIChwb2ludHMsIGxpbmVzLCBiYXJzKS4gVGhlIHBsb3QgbWF5IGFsc28gY29udGFpbiBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGRhdGEgYW5kIGlzIGRyYXduIG9uIGEgc3BlY2lmaWMgY29vcmRpbmF0ZXMgc3lzdGVtLg0KDQoqKl9gZ2dwbG90MmAgVGVybWlub2xvZ3lfKioNCg0KKiAqKmdncGxvdCoqIC0gdGhlIG1haW4gZnVuY3Rpb24gd2hlcmUgeW91IHNwZWNpZnkgdGhlIGRhdGFzZXQgYW5kIHZhcmlhYmxlcyB0byBwbG90ICh0aGlzIGlzIHdoZXJlIHdlIGRlZmluZSB0aGUgYHhgIGFuZA0KYHlgIHZhcmlhYmxlIG5hbWVzKQ0KKiAqKmdlb21zKiogLSBnZW9tZXRyaWMgb2JqZWN0cw0KICAgICogZS5nLiBgZ2VvbV9wb2ludCgpYCwgYGdlb21fYmFyKClgLCBgZ2VvbV9saW5lKClgLCBgZ2VvbV9oaXN0b2dyYW0oKWANCiogKiphZXMqKiAtIGFlc3RoZXRpY3MNCiAgICAqIHNoYXBlLCB0cmFuc3BhcmVuY3ksIGNvbG9yLCBmaWxsLCBsaW5lIHR5cGVzDQoqICoqc2NhbGVzKiogLSBkZWZpbmUgaG93IHlvdXIgZGF0YSB3aWxsIGJlIHBsb3R0ZWQNCiAgICAqIGNvbnRpbnVvdXMsIGRpc2NyZXRlLCBsb2csIGV0Yw0KDQpUaGUgZnVuY3Rpb24gYGFlcygpYCBpcyBhbiBhZXN0aGV0aWMgbWFwcGluZyBmdW5jdGlvbiBpbnNpZGUgdGhlIGBnZ3Bsb3QoKWAgb2JqZWN0LiANCldlIHVzZSB0aGlzIGZ1bmN0aW9uIHRvIHNwZWNpZnkgcGxvdCBhdHRyaWJ1dGVzIChlLmcuIGB4YCBhbmQgYHlgIHZhcmlhYmxlIG5hbWVzKSB0aGF0IHdpbGwgbm90IGNoYW5nZSBhcyB3ZSBhZGQgbW9yZSBsYXllcnMuICANCg0KQW55dGhpbmcgdGhhdCBnb2VzIGluIHRoZSBgZ2dwbG90KClgIG9iamVjdCBiZWNvbWVzIGEgZ2xvYmFsIHNldHRpbmcuIA0KRnJvbSB0aGVyZSwgd2UgdXNlIHRoZSBgZ2VvbWAgb2JqZWN0cyB0byBhZGQgbW9yZSBsYXllcnMgdG8gdGhlIGJhc2UgYGdncGxvdCgpYCBvYmplY3QuIA0KVGhlc2Ugd2lsbCBkZWZpbmUgd2hhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBpbGx1c3RyYXRpbmcgdXNpbmcgdGhlIGRhdGEuDQoNCjwvZGV0YWlscz4gDQoqKioNCg0KYGBge3J9DQogIGdncGxvdDI6OmdncGxvdChub3JtX0JNSV9leF9kYXRhLCBhZXMoeCA9IG5vcm1fZGF0YSkpICsgDQogIGdncGxvdDI6Omdlb21faGlzdG9ncmFtKCkgDQpgYGANCg0KSGVyZSwgd2UgYXJlIHVzaW5nIHRoZSBjb2x1bW4gY2FsbGVkIGBub3JtX2RhdGFgIHdpdGhpbiBgbm9ybV9CTUlfZXhfZGF0YWAuDQpBcyB3ZSBub3RlZCwgd2l0aCBgZ2dwbG90MmAgd2UgbXVzdCBmaXJzdCBzcGVjaWZ5IHRoZSBkYXRhIHRvIHBsb3Qgd2l0aCB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbi4gDQpUaGVuLCB0aGUgYGdlb21fKmAgZnVuY3Rpb24gc3BlY2lmaWVzIHdoYXQgdHlwZSBvZiBwbG90IHRvIGNyZWF0ZSAoZS5nLiBgZ2VvbV9oaXN0b2dyYW0oKWAgY3JlYXRlIGEgaGlzdG9ncmFtKS4gDQoNCldlIGNvdWxkIGFsc28gbWFrZSB0aGlzIHNhbWUgcGxvdCBieSBwaXBpbmcgdGhlIGBub3JtX0JNSV9leF9kYXRhYCBkYXRhIGludG8gdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb246DQpgYGB7cn0NCm5vcm1fQk1JX2V4X2RhdGEgJT4lIA0KICBnZ3Bsb3QyOjpnZ3Bsb3QoYWVzKHggPSBub3JtX2RhdGEpKSArIA0KICBnZ3Bsb3QyOjpnZW9tX2hpc3RvZ3JhbSgpIA0KYGBgDQoNCldlIHdpbGwgc2VlIGxhdGVyIGhvdyB3ZSBjYW4gYWRkIG1hbnkgbGF5ZXJzIHRvIHBsb3RzIHdpdGggYGdncGxvdDJgLg0KDQoNCk5vdyBpbnN0ZWFkIG9mIHVzaW5nIHNpbXVsYXRlZCBkYXRhIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBsZXQncyBtYWtlIGEgaGlzdG9ncmFtIG9mIG91ciBCTUkgZGF0YToNCmBgYHtyfQ0KQk1JX2xvbmcgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBCTUkpKSArIA0KICBnZW9tX2hpc3RvZ3JhbSgpIA0KYGBgDQoNCkludGVyZXN0aW5nISBUaGUgZGF0YSBsb29rcyBsaWtlIGl0IGhhcyBvbmUgbW9kZSwgYnV0IGl0IGRvZXMgbm90IGxvb2sgY29tcGxldGVseSBzeW1tZXRyaWMuIEluIGZhY3QgaXQgbG9va3MgbGlrZSBpdCBtaWdodCBiZSB3aGF0IGlzIGNhbGxlZCByaWdodCBza2V3ZWQuIFNlZSBbaGVyZV0oaHR0cDovL29ubGluZXN0YXRib29rLmNvbS8yL2dsb3NzYXJ5L3NrZXcuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBza2V3ZWQgZGlzdHJpYnV0aW9ucy4NCg0KVG8gY2hlY2sgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgbWVhbiBhbmQgbWVkaWFuIG9mIHRoZSBCTUkgZGF0YSB1c2luZyB0aGUgYmFzZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuIElmIHRoZSBtZWFuIGFuZCBtZWRpYW4gYXJlIHZlcnkgc2ltaWxhciwgdGhlbiB3ZSB3aWxsIGtub3cgdGhhdCB0aGUgZGF0YSBpc24ndCBza2V3ZWQuIE5vdGUgdGhhdCBpdCBpcyBuZWNlc3NhcnkgdG8gZmlsdGVyIG91dCB0aGUgTkEgdmFsdWVzIGZpcnN0Lg0KDQpgYGB7cn0NCkJNSV9sb25nICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEJNSSkpJT4lDQogIHN1bW1hcml6ZShtZWFuID0gbWVhbihCTUkpLCBtZWRpYW4gPSBtZWRpYW4oQk1JKSkNCmBgYA0KT0ssIHNvIHRoZXkgYXJlIGVzc2VudGlhbGx5IHRoZSBzYW1lLCBtZWFuPSAyNC44NyBhbmQgbWVkaWFuID0gMjQuOSwgc28gdGhpcyBkb2VzIG5vdCBmaXQgdGhlIGhhbGxtYXJrIG9mIHJpZ2h0LXNrZXdlZCBkYXRhIHdoZXJlIHRoZSB0aGUgbWVhbiBpcyBncmVhdGVyIHRoYW4gdGhlIG1lZGlhbi4gDQoNCg0KSG93ZXZlciBzbyBmYXIgd2UgaGF2ZSBtYWRlIGEgaGlzdG9ncmFtIG9mIHRoZSBCTUkgZGF0YSBpbiB0aGUgbG9uZyBmb3JtYXQgd2hpY2ggY29tYmluZXMgQk1JIGVzdGltYXRlcyBmb3IgZGlmZmVyZW50IHN1Ymdyb3VwcyBvZiBwZW9wbGUsIHNvIHJ1cmFsIHdvbWVuLCBydXJhbCBtZW4sIHVyYmFuIHdvbWVuLCB1cmJhbiBtZW4sIGV0Yy4gYXJlIGFsbCBjb21iaW5lZC4gSWYgd2Ugd2FudCB0byBtYWtlIGdyb3VwIGNvbXBhcmlzb25zLCB3ZSBuZWVkIHRvIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSBzcGVjaWZpYyBncm91cHMgdGhhdCB3ZSBhcmUgZXZhbHVhdGluZy4gU28sIHdlIG5lZWQgdG8gcGljayBhIHBhcnRpY3VsYXIgcG9wdWxhdGlvbiBvZiBpbmRpdmlkdWFscyBhbmQgZ3JhcGggdGhlaXIgQk1JLCBmb3IgZXhhbXBsZSBqdXN0IHRoZSBuYXRpb25hbCBlc3RpbWF0ZXMgZm9yIHdvbWVuIGFuZCBmb3IgbWVuIGluIDE5ODUuICANCg0KVGhlIGVhc2llc3Qgd2F5IHRvIGRvIHRoaXMgaXMgdG8gdXNlIHNvbWUgb3RoZXIgZnVuY3Rpb25zIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZSwgcGFydGljdWxhcmx5IHRoZSBgZmFjZXRfd3JhcCgpYCBmdW5jdGlvbiwgd2hpY2ggd2lsbCBhbGxvdyB1cyB0byBsb29rIGF0IGRpZmZlcmVuY2VzIGluIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIEJNSSBlc3RpbWF0ZXMgc3RyYXRpZmllZCBieSBgWWVhcmAsIGBTZXhgLCBhbmQgYFJlZ2lvbmAuDQpXZSBjYW4gc2VxdWVudGlhbGx5IGRpdmlkZSBvdXIgcGxvdHMgYnkgZGVlcGVyIGxldmVscyB1c2luZyBtdWx0aXBsZSB2YXJpYWJsZXMgYW5kIHRoZSBgK2AgcGx1cyBzaWduIHdpdGhpbiBgZmFjZXRfd3JhcCgpYC4NCg0KYGBge3J9DQpCTUlfbG9uZyAlPiUNCiAgZ2dwbG90KGFlcyh4PUJNSSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGZhY2V0X3dyYXAofiBTZXggKyBZZWFyICsgUmVnaW9uKSANCmBgYA0KDQpTb21lIG9mIHRoZXNlIHBsb3RzIGxvb2sgcHJldHR5IHNpbWlsYXIgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBsaWtlIHRoZSBgV29tZW4gMjAxNyBVcmJhbmAgcGxvdCAoYWx0aG91Z2ggaXQgbWlnaHQgYmUgcmlnaHQtc2tld2VkLCBhZ2FpbiB3ZSBjb3VsZCBjaGVjayB0aGUgbWVhbiBhbmQgbWVkaWFuIHRvIGRldGVybWluZSB0aGF0KS4NCkhvd2V2ZXIgc29tZSBvZiB0aGUgb3RoZXIgcGxvdHMsIGxpa2UgdGhlIGBNZW4gMjAxNyBVcmJhbmAgcGxvdCwgbG9vayB2ZXJ5IGRpZmZlcmVudC4gDQpUaGVzZSBwbG90cyBzaG93IHdoYXQgaXMgY2FsbGVkIGEgW2JpbW9kYWwgZGlzdHJpYnV0aW9uXShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvaW50cm9kdWN0aW9uL2Rpc3RyaWJ1dGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSwgbWVhbmluZyB0aGF0IHRoZXJlIGFwcGVhciB0byBiZSB0d28gcGVha3MgKG9yIG1vZGVzKSwgb3IgaW4gb3RoZXIgd29yZHMgdHdvIGRpZmZlcmVudCB2YWx1ZXMgKGFuZCB2YWx1ZXMgbmVhciBlYWNoIG9mIHRoZXNlKSB0aGF0IGFyZSB0aGUgbW9zdCBmcmVxdWVudC4NCg0KIyMjICoqUXVhbnRpbGUtUXVhbnRpbGUgUGxvdHMqKg0KKioqDQoNCkFub3RoZXIgdXNlZnVsIHBsb3QgdG8gY3JlYXRlIHdoZW4gZXhwbG9yaW5nIGRpc3RyaWJ1dGlvbnMgaXMgc29tZXRoaW5nIGNhbGxlZCBhICJxdWFudGlsZS1xdWFudGlsZSIgcGxvdCAob3IgPGI+US1RIHBsb3Q8L2I+IGZvciBzaG9ydCkuIFdoZW4gd2UgdGFsayBhYm91dCBhIDxiPnF1YW50aWxlPC9iPiwgd2UgYXJlIHRhbGtpbmcgYWJvdXQgZGl2aWRpbmcgdXAgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpbnRvIHJvdWdobHkgZXF1YWwgcG9ydGlvbnMgd2hlcmUgcm91Z2hseSB0aGUgc2FtZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZhbGwgaW50byBlYWNoIHBvcnRpb24uIEZvciBleGFtcGxlLCBpZiB5b3UgZGl2aWRlIHlvdXIgZGF0YSBpbnRvIDEwMCBxdWFudGlsZXMsIHlvdSBjYW4gdGhpbmsgYWJvdXQgdGhpcyBhcyBwZXJjZW50aWxlcywgYnV0IHlvdSBjb3VsZCBhbHNvIGRpdmlkZSB5b3VyIGRhdGEgaW50byAxMCBxdWFudGlsZXMgYW5kIHRoZXNlIHdvdWxkIGJlIGNhbGxlZCBkZWNpbGVzLiANCg0KPGI+V2h5IFEtUSBwbG90cz88L2I+IFRoaXMgcGxvdCBhbGxvd3MgdXMgdG8gY29tcGFyZSB0aGUgcXVhbnRpbGVzIG9mIHR3byBkaXN0cmlidXRpb25zIHRvZ2V0aGVyOiAoMSkgcXVhbnRpbGVzIG9mIGEga25vd24gdGhlb3JldGljYWwgZGlzdHJpYnV0aW9uIChsaWtlIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uKSBjb21wYXJlZCB0byAoMikgcXVhbnRpbGVzIG9mIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGEuICBJZiB0aGUgcXVhbnRpbGVzIGZyb20gdGhlc2UgdHdvIGRpc3RyaWJ1dGlvbnMgbGluZSB1cCBpbiB0aGUgcGxvdCwgdGhlbiB0aGF0IGlzIGEgdmlzdWFsIHBpZWNlIG9mIGV2aWRlbmNlIHRoYXQgb3VyIGRhdGEgZm9sbG93IHRoYXQgdGhlb3JldGljYWwgZGlzdHJpYnV0aW9uIChsaWtlIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uKS4gIA0KDQo8Yj5Ib3cgZG9lcyB0aGlzIHdvcms/PC9iPiBUbyBkbyB0aGlzIHdlIHdpbGwgcGxvdCB0aGUgcXVhbnRpbGVzIG9mIG91ciBkYXRhIG9uIHRoZSB5LWF4aXMgYW5kIHRoZSBxdWFudGlsZXMgb2YgdGhlIHRoZW9yZXRpY2FsIG5vcm1hbCBkaXN0cmlidXRpb24gb24gdGhlIHgtYXhpcy4gSWYgdGhlIHF1YW50aWxlcyBsaW5lIHVwIHRoZW4gd2UgY2FuIHNheSB0aGF0IG91ciBkYXRhIGlzIGZhaXJseSBub3JtYWwuIFNlZSBbaGVyZV0oaHR0cDovL29ubGluZXN0YXRib29rLmNvbS8yL2FkdmFuY2VkX2dyYXBocy9xLXFfcGxvdHMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBRLVEgcGxvdHMuDQoNClVzaW5nIHRoZSBgc3RhdF9xcSgpYCBmdW5jdGlvbiBvZiB0aGUgYGdncGxvdDJgIHBhY2thZ2UsIHdlIGNhbiBlYXNpbHkgY3JlYXRlIGEgUS1RIHBsb3QgZm9yIG91ciBkYXRhIHJhbmRvbWx5IHNhbXBsZWQgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gKCJzYW1wbGUiKSBhbmQgY29tcGFyZSBpdCB0byB0aGUgcXVhbnRpbGVzIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uICgidGhlb3JldGljYWwiKS4gIFRoZSBkZWZhdWx0IGNvbXBhcmlzb24gZGlzdHJpYnV0aW9uIGZvciB0aGVzZSBmdW5jdGlvbnMgaXMgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24sIHNvIHdlIGRvbid0IG5lZWQgdG8gc3BlY2lmeSBpdCBpbiBvdXIgY29kZS4NCmBgYHtyfQ0Kbm9ybV9CTUlfZXhfZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyhzYW1wbGUgPSBub3JtX2RhdGEpKSArDQogIHN0YXRfcXEoKSArIA0KICBzdGF0X3FxX2xpbmUoKQ0KYGBgDQoNClRoZSBgc3RhdF9xcV9saW5lKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gYWRkIGEgbGluZSAoY29tcHV0ZXMgdGhlIHNsb3BlIGFuIGludGVyY2VwdCkgb24gdGhlIHBsb3QuIElmIHRoZSBwb2ludHMgbGllIG9uIHRoaXMgc3RyYWlnaHQgbGluZSwgdGhpcyBpcyBldmlkZW5jZSB0aGF0IHRoZSBkYXRhIGhhdmUgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBub3QgdGhhdCB0aGUgZGF0YSBoYXZlIGEgcGFydGljdWxhciBub3JtYWwgZGlzdHJpYnV0aW9uLg0KTm90IHN1cnByaXNpbmdseSwgd2Ugc2VlIHRoYXQgdGhlIHBvaW50cyBtb3N0bHkgZmFsbCBvbiB0aGUgbGluZSwgaW5kaWNhdGluZyB0aGF0IHRoZSBxdWFudGlsZXMgYXJlIGZhaXJseSBzaW1pbGFyIGJldHdlZW4gdGhlIG9ic2VydmVkIGFuZCB0aGVvcmV0aWNhbCBkYXRhLg0KDQpUaGVyZSBhcmUgc29tZSBwb2ludHMgdGhhdCBhcmUgYSBiaXQgZnVydGhlciBmcm9tIHRoZSBsaW5lIGFzIHdlIGdldCB0byB0aGUgZXh0cmVtZSBxdWFudGlsZXMuDQoNCk5vdGUgdGhhdCB0aGlzIHBsb3QgaXMgY29tcGFyaW5nIG91ciBgbm9ybV9CTUlfZXhfZGF0YWAgd2hpY2ggd2FzIHJhbmRvbWx5IHNhbXBsZWQgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIDI0IGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gMSB0byBhICpzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uKiB3aXRoIG1lYW4gMCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIDEsIHdoaWNoIGlzIHdoeSB0aGUgdmFsdWVzIG9uIHRoZSB4LWF4aXMgYXJlIC0zIHRvIDMgaW5zdGVhZCBvZiAyMSB0byAyNy4gDQoNCkFzIGV4cGVjdGVkIHdlIHNlZSB0aGF0IGFib3V0IGhhbGYgdGhlIHBvaW50cyBhcmUgYmVsb3cgb3VyIG1lYW4gb2YgMjQgb24gdGhlIHktYXhpcy4gIA0KDQpXZSBjYW4gc3BlY2lmeSBhIHNwZWNpZmljIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3Igb3VyIHRoZW9yZXRpY2FsIGRpc3RyaWJ1dGlvbiBpbiBvdXIgUS1RIHBsb3Q7IGhlcmUgd2Ugc3BlY2lmeSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIDI0IGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gMSAoc28gdGhhdCB0aGUgdGhlb3JldGljYWwgZGlzdHJpYnV0aW9uIGxvb2tzIG1vcmUgbGlrZSBvdXIgZGF0YSk6DQpgYGB7cn0NCm5vcm1fQk1JX2V4X2RhdGEgJT4lDQogIGdncGxvdChhZXMoc2FtcGxlID0gbm9ybV9kYXRhKSkgKw0KICBzdGF0X3FxKGRpc3RyaWJ1dGlvbiA9IHFub3JtLCBkcGFyYW1zID0gYyhtZWFuID0gMjQsIHNkID0gMSkpICsgDQogIHN0YXRfcXFfbGluZShkaXN0cmlidXRpb24gPSBxbm9ybSwgZHBhcmFtcyA9IGMobWVhbiA9IDI0LCBzZCA9IDEpKQ0KYGBgDQpOb3RpY2UgdGhhdCB0aGUgcGxvdCBsb29rcyB0aGUgc2FtZSBleGNlcHQgZm9yIHRoZSB2YWx1ZXMgb24gdGhlIHgtYXhpcyBoYXZlIGNoYW5nZWQsIG1lYW5pbmcgb3VyIGludGVycHJldGF0aW9uIHdpbGwgYmUgdGhlIHNhbWUgLS0gaXQgbG9va3MgbGlrZSB0aGUgdmFsdWVzIGFyZSBjb25zaXN0ZW50IHdpdGggYSBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQoNCk5vdywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIGFjdHVhbCBCTUkgZGF0YToNCmBgYHtyfQ0KQk1JX2xvbmcgJT4lDQogIGdncGxvdChhZXMoc2FtcGxlID1CTUkpKSArDQogIHN0YXRfcXEoKSArIA0KICBzdGF0X3FxX2xpbmUoKSArDQogIGZhY2V0X3dyYXAofiBTZXggKyBZZWFyICsgUmVnaW9uKQ0KYGBgDQoNClRoaXMgaXMgYSBsaXR0bGUgaGFyZCB0byBzZWUsIHNvbWUgcGxvdHMgc3VnZ2VzdCB0aG9zZSBzdWJzZXRzIG9mIHRoZSBkYXRhIGFyZSBzaW1pbGFyIHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgYnV0IG90aGVycyBzZWVtIHRvIGJlIHNrZXdlZC4gTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rLg0KDQpGb3IgdGhlIHNha2Ugb2Ygc2ltcGxpY2l0eSwgd2UgYXJlIGdvaW5nIHRvIGZvY3VzIG9uIHRoZSBzdWJzZXQgb2YgZGF0YSBmb3IgdGhlIHdvbWVuLiANClRoaXMgaXMgYmVjYXVzZSB3b21lbiB3ZXJlIGlkZW50aWZpZWQgaW4gdGhlIHBhcGVyIHRvIGhhdmUgZmFzdGVzdCBpbmNyZWFzZXMgaW4gQk1JIG92ZXIgdGltZS4NCklmIHdlIHdhbnQgdG8gY29tcGFyZSB0d28gc3Vic2V0cyBvZiB3b21lbiAoZS5nLiAxOTg1IHZlcnN1cyAyMDE3IG9yIHJ1cmFsIHZlcnN1cyB1cmJhbiksIHRoZW4gd2UgbmVlZCB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBvZiB0aGVzZSBzdWJzZXRzLg0KDQpgYGB7cn0NCkJNSV9sb25nICU+JQ0KICBmaWx0ZXIoU2V4ID09ICJXb21lbiIpICU+JQ0KICBnZ3Bsb3QoYWVzKHNhbXBsZSA9IEJNSSkpICsNCiAgc3RhdF9xcSgpICsgDQogIHN0YXRfcXFfbGluZSgpICsNCiAgZmFjZXRfd3JhcCh+IFllYXIgKyBSZWdpb24pDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IGF0IHRoZSBleHRyZW1lcyBvZiBvdXIgcXVhbnRpbGVzLCBmb3IgbW9zdCBvZiBvdXIgZGF0YSwgb3VyIHRhaWxzIGFyZSBub3QgdmVyeSBzaW1pbGFyIHRvIHRoZSB0aGVvcmV0aWNhbCBkaXN0cmlidXRpb24uIA0KV2UgY2FuIGNhbiBhbHNvIHNlZSB0aGF0IG91ciBkYXRhIGlzIHJpZ2h0LSBvciBwb3NpdGl2ZS1za2V3ZWQsIGJlY2F1c2UgdGhlIGV4dHJlbWUgdmFsdWVzIGFyZSBhYm92ZSB0aGUgbGluZS4NCg0KVGhlIHJ1cmFsIGRhdGEgbG9va3MgbW9yZSBub3JtYWwgdGhhbiB0aGUgdXJiYW4gZGF0YS4gDQpZb3UgbWlnaHQgYmUgYXNraW5nLCB3aHkgZG8gd2UgY2FyZSBpZiB0aGUgZGF0YSBsb29rIG1vcmUgbm9ybWFsIG9yIG5vdD8gDQpJdCB0dXJucyBvdXQgaWYgeW91IHdhbnQgdG8gYXNrIHF1ZXN0aW9ucyBsaWtlICJJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2UgQk1JIGVzdGltYXRlcyBmcm9tIHdvbWVuIGluIDE5ODUgY29tcGFyZWQgdG8gd29tZW4gaW4gMjAxNz8iLCB3ZSB3aWxsIG5lZWQgdG8gdXNlIHNvbWUgb2YgdHlwZSBvZiBzdGF0aXN0aWNhbCB0ZXN0LiANClRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgc3RhdGlzdGljYWwgdGVzdHMgdGhhdCBjYW4gYW5zd2VyIHRoYXQgcXVlc3Rpb24gcmVseSBvbiBhbiBhc3N1bXB0aW9uIGFib3V0IHRoZSBkYXRhIHlvdSBoYXZlLCBuYW1lbHkgdGhhdCB5b3VyIGRhdGEgaGF2ZSBhIHNpbWlsYXIgc2hhcGUgYXMgYSB0aGVvcmV0aWNhbCBub3JtYWwgZGlzdHJpYnV0aW9uLiANClNvLCBpZiB3ZSB3YW50IHRvIHVzZSB0aG9zZSBwb3dlcmZ1bCBzdGF0aXN0aWNhbCB0ZXN0cyB0aGF0IHJlbHkgb24gdGhpcyBhc3N1bXB0aW9uIG9mICJub3JtYWxpdHkiLCB0aGVuIHdlIG5lZWQgdG8gY2hlY2sgdGhhdCBhc3N1bXB0aW9uIGJlZm9yZSB1c2luZyB0aG9zZSB0ZXN0cy4gDQoNCldlIGNhbiBjb25jbHVkZSBmcm9tIHRoZXNlIHBsb3RzIHRoYXQgdGhlIGFzc3VtcHRpb24gb2Ygbm9ybWFsaXR5IGFwcGVhcnMgdG8gYmUgdmlvbGF0ZWQgaW4gb3VyIGRhdGEuDQoNCg0KIyMgKipEYXRhIEFuYWx5c2lzKioNCioqKg0KDQpJZiB5b3UgaGF2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyBidXQgc3RvcHBlZCwgd2UgY291bGQgbG9hZCBvdXIgd3JhbmdsZWQgZGF0YSBsaWtlIHNvOg0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsICJ3cmFuZ2xlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCioqKg0KPGRldGFpbHM+IDxzdW1tYXJ5PiBJZiB5b3Ugc2tpcHBlZCB0aGUgZGF0YSB3cmFuZ2xpbmcgc2VjdGlvbiBjbGljayBoZXJlLiA8L3N1bW1hcnk+DQoNCkZpcnN0IHlvdSBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoIk9DU2RhdGEiKQ0KbGlicmFyeShPQ1NkYXRhKQ0KYGBgDQoNClRoZW4sIHlvdSBtYXkgbG9hZCB0aGUgd3JhbmdsZWQgZGF0YSB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGU6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kd3JhbmdsZWRfcmRhKCJvY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkiLCBvdXRwYXRoID0gZ2V0d2QoKSkNCmxvYWQoaGVyZTo6aGVyZSgiT0NTZGF0YSIsICJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGEucmRhIikpDQpgYGANCg0KSWYgdGhlIHBhY2thZ2UgZG9lcyBub3Qgd29yayBmb3IgeW91LCBhbHRlcm5hdGl2ZWx5LCBhbiBSREEgZmlsZSAoc3RhbmRzIGZvciBSIGRhdGEpIG9mIHRoZSBkYXRhIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5L3RyZWUvbWFzdGVyL2RhdGEvd3JhbmdsZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5L21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5Lg0KDQpgYGB7cn0NCmxvYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJ3cmFuZ2xlZCIsICJ3cmFuZ2xlZF9kYXRhLnJkYSIpKQ0KYGBgDQoNCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIG1vcmUgYWJvdXQgY3JlYXRpbmcgbmV3IHByb2plY3RzIGluIFJTdHVkaW8uIDwvc3VtbWFyeT4NCg0KWW91IGNhbiBjcmVhdGUgYSBwcm9qZWN0IGJ5IGdvaW5nIHRvIHRoZSBGaWxlIG1lbnUgb2YgUlN0dWRpbyBsaWtlIHNvOg0KDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJOZXdfcHJvamVjdC5wbmciKSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gZG8gc28gYnkgY2xpY2tpbmcgdGhlIHByb2plY3QgYnV0dG9uOg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicHJvamVjdF9idXR0b24ucG5nIikpDQpgYGANCg0KU2VlIFtoZXJlXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzKSB0byBsZWFybiBtb3JlIGFib3V0IHVzaW5nIFJTdHVkaW8gcHJvamVjdHMgYW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIGBoZXJlYCBwYWNrYWdlLg0KDQo8L2RldGFpbHM+DQoqKioNCjwvZGV0YWlscz4NCioqKg0KDQoNCkluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCB3ZSBoaW50ZWQgYXQgdGhlIGV4aXN0ZW5jZSBvZiBzdGF0aXN0aWNhbCB0ZXN0cyB0byBiZSBhYmxlIHRvIGFzc2VzcyBpZiB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2UgQk1JIGVzdGltYXRlcyBiZXR3ZWVuIHR3byBncm91cHMgKGUuZy4gbmF0aW9uYWwgQk1JIGVzdGltYXRlcyBpbiBydXJhbCBhcmVhcyBhbmQgbmF0aW9uYWwgQk1JIGVzdGltYXRlcyBpbiB1cmJhbiBhcmVhcykuIA0KT25lIHN1Y2ggdGVzdCBpcyB0aGUgW1N0dWRlbnQncyAkdCQtdGVzdF0oaHR0cHM6Ly9zdGF0dHJlay5jb20vc3RhdGlzdGljcy9kaWN0aW9uYXJ5LmFzcHg/ZGVmaW5pdGlvbj10d28tc2FtcGxlJTIwdC10ZXN0KXt0YXJnZXQ9Il9ibGFuayJ9LCB3aGljaCBjYW4gYmUgdXNlZCB0byBkZXRlcm1pbmUgaWYgW3R3byBncm91cCBtZWFucyBhcmUgZGlmZmVyZW50XShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvZXN0aW1hdGlvbi9kaWZmZXJlbmNlX21lYW5zLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uDQpIb3dldmVyLCBhcyBtZW50aW9uZWQgYmVmb3JlLCB0aGlzIHRlc3QgZGVwZW5kcyBvbiBjZXJ0YWluIGFzc3VtcHRpb24gb2Ygb3VyIGRhdGEuIA0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGFyZSBnb2luZyB0byB0YWxrIGFib3V0IHRoaXMgdGVzdCBhbmQgb3RoZXIgdGVzdHMgdGhhdCBkbyBub3QgcmVxdWlyZSB0aGUgc2FtZSBhc3N1bXB0aW9ucyBhYm91dCBvdXIgZGF0YS4gDQoNCiMjIyAqKkh5cG90aGVzaXMgdGVzdGluZyoqDQoqKioNCg0KTGV0J3MgcmVtaW5kIG91cnNlbHZlcyBvZiBvbmUgb2Ygb3VyIG9yaWdpbmFsIHF1ZXN0aW9ucywgDQoNCj4gSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIEJNSSBlc3RpbWF0ZXMgYXJvdW5kIHRoZSB3b3JsZD8NCg0KSW4gaHlwb3RoZXNpcyB0ZXN0aW5nLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcgdHdvIGRpZmZlcmVudCBoeXBvdGhlc2VzOiBhICJudWxsIiBoeXBvdGhlc2lzIChjYW4gYmUgdGhvdWdodCBvZiBsaWtlIGEgYmFzZWxpbmUgZS5nLiB0aGUgbWVhbnMgYmV0d2VlbiB0d28gZ3JvdXBzIGFyZSB0aGUgc2FtZSkgY29tcGFyZWQgdG8gYW4gImFsdGVybmF0aXZlIiBoeXBvdGhlc2lzIChlLmcuIHRoZSBtZWFucyBiZXR3ZWVuIHR3byBncm91cHMgYXJlIGRpZmZlcmVudCkuIFdlIGFyZSBnb2luZyB0byBhc2sgaWYgdGhlcmUgaXMgZW5vdWdoIGV2aWRlbmNlIGluIG91ciBkYXRhIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiAgDQoNCkxldCdzIHRyeSB0byBmb3JtYWxpemUgdGhpcyBhIGJpdC4gDQoNClN1cHBvc2Ugd2Ugd2FudCB0byB0ZXN0IHdoZXRoZXIgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIEJNSSBlc3RpbWF0ZXMgdXNpbmcgdGhlIGRhdGEgZnJvbSAxOTg1LiAgSW4gb3VyIGNhc2UsIHRoZW4sIHdlIHdhbnQgdG8gdGVzdCB3aGV0aGVyIHRoZSBtZWFuIEJNSSBlc3RpbWF0ZSBhbW9uZyB0aGUgcnVyYWwgYXJlYXMgaXMgdGhlIHNhbWUgYXMgdGhlIG1lYW4gQk1JIGVzdGltYXRlIGluIHRoZSB1cmJhbiBhcmVhcy4gSWYgd2UgY2FsbCB0aGUgdHJ1ZSB1bmtub3duIG1lYW5zIG9mIHRoZSB0d28gZ3JvdXBzICRcbXVfVSQgYW5kICRcbXVfUiQsIGZvciB0aGUgdXJiYW4gYW5kIHJ1cmFsIGFyZWFzLCByZXNwZWN0aXZlbHksIHRoZW4gd2UgY2FuIGRlZmluZSB0aGUgPGI+bnVsbCBoeXBvdGhlc2lzPC9iPiB0aGF0IHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgaW4gdGhlIHR3byBtZWFuczoNCg0KJCQgSF8wOiBcbXVfVSA9IFxtdV9SICQkICANCg0KSW4gY29udHJhc3QsIHdlIGFsc28gZGVmaW5lIGFuIDxiPmFsdGVybmF0aXZlIGh5cG90aGVzaXM8L2I+IHRoYXQgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gQk1JIGVzdGltYXRlczogDQoNCiQkIEhfYTogXG11X1UgXG5lcSBcbXVfUiAkJA0KDQpUaGUgaWRlYSBiZWhpbmQgYSBoeXBvdGhlc2lzIHRlc3QgaXMgdGhhdCB3ZSBhc3N1bWUgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlIGFuZCB3ZSB1c2Ugb3VyIGRhdGEgdG8gaGVscCB1cyBpZGVudGlmeSBpZiB0aGVyZSBpcyBlbm91Z2ggZXZpZGVuY2UgdG8gX3JlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzXy4gDQoNClRoaXMgaXMgc2ltaWxhciB0byB0aGUgaWRlYSBvZiBhc3N1bWluZyB0aGF0IGluZGl2aWR1YWxzIGFyZSBub3QgZ3VpbHR5IHVudGlsIHByb3ZlbiBvdGhlcndpc2UuDQpJZiB0aGVyZSBpcyBub3QgZW5vdWdoIGV2aWRlbmNlIGluIHRoZSBkYXRhLCB0aGVuIHdlIHNheSB3ZSAiZmFpbCB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyIuDQoNCllvdSBtaWdodCBiZSBhc2tpbmcsICJCdXQgd2UgZG8gbm90IGtub3cgd2hhdCB0aGUgdHJ1ZSBncm91cCBtZWFucyBhcmU/IiANClRoYXQgaXMgY29ycmVjdC4gSW5zdGVhZCwgd2UgbXVzdCBlc3RpbWF0ZSB0aGVzZSBtZWFucyBiYXNlZCBvbiB0aGUgZGF0YSB0aGF0IHdlIGhhdmUuDQpGb3IgZXhhbXBsZSwgaW4gb3VyIGRhdGEsIHdlIGNhbiBlc3RpbWF0ZSB0aGUgYXZlcmFnZSBCTUkgZXN0aW1hdGVzIGZvciB3b21lbiBpbiBydXJhbCBhbmQgdXJiYW4gcmVnaW9ucyBpbiAxOTg1LiBIZXJlIHdlIHdpbGwgdXNlIHRoZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSBhZ2FpbiwgdGhpcyB0aW1lIHVzaW5nIHRoZSBhcmd1bWVudCBgbmEucm0gPSBUUlVFYCB0byByZW1vdmUgTkEgdmFsdWVzLg0KDQpgYGB7cn0NCkJNSV9sb25nICU+JSANCiAgZmlsdGVyKFllYXIgPT0gMTk4NSwgU2V4ID09ICJXb21lbiIsIFJlZ2lvbiAhPSAiTmF0aW9uYWwiKSAlPiUgDQogIGdyb3VwX2J5KFJlZ2lvbikgJT4lDQogIHN1bW1hcml6ZShhdmdfYm1pID0gbWVhbihCTUksIG5hLnJtID0gVFJVRSkpIA0KYGBgDQoNCkhlcmUgd2Ugc2VlIHRoYXQgdGhhdCBpbiAxOTg1LCB0aGUgYXZlcmFnZSBCTUkgZXN0aW1hdGUgZm9yIHdvbWVuIGFjcm9zcyB1cmJhbiBhcmVhcyBpcyAyNC42IGNvbXBhcmVkIHRvIDIzLjYgYWNyb3NzIHJ1cmFsIGFyZWFzLiAgSXQgbG9va3MgbGlrZSBCTUkgZXN0aW1hdGVzIGFyZSwgb24gYXZlcmFnZSwgaGlnaGVyIGluIHVyYmFuIGFyZWFzIGNvbXBhcmVkIHRvIHJ1cmFsIGFyZWFzLiAgSG93ZXZlciwgbm93IHdlIHdhbnQgdG8gdXNlIGEgc3RhdGlzdGljYWwgdGVzdCB0byBhc3Nlc3MgaWYgdGhpcyBkaWZmZXJlbmNlIGlzIHN0YXRpc3RpY2FsbHkgbWVhbmluZ2Z1bCBvciBhIGRpZmZlcmVuY2Ugb2YgdGhpcyBzaXplIGNvdWxkIGJlIGZvdW5kIGR1ZSB0byByYW5kb20gY2hhbmNlLiANCg0KIyMjICoqVHdvLXNhbXBsZSAkdCQtdGVzdCoqDQoqKioNCg0KVGhlIHR3by1zYW1wbGUgJHQkLXRlc3QgaXMgYSBjb21tb24gd2F5IHRvIGRldGVybWluZSBpZiB0aGUgbWVhbnMgb2YgdHdvIGdyb3VwcyBhcmUgZGlmZmVyZW50LiBJdCBkb2VzIHRoaXMgYnkgY2FsY3VsYXRpbmcgYSBvYnNlcnZlZCB0ZXN0LXN0YXRpc3RpYywgJHQkLCB0aGF0IGxvb2tzIGF0IHRoZSBkaWZmZXJlbmNlIGluIHRoZSBzYW1wbGUgbWVhbnMgb2YgdGhlIHR3byBncm91cHMgZGl2aWRlZCBieSBhbiBlc3RpbWF0ZSBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRpZmZlcmVuY2UgaW4gc2FtcGxlIG1lYW5zLiAgSGVyZSAkc197cG9vbGVkfSQgaXMgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBkYXRhIHdoZW4gcG9vbGluZyB0aGUgc2FtcGxlcyB0b2dldGhlci4NCiQkDQp0ID0gXGZyYWN7XGJhcntYfV9VLVxiYXJ7WH1fUn17c197cG9vbGVkfVxzcXJ0e1xmcmFjezF9e25fVX0gKyBcZnJhY3sxfXtuX1J9fX0NCiQkDQpJZiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUsIGFuZCB0aGUgbWVhbnMgZm9yIHVyYmFuIGFuZCBydXJhbCBhcmVhcyBhcmUgdGhlIHNhbWUsIHRoZW4gdGhlIHNhbXBsZSBtZWFucyBpbiB0aGUgbnVtZXJhdG9yIHdpbGwgYmUgc2ltaWxhciBhbmQgdGhpcyB0ZXN0IHN0YXRpc3RpYyB3aWxsIGJlIGNsb3NlIHRvIDAuICBIb3dldmVyLCBpZiB0aGUgdGVzdCBzdGF0aXN0aWMgaXMgbm90IGNsb3NlIHRvIDAsIHRoaXMgcHJvdmlkZXMgZXZpZGVuY2UgdGhlIG1lYW5zIGZvciB0aGUgdXJiYW4gYW5kIHJ1cmFsIGFyZWFzIGFyZSBkaWZmZXJlbmNlLCBhbmQgc28gd2UgbWF5IGNob29zZSB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4gIA0KDQpUbyBmb3JtYWxseSBkZWNpZGUgd2hldGhlciB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgd2UgY29tcGFyZSB0aGUgb2JzZXJ2ZWQgdGVzdCBzdGF0aXN0aWMgdG8gdGhlIGRpc3RyaWJ1dGlvbiBvZiBwb3NzaWJsZSB0ZXN0IHN0YXRpc3RpY3MgdGhhdCBjb3VsZCBvY2N1ciBpZiB0aGUgbnVsbCBoeXBvdGhlc2lzIHdlcmUgdHJ1ZSB1c2luZyB3aGF0IHdlIGNhbGwgYSAqKnAtdmFsdWUqKi4gIFRoZSBwLXZhbHVlIHRlbGxzIHVzIHRoZSBwcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgYSB0ZXN0IHN0YXRpc3RpYyBhcyBvciBtb3JlIGV4dHJlbWUgdGhhbiB0aGUgb25lIHdlIG9ic2VydmVkIGlmIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gIElmIHRoZSBwLXZhbHVlIGlzIHNtYWxsLCBzYXkgbGVzcyB0aGFuIDAuMDUsIHRoZW4gb3VyIG9ic2VydmVkIHRlc3Qgc3RhdGlzdGljIGlzIHByZXR0eSByYXJlIGlmIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gIFRoaXMgd291bGQgbGVhZCB1cyB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBhbmQgY29uY2x1ZGUgdGhhdCB0aGUgbWVhbnMgb2YgdGhlIHR3byBncm91cHMgYXJlIGRpZmZlcmVudC4gV2Ugd2lsbCB0YWxrIG1vcmUgYWJvdXQgcC12YWx1ZXMsIGFuZCBpbiBwYXJ0aWN1bGFyIG91ciBjaG9pY2Ugb2YgMC4wNSBhcyBhIGNvbXBhcmlzb24gZm9yIHRoZSBwLXZhbHVlLCBhIGxpdHRsZSBsYXRlci4NCg0KSGVyZSB3ZSBwZXJmb3JtIGEgdHdvLXNhbXBsZSAkdCQtdGVzdCBjb21wYXJpbmcgZXN0aW1hdGUgZm9yIHJ1cmFsIHdvbWVuIGluIDE5ODUgdG8gZXN0aW1hdGVzIGZvciB1cmJhbiB3b21lbiBpbiAxOTg1LiAgV2UgdXNlIHRoZSBgdmFyLmVxdWFsID0gVFJVRWAgb3B0aW9uIGhlcmUgdG8gdXNlIHRoZSBwb29sZWQgc3RhbmRhcmQgZGV2aWF0aW9uICRzX3twb29sZWR9JCBmcm9tIHRoZSBmb3JtdWxhIGFib3ZlLg0KYGBge3J9DQp0LnRlc3QocHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICBZZWFyID09ICIxOTg1IiwgDQogICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpLCBCTUkpLCANCiAgICAgICBwdWxsKGZpbHRlcihCTUlfbG9uZywgU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgIFllYXIgPT0gIjE5ODUiLCANCiAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIlVyYmFuIiksIEJNSSksIA0KICAgICAgIHZhci5lcXVhbCA9IFRSVUUpDQojIG1lYW5zIGFyZSBkaWZmZXJlbnQgLSBwIHZhbHVlIDwuMDUgcmVqZWN0IHRoZSBudWxsOiBubyBkaWZmZXJlbmNlIGluIHRoZSBtZWFucw0KYGBgDQoNCkluIHRoaXMgY2FzZSB0aGUgcC12YWx1ZSBmb3IgdGhlIHRlc3QgaXMgMC4wMDAxMiwgd2hpY2ggaXMgcXVpdGUgc21hbGwuICBIZXJlIHdvdWxkIHdvdWxkIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIG1lYW5zIGluIHRoZSB0d28gZ3JvdXBzIGFyZSBlcXVhbCBhbmQgY29uY2x1ZGUgdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIG1lYW4gQk1JIGVzdGltYXRlcyBmb3Igd29tZW4gaW4gdXJiYW4gYW5kIHJ1cmFsIGFyZWFzLiAgDQoNCkJ1dCB3YWl0ISAgV2UgbmVlZCB0byBjaGVjayB0aGUgYXNzdW1wdGlvbnMgb2YgdGhlIHN0YXRpc3RpY2FsIHRlc3Qgd2UgdXNlZCB0byBiZSBzdXJlIGl0IGlzIHZhbGlkLiAgVGhlIHR3by1zYW1wbGUgJHQkLXRlc3QgcmVsaWVzIG9uIHNldmVyYWwgPHU+PGI+YXNzdW1wdGlvbnM8L2I+PC91PjoNCg0KMSkgVGhlIGRhdGEgZm9yIGVhY2ggZ3JvdXAgaXMgW25vcm1hbGx5IGRpc3RyaWJ1dGVkXShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvaW50cm9kdWN0aW9uL2Rpc3RyaWJ1dGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4NCjIpIFRoZSBbdmFyaWFuY2VdKGh0dHBzOi8vc3RhdHRyZWsuY29tL3N0YXRpc3RpY3MvZGljdGlvbmFyeS5hc3B4P2RlZmluaXRpb249dmFyaWFuY2Upe3RhcmdldD0iX2JsYW5rIn0gb2YgYm90aCBncm91cHMgaXMgc2ltaWxhci4NCjMpIFRoZSBvYnNlcnZhdGlvbnMgZnJvbSB0aGUgdHdvIGdyb3VwcyBhcmUgW2luZGVwZW5kZW50XShodHRwczovL3d3dy5zdGF0LmNtdS5lZHUvfmNzaGFsaXppLzM2LTIyMC9sZWN0dXJlLTUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IChtZWFuaW5nIHRoYXQgb2JzZXJ2YXRpb25zIGRvIG5vdCBpbmZsdWVuY2UgZWFjaCBvdGhlcikuDQo0KSBUaGUgb2JzZXJ2YXRpb25zIHdpdGhpbiBlYWNoIGdyb3VwIGFyZSBbaW5kZXBlbmRlbnRdKGh0dHBzOi8vd3d3LnN0YXQuY211LmVkdS9+Y3NoYWxpemkvMzYtMjIwL2xlY3R1cmUtNS5wZGYpe3RhcmdldD0iX2JsYW5rIn0gKG1lYW5pbmcgdGhhdCBvYnNlcnZhdGlvbnMgZG8gbm90IGluZmx1ZW5jZSBlYWNoIG90aGVyKS4NCg0KDQpJZiB0aGVzZSBhc3N1bXB0aW9ucyBhcmUgdmlvbGF0ZWQsIHRoaXMgZG9lc24ndCBuZWNlc3NhcmlseSBtZWFuIHdlIGNhbid0IHBlcmZvcm0gYSBzdGF0aXN0aWNhbCB0ZXN0LiBJdCBqdXN0IG1lYW5zIHdlIG1heSBuZWVkIHRvIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgb3B0aW9uczoNCg0KMSkgSWYgdGhlIGRhdGEgaXNuJ3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHdlIG1heSBuZWVkIHRvIGNvbnNpZGVyIGVpdGhlciBhIFt0cmFuc2Zvcm1hdGlvbl0oaHR0cHM6Ly93d3cuc3RhdGlzdGljc2hvd3RvLmRhdGFzY2llbmNlY2VudHJhbC5jb20vdHJhbnNmb3JtYXRpb24tc3RhdGlzdGljcy8pe3RhcmdldD0iX2JsYW5rIn0gb2YgdGhlIGRhdGEgdG8gbWFrZSBpdCBtb3JlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIG9yIGEgW25vbnBhcmFtZXRyaWNdKGh0dHBzOi8vd3d3Lm1heW8uZWR1L3Jlc2VhcmNoL2RvY3VtZW50cy9wYXJhbWV0cmljLWFuZC1ub25wYXJhbWV0cmljLWRlbXlzdGlmeWluZy10aGUtdGVybXMvZG9jLTIwNDA4OTYwKXt0YXJnZXQ9Il9ibGFuayJ9IHRlc3Qgd2hpY2ggZG9lc24ndCByZWx5IG9uIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbi4NCjIpIElmIHRoZSB2YXJpYW5jZXMgb2YgdGhlIHR3byBncm91cHMgYXJlbid0IHNpbWlsYXIsIHdlIGNhbiBwZXJmb3JtIGEgW1dlbGNoJ3MgJHQkLXRlc3RdKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5kYXRhc2NpZW5jZWNlbnRyYWwuY29tL3dlbGNocy10ZXN0LWZvci11bmVxdWFsLXZhcmlhbmNlcy8pe3RhcmdldD0iX2JsYW5rIn0gKGFsc28gY2FsbGVkIHRoZSB1bmVxdWFsIHZhcmlhbmNlICR0JC10ZXN0KSB0byBhY2NvdW50IGZvciBkaWZmZXJlbmNlIHZhcmlhbmNlcy4gIFRoaXMgbW9kaWZpZXMgdGhlIGRlbm9taW5hdG9yIG9mIHRoZSB0ZXN0IHN0YXRpc3RpYyB0byBubyBsb25nZXIgdXNlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gZnJvbSBwb29saW5nIHRoZSBzYW1wbGVzLiAgV2UgY2FuIGRvIHRoaXMgaW4gYFJgIGJ5IHVzaW5nIHRoZSBgdmFyLmVxdWFsID0gRkFMU0VgIG9wdGlvbiBpbiB0aGUgYHQudGVzdCgpYCBmdW5jdGlvbi4NCjMpIElmIHRoZSBkYXRhIGluIHRoZSB0d28gc2FtcGxlcyBhcmUgbm90IGluZGVwZW5kZW50IGFuZCBhcmUgaW5zdGVhZCB3aGF0IHdlIGNhbGwgcGFpcmVkLCB3ZSBzaG91bGQgdXNlIHRoZSBbUGFpcmVkICR0JC10ZXN0XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1NTc5NDY1Lyl7dGFyZ2V0PSJfYmxhbmsifS4NCjQpIElmIHRoZSBkYXRhIHdpdGhpbiBhIGdyb3VwIGlzIG5vdCBpbmRlcGVuZGVudCwgd2Ugd291bGQgbmVlZCB0byBjb25zaWRlciBtZXRob2RzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBjYXNlIHN0dWR5Lg0KDQpBcmUgdGhlc2UgYXNzdW1wdGlvbnMgbWV0IGluIG91ciBjYXNlPyAgV2UndmUgYWxyZWFkeSBzZWVuLCB1c2luZyBRLVEgcGxvdHMsIHRoYXQgdGhlc2UgQk1JIGVzdGltYXRlcyBtYXkgbm90IGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAgV2UgaGF2ZW4ndCBjaGVja2VkIHRoZSBhc3N1bXB0aW9uIG9mIGVxdWFsIHZhcmlhbmNlIHlldC4gIEJ1dCBpdCBzZWVtcyBjbGVhciB0aGF0IHRoZSBsYXN0IGFzc3VtcHRpb24gKG9mIGluZGVwZW5kZW50IHNhbXBsZXMpIGlzIHZpb2xhdGVkIGluIG91ciBjYXNlISAgVGhlIEJNSSBkYXRhIGZvciBydXJhbCBhcmVhcyBjb21lcyBmcm9tIHRoZSBzYW1lIGNvdW50cmllcyBhbmQgdGhlIEJNSSBkYXRhIGZvciB1cmJhbiBhcmVhcywgbWVhbmluZyB0aGUgc2FtcGxlcyBhcmUgbm90IGluZGVwZW5kZW50IGFuZCBhcmUgaW5zdGVhZCB3aGF0IHdlIGNhbGwgW3BhaXJlZF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNTU3OTQ2NS8pe3RhcmdldD0iX2JsYW5rIn0uIFdlIG5lZWQgdG8gYWNjb3VudCBmb3IgdGhpcyBiZWNhdXNlIHBhaXJlZCB2YWx1ZXMgbWF5IGJlIG1vcmUgc2ltaWxhciB0byBvbmUgYW5vdGhlciBiZWNhdXNlIHRoZXkgY29tZSBmcm9tIHRoZSBzYW1lIGNvdW50cnkuIE5vdGUgdGhhdCB0aGlzIHdvdWxkIGFsc28gYmUgdHJ1ZSBmb3IgdGhlIG1hbGUgYW5kIGZlbWFsZSBtZWFzdXJlbWVudHMgZnJvbSB0aGUgc2FtZSBjb3VudHJ5IG9yIHRoZSB2YWx1ZXMgaW4gdGhlIHNhbWUgY291bnRyaWVzIGZyb20gMTk4NSBhbmQgbGF0ZXIgaW4gMjAxNy4gDQoNCiMjIyAqKlBhaXJlZCAkdCQtdGVzdCoqDQoqKioNCg0KV2hlbiB3ZSBwZXJmb3JtIGEgcGFpcmVkICR0JC10ZXN0LCBpbnN0ZWFkIG9mIGxvb2tpbmcgYXQgdGhlIG1lYW5zIG9mIHRoZSB0d28gc2FtcGxlcyBzZXBhcmF0ZWx5LCB3ZSBjb21iaW5lIG9ic2VydmF0aW9ucyBpbiBhIHBhaXIgKGZyb20gdGhlIHNhbWUgY291bnRyeSBpbiBvdXIgY2FzZSkgdG8gY2FsY3VsYXRlIGEgZGlmZmVyZW5jZSBpbiB2YWx1ZXMgYmV0d2VlbiB0aGUgdHdvIHNhbXBsZXMuICBGb3IgZXhhbXBsZSwgd2UgYXJlIHJlYWxseSB0ZXN0aW5nIHRoZSBgYm1pX2RpZmZgIGNvbHVtbiBpbiB0aGUgZGF0YSBiZWxvdzoNCg0KYGBge3J9DQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihZZWFyID09IDE5ODUsIFNleCA9PSAiV29tZW4iLCBSZWdpb24gIT0gIk5hdGlvbmFsIikgJT4lIA0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXplKGJtaV9ydXJhbCA9IEJNSVtSZWdpb24gPT0gIlJ1cmFsIl0sDQogICAgICAgICAgICBibWlfdXJiYW4gPSBCTUlbUmVnaW9uID09ICJVcmJhbiJdLA0KICAgICAgICAgICAgYm1pX2RpZmYgPSBibWlfcnVyYWwgLSBibWlfdXJiYW4pIA0KYGBgDQoNClRoaXMgbWVhbnMgb3VyIGh5cG90aGVzZXMgYXJlIHNsaWdodGx5IGRpZmZlcmVudCBmcm9tIHRoZSB0d28tc2FtcGxlICR0JC10ZXN0LiBJbiB0aGlzIGNhc2Ugd2UgYXJlIHRlc3RpbmcgdGhlIGRpZmZlcmVuY2VzIGFtb25nIHRoZSBwYWlycyBvZiBvYnNlcnZhdGlvbnMgYW5kIGhvdyBjbG9zZSB0aGVzZSBkaWZmZXJlbmNlcyBhcmUgdG8gemVyby4gKEEgbWVhbiBkaWZmZXJlbmNlIG9mIHplcm8gd291bGQgbWVhbiB0aGUgQk1JIGVzdGltYXRlcyBhcmUgdGhlIHNhbWUgaW4gdGhlIHJ1cmFsIGFuZCB1cmJhbiBhcmVhcy4pIElmIHdlIGxldCAkXG11X2QkIHJlcHJlc2VudCB0aGUgdHJ1ZSBtZWFuIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcGFpcmVkIG9ic2VydmF0aW9ucyBvZiB0aGUgdHdvIGdyb3VwcywgdGhlbiBvdXIgbnVsbCBoeXBvdGhlc2lzIGlzIHRoYXQgdGhlIG1lYW4gb2YgdGhlIGRpZmZlcmVuY2VzIGlzIGVxdWFsIHRvIHplcm86DQokJA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhfMCA6IFxtdV9kID0gMA0KJCQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCkFuZCB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBpcyB0aGF0IHRoZSBtZWFuIG9mIHRoZSBkaWZmZXJlbmNlcyBpcyBub3QgZXF1YWwgdG8gemVybzoNCiQkDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSF9hIDogXG11X2QgXG5lcSAwDQokJCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCldlIGNhbiBwZXJmb3JtIGEgcGFpcmVkICR0JC10ZXN0IGJ5IGFkZGluZyB0aGUgb3B0aW9uIGBwYWlyZWQgPSBUUlVFYCB0byB0aGUgYHQudGVzdCgpYCBjb21tYW5kLiAgTm90ZSB0aGF0IHdlIGRvbid0IG5lZWQgdG8gc3BlY2lmeSBhbnl0aGluZyBhYm91dCBlcXVhbGl0eSBvZiB2YXJpYW5jZXMgbm93LCBiZWNhdXNlIHRoZXJlIGlzIG9ubHkgb25lIHZhcmlhbmNlLCB0aGUgdmFyaWFuY2Ugb2YgdGhlIHBhaXJlZCBkaWZmZXJlbmNlcyENCmBgYHtyfQ0KdC50ZXN0KHB1bGwoZmlsdGVyKEJNSV9sb25nLCBTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgWWVhciA9PSAiMTk4NSIsIA0KICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSwgQk1JKSwgDQogICAgICAgcHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICBZZWFyID09ICIxOTg1IiwgDQogICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJVcmJhbiIpLCBCTUkpLCANCiAgICAgICBwYWlyZWQgPSBUUlVFKQ0KIyBtZWFucyBhcmUgZGlmZmVyZW50IC0gcCB2YWx1ZSA8LjA1IHJlamVjdCB0aGUgbnVsbDogbm8gZGlmZmVyZW5jZSBpbiB0aGUgbWVhbnMNCmBgYA0KDQojIyMjIHsucXVlc3Rpb25fYmxvY2t9DQo8dT5RdWVzdGlvbiBvcHBvcnR1bml0eTo8L3U+IA0KTG9va2luZyBhdCB0aGUgdCB2YWx1ZSwgd2FzIGdsb2JhbCBCTUkgbG93ZXIgaW4gUnVyYWwgb3IgVXJiYW4gYXJlYXMgaW4gMTk4NT8NCg0KIyMjIw0KDQpMZXQncyBjb21lIGJhY2sgdG8gdGhlIG90aGVyICR0JC10ZXN0IGFzc3VtcHRpb25zIG5vdy4gIFdpdGggYSBwYWlyZWQgJHQkLXRlc3QsIHdlIG5vIGxvbmdlciBuZWVkIHRvIGFzc2VzcyB0aGUgYXNzdW1wdGlvbiBhYm91dCBlcXVhbCB2YXJpYW5jZSwgc2luY2Ugd2UgYXJlIHJlYWxseSB0ZXN0aW5nIHRoZSBtZWFuIG9mIG9uZSBzYW1wbGUgKG9mIHBhaXJlZCBkaWZmZXJlbmNlcykgcmF0aGVyIHRoYW4gY29tcGFyaW5nIHR3byBzYW1wbGVzLiAgSG93ZXZlciwgd2Ugc3RpbGwgaGF2ZSB0byBjaGVjayBub3JtYWxpdHkgYW5kIGluZGVwZW5kZW5jZSBvZiBvYnNlcnZhdGlvbnMuICBXZSBjYW4gcmVwaHJhc2UgdGhlc2Ugbm93IHRvIGJlIGFib3V0IHRoZSBwYWlyZWQgZGlmZmVyZW5jZXMgcmF0aGVyIHRoYW4gdGhlIHR3byBzZXBhcmF0ZSBzYW1wbGVzOg0KDQoxKSBUaGUgcGFpcmVkIGRpZmZlcmVuY2VzIGFyZSBbbm9ybWFsbHkgZGlzdHJpYnV0ZWRdKGh0dHA6Ly9vbmxpbmVzdGF0Ym9vay5jb20vMi9pbnRyb2R1Y3Rpb24vZGlzdHJpYnV0aW9ucy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9Lg0KMikgVGhlIHBhaXJlZCBkaWZmZXJlbmNlcyBhcmUgW2luZGVwZW5kZW50XShodHRwczovL3d3dy5zdGF0LmNtdS5lZHUvfmNzaGFsaXppLzM2LTIyMC9sZWN0dXJlLTUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IChtZWFuaW5nIHRoYXQgb2JzZXJ2YXRpb25zIGRvIG5vdCBpbmZsdWVuY2UgZWFjaCBvdGhlcikuDQoNCklmIHdlIGFzc3VtZSB0aGF0IG1lYXN1cmVtZW50cyBiZXR3ZWVuIGRpZmZlcmVudCBjb3VudHJpZXMgYXJlIGluZGVwZW5kZW50LCB0aGlzIHNlY29uZCBhc3N1bXB0aW9uIGlzIG5vdCB2aW9sYXRlZC4gIFRoZXJlZm9yZSwgd2Ugb25seSBub3cgbmVlZCB0byBjaGVjayBub3JtYWxpdHkgb2YgdGhlc2UgcGFpcmVkIGRpZmZlcmVuY2VzLg0KDQpXZSBjYW4gc3RhcnQgYnkgbWFraW5nIGEgUS1RIHBsb3Qgb2YgdGhlIHBhaXJlZCBkaWZmZXJlbmNlcy4NCmBgYHtyfQ0KQk1JX2xvbmcgJT4lIA0KICBmaWx0ZXIoWWVhciA9PSAxOTg1LCBTZXggPT0gIldvbWVuIiwgUmVnaW9uICE9ICJOYXRpb25hbCIpICU+JSANCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIHN1bW1hcml6ZShibWlfcnVyYWwgPSBCTUlbUmVnaW9uID09ICJSdXJhbCJdLA0KICAgICAgICAgICAgYm1pX3VyYmFuID0gQk1JW1JlZ2lvbiA9PSAiVXJiYW4iXSwNCiAgICAgICAgICAgIGJtaV9kaWZmID0gYm1pX3J1cmFsIC0gYm1pX3VyYmFuKSAgJT4lDQogIGdncGxvdChhZXMoc2FtcGxlID0gYm1pX2RpZmYpKSArDQogIHN0YXRfcXEoKSArDQogIHN0YXRfcXFfbGluZSgpDQpgYGANCg0KV2Ugc2VlIHRoYXQgdGhlc2UgcGFpcmVkIGRpZmZlcmVuY2VzIGRvIG5vdCBhcHBlYXIgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYmVjYXVzZSB0aGUgcG9pbnRzIGRvIG5vdCBsaWUgb24gdGhlIHN0cmFpZ2h0IGxpbmUuICBXZSBjYW4gYWxzbyBzZWUgdGhpcyBieSBtYWtpbmcgYSBoaXN0b2dyYW0gb2YgdGhlc2UgcGFpcmVkIGRpZmZlcmVuY2VzOg0KYGBge3J9DQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihZZWFyID09IDE5ODUsIFNleCA9PSAiV29tZW4iLCBSZWdpb24gIT0gIk5hdGlvbmFsIikgJT4lIA0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXplKGJtaV9ydXJhbCA9IEJNSVtSZWdpb24gPT0gIlJ1cmFsIl0sDQogICAgICAgICAgICBibWlfdXJiYW4gPSBCTUlbUmVnaW9uID09ICJVcmJhbiJdLA0KICAgICAgICAgICAgYm1pX2RpZmYgPSBibWlfcnVyYWwgLSBibWlfdXJiYW4pICAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gYm1pX2RpZmYpKSArDQogIGdlb21faGlzdG9ncmFtKCkNCmBgYA0KDQpUaGlzIGhpc3RvZ3JhbSBhcHBlYXJzIHRvIHNob3cgc2hvdyBhIGJpbW9kYWwgZGlzdHJpYnV0aW9uLCBtZWFuaW5nIHR3byBncm91cHMgb2YgY291bnRyaWVzIC0tIG1vc3QgY291bnRyaWVzIGhhdmUgYSBydXJhbCBCTUkgZXN0aW1hdGVzIGxvd2VyIHRoYW4gdXJiYW4gQk1JIGVzdGltYXRlcyAoYGJtaV9kaWZmYCA8IDApLCB3aGlsZSBhIHNtYWxsZXIgZ3JvdXAgb2YgY291bnRyaWVzIGhhdmUgdXJiYW4gQk1JIGVzdGltYXRlcyBoaWdoZXIgdGhhbiB1cmJhbiBCTUkgZXN0aW1hdGVzIChgYm1pX2RpZmZgID4wKS4NCg0KRmlyc3Qgd2Ugd2lsbCB0cnkgdHJhbnNmb3JtIG91ciBkYXRhIHRvIG1ha2UgaXQgbW9yZSBub3JtYWxseSBkaXN0cmlidXRlZC4gT25lIHdheSB0byBkbyB0aGlzIGlzIHRvIHRha2UgdGhlIGxvZ2FyaXRobSBvZiB0aGUgZGF0YSB2YWx1ZXMuIExvZ2FyaXRobXMgbWFrZSBleHRyZW1lIHZhbHVlcyBsZXNzIGV4dHJlbWUsIHNvIGNhbiBiZSBoZWxwZnVsIHdoZW4gZGF0YSBoYXMgYSByaWdodCBza2V3Lg0KDQpgYGB7cn0NCiMgY3JlYXRlIGEgbmV3IGRhdGFzZXQgd2l0aCB0aGUgcGFpcmVkIGRpZmZlcmVuY2VzDQojIGNyZWF0ZSBhIGxvZyB2ZXJzaW9uIG9mIHRoZSBkaWZmZXJlbmNlIHZhcmlhYmxlIHVzaW5nIG11dGF0ZSgpDQpCTUlfZGlmZl9sb2cgPC0gQk1JX2xvbmcgJT4lDQogIGZpbHRlcihZZWFyID09IDE5ODUsIFNleCA9PSAiV29tZW4iLCBSZWdpb24gIT0gIk5hdGlvbmFsIikgJT4lIA0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXplKGJtaV9ydXJhbCA9IEJNSVtSZWdpb24gPT0gIlJ1cmFsIl0sDQogICAgICAgICAgICBibWlfdXJiYW4gPSBCTUlbUmVnaW9uID09ICJVcmJhbiJdLA0KICAgICAgICAgICAgYm1pX2RpZmYgPSBibWlfcnVyYWwgLSBibWlfdXJiYW4pICU+JSAgDQogIG11dGF0ZShsb2dfZGlmZj1sb2coYm1pX2RpZmYpKQ0KYGBgDQoNCk5vdGljZSB0aGUgd2FybmluZyBtZXNzYWdlIHRoYXQgYEluIGxvZyhibWlfZGlmZikgOiAgTmFOcyBwcm9kdWNlZGAuICBUaGlzIGlzIGJlY2F1c2Ugc29tZSBvZiBvdXIgZGlmZmVyZW5jZXMgd2VyZSBsZXNzIHRoYW4gemVybywgYW5kIHRoZSBsb2dhcml0aG0gb2YgYSBuZWdhdGl2ZSBudW1iZXIgaXMgbm90IGRlZmluZWQuICBXZSBjYW4gZml4IHRoaXMgaXNzdWUgYnkgYWRkaW5nIHRoZSBzYW1lIHBvc2l0aXZlIG51bWJlciwgc2F5IDUsIHRvIGVhY2ggZGlmZmVyZW5jZSBiZWZvcmUgdGFraW5nIHRoZSBsb2dhcml0aG06DQpgYGB7cn0NCkJNSV9kaWZmX2xvZyA8LSBCTUlfbG9uZyAlPiUNCiAgZmlsdGVyKFllYXIgPT0gMTk4NSwgU2V4ID09ICJXb21lbiIsIFJlZ2lvbiAhPSAiTmF0aW9uYWwiKSAlPiUgDQogIGdyb3VwX2J5KENvdW50cnkpICU+JQ0KICBzdW1tYXJpemUoYm1pX3J1cmFsID0gQk1JW1JlZ2lvbiA9PSAiUnVyYWwiXSwNCiAgICAgICAgICAgIGJtaV91cmJhbiA9IEJNSVtSZWdpb24gPT0gIlVyYmFuIl0sDQogICAgICAgICAgICBibWlfZGlmZiA9IGJtaV9ydXJhbCAtIGJtaV91cmJhbikgJT4lICANCiAgbXV0YXRlKGxvZ19kaWZmPWxvZyhibWlfZGlmZiArIDUpKQ0KYGBgDQoNCkxldCdzIGxvb2sgYXQgYSBRLVEgcGxvdCBhbmQgaGlzdG9ncmFtIG9mIHRoaXMgdHJhbnNmb3JtZWQgZGF0YToNCmBgYHtyfQ0KQk1JX2RpZmZfbG9nICU+JSANCiAgZ2dwbG90KGFlcyhzYW1wbGUgPSBsb2dfZGlmZikpICsNCiAgc3RhdF9xcSgpICsNCiAgc3RhdF9xcV9saW5lKCkNCg0KQk1JX2RpZmZfbG9nICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nX2RpZmYpKSArDQogIGdlb21faGlzdG9ncmFtKCkNCmBgYA0KDQpUaGUgdHJhbnNmb3JtZWQgZGF0YSBzdGlsbCBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIHRoZSBiaW1vZGFsIG5hdHVyZSBvZiB0aGUgZGlzdHJpYnV0aW9uIGlzIHN0aWxsIGFwcGFyZW50IGluIHRoZSBoaXN0b2dyYW0uICBXaGF0IHNob3VsZCB3ZSBkbz8gIFRoZSAkdCQtdGVzdCBpcyBmYWlybHkgKipyb2J1c3QqKiB0byB2aW9sYXRpb25zIG9mIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBpZiB0aGUgc2FtcGxlIHNpemUgaXMgcmVsYXRpdmVseSBsYXJnZSwgZHVlIHRvIHdoYXQgaXMgY2FsbGVkIHRoZSBbY2VudHJhbCBsaW1pdCB0aGVvcmVtXShodHRwczovL3d3dy5hbmFseXRpY3N2aWRoeWEuY29tL2Jsb2cvMjAxOS8wNS9zdGF0aXN0aWNzLTEwMS1pbnRyb2R1Y3Rpb24tY2VudHJhbC1saW1pdC10aGVvcmVtLyl7dGFyZ2V0PSJfYmxhbmsifSwgd2hpY2ggc3RhdGVzIHRoYXQgYXMgc2FtcGxlcyBnZXQgbGFyZ2VyLCB0aGUgc2FtcGxlIG1lYW4gaGFzIGFuIGFwcHJveGltYXRlIG5vcm1hbCBkaXN0cmlidXRpb24uICANCg0KV2l0aCBvdXIgc2FtcGxlIHNpemUgb2YgMjAwLCB3ZSBjb3VsZCBjb25zaWRlciB1c2luZyB0aGUgcGFpcmVkICR0JC10ZXN0IGV2ZW4gd2l0aCBvdXIgbm9uLW5vcm1hbCBkYXRhLiAgSG93ZXZlciwgaWYgd2UgaGFkIGEgc21hbGxlciBzYW1wbGUsIHdlIG1pZ2h0IHN0aWxsIG5lZWQgdG8gY29uc2lkZXIgYW5vdGhlciBhbHRlcm5hdGl2ZSBzaW5jZSB0aGUgbG9nLXRyYW5zZm9ybWVkIGRhdGEgc3RpbGwgZG9lc24ndCBsb29rIG5vcm1hbC4gIFdlIG1pZ2h0IHdhbnQgdG8gY29uc2lkZXIgYSBub24tcGFyYW1ldHJpYyB0ZXN0IGluIHRoYXQgY2FzZS4NCg0KIyMjICoqTm9ucGFyYW1ldHJpYyB0ZXN0cyoqDQoqKioNCg0KV2hlbiB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gb2YgYSBwYWlyZWQgJHQkLXRlc3QgaXMgdmlvbGF0ZWQsIHdlIGNhbiBjb25zaWRlciBhIG5vbnBhcmFtZXRyaWMgYWx0ZXJuYXRpdmUgaW5zdGVhZC4gIFdoYXQncyBhIG5vbi1wYXJhbWV0cmljIHRlc3Q/ICA8Yj5QYXJhbWV0cmljIHRlc3RzPC9iPiBhcmUgYmFzZWQgb24gYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdW5kZXJseWluZyBkYXRhLCB3aGlsZSA8Yj5ub25wYXJhbWV0cmljIHRlc3RzPC9iPiBkbyBub3QgcmVseSBvbiB0aGVzZSBkaXN0cmlidXRpb25hbCBhc3N1bXB0aW9ucy4gDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly93d3cubWF5by5lZHUvcmVzZWFyY2gvZG9jdW1lbnRzL3BhcmFtZXRyaWMtYW5kLW5vbnBhcmFtZXRyaWMtZGVteXN0aWZ5aW5nLXRoZS10ZXJtcy9kb2MtMjA0MDg5NjApe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGVzZSB0d28gY2xhc3NlcyBvZiB0ZXN0cy4NCg0KVGhlIG5vbnBhcmFtZXRyaWMgYWx0ZXJuYXRpdmUgdG8gdGhlIHBhaXJlZCAkdCQtdGVzdCBpcyBjYWxsZWQgdGhlIFtXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0XShodHRwOi8vd3d3LmJpb3N0YXRoYW5kYm9vay5jb20vd2lsY294b25zaWduZWRyYW5rLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uICBUaGlzIHRlc3QgZG9lc24ndCByZXF1aXJlIHRoZSBzYW1lIG5vcm1hbGl0eSBhc3N1bXB0aW9uIGFzIHRoZSAkdCQtdGVzdCwgc28gY2FuIGJlIGNvbnNpZGVyZWQgd2l0aCB0aGUgZGF0YSBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIHBhcnRpY3VsYXJseSB3aGVuIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpcyBsb3cuIFRoZSBudWxsIGh5cG90aGVzaXMgZm9yIHRoaXMgdGVzdCBpcyB0aGF0IHRoZSAqKm1lZGlhbioqIG9mIHRoZSBwYWlyZWQgZGlmZmVyZW5jZXMgaXMgMC4NCg0KVG8gZG8gYSBXaWxjb3hvbiB0ZXN0LCB3ZSB1c2UgdGhlIGB3aWxjb3gudGVzdCgpYCBmdW5jdGlvbjoNCmBgYHtyfQ0Kd2lsY294LnRlc3QocHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgIFllYXIgPT0gIjE5ODUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSwgQk1JKSwNCiAgICAgICAgICAgcHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgWWVhciA9PSAiMTk4NSIsIA0KICAgICAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIlVyYmFuIiksIEJNSSksDQogICAgICAgICAgIHBhaXJlZCA9IFRSVUUpDQpgYGANCg0KV2l0aCB0aGlzIG5vbnBhcmFtZXRyaWMgdGVzdCwgd2Ugc3RpbGwgc2VlIGEgc21hbGwgcC12YWx1ZSwgaW5kaWNhdGluZyB0aGF0IEJNSSBlc3RpbWF0ZXMgYmV0d2VlbiB1cmJhbiBhbmQgcnVyYWwgYXJlYXMgYXJlIGRpZmZlcmVudC4gV2Ugd291bGQgY29uY2x1ZGUgdGhhdCB0aGVyZSdzIGEgZGlmZmVyZW5jZSBpbiB0aGUgbWVkaWFuIEJNSSBlc3RpbWF0ZXMgYmV0d2VlbiB0aGVzZSB0eXBlcyBvZiByZWdpb25zLg0KDQojIyMgKipNdWx0aXBsZSBUZXN0aW5nIENvcnJlY3Rpb24qKg0KKioqDQoNCkxldCdzIHRha2UgYSBsb29rIGJhY2sgYXQgdGhlIHF1ZXN0aW9ucyB3ZSB3YW50ZWQgdG8gYW5zd2VyOg0KMSkgSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIEJNSSBlc3RpbWF0ZXMgYXJvdW5kIHRoZSB3b3JsZD8gSW4gcGFydGljdWxhciwgaG93IGRvZXMgdGhpcyBsb29rIGZvciBmZW1hbGVzPw0KMikgSG93IGhhdmUgQk1JIGVzdGltYXRlcyBjaGFuZ2VkIGZyb20gMTk4NSB0byAyMDE3PyBBZ2FpbiwgSW4gcGFydGljdWxhciwgaG93IGRvZXMgdGhpcyBsb29rIGZvciBmZW1hbGVzPw0KMykgSG93IGRvIGRpZmZlcmVudCBjb3VudHJpZXMgY29tcGFyZSBmb3IgQk1JIGVzdGltYXRlcz8gSW4gcGFydGljdWxhciwgaG93IGRvZXMgdGhlIFVuaXRlZCBTdGF0ZXMgY29tcGFyZSB0byB0aGUgcmVzdCBvZiB0aGUgd29ybGQ/DQoNCkl0J3MgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB1bHRpbWF0ZWx5IHdlIHdhbnRlZCB0byB0ZXN0IDQgZGlmZmVyZW50IHRlc3RzIHRvIGFuc3dlciBxdWVzdGlvbiAxIGFuZCAyOg0KDQoxKSBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gbWVhbiBCTUkgZm9yIHdvbWVuIGJldHdlZW4gUnVyYWwgYW5kIFVyYmFuIGFyZWFzIGluIDE5ODU/DQoyKSBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gbWVhbiBCTUkgZm9yIHdvbWVuIGJldHdlZW4gUnVyYWwgYW5kIFVyYmFuIGFyZWFzIGluIDIwMTc/DQozKSBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gbWVhbiBCTUkgZm9yIHdvbWVuIGJldHdlZW4gMTk4NSBhbmQgMjAxNyBpbiBSdXJhbCBhcmVhcz8NCjQpIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBpbiBtZWFuIEJNSSBmb3Igd29tZW4gYmV0d2VlbiAxOTg1IGFuZCAyMDE3IGluIFVyYmFuIGFyZWFzPw0KDQpTbyBmYXIgd2UgaGF2ZSBvbmx5IGxvb2tlZCBhdCB0aGUgZmlyc3QgdGVzdC4gIExldCdzIHBlcmZvcm0gYWxsIDQgb2YgdGhlc2UgdGVzdHMgdXNpbmcgdGhlIG5vbi1wYXJhbWV0cmljIFdpbGNveG9uIHNpZ25lZC1yYW5rIHRlc3Q6DQpgYGB7cn0NCndpbGNveC50ZXN0KHB1bGwoZmlsdGVyKEJNSV9sb25nLCBTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBZZWFyID09ICIxOTg1IiwgDQogICAgICAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIlJ1cmFsIiksIEJNSSksDQogICAgICAgICAgIHB1bGwoZmlsdGVyKEJNSV9sb25nLCBTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgIFllYXIgPT0gIjE5ODUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJVcmJhbiIpLCBCTUkpLA0KICAgICAgICAgICBwYWlyZWQgPSBUUlVFKQ0KDQp3aWxjb3gudGVzdChwdWxsKGZpbHRlcihCTUlfbG9uZywgU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgWWVhciA9PSAiMjAxNyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpLCBCTUkpLA0KICAgICAgICAgICBwdWxsKGZpbHRlcihCTUlfbG9uZywgU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICBZZWFyID09ICIyMDE3IiwgDQogICAgICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiVXJiYW4iKSwgQk1JKSwNCiAgICAgICAgICAgcGFpcmVkID0gVFJVRSkNCg0Kd2lsY294LnRlc3QocHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgIFllYXIgPT0gIjE5ODUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSwgQk1JKSwNCiAgICAgICAgICAgcHVsbChmaWx0ZXIoQk1JX2xvbmcsIFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgWWVhciA9PSAiMjAxNyIsIA0KICAgICAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIlJ1cmFsIiksIEJNSSksDQogICAgICAgICAgIHBhaXJlZCA9IFRSVUUpDQoNCndpbGNveC50ZXN0KHB1bGwoZmlsdGVyKEJNSV9sb25nLCBTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBZZWFyID09ICIxOTg1IiwgDQogICAgICAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIlVyYmFuIiksIEJNSSksDQogICAgICAgICAgIHB1bGwoZmlsdGVyKEJNSV9sb25nLCBTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgIFllYXIgPT0gIjIwMTciLCANCiAgICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJVcmJhbiIpLCBCTUkpLA0KICAgICAgICAgICBwYWlyZWQgPSBUUlVFKQ0KYGBgDQpJbiBlYWNoIG9mIHRoZXNlIHRlc3RzLCB0aGUgdmVyeSBzbWFsbCBwLXZhbHVlIHRoYXQgd291bGQgbGVhZCB1cyB0byBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIHdlIGFyZSBjb21wYXJpbmcuICBIb3dldmVyLCB3aGVuIHdlIHRlc3QgbXVsdGlwbGUgcXVlc3Rpb25zIGxpa2UgdGhpcywgd2UgbmVlZCB0byBjb3JyZWN0IGZvciB0aGUgbXVsdGlwbGUgdGVzdHMgdGhhdCB3ZSBhcmUgcGVyZm9ybWluZy4NCg0KVGhlIG1vcmUgd2UgdGVzdCBkYXRhLCB0aGUgbW9yZSBsaWtlbHkgd2UgYXJlIHRvIHNlZSBhIHNpZ25pZmljYW50IGZpbmRpbmcganVzdCBieSBbcmFuZG9tIGNoYW5jZV0oaHR0cHM6Ly93d3cuc3RhdC5iZXJrZWxleS5lZHUvfm1nb2xkbWFuL1NlY3Rpb24wNDAyLnBkZil7dGFyZ2V0PSJfYmxhbmsifSBldmVuIHdoZW4gdGhlIGZpbmRpbmcgaXMgaW4gZmFjdCBub3QgcmVhbC4gSXQgYWxsIGNvbWVzIGRvd24gdG8gcHJvYmFiaWxpdHkuDQoNCiMjIyMgUHJvYmFiaWxpdHkNCg0KWW91IG1heSByZWNhbGwgdGhhdCBwcm9iYWJpbGl0eSBpcyB0aGUgY2hhbmNlIHRoYXQgYW4gZXZlbnQgd2lsbCBoYXBwZW4gYW5kIGl0IHJhbmdlcyBmcm9tIDAgdG8gMS4gDQpZb3UgY2FuIGFsc28gdGhpbmsgb2YgaXQgYXMgYSBwZXJjZW50YWdlIG9mIDAlIChwcm9iYWJpbGl0eSA9MCkgdG8gMTAwJSAocHJvYmFiaWxpdHkgPSAxKSBvZiBjaGFuY2UgdGhhdCBhbiBldmVudCB3aWxsIGhhcHBlbi4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPScxMDAlJ30NCmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2JhYmlsaXR5LWxpbmUuc3ZnIikpDQoNCmBgYA0KDQoNCg0KW1tzb3VyY2VdKGh0dHBzOi8vd3d3Lm1hdGhzaXNmdW4uY29tL2RhdGEvaW1hZ2VzL3Byb2JhYmlsaXR5LWxpbmUuc3ZnKXt0YXJnZXQ9Il9ibGFuayJ9XQ0KDQpJZiB3ZSBjb25zaWRlciBmbGlwcGluZyBhIGNvaW4sIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBldmVudCBoYXBwZW5pbmcgKGhlYWRzKSBpcyBhbHdheXMgZXF1YWwgdG8gIDEgKCBvciAxMDAlIGNoYW5jZSkgbWludXMgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBldmVudCBub3QgaGFwcGVuaW5nICh0YWlscyBpbnN0ZWFkIG9mIGhlYWRzKSBiZWNhdXNlIHRoZSB0b3RhbCBwcm9iYWJpbGl0eSBjYW4gbm90IGV4Y2VlZCAxMDAlLg0KDQogICAgICAgIHByb2JhYmlsaXR5IG9mIDEgKDEwMCUgY2hhbmNlKSA9IHRoZSBwcm9iYWJpbGl0eSBvZiBoZWFkcyAoNTAlIGNoYW5jZSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKyB0aGUgcHJvYmFiaWxpdHkgb2YgdGFpbHMgKDUwJSBjaGFuY2UpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxID0gUChoZWFkcykgKyBQKHRhaWxzKQ0KICAgICAgICAgICAgcHJvYmFiaWxpdHkgb2Ygb25lIG91dGNvbWUgPSAxIC0gdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBvdGhlciBvdXRjb21lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQKGhlYWRzKSA9IDEgLSBQKHRhaWxzKSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUCh0YWlscykgPSAxIC0gUChoZWFkcykNCg0KSWYgd2UgdGhpbmsgYWJvdXQgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgc2lnbmlmaWNhbnQgb3Igbm9uc2lnbmlmaWNhbnQgcmVzdWx0cyB3ZSBjYW4gdGhpbmsgb2YgaXQgbGlrZSB0aGlzOg0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSA9IFAoc2lnbmlmaWNhbnQgcmVzdWx0cykgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICArIFAobm8gc2lnbmlmaWNhbnQgcmVzdWx0cykNCiAgICAgICAgICAgICAgICBQKHNpZ25pZmljYW50IHJlc3VsdHMpID0gMSAtIFAobm8gc2lnbmlmaWNhbnQgcmVzdWx0cykNCg0KIyMjICoqQWxwaGEqKg0KKioqDQoNCkFscGhhIGlzIHRoZSBhbW91bnQgb2YgcmlzayB0aGF0IHlvdSBhcmUgd2lsbGluZyB0byBhY2NlcHQgdGhhdCBhIHN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0IGlzIGFjdHVhbGx5IGEgZmFsc2UgcG9zaXRpdmUsIGluIG90aGVyIHdvcmRzIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLiBUaGlzIGlzIGFsc28gY2FsbGVkIFt0eXBlIDEgZXJyb3JdKGh0dHBzOi8vd2ViLm1hLnV0ZXhhcy5lZHUvdXNlcnMvbWtzL3N0YXRtaXN0YWtlcy9lcnJvcnR5cGVzLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uIEEgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBvZiAuMDUgaXMgY3VzdG9tYXJ5IGFuZCBpdCBtZWFucyB0aGF0IHdlIGFyZSBhY2NlcHRpbmcgYSA1JSBjaGFuY2UgdGhhdCBvdXIgcmVzdWx0IGlzIGFjdHVhbGx5IGEgZmFsc2UgcG9zaXRpdmUuDQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gcHJvYmFiaWxpdHkgb2YgZmFsc2Ugc2lnbmlmaWNhbnQgcmVzdWx0cyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIM6xID0gUChNYWtpbmcgYSB0eXBlIDEgZXJyb3IpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KUmVtZW1iZXIgdGhhdCBpbiBvdXIgc3RhdGlzdGljYWwgdGVzdHMgaWYgdGhlIHAtdmFsdWUgPCBhbHBoYSBpdCBpcyBjb25zaWRlcmVkIHNpZ25pZmljYW50IGFuZCBpZiBwLXZhbHVlID4gYWxwaGEgaXQgaXMgY29uc2lkZXJlZCBub3Qgc2lnbmlmaWNhbnQNCg0KDQpCdXQgd2hhdCByZWFsbHkgaXMgdGhlIFtwLXZhbHVlXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vcC12YWx1ZXMtZXhwbGFpbmVkLWJ5LWRhdGEtc2NpZW50aXN0LWY0MGE3NDZjZmM4KXt0YXJnZXQ9Il9ibGFuayJ9Pw0KDQojIyMgKipwLXZhbHVlcyoqDQoqKioNCg0KVGhlIHAgaW4gcC12YWx1ZSBzdGFuZHMgZm9yIDxiPnByb2JhYmlsaXR5PC9iPiAtIHRoZSBwcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgYSB0ZXN0IHN0YXRpc3RpYyAoZm9yIGV4YW1wbGUgdGhlIHQgaW4gb3VyIHN0dWRlbnQgJHQkLXRlc3RzIGJhc2VkIG9uIHRoZSBtZWFucyBvZiBvdXIgZ3JvdXBzIG9mIGNvbXBhcmlzb24pIGFzIG9yIG1vcmUgZXh0cmVtZSB0aGFuIHdoYXQgd2Ugb2JzZXJ2ZSBpZiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUgKGNvbnRpbnVpbmcgd2l0aCB0aGUgdCB0ZXN0IGV4YW1wbGUsIHRoZSBkaXN0cmlidXRpb24gb2YgcG9zc2libGUgdCBzdGF0aXN0aWNzIGlmIHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMpLiBUaGVyZWZvcmUgYSBwLXZhbHVlIG9mIDAuMDIgbWVhbnMgdGhhdCB0aGVyZSBpcyBhIDIgcGVyY2VudCBjaGFuY2UgdGhhdCBvdXIgZGF0YSBtYXkgYWxzbyBsb29rIHRoZSB3YXkgaXQgZG9lcyBpZiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUuIFRoaXMgZG9lcyBub3QgbWVhbiB0aGVyZSBpcyBhIDIlIGNoYW5jZSB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gSXQgb25seSBtZWFucyB0aGF0IHRoZXJlIGlzIGEgMiUgY2hhbmNlIHdlIHdvdWxkIG9ic2VydmUgZGF0YSBhcyBleHRyZW1lIGFzIG91cnMgaWYgdGhlcmUgcmVhbGx5IGlzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZ3JvdXBzLiANCg0KU28gdGhlbiBpZiBhbHBoYSBpcyB0aGUgdGhyZXNob2xkIGZvciB0aGUgcC12YWx1ZSBmb3Igb2J0YWluaW5nIGZhbHNlIHNpZ25pZmljYW50IHJlc3VsdHMgdGhlbiB0aGUgcHJvYmFiaWxpdHkgb2Ygbm90IG1ha2luZyBpbmNvcnJlY3QgY29uY2x1c2lvbnMgaXMgMSAtIGFscGhhOg0KDQogICAgICAgICAgICAgICAgIFAoTm90IG1ha2luZyBhIHR5cGUgMSBlcnJvcikgPSAxIC0gzrENCg0KYGBge3J9DQojUChOb3QgbWFraW5nIGEgdHlwZSAxIGVycm9yKSA9IDEgLSDOsQ0KMS0uMDUgDQoNCmBgYA0KICANCk9LIHNvIGlmIHdlIHVzZSBhbiBhbHBoYSBvZiAuMDUgd2UgYXJlIGFjY2VwdGluZyB0aGF0IDk1JSBvZiB0aGUgdGltZSB3ZSB3aWxsIG5vdCBtYWtlIGEgVHlwZSAxIGVycm9yIGFuZCA1ICUgb2YgdGhlIHRpbWUgd2Ugd2lsbCBqdXN0IGJ5IHJhbmRvbSBjaGFuY2UuICAgICAgICAgICAgICANClRha2luZyB0aGlzIG9uZSBzdGVwIGZ1cnRoZXI6DQoNCiAgICAgICAgICAgICAgICAgICAgIFAoTWFraW5nIGFuIGVycm9yKSA9IDEgLSBQKE5vdCBtYWtpbmcgYW4gZXJyb3IpDQogICAgICAgICAgICAgICAgICAgICBQKE1ha2luZyBhbiBlcnJvcikgPSAxIC0gKDEgLSDOsSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCkhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoaXMgY2hlY2tzIG91dDogICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KYGBge3J9DQojUChNYWtpbmcgYW4gZXJyb3IpID0gMSAtICgxIC0gzrEpDQoxLSgxLS4wNSkNCmBgYA0KDQpTbyB3aGF0IGhhcHBlbnMgaWYgd2UgcGVyZm9ybSBtdWx0aXBsZSB0ZXN0cz8NCg0KVGhlIHByb2JhYmlsaXR5IG9mIG5vdCBtYWtpbmcgYSB0eXBlIDEgZXJyb3Igd291bGQgcmVtYWluIHRoZSBzYW1lIGZvciBlYWNoIHRlc3QuIFRoZXJlZm9yZSB3ZSBuZWVkIHRvIG11bHRpcGx5IHRoZSBwcm9iYWJpbGl0aWVzIHRvZ2V0aGVyIGVhY2ggdGltZSB0byBkZXRlcm1pbmUgdGhlIG92ZXIgYWxsIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhbiBlcnJvciBhY3Jvc3MgbXVsdGlwbGUgdGVzdHMuIFNlZSBbaGVyZV0oaHR0cDovL21hdGhmb3J1bS5vcmcvbGlicmFyeS9kcm1hdGgvdmlldy83NDA2NS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IGFib3V0IHdoeSB3ZSBtdWx0aXBseSBwcm9iYWJpbGl0aWVzIHRvZ2V0aGVyLg0KDQogICAgICAgIFAoTm90IG1ha2luZyBhbiBlcnJvciBpbiBtIHRlc3RzKSA9ICgxIC0gzrEpXm0NCiAgICBQKE1ha2luZyBhdCBsZWFzdCAxIGVycm9yIGluIG0gdGVzdHMpID0gMSAtICgxIC0gzrEpXm0NCg0KTGV0J3MgY29uc2lkZXIgaWYgd2UgcGVyZm9ybWVkIDEwIGRpZmZlcmVudCBzdGF0aXN0aWNhbCB0ZXN0cyBhbmQgaWYgd2UgYXMgdXN1YWwgY29uc2lkZXJlZCB0aGUgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBhbHBoYSBvZiAuMDU6DQoNClNvIHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIDEgc2lnbmlmaWNhbnQgcmVzdWx0IHdpdGggMTAgdGVzdHMgaXM6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYHtSfQ0KI1AoTWFraW5nIGF0IGxlYXN0IDEgZXJyb3IgaW4gMTAgdGVzdHMpID0gMSAtICgoMSAtIM6xKSgxIC0gzrEpKDEgLSDOsSkoMSAtIM6xKSgxIC0gzrEpKDEgLSDOsSkoMSAtIM6xKSgxIC0gzrEpKDEgLSDOsSkoMSAtIM6xKSkNCiN0aGlzIGlzIHRoZSBzYW1lIGFzOg0KI1AoTWFraW5nIGF0IGxlYXN0IDEgZXJyb3IgaW4gMTAgdGVzdHMpID0gMSAtICgxIC0gzrEpXjEwDQogMS0gKDEtLjA1KV4xMA0KYGBgDQoNClNvIHRoZXJlIGlzIGEgNDAlIGNoYW5jZSB0aGF0IHRoYXQgdGhlcmUgd2lsbCBiZSBhIHNpZ25pZmljYW50IGZpbmRpbmcgc2ltcGx5IGR1ZSB0byByYW5kb20gY2hhbmNlIGFsb25lLg0KDQpXaGF0IGFib3V0IDEwMCB0ZXN0cz8NCg0KYGBge1J9DQoNCiNQKE1ha2luZyBhdCBsZWFzdCAxIGVycm9yIGluIDEwMCB0ZXN0cykgPSAxIC0gKDEgLSDOsSleMTAwDQogMS0gKDEtLjA1KV4xMDANCmBgYA0KICAgICAgICAgICAgICAgICAgICAgICAgDQpZaWtlcyEhIFRoYXQgaXMgYWxtb3N0IGEgMTAwJSBjaGFuY2UgdGhhdCB0aGVyZSB3aWxsIGJlIGEgc2lnbmlmaWNhbnQgZmluZGluZyBzaW1wbHkgZHVlIHRvIGNoYW5jZSBhbG9uZSENCg0KTXVjaCBvZiB0aGlzIGV4cGxhbmF0aW9uIGlzIGRlc2NyaWJlZCBpbiB0aGlzIFtsZWN0dXJlXShodHRwczovL3d3dy5ncy53YXNoaW5ndG9uLmVkdS9hY2FkZW1pY3MvY291cnNlcy9ha2V5LzU2MDA4L2xlY3R1cmUvbGVjdHVyZTEwLnBkZil7dGFyZ2V0PSJfYmxhbmsifS4NCg0KDQpPbmUgd2F5IHRvIGNvcnJlY3QgZm9yIHRoaXMgaXMgbXVsdGlwbGUgdGVzdGluZyBpc3N1ZSBpcyB1c2luZyB0aGUgW0JvbmZlcnJvbmkgbWV0aG9kXShodHRwOi8vbWF0aHdvcmxkLndvbGZyYW0uY29tL0JvbmZlcnJvbmlDb3JyZWN0aW9uLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uIA0KDQpJbiB0aGlzIG1ldGhvZCB3ZSB3b3VsZCBkaXZpZGUgb3VyIHNpZ25pZmljYW5jZSB0aHJlc2hvbGQgKGdlbmVyYWxseSAwLjA1KSBieSB0aGUgbnVtYmVyIG9mIHRlc3RzLg0KDQpgYGB7cn0NCi4wNS80ICMgNCB0ZXN0cw0KYGBgDQoNCk91ciBuZXcgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBpcyBub3cgMC4wMTI1LiANClRodXMgb3VyIHAtdmFsdWVzIHNob3VsZCBiZSBsZXNzIHRoYW4gdGhpcyB2YWx1ZSBmb3IgdXMgdG8gcmVqZWN0IHRoZSBudWxsIHRoYXQgdGhlcmUgaXMgbm8gZGlmZmVyZW5jZSBpbiBtZWFucy4NCkluIG91ciA0IFdpbGNveG9uIHRlc3RzLCBvdXIgcC12YWx1ZXMgd2VyZSBsZXNzIHRoYW4gMC4wMTI1Lg0KU28gd2Ugc2VlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiB0aGUgbWVhbnMgb2YgdGhlIGdyb3VwcyBhZnRlciBtdWx0aXBsZSB0ZXN0aW5nIGNvcnJlY3Rpb24gZm9yIG91ciBmb3VyIGRpZmZlcmVudCB0ZXN0cy4NCg0KIyMgKipEYXRhIFZpc3VhbGl6YXRpb24qKg0KKioqDQoNCklmIHlvdSBoYXZlIGJlZW4gZm9sbG93aW5nIGFsb25nIGJ1dCBzdG9wcGVkLCB3ZSBjb3VsZCBsb2FkIG91ciB3cmFuZ2xlZCBkYXRhIGxpa2Ugc286DQoNCmBgYHtyfQ0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGEucmRhIikpDQpgYGANCg0KKioqDQo8ZGV0YWlscz4gPHN1bW1hcnk+IElmIHlvdSBza2lwcGVkIHRoZSBkYXRhIHdyYW5nbGluZyBzZWN0aW9uIGNsaWNrIGhlcmUuIDwvc3VtbWFyeT4NCg0KRmlyc3QgeW91IG5lZWQgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiT0NTZGF0YSIpDQpsaWJyYXJ5KE9DU2RhdGEpDQpgYGANCg0KVGhlbiwgeW91IG1heSBsb2FkIHRoZSB3cmFuZ2xlZCBkYXRhIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29kZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQp3cmFuZ2xlZF9yZGEoIm9jcy1icC1ydXJhbC1hbmQtdXJiYW4tb2Jlc2l0eSIsIG91dHBhdGggPSBnZXR3ZCgpKQ0KbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkNCmBgYA0KDQpJZiB0aGUgcGFja2FnZSBkb2VzIG5vdCB3b3JrIGZvciB5b3UsIGFsdGVybmF0aXZlbHksIGFuIFJEQSBmaWxlIChzdGFuZHMgZm9yIFIgZGF0YSkgb2YgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkvdHJlZS9tYXN0ZXIvZGF0YS93cmFuZ2xlZCkgb3Igc2xpZ2h0bHkgbW9yZSBkaXJlY3RseSBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtcnVyYWwtYW5kLXVyYmFuLW9iZXNpdHkvbWFzdGVyL2RhdGEvd3JhbmdsZWQvd3JhbmdsZWRfZGF0YS5yZGEpLiBEb3dubG9hZCB0aGlzIGZpbGUgYW5kIHRoZW4gcGxhY2UgaXQgaW4geW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IHdpdGhpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgIndyYW5nbGVkIiB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJkYXRhIiB0byBjb3B5IGFuZCBwYXN0ZSBvdXIgY29kZS4gV2UgdXNlZCBhbiBSU3R1ZGlvIHByb2plY3QgYW5kIHRoZSBbYGhlcmVgIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSkgdG8gbmF2aWdhdGUgdG8gdGhlIGZpbGUgbW9yZSBlYXNpbHkuDQoNCmBgYHtyfQ0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGEucmRhIikpDQpgYGANCg0KDQoqKioNCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgbW9yZSBhYm91dCBjcmVhdGluZyBuZXcgcHJvamVjdHMgaW4gUlN0dWRpby4gPC9zdW1tYXJ5Pg0KDQpZb3UgY2FuIGNyZWF0ZSBhIHByb2plY3QgYnkgZ29pbmcgdG8gdGhlIEZpbGUgbWVudSBvZiBSU3R1ZGlvIGxpa2Ugc286DQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk5ld19wcm9qZWN0LnBuZyIpKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyBkbyBzbyBieSBjbGlja2luZyB0aGUgcHJvamVjdCBidXR0b246DQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwcm9qZWN0X2J1dHRvbi5wbmciKSkNCmBgYA0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpIHRvIGxlYXJuIG1vcmUgYWJvdXQgdXNpbmcgUlN0dWRpbyBwcm9qZWN0cyBhbmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSkgdG8gbGVhcm4gbW9yZSBhYm91dCB0aGUgYGhlcmVgIHBhY2thZ2UuDQoNCjwvZGV0YWlscz4NCioqKg0KPC9kZXRhaWxzPg0KKioqDQoNCg0KQWdhaW4gd2Ugd2lsbCB1dGlsaXplIGBnZ3Bsb3QyYCB0byBjcmVhdGUgcGxvdHMgdG8gbG9vayBhdCB0aGUgZGlyZWN0aW9uYWwgYW5kIG1hZ25pdHVkZSBvZiB0aGUgZGlmZmVyZW5jZXMgaW4gQk1JIHdlIGFyZSBpbnRlcmVzdGVkIGluLiBJZiB5b3UgbmVlZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHBsZWFzZSBzZWUgdGhlIFtEYXRhIEV4cGxvcmF0aW9uIHNlY3Rpb25dKGh0dHBzOi8vd3d3Lm9wZW5jYXNlc3R1ZGllcy5vcmcvb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5LyNEaXN0cmlidXRpb24vIzp+OnRleHQ9Q2xpY2slMjBoZXJlJTIwZm9yJTIwYWRkaXRpb25hbCUyMGluZm9ybWF0aW9uJTIwb24lMjB1c2luZyUyMGdncGxvdDIuKXt0YXJnZXQ9Il9ibGFuayJ9IGVhcmxpZXIgaW4gdGhpcyBjYXNlIHN0dWR5LiBUaGUgdG9wIHR3byBsaW5lcyBvZiB0aGUgY29kZSBmb3IgdGhlIGZvbGxvd2luZyBwbG90cywgZmlsdGVyIHRoZSBkYXRhIHRvIG9ubHkgc3BlY2lmaWMgdmFsdWVzIG9mIGludGVyZXN0LiBUaGVuIHdlIGxheWVyIHdoYXQgaXMgY2FsbGVkIGEgaml0dGVyIG9uIHRvcCBvZiBhIGJveCBwbG90LiBBIGppdHRlciBpcyBlc3NlbnRpYWxseSBhIGRvdCBwbG90IGJ1dCB3aXRoIHNvbWUgdmFyaWF0aW9uIG9uIHRoZSBsb2NhdGlvbiBvZiB0aGUgcG9pbnRzIHNvIHRoYXQgdGhleSBkbyBub3QgbGluZSB1cCB2ZXJ0aWNhbGx5IHdoaWNoIGNhbiBtYWtlIHRoZSBpbmRpdmlkdWFsIHBvaW50cyBkaWZmaWN1bHQgdG8gc2VlLg0KDQpPdXIgZmlyc3QgbWFpbiBxdWVzdGlvbiB3YXM6IElzIHRoZXJlIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHJ1cmFsIGFuZCB1cmJhbiBCTUkgZXN0aW1hdGVzIGFyb3VuZCB0aGUgd29ybGQ/DQpMZXQncyBsb29rIGF0IHRoZSBuYXRpb25hbCBtZWFuIEJNSSBlc3RpbWF0ZXMgZm9yIGVhY2ggb2YgdGhlIHllYXJzOg0KYGBge3J9DQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICBZZWFyID09ICIxOTg1IiwgDQogICAgICAgICBSZWdpb24gIT0gIk5hdGlvbmFsIikgJT4lDQogIGdncGxvdChhZXMoeCA9IFJlZ2lvbiwgeSA9IEJNSSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4zKSANCiMgdGhlIHdpZHRoIGRldGVybWluZXMgaG93IHdpZGUgdGhlIHBvaW50cyBhcmUgcGxvdHRlZA0KDQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICBZZWFyID09ICIyMDE3IiwgDQogICAgICAgICBSZWdpb24gIT0gIk5hdGlvbmFsIikgJT4lDQogIGdncGxvdChhZXMoeCA9IFJlZ2lvbiwgeSA9IEJNSSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4zKQ0KDQpgYGANCg0KT3VyIDJuZCBtYWluIHF1ZXN0aW9uIHdhczogSG93IGhhdmUgQk1JIGVzdGltYXRlcyBjaGFuZ2VkIGZyb20gMTk4NSB0byAyMDE3Pw0KTGV0J3MgbG9vayBhdCB0aGUgY2hhbmdlIGluIHJ1cmFsIGFuZCB1cmJhbiBtZWFuIEJNSSBlc3RpbWF0ZXMgb3ZlciB0aW1lOg0KYGBge3J9DQojdGhpcyB0aW1lIHdlIHdpbGwgYWRkIHRpdGxlcyBmb3IgUnVyYWwgYW5kIFVyYmFuIHVzaW5nIGdndGl0bGUoKQ0KQk1JX2xvbmcgJT4lIA0KICBmaWx0ZXIoU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSAlPiUNCiAgIyBmaWx0ZXJpbmcgdGhpcyB3YXkgYWxsb3dzIHVzIHRvIGtlZXAgZGF0YSBmcm9tIGJvdGggeWVhcnMNCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IEJNSSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4zKSArDQogIGdndGl0bGUoIlJ1cmFsIikNCg0KQk1JX2xvbmcgJT4lIA0KICBmaWx0ZXIoU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgIFJlZ2lvbiA9PSAiVXJiYW4iKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IEJNSSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4zKSArDQogIGdndGl0bGUoIlVyYmFuIikNCg0KYGBgDQoNCkxldCdzIHB1dCB0aGUgcGxvdHMgdG9nZXRoZXIgdG8gc2VlIGhvdyB0aGUgY2hhbmdlIG92ZXIgdGhlIHllYXJzIGRpZmZlcnMgYmV0d2VlbiB0aGUgcmVnaW9ucy4NCldlIHdpbGwgdXNlIGFnYWluIHVzZSBgZmFjZXRfd3JhcCgpYCB0byBkbyB0aGlzOg0KDQpgYGB7cn0NCg0KQk1JX2xvbmcgJT4lIA0KICBmaWx0ZXIoU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgIFJlZ2lvbiAlaW4lIGMoIlJ1cmFsIiwgIlVyYmFuIikpICU+JQ0KICAjIGZpbHRlcmluZyB0aGlzIHdheSBhbGxvd3MgdXMgdG8ga2VlcCBkYXRhIGZyb20gYm90aCB5ZWFycw0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gQk1JKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjMpICsNCiAgZmFjZXRfd3JhcCh+IFJlZ2lvbikNCg0KIyB3ZSB3b3VsZCBhbHNvIGdldCBuYXRpb25hbCBkYXRhIHdpdGhvdXQgYFJlZ2lvbiAlaW4lIGMoIlJ1cmFsIiwgIlVyYmFuIikpYA0KDQpgYGANCkluZGVlZCB0aGUgY2hhbmdlIGluIEJNSSBvdmVyIHRpbWUgbG9va3MgYmlnZ2VyIGluIHRoZSBydXJhbCBhcmVhcyENCg0KDQpPbmUgdGhpcmQgbWFpbiBxdWVzdGlvbnMgd2FzOiBIb3cgZG8gdGhlIGRpZmZlcmVudCBjb3VudHJpZXMgY29tcGFyZT8gIE9yIGluIG90aGVyIHdvcmRzIHdoYXQgZG8gdGhlIGluZGl2aWR1YWwgZG90cyByZXByZXNlbnQgaW4gb3VyIGJveCBwbG90cz8NCldlIHdpbGwgdGFrZSBhIGxvb2sgdXNpbmcgYGdlb21fbGFiZWwoKWA6DQoNCmBgYHtyfQ0KDQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gQk1JKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjMpICsNCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBDb3VudHJ5KSkNCg0KYGBgDQoNCklmIHdlIGluY2x1ZGUgYWxsIGNvdW50cnkgbmFtZXMgdGhpcyBpcyBhIGJpdCB0b28gbXVjaC4uLiBzbyBwZXJoYXBzIHdlIHNob3VsZCBmb2N1cyBvbiBqdXN0IHRoZSBleHRyZW1lIEJNSSB2YWx1ZXMgdXNpbmcgYGZpbHRlcigpYCBmdW5jdGlvbiBvZiB0aGVgZHBseXJgIHBhY2thZ2UuIFdlIHdpbGwgYWxzbyB1c2UgdGhlIGBnZ3JlcGVsYCBwYWNrYWdlIHRvIGhhdmUgb3VyIGxhYmVscyBub3Qgb3ZlcmxhcCBlYWNoIG90aGVyLg0KDQpQYXJ0IG9mIHRoZSB0aGlyZCBxdWVzdGlvbiB3YXM6IEhvdyBkb2VzIHRoZSBVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EgY29tcGFyZT8gU28gbGV0J3MgbGFiZWwgdGhlIGRhdGEgcG9pbnRzIGZvciB0aGUgVW5pdGVkIHN0YXRlcy4NCg0KYGBge3J9DQoNCkJNSV9sb25nICU+JSANCiAgZmlsdGVyKFNleCA9PSAiV29tZW4iLCANCiAgICAgICAgIFllYXIgJWluJSBjKCIxOTg1IiwgIjIwMTciKSwgDQogICAgICAgICBSZWdpb24gPT0gIlJ1cmFsIikgJT4lDQogIGdncGxvdChhZXMoeCA9IFllYXIsIHkgPSBCTUkpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YT1CTUlfbG9uZyAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpICU+JQ0KICAgICAgICAgICAgICBmaWx0ZXIoQk1JPjMxIHwgQk1JPDE5IHwgQ291bnRyeSA9PSAiVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhIiksIA0KICAgICAgICAgICAgICBhZXMoeCA9WWVhciwgeSA9Qk1JKSwNCiAgICAgICAgICAgICAgd2lkdGggPSAuMDIpICsNCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9Qk1JX2xvbmcgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIoU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSAlPiUNCiAgICAgICAgICAgICAgZmlsdGVyKEJNST4zMSB8IEJNSTwxOSB8IENvdW50cnkgPT0gIlVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYSIpLCANCiAgICAgICAgICAgICAgYWVzKHggPVllYXIsIHkgPUJNSSwgbGFiZWwgPSBDb3VudHJ5KSkNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIA0KYGBgDQoNCkFuZCBsZXQncyBmaWxsIHRoZSBib3ggcGxvdHMgd2l0aCBjb2xvciBhbmQgb3V0bGluZSBpbiBibGFjazoNCmBgYHtyfQ0KDQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gQk1JKSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBjb2xvciA9ICJibGFjayIsIGFlcyhmaWxsID0gWWVhcikpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YT1CTUlfbG9uZyAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcihTZXggPT0gIldvbWVuIiwgDQogICAgICAgICAgICAgICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJSdXJhbCIpICU+JQ0KICAgICAgICAgICAgICBzdWJzZXQoQk1JPjMxIHwgQk1JPDE5IHwgQ291bnRyeSA9PSAiVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhIiksIA0KICAgICAgICAgICAgICBhZXMoeCA9WWVhciwgeSA9Qk1JKSwNCiAgICAgICAgICAgICAgd2lkdGggPSAuMDIpICsNCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9Qk1JX2xvbmcgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIoU2V4ID09ICJXb21lbiIsIA0KICAgICAgICAgICAgICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSAiUnVyYWwiKSAlPiUNCiAgICAgICAgICAgICAgc3Vic2V0KEJNST4zMSB8IEJNSTwxOSB8IENvdW50cnkgPT0gIlVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYSIpLCANCiAgICAgICAgICAgICAgYWVzKHggPVllYXIsIHkgPUJNSSwgbGFiZWwgPSBDb3VudHJ5KSkNCg0KYGBgDQoNCg0KIyMjICoqT3ZlcmFsbCBkaWZmZXJlbmNlcyoqDQoqKioNCg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgYWxsIHRoZSBkYXRhIHRvZ2V0aGVyLCBpbmNsdWRpbmcgdGhlIGRhdGEgZm9yIG1lbjoNCmBgYHtyfQ0KZ2dwbG90KEJNSV9sb25nLCBhZXMoeCA9IFllYXIsIHkgPSBCTUksIGNvbCA9IFJlZ2lvbikpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSwgY29sb3IgPSAiYmxhY2siICwgYWVzKGZpbGwgPSBSZWdpb24pKSArIA0KICBmYWNldF9ncmlkKH4gU2V4KSAgIA0KYGBgDQoNClRoYXQncyB1c2VmdWwsIGJ1dCBsZXQncyBsb29rIGF0IHRoZSBpbmRpdmlkdWFsIHBvaW50cyBhbmQgaW5jbHVkZSBvdXIgY291bnRyeSBsYWJlbHMsIHRvIGRvIHNvIGxldHMgY2hhbmdlIG91ciBVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EgbGFiZWwgdG8gVVNBOg0KDQpgYGB7cn0NCkJNSV9sb25nW1siQ291bnRyeSJdXSA8LUJNSV9sb25nW1siQ291bnRyeSJdXSAlPiUNCiAgc3RyX3JlcGxhY2UoIHBhdHRlcm4gPSAiVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhIiwgcmVwbGFjZW1lbnQgPSAiVVNBIikNCg0KDQpnZ3Bsb3QoQk1JX2xvbmcsIGFlcyh4ID0gWWVhciwgeSA9IEJNSSwgY29sID0gWWVhcikpICsgDQogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEsIGNvbG9yID0gImJsYWNrIiAsIGFlcyhmaWxsID0gU2V4KSkgKyANCiAgZmFjZXRfZ3JpZCh+IFNleCArIFJlZ2lvbikgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTMwLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9MSkgKyANCiAgIyBUaGlzIHdpbGwgYWRkIGEgaG9yaXpvbnRhbCBkYXNoZWQgbGluZSB0byBpbmRpY2F0ZSB0aGUgb2Jlc2l0eSBCTUkgdGhyZXNob2xkDQogIGdlb21faml0dGVyKGRhdGE9c3Vic2V0KEJNSV9sb25nKSwgDQogICAgICAgICAgICAgIGFlcyh4ID1ZZWFyLCB5ID1CTUkpLCANCiAgICAgICAgICAgICAgd2lkdGggPSAuMiwgc2l6ZSA9Miwgc2hhcGUgPTIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheSIpICsgDQogICMgVGhpcyB3aWxsIGFkZCB0aGUgaW5kaXZpZHVhbCBjb3VudHJ5IGRhdGEgcG9pbnRzDQogICMgVGhlIHNoYXBlIDIxIGFsbG93cyBmb3IgYSBkaWZmZXJlbnQgZmlsbCBhbmQgb3V0bGluZSBjb2xvcg0KICAjIFRoZSB3aWR0aCBkZXRlcm1pbmVzIGhvdyB3aWRlIHRoZSBqaXR0ZXIgcG9pbnRzIGFyZSBwbG90dGVkDQogIGdlb21faml0dGVyKGRhdGE9c3Vic2V0KEJNSV9sb25nLCBDb3VudHJ5ID09ICJVU0EiKSwgDQogICAgICAgICAgICAgIGFlcyh4ID1ZZWFyLCB5ID1CTUkpLCANCiAgICAgICAgICAgICAgd2lkdGggPSAuMDIsIHNpemUgPTEyLCBzaGFwZSA9MjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJncmF5IikgKyANCiAgIyBUaGlzIHdpbGwgYWRkIHBvaW50cyB0aGF0IGFyZSBsYXJnZXIgZm9yIHRoZSBVU0EgZGF0YQ0KICBnZW9tX3RleHQoZGF0YT1zdWJzZXQoQk1JX2xvbmcsICBDb3VudHJ5ID09ICJVU0EiKSwgDQogICAgICAgICAgICBhZXMoeCA9WWVhciwgeSA9Qk1JLGxhYmVsPUNvdW50cnkpLCANCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKyANCiAgIyBUaGlzIHdpbGwgYWRkIFVTQSBsYWJlbHMgdG8gdGhlIFVTQSBwb2ludHMNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCANCiAgIyBUaGlzIGlzIHVzZWZ1bCBmb3IgcmVtb3ZpbmcgdGhlIGxlZ2VuZA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsYW5nbGUgPSAzMCksIA0KICAgICAgICAjIHRoaXMgY2hhbmdlcyB0aGUgc2l6ZSBhbmQgYW5nbGUgb2YgdGhlIHggYXhpcyBwb2ludCBsYWJlbHMgDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9MTUpLCANCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPTE1KSwgDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArDQogICMgVGhpcyBjaGFuZ2VzIHRoZSBzaXplIG9mIHggYXhpcyBsYWJlbHMgZm9yIHRoZSBmYWNldCANCiAgICAgICAgZ2d0aXRsZSggIkRpZmZlcmVuY2VzIGluIEJNSSBPdmVyIFRpbWUgYW5kIEFjcm9zcyBSZWdpb24gVHlwZSBhbmQgR2VuZGVyIikgKw0KICAjIEFkZCBhICBwbG90IHRpdGxlDQogICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlIiwgIm9yY2hpZDIiKSkNCg0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSB0aGF0IG92ZXJhbGwgQk1JIGFwcGVhcnMgdG8gYmUgaW5jcmVhc2luZyBnbG9iYWxseSBvdmVyIHRpbWUuIEFkZGl0aW9uYWxseSB3ZSBjYW4gc2VlIHRoYXQgdGhpcyBpcyBvY2N1cnJpbmcgbm90IGp1c3QgaW4gdXJiYW4gYXJlYXMsIGJ1dCBhbHNvIGluIHJ1cmFsIGFyZWFzLiBUaGUgVVMgaXMgY29uc2lzdGVudGx5IGFib3ZlIHRoZSBtZWRpYW4gaW4gYWxsIHN0cmF0YSBvZiB0aGUgZGF0YS4gSW4gZ2VuZXJhbCwgdGhlIGZlbWFsZSBkYXRhIHNob3dzIGhpZ2hlciBCTUkgdmFsdWVzIHRoYW4gdGhlIG1hbGUgZGF0YS4gVGhlIHJ1cmFsIFVTQSBCTUkgYXBwZWFycyB0byBiZSBoaWdoZXIgdGhhbiB0aGUgdXJiYW4gQk1JIGZvciBib3RoIG1lbiBhbmQgd29tZW4uIE1hbnkgY291bnRyaWVzIGhhdmUgYXZlcmFnZSBCTUkgZXN0aW1hdGVzIGFib3ZlIHRoZSBvYmVzaXR5IHRocmVzaG9sZCBvZiAzMC4gVGh1cyBpdCBhcHBlYXJzIHRoYXQgZWR1Y2F0aW9uIGFuZCBvdXRyZWFjaCBwcm9ncmFtcyBmb3Igd2VpZ2h0IG1hbmFnZW1lbnQgc2hvdWxkIGZvY3VzIG9uIGJvdGggcnVyYWwgYW5kIHVyYmFuIGFyZWFzIGFuZCBib3RoIGdlbmRlcnMuIEVkdWNhdGlvbiBhbmQgYXNzaXN0YW5jZSBmb3Igd29tZW4gbWF5IGJlIGVzcGVjaWFsbHkgaGVscGZ1bC4gDQoNCiMjIyAqKkRpZmZlcmVuY2VzIGluIHJhdGUgb2YgY2hhbmdlKioNCioqKg0KDQpIb3cgZG9lcyB0aGUgcmF0ZSBvZiBjaGFuZ2UgaW4gQk1JIGRpZmZlciBiZXR3ZWVuIGdyb3Vwcz8gV2hpY2ggZ3JvdXAgbWlnaHQgZXNwZWNpYWxseSBuZWVkIGF0dGVudGlvbj8NCg0KRmlyc3QgbGV0J3MgY2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlcyBpbiBCTUkgZnJvbSAyMDE3IGFuZCAxOTg1IGFuZCBhZGQgdGhpcyB0byBvdXIgQk1JX2xvbmcgZGF0YSBvYmplY3QuIFRoaXMgd2lsbCBjcmVhdGUgbmV3IHZhcmlhYmxlcyBjYWxsZWQgYFJ1cmFsX2RpZmZlcmVuY2UsYCBgVXJiYW5fZGlmZmVyZW5jZWAsIGFuZCBgTmF0aW9uYWxfZGlmZmVyZW5jZWAuDQpgYGB7cn0NCkJNSV9udW1lcmljICAlPD4lIA0KICBtdXRhdGUoUnVyYWxfZGlmZmVyZW5jZSA9IFJ1cmFsX0JNSV8yMDE3IC0gUnVyYWxfQk1JXzE5ODUpICU+JQ0KICBtdXRhdGUoVXJiYW5fZGlmZmVyZW5jZSA9IFVyYmFuX0JNSV8yMDE3IC0gVXJiYW5fQk1JXzE5ODUpICU+JQ0KICBtdXRhdGUoTmF0aW9uYWxfZGlmZmVyZW5jZSA9IE5hdGlvbmFsX0JNSV8yMDE3IC0gTmF0aW9uYWxfQk1JXzE5ODUpDQoNCkJNSV9kaWZmX2xvbmcgPC0gQk1JX251bWVyaWMgJT4lIA0KICBzZWxlY3QoQ291bnRyeTpOYXRpb25hbF9kaWZmZXJlbmNlKSAlPiUgDQogIGdhdGhlcihrZXkgPSBUeXBlLCB2YWx1ZSA9IERpZmZlcmVuY2UgLCBSdXJhbF9kaWZmZXJlbmNlOk5hdGlvbmFsX2RpZmZlcmVuY2UpDQoNCg0KQk1JX2RpZmZfbG9uZyA8LSBCTUlfbnVtZXJpYyAlPiUgDQogIHNlbGVjdChDb3VudHJ5OiBOYXRpb25hbF9kaWZmZXJlbmNlKSAlPiUgDQogIHBpdm90X2xvbmdlciggbmFtZXNfdG8gPSAiRGlmZmVyZW5jZSIgLCBjb2xzID0gUnVyYWxfZGlmZmVyZW5jZTpOYXRpb25hbF9kaWZmZXJlbmNlKQ0KDQoNCmhlYWQoQk1JX2RpZmZfbG9uZykNCmBgYA0KDQpMZXQncyByZXBsYWNlICJVbml0ZWQgc3RhdGVzIG9mIEFtZXJpY2EiIHdpdGggIlVTQSIgYW5kIG1ha2UgYSBwbG90IHdpdGggdGhpcyBkYXRhIHRvIGNvbXBhcmUgdGhlIGNoYW5nZSBpbiBCTUk6DQoNCmBgYHtyfQ0KDQpCTUlfZGlmZl9sb25nW1siQ291bnRyeSJdXSA8LUJNSV9kaWZmX2xvbmdbWyJDb3VudHJ5Il1dICU+JQ0KICBzdHJfcmVwbGFjZSggcGF0dGVybiA9ICJVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EiLCByZXBsYWNlbWVudCA9ICJVU0EiKQ0KDQoNCmdncGxvdChCTUlfZGlmZl9sb25nLCBhZXMoeCA9IERpZmZlcmVuY2UsIHkgPSB2YWx1ZSwgY29sID0gRGlmZmVyZW5jZSkpICsgDQogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEsIGNvbG9yID0gImJsYWNrIiAsIGFlcyhmaWxsID0gRGlmZmVyZW5jZSkpICsgDQogIGZhY2V0X2dyaWQofiBTZXgpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YT1zdWJzZXQoQk1JX2RpZmZfbG9uZyksIA0KICAgICAgICAgICAgICBhZXMoeCA9RGlmZmVyZW5jZSwgeSA9dmFsdWUpLCANCiAgICAgICAgICAgICAgd2lkdGggPSAuMiwgc2l6ZSA9IDIsIHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJncmF5IikgKyANCiAgIyBUaGlzIHdpbGwgYWRkIHRoZSBpbmRpdmlkdWFsIGNvdW50cnkgZGF0YSBwb2ludHMNCiAgIyBUaGUgc2hhcGUgMjEgYWxsb3dzIGZvciBhIGRpZmZlcmVudCBmaWxsIGFuZCBvdXRsaW5lIGNvbG9yDQogICMgVGhlIHdpZHRoIGRldGVybWluZXMgaG93IHdpZGUgdGhlIGppdHRlciBwb2ludHMgYXJlIHBsb3R0ZWQNCiAgZ2VvbV9qaXR0ZXIoZGF0YT1zdWJzZXQoQk1JX2RpZmZfbG9uZywgQ291bnRyeSA9PSAiVVNBIiksIA0KICAgICAgICAgICAgICBhZXMoeCA9IERpZmZlcmVuY2UsIHkgPSB2YWx1ZSksIA0KICAgICAgICAgICAgICB3aWR0aCA9IC4wMiwgc2l6ZSA9IDEyLCBzaGFwZSA9IDIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheSIpICsgDQogICMgVGhpcyB3aWxsIGFkZCBwb2ludHMgdGhhdCBhcmUgbGFyZ2VyIGZvciB0aGUgVVNBIGRhdGENCiAgZ2VvbV90ZXh0KGRhdGE9c3Vic2V0KEJNSV9kaWZmX2xvbmcsICBDb3VudHJ5ID09ICJVU0EiKSwgDQogICAgICAgICAgICBhZXMoeCA9RGlmZmVyZW5jZSwgeSA9IHZhbHVlLCBsYWJlbCA9IENvdW50cnkpLCBjb2xvciA9ICJibGFjayIpICsgDQogICMgVGhpcyB3aWxsIGFkZCBVU0EgbGFiZWxzIHRvIHRoZSBVU0EgcG9pbnRzDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgDQogICMgVGhpcyBpcyB1c2VmdWwgZm9yIHJlbW92aW5nIHRoZSBsZWdlbmQNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LGFuZ2xlID0gMzApLCANCiAgICAgICAgIyB0aGlzIGNoYW5nZXMgdGhlIHNpemUgYW5kIGFuZ2xlIG9mIHRoZSB4IGF4aXMgcG9pbnQgbGFiZWxzIA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLCANCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksIA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArDQogICAgICAgICMgdGhpcyBjaGFuZ2VzIHRoZSBzaXplIG9mIHggYXhpcyBsYWJlbHMgZm9yIHRoZSBmYWNldA0KICAgICAgICBnZ3RpdGxlKCAiQ2hhbmdlIGluIEJNSSBPdmVyIFRpbWUgYW5kIEFjcm9zcyBSZWdpb24gVHlwZSBhbmQgR2VuZGVyIikNCg0KYGBgDQoNCldlIGNhbiBub3cgc2VlIHRoYXQgdGhlIHJhdGUgb2YgY2hhbmdlIGZyb20gMTk4NSB0byAyMDE3IGFwcGVhcnMgdG8gYmUgbGFyZ2VyIGluIHRoZSB3b21lbiBjb21wYXJlZCB0byB0aGUgbWVuIGluIGFsbCByZWdpb25zLiBUaGUgZ3JvdXAgd2l0aCB0aGUgbGFyZ2VzdCBpbmNyZWFzZSBpbiB0aGUgVVNBIGlzIHRoZSB3b21lbiBsaXZpbmcgaW4gcnVyYWwgYXJlYXMuDQoNCg0KDQpMZXQncyBjaGVjayB0aGUgZGlmZmVyZW5jZSB3aXRoIHNvbWUgc3RhdGlzdGljYWwgdGVzdHMuICBSZW1lbWJlciB0aGF0IHRoZXNlIHJ1cmFsIGRpZmZlcmVuY2VzIGFyZSBtZWFzdXJlZCBvbiB0aGUgc2FtZSBjb3VudHJpZXMgYXMgdGhlIHVyYmFuIGRpZmZlcmVuY2VzLCBzbyB3ZSB3aWxsIG5lZWQgdG8gdXNlIHBhaXJlZCB0ZXN0cyB0byBtYWtlIHRoZXNlIGNvbXBhcmlzb25zLiAgV2UgY2FuIGVpdGhlciBjb25zaWRlciB0aGUgcGFpcmVkICR0JC10ZXN0IG9yIHRoZSBXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0LiAgVG8gZGVjaWRlIGJldHdlZW4gdGhlbSwgd2UnbGwgbG9vayBhdCB0aGUgbm9ybWFsaXR5IG9mIHRoZSBwYWlyZWQgZGlmZmVyZW5jZXMgaW4gY2hhbmdlIG92ZXIgdGltZS4NCg0KTGV0J3MgY29tcGFyZSB0aGUgY2hhbmdlIG92ZXIgdGltZSBpbiBydXJhbCBjb21tdW5pdGllcyB0byB0aGUgY2hhbmdlIGluIHVyYmFuIGNvbW11bml0aWVzIGFtb25nIHdvbWVuIGFuZCBzZXBhcmF0ZWx5IGFtb25nIG1lbi4NCg0KYGBge3J9DQojIGxldHMgbG9vayBhdCB0aGUgbm9ybWFsaXR5IG9mIHRoZXNlIHBhaXJlZCBkaWZmZXJlbmNlcw0KIyBmaXJzdCB3ZSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcGFpcmVkIGRpZmZlcmVuY2VzIG9mIGNoYW5nZSBvdmVyIHRpbWUNCkJNSV9kaWZmX2xvbmcgJT4lDQogIGZpbHRlcihEaWZmZXJlbmNlICE9ICJOYXRpb25hbF9kaWZmZXJlbmNlIikgJT4lDQogIGdyb3VwX2J5KENvdW50cnksIFNleCkgJT4lDQogIHN1bW1hcml6ZShjaGFuZ2VfcnVyYWwgPSB2YWx1ZVtEaWZmZXJlbmNlID09ICJSdXJhbF9kaWZmZXJlbmNlIl0sDQogICAgICAgICAgICBjaGFuZ2VfdXJiYW4gPSB2YWx1ZVtEaWZmZXJlbmNlID09ICJVcmJhbl9kaWZmZXJlbmNlIl0sDQogICAgICAgICAgICBjaGFuZ2VfZGlmZiA9IGNoYW5nZV9ydXJhbCAtIGNoYW5nZV91cmJhbikgJT4lDQogIGdncGxvdChhZXMoeD1jaGFuZ2VfZGlmZikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGZhY2V0X3dyYXAofiBTZXgpDQojIGludGVyZXN0aW5nLi4udGhlIHNwcmVhZCBvZiB0aGUgZGlmZmVyZW5jZSBpbiBjaGFuZ2Ugb3ZlciB0aW1lIGlzIG11Y2ggDQojIGJyb2FkZXIgZm9yIHdvbWVuIHRoYW4gZm9yIG1lbg0KIyBmb3Igd29tZW4sIHRoZSBkaWZmZXJlbmNlcyBsb29rIHJlYXNvbmFibHkgbm9ybWFsLCBzcHJlYWQgYmV0d2VlbiAtMSBhbmQgKzENCiMgZm9yIG1lbiB0aGVyZSBhcHBlYXIgdG8gYmUgdHdvIGdyb3VwcyBvZiBjb3VudHJpZXM7IG9uZSB3aXRoIGRpZmZlcmVuY2UgaW4gY2hhbmdlIG92ZXIgdGltZQ0KIyBjZW50ZXJlZCBhcm91bmQgMCBhbmQgdGhlIG90aGVyIHdpdGggZGlmZmVyZW5jZSBpbiBjaGFuZ2Ugb3ZlciB0aW1lIGJldHdlZW4gMSBhbmQgMi4NCg0KDQojIExldCdzIGxvb2sgYXQgUS1RIHBsb3RzOg0KQk1JX2RpZmZfbG9uZyAlPiUNCiAgZmlsdGVyKERpZmZlcmVuY2UgIT0gIk5hdGlvbmFsX2RpZmZlcmVuY2UiKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSwgU2V4KSAlPiUNCiAgc3VtbWFyaXplKGNoYW5nZV9ydXJhbCA9IHZhbHVlW0RpZmZlcmVuY2UgPT0gIlJ1cmFsX2RpZmZlcmVuY2UiXSwNCiAgICAgICAgICAgIGNoYW5nZV91cmJhbiA9IHZhbHVlW0RpZmZlcmVuY2UgPT0gIlVyYmFuX2RpZmZlcmVuY2UiXSwNCiAgICAgICAgICAgIGNoYW5nZV9kaWZmID0gY2hhbmdlX3J1cmFsIC0gY2hhbmdlX3VyYmFuKSAlPiUNCiAgZ2dwbG90KGFlcyhzYW1wbGUgPSBjaGFuZ2VfZGlmZikpICsNCiAgc3RhdF9xcSgpICsgIyB0byBhZGQgdGhlIGxpbmUgd2UgbmVlZCB0byBhbHNvIGluY2x1ZGUgc3RhdF9xcV9saW5lKCkNCiAgc3RhdF9xcV9saW5lKCkgKw0KICBmYWNldF93cmFwKH4gU2V4KQ0KDQojIHRoZSBkaXN0cmlidXRpb24gb2YgZGlmZmVyZW5jZXMgaW4gY2hhbmdlIG92ZXIgdGltZSBkb2VzIG5vdCBhcHBlYXIgbm9ybWFsDQojIGZvciBlaXRoZXIgbWVuIG9yIHdvbWVuDQpgYGANCg0KQmFzZWQgb24gdGhlIG5vbi1ub3JtYWxpdHkgb2YgdGhlc2UgcGFpcmVkIGRpZmZlcmVuY2VzLCB3ZSB3aWxsIHVzZSBhIFdpbGNveG9uIHNpZ25lZC1yYW5rIHRlc3QgdG8gY29tcGFyZSBjaGFuZ2VzIG92ZXIgdGltZSBiZXR3ZWVuIHVyYmFuIGFuZCBydXJhbCBhcmVhcyBzZXBhcmF0ZWx5IGZvciBtZW4gYW5kIHdvbWVuOg0KYGBge3J9DQp3aWxjb3gudGVzdChwdWxsKGZpbHRlcihCTUlfZGlmZl9sb25nLCANCiAgICAgICAgICAgICAgICAgICAgU2V4ID09ICJXb21lbiIsICANCiAgICAgICAgICAgICAgICAgICAgRGlmZmVyZW5jZSA9PSAiUnVyYWxfZGlmZmVyZW5jZSIpLCB2YWx1ZSksIA0KICAgICAgICBwdWxsKGZpbHRlcihCTUlfZGlmZl9sb25nLA0KICAgICAgICAgICAgICAgICAgICBTZXggPT0gIldvbWVuIiwgIA0KICAgICAgICAgICAgICAgICAgICBEaWZmZXJlbmNlID09ICJVcmJhbl9kaWZmZXJlbmNlIiksIHZhbHVlKSwNCiAgICAgICAgcGFpcmVkID0gVFJVRSkNCg0Kd2lsY294LnRlc3QocHVsbChmaWx0ZXIoQk1JX2RpZmZfbG9uZywNCiAgICAgICAgICAgICAgICAgICAgU2V4ID09ICJNZW4iLA0KICAgICAgICAgICAgICAgICAgICBEaWZmZXJlbmNlID09ICJSdXJhbF9kaWZmZXJlbmNlIiksIHZhbHVlKSwgDQogICAgICAgIHB1bGwoZmlsdGVyKEJNSV9kaWZmX2xvbmcsDQogICAgICAgICAgICAgICAgICAgIFNleCA9PSAiTWVuIiwgDQogICAgICAgICAgICAgICAgICAgIERpZmZlcmVuY2UgPT0gIlVyYmFuX2RpZmZlcmVuY2UiKSwgdmFsdWUpLA0KICAgICAgICBwYWlyZWQgPSBUUlVFKQ0KYGBgDQpVc2luZyBhIHAtdmFsdWUgdGhyZXNob2xkIG9mIDAuMDUsIHdlIHdvdWxkIGNvbmNsdWRlIHRoZXJlJ3MgYSBkaWZmZXJlbmNlIGluIGNoYW5nZSBvdmVyIHRpbWUgYmV0d2VlbiB1cmJhbiBhbmQgcnVyYWwgYXJlYXMgZm9yIHdvbWVuIChwID0gMC4wMDMyNykgYnV0IG5vdCBmb3IgbWVuIChwID0gMC4wODI1NikuICANCg0KSG93ZXZlciwgbm93IHdlIGhhdmUgcGVyZm9ybWVkIHNpeCBjb21wYXJpc29ucyAoNCBlYXJsaWVyKSBzbyB3ZSBzaG91bGQgYXBwbHkgb3VyIG11bHRpcGxlIHRlc3RpbmcgY29ycmVjdGlvbiBhbmQgdXNlIGEgbmV3IHAtdmFsdWUgdGhyZXNob2xkIG9mOg0KYGBge3J9DQouMDUvNg0KYGBgDQoNCldpdGggdGhlIHRocmVzaG9sZCBhZGp1c3RlZCBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMsIHdlIHdvdWxkIHN0aWxsIGNvbmNsdWRlIHRoZXJlJ3MgYSBkaWZmZXJlbmNlIGluIHRoZSBjaGFuZ2Ugb3ZlciB0aW1lIGJldHdlZW4gdXJiYW4gYW5kIHJ1cmFsIGFyZWFzIGZvciB3b21lbiBidXQgbm90IGZvciBtZW4uDQoNCkludGVyZXN0aW5nbHksIGFsdGhvdWdoIHdlIGRvbid0IHNlZSBhIGRpZmZlcmVuY2UgaW4gY2hhbmdlIG92ZXIgdGltZSBmb3IgbWVuLCBvdXIgaGlzdG9ncmFtIG9mIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHVyYmFuIGFuZCBydXJhbCBhcmVhcyBmb3IgbWVuIGhpZ2hsaWdodGVkIHR3byBncm91cHMgb2YgY291bnRyaWVzIC0tIG9uZSBjZW50ZXJlZCBhcm91bmQgemVybyAobm8gZGlmZmVyZW5jZSkgYW5kIG9uZSBhYm92ZSAxIChzaG93aW5nIGEgZGlmZmVyZW5jZSk6ICANCmBgYHtyfQ0KQk1JX2RpZmZfbG9uZyAlPiUNCiAgZmlsdGVyKERpZmZlcmVuY2UgIT0gIk5hdGlvbmFsX2RpZmZlcmVuY2UiKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSwgU2V4KSAlPiUNCiAgc3VtbWFyaXplKGNoYW5nZV9ydXJhbCA9IHZhbHVlW0RpZmZlcmVuY2UgPT0gIlJ1cmFsX2RpZmZlcmVuY2UiXSwNCiAgICAgICAgICAgIGNoYW5nZV91cmJhbiA9IHZhbHVlW0RpZmZlcmVuY2UgPT0gIlVyYmFuX2RpZmZlcmVuY2UiXSwNCiAgICAgICAgICAgIGNoYW5nZV9kaWZmID0gY2hhbmdlX3J1cmFsIC0gY2hhbmdlX3VyYmFuKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWNoYW5nZV9kaWZmKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZmFjZXRfd3JhcCh+IFNleCkNCmBgYA0KDQpJbiBmYWN0IGZvciB0aGVzZSBjb3VudHJpZXMsIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIGFyZWFzIGZvciBtZW4gaXMgaGlnaGVyIHRoYW4gdGhlIGRpZmZlcmVuY2UgZm9yIHdvbWVuIGZvciBhbG1vc3QgYWxsIG9mIHRoZSBjb3VudHJpZXMhDQoNCldlIGNhbiBsb29rIHRvIHNlZSB3aGljaCBjb3VudHJpZXMgaGF2ZSBzdWNoIGEgbGFyZ2UgZGlmZmVyZW5jZSBpbiBjaGFuZ2Ugb3ZlciB0aW1lIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIGFyZWFzIGZvciBtZW46DQpgYGB7cn0NCkJNSV9kaWZmX2xvbmcgJT4lDQogIGZpbHRlcihTZXggPT0gIk1lbiIsIERpZmZlcmVuY2UgIT0gIk5hdGlvbmFsX2RpZmZlcmVuY2UiKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIHN1bW1hcml6ZShjaGFuZ2VfcnVyYWwgPSB2YWx1ZVtEaWZmZXJlbmNlID09ICJSdXJhbF9kaWZmZXJlbmNlIl0sDQogICAgICAgICAgICBjaGFuZ2VfdXJiYW4gPSB2YWx1ZVtEaWZmZXJlbmNlID09ICJVcmJhbl9kaWZmZXJlbmNlIl0sDQogICAgICAgICAgICBjaGFuZ2VfZGlmZiA9IGNoYW5nZV9ydXJhbCAtIGNoYW5nZV91cmJhbikgJT4lDQogIGZpbHRlcihjaGFuZ2VfZGlmZiA+IDEpDQpgYGANCg0KDQpGb3Igd29tZW4sIHdlIGRpZCBzZWUgYSBkaWZmZXJlbmNlIGluIHRoZSBjaGFuZ2Ugb3ZlciB0aW1lIGJldHdlZW4gdXJiYW4gYW5kIHJ1cmFsIGFyZWFzLiAgVGhlcmUgYXBwZWFyIHRvIGJlIHNwZWNpZmljIGNvdW50cmllcyB3aGVyZSBCTUkgc2hvd3MgYSBwYXJ0aWN1bGFyIGluY3JlYXNlIGVzcGVjaWFsbHkgZm9yIHdvbWVuLiBXaGljaCBjb3VudHJpZXMgYXJlIHRob3NlPyBIb3cgZG9lcyB0aGF0IGNvbXBhcmUgd2l0aCB0aGUgVVM/IENsZWFybHkgdGhlIFVTIGlzIGFtb25nIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgaGlnaGVzdCBkaWZmZXJlbmNlcy4gDQoNCmBgYHtyfQ0KQk1JX2RpZmZfbG9uZyAlPiUgDQogIGZpbHRlcihDb3VudHJ5ID09ICJVU0EiKQ0KDQpgYGANCg0KSW4gdGhlIFVTLCBmb2N1cyBzaG91bGQgYmUgcGxhY2VkIG9uIDxiPiBib3RoIHVyYmFuIGFuZCBydXJhbCA8L2I+IHdvbWVuIHRvIGltcHJvdmUgdGhpcyBwdWJsaWMgaGVhbHRoIGlzc3VlLiANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoZSBjb3VudHJpZXMgdGhhdCBoYXZlIHRoZSBsYXJnZXN0IGRpZmZlcmVuY2VzIGluIEJNSSBmcm9tIDE5ODUtMjAxNzoNCg0KYGBge3J9DQpCTUlfZGlmZl9sb25nICU+JQ0KICBmaWx0ZXIodmFsdWUgPiA0LjkpICU+JQ0KICBhcnJhbmdlKC0gdmFsdWUpDQojaGVyZSB3ZSBzb3J0ZWQgYnkgdGhlIGhpZ2hlc3QgdG8gbG93ZXN0IHZhbHVlcyBvZiBCTUkgRGlmZmVyZW5jZQ0KYGBgDQoNCkhvd2V2ZXIgaXQgaXMgaW1wb3J0YW50IHRvIHNlZSB3aGF0IHRoZSBtZWFuIEJNSSB2YWx1ZXMgd2VyZSBmb3IgdGhlc2UgY291bnRyaWVzIGluIDIwMTcuIA0KSXQgY291bGQgYmUgdGhhdCB0aGUgYXZlcmFnZSB3YXMgdW5kZXJ3ZWlnaHQgaW4gMTk4NS4uLiBsZXQncyB0YWtlIGEgbG9vay4NCg0KYGBge3J9DQpCTUlfbG9uZyAlPiUgDQogIGZpbHRlcihDb3VudHJ5ICVpbiUgcHVsbChmaWx0ZXIoQk1JX2RpZmZfbG9uZywgdmFsdWUgPiA0LjkpLCBDb3VudHJ5KSwgDQogICAgICAgICAgICAgICAgICAgIFNleCAlaW4lIHB1bGwoZmlsdGVyKEJNSV9kaWZmX2xvbmcsIHZhbHVlID4gNC45KSwgU2V4KSwNCiAgICAgICAgICAgICAgICAgICAgWWVhciA9PSIyMDE3IikgJT4lDQogIGFycmFuZ2UoLSBCTUkpDQoNCiNIZXJlIGlzIHRoZSBkYXRhIGp1c3QgZm9yIHRoZSBjaGFuZ2VzIGluIEJNSSBpbiBFZ3lwdA0KZmlsdGVyKEJNSV9kaWZmX2xvbmcsIENvdW50cnkgPT0gIkVneXB0IikgJT4lIA0KICBhcnJhbmdlKC0gdmFsdWUpDQoNCmBgYA0KDQpUaHVzIHJ1cmFsIHdvbWVuIGluIEVneXB0IGhhZCB0aGUgZ3JlYXRlc3QgaW5jcmVhc2UgaW4gQk1JIGZyb20gMTk4NSB0byAyMDE3IGluIHRoaXMgZGF0YSAoNS45KSBhbmQgdGhlIGF2ZXJhZ2UgQk1JIGlzIG5vdyBvdmVyIHRoZSBvYmVzaXR5IHRocmVzaG9sZCBvZiAzMC4NCg0KVGhlIGRhdGEgc3VnZ2VzdHMgdGhhdCBydXJhbCB3b21lbiBpbiBFZ3lwdCBhbmQgb3RoZXIgY291bnRyaWVzIG1heSBlc3BlY2lhbGx5IGJlbmVmaXQgZnJvbSBkaWV0YXJ5IHJlc291cmNlcyBhbmQgb3VyIGludGVydmVudGlvbnMgYW5kIHByb2dyYW1zIHRvIGFzc2lzdCB3aXRoIHdlaWdodCBtYW5hZ2VtZW50Lg0KDQoNCg0KIyMgKipTdW1tYXJ5KioNCioqKg0KDQojIyMgKipTdW1tYXJ5IFBsb3QqKg0KKioqDQpOb3cgbGV0J3MgbWFrZSBhIHBsb3QgdGhhdCBzdW1tYXJpemVzIG91ciBmaW5kaW5ncy4gVG8gZG8gdGhpcyB3ZSB3aWxsIHNpbXBsaWZ5IHRoZSBvdGhlciBwbG90cyB3ZSBtYWRlIGFuZCB0aGVuIGNvbWJpbmUgdGhlbSB0b2dldGhlci4NCg0KYGBge3J9DQoNCiMgc2ltcGxpZmllZCBuYXRpb25hbCBtZWFucyBwbG90DQpNZWFuc19wbG90PC1CTUlfbG9uZyAlPiUgDQogIGZpbHRlcihTZXggJWluJSBjKCJNZW4iLCAiV29tZW4iKSwgDQogICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgUmVnaW9uID09ICJOYXRpb25hbCIpICU+JQ0KZ2dwbG90KGFlcyh4ID0gU2V4LCB5ID0gQk1JKSkgKyANCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSwgY29sb3IgPSAiYmxhY2siICwgYWVzKGZpbGwgPSBTZXgpKSArIA0KIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlIiwgIm9yY2hpZDIiKSkgKw0KICBmYWNldF9ncmlkKH4gWWVhcikgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTMwLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9MSkgKyANCiAgZ2VvbV9qaXR0ZXIoZGF0YT1CTUlfbG9uZyAlPiUNCiAgICAgICAgICAgICAgICBmaWx0ZXIoU2V4ICVpbiUgYygiTWVuIiwgIldvbWVuIiksIA0KICAgICAgICAgICAgICAgICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgICAgICAgICAgICAgICBSZWdpb24gPT0gIk5hdGlvbmFsIiksDQogICAgICAgICAgICAgICAgYWVzKHggPVNleCwgeSA9Qk1JKSwgDQogICAgICAgICAgICAgICAgd2lkdGggPSAuMiwgc2l6ZSA9Miwgc2hhcGUgPTIxLCANCiAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheSIpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YT1zdWJzZXQoQk1JX2xvbmcgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKFNleCAlaW4lIGMoIk1lbiIsICJXb21lbiIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWWVhciAlaW4lIGMoIjE5ODUiLCAiMjAxNyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJOYXRpb25hbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb3VudHJ5ID09ICJVU0EiKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9U2V4LCB5ID1CTUkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAuMDIsIHNpemUgPTEyLCBzaGFwZSA9MjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheSIpICsgDQogICMgVGhpcyB3aWxsIGFkZCBwb2ludHMgdGhhdCBhcmUgbGFyZ2VyIGZvciB0aGUgVVNBIGRhdGENCiAgZ2VvbV90ZXh0KGRhdGE9c3Vic2V0KEJNSV9sb25nJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihTZXggJWluJSBjKCJNZW4iLCAiV29tZW4iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZZWFyICVpbiUgYygiMTk4NSIsICIyMDE3IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVnaW9uID09ICJOYXRpb25hbCIsIENvdW50cnkgPT0gIlVTQSIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPVNleCwgeSA9Qk1JLGxhYmVsPUNvdW50cnkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArIA0KICAjIFRoaXMgd2lsbCBhZGQgVVNBIGxhYmVscyB0byB0aGUgVVNBIHBvaW50cw0KICB0aGVtZV9saW5lZHJhdygpICsNCiAgIyBUaGlzIHdpbGwgbWFrZSB0aGUgYmFja2dyb3VuZCBvZiB0aGUgcGxvdCB3aGl0ZSBhbmQgdGhlIGZhY2V0IGxhYmVscyBibGFjaw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIA0KICAjIFRoaXMgaXMgdXNlZnVsIGZvciByZW1vdmluZyB0aGUgbGVnZW5kDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSxhbmdsZSA9IDMwLCB2anVzdCA9IDAuNSksIA0KICAgICAgICAjIHRoaXMgY2hhbmdlcyB0aGUgc2l6ZSBhbmQgYW5nbGUgb2YgdGhlIHggYXhpcyBwb2ludCBsYWJlbHMgDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9MTUpLCANCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPTE1KSwgDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQ0KICAgDQojU2ltcGxpZmllZCBkaWZmZXJlbmNlIHBsb3QgDQpCTUlfZGlmZl9sb25nW1siRGlmZmVyZW5jZSJdXSA8LUJNSV9kaWZmX2xvbmdbWyJEaWZmZXJlbmNlIl1dJT4lDQogIHN0cl9yZXBsYWNlKCBwYXR0ZXJuID0gIl9kaWZmZXJlbmNlIiwgcmVwbGFjZW1lbnQgPSAiIikNCg0KRGlmZl9wbG90PC1CTUlfZGlmZl9sb25nICU+JSANCiAgZmlsdGVyKFNleCAlaW4lIGMoIk1lbiIsICJXb21lbiIpLCANCiAgICAgICAgIERpZmZlcmVuY2UgIT0gIk5hdGlvbmFsIikgJT4lDQpnZ3Bsb3QoIGFlcyh4ID0gRGlmZmVyZW5jZSwgeSA9IHZhbHVlLCBjb2wgPSBEaWZmZXJlbmNlKSkgKyANCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSwgY29sb3IgPSAiYmxhY2siICwgYWVzKGZpbGwgPSBTZXgpKSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZSIsICJvcmNoaWQyIikpICsNCiAgZmFjZXRfZ3JpZCh+IFNleCkgKw0KICBnZW9tX2ppdHRlcihkYXRhPUJNSV9kaWZmX2xvbmcgJT4lIA0KICAgIGZpbHRlcihTZXggJWluJSBjKCJNZW4iLCAiV29tZW4iKSwgDQogICAgICAgICAgIERpZmZlcmVuY2UgIT0gIk5hdGlvbmFsIiksIA0KICAgIGFlcyh4ID1EaWZmZXJlbmNlLCB5ID0gdmFsdWUpLCANCiAgICB3aWR0aCA9IC4yLCBzaXplID0gMiwgc2hhcGUgPSAyMSwgDQogICAgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImdyYXkiKSArIA0KICBnZW9tX2ppdHRlcihkYXRhPXN1YnNldChCTUlfZGlmZl9sb25nICU+JSANCiAgICBmaWx0ZXIoU2V4ICVpbiUgYygiTWVuIiwgIldvbWVuIiksIA0KICAgICAgICAgICBEaWZmZXJlbmNlICE9ICJOYXRpb25hbCIsIA0KICAgICAgICAgICBDb3VudHJ5ID09ICJVU0EiKSksIA0KICAgIGFlcyh4ID0gRGlmZmVyZW5jZSwgeSA9IHZhbHVlKSwgDQogICAgd2lkdGggPSAuMDIsIHNpemUgPTEyLCBzaGFwZSA9MjEsIA0KICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJncmF5IikgKyANCiAgIyBUaGlzIHdpbGwgYWRkIHBvaW50cyB0aGF0IGFyZSBsYXJnZXIgZm9yIHRoZSBVU0EgZGF0YQ0KICBnZW9tX3RleHQoZGF0YT1zdWJzZXQoQk1JX2RpZmZfbG9uZyAlPiUgDQogICAgZmlsdGVyKFNleCAlaW4lIGMoIk1lbiIsICJXb21lbiIpLA0KICAgICAgICAgICBEaWZmZXJlbmNlICE9ICJOYXRpb25hbCIpLCANCiAgICAgICAgICAgQ291bnRyeSA9PSAiVVNBIiksDQogICAgYWVzKHggPSBEaWZmZXJlbmNlLCB5ID0gdmFsdWUsbGFiZWw9Q291bnRyeSksIA0KICAgIGNvbG9yID0gImJsYWNrIikgKyANCiAgIyBUaGlzIHdpbGwgYWRkIFVTQSBsYWJlbHMgdG8gdGhlIFVTQSBwb2ludHMNCiAgdGhlbWVfbGluZWRyYXcoKSArDQogICAgIyBUaGlzIHdpbGwgbWFrZSB0aGUgYmFja2dyb3VuZCBvZiB0aGUgcGxvdCB3aGl0ZSBhbmQgdGhlIGZhY2V0IGxhYmVscyBibGFjaw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIA0KICAjIFRoaXMgaXMgdXNlZnVsIGZvciByZW1vdmluZyB0aGUgbGVnZW5kDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSxhbmdsZSA9IDMwLCB2anVzdCA9IDAuNSksIA0KICAgICAgICAjIHRoaXMgY2hhbmdlcyB0aGUgc2l6ZSBhbmQgYW5nbGUgb2YgdGhlIHggYXhpcyBwb2ludCBsYWJlbHMgDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9MTUpLCANCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPTE1KSwgDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQ0KICAjIHRoaXMgY2hhbmdlcyB0aGUgc2l6ZSBvZiB4IGF4aXMgbGFiZWxzIGZvciB0aGUgZmFjZXQNCg0KI2FkZCBsYWJlbHMNCkRpZmZfcGxvdDwtRGlmZl9wbG90ICsgDQogICAgICAgIGxhYnModGl0bGUgPSAiQ2hhbmdlIGluIEJNSSBieSByZWdpb24iLCANCiAgICAgICAgICAgICB4ID0gIiIsIA0KICAgICAgICAgICAgIHkgPSAiQ2hhbmdlIGluIEJNSSBcbiAoMTk4NSB0byAyMDE3KSIpICsNCiAgICAgICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQgKHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikpDQoNCk1lYW5zX3Bsb3QgPC1NZWFuc19wbG90ICsgDQogICAgICAgIGxhYnModGl0bGUgPSAiTWVhbiBOYXRpb25hbCBCTUkgb3ZlciB0aW1lIiwgDQogICAgICAgICAgICAgeCA9ICIiLCANCiAgICAgICAgICAgICB5ID0gIk1lYW4gQk1JIikgKw0KICAgICAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dCAoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSkNCg0KDQoNCm9iZXNpdHlfdGV4dDwtdGliYmxlKFllYXI9YygxOTg1KSxCTUk9YygzMSksU2V4PWMoIk1lbiIpLGxhYmVsPWMoIk9iZXNpdHkiKSkNCg0KTWVhbnNfcGxvdCA8LU1lYW5zX3Bsb3QgKyANCiAgICAgICAgICAgICBnZW9tX3RleHQoZGF0YSA9IG9iZXNpdHlfdGV4dCwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cHVsbChvYmVzaXR5X3RleHQsbGFiZWwpLCANCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICAgICAgICAgICAgICAgICAgIGFlcyggZm9udGZhY2UgPSJib2xkLml0YWxpYyIsIHNpemUgPSAxMykpDQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZChNZWFuc19wbG90LCBEaWZmX3Bsb3QsIGxhYmVscyA9IGMoIkEiLCAiQiIpKQ0KYGBgDQoNCldlIGNvdWxkIG1ha2UgYSBzaW1pbGFyIHBsb3Qgd2l0aCB0aGUgYHBhdGNod29ya2AgcGFja2FnZToNCmBgYHtyfQ0KTWVhbnNfcGxvdCArbGFicyh0YWcgPSAnQScpICsgRGlmZl9wbG90KyBsYWJzKHRhZyA9ICdCJykgKw0KICBwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcsIG5jb2wgPSAyKQ0KYGBgDQoNCkdyZWF0LCBub3cgd2UgaGF2ZSBwdXQgdHdvIHBsb3RzIHRvZ2V0aGVyIHVzaW5nIHRoZSBgcGxvdF9ncmlkKClgIGZ1bmN0aW9uIG9mIHRoZSBgY293cGxvdGAgcGFja2FnZS4gVGhpcyB3YXkgd2UgY2FuIGNsZWFybHkgY29tbXVuaWNhdGUgdHdvIG1lc3NhZ2VzLiBUaGUgZmlyc3QgYmVpbmcgdGhhdCBCTUkgaGFzIGluY3JlYXNlZCBvdmVyIHRpbWUgZ2xvYmFsbHkgYW5kIHRoYXQgbWFueSBjb3VudHJpZXMgaW5jbHVkaW5nIHRoZSBVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EgYXJlIGFwcHJvYWNoaW5nIGEgbWVhbiBCTUkgdGhhdCBpcyBhYm92ZSB0aGUgb2Jlc2l0eSB0aHJlc2hvbGQgb2YgMzAuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHdvbWVuIG9uIGF2ZXJhZ2UgaGF2ZSBsYXJnZXIgQk1JIHZhbHVlcyB0aGFuIG1hbGVzLCBidXQgdGhhdCBib3RoIGdlbmRlcnMgc2hvdyBpbmNyZWFzZWQgbGV2ZWxzIG92ZXIgdGltZS4gSW4gdGhlIHNlY29uZCBwbG90IHdlIGNhbiBzZWUgdGhhdCB0aGUgaW5jcmVhc2UgaW4gQk1JIGlzIG5vdCBqdXN0IGhhcHBlbmluZyBpbiAgdXJiYW4gY29tbXVuaXRpZXMsIGJ1dCBpbiBib3RoIHJ1cmFsIGFuZCB1cmJhbiBjb21tdW5pdGllcyBhbmQgcGFydGljdWxhcmx5IGluIHdvbWVuLiANCg0KT3VyIHBsb3QgdmlzdWFsbHkgZXhwbG9yZXMgYWxsIG9mIG91ciBtYWluIHF1ZXN0aW9uczoNCjEpIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHJ1cmFsIGFuZCB1cmJhbiBCTUkgZXN0aW1hdGVzIGFyb3VuZCB0aGUgd29ybGQ/IEluIHBhcnRpY3VsYXIsIGhvdyBkb2VzIHRoaXMgbG9vayBmb3IgZmVtYWxlcz8NCjIpIEhvdyBoYXZlIEJNSSBlc3RpbWF0ZXMgY2hhbmdlZCBmcm9tIDE5ODUgdG8gMjAxNz8gQWdhaW4sIEluIHBhcnRpY3VsYXIsIGhvdyBkb2VzIHRoaXMgbG9vayBmb3IgZmVtYWxlcz8NCjMpIEhvdyBkbyBkaWZmZXJlbnQgY291bnRyaWVzIGNvbXBhcmUgZm9yIEJNSSBlc3RpbWF0ZXM/IEluIHBhcnRpY3VsYXIsIGhvdyBkb2VzIHRoZSBVbml0ZWQgU3RhdGVzIGNvbXBhcmUgdG8gdGhlIHJlc3Qgb2YgdGhlIHdvcmxkPw0KDQpOb3cgbGV0J3Mgc2F2ZSBvdXIgcGxvdCBhcyBhIHBuZyBmaWxlIHVzaW5nIHRoZSBgcG5nKClgIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCnBuZyhoZXJlOjpoZXJlKCJpbWciLCAibWFpbl9wbG90LnBuZyIpLCBoZWlnaHQgPSAxNTAwLCB3aWR0aCA9IDIzMDAsIHJlcyA9IDMwMCkNCmNvd3Bsb3Q6OnBsb3RfZ3JpZChNZWFuc19wbG90LCBEaWZmX3Bsb3QsIGxhYmVscyA9IGMoIkEiLCAiQiIpKQ0KZGV2Lm9mZigpDQpgYGANCg0KWW91IG1pZ2h0IHRoaW5rIHRoYXQgdGhlc2UgdHdvIHBsb3RzIGxvb2sgYSBiaXQgb2RkIHRvZ2V0aGVyLCB0aGF0IGlmIHlvdSBsb29rIGF0IHRoZSBjaGFuZ2UgaW4gdGhlIGJveHBsb3RzIGluIHBhcnQgQSwgdGhhdCB0aGUgZGF0YSBmb3IgTWVuIHNob3dzIGEgYmlnZ2VyIGNoYW5nZSwgaG93ZXZlciwgd2UgY2FuJ3QgdGVsbCBmcm9tIHRoaXMgcGxvdCBob3cgdGhlIGluZGl2aWR1YWwgY291bnRyaWVzIGFyZSBjaGFuZ2luZy4gUGFydCBCIG9mIHRoZSBmaWd1cmUgbG9va3MgYXQgdGhlIHNwZWNpZmljIGNoYW5nZSBmb3IgZWFjaCBjb3VudHJ5Lg0KDQpXZSBjb3VsZCBjaGVjayBvdXIgcGxvdCB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGU6DQpgYGB7cn0NCkJNSV9kaWZmX2xvbmcgJT4lDQogIGdyb3VwX2J5KFNleCwgRGlmZmVyZW5jZSkgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSU+JQ0KICBmaWx0ZXIoQ291bnRyeSA9PSAiVVNBIikgJT4lDQogIHN1bW1hcml6ZShtZWFuID0gbWVhbih2YWx1ZSksIG1lZGlhbiA9IG1lZGlhbih2YWx1ZSkpDQoNCkJNSV9kaWZmX2xvbmcgJT4lDQogIGdyb3VwX2J5KFNleCwgRGlmZmVyZW5jZSkgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSU+JQ0KICBzdW1tYXJpemUobWVhbiA9IG1lYW4odmFsdWUpLCBtZWRpYW4gPSBtZWRpYW4odmFsdWUpKQ0KDQpCTUlfbG9uZyAlPiUNCiAgZ3JvdXBfYnkoU2V4LCBZZWFyLCBSZWdpb24pICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEJNSSkpJT4lDQogIGZpbHRlcihDb3VudHJ5ID09ICJVU0EiKSAlPiUNCiAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKEJNSSksIG1lZGlhbiA9IG1lZGlhbihCTUkpKQ0KDQpCTUlfbG9uZyAlPiUNCiAgZ3JvdXBfYnkoU2V4LCBZZWFyLCBSZWdpb24pICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEJNSSkpJT4lDQogIHN1bW1hcml6ZShtZWFuID0gbWVhbihCTUkpLCBtZWRpYW4gPSBtZWRpYW4oQk1JKSkNCg0KQk1JX251bWVyaWMgJT4lDQogIGdyb3VwX2J5KFNleCklPiUNCiAgc3VtbWFyaXplKE5hdF9tZWFuMTk4NSA9IG1lYW4oTmF0aW9uYWxfQk1JXzE5ODUpLCANCiAgICAgICAgICAgIE5hdF9tZWFuMjAxNyA9IG1lYW4oTmF0aW9uYWxfQk1JXzIwMTcpKQ0KDQpCTUlfbnVtZXJpYyAlPiUNCiAgZ3JvdXBfYnkoU2V4KSU+JQ0KICBzdW1tYXJpemUoTmF0X21lZDE5ODUgPSBtZWRpYW4oTmF0aW9uYWxfQk1JXzE5ODUpLCANCiAgICAgICAgICAgIE5hdF9tZWQyMDE3ID0gbWVkaWFuKE5hdGlvbmFsX0JNSV8yMDE3KSkNCmBgYA0KDQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgbGFyZ2VzdCBjaGFuZ2VzIG9jY3VyIGZvciBXb21lbi4NCg0KU2Nyb2xsIHRocm91Z2ggdGhlIG91dHB1dCENCg0KIyMjIyB7LnNjcm9sbGFibGUgfQ0KYGBge3J9DQpCTUlfZGlmZl9sb25nICU+JQ0KICBmaWx0ZXIodmFsdWU+My41KSAlPiUNCiAgYXJyYW5nZSgtdmFsdWUpICU+JQ0KICAjIHRoaXMgYWxsb3dzIHVzIHRvIHNob3cgdGhlIGZ1bGwgb3V0cHV0DQogIHByaW50KG4gPSAxZTMpDQpgYGANCiMjIyMNCg0KIyMjICoqU3lub3BzaXMNCioqKg0KDQpXZSBoYXZlIGV2YWx1YXRlZCBCTUkgYXZlcmFnZSBlc3RpbWF0ZXMgZnJvbSAyMDAgZGlmZmVyZW50IGNvdW50cmllcyBhcm91bmQgdGhlIHdvcmxkLiBUbyBkbyBzbyB3ZSBpbXBvcnRlZCBkYXRhIGZyb20gYSBwZGYgdXNpbmcgdGhlIGBwZGZ0b29sc2AgcGFja2FnZS4gV2UgdXNlZCBgdGlkeXZlcnNlYCBwYWNrYWdlcyBzdWNoIGFzIGBkcGx5cmAsIGBzdHJpbmdyYCwgYW5kIGB0aWR5YCB0byBjbGVhbiB0aGUgZGF0YSBhbmQgZ2V0IGl0IGluIGEgd29ya2FibGUgZm9ybWF0LiBPdXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgZm9jdXNlZCBvbiBldmFsdWF0aW5nIGRpZmZlcmVuY2VzIGluIEJNSSBpbiBmZW1hbGVzIGFyb3VuZCB0aGUgd29ybGQgYWNyb3NzIHRpbWUgYW5kIGJldHdlZW4gcnVyYWwgYW5kIHVyYmFuIGFyZWFzLiBXZSBmb3VuZCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYm90aCBiZXR3ZWVuIHllYXJzIGFuZCBiZXR3ZWVuIHRoZSB0eXBlIG9mIGNvbW11bml0eSBhbW9uZyB3b21lbiBnbG9iYWxseSB1c2luZyBbcGFpcmVkICR0JC10ZXN0c10oaHR0cHM6Ly9zdGF0dHJlay5jb20vaHlwb3RoZXNpcy10ZXN0L3BhaXJlZC1tZWFucy5hc3B4I2V4YW1wbGUxKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCBbbm9ucGFyYW1ldHJpY10oaHR0cHM6Ly93d3cubWF5by5lZHUvcmVzZWFyY2gvZG9jdW1lbnRzL3BhcmFtZXRyaWMtYW5kLW5vbnBhcmFtZXRyaWMtZGVteXN0aWZ5aW5nLXRoZS10ZXJtcy9kb2MtMjA0MDg5NjApe3RhcmdldD0iX2JsYW5rIn0gdGVzdHMuIFRodXMgQk1JIGhhcyBpbmNyZWFzZWQgaW4gd29tZW4gc2luY2UgMTk4NS4gQWx0aG91Z2ggQk1JIGVzdGltYXRlcyBhcmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgaW4gVXJiYW4gYXJlYXMgY29tcGFyZWQgdG8gcnVyYWwgYXJlYXMsIEJNSSBlc3RpbWF0ZXMgaGF2ZSBpbmNyZWFzZWQgaW4gYm90aCByZWdpb25zLiBXZSBsZWFybmVkIHRoYXQgdGhlcmUgYXJlIGFzc3VtcHRpb25zIGFuZCBjb25zaWRlcmF0aW9ucyB0byBrZWVwIGluIG1pbmQgd2hlbiBwZXJmb3JtaW5nIHRlc3RzIHRoYXQgY29tcGFyZSB0d28gZ3JvdXBzLiANCg0KPHU+IFdlIGxlYXJuZWQgdGhhdCB0aGUgdHdvIHNhbXBsZSAkdCQtdGVzdCByZWxpZXMgb246PC91Pg0KDQoxKSBub3JtYWxpdHkgb2YgdGhlIGRhdGEgZm9yIGJvdGggZ3JvdXBzICh0aGlzIGlzIG5vdCBhcyBtdWNoIG9mIGFuIGlzc3VlIGlmIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGlzIHJlbGF0aXZlbHkgbGFyZ2UgdG90YWwgbj4zMCkNCjIpIGVxdWFsIHZhcmlhbmNlIGJldHdlZW4gdGhlIHR3byBncm91cHMgKG1ha2Ugc3VyZSB5b3UgZG8gdGhlIGNvcnJlY3QgdGVzdCBpZiB0aGUgZGF0YSBpcyBub3Qgbm9ybWFsKQ0KMykgaW5kZXBlbmRlbnQgb2JzZXJ2YXRpb25zDQoNCldlIGxlYXJuZWQgdGhhdCB3ZSBjYW4gZXZhbHVhdGUgaWYgb3VyIGRhdGEgaXMgW25vcm1hbGx5IGRpc3RyaWJ1dGVkXShodHRwczovL3d3dy5waHlzaW9sb2d5Lm9yZy9kb2kvZnVsbC8xMC4xMTUyL2FkdmFuLjAwMDY0LjIwMTcpe3RhcmdldD0iX2JsYW5rIn0gYnkgcGxvdHRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgYnkgY3JlYXRpbmcgUS1RIHBsb3RzLg0KDQo8dT5XZSBsZWFybmVkIHRoYXQgaWYgb3VyIGRhdGEgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3ZSBjYW4gY29uc2lkZXIgdGhlc2Ugb3B0aW9uczo8L3U+DQoNCjEpIFdlIGNhbiBzdGlsbCBwZXJmb3JtIGEgJHQkLXRlc3QgaWYgb3VyIG4gaXMgbGFyZ2UNCjIpIFdlIGNhbiB0cmFuc2Zvcm0gdGhlIGRhdGEgYmVmb3JlIHBlcmZvcm1pbmcgYSAkdCQtdGVzdA0KDQpXZSBsZWFybmVkIHRoYXQgaWYgb3VyIGRhdGEgaGFzIHVuZXF1YWwgdmFyaWFuY2UsIHdlIGNhbiBjb25zaWRlciB1c2luZyB0aGUgW1dlbGNoJ3MgJHQkLXRlc3RdKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5kYXRhc2NpZW5jZWNlbnRyYWwuY29tL3dlbGNocy10ZXN0LWZvci11bmVxdWFsLXZhcmlhbmNlcy8pe3RhcmdldD0iX2JsYW5rIn0uDQoNCg0KSW1wb3J0YW50bHksIHdlIGxlYXJuZWQgdGhhdCB3aGVuIHdlIGhhdmUgW3BhaXJlZCBkYXRhXShodHRwczovL3d3dy5zdGF0aXN0aWNzaG93dG8uZGF0YXNjaWVuY2VjZW50cmFsLmNvbS9wYWlyZWQtZGF0YS8pe3RhcmdldD0iX2JsYW5rIn0gKGFyZSBvYnNlcnZhdGlvbnMgYXJlIG5vdCBpbmRlcGVuZGVudCkgYW5kICB3ZSBpbnRlbmQgdG8gY29tcGFyZSBtZWFucywgd2Ugc2hvdWxkIHBlcmZvcm0gYSBwYWlyZWQgdGVzdCAoW3BhaXJlZCAkdCQtdGVzdF0oaHR0cHM6Ly9zdGF0dHJlay5jb20vaHlwb3RoZXNpcy10ZXN0L3BhaXJlZC1tZWFucy5hc3B4I2V4YW1wbGUxKXt0YXJnZXQ9Il9ibGFuayJ9IG9yIHRoZSBub25wYXJhbWV0cmljIFdpbGNveG9uIHNpZ25lZCByYW5rIHRlc3QuIE91ciBkYXRhIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5IHdhcyBwYWlyZWQgYmVjYXVzZSBvYnNlcnZhdGlvbnMgd2VyZSB0YWtlbiBmb3IgdGhlIHNhbWUgY291bnRyaWVzIGZvciBkaWZmZXJlbnQgY2F0ZWdvcmllcyBvZiBwb3B1bGF0aW9ucyAtIHRodXMsIHdlIHdhbnRlZCB0byBjb21wYXJlIHRoZXNlIHBvcHVsYXRpb25zIHdpdGhpbiB0aGUgc2FtZSBjb3VudHJ5IChtYWxlIHZzIGZlbWFsZSwgcnVyYWwgdnMgdXJiYW4gZXRjLikuIE90aGVyIGV4YW1wbGVzIHdvdWxkIGJlIGluIGEgY2FzZS1jb250cm9sIHN0dWR5IChpZiBzYW1wbGVzIGFyZSBtYXRjaGVkIGZvciB2YXJpb3VzIGRlbW9ncmFwaGljcykgb3IgYSBzdHVkeSB3aXRoIHJlcGVhdGVkIG1lYXN1cmVzIChmb3IgZXhhbXBsZSwgc3ltcHRvbSBtZWFzdXJlcyBmcm9tIHRoZSBzYW1lIGluZGl2aWR1YWwgYmVmb3JlIGFuZCBhZnRlciBhIHRyZWF0bWVudCkuIFdlIGxlYXJuZWQgdGhhdCBbcGFpcmVkICR0JC10ZXN0XShodHRwczovL3N0YXR0cmVrLmNvbS9oeXBvdGhlc2lzLXRlc3QvcGFpcmVkLW1lYW5zLmFzcHgjZXhhbXBsZTEpe3RhcmdldD0iX2JsYW5rIn0gdGVzdHMgYSBkaWZmZXJlbnQgaHlwb3RoZXNpcyB0aGUgdHdvLXNhbXBsZSAkdCQtdGVzdC4gSW4gdGhpcyBjYXNlIHdlIGFyZSB0ZXN0aW5nIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyB0aGUgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zIGFuZCBob3cgY2xvc2UgdGhlc2UgZGlmZmVyZW5jZXMgYXJlIHRvIHplcm8sIHRodXMgdGhlcmUgaXMgb25seSBvbmUgdmFyaWFuY2UsIHRoZSB2YXJpYW5jZSBvZiB0aGUgcGFpcmVkIGRpZmZlcmVuY2VzIGFuZCB0aGVyZWZvcmUsIHdlIGRvIG5vdCBuZWVkIHRvIHdvcnJ5IGFib3V0IHRoZSBlcXVhbGl0eSBvZiB2YXJpYW5jZSBhc3N1bXB0aW9uIGxpa2Ugd2UgZG8gd2l0aCB0aGUgdHdvLXNhbXBsZSAkdCR0ZXN0Lg0KV2UgbGVhcm5lZCB0aGF0IHdlIGNhbiBwZXJmb3JtIGEgcGFpcmVkICR0JC10ZXN0IGJ5IGFkZGluZyB0aGUgb3B0aW9uIGBwYWlyZWQgPSBUUlVFYCB0byB0aGUgYHQudGVzdCgpYCBjb21tYW5kLg0KDQpUaGUgcGFpcmVkICR0JC10ZXN0IGFzc3VtZXMgdGhhdDoNCg0KMSkgVGhlIHBhaXJlZCBkaWZmZXJlbmNlcyBhcmUgW25vcm1hbGx5IGRpc3RyaWJ1dGVkXShodHRwOi8vb25saW5lc3RhdGJvb2suY29tLzIvaW50cm9kdWN0aW9uL2Rpc3RyaWJ1dGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSwgd2hpY2ggd2UgY2FuIGV2YWx1YXRlIHVzaW5nIFEtUSBwbG90cyBvciBwbG90dGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkaWZmZXJlbmNlcy4NCjIpIFRoZSBwYWlyZWQgZGlmZmVyZW5jZXMgYXJlIFtpbmRlcGVuZGVudF0oaHR0cHM6Ly93d3cuc3RhdC5jbXUuZWR1L35jc2hhbGl6aS8zNi0yMjAvbGVjdHVyZS01LnBkZil7dGFyZ2V0PSJfYmxhbmsifSAobWVhbmluZyB0aGF0IG9ic2VydmF0aW9ucyBkbyBub3QgaW5mbHVlbmNlIGVhY2ggb3RoZXIpLg0KDQpXZSBsZWFybmVkIHRoYXQgaWYgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uIGlzIHZpb2xhdGVkLCB3ZSBjYW4gdXNlIHRoZSBXaWxjb3hvbiBzaWduZWQgcmFuayB0ZXN0LCBvciBpZiBvdXIgZGF0YSBpcyBsYXJnZSBlbm91Z2gsIGR1ZSB0byB0aGUgY2VudHJhbCBsaW1pdCB0aGVvcmVtLCB0aGUgcGFpcmVkICR0JC10ZXN0IHNob3VsZCBzdGlsbCBiZSByb2J1c3QgdG8gZGV2aWF0aW9ucyBmcm9tIG5vcm1hbGl0eS4NCg0KVXNpbmcgdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIHdlIHdlcmUgYWJsZSB0byB2aXN1YWxpemUgdHJlbmRzIGluIHRoZSBkYXRhLiBJbXBvcnRhbnRseSwgdGhlIGxhcmdlc3QgaW5jcmVhc2UgYXBwZWFycyB0byBiZSBpbiB0aGUgcnVyYWwgYXJlYXMgcGFydGljdWxhcmx5IGZvciB3b21lbi4gV2Ugd2VyZSBhbHNvIGFibGUgdG8gaWRlbnRpZnkgaG93IHRoZSBVUyBjb21wYXJlZCB0byBvdGhlciBjb3VudHJpZXMgYW5kIHdoaWNoIGNvdW50cmllcyBzaG93ZWQgdGhlIGxhcmdlc3QgaW5jcmVhc2UgaW4gQk1JIG92ZXIgdGltZS4gQW5hbHlzZXMgbGlrZSB0aGlzIGFyZSBpbXBvcnRhbnQgZm9yIGRlZmluaW5nIHdoaWNoIGdyb3VwcyBjb3VsZCBiZW5lZml0IHRoZSBtb3N0IGZyb20gaW50ZXJ2ZW50aW9ucywgZWR1Y2F0aW9uLCBhbmQgcG9saWN5IGNoYW5nZXMgd2hlbiBhdHRlbXB0aW5nIHRvIG1pdGlnYXRlIHB1YmxpYyBoZWFsdGggY2hhbGxlbmdlcy4gWW91IGNhbiBzZWUgaW4gdGhlIFthcnRpY2xlXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4Ni0wMTktMTE3MS14LnBkZil7dGFyZ2V0PSJfYmxhbmsifSB0aGF0IHRoaXMgZGF0YSBjYW1lIGZyb20gdGhhdCBtYW55IGFkZGl0aW9uYWwgY29uc2lkZXJhdGlvbnMgd291bGQgYmUgaW52b2x2ZWQgdG8gcGVyZm9ybSBzdWNoIGFuIGFuYWx5c2lzLg0KDQoNCiMjICoqU3VnZ2VzdGVkIEhvbWV3b3JrKioNCioqKg0KDQpTdHVkZW50cyBjYW4gZXZhbHVhdGUgdGhlIGNoYW5nZSBpbiBCTUkgb3ZlciB0aW1lIHVzaW5nIHRoZSBnbG9iYWwgZGF0YSBhdmFpbGFibGUgZm9yIGVhY2ggeWVhciBiZXR3ZWVuIDIwMTUgYW5kIDIwMTcuIFRoZSBkYXRhIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cDovL3d3dy5uY2RyaXNjLm9yZy9kb3dubG9hZHMvYm1pL05DRF9SaXNDX0xhbmNldF8yMDE3X0JNSV9hZ2Vfc3RhbmRhcmRpc2VkX3dvcmxkLmNzdil7dGFyZ2V0PSJfYmxhbmsifS4NCg0KIyMgKipBZGRpdGlvbmFsIEluZm9ybWF0aW9uKioNCioqKg0KDQojIyMgKipIZWxwZnVsIExpbmtzKioNCioqKg0KDQpbQk1JXShodHRwczovL3d3dy5jZGMuZ292L2hlYWx0aHl3ZWlnaHQvYXNzZXNzaW5nL2JtaS9hZHVsdF9ibWkvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbTG9uZyBhbmQgV2lkZSBEYXRhIEZvcm1hdHNdKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtaGVhbHRoZXhwZW5kaXR1cmUvb2NzLWhlYWx0aGV4cGVuZGl0dXJlLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gIA0KW0Rpc3RyaWJ1dGlvbnNdKGh0dHA6Ly9vbmxpbmVzdGF0Ym9vay5jb20vMi9pbnRyb2R1Y3Rpb24vZGlzdHJpYnV0aW9ucy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9DQpbTm9ybWFsIERpc3RyaWJ1dGlvbl0oaHR0cDovL29ubGluZXN0YXRib29rLmNvbS8yL2ludHJvZHVjdGlvbi9kaXN0cmlidXRpb25zLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0NCltTa2V3ZWQgRGlzdHJpYnV0aW9uc10oaHR0cDovL29ubGluZXN0YXRib29rLmNvbS8yL2dsb3NzYXJ5L3NrZXcuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSANCltCaW1vZGFsIERpc3RyaWJ1dGlvbl0oaHR0cDovL29ubGluZXN0YXRib29rLmNvbS8yL2ludHJvZHVjdGlvbi9kaXN0cmlidXRpb25zLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gDQpbZ2dwbG90Ml0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1oZWFsdGhleHBlbmRpdHVyZS9vY3MtaGVhbHRoZXhwZW5kaXR1cmUuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgICANCltRLVEgUGxvdHNdKGh0dHA6Ly9vbmxpbmVzdGF0Ym9vay5jb20vMi9hZHZhbmNlZF9ncmFwaHMvcS1xX3Bsb3RzLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gIA0KW1N0dWRlbnQgJHQkLXRlc3RdKGh0dHBzOi8vc3RhdHRyZWsuY29tL3N0YXRpc3RpY3MvZGljdGlvbmFyeS5hc3B4P2RlZmluaXRpb249dHdvLXNhbXBsZSUyMHQtdGVzdCkgICANCltQYWlyZWQgRGF0YV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNTU3OTQ2NS8pe3RhcmdldD0iX2JsYW5rIn0gIA0KW1dlbGNoJ3MgJHQkLXRlc3RdKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5kYXRhc2NpZW5jZWNlbnRyYWwuY29tL3dlbGNocy10ZXN0LWZvci11bmVxdWFsLXZhcmlhbmNlcy8pe3RhcmdldD0iX2JsYW5rIn0gICAgDQpbUGFyYW1ldHJpYyBhbmQgTm9ucGFyYW1ldHJpYyBNZXRob2RzXShodHRwczovL3d3dy5tYXlvLmVkdS9yZXNlYXJjaC9kb2N1bWVudHMvcGFyYW1ldHJpYy1hbmQtbm9ucGFyYW1ldHJpYy1kZW15c3RpZnlpbmctdGhlLXRlcm1zL2RvYy0yMDQwODk2MCkgIA0KW1ZhcmlhbmNlXShodHRwczovL3N0YXR0cmVrLmNvbS9zdGF0aXN0aWNzL2RpY3Rpb25hcnkuYXNweD9kZWZpbml0aW9uPXZhcmlhbmNlKXt0YXJnZXQ9Il9ibGFuayJ9ICANCltCYWxhbmNlZCBTdHVkeSBEZXNpZ25dKGh0dHBzOi8vd3d3LnN0YXRpc3RpY3Nob3d0by5kYXRhc2NpZW5jZWNlbnRyYWwuY29tL2JhbGFuY2VkLWFuZC11bmJhbGFuY2VkLWRlc2lnbnMvKXt0YXJnZXQ9Il9ibGFuayJ9ICANCltJbmRlcGVuZGVudCBPYnNlcnZhdGlvbnNdKGh0dHBzOi8vd3d3LnN0YXQuY211LmVkdS9+Y3NoYWxpemkvMzYtMjIwL2xlY3R1cmUtNS5wZGYpe3RhcmdldD0iX2JsYW5rIn0gIA0KW1RyYW5zZm9ybWF0aW9uXShodHRwczovL3d3dy5zdGF0aXN0aWNzaG93dG8uZGF0YXNjaWVuY2VjZW50cmFsLmNvbS90cmFuc2Zvcm1hdGlvbi1zdGF0aXN0aWNzLyl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbUGVybXV0YXRpb24vUmVzYW1wbGluZyBNZXRob2RzXShodHRwczovL2podS1hZHZkYXRhc2NpLmdpdGh1Yi5pby8yMDE5L2xlY3R1cmVzLzIxLXJlc2FtcGxpbmctdGVjaG5pcXVlcy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgDQpbQ2VudHJhbCBMaW1pdCBUaGVvcmVtXShodHRwczovL3d3dy5hbmFseXRpY3N2aWRoeWEuY29tL2Jsb2cvMjAxOS8wNS9zdGF0aXN0aWNzLTEwMS1pbnRyb2R1Y3Rpb24tY2VudHJhbC1saW1pdC10aGVvcmVtLyl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbTW9vZCdzIFR3by1TYW1wbGUgU2NhbGUgVGVzdF0oaHR0cHM6Ly9maWxlcy5lcmljLmVkLmdvdi9mdWxsdGV4dC9FRDA2NTU1OS5wZGYpe3RhcmdldD0iX2JsYW5rIn0gIA0KW1dpbGNveG9uIFNpZ25lZCBSYW5rIFRlc3RdKGh0dHA6Ly93d3cuYmlvc3RhdGhhbmRib29rLmNvbS93aWxjb3hvbnNpZ25lZHJhbmsuaHRtbCkgICANCltXaWxjb3hvbiBSYW5rIFN1bSBUZXN0XShodHRwOi8vc3Bod2ViLmJ1bWMuYnUuZWR1L290bHQvbXBoLW1vZHVsZXMvYnMvYnM3MDRfbm9ucGFyYW1ldHJpYy9CUzcwNF9Ob25wYXJhbWV0cmljNC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICANCltUd28tc2FtcGxlIEtvbG1vZ29yb3YtU21pcm5vdiBUZXN0XShodHRwczovL3d3dy5pdGwubmlzdC5nb3YvZGl2ODk4L3NvZnR3YXJlL2RhdGFwbG90L3JlZm1hbjEvYXV4aWxsYXIva3Myc2FtcC5odG0pe3RhcmdldD0iX2JsYW5rIn0gIA0KW1R5cGUgMSBFcnJvcl0oaHR0cHM6Ly93ZWIubWEudXRleGFzLmVkdS91c2Vycy9ta3Mvc3RhdG1pc3Rha2VzL2Vycm9ydHlwZXMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbcC12YWx1ZV0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3AtdmFsdWVzLWV4cGxhaW5lZC1ieS1kYXRhLXNjaWVudGlzdC1mNDBhNzQ2Y2ZjOCl7dGFyZ2V0PSJfYmxhbmsifSAgDQpbTXVsdGlwbGUgVGVzdGluZ10oaHR0cHM6Ly93d3cuZ3Mud2FzaGluZ3Rvbi5lZHUvYWNhZGVtaWNzL2NvdXJzZXMvYWtleS81NjAwOC9sZWN0dXJlL2xlY3R1cmUxMC5wZGYpe3RhcmdldD0iX2JsYW5rIn0gICAgDQpbQm9uZmVycm9uaSBNZXRob2Qgb2YgTXVsdGlwbGUgVGVzdGluZyBDb3JyZWN0aW9uXShodHRwOi8vbWF0aHdvcmxkLndvbGZyYW0uY29tL0JvbmZlcnJvbmlDb3JyZWN0aW9uLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0NCg0KPHU+KipQYWNrYWdlcyB1c2VkIGluIHRoaXMgY2FzZSBzdHVkeToqKiA8L3U+DQoNCiBQYWNrYWdlICAgfCBVc2UgaW4gdGhpcyBjYXNlIHN0dWR5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQ0KW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSl7dGFyZ2V0PSJfYmxhbmsifSAgICAgICB8IHRvIGVhc2lseSBsb2FkIGFuZCBzYXZlIGRhdGEgd2l0aCByZWxhdGl2ZSBwYXRocw0KW3BkZnRvb2xzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcGRmdG9vbHMvcGRmdG9vbHMucGRmKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byByZWFkIGEgdGV4dCBmcm9tIHBkZiBpbnRvIFIgICANCltzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9zdHJpbmdyLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICAgfCB0byBtYW5pcHVsYXRlIHRoZSB0ZXh0IGRhdGENCltyZWFkcl0oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gbWFuaXB1bGF0ZSB0aGUgdGV4dCBkYXRhIHdpdGhpbiB0aGUgcGRmIGludG8gaW5kaXZpZHVhbCBsaW5lcyAgDQpbZHBseXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFycmFuZ2UvZmlsdGVyL3NlbGVjdCBzdWJzZXRzIG9mIHRoZSBkYXRhIA0KW3RpYmJsZV0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgIHwgdG8gY3JlYXRlIGRhdGEgb2JqZWN0cyB0aGF0IHdlIGNhbiBtYW5pcHVsYXRlIHdpdGggYGRwbHlyYC9gc3RyaW5ncmAvYHRpZHlyYC9gcHVycnJgDQpbbWFncml0dHJdKGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9tYWdyaXR0ci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byB1c2UgdGhlIGAlPD4lYCBwaXBpbmcgb3BlcmF0b3INCltnbHVlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnL2Jsb2cvMjAxNy8xMC9nbHVlLTEuMi4wLyl7dGFyZ2V0PSJfYmxhbmsifSAgfCB0byBwYXN0ZSBvciBjb21iaW5lIGNoYXJhY3RlciBzdHJpbmdzIGFuZCBkYXRhIHRvZ2V0aGVyDQpbcHVycnJdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHBlcmZvcm0gZnVuY3Rpb25zIG9uIGFsbCBjb2x1bW5zIG9mIGEgdGliYmxlDQpbdGlkeXJdKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGNvbnZlcnQgZGF0YSBmcm9tICd3aWRlJyB0byAnbG9uZycgZm9ybWF0DQpbZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSAgICB8IHRvIG1ha2UgdmlzdWFsaXphdGlvbnMgd2l0aCBtdWx0aXBsZSBsYXllcnMNCltnZ3JlcGVsXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyZXBlbC92aWduZXR0ZXMvZ2dyZXBlbC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gYWxsb3cgbGFiZWxzIGluIGZpZ3VyZXMgbm90IHRvIG92ZXJsYXANCltjb3dwbG90XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY293cGxvdC92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gYW5kIFtwYXRjaHdvcmtdKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvcGF0Y2h3b3JrKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gYWxsb3cgcGxvdHMgdG8gYmUgY29tYmluZWQgDQoNCg0KIyMjICoqU2Vzc2lvbiBpbmZvKioNCioqKg0KDQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQoqKkVzdGltYXRlIG9mIFJNYXJrZG93biBDb21waWxhdGlvbiBUaW1lOiAqKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnJtYXJrZG93bjo6OnBlcmZfdGltZXJfc3RvcCgicmVuZGVyIikNCnB0cyA9IHJtYXJrZG93bjo6OnBlcmZfdGltZXJfc3VtbWFyeSgpDQpjYXQoIkFib3V0Iiwgcm91bmQocHRzJHRpbWVbMV0vMTAwMCArIDUpLCAiLSIsIHJvdW5kKHB0cyR0aW1lWzFdLzEwMDAgKyAxNSksInNlY29uZHMiKQ0KYGBgDQoNClRoaXMgY29tcGlsYXRpb24gdGltZSB3YXMgbWVhc3VyZWQgb24gYSBQQyBtYWNoaW5lIG9wZXJhdGluZyBvbiBXaW5kb3dzIDEwLiBUaGlzIHJhbmdlIHNob3VsZCBvbmx5IGJlIHVzZWQgYXMgYW4gZXN0aW1hdGUgYXMgY29tcGlsYXRpb24gdGltZSB3aWxsIHZhcnkgd2l0aCBkaWZmZXJlbnQgbWFjaGluZXMgYW5kIG9wZXJhdGluZyBzeXN0ZW1zLiANCg0KIyMjICoqQWNrbm93bGVkZ21lbnRzKioNCioqKg0KDQpXZSB3b3VsZCBsaWtlIHRvIGFja25vd2xlZGdlIFtKZXNzaWNhIEZhbnpvXShodHRwczovL3d3dy5qaHNwaC5lZHUvZmFjdWx0eS9kaXJlY3RvcnkvcHJvZmlsZS8zMzgwL2plc3NpY2EtZmFuem8pIGZvciBhc3Npc3RpbmcgaW4gZnJhbWluZyB0aGUgbWFqb3IgZGlyZWN0aW9uIG9mIHRoZSBjYXNlIHN0dWR5Lg0KDQpXZSB3b3VsZCBsaWtlIHRvIGFja25vd2xlZGdlIFtNaWNoYWVsIEJyZXNob2NrXShodHRwczovL21icmVzaG9jay5naXRodWIuaW8vKSBmb3IgaGlzIGNvbnRyaWJ1dGlvbnMgdG8gdGhpcyBjYXNlIHN0dWR5IGFuZCBkZXZlbG9waW5nIHRoZSBgT0NTZGF0YWAgcGFja2FnZS4NCg0KV2Ugd291bGQgYWxzbyBsaWtlIHRvIGFja25vd2xlZGdlIHRoZSBbQmxvb21iZXJnIEFtZXJpY2FuIEhlYWx0aCBJbml0aWF0aXZlXShodHRwczovL2FtZXJpY2FuaGVhbHRoLmpodS5lZHUvKSBmb3IgZnVuZGluZyB0aGlzIHdvcmsuIA0KDQo8c2NyaXB0IHR5cGU9J3RleHQvamF2YXNjcmlwdCcgaWQ9J2NsdXN0cm1hcHMnIHNyYz0nLy9jZG4uY2x1c3RybWFwcy5jb20vbWFwX3YyLmpzP2NsPTA4MDgwOCZ3PWEmdD10dCZkPWExRXdzMmVIRVdsWTh2OElGSkhXVEVSZWdZWmZNbXR5Q3hQaVl1NXk5RE0mY289ZmZmZmZmJmNtbz0zYWNjM2EmY21uPWZmNTM1MyZjdD04MDgwODAnPjwvc2NyaXB0Pg0KDQo=