1 Intro and Overview

1.1 Pre-requisites

1.1.1 R and RStudio

Make sure that R and RStudio are installed in your computer. You can create a new R Script in RStudio to copy example source code from this tutorial and write code for the exercises. Save the script in your home directory (or a sub directory of your choice). To run the code written on the current line (or of selected lines), click on button Run or press CTRL+ENTER. Results will be displayed either in the RStudio’s console or the Viewer panel (graphics). To read the documentation about a function (Help panel), in the script, place the cursor on the function’s name and press F1.

1.1.2 The tidyverse

This tutorial is based on a powerful set of packages for data manipulation and visualization known as the tidyverse. We will also need the readxl package to enable reading data from Microsoft Excel files. So, make sure that those 2 packages (tidyverse and readxl) have been installed in your R environment (see RStudio’s menu Tools/Install Packages…).

1.2 Introduction

Because of the ever-growing number of publicly available and large datasets in life sciences, computational and statistical skills are becoming a key part of the life scientist’s curriculum.

The goal of this tutorial is to give you a foundation in the most important tools for data analysis in R. You can then complement and expand your knowledge on your own in order to perform more complex analyses.

This tutorial is an adaptation of Dr. Alanis-Lobato’s tutorial that borrows materials and concepts from the book R for Data Science by Garret Grolemund and Hadley Wickham.

1.3 Overview

In most data analysis projects you will have to do as shown below. The tidyverse set of packages include functions to tackle each one of the parts of the data analysis process.

1.3.1 Import

The first step in data science is to import your data into R in order to manipulate it with R functions. This means that you will take data stored in a file, database or website and load it into a variable in R. These datasets can be the output of a genomics project, the result of a survey, measurements done in the lab, etc. In this tutorial, you will use functions from the readr and readxl packages to load data tables into R.

1.3.2 Tidy

Second, you will have to tidy up your data. Briefly, in a tidy dataset each row is an observation or a sample and each column is a variable. If your data is tidy and has a consistent structure, you will be able to use it seamlessly throughout the tidyverse functions for analysis, manipulation and visualization. In this tutorial, you will use functions from the tidyr package to tidy up your data.

1.3.3 Transform

Once your data is tidy, often times you need to transform it. Common transformations include normalization, the creation of new variables from existing ones (e.g. convert from inches to centimeters) or the computation of summary statistics (e.g. means and counts). In this tutorial, you will use functions from the dplyr package (the d comes from data-frame and plyr from “pliers”, the pincers for gripping small objects or bending wire) to perform data transformations.

1.3.4 visualize and model

Armed with tidy data that has the variables you need, you are ready for knowledge generation. Good data graphics will show you things that you did not know about the data, they will also show you things you did not expect, raise more questions or tell you that you are asking the wrong questions. In this tutorial, you will generate plots to gain a better understanding of your data. This will be done with functions from the ggplot package.

Models are complementary to visualizations. Models are mathematical representations of a system that are often derived from precise data analyzes and observations. Models are key to making predictions that can be later tested in the lab.

Even though there are powerful tools in R for data modeling, in this tutorial you will only propose simple models based on visualizations.

It is important to stress that visualization and modeling will often derive in new questions and hypotheses that will bring you back to the data transformation step. With new variables and stats, you can visualize again and fine tune your model.

1.3.5 Program

Surrounding all the above steps is programming, a critical part of any data analysis project. You do not need to be an expert programmer to analyze data, but programming skills allow you to automate common tasks and solve new problems more easily. There are hundreds of programming languages out there, but R and its rich collection of packages, functions and data are particularly useful when it comes to doing statistics and data analysis.

1.3.6 Communicate

Cool graphics and precise models are of no use if you cannot communicate your results to others. Communication of methods and results can be done via oral or poster presentations, theses, scientific articles, reports, etc. There are tons of tools for communication of data analyzes, from PowerPoint or Word to web pages and R Markdown. These interactive notes have been written with the latter.

1.4 Example data analysis pipeline

To motivate your interest for data analysis and give you a glimpse of how easy it is to do it in the tidyverse, the following lines will guide you through an example project aimed at automating the classification of iris flowers.

The example will go through all the steps covered above and highlight some of the functions that you will learn and use in this tutorial. Don not freak out about the code or the data analysis itself. Everything will be explained in more detail in the following Parts.

1.4.1 The iris flower dataset

The iris flower dataset was collected by Edgar Anderson, an American botanist, in the 1920s. This data was used by statistician Ronald Fisher to demonstrate statistical methods of classification. You may know it already, as it is still quite popular among statistical and programming teachers.

The original dataset contains 150 samples from three iris species. You are going to work with a reduced version with only 100 samples from two species: Iris setosa and Iris virginica.

For each sample, Anderson recorded the sepal length and width, as well as the petal length and width:

Figure: Iris Flower and Anderson’s measurements (taken from Kaggle.com)

Given an iris flower, is it possible to classify it as setosa or virginica based on these measurements?

Before starting our analysis, we need to load the tidyverse and ggvis packages:

library(tidyverse)
library(readxl)

As shown below, by calling some functions such as library, warnings and technical messages may be displayed on the console. Although they could provide the user with useful information, we will focus on the ‘normal’ output in this tutorial (i.e. tables, figures, and vectors).

# -- Attaching packages --------------------------------------- tidyverse 1.3.0 --
# v ggplot2 3.2.1     v purrr   0.3.3
# v tibble  2.1.3     v dplyr   0.8.3
# v tidyr   1.0.2     v stringr 1.4.0
# v readr   1.3.1     v forcats 0.4.0
# -- Conflicts ------------------------------------------ tidyverse_conflicts() --
# x dplyr::filter() masks stats::filter()
# x dplyr::lag()    masks stats::lag()
# Registered S3 methods overwritten by 'htmltools':
#   method               from         
#   print.html           tools:rstudio
#   print.shiny.tag      tools:rstudio
#   print.shiny.tag.list tools:rstudio
# 
# Attaching package: ggvis
# 
# The following object is masked from package:ggplot2
# 
#     resolution

1.4.2 Importing the iris dataset

We will import the iris.tsv data file. The file extension tells us that this is a “tab-separated value” type of file (.tsv) that stores a table of data. The readr package from the tidyverse has function read_tsv to import them into R. If on the local computer, the function will need the file path (e.g. “datasets/iris.tsv”). If on Internet, we will provide the function with the URL.

# example if on local computer
# ir <- read_tsv("datasets/iris.tsv") 

# Using an URL
ir <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/iris.tsv")
# Parsed with column specification:
# cols(
#   ratio_sl_sw = col_character(),
#   ratio_pl_pw = col_character(),
#   species = col_character()
# )

Functions that load data usually display messages in the R console, such as shown above to notify which data type is set by column of the loaded table.

Variable ir now contains the iris flower data table.

ir

Note that in the R console, only a text version of the table will be displayed.

1.4.3 Tidying the iris dataset

Remember that tidy datasets report each sample in its own row and each variable in its own column. The iris dataset reports sepal length and width and petal length and width as ratios in columns ratio_sl_sw and ratio_pl_pw, respectively. We can tidy up this table with function separate from package tidyr:

ir <- ir %>% 
   separate(col = ratio_sl_sw, 
            into = c("sepal_length", "sepal_width"), 
            sep = "/") %>% 
   separate(col = ratio_pl_pw, 
            into = c("petal_length", "petal_width"), 
            sep = "/")
ir

1.4.4 Transforming the iris dataset

Note that the width and length columns in the tidied iris dataset are of type character (chr) instead of numeric (dbl or int). If we want to compute sums or other statistics on this table, we need to transform these columns into the appropriate type. We can do this with function mutate from package dplyr:

ir <- ir %>% 
   mutate(sepal_length = as.numeric(sepal_length),
          sepal_width  = as.numeric(sepal_width),
          petal_length = as.numeric(petal_length),
          petal_width  = as.numeric(petal_width))

1.4.5 visualizing the iris dataset

Let us now plot histograms for each flower feature to see if there is one that can be used to classify the two iris species:

# sepal length
ir %>% 
  ggplot(aes(x=sepal_length, fill = species)) + 
  geom_histogram(binwidth = 0.15)

# sepal width
ir %>% 
  ggplot(aes(x=sepal_width, fill = species)) + 
  geom_histogram(binwidth = 0.15)

# petal length
ir %>% 
  ggplot(aes(x=petal_length, fill=species)) + 
  geom_histogram(binwidth = 0.15)

# petal width
ir %>% 
  ggplot(aes(x=petal_width, fill=species)) + 
  geom_histogram(binwidth = 0.15)

1.4.6 modeling

Thanks to the above plots, we know that we can use petal length and width to differentiate between a flower from the species Iris setosa and the species Iris virginica. Under the assumption that we are only dealing with flowers from these two species, let us use our observations to propose a simple model for iris flower classification:

# the model is implemented with a function that takes 2 parameters
which_species <- function(petal_length, petal_width){

  if (petal_length <= 3 && petal_width <= 1){
    print("Iris setosa")
    }
  else{
    print("Iris virginica")
  }

}
# We can call the function with 2 specific values to get an answer
which_species(petal_length=2, petal_width=0.5)
[1] "Iris setosa"
which_species(petal_length=10, petal_width=0.5)
[1] "Iris virginica"

2 Import and the tibble

2.1 Importing data into R

In Part 1, you saw that the first step in any data analysis pipeline is importing your tables into R. You do this to take advantage of R’s functions, which simplify the manipulation of your data.

There are two packages in the tidyverse that make data import into R a matter of a single line of code.

Package readr provides functions to import data tables stored in plain-text files, such as “comma-separated value” (.csv) or “tab-separated value” (.tsv) files.

Package readxl provides functions to import data stored in Microsoft Excel files. These functions also let you import from specific sheets or ranges within the Excel file.

2.2 Importing from plain-text files

To demarcate the content of each cell in a file, people use special characters like commas, tabs, pipes, etc. The extension of plain-text files is often a good hint of what delimiter has been used. For example, csv files should use “commas” as the delimiter, whereas tsv files should use “tabs”. Nevertheless, you can find csv or tsv files that are delimited with different characters.

The first step in data import is locating the file in your computer or in a remote location (e.g. by URL). The most important parameter for readr’s functions is the path to the file of interest.

In this part, you are going to import files located on internet. Packages tidyverse and readxl must be loaded.

2.2.1 Importing a tsv file

Let’s start by learning how to import a tsv file:

who_ref <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who_ref.tsv")
who_ref

2.2.2 Importing a csv file

Importing a csv file is very similar:

who1 <- read_csv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who1.csv")
who1

Note how simple the syntax for data import is: you choose the name of a variable to store your table, use the <- operator (written with 2 characters: < and -), call the appropriate readr function and specify the location of your file.

2.2.3 Make the example data locally available

So far, we used URLs to access the datasets used for the examples. However, you can also download them to save them locally and work with the files directly. There is a folder online, available for download via this link: “http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/datasets.zip”. Download it, unzip it and save the files under “datasets”. You should also use an R script (or several scripts) to write the code for the upcoming examples. Put such scripts in an appropriate folder together with the “datasets” folder. If you then set your “Working Directory” to the location of your R scripts, you can access the example files wih a direct path like this “./datasets/andrade_lab.csv” (see next small exercise).

To change your “Working Directory”, go to “Session” -> “Set Working Directory” -> “To source file location”

2.2.4 Exercise 2.1

Read file ./datasets/andrade_lab.csv into variable al and show the content of the variable.

2.2.5 Importing a text file

If the column delimiter of a file is not obvious from its extension and the person in charge of the file didn’t provide this information, you must first figure out which delimiter was used. For example, open the following file ./datasets/who2.txt.

Since the delimiter is a -, we need to call read_delim to import:

who2 <- read_delim("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who2.txt", delim = "-")
who2

Note that in this special case, you have to specify the delimiter using parameter delim.

2.2.6 Exercise 2.2

From its extension, file ./datasets/andrade_lab.tsv is supposed to be tab-separated. Read the file with function read_tsv into variable al and show its contents. What character is the delimiter in this file? Use the appropriate readr function to import this table into R.

2.3 Importing from Excel files

The syntax to import from Excel files is very similar to the one used above. By default, function read_excel imports from the first available sheet in the Excel workbook.

Load a EXCEL sheet via the following path in your lacal folder. Open the file also with EXCEL to see its content.

./datasets/who_split.xlsx

who_cases <- read_excel("datasets/who_split.xlsx") # will load first sheet named 'cases'
who_cases

If you want data from a different sheet, you must use parameter sheet:

who_pop <- read_excel("datasets/who_split.xlsx", 
                      sheet = "population")
who_pop

You can also specify a range to import from:

who_pop_99 <- read_excel("datasets/who_split.xlsx", 
                         sheet = "population",
                         range = "A1:B4")
who_pop_99

2.3.1 Exercise 2.3

Copy the following file to the datasets subfolder: ./datasets/andrade_lab.xlsx

Import the list of PhD students in Andrade Lab from the file. Choose a variable name to store the file contents.

2.4 The tibble

Reading files with readr or readxl functions results in the creation of a variable of type tbl_df instead of R’s traditional data frame (type data.frame). This is because one of the unifying features of the tidyverse is the tibble (type tbl_df).

# Using base R function to import CSV files
who1_df <- read.csv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who1.csv",
                    stringsAsFactors=TRUE)
