Buckle up 👨‍🚀

In this learning path, we’ll learn how to create Machine learning models using R 😊. Machine learning is the foundation for predictive modeling and artificial intelligence.We’ll learn the core principles of machine learning and how to use common tools and frameworks to train, evaluate, and use machine learning models.

Modules that will be covered in this learning path include:

  • Explore and analyze data with R

  • Train and evaluate regression models (under development)

  • Train and evaluate classification models (under development)

  • Train and evaluate clustering models (under development)

  • Train and evaluate deep learning models (under development)

Prerequisites

This learning path assumes knowledge of basic mathematical concepts. Some experience with R and the tidyverse is also beneficial though we’ll try as much as possible to skim through the core concepts. To get started with R and the tidyverse , the best place to start would be R for Data Science an O’Reilly book written by Hadley Wickham and Garrett Grolemund. It’s designed to take you from knowing nothing about R or the tidyverse to having all the basic tools of data science at your fingertips.

This tutorial will help you set up your computer to use R. It is for you if you need to:

  • Install R on your computer

  • Install the RStudio IDE

  • Install the tidyverse R package

You can skip this tutorial if you’ve already done these things.

We’ll require some packages to knock-off this module. You can have them installed as:

install.packages(c('tidyverse', 'patchwork', 'paletteer', 'statip', 'summarytools'))

The Python edition of the learning path can be found at this learning path.

Why R?

We think R is a great place to start your data science journey because it is an environment designed from the ground up to support data science. R is not just a programming language, but it is also an interactive environment for doing data science. To support interaction, R is a much more flexible language than many of its peers. This flexibility comes with its downsides, but the big upside is how easy it is to evolve tailored grammars for specific parts of the data science process. These mini languages help you think about problems as a data scientist, while supporting fluent interaction between your brain and the computer. - R for Data Scienceby Hadley Wickham and Garrett Grolemund

Now, let’s get started!

Artwork by @allison_horst

Artwork by @allison_horst

Explore and analyze data with R

Introduction

Unsurprisingly, the role of a data scientist primarily involves exploring and analyzing data. The results of this analysis might form the basis of a report or a machine learning model; but it all begins with data.

Machine learning is a subset of data science that deals with predictive modeling. In other words, using data to create models that can predict unknown values. It works by identifying relationships between data values that describe characteristics of something (its features) and the value we want to predict (the label), and encapsulating these relationships in a model through a training process.

Usually, a data analysis project is designed to establish insights around a particular scenario or to test a hypothesis. For example, suppose a university professor collects data from data science students, including the number of lectures attended, the hours spent studying, and the final grade achieved on the end of term exam. The professor could then take a sample of the data and analyze it to determine if there is a relationship between the amount of study a student undertakes and the final grade they achieve. They might use the data to test a hypothesis that only students who study for a minimum number of hours can expect to achieve a passing grade; or even prepare the data to train a machine learning model that predicts a student’s grade based on their study habits.

Exercise - Explore data

Data exploration and analysis is typically an iterative process, in which the data scientist takes a sample of data, and performs the following kinds of task to analyze it and test hypotheses:

  • Clean data to handle errors, missing values, and other issues.

  • Apply statistical techniques to better understand the data, and how the sample might be expected to represent the real-world population of data, allowing for random variation.

  • Visualize data to determine relationships between variables, and in the case of a machine learning project, identify features that are potentially predictive of the label.

  • Derive new features from existing ones that might better encapsulate relationships within the data.

  • Revise the hypothesis and repeat the process.

Data scientists can use a variety of tools and techniques to explore, visualize, and manipulate data. One of the most common ways in which data scientists work with data is to use the R language and some specific packages for data processing.

The best way to learn about exploring and preparing data is to try it for yourself, so that’s what you’ll do in this exercise.

1. Getting to know R … a little

I’ve heard R described as quirky. But quirky is good, it means it’s a little bit out of the norm. And I think that’s what makes R, R. So to easen things up, we’ll briefly discuss some of the most important family of data types in base R: vectors.

In R, assignment statements have the following form:

object_name <- value

= will work in place of <-, but it will cause confusion later. Use the keyboard shortcut: Alt + - (the minus sign) to poof up the assignment operator, <- , in a flash. 💫

Atomic vectors

The basic data structure in R is the vector (R’s 1D data structure). Vectors come in two flavours: atomic vectors and lists. The two differ in the types of their elements: all elements of an atomic vector must be the same type, whereas the elements of a list can have different types. There are four primary types of atomic vectors: logical, integer, double, and character (which contains strings).

Vectors are created using use c(), short for combine. You can determine the type of a vector with typeof() and how many elements it contains with length().

## [1]  TRUE FALSE
## [1] "logical"
## [1] 1.0 2.5 4.5
## [1]  1  6 10
## [1] 3
## [1] "these are"    "some strings"

Adding a dim attribute to a vector allows it to behave like a 2-dimensional matrix or a multi-dimensional array.

You can create matrices and arrays with matrix() and array(), or by using the assignment form of dim(): We’ll use dim in this walk through. See how to use matrix or array by typing ?matrix or ?array on your RStudio console.

For example, you can reorganize int_seq into a 2 × 3 matrix (which has 2 rows and 3 columns):

##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6

In R, data frames (which we’ll cover in just a lil bit), are the most common way of storing and analyzing data. However, vectors too can suffice for general and basic data manipulation

With that said, let’s take atomic vectors out for a try!

We’ll start by looking at some simple data. Suppose a college takes a sample of student grades for a grades science class:

##  [1] 50 50 47 97 49  3 53 42 26 74 82 62 37 15 70 27 36 35 48 52 63 64

So, how many students does the sample contain 🗒?

## [1] 22

Indexing Vectors

Oftentimes we may want to access only part of a vector, or perhaps an individual element. This is called indexing and is accomplished with square brackets, []. R has a very flexible system that gives us several choices of index:

  • Passing a vector of positive numbers returns the slice of the vector containing the elements at those locations. The first position is 1 (not 0, as in some other languages).

  • Passing a vector of negative numbers returns the slice of the vector containing the elements everywhere except at those locations.

Let’s get the first and sixth grade:

## [1] 50  3

Perfect! Now let’s get a vector of all grades except for the 1st and the 6th student.

##  [1] 50 47 97 49 53 42 26 74 82 62 37 15 70 27 36 35 48 52 63 64

Alright, now we know our way around vectors, enough to knock out this module 💪.

The chapter Vectors in R for Data Science would be a good place to get you started on all things atomic vectors, lists (which we didn’t cover), and a whole lot on base R data structures! Be sure to check it out.

It’s time we performed some analysis of the grades data. For a first, we can find the simple average grade (in other words, the mean grade value).

## [1] 49.18182

So the mean grade is just around 50 - more or less in the middle of the possible range from 0 to 100.

Combining vectors

Let’s add a second set of data for the same students, this time recording the typical number of hours per week they devoted to studying.

We can create a 2-dimensional matrix from grades and study_hours by combining the vectors by columns using cbind()

##       study_hours grades
##  [1,]       10.00     50
##  [2,]       11.50     50
##  [3,]        9.00     47
##  [4,]       16.00     97
##  [5,]        9.25     49
##  [6,]        1.00      3
##  [7,]       11.50     53
##  [8,]        9.00     42
##  [9,]        8.50     26
## [10,]       14.50     74
## [11,]       15.50     82
## [12,]       13.75     62
## [13,]        9.00     37
## [14,]        8.00     15
## [15,]       15.50     70
## [16,]        8.00     27
## [17,]        9.00     36
## [18,]        6.00     35
## [19,]       10.00     48
## [20,]       12.00     52
## [21,]       12.50     63
## [22,]       12.00     64

Before going into anything complex, we might want to find out the dimensions of our object. Turns out, dim() can also help you retrieve the number of rows and columns of an object.

## [1] 22  2

As expected 🤩, the matrix contains 2 columns each with 22 rows.

We subset matrices using the notation [row_number,column_number]

So, for instance, if we wanted to access the first element in the in the first column (study_hours), we would do it as below:

## study_hours 
##          10

Accessing all the elements in the first row would also follow a similar fashion:

## study_hours      grades 
##          10          50

Now we have a multidimensional array containing both the student’s study time and grade information, which you can use to compare data. For example, how does the mean study time compare to the mean grade?

## Average study hours: 10.52
## Average grade: 49.18

Analysis using atomic vectors and matrices? Not so fast… 🤷

One shortcoming of atomic vectors and matrices is that they can only hold one mode of data; they don’t allow us to mix, say, numbers and character strings. If we try to do so, it will change the mode of the other elements in the vector to conform. Let’s see this in action:

## [1] "character"

Let’s take a look at the fine print!

##  [1] "50" "50" "47" "97" "49" "3"  "53" "42" "26" "74" "82" "62" "37" "15" "70"
## [16] "27" "36" "35" "48" "52" "63" "64" "22"
##  [1] 50 50 47 97 49  3 53 42 26 74 82 62 37 15 70 27 36 35 48 52 63 64

From the print() output, we can notice that the numbers got changed to character values so that the vector could accommodate all the elements we passed to the c() function. As such, further analysis like finding the mean would be quite a pain to say the least!

## Warning in mean.default(grades_mod1): argument is not numeric or logical:
## returning NA
## [1] NA

In R, a special object known as a data frame resolves this problem 🦸.

2. A glimpse into the tidyverse

Data frames and tibbles

Data frames are one of the biggest and most important ideas in R, and one of the things that make R different from other programming languages. A data frame is a rectangular collection of variables (in the columns) and observations (in the rows).

They are considered as far and away the most useful storage structure for data analysis.

Data frames can be thought as R’s equivalent to the Excel spreadsheet because it stores data in a similar format. A high-level overview of data frames is:

  • Data frames group vectors together into a two-dimensional table.

  • Each vector becomes a column in the table.

  • Each column of a data frame can be a different data type.

  • Every column in a data frame must be the same length.

R is an old language and over the course of time, the ways (and reasons) that people use R have changed, and some of the design decisions that made sense at the time data frames were created now cause frustration.

This frustration lead to the creation of the tibble, a modern reimagining of the data frame, that keeps what time has proven to be effective and retiring what is not.

Tibble is a part of the tidyverse, an opinionated collection of R packages , that make the data science workflow (such as importing, wranging, visualising, programming and modelling) faster, easier and more fun.

The easiest way to get tibble or any other package in the tidyverse is to install the whole tidyverse:

install.packages("tidyverse")

We’ll create a tibble by specifying a vector of student names, then wrap it together with the existing study_hours and grades vectors.

## # A tibble: 22 x 3
##    Name     StudyHours Grade
##    <chr>         <dbl> <dbl>
##  1 Dan           10       50
##  2 Joann         11.5     50
##  3 Pedro          9       47
##  4 Rosie         16       97
##  5 Ethan          9.25    49
##  6 Vicky          1        3
##  7 Frederic      11.5     53
##  8 Jimmie         9       42
##  9 Rhonda         8.5     26
## 10 Giovanni      14.5     74
## # ... with 12 more rows

Tidyverse’s dplyr

dplyr, part of the tidyverse, is a grammar of data manipulation, providing a consistent set of verbs that makes it easier for us to work with data.

dplyr::slice() allows you to select, remove, and duplicate rows by their (integer) locations.

Let’s start by retrieving data for a specific index. Remember indexing in R starts at 1.

We can also get the data at a range of integer locations, like this:

Often, there are multiple ways to achieve the same results. Here are other subtle ways of subsetting data frames:

Using [] to subset rows 5:10 and columns 1&3

Using %>%, filter, and select to subset rows 5:10 and columns 1&3

Let’s brush over some new concepts we’ll encounter in the example below:

  • Introducing the pipe operator (%>%): performs operations in logical sequence by passing an object forward into a function or call expression. You can think of the pipe operator as saying “and then” in your code.

  • dplyr::filter(): creates a subset of the data only containing rows that satisfy your conditions.

  • dplyr::select(): helps you pick which columns to keep or exclude.

Voila! There you have it! With the longevity and evolution R has had since its inception 20 years ago, you can be assured of multiple solutions to a task. And that in itself is amazing!

Sometimes, you may want to find which row index in a column matches a particular expression. The following trick will do:

## [1] 22

A dplyr approach of the same:

## [1] 22

Pipe 🚀 your way into dplyr using the data transformation chapter in R for data science.

3. Loading data from a file

We constructed a data frame from some existing vectors which we typed by hand in our console or markdown. However, typing invites typos and errors. In many real-world scenarios, data is loaded from sources such as files. You can then ask R to read the file and store the contents as an object.

Let’s replace the student grades data frame with the contents of a text file from the original repo of this course.

Notice that the data are already in tidy format - meaning that:

  • Each variable is a column

  • Each observation is a row

  • Each value is in its own cell

Name StudyHours Grade
Dan 10.00 50
Joann 11.50 50
Pedro 9.00 47
Rosie 16.00 97
Ethan 9.25 49
Vicky 1.00 3
Frederic 11.50 53
Jimmie 9.00 42
Rhonda 8.50 26
Giovanni 14.50 74
Francesca 15.50 82
Rajab 13.75 62
Naiyana 9.00 37
Kian 8.00 15
Jenny 15.50 70
Jakeem 8.00 27
Helena 9.00 36
Ismat 6.00 35
Anila 10.00 48
Skye 12.00 52
Daniel 12.50 63
Aisha 12.00 64
Bill 8.00 NA
Ted NA NA

Oh 😯! Look what we’ve got here: NAs and they bring us to the next part of our adventure.

Handling missing values

One of the most common issues data scientists need to deal with is incomplete or missing data. R represents missing, or unknown values, with special sentinel value: NA (Not Available).

So how would we know that the data frame contains missing values?

  • One way to know if our data contains any NAs in two shakes would be to use the generic function anyNA
## [1] TRUE
  • Another way would be to use the generic function is.na() which indicates which individual elements are missing with a logical TRUE.
##        Name StudyHours Grade
##  [1,] FALSE      FALSE FALSE
##  [2,] FALSE      FALSE FALSE
##  [3,] FALSE      FALSE FALSE
##  [4,] FALSE      FALSE FALSE
##  [5,] FALSE      FALSE FALSE
##  [6,] FALSE      FALSE FALSE
##  [7,] FALSE      FALSE FALSE
##  [8,] FALSE      FALSE FALSE
##  [9,] FALSE      FALSE FALSE
## [10,] FALSE      FALSE FALSE
## [11,] FALSE      FALSE FALSE
## [12,] FALSE      FALSE FALSE
## [13,] FALSE      FALSE FALSE
## [14,] FALSE      FALSE FALSE
## [15,] FALSE      FALSE FALSE
## [16,] FALSE      FALSE FALSE
## [17,] FALSE      FALSE FALSE
## [18,] FALSE      FALSE FALSE
## [19,] FALSE      FALSE FALSE
## [20,] FALSE      FALSE FALSE
## [21,] FALSE      FALSE FALSE
## [22,] FALSE      FALSE FALSE
## [23,] FALSE      FALSE  TRUE
## [24,] FALSE       TRUE  TRUE

Okay, got the job done but with a larger data frame, it would be inefficient and practically impossible to review all of the rows and columns individually😴.

  • Another more intuitive way would be to get the sum of missing values for each column, like this:
##       Name StudyHours      Grade 
##          0          1          2

So now we know that there’s one missing value in the StudyHours column, and two missing values in the Grade column.

To see them in context, we can filter the data frame to include only rows where any of the columns have an NA. The code below returns the rows where there is an NA in at least one of the column.

if_any() applies the same predicate function to a selection of columns and combines the results into a single logical vector.

everything() selects all columns.

So now that we’ve found the null values, what can we do about them?

One common approach is to impute replacement values. For example, if the number of study hours is missing, we could just assume that the student studied for an average amount of time and replace the missing value with the mean study hours.

To do this, we can use the tidyr::replace_na that replaces NAs with specified values.

tidyr is a part of the tidyverse and its goal is to help you create tidy data. The chapter Tidy data in R for Data Science would be a good place to get you started on the tidying up messy datasets.

Alright, how would we go about modifying existing columns?

Meet dplyr::mutate, the magical word that allows us to modify or even create new columns, while keeping the existing columns.

Artwork by @allison_horst

Artwork by @allison_horst

Good job!

Alternatively, it might be important to ensure that you only use data you know to be absolutely correct; so you can drop rows that contain null values by using tidyr::drop_na method. In this case, we’ll remove rows where any of the columns contain NA values.

Amazing! Our data is much more tidy-r! 💫

4. Explore data in the data frame

Now that we’ve cleaned up the missing values, we’re ready to explore the data in the data frame. Let’s start by comparing the mean study hours and grades.

## Average weekly study hours: 10.52
## Average grade: 49.18

Time to do some basic data wrangling with dplyr verbs

We’ll start with filtering the data frame to find only the students who studied for more than the average amount of time.

Note that the filtered result retained the attributes of the original tibble/data frame and is a tibble, so you can work with its rows and columns just like any other tibble.

For example, let’s find the average grade for students who undertook more than the average amount of study time.

## [1] 66.7

Let’s assume that the passing grade for the course is 60.

We can use that information to add a new column to the data frame, indicating whether or not each student passed.

Of course, there are multiple way to achieve this but in this example, we’ll result to a simple, lazy way to achieve a TRUE/FALSE indicator 🙇.

Once again, dplyr::mutate() will be on full display! The general structure of adding new columns is:

df %>% mutate(new_column_name = what_it_contains)

Let’s go forth and mutate!

Data frames are designed for tabular data, and you can use them to perform many of the kinds of data analytics operation you can do in a relational database; such as grouping and aggregating tables of data. In R, this is achieved using dplyr::group_by() %>% summarize()

  • dplyr::group_by() changes the unit of analysis from the complete dataset to individual groups.

  • dplyr::summarize() creates a new data frame with one column for each grouping variable and one column for each of the summary statistics that you have specified.

For example, we can use the dplyr::group_by() %>% summarize() to group the student data into groups based on the Pass column and then find the mean study time and grade for the groups of students who passed and failed the course.

Since we are applying the same function, mean, across multiple columns, dplyr::across will do the heavy lifting for us and reduce the code we’ll have to write.

What if we wanted to determine how many students passed or failed? We’d have to group our data based on the Pass column, then do a tally for each group. dplyr::count() wraps all this to give you a nice grouped count!

It’s worth mentioning that data_frame %>% … operation returns a new copy of the data frame; so if you want to modify a data frame but keep the existing variable, you need to assign the result of the operation to the existing variable.

For example, the following code sorts the student data into descending order of Grade, and assigns the resulting sorted data frame to the original df_students variable.

To do this, we’ll need to reach into dplyr and take one more verb: arrange(). It orders the rows of a data frame by column values.

In R, factors are used to work with categorical variables such as the students’ names column. Once created, factors can only contain a pre-defined set values, known as levels. By default, R always sorts levels in alphabetical order. In the above example, we converted the Name column from type character to factor and specified that the levels (student names), will always follow the descending order.

Know your way around factors with the factors chapter in R for Data Science.

5. Data visualization with ggplot2

Artwork by @allison_horst

Artwork by @allison_horst

Tibbles/data frames provide a great way to explore and analyze tabular data but I’ve heard it said:

“The simple graph has brought more information to the data analyst’s mind than any other device.” — John Tukey

R has several systems for making graphs, but ggplot2 is one of the most elegant and most versatile.

ggplot2 is designed to work iteratively. You start with a layer that shows the raw data. Then you add layers of annotations and statistical summaries. That’s why the ggplot2 artwork contains monsteRs carrying different stuff 😄.

This allows you to compose graphs by combining independent components.

Let’s start with a simple bar chart that shows the grade of each student.

So in this case, we’ll start with ggplot(), supply a dataset and aesthetic mapping (with aes()) then add a layers (like geom_bar()) for bar charts.

Well, that worked; but the chart could use some improvements to make it clearer what we’re looking at.

For example, the following code:

  • Specifies the color of the bar chart.

  • Adds a title to the chart (so we know what it represents)

  • Adds labels to the X and Y (so we know which axis shows which data)

  • Adds a grid (to make it easier to determine the values for the bars)

  • Rotates the X markers (so we can read them)

Much better 🤩!

We can also layout multiple plots in a single view. For example, the following code creates a figure with two subplots - one is a bar chart showing student grades, and the other is a pie chart (which are basically a stacked bar chart in polar coordinates) comparing the number of passing grades to non-passing grades.

patchwork extends ggplot API by providing mathematical operators (such as + or /) for combining multiple plots. Yes, as easy as that!

The Data visualisation chapter in R for Data Science covers how to make much more elegant visualisations than what we have just done. Be sure to check it out!

6. Getting started with statistical analysis

Now that you know how to use R to manipulate and visualize data, you can start analyzing it 🎉.

A lot of data science is rooted in statistics, so we’ll explore some basic statistical techniques.

Note: This is not intended to teach you statistics - that’s much too big a topic for this notebook. It will however introduce you to some statistical concepts and techniques that data scientists use as they explore data in preparation for machine learning modeling.

Descriptive statistics and data distribution

When examining a variable (for example a sample of student grades), data scientists are particularly interested in its distribution (in other words, how are all the different grade values spread across the sample). The starting point for this exploration is often to visualize the data as a histogram, and see how frequently each value for the variable occurs.

So what geom are we going to use? " geom_histogram (or something close)", you’ll say. Because you are already getting the gist/geom of it 🥳!

The histogram for grades is a symmetric shape, where the most frequently occurring grades tend to be in the middle of the range (around 50), with fewer grades at the extreme ends of the scale.

Measures of central tendency

To understand the distribution better, we can examine so-called measures of central tendency; which is a fancy way of describing statistics that represent the “middle” of the data. The goal of this is to try to find a “typical” value. Common ways to define the middle of the data include:

  • The mean: A simple average based on adding together all of the values in the sample set, and then dividing the total by the number of samples.

  • The median: The value in the middle of the range of all of the sample values.

  • The mode: The most commonly occuring value in the sample set*.

Let’s calculate these values, along with the minimum and maximum values for comparison, and show them on the histogram.

*Of course, in some sample sets , there may be a tie for the most common value - in which case the dataset is described as bimodal or even multimodal.

Base R does not provide a function for finding the mode. But worry not, statip::mfv returns the most frequent value(s) (or mode(s)) found in a vector. Other pretty awesome workarounds can be found can be found on this stackoverflow thread.

## Minimum: 3.00
## Mean: 49.18
## Median: 49.50
## Mode: 50.00
## Maximum: 97.00

For the grade data, the mean, median, and mode all seem to be more or less in the middle of the minimum and maximum, at around 50.

Another way to visualize the distribution of a variable is to use a box plot (sometimes called a box-and-whiskers plot). Let’s create one for the grade data.

The box plot shows the distribution of the grade values in a different format to the histogram. The box part of the plot shows where the inner two quartiles of the data reside - so in this case, half of the grades are between approximately 36 and 63. The whiskers extending from the box show the outer two quartiles; so the other half of the grades in this case are between 0 and 36 or 63 and 100. The line in the box indicates the median value.

It’s often useful to combine histograms and box plots, with the box plot’s orientation changed to align it with the histogram (in some ways, it can be helpful to think of the histogram as a “front elevation” view of the distribution, and the box plot as a “plan” view of the distribution from above). Since we may need to plot the histograms and box plots for different variables, it will be convenient to write a function. Functions allows you to automate common tasks in a more powerful and general way than copy-and-pasting.

This Introduction to R Functions by Stephanie Kirmer will get you writing functions in the R language in no time.

Let’s get right to it! Functions in R are generally defined in this fashion:

name <- function(variables) {return(value)}

Just for the sake of pretty and more elaborative plots, we’ll include a parameter binwidth as one of the input variables to our function. Binwidth will specify the width of the bins used for our histogram and will help us explore multiple widths to find the best that will illustrate the stories in our data.

# Create a function that we can reuse
show_distribution <- function(var_data, binwidth) {
  library(ggplot2)
  library(dplyr)
  library(glue)
  library(patchwork)
  
  # Get statistics
  min_val <- min(pull(var_data))
  max_val <- max(pull(var_data))
  mean_val <- mean(pull(var_data))
  med_val <- median(pull(var_data))
  mod_val <- statip::mfv(pull(var_data))

  # Print the stats
  stats <- glue::glue(
  'Minimum: {format(round(min_val, 2), nsmall = 2)}
   Mean: {format(round(mean_val, 2), nsmall = 2)}
   Median: {format(round(med_val, 2), nsmall = 2)}
   Mode: {format(round(mod_val, 2), nsmall = 2)}
   Maximum: {format(round(max_val, 2), nsmall = 2)}'
  )
  
  # Plot the histogram
  hist_gram <- var_data %>% 
  ggplot(aes(x = pull(var_data)))+
  geom_histogram(binwidth = binwidth, fill = "midnightblue", alpha = 0.7, boundary = 0.4)+
  # Add lines for the statistics
  geom_vline(xintercept = min_val, color = 'gray33', linetype = "dashed", size = 1.3)+
  geom_vline(xintercept = mean_val, color = 'cyan', linetype = "dashed", size = 1.3)+
  geom_vline(xintercept = med_val, color = 'red', linetype = "dashed", size = 1.3 )+
  geom_vline(xintercept = mod_val, color = 'yellow', linetype = "dashed", size = 1.3 )+
  geom_vline(xintercept = max_val, color = 'gray33', linetype = "dashed", size = 1.3 )+
  # Add titles and labels
  ggtitle('Data Distribution')+
  xlab('')+
  ylab('Frequency')+
  theme(plot.title = element_text(hjust = 0.5))
  
  # Plot the box plot
  bx_plt <- var_data %>% 
  ggplot(aes(x = pull(var_data), y = 1))+
  geom_boxplot(fill = "#E69F00", color = "gray23", alpha = 0.7
               )+
    # Add titles and labels
  xlab("Value")+
  ylab("")+
  theme(plot.title = element_text(hjust = 0.5))
  
  
  # To return multiple outputs, use a `list`
  return(list(stats,
              hist_gram / bx_plt)
        ) # End of returned outputs
  
} # End of function

Now that the show_distribution() function is done for, let’s get a variable/column to examine and then call the function.

## [[1]]
## Minimum: 3.00
## Mean: 49.18
## Median: 49.50
## Mode: 50.00
## Maximum: 97.00
## 
## [[2]]

All of the measurements of central tendency are right in the middle of the data distribution, which is symmetric with values becoming progressively lower in both directions from the middle.

To explore this distribution in more detail, you need to understand that statistics is fundamentally about taking samples of data and using probability functions to extrapolate information about the full population of data. For example, the student data consists of 22 samples, and for each sample there is a grade value. You can think of each sample grade as a variable that’s been randomly selected from the set of all grades awarded for this course. With enough of these random variables, you can calculate something called a probability density function, which estimates the distribution of grades for the full population.

A density plot is a representation of the distribution of a numeric variable. It is a smoothed version of the histogram and is used in the same kind of situation.

ggplot2() has got you covered with geom_density!

As expected from the histogram of the sample, the density of the Grade column shows the characteristic ’bell curve" of what statisticians call a normal distribution with the mean and mode at the center and symmetric tails.

Now let’s take a look at the distribution of the study hours data. Woo-hoo, this should a breeze since we’ll just be calling the respective functions 🥳!

## [[1]]
## Minimum: 1.00
## Mean: 10.52
## Median: 10.00
## Mode: 9.00
## Maximum: 16.00
## 
## [[2]]

The distribution of the study time data is significantly different from that of the grades. Note that the whiskers of the box plot only extend to around 6.0, indicating that the vast majority of the first quarter of the data is above this value. The minimum (1) is marked with an o, indicating that it is statistically an outlier - a value that lies significantly outside the range of the rest of the distribution.

Outliers can occur for many reasons. Maybe a student meant to record “10” hours of study time, but entered “1” and missed the “0”. Or maybe the student was abnormally lazy when it comes to studying! Either way, it’s a statistical anomaly that doesn’t represent a typical student. Let’s see what the distribution looks like without it.

## [[1]]
## Minimum: 6.00
## Mean: 10.98
## Median: 10.00
## Mode: 9.00
## Maximum: 16.00
## 
## [[2]]

In this example, the dataset is small enough to clearly see that the value 1 is an outlier for the StudyHours column, so you can exclude it explicitly.

In most real-world cases, it’s easier to consider outliers as being values that fall below or above percentiles within which most of the data lie. For example, the following code uses the stats::quantile() function to exclude observations below the 0.01th percentile (the value above which 99% of the data reside).

## [[1]]
## Minimum: 6.00
## Mean: 10.98
## Median: 10.00
## Mode: 9.00
## Maximum: 16.00
## 
## [[2]]

Tip: You can also eliminate outliers at the upper end of the distribution by defining a threshold at a high percentile value - for example, you could use the stats::quantile() function to find the 0.99 percentile below which 99% of the data reside.

