Below is a warm-up graph to get you going with R.

plot(cars, col = "darkblue", cex = 2)

The dataset used for this analysis is Prestige from the package car. First, we need to install the required packages for our analysis.

# Load the packages that contain the dataset and the data viz package
library(car) # Package containing the dataset
library(ggplot2) # Package for some nice and cool visualizations
library(MASS) # Package for box-cox transformation

Prestige is a dataset of Canadian Occupations. The Prestige data frame contains 102 rows and 6 columns. The observations relate to occupations. The data frame includes the following columns:

  1. Variable education is the average education of occupational incumbents, years, in 1971.

  2. variable income is the average income of incumbents, in 1971.

  3. variable women is the percentage of incumbents who are women.

  4. Variable prestige is Pineo-Porter score for occupation, from a social survey conducted in mid-1960s.

  5. Variable census is the Canadian Census occupational code.

  6. Variable type relates to the type of occupation. A factor with levels (note: out of order): bc, Blue Collar; prof, Professional, Management, and Technical; wc, White Collar.

Source of the dataset is: Canada(1971) Census of Canada. Vol. 3, Part 6. Statistics Canada [pp. 19-1-19-21].

  1. SUMMARY and DESCRIPTIVE STATISTICS

Summary (or descriptive) statistics are the first figures used to represent nearly every dataset. They also form the foundations for more complicated computations and analyses. Therefore, they are essential to the analysis process. In this paper, we will explore the ways in which R can be used to calculate summary statistics, including mean, standard deviation, range, and percentile. Also included here is the summary function, which is one of the most useful tools in the R set of commands.

First, let us inspect the Prestige dataset.

# Display or print the first 10 observations of our dataset
print(head(Prestige, n = 10))

We observe that the rows of our dataset refer to occupations. Some of the occupations are gov.administrators, general.managers, accountants, chemists, physicists, biologists, architects, civil.engineers and mining.engineers etc. In addition, each occupation has its own records. We can also explore the nature of variables/columns we have in the Prestige dataset, using ls(DATAVAR) Or names(DATAVAR).

# To see the variable in the dataset; Use names(dataset) or ls(dataset)
ls(Prestige)
[1] "census"    "education" "income"    "prestige"  "type"      "women"    
# To see the number of columns and number of rows in the Prestige dataset; use ncol(dataset) and nrow(dataset)
ncol(Prestige)
[1] 6
nrow(Prestige)
[1] 102

From the result output, the Prestige dataset contains 6 variables and 102 rows. A more advanced technique to see the structure of the dataset is to use the str(DATAVAR) function.

# A more advanced and complete way to see the structure of our dataset
str(Prestige)
'data.frame':   102 obs. of  6 variables:
 $ education: num  13.1 12.3 12.8 11.4 14.6 ...
 $ income   : int  12351 25879 9271 8865 8403 11030 8258 14163 11377 11023 ...
 $ women    : num  11.16 4.02 15.7 9.11 11.68 ...
 $ prestige : num  68.8 69.1 63.4 56.8 73.5 77.6 72.6 78.1 73.1 68.8 ...
 $ census   : int  1113 1130 1171 1175 2111 2113 2133 2141 2143 2153 ...
 $ type     : Factor w/ 3 levels "bc","prof","wc": 2 2 2 2 2 2 2 2 2 2 ...

Prestige is a data.frame, a datatype with more than one row and column. Prestige includes 5 numeric variables and one categorical variable. From here, we can now perform a summary and descriptive statistics of our dataset.

MEAN of EACH VARIABLE

In R, a mean can be calculated on an isolated variable via the mean(VAR) command, VAR is the name of the variable whose mean we want to compute. Alternative, the mean can be calculated for all the variables in the dataset using mean(DATAVAR) function, where DATAVAR is the name of the dataset containing the variables. For analysis purposes, we are going to exclude the variables census and type from the descriptive statistics. To select a subset of a dataset, use subset(DATAVAR, select = c(“VAR1”, “VAR2”, “VAR3”….“VARi”)) command. You can also type ?subset() in your R console followed by ENTER to learn how to subset() vectors, matrices and data frames. The code below demonstrates how to select a subset of Prestige dataset.

# Subsetting education, income, women, and prestige from the dataset Prestige
subset.data <- subset(Prestige, select = c("education", "income", "women", "prestige"))
# Checking subset.data to make sure we have the needed subset
str(subset.data)
'data.frame':   102 obs. of  4 variables:
 $ education: num  13.1 12.3 12.8 11.4 14.6 ...
 $ income   : int  12351 25879 9271 8865 8403 11030 8258 14163 11377 11023 ...
 $ women    : num  11.16 4.02 15.7 9.11 11.68 ...
 $ prestige : num  68.8 69.1 63.4 56.8 73.5 77.6 72.6 78.1 73.1 68.8 ...

From the above output, we see that we got the subset we need. Let’s find the mean of each variable in the selected subset.

# Calculate the mean of a variable with mean(DATAVAR$VAR); mean of variable education
mean(subset.data$education)
[1] 10.73804

The mean year of education was 11 years in Canada in 1971. That means that the average Canadian had a least a High School education in 1971.

# Mean of income variable
mean(subset.data$income)
[1] 6797.902

Here the mean income for incumbents in 1971 is about $6,798.

# Mean of percentage of incumbents who are women
mean(subset.data$women)
[1] 28.97902

The average percentage of incumbents in the survey who are women is about 29%.

# The last variable is prestige
mean(subset.data$prestige)
[1] 46.83333

The mean score of Pineo_Porter measure for occupation is about 47. The computations above are done on individual variables. Alternatively, a mean can be calculated for each of the variables in the dataset by using the formula summary(dataset). We will approach this alternative later. Let’s find the standard deviation of each variable in our subset dataset.

STANDARD DEVIATION OF EACH VARIABLE

Standard deviations are calculated in the same way as means within R. The standard deviation of a single variable can be computed using the formula sd(VAR), where VAR is the name of the variable whose standard deviation you want to retrieve. Standard deviation measures how spread your data are. The codes below demonstrate the use of the standard deviation function.

# What is the standard deviation of education?
sd(subset.data$education)
[1] 2.728444
# What is the standard deviation of income?
sd(subset.data$income)
[1] 4245.922
# What is the standard deviation of women?
sd(subset.data$women)
[1] 31.72493
# What is the standard deviation of prestige?
sd(subset.data$prestige)
[1] 17.20449

RANGE of EACH VARIABLE : MINIMUM & MAXIMUM

Continuing in the same trajectory, minimum can be computed on a single variable using the min(VAR) formula. In the same token, max(VAR) operates similarly. Minimum and Maximum give the min and max of individual variables in the dataset. The codes below show how to calculate minimums and maximums.

# Minimum and maximum years of education of survey participants in 1971
min(subset.data$education) 
[1] 6.38
max(subset.data$education)
[1] 15.97

The minimum year of incumbents’ education in 1971 was about 6 years. And the maximum year of participants’ education was 16 years, meaning we had some college-educated incumbents in the survey.

# Minimum and Maximum years of income of survey participants in 1971
min(subset.data$income); max(subset.data$income)
[1] 611
[1] 25879

From the output, the minimum income is 611 and the maximum income is about $26000. This shows large gaps in income distribution among survey participants. The maximum income is about four times the minimum one.

# Minimum and Maximum percentage of incumbents who are women.
min(subset.data$women); max(subset.data$women)
[1] 0
[1] 97.51

From the output, we may infer that a large percentage of women participated in the survey.

# Minimum and maximum score for occupation
min(subset.data$prestige) ; max(subset.data$prestige)
[1] 14.8
[1] 87.2

The range for prestige scores is from ~ 15 to ~ 87. The Range() function can provide minimums and maximums of different variables in our subset dataset.

RANGE

The range of a particular variable, that is, its minimum and maximum, can be retrieved using the range(VAR) command. Like with min and max functions, using range(dataset) is not very useful since it considers the entire dataset, rather than each individual variable. Consequently, it is recommended that ranges be computed on individual variables. These computations are demonstrated in the following codes:

# Calculate the range of a variable with range(VAR)
# Range of variable education
range(subset.data$education)
[1]  6.38 15.97
# Range of variable income
range(subset.data$income)
[1]   611 25879
# Range of variable women
range(subset.data$women)
[1]  0.00 97.51
# Range of variable prestige
range(subset.data$prestige)
[1] 14.8 87.2

PERCENTILES : VALUES from PERCENTILES (QUANTILE)

You can get more insight into the distribution of a set of observations by examining quantiles. A quantile is a value computed from a collection of numeric measurements that indicates an observation’s rank when compared to all other present observations. Alternatively, quantile can be expressed as a percentile, this is identical but on a percent scale of 0 to 100.

Obtaining quantiles and percentile in R is done using the quantile() function. The command is written quantile(VAR, c(PROB1, PROB2, PROB3,….PROBi)) or quantile(VAR, prob = c(prob value1, prob value 2, prob value 3…prob valuei)).

# Calculate the 25th, 50th, 75th, and 95th percentiles for education in the subset dataset
quantile(subset.data$education, prob = c(.25, .50, .75, .95))
    25%     50%     75%     95% 
 8.4450 10.5400 12.6475 15.4290 
quantile(subset.data$education, c(.25, .50, .75, .95))
    25%     50%     75%     95% 
 8.4450 10.5400 12.6475 15.4290 

We have used two slightly different codes that lead to the same output. From the output, we see that 25% of average years of education was about 8 years of education, the median year of education was about 10.5 years of education, 75% of average education of incumbents was about 13 years of education, and 95% of participants have about 16 years of education. Also, this may explain the wide gap in wage range since education seems to be correlated to income.

# Calculate the 25th, 50th, 75th, and 95th percentiles for income in the dataset
quantile(subset.data$income, prob = c(.25, .50, .75, .95))
     25%      50%      75%      95% 
 4106.00  5930.50  8187.25 14156.45 

For the income variable, let find the 0 th, the 25th , the 50th, the 75th, and the 100th percentiles.

# Calculate the 0th, 25th, 50th, 75th, and 100th percentiles
quantile(subset.data$income, c(0, .25, .50, .75, 1))
      0%      25%      50%      75%     100% 
  611.00  4106.00  5930.50  8187.25 25879.00 

The median income of survey participants was $US 5930.50 in 1971. Higher income tended to be in the upper bounds of the distribution. Income distribution seems to reflect the levels of education of participants in the survey. The more educated you are, the higher the likelihood of being in the upper income’s bound.

# Calculate the 0th, 25th, 50th, 75th, and 100th perentiles for women
quantile(subset.data$women, prob = c(0, .25, .50, .75, 1))
     0%     25%     50%     75%    100% 
 0.0000  3.5925 13.6000 52.2025 97.5100 

The minimum proportion of incumbents who are women is 0, and the maximum proportion of participants who are women is ~ 98. Finally, the median number of women is ~ 17.