class(who1_df)
[1] "data.frame"
# Using readr function to import CSV files
who1_tb <- read_csv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who1.csv")
class(who1_tb)
[1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame" 

tibbles are data frames but they tweak some old behaviors that make our life easier when analyzing data.

To start with, printing the content of a tibble shows a rich output that gives information about the data type of each column. By contrast, data frames just show the records as is:

who1_tb

Also, tibbles never change the type of the inputs. While column country is of type character in who1_tb, it could be imported as a factor in who1_df depending on the R version and settings. For the sake of this demonstration, we used option stringsAsFactors=TRUE when importing the data frame with the base R function read.csv. Except if required, I recommend users importing data with base R functions such as read.csv to set this option to FALSE (stringsAsFactors=FALSE).

class(who1_tb$country)
[1] "character"
class(who1_df$country)
[1] "factor"

Factor can be problematic to some users (especially beginners): in the data frame, country cannot take other values other than the ones already imported. If we want to append information about a new country to our table, it will be added but as an NA (Not Applicable):

# Adding a new row
tmp <- rbind(who1_df, c("Mexico", 1999, "cases", 100))
Warnung: ungültiges Faktorniveau, NA erzeugt
# Printing the bottom of the table (country is "NA" on last row)
tmp %>% tail()

Since packages outside the tidyverse use data frames, you might want to coerce them to tibbles with function as_tibble:

who1_df <- as_tibble(who1_df)

In addition, it’s possible for a tibble to have column names that are not valid R variable names that are limited to specific rules. For example, they might not start with a letter, or they might contain unusual characters:

unusual <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/unusual.tsv")
unusual

To refer to these column names, you have to use back-ticks:

unusual$`:)`
[1] "smile"

Finally, you can create your own tibbles with function tibble:

stocks <- tibble(
  year    = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
  qtr     = c(   1,    2,    3,    4,    2,    3,    4),
  revenue = c(1.88, 0.59, 0.35,   NA, 0.92, 0.17, 2.66)
)
stocks

3 Tidy data

3.1 Foreword

The rationale behind the tidy data philosophy is to organize the data you imported to or created in R into a consistent format. The goal is to spend less time formatting your data and more time analyzing it. If your data is tidy you will be able to use it seamlessly across the tidyverse.

This part will show you how to use the functions from package tidyr to tidy up your data. As a result, data transformations and visualizations will be a real piece of cake.

3.2 What is tidy data?

There are three simple and interrelated rules that make a dataset tidy:

  1. Each observation/sample must be reported in its own row
  2. Each variable/feature describing the sample must have its own column
  3. Each value must have its own cell

We will import some tables and check if they fulfill the above rules.

3.2.1 Exercise 3.1

Import file ./datasets/who1.csv into variable who1 and show the imported data. Is the who1 dataset tidy?

3.2.2 Exercise 3.2

Import file ./datasets/who2.txt into variable who2 and show the imported data. Is the who2 dataset tidy?

3.2.3 Exercise 3.3

Import sheet population from file datasets/who_split.xlsx into variable who_pop and show the imported data. Is who_pop tidy?

3.2.4 Why tidy data?

Why ensure that your data is tidy? There are two main advantages:

  1. If you have a consistent data structure, it’s easier to learn the tools that work with it because they have an underlying uniformity. The functions in the tidyverse all work flawlessly with tidy datasets.
  2. Placing variables in columns allows R’s vectorised nature to shine.

The principles of tidy data seem very obvious. Unfortunately, most data that you will encounter will be untidy. There are two main reasons:

  1. Most people aren’t familiar with the principles of tidy data.
  2. Data is often organized to facilitate some use other than analysis.

This means that for most real analyzes, you will need to tidy your data.

3.3 Spreading and gathering

When you are in front of a dataset, the first step is always to understand what the observations and the features that describe them are. The second step is to solve one of two common problems:

  1. One observation is scattered across multiple rows
  2. One feature is spread across multiple columns

To fix these problems, the tidyverse provides you with functions spread and gather.

3.3.1 Spreading

You use function spread for Problem 1: one observation is scattered across multiple rows. That’s exactly the problem we saw in table who1:

who1

who1 is an excerpt of the World Health Organization Tuberculosis Report. Each observation, a country in a year in this case, should be accompanied by that year’s number of tuberculosis cases and population. However, each country-year pair appears twice in who1: one for the tuberculosis cases and one for the country’s population.

To tidy up this dataset, we need to spread column count into new columns, one for each of the keys specified in column type:

Figure: Adapted from “R for Data Science”, Chapter 12

We can do this with function spread as follows:

who1_tidy <- who1 %>% 
   spread(value = count, key = type)
who1_tidy

Note the syntax used to tidy up who1:

  1. Choose a variable name to store the tidy data (this could very well be who1 again).
  2. Use the assignment operator <-.
  3. Specify the variable that we want to tidy up.
  4. Use the pipe operator %>% to channel the content of who1 to the function spread (more on the pipe operator later in this Part).
  5. Call function spread and specify the column containing the values that we want to spread and the column containing the new variable names (known as the key column).

3.3.2 Gathering

You use function gather for Problem 2: one feature is spread across multiple columns. That is exactly the problem we saw in, for example, table who_cases from the Excel file who_split.xlsx:

who_cases

The column names 1999 and 2000 are actually values of the variable year, which means that each row represents two observations instead of one.

To tidy this dataset, we need to gather columns 1999 and 2000 into a new pair of variables:

Figure: Adapted from “R for Data Science”, Chapter 12

We can do this with function gather as follows:

who_cases_tidy <- who_cases %>% 
   gather(`1999`, `2000`, key = "year", value = "cases")
who_cases_tidy

Note the syntax used to tidy up who_cases:

  1. Choose a variable name to store the tidy data (this could very well be who_cases again).
  2. Use the assignment operator <-.
  3. Specify the variable that we want to tidy up.
  4. Use the pipe operator %>% to channel the contents of who_cases to function gather.
  5. Call function gather and specify the columns we want to merge, the name of the column that will contain the merged keys and the name of the column that will contain the value for the number of tuberculosis cases per country.

3.3.3 Exercise 3.4

Consider the following tibble, which records heights and weights of 3 students:

students <- tibble(name = rep(c("Jonas", "Ines", "Hanna"), each = 2),
                   type = rep(c("height", "weight"), 3),
                   measure = c(1.83, 81, 1.75, 71, 1.69, 55))
students

Use the appropriate tidyr function on variable students to make it tidy.

3.3.4 Exercise 3.5

Consider the following tibble, which records the expression of 4 genes at 3 different time points (d00, d02 and d04):

genes <- tibble(symbol = c("DMD", "MYOG", "MYF5", "MYOD1"),
                d00 = c(0.697, 0.844, 1.878, 1.622),
                d02 = c(1.986, 0.051, 0.887, 1.313),
                d04 = c(0.157, 0.774, 1.507, 0.628))
genes

Here, an observation should be a gene at a given time point. Use the appropriate tidyr function on variable genes to make it tidy.

3.4 Separating and uniting

3.4.1 Separating

When one column in a dataset contains two variables, we’ll need to use function separate to fix the issue. That’s exactly the problem we saw in table who2:

who2

Column rate contains both cases and population, so we need to split it in two:

Figure: Adapted from “R for Data Science”, Chapter12

We can do this with function separate as follows:

who2_tidy <- who2 %>% 
   separate(rate, into = c("cases", "population"), sep = "/")
who2_tidy

Note the syntax used to tidy up who2:

  1. Choose a variable name to store the tidy data (this could very well be who2 again).
  2. Use the assignment operator <-.
  3. Specify the variable that we want to tidy up.
  4. Use the pipe operator %>% to channel the contents of who2 to function separate.
  5. Call function separate and specify the column we want to split, the name of the new columns and the separator that is currently merging the variables.

3.4.2 Uniting

When a variable is stored in two separate columns and is more convenient to combine them, we need to use function **unite. Table who3* has this problem:

who3 <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who3.tsv")
who3

Column century and year can be combined into a single column called year:

Figure: Adapted from “R for Data Science”, Chapter12

We can do this with function unite as follows:

who3_tidy <- who3 %>% 
   unite(century, year, col = "year", sep = "")
who3_tidy

Note the syntax used to tidy up who3:

  1. Choose a variable name to store the tidy data (this could very well be who3 again).
  2. Use the assignment operator <-.
  3. Specify the variable that we want to tidy up.
  4. Use the pipe operator %>% to channel the contents of who3 to function unite.
  5. Call function unite and specify the columns we want to merge, the name of the new column and the separator that we want to use to merge the column values.

3.4.3 Exercise 3.6

Consider the following tibble, which records heights and weights of 3 students:

students <- tibble(name = c("Jonas", "Ines", "Hanna"),
                   ratio = c("81/1.83", "71/1.75", "55/1.69"))
students

Use the appropriate tidyr function on variable students to make it tidy.

3.4.4 Exercise 3.7

Consider the following tibble, which records the price of 4 drugs:

drugs <- tibble(name = c("penicillin", "insuline", "aspirin", "lanoxin"),
                euros = c(13, 17, 5, 25),
                cents = c(81, 20, 14, 12))
drugs

Use the appropriate tidyr function on variable drugs to make it tidy.

3.5 The %>% operator

As you know already, we channel the contents of a tibble to the different tidyr functions using the pipe operator: %>%.

Pipes are a powerful tool for clearly expressing the operation we want to perform on a variable. In addition, they can be used to apply a sequence of operations to a variable.

Table who3, for example, has two problems: cases and population are expressed as a rate and year is split in columns century and year:

who3

So, we clearly need two steps to tidy up this dataset:

who3_tidy <- who3 %>% 
   separate(rate, into = c("cases", "population"), sep = "/")

who3_tidy <- who3_tidy %>% 
   unite(century, year, col = "yyyy", sep = "")

who3_tidy

Thanks to the pipe, we can apply these two operations to who3 in one go:

who3_tidy <- who3 %>% 
  separate(rate, into = c("cases", "population"), sep = "/") %>% 
  unite(century, year, col = "yyyy", sep = "")
who3_tidy

Note that this process reads almost like natural language:

  1. We channel (or pipe) the content of who3 to function separate.
  2. separate splits column rate into columns cases and population using “/” as separator.
  3. The result of separate is piped to function unite.
  4. unite combines columns century and year into column yyyy using nothing (empty string ““) as separator.

Even though writing the above code on a single line of code is still valid, it is good practice to use ENTER (i.e. a new line) after every %>% for clarity.

4 Data transformation

4.1 Foreword

Once you have tidied your dataset of interest, you will often need to create some new variables or summaries, or to reorder the observations to make the data easier to work with.

This part will show you how to use the functions from package dplyr to transform your data and understand it better.

Throughout this part, you are going to use a tidied up excerpt from the World Health Organization Global Tuberculosis Report. We have been working with this dataset, so you should be familiar with it:

who_ref <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/who_ref.tsv")
who_ref

The tibble reports the number of tuberculosis cases in 1999 and 2000 for three different countries, as well as their population.

4.2 Sorting by column(s)

To sort observations by one or more columns, dplyr offers function arrange. This function takes a tibble and a column name to order by:

who_ref %>% 
  arrange(year)

If you provide more columns, arrange will first sort by the first one, then the second one and so on. This process breaks ties in the values of the preceding columns:

who_ref %>% 
  arrange(year, cases, population)

To sort in descending order, you have to use desc:

who_ref %>% 
  arrange(desc(country), cases)

4.3 Selecting columns

Function select allows you to focus on variables you are really interested in by removing unwanted columns:

who_ref %>% 
  select(country, cases)

You can also use the : operator to select a group of columns:

who_ref %>% 
  select(year:population)

If you put a minus sign before columns in select, it means that you want to discard such variables:

who_ref %>% 
  select(-population)

This, in combination with the : operator is specially useful when your dataset has hundreds or even thousands of columns and you want to focus only on a few:

who_ref %>% 
  select(-(year:population))

4.4 Renaming columns

If a column in your tibble has a strange or non-informative name, you can use function rename to solve this issue:

who_ref %>% 
  rename(tuberculosis_cases = cases)

4.5 Filtering rows

Function filter is one of the most useful tools in package dplyr as it allows you to subset observations based on their values. filter takes the contents of a tibble and its arguments are logical expressions to filter it.

For example, we can focus on tuberculosis cases in Brazil as follows:

who_ref %>% 
  filter(country == "Brazil")

To retrieve Chinese cases that occurred after 1999, we do:

who_ref %>% 
  filter(country == "China" & year > 1999)

To subset observations from Brazil or China:

who_ref %>% 
  filter(country == "Brazil" | country == "China")

Note that we use the comparison operators >, <, >=, <=, ==, != (not equal) to specify the column value or range of values we want to focus on. In addition, we use the logical operators & (and) and | (or) to combine multiple conditions passed on to filter.

You can negate conditions with the ! operator:

who_ref %>% 
  filter(!(country == "Brazil"))

Basically, any operation that generates a logical vector can be used within filter to subset your tibble.

4.6 Adding new variables

Besides selecting existing columns, it is often useful to create new ones that are functions of existing variables. dplyr offers function mutate to add new columns at the end of your dataset.

For example, it might be tempting to say that Afghanistan has better programmes against tuberculosis than Brazil and China. However, Afghanistan’s population is smaller. Let’s look at the number of cases per 10 thousand individuals to get a better picture:

who_ref %>% 
  mutate(cases_per_10k = cases/(population/10000))

mutate can be used to create one or more variables at once:

who_ref %>% 
  mutate(cases_per_10k = cases/(population/10000), thousand_cases = cases/1000)

4.7 Grouped operations

It is often the case that some records in your dataset belong to certain groups. For example, we can group data in who_ref by country or by year:

who_ref %>% 
  group_by(country)
who_ref %>% 
  group_by(year)

Note that grouping by country results in 3 groups, whereas grouping by year results in 2.

It is also possible to group by multiple variables:

who_ref %>% 
  group_by(country, year)

The result is 6 groups. This is not very useful in this particular example, because each observation belongs to its own group.

4.7.1 Grouped summaries

If we pipe the result of a group_by operation to the function summarise, the unit of analysis changes from the complete tibble to each individual group. This is very useful, because we can compute useful statistics and summaries on a per group basis.

The following example computes the total number of tuberculosis cases per year:

who_ref %>% 
  group_by(year) %>% 
  summarise(total_cases = sum(cases))

The following example computes the average number of cases per country and the corresponding standard deviation:

who_ref %>% 
  group_by(country) %>% 
  summarise(avg_cases = mean(cases), st_dev = sd(cases))

Note how summarise collapses each group to a single row and drops columns not involved in the grouping process. In fact, if we use summarise without grouping first, it sees the entire tibble as a single group and the result is a single row:

who_ref %>% 
  summarise(total_cases = sum(cases))

The above means that grouped summaries should be generated with functions that collapse their argument into a single value. Some useful summary functions are sum, mean, median, sd, min, max and n.

4.7.2 Grouped mutates

If we pipe the result of a group_by operation to the function mutate, we can create new variables on a per group basis.

Say, for example, that we want to compute the fraction of tuberculosis cases in a year from the total occurred in a country over all the years:

who_ref %>% 
  group_by(country) %>% 
  mutate(fraction = cases/sum(cases))

Note that grouped mutates do not collapse the input tibble. They maintain the original number of samples and columns. As a result, functions used to create new variables in mutate should produce the same number of members within the group.

4.7.3 Grouped filters

If we pipe the result of a group_by operation to the function filter, we can remove observations (i.e. rows) on a per group basis.

For example, to find the year with the minimum number of tuberculosis cases in a country, we can do:

who_ref %>% 
  group_by(country) %>% 
  filter(cases == min(cases))

As shown below, using summarise doesn’t work in this case because we lose the information about the year:

who_ref %>%
  group_by(country) %>% 
  summarise(min_cases = min(cases))

4.8 Case study

4.8.1 Exercise 4.1

In this case study, you’re going to work with a dataset giving the monthly deaths from lung diseases in the UK between 1974 and 1979.

  1. Import file ./datasets/uk_lung_deaths.csv into variable uk.
  2. Tidy up the dataset. The goal is to have a tibble with three columns: year, month and deaths.
  3. Use select to re-accommodate the columns, such that they appear in the following order: month, year and deaths.
  4. Data from 1974 is too old to be reliable, filter it out.
  5. Use the %>% to perform points 1-4 in a single go and assign the result to variable uk.
  6. Report the average number of deaths per month. Sort the result decreasingly by average deaths.
  7. Report the average and total number of deaths per year. Sort the result by year.
  8. For each month of a given year, calculate the percentage of deaths that the month represents in comparison to the total number of deaths in the given year.
  9. For each year, report the month with the maximum number of deaths. Sort the result by year.
  10. Speculate about possible reasons why the months resulting from point 9 have the most deaths from lung diseases.

5 visualization and modeling

5.1 Case study

The best way to show you the power of visualization for knowledge generation is by means of an example. In this part, you will use a dataset comprised of subjects who were part of a study aimed at understanding the prevalence of diabetes in central Virginia, USA. The variables describing the subjects are:

  • id: A numeric ID for each subject.
  • ratio_ch: Cholesterol/High Density Lipoprotein ratio.
  • stab.glu: Stabilised glucose.
  • glyhb: Glycosolated haemoglobin.
  • location: County of origin.
  • age: Age of the subject.
  • gender: Gender of the subject.
  • ratio_wh: Waist/Hip ratio.
  • body_measure: Which body measurement was recorded.
  • measure: Height or weight of the subject.

After tidying and transforming the “Diabetes Dataset”, you will visualize relationships between variables and feature distributions with functions from package ggvis in order to propose a simple model for susceptibility to diabetes.

You must load packages tidyverse and ggvis.

5.1.1 Exercise 5.1

  1. Import file ./datasets/diabetes.tsv into variable diab.
  2. Tidy up the imported tibble by piping a series of tidyr operations and assign the result to variable diab.
  3. Get rid of column id.
  4. Use mutate and as.numeric to transform character columns that should be numeric.
  5. Height, waist and hip measurements are given in inches, convert them to centimetres (1 inch = 2.54 cm).
  6. Weights are given in pounds, convert them to kilograms (1 pound = 0.454 kg).
  7. Glycosolated haemoglobin levels above 7.0 are usually taken as a positive diagnosis of diabetes. Create a new column called diagnosis that takes the value “diabetic” when a subject’s level is greater than 7.0 and “healthy” otherwise (HINT: use function ifelse).

Result should look like the following table:

5.2 Plotting with ggplot

In order to plot with package ggplot, we need the %>% operator to channel information into function ggplot. This creates a blank canvas on top of which we add layers of information and polish our plot. Note that, The ggplot functions are chained by the + operator!

diab %>%
  ggplot(aes(x = age, y = chol)) +
  geom_point() +
  xlab("Subject's age") +
  ylab("Cholesterol Level")

The above example feeds function ggplot with the diab table, indicates that we want to compare age with cholesterol levels (aes stands for aesthetic), uses geom_points to generate a scatter plot with the data and polishes the plot by providing custom axis labels (xlab and ylab).

You will see that following this syntax allows you to generate many different types of plots. It is just a matter of changing the layer function or adding more layers to your plot:

diab %>%
  ggplot(aes(x = age, y = chol )) +
  geom_point(shape=23, color='red') +
  geom_smooth() +
  xlab("Subject's age") +
  ylab("Cholesterol Level")

See how the above example adds a geom_smooth layer on top of geom_point. Also, note that it is possible to further polish the plot via parameters that are specific to each type of layer. For example, we can change the color and shape of points through parameters color and shape of function geom_point.

5.3 Bar plots

Bar plots are useful when we want to graphically show counts of a discrete or categorical variable. For example, to see the number of male and female subjects in diab we do:

diab %>%
  ggplot(aes(x=gender)) +
  geom_bar(width = 0.5) +
  xlab("Gender") +
  ylab("Number of subjects")

Note how the width parameter of geom_bar has been used to plot narrow bars.

We can also see the number of diabetic and healthy subjects:

diab %>%
  ggplot(aes(x=diagnosis)) +
  geom_bar(width = 0.5) +
  xlab("Diagnosis") +
  ylab("Number of subjects")

Finally, we can also use the y-axis of bar plots to show information other than counts. For example, we can contrast the average cholesterol levels of men and women:

diab %>% 
  group_by(gender) %>% 
  summarise(avg_chol = mean(chol)) %>% 
  ggplot(aes(x = gender, y = avg_chol)) + 
  geom_col(width = 0.5) +
  xlab("Gender") +
  ylab("Avg. cholesterol")

Note how data transformations can be applied on the fly and piped to ggplot.

5.4 Scatter plots and regressions

Scatter plots allow us to study the relationships between two numerical variables and regressions try to find the curve that best describes how x is related to y.

Let’s see how weight and waist measurements are related:

diab %>% 
  ggplot(aes(x = weight, y = waist)) + 
  geom_point() + 
  geom_smooth() + 
  xlab("Subject's weight") + 
  ylab("Waist measurement")

In the above example, geom_point is in charge of the scatter plot and geom_smooth finds a curve that follows the points and summarizes the observed trend. By default, a standard error grey band is shown to indicate how much we can trust the blue regression curve.

If we want to have more control over the type of curve that is fitted to the points, we can use the method parameter. For example, to fit a straight line to the above plot, we have to use a linear regression model (“lm”):

diab %>% 
  ggplot(aes(x = weight, y = waist)) + 
  geom_point() + 
  geom_smooth(method = "lm") + 
  xlab("Subject's weight") + 
  ylab("Waist measurement")

Even though a scatter plot is intrinsically in 2 dimensions, we can add more layers of information to learn more about our data. For example, we can color our points according to the subject’s diagnosis and adjust their size by glyhb:

diab %>% 
  ggplot(aes(x = weight, y = waist, color = diagnosis, size = glyhb)) + 
  geom_point() + 
  xlab("Subject's weight") + 
  ylab("Waist measurement")

5.5 Line graphs

Line plots are similar to scatter plots, but the variable in the x-axis is sorted and points are connected in order with a line:

diab %>% 
  ggplot(aes(x = waist, y = hip)) +
  geom_line() +
  xlab("Subject's weight") + 
  ylab("Waist measurement")

It is very common to add a point layer on top of line plots:

diab %>% 
  ggplot(aes(x = waist, y = hip)) +
  geom_line() +
  geom_point() +
  xlab("Subject's weight") + 
  ylab("Waist measurement")

5.6 Box plots

Box plots are very powerful statistical tools that summarize the most important aspects of a variable’s distribution:

The x-axis in a box plot is usually a categorical variable and the y-axis is normally a numeric variable who’s distribution we want to explore. For example, let’s visualize the distribution of High Density Lipoprotein values for males and females in diab:

diab %>% 
  ggplot(aes(x = gender, y = hdl)) +
  geom_boxplot() +
  xlab("gender") + 
  ylab("HDL level ")

5.7 Histograms and densities

Histograms work on a single variable and show how it is distributed by dividing its range into a certain number of bins. Thus, the x-axis reports the values that the variable can take (binned) and the y-axis reports the number or fraction of observations that take such values.

Let’s look at the distribution of Stabilized glucose values, specifying the width of the histogram bins with parameter binwidth:

diab %>%
  ggplot(aes(x = stab.glu)) +
  geom_histogram(binwidth = 15) +
  xlab("Stabilised glucose") + 
  ylab("Counts")

We can also put two histograms in the same plot:

diab %>% 
  group_by(diagnosis) %>% 
  ggplot(aes(x = stab.glu, fill = diagnosis)) +
  geom_histogram(binwidth = 15) +
  xlab("Stabilised glucose") + 
  ylab("Counts")

Density plots are like regressions but for histograms. Density plots try to find the curve that best summarizes the distribution of a variable and report probability densities instead of counts. To control the smoothing of the density plot, we use parameter adjust:

diab %>% 
  ggplot(aes(x = stab.glu)) + 
  geom_density(adjust = 0.7, fill = 'grey') + 
  xlab("Stabilised glucose") + 
  ylab("Density")

5.8 Facets

Ggplot’s facets are a powerful feature that subdivides a plot in subplots according to given variables (categories). There are 2 main functions to create the subplots: facet_wrap and facet_grid.

For example, the density plot can be subdivided by gender (it also works for other types of plots):

diab %>% 
  ggplot(aes(x = stab.glu)) + 
  geom_density(adjust = 0.7, fill='grey') + 
  facet_wrap(vars(gender)) +
  xlab("Stabilised glucose") + 
  ylab("Density")

You can provide a list of columns to the facets function such as gender and location:

diab %>% 
  ggplot(aes(x = stab.glu)) + 
  geom_density(adjust = 0.7, fill='grey') + 
  facet_wrap(vars(gender, location)) +
  xlab("Stabilised glucose") + 
  ylab("Density")

You can combine facets with the fill parameter of aes (not using anymore fill in geom_density) to create subplots by gender and location, and to compare the diagnosis in each subplot:

diab %>% 
  ggplot(aes(x = stab.glu, fill=diagnosis)) + 
  geom_density(adjust = 0.7) + 
  facet_wrap(vars(gender, location)) +
  xlab("Stabilised glucose") + 
  ylab("Density")

Finally, facets can be displayed as a grid using facet_grid. Using the example below, one can compare the distributions of male and female HDL levels in relation to the diagnosis and location:

diab %>% 
  ggplot(aes(x = gender, y = hdl, fill=gender)) +
  geom_boxplot() +
  facet_grid(rows=vars(location), cols=vars(diagnosis)) +
  xlab("gender") + 
  ylab("HDL level ")

5.9 A simple model

In Exercise 5.1, you used Glycosolated haemoglobin levels above 7.0 to diagnose diabetes. The histograms from the previous section showed that diabetics have bigger levels of Stabilized glucose compared to healthy subjects.

5.9.1 Exercise 5.2

  1. Generate a scatter plot comparing glyhb with stab.glu using the subject diagnosis as the point color.
  2. Use facets to create subplots to observe the data by location and gender.
  3. Do you observe differences between the subpopulations of patients?
LS0tDQp0aXRsZTogIkRhdGEgYW5hbHlzaXMgd2l0aCBSIGFuZCB0aGUgdGlkeXZlcnNlIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzInDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCjxzdHlsZT4NCiNoZWFkZXIgLmJ0bi1ncm91cCB7DQogICAgZGlzcGxheTogbm9uZTsNCn0NCjwvc3R5bGU+DQoNCjwhLS0gVE8gRE86IEdJVkUgTU9SRSBFWFBMQU5BVElPTlMgQUJPVVQgV09SS0lORyBESVJFQ1RPUlkgLS0+DQo8IS0tIFRPIERPOiBSRU5BTUUgRVhFUkNJU0VTIEJZIEEsIEIsIEMsIC4uLiAtLT4NCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyBJbnRybyBhbmQgT3ZlcnZpZXcNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgUHJlLXJlcXVpc2l0ZXMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KIyMjIFIgYW5kIFJTdHVkaW8NCk1ha2Ugc3VyZSB0aGF0ICoqUioqIGFuZCAqKlJTdHVkaW8qKiBhcmUgaW5zdGFsbGVkIGluIHlvdXIgY29tcHV0ZXIuIFlvdSBjYW4gY3JlYXRlIGEgbmV3ICoqUiBTY3JpcHQqKiBpbiAqKlJTdHVkaW8qKiB0byBjb3B5IGV4YW1wbGUgc291cmNlIGNvZGUgZnJvbSB0aGlzIHR1dG9yaWFsIGFuZCB3cml0ZSBjb2RlIGZvciB0aGUgZXhlcmNpc2VzLiBTYXZlIHRoZSBzY3JpcHQgaW4geW91ciBob21lIGRpcmVjdG9yeSAob3IgYSBzdWIgZGlyZWN0b3J5IG9mIHlvdXIgY2hvaWNlKS4gVG8gcnVuIHRoZSBjb2RlIHdyaXR0ZW4gb24gdGhlIGN1cnJlbnQgbGluZSAob3Igb2Ygc2VsZWN0ZWQgbGluZXMpLCBjbGljayBvbiBidXR0b24gKipSdW4qKiBvciBwcmVzcyAqKkNUUkwrRU5URVIqKi4gUmVzdWx0cyB3aWxsIGJlIGRpc3BsYXllZCBlaXRoZXIgaW4gdGhlICoqUlN0dWRpbyoqJ3MgY29uc29sZSBvciB0aGUgVmlld2VyIHBhbmVsIChncmFwaGljcykuIFRvIHJlYWQgdGhlIGRvY3VtZW50YXRpb24gYWJvdXQgYSBmdW5jdGlvbiAoSGVscCBwYW5lbCksIGluIHRoZSBzY3JpcHQsIHBsYWNlIHRoZSBjdXJzb3Igb24gdGhlIGZ1bmN0aW9uJ3MgbmFtZSBhbmQgcHJlc3MgRjEuIA0KDQojIyMgVGhlIHRpZHl2ZXJzZQ0KVGhpcyB0dXRvcmlhbCBpcyBiYXNlZCBvbiBhIHBvd2VyZnVsIHNldCBvZiBwYWNrYWdlcyBmb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGl6YXRpb24ga25vd24gYXMgdGhlICoqdGlkeXZlcnNlKiouIFdlIHdpbGwgYWxzbyBuZWVkIHRoZSAqKnJlYWR4bCoqIHBhY2thZ2UgdG8gZW5hYmxlIHJlYWRpbmcgZGF0YSBmcm9tIE1pY3Jvc29mdCBFeGNlbCBmaWxlcy4gU28sIG1ha2Ugc3VyZSB0aGF0IHRob3NlIDIgcGFja2FnZXMgKHRpZHl2ZXJzZSBhbmQgcmVhZHhsKSBoYXZlIGJlZW4gaW5zdGFsbGVkIGluIHlvdXIgUiBlbnZpcm9ubWVudCAoc2VlIFJTdHVkaW8ncyBtZW51IFRvb2xzL0luc3RhbGwgUGFja2FnZXMuLi4pLiANCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBJbnRyb2R1Y3Rpb24NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KQmVjYXVzZSBvZiB0aGUgZXZlci1ncm93aW5nIG51bWJlciBvZiBwdWJsaWNseSBhdmFpbGFibGUgYW5kIGxhcmdlIGRhdGFzZXRzIGluIGxpZmUgc2NpZW5jZXMsIGNvbXB1dGF0aW9uYWwgYW5kIHN0YXRpc3RpY2FsIHNraWxscyBhcmUgYmVjb21pbmcgYSBrZXkgcGFydCBvZiB0aGUgbGlmZSBzY2llbnRpc3QncyBjdXJyaWN1bHVtLg0KDQpUaGUgZ29hbCBvZiB0aGlzIHR1dG9yaWFsIGlzIHRvIGdpdmUgeW91IGEgZm91bmRhdGlvbiBpbiB0aGUgbW9zdCBpbXBvcnRhbnQgdG9vbHMgZm9yIGRhdGEgYW5hbHlzaXMgaW4gUi4gWW91IGNhbiB0aGVuIGNvbXBsZW1lbnQgYW5kIGV4cGFuZCB5b3VyIGtub3dsZWRnZSBvbiB5b3VyIG93biBpbiBvcmRlciB0byBwZXJmb3JtIG1vcmUgY29tcGxleCBhbmFseXNlcy4NCg0KVGhpcyB0dXRvcmlhbCBpcyBhbiBhZGFwdGF0aW9uIG9mIERyLiBBbGFuaXMtTG9iYXRvJ3MgdHV0b3JpYWwgdGhhdCBib3Jyb3dzIG1hdGVyaWFscyBhbmQgY29uY2VwdHMgZnJvbSB0aGUgYm9vayBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgYnkgR2FycmV0IEdyb2xlbXVuZCBhbmQgSGFkbGV5IFdpY2toYW0uIA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIE92ZXJ2aWV3DQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNCkluIG1vc3QgZGF0YSBhbmFseXNpcyBwcm9qZWN0cyB5b3Ugd2lsbCBoYXZlIHRvIGRvIGFzIHNob3duIGJlbG93LiBUaGUgKip0aWR5dmVyc2UqKiBzZXQgb2YgcGFja2FnZXMgaW5jbHVkZSBmdW5jdGlvbnMgdG8gdGFja2xlIGVhY2ggb25lIG9mIHRoZSBwYXJ0cyBvZiB0aGUgZGF0YSBhbmFseXNpcyBwcm9jZXNzLg0KDQohW10oaW1hZ2VzL2RhbmFseXNpcy5wbmcpDQoNCg0KIyMjIEltcG9ydA0KVGhlIGZpcnN0IHN0ZXAgaW4gZGF0YSBzY2llbmNlIGlzIHRvICoqaW1wb3J0KiogeW91ciBkYXRhIGludG8gUiBpbiBvcmRlciB0byBtYW5pcHVsYXRlIGl0IHdpdGggUiBmdW5jdGlvbnMuIFRoaXMgbWVhbnMgdGhhdCB5b3Ugd2lsbCB0YWtlIGRhdGEgc3RvcmVkIGluIGEgZmlsZSwgZGF0YWJhc2Ugb3Igd2Vic2l0ZSBhbmQgbG9hZCBpdCBpbnRvIGEgdmFyaWFibGUgaW4gUi4gVGhlc2UgZGF0YXNldHMgY2FuIGJlIHRoZSBvdXRwdXQgb2YgYSBnZW5vbWljcyBwcm9qZWN0LCB0aGUgcmVzdWx0IG9mIGEgc3VydmV5LCBtZWFzdXJlbWVudHMgZG9uZSBpbiB0aGUgbGFiLCBldGMuIEluIHRoaXMgdHV0b3JpYWwsIHlvdSB3aWxsIHVzZSBmdW5jdGlvbnMgZnJvbSB0aGUgKipyZWFkcioqIGFuZCAqKnJlYWR4bCoqIHBhY2thZ2VzIHRvIGxvYWQgZGF0YSB0YWJsZXMgaW50byBSLg0KDQohW10oaW1hZ2VzL2RfaW1wb3J0LnBuZykNCg0KDQojIyMgVGlkeQ0KU2Vjb25kLCB5b3Ugd2lsbCBoYXZlIHRvICoqdGlkeSoqIHVwIHlvdXIgZGF0YS4gQnJpZWZseSwgaW4gYSB0aWR5IGRhdGFzZXQgZWFjaCByb3cgaXMgYW4gb2JzZXJ2YXRpb24gb3IgYSBzYW1wbGUgYW5kIGVhY2ggY29sdW1uIGlzIGEgdmFyaWFibGUuIElmIHlvdXIgZGF0YSBpcyB0aWR5IGFuZCBoYXMgYSBjb25zaXN0ZW50IHN0cnVjdHVyZSwgeW91IHdpbGwgYmUgYWJsZSB0byB1c2UgaXQgc2VhbWxlc3NseSB0aHJvdWdob3V0IHRoZSAqKnRpZHl2ZXJzZSoqIGZ1bmN0aW9ucyBmb3IgYW5hbHlzaXMsIG1hbmlwdWxhdGlvbiBhbmQgdmlzdWFsaXphdGlvbi4gSW4gdGhpcyB0dXRvcmlhbCwgeW91IHdpbGwgdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKnRpZHlyKiogcGFja2FnZSB0byB0aWR5IHVwIHlvdXIgZGF0YS4NCg0KIVtdKGltYWdlcy9kX3RpZHkucG5nKQ0KDQoNCiMjIyBUcmFuc2Zvcm0NCk9uY2UgeW91ciBkYXRhIGlzIHRpZHksIG9mdGVuIHRpbWVzIHlvdSBuZWVkIHRvICoqdHJhbnNmb3JtKiogaXQuIENvbW1vbiB0cmFuc2Zvcm1hdGlvbnMgaW5jbHVkZSBub3JtYWxpemF0aW9uLCB0aGUgY3JlYXRpb24gb2YgbmV3IHZhcmlhYmxlcyBmcm9tIGV4aXN0aW5nIG9uZXMgKGUuZy4gY29udmVydCBmcm9tIGluY2hlcyB0byBjZW50aW1ldGVycykgb3IgdGhlIGNvbXB1dGF0aW9uIG9mIHN1bW1hcnkgc3RhdGlzdGljcyAoZS5nLiBtZWFucyBhbmQgY291bnRzKS4gSW4gdGhpcyB0dXRvcmlhbCwgeW91IHdpbGwgdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKmRwbHlyKiogcGFja2FnZSAodGhlICoqZCoqIGNvbWVzIGZyb20gZGF0YS1mcmFtZSBhbmQgKipwbHlyKiogZnJvbSAicGxpZXJzIiwgdGhlIHBpbmNlcnMgZm9yIGdyaXBwaW5nIHNtYWxsIG9iamVjdHMgb3IgYmVuZGluZyB3aXJlKSB0byBwZXJmb3JtIGRhdGEgdHJhbnNmb3JtYXRpb25zLg0KDQohW10oaW1hZ2VzL2RfdHJhbnMucG5nKQ0KDQoNCiMjIyB2aXN1YWxpemUgYW5kIG1vZGVsDQpBcm1lZCB3aXRoIHRpZHkgZGF0YSB0aGF0IGhhcyB0aGUgdmFyaWFibGVzIHlvdSBuZWVkLCB5b3UgYXJlIHJlYWR5IGZvciBrbm93bGVkZ2UgZ2VuZXJhdGlvbi4gR29vZCAqKmRhdGEgZ3JhcGhpY3MqKiB3aWxsIHNob3cgeW91IHRoaW5ncyB0aGF0IHlvdSBkaWQgbm90IGtub3cgYWJvdXQgdGhlIGRhdGEsIHRoZXkgd2lsbCBhbHNvIHNob3cgeW91IHRoaW5ncyB5b3UgZGlkIG5vdCBleHBlY3QsIHJhaXNlIG1vcmUgcXVlc3Rpb25zIG9yIHRlbGwgeW91IHRoYXQgeW91IGFyZSBhc2tpbmcgdGhlIHdyb25nIHF1ZXN0aW9ucy4gSW4gdGhpcyB0dXRvcmlhbCwgeW91IHdpbGwgZ2VuZXJhdGUgcGxvdHMgdG8gZ2FpbiBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHlvdXIgZGF0YS4gVGhpcyB3aWxsIGJlIGRvbmUgd2l0aCBmdW5jdGlvbnMgZnJvbSB0aGUgKipnZ3Bsb3QqKiBwYWNrYWdlLg0KDQohW10oaW1hZ2VzL2RfdmlzLnBuZykNCg0KDQoqKk1vZGVscyoqIGFyZSBjb21wbGVtZW50YXJ5IHRvIHZpc3VhbGl6YXRpb25zLiBNb2RlbHMgYXJlIG1hdGhlbWF0aWNhbCByZXByZXNlbnRhdGlvbnMgb2YgYSBzeXN0ZW0gdGhhdCBhcmUgb2Z0ZW4gZGVyaXZlZCBmcm9tIHByZWNpc2UgZGF0YSBhbmFseXplcyBhbmQgb2JzZXJ2YXRpb25zLiBNb2RlbHMgYXJlIGtleSB0byBtYWtpbmcgcHJlZGljdGlvbnMgdGhhdCBjYW4gYmUgbGF0ZXIgdGVzdGVkIGluIHRoZSBsYWIuDQoNCkV2ZW4gdGhvdWdoIHRoZXJlIGFyZSBwb3dlcmZ1bCB0b29scyBpbiBSIGZvciBkYXRhIG1vZGVsaW5nLCBpbiB0aGlzIHR1dG9yaWFsIHlvdSB3aWxsIG9ubHkgcHJvcG9zZSBzaW1wbGUgbW9kZWxzIGJhc2VkIG9uIHZpc3VhbGl6YXRpb25zLg0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gc3RyZXNzIHRoYXQgdmlzdWFsaXphdGlvbiBhbmQgbW9kZWxpbmcgd2lsbCBvZnRlbiBkZXJpdmUgaW4gbmV3IHF1ZXN0aW9ucyBhbmQgaHlwb3RoZXNlcyB0aGF0IHdpbGwgYnJpbmcgeW91IGJhY2sgdG8gdGhlIGRhdGEgdHJhbnNmb3JtYXRpb24gc3RlcC4gV2l0aCBuZXcgdmFyaWFibGVzIGFuZCBzdGF0cywgeW91IGNhbiB2aXN1YWxpemUgYWdhaW4gYW5kIGZpbmUgdHVuZSB5b3VyIG1vZGVsLg0KDQojIyMgUHJvZ3JhbQ0KU3Vycm91bmRpbmcgYWxsIHRoZSBhYm92ZSBzdGVwcyBpcyAqKnByb2dyYW1taW5nKiosIGEgY3JpdGljYWwgcGFydCBvZiBhbnkgZGF0YSBhbmFseXNpcyBwcm9qZWN0LiBZb3UgZG8gbm90IG5lZWQgdG8gYmUgYW4gZXhwZXJ0IHByb2dyYW1tZXIgdG8gYW5hbHl6ZSBkYXRhLCBidXQgcHJvZ3JhbW1pbmcgc2tpbGxzIGFsbG93IHlvdSB0byBhdXRvbWF0ZSBjb21tb24gdGFza3MgYW5kIHNvbHZlIG5ldyBwcm9ibGVtcyBtb3JlIGVhc2lseS4gVGhlcmUgYXJlIGh1bmRyZWRzIG9mIHByb2dyYW1taW5nIGxhbmd1YWdlcyBvdXQgdGhlcmUsIGJ1dCAqKlIqKiBhbmQgaXRzIHJpY2ggY29sbGVjdGlvbiBvZiBwYWNrYWdlcywgZnVuY3Rpb25zIGFuZCBkYXRhIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4gaXQgY29tZXMgdG8gZG9pbmcgc3RhdGlzdGljcyBhbmQgZGF0YSBhbmFseXNpcy4NCg0KIVtdKGltYWdlcy9kX3Byb2dyYW0ucG5nKQ0KDQoNCiMjIyBDb21tdW5pY2F0ZQ0KQ29vbCBncmFwaGljcyBhbmQgcHJlY2lzZSBtb2RlbHMgYXJlIG9mIG5vIHVzZSBpZiB5b3UgY2Fubm90ICoqY29tbXVuaWNhdGUqKiB5b3VyIHJlc3VsdHMgdG8gb3RoZXJzLiBDb21tdW5pY2F0aW9uIG9mIG1ldGhvZHMgYW5kIHJlc3VsdHMgY2FuIGJlIGRvbmUgdmlhIG9yYWwgb3IgcG9zdGVyIHByZXNlbnRhdGlvbnMsIHRoZXNlcywgc2NpZW50aWZpYyBhcnRpY2xlcywgcmVwb3J0cywgZXRjLiBUaGVyZSBhcmUgdG9ucyBvZiB0b29scyBmb3IgY29tbXVuaWNhdGlvbiBvZiBkYXRhIGFuYWx5emVzLCBmcm9tIFBvd2VyUG9pbnQgb3IgV29yZCB0byB3ZWIgcGFnZXMgYW5kICoqUiBNYXJrZG93bioqLiBUaGVzZSBpbnRlcmFjdGl2ZSBub3RlcyBoYXZlIGJlZW4gd3JpdHRlbiB3aXRoIHRoZSBsYXR0ZXIuDQoNCiFbXShpbWFnZXMvZF9jb21tLnBuZykNCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBFeGFtcGxlIGRhdGEgYW5hbHlzaXMgcGlwZWxpbmUNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVG8gbW90aXZhdGUgeW91ciBpbnRlcmVzdCBmb3IgZGF0YSBhbmFseXNpcyBhbmQgZ2l2ZSB5b3UgYSBnbGltcHNlIG9mIGhvdyBlYXN5IGl0IGlzIHRvIGRvIGl0IGluIHRoZSB0aWR5dmVyc2UsIHRoZSBmb2xsb3dpbmcgbGluZXMgd2lsbCBndWlkZSB5b3UgdGhyb3VnaCBhbiBleGFtcGxlIHByb2plY3QgYWltZWQgYXQgYXV0b21hdGluZyB0aGUgY2xhc3NpZmljYXRpb24gb2YgaXJpcyBmbG93ZXJzLg0KDQpUaGUgZXhhbXBsZSB3aWxsIGdvIHRocm91Z2ggYWxsIHRoZSBzdGVwcyBjb3ZlcmVkIGFib3ZlIGFuZCBoaWdobGlnaHQgc29tZSBvZiB0aGUgZnVuY3Rpb25zIHRoYXQgeW91IHdpbGwgbGVhcm4gYW5kIHVzZSBpbiB0aGlzIHR1dG9yaWFsLiBEb24gbm90IGZyZWFrIG91dCBhYm91dCB0aGUgY29kZSBvciB0aGUgZGF0YSBhbmFseXNpcyBpdHNlbGYuIEV2ZXJ5dGhpbmcgd2lsbCBiZSBleHBsYWluZWQgaW4gbW9yZSBkZXRhaWwgaW4gdGhlIGZvbGxvd2luZyBQYXJ0cy4NCg0KIyMjIFRoZSBpcmlzIGZsb3dlciBkYXRhc2V0DQpUaGUgW2lyaXMgZmxvd2VyIGRhdGFzZXRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lyaXNfZmxvd2VyX2RhdGFfc2V0KSB3YXMgY29sbGVjdGVkIGJ5IEVkZ2FyIEFuZGVyc29uLCBhbiBBbWVyaWNhbiBib3RhbmlzdCwgaW4gdGhlIDE5MjBzLiBUaGlzIGRhdGEgd2FzIHVzZWQgYnkgc3RhdGlzdGljaWFuIFJvbmFsZCBGaXNoZXIgdG8gZGVtb25zdHJhdGUgc3RhdGlzdGljYWwgbWV0aG9kcyBvZiBjbGFzc2lmaWNhdGlvbi4gWW91IG1heSBrbm93IGl0IGFscmVhZHksIGFzIGl0IGlzIHN0aWxsIHF1aXRlIHBvcHVsYXIgYW1vbmcgc3RhdGlzdGljYWwgYW5kIHByb2dyYW1taW5nIHRlYWNoZXJzLiAgDQoNClRoZSBvcmlnaW5hbCBkYXRhc2V0IGNvbnRhaW5zIDE1MCBzYW1wbGVzIGZyb20gdGhyZWUgaXJpcyBzcGVjaWVzLiBZb3UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aCBhIHJlZHVjZWQgdmVyc2lvbiB3aXRoIG9ubHkgMTAwIHNhbXBsZXMgZnJvbSB0d28gc3BlY2llczogSXJpcyBzZXRvc2EgYW5kIElyaXMgdmlyZ2luaWNhLg0KDQpGb3IgZWFjaCBzYW1wbGUsIEFuZGVyc29uIHJlY29yZGVkIHRoZSBzZXBhbCBsZW5ndGggYW5kIHdpZHRoLCBhcyB3ZWxsIGFzIHRoZSBwZXRhbCBsZW5ndGggYW5kIHdpZHRoOg0KDQohWyoqRmlndXJlOiBJcmlzIEZsb3dlciBhbmQgQW5kZXJzb24ncyBtZWFzdXJlbWVudHMgKHRha2VuIGZyb20gS2FnZ2xlLmNvbSkqKl0oaW1hZ2VzL2lyaXNfZmxvd2VyLnBuZykNCg0KR2l2ZW4gYW4gaXJpcyBmbG93ZXIsIGlzIGl0IHBvc3NpYmxlIHRvIGNsYXNzaWZ5IGl0IGFzICoqc2V0b3NhKiogb3IgKip2aXJnaW5pY2EqKiBiYXNlZCBvbiB0aGVzZSBtZWFzdXJlbWVudHM/DQoNCkJlZm9yZSBzdGFydGluZyBvdXIgYW5hbHlzaXMsIHdlIG5lZWQgdG8gbG9hZCB0aGUgKip0aWR5dmVyc2UqKiBhbmQgKipnZ3ZpcyoqIHBhY2thZ2VzOg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KYGBgDQoNCkFzIHNob3duIGJlbG93LCBieSBjYWxsaW5nIHNvbWUgZnVuY3Rpb25zIHN1Y2ggYXMgKipsaWJyYXJ5KiosIHdhcm5pbmdzIGFuZCB0ZWNobmljYWwgbWVzc2FnZXMgbWF5IGJlIGRpc3BsYXllZCBvbiB0aGUgY29uc29sZS4gQWx0aG91Z2ggdGhleSBjb3VsZCBwcm92aWRlIHRoZSB1c2VyIHdpdGggdXNlZnVsIGluZm9ybWF0aW9uLCB3ZSB3aWxsIGZvY3VzIG9uIHRoZSAnbm9ybWFsJyBvdXRwdXQgaW4gdGhpcyB0dXRvcmlhbCAoaS5lLiB0YWJsZXMsIGZpZ3VyZXMsIGFuZCB2ZWN0b3JzKS4gDQoNCmBgYHtyfQ0KIyAtLSBBdHRhY2hpbmcgcGFja2FnZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHRpZHl2ZXJzZSAxLjMuMCAtLQ0KIyB2IGdncGxvdDIgMy4yLjEgICAgIHYgcHVycnIgICAwLjMuMw0KIyB2IHRpYmJsZSAgMi4xLjMgICAgIHYgZHBseXIgICAwLjguMw0KIyB2IHRpZHlyICAgMS4wLjIgICAgIHYgc3RyaW5nciAxLjQuMA0KIyB2IHJlYWRyICAgMS4zLjEgICAgIHYgZm9yY2F0cyAwLjQuMA0KIyAtLSBDb25mbGljdHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHRpZHl2ZXJzZV9jb25mbGljdHMoKSAtLQ0KIyB4IGRwbHlyOjpmaWx0ZXIoKSBtYXNrcyBzdGF0czo6ZmlsdGVyKCkNCiMgeCBkcGx5cjo6bGFnKCkgICAgbWFza3Mgc3RhdHM6OmxhZygpDQojIFJlZ2lzdGVyZWQgUzMgbWV0aG9kcyBvdmVyd3JpdHRlbiBieSAnaHRtbHRvb2xzJzoNCiMgICBtZXRob2QgICAgICAgICAgICAgICBmcm9tICAgICAgICAgDQojICAgcHJpbnQuaHRtbCAgICAgICAgICAgdG9vbHM6cnN0dWRpbw0KIyAgIHByaW50LnNoaW55LnRhZyAgICAgIHRvb2xzOnJzdHVkaW8NCiMgICBwcmludC5zaGlueS50YWcubGlzdCB0b29sczpyc3R1ZGlvDQojIA0KIyANCiMgVGhlIGZvbGxvd2luZyBvYmplY3QgaXMgbWFza2VkIGZyb20gcGFja2FnZTpnZ3Bsb3QyDQojIA0KIyAgICAgcmVzb2x1dGlvbg0KYGBgDQoNCg0KIyMjIEltcG9ydGluZyB0aGUgaXJpcyBkYXRhc2V0DQpXZSB3aWxsIGltcG9ydCB0aGUgKippcmlzLnRzdioqIGRhdGEgZmlsZS4gVGhlIGZpbGUgZXh0ZW5zaW9uIHRlbGxzIHVzIHRoYXQgdGhpcyBpcyBhICJ0YWItc2VwYXJhdGVkIHZhbHVlIiB0eXBlIG9mIGZpbGUgKC50c3YpIHRoYXQgc3RvcmVzIGEgdGFibGUgb2YgZGF0YS4gVGhlICoqcmVhZHIqKiBwYWNrYWdlIGZyb20gdGhlIHRpZHl2ZXJzZSBoYXMgZnVuY3Rpb24gKipyZWFkX3RzdioqIHRvIGltcG9ydCB0aGVtIGludG8gUi4gSWYgb24gdGhlIGxvY2FsIGNvbXB1dGVyLCB0aGUgZnVuY3Rpb24gd2lsbCBuZWVkIHRoZSBmaWxlIHBhdGggKGUuZy4gImRhdGFzZXRzL2lyaXMudHN2IikuIElmIG9uIEludGVybmV0LCB3ZSB3aWxsIHByb3ZpZGUgdGhlIGZ1bmN0aW9uIHdpdGggdGhlIFVSTC4gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGV4YW1wbGUgaWYgb24gbG9jYWwgY29tcHV0ZXINCiMgaXIgPC0gcmVhZF90c3YoImRhdGFzZXRzL2lyaXMudHN2IikgDQoNCiMgVXNpbmcgYW4gVVJMDQppciA8LSByZWFkX3RzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvaXJpcy50c3YiKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgUGFyc2VkIHdpdGggY29sdW1uIHNwZWNpZmljYXRpb246DQojIGNvbHMoDQojICAgcmF0aW9fc2xfc3cgPSBjb2xfY2hhcmFjdGVyKCksDQojICAgcmF0aW9fcGxfcHcgPSBjb2xfY2hhcmFjdGVyKCksDQojICAgc3BlY2llcyA9IGNvbF9jaGFyYWN0ZXIoKQ0KIyApDQpgYGANCg0KRnVuY3Rpb25zIHRoYXQgbG9hZCBkYXRhIHVzdWFsbHkgZGlzcGxheSBtZXNzYWdlcyBpbiB0aGUgUiBjb25zb2xlLCBzdWNoIGFzIHNob3duIGFib3ZlIHRvIG5vdGlmeSB3aGljaCBkYXRhIHR5cGUgaXMgc2V0IGJ5IGNvbHVtbiBvZiB0aGUgbG9hZGVkIHRhYmxlLg0KDQpWYXJpYWJsZSBpciBub3cgY29udGFpbnMgdGhlIGlyaXMgZmxvd2VyIGRhdGEgdGFibGUuDQpgYGB7cn0NCmlyDQpgYGANCg0KTm90ZSB0aGF0IGluIHRoZSBSIGNvbnNvbGUsIG9ubHkgYSB0ZXh0IHZlcnNpb24gb2YgdGhlIHRhYmxlIHdpbGwgYmUgZGlzcGxheWVkLg0KDQoNCiMjIyBUaWR5aW5nIHRoZSBpcmlzIGRhdGFzZXQNClJlbWVtYmVyIHRoYXQgdGlkeSBkYXRhc2V0cyByZXBvcnQgZWFjaCBzYW1wbGUgaW4gaXRzIG93biByb3cgYW5kIGVhY2ggdmFyaWFibGUgaW4gaXRzIG93biBjb2x1bW4uIFRoZSBpcmlzIGRhdGFzZXQgcmVwb3J0cyBzZXBhbCBsZW5ndGggYW5kIHdpZHRoIGFuZCBwZXRhbCBsZW5ndGggYW5kIHdpZHRoIGFzIHJhdGlvcyBpbiBjb2x1bW5zICoqcmF0aW9fc2xfc3cqKiBhbmQgKipyYXRpb19wbF9wdyoqLCByZXNwZWN0aXZlbHkuIFdlIGNhbiB0aWR5IHVwIHRoaXMgdGFibGUgd2l0aCBmdW5jdGlvbiAqKnNlcGFyYXRlKiogZnJvbSBwYWNrYWdlICoqdGlkeXIqKjoNCg0KYGBge3J9DQppciA8LSBpciAlPiUgDQogICBzZXBhcmF0ZShjb2wgPSByYXRpb19zbF9zdywgDQogICAgICAgICAgICBpbnRvID0gYygic2VwYWxfbGVuZ3RoIiwgInNlcGFsX3dpZHRoIiksIA0KICAgICAgICAgICAgc2VwID0gIi8iKSAlPiUgDQogICBzZXBhcmF0ZShjb2wgPSByYXRpb19wbF9wdywgDQogICAgICAgICAgICBpbnRvID0gYygicGV0YWxfbGVuZ3RoIiwgInBldGFsX3dpZHRoIiksIA0KICAgICAgICAgICAgc2VwID0gIi8iKQ0KaXINCmBgYA0KDQoNCiMjIyBUcmFuc2Zvcm1pbmcgdGhlIGlyaXMgZGF0YXNldA0KTm90ZSB0aGF0IHRoZSB3aWR0aCBhbmQgbGVuZ3RoIGNvbHVtbnMgaW4gdGhlIHRpZGllZCBpcmlzIGRhdGFzZXQgYXJlIG9mIHR5cGUgY2hhcmFjdGVyICgqKmNocioqKSBpbnN0ZWFkIG9mIG51bWVyaWMgKCoqZGJsKiogb3IgKippbnQqKikuIElmIHdlIHdhbnQgdG8gY29tcHV0ZSBzdW1zIG9yIG90aGVyIHN0YXRpc3RpY3Mgb24gdGhpcyB0YWJsZSwgd2UgbmVlZCB0byB0cmFuc2Zvcm0gdGhlc2UgY29sdW1ucyBpbnRvIHRoZSBhcHByb3ByaWF0ZSB0eXBlLiBXZSBjYW4gZG8gdGhpcyB3aXRoIGZ1bmN0aW9uIG11dGF0ZSBmcm9tIHBhY2thZ2UgKipkcGx5cioqOg0KDQpgYGB7cn0NCmlyIDwtIGlyICU+JSANCiAgIG11dGF0ZShzZXBhbF9sZW5ndGggPSBhcy5udW1lcmljKHNlcGFsX2xlbmd0aCksDQogICAgICAgICAgc2VwYWxfd2lkdGggID0gYXMubnVtZXJpYyhzZXBhbF93aWR0aCksDQogICAgICAgICAgcGV0YWxfbGVuZ3RoID0gYXMubnVtZXJpYyhwZXRhbF9sZW5ndGgpLA0KICAgICAgICAgIHBldGFsX3dpZHRoICA9IGFzLm51bWVyaWMocGV0YWxfd2lkdGgpKQ0KYGBgDQoNCg0KIyMjIHZpc3VhbGl6aW5nIHRoZSBpcmlzIGRhdGFzZXQNCkxldCB1cyBub3cgcGxvdCBoaXN0b2dyYW1zIGZvciBlYWNoIGZsb3dlciBmZWF0dXJlIHRvIHNlZSBpZiB0aGVyZSBpcyBvbmUgdGhhdCBjYW4gYmUgdXNlZCB0byBjbGFzc2lmeSB0aGUgdHdvIGlyaXMgc3BlY2llczoNCg0KYGBge3J9DQojIHNlcGFsIGxlbmd0aA0KaXIgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9c2VwYWxfbGVuZ3RoLCBmaWxsID0gc3BlY2llcykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xNSkNCmBgYA0KDQpgYGB7cn0NCiMgc2VwYWwgd2lkdGgNCmlyICU+JSANCiAgZ2dwbG90KGFlcyh4PXNlcGFsX3dpZHRoLCBmaWxsID0gc3BlY2llcykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xNSkNCmBgYA0KDQpgYGB7cn0NCiMgcGV0YWwgbGVuZ3RoDQppciAlPiUgDQogIGdncGxvdChhZXMoeD1wZXRhbF9sZW5ndGgsIGZpbGw9c3BlY2llcykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xNSkNCmBgYA0KDQpgYGB7cn0NCiMgcGV0YWwgd2lkdGgNCmlyICU+JSANCiAgZ2dwbG90KGFlcyh4PXBldGFsX3dpZHRoLCBmaWxsPXNwZWNpZXMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMTUpDQpgYGANCg0KDQojIyMgbW9kZWxpbmcNClRoYW5rcyB0byB0aGUgYWJvdmUgcGxvdHMsIHdlIGtub3cgdGhhdCB3ZSBjYW4gdXNlIHBldGFsIGxlbmd0aCBhbmQgd2lkdGggdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGEgZmxvd2VyIGZyb20gdGhlIHNwZWNpZXMgSXJpcyBzZXRvc2EgYW5kIHRoZSBzcGVjaWVzIElyaXMgdmlyZ2luaWNhLiBVbmRlciB0aGUgYXNzdW1wdGlvbiB0aGF0IHdlIGFyZSBvbmx5IGRlYWxpbmcgd2l0aCBmbG93ZXJzIGZyb20gdGhlc2UgdHdvIHNwZWNpZXMsIGxldCB1cyB1c2Ugb3VyIG9ic2VydmF0aW9ucyB0byBwcm9wb3NlIGEgc2ltcGxlIG1vZGVsIGZvciBpcmlzIGZsb3dlciBjbGFzc2lmaWNhdGlvbjoNCg0KYGBge3J9DQojIHRoZSBtb2RlbCBpcyBpbXBsZW1lbnRlZCB3aXRoIGEgZnVuY3Rpb24gdGhhdCB0YWtlcyAyIHBhcmFtZXRlcnMNCndoaWNoX3NwZWNpZXMgPC0gZnVuY3Rpb24ocGV0YWxfbGVuZ3RoLCBwZXRhbF93aWR0aCl7DQoNCiAgaWYgKHBldGFsX2xlbmd0aCA8PSAzICYmIHBldGFsX3dpZHRoIDw9IDEpew0KICAgIHByaW50KCJJcmlzIHNldG9zYSIpDQogICAgfQ0KICBlbHNlew0KICAgIHByaW50KCJJcmlzIHZpcmdpbmljYSIpDQogIH0NCg0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyBXZSBjYW4gY2FsbCB0aGUgZnVuY3Rpb24gd2l0aCAyIHNwZWNpZmljIHZhbHVlcyB0byBnZXQgYW4gYW5zd2VyDQp3aGljaF9zcGVjaWVzKHBldGFsX2xlbmd0aD0yLCBwZXRhbF93aWR0aD0wLjUpDQpgYGANCg0KDQpgYGB7cn0NCndoaWNoX3NwZWNpZXMocGV0YWxfbGVuZ3RoPTEwLCBwZXRhbF93aWR0aD0wLjUpDQpgYGANCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyBJbXBvcnQgYW5kIHRoZSB0aWJibGUNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBJbXBvcnRpbmcgZGF0YSBpbnRvIFINCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCkluIFBhcnQgMSwgeW91IHNhdyB0aGF0IHRoZSBmaXJzdCBzdGVwIGluIGFueSBkYXRhIGFuYWx5c2lzIHBpcGVsaW5lIGlzIGltcG9ydGluZyB5b3VyIHRhYmxlcyBpbnRvIFIuIFlvdSBkbyB0aGlzIHRvIHRha2UgYWR2YW50YWdlIG9mIFLigJlzIGZ1bmN0aW9ucywgd2hpY2ggc2ltcGxpZnkgdGhlIG1hbmlwdWxhdGlvbiBvZiB5b3VyIGRhdGEuDQoNCiFbXShpbWFnZXMvZF9pbXBvcnQucG5nKQ0KDQpUaGVyZSBhcmUgdHdvIHBhY2thZ2VzIGluIHRoZSB0aWR5dmVyc2UgdGhhdCBtYWtlIGRhdGEgaW1wb3J0IGludG8gUiBhIG1hdHRlciBvZiBhIHNpbmdsZSBsaW5lIG9mIGNvZGUuDQoNClBhY2thZ2UgKipyZWFkcioqIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBpbXBvcnQgZGF0YSB0YWJsZXMgc3RvcmVkIGluIHBsYWluLXRleHQgZmlsZXMsIHN1Y2ggYXMg4oCcY29tbWEtc2VwYXJhdGVkIHZhbHVl4oCdICguY3N2KSBvciDigJx0YWItc2VwYXJhdGVkIHZhbHVl4oCdICgudHN2KSBmaWxlcy4NCg0KUGFja2FnZSAqKnJlYWR4bCoqIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBpbXBvcnQgZGF0YSBzdG9yZWQgaW4gTWljcm9zb2Z0IEV4Y2VsIGZpbGVzLiBUaGVzZSBmdW5jdGlvbnMgYWxzbyBsZXQgeW91IGltcG9ydCBmcm9tIHNwZWNpZmljIHNoZWV0cyBvciByYW5nZXMgd2l0aGluIHRoZSBFeGNlbCBmaWxlLg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEltcG9ydGluZyBmcm9tIHBsYWluLXRleHQgZmlsZXMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NClRvIGRlbWFyY2F0ZSB0aGUgY29udGVudCBvZiBlYWNoIGNlbGwgaW4gYSBmaWxlLCBwZW9wbGUgdXNlIHNwZWNpYWwgY2hhcmFjdGVycyBsaWtlIGNvbW1hcywgdGFicywgcGlwZXMsIGV0Yy4gVGhlIGV4dGVuc2lvbiBvZiBwbGFpbi10ZXh0IGZpbGVzIGlzIG9mdGVuIGEgZ29vZCBoaW50IG9mIHdoYXQgZGVsaW1pdGVyIGhhcyBiZWVuIHVzZWQuIEZvciBleGFtcGxlLCBjc3YgZmlsZXMgc2hvdWxkIHVzZSDigJxjb21tYXPigJ0gYXMgdGhlIGRlbGltaXRlciwgd2hlcmVhcyB0c3YgZmlsZXMgc2hvdWxkIHVzZSDigJx0YWJz4oCdLiBOZXZlcnRoZWxlc3MsIHlvdSBjYW4gZmluZCBjc3Ygb3IgdHN2IGZpbGVzIHRoYXQgYXJlIGRlbGltaXRlZCB3aXRoIGRpZmZlcmVudCBjaGFyYWN0ZXJzLg0KDQpUaGUgZmlyc3Qgc3RlcCBpbiBkYXRhIGltcG9ydCBpcyBsb2NhdGluZyB0aGUgZmlsZSBpbiB5b3VyIGNvbXB1dGVyIG9yIGluIGEgcmVtb3RlIGxvY2F0aW9uIChlLmcuIGJ5IFVSTCkuIFRoZSBtb3N0IGltcG9ydGFudCBwYXJhbWV0ZXIgZm9yIHJlYWRy4oCZcyBmdW5jdGlvbnMgaXMgdGhlIHBhdGggdG8gdGhlIGZpbGUgb2YgaW50ZXJlc3QuDQoNCkluIHRoaXMgcGFydCwgeW91IGFyZSBnb2luZyB0byBpbXBvcnQgZmlsZXMgbG9jYXRlZCBvbiBpbnRlcm5ldC4gUGFja2FnZXMgKip0aWR5dmVyc2UqKiBhbmQgKipyZWFkeGwqKiBtdXN0IGJlIGxvYWRlZC4NCg0KDQojIyMgSW1wb3J0aW5nIGEgdHN2IGZpbGUNCkxldOKAmXMgc3RhcnQgYnkgbGVhcm5pbmcgaG93IHRvIGltcG9ydCBhIHRzdiBmaWxlOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd2hvX3JlZiA8LSByZWFkX3RzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvd2hvX3JlZi50c3YiKQ0Kd2hvX3JlZg0KYGBgDQoNCg0KDQojIyMgSW1wb3J0aW5nIGEgY3N2IGZpbGUNCkltcG9ydGluZyBhIGNzdiBmaWxlIGlzIHZlcnkgc2ltaWxhcjoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCndobzEgPC0gcmVhZF9jc3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dobzEuY3N2IikNCndobzENCmBgYA0KDQpOb3RlIGhvdyBzaW1wbGUgdGhlIHN5bnRheCBmb3IgZGF0YSBpbXBvcnQgaXM6IHlvdSBjaG9vc2UgdGhlIG5hbWUgb2YgYSB2YXJpYWJsZSB0byBzdG9yZSB5b3VyIHRhYmxlLCB1c2UgdGhlICoqPC0qKiBvcGVyYXRvciAod3JpdHRlbiB3aXRoIDIgY2hhcmFjdGVyczogKio8KiogYW5kICoqLSoqKSwgY2FsbCB0aGUgYXBwcm9wcmlhdGUgKipyZWFkcioqIGZ1bmN0aW9uIGFuZCBzcGVjaWZ5IHRoZSBsb2NhdGlvbiBvZiB5b3VyIGZpbGUuDQoNCiMjIyBNYWtlIHRoZSBleGFtcGxlIGRhdGEgbG9jYWxseSBhdmFpbGFibGUNCg0KU28gZmFyLCB3ZSB1c2VkIFVSTHMgdG8gYWNjZXNzIHRoZSBkYXRhc2V0cyB1c2VkIGZvciB0aGUgZXhhbXBsZXMuIEhvd2V2ZXIsIHlvdSBjYW4gYWxzbyBkb3dubG9hZCB0aGVtIHRvIHNhdmUgdGhlbSBsb2NhbGx5IGFuZCB3b3JrIHdpdGggdGhlIGZpbGVzIGRpcmVjdGx5LiBUaGVyZSBpcyBhIGZvbGRlciBvbmxpbmUsIGF2YWlsYWJsZSBmb3IgZG93bmxvYWQgdmlhIHRoaXMgbGluazogImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL2RhdGFzZXRzLnppcCIuIERvd25sb2FkIGl0LCB1bnppcCBpdCBhbmQgc2F2ZSB0aGUgZmlsZXMgdW5kZXIgImRhdGFzZXRzIi4gWW91IHNob3VsZCBhbHNvIHVzZSBhbiBSIHNjcmlwdCAob3Igc2V2ZXJhbCBzY3JpcHRzKSB0byB3cml0ZSB0aGUgY29kZSBmb3IgdGhlIHVwY29taW5nIGV4YW1wbGVzLiBQdXQgc3VjaCBzY3JpcHRzIGluIGFuIGFwcHJvcHJpYXRlIGZvbGRlciB0b2dldGhlciB3aXRoIHRoZSAiZGF0YXNldHMiIGZvbGRlci4gSWYgeW91IHRoZW4gc2V0IHlvdXIgIldvcmtpbmcgRGlyZWN0b3J5IiB0byB0aGUgbG9jYXRpb24gb2YgeW91ciBSIHNjcmlwdHMsIHlvdSBjYW4gYWNjZXNzIHRoZSBleGFtcGxlIGZpbGVzIHdpaCBhIGRpcmVjdCBwYXRoIGxpa2UgdGhpcyAiLi9kYXRhc2V0cy9hbmRyYWRlX2xhYi5jc3YiIChzZWUgbmV4dCBzbWFsbCBleGVyY2lzZSkuIA0KDQpUbyBjaGFuZ2UgeW91ciAiV29ya2luZyBEaXJlY3RvcnkiLCBnbyB0byAiU2Vzc2lvbiIgLT4gIlNldCBXb3JraW5nIERpcmVjdG9yeSIgLT4gIlRvIHNvdXJjZSBmaWxlIGxvY2F0aW9uIg0KDQojIyMgRXhlcmNpc2UgMi4xDQpSZWFkIGZpbGUgYC4vZGF0YXNldHMvYW5kcmFkZV9sYWIuY3N2YCBpbnRvIHZhcmlhYmxlICoqYWwqKiBhbmQgc2hvdyB0aGUgY29udGVudCBvZiB0aGUgdmFyaWFibGUuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDIuMSAtIFNPTFVUSU9ODQphbCA8LSByZWFkX2NzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvYW5kcmFkZV9sYWIuY3N2IikNCmFsDQpgYGANCg0KDQojIyMgSW1wb3J0aW5nIGEgdGV4dCBmaWxlDQpJZiB0aGUgY29sdW1uIGRlbGltaXRlciBvZiBhIGZpbGUgaXMgbm90IG9idmlvdXMgZnJvbSBpdHMgZXh0ZW5zaW9uIGFuZCB0aGUgcGVyc29uIGluIGNoYXJnZSBvZiB0aGUgZmlsZSBkaWRu4oCZdCBwcm92aWRlIHRoaXMgaW5mb3JtYXRpb24sIHlvdSBtdXN0IGZpcnN0IGZpZ3VyZSBvdXQgd2hpY2ggZGVsaW1pdGVyIHdhcyB1c2VkLiBGb3IgZXhhbXBsZSwgb3BlbiB0aGUgZm9sbG93aW5nIGZpbGUgYC4vZGF0YXNldHMvd2hvMi50eHRgLg0KDQpTaW5jZSB0aGUgZGVsaW1pdGVyIGlzIGEgKiotKiosIHdlIG5lZWQgdG8gY2FsbCAqKnJlYWRfZGVsaW0qKiB0byBpbXBvcnQ6DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3aG8yIDwtIHJlYWRfZGVsaW0oImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dobzIudHh0IiwgZGVsaW0gPSAiLSIpDQp3aG8yDQpgYGANCg0KTm90ZSB0aGF0IGluIHRoaXMgc3BlY2lhbCBjYXNlLCB5b3UgaGF2ZSB0byBzcGVjaWZ5IHRoZSBkZWxpbWl0ZXIgdXNpbmcgcGFyYW1ldGVyICoqZGVsaW0qKi4NCg0KIyMjIEV4ZXJjaXNlIDIuMg0KRnJvbSBpdHMgZXh0ZW5zaW9uLCBmaWxlIGAuL2RhdGFzZXRzL2FuZHJhZGVfbGFiLnRzdmAgaXMgc3VwcG9zZWQgdG8gYmUgdGFiLXNlcGFyYXRlZC4gUmVhZCB0aGUgZmlsZSB3aXRoIGZ1bmN0aW9uICoqcmVhZF90c3YqKiBpbnRvIHZhcmlhYmxlICoqYWwqKiBhbmQgc2hvdyBpdHMgY29udGVudHMuIFdoYXQgY2hhcmFjdGVyIGlzIHRoZSBkZWxpbWl0ZXIgaW4gdGhpcyBmaWxlPyBVc2UgdGhlIGFwcHJvcHJpYXRlICoqcmVhZHIqKiBmdW5jdGlvbiB0byBpbXBvcnQgdGhpcyB0YWJsZSBpbnRvIFIuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDIuMiAtIFNPTFVUSU9ODQphbCA8LSByZWFkX3RzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvYW5kcmFkZV9sYWIudHN2IikNCmFsDQojIFF1ZXN0aW9uIG1hcmsgaXMgYWN0dWFsbHkgdGhlIGRlbGltaXRlciBpbiB0aGUgZmlsZQ0KYWwgPC0gcmVhZF9kZWxpbSgiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvYW5kcmFkZV9sYWIudHN2IiwgZGVsaW09Ij8iKQ0KYWwNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEltcG9ydGluZyBmcm9tIEV4Y2VsIGZpbGVzDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpUaGUgc3ludGF4IHRvIGltcG9ydCBmcm9tIEV4Y2VsIGZpbGVzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgb25lIHVzZWQgYWJvdmUuIA0KQnkgZGVmYXVsdCwgZnVuY3Rpb24gKipyZWFkX2V4Y2VsKiogaW1wb3J0cyBmcm9tIHRoZSBmaXJzdCBhdmFpbGFibGUgc2hlZXQgaW4gdGhlIEV4Y2VsIHdvcmtib29rLiANCg0KTG9hZCBhIEVYQ0VMIHNoZWV0IHZpYSB0aGUgZm9sbG93aW5nIHBhdGggaW4geW91ciBsYWNhbCBmb2xkZXIuIE9wZW4gdGhlIGZpbGUgYWxzbyB3aXRoIEVYQ0VMIHRvIHNlZSBpdHMgY29udGVudC4NCg0KKiouL2RhdGFzZXRzL3dob19zcGxpdC54bHN4KioNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd2hvX2Nhc2VzIDwtIHJlYWRfZXhjZWwoImRhdGFzZXRzL3dob19zcGxpdC54bHN4IikgIyB3aWxsIGxvYWQgZmlyc3Qgc2hlZXQgbmFtZWQgJ2Nhc2VzJw0Kd2hvX2Nhc2VzDQpgYGANCg0KSWYgeW91IHdhbnQgZGF0YSBmcm9tIGEgZGlmZmVyZW50IHNoZWV0LCB5b3UgbXVzdCB1c2UgcGFyYW1ldGVyIHNoZWV0Og0KDQpgYGB7cn0NCndob19wb3AgPC0gcmVhZF9leGNlbCgiZGF0YXNldHMvd2hvX3NwbGl0Lnhsc3giLCANCiAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9ICJwb3B1bGF0aW9uIikNCndob19wb3ANCmBgYA0KDQpZb3UgY2FuIGFsc28gc3BlY2lmeSBhIHJhbmdlIHRvIGltcG9ydCBmcm9tOg0KDQpgYGB7cn0NCndob19wb3BfOTkgPC0gcmVhZF9leGNlbCgiZGF0YXNldHMvd2hvX3NwbGl0Lnhsc3giLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9ICJwb3B1bGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9ICJBMTpCNCIpDQp3aG9fcG9wXzk5DQpgYGANCg0KDQojIyMgRXhlcmNpc2UgMi4zDQpDb3B5IHRoZSBmb2xsb3dpbmcgZmlsZSB0byB0aGUgKipkYXRhc2V0cyoqIHN1YmZvbGRlcjogKiouL2RhdGFzZXRzL2FuZHJhZGVfbGFiLnhsc3gqKg0KDQpJbXBvcnQgdGhlIGxpc3Qgb2YgUGhEIHN0dWRlbnRzIGluIEFuZHJhZGUgTGFiIGZyb20gdGhlIGZpbGUuIA0KQ2hvb3NlIGEgdmFyaWFibGUgbmFtZSB0byBzdG9yZSB0aGUgZmlsZSBjb250ZW50cy4NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCiMgRXhlcmNpc2UgMi4zIC0gU09MVVRJT04NCmFsMiA8LSByZWFkX2V4Y2VsKCJkYXRhc2V0cy9hbmRyYWRlX2xhYi54bHN4IikNCmFsMg0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgVGhlIHRpYmJsZQ0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KUmVhZGluZyBmaWxlcyB3aXRoICoqcmVhZHIqKiBvciAqKnJlYWR4bCoqIGZ1bmN0aW9ucyByZXN1bHRzIGluIHRoZSBjcmVhdGlvbiBvZiBhIHZhcmlhYmxlIG9mIHR5cGUgKip0YmxfZGYqKiBpbnN0ZWFkIG9mIFLigJlzIHRyYWRpdGlvbmFsIGRhdGEgZnJhbWUgKHR5cGUgKipkYXRhLmZyYW1lKiopLiBUaGlzIGlzIGJlY2F1c2Ugb25lIG9mIHRoZSB1bmlmeWluZyBmZWF0dXJlcyBvZiB0aGUgKip0aWR5dmVyc2UqKiBpcyB0aGUgdGliYmxlICh0eXBlICoqdGJsX2RmKiopLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBVc2luZyBiYXNlIFIgZnVuY3Rpb24gdG8gaW1wb3J0IENTViBmaWxlcw0Kd2hvMV9kZiA8LSByZWFkLmNzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvd2hvMS5jc3YiLA0KICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPVRSVUUpDQpjbGFzcyh3aG8xX2RmKQ0KDQojIFVzaW5nIHJlYWRyIGZ1bmN0aW9uIHRvIGltcG9ydCBDU1YgZmlsZXMNCndobzFfdGIgPC0gcmVhZF9jc3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dobzEuY3N2IikNCmNsYXNzKHdobzFfdGIpDQpgYGANCg0KdGliYmxlcyBhcmUgZGF0YSBmcmFtZXMgYnV0IHRoZXkgdHdlYWsgc29tZSBvbGQgYmVoYXZpb3JzIHRoYXQgbWFrZSBvdXIgbGlmZSBlYXNpZXIgd2hlbiBhbmFseXppbmcgZGF0YS4NCg0KVG8gc3RhcnQgd2l0aCwgcHJpbnRpbmcgdGhlIGNvbnRlbnQgb2YgYSB0aWJibGUgc2hvd3MgYSByaWNoIG91dHB1dCB0aGF0IGdpdmVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhIHR5cGUgb2YgZWFjaCBjb2x1bW4uIEJ5IGNvbnRyYXN0LCBkYXRhIGZyYW1lcyBqdXN0IHNob3cgdGhlIHJlY29yZHMgYXMgaXM6DQoNCmBgYHtyfQ0Kd2hvMV90Yg0KYGBgDQoNCkFsc28sIHRpYmJsZXMgbmV2ZXIgY2hhbmdlIHRoZSB0eXBlIG9mIHRoZSBpbnB1dHMuIFdoaWxlIGNvbHVtbiBjb3VudHJ5IGlzIG9mIHR5cGUgY2hhcmFjdGVyIGluICoqd2hvMV90YioqLCBpdCBjb3VsZCBiZSBpbXBvcnRlZCBhcyBhICoqZmFjdG9yKiogaW4gKip3aG8xX2RmKiogZGVwZW5kaW5nIG9uIHRoZSBSIHZlcnNpb24gYW5kIHNldHRpbmdzLiANCkZvciB0aGUgc2FrZSBvZiB0aGlzIGRlbW9uc3RyYXRpb24sIHdlIHVzZWQgb3B0aW9uICoqc3RyaW5nc0FzRmFjdG9ycz1UUlVFKiogd2hlbiBpbXBvcnRpbmcgdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgYmFzZSBSIGZ1bmN0aW9uICoqcmVhZC5jc3YqKi4gRXhjZXB0IGlmIHJlcXVpcmVkLCBJIHJlY29tbWVuZCB1c2VycyBpbXBvcnRpbmcgZGF0YSB3aXRoIGJhc2UgUiBmdW5jdGlvbnMgc3VjaCBhcyAqKnJlYWQuY3N2KiogdG8gc2V0IHRoaXMgb3B0aW9uIHRvIEZBTFNFICgqKnN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UqKikuDQoNCmBgYHtyfQ0KY2xhc3Mod2hvMV90YiRjb3VudHJ5KQ0KY2xhc3Mod2hvMV9kZiRjb3VudHJ5KQ0KYGBgDQoNCkZhY3RvciBjYW4gYmUgcHJvYmxlbWF0aWMgdG8gc29tZSB1c2VycyAoZXNwZWNpYWxseSBiZWdpbm5lcnMpOiBpbiB0aGUgZGF0YSBmcmFtZSwgY291bnRyeSBjYW5ub3QgdGFrZSBvdGhlciB2YWx1ZXMgb3RoZXIgdGhhbiB0aGUgb25lcyBhbHJlYWR5IGltcG9ydGVkLiBJZiB3ZSB3YW50IHRvIGFwcGVuZCBpbmZvcm1hdGlvbiBhYm91dCBhIG5ldyBjb3VudHJ5IHRvIG91ciB0YWJsZSwgaXQgd2lsbCBiZSBhZGRlZCBidXQgYXMgYW4gKipOQSoqIChOb3QgQXBwbGljYWJsZSk6DQoNCmBgYHtyfQ0KIyBBZGRpbmcgYSBuZXcgcm93DQp0bXAgPC0gcmJpbmQod2hvMV9kZiwgYygiTWV4aWNvIiwgMTk5OSwgImNhc2VzIiwgMTAwKSkNCiMgUHJpbnRpbmcgdGhlIGJvdHRvbSBvZiB0aGUgdGFibGUgKGNvdW50cnkgaXMgIk5BIiBvbiBsYXN0IHJvdykNCnRtcCAlPiUgdGFpbCgpDQpgYGANCg0KU2luY2UgcGFja2FnZXMgb3V0c2lkZSB0aGUgdGlkeXZlcnNlIHVzZSBkYXRhIGZyYW1lcywgeW91IG1pZ2h0IHdhbnQgdG8gY29lcmNlIHRoZW0gdG8gdGliYmxlcyB3aXRoIGZ1bmN0aW9uICoqYXNfdGliYmxlKio6DQoNCmBgYHtyfQ0Kd2hvMV9kZiA8LSBhc190aWJibGUod2hvMV9kZikNCmBgYA0KDQpJbiBhZGRpdGlvbiwgaXTigJlzIHBvc3NpYmxlIGZvciBhIHRpYmJsZSB0byBoYXZlIGNvbHVtbiBuYW1lcyB0aGF0IGFyZSBub3QgdmFsaWQgUiB2YXJpYWJsZSBuYW1lcw0KdGhhdCBhcmUgbGltaXRlZCB0byBzcGVjaWZpYyBydWxlcy4gRm9yIGV4YW1wbGUsIHRoZXkgbWlnaHQgbm90IHN0YXJ0IHdpdGggYSBsZXR0ZXIsIG9yIHRoZXkgbWlnaHQgY29udGFpbiB1bnVzdWFsIGNoYXJhY3RlcnM6DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp1bnVzdWFsIDwtIHJlYWRfdHN2KCJodHRwOi8vY2JkbS0wMS56ZHYudW5pLW1haW56LmRlL35zdGFsYnJlYy9SY291cnNlRGF0YS91bnVzdWFsLnRzdiIpDQp1bnVzdWFsDQpgYGANCg0KVG8gcmVmZXIgdG8gdGhlc2UgY29sdW1uIG5hbWVzLCB5b3UgaGF2ZSB0byB1c2UgYmFjay10aWNrczoNCg0KYGBge3J9DQp1bnVzdWFsJGA6KWANCmBgYA0KDQpGaW5hbGx5LCB5b3UgY2FuIGNyZWF0ZSB5b3VyIG93biB0aWJibGVzIHdpdGggZnVuY3Rpb24gdGliYmxlOg0KDQpgYGB7cn0NCnN0b2NrcyA8LSB0aWJibGUoDQogIHllYXIgICAgPSBjKDIwMTUsIDIwMTUsIDIwMTUsIDIwMTUsIDIwMTYsIDIwMTYsIDIwMTYpLA0KICBxdHIgICAgID0gYyggICAxLCAgICAyLCAgICAzLCAgICA0LCAgICAyLCAgICAzLCAgICA0KSwNCiAgcmV2ZW51ZSA9IGMoMS44OCwgMC41OSwgMC4zNSwgICBOQSwgMC45MiwgMC4xNywgMi42NikNCikNCnN0b2Nrcw0KYGBgDQoNCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIFRpZHkgZGF0YQ0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEZvcmV3b3JkDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNClRoZSByYXRpb25hbGUgYmVoaW5kIHRoZSB0aWR5IGRhdGEgcGhpbG9zb3BoeSBpcyB0byBvcmdhbml6ZSB0aGUgZGF0YSB5b3UgaW1wb3J0ZWQgdG8gb3IgY3JlYXRlZCBpbiBSIGludG8gYSBjb25zaXN0ZW50IGZvcm1hdC4gVGhlIGdvYWwgaXMgdG8gc3BlbmQgbGVzcyB0aW1lIGZvcm1hdHRpbmcgeW91ciBkYXRhIGFuZCBtb3JlIHRpbWUgYW5hbHl6aW5nIGl0LiBJZiB5b3VyIGRhdGEgaXMgdGlkeSB5b3Ugd2lsbCBiZSBhYmxlIHRvIHVzZSBpdCBzZWFtbGVzc2x5IGFjcm9zcyB0aGUgKip0aWR5dmVyc2UqKi4NCg0KVGhpcyBwYXJ0IHdpbGwgc2hvdyB5b3UgaG93IHRvIHVzZSB0aGUgZnVuY3Rpb25zIGZyb20gcGFja2FnZSAqKnRpZHlyKiogdG8gdGlkeSB1cCB5b3VyIGRhdGEuIEFzIGEgcmVzdWx0LCBkYXRhIHRyYW5zZm9ybWF0aW9ucyBhbmQgdmlzdWFsaXphdGlvbnMgd2lsbCBiZSBhIHJlYWwgcGllY2Ugb2YgY2FrZS4NCg0KIVtdKGltYWdlcy9kX3RpZHkucG5nKQ0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFdoYXQgaXMgdGlkeSBkYXRhPw0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KVGhlcmUgYXJlIHRocmVlIHNpbXBsZSBhbmQgaW50ZXJyZWxhdGVkIHJ1bGVzIHRoYXQgbWFrZSBhIGRhdGFzZXQgdGlkeToNCg0KMS4gRWFjaCAqKm9ic2VydmF0aW9uL3NhbXBsZSoqIG11c3QgYmUgcmVwb3J0ZWQgaW4gaXRzIG93biAqKnJvdyoqDQoxLiBFYWNoICoqdmFyaWFibGUvZmVhdHVyZSoqIGRlc2NyaWJpbmcgdGhlIHNhbXBsZSBtdXN0IGhhdmUgaXRzIG93biAqKmNvbHVtbioqDQoxLiBFYWNoICoqdmFsdWUqKiBtdXN0IGhhdmUgaXRzIG93biAqKmNlbGwqKg0KDQpXZSB3aWxsIGltcG9ydCBzb21lIHRhYmxlcyBhbmQgY2hlY2sgaWYgdGhleSBmdWxmaWxsIHRoZSBhYm92ZSBydWxlcy4NCg0KIyMjIEV4ZXJjaXNlIDMuMQ0KSW1wb3J0IGZpbGUgYC4vZGF0YXNldHMvd2hvMS5jc3ZgIGludG8gdmFyaWFibGUgKip3aG8xKiogYW5kIHNob3cgdGhlIGltcG9ydGVkIGRhdGEuIElzIHRoZSB3aG8xIGRhdGFzZXQgdGlkeT8NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgRXhlcmNpc2UgMy4xIC0gU09MVVRJT04NCndobzEgPC0gcmVhZF9jc3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dobzEuY3N2IikNCndobzENCiMgd2hvMSBpcyBub3QgdGlkeSBiZWNhdXNlIHZhcmlhYmxlcyBhcmUgZ2F0aGVyZWQNCmBgYA0KDQoNCiMjIyBFeGVyY2lzZSAzLjINCkltcG9ydCBmaWxlIGAuL2RhdGFzZXRzL3dobzIudHh0YCBpbnRvIHZhcmlhYmxlICoqd2hvMioqIGFuZCBzaG93IHRoZSBpbXBvcnRlZCBkYXRhLiBJcyB0aGUgKip3aG8yKiogZGF0YXNldCB0aWR5Pw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBFeGVyY2lzZSAzLjEgLSBTT0xVVElPTg0Kd2hvMiA8LSByZWFkX2RlbGltKCJodHRwOi8vY2JkbS0wMS56ZHYudW5pLW1haW56LmRlL35zdGFsYnJlYy9SY291cnNlRGF0YS93aG8yLnR4dCIsIGRlbGltPSItIikNCndobzINCiMgd2hvMiBpcyBub3QgdGlkeSBiZWNhdXNlIHZhbHVlcyBhcmUgbm90IHNlcGFyYXRlZA0KYGBgDQoNCg0KIyMjIEV4ZXJjaXNlIDMuMw0KSW1wb3J0IHNoZWV0IHBvcHVsYXRpb24gZnJvbSBmaWxlICoqZGF0YXNldHMvd2hvX3NwbGl0Lnhsc3gqKiBpbnRvIHZhcmlhYmxlICoqd2hvX3BvcCoqIGFuZCBzaG93IHRoZSBpbXBvcnRlZCBkYXRhLiBJcyAqKndob19wb3AqKiB0aWR5Pw0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KIyBFeGVyY2lzZSAzLjEgLSBTT0xVVElPTg0Kd2hvX3BvcCA8LSByZWFkX2V4Y2VsKCJkYXRhc2V0cy93aG9fc3BsaXQueGxzeCIpDQp3aG9fcG9wDQojIG5vdCB0aWR5IGJlY2F1c2UgYSB2YXJpYWJsZSBpcyBzcHJlYWQgaW4gY29sdW1ucw0KYGBgDQoNCg0KIyMjIFdoeSB0aWR5IGRhdGE/DQpXaHkgZW5zdXJlIHRoYXQgeW91ciBkYXRhIGlzIHRpZHk/IFRoZXJlIGFyZSB0d28gbWFpbiBhZHZhbnRhZ2VzOg0KDQoxLiBJZiB5b3UgaGF2ZSBhIGNvbnNpc3RlbnQgZGF0YSBzdHJ1Y3R1cmUsIGl04oCZcyBlYXNpZXIgdG8gbGVhcm4gdGhlIHRvb2xzIHRoYXQgd29yayB3aXRoIGl0IGJlY2F1c2UgdGhleSBoYXZlIGFuIHVuZGVybHlpbmcgdW5pZm9ybWl0eS4gVGhlIGZ1bmN0aW9ucyBpbiB0aGUgdGlkeXZlcnNlIGFsbCB3b3JrIGZsYXdsZXNzbHkgd2l0aCB0aWR5IGRhdGFzZXRzLg0KMS4gUGxhY2luZyB2YXJpYWJsZXMgaW4gY29sdW1ucyBhbGxvd3MgUuKAmXMgdmVjdG9yaXNlZCBuYXR1cmUgdG8gc2hpbmUuDQoNClRoZSBwcmluY2lwbGVzIG9mIHRpZHkgZGF0YSBzZWVtIHZlcnkgb2J2aW91cy4gVW5mb3J0dW5hdGVseSwgbW9zdCBkYXRhIHRoYXQgeW91IHdpbGwgZW5jb3VudGVyIHdpbGwgYmUgdW50aWR5LiBUaGVyZSBhcmUgdHdvIG1haW4gcmVhc29uczoNCg0KMS4gTW9zdCBwZW9wbGUgYXJlbuKAmXQgZmFtaWxpYXIgd2l0aCB0aGUgcHJpbmNpcGxlcyBvZiB0aWR5IGRhdGEuDQoxLiBEYXRhIGlzIG9mdGVuIG9yZ2FuaXplZCB0byBmYWNpbGl0YXRlIHNvbWUgdXNlIG90aGVyIHRoYW4gYW5hbHlzaXMuDQoNClRoaXMgbWVhbnMgdGhhdCBmb3IgbW9zdCByZWFsIGFuYWx5emVzLCB5b3Ugd2lsbCBuZWVkIHRvIHRpZHkgeW91ciBkYXRhLg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFNwcmVhZGluZyBhbmQgZ2F0aGVyaW5nDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpXaGVuIHlvdSBhcmUgaW4gZnJvbnQgb2YgYSBkYXRhc2V0LCB0aGUgZmlyc3Qgc3RlcCBpcyBhbHdheXMgdG8gdW5kZXJzdGFuZCB3aGF0IHRoZSBvYnNlcnZhdGlvbnMgYW5kIHRoZSBmZWF0dXJlcyB0aGF0IGRlc2NyaWJlIHRoZW0gYXJlLiBUaGUgc2Vjb25kIHN0ZXAgaXMgdG8gc29sdmUgb25lIG9mIHR3byBjb21tb24gcHJvYmxlbXM6DQoNCjEuICoqT25lIG9ic2VydmF0aW9uKiogaXMgc2NhdHRlcmVkIGFjcm9zcyAqKm11bHRpcGxlIHJvd3MqKg0KMS4gKipPbmUgZmVhdHVyZSoqIGlzIHNwcmVhZCBhY3Jvc3MgKiptdWx0aXBsZSBjb2x1bW5zKioNCg0KVG8gZml4IHRoZXNlIHByb2JsZW1zLCB0aGUgKip0aWR5dmVyc2UqKiBwcm92aWRlcyB5b3Ugd2l0aCBmdW5jdGlvbnMgKipzcHJlYWQqKiBhbmQgKipnYXRoZXIqKi4NCg0KDQojIyMgU3ByZWFkaW5nDQpZb3UgdXNlIGZ1bmN0aW9uICoqc3ByZWFkKiogZm9yIFByb2JsZW0gMTogb25lIG9ic2VydmF0aW9uIGlzIHNjYXR0ZXJlZCBhY3Jvc3MgbXVsdGlwbGUgcm93cy4gVGhhdOKAmXMgZXhhY3RseSB0aGUgcHJvYmxlbSB3ZSBzYXcgaW4gdGFibGUgKip3aG8xKio6DQoNCmBgYHtyfQ0Kd2hvMQ0KYGBgDQoNCioqd2hvMSoqIGlzIGFuIGV4Y2VycHQgb2YgdGhlIFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24gVHViZXJjdWxvc2lzIFJlcG9ydC4gRWFjaCBvYnNlcnZhdGlvbiwgYSBjb3VudHJ5IGluIGEgeWVhciBpbiB0aGlzIGNhc2UsIHNob3VsZCBiZSBhY2NvbXBhbmllZCBieSB0aGF0IHllYXLigJlzIG51bWJlciBvZiB0dWJlcmN1bG9zaXMgY2FzZXMgYW5kIHBvcHVsYXRpb24uIEhvd2V2ZXIsIGVhY2ggY291bnRyeS15ZWFyIHBhaXIgYXBwZWFycyB0d2ljZSBpbiB3aG8xOiBvbmUgZm9yIHRoZSB0dWJlcmN1bG9zaXMgY2FzZXMgYW5kIG9uZSBmb3IgdGhlIGNvdW50cnnigJlzIHBvcHVsYXRpb24uDQoNClRvIHRpZHkgdXAgdGhpcyBkYXRhc2V0LCB3ZSBuZWVkIHRvIHNwcmVhZCBjb2x1bW4gY291bnQgaW50byBuZXcgY29sdW1ucywgb25lIGZvciBlYWNoIG9mIHRoZSBrZXlzIHNwZWNpZmllZCBpbiBjb2x1bW4gdHlwZToNCg0KIVsqKkZpZ3VyZSoqOiBBZGFwdGVkIGZyb20g4oCcUiBmb3IgRGF0YSBTY2llbmNl4oCdLCBDaGFwdGVyIDEyXShpbWFnZXMvc3ByZWFkLnBuZykNCg0KV2UgY2FuIGRvIHRoaXMgd2l0aCBmdW5jdGlvbiAqKnNwcmVhZCoqIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0Kd2hvMV90aWR5IDwtIHdobzEgJT4lIA0KICAgc3ByZWFkKHZhbHVlID0gY291bnQsIGtleSA9IHR5cGUpDQp3aG8xX3RpZHkNCmBgYA0KDQpOb3RlIHRoZSBzeW50YXggdXNlZCB0byB0aWR5IHVwICoqd2hvMSoqOg0KDQoxLiBDaG9vc2UgYSB2YXJpYWJsZSBuYW1lIHRvIHN0b3JlIHRoZSB0aWR5IGRhdGEgKHRoaXMgY291bGQgdmVyeSB3ZWxsIGJlICoqd2hvMSoqIGFnYWluKS4NCjEuIFVzZSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciAqKjwtKiouDQoxLiBTcGVjaWZ5IHRoZSB2YXJpYWJsZSB0aGF0IHdlIHdhbnQgdG8gdGlkeSB1cC4NCjEuIFVzZSB0aGUgcGlwZSBvcGVyYXRvciAqKiU+JSoqIHRvIGNoYW5uZWwgdGhlIGNvbnRlbnQgb2Ygd2hvMSB0byB0aGUgZnVuY3Rpb24gKipzcHJlYWQqKiAobW9yZSBvbiB0aGUgcGlwZSBvcGVyYXRvciBsYXRlciBpbiB0aGlzIFBhcnQpLg0KMS4gQ2FsbCBmdW5jdGlvbiAqKnNwcmVhZCoqIGFuZCBzcGVjaWZ5IHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGUgKip2YWx1ZXMqKiB0aGF0IHdlIHdhbnQgdG8gc3ByZWFkIGFuZCB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIG5ldyB2YXJpYWJsZSBuYW1lcyAoa25vd24gYXMgdGhlICoqa2V5KiogY29sdW1uKS4NCg0KDQojIyMgR2F0aGVyaW5nDQpZb3UgdXNlIGZ1bmN0aW9uICoqZ2F0aGVyKiogZm9yIFByb2JsZW0gMjogKipvbmUgZmVhdHVyZSoqIGlzIHNwcmVhZCBhY3Jvc3MgKiptdWx0aXBsZSBjb2x1bW5zKiouIFRoYXQgaXMgZXhhY3RseSB0aGUgcHJvYmxlbSB3ZSBzYXcgaW4sIGZvciBleGFtcGxlLCB0YWJsZSAqd2hvX2Nhc2VzKiBmcm9tIHRoZSBFeGNlbCBmaWxlICp3aG9fc3BsaXQueGxzeCo6DQoNCmBgYHtyfQ0Kd2hvX2Nhc2VzDQpgYGANCg0KDQpUaGUgY29sdW1uIG5hbWVzIDE5OTkgYW5kIDIwMDAgYXJlIGFjdHVhbGx5IHZhbHVlcyBvZiB0aGUgdmFyaWFibGUgeWVhciwgd2hpY2ggbWVhbnMgdGhhdCBlYWNoIHJvdyByZXByZXNlbnRzIHR3byBvYnNlcnZhdGlvbnMgaW5zdGVhZCBvZiBvbmUuDQoNClRvIHRpZHkgdGhpcyBkYXRhc2V0LCB3ZSBuZWVkIHRvIGdhdGhlciBjb2x1bW5zIDE5OTkgYW5kIDIwMDAgaW50byBhIG5ldyBwYWlyIG9mIHZhcmlhYmxlczoNCg0KDQohWyoqRmlndXJlKio6IEFkYXB0ZWQgZnJvbSDigJxSIGZvciBEYXRhIFNjaWVuY2XigJ0sIENoYXB0ZXIgMTJdKGltYWdlcy9nYXRoZXIucG5nKQ0KDQoNCldlIGNhbiBkbyB0aGlzIHdpdGggZnVuY3Rpb24gKipnYXRoZXIqKiBhcyBmb2xsb3dzOg0KYGBge3J9DQp3aG9fY2FzZXNfdGlkeSA8LSB3aG9fY2FzZXMgJT4lIA0KICAgZ2F0aGVyKCcxOTk5JywgJzIwMDAnLCBrZXkgPSAieWVhciIsIHZhbHVlID0gImNhc2VzIikNCndob19jYXNlc190aWR5DQpgYGANCg0KDQpOb3RlIHRoZSBzeW50YXggdXNlZCB0byB0aWR5IHVwICp3aG9fY2FzZXMqOg0KDQoxLiBDaG9vc2UgYSB2YXJpYWJsZSBuYW1lIHRvIHN0b3JlIHRoZSB0aWR5IGRhdGEgKHRoaXMgY291bGQgdmVyeSB3ZWxsIGJlICp3aG9fY2FzZXMqIGFnYWluKS4NCjEuIFVzZSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciAqKjwtKiouDQoxLiBTcGVjaWZ5IHRoZSB2YXJpYWJsZSB0aGF0IHdlIHdhbnQgdG8gdGlkeSB1cC4NCjEuIFVzZSB0aGUgcGlwZSBvcGVyYXRvciAqKiU+JSoqIHRvIGNoYW5uZWwgdGhlIGNvbnRlbnRzIG9mICp3aG9fY2FzZXMqIHRvIGZ1bmN0aW9uICoqZ2F0aGVyKiouDQoxLiBDYWxsIGZ1bmN0aW9uICoqZ2F0aGVyKiogYW5kIHNwZWNpZnkgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBtZXJnZSwgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB0aGF0IHdpbGwgY29udGFpbiB0aGUgbWVyZ2VkICoqa2V5cyoqIGFuZCB0aGUgbmFtZSBvZiB0aGUgY29sdW1uIHRoYXQgd2lsbCBjb250YWluIHRoZSAqKnZhbHVlKiogZm9yIHRoZSBudW1iZXIgb2YgdHViZXJjdWxvc2lzIGNhc2VzIHBlciBjb3VudHJ5Lg0KDQoNCiMjIyBFeGVyY2lzZSAzLjQNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgdGliYmxlLCB3aGljaCByZWNvcmRzIGhlaWdodHMgYW5kIHdlaWdodHMgb2YgMyBzdHVkZW50czoNCg0KYGBge3J9DQpzdHVkZW50cyA8LSB0aWJibGUobmFtZSA9IHJlcChjKCJKb25hcyIsICJJbmVzIiwgIkhhbm5hIiksIGVhY2ggPSAyKSwNCiAgICAgICAgICAgICAgICAgICB0eXBlID0gcmVwKGMoImhlaWdodCIsICJ3ZWlnaHQiKSwgMyksDQogICAgICAgICAgICAgICAgICAgbWVhc3VyZSA9IGMoMS44MywgODEsIDEuNzUsIDcxLCAxLjY5LCA1NSkpDQpzdHVkZW50cw0KYGBgDQoNClVzZSB0aGUgYXBwcm9wcmlhdGUgKip0aWR5cioqIGZ1bmN0aW9uIG9uIHZhcmlhYmxlICoqc3R1ZGVudHMqKiB0byBtYWtlIGl0IHRpZHkuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDMuNCAtIFNPTFVUSU9ODQojIFdlIG5lZWQgdG8gc3ByZWFkIHR5cGUgYW5kIG1lYXN1cmUNCnN0dWRlbnRzX3RpZHkgPC0gc3R1ZGVudHMgJT4lIHNwcmVhZCh2YWx1ZSA9IG1lYXN1cmUsIGtleSA9IHR5cGUpDQpzdHVkZW50c190aWR5DQpgYGANCg0KDQojIyMgRXhlcmNpc2UgMy41DQpDb25zaWRlciB0aGUgZm9sbG93aW5nIHRpYmJsZSwgd2hpY2ggcmVjb3JkcyB0aGUgZXhwcmVzc2lvbiBvZiA0IGdlbmVzIGF0IDMgZGlmZmVyZW50IHRpbWUgcG9pbnRzIChkMDAsIGQwMiBhbmQgZDA0KToNCg0KYGBge3J9DQpnZW5lcyA8LSB0aWJibGUoc3ltYm9sID0gYygiRE1EIiwgIk1ZT0ciLCAiTVlGNSIsICJNWU9EMSIpLA0KICAgICAgICAgICAgICAgIGQwMCA9IGMoMC42OTcsIDAuODQ0LCAxLjg3OCwgMS42MjIpLA0KICAgICAgICAgICAgICAgIGQwMiA9IGMoMS45ODYsIDAuMDUxLCAwLjg4NywgMS4zMTMpLA0KICAgICAgICAgICAgICAgIGQwNCA9IGMoMC4xNTcsIDAuNzc0LCAxLjUwNywgMC42MjgpKQ0KZ2VuZXMNCmBgYA0KDQpIZXJlLCBhbiBvYnNlcnZhdGlvbiBzaG91bGQgYmUgYSBnZW5lIGF0IGEgZ2l2ZW4gdGltZSBwb2ludC4gVXNlIHRoZSBhcHByb3ByaWF0ZSB0aWR5ciBmdW5jdGlvbiBvbiB2YXJpYWJsZSBnZW5lcyB0byBtYWtlIGl0IHRpZHkuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDMuNSAtIFNPTFVUSU9ODQojIFdlIG5lZWQgdG8gZ2F0aGVyIGNvbHVtbnMgZGVsaW1pdGVkIGluIHRoZSB0YWJsZSBieSBkMDAgYW5kIGQwNA0KZ2VuZXNfdGlkeSA8LSBnZW5lcyAlPiUgZ2F0aGVyKGQwMDpkMDQsIGtleT0idGltZSIsIHZhbHVlPSJleHByZXNzaW9uIikNCmdlbmVzX3RpZHkNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFNlcGFyYXRpbmcgYW5kIHVuaXRpbmcNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KIyMjIFNlcGFyYXRpbmcNCldoZW4gb25lIGNvbHVtbiBpbiBhIGRhdGFzZXQgY29udGFpbnMgdHdvIHZhcmlhYmxlcywgd2XigJlsbCBuZWVkIHRvIHVzZSBmdW5jdGlvbiAqKnNlcGFyYXRlKiogdG8gZml4IHRoZSBpc3N1ZS4gVGhhdOKAmXMgZXhhY3RseSB0aGUgcHJvYmxlbSB3ZSBzYXcgaW4gdGFibGUgKndobzIqOg0KDQpgYGB7cn0NCndobzINCmBgYA0KDQpDb2x1bW4gcmF0ZSBjb250YWlucyBib3RoIGNhc2VzIGFuZCBwb3B1bGF0aW9uLCBzbyB3ZSBuZWVkIHRvIHNwbGl0IGl0IGluIHR3bzoNCg0KIVsqKkZpZ3VyZSoqOiBBZGFwdGVkIGZyb20g4oCcUiBmb3IgRGF0YSBTY2llbmNl4oCdLCBDaGFwdGVyMTJdKGltYWdlcy9zZXBhcmF0ZS5wbmcpDQoNCldlIGNhbiBkbyB0aGlzIHdpdGggZnVuY3Rpb24gKipzZXBhcmF0ZSoqIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0Kd2hvMl90aWR5IDwtIHdobzIgJT4lIA0KICAgc2VwYXJhdGUocmF0ZSwgaW50byA9IGMoImNhc2VzIiwgInBvcHVsYXRpb24iKSwgc2VwID0gIi8iKQ0Kd2hvMl90aWR5DQpgYGANCg0KTm90ZSB0aGUgc3ludGF4IHVzZWQgdG8gdGlkeSB1cCAqKndobzIqKjoNCg0KMS4gQ2hvb3NlIGEgdmFyaWFibGUgbmFtZSB0byBzdG9yZSB0aGUgdGlkeSBkYXRhICh0aGlzIGNvdWxkIHZlcnkgd2VsbCBiZSAqKndobzIqKiBhZ2FpbikuDQoxLiBVc2UgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgKio8LSoqLg0KMS4gU3BlY2lmeSB0aGUgdmFyaWFibGUgdGhhdCB3ZSB3YW50IHRvIHRpZHkgdXAuDQoxLiBVc2UgdGhlIHBpcGUgb3BlcmF0b3IgKiolPiUqKiB0byBjaGFubmVsIHRoZSBjb250ZW50cyBvZiAqKndobzIqKiB0byBmdW5jdGlvbiAqKnNlcGFyYXRlKiouDQoxLiBDYWxsIGZ1bmN0aW9uICoqc2VwYXJhdGUqKiBhbmQgc3BlY2lmeSB0aGUgY29sdW1uIHdlIHdhbnQgdG8gc3BsaXQsIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1ucyBhbmQgdGhlIHNlcGFyYXRvciB0aGF0IGlzIGN1cnJlbnRseSBtZXJnaW5nIHRoZSB2YXJpYWJsZXMuDQoNCg0KIyMjIFVuaXRpbmcNCldoZW4gYSB2YXJpYWJsZSBpcyBzdG9yZWQgaW4gdHdvIHNlcGFyYXRlIGNvbHVtbnMgYW5kIGlzIG1vcmUgY29udmVuaWVudCB0byBjb21iaW5lIHRoZW0sIHdlIG5lZWQgdG8gdXNlIGZ1bmN0aW9uICoqdW5pdGUqLiBUYWJsZSAqd2hvMyogaGFzIHRoaXMgcHJvYmxlbToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCndobzMgPC0gcmVhZF90c3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dobzMudHN2IikNCndobzMNCmBgYA0KDQoNCkNvbHVtbiAqKmNlbnR1cnkqKiBhbmQgKip5ZWFyKiogY2FuIGJlIGNvbWJpbmVkIGludG8gYSBzaW5nbGUgY29sdW1uIGNhbGxlZCAqKnllYXIqKjoNCg0KIVsqKkZpZ3VyZSoqOiBBZGFwdGVkIGZyb20g4oCcUiBmb3IgRGF0YSBTY2llbmNl4oCdLCBDaGFwdGVyMTJdKGltYWdlcy91bml0ZS5wbmcpDQoNCldlIGNhbiBkbyB0aGlzIHdpdGggZnVuY3Rpb24gKip1bml0ZSoqIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0Kd2hvM190aWR5IDwtIHdobzMgJT4lIA0KICAgdW5pdGUoY2VudHVyeSwgeWVhciwgY29sID0gInllYXIiLCBzZXAgPSAiIikNCndobzNfdGlkeQ0KYGBgDQoNCk5vdGUgdGhlIHN5bnRheCB1c2VkIHRvIHRpZHkgdXAgd2hvMzoNCg0KMS4gQ2hvb3NlIGEgdmFyaWFibGUgbmFtZSB0byBzdG9yZSB0aGUgdGlkeSBkYXRhICh0aGlzIGNvdWxkIHZlcnkgd2VsbCBiZSAqKndobzMqKiBhZ2FpbikuDQoxLiBVc2UgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgKio8LSoqLg0KMS4gU3BlY2lmeSB0aGUgdmFyaWFibGUgdGhhdCB3ZSB3YW50IHRvIHRpZHkgdXAuDQoxLiBVc2UgdGhlIHBpcGUgb3BlcmF0b3IgKiolPiUqKiB0byBjaGFubmVsIHRoZSBjb250ZW50cyBvZiAqKndobzMqKiB0byBmdW5jdGlvbiAqKnVuaXRlKiouDQoxLiBDYWxsIGZ1bmN0aW9uICoqdW5pdGUqKiBhbmQgc3BlY2lmeSB0aGUgY29sdW1ucyB3ZSB3YW50IHRvIG1lcmdlLCB0aGUgbmFtZSBvZiB0aGUgbmV3IGNvbHVtbiBhbmQgdGhlIHNlcGFyYXRvciB0aGF0IHdlIHdhbnQgdG8gdXNlIHRvIG1lcmdlIHRoZSBjb2x1bW4gdmFsdWVzLg0KDQoNCiMjIyBFeGVyY2lzZSAzLjYNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgdGliYmxlLCB3aGljaCByZWNvcmRzIGhlaWdodHMgYW5kIHdlaWdodHMgb2YgMyBzdHVkZW50czoNCg0KYGBge3J9DQpzdHVkZW50cyA8LSB0aWJibGUobmFtZSA9IGMoIkpvbmFzIiwgIkluZXMiLCAiSGFubmEiKSwNCiAgICAgICAgICAgICAgICAgICByYXRpbyA9IGMoIjgxLzEuODMiLCAiNzEvMS43NSIsICI1NS8xLjY5IikpDQpzdHVkZW50cw0KYGBgDQoNClVzZSB0aGUgYXBwcm9wcmlhdGUgKip0aWR5cioqIGZ1bmN0aW9uIG9uIHZhcmlhYmxlICoqc3R1ZGVudHMqKiB0byBtYWtlIGl0IHRpZHkuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDMuNiAtIFNPTFVUSU9ODQojIFdlIG5lZWQgdG8gc2VwYXJhdGUgcmF0aW8NCnN0dWRlbnRzX3RpZHkgPC0gc3R1ZGVudHMgJT4lIA0KICBzZXBhcmF0ZShyYXRpbywgaW50bz1jKCJoZWlnaHQiLCAid2VpZ2h0IiksIHNlcCA9ICIvIikNCnN0dWRlbnRzX3RpZHkNCmBgYA0KDQoNCiMjIyBFeGVyY2lzZSAzLjcNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgdGliYmxlLCB3aGljaCByZWNvcmRzIHRoZSBwcmljZSBvZiA0IGRydWdzOg0KDQpgYGB7cn0NCmRydWdzIDwtIHRpYmJsZShuYW1lID0gYygicGVuaWNpbGxpbiIsICJpbnN1bGluZSIsICJhc3BpcmluIiwgImxhbm94aW4iKSwNCiAgICAgICAgICAgICAgICBldXJvcyA9IGMoMTMsIDE3LCA1LCAyNSksDQogICAgICAgICAgICAgICAgY2VudHMgPSBjKDgxLCAyMCwgMTQsIDEyKSkNCmRydWdzDQpgYGANCg0KVXNlIHRoZSBhcHByb3ByaWF0ZSAqKnRpZHlyKiogZnVuY3Rpb24gb24gdmFyaWFibGUgZHJ1Z3MgdG8gbWFrZSBpdCB0aWR5Lg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBFeGVyY2lzZSAzLjcgLSBTT0xVVElPTg0KIyBXZSBuZWVkIHRvIHVuaXRlIGV1cm9zIGFuZCBjZW50cw0KZHJ1Z3MyPC1kcnVncyAlPiUgdW5pdGUoZXVyb3MsIGNlbnRzLCBjb2wgPSAicHJpY2UiLCBzZXA9ICIuIikNCmRydWdzMg0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgVGhlICU+JSBvcGVyYXRvcg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KDQpBcyB5b3Uga25vdyBhbHJlYWR5LCB3ZSBjaGFubmVsIHRoZSBjb250ZW50cyBvZiBhICoqdGliYmxlKiogdG8gdGhlIGRpZmZlcmVudCAqKnRpZHlyKiogZnVuY3Rpb25zIHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yOiAqKiU+JSoqLg0KDQpQaXBlcyBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBjbGVhcmx5IGV4cHJlc3NpbmcgdGhlIG9wZXJhdGlvbiB3ZSB3YW50IHRvIHBlcmZvcm0gb24gYSB2YXJpYWJsZS4gSW4gYWRkaXRpb24sIHRoZXkgY2FuIGJlIHVzZWQgdG8gYXBwbHkgYSBzZXF1ZW5jZSBvZiBvcGVyYXRpb25zIHRvIGEgdmFyaWFibGUuDQoNClRhYmxlICp3aG8zKiwgZm9yIGV4YW1wbGUsIGhhcyB0d28gcHJvYmxlbXM6ICoqY2FzZXMqKiBhbmQgKipwb3B1bGF0aW9uKiogYXJlIGV4cHJlc3NlZCBhcyBhICoqcmF0ZSoqIGFuZCAqKnllYXIqKiBpcyBzcGxpdCBpbiBjb2x1bW5zICoqY2VudHVyeSoqIGFuZCAqKnllYXIqKjoNCg0KYGBge3J9DQp3aG8zDQpgYGANCg0KU28sIHdlIGNsZWFybHkgbmVlZCB0d28gc3RlcHMgdG8gdGlkeSB1cCB0aGlzIGRhdGFzZXQ6DQoNCmBgYHtyfQ0Kd2hvM190aWR5IDwtIHdobzMgJT4lIA0KICAgc2VwYXJhdGUocmF0ZSwgaW50byA9IGMoImNhc2VzIiwgInBvcHVsYXRpb24iKSwgc2VwID0gIi8iKQ0KDQp3aG8zX3RpZHkgPC0gd2hvM190aWR5ICU+JSANCiAgIHVuaXRlKGNlbnR1cnksIHllYXIsIGNvbCA9ICJ5eXl5Iiwgc2VwID0gIiIpDQoNCndobzNfdGlkeQ0KYGBgDQoNClRoYW5rcyB0byB0aGUgcGlwZSwgd2UgY2FuIGFwcGx5IHRoZXNlIHR3byBvcGVyYXRpb25zIHRvICp3aG8zKiBpbiBvbmUgZ286DQoNCmBgYHtyfQ0Kd2hvM190aWR5IDwtIHdobzMgJT4lIA0KICBzZXBhcmF0ZShyYXRlLCBpbnRvID0gYygiY2FzZXMiLCAicG9wdWxhdGlvbiIpLCBzZXAgPSAiLyIpICU+JSANCiAgdW5pdGUoY2VudHVyeSwgeWVhciwgY29sID0gInl5eXkiLCBzZXAgPSAiIikNCndobzNfdGlkeQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGlzIHByb2Nlc3MgcmVhZHMgYWxtb3N0IGxpa2UgbmF0dXJhbCBsYW5ndWFnZToNCg0KMS4gV2UgY2hhbm5lbCAob3IgcGlwZSkgdGhlIGNvbnRlbnQgb2YgKndobzMqIHRvIGZ1bmN0aW9uICoqc2VwYXJhdGUqKi4NCjEuICoqc2VwYXJhdGUqKiBzcGxpdHMgY29sdW1uIHJhdGUgaW50byBjb2x1bW5zICpjYXNlcyogYW5kICpwb3B1bGF0aW9uKiB1c2luZyDigJwv4oCdIGFzIHNlcGFyYXRvci4NCjEuIFRoZSByZXN1bHQgb2YgKipzZXBhcmF0ZSoqIGlzIHBpcGVkIHRvIGZ1bmN0aW9uICoqdW5pdGUqKi4NCjEuICoqdW5pdGUqKiBjb21iaW5lcyBjb2x1bW5zICpjZW50dXJ5KiBhbmQgKnllYXIqIGludG8gY29sdW1uICoqeXl5eSoqIHVzaW5nIG5vdGhpbmcgKGVtcHR5IHN0cmluZyAiIikgYXMgc2VwYXJhdG9yLg0KDQpFdmVuIHRob3VnaCB3cml0aW5nIHRoZSBhYm92ZSBjb2RlIG9uIGEgc2luZ2xlIGxpbmUgb2YgY29kZSBpcyBzdGlsbCB2YWxpZCwgaXQgaXMgZ29vZCBwcmFjdGljZSB0byB1c2UgKipFTlRFUioqIChpLmUuIGEgbmV3IGxpbmUpIGFmdGVyIGV2ZXJ5ICoqJT4lKiogZm9yIGNsYXJpdHkuDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyBEYXRhIHRyYW5zZm9ybWF0aW9uDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgRm9yZXdvcmQNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCk9uY2UgeW91IGhhdmUgdGlkaWVkIHlvdXIgZGF0YXNldCBvZiBpbnRlcmVzdCwgeW91IHdpbGwgb2Z0ZW4gbmVlZCB0byBjcmVhdGUgc29tZSBuZXcgdmFyaWFibGVzIG9yIHN1bW1hcmllcywgb3IgdG8gcmVvcmRlciB0aGUgb2JzZXJ2YXRpb25zIHRvIG1ha2UgdGhlIGRhdGEgZWFzaWVyIHRvIHdvcmsgd2l0aC4NCg0KVGhpcyBwYXJ0IHdpbGwgc2hvdyB5b3UgaG93IHRvIHVzZSB0aGUgZnVuY3Rpb25zIGZyb20gcGFja2FnZSAqKmRwbHlyKiogdG8gdHJhbnNmb3JtIHlvdXIgZGF0YSBhbmQgdW5kZXJzdGFuZCBpdCBiZXR0ZXIuDQoNCg0KVGhyb3VnaG91dCB0aGlzIHBhcnQsIHlvdSBhcmUgZ29pbmcgdG8gdXNlIGEgdGlkaWVkIHVwIGV4Y2VycHQgZnJvbSB0aGUgV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbiBHbG9iYWwgVHViZXJjdWxvc2lzIFJlcG9ydC4gV2UgaGF2ZSBiZWVuIHdvcmtpbmcgd2l0aCB0aGlzIGRhdGFzZXQsIHNvIHlvdSBzaG91bGQgYmUgZmFtaWxpYXIgd2l0aCBpdDoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCndob19yZWYgPC0gcmVhZF90c3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL3dob19yZWYudHN2IikNCndob19yZWYNCmBgYA0KDQpUaGUgdGliYmxlIHJlcG9ydHMgdGhlIG51bWJlciBvZiB0dWJlcmN1bG9zaXMgY2FzZXMgaW4gMTk5OSBhbmQgMjAwMCBmb3IgdGhyZWUgZGlmZmVyZW50IGNvdW50cmllcywgYXMgd2VsbCBhcyB0aGVpciBwb3B1bGF0aW9uLg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFNvcnRpbmcgYnkgY29sdW1uKHMpDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpUbyBzb3J0IG9ic2VydmF0aW9ucyBieSBvbmUgb3IgbW9yZSBjb2x1bW5zLCAqKmRwbHlyKiogb2ZmZXJzIGZ1bmN0aW9uICoqYXJyYW5nZSoqLiBUaGlzIGZ1bmN0aW9uIHRha2VzIGEgdGliYmxlIGFuZCBhIGNvbHVtbiBuYW1lIHRvIG9yZGVyIGJ5Og0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBhcnJhbmdlKHllYXIpDQpgYGANCg0KSWYgeW91IHByb3ZpZGUgbW9yZSBjb2x1bW5zLCAqKmFycmFuZ2UqKiB3aWxsIGZpcnN0IHNvcnQgYnkgdGhlIGZpcnN0IG9uZSwgdGhlbiB0aGUgc2Vjb25kIG9uZSBhbmQgc28gb24uIFRoaXMgcHJvY2VzcyBicmVha3MgdGllcyBpbiB0aGUgdmFsdWVzIG9mIHRoZSBwcmVjZWRpbmcgY29sdW1uczoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgYXJyYW5nZSh5ZWFyLCBjYXNlcywgcG9wdWxhdGlvbikNCmBgYA0KDQpUbyBzb3J0IGluIGRlc2NlbmRpbmcgb3JkZXIsIHlvdSBoYXZlIHRvIHVzZSAqKmRlc2MqKjoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgYXJyYW5nZShkZXNjKGNvdW50cnkpLCBjYXNlcykNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFNlbGVjdGluZyBjb2x1bW5zDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpGdW5jdGlvbiAqKnNlbGVjdCoqIGFsbG93cyB5b3UgdG8gZm9jdXMgb24gdmFyaWFibGVzIHlvdSBhcmUgcmVhbGx5IGludGVyZXN0ZWQgaW4gYnkgcmVtb3ZpbmcgdW53YW50ZWQgY29sdW1uczoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgc2VsZWN0KGNvdW50cnksIGNhc2VzKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyB1c2UgdGhlICoqOioqIG9wZXJhdG9yIHRvIHNlbGVjdCBhIGdyb3VwIG9mIGNvbHVtbnM6DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIHNlbGVjdCh5ZWFyOnBvcHVsYXRpb24pDQpgYGANCg0KSWYgeW91IHB1dCBhIG1pbnVzIHNpZ24gYmVmb3JlIGNvbHVtbnMgaW4gKipzZWxlY3QqKiwgaXQgbWVhbnMgdGhhdCB5b3Ugd2FudCB0byBkaXNjYXJkIHN1Y2ggdmFyaWFibGVzOg0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBzZWxlY3QoLXBvcHVsYXRpb24pDQpgYGANCg0KVGhpcywgaW4gY29tYmluYXRpb24gd2l0aCB0aGUgKio6Kiogb3BlcmF0b3IgaXMgc3BlY2lhbGx5IHVzZWZ1bCB3aGVuIHlvdXIgZGF0YXNldCBoYXMgaHVuZHJlZHMgb3IgZXZlbiB0aG91c2FuZHMgb2YgY29sdW1ucyBhbmQgeW91IHdhbnQgdG8gZm9jdXMgb25seSBvbiBhIGZldzoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgc2VsZWN0KC0oeWVhcjpwb3B1bGF0aW9uKSkNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFJlbmFtaW5nIGNvbHVtbnMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCklmIGEgY29sdW1uIGluIHlvdXIgdGliYmxlIGhhcyBhIHN0cmFuZ2Ugb3Igbm9uLWluZm9ybWF0aXZlIG5hbWUsIHlvdSBjYW4gdXNlIGZ1bmN0aW9uICoqcmVuYW1lKiogdG8gc29sdmUgdGhpcyBpc3N1ZToNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgcmVuYW1lKHR1YmVyY3Vsb3Npc19jYXNlcyA9IGNhc2VzKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgRmlsdGVyaW5nIHJvd3MNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCkZ1bmN0aW9uICoqZmlsdGVyKiogaXMgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBpbiBwYWNrYWdlICoqZHBseXIqKiBhcyBpdCBhbGxvd3MgeW91IHRvIHN1YnNldCBvYnNlcnZhdGlvbnMgYmFzZWQgb24gdGhlaXIgdmFsdWVzLiAqKmZpbHRlcioqIHRha2VzIHRoZSBjb250ZW50cyBvZiBhIHRpYmJsZSBhbmQgaXRzIGFyZ3VtZW50cyBhcmUgbG9naWNhbCBleHByZXNzaW9ucyB0byBmaWx0ZXIgaXQuDQoNCkZvciBleGFtcGxlLCB3ZSBjYW4gZm9jdXMgb24gdHViZXJjdWxvc2lzIGNhc2VzIGluIEJyYXppbCBhcyBmb2xsb3dzOg0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSAiQnJhemlsIikNCmBgYA0KDQpUbyByZXRyaWV2ZSBDaGluZXNlIGNhc2VzIHRoYXQgb2NjdXJyZWQgYWZ0ZXIgMTk5OSwgd2UgZG86DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIGZpbHRlcihjb3VudHJ5ID09ICJDaGluYSIgJiB5ZWFyID4gMTk5OSkNCmBgYA0KDQpUbyBzdWJzZXQgb2JzZXJ2YXRpb25zIGZyb20gQnJhemlsIG9yIENoaW5hOg0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSAiQnJhemlsIiB8IGNvdW50cnkgPT0gIkNoaW5hIikNCmBgYA0KDQpOb3RlIHRoYXQgd2UgdXNlIHRoZSBjb21wYXJpc29uIG9wZXJhdG9ycyA+LCA8LCA+PSwgPD0sID09LCAhPSAobm90IGVxdWFsKSB0byBzcGVjaWZ5IHRoZSBjb2x1bW4gdmFsdWUgb3IgcmFuZ2Ugb2YgdmFsdWVzIHdlIHdhbnQgdG8gZm9jdXMgb24uIEluIGFkZGl0aW9uLCB3ZSB1c2UgdGhlIGxvZ2ljYWwgb3BlcmF0b3JzICYgKGFuZCkgYW5kIHwgKG9yKSB0byBjb21iaW5lIG11bHRpcGxlIGNvbmRpdGlvbnMgcGFzc2VkIG9uIHRvICoqZmlsdGVyKiouDQoNCllvdSBjYW4gbmVnYXRlIGNvbmRpdGlvbnMgd2l0aCB0aGUgKiohKiogb3BlcmF0b3I6DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIGZpbHRlcighKGNvdW50cnkgPT0gIkJyYXppbCIpKQ0KYGBgDQoNCkJhc2ljYWxseSwgYW55IG9wZXJhdGlvbiB0aGF0IGdlbmVyYXRlcyBhIGxvZ2ljYWwgdmVjdG9yIGNhbiBiZSB1c2VkIHdpdGhpbiAqKmZpbHRlcioqIHRvIHN1YnNldCB5b3VyIHRpYmJsZS4NCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBBZGRpbmcgbmV3IHZhcmlhYmxlcw0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KQmVzaWRlcyBzZWxlY3RpbmcgZXhpc3RpbmcgY29sdW1ucywgaXQgaXMgb2Z0ZW4gdXNlZnVsIHRvIGNyZWF0ZSBuZXcgb25lcyB0aGF0IGFyZSBmdW5jdGlvbnMgb2YgZXhpc3RpbmcgdmFyaWFibGVzLiAqKmRwbHlyKiogb2ZmZXJzIGZ1bmN0aW9uICoqbXV0YXRlKiogdG8gYWRkIG5ldyBjb2x1bW5zIGF0IHRoZSBlbmQgb2YgeW91ciBkYXRhc2V0Lg0KDQpGb3IgZXhhbXBsZSwgaXQgbWlnaHQgYmUgdGVtcHRpbmcgdG8gc2F5IHRoYXQgQWZnaGFuaXN0YW4gaGFzIGJldHRlciBwcm9ncmFtbWVzIGFnYWluc3QgdHViZXJjdWxvc2lzIHRoYW4gQnJhemlsIGFuZCBDaGluYS4gSG93ZXZlciwgQWZnaGFuaXN0YW7igJlzIHBvcHVsYXRpb24gaXMgc21hbGxlci4gTGV04oCZcyBsb29rIGF0IHRoZSBudW1iZXIgb2YgY2FzZXMgcGVyIDEwIHRob3VzYW5kIGluZGl2aWR1YWxzIHRvIGdldCBhIGJldHRlciBwaWN0dXJlOg0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBtdXRhdGUoY2FzZXNfcGVyXzEwayA9IGNhc2VzLyhwb3B1bGF0aW9uLzEwMDAwKSkNCmBgYA0KDQoqKm11dGF0ZSoqIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBvbmUgb3IgbW9yZSB2YXJpYWJsZXMgYXQgb25jZToNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgbXV0YXRlKGNhc2VzX3Blcl8xMGsgPSBjYXNlcy8ocG9wdWxhdGlvbi8xMDAwMCksIHRob3VzYW5kX2Nhc2VzID0gY2FzZXMvMTAwMCkNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEdyb3VwZWQgb3BlcmF0aW9ucw0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KSXQgaXMgb2Z0ZW4gdGhlIGNhc2UgdGhhdCBzb21lIHJlY29yZHMgaW4geW91ciBkYXRhc2V0IGJlbG9uZyB0byBjZXJ0YWluIGdyb3Vwcy4gRm9yIGV4YW1wbGUsIHdlIGNhbiBncm91cCBkYXRhIGluICoqd2hvX3JlZioqIGJ5IGNvdW50cnkgb3IgYnkgeWVhcjoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgZ3JvdXBfYnkoY291bnRyeSkNCmBgYA0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBncm91cF9ieSh5ZWFyKQ0KYGBgDQoNCk5vdGUgdGhhdCBncm91cGluZyBieSBjb3VudHJ5IHJlc3VsdHMgaW4gMyBncm91cHMsIHdoZXJlYXMgZ3JvdXBpbmcgYnkgeWVhciByZXN1bHRzIGluIDIuDQoNCkl0IGlzIGFsc28gcG9zc2libGUgdG8gZ3JvdXAgYnkgbXVsdGlwbGUgdmFyaWFibGVzOg0KDQpgYGB7cn0NCndob19yZWYgJT4lIA0KICBncm91cF9ieShjb3VudHJ5LCB5ZWFyKQ0KYGBgDQoNClRoZSByZXN1bHQgaXMgNiBncm91cHMuIFRoaXMgaXMgbm90IHZlcnkgdXNlZnVsIGluIHRoaXMgcGFydGljdWxhciBleGFtcGxlLCBiZWNhdXNlIGVhY2ggb2JzZXJ2YXRpb24gYmVsb25ncyB0byBpdHMgb3duIGdyb3VwLg0KDQojIyMgR3JvdXBlZCBzdW1tYXJpZXMNCklmIHdlIHBpcGUgdGhlIHJlc3VsdCBvZiBhICoqZ3JvdXBfYnkqKiBvcGVyYXRpb24gdG8gdGhlIGZ1bmN0aW9uICoqc3VtbWFyaXNlKiosIHRoZSB1bml0IG9mIGFuYWx5c2lzIGNoYW5nZXMgZnJvbSB0aGUgY29tcGxldGUgdGliYmxlIHRvIGVhY2ggaW5kaXZpZHVhbCBncm91cC4gVGhpcyBpcyB2ZXJ5IHVzZWZ1bCwgYmVjYXVzZSB3ZSBjYW4gY29tcHV0ZSB1c2VmdWwgc3RhdGlzdGljcyBhbmQgc3VtbWFyaWVzIG9uIGEgcGVyIGdyb3VwIGJhc2lzLg0KDQpUaGUgZm9sbG93aW5nIGV4YW1wbGUgY29tcHV0ZXMgdGhlIHRvdGFsIG51bWJlciBvZiB0dWJlcmN1bG9zaXMgY2FzZXMgcGVyIHllYXI6DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIGdyb3VwX2J5KHllYXIpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2Nhc2VzID0gc3VtKGNhc2VzKSkNCmBgYA0KDQpUaGUgZm9sbG93aW5nIGV4YW1wbGUgY29tcHV0ZXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGNhc2VzIHBlciBjb3VudHJ5IGFuZCB0aGUgY29ycmVzcG9uZGluZyBzdGFuZGFyZCBkZXZpYXRpb246DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIGdyb3VwX2J5KGNvdW50cnkpICU+JSANCiAgc3VtbWFyaXNlKGF2Z19jYXNlcyA9IG1lYW4oY2FzZXMpLCBzdF9kZXYgPSBzZChjYXNlcykpDQpgYGANCg0KTm90ZSBob3cgKipzdW1tYXJpc2UqKiBjb2xsYXBzZXMgZWFjaCBncm91cCB0byBhIHNpbmdsZSByb3cgYW5kIGRyb3BzIGNvbHVtbnMgbm90IGludm9sdmVkIGluIHRoZSBncm91cGluZyBwcm9jZXNzLiBJbiBmYWN0LCBpZiB3ZSB1c2UgKipzdW1tYXJpc2UqKiB3aXRob3V0IGdyb3VwaW5nIGZpcnN0LCBpdCBzZWVzIHRoZSBlbnRpcmUgdGliYmxlIGFzIGEgc2luZ2xlIGdyb3VwIGFuZCB0aGUgcmVzdWx0IGlzIGEgc2luZ2xlIHJvdzoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2Nhc2VzID0gc3VtKGNhc2VzKSkNCmBgYA0KDQpUaGUgYWJvdmUgbWVhbnMgdGhhdCBncm91cGVkIHN1bW1hcmllcyBzaG91bGQgYmUgZ2VuZXJhdGVkIHdpdGggZnVuY3Rpb25zIHRoYXQgY29sbGFwc2UgdGhlaXIgYXJndW1lbnQgaW50byBhIHNpbmdsZSB2YWx1ZS4gU29tZSB1c2VmdWwgc3VtbWFyeSBmdW5jdGlvbnMgYXJlICoqc3VtLCBtZWFuLCBtZWRpYW4sIHNkLCBtaW4sIG1heCBhbmQgbioqLg0KDQojIyMgR3JvdXBlZCBtdXRhdGVzDQpJZiB3ZSBwaXBlIHRoZSByZXN1bHQgb2YgYSAqKmdyb3VwX2J5Kiogb3BlcmF0aW9uIHRvIHRoZSBmdW5jdGlvbiAqKm11dGF0ZSoqLCB3ZSBjYW4gY3JlYXRlIG5ldyB2YXJpYWJsZXMgb24gYSBwZXIgZ3JvdXAgYmFzaXMuDQoNClNheSwgZm9yIGV4YW1wbGUsIHRoYXQgd2Ugd2FudCB0byBjb21wdXRlIHRoZSBmcmFjdGlvbiBvZiB0dWJlcmN1bG9zaXMgY2FzZXMgaW4gYSB5ZWFyIGZyb20gdGhlIHRvdGFsIG9jY3VycmVkIGluIGEgY291bnRyeSBvdmVyIGFsbCB0aGUgeWVhcnM6DQoNCmBgYHtyfQ0Kd2hvX3JlZiAlPiUgDQogIGdyb3VwX2J5KGNvdW50cnkpICU+JSANCiAgbXV0YXRlKGZyYWN0aW9uID0gY2FzZXMvc3VtKGNhc2VzKSkNCmBgYA0KDQpOb3RlIHRoYXQgZ3JvdXBlZCBtdXRhdGVzIGRvIG5vdCBjb2xsYXBzZSB0aGUgaW5wdXQgdGliYmxlLiBUaGV5IG1haW50YWluIHRoZSBvcmlnaW5hbCBudW1iZXIgb2Ygc2FtcGxlcyBhbmQgY29sdW1ucy4gQXMgYSByZXN1bHQsIGZ1bmN0aW9ucyB1c2VkIHRvIGNyZWF0ZSBuZXcgdmFyaWFibGVzIGluICoqbXV0YXRlKiogc2hvdWxkIHByb2R1Y2UgdGhlIHNhbWUgbnVtYmVyIG9mIG1lbWJlcnMgd2l0aGluIHRoZSBncm91cC4NCg0KIyMjIEdyb3VwZWQgZmlsdGVycw0KSWYgd2UgcGlwZSB0aGUgcmVzdWx0IG9mIGEgKipncm91cF9ieSoqIG9wZXJhdGlvbiB0byB0aGUgZnVuY3Rpb24gKipmaWx0ZXIqKiwgd2UgY2FuIHJlbW92ZSBvYnNlcnZhdGlvbnMgKGkuZS4gcm93cykgb24gYSBwZXIgZ3JvdXAgYmFzaXMuDQoNCkZvciBleGFtcGxlLCB0byBmaW5kIHRoZSB5ZWFyIHdpdGggdGhlIG1pbmltdW0gbnVtYmVyIG9mIHR1YmVyY3Vsb3NpcyBjYXNlcyBpbiBhIGNvdW50cnksIHdlIGNhbiBkbzoNCg0KYGBge3J9DQp3aG9fcmVmICU+JSANCiAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIA0KICBmaWx0ZXIoY2FzZXMgPT0gbWluKGNhc2VzKSkNCmBgYA0KDQpBcyBzaG93biBiZWxvdywgdXNpbmcgKipzdW1tYXJpc2UqKiBkb2VzbuKAmXQgd29yayBpbiB0aGlzIGNhc2UgYmVjYXVzZSB3ZSBsb3NlIHRoZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgeWVhcjoNCg0KYGBge3J9DQp3aG9fcmVmICU+JQ0KICBncm91cF9ieShjb3VudHJ5KSAlPiUgDQogIHN1bW1hcmlzZShtaW5fY2FzZXMgPSBtaW4oY2FzZXMpKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQ2FzZSBzdHVkeQ0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMjIEV4ZXJjaXNlIDQuMQ0KDQpJbiB0aGlzIGNhc2Ugc3R1ZHksIHlvdeKAmXJlIGdvaW5nIHRvIHdvcmsgd2l0aCBhIGRhdGFzZXQgZ2l2aW5nIHRoZSBtb250aGx5IGRlYXRocyBmcm9tIGx1bmcgZGlzZWFzZXMgaW4gdGhlIFVLIGJldHdlZW4gMTk3NCBhbmQgMTk3OS4NCg0KMS4gSW1wb3J0IGZpbGUgYC4vZGF0YXNldHMvdWtfbHVuZ19kZWF0aHMuY3N2YCBpbnRvIHZhcmlhYmxlICoqdWsqKi4NCjEuIFRpZHkgdXAgdGhlIGRhdGFzZXQuIFRoZSBnb2FsIGlzIHRvIGhhdmUgYSB0aWJibGUgd2l0aCB0aHJlZSBjb2x1bW5zOiB5ZWFyLCBtb250aCBhbmQgZGVhdGhzLg0KMS4gVXNlICoqc2VsZWN0KiogdG8gcmUtYWNjb21tb2RhdGUgdGhlIGNvbHVtbnMsIHN1Y2ggdGhhdCB0aGV5IGFwcGVhciBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOiBtb250aCwgeWVhciBhbmQgZGVhdGhzLg0KMS4gRGF0YSBmcm9tIDE5NzQgaXMgdG9vIG9sZCB0byBiZSByZWxpYWJsZSwgZmlsdGVyIGl0IG91dC4NCjEuIFVzZSB0aGUgKiolPiUqKiB0byBwZXJmb3JtIHBvaW50cyAxLTQgaW4gYSBzaW5nbGUgZ28gYW5kIGFzc2lnbiB0aGUgcmVzdWx0IHRvIHZhcmlhYmxlIHVrLg0KMS4gUmVwb3J0IHRoZSBhdmVyYWdlIG51bWJlciBvZiBkZWF0aHMgcGVyIG1vbnRoLiBTb3J0IHRoZSByZXN1bHQgZGVjcmVhc2luZ2x5IGJ5IGF2ZXJhZ2UgZGVhdGhzLg0KMS4gUmVwb3J0IHRoZSBhdmVyYWdlIGFuZCB0b3RhbCBudW1iZXIgb2YgZGVhdGhzIHBlciB5ZWFyLiBTb3J0IHRoZSByZXN1bHQgYnkgeWVhci4NCjEuIEZvciBlYWNoIG1vbnRoIG9mIGEgZ2l2ZW4geWVhciwgY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGRlYXRocyB0aGF0IHRoZSBtb250aCByZXByZXNlbnRzIGluIGNvbXBhcmlzb24gdG8gdGhlIHRvdGFsIG51bWJlciBvZiBkZWF0aHMgaW4gdGhlIGdpdmVuIHllYXIuIA0KMS4gRm9yIGVhY2ggeWVhciwgcmVwb3J0IHRoZSBtb250aCB3aXRoIHRoZSBtYXhpbXVtIG51bWJlciBvZiBkZWF0aHMuIFNvcnQgdGhlIHJlc3VsdCBieSB5ZWFyLg0KMS4gU3BlY3VsYXRlIGFib3V0IHBvc3NpYmxlIHJlYXNvbnMgd2h5IHRoZSBtb250aHMgcmVzdWx0aW5nIGZyb20gcG9pbnQgOSBoYXZlIHRoZSBtb3N0IGRlYXRocyBmcm9tIGx1bmcgZGlzZWFzZXMuDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgQ2FzZSBTdHVkeSAtIFNPTFVUSU9ODQoNCiMgU3RlcCAxLTU6DQp1ayA8LSByZWFkX2NzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvdWtfbHVuZ19kZWF0aHMuY3N2IikgJT4lICAjIFN0ZXAgMQ0KICBnYXRoZXIoSmFuOkRlYywga2V5ID0gbW9udGgsIHZhbHVlID0gZGVhdGgpICU+JSAgIyBTdGVwIDINCiAgc2VsZWN0KG1vbnRoLCB5ZWFyLCBkZWF0aCkgJT4lICAgICAgICAgICAgICAgICAgICMgU3RlcCAzDQogIGZpbHRlcih5ZWFyID4gMTk3NCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0ZXAgNA0KdWsNCg0KIyBTdGVwIDY6DQp1ayAlPiUgDQogIGdyb3VwX2J5KG1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfZGVhdGhzID0gbWVhbihkZWF0aCkpICU+JSANCiAgYXJyYW5nZShkZXNjKGF2Z19kZWF0aHMpKQ0KDQojIFN0ZXAgNzoNCnVrICU+JSANCiAgZ3JvdXBfYnkoeWVhcikgJT4lIA0KICBzdW1tYXJpc2UoYXZnX2RlYXRocz1tZWFuKGRlYXRoKSwgdG90YWxfZGVhdGhzPXN1bShkZWF0aCkpICU+JSANCiAgYXJyYW5nZSh5ZWFyKQ0KDQojIFN0ZXAgODoNCnVrICU+JSANCiAgZ3JvdXBfYnkoeWVhcikgJT4lIA0KICBtdXRhdGUocGVyY19wZXJfbW9udGggPSBkZWF0aCAvIHN1bShkZWF0aCkgKiAxMDApDQoNCiMgU3RlcCA5Og0KdWsgJT4lIA0KICBncm91cF9ieSh5ZWFyKSAlPiUgDQogIGZpbHRlcihkZWF0aCA9PSBtYXgoZGVhdGgpKSAlPiUgDQogIGFycmFuZ2UoeWVhcikNCg0KIyBTdGVwIDEwOg0KIyBsdW5nIGRpc2Vhc2VzIG9jY3VyIG1vcmUgb2Z0ZW4gaW4gd2ludGVyIG1vbnRocw0KDQpgYGANCg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMgdmlzdWFsaXphdGlvbiBhbmQgbW9kZWxpbmcNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBDYXNlIHN0dWR5DQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpUaGUgYmVzdCB3YXkgdG8gc2hvdyB5b3UgdGhlIHBvd2VyIG9mIHZpc3VhbGl6YXRpb24gZm9yIGtub3dsZWRnZSBnZW5lcmF0aW9uIGlzIGJ5IG1lYW5zIG9mIGFuIGV4YW1wbGUuIEluIHRoaXMgcGFydCwgeW91IHdpbGwgdXNlIGEgZGF0YXNldCBjb21wcmlzZWQgb2Ygc3ViamVjdHMgd2hvIHdlcmUgcGFydCBvZiBhIHN0dWR5IGFpbWVkIGF0IHVuZGVyc3RhbmRpbmcgdGhlIHByZXZhbGVuY2Ugb2YgZGlhYmV0ZXMgaW4gY2VudHJhbCBWaXJnaW5pYSwgVVNBLiBUaGUgdmFyaWFibGVzIGRlc2NyaWJpbmcgdGhlIHN1YmplY3RzIGFyZToNCg0KKiAqKmlkKio6IEEgbnVtZXJpYyBJRCBmb3IgZWFjaCBzdWJqZWN0Lg0KKiAqKnJhdGlvX2NoKio6IENob2xlc3Rlcm9sL0hpZ2ggRGVuc2l0eSBMaXBvcHJvdGVpbiByYXRpby4NCiogKipzdGFiLmdsdSoqOiBTdGFiaWxpc2VkIGdsdWNvc2UuDQoqICoqZ2x5aGIqKjogR2x5Y29zb2xhdGVkIGhhZW1vZ2xvYmluLg0KKiAqKmxvY2F0aW9uKio6IENvdW50eSBvZiBvcmlnaW4uDQoqICoqYWdlKio6IEFnZSBvZiB0aGUgc3ViamVjdC4NCiogKipnZW5kZXIqKjogR2VuZGVyIG9mIHRoZSBzdWJqZWN0Lg0KKiAqKnJhdGlvX3doKio6IFdhaXN0L0hpcCByYXRpby4NCiogKipib2R5X21lYXN1cmUqKjogV2hpY2ggYm9keSBtZWFzdXJlbWVudCB3YXMgcmVjb3JkZWQuDQoqICoqbWVhc3VyZSoqOiBIZWlnaHQgb3Igd2VpZ2h0IG9mIHRoZSBzdWJqZWN0Lg0KDQpBZnRlciB0aWR5aW5nIGFuZCB0cmFuc2Zvcm1pbmcgdGhlIOKAnERpYWJldGVzIERhdGFzZXTigJ0sIHlvdSB3aWxsIHZpc3VhbGl6ZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIGFuZCBmZWF0dXJlIGRpc3RyaWJ1dGlvbnMgd2l0aCBmdW5jdGlvbnMgZnJvbSBwYWNrYWdlIGdndmlzIGluIG9yZGVyIHRvIHByb3Bvc2UgYSBzaW1wbGUgbW9kZWwgZm9yIHN1c2NlcHRpYmlsaXR5IHRvIGRpYWJldGVzLg0KDQohW10oaW1hZ2VzL2RfdmlzLnBuZykNCg0KWW91IG11c3QgbG9hZCBwYWNrYWdlcyB0aWR5dmVyc2UgYW5kIGdndmlzLg0KDQojIyMgRXhlcmNpc2UgNS4xDQoxLiBJbXBvcnQgZmlsZSBgLi9kYXRhc2V0cy9kaWFiZXRlcy50c3ZgIGludG8gdmFyaWFibGUgZGlhYi4NCjEuIFRpZHkgdXAgdGhlIGltcG9ydGVkIHRpYmJsZSBieSBwaXBpbmcgYSBzZXJpZXMgb2YgdGlkeXIgb3BlcmF0aW9ucyBhbmQgYXNzaWduIHRoZSByZXN1bHQgdG8gdmFyaWFibGUgKipkaWFiKiouDQoxLiBHZXQgcmlkIG9mIGNvbHVtbiAqKmlkKiouDQoxLiBVc2UgKiptdXRhdGUqKiBhbmQgKiphcy5udW1lcmljKiogdG8gdHJhbnNmb3JtIGNoYXJhY3RlciBjb2x1bW5zIHRoYXQgc2hvdWxkIGJlIG51bWVyaWMuDQoxLiBIZWlnaHQsIHdhaXN0IGFuZCBoaXAgbWVhc3VyZW1lbnRzIGFyZSBnaXZlbiBpbiBpbmNoZXMsIGNvbnZlcnQgdGhlbSB0byBjZW50aW1ldHJlcyAoMSBpbmNoID0gMi41NCBjbSkuDQoxLiBXZWlnaHRzIGFyZSBnaXZlbiBpbiBwb3VuZHMsIGNvbnZlcnQgdGhlbSB0byBraWxvZ3JhbXMgKDEgcG91bmQgPSAwLjQ1NCBrZykuDQoxLiBHbHljb3NvbGF0ZWQgaGFlbW9nbG9iaW4gbGV2ZWxzIGFib3ZlIDcuMCBhcmUgdXN1YWxseSB0YWtlbiBhcyBhIHBvc2l0aXZlIGRpYWdub3NpcyBvZiBkaWFiZXRlcy4gQ3JlYXRlIGEgbmV3IGNvbHVtbiBjYWxsZWQgKipkaWFnbm9zaXMqKiB0aGF0IHRha2VzIHRoZSB2YWx1ZSDigJxkaWFiZXRpY+KAnSB3aGVuIGEgc3ViamVjdOKAmXMgbGV2ZWwgaXMgZ3JlYXRlciB0aGFuIDcuMCBhbmQg4oCcaGVhbHRoeeKAnSBvdGhlcndpc2UgKEhJTlQ6IHVzZSBmdW5jdGlvbiAqKmlmZWxzZSoqKS4NCg0KUmVzdWx0IHNob3VsZCBsb29rIGxpa2UgdGhlIGZvbGxvd2luZyB0YWJsZTogDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENoYXB0ZXIgNSAtIENhc2Ugc3R1ZHkNCiMgRXhlcmNpc2UgNS4xIC0gU09MVVRJT04NCmRpYWIgPC0gcmVhZF90c3YoImh0dHA6Ly9jYmRtLTAxLnpkdi51bmktbWFpbnouZGUvfnN0YWxicmVjL1Jjb3Vyc2VEYXRhL2RpYWJldGVzLnRzdiIpICU+JSANCiAgc3ByZWFkKGtleSA9IGJvZHlfbWVhc3VyZSwgdmFsdWUgPSBtZWFzdXJlKSAlPiUgDQogIHNlcGFyYXRlKHJhdGlvX2NoLCBpbnRvID0gYygiY2hvbCIsICJoZGwiKSwgc2VwID0gIi8iKSAlPiUgDQogIHNlcGFyYXRlKHJhdGlvX3doLCBpbnRvID0gYygid2Fpc3QiLCAiaGlwIiksIHNlcCA9ICIvIikgJT4lIA0KICBtdXRhdGUoIGNob2wgID0gYXMubnVtZXJpYyhjaG9sKSwNCiAgICAgICAgICBoZGwgICA9IGFzLm51bWVyaWMoaGRsKSwNCiAgICAgICAgICB3YWlzdCA9IGFzLm51bWVyaWMod2Fpc3QpLA0KICAgICAgICAgIGhpcCAgID0gYXMubnVtZXJpYyhoaXApLA0KICAgICAgICAgIHdhaXN0ICA9IHdhaXN0ICAqIDIuNTQsDQogICAgICAgICAgaGlwICAgID0gaGlwICAgICogMi41NCwNCiAgICAgICAgICBoZWlnaHQgPSBoZWlnaHQgKiAyLjU0LA0KICAgICAgICAgIHdlaWdodCA9IHdlaWdodCAqIDAuNDU0LA0KICAgICAgICAgIGRpYWdub3NpcyA9IGlmZWxzZShnbHloYj43LCAiZGlhYmV0aWMiLCAiaGVhbHRoeSIpIA0KICAgICAgICApDQpkaWFiDQpgYGANCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBQbG90dGluZyB3aXRoIGdncGxvdA0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KSW4gb3JkZXIgdG8gcGxvdCB3aXRoIHBhY2thZ2UgKipnZ3Bsb3QqKiwgd2UgbmVlZCB0aGUgKiolPiUqKiBvcGVyYXRvciB0byBjaGFubmVsIGluZm9ybWF0aW9uIGludG8gZnVuY3Rpb24gKipnZ3Bsb3QqKi4gVGhpcyBjcmVhdGVzIGEgYmxhbmsgY2FudmFzIG9uIHRvcCBvZiB3aGljaCB3ZSBhZGQgbGF5ZXJzIG9mIGluZm9ybWF0aW9uIGFuZCBwb2xpc2ggb3VyIHBsb3QuIE5vdGUgdGhhdCwgVGhlICoqZ2dwbG90KiogZnVuY3Rpb25zIGFyZSBjaGFpbmVkIGJ5IHRoZSAqKisqKiBvcGVyYXRvciENCg0KDQpgYGB7cn0NCmRpYWIgJT4lDQogIGdncGxvdChhZXMoeCA9IGFnZSwgeSA9IGNob2wpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHhsYWIoIlN1YmplY3QncyBhZ2UiKSArDQogIHlsYWIoIkNob2xlc3Rlcm9sIExldmVsIikNCmBgYA0KDQoNClRoZSBhYm92ZSBleGFtcGxlIGZlZWRzIGZ1bmN0aW9uICoqZ2dwbG90Kiogd2l0aCB0aGUgKipkaWFiKiogdGFibGUsIGluZGljYXRlcyB0aGF0IHdlIHdhbnQgdG8gY29tcGFyZSBhZ2Ugd2l0aCBjaG9sZXN0ZXJvbCBsZXZlbHMgKCoqYWVzKiogc3RhbmRzIGZvciBhZXN0aGV0aWMpLCB1c2VzICoqZ2VvbV9wb2ludHMqKiB0byBnZW5lcmF0ZSBhIHNjYXR0ZXIgcGxvdCB3aXRoIHRoZSBkYXRhIGFuZCBwb2xpc2hlcyB0aGUgcGxvdCBieSBwcm92aWRpbmcgY3VzdG9tIGF4aXMgbGFiZWxzICgqKnhsYWIqKiBhbmQgKip5bGFiKiopLg0KDQpZb3Ugd2lsbCBzZWUgdGhhdCBmb2xsb3dpbmcgdGhpcyBzeW50YXggYWxsb3dzIHlvdSB0byBnZW5lcmF0ZSBtYW55IGRpZmZlcmVudCB0eXBlcyBvZiBwbG90cy4gSXQgaXMganVzdCBhIG1hdHRlciBvZiBjaGFuZ2luZyB0aGUgbGF5ZXIgZnVuY3Rpb24gb3IgYWRkaW5nIG1vcmUgbGF5ZXJzIHRvIHlvdXIgcGxvdDoNCg0KYGBge3J9DQpkaWFiICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBhZ2UsIHkgPSBjaG9sICkpICsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMywgY29sb3I9J3JlZCcpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoIlN1YmplY3QncyBhZ2UiKSArDQogIHlsYWIoIkNob2xlc3Rlcm9sIExldmVsIikNCmBgYA0KDQoNClNlZSBob3cgdGhlIGFib3ZlIGV4YW1wbGUgYWRkcyBhICoqZ2VvbV9zbW9vdGgqKiBsYXllciBvbiB0b3Agb2YgKipnZW9tX3BvaW50KiouIEFsc28sIG5vdGUgdGhhdCBpdCBpcyBwb3NzaWJsZSB0byBmdXJ0aGVyIHBvbGlzaCB0aGUgcGxvdCB2aWEgcGFyYW1ldGVycyB0aGF0IGFyZSBzcGVjaWZpYyB0byBlYWNoIHR5cGUgb2YgbGF5ZXIuIEZvciBleGFtcGxlLCB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvciBhbmQgc2hhcGUgb2YgcG9pbnRzIHRocm91Z2ggcGFyYW1ldGVycyBjb2xvciBhbmQgc2hhcGUgb2YgZnVuY3Rpb24gKipnZW9tX3BvaW50KiouDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQmFyIHBsb3RzDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpCYXIgcGxvdHMgYXJlIHVzZWZ1bCB3aGVuIHdlIHdhbnQgdG8gZ3JhcGhpY2FsbHkgc2hvdyBjb3VudHMgb2YgYSBkaXNjcmV0ZSBvciBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gRm9yIGV4YW1wbGUsIHRvIHNlZSB0aGUgbnVtYmVyIG9mIG1hbGUgYW5kIGZlbWFsZSBzdWJqZWN0cyBpbiAqKmRpYWIqKiB3ZSBkbzoNCg0KYGBge3J9DQpkaWFiICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Z2VuZGVyKSkgKw0KICBnZW9tX2Jhcih3aWR0aCA9IDAuNSkgKw0KICB4bGFiKCJHZW5kZXIiKSArDQogIHlsYWIoIk51bWJlciBvZiBzdWJqZWN0cyIpDQpgYGANCg0KDQpOb3RlIGhvdyB0aGUgKip3aWR0aCoqIHBhcmFtZXRlciBvZiAqKmdlb21fYmFyKiogaGFzIGJlZW4gdXNlZCB0byBwbG90IG5hcnJvdyBiYXJzLg0KDQpXZSBjYW4gYWxzbyBzZWUgdGhlIG51bWJlciBvZiBkaWFiZXRpYyBhbmQgaGVhbHRoeSBzdWJqZWN0czoNCg0KYGBge3J9DQpkaWFiICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGlhZ25vc2lzKSkgKw0KICBnZW9tX2Jhcih3aWR0aCA9IDAuNSkgKw0KICB4bGFiKCJEaWFnbm9zaXMiKSArDQogIHlsYWIoIk51bWJlciBvZiBzdWJqZWN0cyIpDQpgYGANCg0KRmluYWxseSwgd2UgY2FuIGFsc28gdXNlIHRoZSB5LWF4aXMgb2YgYmFyIHBsb3RzIHRvIHNob3cgaW5mb3JtYXRpb24gb3RoZXIgdGhhbiBjb3VudHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gY29udHJhc3QgdGhlIGF2ZXJhZ2UgY2hvbGVzdGVyb2wgbGV2ZWxzIG9mIG1lbiBhbmQgd29tZW46DQoNCmBgYHtyfQ0KZGlhYiAlPiUgDQogIGdyb3VwX2J5KGdlbmRlcikgJT4lIA0KICBzdW1tYXJpc2UoYXZnX2Nob2wgPSBtZWFuKGNob2wpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGdlbmRlciwgeSA9IGF2Z19jaG9sKSkgKyANCiAgZ2VvbV9jb2wod2lkdGggPSAwLjUpICsNCiAgeGxhYigiR2VuZGVyIikgKw0KICB5bGFiKCJBdmcuIGNob2xlc3Rlcm9sIikNCmBgYA0KDQpOb3RlIGhvdyBkYXRhIHRyYW5zZm9ybWF0aW9ucyBjYW4gYmUgYXBwbGllZCBvbiB0aGUgZmx5IGFuZCBwaXBlZCB0byAqKmdncGxvdCoqLg0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIFNjYXR0ZXIgcGxvdHMgYW5kIHJlZ3Jlc3Npb25zDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpTY2F0dGVyIHBsb3RzIGFsbG93IHVzIHRvIHN0dWR5IHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHJlZ3Jlc3Npb25zIHRyeSB0byBmaW5kIHRoZSBjdXJ2ZSB0aGF0IGJlc3QgZGVzY3JpYmVzIGhvdyB4IGlzIHJlbGF0ZWQgdG8geS4NCg0KTGV04oCZcyBzZWUgaG93IHdlaWdodCBhbmQgd2Fpc3QgbWVhc3VyZW1lbnRzIGFyZSByZWxhdGVkOg0KDQpgYGB7cn0NCmRpYWIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB3ZWlnaHQsIHkgPSB3YWlzdCkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aCgpICsgDQogIHhsYWIoIlN1YmplY3QncyB3ZWlnaHQiKSArIA0KICB5bGFiKCJXYWlzdCBtZWFzdXJlbWVudCIpDQpgYGANCg0KDQpJbiB0aGUgYWJvdmUgZXhhbXBsZSwgKipnZW9tX3BvaW50KiogaXMgaW4gY2hhcmdlIG9mIHRoZSBzY2F0dGVyIHBsb3QgYW5kICoqZ2VvbV9zbW9vdGgqKiBmaW5kcyBhIGN1cnZlIHRoYXQgZm9sbG93cyB0aGUgcG9pbnRzIGFuZCBzdW1tYXJpemVzIHRoZSBvYnNlcnZlZCB0cmVuZC4gQnkgZGVmYXVsdCwgYSBzdGFuZGFyZCBlcnJvciBncmV5IGJhbmQgaXMgc2hvd24gdG8gaW5kaWNhdGUgaG93IG11Y2ggd2UgY2FuIHRydXN0IHRoZSBibHVlIHJlZ3Jlc3Npb24gY3VydmUuDQoNCklmIHdlIHdhbnQgdG8gaGF2ZSBtb3JlIGNvbnRyb2wgb3ZlciB0aGUgdHlwZSBvZiBjdXJ2ZSB0aGF0IGlzIGZpdHRlZCB0byB0aGUgcG9pbnRzLCB3ZSBjYW4gdXNlIHRoZSAqKm1ldGhvZCoqIHBhcmFtZXRlci4gRm9yIGV4YW1wbGUsIHRvIGZpdCBhIHN0cmFpZ2h0IGxpbmUgdG8gdGhlIGFib3ZlIHBsb3QsIHdlIGhhdmUgdG8gdXNlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgKCoq4oCcbG3igJ0qKik6DQoNCmBgYHtyfQ0KZGlhYiAlPiUgDQogIGdncGxvdChhZXMoeCA9IHdlaWdodCwgeSA9IHdhaXN0KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsgDQogIHhsYWIoIlN1YmplY3QncyB3ZWlnaHQiKSArIA0KICB5bGFiKCJXYWlzdCBtZWFzdXJlbWVudCIpDQpgYGANCg0KDQpFdmVuIHRob3VnaCBhIHNjYXR0ZXIgcGxvdCBpcyBpbnRyaW5zaWNhbGx5IGluIDIgZGltZW5zaW9ucywgd2UgY2FuIGFkZCBtb3JlIGxheWVycyBvZiBpbmZvcm1hdGlvbiB0byBsZWFybiBtb3JlIGFib3V0IG91ciBkYXRhLiBGb3IgZXhhbXBsZSwgd2UgY2FuIGNvbG9yIG91ciBwb2ludHMgYWNjb3JkaW5nIHRvIHRoZSBzdWJqZWN04oCZcyBkaWFnbm9zaXMgYW5kIGFkanVzdCB0aGVpciBzaXplIGJ5IGdseWhiOg0KDQpgYGB7cn0NCmRpYWIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB3ZWlnaHQsIHkgPSB3YWlzdCwgY29sb3IgPSBkaWFnbm9zaXMsIHNpemUgPSBnbHloYikpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICB4bGFiKCJTdWJqZWN0J3Mgd2VpZ2h0IikgKyANCiAgeWxhYigiV2Fpc3QgbWVhc3VyZW1lbnQiKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgTGluZSBncmFwaHMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCkxpbmUgcGxvdHMgYXJlIHNpbWlsYXIgdG8gc2NhdHRlciBwbG90cywgYnV0IHRoZSB2YXJpYWJsZSBpbiB0aGUgeC1heGlzIGlzIHNvcnRlZCBhbmQgcG9pbnRzIGFyZSBjb25uZWN0ZWQgaW4gb3JkZXIgd2l0aCBhIGxpbmU6DQoNCmBgYHtyfQ0KZGlhYiAlPiUgDQogIGdncGxvdChhZXMoeCA9IHdhaXN0LCB5ID0gaGlwKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIHhsYWIoIlN1YmplY3QncyB3ZWlnaHQiKSArIA0KICB5bGFiKCJXYWlzdCBtZWFzdXJlbWVudCIpDQpgYGANCg0KSXQgaXMgdmVyeSBjb21tb24gdG8gYWRkIGEgcG9pbnQgbGF5ZXIgb24gdG9wIG9mIGxpbmUgcGxvdHM6DQoNCmBgYHtyfQ0KZGlhYiAlPiUgDQogIGdncGxvdChhZXMoeCA9IHdhaXN0LCB5ID0gaGlwKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoKSArDQogIHhsYWIoIlN1YmplY3QncyB3ZWlnaHQiKSArIA0KICB5bGFiKCJXYWlzdCBtZWFzdXJlbWVudCIpDQpgYGANCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBCb3ggcGxvdHMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCkJveCBwbG90cyBhcmUgdmVyeSBwb3dlcmZ1bCBzdGF0aXN0aWNhbCB0b29scyB0aGF0IHN1bW1hcml6ZSB0aGUgbW9zdCBpbXBvcnRhbnQgYXNwZWN0cyBvZiBhIHZhcmlhYmxl4oCZcyBkaXN0cmlidXRpb246DQoNCiFbXShpbWFnZXMvYm94cGxvdC5wbmcpDQoNCg0KVGhlIHgtYXhpcyBpbiBhIGJveCBwbG90IGlzIHVzdWFsbHkgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQgdGhlIHktYXhpcyBpcyBub3JtYWxseSBhIG51bWVyaWMgdmFyaWFibGUgd2hv4oCZcyBkaXN0cmlidXRpb24gd2Ugd2FudCB0byBleHBsb3JlLiBGb3IgZXhhbXBsZSwgbGV04oCZcyB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBIaWdoIERlbnNpdHkgTGlwb3Byb3RlaW4gdmFsdWVzIGZvciBtYWxlcyBhbmQgZmVtYWxlcyBpbiAqKmRpYWIqKjoNCg0KYGBge3J9DQpkaWFiICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZ2VuZGVyLCB5ID0gaGRsKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHhsYWIoImdlbmRlciIpICsgDQogIHlsYWIoIkhETCBsZXZlbCAiKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgSGlzdG9ncmFtcyBhbmQgZGVuc2l0aWVzDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQpIaXN0b2dyYW1zIHdvcmsgb24gYSBzaW5nbGUgdmFyaWFibGUgYW5kIHNob3cgaG93IGl0IGlzIGRpc3RyaWJ1dGVkIGJ5IGRpdmlkaW5nIGl0cyByYW5nZSBpbnRvIGEgY2VydGFpbiBudW1iZXIgb2YgYmlucy4gVGh1cywgdGhlIHgtYXhpcyByZXBvcnRzIHRoZSB2YWx1ZXMgdGhhdCB0aGUgdmFyaWFibGUgY2FuIHRha2UgKGJpbm5lZCkgYW5kIHRoZSB5LWF4aXMgcmVwb3J0cyB0aGUgbnVtYmVyIG9yIGZyYWN0aW9uIG9mIG9ic2VydmF0aW9ucyB0aGF0IHRha2Ugc3VjaCB2YWx1ZXMuDQoNCkxldOKAmXMgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIFN0YWJpbGl6ZWQgZ2x1Y29zZSB2YWx1ZXMsIHNwZWNpZnlpbmcgdGhlIHdpZHRoIG9mIHRoZSBoaXN0b2dyYW0gYmlucyB3aXRoIHBhcmFtZXRlciAqKmJpbndpZHRoKio6DQoNCmBgYHtyfQ0KZGlhYiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gc3RhYi5nbHUpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTUpICsNCiAgeGxhYigiU3RhYmlsaXNlZCBnbHVjb3NlIikgKyANCiAgeWxhYigiQ291bnRzIikNCmBgYA0KDQpXZSBjYW4gYWxzbyBwdXQgdHdvIGhpc3RvZ3JhbXMgaW4gdGhlIHNhbWUgcGxvdDoNCg0KYGBge3J9DQpkaWFiICU+JSANCiAgZ3JvdXBfYnkoZGlhZ25vc2lzKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHN0YWIuZ2x1LCBmaWxsID0gZGlhZ25vc2lzKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDE1KSArDQogIHhsYWIoIlN0YWJpbGlzZWQgZ2x1Y29zZSIpICsgDQogIHlsYWIoIkNvdW50cyIpDQpgYGANCg0KRGVuc2l0eSBwbG90cyBhcmUgbGlrZSByZWdyZXNzaW9ucyBidXQgZm9yIGhpc3RvZ3JhbXMuIERlbnNpdHkgcGxvdHMgdHJ5IHRvIGZpbmQgdGhlIGN1cnZlIHRoYXQgYmVzdCBzdW1tYXJpemVzIHRoZSBkaXN0cmlidXRpb24gb2YgYSB2YXJpYWJsZSBhbmQgcmVwb3J0IHByb2JhYmlsaXR5IGRlbnNpdGllcyBpbnN0ZWFkIG9mIGNvdW50cy4gVG8gY29udHJvbCB0aGUgc21vb3RoaW5nIG9mIHRoZSBkZW5zaXR5IHBsb3QsIHdlIHVzZSBwYXJhbWV0ZXIgKiphZGp1c3QqKjoNCg0KYGBge3J9DQpkaWFiICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gc3RhYi5nbHUpKSArIA0KICBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC43LCBmaWxsID0gJ2dyZXknKSArIA0KICB4bGFiKCJTdGFiaWxpc2VkIGdsdWNvc2UiKSArIA0KICB5bGFiKCJEZW5zaXR5IikNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEZhY2V0cw0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KDQpHZ3Bsb3QncyBmYWNldHMgYXJlIGEgcG93ZXJmdWwgZmVhdHVyZSB0aGF0IHN1YmRpdmlkZXMgYSBwbG90IGluIHN1YnBsb3RzIA0KYWNjb3JkaW5nIHRvIGdpdmVuIHZhcmlhYmxlcyAoY2F0ZWdvcmllcykuIFRoZXJlIGFyZSAyIG1haW4gZnVuY3Rpb25zIHRvIGNyZWF0ZSANCnRoZSBzdWJwbG90czogKipmYWNldF93cmFwKiogYW5kICoqZmFjZXRfZ3JpZCoqLg0KDQpGb3IgZXhhbXBsZSwgdGhlIGRlbnNpdHkgcGxvdCBjYW4gYmUgc3ViZGl2aWRlZCBieSBnZW5kZXIgKGl0IGFsc28gd29ya3MgZm9yIA0Kb3RoZXIgdHlwZXMgb2YgcGxvdHMpOg0KYGBge3J9DQpkaWFiICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gc3RhYi5nbHUpKSArIA0KICBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC43LCBmaWxsPSdncmV5JykgKyANCiAgZmFjZXRfd3JhcCh2YXJzKGdlbmRlcikpICsNCiAgeGxhYigiU3RhYmlsaXNlZCBnbHVjb3NlIikgKyANCiAgeWxhYigiRGVuc2l0eSIpDQpgYGANCg0KWW91IGNhbiBwcm92aWRlIGEgbGlzdCBvZiBjb2x1bW5zIHRvIHRoZSBmYWNldHMgZnVuY3Rpb24gc3VjaCBhcyBnZW5kZXIgYW5kIGxvY2F0aW9uOg0KYGBge3J9DQpkaWFiICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gc3RhYi5nbHUpKSArIA0KICBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC43LCBmaWxsPSdncmV5JykgKyANCiAgZmFjZXRfd3JhcCh2YXJzKGdlbmRlciwgbG9jYXRpb24pKSArDQogIHhsYWIoIlN0YWJpbGlzZWQgZ2x1Y29zZSIpICsgDQogIHlsYWIoIkRlbnNpdHkiKQ0KYGBgDQoNCllvdSBjYW4gY29tYmluZSBmYWNldHMgd2l0aCB0aGUgKipmaWxsKiogcGFyYW1ldGVyIG9mICoqYWVzKiogKG5vdCB1c2luZyBhbnltb3JlIA0KKipmaWxsKiogaW4gKipnZW9tX2RlbnNpdHkqKikgdG8gY3JlYXRlIHN1YnBsb3RzIGJ5IGdlbmRlciBhbmQgbG9jYXRpb24sIGFuZCB0byANCmNvbXBhcmUgdGhlIGRpYWdub3NpcyBpbiBlYWNoIHN1YnBsb3Q6DQpgYGB7cn0NCmRpYWIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBzdGFiLmdsdSwgZmlsbD1kaWFnbm9zaXMpKSArIA0KICBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC43KSArIA0KICBmYWNldF93cmFwKHZhcnMoZ2VuZGVyLCBsb2NhdGlvbikpICsNCiAgeGxhYigiU3RhYmlsaXNlZCBnbHVjb3NlIikgKyANCiAgeWxhYigiRGVuc2l0eSIpDQpgYGANCg0KRmluYWxseSwgZmFjZXRzIGNhbiBiZSBkaXNwbGF5ZWQgYXMgYSBncmlkIHVzaW5nIGZhY2V0X2dyaWQuIFVzaW5nIHRoZSBleGFtcGxlDQpiZWxvdywgb25lIGNhbiBjb21wYXJlIHRoZSBkaXN0cmlidXRpb25zIG9mIG1hbGUgYW5kIGZlbWFsZSBIREwgbGV2ZWxzIGluIA0KcmVsYXRpb24gdG8gdGhlIGRpYWdub3NpcyBhbmQgbG9jYXRpb246DQpgYGB7cn0NCmRpYWIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBnZW5kZXIsIHkgPSBoZGwsIGZpbGw9Z2VuZGVyKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGZhY2V0X2dyaWQocm93cz12YXJzKGxvY2F0aW9uKSwgY29scz12YXJzKGRpYWdub3NpcykpICsNCiAgeGxhYigiZ2VuZGVyIikgKyANCiAgeWxhYigiSERMIGxldmVsICIpDQpgYGANCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBBIHNpbXBsZSBtb2RlbA0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KSW4gRXhlcmNpc2UgNS4xLCB5b3UgdXNlZCBHbHljb3NvbGF0ZWQgaGFlbW9nbG9iaW4gbGV2ZWxzIGFib3ZlIDcuMCB0byBkaWFnbm9zZSBkaWFiZXRlcy4gVGhlIGhpc3RvZ3JhbXMgZnJvbSB0aGUgcHJldmlvdXMgc2VjdGlvbiBzaG93ZWQgdGhhdCBkaWFiZXRpY3MgaGF2ZSBiaWdnZXIgbGV2ZWxzIG9mIFN0YWJpbGl6ZWQgZ2x1Y29zZSBjb21wYXJlZCB0byBoZWFsdGh5IHN1YmplY3RzLg0KDQojIyMgRXhlcmNpc2UgNS4yDQoNCjEuIEdlbmVyYXRlIGEgc2NhdHRlciBwbG90IGNvbXBhcmluZyAqKmdseWhiKiogd2l0aCAqKnN0YWIuZ2x1KiogdXNpbmcgdGhlIHN1YmplY3QgZGlhZ25vc2lzIGFzIHRoZSBwb2ludCBjb2xvci4NCjEuIFVzZSBmYWNldHMgdG8gY3JlYXRlIHN1YnBsb3RzIHRvIG9ic2VydmUgdGhlIGRhdGEgYnkgbG9jYXRpb24gYW5kIGdlbmRlci4gDQoxLiBEbyB5b3Ugb2JzZXJ2ZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzdWJwb3B1bGF0aW9ucyBvZiBwYXRpZW50cz8gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEV4ZXJjaXNlIDUuMiAtIFNPTFVUSU9ODQpkaWFiICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBnbHloYiwgeSA9IHN0YWIuZ2x1LCBjb2xvciA9IGRpYWdub3NpcykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeGxhYigiZ2x5Y29zb2xhdGVkIGhhZW1vZ2xvYmluIikgKw0KICB5bGFiKCJzdGFiaWxpc2VkIGdsdWNvc2UiKQ0KDQpkaWFiICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBnbHloYiwgeSA9IHdlaWdodCwgY29sb3IgPSBkaWFnbm9zaXMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAodmFycyhnZW5kZXIsIGxvY2F0aW9uKSkgKw0KICB4bGFiKCJnbHljb3NvbGF0ZWQgaGFlbW9nbG9iaW4iKSArDQogIHlsYWIoInN0YWJpbGlzZWQgZ2x1Y29zZSIpDQpgYGANCg==