With the outliers removed, the box plot shows all data within the four quartiles. Note that the distribution is not symmetric like it is for the grade data though - there are some students with very high study times of around 16 hours, but the bulk of the data is between 7 and 13 hours; The few extremely high values pull the mean towards the higher end of the scale.

Let’s look at the density for this distribution.

This kind of distribution is called right skewed. The mass of the data is on the left side of the distribution, creating a long tail to the right because of the values at the extreme high end; which pull the mean to the right.

Measures of variance

So now we have a good idea where the middle of the grade and study hours data distributions are. However, there’s another aspect of the distributions we should examine: how much variability is there in the data?

Typical statistics that measure variability in the data include:

  • Range: The difference between the maximum and minimum. There’s no built-in function for this, but it’s easy to calculate using the min and max functions. Another approach would be to use Base R’s base::range() which returns a vector containing the minimum and maximum of all the given arguments. Wrapping this in base::diff() will get you well on your way to finding the range.

  • Variance: The average of the squared difference from the mean. You can use the built-in var function to find this.

  • Standard Deviation: The square root of the variance. You can use the built-in sd function to find this.

## $Grade
## - Range: 94.00
## - Variance : 472.54
## - Std.Dev : 21.74
## 
## $StudyHours
## - Range: 15.00
## - Variance : 12.16
## - Std.Dev : 3.49

Of these statistics, the standard deviation is generally the most useful. It provides a measure of variance in the data on the same scale as the data itself (so grade points for the Grade distribution and hours for the StudyHours distribution). The higher the standard deviation, the more variance there is when comparing values in the distribution to the distribution mean - in other words, the data is more spread out.

When working with a normal distribution, the standard deviation works with the particular characteristics of a normal distribution to provide even greater insight. This can be summarized using the 68–95–99.7 rule, also known as the empirical rulewhich is described as follows:

In any normal distribution:

  • Approximately 68.26% of values fall within one standard deviation from the mean.

  • Approximately 95.45% of values fall within two standard deviations from the mean.

  • Approximately 99.73% of values fall within three standard deviations from the mean.

As a quick detour, let’s see verify that the distribution of grades follows a normal distribution.

## 68.27% of grades fall within one standard deviation from the mean.
## 95.45% of grades fall within one standard deviation from the mean.
## 99.73% of grades fall within one standard deviation from the mean.

There is no doubt that the distribution of grades indeed follows a normal distribution!

Wouldn’t hurt if we showed this graphically right? As it were 💁, “The simple graph has …”

Instead of using geom_density, we’ll calculate the density of the Grade column and then use the results to whip up a density plot. We’ll then shade/fill the density plot depending on the number of standard deviations from the mean.

# Get the Grade column
col <- df_students['Grade']

# Compute kernel density estimates
density <- density(pull(col), kernel = "gaussian")

# Create a dataframe containing coordinates where density is estimated 
#and the corresponding estimated value
density_xy <- tibble(
  x = density$x,
  y = density$y
)

# Make density plots for various deviations of the mean

plt_sd_1 <- density_xy %>%
  ggplot(aes(x = x, y = y))+
  geom_line()+
  geom_area(aes(x = ifelse(x>(me_an-st_dev) & x<(me_an+st_dev), x, 0)), fill = "plum1", alpha = 0.8)+
  annotate("text", x = 50, y = 0.01, label = "1 std (68.26%)")+
  xlim(1, max(col))+
  geom_vline(xintercept = me_an, linetype = "dashed")+
  labs(x = "", y = "Density")


# 2 std deviations from the mean
plt_sd_2 <- density_xy %>%
  ggplot(aes(x = x, y = y))+
  geom_line()+
  geom_area(aes(x = ifelse(x>(me_an-(2*st_dev)) & x<(me_an+(2*st_dev)), x, 0)), fill = "plum3", alpha = 0.7)+
  annotate("text", x = 50, y = 0.01, label = "2 std (95.45%)")+
  xlim(1, max(col))+
  geom_vline(xintercept = me_an, linetype = "dashed")+
  labs(x = "", y = "")


# 3 std deviations from the mean
plt_sd_3 <- density_xy %>%
  ggplot(aes(x = x, y = y))+
  geom_line()+
  geom_area(aes(x = ifelse(x>(me_an-(3*st_dev)) & x<(me_an+(3*st_dev)), x, 0)), fill = "plum4", alpha = 0.5)+
  annotate("text", x = 50, y = 0.01, label = "3 std (99.73%)")+
  xlim(1, max(col))+
  geom_vline(xintercept = me_an, linetype = "dashed")+
  labs(x = "Grade", y = "")



# Patching things up
plt_sd_1| (plt_sd_2/plt_sd_3)+
  plot_annotation(tag_levels = 'a')

Since we know that the mean grade is 49.18, the standard deviation is 21.74, and distribution of grades is normal; we can calculate that 68.26% of students should achieve a grade between 27.44 and 70.92 as shown in the first plot above.

The descriptive statistics we’ve used to understand the distribution of the student data variables are the basis of statistical analysis; and because they’re such an important part of exploring your data, there’s a built-in Base R function, base::summary(), that returns the result summaries of the results of various objects passed to it.

##         Name      StudyHours        Grade          Pass        
##  Rosie    : 1   Min.   : 1.00   Min.   : 3.00   Mode :logical  
##  Francesca: 1   1st Qu.: 9.00   1st Qu.:36.25   FALSE:15       
##  Giovanni : 1   Median :10.00   Median :49.50   TRUE :7        
##  Jenny    : 1   Mean   :10.52   Mean   :49.18                  
##  Aisha    : 1   3rd Qu.:12.38   3rd Qu.:62.75                  
##  Daniel   : 1   Max.   :16.00   Max.   :97.00                  
##  (Other)  :16

Of course, with the need for much more robust data exploration and reporting, many packages have been developed for summarizing data.

A good example would be the summarytools package. summarytools::descr() does a remarkable job at finding statistics for numerical data.

## Descriptive Statistics  
## df_students  
## N: 22  
## 
##                    Grade   StudyHours
## --------------- -------- ------------
##            Mean    49.18        10.52
##         Std.Dev    21.74         3.49
##             Min     3.00         1.00
##          Median    49.50        10.00
##             Max    97.00        16.00
##         N.Valid    22.00        22.00
##       Pct.Valid   100.00       100.00

7. Comparing data

Now that you know something about the statistical distribution of the data in your dataset, you’re ready to examine your data to identify any apparent relationships between variables.

First of all, let’s get rid of any rows that contain outliers so that we have a sample that is representative of a typical class of students. We identified that the StudyHours column contains some outliers with extremely low values, so we’ll remove those rows.

Comparing numeric and categorical variables

The data includes two numeric variables (StudyHours and Grade) and two categorical variables (Name and Pass). In R, categorical variables are usually saved as factors or character vectors.

Let’s start by comparing the numeric StudyHours column to the categorical Pass column to see if there’s an apparent relationship between the number of hours studied and a passing grade.

To make this comparison, let’s create box plots showing the distribution of StudyHours for each possible Pass value (TRUE and FALSE).

Comparing the StudyHours distributions, it’s immediately apparent (if not particularly surprising) that students who passed the course tended to study for more hours than students who didn’t. So if you wanted to predict whether or not a student is likely to pass the course, the amount of time they spend studying may be a good predictive feature.

Comparing numeric variables

Now let’s compare two numeric variables. We’ll start by creating a bar chart that shows both grade and study hours.

To achieve this, we can first transform our data to a long format using tidyr::pivot_longer() then play around with the fill aesthetic and position_dodge() to place overlapping objects directly beside one another.

Also, we’ll try out paletter, a collection of most color palettes in a single R package.

The chart shows bars for both grade and study hours for each student; but it’s not easy to compare because the values are on different scales. Grades are measured in grade points, and range from 3 to 97; while study time is measured in hours and ranges from 1 to 16.

A common technique when dealing with numeric data in different scales is to normalize the data so that the values retain their proportional distribution, but are measured on the same scale. To accomplish this, we’ll use a technique called MinMaxscaling that distributes the values proportionally on a scale of 0 to 1. You could write the code to apply this transformation as shown in this; but the scales library provides a scaler (scales::rescale()) to do it for you.

Good news is that Scales is installed when you install ggplot2 or the tidyverse!

With the data normalized, it’s easier to see an apparent relationship between grade and study time. It’s not an exact match, but it definitely seems like students with higher grades tend to have studied more.

8. Fitting a simple linear regression model

From the previous comparisons, there seems to be a correlation between study time and grade; and in fact, there’s a statistical correlation measurement we can use to quantify the relationship between these columns.

## [1] 0.9117666

The correlation statistic is a value between -1 and 1 that indicates the strength of a relationship. Values above 0 indicate a positive correlation (high values of one variable tend to coincide with high values of the other), while values below 0 indicate a negative correlation (high values of one variable tend to coincide with low values of the other). In this case, the correlation value is close to 1; showing a strongly positive correlation between study time and grade.

Note: Data scientists often quote the maxim “correlation is not causation”. In other words, as tempting as it might be, you shouldn’t interpret the statistical correlation as explaining why one of the values is high. In the case of the student data, the statistics demonstrates that students with high grades tend to also have high amounts of study time; but this is not the same as proving that they achieved high grades because they studied a lot. The statistic could equally be used as evidence to support the nonsensical conclusion that the students studied a lot because their grades were going to be high.

Another way to visualize the apparent correlation between two numeric columns is to use a scatter plot. With geom_point, you’ll be well on your way to whipping up a neat scatterplot.

Again, it looks like there’s a discernible pattern in which the students who studied the most hours are also the students who got the highest grades.

We can see this more clearly by adding a regression line (or a line of best fit) to the plot that shows the general trend in the data. To do this, we’ll use a statistical technique called least squares regression.

Warning - Math Ahead!

Cast your mind back to when you were learning how to solve linear equations in school, and recall that the slope-intercept form of a linear equation looks like this:

y = mx + b

In this equation, y and x are the coordinate variables, m is the slope of the line, and b is the y-intercept (where the line goes through the Y-axis).

In the case of our scatter plot for our student data, we already have our values for x (StudyHours) and y (Grade), so we just need to calculate the intercept and slope of the straight line that lies closest to those points. Then we can form a linear equation that calculates a new y value on that line for each of our x (StudyHours) values - to avoid confusion, we’ll call this new y value f(x) (because it’s the output from a linear equation function based on x). The difference between the original y (Grade) value and the f(x) value is the error between our regression line and the actual Grade achieved by the student. Our goal is to calculate the slope and intercept for a line with the lowest overall error.

Specifically, we define the overall error by taking the error for each point, squaring it, and adding all the squared errors together. The line of best fit is the line that gives us the lowest value for the sum of the squared errors - hence the name least squares regression.

Fortunately, you don’t need to code the regression calculation yourself - the The R Stats Package stats provides the lm() function to do the hard work for you. lm() takes a formula of the form:

outcome ~ predictor

This returns (among other things) the coefficients you need for the slope equation - slope (m) and intercept (b) based on a given pair of variable samples you want to compare.

## slope: 6.3134
## y-intercept: -17.9164
## so…
##  f(x) = 6.3134x + -17.9164

Now that we have the equation f(x) = 6.3134x + -17.9164 of the least squares regression line, we can use that to estimate the grade given the amount of hours spent studying.

We now have a data frame that contains the following values:

  • The StudyHours for each student.

  • The Grade achieved by each student.

  • The f(x) value calculated using the regression line coefficients.

  • The error between the calculated f(x) value and the actual Grade value.

Some of the errors, particularly at the extreme ends, are quite large (up to over 17.5 grade points); but in general, the line is pretty close to the actual grades.

Now, time to see this visually 📈. We’ll create the scatter plot of the sample study hours and grades as before, and then add a line of best fit based on the least squares regression coefficients.

Adding a line based on the linear model can be done much easily by replacing

geom_abline(intercept = intercept_b, slope = slope_m, ...)

with

geom_smooth(method = "lm", se = F)

Using the regression coefficients for prediction

Now that we have the regression coefficients for the study time and grade relationship, you can use them in a function to estimate the expected grade for a given amount of study.

## Studying for 14 hours per week may result in a grade of 70

So by applying statistics to sample data, you’ve determined a relationship between study time and grade; and encapsulated that relationship in a general function that can be used to predict a grade for a given amount of study time.

This technique is in fact the basic premise of machine learning. You can take a set of sample data that includes one or more features (in this case, the number of hours studied) and a known label value (in this case, the grade achieved) and use the sample data to derive a function that calculates predicted label values for any given set of features.

9. Summary

Now, it’s time we wrapped it up! Congratulations 👏👏 on finishing the first module.

In this module, you learned how to use R to explore, visualize, and manipulate data. Data exploration is at the core of data science, and is a key element in data analysis and machine learning.

Be sure to join us in the next module as we dive into how to train and evaluate regression models.

Further reading

Happy Learning,

Eric (R_ic) (Gold Microsoft Learn Student Ambassador) and Ian (Co-organizer DekutR Data Science Community).

Thank you to:

  • Florence Kuria

  • Josh (Gold Microsoft Learn Student Ambassador)

  • Bethany (Gold Microsoft Learn Student Ambassador)

for their valuable feedback and suggestions.