# Calculate the 0th, 25th, 50th, 75th, and 100th percentiles for prestige
quantile(subset.data$prestige, c(0, .25, .50, .75, 1))
    0%    25%    50%    75%   100% 
14.800 35.225 43.600 59.275 87.200 

Here we have used the quantile function to obtain what is called the FIVE-NUMBER SUMMARY of subset.data, comprised of 0th percentile(the minimum), the 25th percentile, the 50th percentile, and the 100th percentile(the maximum). The 0.25th quantile is referred to as the first or LOWER QUARTILE, and 0.75th quantile is referred to as the third or UPPER QUARTILE. Also, the 50th percentile refers to the MEDIAN. The median is the second quartile, with the maximum value being the fourth quartile.

PERCENTILES FROM VALUES (PERCENTILE RANK)

In the opposite situation, where a percentile rank corresponding to a given value is needed, one has to devise a custom method. Here are the steps involved in computing a percentile rank.

  1. Count the number of data points that are at or below the given value
  2. Divide by the total number of data points
  3. multiple by 100

The formula for calculating a percentile rank can be derived from this command: percentile rank = length(VAR[VAR <= VALUE]) / length(VAR) * 100. length(VAR[VAR <= VALUE]) counts the number of data points in a variable that are below the given value. The ‘<=’ can be replaced with other operators, such as ‘<’, ‘>’, and =. The length(VAR) counts the number of data points in the variable. The final step is to multiply the result by 100 to transform the decimal value into a percentage. Let’s apply these steps in the following examples:

# In the sample, 8 years of education is at what percentile rank?
length(subset.data$education[subset.data$education <= 8]) / length(subset.data$education) * 100
[1] 19.60784

8 years of education is at 19.60 percentile rank.

# In the sample, $10000 is at what percentile rank?
length(subset.data$income[subset.data$income <= 10000]) / length(subset.data$income) * 100
[1] 87.2549
# In the sample, $10000 is at what percentile rank?
length(subset.data$income[subset.data$income >= 10000]) / length(subset.data$income) * 100
[1] 12.7451

The percentile rank reveals some interesting facts on income distribution of survey incumbents. About 13 % of participants earned more than or equal to $10,000.00 in 1971, while about 87 % earned less than or equal to 10,000.00. Similarly, let’s find the percentile rank of income above and below, say 15,000 dollars.

# In the sample, income less than or equal to say 15000 is at what percentile rank?
length(subset.data$income[subset.data$income <= 15000]) / length(subset.data$income) * 100
[1] 96.07843
# In the sample, income greater than or equal to say 15000 is at percentile rank?
length(subset.data$income[subset.data$income >= 15000]) / length(subset.data$income) * 100
[1] 3.921569

These two outputs confirm the wide gap in income distribution among survey participants. A useful multipurpose function in R is the use of the SUMMARY(X) function, as I previously mentioned.

SUMMARY

A very useful function in R is summary(x), where x can be one of any number of objects, including datasets, variables, and linear models. When used, the summary(x) provides summary data related to the individual object that is included into it. Thus, the summary function has a different output depending on what kind of object it takes as an argument. This method is valuable because it often sums up what we previously did and provides exactly what is needed in summary statistics. Let’s apply this command to the sample dataset.

# Summarize a variable using summary(VAR). Summary statistics of education
print(summary(subset.data$education))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  6.380   8.445  10.540  10.740  12.650  15.970 

We get the min, 1st quartile, Median, Mean, 3rd quartile, and the maximum years of education. This step provides more summary statistics information. We will apply,the same command this time,to the dataset.

# Summarize the subset.data sample using the command summary(subset.data)
print(summary(subset.data))
   education          income          women           prestige    
 Min.   : 6.380   Min.   :  611   Min.   : 0.000   Min.   :14.80  
 1st Qu.: 8.445   1st Qu.: 4106   1st Qu.: 3.592   1st Qu.:35.23  
 Median :10.540   Median : 5930   Median :13.600   Median :43.60  
 Mean   :10.738   Mean   : 6798   Mean   :28.979   Mean   :46.83  
 3rd Qu.:12.648   3rd Qu.: 8187   3rd Qu.:52.203   3rd Qu.:59.27  
 Max.   :15.970   Max.   :25879   Max.   :97.510   Max.   :87.20  

The output of the preceding summary provides the descriptive statistics of all objects in the sample data set. Under each variable, we have its summary statistics. Now that we know how to do summary statistics, we can delve into the visual part of the analysis to see the relation between the variables. The visual part will be done using basic R graphics packages and ggplot2, thanks to Hadley Wickham, Chief Scientist at Rstudio.

DATA VISUALIZATION

  1. MATRIX of PLOTS

The single type of planar scatterplot is really useful only when comparing two numeric-continuous variables. When there are more continuous variables of interest, it is possible to display this information satisfactorily on a single plot. A simple and common solution is to generate a two-variable scatterplot for each pair of variables and show them together in a structured way; this is referred to as a Scatterplot Matrix. We have four continuous/numeric variables in the subset dataset we have selected. Working with base R graphics, use the pairs function.

# Drawing a scatterplot matrix of education, income, women, and prestige using the pairs function
pairs(subset.data, pch = 16, col = "blue", main = "Matrix Scatterplot of Education, Income, Women, Prestige")

Our matrix scatterplot may be too big to fit on our screen. However, if you run the above code in your console, you will get the matrix plot in Rstudio’s graphics area. The interpretation of the above plots depends upon the labeling of the diagonal panels, running from the top left to the bottom right. They will appear in the same order as the columns given as the first argument. These “label panels” allow you to determine which individual plot in the matrix corresponds to each pair of variables. For instance, the first column of the scatterplot matrix corresponds to an x-axis variable of education; the third row of the matrix corresponds to a y-axis variable of women, and each row and column displays a scale that is constant moving left/right or up/down, respectively. The plot of prestige(y) on income(x) at position row 4 and column 2 displays the same data as the scatterplot at position row 2 and column 4 but flipped on its axis. Likewise, the plot of income(y) on education(x) at position row 2 and column 1 displays the same data as the scatterplot at column 2 row 1 flippedd on its axis. The scatterplot matrices therefore allow for easier comparison of pairwise relationships formed by observations made on multiple continuous variables. Instead of viewing a panoramic relationship between all the variables in our subset dataset, let’s use simple scatter plots to visualize the relationship between two variables.

  1. SCATTERPLOT

A scatterplot is a useful way to visualize the relationship between two variables displayed as x-y coordinate plots. Similar to correlations, scatterplots are often used to make initial diagnoses before any statistical analyses are performed.

The simplest way to create a scatterplot is to directly graph two variables using the default settings. In R, this can be achieved using the formula plot(VARX, VARY) function, where VARX is the variable to plot along the x-axis and VARY is the variable to plot along the y-axis. I will also add the ggplot2 version for the scatterplot. Let’s look at the relationship between education and income.

# Picture of the relationship between education and income
plot(subset.data$education, subset.data$income, xlab = "Education", ylab = "Income", pch = 16, col = "blue",
     main = " Scatterplot of Education vs. Income") 

# Same plot nicely done with ggplot2
ggplot(subset.data) +
  geom_point(aes(x = education, y = income), col = 'blue', size = 3) +
  ggtitle("Education vs. Income Scatterplot") +
  theme(plot.title = element_text(hjust = 0.5))

From the output, we see that as the average of years of education and income are positively related. That is higher education is rewarded by higher income. The scatterplot also displays the presence of potential outliers. We can further expand our visual analysis by calculating the slope and intercept of line of best fit.

# Calculate slope and intercept of line of best fit
coef(lm(income ~ education, data = subset.data))
(Intercept)   education 
 -2853.5856    898.8128 

We will use geom_abline() function to add the intercept avlue and estimated coefficient of education to our plot.

# Adding a line of best fit; intercept and slope
ggplot(subset.data) +
  
  geom_point(aes(x = education, y = income), col = "blue", size = 3) +
  
  geom_abline(aes(intercept = -2853, slope = 899), col = "darkred") +
  
  ggtitle("Education vs. Income Scatterplot With The Best Fit Line") +
  
  theme(plot.title = element_text(hjust = 0.5))

We can use our original Prestige dataset to display the income by types of occupation. In our dataset, we have type, a factor variable, that refers to three occupational levels: bc for Blue Collar; prof for Professional, Managerial, and Technical; and wc for White Collar. Let’s construct a bar plot for this categorial variable.

# Easy way to do it using the plot() function
plot(Prestige$type, main = "Bar Plot of Occupational Types", col = "cyan", ylab = "COUNT")

The output demonstrates that there are more blue collar workers in the survey than any other occupational groups. That may explain the disproportional distribution of income.

# Same plot using ggplot2
ggplot(Prestige) +
  
  geom_bar(aes(x = type)) +
  
  ggtitle("Count of Incumbents' Occupation") +
  
  theme(plot.title = element_text(hjust = 0.5))

We clearly see the difference between the two plots. The first one doesn’t take into account NAs occupation type, but the second plot using ggplot takes into account the NAs. Moving forward, we are going to plot income against types of occupations and add labels to these occupations. We are going to install a useful package called ggrepel that labels the scatterplot’s points.

ggplot(Prestige, aes(x = education, y = income)) +
  
  geom_text(aes(label = type), size = 4) +
  
   ggtitle("Scatterplot With Data Labels") +
  
  theme(plot.title = element_text(hjust = 0.5))

From the output, we see that Professionals have higher average years of education and earn higher income than other groups. Blue collars have less average years of education and are in the lower bounds of income.

# Using ggrepel to come up with the same plot
install.packages("ggrepel")
Installing package into <U+393C><U+3E31>C:/Users/pkolo/Documents/R/win-library/3.3<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/ggrepel_0.6.5.zip'
Content type 'application/zip' length 936513 bytes (914 KB)
downloaded 914 KB
package ‘ggrepel’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\pkolo\AppData\Local\Temp\RtmpCw3QNq\downloaded_packages
library(ggrepel)
ggplot(Prestige, mapping = aes(x = education, y = income)) +
  
  geom_point() +
  
  geom_text_repel(aes(label = type), size = 4) +
  
  ggtitle("Scatterpot With Data Points and Labels") +
  
  theme(plot.title = element_text(hjust = 0.5))

We can map variables to other variables as well.

ggplot(Prestige, mapping = aes(x = education, y = income)) +
  
  geom_point(aes(color = prestige, shape = type)) +
  
  ggtitle("Education vs. Income Scatterpot Per Prestige and Type") +
  
  theme(plot.title = element_text(hjust = 0.5))

In this output we map income and education with type and prestige. The output demonstrates that blue collars and white collar workers have low prestige scores, compared to professional. Income and levels of education seem to explain these trends.