LS0tDQp0aXRsZTogJ0NyZWF0ZSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsczogQW4gUiB2ZXJzaW9uJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNzczogc3R5bGVfNS5jc3MNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0aGVtZTogZmxhdGx5DQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQojYWx3YXlzX2FsbG93X2h0bWw6IHRydWUNCmRlc2NyaXB0aW9uOiBNYWNoaW5lIGxlYXJuaW5nIGlzIHRoZSBmb3VuZGF0aW9uIGZvciBwcmVkaWN0aXZlIG1vZGVsaW5nIGFuZCBhcnRpZmljaWFsDQogIGludGVsbGlnZW5jZS4gTGVhcm4gdGhlIGNvcmUgcHJpbmNpcGxlcyBvZiBtYWNoaW5lIGxlYXJuaW5nIGFuZCBob3cgdG8gdXNlIGNvbW1vbg0KICB0b29scyBhbmQgZnJhbWV3b3JrcyB0byB0cmFpbiwgZXZhbHVhdGUsIGFuZCB1c2UgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojbGlicmFyeShsZWFybnIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2x1ZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KHN1bW1hcnl0b29scykNCiN0dXRvcmlhbF9vcHRpb25zKGV4ZXJjaXNlLnRpbWVsaW1pdCA9IDYwKQ0KI2tuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUpDQpgYGANCg0KIyMgQnVja2xlIHVwIPCfkajigI3wn5qADQoNCkluIHRoaXMgbGVhcm5pbmcgcGF0aCwgd2UnbGwgbGVhcm4gaG93IHRvIGNyZWF0ZSBNYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB1c2luZyBgUmAg8J+Yii4gTWFjaGluZSBsZWFybmluZyBpcyB0aGUgZm91bmRhdGlvbiBmb3IgcHJlZGljdGl2ZSBtb2RlbGluZyBhbmQgYXJ0aWZpY2lhbCBpbnRlbGxpZ2VuY2UuV2UnbGwgbGVhcm4gdGhlIGNvcmUgcHJpbmNpcGxlcyBvZiBtYWNoaW5lIGxlYXJuaW5nIGFuZCBob3cgdG8gdXNlIGNvbW1vbiB0b29scyBhbmQgZnJhbWV3b3JrcyB0byB0cmFpbiwgZXZhbHVhdGUsIGFuZCB1c2UgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuDQoNCk1vZHVsZXMgdGhhdCB3aWxsIGJlIGNvdmVyZWQgaW4gdGhpcyBsZWFybmluZyBwYXRoIGluY2x1ZGU6DQoNCi0gICAqRXhwbG9yZSBhbmQgYW5hbHl6ZSBkYXRhIHdpdGggUioNCg0KLSAgICpUcmFpbiBhbmQgZXZhbHVhdGUgcmVncmVzc2lvbiBtb2RlbHMgKHVuZGVyIGRldmVsb3BtZW50KSoNCg0KLSAgICpUcmFpbiBhbmQgZXZhbHVhdGUgY2xhc3NpZmljYXRpb24gbW9kZWxzICh1bmRlciBkZXZlbG9wbWVudCkqDQoNCi0gICAqVHJhaW4gYW5kIGV2YWx1YXRlIGNsdXN0ZXJpbmcgbW9kZWxzICh1bmRlciBkZXZlbG9wbWVudCkqDQoNCi0gICAqVHJhaW4gYW5kIGV2YWx1YXRlIGRlZXAgbGVhcm5pbmcgbW9kZWxzICh1bmRlciBkZXZlbG9wbWVudCkqDQoNCiMjIyAqKlByZXJlcXVpc2l0ZXMqKg0KDQpUaGlzIGxlYXJuaW5nIHBhdGggYXNzdW1lcyBrbm93bGVkZ2Ugb2YgYmFzaWMgbWF0aGVtYXRpY2FsIGNvbmNlcHRzLiBTb21lIGV4cGVyaWVuY2Ugd2l0aCBgUiBhbmQgdGhlIHRpZHl2ZXJzZWAgaXMgYWxzbyBiZW5lZmljaWFsIHRob3VnaCB3ZSdsbCB0cnkgYXMgbXVjaCBhcyBwb3NzaWJsZSB0byBza2ltIHRocm91Z2ggdGhlIGNvcmUgY29uY2VwdHMuIFRvIGdldCBzdGFydGVkIHdpdGggUiBhbmQgdGhlIHRpZHl2ZXJzZSAsIHRoZSBiZXN0IHBsYWNlIHRvIHN0YXJ0IHdvdWxkIGJlIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHA6Ly9yNGRzLmhhZC5jby5uei8pIGFuIE8nUmVpbGx5IGJvb2sgd3JpdHRlbiBieSBIYWRsZXkgV2lja2hhbSBhbmQgR2FycmV0dCBHcm9sZW11bmQuIEl0J3MgZGVzaWduZWQgdG8gdGFrZSB5b3UgZnJvbSBrbm93aW5nIG5vdGhpbmcgYWJvdXQgUiBvciB0aGUgdGlkeXZlcnNlIHRvIGhhdmluZyBhbGwgdGhlIGJhc2ljIHRvb2xzIG9mIGRhdGEgc2NpZW5jZSBhdCB5b3VyIGZpbmdlcnRpcHMuDQoNClRoaXMgW3R1dG9yaWFsXShodHRwczovL2xlYXJuci1leGFtcGxlcy5zaGlueWFwcHMuaW8vZXgtc2V0dXAtci8jc2VjdGlvbi13ZWxjb21lKSB3aWxsIGhlbHAgeW91IHNldCB1cCB5b3VyIGNvbXB1dGVyIHRvIHVzZSBSLiBJdCBpcyBmb3IgeW91IGlmIHlvdSBuZWVkIHRvOg0KDQotICAgSW5zdGFsbCBSIG9uIHlvdXIgY29tcHV0ZXINCg0KLSAgIEluc3RhbGwgdGhlIFJTdHVkaW8gSURFDQoNCi0gICBJbnN0YWxsIHRoZSBgdGlkeXZlcnNlYCBSIHBhY2thZ2UNCg0KWW91IGNhbiBza2lwIHRoaXMgdHV0b3JpYWwgaWYgeW91J3ZlIGFscmVhZHkgZG9uZSB0aGVzZSB0aGluZ3MuDQoNCldlJ2xsIHJlcXVpcmUgc29tZSBwYWNrYWdlcyB0byBrbm9jay1vZmYgdGhpcyBtb2R1bGUuIFlvdSBjYW4gaGF2ZSB0aGVtIGluc3RhbGxlZCBhczoNCg0KYGluc3RhbGwucGFja2FnZXMoYygndGlkeXZlcnNlJywgJ3BhdGNod29yaycsICdwYWxldHRlZXInLCAnc3RhdGlwJywgJ3N1bW1hcnl0b29scycpKWANCg0KVGhlIGBQeXRob25gIGVkaXRpb24gb2YgdGhlIGxlYXJuaW5nIHBhdGggY2FuIGJlIGZvdW5kIGF0IFt0aGlzIGxlYXJuaW5nIHBhdGhdKGh0dHBzOi8vZG9jcy5taWNyb3NvZnQuY29tL2VuLXVzL2xlYXJuL3BhdGhzL2NyZWF0ZS1tYWNoaW5lLWxlYXJuLW1vZGVscy8pLg0KDQojIyMgV2h5IFI/DQoNCj4gV2UgdGhpbmsgUiBpcyBhIGdyZWF0IHBsYWNlIHRvIHN0YXJ0IHlvdXIgZGF0YSBzY2llbmNlIGpvdXJuZXkgYmVjYXVzZSBpdCBpcyBhbiBlbnZpcm9ubWVudCBkZXNpZ25lZCBmcm9tIHRoZSBncm91bmQgdXAgdG8gc3VwcG9ydCBkYXRhIHNjaWVuY2UuIFIgaXMgbm90IGp1c3QgYSBwcm9ncmFtbWluZyBsYW5ndWFnZSwgYnV0IGl0IGlzIGFsc28gYW4gaW50ZXJhY3RpdmUgZW52aXJvbm1lbnQgZm9yIGRvaW5nIGRhdGEgc2NpZW5jZS4gVG8gc3VwcG9ydCBpbnRlcmFjdGlvbiwgUiBpcyBhIG11Y2ggbW9yZSBmbGV4aWJsZSBsYW5ndWFnZSB0aGFuIG1hbnkgb2YgaXRzIHBlZXJzLiBUaGlzIGZsZXhpYmlsaXR5IGNvbWVzIHdpdGggaXRzIGRvd25zaWRlcywgYnV0IHRoZSBiaWcgdXBzaWRlIGlzIGhvdyBlYXN5IGl0IGlzIHRvIGV2b2x2ZSB0YWlsb3JlZCBncmFtbWFycyBmb3Igc3BlY2lmaWMgcGFydHMgb2YgdGhlIGRhdGEgc2NpZW5jZSBwcm9jZXNzLiBUaGVzZSBtaW5pIGxhbmd1YWdlcyBoZWxwIHlvdSB0aGluayBhYm91dCBwcm9ibGVtcyBhcyBhIGRhdGEgc2NpZW50aXN0LCB3aGlsZSBzdXBwb3J0aW5nIGZsdWVudCBpbnRlcmFjdGlvbiBiZXR3ZWVuIHlvdXIgYnJhaW4gYW5kIHRoZSBjb21wdXRlci4gLSBbKipSIGZvciBEYXRhIFNjaWVuY2UqKl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9pbmRleC5odG1sKWJ5IEhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZA0KDQpOb3csIGxldCdzIGdldCBzdGFydGVkIQ0KDQohW0FydHdvcmsgYnkgXEBhbGxpc29uX2hvcnN0XShpbWFnZXMvcl9sZWFybmVyc19zbS5qcGVnKXt3aWR0aD0iNTY5In0NCg0KIyMgRXhwbG9yZSBhbmQgYW5hbHl6ZSBkYXRhIHdpdGggUg0KDQojIyMgSW50cm9kdWN0aW9uDQoNClVuc3VycHJpc2luZ2x5LCB0aGUgcm9sZSBvZiBhIGRhdGEgc2NpZW50aXN0IHByaW1hcmlseSBpbnZvbHZlcyBleHBsb3JpbmcgYW5kIGFuYWx5emluZyBkYXRhLiBUaGUgcmVzdWx0cyBvZiB0aGlzIGFuYWx5c2lzIG1pZ2h0IGZvcm0gdGhlIGJhc2lzIG9mIGEgcmVwb3J0IG9yIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbDsgYnV0IGl0IGFsbCBiZWdpbnMgd2l0aCBkYXRhLg0KDQo+ICpNYWNoaW5lIGxlYXJuaW5nIGlzIGEgc3Vic2V0IG9mIGRhdGEgc2NpZW5jZSB0aGF0IGRlYWxzIHdpdGggcHJlZGljdGl2ZSBtb2RlbGluZy4gSW4gb3RoZXIgd29yZHMsIHVzaW5nIGRhdGEgdG8gY3JlYXRlIG1vZGVscyB0aGF0IGNhbiBwcmVkaWN0IHVua25vd24gdmFsdWVzLiBJdCB3b3JrcyBieSBpZGVudGlmeWluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gZGF0YSB2YWx1ZXMgdGhhdCBkZXNjcmliZSBjaGFyYWN0ZXJpc3RpY3Mgb2Ygc29tZXRoaW5nIChpdHMgZmVhdHVyZXMpIGFuZCB0aGUgdmFsdWUgd2Ugd2FudCB0byBwcmVkaWN0ICh0aGUgbGFiZWwpLCBhbmQgZW5jYXBzdWxhdGluZyB0aGVzZSByZWxhdGlvbnNoaXBzIGluIGEgbW9kZWwgdGhyb3VnaCBhIHRyYWluaW5nIHByb2Nlc3MuKg0KDQpVc3VhbGx5LCBhIGRhdGEgYW5hbHlzaXMgcHJvamVjdCBpcyBkZXNpZ25lZCB0byBlc3RhYmxpc2ggaW5zaWdodHMgYXJvdW5kIGEgcGFydGljdWxhciBzY2VuYXJpbyBvciB0byB0ZXN0IGEgaHlwb3RoZXNpcy4gRm9yIGV4YW1wbGUsIHN1cHBvc2UgYSB1bml2ZXJzaXR5IHByb2Zlc3NvciBjb2xsZWN0cyBkYXRhIGZyb20gZGF0YSBzY2llbmNlIHN0dWRlbnRzLCBpbmNsdWRpbmcgdGhlIG51bWJlciBvZiBsZWN0dXJlcyBhdHRlbmRlZCwgdGhlIGhvdXJzIHNwZW50IHN0dWR5aW5nLCBhbmQgdGhlIGZpbmFsIGdyYWRlIGFjaGlldmVkIG9uIHRoZSBlbmQgb2YgdGVybSBleGFtLiBUaGUgcHJvZmVzc29yIGNvdWxkIHRoZW4gdGFrZSBhIHNhbXBsZSBvZiB0aGUgZGF0YSBhbmQgYW5hbHl6ZSBpdCB0byBkZXRlcm1pbmUgaWYgdGhlcmUgaXMgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgYW1vdW50IG9mIHN0dWR5IGEgc3R1ZGVudCB1bmRlcnRha2VzIGFuZCB0aGUgZmluYWwgZ3JhZGUgdGhleSBhY2hpZXZlLiBUaGV5IG1pZ2h0IHVzZSB0aGUgZGF0YSB0byB0ZXN0IGEgaHlwb3RoZXNpcyB0aGF0IG9ubHkgc3R1ZGVudHMgd2hvIHN0dWR5IGZvciBhIG1pbmltdW0gbnVtYmVyIG9mIGhvdXJzIGNhbiBleHBlY3QgdG8gYWNoaWV2ZSBhIHBhc3NpbmcgZ3JhZGU7IG9yIGV2ZW4gcHJlcGFyZSB0aGUgZGF0YSB0byB0cmFpbiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgdGhhdCBwcmVkaWN0cyBhIHN0dWRlbnQncyBncmFkZSBiYXNlZCBvbiB0aGVpciBzdHVkeSBoYWJpdHMuDQoNCiFbXShpbWFnZXMvc3R1ZGVudC1ncmFkZXMucG5nKXt3aWR0aD0iNTY5In0NCg0KIyMgKipFeGVyY2lzZSAtIEV4cGxvcmUgZGF0YSoqDQoNCkRhdGEgZXhwbG9yYXRpb24gYW5kIGFuYWx5c2lzIGlzIHR5cGljYWxseSBhbiAqaXRlcmF0aXZlKiBwcm9jZXNzLCBpbiB3aGljaCB0aGUgZGF0YSBzY2llbnRpc3QgdGFrZXMgYSBzYW1wbGUgb2YgZGF0YSwgYW5kIHBlcmZvcm1zIHRoZSBmb2xsb3dpbmcga2luZHMgb2YgdGFzayB0byBhbmFseXplIGl0IGFuZCB0ZXN0IGh5cG90aGVzZXM6DQoNCi0gICBDbGVhbiBkYXRhIHRvIGhhbmRsZSBlcnJvcnMsIG1pc3NpbmcgdmFsdWVzLCBhbmQgb3RoZXIgaXNzdWVzLg0KDQotICAgQXBwbHkgc3RhdGlzdGljYWwgdGVjaG5pcXVlcyB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgZGF0YSwgYW5kIGhvdyB0aGUgc2FtcGxlIG1pZ2h0IGJlIGV4cGVjdGVkIHRvIHJlcHJlc2VudCB0aGUgcmVhbC13b3JsZCBwb3B1bGF0aW9uIG9mIGRhdGEsIGFsbG93aW5nIGZvciByYW5kb20gdmFyaWF0aW9uLg0KDQotICAgVmlzdWFsaXplIGRhdGEgdG8gZGV0ZXJtaW5lIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMsIGFuZCBpbiB0aGUgY2FzZSBvZiBhIG1hY2hpbmUgbGVhcm5pbmcgcHJvamVjdCwgaWRlbnRpZnkgKmZlYXR1cmVzKiB0aGF0IGFyZSBwb3RlbnRpYWxseSBwcmVkaWN0aXZlIG9mIHRoZSAqbGFiZWwqLg0KDQotICAgRGVyaXZlIG5ldyBmZWF0dXJlcyBmcm9tIGV4aXN0aW5nIG9uZXMgdGhhdCBtaWdodCBiZXR0ZXIgZW5jYXBzdWxhdGUgcmVsYXRpb25zaGlwcyB3aXRoaW4gdGhlIGRhdGEuDQoNCi0gICBSZXZpc2UgdGhlIGh5cG90aGVzaXMgYW5kIHJlcGVhdCB0aGUgcHJvY2Vzcy4NCg0KRGF0YSBzY2llbnRpc3RzIGNhbiB1c2UgYSB2YXJpZXR5IG9mIHRvb2xzIGFuZCB0ZWNobmlxdWVzIHRvIGV4cGxvcmUsIHZpc3VhbGl6ZSwgYW5kIG1hbmlwdWxhdGUgZGF0YS4gT25lIG9mIHRoZSBtb3N0IGNvbW1vbiB3YXlzIGluIHdoaWNoIGRhdGEgc2NpZW50aXN0cyB3b3JrIHdpdGggZGF0YSBpcyB0byB1c2UgdGhlIFIgbGFuZ3VhZ2UgYW5kIHNvbWUgc3BlY2lmaWMgcGFja2FnZXMgZm9yIGRhdGEgcHJvY2Vzc2luZy4NCg0KVGhlIGJlc3Qgd2F5IHRvIGxlYXJuIGFib3V0IGV4cGxvcmluZyBhbmQgcHJlcGFyaW5nIGRhdGEgaXMgdG8gdHJ5IGl0IGZvciB5b3Vyc2VsZiwgc28gdGhhdCdzIHdoYXQgeW91J2xsIGRvIGluIHRoaXMgZXhlcmNpc2UuDQoNCiMjIDEuIEdldHRpbmcgdG8ga25vdyBSIC4uLiBhIGxpdHRsZQ0KDQpJJ3ZlIGhlYXJkIFIgZGVzY3JpYmVkIGFzIHF1aXJreS4gQnV0IHF1aXJreSBpcyBnb29kLCBpdCBtZWFucyBpdCdzIGEgbGl0dGxlIGJpdCBvdXQgb2YgdGhlIG5vcm0uIEFuZCBJIHRoaW5rIHRoYXQncyB3aGF0IG1ha2VzIFIsIFIuIFNvIHRvIGVhc2VuIHRoaW5ncyB1cCwgd2UnbGwgYnJpZWZseSBkaXNjdXNzIHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IGZhbWlseSBvZiBkYXRhIHR5cGVzIGluIGJhc2UgUjogYHZlY3RvcnNgLg0KDQo+IEluIFIsICoqYXNzaWdubWVudCoqIHN0YXRlbWVudHMgaGF2ZSB0aGUgZm9sbG93aW5nIGZvcm06DQo+DQo+IGBvYmplY3RfbmFtZSA8LSB2YWx1ZWANCj4NCj4gYD1gIHdpbGwgd29yayBpbiBwbGFjZSBvZiBgPC1gLCBidXQgaXQgd2lsbCBjYXVzZSBjb25mdXNpb24gbGF0ZXIuIFVzZSB0aGUga2V5Ym9hcmQgc2hvcnRjdXQ6IGBBbHRgICsgYC1gICh0aGUgbWludXMgc2lnbikgdG8gcG9vZiB1cCB0aGUgYXNzaWdubWVudCBvcGVyYXRvciwgXDwtICwgaW4gYSBmbGFzaC4g8J+Sqw0KDQojIyMgQXRvbWljIHZlY3RvcnMNCg0KVGhlIGJhc2ljIGRhdGEgc3RydWN0dXJlIGluIFIgaXMgdGhlIFt2ZWN0b3JdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdmVjdG9ycy5odG1sP3E9bGlzdCN2ZWN0b3JzKSAoUidzIDFEIGRhdGEgc3RydWN0dXJlKS4gVmVjdG9ycyBjb21lIGluIHR3byBmbGF2b3VyczogKmF0b21pYyB2ZWN0b3JzKiBhbmQgKmxpc3RzKi4gVGhlIHR3byBkaWZmZXIgaW4gdGhlIHR5cGVzIG9mIHRoZWlyIGVsZW1lbnRzOiBhbGwgZWxlbWVudHMgb2YgYW4gYXRvbWljIHZlY3RvciBtdXN0IGJlIHRoZSBzYW1lIHR5cGUsIHdoZXJlYXMgdGhlIGVsZW1lbnRzIG9mIGEgbGlzdCBjYW4gaGF2ZSBkaWZmZXJlbnQgdHlwZXMuIFRoZXJlIGFyZSBmb3VyIHByaW1hcnkgdHlwZXMgb2YgYXRvbWljIHZlY3RvcnM6ICpsb2dpY2FsKiwgKmludGVnZXIqLCAqZG91YmxlKiwgYW5kICpjaGFyYWN0ZXIqICh3aGljaCBjb250YWlucyBzdHJpbmdzKS4NCg0KVmVjdG9ycyBhcmUgY3JlYXRlZCB1c2luZyB1c2UgW2BjKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL2MuaHRtbCksIHNob3J0IGZvciBjb21iaW5lLiBZb3UgY2FuIGRldGVybWluZSB0aGUgdHlwZSBvZiBhIHZlY3RvciB3aXRoIFtgdHlwZW9mKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL3R5cGVvZi5odG1sKSBhbmQgaG93IG1hbnkgZWxlbWVudHMgaXQgY29udGFpbnMgd2l0aCBbYGxlbmd0aCgpYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9sZW5ndGguaHRtbCkuDQoNCmBgYHtyfQ0KIyBMb2dpY2FsIHZlY3Rvcg0KbGdsX3ZhciA8LSBjKFRSVUUsIEZBTFNFKQ0KcHJpbnQobGdsX3ZhcikNCnR5cGVvZihsZ2xfdmFyKQ0KDQojIERvdWJsZSB2ZWN0b3I6IFRoZXNlIGFyZSBzcGVjaWFsIHZhbHVlcyBkZWZpbmVkIGJ5IHRoZSBmbG9hdGluZyBwb2ludCBzdGFuZGFyZC4NCmRibF92YXIgPC0gYygxLCAyLjUsIDQuNSkNCnByaW50KGRibF92YXIpDQoNCg0KIyBJbnRlZ2VyIHZlY3Rvcjogd3JpdHRlbiBzaW1pbGFybHkgdG8gZG91YmxlcyBidXQgbXVzdCBiZSBmb2xsb3dlZCBieSBMDQppbnRfdmFyIDwtIGMoMUwsIDZMLCAxMEwpDQpwcmludChpbnRfdmFyKQ0KbGVuZ3RoKGludF92YXIpDQoNCg0KIyBDaGFyYWN0ZXIgdmVjdG9yDQpjaHJfdmFyIDwtIGMoInRoZXNlIGFyZSIsICJzb21lIHN0cmluZ3MiKQ0KY2hyX3Zhcg0KYGBgDQoNCkFkZGluZyBhIGBkaW1gIGF0dHJpYnV0ZSB0byBhIHZlY3RvciBhbGxvd3MgaXQgdG8gYmVoYXZlIGxpa2UgYSAyLWRpbWVuc2lvbmFsICoqbWF0cml4Kiogb3IgYSBtdWx0aS1kaW1lbnNpb25hbCAqKmFycmF5KiouDQoNCllvdSBjYW4gY3JlYXRlIG1hdHJpY2VzIGFuZCBhcnJheXMgd2l0aCBbYG1hdHJpeCgpYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9tYXRyaXguaHRtbCkgYW5kIFtgYXJyYXkoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvYXJyYXkuaHRtbCksIG9yIGJ5IHVzaW5nIHRoZSBhc3NpZ25tZW50IGZvcm0gb2YgW2BkaW0oKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvZGltLmh0bWwpOiBXZSdsbCB1c2UgYGRpbWAgaW4gdGhpcyB3YWxrIHRocm91Z2guIFNlZSBob3cgdG8gdXNlIGBtYXRyaXhgIG9yIGBhcnJheWAgYnkgdHlwaW5nIGA/bWF0cml4YCBvciBgP2FycmF5YCBvbiB5b3VyIFJTdHVkaW8gY29uc29sZS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIFNlcXVlbmNlIG9mIGludGVnZXJzIHN0YXJ0aW5nIGZyb20gMSB0byA2DQppbnRfc2VxIDwtIDE6Ng0KYGBgDQoNCkZvciBleGFtcGxlLCB5b3UgY2FuIHJlb3JnYW5pemUgYGludF9zZXFgIGludG8gYSAyIMOXIDMgbWF0cml4ICh3aGljaCBoYXMgMiByb3dzIGFuZCAzIGNvbHVtbnMpOg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmRpbShpbnRfc2VxKSA8LSBjKDIsMykNCnByaW50KGludF9zZXEpDQpgYGANCg0KSW4gUiwgYGRhdGEgZnJhbWVzYCAod2hpY2ggd2UnbGwgY292ZXIgaW4ganVzdCBhIGxpbCBiaXQpLCBhcmUgdGhlIG1vc3QgY29tbW9uIHdheSBvZiBzdG9yaW5nIGFuZCBhbmFseXppbmcgZGF0YS4gSG93ZXZlciwgdmVjdG9ycyB0b28gY2FuIHN1ZmZpY2UgZm9yIGdlbmVyYWwgYW5kIGJhc2ljIGRhdGEgbWFuaXB1bGF0aW9uDQoNCldpdGggdGhhdCBzYWlkLCBsZXQncyB0YWtlIGF0b21pYyB2ZWN0b3JzIG91dCBmb3IgYSB0cnkhDQoNCldlJ2xsIHN0YXJ0IGJ5IGxvb2tpbmcgYXQgc29tZSBzaW1wbGUgZGF0YS4gU3VwcG9zZSBhIGNvbGxlZ2UgdGFrZXMgYSBzYW1wbGUgb2Ygc3R1ZGVudCBncmFkZXMgZm9yIGEgZ3JhZGVzIHNjaWVuY2UgY2xhc3M6DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBEZWZpbmluZyBhIHZlY3RvciBvZiBzdHVkZW50IGdyYWRlcw0KZ3JhZGVzIDwtICBjKDUwLCA1MCwgNDcsIDk3LCA0OSwgMywgNTMsIDQyLCAyNiwNCiAgICAgICAgICAgICA3NCwgODIsIDYyLCAzNywgMTUsIDcwLCAyNywgMzYsIDM1LA0KICAgICAgICAgICAgIDQ4LCA1MiwgNjMsIDY0KQ0KcHJpbnQoZ3JhZGVzKQ0KYGBgDQoNClNvLCBob3cgbWFueSBzdHVkZW50cyBkb2VzIHRoZSBzYW1wbGUgY29udGFpbiDwn5eSPw0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgRGV0ZXJtaW5pbmcgbnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSB2ZWN0b3INCmxlbmd0aChncmFkZXMpDQpgYGANCg0KIyMjICoqSW5kZXhpbmcgVmVjdG9ycyoqDQoNCk9mdGVudGltZXMgd2UgbWF5IHdhbnQgdG8gYWNjZXNzIG9ubHkgcGFydCBvZiBhIHZlY3Rvciwgb3IgcGVyaGFwcyBhbiBpbmRpdmlkdWFsIGVsZW1lbnQuIFRoaXMgaXMgY2FsbGVkICppbmRleGluZyogYW5kIGlzIGFjY29tcGxpc2hlZCB3aXRoIHNxdWFyZSBicmFja2V0cywgYFtdYC4gUiBoYXMgYSB2ZXJ5IGZsZXhpYmxlIHN5c3RlbSB0aGF0IGdpdmVzIHVzIHNldmVyYWwgY2hvaWNlcyBvZiBpbmRleDoNCg0KLSAgIFBhc3NpbmcgYSB2ZWN0b3Igb2YgcG9zaXRpdmUgbnVtYmVycyByZXR1cm5zIHRoZSBzbGljZSBvZiB0aGUgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIGVsZW1lbnRzIGF0IHRob3NlIGxvY2F0aW9ucy4gVGhlIGBmaXJzdGAgcG9zaXRpb24gaXMgYDFgIChub3QgMCwgYXMgaW4gc29tZSBvdGhlciBsYW5ndWFnZXMpLg0KDQotICAgUGFzc2luZyBhIHZlY3RvciBvZiBgbmVnYXRpdmUgbnVtYmVyc2AgcmV0dXJucyB0aGUgc2xpY2Ugb2YgdGhlIHZlY3RvciBjb250YWluaW5nIHRoZSBlbGVtZW50cyBldmVyeXdoZXJlICpleGNlcHQqIGF0IHRob3NlIGxvY2F0aW9ucy4NCg0KTGV0J3MgZ2V0IHRoZSBmaXJzdCBhbmQgc2l4dGggZ3JhZGU6DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyB3ZSBzaG91bGQgZXhwZWN0IDUwICYgMw0KZ3JhZGVzW2MoMSw2KV0NCmBgYA0KDQpQZXJmZWN0ISBOb3cgbGV0J3MgZ2V0IGEgdmVjdG9yIG9mIGFsbCBncmFkZXMgZXhjZXB0IGZvciB0aGUgMXN0IGFuZCB0aGUgNnRoIHN0dWRlbnQuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyB3ZSBzaG91bGQgZXhwZWN0IGFsbCBvdGhlciBncmFkZXMgZXhjZXB0IGF0IGluZGV4IDEgYW5kIDYNCmdyYWRlc1stYygxLDYpXQ0KYGBgDQoNCkFscmlnaHQsIG5vdyB3ZSBrbm93IG91ciB3YXkgYXJvdW5kIHZlY3RvcnMsIGVub3VnaCB0byBrbm9jayBvdXQgdGhpcyBtb2R1bGUg8J+Sqi4NCg0KPiBUaGUgY2hhcHRlciBbVmVjdG9yc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei92ZWN0b3JzLmh0bWwpIGluIGBSIGZvciBEYXRhIFNjaWVuY2VgIHdvdWxkIGJlIGEgZ29vZCBwbGFjZSB0byBnZXQgeW91IHN0YXJ0ZWQgb24gYWxsIHRoaW5ncyBhdG9taWMgdmVjdG9ycywgbGlzdHMgKHdoaWNoIHdlIGRpZG4ndCBjb3ZlciksIGFuZCBhIHdob2xlIGxvdCBvbiBiYXNlIFIgZGF0YSBzdHJ1Y3R1cmVzISBCZSBzdXJlIHRvIGNoZWNrIGl0IG91dC4NCg0KSXQncyB0aW1lIHdlIHBlcmZvcm1lZCBzb21lIGFuYWx5c2lzIG9mIHRoZSBncmFkZXMgZGF0YS4gRm9yIGEgZmlyc3QsIHdlIGNhbiBmaW5kIHRoZSBzaW1wbGUgYXZlcmFnZSBncmFkZSAoaW4gb3RoZXIgd29yZHMsIHRoZSAqbWVhbiogZ3JhZGUgdmFsdWUpLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCm1lYW4oZ3JhZGVzKQ0KYGBgDQoNClNvIHRoZSBtZWFuIGdyYWRlIGlzIGp1c3QgYXJvdW5kIDUwIC0gbW9yZSBvciBsZXNzIGluIHRoZSBtaWRkbGUgb2YgdGhlIHBvc3NpYmxlIHJhbmdlIGZyb20gMCB0byAxMDAuDQoNCiMjIyAqKkNvbWJpbmluZyB2ZWN0b3JzKioNCg0KTGV0J3MgYWRkIGEgc2Vjb25kIHNldCBvZiBkYXRhIGZvciB0aGUgc2FtZSBzdHVkZW50cywgdGhpcyB0aW1lIHJlY29yZGluZyB0aGUgdHlwaWNhbCBudW1iZXIgb2YgaG91cnMgcGVyIHdlZWsgdGhleSBkZXZvdGVkIHRvIHN0dWR5aW5nLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgRGVmaW5pbmcgYSB2ZWN0b3Igb2Ygc3R1ZHkgaG91cnMNCnN0dWR5X2hvdXJzIDwtIGMoMTAuMCwgMTEuNSwgOS4wLCAxNi4wLCA5LjI1LCAxLjAsIDExLjUsIDkuMCwNCiAgICAgICAgICAgICAgICAgOC41LCAxNC41LCAxNS41LCAxMy43NSwgOS4wLCA4LjAsIDE1LjUsIDguMCwNCiAgICAgICAgICAgICAgICAgOS4wLCA2LjAsIDEwLjAsIDEyLjAsIDEyLjUsIDEyLjApDQpgYGANCg0KV2UgY2FuIGNyZWF0ZSBhIDItZGltZW5zaW9uYWwgKiptYXRyaXgqKiBmcm9tIGBncmFkZXNgIGFuZCBgc3R1ZHlfaG91cnNgIGJ5IGNvbWJpbmluZyB0aGUgdmVjdG9ycyBieSBjb2x1bW5zIHVzaW5nIGBjYmluZCgpYA0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQ3JlYXRlIGEgMkQgbWF0cml4DQpzdHVkZW50X2RhdGEgPC0gY2JpbmQoc3R1ZHlfaG91cnMsIGdyYWRlcykNCnByaW50KHN0dWRlbnRfZGF0YSkNCmBgYA0KDQpCZWZvcmUgZ29pbmcgaW50byBhbnl0aGluZyBjb21wbGV4LCB3ZSBtaWdodCB3YW50IHRvIGZpbmQgb3V0IHRoZSBkaW1lbnNpb25zIG9mIG91ciBvYmplY3QuIFR1cm5zIG91dCwgYGRpbSgpYCBjYW4gYWxzbyBoZWxwIHlvdSByZXRyaWV2ZSB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgb2YgYW4gb2JqZWN0Lg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgRGltZW5zaW9uIG9mIHRoZSByZXN1bHRpbmcgbWF0cml4IFtSb3dzLCBDb2x1bW5zXQ0KZGltKHN0dWRlbnRfZGF0YSkNCmBgYA0KDQpBcyBleHBlY3RlZCDwn6SpLCB0aGUgbWF0cml4IGNvbnRhaW5zIDIgY29sdW1ucyBlYWNoIHdpdGggMjIgcm93cy4NCg0KV2Ugc3Vic2V0IG1hdHJpY2VzIHVzaW5nIHRoZSBub3RhdGlvbiBgW3Jvd19udW1iZXIsY29sdW1uX251bWJlcl1gDQoNClNvLCBmb3IgaW5zdGFuY2UsIGlmIHdlIHdhbnRlZCB0byBhY2Nlc3MgdGhlIGZpcnN0IGVsZW1lbnQgaW4gdGhlIGluIHRoZSBmaXJzdCBjb2x1bW4gKHN0dWR5X2hvdXJzKSwgd2Ugd291bGQgZG8gaXQgYXMgYmVsb3c6DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBTaG93IHRoZSBmaXJzdCBlbGVtZW50IG9mIHRoZSBmaXJzdCBjb2x1bW4NCnN0dWRlbnRfZGF0YVsxLCAxXQ0KYGBgDQoNCkFjY2Vzc2luZyBhbGwgdGhlIGVsZW1lbnRzIGluIHRoZSBmaXJzdCByb3cgd291bGQgYWxzbyBmb2xsb3cgYSBzaW1pbGFyIGZhc2hpb246DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBTaG93IGFsbCBjb2x1bW5zIGluIHRoZSBmaXJzdCByb3cNCnN0dWRlbnRfZGF0YVsxLF0NCmBgYA0KDQpOb3cgd2UgaGF2ZSBhIG11bHRpZGltZW5zaW9uYWwgYXJyYXkgY29udGFpbmluZyBib3RoIHRoZSBzdHVkZW50J3Mgc3R1ZHkgdGltZSBhbmQgZ3JhZGUgaW5mb3JtYXRpb24sIHdoaWNoIHlvdSBjYW4gdXNlIHRvIGNvbXBhcmUgZGF0YS4gRm9yIGV4YW1wbGUsIGhvdyBkb2VzIHRoZSBtZWFuIHN0dWR5IHRpbWUgY29tcGFyZSB0byB0aGUgbWVhbiBncmFkZT8NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEdldCB0aGUgbWVhbiB2YWx1ZSBvZiBlYWNoIHN1YnZlY3RvciBpbiB0aGUgbWF0cml4DQphdmdfc3R1ZHkgPC0gbWVhbihzdHVkZW50X2RhdGFbLCAxXSkNCmF2Z19ncmFkZSA8LSBtZWFuKHN0dWRlbnRfZGF0YVssIDJdKQ0KDQojIEZvcm1hdCBzdHJpbmdzIHVzaW5nIHBhY2thZ2UgJ2dsdWUnDQpsaWJyYXJ5KGdsdWUpDQpnbHVlKA0KICAnQXZlcmFnZSBzdHVkeSBob3Vyczoge2Zvcm1hdChyb3VuZChhdmdfc3R1ZHksIDIpLCBuc21hbGwgPSAyKX0NCiAgQXZlcmFnZSBncmFkZToge2Zvcm1hdChyb3VuZChhdmdfZ3JhZGUsIDIpLCBuc21hbGwgPSAyKX0nKQ0KYGBgDQoNCiMjIyAqKkFuYWx5c2lzIHVzaW5nIGF0b21pYyB2ZWN0b3JzIGFuZCBtYXRyaWNlcz8gTm90IHNvIGZhc3QuLi4qKiDwn6S3DQoNCk9uZSBzaG9ydGNvbWluZyBvZiBhdG9taWMgdmVjdG9ycyBhbmQgbWF0cmljZXMgaXMgdGhhdCB0aGV5IGNhbiBvbmx5IGhvbGQgb25lIG1vZGUgb2YgZGF0YTsgdGhleSBkb24ndCBhbGxvdyB1cyB0byBtaXgsIHNheSwgbnVtYmVycyBhbmQgY2hhcmFjdGVyIHN0cmluZ3MuIElmIHdlIHRyeSB0byBkbyBzbywgaXQgd2lsbCBjaGFuZ2UgdGhlIG1vZGUgb2YgdGhlIG90aGVyIGVsZW1lbnRzIGluIHRoZSB2ZWN0b3IgdG8gY29uZm9ybS4gTGV0J3Mgc2VlIHRoaXMgaW4gYWN0aW9uOg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgTW9kaWZ5aW5nIGdyYWRlcyBieSBhZGRpbmcgYSBjaGFyYWN0ZXIgc3RyaW5nICIyMiINCmdyYWRlc19tb2QxIDwtIGMoZ3JhZGVzLCAiMjIiKQ0KDQojIEFkZGluZyBhIGNoYXJhY3RlciB0cmFuZm9ybXMgYGdyYWRlc2AgdG8gYSBjaGFyYWN0ZXIgdmVjdG9yDQp0eXBlb2YoZ3JhZGVzX21vZDEpDQpgYGANCg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGZpbmUgcHJpbnQhDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBNb2RpZmllZCBncmFkZXMNCnByaW50KGdyYWRlc19tb2QxKQ0KDQojIE9yaWdpbmFsIGdyYWRlcw0KcHJpbnQoZ3JhZGVzKQ0KYGBgDQoNCkZyb20gdGhlIGBwcmludCgpYCBvdXRwdXQsIHdlIGNhbiBub3RpY2UgdGhhdCB0aGUgbnVtYmVycyBnb3QgY2hhbmdlZCB0byBjaGFyYWN0ZXIgdmFsdWVzIHNvIHRoYXQgdGhlIHZlY3RvciBjb3VsZCBhY2NvbW1vZGF0ZSBhbGwgdGhlIGVsZW1lbnRzIHdlIHBhc3NlZCB0byB0aGUgYGMoKWAgZnVuY3Rpb24uIEFzIHN1Y2gsIGZ1cnRoZXIgYW5hbHlzaXMgbGlrZSBmaW5kaW5nIHRoZSBtZWFuIHdvdWxkIGJlIHF1aXRlIGEgcGFpbiB0byBzYXkgdGhlIGxlYXN0IQ0KDQpgYGB7cn0NCm1lYW4oZ3JhZGVzX21vZDEpDQpgYGANCg0KSW4gUiwgYSBzcGVjaWFsIG9iamVjdCBrbm93biBhcyBhIGBkYXRhIGZyYW1lYCByZXNvbHZlcyB0aGlzIHByb2JsZW0g8J+muC4NCg0KIyMgMi4gQSBnbGltcHNlIGludG8gdGhlIHRpZHl2ZXJzZSAhW10oaW1hZ2VzL3RpZHl2ZXJzZS5wbmcpe3dpZHRoPSIyNSJ9DQoNCiMjIyBEYXRhIGZyYW1lcyBhbmQgdGliYmxlcw0KDQpEYXRhIGZyYW1lcyBhcmUgb25lIG9mIHRoZSBiaWdnZXN0IGFuZCBtb3N0IGltcG9ydGFudCBpZGVhcyBpbiBSLCBhbmQgb25lIG9mIHRoZSB0aGluZ3MgdGhhdCBtYWtlIFIgZGlmZmVyZW50IGZyb20gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLiBBIGRhdGEgZnJhbWUgaXMgYSByZWN0YW5ndWxhciBjb2xsZWN0aW9uIG9mIHZhcmlhYmxlcyAoaW4gdGhlIGNvbHVtbnMpIGFuZCBvYnNlcnZhdGlvbnMgKGluIHRoZSByb3dzKS4NCg0KVGhleSBhcmUgY29uc2lkZXJlZCBhcyBmYXIgYW5kIGF3YXkgdGhlIG1vc3QgdXNlZnVsIHN0b3JhZ2Ugc3RydWN0dXJlIGZvciBkYXRhIGFuYWx5c2lzLg0KDQpEYXRhIGZyYW1lcyBjYW4gYmUgdGhvdWdodCBhcyBSJ3MgZXF1aXZhbGVudCB0byB0aGUgRXhjZWwgc3ByZWFkc2hlZXQgYmVjYXVzZSBpdCBzdG9yZXMgZGF0YSBpbiBhIHNpbWlsYXIgZm9ybWF0LiBBIGhpZ2gtbGV2ZWwgb3ZlcnZpZXcgb2YgZGF0YSBmcmFtZXMgaXM6DQoNCi0gICBEYXRhIGZyYW1lcyBncm91cCB2ZWN0b3JzIHRvZ2V0aGVyIGludG8gYSB0d28tZGltZW5zaW9uYWwgdGFibGUuDQoNCi0gICBFYWNoIHZlY3RvciBiZWNvbWVzIGEgY29sdW1uIGluIHRoZSB0YWJsZS4NCg0KLSAgIEVhY2ggY29sdW1uIG9mIGEgZGF0YSBmcmFtZSBjYW4gYmUgYSBkaWZmZXJlbnQgZGF0YSB0eXBlLg0KDQotICAgRXZlcnkgY29sdW1uIGluIGEgZGF0YSBmcmFtZSBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aC4NCg0KUiBpcyBhbiBvbGQgbGFuZ3VhZ2UgYW5kIG92ZXIgdGhlIGNvdXJzZSBvZiB0aW1lLCB0aGUgd2F5cyAoYW5kIHJlYXNvbnMpIHRoYXQgcGVvcGxlIHVzZSBSIGhhdmUgY2hhbmdlZCwgYW5kIHNvbWUgb2YgdGhlIGRlc2lnbiBkZWNpc2lvbnMgdGhhdCBtYWRlIHNlbnNlIGF0IHRoZSB0aW1lIGRhdGEgZnJhbWVzIHdlcmUgY3JlYXRlZCBub3cgY2F1c2UgZnJ1c3RyYXRpb24uDQoNClRoaXMgZnJ1c3RyYXRpb24gbGVhZCB0byB0aGUgY3JlYXRpb24gb2YgdGhlIGB0aWJibGVgLCBhIG1vZGVybiByZWltYWdpbmluZyBvZiB0aGUgZGF0YSBmcmFtZSwgdGhhdCBrZWVwcyB3aGF0IHRpbWUgaGFzIHByb3ZlbiB0byBiZSBlZmZlY3RpdmUgYW5kIHJldGlyaW5nIHdoYXQgaXMgbm90Lg0KDQpUaWJibGUgaXMgYSBwYXJ0IG9mIHRoZSAqKnRpZHl2ZXJzZSoqLCBhbiBvcGluaW9uYXRlZCBbKipjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMqKl0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9wYWNrYWdlcykgLCB0aGF0IG1ha2UgdGhlIGRhdGEgc2NpZW5jZSB3b3JrZmxvdyAoc3VjaCBhcyBpbXBvcnRpbmcsIHdyYW5naW5nLCB2aXN1YWxpc2luZywgcHJvZ3JhbW1pbmcgYW5kIG1vZGVsbGluZykgZmFzdGVyLCBlYXNpZXIgYW5kIG1vcmUgZnVuLg0KDQo+ICpUaGUgZWFzaWVzdCB3YXkgdG8gZ2V0IHRpYmJsZSBvciBhbnkgb3RoZXIgcGFja2FnZSBpbiB0aGUgdGlkeXZlcnNlIGlzIHRvIGluc3RhbGwgdGhlIHdob2xlIHRpZHl2ZXJzZToqDQo+DQo+IGBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKWANCg0KV2UnbGwgY3JlYXRlIGEgdGliYmxlIGJ5IHNwZWNpZnlpbmcgYSB2ZWN0b3Igb2Ygc3R1ZGVudCBuYW1lcywgdGhlbiB3cmFwIGl0IHRvZ2V0aGVyIHdpdGggdGhlIGV4aXN0aW5nIHN0dWR5X2hvdXJzIGFuZCBncmFkZXMgdmVjdG9ycy4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIExvYWQgdGhlIGNvcmUgdGlkeXZlcnNlIGFuZCBtYWtlIGl0IGF2YWlsYWJsZSBpbiB5b3VyIGN1cnJlbnQgUiBzZXNzaW9uLg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMgQnVpbGQgYSBkYXRhIGZyYW1lIGRmX3N0dWRlbnRzIHVzaW5nIHRpYmJsZSgpDQpkZl9zdHVkZW50cyA8LSB0aWJibGUoDQogIA0KICBOYW1lID0gYygnRGFuJywgJ0pvYW5uJywgJ1BlZHJvJywgJ1Jvc2llJywgJ0V0aGFuJywgJ1ZpY2t5JywNCiAgICAgICAgICAgJ0ZyZWRlcmljJywgJ0ppbW1pZScsICdSaG9uZGEnLCAnR2lvdmFubmknLA0KICAgICAgICAgICAnRnJhbmNlc2NhJywgJ1JhamFiJywgJ05haXlhbmEnLCAnS2lhbicsICdKZW5ueScsDQogICAgICAgICAgICdKYWtlZW0nLCdIZWxlbmEnLCdJc21hdCcsJ0FuaWxhJywnU2t5ZScsJ0RhbmllbCcsDQogICAgICAgICAgICdBaXNoYScpLA0KICANCiAgU3R1ZHlIb3VycyA9IHN0dWR5X2hvdXJzLA0KICANCiAgR3JhZGUgPSBncmFkZXMpDQoNCnByaW50KGRmX3N0dWRlbnRzKQ0KDQpgYGANCg0KIyMjIFRpZHl2ZXJzZSdzIGRwbHlyICFbXShpbWFnZXMvZHBseXIucG5nKXt3aWR0aD0iMjUifQ0KDQpgZHBseXJgLCBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UsIGlzIGEgZ3JhbW1hciBvZiBkYXRhIG1hbmlwdWxhdGlvbiwgcHJvdmlkaW5nIGEgY29uc2lzdGVudCBzZXQgb2YgdmVyYnMgdGhhdCBtYWtlcyBpdCBlYXNpZXIgZm9yIHVzIHRvIHdvcmsgd2l0aCBkYXRhLg0KDQpbZHBseXI6OnNsaWNlKCldKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc2xpY2UuaHRtbCkgYWxsb3dzIHlvdSB0byBzZWxlY3QsIHJlbW92ZSwgYW5kIGR1cGxpY2F0ZSByb3dzIGJ5IHRoZWlyIChpbnRlZ2VyKSBsb2NhdGlvbnMuDQoNCkxldCdzIHN0YXJ0IGJ5IHJldHJpZXZpbmcgZGF0YSBmb3IgYSBzcGVjaWZpYyBpbmRleC4gUmVtZW1iZXIgaW5kZXhpbmcgaW4gUiBzdGFydHMgYXQgYDFgLg0KDQpXZSBjYW4gYWxzbyBnZXQgdGhlIGRhdGEgYXQgYSByYW5nZSBvZiBpbnRlZ2VyIGxvY2F0aW9ucywgbGlrZSB0aGlzOg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgR2V0IHRoZSByb3dzIHdpdGggaW5kZXggdmFsdWVzIGZyb20gMSB0byA2DQpzbGljZShkZl9zdHVkZW50cywgMTo2KQ0KYGBgDQoNCk9mdGVuLCB0aGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBhY2hpZXZlIHRoZSBzYW1lIHJlc3VsdHMuIEhlcmUgYXJlIG90aGVyIHN1YnRsZSB3YXlzIG9mIHN1YnNldHRpbmcgZGF0YSBmcmFtZXM6DQoNCioqVXNpbmcgW10gdG8gc3Vic2V0IHJvd3MgNToxMCBhbmQgY29sdW1ucyAxJjMqKg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmRmX3N0dWRlbnRzWzU6MTAsIGMoMSwgMyldDQpgYGANCg0KKipVc2luZyBgJT4lYCwgYGZpbHRlcmAsIGFuZCBgc2VsZWN0YCB0byBzdWJzZXQgcm93cyA1OjEwIGFuZCBjb2x1bW5zIDEmMyoqDQoNCkxldCdzIGJydXNoIG92ZXIgc29tZSBuZXcgY29uY2VwdHMgd2UnbGwgZW5jb3VudGVyIGluIHRoZSBleGFtcGxlIGJlbG93Og0KDQotICAgSW50cm9kdWNpbmcgdGhlIHBpcGUgb3BlcmF0b3IgKGAlPiVgKTogcGVyZm9ybXMgb3BlcmF0aW9ucyBpbiBsb2dpY2FsIHNlcXVlbmNlIGJ5IHBhc3NpbmcgYW4gb2JqZWN0IGZvcndhcmQgaW50byBhIGZ1bmN0aW9uIG9yIGNhbGwgZXhwcmVzc2lvbi4gWW91IGNhbiB0aGluayBvZiB0aGUgcGlwZSBvcGVyYXRvciBhcyBzYXlpbmcgImFuZCB0aGVuIiBpbiB5b3VyIGNvZGUuDQoNCi0gICBbYGRwbHlyOjpmaWx0ZXIoKWBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZmlsdGVyLmh0bWwpOiBjcmVhdGVzIGEgc3Vic2V0IG9mIHRoZSBkYXRhIG9ubHkgY29udGFpbmluZyAqKnJvd3MqKiB0aGF0IHNhdGlzZnkgeW91ciBjb25kaXRpb25zLg0KDQotICAgW2BkcGx5cjo6c2VsZWN0KClgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NlbGVjdC5odG1sKTogaGVscHMgeW91IHBpY2sgd2hpY2ggKipjb2x1bW5zKiogdG8ga2VlcCBvciBleGNsdWRlLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgVGVsbCBSIHRoZSBkYXRhIGZyYW1lDQojIEFORCBUSEVODQojIFVzZSBmaWx0ZXIoKSB0byBnZXQgcm93cyB3aG9zZSByb3dfbnVtYmVyKCkgaXMgYmV0d2VlbiA1OjEwDQojIEFORCBUSEVODQojIHVzZSBzZWxlY3QoKSB0byBzZWxlY3QgY29sdW1ucyAxJjMNCmRmX3N0dWRlbnRzICU+JSANCiAgZmlsdGVyKGJldHdlZW4ocm93X251bWJlcigpLCA1LCAxMCkpICU+JSANCiAgc2VsZWN0KDEsIDMpDQpgYGANCg0KVm9pbGEhIFRoZXJlIHlvdSBoYXZlIGl0ISBXaXRoIHRoZSBsb25nZXZpdHkgYW5kIGV2b2x1dGlvbiBSIGhhcyBoYWQgc2luY2UgaXRzIGluY2VwdGlvbiAyMCB5ZWFycyBhZ28sIHlvdSBjYW4gYmUgYXNzdXJlZCBvZiBtdWx0aXBsZSBzb2x1dGlvbnMgdG8gYSB0YXNrLiBBbmQgdGhhdCBpbiBpdHNlbGYgaXMgYW1hemluZyENCg0KU29tZXRpbWVzLCB5b3UgbWF5IHdhbnQgdG8gZmluZCB3aGljaCByb3cgaW5kZXggaW4gYSBjb2x1bW4gbWF0Y2hlcyBhIHBhcnRpY3VsYXIgZXhwcmVzc2lvbi4gVGhlIGZvbGxvd2luZyB0cmljayB3aWxsIGRvOg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgd2hpY2ggKCk6IEdpdmVzIHdoaWNoIGluZGljZXMgYXJlIFRSVUUNCiMgYCRgOiB1c2VkIHRvIGFjY2VzcyBjb2x1bW4gdmFsdWVzIGFzIGEgdmVjdG9yDQoNCndoaWNoKGRmX3N0dWRlbnRzJE5hbWUgPT0gIkFpc2hhIikNCmBgYA0KDQpBIGRwbHlyIGFwcHJvYWNoIG9mIHRoZSBzYW1lOg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgU2VsZWN0IHRoZSBjb2x1bW4gYE5hbWVgIHRoZW4gZmluZCB3aGljaCByb3cgaW5kZXggY29udGFpbnMgIkFpc2hhIg0Kd2hpY2goc2VsZWN0KGRmX3N0dWRlbnRzLCBOYW1lKSA9PSAiQWlzaGEiKQ0KYGBgDQoNClBpcGUg8J+agCB5b3VyIHdheSBpbnRvIGBkcGx5cmAgdXNpbmcgdGhlIFtkYXRhIHRyYW5zZm9ybWF0aW9uIGNoYXB0ZXJdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdHJhbnNmb3JtLmh0bWwpIGluIGBSIGZvciBkYXRhIHNjaWVuY2VgLg0KDQojIyAzLiBMb2FkaW5nIGRhdGEgZnJvbSBhIGZpbGUNCg0KV2UgY29uc3RydWN0ZWQgYSBkYXRhIGZyYW1lIGZyb20gc29tZSBleGlzdGluZyB2ZWN0b3JzIHdoaWNoIHdlIHR5cGVkIGJ5IGhhbmQgaW4gb3VyIGNvbnNvbGUgb3IgbWFya2Rvd24uIEhvd2V2ZXIsIHR5cGluZyBpbnZpdGVzIHR5cG9zIGFuZCBlcnJvcnMuIEluIG1hbnkgcmVhbC13b3JsZCBzY2VuYXJpb3MsIGRhdGEgaXMgbG9hZGVkIGZyb20gc291cmNlcyBzdWNoIGFzIGZpbGVzLiBZb3UgY2FuIHRoZW4gYXNrIFIgdG8gcmVhZCB0aGUgZmlsZSBhbmQgc3RvcmUgdGhlIGNvbnRlbnRzIGFzIGFuIG9iamVjdC4NCg0KTGV0J3MgcmVwbGFjZSB0aGUgc3R1ZGVudCBncmFkZXMgZGF0YSBmcmFtZSB3aXRoIHRoZSBjb250ZW50cyBvZiBhIHRleHQgZmlsZSBmcm9tIHRoZSBbb3JpZ2luYWwgcmVwb10oaHR0cHM6Ly9naXRodWIuY29tL01pY3Jvc29mdERvY3MvbWwtYmFzaWNzKSBvZiB0aGlzIGNvdXJzZS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEdldCB0aGUgdXJsIG9mIHRoZSByYXcgdmVyc2lvbiBvZiB0aGUgZ3JhZGVzLmNzdiBkYXRhDQp1cmxfZGF0YSA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL01pY3Jvc29mdERvY3MvbWwtYmFzaWNzL21hc3Rlci9kYXRhL2dyYWRlcy5jc3YiDQoNCmRmX3N0dWRlbnRzIDwtIHJlYWRfY3N2KHVybF9kYXRhKSAlPiUNCiAgICAgICAgICAgICAgICAjIHR1cm5zIGFuIGV4aXN0aW5nIG9iamVjdCBpbnRvIGEgdGliYmxlDQogICAgICAgICAgICAgICAgICBhc190aWJibGUoKQ0KDQpgYGANCg0KTm90aWNlIHRoYXQgdGhlIGRhdGEgYXJlIGFscmVhZHkgaW4gKnRpZHkgZm9ybWF0KiAtIG1lYW5pbmcgdGhhdDoNCg0KLSAgIEVhY2ggdmFyaWFibGUgaXMgYSBjb2x1bW4NCg0KLSAgIEVhY2ggb2JzZXJ2YXRpb24gaXMgYSByb3cNCg0KLSAgIEVhY2ggdmFsdWUgaXMgaW4gaXRzIG93biBjZWxsDQoNCmBgYHtyIG5pY2VfdGFibGUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KZGZfc3R1ZGVudHMgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9tYXRlcmlhbChjKCJzdHJpcGVkIiwgImhvdmVyIikpDQoNCmBgYA0KDQpPaCDwn5ivISBMb29rIHdoYXQgd2UndmUgZ290IGhlcmU6IGBOQWBzIGFuZCB0aGV5IGJyaW5nIHVzIHRvIHRoZSBuZXh0IHBhcnQgb2Ygb3VyIGFkdmVudHVyZS4NCg0KIyMjIEhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzDQoNCk9uZSBvZiB0aGUgbW9zdCBjb21tb24gaXNzdWVzIGRhdGEgc2NpZW50aXN0cyBuZWVkIHRvIGRlYWwgd2l0aCBpcyBpbmNvbXBsZXRlIG9yIG1pc3NpbmcgZGF0YS4gUiByZXByZXNlbnRzIG1pc3NpbmcsIG9yIHVua25vd24gdmFsdWVzLCB3aXRoIHNwZWNpYWwgc2VudGluZWwgdmFsdWU6IGBOQWAgKE5vdCBBdmFpbGFibGUpLg0KDQpTbyBob3cgd291bGQgd2Uga25vdyB0aGF0IHRoZSBkYXRhIGZyYW1lIGNvbnRhaW5zIG1pc3NpbmcgdmFsdWVzPw0KDQotICAgT25lIHdheSB0byBrbm93IGlmIG91ciBkYXRhIGNvbnRhaW5zIGFueSBgTkFgcyBpbiB0d28gc2hha2VzIHdvdWxkIGJlIHRvIHVzZSB0aGUgZ2VuZXJpYyBmdW5jdGlvbiBgYW55TkFgDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KZGZfc3R1ZGVudHMgJT4lIA0KICBhbnlOQSgpDQpgYGANCg0KLSAgIEFub3RoZXIgd2F5IHdvdWxkIGJlIHRvIHVzZSB0aGUgZ2VuZXJpYyBmdW5jdGlvbiBgaXMubmEoKWAgd2hpY2ggaW5kaWNhdGVzIHdoaWNoIGluZGl2aWR1YWwgZWxlbWVudHMgYXJlIG1pc3Npbmcgd2l0aCBhIGxvZ2ljYWwgYFRSVUVgLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmlzLm5hKGRmX3N0dWRlbnRzKQ0KYGBgDQoNCk9rYXksIGdvdCB0aGUgam9iIGRvbmUgYnV0IHdpdGggYSBsYXJnZXIgZGF0YSBmcmFtZSwgaXQgd291bGQgYmUgaW5lZmZpY2llbnQgYW5kIHByYWN0aWNhbGx5IGltcG9zc2libGUgdG8gcmV2aWV3IGFsbCBvZiB0aGUgcm93cyBhbmQgY29sdW1ucyBpbmRpdmlkdWFsbHnwn5i0Lg0KDQotICAgQW5vdGhlciBtb3JlIGludHVpdGl2ZSB3YXkgd291bGQgYmUgdG8gZ2V0IHRoZSBzdW0gb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uLCBsaWtlIHRoaXM6DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KY29sU3Vtcyhpcy5uYShkZl9zdHVkZW50cykpDQpgYGANCg0KU28gbm93IHdlIGtub3cgdGhhdCB0aGVyZSdzIGBvbmVgIG1pc3NpbmcgdmFsdWUgaW4gdGhlICoqU3R1ZHlIb3VycyoqIGNvbHVtbiwgYW5kIGB0d29gIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAqKkdyYWRlKiogY29sdW1uLg0KDQpUbyBzZWUgdGhlbSBpbiBjb250ZXh0LCB3ZSBjYW4gZmlsdGVyIHRoZSBkYXRhIGZyYW1lIHRvIGluY2x1ZGUgb25seSByb3dzIHdoZXJlIGFueSBvZiB0aGUgY29sdW1ucyBoYXZlIGFuIGBOQWAuIFRoZSBjb2RlIGJlbG93IHJldHVybnMgdGhlIHJvd3Mgd2hlcmUgdGhlcmUgaXMgYW4gYE5BYCBpbiBhdCBsZWFzdCBvbmUgb2YgdGhlIGNvbHVtbi4NCg0KPiAqYGlmX2FueSgpYCBhcHBsaWVzIHRoZSBzYW1lIHByZWRpY2F0ZSBmdW5jdGlvbiB0byBhIHNlbGVjdGlvbiBvZiBjb2x1bW5zIGFuZCBjb21iaW5lcyB0aGUgcmVzdWx0cyBpbnRvIGEgc2luZ2xlIGxvZ2ljYWwgdmVjdG9yLioNCj4NCj4gKmBldmVyeXRoaW5nKClgIHNlbGVjdHMgYWxsIGNvbHVtbnMuKg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmRmX3N0dWRlbnRzICU+JSANCiAgZmlsdGVyKGlmX2FueShldmVyeXRoaW5nKCksIGlzLm5hKSkNCmBgYA0KDQpTbyBub3cgdGhhdCB3ZSd2ZSBmb3VuZCB0aGUgbnVsbCB2YWx1ZXMsIHdoYXQgY2FuIHdlIGRvIGFib3V0IHRoZW0/DQoNCk9uZSBjb21tb24gYXBwcm9hY2ggaXMgdG8gKmltcHV0ZSogcmVwbGFjZW1lbnQgdmFsdWVzLiBGb3IgZXhhbXBsZSwgaWYgdGhlIG51bWJlciBvZiBzdHVkeSBob3VycyBpcyBtaXNzaW5nLCB3ZSBjb3VsZCBqdXN0IGFzc3VtZSB0aGF0IHRoZSBzdHVkZW50IHN0dWRpZWQgZm9yIGFuIGF2ZXJhZ2UgYW1vdW50IG9mIHRpbWUgYW5kIHJlcGxhY2UgdGhlIG1pc3NpbmcgdmFsdWUgd2l0aCB0aGUgbWVhbiBzdHVkeSBob3Vycy4NCg0KVG8gZG8gdGhpcywgd2UgY2FuIHVzZSB0aGUgYHRpZHlyOjpyZXBsYWNlX25hYCB0aGF0IHJlcGxhY2VzIE5BcyB3aXRoIHNwZWNpZmllZCB2YWx1ZXMuDQoNCj4gKmB0aWR5cmAgaXMgYSBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UgYW5kIGl0cyBnb2FsIGlzIHRvIGhlbHAgeW91IGNyZWF0ZSAqKnRpZHkgZGF0YSoqLiBUaGUgY2hhcHRlciBbVGlkeSBkYXRhXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpZHktZGF0YS5odG1sI3RpZHktZGF0YSkgaW4gYFIgZm9yIERhdGEgU2NpZW5jZWAgd291bGQgYmUgYSBnb29kIHBsYWNlIHRvIGdldCB5b3Ugc3RhcnRlZCBvbiB0aGUgdGlkeWluZyB1cCBtZXNzeSBkYXRhc2V0cy4qDQoNCkFscmlnaHQsIGhvdyB3b3VsZCB3ZSBnbyBhYm91dCBtb2RpZnlpbmcgZXhpc3RpbmcgY29sdW1ucz8NCg0KTWVldCBgZHBseXI6Om11dGF0ZWAsIHRoZSBtYWdpY2FsIHdvcmQgdGhhdCBhbGxvd3MgdXMgdG8gbW9kaWZ5IG9yIGV2ZW4gY3JlYXRlIG5ldyBjb2x1bW5zLCB3aGlsZSBrZWVwaW5nIHRoZSBleGlzdGluZyBjb2x1bW5zLg0KDQohW0FydHdvcmsgYnkgXEBhbGxpc29uX2hvcnN0XShpbWFnZXMvZHBseXJfbXV0YXRlLnBuZyl7d2lkdGg9IjU2OSJ9DQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBSZXBsYWNlIE5BIGluIGNvbHVtbiBTdHVkeUhvdXJzIHdpdGggdGhlIG1lYW4gc3R1ZHkgaG91cnMNCmRmX3N0dWRlbnRzIDwtIGRmX3N0dWRlbnRzICU+JSANCiAgbXV0YXRlKFN0dWR5SG91cnMgPSByZXBsYWNlX25hKFN0dWR5SG91cnMsIG1lYW4oU3R1ZHlIb3VycywgbmEucm0gPSBUKSkpDQoNCiMgU2hvdyB0aGUgZGF0YSBmcmFtZQ0KZGZfc3R1ZGVudHMNCmBgYA0KDQpHb29kIGpvYiENCg0KQWx0ZXJuYXRpdmVseSwgaXQgbWlnaHQgYmUgaW1wb3J0YW50IHRvIGVuc3VyZSB0aGF0IHlvdSBvbmx5IHVzZSBkYXRhIHlvdSBrbm93IHRvIGJlIGFic29sdXRlbHkgY29ycmVjdDsgc28geW91IGNhbiBkcm9wIHJvd3MgdGhhdCBjb250YWluIG51bGwgdmFsdWVzIGJ5IHVzaW5nIGB0aWR5cjo6ZHJvcF9uYWAgbWV0aG9kLiBJbiB0aGlzIGNhc2UsIHdlJ2xsIHJlbW92ZSByb3dzIHdoZXJlIGFueSBvZiB0aGUgY29sdW1ucyBjb250YWluIE5BIHZhbHVlcy4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIERyb3AgTkFzDQpkZl9zdHVkZW50cyA8LSBkZl9zdHVkZW50cyAlPiUgDQogICAgICAgICAgICAgICAgICBkcm9wX25hKCkNCg0KZGZfc3R1ZGVudHMNCmBgYA0KDQpBbWF6aW5nISBPdXIgZGF0YSBpcyBtdWNoIG1vcmUgdGlkeS1yISDwn5KrDQoNCiMjIDQuIEV4cGxvcmUgZGF0YSBpbiB0aGUgZGF0YSBmcmFtZQ0KDQpOb3cgdGhhdCB3ZSd2ZSBjbGVhbmVkIHVwIHRoZSBtaXNzaW5nIHZhbHVlcywgd2UncmUgcmVhZHkgdG8gZXhwbG9yZSB0aGUgZGF0YSBpbiB0aGUgZGF0YSBmcmFtZS4gTGV0J3Mgc3RhcnQgYnkgY29tcGFyaW5nIHRoZSBtZWFuIHN0dWR5IGhvdXJzIGFuZCBncmFkZXMuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBHZXQgdGhlIG1lYW4gc3R1ZHkgaG91cnMgdXNpbmcgdGhlIGFjY2Vzc29yIGAkYCANCm1lYW5fc3R1ZHkgPC0gbWVhbihkZl9zdHVkZW50cyRTdHVkeUhvdXJzKQ0KDQoNCiMgR2V0IHRoZSBtZWFuIGdyYWRlIHNlcXVlbnRpYWxseSB1c2luZyBgICU+JSBgIGFuZCBkcGx5cjo6cHVsbA0KbWVhbl9ncmFkZSA8LSBkZl9zdHVkZW50cyAlPiUgDQogICAgICAgICAgICAgICAgcHVsbChHcmFkZSkgJT4lIA0KICAgICAgICAgICAgICAgICAgbWVhbigpDQoNCg0KIyBHbHVlIGl0IGFsbCB0b2dldGhlcg0KZ2x1ZSgNCiAgJ0F2ZXJhZ2Ugd2Vla2x5IHN0dWR5IGhvdXJzOiB7Zm9ybWF0KHJvdW5kKG1lYW5fc3R1ZHksIDIpLCBuc21hbGwgPSAyKX0NCiAgIEF2ZXJhZ2UgZ3JhZGU6IHtmb3JtYXQocm91bmQobWVhbl9ncmFkZSwgMiksIG5zbWFsbCA9IDIpfScNCikNCmBgYA0KDQpUaW1lIHRvIGRvIHNvbWUgYmFzaWMgZGF0YSB3cmFuZ2xpbmcgd2l0aCBkcGx5ciB2ZXJicw0KDQpXZSdsbCBzdGFydCB3aXRoIGZpbHRlcmluZyB0aGUgZGF0YSBmcmFtZSB0byBmaW5kIG9ubHkgdGhlIHN0dWRlbnRzIHdobyBzdHVkaWVkIGZvciBtb3JlIHRoYW4gdGhlIGF2ZXJhZ2UgYW1vdW50IG9mIHRpbWUuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KZGZfc3R1ZGVudHMgJT4lDQogIGZpbHRlcihTdHVkeUhvdXJzID4gbWVhbl9zdHVkeSkNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGZpbHRlcmVkIHJlc3VsdCByZXRhaW5lZCB0aGUgYXR0cmlidXRlcyBvZiB0aGUgb3JpZ2luYWwgdGliYmxlL2RhdGEgZnJhbWUgYW5kIGlzIGEgdGliYmxlLCBzbyB5b3UgY2FuIHdvcmsgd2l0aCBpdHMgcm93cyBhbmQgY29sdW1ucyBqdXN0IGxpa2UgYW55IG90aGVyIHRpYmJsZS4NCg0KRm9yIGV4YW1wbGUsIGxldCdzIGZpbmQgdGhlIGF2ZXJhZ2UgZ3JhZGUgZm9yIHN0dWRlbnRzIHdobyB1bmRlcnRvb2sgbW9yZSB0aGFuIHRoZSBhdmVyYWdlIGFtb3VudCBvZiBzdHVkeSB0aW1lLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmRmX3N0dWRlbnRzICU+JSANCiAgZmlsdGVyKFN0dWR5SG91cnMgPiBtZWFuX3N0dWR5KSAlPiUgDQogIHB1bGwoR3JhZGUpICU+JSANCiAgbWVhbigpDQpgYGANCg0KTGV0J3MgYXNzdW1lIHRoYXQgdGhlIHBhc3NpbmcgZ3JhZGUgZm9yIHRoZSBjb3Vyc2UgaXMgNjAuDQoNCldlIGNhbiB1c2UgdGhhdCBpbmZvcm1hdGlvbiB0byBhZGQgYSBuZXcgY29sdW1uIHRvIHRoZSBkYXRhIGZyYW1lLCBpbmRpY2F0aW5nIHdoZXRoZXIgb3Igbm90IGVhY2ggc3R1ZGVudCBwYXNzZWQuDQoNCk9mIGNvdXJzZSwgdGhlcmUgYXJlIG11bHRpcGxlIHdheSB0byBhY2hpZXZlIHRoaXMgYnV0IGluIHRoaXMgZXhhbXBsZSwgd2UnbGwgcmVzdWx0IHRvIGEgc2ltcGxlLCBsYXp5IHdheSB0byBhY2hpZXZlIGEgVFJVRS9GQUxTRSBpbmRpY2F0b3Ig8J+Zhy4NCg0KT25jZSBhZ2FpbiwgYGRwbHlyOjptdXRhdGUoKWAgd2lsbCBiZSBvbiBmdWxsIGRpc3BsYXkhIFRoZSBnZW5lcmFsIHN0cnVjdHVyZSBvZiBhZGRpbmcgbmV3IGNvbHVtbnMgaXM6DQoNCmBkZiAlPiUgbXV0YXRlKG5ld19jb2x1bW5fbmFtZSA9IHdoYXRfaXRfY29udGFpbnMpYA0KDQpMZXQncyBnbyBmb3J0aCBhbmQgbXV0YXRlIQ0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgVFJVRS9GQUxTRSBjb2x1bW4gYmFzZWQgb24gY29uZGl0aW9ucw0KZGZfc3R1ZGVudHMgPC0gZGZfc3R1ZGVudHMgJT4lIA0KICAgICAgICAgICAgICAgICAgbXV0YXRlKFBhc3MgPSAoR3JhZGUgPj0gNjAgKSkNCmRmX3N0dWRlbnRzDQpgYGANCg0KRGF0YSBmcmFtZXMgYXJlIGRlc2lnbmVkIGZvciB0YWJ1bGFyIGRhdGEsIGFuZCB5b3UgY2FuIHVzZSB0aGVtIHRvIHBlcmZvcm0gbWFueSBvZiB0aGUga2luZHMgb2YgZGF0YSBhbmFseXRpY3Mgb3BlcmF0aW9uIHlvdSBjYW4gZG8gaW4gYSByZWxhdGlvbmFsIGRhdGFiYXNlOyBzdWNoIGFzIGdyb3VwaW5nIGFuZCBhZ2dyZWdhdGluZyB0YWJsZXMgb2YgZGF0YS4gSW4gUiwgdGhpcyBpcyBhY2hpZXZlZCB1c2luZyBgZHBseXI6Omdyb3VwX2J5KCkgJT4lIHN1bW1hcml6ZSgpYA0KDQotICAgYGRwbHlyOjpncm91cF9ieSgpYCBjaGFuZ2VzIHRoZSB1bml0IG9mIGFuYWx5c2lzIGZyb20gdGhlIGNvbXBsZXRlIGRhdGFzZXQgdG8gaW5kaXZpZHVhbCBncm91cHMuDQoNCi0gICBgZHBseXI6OnN1bW1hcml6ZSgpYCBjcmVhdGVzIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCBvbmUgY29sdW1uIGZvciBlYWNoIGdyb3VwaW5nIHZhcmlhYmxlIGFuZCBvbmUgY29sdW1uIGZvciBlYWNoIG9mIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgdGhhdCB5b3UgaGF2ZSBzcGVjaWZpZWQuDQoNCkZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIHRoZSBgZHBseXI6Omdyb3VwX2J5KCkgJT4lIHN1bW1hcml6ZSgpYCB0byBncm91cCB0aGUgc3R1ZGVudCBkYXRhIGludG8gZ3JvdXBzIGJhc2VkIG9uIHRoZSAqKlBhc3MqKiBjb2x1bW4gYW5kIHRoZW4gZmluZCB0aGUgbWVhbiBzdHVkeSB0aW1lIGFuZCBncmFkZSBmb3IgdGhlIGdyb3VwcyBvZiBzdHVkZW50cyB3aG8gcGFzc2VkIGFuZCBmYWlsZWQgdGhlIGNvdXJzZS4NCg0KU2luY2Ugd2UgYXJlIGFwcGx5aW5nIHRoZSBzYW1lIGZ1bmN0aW9uLCBgbWVhbmAsIGFjcm9zcyBtdWx0aXBsZSBjb2x1bW5zLCBgZHBseXI6OmFjcm9zc2Agd2lsbCBkbyB0aGUgaGVhdnkgbGlmdGluZyBmb3IgdXMgYW5kIHJlZHVjZSB0aGUgY29kZSB3ZSdsbCBoYXZlIHRvIHdyaXRlLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgR3JvdXAgc3R1ZGVudHMgYmFzZWQgb24gUGFzcyBjb2x1bW4gdGhlbiBmaW5kIG1lYW4gb2Ygc3R1ZHkgdGltZSBhbmQgZ3JhZGUNCmRmX3N0dWRlbnRzICU+JSANCiAgZ3JvdXBfYnkoUGFzcykgJT4lIA0KICBzdW1tYXJpc2UoYWNyb3NzKGMoU3R1ZHlIb3VycywgR3JhZGUpLCBtZWFuKSkNCmBgYA0KDQpXaGF0IGlmIHdlIHdhbnRlZCB0byBkZXRlcm1pbmUgaG93IG1hbnkgc3R1ZGVudHMgcGFzc2VkIG9yIGZhaWxlZD8gV2UnZCBoYXZlIHRvIGdyb3VwIG91ciBkYXRhIGJhc2VkIG9uIHRoZSBgUGFzc2AgY29sdW1uLCB0aGVuIGRvIGEgdGFsbHkgZm9yIGVhY2ggZ3JvdXAuIGBkcGx5cjo6Y291bnQoKWAgd3JhcHMgYWxsIHRoaXMgdG8gZ2l2ZSB5b3UgYSBuaWNlIGdyb3VwZWQgY291bnQhDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KZGZfc3R1ZGVudHMgJT4lIA0KICBjb3VudChQYXNzKQ0KYGBgDQoNCkl0J3Mgd29ydGggbWVudGlvbmluZyB0aGF0IGBkYXRhX2ZyYW1lICU+JSDigKZgIG9wZXJhdGlvbiByZXR1cm5zIGEgbmV3IGNvcHkgb2YgdGhlIGRhdGEgZnJhbWU7IHNvIGlmIHlvdSB3YW50IHRvIG1vZGlmeSBhIGRhdGEgZnJhbWUgYnV0IGtlZXAgdGhlIGV4aXN0aW5nIHZhcmlhYmxlLCB5b3UgbmVlZCB0byBhc3NpZ24gdGhlIHJlc3VsdCBvZiB0aGUgb3BlcmF0aW9uIHRvIHRoZSBleGlzdGluZyB2YXJpYWJsZS4NCg0KRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29kZSBzb3J0cyB0aGUgc3R1ZGVudCBkYXRhIGludG8gZGVzY2VuZGluZyBvcmRlciBvZiBHcmFkZSwgYW5kIGFzc2lnbnMgdGhlIHJlc3VsdGluZyBzb3J0ZWQgZGF0YSBmcmFtZSB0byB0aGUgb3JpZ2luYWwgKipkZl9zdHVkZW50cyoqIHZhcmlhYmxlLg0KDQpUbyBkbyB0aGlzLCB3ZSdsbCBuZWVkIHRvIHJlYWNoIGludG8gZHBseXIgYW5kIHRha2Ugb25lIG1vcmUgdmVyYjogYGFycmFuZ2UoKWAuIEl0IG9yZGVycyB0aGUgcm93cyBvZiBhIGRhdGEgZnJhbWUgYnkgY29sdW1uIHZhbHVlcy4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgZGF0YSBzb3J0ZWQgYnkgR3JhZGUgKGRlc2NlbmRpbmcpDQpkZl9zdHVkZW50cyA8LSBkZl9zdHVkZW50cyAlPiUNCiAgIyBTb3J0IGJ5IGRlc2NlbmRpbmcgb3JkZXINCiAgYXJyYW5nZShkZXNjKEdyYWRlKSkgJT4lIA0KICAjIEV4cGxpY2l0bHkgZGVmaW5pbmcgJ05hbWUnIGFzIGNhdGVnb3JpY2FsIGFuZCBlbnN1cmluZyBpdHMgbGV2ZWxzIGZvbGxvdyB0aGUgZGVzY2VuZGluZyBvcmRlcg0KICBtdXRhdGUoTmFtZSA9IGZhY3RvcihOYW1lKSAlPiUgZmN0X2lub3JkZXIoKSkNCg0KDQojIFNob3cgdGhlIGRhdGEgZnJhbWUNCiBkZl9zdHVkZW50cyANCg0KYGBgDQoNCj4gKkluIFIsIGZhY3RvcnMgYXJlIHVzZWQgdG8gd29yayB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBzdWNoIGFzIHRoZSBzdHVkZW50cycgbmFtZXMgY29sdW1uLiBPbmNlIGNyZWF0ZWQsIGZhY3RvcnMgY2FuIG9ubHkgY29udGFpbiBhIHByZS1kZWZpbmVkIHNldCB2YWx1ZXMsIGtub3duIGFzIGxldmVscy4gQnkgZGVmYXVsdCwgUiBhbHdheXMgc29ydHMgbGV2ZWxzIGluIGFscGhhYmV0aWNhbCBvcmRlci4gSW4gdGhlIGFib3ZlIGV4YW1wbGUsIHdlIGNvbnZlcnRlZCB0aGUgYE5hbWVgIGNvbHVtbiBmcm9tIHR5cGUgY2hhcmFjdGVyIHRvIGBmYWN0b3JgIGFuZCBzcGVjaWZpZWQgdGhhdCB0aGUgbGV2ZWxzIChzdHVkZW50IG5hbWVzKSwgd2lsbCBhbHdheXMgZm9sbG93IHRoZSBkZXNjZW5kaW5nIG9yZGVyLioNCj4NCj4gKktub3cgeW91ciB3YXkgYXJvdW5kIGZhY3RvcnMgd2l0aCB0aGUgW2ZhY3RvcnMgY2hhcHRlcl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9mYWN0b3JzLmh0bWwpIGluIFIgZm9yIERhdGEgU2NpZW5jZS4qDQoNCiMjIDUuIERhdGEgdmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDINCg0KIVtBcnR3b3JrIGJ5IFxAYWxsaXNvbl9ob3JzdF0oaW1hZ2VzL2dncGxvdDJfbWFzdGVycGllY2UucG5nKXt3aWR0aD0iNTY5In0NCg0KVGliYmxlcy9kYXRhIGZyYW1lcyBwcm92aWRlIGEgZ3JlYXQgd2F5IHRvIGV4cGxvcmUgYW5kIGFuYWx5emUgdGFidWxhciBkYXRhIGJ1dCBJJ3ZlIGhlYXJkIGl0IHNhaWQ6DQoNCj4gKiJUaGUgc2ltcGxlIGdyYXBoIGhhcyBicm91Z2h0IG1vcmUgaW5mb3JtYXRpb24gdG8gdGhlIGRhdGEgYW5hbHlzdCdzIG1pbmQgdGhhbiBhbnkgb3RoZXIgZGV2aWNlLiIgLS0tIEpvaG4gVHVrZXkqDQoNClIgaGFzIHNldmVyYWwgc3lzdGVtcyBmb3IgbWFraW5nIGdyYXBocywgYnV0IGBnZ3Bsb3QyYCBpcyBvbmUgb2YgdGhlIG1vc3QgZWxlZ2FudCBhbmQgbW9zdCB2ZXJzYXRpbGUuDQoNCmdncGxvdDIgaXMgZGVzaWduZWQgdG8gd29yayAqKml0ZXJhdGl2ZWx5KiouIFlvdSBzdGFydCB3aXRoIGEgbGF5ZXIgdGhhdCBzaG93cyB0aGUgcmF3IGRhdGEuIFRoZW4geW91IGFkZCBsYXllcnMgb2YgYW5ub3RhdGlvbnMgYW5kIHN0YXRpc3RpY2FsIHN1bW1hcmllcy4gVGhhdCdzIHdoeSB0aGUgZ2dwbG90MiBhcnR3b3JrIGNvbnRhaW5zIG1vbnN0ZVJzIGNhcnJ5aW5nIGRpZmZlcmVudCBzdHVmZiDwn5iELg0KDQpUaGlzIGFsbG93cyB5b3UgdG8gY29tcG9zZSBncmFwaHMgYnkgKipjb21iaW5pbmcgaW5kZXBlbmRlbnQgY29tcG9uZW50cyoqLg0KDQpMZXQncyBzdGFydCB3aXRoIGEgc2ltcGxlIGJhciBjaGFydCB0aGF0IHNob3dzIHRoZSBncmFkZSBvZiBlYWNoIHN0dWRlbnQuDQoNClNvIGluIHRoaXMgY2FzZSwgd2UnbGwgc3RhcnQgd2l0aCBbYGdncGxvdCgpYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dncGxvdC5odG1sKSwgc3VwcGx5IGEgZGF0YXNldCBhbmQgYWVzdGhldGljIG1hcHBpbmcgKHdpdGggW2BhZXMoKWBdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXMuaHRtbCkpIHRoZW4gYWRkIGEgbGF5ZXJzIChsaWtlIFtgZ2VvbV9iYXIoKWBdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX2Jhci5odG1sKSkgZm9yIGJhciBjaGFydHMuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KZ2dwbG90KGRhdGEgPSBkZl9zdHVkZW50cywgYWVzKHggPSBOYW1lLCB5ID0gR3JhZGUpKSsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpDQoNCmBgYA0KDQpXZWxsLCB0aGF0IHdvcmtlZDsgYnV0IHRoZSBjaGFydCBjb3VsZCB1c2Ugc29tZSBpbXByb3ZlbWVudHMgdG8gbWFrZSBpdCBjbGVhcmVyIHdoYXQgd2UncmUgbG9va2luZyBhdC4NCg0KRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29kZToNCg0KLSAgIFNwZWNpZmllcyB0aGUgY29sb3Igb2YgdGhlIGJhciBjaGFydC4NCg0KLSAgIEFkZHMgYSB0aXRsZSB0byB0aGUgY2hhcnQgKHNvIHdlIGtub3cgd2hhdCBpdCByZXByZXNlbnRzKQ0KDQotICAgQWRkcyBsYWJlbHMgdG8gdGhlIFggYW5kIFkgKHNvIHdlIGtub3cgd2hpY2ggYXhpcyBzaG93cyB3aGljaCBkYXRhKQ0KDQotICAgQWRkcyBhIGdyaWQgKHRvIG1ha2UgaXQgZWFzaWVyIHRvIGRldGVybWluZSB0aGUgdmFsdWVzIGZvciB0aGUgYmFycykNCg0KLSAgIFJvdGF0ZXMgdGhlIFggbWFya2VycyAoc28gd2UgY2FuIHJlYWQgdGhlbSkNCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIENoYW5nZSB0aGUgdGhlbWUgZnJvbSBncmV5IGJhY2tncm91bmQNCnRoZW1lX3NldCh0aGVtZV9saWdodCgpKQ0KDQoNCmdncGxvdChkYXRhID0gZGZfc3R1ZGVudHMsDQogICAgICAgYWVzKHggPSBOYW1lLCB5ID0gR3JhZGUpKSsNCiAgIyBCYXIgY2hhcnQNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsDQogICAgICAgICAgICMgU3BlY2lmeSBjb2xvciBvZiB0aGUgYmFyIGNoYXJ0DQogICAgICAgICAgIGZpbGwgPSAibWlkbmlnaHRibHVlIiwgYWxwaGEgPSAwLjcpKw0KICAjIEFkZCBhIHRpdGxlIHRoZSBjaGFydA0KICAgIGdndGl0bGUoJ1N0dWRlbnQgR3JhZGVzJykrDQogICMgQWRkIGxhYmVscyB0byB0aGUgeCBhbmQgeQ0KICAgIHhsYWIoJ1N0dWRlbnQnKSsgeWxhYignR3JhZGUnKSsNCiAgdGhlbWUoDQogICAgIyBBZGRzIGEgZ3JpZCAodG8gbWFrZSBpdCBlYXNpZXIgdG8gZGV0ZXJtaW5lIHRoZSB2YWx1ZXMgZm9yIHRoZSBiYXJzKQ0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gJyM5NWE1YTYnLCBsaW5ldHlwZSA9ICdkYXNoZWQnLCBzaXplID0gMC41KSwNCiAgICANCiAgICAjIENlbnRlciB0aGUgdGl0bGUNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICANCiAgICAjIFJvdGF0ZXMgdGhlIFggbWFya2VycyAoc28gd2UgY2FuIHJlYWQgdGhlbSkNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKQ0KICApDQpgYGANCg0KTXVjaCBiZXR0ZXIg8J+kqSENCg0KV2UgY2FuIGFsc28gbGF5b3V0IG11bHRpcGxlIHBsb3RzIGluIGEgc2luZ2xlIHZpZXcuIEZvciBleGFtcGxlLCB0aGUgZm9sbG93aW5nIGNvZGUgY3JlYXRlcyBhIGZpZ3VyZSB3aXRoIHR3byBzdWJwbG90cyAtIG9uZSBpcyBhIGJhciBjaGFydCBzaG93aW5nIHN0dWRlbnQgZ3JhZGVzLCBhbmQgdGhlIG90aGVyIGlzIGEgcGllIGNoYXJ0ICh3aGljaCBhcmUgYmFzaWNhbGx5IGEgc3RhY2tlZCBiYXIgY2hhcnQgaW4gcG9sYXIgY29vcmRpbmF0ZXMpIGNvbXBhcmluZyB0aGUgbnVtYmVyIG9mIHBhc3NpbmcgZ3JhZGVzIHRvIG5vbi1wYXNzaW5nIGdyYWRlcy4NCg0KW3BhdGNod29ya10oaHR0cHM6Ly9wYXRjaHdvcmsuZGF0YS1pbWFnaW5pc3QuY29tLykgZXh0ZW5kcyBgZ2dwbG90YCBBUEkgYnkgcHJvdmlkaW5nIG1hdGhlbWF0aWNhbCBvcGVyYXRvcnMgKHN1Y2ggYXMgYCtgIG9yIGAvYCkgZm9yIGNvbWJpbmluZyBtdWx0aXBsZSBwbG90cy4gWWVzLCBhcyBlYXN5IGFzIHRoYXQhDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KbGlicmFyeShwYXRjaHdvcmspDQojIENyZWF0ZSBhIGJhciBwbG90IG9mIG5hbWUgdnMgZ3JhZGUNCmJhcl9jaGFydCA8LSBnZ3Bsb3QoZGF0YSA9IGRmX3N0dWRlbnRzLA0KICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IE5hbWUsIHkgPSBHcmFkZSkpKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJtaWRuaWdodGJsdWUiLA0KICAgICAgICAgICBhbHBoYSA9IDAuNykrDQogIGdndGl0bGUoJ1N0dWRlbnQgR3JhZGVzJykrDQogIHhsYWIoJ1N0dWRlbnQnKSsNCiAgeWxhYignR3JhZGUnKSsNCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAnIzk1YTVhNicsIGxpbmV0eXBlID0gJ2Rhc2hlZCcsIHNpemUgPSAwLjUpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApDQogICAgDQogICkNCg0KIyBDcmVhdGUgYSBwaWUgY2hhcnQgb2YgcGFzcyBjb3VudHMNCnBpZV9jaGFydCA8LSBnZ3Bsb3QoZGF0YSA9IGNvdW50KGRmX3N0dWRlbnRzLCBQYXNzKSwNCiAgICAgICBhZXMoeCA9ICIiLCB5ID0gbiwgZmlsbCA9IFBhc3MpKSsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkrDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkrDQogICMgU3BlY2lmeSBjb2xvdXINCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwNzNDMkZGIiwgIiNFRkMwMDBGRiIpKSsNCiAgeGxhYihOVUxMKSsNCiAgeWxhYihOVUxMKSsNCiAgZ2d0aXRsZSgiUGFzc2luZyBHcmFkZXMiKSsNCiAgdGhlbWVfY2xhc3NpYygpKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICBheGlzLmxpbmUgID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpDQogICkNCg0KDQoNCiMgQ29tcG9zZSBtdWx0aXBsb3QgbGF5b3V0cyB1c2luZyBwYXRjaHdvcmsNCmJhcl9jaGFydCArIHBpZV9jaGFydA0KYGBgDQoNCj4gKlRoZSBbRGF0YSB2aXN1YWxpc2F0aW9uXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKSBjaGFwdGVyIGluIFIgZm9yIERhdGEgU2NpZW5jZSBjb3ZlcnMgaG93IHRvIG1ha2UgbXVjaCBtb3JlIGVsZWdhbnQgdmlzdWFsaXNhdGlvbnMgdGhhbiB3aGF0IHdlIGhhdmUganVzdCBkb25lLiBCZSBzdXJlIHRvIGNoZWNrIGl0IG91dCEqDQoNCiMjIDYuIEdldHRpbmcgc3RhcnRlZCB3aXRoIHN0YXRpc3RpY2FsIGFuYWx5c2lzDQoNCk5vdyB0aGF0IHlvdSBrbm93IGhvdyB0byB1c2UgUiB0byBtYW5pcHVsYXRlIGFuZCB2aXN1YWxpemUgZGF0YSwgeW91IGNhbiBzdGFydCBhbmFseXppbmcgaXQg8J+OiS4NCg0KQSBsb3Qgb2YgZGF0YSBzY2llbmNlIGlzIHJvb3RlZCBpbiAqc3RhdGlzdGljcyosIHNvIHdlJ2xsIGV4cGxvcmUgc29tZSBiYXNpYyBzdGF0aXN0aWNhbCB0ZWNobmlxdWVzLg0KDQo+ICoqTm90ZSoqOiBUaGlzIGlzIG5vdCBpbnRlbmRlZCB0byB0ZWFjaCB5b3Ugc3RhdGlzdGljcyAtIHRoYXQncyBtdWNoIHRvbyBiaWcgYSB0b3BpYyBmb3IgdGhpcyBub3RlYm9vay4gSXQgd2lsbCBob3dldmVyIGludHJvZHVjZSB5b3UgdG8gc29tZSBzdGF0aXN0aWNhbCBjb25jZXB0cyBhbmQgdGVjaG5pcXVlcyB0aGF0IGRhdGEgc2NpZW50aXN0cyB1c2UgYXMgdGhleSBleHBsb3JlIGRhdGEgaW4gcHJlcGFyYXRpb24gZm9yIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxpbmcuDQoNCiMjIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFuZCBkYXRhIGRpc3RyaWJ1dGlvbg0KDQpXaGVuIGV4YW1pbmluZyBhICp2YXJpYWJsZSogKGZvciBleGFtcGxlIGEgc2FtcGxlIG9mIHN0dWRlbnQgZ3JhZGVzKSwgZGF0YSBzY2llbnRpc3RzIGFyZSBwYXJ0aWN1bGFybHkgaW50ZXJlc3RlZCBpbiBpdHMgKmRpc3RyaWJ1dGlvbiogKGluIG90aGVyIHdvcmRzLCBob3cgYXJlIGFsbCB0aGUgZGlmZmVyZW50IGdyYWRlIHZhbHVlcyBzcHJlYWQgYWNyb3NzIHRoZSBzYW1wbGUpLiBUaGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoaXMgZXhwbG9yYXRpb24gaXMgb2Z0ZW4gdG8gdmlzdWFsaXplIHRoZSBkYXRhIGFzIGEgYGhpc3RvZ3JhbWAsIGFuZCBzZWUgaG93IGZyZXF1ZW50bHkgZWFjaCB2YWx1ZSBmb3IgdGhlIHZhcmlhYmxlIG9jY3Vycy4NCg0KU28gd2hhdCBgZ2VvbWAgYXJlIHdlIGdvaW5nIHRvIHVzZT8gIiBbYGdlb21faGlzdG9ncmFtYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21faGlzdG9ncmFtLmh0bWwjOn46dGV4dD1nZW9tX2hpc3RvZ3JhbS5SZCxkaXNwbGF5JTIwdGhlJTIwY291bnRzJTIwd2l0aCUyMGxpbmVzLikgKG9yIHNvbWV0aGluZyBjbG9zZSkiLCB5b3UnbGwgc2F5LiBCZWNhdXNlIHlvdSBhcmUgYWxyZWFkeSBnZXR0aW5nIHRoZSBnaXN0L2dlb20gb2YgaXQg8J+lsyENCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEdldCB0aGUgdmFyaWFibGUgdG8gZXhhbWluZQ0KdmFyX2RhdGEgPC0gZGZfc3R1ZGVudHMgJT4lIHNlbGVjdChHcmFkZSkNCg0KIyBQbG90IGEgaGlzdG9ncmFtDQp2YXJfZGF0YSAlPiUgDQogIGdncGxvdChhZXMoeCA9IEdyYWRlKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjAsIGZpbGwgPSAibWlkbmlnaHRibHVlIiwgYWxwaGEgPSAwLjcsIGJvdW5kYXJ5ID0gMC41KSsNCiAgZ2d0aXRsZSgnRGF0YSBEaXN0cmlidXRpb24nKSsNCiAgeGxhYignVmFsdWUnKSsNCiAgeWxhYignRnJlcXVlbmN5JykrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNClRoZSBoaXN0b2dyYW0gZm9yIGdyYWRlcyBpcyBhIHN5bW1ldHJpYyBzaGFwZSwgd2hlcmUgdGhlIG1vc3QgZnJlcXVlbnRseSBvY2N1cnJpbmcgZ3JhZGVzIHRlbmQgdG8gYmUgaW4gdGhlIG1pZGRsZSBvZiB0aGUgcmFuZ2UgKGFyb3VuZCA1MCksIHdpdGggZmV3ZXIgZ3JhZGVzIGF0IHRoZSBleHRyZW1lIGVuZHMgb2YgdGhlIHNjYWxlLg0KDQojIyMgTWVhc3VyZXMgb2YgY2VudHJhbCB0ZW5kZW5jeQ0KDQpUbyB1bmRlcnN0YW5kIHRoZSBkaXN0cmlidXRpb24gYmV0dGVyLCB3ZSBjYW4gZXhhbWluZSBzby1jYWxsZWQgKm1lYXN1cmVzIG9mIGNlbnRyYWwgdGVuZGVuY3kqOyB3aGljaCBpcyBhIGZhbmN5IHdheSBvZiBkZXNjcmliaW5nIHN0YXRpc3RpY3MgdGhhdCByZXByZXNlbnQgdGhlICJtaWRkbGUiIG9mIHRoZSBkYXRhLiBUaGUgZ29hbCBvZiB0aGlzIGlzIHRvIHRyeSB0byBmaW5kIGEgInR5cGljYWwiIHZhbHVlLiBDb21tb24gd2F5cyB0byBkZWZpbmUgdGhlIG1pZGRsZSBvZiB0aGUgZGF0YSBpbmNsdWRlOg0KDQotICAgVGhlICptZWFuKjogQSBzaW1wbGUgYXZlcmFnZSBiYXNlZCBvbiBhZGRpbmcgdG9nZXRoZXIgYWxsIG9mIHRoZSB2YWx1ZXMgaW4gdGhlIHNhbXBsZSBzZXQsIGFuZCB0aGVuIGRpdmlkaW5nIHRoZSB0b3RhbCBieSB0aGUgbnVtYmVyIG9mIHNhbXBsZXMuDQoNCi0gICBUaGUgKm1lZGlhbio6IFRoZSB2YWx1ZSBpbiB0aGUgbWlkZGxlIG9mIHRoZSByYW5nZSBvZiBhbGwgb2YgdGhlIHNhbXBsZSB2YWx1ZXMuDQoNCi0gICBUaGUgKm1vZGUqOiBUaGUgbW9zdCBjb21tb25seSBvY2N1cmluZyB2YWx1ZSBpbiB0aGUgc2FtcGxlIHNldF5cKl4uDQoNCkxldCdzIGNhbGN1bGF0ZSB0aGVzZSB2YWx1ZXMsIGFsb25nIHdpdGggdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIGZvciBjb21wYXJpc29uLCBhbmQgc2hvdyB0aGVtIG9uIHRoZSBoaXN0b2dyYW0uDQoNCj4gXlwqXk9mIGNvdXJzZSwgaW4gc29tZSBzYW1wbGUgc2V0cyAsIHRoZXJlIG1heSBiZSBhIHRpZSBmb3IgdGhlIG1vc3QgY29tbW9uIHZhbHVlIC0gaW4gd2hpY2ggY2FzZSB0aGUgZGF0YXNldCBpcyBkZXNjcmliZWQgYXMgKmJpbW9kYWwqIG9yIGV2ZW4gKm11bHRpbW9kYWwqLg0KDQpCYXNlICpSIGRvZXMgbm90KiBwcm92aWRlIGEgKmZ1bmN0aW9uKiBmb3IgZmluZGluZyB0aGUgKm1vZGUqLiBCdXQgd29ycnkgbm90LCBgc3RhdGlwOjptZnZgIHJldHVybnMgdGhlIG1vc3QgZnJlcXVlbnQgdmFsdWUocykgKG9yIG1vZGUocykpIGZvdW5kIGluIGEgdmVjdG9yLiBPdGhlciBwcmV0dHkgYXdlc29tZSB3b3JrYXJvdW5kcyBjYW4gYmUgZm91bmQgY2FuIGJlIGZvdW5kIG9uIHRoaXMgW3N0YWNrb3ZlcmZsb3cgdGhyZWFkXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yNTQ3NDAyL2hvdy10by1maW5kLXRoZS1zdGF0aXN0aWNhbC1tb2RlKS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQoNCmxpYnJhcnkoc3RhdGlwKQ0KIyBHZXQgdGhlIHZhcmlhYmxlIHRvIGV4YW1pbmUNCnZhciA8LSBkZl9zdHVkZW50cyAlPiUgc2VsZWN0KEdyYWRlKQ0KDQojIEdldCBzdGF0aXN0aWNzDQptaW5fdmFsIDwtIG1pbih2YXIkR3JhZGUpDQptYXhfdmFsIDwtIG1heCh2YXIkR3JhZGUpDQptZWFuX3ZhbCA8LSBtZWFuKHZhciRHcmFkZSkNCm1lZF92YWwgPC0gbWVkaWFuKHZhciRHcmFkZSkNCm1vZF92YWwgPC0gbWZ2KHZhciRHcmFkZSkNCg0KIyBQcmludCB0aGUgc3RhdHMNCmdsdWU6OmdsdWUoDQogICdNaW5pbXVtOiB7Zm9ybWF0KHJvdW5kKG1pbl92YWwsIDIpLCBuc21hbGwgPSAyKX0NCiAgIE1lYW46IHtmb3JtYXQocm91bmQobWVhbl92YWwsIDIpLCBuc21hbGwgPSAyKX0NCiAgIE1lZGlhbjoge2Zvcm1hdChyb3VuZChtZWRfdmFsLCAyKSwgbnNtYWxsID0gMil9DQogICBNb2RlOiB7Zm9ybWF0KHJvdW5kKG1vZF92YWwsIDIpLCBuc21hbGwgPSAyKX0NCiAgIE1heGltdW06IHtmb3JtYXQocm91bmQobWF4X3ZhbCwgMiksIG5zbWFsbCA9IDIpfScNCikNCg0KDQojIFBsb3QgYSBoaXN0b2dyYW0NCnZhciAlPiUgDQogIGdncGxvdChhZXMoeCA9IEdyYWRlKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjAsIGZpbGwgPSAibWlkbmlnaHRibHVlIiwgYWxwaGEgPSAwLjcsIGJvdW5kYXJ5ID0gMC41KSsNCiAgDQojIEFkZCBsaW5lcyBmb3IgdGhlIHN0YXRpc3RpY3MNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWluX3ZhbCwgY29sb3IgPSAnZ3JheTMzJywgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMykrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW5fdmFsLCBjb2xvciA9ICdjeWFuJywgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMykrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lZF92YWwsIGNvbG9yID0gJ3JlZCcsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjMgKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbW9kX3ZhbCwgY29sb3IgPSAneWVsbG93JywgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMyApKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtYXhfdmFsLCBjb2xvciA9ICdncmF5MzMnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMS4zICkrDQogIA0KIyBBZGQgdGl0bGVzIGFuZCBsYWJlbHMNCiAgZ2d0aXRsZSgnRGF0YSBEaXN0cmlidXRpb24nKSsNCiAgeGxhYignVmFsdWUnKSsNCiAgeWxhYignRnJlcXVlbmN5JykrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCkZvciB0aGUgZ3JhZGUgZGF0YSwgdGhlIG1lYW4sIG1lZGlhbiwgYW5kIG1vZGUgYWxsIHNlZW0gdG8gYmUgbW9yZSBvciBsZXNzIGluIHRoZSBtaWRkbGUgb2YgdGhlIG1pbmltdW0gYW5kIG1heGltdW0sIGF0IGFyb3VuZCA1MC4NCg0KQW5vdGhlciB3YXkgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgYSB2YXJpYWJsZSBpcyB0byB1c2UgYSAqYm94KiBwbG90IChzb21ldGltZXMgY2FsbGVkIGEgKmJveC1hbmQtd2hpc2tlcnMqIHBsb3QpLiBMZXQncyBjcmVhdGUgb25lIGZvciB0aGUgZ3JhZGUgZGF0YS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEdldCB0aGUgdmFyaWFibGUgdG8gZXhhbWluZQ0KdmFyIDwtIGRmX3N0dWRlbnRzICU+JSBzZWxlY3QoR3JhZGUpDQoNCiMgUGxvdCBhIGJveCBwbG90DQp2YXIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAxLCB5ID0gR3JhZGUpKSsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI0U2OUYwMCIsIGNvbG9yID0gImdyYXkyMyIsIGFscGhhID0gMC43KSsNCiMgQWRkIHRpdGxlcyBhbmQgbGFiZWxzDQogIGdndGl0bGUoIkRhdGEgRGlzdHJpYnV0aW9uIikrDQogIHhsYWIoIiIpKw0KICB5bGFiKCJHcmFkZSIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpUaGUgYm94IHBsb3Qgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZ3JhZGUgdmFsdWVzIGluIGEgZGlmZmVyZW50IGZvcm1hdCB0byB0aGUgaGlzdG9ncmFtLiBUaGUgKmJveCogcGFydCBvZiB0aGUgcGxvdCBzaG93cyB3aGVyZSB0aGUgaW5uZXIgdHdvICpxdWFydGlsZXMqIG9mIHRoZSBkYXRhIHJlc2lkZSAtIHNvIGluIHRoaXMgY2FzZSwgaGFsZiBvZiB0aGUgZ3JhZGVzIGFyZSBiZXR3ZWVuIGFwcHJveGltYXRlbHkgMzYgYW5kIDYzLiBUaGUgKndoaXNrZXJzKiBleHRlbmRpbmcgZnJvbSB0aGUgYm94IHNob3cgdGhlIG91dGVyIHR3byBxdWFydGlsZXM7IHNvIHRoZSBvdGhlciBoYWxmIG9mIHRoZSBncmFkZXMgaW4gdGhpcyBjYXNlIGFyZSBiZXR3ZWVuIDAgYW5kIDM2IG9yIDYzIGFuZCAxMDAuIFRoZSBsaW5lIGluIHRoZSBib3ggaW5kaWNhdGVzIHRoZSAqbWVkaWFuKiB2YWx1ZS4NCg0KSXQncyBvZnRlbiB1c2VmdWwgdG8gY29tYmluZSBoaXN0b2dyYW1zIGFuZCBib3ggcGxvdHMsIHdpdGggdGhlICpib3ggcGxvdCdzIG9yaWVudGF0aW9uIGNoYW5nZWQqIHRvIGFsaWduIGl0IHdpdGggdGhlIGhpc3RvZ3JhbSAoaW4gc29tZSB3YXlzLCBpdCBjYW4gYmUgaGVscGZ1bCB0byB0aGluayBvZiB0aGUgaGlzdG9ncmFtIGFzIGEgImZyb250IGVsZXZhdGlvbiIgdmlldyBvZiB0aGUgZGlzdHJpYnV0aW9uLCBhbmQgdGhlIGJveCBwbG90IGFzIGEgInBsYW4iIHZpZXcgb2YgdGhlIGRpc3RyaWJ1dGlvbiBmcm9tIGFib3ZlKS4gU2luY2Ugd2UgbWF5IG5lZWQgdG8gcGxvdCB0aGUgaGlzdG9ncmFtcyBhbmQgYm94IHBsb3RzIGZvciBkaWZmZXJlbnQgdmFyaWFibGVzLCBpdCB3aWxsIGJlIGNvbnZlbmllbnQgdG8gd3JpdGUgYSBmdW5jdGlvbi4gRnVuY3Rpb25zIGFsbG93cyB5b3UgdG8gYXV0b21hdGUgY29tbW9uIHRhc2tzIGluIGEgbW9yZSBwb3dlcmZ1bCBhbmQgZ2VuZXJhbCB3YXkgdGhhbiBjb3B5LWFuZC1wYXN0aW5nLg0KDQo+IFRoaXMgW2BJbnRyb2R1Y3Rpb24gdG8gUiBGdW5jdGlvbnNgXSh3cml0aW5nJTIwZnVuY3Rpb25zJTIwaW4lMjB0aGUlMjBSJTIwbGFuZ3VhZ2UpIGJ5IFN0ZXBoYW5pZSBLaXJtZXIgd2lsbCBnZXQgeW91IHdyaXRpbmcgZnVuY3Rpb25zIGluIHRoZSBSIGxhbmd1YWdlIGluIG5vIHRpbWUuDQoNCkxldCdzIGdldCByaWdodCB0byBpdCEgRnVuY3Rpb25zIGluIFIgYXJlIGdlbmVyYWxseSBkZWZpbmVkIGluIHRoaXMgZmFzaGlvbjoNCg0KYG5hbWUgPC0gZnVuY3Rpb24odmFyaWFibGVzKSB7cmV0dXJuKHZhbHVlKX1gDQoNCj4gSnVzdCBmb3IgdGhlIHNha2Ugb2YgcHJldHR5IGFuZCBtb3JlIGVsYWJvcmF0aXZlIHBsb3RzLCB3ZSdsbCBpbmNsdWRlIGEgcGFyYW1ldGVyIGBiaW53aWR0aGAgYXMgb25lIG9mIHRoZSBpbnB1dCB2YXJpYWJsZXMgdG8gb3VyIGZ1bmN0aW9uLiBCaW53aWR0aCB3aWxsIHNwZWNpZnkgdGhlIHdpZHRoIG9mIHRoZSBiaW5zIHVzZWQgZm9yIG91ciBoaXN0b2dyYW0gYW5kIHdpbGwgaGVscCB1cyBleHBsb3JlIG11bHRpcGxlIHdpZHRocyB0byBmaW5kIHRoZSBiZXN0IHRoYXQgd2lsbCBpbGx1c3RyYXRlIHRoZSBzdG9yaWVzIGluIG91ciBkYXRhLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdGhhdCB3ZSBjYW4gcmV1c2UNCnNob3dfZGlzdHJpYnV0aW9uIDwtIGZ1bmN0aW9uKHZhcl9kYXRhLCBiaW53aWR0aCkgew0KICBsaWJyYXJ5KGdncGxvdDIpDQogIGxpYnJhcnkoZHBseXIpDQogIGxpYnJhcnkoZ2x1ZSkNCiAgbGlicmFyeShwYXRjaHdvcmspDQogIA0KICAjIEdldCBzdGF0aXN0aWNzDQogIG1pbl92YWwgPC0gbWluKHB1bGwodmFyX2RhdGEpKQ0KICBtYXhfdmFsIDwtIG1heChwdWxsKHZhcl9kYXRhKSkNCiAgbWVhbl92YWwgPC0gbWVhbihwdWxsKHZhcl9kYXRhKSkNCiAgbWVkX3ZhbCA8LSBtZWRpYW4ocHVsbCh2YXJfZGF0YSkpDQogIG1vZF92YWwgPC0gc3RhdGlwOjptZnYocHVsbCh2YXJfZGF0YSkpDQoNCiAgIyBQcmludCB0aGUgc3RhdHMNCiAgc3RhdHMgPC0gZ2x1ZTo6Z2x1ZSgNCiAgJ01pbmltdW06IHtmb3JtYXQocm91bmQobWluX3ZhbCwgMiksIG5zbWFsbCA9IDIpfQ0KICAgTWVhbjoge2Zvcm1hdChyb3VuZChtZWFuX3ZhbCwgMiksIG5zbWFsbCA9IDIpfQ0KICAgTWVkaWFuOiB7Zm9ybWF0KHJvdW5kKG1lZF92YWwsIDIpLCBuc21hbGwgPSAyKX0NCiAgIE1vZGU6IHtmb3JtYXQocm91bmQobW9kX3ZhbCwgMiksIG5zbWFsbCA9IDIpfQ0KICAgTWF4aW11bToge2Zvcm1hdChyb3VuZChtYXhfdmFsLCAyKSwgbnNtYWxsID0gMil9Jw0KICApDQogIA0KICAjIFBsb3QgdGhlIGhpc3RvZ3JhbQ0KICBoaXN0X2dyYW0gPC0gdmFyX2RhdGEgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBwdWxsKHZhcl9kYXRhKSkpKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IGJpbndpZHRoLCBmaWxsID0gIm1pZG5pZ2h0Ymx1ZSIsIGFscGhhID0gMC43LCBib3VuZGFyeSA9IDAuNCkrDQogICMgQWRkIGxpbmVzIGZvciB0aGUgc3RhdGlzdGljcw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtaW5fdmFsLCBjb2xvciA9ICdncmF5MzMnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMS4zKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbl92YWwsIGNvbG9yID0gJ2N5YW4nLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMS4zKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkX3ZhbCwgY29sb3IgPSAncmVkJywgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMyApKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtb2RfdmFsLCBjb2xvciA9ICd5ZWxsb3cnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMS4zICkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1heF92YWwsIGNvbG9yID0gJ2dyYXkzMycsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjMgKSsNCiAgIyBBZGQgdGl0bGVzIGFuZCBsYWJlbHMNCiAgZ2d0aXRsZSgnRGF0YSBEaXN0cmlidXRpb24nKSsNCiAgeGxhYignJykrDQogIHlsYWIoJ0ZyZXF1ZW5jeScpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCiAgDQogICMgUGxvdCB0aGUgYm94IHBsb3QNCiAgYnhfcGx0IDwtIHZhcl9kYXRhICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcHVsbCh2YXJfZGF0YSksIHkgPSAxKSkrDQogIGdlb21fYm94cGxvdChmaWxsID0gIiNFNjlGMDAiLCBjb2xvciA9ICJncmF5MjMiLCBhbHBoYSA9IDAuNw0KICAgICAgICAgICAgICAgKSsNCiAgICAjIEFkZCB0aXRsZXMgYW5kIGxhYmVscw0KICB4bGFiKCJWYWx1ZSIpKw0KICB5bGFiKCIiKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQogIA0KICANCiAgIyBUbyByZXR1cm4gbXVsdGlwbGUgb3V0cHV0cywgdXNlIGEgYGxpc3RgDQogIHJldHVybihsaXN0KHN0YXRzLA0KICAgICAgICAgICAgICBoaXN0X2dyYW0gLyBieF9wbHQpDQogICAgICAgICkgIyBFbmQgb2YgcmV0dXJuZWQgb3V0cHV0cw0KICANCn0gIyBFbmQgb2YgZnVuY3Rpb24NCg0KYGBgDQoNCk5vdyB0aGF0IHRoZSBgc2hvd19kaXN0cmlidXRpb24oKWAgZnVuY3Rpb24gaXMgZG9uZSBmb3IsIGxldCdzIGdldCBhIHZhcmlhYmxlL2NvbHVtbiB0byBleGFtaW5lIGFuZCB0aGVuIGNhbGwgdGhlIGZ1bmN0aW9uLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgR2V0IHRoZSB2YXJpYWJsZSB0byBleGFtaW5lDQpjb2wgPC0gZGZfc3R1ZGVudHMgJT4lIHNlbGVjdChHcmFkZSkNCg0KIyBDYWxsIHRoZSBmdW5jdGlvbg0Kc2hvd19kaXN0cmlidXRpb24odmFyX2RhdGEgPSBjb2wsIGJpbndpZHRoID0gMjApDQpgYGANCg0KQWxsIG9mIHRoZSBtZWFzdXJlbWVudHMgb2YgY2VudHJhbCB0ZW5kZW5jeSBhcmUgcmlnaHQgaW4gdGhlIG1pZGRsZSBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24sIHdoaWNoIGlzIHN5bW1ldHJpYyB3aXRoIHZhbHVlcyBiZWNvbWluZyBwcm9ncmVzc2l2ZWx5IGxvd2VyIGluIGJvdGggZGlyZWN0aW9ucyBmcm9tIHRoZSBtaWRkbGUuDQoNClRvIGV4cGxvcmUgdGhpcyBkaXN0cmlidXRpb24gaW4gbW9yZSBkZXRhaWwsIHlvdSBuZWVkIHRvIHVuZGVyc3RhbmQgdGhhdCBzdGF0aXN0aWNzIGlzIGZ1bmRhbWVudGFsbHkgYWJvdXQgdGFraW5nICpzYW1wbGVzKiBvZiBkYXRhIGFuZCB1c2luZyBwcm9iYWJpbGl0eSBmdW5jdGlvbnMgdG8gKmV4dHJhcG9sYXRlIGluZm9ybWF0aW9uKiBhYm91dCB0aGUgZnVsbCAqcG9wdWxhdGlvbiogb2YgZGF0YS4gRm9yIGV4YW1wbGUsIHRoZSBzdHVkZW50IGRhdGEgY29uc2lzdHMgb2YgMjIgc2FtcGxlcywgYW5kIGZvciBlYWNoIHNhbXBsZSB0aGVyZSBpcyBhIGdyYWRlIHZhbHVlLiBZb3UgY2FuIHRoaW5rIG9mIGVhY2ggc2FtcGxlIGdyYWRlIGFzIGEgdmFyaWFibGUgdGhhdCdzIGJlZW4gcmFuZG9tbHkgc2VsZWN0ZWQgZnJvbSB0aGUgc2V0IG9mIGFsbCBncmFkZXMgYXdhcmRlZCBmb3IgdGhpcyBjb3Vyc2UuIFdpdGggZW5vdWdoIG9mIHRoZXNlIHJhbmRvbSB2YXJpYWJsZXMsIHlvdSBjYW4gY2FsY3VsYXRlIHNvbWV0aGluZyBjYWxsZWQgYSAqcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiosIHdoaWNoIGVzdGltYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIGdyYWRlcyBmb3IgdGhlIGZ1bGwgcG9wdWxhdGlvbi4NCg0KQSBbZGVuc2l0eSBwbG90XShodHRwczovL3d3dy5kYXRhLXRvLXZpei5jb20vZ3JhcGgvZGVuc2l0eS5odG1sKSBpcyBhIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgYSBudW1lcmljIHZhcmlhYmxlLiBJdCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbSBhbmQgaXMgdXNlZCBpbiB0aGUgc2FtZSBraW5kIG9mIHNpdHVhdGlvbi4NCg0KYGdncGxvdDIoKWAgaGFzIGdvdCB5b3UgY292ZXJlZCB3aXRoIGBnZW9tX2RlbnNpdHlgIQ0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgZGVuc2l0eSBwbG90DQpzaG93X2RlbnNpdHkgPC0gZnVuY3Rpb24odmFyX2RhdGEpIHsNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KICBsaWJyYXJ5KGRwbHlyKQ0KICBsaWJyYXJ5KHBhdGNod29yaykNCiAgDQogICMgR2V0IHN0YXRpc3RpY3MNCiAgbWVhbl92YWwgPC0gbWVhbihwdWxsKHZhcl9kYXRhKSkNCiAgbWVkX3ZhbCA8LSBtZWRpYW4ocHVsbCh2YXJfZGF0YSkpDQogIG1vZF92YWwgPC0gc3RhdGlwOjptZnYocHVsbCh2YXJfZGF0YSkpDQogIA0KICANCiAgIyBQbG90IHRoZSBkZW5zaXR5IHBsb3QNCiAgZGVuc2l0eV9wbG90IDwtIHZhcl9kYXRhICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcHVsbCh2YXJfZGF0YSkpKSsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9Im9yYW5nZXJlZCIsIGNvbG9yPSJ3aGl0ZSIsIGFscGhhPTAuNCkrDQogICMgQWRkIGxpbmVzIGZvciB0aGUgc3RhdGlzdGljcw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuX3ZhbCwgY29sb3IgPSAnY3lhbicsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjMpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWRfdmFsLCBjb2xvciA9ICdyZWQnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMS4zICkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vZF92YWwsIGNvbG9yID0gJ3llbGxvdycsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjMgKSsNCiAgIyBBZGQgdGl0bGVzIGFuZCBsYWJlbHMNCiAgZ2d0aXRsZSgnRGF0YSBEZW5zaXR5JykrDQogIHhsYWIoJycpKw0KICB5bGFiKCdEZW5zaXR5JykrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KICANCiAgDQogIA0KICByZXR1cm4oZGVuc2l0eV9wbG90KSAjIEVuZCBvZiByZXR1cm5lZCBvdXRwdXRzDQogIA0KfSAjIEVuZCBvZiBmdW5jdGlvbg0KDQoNCg0KIyBHZXQgdGhlIGRlbnNpdHkgb2YgR3JhZGUNCmNvbCA8LSBkZl9zdHVkZW50cyAlPiUgc2VsZWN0KEdyYWRlKQ0Kc2hvd19kZW5zaXR5KHZhcl9kYXRhID0gY29sKQ0KDQpgYGANCg0KQXMgZXhwZWN0ZWQgZnJvbSB0aGUgaGlzdG9ncmFtIG9mIHRoZSBzYW1wbGUsIHRoZSBkZW5zaXR5IG9mIHRoZSBgR3JhZGVgIGNvbHVtbiBzaG93cyB0aGUgY2hhcmFjdGVyaXN0aWMgJ2JlbGwgY3VydmUiIG9mIHdoYXQgc3RhdGlzdGljaWFucyBjYWxsIGEgKm5vcm1hbCogZGlzdHJpYnV0aW9uIHdpdGggdGhlIG1lYW4gYW5kIG1vZGUgYXQgdGhlIGNlbnRlciBhbmQgc3ltbWV0cmljIHRhaWxzLg0KDQpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc3R1ZHkgaG91cnMgZGF0YS4gV29vLWhvbywgdGhpcyBzaG91bGQgYSBicmVlemUgc2luY2Ugd2UnbGwganVzdCBiZSBjYWxsaW5nIHRoZSByZXNwZWN0aXZlIGZ1bmN0aW9ucyDwn6WzIQ0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgR2V0IHRoZSB2YXJpYWJsZSB0byBleGFtaW5lDQpjb2wgPC0gZGZfc3R1ZGVudHMgJT4lIHNlbGVjdChTdHVkeUhvdXJzKQ0KDQojIENhbGwgdGhlIGZ1bmN0aW9uDQpzaG93X2Rpc3RyaWJ1dGlvbih2YXJfZGF0YSA9IGNvbCwgYmlud2lkdGggPSAyKQ0KDQpgYGANCg0KVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc3R1ZHkgdGltZSBkYXRhIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gdGhhdCBvZiB0aGUgZ3JhZGVzLiBOb3RlIHRoYXQgdGhlIHdoaXNrZXJzIG9mIHRoZSBib3ggcGxvdCBvbmx5IGV4dGVuZCB0byBhcm91bmQgNi4wLCBpbmRpY2F0aW5nIHRoYXQgdGhlIHZhc3QgbWFqb3JpdHkgb2YgdGhlIGZpcnN0IHF1YXJ0ZXIgb2YgdGhlIGRhdGEgaXMgYWJvdmUgdGhpcyB2YWx1ZS4gVGhlIG1pbmltdW0gKGAxYCkgaXMgbWFya2VkIHdpdGggYW4gKipvKiosIGluZGljYXRpbmcgdGhhdCBpdCBpcyBzdGF0aXN0aWNhbGx5IGFuICpvdXRsaWVyKiAtIGEgdmFsdWUgdGhhdCBsaWVzIHNpZ25pZmljYW50bHkgb3V0c2lkZSB0aGUgcmFuZ2Ugb2YgdGhlIHJlc3Qgb2YgdGhlIGRpc3RyaWJ1dGlvbi4NCg0KT3V0bGllcnMgY2FuIG9jY3VyIGZvciBtYW55IHJlYXNvbnMuIE1heWJlIGEgc3R1ZGVudCBtZWFudCB0byByZWNvcmQgIjEwIiBob3VycyBvZiBzdHVkeSB0aW1lLCBidXQgZW50ZXJlZCAiMSIgYW5kIG1pc3NlZCB0aGUgIjAiLiBPciBtYXliZSB0aGUgc3R1ZGVudCB3YXMgYWJub3JtYWxseSBsYXp5IHdoZW4gaXQgY29tZXMgdG8gc3R1ZHlpbmchIEVpdGhlciB3YXksIGl0J3MgYSBzdGF0aXN0aWNhbCBhbm9tYWx5IHRoYXQgZG9lc24ndCByZXByZXNlbnQgYSB0eXBpY2FsIHN0dWRlbnQuIExldCdzIHNlZSB3aGF0IHRoZSBkaXN0cmlidXRpb24gbG9va3MgbGlrZSB3aXRob3V0IGl0Lg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgR2V0IHRoZSB2YXJpYWJsZSB0byBleGFtaW5lIHdpdGhvdXQgb3V0bGllcnMNCmNvbCA8LSBkZl9zdHVkZW50cyAlPiUgDQogIHNlbGVjdChTdHVkeUhvdXJzKSAlPiUgDQogIGZpbHRlcihTdHVkeUhvdXJzID4gMSkNCg0KIyBUaGlzIGNvdWxkIGhhdmUgZG9uZSB0aGUgdHJpY2sgdG9vIQ0KI2NvbCA8LSBkZl9zdHVkZW50cyAlPiUgc2VsZWN0KFN0dWR5SG91cnMpICU+JSBmaWx0ZXIoLj4xKQ0KDQojIENhbGwgdGhlIGZ1bmN0aW9uDQpzaG93X2Rpc3RyaWJ1dGlvbih2YXJfZGF0YSA9IGNvbCwgYmlud2lkdGggPSAyLjQpDQpgYGANCg0KSW4gdGhpcyBleGFtcGxlLCB0aGUgZGF0YXNldCBpcyBzbWFsbCBlbm91Z2ggdG8gY2xlYXJseSBzZWUgdGhhdCB0aGUgdmFsdWUgKioxKiogaXMgYW4gb3V0bGllciBmb3IgdGhlICoqU3R1ZHlIb3VycyoqIGNvbHVtbiwgc28geW91IGNhbiBleGNsdWRlIGl0IGV4cGxpY2l0bHkuDQoNCkluIG1vc3QgcmVhbC13b3JsZCBjYXNlcywgaXQncyBlYXNpZXIgdG8gY29uc2lkZXIgb3V0bGllcnMgYXMgYmVpbmcgKnZhbHVlcyB0aGF0IGZhbGwgYmVsb3cgb3IgYWJvdmUgcGVyY2VudGlsZXMgd2l0aGluIHdoaWNoIG1vc3Qgb2YgdGhlIGRhdGEgbGllKi4gRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29kZSB1c2VzIHRoZSBgc3RhdHM6OnF1YW50aWxlKClgIGZ1bmN0aW9uIHRvIGV4Y2x1ZGUgb2JzZXJ2YXRpb25zIGJlbG93IHRoZSAwLjAxdGggcGVyY2VudGlsZSAodGhlIHZhbHVlIGFib3ZlIHdoaWNoIDk5JSBvZiB0aGUgZGF0YSByZXNpZGUpLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgUHJvZHVjZSBhIHF1YW50aWxlIGNvcnJlc3BvbmRpbmcgdG8gMSUNCnEwMSA8LSBkZl9zdHVkZW50cyRTdHVkeUhvdXJzICU+JSANCiAgcXVhbnRpbGUocHJvYnMgPSAxLzEwMCwgbmFtZXMgPSBGQUxTRSkNCg0KIyBHZXQgdGhlIHZhcmlhYmxlIHRvIGV4YW1pbmUNCmNvbCA8LSBkZl9zdHVkZW50cyAlPiUgDQogIHNlbGVjdChTdHVkeUhvdXJzKSAlPiUgDQogIGZpbHRlciguID4gcTAxKQ0KICANCiMgQ2FsbCB0aGUgZnVuY3Rpb24NCnNob3dfZGlzdHJpYnV0aW9uKHZhcl9kYXRhID0gY29sLCBiaW53aWR0aCA9IDIuNCkNCmBgYA0KDQo+ICoqVGlwKio6ICpZb3UgY2FuIGFsc28gZWxpbWluYXRlIG91dGxpZXJzIGF0IHRoZSB1cHBlciBlbmQgb2YgdGhlIGRpc3RyaWJ1dGlvbiBieSBkZWZpbmluZyBhIHRocmVzaG9sZCBhdCBhIGhpZ2ggcGVyY2VudGlsZSB2YWx1ZSAtIGZvciBleGFtcGxlLCB5b3UgY291bGQgdXNlIHRoZSBgc3RhdHM6OnF1YW50aWxlKClgIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIDAuOTkgcGVyY2VudGlsZSBiZWxvdyB3aGljaCA5OSUgb2YgdGhlIGRhdGEgcmVzaWRlLioNCg0KV2l0aCB0aGUgb3V0bGllcnMgcmVtb3ZlZCwgdGhlIGJveCBwbG90IHNob3dzIGFsbCBkYXRhIHdpdGhpbiB0aGUgZm91ciBxdWFydGlsZXMuIE5vdGUgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIGlzIG5vdCBzeW1tZXRyaWMgbGlrZSBpdCBpcyBmb3IgdGhlIGdyYWRlIGRhdGEgdGhvdWdoIC0gdGhlcmUgYXJlIHNvbWUgc3R1ZGVudHMgd2l0aCB2ZXJ5IGhpZ2ggc3R1ZHkgdGltZXMgb2YgYXJvdW5kIDE2IGhvdXJzLCBidXQgdGhlIGJ1bGsgb2YgdGhlIGRhdGEgaXMgYmV0d2VlbiA3IGFuZCAxMyBob3VyczsgVGhlIGZldyBleHRyZW1lbHkgaGlnaCB2YWx1ZXMgcHVsbCB0aGUgbWVhbiB0b3dhcmRzIHRoZSBoaWdoZXIgZW5kIG9mIHRoZSBzY2FsZS4NCg0KTGV0J3MgbG9vayBhdCB0aGUgZGVuc2l0eSBmb3IgdGhpcyBkaXN0cmlidXRpb24uDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBHZXQgdGhlIGRlbnNpdHkgb2YgU3R1ZHlIb3Vycw0Kc2hvd19kZW5zaXR5KGNvbCkNCmBgYA0KDQpUaGlzIGtpbmQgb2YgZGlzdHJpYnV0aW9uIGlzIGNhbGxlZCAqcmlnaHQgc2tld2VkKi4gVGhlIG1hc3Mgb2YgdGhlIGRhdGEgaXMgb24gdGhlIGxlZnQgc2lkZSBvZiB0aGUgZGlzdHJpYnV0aW9uLCBjcmVhdGluZyBhIGxvbmcgdGFpbCB0byB0aGUgcmlnaHQgYmVjYXVzZSBvZiB0aGUgdmFsdWVzIGF0IHRoZSBleHRyZW1lIGhpZ2ggZW5kOyB3aGljaCBwdWxsIHRoZSBtZWFuIHRvIHRoZSByaWdodC4NCg0KIyMjIE1lYXN1cmVzIG9mIHZhcmlhbmNlDQoNClNvIG5vdyB3ZSBoYXZlIGEgZ29vZCBpZGVhIHdoZXJlIHRoZSBtaWRkbGUgb2YgdGhlIGdyYWRlIGFuZCBzdHVkeSBob3VycyBkYXRhIGRpc3RyaWJ1dGlvbnMgYXJlLiBIb3dldmVyLCB0aGVyZSdzIGFub3RoZXIgYXNwZWN0IG9mIHRoZSBkaXN0cmlidXRpb25zIHdlIHNob3VsZCBleGFtaW5lOiBob3cgbXVjaCB2YXJpYWJpbGl0eSBpcyB0aGVyZSBpbiB0aGUgZGF0YT8NCg0KVHlwaWNhbCBzdGF0aXN0aWNzIHRoYXQgbWVhc3VyZSB2YXJpYWJpbGl0eSBpbiB0aGUgZGF0YSBpbmNsdWRlOg0KDQotICAgKipSYW5nZSoqOiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtYXhpbXVtIGFuZCBtaW5pbXVtLiBUaGVyZSdzIG5vIGJ1aWx0LWluIGZ1bmN0aW9uIGZvciB0aGlzLCBidXQgaXQncyBlYXN5IHRvIGNhbGN1bGF0ZSB1c2luZyB0aGUgKiptaW4qKiBhbmQgKiptYXgqKiBmdW5jdGlvbnMuIEFub3RoZXIgYXBwcm9hY2ggd291bGQgYmUgdG8gdXNlIEJhc2UgUidzIGBiYXNlOjpyYW5nZSgpYCB3aGljaCByZXR1cm5zIGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlICptaW5pbXVtKiBhbmQgKm1heGltdW0qIG9mIGFsbCB0aGUgZ2l2ZW4gYXJndW1lbnRzLiBXcmFwcGluZyB0aGlzIGluIGBiYXNlOjpkaWZmKClgIHdpbGwgZ2V0IHlvdSB3ZWxsIG9uIHlvdXIgd2F5IHRvIGZpbmRpbmcgdGhlIHJhbmdlLg0KDQotICAgKipWYXJpYW5jZSoqOiBUaGUgYXZlcmFnZSBvZiB0aGUgc3F1YXJlZCBkaWZmZXJlbmNlIGZyb20gdGhlIG1lYW4uIFlvdSBjYW4gdXNlIHRoZSBidWlsdC1pbiAqKnZhcioqIGZ1bmN0aW9uIHRvIGZpbmQgdGhpcy4NCg0KLSAgICoqU3RhbmRhcmQgRGV2aWF0aW9uKio6IFRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgdmFyaWFuY2UuIFlvdSBjYW4gdXNlIHRoZSBidWlsdC1pbiAqKnNkKiogZnVuY3Rpb24gdG8gZmluZCB0aGlzLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgU2VsZWN0IGNvbHVtbnMgdG8gYW5hbHl6ZSB0aGUgbWVhc3VyZSBvZiB2YXJpYW5jZQ0KY29scyA8LSBkZl9zdHVkZW50cyAlPiUNCiAgc2VsZWN0KGMoR3JhZGUsIFN0dWR5SG91cnMpKQ0KDQojIEFwcGx5IGEgZnVuY3Rpb24gdG8gZWFjaCBjb2x1bW4gaW4gY29scw0KbGFwcGx5KGNvbHMsIGZ1bmN0aW9uKGNvbHVtbil7DQogIHJuZyA8LSBkaWZmKHJhbmdlKGNvbHVtbikpICMgLS0+IHNhbWUgYXM6IG1heChjb2x1bW4pIC0gbWluKGNvbHVtbikNCiAgdmFyIDwtIHZhcihjb2x1bW4pDQogIHN0ZCA8LSBzZChjb2x1bW4pDQogIGdsdWU6OmdsdWUoDQogICcNCiAgLSBSYW5nZToge2Zvcm1hdChyb3VuZChybmcsIDIpLCBuc21hbGwgPSAyKX0NCiAgLSBWYXJpYW5jZSA6IHtmb3JtYXQocm91bmQodmFyLCAyKSwgbnNtYWxsID0gMil9DQogIC0gU3RkLkRldiA6IHtmb3JtYXQocm91bmQoc3RkLCAyKSwgbnNtYWxsID0gMil9JywNCiAgLnNlcCA9ICdcbicpDQp9KQ0KYGBgDQoNCk9mIHRoZXNlIHN0YXRpc3RpY3MsIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgZ2VuZXJhbGx5IHRoZSBtb3N0IHVzZWZ1bC4gSXQgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIHZhcmlhbmNlIGluIHRoZSBkYXRhIG9uIHRoZSBzYW1lIHNjYWxlIGFzIHRoZSBkYXRhIGl0c2VsZiAoc28gZ3JhZGUgcG9pbnRzIGZvciB0aGUgR3JhZGUgZGlzdHJpYnV0aW9uIGFuZCBob3VycyBmb3IgdGhlIFN0dWR5SG91cnMgZGlzdHJpYnV0aW9uKS4gVGhlIGhpZ2hlciB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCB0aGUgbW9yZSB2YXJpYW5jZSB0aGVyZSBpcyB3aGVuIGNvbXBhcmluZyB2YWx1ZXMgaW4gdGhlIGRpc3RyaWJ1dGlvbiB0byB0aGUgZGlzdHJpYnV0aW9uIG1lYW4gLSBpbiBvdGhlciB3b3JkcywgdGhlIGRhdGEgaXMgbW9yZSBzcHJlYWQgb3V0Lg0KDQpXaGVuIHdvcmtpbmcgd2l0aCBhICpub3JtYWwqIGRpc3RyaWJ1dGlvbiwgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiB3b3JrcyB3aXRoIHRoZSBwYXJ0aWN1bGFyIGNoYXJhY3RlcmlzdGljcyBvZiBhIG5vcm1hbCBkaXN0cmlidXRpb24gdG8gcHJvdmlkZSBldmVuIGdyZWF0ZXIgaW5zaWdodC4gVGhpcyBjYW4gYmUgc3VtbWFyaXplZCB1c2luZyB0aGUgYDY44oCTOTXigJM5OS43IHJ1bGVgLCBhbHNvIGtub3duIGFzIHRoZSBgZW1waXJpY2FsIHJ1bGVgd2hpY2ggaXMgZGVzY3JpYmVkIGFzIGZvbGxvd3M6DQoNCkluIGFueSBub3JtYWwgZGlzdHJpYnV0aW9uOg0KDQotICAgQXBwcm94aW1hdGVseSA2OC4yNiUgb2YgdmFsdWVzIGZhbGwgd2l0aGluIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gZnJvbSB0aGUgbWVhbi4NCg0KLSAgIEFwcHJveGltYXRlbHkgOTUuNDUlIG9mIHZhbHVlcyBmYWxsIHdpdGhpbiB0d28gc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuLg0KDQotICAgQXBwcm94aW1hdGVseSA5OS43MyUgb2YgdmFsdWVzIGZhbGwgd2l0aGluIHRocmVlIHN0YW5kYXJkIGRldmlhdGlvbnMgZnJvbSB0aGUgbWVhbi4NCg0KQXMgYSBxdWljayBkZXRvdXIsIGxldCdzIHNlZSB2ZXJpZnkgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGdyYWRlcyBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEdldCB0aGUgdmFyaWFibGUgdG8gZXhhbWluZQ0KY29sIDwtIGRmX3N0dWRlbnRzWydHcmFkZSddDQoNCiMgR2V0IHRoZSBtZWFuDQptZV9hbiA8LSBtZWFuKHB1bGwoY29sKSkNCg0KIyBHZXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbg0Kc3RfZGV2IDwtIHNkKHB1bGwoY29sKSkNCg0KIyBGaW5kIHByb3BvcnRpb24gdGhhdCB3aWxsIGZhbGwgd2l0aGluIDEgc3RhbmRhcmQgZGV2aWF0aW9uDQpvbmVfc3RfZGV2IDwtIHBub3JtKChtZV9hbiArIHN0X2RldiksIG1lYW4gPSBtZV9hbiwgc2QgPSBzdF9kZXYpIC0NCiAgcG5vcm0oKG1lX2FuIC0gc3RfZGV2KSwgbWVhbiA9IG1lX2FuLCBzZCA9IHN0X2RldikNCg0KIyBGaW5kIHByb3BvcnRpb24gdGhhdCB3aWxsIGZhbGwgd2l0aGluIDIgc3RhbmRhcmQgZGV2aWF0aW9uDQp0d29fc3RfZGV2IDwtIHBub3JtKChtZV9hbiArICgyKnN0X2RldikpLCBtZWFuID0gbWVfYW4sIHNkID0gc3RfZGV2KSAtIA0KICBwbm9ybSgobWVfYW4gLSAoMipzdF9kZXYpKSwgbWVhbiA9IG1lX2FuLCBzZCA9IHN0X2RldikNCg0KIyBGaW5kIHByb3BvcnRpb24gdGhhdCB3aWxsIGZhbGwgd2l0aGluIDMgc3RhbmRhcmQgZGV2aWF0aW9uDQp0aHJlZV9zdF9kZXYgPC0gcG5vcm0oKG1lX2FuICsgKDMqc3RfZGV2KSksIG1lYW4gPSBtZV9hbiwgc2QgPSBzdF9kZXYpIC0gDQogIHBub3JtKChtZV9hbiAtICgzKnN0X2RldikpLCBtZWFuID0gbWVfYW4sIHNkID0gc3RfZGV2KQ0KDQpnbHVlOjpnbHVlKA0KICAnDQogIHtmb3JtYXQocm91bmQob25lX3N0X2RldioxMDAsIDIpLCBuc21hbGwgPSAyKX0lIG9mIGdyYWRlcyBmYWxsIHdpdGhpbiBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uIGZyb20gdGhlIG1lYW4uDQogIHtmb3JtYXQocm91bmQodHdvX3N0X2RldioxMDAsIDIpLCBuc21hbGwgPSAyKX0lIG9mIGdyYWRlcyBmYWxsIHdpdGhpbiBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uIGZyb20gdGhlIG1lYW4uDQogIHtmb3JtYXQocm91bmQodGhyZWVfc3RfZGV2KjEwMCwgMiksIG5zbWFsbCA9IDIpfSUgb2YgZ3JhZGVzIGZhbGwgd2l0aGluIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gZnJvbSB0aGUgbWVhbi4NCiAgDQogICcNCikNCmBgYA0KDQpUaGVyZSBpcyBubyBkb3VidCB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZ3JhZGVzIGluZGVlZCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiENCg0KV291bGRuJ3QgaHVydCBpZiB3ZSBzaG93ZWQgdGhpcyBncmFwaGljYWxseSByaWdodD8gQXMgaXQgd2VyZSDwn5KBLCAiVGhlIHNpbXBsZSBncmFwaCBoYXMgLi4uIg0KDQpJbnN0ZWFkIG9mIHVzaW5nIGBnZW9tX2RlbnNpdHlgLCB3ZSdsbCBjYWxjdWxhdGUgdGhlIGRlbnNpdHkgb2YgdGhlIGBHcmFkZWAgY29sdW1uIGFuZCB0aGVuIHVzZSB0aGUgcmVzdWx0cyB0byB3aGlwIHVwIGEgZGVuc2l0eSBwbG90LiBXZSdsbCB0aGVuIHNoYWRlL2ZpbGwgdGhlIGRlbnNpdHkgcGxvdCBkZXBlbmRpbmcgb24gdGhlIG51bWJlciBvZiBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4uDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBHZXQgdGhlIEdyYWRlIGNvbHVtbg0KY29sIDwtIGRmX3N0dWRlbnRzWydHcmFkZSddDQoNCiMgQ29tcHV0ZSBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMNCmRlbnNpdHkgPC0gZGVuc2l0eShwdWxsKGNvbCksIGtlcm5lbCA9ICJnYXVzc2lhbiIpDQoNCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIGNvbnRhaW5pbmcgY29vcmRpbmF0ZXMgd2hlcmUgZGVuc2l0eSBpcyBlc3RpbWF0ZWQgDQojYW5kIHRoZSBjb3JyZXNwb25kaW5nIGVzdGltYXRlZCB2YWx1ZQ0KZGVuc2l0eV94eSA8LSB0aWJibGUoDQogIHggPSBkZW5zaXR5JHgsDQogIHkgPSBkZW5zaXR5JHkNCikNCg0KIyBNYWtlIGRlbnNpdHkgcGxvdHMgZm9yIHZhcmlvdXMgZGV2aWF0aW9ucyBvZiB0aGUgbWVhbg0KDQpwbHRfc2RfMSA8LSBkZW5zaXR5X3h5ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpKw0KICBnZW9tX2xpbmUoKSsNCiAgZ2VvbV9hcmVhKGFlcyh4ID0gaWZlbHNlKHg+KG1lX2FuLXN0X2RldikgJiB4PChtZV9hbitzdF9kZXYpLCB4LCAwKSksIGZpbGwgPSAicGx1bTEiLCBhbHBoYSA9IDAuOCkrDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDUwLCB5ID0gMC4wMSwgbGFiZWwgPSAiMSBzdGQgKDY4LjI2JSkiKSsNCiAgeGxpbSgxLCBtYXgoY29sKSkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lX2FuLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgbGFicyh4ID0gIiIsIHkgPSAiRGVuc2l0eSIpDQoNCg0KIyAyIHN0ZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4NCnBsdF9zZF8yIDwtIGRlbnNpdHlfeHkgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIHkgPSB5KSkrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX2FyZWEoYWVzKHggPSBpZmVsc2UoeD4obWVfYW4tKDIqc3RfZGV2KSkgJiB4PChtZV9hbisoMipzdF9kZXYpKSwgeCwgMCkpLCBmaWxsID0gInBsdW0zIiwgYWxwaGEgPSAwLjcpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSA1MCwgeSA9IDAuMDEsIGxhYmVsID0gIjIgc3RkICg5NS40NSUpIikrDQogIHhsaW0oMSwgbWF4KGNvbCkpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZV9hbiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpDQoNCg0KIyAzIHN0ZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4NCnBsdF9zZF8zIDwtIGRlbnNpdHlfeHkgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIHkgPSB5KSkrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX2FyZWEoYWVzKHggPSBpZmVsc2UoeD4obWVfYW4tKDMqc3RfZGV2KSkgJiB4PChtZV9hbisoMypzdF9kZXYpKSwgeCwgMCkpLCBmaWxsID0gInBsdW00IiwgYWxwaGEgPSAwLjUpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSA1MCwgeSA9IDAuMDEsIGxhYmVsID0gIjMgc3RkICg5OS43MyUpIikrDQogIHhsaW0oMSwgbWF4KGNvbCkpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZV9hbiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIGxhYnMoeCA9ICJHcmFkZSIsIHkgPSAiIikNCg0KDQoNCiMgUGF0Y2hpbmcgdGhpbmdzIHVwDQpwbHRfc2RfMXwgKHBsdF9zZF8yL3BsdF9zZF8zKSsNCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAnYScpDQpgYGANCg0KU2luY2Ugd2Uga25vdyB0aGF0IHRoZSBtZWFuIGdyYWRlIGlzIGA0OS4xOGAsIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgYDIxLjc0YCwgYW5kIGRpc3RyaWJ1dGlvbiBvZiBncmFkZXMgaXMgYG5vcm1hbGA7IHdlIGNhbiBjYWxjdWxhdGUgdGhhdCBgNjguMjYlYCBvZiBzdHVkZW50cyBzaG91bGQgYWNoaWV2ZSBhIGdyYWRlIGJldHdlZW4gYDI3LjQ0YCBhbmQgYDcwLjkyYCBhcyBzaG93biBpbiB0aGUgZmlyc3QgcGxvdCBhYm92ZS4NCg0KVGhlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgd2UndmUgdXNlZCB0byB1bmRlcnN0YW5kIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHN0dWRlbnQgZGF0YSB2YXJpYWJsZXMgYXJlIHRoZSBiYXNpcyBvZiBzdGF0aXN0aWNhbCBhbmFseXNpczsgYW5kIGJlY2F1c2UgdGhleSdyZSBzdWNoIGFuIGltcG9ydGFudCBwYXJ0IG9mIGV4cGxvcmluZyB5b3VyIGRhdGEsIHRoZXJlJ3MgYSBidWlsdC1pbiBCYXNlIFIgZnVuY3Rpb24sIGBiYXNlOjpzdW1tYXJ5KClgLCB0aGF0IHJldHVybnMgdGhlIHJlc3VsdCBzdW1tYXJpZXMgb2YgdGhlIHJlc3VsdHMgb2YgdmFyaW91cyBvYmplY3RzIHBhc3NlZCB0byBpdC4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpzdW1tYXJ5KGRmX3N0dWRlbnRzKQ0KYGBgDQoNCk9mIGNvdXJzZSwgd2l0aCB0aGUgbmVlZCBmb3IgbXVjaCBtb3JlIHJvYnVzdCBkYXRhIGV4cGxvcmF0aW9uIGFuZCByZXBvcnRpbmcsIG1hbnkgcGFja2FnZXMgaGF2ZSBiZWVuIGRldmVsb3BlZCBmb3Igc3VtbWFyaXppbmcgZGF0YS4NCg0KQSBnb29kIGV4YW1wbGUgd291bGQgYmUgdGhlIFtgc3VtbWFyeXRvb2xzYF0oaHR0cHM6Ly9naXRodWIuY29tL2Rjb210b2lzL3N1bW1hcnl0b29scykgcGFja2FnZS4gYHN1bW1hcnl0b29sczo6ZGVzY3IoKWAgZG9lcyBhIHJlbWFya2FibGUgam9iIGF0IGZpbmRpbmcgc3RhdGlzdGljcyBmb3IgbnVtZXJpY2FsIGRhdGEuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KbGlicmFyeShzdW1tYXJ5dG9vbHMpDQpkZXNjcigNCiAgZGZfc3R1ZGVudHMsDQogIHN0YXRzID0gImNvbW1vbiINCikNCmBgYA0KDQojIyA3LiBDb21wYXJpbmcgZGF0YQ0KDQpOb3cgdGhhdCB5b3Uga25vdyBzb21ldGhpbmcgYWJvdXQgdGhlIHN0YXRpc3RpY2FsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpbiB5b3VyIGRhdGFzZXQsIHlvdSdyZSByZWFkeSB0byBleGFtaW5lIHlvdXIgZGF0YSB0byBpZGVudGlmeSBhbnkgYXBwYXJlbnQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcy4NCg0KRmlyc3Qgb2YgYWxsLCBsZXQncyBnZXQgcmlkIG9mIGFueSByb3dzIHRoYXQgY29udGFpbiBvdXRsaWVycyBzbyB0aGF0IHdlIGhhdmUgYSBzYW1wbGUgdGhhdCBpcyByZXByZXNlbnRhdGl2ZSBvZiBhIHR5cGljYWwgY2xhc3Mgb2Ygc3R1ZGVudHMuIFdlIGlkZW50aWZpZWQgdGhhdCB0aGUgU3R1ZHlIb3VycyBjb2x1bW4gY29udGFpbnMgc29tZSBvdXRsaWVycyB3aXRoIGV4dHJlbWVseSBsb3cgdmFsdWVzLCBzbyB3ZSdsbCByZW1vdmUgdGhvc2Ugcm93cy4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIEZpbHRlciB0byByZW1vdmUgb3V0bGllcnMNCmRmX3NhbXBsZSA8LSBkZl9zdHVkZW50cyAlPiUgDQogIGZpbHRlcihTdHVkeUhvdXJzID4gMSkNCmBgYA0KDQojIyMgQ29tcGFyaW5nIG51bWVyaWMgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KDQpUaGUgZGF0YSBpbmNsdWRlcyB0d28gKm51bWVyaWMqIHZhcmlhYmxlcyAoYFN0dWR5SG91cnNgIGFuZCBgR3JhZGVgKSBhbmQgdHdvICpjYXRlZ29yaWNhbCogdmFyaWFibGVzIChgTmFtZWAgYW5kIGBQYXNzYCkuIEluIFIsIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgdXN1YWxseSBzYXZlZCBhcyBbZmFjdG9yc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9mYWN0b3JzLmh0bWwpIG9yIGNoYXJhY3RlciB2ZWN0b3JzLg0KDQpMZXQncyBzdGFydCBieSBjb21wYXJpbmcgdGhlIGBudW1lcmljYCAqU3R1ZHlIb3VycyogY29sdW1uIHRvIHRoZSBgY2F0ZWdvcmljYWxgICpQYXNzKiBjb2x1bW4gdG8gc2VlIGlmIHRoZXJlJ3MgYW4gYXBwYXJlbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG51bWJlciBvZiBob3VycyBzdHVkaWVkIGFuZCBhIHBhc3NpbmcgZ3JhZGUuDQoNClRvIG1ha2UgdGhpcyBjb21wYXJpc29uLCBsZXQncyBjcmVhdGUgYm94IHBsb3RzIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBTdHVkeUhvdXJzIGZvciBlYWNoIHBvc3NpYmxlIFBhc3MgdmFsdWUgKFRSVUUgYW5kIEZBTFNFKS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIFBsb3QgYSBib3ggcGxvdCBjb21wYXJpbmcgU3R1ZHlIb3VycyBhbmQgUGFzcw0KZGZfc2FtcGxlICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gUGFzcywgeSA9IFN0dWR5SG91cnMpKSsNCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gUGFzcyksIGNvbG9yID0gImdyYXkyMyIsIGFscGhhID0gMC43KSsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikrDQogIA0KIyBBZGQgdGl0bGVzIGFuZCBsYWJlbHMNCiAgZ2d0aXRsZSgiQ29tcGFyaW5nIFBhc3Mgc3RhdHVzIGFuZCB0aGUgU3R1ZHkgaG91cnMiKSsNCiAgeGxhYigiUGFzcyIpKw0KICB5bGFiKCJTdHVkeSBIb3VycyIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KYGBgDQoNCkNvbXBhcmluZyB0aGUgU3R1ZHlIb3VycyBkaXN0cmlidXRpb25zLCBpdCdzIGltbWVkaWF0ZWx5IGFwcGFyZW50IChpZiBub3QgcGFydGljdWxhcmx5IHN1cnByaXNpbmcpIHRoYXQgc3R1ZGVudHMgd2hvIGBwYXNzZWRgIHRoZSBjb3Vyc2UgdGVuZGVkIHRvIGBzdHVkeSBmb3IgbW9yZWAgaG91cnMgdGhhbiBzdHVkZW50cyB3aG8gZGlkbid0LiBTbyBpZiB5b3Ugd2FudGVkIHRvIHByZWRpY3Qgd2hldGhlciBvciBub3QgYSBzdHVkZW50IGlzIGxpa2VseSB0byBwYXNzIHRoZSBjb3Vyc2UsIHRoZSBhbW91bnQgb2YgdGltZSB0aGV5IHNwZW5kIHN0dWR5aW5nIG1heSBiZSBhIGdvb2QgcHJlZGljdGl2ZSBmZWF0dXJlLg0KDQojIyMgQ29tcGFyaW5nIG51bWVyaWMgdmFyaWFibGVzDQoNCk5vdyBsZXQncyBjb21wYXJlIHR3byBudW1lcmljIHZhcmlhYmxlcy4gV2UnbGwgc3RhcnQgYnkgY3JlYXRpbmcgYSBiYXIgY2hhcnQgdGhhdCBzaG93cyBib3RoIGdyYWRlIGFuZCBzdHVkeSBob3Vycy4NCg0KVG8gYWNoaWV2ZSB0aGlzLCB3ZSBjYW4gZmlyc3QgdHJhbnNmb3JtIG91ciBkYXRhIHRvIGEgYGxvbmdgIGZvcm1hdCB1c2luZyBgdGlkeXI6OnBpdm90X2xvbmdlcigpYCB0aGVuIHBsYXkgYXJvdW5kIHdpdGggdGhlIGBmaWxsYCBhZXN0aGV0aWMgYW5kIGBwb3NpdGlvbl9kb2RnZSgpYCB0byBwbGFjZSBvdmVybGFwcGluZyBvYmplY3RzIGRpcmVjdGx5ICpiZXNpZGUqIG9uZSBhbm90aGVyLg0KDQpBbHNvLCB3ZSdsbCB0cnkgb3V0IFtgcGFsZXR0ZXJgXShodHRwczovL3BtYXNzaWNvdHRlLmdpdGh1Yi5pby9wYWxldHRlZXJfZ2FsbGVyeS8pLCBhIGNvbGxlY3Rpb24gb2YgbW9zdCBjb2xvciBwYWxldHRlcyBpbiBhIHNpbmdsZSBSIHBhY2thZ2UuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBQaXZvdCBkYXRhIGZyb20gd2lkZSB0byBsb25nDQpkZl9zYW1wbGVfbG9uZyA8LSBkZl9zYW1wbGUgJT4lDQogIHNlbGVjdCgtUGFzcykgJT4lDQogIHBpdm90X2xvbmdlcighTmFtZSwgbmFtZXNfdG8gPSAiTWV0cmljcyIsIHZhbHVlc190byA9ICJWYWx1ZXMiKQ0KDQojIFNob3cgZmlyc3QgMTAgcm93cw0KZGZfc2FtcGxlX2xvbmcgJT4lIA0KICBzbGljZV9oZWFkKG4gPSAxMCkNCg0KIyBQbG90DQpnZ3Bsb3QoZGF0YSA9IGRmX3NhbXBsZV9sb25nLCBhZXMoeCA9IE5hbWUsIHkgPSBWYWx1ZXMpKSsNCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBNZXRyaWNzKSwgYWxwaGEgPSAwLjcsIHN0YXQgPSAiaWRlbnRpdHkiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpKSsNCiAgeGxhYignU3R1ZGVudCcpKw0KICB5bGFiKCcnKSsNCiAgcGFsZXR0ZWVyOjpzY2FsZV9maWxsX3BhbGV0dGVlcl9kKCJjYWxlY29wYWw6OmtlbHAxIikrDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gJyM5NWE1YTYnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9ICdkYXNoZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41KSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkNCiAgICANCiAgKQ0KYGBgDQoNClRoZSBjaGFydCBzaG93cyBiYXJzIGZvciBib3RoIGdyYWRlIGFuZCBzdHVkeSBob3VycyBmb3IgZWFjaCBzdHVkZW50OyBidXQgaXQncyBub3QgZWFzeSB0byBjb21wYXJlIGJlY2F1c2UgdGhlIHZhbHVlcyBhcmUgb24gZGlmZmVyZW50IHNjYWxlcy4gR3JhZGVzIGFyZSBtZWFzdXJlZCBpbiBncmFkZSBwb2ludHMsIGFuZCByYW5nZSBmcm9tIDMgdG8gOTc7IHdoaWxlIHN0dWR5IHRpbWUgaXMgbWVhc3VyZWQgaW4gaG91cnMgYW5kIHJhbmdlcyBmcm9tIDEgdG8gMTYuDQoNCkEgY29tbW9uIHRlY2huaXF1ZSB3aGVuIGRlYWxpbmcgd2l0aCBudW1lcmljIGRhdGEgaW4gZGlmZmVyZW50IHNjYWxlcyBpcyB0byAqbm9ybWFsaXplKiB0aGUgZGF0YSBzbyB0aGF0IHRoZSB2YWx1ZXMgcmV0YWluIHRoZWlyIHByb3BvcnRpb25hbCBkaXN0cmlidXRpb24sIGJ1dCBhcmUgbWVhc3VyZWQgb24gdGhlIHNhbWUgc2NhbGUuIFRvIGFjY29tcGxpc2ggdGhpcywgd2UnbGwgdXNlIGEgdGVjaG5pcXVlIGNhbGxlZCAqYE1pbk1heGAqYHNjYWxpbmdgIHRoYXQgZGlzdHJpYnV0ZXMgdGhlIHZhbHVlcyBwcm9wb3J0aW9uYWxseSBvbiBhIHNjYWxlIG9mIDAgdG8gMS4gWW91IGNvdWxkIHdyaXRlIHRoZSBjb2RlIHRvIGFwcGx5IHRoaXMgdHJhbnNmb3JtYXRpb24gYXMgc2hvd24gaW4gdGhpczsgYnV0IHRoZSBbKipgc2NhbGVzYCoqXShodHRwczovL3NjYWxlcy5yLWxpYi5vcmcvKSBsaWJyYXJ5IHByb3ZpZGVzIGEgc2NhbGVyIChgc2NhbGVzOjpyZXNjYWxlKClgKSB0byBkbyBpdCBmb3IgeW91Lg0KDQpHb29kIG5ld3MgaXMgdGhhdCBTY2FsZXMgaXMgaW5zdGFsbGVkIHdoZW4geW91IGluc3RhbGwgZ2dwbG90MiBvciB0aGUgdGlkeXZlcnNlIQ0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmxpYnJhcnkoc2NhbGVzKQ0KDQojIE5vcm1hbGl6ZSB0aGUgbnVtZXJpYyBjb2x1bW5zDQojIGdyb3VwX2J5IGVuc3VyZXMgU3R1ZHlIb3VycyBhbmQgR3JhZGUgYXJlIG5vcm1hbGlzZWQgaW5kZXBlbmRlbnRseQ0KZGZfbm9ybWFsaXplZCA8LSBkZl9zYW1wbGVfbG9uZyAlPiUgDQogIGdyb3VwX2J5KE1ldHJpY3MpICU+JSANCiAgbXV0YXRlKFZhbHVlcyA9IHJlc2NhbGUoVmFsdWVzLCB0byA9IGMoMCwgMSkpKQ0KDQojIFNob3cgc29tZSBvZiB0aGUgbm9ybWFsaXplZCBkYXRhDQpkZl9ub3JtYWxpemVkICU+JSANCiAgc2xpY2VfaGVhZChuID0gNSkNCg0KIyBQbG90DQpnZ3Bsb3QoZGF0YSA9IGRmX25vcm1hbGl6ZWQsIGFlcyh4ID0gTmFtZSwgeSA9IFZhbHVlcykpKw0KICBnZW9tX2JhcihhZXMoZmlsbCA9IE1ldHJpY3MpLCBhbHBoYSA9IDAuNywgc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSkpKw0KICB4bGFiKCdTdHVkZW50JykrDQogIHlsYWIoJycpKw0KICBwYWxldHRlZXI6OnNjYWxlX2ZpbGxfcGFsZXR0ZWVyX2QoImNhbGVjb3BhbDo6a2VscDEiKSsNCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAnIzk1YTVhNicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gJ2Rhc2hlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSwNCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKQ0KICAgIA0KICApDQpgYGANCg0KV2l0aCB0aGUgZGF0YSBub3JtYWxpemVkLCBpdCdzIGVhc2llciB0byBzZWUgYW4gYXBwYXJlbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gZ3JhZGUgYW5kIHN0dWR5IHRpbWUuIEl0J3Mgbm90IGFuIGV4YWN0IG1hdGNoLCBidXQgaXQgZGVmaW5pdGVseSBzZWVtcyBsaWtlIHN0dWRlbnRzIHdpdGggaGlnaGVyIGdyYWRlcyB0ZW5kIHRvIGhhdmUgc3R1ZGllZCBtb3JlLg0KDQojIyA4LiBGaXR0aW5nIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQoNCkZyb20gdGhlIHByZXZpb3VzIGNvbXBhcmlzb25zLCB0aGVyZSBzZWVtcyB0byBiZSBhIGNvcnJlbGF0aW9uIGJldHdlZW4gc3R1ZHkgdGltZSBhbmQgZ3JhZGU7IGFuZCBpbiBmYWN0LCB0aGVyZSdzIGEgc3RhdGlzdGljYWwgKmNvcnJlbGF0aW9uKiBtZWFzdXJlbWVudCB3ZSBjYW4gdXNlIHRvIHF1YW50aWZ5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSBjb2x1bW5zLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQ29tcHV0ZSBQZWFyc29uIFByb2R1Y3QgTW9tZW50IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50DQpjb3IoZGZfc2FtcGxlJFN0dWR5SG91cnMsIGRmX3NhbXBsZSRHcmFkZSkNCmBgYA0KDQpUaGUgY29ycmVsYXRpb24gc3RhdGlzdGljIGlzIGEgdmFsdWUgYmV0d2VlbiAtMSBhbmQgMSB0aGF0IGluZGljYXRlcyB0aGUgc3RyZW5ndGggb2YgYSByZWxhdGlvbnNoaXAuIFZhbHVlcyBhYm92ZSAwIGluZGljYXRlIGEgKnBvc2l0aXZlKiBjb3JyZWxhdGlvbiAoaGlnaCB2YWx1ZXMgb2Ygb25lIHZhcmlhYmxlIHRlbmQgdG8gY29pbmNpZGUgd2l0aCBoaWdoIHZhbHVlcyBvZiB0aGUgb3RoZXIpLCB3aGlsZSB2YWx1ZXMgYmVsb3cgMCBpbmRpY2F0ZSBhICpuZWdhdGl2ZSogY29ycmVsYXRpb24gKGhpZ2ggdmFsdWVzIG9mIG9uZSB2YXJpYWJsZSB0ZW5kIHRvIGNvaW5jaWRlIHdpdGggbG93IHZhbHVlcyBvZiB0aGUgb3RoZXIpLiBJbiB0aGlzIGNhc2UsIHRoZSBjb3JyZWxhdGlvbiB2YWx1ZSBpcyBjbG9zZSB0byAxOyBzaG93aW5nIGEgc3Ryb25nbHkgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBzdHVkeSB0aW1lIGFuZCBncmFkZS4NCg0KPiAqKk5vdGUqKjogRGF0YSBzY2llbnRpc3RzIG9mdGVuIHF1b3RlIHRoZSBtYXhpbSAiKmNvcnJlbGF0aW9uIGlzIG5vdCBjYXVzYXRpb24qIi4gSW4gb3RoZXIgd29yZHMsIGFzIHRlbXB0aW5nIGFzIGl0IG1pZ2h0IGJlLCB5b3Ugc2hvdWxkbid0IGludGVycHJldCB0aGUgc3RhdGlzdGljYWwgY29ycmVsYXRpb24gYXMgZXhwbGFpbmluZyAqd2h5IG9uZSBvZiB0aGUgdmFsdWVzIGlzIGhpZ2gqLiBJbiB0aGUgY2FzZSBvZiB0aGUgc3R1ZGVudCBkYXRhLCB0aGUgc3RhdGlzdGljcyBkZW1vbnN0cmF0ZXMgdGhhdCBzdHVkZW50cyB3aXRoIGhpZ2ggZ3JhZGVzIHRlbmQgdG8gYWxzbyBoYXZlIGhpZ2ggYW1vdW50cyBvZiBzdHVkeSB0aW1lOyBidXQgdGhpcyBpcyBub3QgdGhlIHNhbWUgYXMgcHJvdmluZyB0aGF0IHRoZXkgYWNoaWV2ZWQgaGlnaCBncmFkZXMgKmJlY2F1c2UqIHRoZXkgc3R1ZGllZCBhIGxvdC4gVGhlIHN0YXRpc3RpYyBjb3VsZCBlcXVhbGx5IGJlIHVzZWQgYXMgZXZpZGVuY2UgdG8gc3VwcG9ydCB0aGUgbm9uc2Vuc2ljYWwgY29uY2x1c2lvbiB0aGF0IHRoZSBzdHVkZW50cyBzdHVkaWVkIGEgbG90ICpiZWNhdXNlKiB0aGVpciBncmFkZXMgd2VyZSBnb2luZyB0byBiZSBoaWdoLg0KDQpBbm90aGVyIHdheSB0byB2aXN1YWxpemUgdGhlIGFwcGFyZW50IGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIG51bWVyaWMgY29sdW1ucyBpcyB0byB1c2UgYSAqc2NhdHRlciogcGxvdC4gV2l0aCBgZ2VvbV9wb2ludGAsIHlvdSdsbCBiZSB3ZWxsIG9uIHlvdXIgd2F5IHRvIHdoaXBwaW5nIHVwIGEgbmVhdCBzY2F0dGVycGxvdC4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpkZl9zYW1wbGUgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBTdHVkeUhvdXJzLCB5ID0gR3JhZGUpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZ3RpdGxlKCdTdHVkeSBUaW1lIHZzIEdyYWRlJykrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCkFnYWluLCBpdCBsb29rcyBsaWtlIHRoZXJlJ3MgYSBkaXNjZXJuaWJsZSBwYXR0ZXJuIGluIHdoaWNoIHRoZSBzdHVkZW50cyB3aG8gc3R1ZGllZCB0aGUgbW9zdCBob3VycyBhcmUgYWxzbyB0aGUgc3R1ZGVudHMgd2hvIGdvdCB0aGUgaGlnaGVzdCBncmFkZXMuDQoNCldlIGNhbiBzZWUgdGhpcyBtb3JlIGNsZWFybHkgYnkgYWRkaW5nIGEgKnJlZ3Jlc3Npb24qIGxpbmUgKG9yIGEgKmxpbmUgb2YgYmVzdCBmaXQqKSB0byB0aGUgcGxvdCB0aGF0IHNob3dzIHRoZSBnZW5lcmFsIHRyZW5kIGluIHRoZSBkYXRhLiBUbyBkbyB0aGlzLCB3ZSdsbCB1c2UgYSBzdGF0aXN0aWNhbCB0ZWNobmlxdWUgY2FsbGVkICpsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24qLg0KDQo+ICoqV2FybmluZyAtIE1hdGggQWhlYWQhKioNCj4NCj4gQ2FzdCB5b3VyIG1pbmQgYmFjayB0byB3aGVuIHlvdSB3ZXJlIGxlYXJuaW5nIGhvdyB0byBzb2x2ZSBsaW5lYXIgZXF1YXRpb25zIGluIHNjaG9vbCwgYW5kIHJlY2FsbCB0aGF0IHRoZSAqc2xvcGUtaW50ZXJjZXB0KiBmb3JtIG9mIGEgbGluZWFyIGVxdWF0aW9uIGxvb2tzIGxpa2UgdGhpczoNCj4NCj4gYHkgPSBteCArIGJgDQo+DQo+IEluIHRoaXMgZXF1YXRpb24sICp5KiBhbmQgKngqIGFyZSB0aGUgY29vcmRpbmF0ZSB2YXJpYWJsZXMsICptKiBpcyB0aGUgc2xvcGUgb2YgdGhlIGxpbmUsIGFuZCAqYiogaXMgdGhlIHktaW50ZXJjZXB0ICh3aGVyZSB0aGUgbGluZSBnb2VzIHRocm91Z2ggdGhlIFktYXhpcykuDQo+DQo+IEluIHRoZSBjYXNlIG9mIG91ciBzY2F0dGVyIHBsb3QgZm9yIG91ciBzdHVkZW50IGRhdGEsIHdlIGFscmVhZHkgaGF2ZSBvdXIgdmFsdWVzIGZvciAqeCogKCpTdHVkeUhvdXJzKikgYW5kICp5KiAoKkdyYWRlKiksIHNvIHdlIGp1c3QgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGludGVyY2VwdCBhbmQgc2xvcGUgb2YgdGhlIHN0cmFpZ2h0IGxpbmUgdGhhdCBsaWVzIGNsb3Nlc3QgdG8gdGhvc2UgcG9pbnRzLiBUaGVuIHdlIGNhbiBmb3JtIGEgbGluZWFyIGVxdWF0aW9uIHRoYXQgY2FsY3VsYXRlcyBhIG5ldyAqeSogdmFsdWUgb24gdGhhdCBsaW5lIGZvciBlYWNoIG9mIG91ciAqeCogKCpTdHVkeUhvdXJzKikgdmFsdWVzIC0gdG8gYXZvaWQgY29uZnVzaW9uLCB3ZSdsbCBjYWxsIHRoaXMgbmV3ICp5KiB2YWx1ZSAqZih4KSogKGJlY2F1c2UgaXQncyB0aGUgb3V0cHV0IGZyb20gYSBsaW5lYXIgZXF1YXRpb24gKioqZioqKnVuY3Rpb24gYmFzZWQgb24gKngqKS4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgb3JpZ2luYWwgKnkqICgqR3JhZGUqKSB2YWx1ZSBhbmQgdGhlICpmKHgpKiB2YWx1ZSBpcyB0aGUgKmVycm9yKiBiZXR3ZWVuIG91ciByZWdyZXNzaW9uIGxpbmUgYW5kIHRoZSBhY3R1YWwgKkdyYWRlKiBhY2hpZXZlZCBieSB0aGUgc3R1ZGVudC4gT3VyIGdvYWwgaXMgdG8gY2FsY3VsYXRlIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IGZvciBhIGxpbmUgd2l0aCB0aGUgbG93ZXN0IG92ZXJhbGwgZXJyb3IuDQo+DQo+IFNwZWNpZmljYWxseSwgd2UgZGVmaW5lIHRoZSBvdmVyYWxsIGVycm9yIGJ5IHRha2luZyB0aGUgZXJyb3IgZm9yIGVhY2ggcG9pbnQsIHNxdWFyaW5nIGl0LCBhbmQgYWRkaW5nIGFsbCB0aGUgc3F1YXJlZCBlcnJvcnMgdG9nZXRoZXIuIFRoZSBsaW5lIG9mIGJlc3QgZml0IGlzIHRoZSBsaW5lIHRoYXQgZ2l2ZXMgdXMgdGhlIGxvd2VzdCB2YWx1ZSBmb3IgdGhlIHN1bSBvZiB0aGUgc3F1YXJlZCBlcnJvcnMgLSBoZW5jZSB0aGUgbmFtZSAqbGVhc3Qgc3F1YXJlcyByZWdyZXNzaW9uKi4NCg0KRm9ydHVuYXRlbHksIHlvdSBkb24ndCBuZWVkIHRvIGNvZGUgdGhlIHJlZ3Jlc3Npb24gY2FsY3VsYXRpb24geW91cnNlbGYgLSB0aGUgVGhlIFIgU3RhdHMgUGFja2FnZSAqKmBzdGF0c2AqKiBwcm92aWRlcyB0aGUgKipgbG0oKWAqKiBmdW5jdGlvbiB0byBkbyB0aGUgaGFyZCB3b3JrIGZvciB5b3UuIGxtKCkgdGFrZXMgYSBmb3JtdWxhIG9mIHRoZSBmb3JtOg0KDQpgb3V0Y29tZSB+IHByZWRpY3RvcmANCg0KVGhpcyByZXR1cm5zIChhbW9uZyBvdGhlciB0aGluZ3MpIHRoZSBjb2VmZmljaWVudHMgeW91IG5lZWQgZm9yIHRoZSBzbG9wZSBlcXVhdGlvbiAtIHNsb3BlICgqbSopIGFuZCBpbnRlcmNlcHQgKCpiKikgYmFzZWQgb24gYSBnaXZlbiBwYWlyIG9mIHZhcmlhYmxlIHNhbXBsZXMgeW91IHdhbnQgdG8gY29tcGFyZS4NCg0KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIERyb3AgYWxsIGNvbHVtbnMgZXhjZXB0IEdyYWRlIGFuZCBTdHVkeUhvdXJzDQpkZl9yZWdyZXNzaW9uIDwtIGRmX3NhbXBsZSAlPiUgDQogIHNlbGVjdChjKEdyYWRlLCBTdHVkeUhvdXJzKSkNCg0KIyBGaXQgYSBsaW5lYXIgbW9kZWwNCmxtX2RmX3JlZ3Jlc3Npb24gPC0gbG0oR3JhZGUgfiBTdHVkeUhvdXJzLCBkYXRhID0gZGZfcmVncmVzc2lvbikNCg0KIyBHZXQgdGhlIHJlZ3Jlc3Npb24gc2xvcGUgYW5kIGludGVyY2VwdA0KaW50ZXJjZXB0X2IgPC0gbG1fZGZfcmVncmVzc2lvbiRjb2VmZmljaWVudHNbMV0NCnNsb3BlX20gPC0gbG1fZGZfcmVncmVzc2lvbiRjb2VmZmljaWVudHNbMl0NCmdsdWUoDQogICdzbG9wZToge2Zvcm1hdChyb3VuZChzbG9wZV9tLCA0KSwgbnNtYWxsID0gNCl9DQogIHktaW50ZXJjZXB0OiB7Zm9ybWF0KHJvdW5kKGludGVyY2VwdF9iLCA0KSwgbnNtYWxsID0gNCl9DQogIHNv4oCmDQogICBmKHgpID0ge2Zvcm1hdChyb3VuZChzbG9wZV9tLCA0KSwgbnNtYWxsID0gNCl9eCArIHtmb3JtYXQocm91bmQoaW50ZXJjZXB0X2IsIDQpLCBuc21hbGwgPSA0KX0nDQopDQoNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBlcXVhdGlvbiBgZih4KSA9IDYuMzEzNHggKyAtMTcuOTE2NGAgb2YgdGhlIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBsaW5lLCB3ZSBjYW4gdXNlIHRoYXQgdG8gZXN0aW1hdGUgdGhlIGdyYWRlIGdpdmVuIHRoZSBhbW91bnQgb2YgaG91cnMgc3BlbnQgc3R1ZHlpbmcuDQoNCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBVc2UgdGhlIGZ1bmN0aW9uIChteCArIGIpIHRvIGNhbGN1bGF0ZSBmKHgpIGZvciBlYWNoIHggKFN0dWR5SG91cnMpIHZhbHVlDQpkZl9yZWdyZXNzaW9uIDwtIGRmX3JlZ3Jlc3Npb24gJT4lIA0KICBtdXRhdGUoJ2Z4JyA9IChzbG9wZV9tICogU3R1ZHlIb3VycyArIGludGVyY2VwdF9iKSwNCiAgICAgICAgICdlcnJvcicgPSBmeCAtIEdyYWRlKQ0KDQoNCiMgU2hvdyBkYXRhIGZyYW1lDQpkZl9yZWdyZXNzaW9uDQpgYGANCg0KV2Ugbm93IGhhdmUgYSBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgdGhlIGZvbGxvd2luZyB2YWx1ZXM6DQoNCi0gICBUaGUgKipTdHVkeUhvdXJzKiogZm9yIGVhY2ggc3R1ZGVudC4NCg0KLSAgIFRoZSAqKkdyYWRlKiogYWNoaWV2ZWQgYnkgZWFjaCBzdHVkZW50Lg0KDQotICAgVGhlICoqKmYoeCkqKiogdmFsdWUgY2FsY3VsYXRlZCB1c2luZyB0aGUgcmVncmVzc2lvbiBsaW5lIGNvZWZmaWNpZW50cy4NCg0KLSAgIFRoZSAqZXJyb3IqIGJldHdlZW4gdGhlIGNhbGN1bGF0ZWQgKioqZih4KSoqKiB2YWx1ZSBhbmQgdGhlIGFjdHVhbCAqKkdyYWRlKiogdmFsdWUuDQoNClNvbWUgb2YgdGhlIGVycm9ycywgcGFydGljdWxhcmx5IGF0IHRoZSBleHRyZW1lIGVuZHMsIGFyZSBxdWl0ZSBsYXJnZSAodXAgdG8gb3ZlciAxNy41IGdyYWRlIHBvaW50cyk7IGJ1dCBpbiBnZW5lcmFsLCB0aGUgbGluZSBpcyBwcmV0dHkgY2xvc2UgdG8gdGhlIGFjdHVhbCBncmFkZXMuDQoNCk5vdywgdGltZSB0byBzZWUgdGhpcyB2aXN1YWxseSDwn5OILiBXZSdsbCBjcmVhdGUgdGhlIHNjYXR0ZXIgcGxvdCBvZiB0aGUgc2FtcGxlIHN0dWR5IGhvdXJzIGFuZCBncmFkZXMgYXMgYmVmb3JlLCBhbmQgdGhlbiBhZGQgYSBsaW5lIG9mIGJlc3QgZml0IGJhc2VkIG9uIHRoZSBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmRmX3NhbXBsZSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFN0dWR5SG91cnMsIHkgPSBHcmFkZSkpKw0KICBnZW9tX3BvaW50KCkrDQogICMgQWRkIGEgbGluZSBiYXNlZCBvbiB0aGUgbGluZWFyIG1vZGVsDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGludGVyY2VwdF9iLCBzbG9wZSA9IHNsb3BlX20sIGNvbG9yID0gInNwcmluZ2dyZWVuMyIsIHNpemUgPSAxKSsNCiAgZ2d0aXRsZSgnU3R1ZHkgVGltZSB2cyBHcmFkZScpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQo+IEFkZGluZyBhIGxpbmUgYmFzZWQgb24gdGhlIGxpbmVhciBtb2RlbCBjYW4gYmUgZG9uZSBtdWNoIGVhc2lseSBieSByZXBsYWNpbmcNCj4NCj4gYGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGludGVyY2VwdF9iLCBzbG9wZSA9IHNsb3BlX20sIC4uLilgDQo+DQo+IHdpdGgNCj4NCj4gYGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRilgDQoNCiMjIyBVc2luZyB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIHByZWRpY3Rpb24NCg0KTm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIHRoZSBzdHVkeSB0aW1lIGFuZCBncmFkZSByZWxhdGlvbnNoaXAsIHlvdSBjYW4gdXNlIHRoZW0gaW4gYSBmdW5jdGlvbiB0byBlc3RpbWF0ZSB0aGUgZXhwZWN0ZWQgZ3JhZGUgZm9yIGEgZ2l2ZW4gYW1vdW50IG9mIHN0dWR5Lg0KDQpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgRGVmaW5lIGEgZnVuY3Rpb24gYmFzZWQgb24gb3VyIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzDQpmIDwtIGZ1bmN0aW9uKHgpIHsNCiAgc2xvcGVfbSA8LSAgNi4zMTM0DQogIHlfaW50ZXJjZXB0X2IgPC0gIC0xNy45MTY0DQogICMgeSA9IG14ICsgYg0KICBmeCA8LSAgc2xvcGVfbSAqIHggKyB5X2ludGVyY2VwdF9iDQogIHJldHVybihmeCkNCn0NCg0KIyBBc3N1bWUgdGhlIHN0dWRlbnQgc3R1ZGllZCBmb3IgMTQgaG91cnMgcGVyIHdlZWsNCnN0dWR5X3RpbWUgPC0gIDE0DQoNCiMgR2V0IGYoeCkgZm9yIHN0dWR5IHRpbWUNCnByZWRpY3Rpb24gPC0gZih4ID0gc3R1ZHlfdGltZSkNCg0KIyBHcmFkZSBjYW4ndCBiZSBsZXNzIHRoYW4gMCBvciBtb3JlIHRoYW4gMTAwDQpleHBlY3RlZF9ncmFkZSA8LSBtYXgoMCwgbWluKDEwMCwgcHJlZGljdGlvbikpDQoNCiMgUHJpbnQgdGhlIGVzdGltYXRlZCBncmFkZQ0KZ2x1ZSgNCiAgJ1N0dWR5aW5nIGZvciB7c3R1ZHlfdGltZX0gaG91cnMgcGVyIHdlZWsgbWF5IHJlc3VsdCBpbiBhIGdyYWRlIG9mIHtmb3JtYXQocm91bmQoZXhwZWN0ZWRfZ3JhZGUpKX0NCiAgJykNCmBgYA0KDQpTbyBieSBhcHBseWluZyBzdGF0aXN0aWNzIHRvIHNhbXBsZSBkYXRhLCB5b3UndmUgZGV0ZXJtaW5lZCBhIGByZWxhdGlvbnNoaXBgIGJldHdlZW4gc3R1ZHkgdGltZSBhbmQgZ3JhZGU7IGFuZCBlbmNhcHN1bGF0ZWQgdGhhdCByZWxhdGlvbnNoaXAgaW4gYSBgZ2VuZXJhbCBmdW5jdGlvbmAgdGhhdCBjYW4gYmUgdXNlZCB0byBgcHJlZGljdGAgYSBncmFkZSBmb3IgYSBnaXZlbiBhbW91bnQgb2Ygc3R1ZHkgdGltZS4NCg0KVGhpcyB0ZWNobmlxdWUgaXMgaW4gZmFjdCB0aGUgYmFzaWMgcHJlbWlzZSBvZiBtYWNoaW5lIGxlYXJuaW5nLiBZb3UgY2FuIHRha2UgYSBzZXQgb2Ygc2FtcGxlIGRhdGEgdGhhdCBpbmNsdWRlcyBvbmUgb3IgbW9yZSAqYGZlYXR1cmVzYCogKGluIHRoaXMgY2FzZSwgdGhlIG51bWJlciBvZiBob3VycyBzdHVkaWVkKSBhbmQgYSBrbm93biAqYGxhYmVsYCogdmFsdWUgKGluIHRoaXMgY2FzZSwgdGhlIGdyYWRlIGFjaGlldmVkKSBhbmQgdXNlIHRoZSBgc2FtcGxlIGRhdGFgIHRvIGBkZXJpdmUgYSBmdW5jdGlvbmAgdGhhdCBjYWxjdWxhdGVzIGBwcmVkaWN0ZWQgbGFiZWwgdmFsdWVzYCBmb3IgYW55IGdpdmVuIHNldCBvZiBmZWF0dXJlcy4NCg0KIyMgOS4gU3VtbWFyeQ0KDQpOb3csIGl0J3MgdGltZSB3ZSB3cmFwcGVkIGl0IHVwISBDb25ncmF0dWxhdGlvbnMg8J+Rj/CfkY8gb24gZmluaXNoaW5nIHRoZSBmaXJzdCBtb2R1bGUuDQoNCkluIHRoaXMgbW9kdWxlLCB5b3UgbGVhcm5lZCBob3cgdG8gdXNlIFIgdG8gZXhwbG9yZSwgdmlzdWFsaXplLCBhbmQgbWFuaXB1bGF0ZSBkYXRhLiBEYXRhIGV4cGxvcmF0aW9uIGlzIGF0IHRoZSBjb3JlIG9mIGRhdGEgc2NpZW5jZSwgYW5kIGlzIGEga2V5IGVsZW1lbnQgaW4gZGF0YSBhbmFseXNpcyBhbmQgbWFjaGluZSBsZWFybmluZy4NCg0KQmUgc3VyZSB0byBqb2luIHVzIGluIHRoZSBuZXh0IG1vZHVsZSBhcyB3ZSBkaXZlIGludG8gaG93IHRvIHRyYWluIGFuZCBldmFsdWF0ZSByZWdyZXNzaW9uIG1vZGVscy4NCg0KKipGdXJ0aGVyIHJlYWRpbmcqKg0KDQotICAgSC4gV2lja2hhbSBhbmQgRy4gR3JvbGVtdW5kLCBbKlIgZm9yIERhdGEgU2NpZW5jZTogVmlzdWFsaXplLCBNb2RlbCwgVHJhbnNmb3JtLCBUaWR5LCBhbmQgSW1wb3J0IERhdGEqXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykuDQoNCi0gICBILiBXaWNraGFtLCBbQWR2YW5jZWQgUl0oaHR0cHM6Ly9hZHYtci5oYWRsZXkubnovKQ0KDQotICAgRy4gR3JvbGVtdW5kLCBbKkhhbmRzLU9uIFByb2dyYW1taW5nIHdpdGggUiAtIFdyaXRlIHlvdXIgb3duIGZ1bmN0aW9ucyBhbmQgc2ltdWxhdGlvbnMqXShodHRwczovL3JzdHVkaW8tZWR1Y2F0aW9uLmdpdGh1Yi5pby9ob3ByLykuDQoNCkhhcHB5IExlYXJuaW5nLA0KDQpbRXJpYyAoUl9pYyldKGh0dHBzOi8vdHdpdHRlci5jb20vZXJpY250YXkpIChHb2xkIE1pY3Jvc29mdCBMZWFybiBTdHVkZW50IEFtYmFzc2Fkb3IpIGFuZCBbSWFuXShodHRwczovL3R3aXR0ZXIuY29tL0lhbk55YWdhND9zPTA5KSAoQ28tb3JnYW5pemVyIERla3V0UiBEYXRhIFNjaWVuY2UgQ29tbXVuaXR5KS4NCg0KVGhhbmsgeW91IHRvOg0KDQotICAgRmxvcmVuY2UgS3VyaWENCg0KLSAgIEpvc2ggKEdvbGQgTWljcm9zb2Z0IExlYXJuIFN0dWRlbnQgQW1iYXNzYWRvcikNCg0KLSAgIEJldGhhbnkgKEdvbGQgTWljcm9zb2Z0IExlYXJuIFN0dWRlbnQgQW1iYXNzYWRvcikNCg0KZm9yIHRoZWlyIHZhbHVhYmxlIGZlZWRiYWNrIGFuZCBzdWdnZXN0aW9ucy4NCg==