LS0tDQp0aXRsZTogREVTQ1JJUFRJVkUgQU5BTFlTSVMgQU5EIFZJU1VBTElaQVRJT04gSU4gUiBVU0lORyBQUkVTVElHRSBEQVRBU0VUIElOIFRIRSBDQVIgUEFDS0FHRQ0Kb3V0cHV0OiBieSBQSUVSUkUgS09MT1dFDQoNCi0tLQ0KDQpCZWxvdyBpcyBhIHdhcm0tdXAgZ3JhcGggdG8gZ2V0IHlvdSBnb2luZyB3aXRoIFIuDQoNCmBgYHtyfQ0KcGxvdChjYXJzLCBjb2wgPSAiZGFya2JsdWUiLCBjZXggPSAyKQ0KYGBgDQoNClRoZSBkYXRhc2V0IHVzZWQgZm9yIHRoaXMgYW5hbHlzaXMgaXMgUHJlc3RpZ2UgZnJvbSB0aGUgcGFja2FnZSBjYXIuIEZpcnN0LCB3ZSBuZWVkIHRvIGluc3RhbGwgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGZvciBvdXIgYW5hbHlzaXMuDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSBwYWNrYWdlcyB0aGF0IGNvbnRhaW4gdGhlIGRhdGFzZXQgYW5kIHRoZSBkYXRhIHZpeiBwYWNrYWdlDQoNCmxpYnJhcnkoY2FyKSAjIFBhY2thZ2UgY29udGFpbmluZyB0aGUgZGF0YXNldA0KDQpsaWJyYXJ5KGdncGxvdDIpICMgUGFja2FnZSBmb3Igc29tZSBuaWNlIGFuZCBjb29sIHZpc3VhbGl6YXRpb25zDQoNCmxpYnJhcnkoTUFTUykgIyBQYWNrYWdlIGZvciBib3gtY294IHRyYW5zZm9ybWF0aW9uDQpgYGANCg0KUHJlc3RpZ2UgaXMgYSBkYXRhc2V0IG9mIENhbmFkaWFuIE9jY3VwYXRpb25zLiBUaGUgUHJlc3RpZ2UgZGF0YSBmcmFtZSBjb250YWlucyAxMDIgcm93cyBhbmQgNiBjb2x1bW5zLiBUaGUgb2JzZXJ2YXRpb25zIHJlbGF0ZSB0byBvY2N1cGF0aW9ucy4gVGhlIGRhdGEgZnJhbWUgaW5jbHVkZXMgdGhlIGZvbGxvd2luZyBjb2x1bW5zOg0KDQoxKSBWYXJpYWJsZSBlZHVjYXRpb24gaXMgdGhlIGF2ZXJhZ2UgZWR1Y2F0aW9uIG9mIG9jY3VwYXRpb25hbCBpbmN1bWJlbnRzLCB5ZWFycywgaW4gMTk3MS4NCg0KMikgdmFyaWFibGUgaW5jb21lIGlzIHRoZSBhdmVyYWdlIGluY29tZSBvZiBpbmN1bWJlbnRzLCBpbiAxOTcxLg0KDQozKSB2YXJpYWJsZSB3b21lbiBpcyB0aGUgcGVyY2VudGFnZSBvZiBpbmN1bWJlbnRzIHdobyBhcmUgd29tZW4uDQoNCjQpIFZhcmlhYmxlIHByZXN0aWdlIGlzIFBpbmVvLVBvcnRlciBzY29yZSBmb3Igb2NjdXBhdGlvbiwgZnJvbSBhIHNvY2lhbCBzdXJ2ZXkgY29uZHVjdGVkIGluIG1pZC0xOTYwcy4NCg0KNSkgVmFyaWFibGUgY2Vuc3VzIGlzIHRoZSBDYW5hZGlhbiBDZW5zdXMgb2NjdXBhdGlvbmFsIGNvZGUuDQoNCjYpIFZhcmlhYmxlIHR5cGUgcmVsYXRlcyB0byB0aGUgdHlwZSBvZiBvY2N1cGF0aW9uLiBBIGZhY3RvciB3aXRoIGxldmVscyAobm90ZTogb3V0IG9mIG9yZGVyKTogYmMsIEJsdWUgQ29sbGFyOyBwcm9mLCBQcm9mZXNzaW9uYWwsIE1hbmFnZW1lbnQsIGFuZCBUZWNobmljYWw7IHdjLCBXaGl0ZSBDb2xsYXIuDQoNClNvdXJjZSBvZiB0aGUgZGF0YXNldCBpczogQ2FuYWRhKDE5NzEpIENlbnN1cyBvZiBDYW5hZGEuIFZvbC4gMywgUGFydCA2LiBTdGF0aXN0aWNzIENhbmFkYSBbcHAuIDE5LTEtMTktMjFdLg0KDQpBKSBTVU1NQVJZIGFuZCBERVNDUklQVElWRSBTVEFUSVNUSUNTDQoNClN1bW1hcnkgKG9yIGRlc2NyaXB0aXZlKSBzdGF0aXN0aWNzIGFyZSB0aGUgZmlyc3QgZmlndXJlcyB1c2VkIHRvIHJlcHJlc2VudCBuZWFybHkgZXZlcnkgZGF0YXNldC4gVGhleSBhbHNvIGZvcm0gdGhlIGZvdW5kYXRpb25zIGZvciBtb3JlIGNvbXBsaWNhdGVkIGNvbXB1dGF0aW9ucyBhbmQgYW5hbHlzZXMuIFRoZXJlZm9yZSwgdGhleSBhcmUgZXNzZW50aWFsIHRvIHRoZSBhbmFseXNpcyBwcm9jZXNzLiBJbiB0aGlzIHBhcGVyLCB3ZSB3aWxsIGV4cGxvcmUgdGhlIHdheXMgaW4gd2hpY2ggUiBjYW4gYmUgdXNlZCB0byBjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzLCBpbmNsdWRpbmcgbWVhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCByYW5nZSwgYW5kIHBlcmNlbnRpbGUuIEFsc28gaW5jbHVkZWQgaGVyZSBpcyB0aGUgc3VtbWFyeSBmdW5jdGlvbiwgd2hpY2ggaXMgb25lIG9mIHRoZSBtb3N0IHVzZWZ1bCB0b29scyBpbiB0aGUgUiBzZXQgb2YgY29tbWFuZHMuDQoNCkZpcnN0LCBsZXQgdXMgaW5zcGVjdCB0aGUgUHJlc3RpZ2UgZGF0YXNldC4NCg0KYGBge3J9DQojIERpc3BsYXkgb3IgcHJpbnQgdGhlIGZpcnN0IDEwIG9ic2VydmF0aW9ucyBvZiBvdXIgZGF0YXNldA0KDQpwcmludChoZWFkKFByZXN0aWdlLCBuID0gMTApKQ0KYGBgDQoNCldlIG9ic2VydmUgdGhhdCB0aGUgcm93cyBvZiBvdXIgZGF0YXNldCByZWZlciB0byBvY2N1cGF0aW9ucy4gU29tZSBvZiB0aGUgb2NjdXBhdGlvbnMgYXJlIGdvdi5hZG1pbmlzdHJhdG9ycywgZ2VuZXJhbC5tYW5hZ2VycywgYWNjb3VudGFudHMsIGNoZW1pc3RzLCBwaHlzaWNpc3RzLCBiaW9sb2dpc3RzLCBhcmNoaXRlY3RzLCBjaXZpbC5lbmdpbmVlcnMgYW5kIG1pbmluZy5lbmdpbmVlcnMgZXRjLg0KSW4gYWRkaXRpb24sIGVhY2ggb2NjdXBhdGlvbiBoYXMgaXRzIG93biByZWNvcmRzLiBXZSBjYW4gYWxzbyBleHBsb3JlIHRoZSBuYXR1cmUgb2YgdmFyaWFibGVzL2NvbHVtbnMgd2UgaGF2ZSBpbiB0aGUgUHJlc3RpZ2UgZGF0YXNldCwgdXNpbmcgbHMoREFUQVZBUikgT3IgbmFtZXMoREFUQVZBUikuDQoNCmBgYHtyfQ0KIyBUbyBzZWUgdGhlIHZhcmlhYmxlIGluIHRoZSBkYXRhc2V0OyBVc2UgbmFtZXMoZGF0YXNldCkgb3IgbHMoZGF0YXNldCkNCg0KbHMoUHJlc3RpZ2UpDQpgYGANCg0KYGBge3J9DQojIFRvIHNlZSB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgYW5kIG51bWJlciBvZiByb3dzIGluIHRoZSBQcmVzdGlnZSBkYXRhc2V0OyB1c2UgbmNvbChkYXRhc2V0KSBhbmQgbnJvdyhkYXRhc2V0KQ0KDQpuY29sKFByZXN0aWdlKQ0KDQpucm93KFByZXN0aWdlKQ0KYGBgDQoNCkZyb20gdGhlIHJlc3VsdCBvdXRwdXQsIHRoZSBQcmVzdGlnZSBkYXRhc2V0IGNvbnRhaW5zIDYgdmFyaWFibGVzIGFuZCAxMDIgcm93cy4gQSBtb3JlIGFkdmFuY2VkIHRlY2huaXF1ZSB0byBzZWUgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldCBpcyB0byB1c2UgdGhlIHN0cihEQVRBVkFSKSBmdW5jdGlvbi4NCg0KYGBge3J9DQojIEEgbW9yZSBhZHZhbmNlZCBhbmQgY29tcGxldGUgd2F5IHRvIHNlZSB0aGUgc3RydWN0dXJlIG9mIG91ciBkYXRhc2V0DQpzdHIoUHJlc3RpZ2UpDQpgYGANCg0KUHJlc3RpZ2UgaXMgYSBkYXRhLmZyYW1lLCBhIGRhdGF0eXBlIHdpdGggbW9yZSB0aGFuIG9uZSByb3cgYW5kIGNvbHVtbi4gUHJlc3RpZ2UgaW5jbHVkZXMgNSBudW1lcmljIHZhcmlhYmxlcyBhbmQgb25lIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBGcm9tIGhlcmUsIHdlIGNhbiBub3cgcGVyZm9ybSBhIHN1bW1hcnkgYW5kIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb2Ygb3VyIGRhdGFzZXQuDQoNCk1FQU4gb2YgRUFDSCBWQVJJQUJMRQ0KDQpJbiBSLCBhIG1lYW4gY2FuIGJlIGNhbGN1bGF0ZWQgb24gYW4gaXNvbGF0ZWQgdmFyaWFibGUgdmlhIHRoZSBtZWFuKFZBUikgY29tbWFuZCwgVkFSIGlzIHRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZSB3aG9zZSBtZWFuIHdlIHdhbnQgdG8gY29tcHV0ZS4gQWx0ZXJuYXRpdmUsIHRoZSBtZWFuIGNhbiBiZSBjYWxjdWxhdGVkIGZvciBhbGwgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCB1c2luZyBtZWFuKERBVEFWQVIpIGZ1bmN0aW9uLCB3aGVyZSBEQVRBVkFSIGlzIHRoZSBuYW1lIG9mIHRoZSBkYXRhc2V0IGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcy4gRm9yIGFuYWx5c2lzIHB1cnBvc2VzLCB3ZSBhcmUgZ29pbmcgdG8gZXhjbHVkZSB0aGUgdmFyaWFibGVzIGNlbnN1cyBhbmQgdHlwZSBmcm9tIHRoZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLiBUbyBzZWxlY3QgYSBzdWJzZXQgb2YgYSBkYXRhc2V0LCB1c2Ugc3Vic2V0KERBVEFWQVIsIHNlbGVjdCA9IGMoIlZBUjEiLCAiVkFSMiIsICJWQVIzIi4uLi4iVkFSaSIpKSBjb21tYW5kLiBZb3UgY2FuIGFsc28gdHlwZSA/c3Vic2V0KCkgaW4geW91ciBSIGNvbnNvbGUgZm9sbG93ZWQgYnkgRU5URVIgdG8gbGVhcm4gaG93IHRvIHN1YnNldCgpIHZlY3RvcnMsIG1hdHJpY2VzIGFuZCBkYXRhIGZyYW1lcy4gVGhlIGNvZGUgYmVsb3cgZGVtb25zdHJhdGVzIGhvdyB0byBzZWxlY3QgYSBzdWJzZXQgb2YgUHJlc3RpZ2UgZGF0YXNldC4NCg0KYGBge3J9DQojIFN1YnNldHRpbmcgZWR1Y2F0aW9uLCBpbmNvbWUsIHdvbWVuLCBhbmQgcHJlc3RpZ2UgZnJvbSB0aGUgZGF0YXNldCBQcmVzdGlnZQ0KDQpzdWJzZXQuZGF0YSA8LSBzdWJzZXQoUHJlc3RpZ2UsIHNlbGVjdCA9IGMoImVkdWNhdGlvbiIsICJpbmNvbWUiLCAid29tZW4iLCAicHJlc3RpZ2UiKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hlY2tpbmcgc3Vic2V0LmRhdGEgdG8gbWFrZSBzdXJlIHdlIGhhdmUgdGhlIG5lZWRlZCBzdWJzZXQNCg0Kc3RyKHN1YnNldC5kYXRhKQ0KYGBgDQoNCkZyb20gdGhlIGFib3ZlIG91dHB1dCwgd2Ugc2VlIHRoYXQgd2UgZ290IHRoZSBzdWJzZXQgd2UgbmVlZC4gTGV0J3MgZmluZCB0aGUgbWVhbiBvZiBlYWNoIHZhcmlhYmxlIGluIHRoZSBzZWxlY3RlZCBzdWJzZXQuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIG1lYW4gb2YgYSB2YXJpYWJsZSB3aXRoIG1lYW4oREFUQVZBUiRWQVIpOyBtZWFuIG9mIHZhcmlhYmxlIGVkdWNhdGlvbg0KDQptZWFuKHN1YnNldC5kYXRhJGVkdWNhdGlvbikNCmBgYA0KDQpUaGUgbWVhbiB5ZWFyIG9mIGVkdWNhdGlvbiB3YXMgMTEgeWVhcnMgaW4gQ2FuYWRhIGluIDE5NzEuIFRoYXQgbWVhbnMgdGhhdCB0aGUgYXZlcmFnZSBDYW5hZGlhbiBoYWQgYSBsZWFzdCBhIEhpZ2ggU2Nob29sIGVkdWNhdGlvbiBpbiAxOTcxLg0KDQpgYGB7cn0NCiMgTWVhbiBvZiBpbmNvbWUgdmFyaWFibGUNCg0KbWVhbihzdWJzZXQuZGF0YSRpbmNvbWUpDQpgYGANCg0KSGVyZSB0aGUgbWVhbiBpbmNvbWUgZm9yIGluY3VtYmVudHMgaW4gMTk3MSBpcyBhYm91dCAkNiw3OTguIA0KDQpgYGB7cn0NCiMgTWVhbiBvZiBwZXJjZW50YWdlIG9mIGluY3VtYmVudHMgd2hvIGFyZSB3b21lbg0KDQptZWFuKHN1YnNldC5kYXRhJHdvbWVuKQ0KYGBgDQoNClRoZSBhdmVyYWdlIHBlcmNlbnRhZ2Ugb2YgaW5jdW1iZW50cyBpbiB0aGUgc3VydmV5IHdobyBhcmUgd29tZW4gaXMgYWJvdXQgMjklLg0KDQpgYGB7cn0NCiMgVGhlIGxhc3QgdmFyaWFibGUgaXMgcHJlc3RpZ2UNCg0KbWVhbihzdWJzZXQuZGF0YSRwcmVzdGlnZSkNCmBgYA0KDQpUaGUgbWVhbiBzY29yZSBvZiBQaW5lb19Qb3J0ZXIgbWVhc3VyZSBmb3Igb2NjdXBhdGlvbiBpcyBhYm91dCA0Ny4gVGhlIGNvbXB1dGF0aW9ucyBhYm92ZSBhcmUgZG9uZSBvbiBpbmRpdmlkdWFsIHZhcmlhYmxlcy4gQWx0ZXJuYXRpdmVseSwgYSBtZWFuIGNhbiBiZSBjYWxjdWxhdGVkIGZvciBlYWNoIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQgYnkgdXNpbmcgdGhlIGZvcm11bGEgc3VtbWFyeShkYXRhc2V0KS4gV2Ugd2lsbCBhcHByb2FjaCB0aGlzIGFsdGVybmF0aXZlIGxhdGVyLiBMZXQncyBmaW5kIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCB2YXJpYWJsZSBpbiBvdXIgc3Vic2V0IGRhdGFzZXQuDQoNClNUQU5EQVJEIERFVklBVElPTiBPRiBFQUNIIFZBUklBQkxFDQoNClN0YW5kYXJkIGRldmlhdGlvbnMgYXJlIGNhbGN1bGF0ZWQgaW4gdGhlIHNhbWUgd2F5IGFzIG1lYW5zIHdpdGhpbiBSLiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGEgc2luZ2xlIHZhcmlhYmxlIGNhbiBiZSBjb21wdXRlZCB1c2luZyB0aGUgZm9ybXVsYSBzZChWQVIpLCB3aGVyZSBWQVIgaXMgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIHdob3NlIHN0YW5kYXJkIGRldmlhdGlvbiB5b3Ugd2FudCB0byByZXRyaWV2ZS4gU3RhbmRhcmQgZGV2aWF0aW9uIG1lYXN1cmVzIGhvdyBzcHJlYWQgeW91ciBkYXRhIGFyZS4gVGhlIGNvZGVzIGJlbG93IGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBmdW5jdGlvbi4NCg0KYGBge3J9DQojIFdoYXQgaXMgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBlZHVjYXRpb24/DQoNCnNkKHN1YnNldC5kYXRhJGVkdWNhdGlvbikNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGluY29tZT8NCnNkKHN1YnNldC5kYXRhJGluY29tZSkNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHdvbWVuPw0Kc2Qoc3Vic2V0LmRhdGEkd29tZW4pDQpgYGANCg0KYGBge3J9DQojIFdoYXQgaXMgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBwcmVzdGlnZT8NCnNkKHN1YnNldC5kYXRhJHByZXN0aWdlKQ0KYGBgDQoNClJBTkdFIG9mIEVBQ0ggVkFSSUFCTEUgOiBNSU5JTVVNICYgTUFYSU1VTQ0KDQpDb250aW51aW5nIGluIHRoZSBzYW1lIHRyYWplY3RvcnksIG1pbmltdW0gY2FuIGJlIGNvbXB1dGVkIG9uIGEgc2luZ2xlIHZhcmlhYmxlIHVzaW5nIHRoZSBtaW4oVkFSKSBmb3JtdWxhLiBJbiB0aGUgc2FtZSB0b2tlbiwgbWF4KFZBUikgb3BlcmF0ZXMgc2ltaWxhcmx5LiBNaW5pbXVtIGFuZCBNYXhpbXVtIGdpdmUgdGhlIG1pbiBhbmQgbWF4IG9mIGluZGl2aWR1YWwgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0LiBUaGUgY29kZXMgYmVsb3cgc2hvdyBob3cgdG8gY2FsY3VsYXRlIG1pbmltdW1zIGFuZCBtYXhpbXVtcy4NCg0KYGBge3J9DQojIE1pbmltdW0gYW5kIG1heGltdW0geWVhcnMgb2YgZWR1Y2F0aW9uIG9mIHN1cnZleSBwYXJ0aWNpcGFudHMgaW4gMTk3MQ0KbWluKHN1YnNldC5kYXRhJGVkdWNhdGlvbikgDQptYXgoc3Vic2V0LmRhdGEkZWR1Y2F0aW9uKQ0KYGBgDQoNClRoZSBtaW5pbXVtIHllYXIgb2YgaW5jdW1iZW50cycgZWR1Y2F0aW9uIGluIDE5NzEgd2FzIGFib3V0IDYgeWVhcnMuIEFuZCB0aGUgbWF4aW11bSB5ZWFyIG9mIHBhcnRpY2lwYW50cycgZWR1Y2F0aW9uIHdhcyAxNiB5ZWFycywgbWVhbmluZyB3ZSBoYWQgc29tZSBjb2xsZWdlLWVkdWNhdGVkIGluY3VtYmVudHMgaW4gdGhlIHN1cnZleS4NCg0KYGBge3J9DQojIE1pbmltdW0gYW5kIE1heGltdW0geWVhcnMgb2YgaW5jb21lIG9mIHN1cnZleSBwYXJ0aWNpcGFudHMgaW4gMTk3MQ0KbWluKHN1YnNldC5kYXRhJGluY29tZSk7IG1heChzdWJzZXQuZGF0YSRpbmNvbWUpDQpgYGANCg0KRnJvbSB0aGUgb3V0cHV0LCB0aGUgbWluaW11bSBpbmNvbWUgaXMgNjExIGFuZCB0aGUgbWF4aW11bSBpbmNvbWUgaXMgYWJvdXQgJDI2MDAwLiBUaGlzIHNob3dzIGxhcmdlIGdhcHMgaW4gaW5jb21lIGRpc3RyaWJ1dGlvbiBhbW9uZyBzdXJ2ZXkgcGFydGljaXBhbnRzLiBUaGUgbWF4aW11bSBpbmNvbWUgaXMgYWJvdXQgZm91ciB0aW1lcyB0aGUgbWluaW11bSBvbmUuDQoNCmBgYHtyfQ0KIyBNaW5pbXVtIGFuZCBNYXhpbXVtIHBlcmNlbnRhZ2Ugb2YgaW5jdW1iZW50cyB3aG8gYXJlIHdvbWVuLg0KbWluKHN1YnNldC5kYXRhJHdvbWVuKTsgbWF4KHN1YnNldC5kYXRhJHdvbWVuKQ0KYGBgDQoNCkZyb20gdGhlIG91dHB1dCwgd2UgbWF5IGluZmVyIHRoYXQgYSBsYXJnZSBwZXJjZW50YWdlIG9mIHdvbWVuIHBhcnRpY2lwYXRlZCBpbiB0aGUgc3VydmV5Lg0KDQpgYGB7cn0NCiMgTWluaW11bSBhbmQgbWF4aW11bSBzY29yZSBmb3Igb2NjdXBhdGlvbg0KbWluKHN1YnNldC5kYXRhJHByZXN0aWdlKSA7IG1heChzdWJzZXQuZGF0YSRwcmVzdGlnZSkNCmBgYA0KDQpUaGUgcmFuZ2UgZm9yIHByZXN0aWdlIHNjb3JlcyBpcyBmcm9tIH4gMTUgdG8gfiA4Ny4gVGhlIFJhbmdlKCkgZnVuY3Rpb24gY2FuIHByb3ZpZGUgbWluaW11bXMgYW5kIG1heGltdW1zIG9mIGRpZmZlcmVudCB2YXJpYWJsZXMgaW4gb3VyIHN1YnNldCBkYXRhc2V0Lg0KDQpSQU5HRQ0KDQpUaGUgcmFuZ2Ugb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlLCB0aGF0IGlzLCBpdHMgbWluaW11bSBhbmQgbWF4aW11bSwgY2FuIGJlIHJldHJpZXZlZCB1c2luZyB0aGUgcmFuZ2UoVkFSKSBjb21tYW5kLg0KTGlrZSB3aXRoIG1pbiBhbmQgbWF4IGZ1bmN0aW9ucywgdXNpbmcgcmFuZ2UoZGF0YXNldCkgaXMgbm90IHZlcnkgdXNlZnVsIHNpbmNlIGl0IGNvbnNpZGVycyB0aGUgZW50aXJlIGRhdGFzZXQsIHJhdGhlciB0aGFuIGVhY2ggaW5kaXZpZHVhbCB2YXJpYWJsZS4gQ29uc2VxdWVudGx5LCBpdCBpcyByZWNvbW1lbmRlZCB0aGF0IHJhbmdlcyBiZSBjb21wdXRlZCBvbiBpbmRpdmlkdWFsIHZhcmlhYmxlcy4gVGhlc2UgY29tcHV0YXRpb25zIGFyZSBkZW1vbnN0cmF0ZWQgaW4gdGhlIGZvbGxvd2luZyBjb2RlczoNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgcmFuZ2Ugb2YgYSB2YXJpYWJsZSB3aXRoIHJhbmdlKFZBUikNCiMgUmFuZ2Ugb2YgdmFyaWFibGUgZWR1Y2F0aW9uDQpyYW5nZShzdWJzZXQuZGF0YSRlZHVjYXRpb24pDQpgYGANCg0KYGBge3J9DQojIFJhbmdlIG9mIHZhcmlhYmxlIGluY29tZQ0KcmFuZ2Uoc3Vic2V0LmRhdGEkaW5jb21lKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSYW5nZSBvZiB2YXJpYWJsZSB3b21lbg0KcmFuZ2Uoc3Vic2V0LmRhdGEkd29tZW4pDQpgYGANCg0KYGBge3J9DQojIFJhbmdlIG9mIHZhcmlhYmxlIHByZXN0aWdlDQpyYW5nZShzdWJzZXQuZGF0YSRwcmVzdGlnZSkNCmBgYA0KDQpQRVJDRU5USUxFUyA6IFZBTFVFUyBmcm9tIFBFUkNFTlRJTEVTIChRVUFOVElMRSkNCg0KWW91IGNhbiBnZXQgbW9yZSBpbnNpZ2h0IGludG8gdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHNldCBvZiBvYnNlcnZhdGlvbnMgYnkgZXhhbWluaW5nIHF1YW50aWxlcy4gQSBxdWFudGlsZSBpcyBhIHZhbHVlIGNvbXB1dGVkIGZyb20gYSBjb2xsZWN0aW9uIG9mIG51bWVyaWMgbWVhc3VyZW1lbnRzIHRoYXQgaW5kaWNhdGVzIGFuIG9ic2VydmF0aW9uJ3MgcmFuayB3aGVuIGNvbXBhcmVkIHRvIGFsbCBvdGhlciBwcmVzZW50IG9ic2VydmF0aW9ucy4gQWx0ZXJuYXRpdmVseSwgcXVhbnRpbGUgY2FuIGJlIGV4cHJlc3NlZCBhcyBhIHBlcmNlbnRpbGUsIHRoaXMgaXMgaWRlbnRpY2FsIGJ1dCBvbiBhIHBlcmNlbnQgc2NhbGUgb2YgMCB0byAxMDAuDQoNCk9idGFpbmluZyBxdWFudGlsZXMgYW5kIHBlcmNlbnRpbGUgaW4gUiBpcyBkb25lIHVzaW5nIHRoZSBxdWFudGlsZSgpIGZ1bmN0aW9uLiBUaGUgY29tbWFuZCBpcyB3cml0dGVuIHF1YW50aWxlKFZBUiwgYyhQUk9CMSwgUFJPQjIsIFBST0IzLC4uLi5QUk9CaSkpIG9yIHF1YW50aWxlKFZBUiwgcHJvYiA9IGMocHJvYiB2YWx1ZTEsIHByb2IgdmFsdWUgMiwgcHJvYiB2YWx1ZSAzLi4ucHJvYiB2YWx1ZWkpKS4NCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgMjV0aCwgNTB0aCwgNzV0aCwgYW5kIDk1dGggcGVyY2VudGlsZXMgZm9yIGVkdWNhdGlvbiBpbiB0aGUgc3Vic2V0IGRhdGFzZXQNCnF1YW50aWxlKHN1YnNldC5kYXRhJGVkdWNhdGlvbiwgcHJvYiA9IGMoLjI1LCAuNTAsIC43NSwgLjk1KSkNCnF1YW50aWxlKHN1YnNldC5kYXRhJGVkdWNhdGlvbiwgYyguMjUsIC41MCwgLjc1LCAuOTUpKQ0KYGBgDQoNCldlIGhhdmUgdXNlZCB0d28gc2xpZ2h0bHkgZGlmZmVyZW50IGNvZGVzIHRoYXQgbGVhZCB0byB0aGUgc2FtZSBvdXRwdXQuIEZyb20gdGhlIG91dHB1dCwgd2Ugc2VlIHRoYXQgMjUlIG9mIGF2ZXJhZ2UgeWVhcnMgb2YgZWR1Y2F0aW9uIHdhcyBhYm91dCA4IHllYXJzIG9mIGVkdWNhdGlvbiwgdGhlIG1lZGlhbiB5ZWFyIG9mIGVkdWNhdGlvbiB3YXMgYWJvdXQgMTAuNSB5ZWFycyBvZiBlZHVjYXRpb24sIDc1JSBvZiBhdmVyYWdlIGVkdWNhdGlvbiBvZiBpbmN1bWJlbnRzIHdhcyBhYm91dCAxMyB5ZWFycyBvZiBlZHVjYXRpb24sIGFuZCA5NSUgb2YgcGFydGljaXBhbnRzIGhhdmUgYWJvdXQgMTYgeWVhcnMgb2YgZWR1Y2F0aW9uLiBBbHNvLCB0aGlzIG1heSBleHBsYWluIHRoZSB3aWRlIGdhcCBpbiB3YWdlIHJhbmdlIHNpbmNlIGVkdWNhdGlvbiBzZWVtcyB0byBiZSBjb3JyZWxhdGVkIHRvIGluY29tZS4NCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgMjV0aCwgNTB0aCwgNzV0aCwgYW5kIDk1dGggcGVyY2VudGlsZXMgZm9yIGluY29tZSBpbiB0aGUgZGF0YXNldA0KcXVhbnRpbGUoc3Vic2V0LmRhdGEkaW5jb21lLCBwcm9iID0gYyguMjUsIC41MCwgLjc1LCAuOTUpKQ0KYGBgDQoNCkZvciB0aGUgaW5jb21lIHZhcmlhYmxlLCBsZXQgZmluZCB0aGUgMCB0aCwgdGhlIDI1dGggLCB0aGUgNTB0aCwgdGhlIDc1dGgsIGFuZCB0aGUgMTAwdGggcGVyY2VudGlsZXMuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIDB0aCwgMjV0aCwgNTB0aCwgNzV0aCwgYW5kIDEwMHRoIHBlcmNlbnRpbGVzDQpxdWFudGlsZShzdWJzZXQuZGF0YSRpbmNvbWUsIGMoMCwgLjI1LCAuNTAsIC43NSwgMSkpDQpgYGANCg0KVGhlIG1lZGlhbiBpbmNvbWUgb2Ygc3VydmV5IHBhcnRpY2lwYW50cyB3YXMgJFVTIDU5MzAuNTAgaW4gMTk3MS4gSGlnaGVyIGluY29tZSB0ZW5kZWQgdG8gYmUgaW4gdGhlIHVwcGVyIGJvdW5kcyBvZiB0aGUgZGlzdHJpYnV0aW9uLiBJbmNvbWUgZGlzdHJpYnV0aW9uIHNlZW1zIHRvIHJlZmxlY3QgdGhlIGxldmVscyBvZiBlZHVjYXRpb24gb2YgcGFydGljaXBhbnRzIGluIHRoZSBzdXJ2ZXkuIFRoZSBtb3JlIGVkdWNhdGVkIHlvdSBhcmUsIHRoZSBoaWdoZXIgdGhlIGxpa2VsaWhvb2Qgb2YgYmVpbmcgaW4gdGhlIHVwcGVyIGluY29tZSdzIGJvdW5kLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSAwdGgsIDI1dGgsIDUwdGgsIDc1dGgsIGFuZCAxMDB0aCBwZXJlbnRpbGVzIGZvciB3b21lbg0KcXVhbnRpbGUoc3Vic2V0LmRhdGEkd29tZW4sIHByb2IgPSBjKDAsIC4yNSwgLjUwLCAuNzUsIDEpKQ0KYGBgDQoNClRoZSBtaW5pbXVtIHByb3BvcnRpb24gb2YgaW5jdW1iZW50cyB3aG8gYXJlIHdvbWVuIGlzIDAsIGFuZCB0aGUgbWF4aW11bSBwcm9wb3J0aW9uIG9mIHBhcnRpY2lwYW50cyB3aG8gYXJlIHdvbWVuIGlzIH4gOTguIEZpbmFsbHksIHRoZSBtZWRpYW4gbnVtYmVyIG9mIHdvbWVuIGlzIH4gMTcuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIDB0aCwgMjV0aCwgNTB0aCwgNzV0aCwgYW5kIDEwMHRoIHBlcmNlbnRpbGVzIGZvciBwcmVzdGlnZQ0KcXVhbnRpbGUoc3Vic2V0LmRhdGEkcHJlc3RpZ2UsIGMoMCwgLjI1LCAuNTAsIC43NSwgMSkpDQpgYGANCg0KSGVyZSB3ZSBoYXZlIHVzZWQgdGhlIHF1YW50aWxlIGZ1bmN0aW9uIHRvIG9idGFpbiB3aGF0IGlzIGNhbGxlZCB0aGUgRklWRS1OVU1CRVIgU1VNTUFSWSBvZiBzdWJzZXQuZGF0YSwgY29tcHJpc2VkIG9mIDB0aCBwZXJjZW50aWxlKHRoZSBtaW5pbXVtKSwgdGhlIDI1dGggcGVyY2VudGlsZSwgdGhlIDUwdGggcGVyY2VudGlsZSwgYW5kIHRoZSAxMDB0aCBwZXJjZW50aWxlKHRoZSBtYXhpbXVtKS4gVGhlIDAuMjV0aCBxdWFudGlsZSBpcyByZWZlcnJlZCB0byBhcyB0aGUgZmlyc3Qgb3IgTE9XRVIgUVVBUlRJTEUsIGFuZCAwLjc1dGggcXVhbnRpbGUgaXMgcmVmZXJyZWQgdG8gYXMgdGhlIHRoaXJkIG9yIFVQUEVSIFFVQVJUSUxFLiBBbHNvLCB0aGUgNTB0aCBwZXJjZW50aWxlIHJlZmVycyB0byB0aGUgTUVESUFOLiBUaGUgbWVkaWFuIGlzIHRoZSBzZWNvbmQgcXVhcnRpbGUsIHdpdGggdGhlIG1heGltdW0gdmFsdWUgYmVpbmcgdGhlIGZvdXJ0aCBxdWFydGlsZS4NCg0KUEVSQ0VOVElMRVMgRlJPTSBWQUxVRVMgKFBFUkNFTlRJTEUgUkFOSykNCg0KSW4gdGhlIG9wcG9zaXRlIHNpdHVhdGlvbiwgd2hlcmUgYSBwZXJjZW50aWxlIHJhbmsgY29ycmVzcG9uZGluZyB0byBhIGdpdmVuIHZhbHVlIGlzIG5lZWRlZCwgb25lIGhhcyB0byBkZXZpc2UgYSBjdXN0b20gbWV0aG9kLiBIZXJlIGFyZSB0aGUgc3RlcHMgaW52b2x2ZWQgaW4gY29tcHV0aW5nIGEgcGVyY2VudGlsZSByYW5rLg0KDQooMSkgQ291bnQgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyB0aGF0IGFyZSBhdCBvciBiZWxvdyB0aGUgZ2l2ZW4gdmFsdWUNCigyKSBEaXZpZGUgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBkYXRhIHBvaW50cw0KKDMpIG11bHRpcGxlIGJ5IDEwMA0KDQpUaGUgZm9ybXVsYSBmb3IgY2FsY3VsYXRpbmcgYSBwZXJjZW50aWxlIHJhbmsgY2FuIGJlIGRlcml2ZWQgZnJvbSB0aGlzIGNvbW1hbmQ6IHBlcmNlbnRpbGUgcmFuayA9IGxlbmd0aChWQVJbVkFSIDw9IFZBTFVFXSkgLyBsZW5ndGgoVkFSKSAqIDEwMC4gbGVuZ3RoKFZBUltWQVIgPD0gVkFMVUVdKSBjb3VudHMgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBpbiBhIHZhcmlhYmxlIHRoYXQgYXJlIGJlbG93IHRoZSBnaXZlbiB2YWx1ZS4gVGhlICc8PScgY2FuIGJlIHJlcGxhY2VkIHdpdGggb3RoZXIgb3BlcmF0b3JzLCBzdWNoIGFzICc8JywgJz4nLCBhbmQgPS4gVGhlIGxlbmd0aChWQVIpIGNvdW50cyB0aGUgbnVtYmVyIG9mIGRhdGEgcG9pbnRzIGluIHRoZSB2YXJpYWJsZS4gVGhlIGZpbmFsIHN0ZXAgaXMgdG8gbXVsdGlwbHkgdGhlIHJlc3VsdCBieSAxMDAgdG8gdHJhbnNmb3JtIHRoZSBkZWNpbWFsIHZhbHVlIGludG8gYSBwZXJjZW50YWdlLiBMZXQncyBhcHBseSB0aGVzZSBzdGVwcyBpbiB0aGUgZm9sbG93aW5nIGV4YW1wbGVzOg0KDQpgYGB7cn0NCiMgSW4gdGhlIHNhbXBsZSwgOCB5ZWFycyBvZiBlZHVjYXRpb24gaXMgYXQgd2hhdCBwZXJjZW50aWxlIHJhbms/DQpsZW5ndGgoc3Vic2V0LmRhdGEkZWR1Y2F0aW9uW3N1YnNldC5kYXRhJGVkdWNhdGlvbiA8PSA4XSkgLyBsZW5ndGgoc3Vic2V0LmRhdGEkZWR1Y2F0aW9uKSAqIDEwMA0KYGBgDQoNCjggeWVhcnMgb2YgZWR1Y2F0aW9uIGlzIGF0IDE5LjYwIHBlcmNlbnRpbGUgcmFuay4NCg0KYGBge3J9DQojIEluIHRoZSBzYW1wbGUsICQxMDAwMCBpcyBhdCB3aGF0IHBlcmNlbnRpbGUgcmFuaz8NCmxlbmd0aChzdWJzZXQuZGF0YSRpbmNvbWVbc3Vic2V0LmRhdGEkaW5jb21lIDw9IDEwMDAwXSkgLyBsZW5ndGgoc3Vic2V0LmRhdGEkaW5jb21lKSAqIDEwMA0KYGBgDQoNCmBgYHtyfQ0KIyBJbiB0aGUgc2FtcGxlLCAkMTAwMDAgaXMgYXQgd2hhdCBwZXJjZW50aWxlIHJhbms/DQpsZW5ndGgoc3Vic2V0LmRhdGEkaW5jb21lW3N1YnNldC5kYXRhJGluY29tZSA+PSAxMDAwMF0pIC8gbGVuZ3RoKHN1YnNldC5kYXRhJGluY29tZSkgKiAxMDANCmBgYA0KDQpUaGUgcGVyY2VudGlsZSByYW5rIHJldmVhbHMgc29tZSBpbnRlcmVzdGluZyBmYWN0cyBvbiBpbmNvbWUgZGlzdHJpYnV0aW9uIG9mIHN1cnZleSBpbmN1bWJlbnRzLiBBYm91dCAxMyAlIG9mIHBhcnRpY2lwYW50cyBlYXJuZWQgbW9yZSB0aGFuIG9yIGVxdWFsIHRvICQxMCwwMDAuMDAgaW4gMTk3MSwgd2hpbGUgYWJvdXQgODcgJSBlYXJuZWQgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDEwLDAwMC4wMC4gU2ltaWxhcmx5LCBsZXQncyBmaW5kIHRoZSBwZXJjZW50aWxlIHJhbmsgb2YgaW5jb21lIGFib3ZlIGFuZCBiZWxvdywgc2F5IDE1LDAwMCBkb2xsYXJzLg0KDQpgYGB7cn0NCiMgSW4gdGhlIHNhbXBsZSwgaW5jb21lIGxlc3MgdGhhbiBvciBlcXVhbCB0byBzYXkgMTUwMDAgaXMgYXQgd2hhdCBwZXJjZW50aWxlIHJhbms/DQpsZW5ndGgoc3Vic2V0LmRhdGEkaW5jb21lW3N1YnNldC5kYXRhJGluY29tZSA8PSAxNTAwMF0pIC8gbGVuZ3RoKHN1YnNldC5kYXRhJGluY29tZSkgKiAxMDANCmBgYA0KDQpgYGB7cn0NCiMgSW4gdGhlIHNhbXBsZSwgaW5jb21lIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBzYXkgMTUwMDAgaXMgYXQgcGVyY2VudGlsZSByYW5rPw0KbGVuZ3RoKHN1YnNldC5kYXRhJGluY29tZVtzdWJzZXQuZGF0YSRpbmNvbWUgPj0gMTUwMDBdKSAvIGxlbmd0aChzdWJzZXQuZGF0YSRpbmNvbWUpICogMTAwDQpgYGANCg0KVGhlc2UgdHdvIG91dHB1dHMgY29uZmlybSB0aGUgd2lkZSBnYXAgaW4gaW5jb21lIGRpc3RyaWJ1dGlvbiBhbW9uZyBzdXJ2ZXkgcGFydGljaXBhbnRzLiBBIHVzZWZ1bCBtdWx0aXB1cnBvc2UgZnVuY3Rpb24gaW4gUiBpcyB0aGUgdXNlIG9mIHRoZSBTVU1NQVJZKFgpIGZ1bmN0aW9uLCBhcyBJIHByZXZpb3VzbHkgbWVudGlvbmVkLg0KDQpTVU1NQVJZDQoNCkEgdmVyeSB1c2VmdWwgZnVuY3Rpb24gaW4gUiBpcyBzdW1tYXJ5KHgpLCB3aGVyZSB4IGNhbiBiZSBvbmUgb2YgYW55IG51bWJlciBvZiBvYmplY3RzLCBpbmNsdWRpbmcgZGF0YXNldHMsIHZhcmlhYmxlcywgYW5kIGxpbmVhciBtb2RlbHMuIFdoZW4gdXNlZCwgdGhlIHN1bW1hcnkoeCkgcHJvdmlkZXMgc3VtbWFyeSBkYXRhIHJlbGF0ZWQgdG8gdGhlIGluZGl2aWR1YWwgb2JqZWN0IHRoYXQgaXMgaW5jbHVkZWQgaW50byBpdC4gVGh1cywgdGhlIHN1bW1hcnkgZnVuY3Rpb24gaGFzIGEgZGlmZmVyZW50IG91dHB1dCBkZXBlbmRpbmcgb24gd2hhdCBraW5kIG9mIG9iamVjdCBpdCB0YWtlcyBhcyBhbiBhcmd1bWVudC4gVGhpcyBtZXRob2QgaXMgdmFsdWFibGUgYmVjYXVzZSBpdCBvZnRlbiBzdW1zIHVwIHdoYXQgd2UgcHJldmlvdXNseSBkaWQgYW5kIHByb3ZpZGVzIGV4YWN0bHkgd2hhdCBpcyBuZWVkZWQgaW4gc3VtbWFyeSBzdGF0aXN0aWNzLiBMZXQncyBhcHBseSB0aGlzIGNvbW1hbmQgdG8gdGhlIHNhbXBsZSBkYXRhc2V0Lg0KDQpgYGB7cn0NCiMgU3VtbWFyaXplIGEgdmFyaWFibGUgdXNpbmcgc3VtbWFyeShWQVIpLiBTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgZWR1Y2F0aW9uDQpwcmludChzdW1tYXJ5KHN1YnNldC5kYXRhJGVkdWNhdGlvbikpDQpgYGANCg0KV2UgZ2V0IHRoZSBtaW4sIDFzdCBxdWFydGlsZSwgTWVkaWFuLCBNZWFuLCAzcmQgcXVhcnRpbGUsIGFuZCB0aGUgbWF4aW11bSB5ZWFycyBvZiBlZHVjYXRpb24uIFRoaXMgc3RlcCBwcm92aWRlcyBtb3JlIHN1bW1hcnkgc3RhdGlzdGljcyBpbmZvcm1hdGlvbi4gV2Ugd2lsbCBhcHBseSx0aGUgc2FtZSBjb21tYW5kIHRoaXMgdGltZSx0byB0aGUgZGF0YXNldC4NCg0KYGBge3J9DQojIFN1bW1hcml6ZSB0aGUgc3Vic2V0LmRhdGEgc2FtcGxlIHVzaW5nIHRoZSBjb21tYW5kIHN1bW1hcnkoc3Vic2V0LmRhdGEpDQpwcmludChzdW1tYXJ5KHN1YnNldC5kYXRhKSkNCmBgYA0KDQpUaGUgb3V0cHV0IG9mIHRoZSBwcmVjZWRpbmcgc3VtbWFyeSBwcm92aWRlcyB0aGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvZiBhbGwgb2JqZWN0cyBpbiB0aGUgc2FtcGxlIGRhdGEgc2V0LiBVbmRlciBlYWNoIHZhcmlhYmxlLCB3ZSBoYXZlIGl0cyBzdW1tYXJ5IHN0YXRpc3RpY3MuIE5vdyB0aGF0IHdlIGtub3cgaG93IHRvIGRvIHN1bW1hcnkgc3RhdGlzdGljcywgd2UgY2FuIGRlbHZlIGludG8gdGhlIHZpc3VhbCBwYXJ0IG9mIHRoZSBhbmFseXNpcyB0byBzZWUgdGhlIHJlbGF0aW9uIGJldHdlZW4gdGhlIHZhcmlhYmxlcy4gVGhlIHZpc3VhbCBwYXJ0IHdpbGwgYmUgZG9uZSB1c2luZyBiYXNpYyBSIGdyYXBoaWNzIHBhY2thZ2VzIGFuZCBnZ3Bsb3QyLCB0aGFua3MgdG8gSGFkbGV5IFdpY2toYW0sIENoaWVmIFNjaWVudGlzdCBhdCBSc3R1ZGlvLg0KDQpEQVRBIFZJU1VBTElaQVRJT04NCg0KKDEpIE1BVFJJWCBvZiBQTE9UUw0KDQpUaGUgc2luZ2xlIHR5cGUgb2YgcGxhbmFyIHNjYXR0ZXJwbG90IGlzIHJlYWxseSB1c2VmdWwgb25seSB3aGVuIGNvbXBhcmluZyB0d28gbnVtZXJpYy1jb250aW51b3VzIHZhcmlhYmxlcy4gV2hlbiB0aGVyZSBhcmUgbW9yZSBjb250aW51b3VzIHZhcmlhYmxlcyBvZiBpbnRlcmVzdCwgaXQgaXMgcG9zc2libGUgdG8gZGlzcGxheSB0aGlzIGluZm9ybWF0aW9uIHNhdGlzZmFjdG9yaWx5IG9uIGEgc2luZ2xlIHBsb3QuIEEgc2ltcGxlIGFuZCBjb21tb24gc29sdXRpb24gaXMgdG8gZ2VuZXJhdGUgYSB0d28tdmFyaWFibGUgc2NhdHRlcnBsb3QgZm9yIGVhY2ggcGFpciBvZiB2YXJpYWJsZXMgYW5kIHNob3cgdGhlbSB0b2dldGhlciBpbiBhIHN0cnVjdHVyZWQgd2F5OyB0aGlzIGlzIHJlZmVycmVkIHRvIGFzIGEgU2NhdHRlcnBsb3QgTWF0cml4LiBXZSBoYXZlIGZvdXIgY29udGludW91cy9udW1lcmljIHZhcmlhYmxlcyBpbiB0aGUgc3Vic2V0IGRhdGFzZXQgd2UgaGF2ZSBzZWxlY3RlZC4gV29ya2luZyB3aXRoIGJhc2UgUiBncmFwaGljcywgdXNlIHRoZSBwYWlycyBmdW5jdGlvbi4NCg0KYGBge3J9DQojIERyYXdpbmcgYSBzY2F0dGVycGxvdCBtYXRyaXggb2YgZWR1Y2F0aW9uLCBpbmNvbWUsIHdvbWVuLCBhbmQgcHJlc3RpZ2UgdXNpbmcgdGhlIHBhaXJzIGZ1bmN0aW9uDQpwYWlycyhzdWJzZXQuZGF0YSwgcGNoID0gMTYsIGNvbCA9ICJibHVlIiwgbWFpbiA9ICJNYXRyaXggU2NhdHRlcnBsb3Qgb2YgRWR1Y2F0aW9uLCBJbmNvbWUsIFdvbWVuLCBQcmVzdGlnZSIpDQpgYGANCg0KT3VyIG1hdHJpeCBzY2F0dGVycGxvdCBtYXkgYmUgdG9vIGJpZyB0byBmaXQgb24gb3VyIHNjcmVlbi4gSG93ZXZlciwgaWYgeW91IHJ1biB0aGUgYWJvdmUgY29kZSBpbiB5b3VyIGNvbnNvbGUsIHlvdSB3aWxsIGdldCB0aGUgbWF0cml4IHBsb3QgaW4gUnN0dWRpbydzIGdyYXBoaWNzIGFyZWEuIFRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgYWJvdmUgcGxvdHMgZGVwZW5kcyB1cG9uIHRoZSBsYWJlbGluZyBvZiB0aGUgZGlhZ29uYWwgcGFuZWxzLCBydW5uaW5nIGZyb20gdGhlIHRvcCBsZWZ0IHRvIHRoZSBib3R0b20gcmlnaHQuIFRoZXkgd2lsbCBhcHBlYXIgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIGNvbHVtbnMgZ2l2ZW4gYXMgdGhlIGZpcnN0IGFyZ3VtZW50LiBUaGVzZSAibGFiZWwgcGFuZWxzIiBhbGxvdyB5b3UgdG8gZGV0ZXJtaW5lIHdoaWNoIGluZGl2aWR1YWwgcGxvdCBpbiB0aGUgbWF0cml4IGNvcnJlc3BvbmRzIHRvIGVhY2ggcGFpciBvZiB2YXJpYWJsZXMuIEZvciBpbnN0YW5jZSwgdGhlIGZpcnN0IGNvbHVtbiBvZiB0aGUgc2NhdHRlcnBsb3QgbWF0cml4IGNvcnJlc3BvbmRzIHRvIGFuIHgtYXhpcyB2YXJpYWJsZSBvZiBlZHVjYXRpb247IHRoZSB0aGlyZCByb3cgb2YgdGhlIG1hdHJpeCBjb3JyZXNwb25kcyB0byBhIHktYXhpcyB2YXJpYWJsZSBvZiB3b21lbiwgYW5kIGVhY2ggcm93IGFuZCBjb2x1bW4gZGlzcGxheXMgYSBzY2FsZSB0aGF0IGlzIGNvbnN0YW50IG1vdmluZyBsZWZ0L3JpZ2h0IG9yIHVwL2Rvd24sIHJlc3BlY3RpdmVseS4gVGhlIHBsb3Qgb2YgcHJlc3RpZ2UoeSkgb24gaW5jb21lKHgpIGF0IHBvc2l0aW9uIHJvdyA0IGFuZCBjb2x1bW4gMiBkaXNwbGF5cyB0aGUgc2FtZSBkYXRhIGFzIHRoZSBzY2F0dGVycGxvdCBhdCBwb3NpdGlvbiByb3cgMiBhbmQgY29sdW1uIDQgYnV0IGZsaXBwZWQgb24gaXRzIGF4aXMuIExpa2V3aXNlLCB0aGUgcGxvdCBvZiBpbmNvbWUoeSkgb24gZWR1Y2F0aW9uKHgpIGF0IHBvc2l0aW9uIHJvdyAyIGFuZCBjb2x1bW4gMSBkaXNwbGF5cyB0aGUgc2FtZSBkYXRhIGFzIHRoZSBzY2F0dGVycGxvdCBhdCBjb2x1bW4gMiByb3cgMSBmbGlwcGVkZCBvbiBpdHMgYXhpcy4gVGhlIHNjYXR0ZXJwbG90IG1hdHJpY2VzIHRoZXJlZm9yZSBhbGxvdyBmb3IgZWFzaWVyIGNvbXBhcmlzb24gb2YgcGFpcndpc2UgcmVsYXRpb25zaGlwcyBmb3JtZWQgYnkgb2JzZXJ2YXRpb25zIG1hZGUgb24gbXVsdGlwbGUgY29udGludW91cyB2YXJpYWJsZXMuIEluc3RlYWQgb2Ygdmlld2luZyBhIHBhbm9yYW1pYyByZWxhdGlvbnNoaXAgYmV0d2VlbiBhbGwgdGhlIHZhcmlhYmxlcyBpbiBvdXIgc3Vic2V0IGRhdGFzZXQsIGxldCdzIHVzZSBzaW1wbGUgc2NhdHRlciBwbG90cyB0byB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMuDQoNCigyKSBTQ0FUVEVSUExPVA0KDQpBIHNjYXR0ZXJwbG90IGlzIGEgdXNlZnVsIHdheSB0byB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMgZGlzcGxheWVkIGFzIHgteSBjb29yZGluYXRlIHBsb3RzLiBTaW1pbGFyIHRvIGNvcnJlbGF0aW9ucywgc2NhdHRlcnBsb3RzIGFyZSBvZnRlbiB1c2VkIHRvIG1ha2UgaW5pdGlhbCBkaWFnbm9zZXMgYmVmb3JlIGFueSBzdGF0aXN0aWNhbCBhbmFseXNlcyBhcmUgcGVyZm9ybWVkLg0KDQpUaGUgc2ltcGxlc3Qgd2F5IHRvIGNyZWF0ZSBhIHNjYXR0ZXJwbG90IGlzIHRvIGRpcmVjdGx5IGdyYXBoIHR3byB2YXJpYWJsZXMgdXNpbmcgdGhlIGRlZmF1bHQgc2V0dGluZ3MuIEluIFIsIHRoaXMgY2FuIGJlIGFjaGlldmVkIHVzaW5nIHRoZSBmb3JtdWxhIHBsb3QoVkFSWCwgVkFSWSkgZnVuY3Rpb24sIHdoZXJlIFZBUlggaXMgdGhlIHZhcmlhYmxlIHRvIHBsb3QgYWxvbmcgdGhlIHgtYXhpcyBhbmQgVkFSWSBpcyB0aGUgdmFyaWFibGUgdG8gcGxvdCBhbG9uZyB0aGUgeS1heGlzLiBJIHdpbGwgYWxzbyBhZGQgdGhlIGdncGxvdDIgdmVyc2lvbiBmb3IgdGhlIHNjYXR0ZXJwbG90LiBMZXQncyBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlZHVjYXRpb24gYW5kIGluY29tZS4NCg0KYGBge3J9DQojIFBpY3R1cmUgb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVkdWNhdGlvbiBhbmQgaW5jb21lDQpwbG90KHN1YnNldC5kYXRhJGVkdWNhdGlvbiwgc3Vic2V0LmRhdGEkaW5jb21lLCB4bGFiID0gIkVkdWNhdGlvbiIsIHlsYWIgPSAiSW5jb21lIiwgcGNoID0gMTYsIGNvbCA9ICJibHVlIiwNCiAgICAgbWFpbiA9ICIgU2NhdHRlcnBsb3Qgb2YgRWR1Y2F0aW9uIHZzLiBJbmNvbWUiKSANCmBgYA0KDQpgYGB7cn0NCiMgU2FtZSBwbG90IG5pY2VseSBkb25lIHdpdGggZ2dwbG90Mg0KDQpnZ3Bsb3Qoc3Vic2V0LmRhdGEpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IGluY29tZSksIGNvbCA9ICdibHVlJywgc2l6ZSA9IDMpICsNCiAgZ2d0aXRsZSgiRWR1Y2F0aW9uIHZzLiBJbmNvbWUgU2NhdHRlcnBsb3QiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCkZyb20gdGhlIG91dHB1dCwgd2Ugc2VlIHRoYXQgYXMgdGhlIGF2ZXJhZ2Ugb2YgeWVhcnMgb2YgZWR1Y2F0aW9uIGFuZCBpbmNvbWUgYXJlIHBvc2l0aXZlbHkgcmVsYXRlZC4gVGhhdCBpcyBoaWdoZXIgZWR1Y2F0aW9uIGlzIHJld2FyZGVkIGJ5IGhpZ2hlciBpbmNvbWUuIFRoZSBzY2F0dGVycGxvdCBhbHNvIGRpc3BsYXlzIHRoZSBwcmVzZW5jZSBvZiBwb3RlbnRpYWwgb3V0bGllcnMuIFdlIGNhbiBmdXJ0aGVyIGV4cGFuZCBvdXIgdmlzdWFsIGFuYWx5c2lzIGJ5IGNhbGN1bGF0aW5nIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIGxpbmUgb2YgYmVzdCBmaXQuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgc2xvcGUgYW5kIGludGVyY2VwdCBvZiBsaW5lIG9mIGJlc3QgZml0DQpjb2VmKGxtKGluY29tZSB+IGVkdWNhdGlvbiwgZGF0YSA9IHN1YnNldC5kYXRhKSkNCmBgYA0KDQpXZSB3aWxsIHVzZSBnZW9tX2FibGluZSgpIGZ1bmN0aW9uIHRvIGFkZCB0aGUgaW50ZXJjZXB0IGF2bHVlIGFuZCBlc3RpbWF0ZWQgY29lZmZpY2llbnQgb2YgZWR1Y2F0aW9uIHRvIG91ciBwbG90Lg0KDQpgYGB7cn0NCiMgQWRkaW5nIGEgbGluZSBvZiBiZXN0IGZpdDsgaW50ZXJjZXB0IGFuZCBzbG9wZQ0KZ2dwbG90KHN1YnNldC5kYXRhKSArDQogIA0KICBnZW9tX3BvaW50KGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gaW5jb21lKSwgY29sID0gImJsdWUiLCBzaXplID0gMykgKw0KICANCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IC0yODUzLCBzbG9wZSA9IDg5OSksIGNvbCA9ICJkYXJrcmVkIikgKw0KICANCiAgZ2d0aXRsZSgiRWR1Y2F0aW9uIHZzLiBJbmNvbWUgU2NhdHRlcnBsb3QgV2l0aCBUaGUgQmVzdCBGaXQgTGluZSIpICsNCiAgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCldlIGNhbiB1c2Ugb3VyIG9yaWdpbmFsIFByZXN0aWdlIGRhdGFzZXQgdG8gZGlzcGxheSB0aGUgaW5jb21lIGJ5IHR5cGVzIG9mIG9jY3VwYXRpb24uIEluIG91ciBkYXRhc2V0LCB3ZSBoYXZlIHR5cGUsIGEgZmFjdG9yIHZhcmlhYmxlLCB0aGF0IHJlZmVycyB0byB0aHJlZSBvY2N1cGF0aW9uYWwgbGV2ZWxzOiBiYyBmb3IgQmx1ZSBDb2xsYXI7IHByb2YgZm9yIFByb2Zlc3Npb25hbCwgTWFuYWdlcmlhbCwgYW5kIFRlY2huaWNhbDsgYW5kIHdjIGZvciBXaGl0ZSBDb2xsYXIuIExldCdzIGNvbnN0cnVjdCBhIGJhciBwbG90IGZvciB0aGlzIGNhdGVnb3JpYWwgdmFyaWFibGUuDQoNCmBgYHtyfQ0KIyBFYXN5IHdheSB0byBkbyBpdCB1c2luZyB0aGUgcGxvdCgpIGZ1bmN0aW9uDQoNCnBsb3QoUHJlc3RpZ2UkdHlwZSwgbWFpbiA9ICJCYXIgUGxvdCBvZiBPY2N1cGF0aW9uYWwgVHlwZXMiLCBjb2wgPSAiY3lhbiIsIHlsYWIgPSAiQ09VTlQiKQ0KYGBgDQoNClRoZSBvdXRwdXQgZGVtb25zdHJhdGVzIHRoYXQgdGhlcmUgYXJlIG1vcmUgYmx1ZSBjb2xsYXIgd29ya2VycyBpbiB0aGUgc3VydmV5IHRoYW4gYW55IG90aGVyIG9jY3VwYXRpb25hbCBncm91cHMuIFRoYXQgbWF5IGV4cGxhaW4gdGhlIGRpc3Byb3BvcnRpb25hbCBkaXN0cmlidXRpb24gb2YgaW5jb21lLg0KDQpgYGB7cn0NCiMgU2FtZSBwbG90IHVzaW5nIGdncGxvdDINCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIA0KICBnZW9tX2JhcihhZXMoeCA9IHR5cGUpKSArDQogIA0KICBnZ3RpdGxlKCJDb3VudCBvZiBJbmN1bWJlbnRzJyBPY2N1cGF0aW9uIikgKw0KICANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KV2UgY2xlYXJseSBzZWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIHBsb3RzLiBUaGUgZmlyc3Qgb25lIGRvZXNuJ3QgdGFrZSBpbnRvIGFjY291bnQgTkFzIG9jY3VwYXRpb24gdHlwZSwgYnV0IHRoZSBzZWNvbmQgcGxvdCB1c2luZyBnZ3Bsb3QgdGFrZXMgaW50byBhY2NvdW50IHRoZSBOQXMuIE1vdmluZyBmb3J3YXJkLCB3ZSBhcmUgZ29pbmcgdG8gcGxvdCBpbmNvbWUgYWdhaW5zdCB0eXBlcyBvZiBvY2N1cGF0aW9ucyBhbmQgYWRkIGxhYmVscyB0byB0aGVzZSBvY2N1cGF0aW9ucy4gV2UgYXJlIGdvaW5nIHRvIGluc3RhbGwgYSB1c2VmdWwgcGFja2FnZSBjYWxsZWQgZ2dyZXBlbCB0aGF0IGxhYmVscyB0aGUgc2NhdHRlcnBsb3QncyBwb2ludHMuDQoNCmBgYHtyfQ0KZ2dwbG90KFByZXN0aWdlLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IGluY29tZSkpICsNCiAgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0eXBlKSwgc2l6ZSA9IDQpICsNCiAgDQogICBnZ3RpdGxlKCJTY2F0dGVycGxvdCBXaXRoIERhdGEgTGFiZWxzIikgKw0KICANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KRnJvbSB0aGUgb3V0cHV0LCB3ZSBzZWUgdGhhdCBQcm9mZXNzaW9uYWxzIGhhdmUgaGlnaGVyIGF2ZXJhZ2UgeWVhcnMgb2YgZWR1Y2F0aW9uIGFuZCBlYXJuIGhpZ2hlciBpbmNvbWUgdGhhbiBvdGhlciBncm91cHMuIEJsdWUgY29sbGFycyBoYXZlIGxlc3MgYXZlcmFnZSB5ZWFycyBvZiBlZHVjYXRpb24gYW5kIGFyZSBpbiB0aGUgbG93ZXIgYm91bmRzIG9mIGluY29tZS4NCg0KYGBge3J9DQojIFVzaW5nIGdncmVwZWwgdG8gY29tZSB1cCB3aXRoIHRoZSBzYW1lIHBsb3QNCmluc3RhbGwucGFja2FnZXMoImdncmVwZWwiKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3JlcGVsKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UsIG1hcHBpbmcgPSBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IGluY29tZSkpICsNCiAgDQogIGdlb21fcG9pbnQoKSArDQogIA0KICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gdHlwZSksIHNpemUgPSA0KSArDQogIA0KICBnZ3RpdGxlKCJTY2F0dGVycG90IFdpdGggRGF0YSBQb2ludHMgYW5kIExhYmVscyIpICsNCiAgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCldlIGNhbiBtYXAgdmFyaWFibGVzIHRvIG90aGVyIHZhcmlhYmxlcyBhcyB3ZWxsLg0KDQpgYGB7cn0NCmdncGxvdChQcmVzdGlnZSwgbWFwcGluZyA9IGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gaW5jb21lKSkgKw0KICANCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwcmVzdGlnZSwgc2hhcGUgPSB0eXBlKSkgKw0KICANCiAgZ2d0aXRsZSgiRWR1Y2F0aW9uIHZzLiBJbmNvbWUgU2NhdHRlcnBvdCBQZXIgUHJlc3RpZ2UgYW5kIFR5cGUiKSArDQogIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpJbiB0aGlzIG91dHB1dCB3ZSBtYXAgaW5jb21lIGFuZCBlZHVjYXRpb24gd2l0aCB0eXBlIGFuZCBwcmVzdGlnZS4gVGhlIG91dHB1dCBkZW1vbnN0cmF0ZXMgdGhhdCBibHVlIGNvbGxhcnMgYW5kIHdoaXRlIGNvbGxhciB3b3JrZXJzIGhhdmUgbG93IHByZXN0aWdlIHNjb3JlcywgY29tcGFyZWQgdG8gcHJvZmVzc2lvbmFsLiBJbmNvbWUgYW5kIGxldmVscyBvZiBlZHVjYXRpb24gc2VlbSB0byBleHBsYWluIHRoZXNlIHRyZW5kcy4=