Introduction to Credit Scoring and Scorecard Approach
Granting credit to both retail and nonretail (e.g., corporate) customers is the core business of a bank. In doing so, banks need to have adequate systems to decide to whom to grant credit. Credit scoring is a key risk assessment technique to analyze and quantify a potential obligor’s credit risk. Essentially, credit scoring aims at quantifying the likelihood that an obligor will repay the debt. The outcome of the credit scoring exercise is a score reflecting the creditworthiness of the obligor.
Throughout the past few decades banks have gathered plenty of information describing the default behavior of their customers. Examples are historical information about a customer’s date of birth, gender, income, employment status, and so on. All this data has been nicely stored into huge (e.g., relational) databases or data warehouses. On top of this, banks have accumulated lots of business experience about their credit products. As an example, many credit experts do a pretty good job of discriminating between low-risk and high-risk mort- gages using their business expertise only. It is now the aim of credit scoring to analyze both sources of data in more detail and come up with a statistically based decision model that allows scoring future credit applications and ultimately deciding which ones to accept and which to reject.
For the historical customers, we know which ones turned out to be good payers and which ones turned out to be bad payers. This good/bad status is now the binary target variable Y, which we will relate to all information available at scoring time about our obligors. The goal of credit scoring is now to quantify this relationship as precisely as possible to assist credit decisions, monitoring, and management. Banks score bor- rowers at loan application, as well as at regular times during the term of a financial contract (generally loans, loan commitments, and guarantees).
Once we have our credit scoring model built, we can then use it to decide whether the credit application should be accepted or rejected, or to derive the probability of a future default. To summarize, credit scoring is a key risk management tool for a bank to optimally manage, understand, and model the credit risk it is exposed to.
The Two Available Approaches: Judgmental Versus Statistical Scoring
There are basically two main approaches to assessing credit risk: (1) the judgmental approach, and (2) the statistical approach. Both rely on historical information, but the type of information they use is different.
The judgmental approach is a qualitative, expert-based approach whereby, based on business experience and common sense, the credit expert or credit committee, which is a group of credit experts, will make a decision about the credit risk. Usually, this is done based on inspecting the five Cs of the applicant and loan:
Character measures the borrower’s character and integrity (e.g., reputation, honesty, etc.).
Capital measures the difference between the borrower’s assets (e.g., car, house, etc.) and liabilities (e.g., renting expenses, etc.).
Collateral measures the collateral provided in case payment problems occur (e.g., house, car, etc.).
Capacity measures the borrower’s ability to pay (e.g., job status, income, etc.).
Condition measures the borrower’s circumstances (e.g., market conditions, competitive pressure, seasonal character, etc.).
In analyzing this information, a qualitative or subjective evaluation of the credit risk is made. Although the judgmental approach might seem subjective and thus unsophisticated at first sight, it is still quite commonly used by banks for very specific credit portfolios such as project finance or new credit products.
With the emergence of statistical classification techniques at the beginning of the 1980s, banks became more and more interested in abandoning the judgmental approach and opting for a more formal data-based statistical approach.
The statistical approach is based on statistical analysis of historical data to find the optimal multivariate relationship between a customer’s characteristics and the binary good/bad target variable (Baesens et al. 2003). It is less subjective than the judgmental approach since it is not tied to a particular credit expert’s background knowledge and experience.
The statistical approach aims at building scorecards, which are based on multivariate correlations between inputs (such as age, marital status, income, savings amount) and a target variable that reflects the risk of default. In other words, a scorecard will assign scores to each of those inputs. In our example, scores will be assigned to age, marital status, income, and savings amount. All those scores will then be added up and compared with the critical threshold, which specifies the minimum level of required credit quality. If the aggregated score exceeds the threshold, then credit will be granted. If it falls below the threshold, then credit will be withheld.
In practice, hybrid approaches may be applied. In a first step, a bank may generate informational values by judgmental scoring. An example may be an expert opinion of a credit analyst on the payment ethics of a borrower (e.g., as a discrete number between 1 and 5). In a second step, the bank may aggregate this judgmental score and other hard information into a statistical score.
Advantages of Statistical Approach to Credit Scoring
Generally speaking, the statistical approach to credit scoring has many advantages compared with the judgmental approach. First, it is better in terms of speed and accuracy. We can now make faster decisions than we were able to do with the judgmental approach. This is especially relevant when working in an online environment where credit decisions need to be made quickly, possibly in real time. Because a credit scorecard is essentially a mathematical formula, it can be easily programmed and evaluated in an automated and fast way.
Another advantage of having statistical credit scoring models is consistency. We no longer have to rely upon the experience, intuition, or common sense of one or multiple business experts. Now it’s just a mathematical formula, and the formula will always evaluate in exactly the same way if given the same set of inputs, like age, marital status, income, and so on.
Finally, statistical credit scoring models will typically also be more powerful than judgmental models. This performance boost will allow a reduction of bad debt loss and operating costs, and consequently it will also improve portfolio management.
Logistic Regression for Developing a Scorecard Model
Logistic regression is a very popular credit scoring classification technique due to its simplicity and good performance. Just as with linear regression, once the parameters have been estimated, the regression can be evaluated in a straightforward way, contributing to its operational efficiency. From an interpretability viewpoint, it can be easily transformed into an interpretable, user-friendly, points-based credit scorecard.
An Important Technical Aspect of Developing Logistic Regression: Variable Selection
Variable selection aims at reducing the number of variables in a model. It will make the model more concise and faster to evaluate. Logistic regression has a built-in procedure to perform variable selection. It is based on a statistical hypothesis test to verify whether the coefficient of a variable included in the model is significantly different from zero.
In credit scoring, it is very important to be aware that statistical significance is only one evaluation criterion to consider in doing variable selection. As mentioned before, interpretability is also an important criterion (Martens et al. 2007). In logistic regression, this can be easily evaluated by inspecting the sign of the regression coefficient. It is highly preferable that a coefficient has the same sign as anticipated by the credit expert; otherwise he or she will be reluctant to use the model. Coefficients can have unexpected signs due to multicollinearity issues, noise, or small sample effects. Sign restrictions can be easily enforced in a forward regression setup by preventing variables with the wrong sign from entering the model.
Legal issues also need to be properly taken into account. For example, in the United States, there is the Equal Credit Opportunity Act, which states that no one is allowed to dis- criminate based on gender, age, ethnic origin, nationality, beliefs, and so on. These variables must not be included in a credit scorecard. Other countries have other regulations, and it is important to be aware of this.
Currently, some methods for choosing relevant variables are:
Based on AIC Criterion for selection.
Information Value (IV) Criterion.
Approaches based on Machine Learning Algorithms.
Key Characteristics of a Useful Scorecard Model
Before bringing a scorecard into production, it needs to be thoroughly evaluated. Depending on the exact setting and usage of the model, different aspects may need to be assessed during evaluation in order to ensure the model is acceptable for implementation. Key characteristics of successful scorecard model are:
Interpretability: A scorecard needs to be interpretable. In other words, a deeper understanding of the detected default behavior is required, for instance to validate the scorecard before it can be used. This aspect involves a certain degree of subjectivism, since interpretability may depend on the credit expert’s knowledge. The interpretability of a model depends on its format, which in turn is determined by the adopted analytical technique. Models that allow the user to understand the underlying reasons why the model signals a customer to be a defaulter are called white box models, whereas complex, incomprehensible, mathematical models are often referred to as black box models.
Statistical accuracy: Refers to the detection power and the correctness of the scorecard in labeling customers as defaulters. Several statistical evaluation criteria exist and may be applied to evaluate this aspect, such as the hit rate, lift curves, area under the curve (AUC), and so on. Statistical accuracy may also refer to statistical significance, meaning that the patterns that have been found in the data have to be valid and not the consequence of noise. In other words, we need to make sure that the model generalizes well and is not overfitted to the historical data set.
Economical cost: Developing and implementing a scorecard involves a significantcost to an organization. The total cost includes the costs togather, preprocess, and analyze the data, and the costs to putthe resulting scorecards into production. In addition, the softwarecosts as well as human and computing resources should betaken into account. Possibly also external (e.g., credit bureau)data has to be bought to enrich the available in-house data.Clearly it is important to perform a thorough cost-benefit analysisat the start of the credit scoring project, and to gain insight intothe constituent factors of the return on investment of building ascorecard system.
Regulatory compliance: A scorecard should be in line and compliant with all applicable regulations and legislation. In a credit scoring setting, the Basel Accords specify what information can or cannot be used and how the target (i.e., default) should be defined. Other regulations (e.g., with respect to privacy and/or discrimination) should also be respected.
Some Practical Aspects of Using Scorecard Model at Banks
The most important usage of application scores is to decide on loan approval. The scores can also be used for pricing purposes. Risk-based pricing (sometimes also referred to as risk-adjusted pricing) sets the price or other characteristics (e.g., loan term, collateral) of the loan based on the perceived risk as measured by the application score. A lower score will imply a higher interest rate and vice versa.
R Codes for Developing Scorecard Model using Logistic Regression
In this section the author will present R Codes for developing Scorecard Model with a focus on:
Problem of processing data (missing data).
Variable Selection based on: (1) BIC Criterion, and (2) IV Value.
Finding optimal threshold for classifying and selecting by using simulations.
Comparing results from alternative approaches.
Deploying model selected in practice by using a R function that takes inputs and returns final score outputs.
Data used in this post can be download from: http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv.
#=================================
# State 1: Data Pre-processing
#=================================
# Load some packages for data manipulation:
library(tidyverse)
library(magrittr)
# Clear workspace:
rm(list = ls())
# Import data:
hmeq <- read.csv("http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv")
# Function for detecting NA observations:
na_rate <- function(x) {x %>% is.na() %>% sum() / length(x)}
sapply(hmeq, na_rate) %>% round(2)
## BAD LOAN MORTDUE VALUE REASON JOB YOJ DEROG DELINQ
## 0.00 0.00 0.09 0.02 0.00 0.00 0.09 0.12 0.10
## CLAGE NINQ CLNO DEBTINC
## 0.05 0.09 0.04 0.21
# Function replaces NA by mean:
replace_by_mean <- function(x) {
x[is.na(x)] <- mean(x, na.rm = TRUE)
return(x)
}
# A function imputes NA observations for categorical variables:
replace_na_categorical <- function(x) {
x %>%
table() %>%
as.data.frame() %>%
arrange(-Freq) ->> my_df
n_obs <- sum(my_df$Freq)
pop <- my_df$. %>% as.character()
set.seed(29)
x[is.na(x)] <- sample(pop, sum(is.na(x)), replace = TRUE, prob = my_df$Freq)
return(x)
}
# Use the two functions:
df <- hmeq %>%
mutate_if(is.factor, as.character) %>%
mutate(REASON = case_when(REASON == "" ~ NA_character_, TRUE ~ REASON),
JOB = case_when(JOB == "" ~ NA_character_, TRUE ~ JOB)) %>%
mutate_if(is_character, as.factor) %>%
mutate_if(is.numeric, replace_by_mean) %>%
mutate_if(is.factor, replace_na_categorical)
# Split our data:
df_train <- df %>%
group_by(BAD) %>%
sample_frac(0.5) %>%
ungroup() # Use 50% data set for training model.
df_test <- dplyr::setdiff(df, df_train) # Use 50% data set for validation.
#===============================================================================
# Scenario 1: Scorecard model with variables selected based on AIC Criterion
#===============================================================================
# Load purrr package for looping:
library(purrr)
# Function lists all combinations of variables:
all_combinations <- function(your_predictors) {
n <- length(your_predictors)
map(1:n, function(x) {combn(variables, x)}) %>%
map(as.data.frame) -> k
all_vec <- c()
for (i in 1:n) {
df <- k[[i]]
n_col <- ncol(df)
for (j in 1:n_col) {
my_vec <- df[, j] %>% as.character() %>% list()
all_vec <- c(all_vec, my_vec)
}
}
return(all_vec)
}
# All potential variables can be used for modelling Logistic Regression:
variables <- df %>% select(-BAD) %>% names()
# AICs for all models. Note that there will be 4095 Logistic Models thus
# training all models may be a time-consuming process:
system.time(
all_combinations(variables) %>%
map(function(x) {as.formula(paste("BAD ~", paste(x, collapse = " + ")))}) %>%
map(function(formular) {glm(formular, family = binomial, data = df_train)}) %>%
map_dbl("aic") -> aic_values
)
## user system elapsed
## 45.85 1.23 47.31
# The conbination of Predictors that results min AIC:
all_combination_of_vars <- variables %>% all_combinations()
predictors_selected <- all_combination_of_vars[[which.min(aic_values)]]
# Thus there are 10 predictors selected based on AIC criterion:
predictors_selected
## [1] "LOAN" "VALUE" "REASON" "JOB" "DEROG" "DELINQ" "CLAGE"
## [8] "NINQ" "CLNO" "DEBTINC"
# Data frame for training Logistic Regression:
df_train_aic <- df_train %>% select(predictors_selected, "BAD")
#-------------------------------------------------------------------------------
# Develop a scorecard Model for variables based on the results from WOE and IV
#-------------------------------------------------------------------------------
library(scorecard)
# Generates optimal binning for numerical, factor and categorical variables:
bins_var <- woebin(df_train_aic, y = "BAD", no_cores = 20, positive = "BAD|1")
## Binning on 2980 rows and 11 columns in 0: 0:11
# Creates a data frame of binned variables for Logistic Regression:
df_train_woe <- woebin_ply(df_train_aic, bins_var)
# Logistic Regression:
my_logistic <- glm(BAD ~ ., family = binomial, data = df_train_woe)
# Show results:
my_logistic %>% summary()
##
## Call:
## glm(formula = BAD ~ ., family = binomial, data = df_train_woe)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.7357 -0.4290 -0.2355 -0.1420 3.0823
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.34162 0.06399 -20.967 < 2e-16 ***
## LOAN_woe 0.67951 0.13148 5.168 2.37e-07 ***
## VALUE_woe 0.68516 0.13341 5.136 2.81e-07 ***
## REASON_woe -0.04987 0.60450 -0.082 0.934256
## JOB_woe 0.77606 0.19054 4.073 4.64e-05 ***
## DEROG_woe 0.57534 0.10453 5.504 3.72e-08 ***
## DELINQ_woe 0.93429 0.07387 12.647 < 2e-16 ***
## CLAGE_woe 0.87815 0.12425 7.067 1.58e-12 ***
## NINQ_woe 0.57934 0.15837 3.658 0.000254 ***
## CLNO_woe 0.71729 0.19969 3.592 0.000328 ***
## DEBTINC_woe 0.94000 0.04614 20.371 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2976.8 on 2979 degrees of freedom
## Residual deviance: 1750.2 on 2969 degrees of freedom
## AIC: 1772.2
##
## Number of Fisher Scoring iterations: 6
# Calculate scorecard scores for variables based on the results from woebin and glm:
my_card <- scorecard(bins_var, my_logistic, points0 = 600, odds0 = 1/19, pdo = 50)
# Show Results:
library(stringr)
do.call("bind_rows", my_card) %>%
slice(-1) %>%
select(-breaks, -is_special_values, -count, -count_distr, -good, -bad, -badprob) %>%
mutate_if(is.numeric, function(x) {round(x, 3)}) %>%
mutate(bin = bin %>%
str_replace_all("\\[", "From ") %>%
str_replace_all("\\,", " to ") %>%
str_replace_all("\\)", "")) -> iv_for_predictors_point
iv_for_predictors_point %>%
knitr::kable(col.names = c("Predictor", "Group", "WOE", "Scorecard", "Bin IV", "Total IV"))
LOAN |
From -Inf to 6000 |
1.193 |
-58 |
0.109 |
0.231 |
LOAN |
From 6000 to 8000 |
0.523 |
-26 |
0.018 |
0.231 |
LOAN |
From 8000 to 15000 |
-0.077 |
4 |
0.002 |
0.231 |
LOAN |
From 15000 to 16000 |
0.638 |
-31 |
0.029 |
0.231 |
LOAN |
From 16000 to 35000 |
-0.427 |
21 |
0.070 |
0.231 |
LOAN |
From 35000 to Inf |
0.220 |
-11 |
0.004 |
0.231 |
VALUE |
From -Inf to 45000 |
0.884 |
-44 |
0.074 |
0.210 |
VALUE |
From 45000 to 85000 |
-0.124 |
6 |
0.005 |
0.210 |
VALUE |
From 85000 to 100000 |
-0.511 |
25 |
0.034 |
0.210 |
VALUE |
From 100000 to 105000 |
1.015 |
-50 |
0.086 |
0.210 |
VALUE |
From 105000 to Inf |
-0.182 |
9 |
0.011 |
0.210 |
REASON |
DebtCon |
-0.075 |
0 |
0.004 |
0.011 |
REASON |
HomeImp |
0.151 |
1 |
0.008 |
0.011 |
JOB |
Mgr |
0.192 |
-11 |
0.005 |
0.111 |
JOB |
Office |
-0.665 |
37 |
0.060 |
0.111 |
JOB |
Other |
0.186 |
-10 |
0.015 |
0.111 |
JOB |
ProfExe |
-0.240 |
13 |
0.012 |
0.111 |
JOB |
Sales% to %Self |
0.537 |
-30 |
0.018 |
0.111 |
DEROG |
From -Inf to 1 |
-0.263 |
11 |
0.056 |
0.325 |
DEROG |
From 1 to Inf |
1.272 |
-53 |
0.270 |
0.325 |
DELINQ |
From -Inf to 0.4494423792 |
-0.581 |
39 |
0.195 |
0.640 |
DELINQ |
From 0.4494423792 to 1 |
-0.246 |
17 |
0.005 |
0.640 |
DELINQ |
From 1 to Inf |
1.235 |
-83 |
0.440 |
0.640 |
CLAGE |
From -Inf to 80 |
0.845 |
-54 |
0.068 |
0.257 |
CLAGE |
From 80 to 150 |
0.364 |
-23 |
0.046 |
0.257 |
CLAGE |
From 150 to 190 |
0.032 |
-2 |
0.000 |
0.257 |
CLAGE |
From 190 to Inf |
-0.662 |
42 |
0.142 |
0.257 |
NINQ |
From -Inf to 1 |
-0.333 |
14 |
0.042 |
0.153 |
NINQ |
From 1 to 2 |
-0.034 |
1 |
0.000 |
0.153 |
NINQ |
From 2 to 3 |
0.077 |
-3 |
0.001 |
0.153 |
NINQ |
From 3 to 4 |
0.471 |
-20 |
0.017 |
0.153 |
NINQ |
From 4 to Inf |
1.044 |
-44 |
0.093 |
0.153 |
CLNO |
From -Inf to 10 |
0.655 |
-34 |
0.049 |
0.092 |
CLNO |
From 10 to 27 |
-0.182 |
9 |
0.021 |
0.092 |
CLNO |
From 27 to 30 |
0.330 |
-17 |
0.007 |
0.092 |
CLNO |
From 30 to 39 |
-0.085 |
4 |
0.001 |
0.092 |
CLNO |
From 39 to Inf |
0.492 |
-25 |
0.015 |
0.092 |
DEBTINC |
From -Inf to 22 |
-0.885 |
60 |
0.032 |
1.677 |
DEBTINC |
From 22 to 33 |
-1.489 |
101 |
0.344 |
1.677 |
DEBTINC |
From 33 to 34 |
1.585 |
-107 |
0.864 |
1.677 |
DEBTINC |
From 34 to 42 |
-1.268 |
86 |
0.409 |
1.677 |
DEBTINC |
From 42 to Inf |
0.630 |
-43 |
0.028 |
1.677 |
# Information Values for predictors:
iv_for_predictors_point %>%
group_by(variable) %>%
summarise(iv_var = mean(total_iv)) %>%
ungroup() %>%
arrange(iv_var) %>%
mutate(variable = factor(variable, levels = variable)) -> iv_values
theme_set(theme_minimal())
iv_values %>%
ggplot(aes(variable, iv_var)) +
geom_col(fill = "#377eb8") +
coord_flip() +
geom_col(data = iv_values %>% filter(iv_var < 0.1), aes(variable, iv_var), fill = "grey60") +
geom_text(data = iv_values %>% filter(iv_var < 0.1), aes(label = round(iv_var, 3)),
hjust = -0.1, size = 5, color = "grey40") +
geom_text(data = iv_values %>% filter(iv_var >= 0.1), aes(label = round(iv_var, 3)),
hjust = -.1, size = 5, color = "#377eb8") +
labs(title = "Figure 1: Information Value (IV) for Variables",
x = NULL, y = "Information Value (IV)") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 2)) +
theme(panel.grid.major.y = element_blank()) +
theme(plot.margin = unit(c(1, 1, 1, 1), "cm"))

# Scorecard point for all observations from train data set:
my_points_train <- scorecard_ply(df_train_aic, my_card, only_total_score = FALSE, print_step = 0) %>% as.data.frame()
# Some statistics scorecard by group:
df_scored_train <- df_train_aic %>%
mutate(SCORE = my_points_train$score) %>%
mutate(BAD = case_when(BAD == 1 ~ "Default", TRUE ~ "NonDefault"))
df_scored_train %>%
group_by(BAD) %>%
summarise_each(funs(min, max, median, mean, n()), SCORE) %>%
mutate_if(is.numeric, function(x) {round(x, 0)}) %>%
knitr::kable(caption = "Table 1: Scorecad Points by Group for Train Data")
Table 1: Scorecad Points by Group for Train Data
Default |
22 |
729 |
362 |
367 |
594 |
NonDefault |
119 |
783 |
618 |
592 |
2386 |
df_scored_train %>%
group_by(BAD) %>%
summarise(tb = mean(SCORE)) %>%
ungroup() -> mean_score_train
df_scored_train %>%
ggplot(aes(SCORE, color = BAD, fill = BAD)) +
geom_density(alpha = 0.3) +
geom_vline(aes(xintercept = mean_score_train$tb[1]), linetype = "dashed", color = "red") +
geom_vline(aes(xintercept = mean_score_train$tb[2]), linetype = "dashed", color = "blue") +
geom_text(aes(x = 400 - 15, y = 0.0042, label = mean_score_train$tb[1] %>% round(0)), color = "red", size = 4) +
geom_text(aes(x = 565, y = 0.0042, label = mean_score_train$tb[2] %>% round(0)), color = "blue", size = 4) +
theme(legend.title = element_blank()) +
theme(legend.position = c(0.2, 0.8)) +
labs(x = NULL, y = NULL, title = "Figure 2: Scorecard Distribution by two Credit Groups for Train Data",
subtitle = "The scorecard point is a numeric expression measuring creditworthiness. Commercial Banks\nusually utilize it as a method to support the decision-making about credit applications.")

# Scorecard Points for test data set:
df_test_aic <- df_test %>% select(predictors_selected, "BAD")
my_points_test <- scorecard_ply(df_test_aic, my_card, print_step = 0,
only_total_score = FALSE) %>% as.data.frame()
df_scored_test <- df_test_aic %>%
mutate(SCORE = my_points_test$score) %>%
mutate(BAD = case_when(BAD == 1 ~ "Default", TRUE ~ "NonDefault"))
df_scored_test %>%
group_by(BAD) %>%
summarise_each(funs(min, max, median, mean, n()), SCORE) %>%
mutate_if(is.numeric, function(x) {round(x, 0)}) %>%
knitr::kable(caption = "Table 2: Scorecad Points by Group for Test Data")
Table 2: Scorecad Points by Group for Test Data
Default |
91 |
740 |
383 |
395 |
595 |
NonDefault |
189 |
760 |
614 |
590 |
2385 |
df_scored_test %>%
group_by(BAD) %>%
summarise(tb = mean(SCORE)) %>%
ungroup() -> mean_score_test
df_scored_test %>%
ggplot(aes(SCORE, color = BAD, fill = BAD)) +
geom_density(alpha = 0.3) +
geom_vline(aes(xintercept = mean_score_test$tb[1]), linetype = "dashed", color = "red") +
geom_vline(aes(xintercept = mean_score_test$tb[2]), linetype = "dashed", color = "blue") +
geom_text(aes(x = 412, y = 0.0042, label = mean_score_test$tb[1] %>% round(0)), color = "red", size = 4) +
geom_text(aes(x = 570, y = 0.0042, label = mean_score_test$tb[2] %>% round(0)), color = "blue", size = 4) +
theme(legend.title = element_blank()) +
theme(legend.position = c(0.2, 0.8)) +
labs(x = NULL, y = NULL, title = "Figure 3: Scorecard Distribution by two Credit Groups for Test Data",
subtitle = "The scorecard point is a numeric expression measuring creditworthiness. Commercial Banks\nusually utilize it as a method to support the decision-making about credit applications.")

Some Criteria for Model Evaluation in Context of Cresit Scoring
It is impossible to use a scoring model effectively without knowing how accurate it is. First, one needs to select the best model according to some criteria for evaluating model performance. The methodology of credit scoring models and some measures of their quality have been discussed in surveys conducted by Hand and Henley (1997), Thomas (2000), and Crook at al. (2007). However, until just ten years ago, the general literature devoted to the issue of credit scoring was not substantial. Fortunately, the situation has improved in the last decade with the publication of works by Anderson (2007), Crook et al. (2007), Siddiqi (2006), Thomas et al. (2002), and Thomas (2009), all of which address the topic of credit scoring. The most used criteria in context of credit scoring are:
Gain or lift is a measure of the effectiveness of a classification model calculated as the ratio between the results obtained with and without the model. Gain and lift charts are visual aids for evaluating performance of classification models. However, in contrast to the confusion matrix that evaluates models on the whole population gain or lift chart evaluates model performance in a portion of the population.
K-S or Kolmogorov-Smirnov chart measures performance of classification models. More accurately, K-S is a measure of the degree of separation between the positive and negative distributions. The K-S is 100 if the scores partition the population into two separate groups in which one group contains all the positives and the other all the negatives. On the other hand, If the model cannot differentiate between positives and negatives, then it is as if the model selects cases randomly from the population. The K-S would be 0. In most classification models the K-S will fall between 0 and 100, and that the higher the value the better the model is at separating the positive from negative cases.
Area under ROC curve is often used as a measure of quality of the classification models. A random classifier has an area under the curve of 0.5, while AUC for a perfect classifier is equal to 1. In practice, most of the classification models have an AUC between 0.5 and 1.
Precision and Recall (for more detail: https://en.wikipedia.org/wiki/Precision_and_recall).
Model performance criteria will be presented in the following code chunk.
# Convert to binned data frame for test data:
df_test_woe <- woebin_ply(df_test_aic, bins_var)
# Caculate probabiliy of default (PD) for observations belonging test data:
test_pred <- predict(my_logistic, df_test_woe, type = "response")
# Model Performance for test data:
perf_eva(df_test_aic$BAD, test_pred,
type = c("ks", "lift", "roc", "pr"),
title = "Test Data")

## $KS
## [1] 0.5999
##
## $AUC
## [1] 0.8692
##
## $Gini
## [1] 0.7385
##
## $pic
## TableGrob (2 x 2) "arrange": 4 grobs
## z cells name grob
## pks 1 (1-1,1-1) arrange gtable[layout]
## plift 2 (1-1,2-2) arrange gtable[layout]
## proc 3 (2-2,1-1) arrange gtable[layout]
## ppr 4 (2-2,2-2) arrange gtable[layout]
Pobabilities of Default
Scorecards based on our model provide scores. A score is a measure that allows lenders to rank customers from high risk (low score) to low risk (high score) and as such provides a relative measure of credit risk. Scores are unlimited and can be measured within any range; they can even be negative. A score is not the same as a probability. A probability also allows us to rank, but on top of that, since it is limited between 0 and 1, it also gives an absolute interpretation of credit risk. Hence, probabilities provide more information than scores do. For application scoring, one does not need well-calibrated probabilities of default. However, for other application areas such as regulatory capital calculation in a Basel setting, as we will discuss later, calibrated default probabilities are needed (Van Gestel and Baesens 2009).
At this state we can evaluate probabilities of default (PD) for all observations by group as follows:
# Cteate a data frame of PD:
df_test_aic %>%
mutate(prob_default = test_pred,
BAD = case_when(BAD == 1 ~ "Default", TRUE ~ "NonDefault")) -> prob_default_df
# Show some statistics:
prob_default_df %>%
group_by(BAD) %>%
summarise_each(funs(min, max, median, mean, n()), prob_default) %>%
mutate_if(is.numeric, function(x) {round(x, 4)}) %>%
knitr::kable(caption = "Table 3: Probabilities of Default by Group for Test Data")
Table 3: Probabilities of Default by Group for Test Data
Default |
0.0074 |
0.9840 |
0.5152 |
0.4946 |
595 |
NonDefault |
0.0056 |
0.9407 |
0.0417 |
0.1140 |
2385 |
# PD Distribution by group:
prob_default_df %>%
group_by(BAD) %>%
summarise(tb = mean(prob_default)) %>%
ungroup() -> mean_prob_test
prob_default_df %>%
ggplot(aes(prob_default, color = BAD, fill = BAD)) +
geom_density(alpha = 0.3) +
geom_vline(aes(xintercept = mean_prob_test$tb[1]), linetype = "dashed", color = "red") +
geom_vline(aes(xintercept = mean_prob_test$tb[2]), linetype = "dashed", color = "blue") +
geom_text(aes(x = 0.44, y = 9.2, label = mean_prob_test$tb[1] %>% round(4)), color = "red", size = 4) +
geom_text(aes(x = 0.1 + 0.07, y = 9.2, label = mean_prob_test$tb[2] %>% round(4)), color = "blue", size = 4) +
theme(legend.title = element_blank()) +
theme(legend.position = c(0.88, 0.8)) +
theme(plot.margin = unit(c(1, 1, 1, 1), "cm")) +
labs(x = NULL, y = NULL, title = "Figure 4: PD Distribution by two Credit Groups for Test Data")

Search Optimal Threshold of Probability that maximize Accuracy
Accuracy criterion is widely used for evaluating model performance in context of credit scoring. However this measure is totally affected by threshold of probability that we select for classifier. In this section I will present approach to finding optimal threshold that maximizes accuracy by using simulation method.
# Function calculates model criteria for Logistic Classifier:
library(caret)
eval_fun <- function(thre) {
lapply(1:30, function(x) {
set.seed(x)
id <- createDataPartition(y = df_test_aic$BAD, p = 1000 / nrow(df_test_aic), list = FALSE)
test_df_mini <- df_test_aic[id, ]
df_test_woe_mini <- woebin_ply(test_df_mini, bins_var)
test_pred <- predict(my_logistic, df_test_woe_mini, type = "response")
predict <- case_when(test_pred >= thre ~ "Default",
test_pred < thre ~ "NonDefault")
cm <- confusionMatrix(case_when(test_df_mini$BAD == 1 ~ "Default", TRUE ~ "NonDefault") %>% as.factor(),
predict %>% as.factor())
bg_gg <- cm$table %>%
as.vector() %>%
matrix(ncol = 4) %>%
as.data.frame()
names(bg_gg) <- c("BB", "GB", "BG", "GG")
results <- c(cm$overall, cm$byClass)
name_results <- results %>%
as.data.frame() %>%
row.names()
results %>%
as.vector() %>%
matrix(ncol = 18) %>%
as.data.frame() -> all_df
names(all_df) <- name_results
all_df <- bind_cols(all_df, bg_gg)
return(all_df)
})
}
# Use above function (may be a time-consuming process):
system.time(compared_df <- lapply(seq(0.1, 0.9, by = 0.05), eval_fun))
## user system elapsed
## 26.81 10.21 879.61
# Convert to data frame:
compared_df <- do.call("bind_rows", compared_df)
compared_df %<>%
mutate(Threshold = lapply(seq(0.1, 0.9, by = 0.05), function(x) {rep(x, 30)}) %>% unlist())
names(compared_df) <- names(compared_df) %>% str_replace_all(" ", "")
# Average Accuracy vs Threshold:
compared_df %>%
group_by(Threshold) %>%
summarise_each(funs(median), Accuracy) -> acc
# Threshold maximize Accuracy:
acc %>% slice(which.max(.$Accuracy))
## # A tibble: 1 x 2
## Threshold Accuracy
## <dbl> <dbl>
## 1 0.5 0.861
# Threshold maximize Accuracy for Default cases:
compared_df %>%
group_by(Threshold) %>%
summarise_each(funs(median), NegPredValue) -> nondefault_acc
nondefault_acc %>% slice(which.max(.$NegPredValue))
## # A tibble: 1 x 2
## Threshold NegPredValue
## <dbl> <dbl>
## 1 0.9 0.998
# Threshold maximize Accuracy for NonDefault cases:
compared_df %>%
group_by(Threshold) %>%
summarise_each(funs(median), PosPredValue) -> default_acc
default_acc %>% slice(which.max(.$PosPredValue))
## # A tibble: 1 x 2
## Threshold PosPredValue
## <dbl> <dbl>
## 1 0.1 0.848
# Visualize our results:
compared_df %>%
group_by(Threshold) %>%
summarise_each(funs(median), Accuracy, NegPredValue, PosPredValue, Kappa, Sensitivity, Specificity) %>%
gather(Metric, b, -Threshold) %>%
ggplot(aes(Threshold, b, color = Metric)) +
geom_line() +
geom_point(size = 3) +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(breaks = seq(0.1, 0.9, by = 0.05)) +
theme(panel.grid.minor.x = element_blank()) +
labs(y = "Accuracy Rate",
title = "Figure 5: Variation of Logistic Classifier's Metrics by Threshold of Probability",
subtitle = "Data Used: Delinquency Data for 5,960 Home Loans",
caption = "Data Source: http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv")

These results reveals that optimal threshold of probability maximizing Accuracy is 0.5. However note that Accuracy should not be the most vital goal for for-profit businesses.
Search Optimal Threshold of Score that maximize Accuracy
In case of using scorecard points for decision-making (accept or reject an credit application) we must determine optimal threshold (or cutoff) of score, which represents the minimum score required by banks for accepting. In this section I will present approach to finding optimal threshold of score.
# Function calculates model measures with input is a score selected:
validate_score <- function(score_selected) {
lapply(1:30, function(x) {
set.seed(x)
id <- createDataPartition(y = df_test_aic$BAD, p = 1000 / nrow(df_test_aic), list = FALSE)
test_df_mini <- df_test_aic[id, ]
my_points_test_mini <- scorecard_ply(test_df_mini, my_card, only_total_score = FALSE, print_step = 0) %>%
as.data.frame() %>%
pull(score)
du_bao <- case_when(my_points_test_mini >= score_selected ~ "Default",
my_points_test_mini < score_selected ~ "NonDefault")
cm <- confusionMatrix(case_when(test_df_mini$BAD == 1 ~ "Default", TRUE ~ "NonDefault") %>% as.factor(),
du_bao %>% as.factor())
bg_gg <- cm$table %>%
as.vector() %>%
matrix(ncol = 4) %>%
as.data.frame()
names(bg_gg) <- c("BB", "GB", "BG", "GG")
results <- c(cm$overall, cm$byClass)
name_results <- results %>%
as.data.frame() %>%
row.names()
results %>%
as.vector() %>%
matrix(ncol = 18) %>%
as.data.frame() -> all_df
names(all_df) <- name_results
all_df <- bind_cols(all_df, bg_gg)
return(all_df)
})
}
# Use the function:
system.time(compared_df_score <- lapply(seq(500, 700, by = 10), validate_score))
## user system elapsed
## 63.03 0.81 63.75
compared_df_score <- do.call("bind_rows", compared_df_score)
compared_df_score %<>%
mutate(Threshold = lapply(seq(500, 700, by = 10), function(x) {rep(x, 30)}) %>% unlist())
names(compared_df_score) <- names(compared_df_score) %>% str_replace_all(" ", "")
compared_df_score$Accuracy %>% summary()
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1860 0.2630 0.3710 0.4057 0.5330 0.7270
compared_df_score %>%
group_by(Threshold) %>%
summarise_each(funs(median), Accuracy, NegPredValue, PosPredValue, Kappa, Sensitivity, Specificity) %>%
gather(Metric, b, -Threshold) %>%
ggplot(aes(Threshold, b, color = Metric)) +
geom_line() +
geom_point(size = 3) +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(breaks = seq(500, 700, by = 50)) +
theme(panel.grid.minor.x = element_blank()) +
labs(y = "Accuracy Rate",
title = "Figure 6: Variation of Logistic Classifier's Metrics by Threshold of Scorecard",
subtitle = "Data Used: Delinquency Data for 5,960 Home Loans",
caption = "Data Source: http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv")

Variable Selection using IV Criterion
Instead of using AIC as a criterion for variable selectin we can use IV as follows:
#===============================================================================
# Scenario 2: Scorecard model with variables selected based on IV Criterion
#===============================================================================
# Calculate information values:
info_values <- iv(df_train, y = "BAD", positive = "BAD|1")
# Show IVs by graph:
info_values %>%
arrange(info_value) %>%
mutate(info_value = round(info_value, 3), variable = factor(variable, levels = variable)) %>%
ggplot(aes(variable, info_value)) +
geom_col(fill = "#377eb8") +
coord_flip() +
geom_text(aes(label = info_value), hjust = -.1, size = 5, color = "#377eb8") +
labs(title = "Figure 7: Information Value (IV) for All Variables",
x = NULL, y = "Information Value (IV)") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 0.9)) +
theme(panel.grid.major.y = element_blank()) +
theme(plot.margin = unit(c(1, 1, 1, 1), "cm"))

# We only use variables with IV >= 0.1 for training Logistic Regression:
variables_selected_iv <- info_values %>%
filter(info_value >= 0.1) %>%
pull(1)
# Data frame for training Logistic Regression:
df_train_iv <- df_train %>% select(variables_selected_iv, "BAD")
# Bin our data set that will be used later for Logistic Regression:
bins_var <- woebin(df_train_iv, y = "BAD", no_cores = 4, positive = "BAD|1")
# Creates a data frame of binned variables for Logistic Regression:
df_train_woe2 <- woebin_ply(df_train_iv, bins_var)
# Logistic Regression:
my_logistic2 <- glm(BAD ~ ., family = binomial, data = df_train_woe2)
# Show results:
my_logistic2 %>% summary()
##
## Call:
## glm(formula = BAD ~ ., family = binomial, data = df_train_woe2)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.0118 -0.5889 -0.3899 -0.2626 2.6822
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.36788 0.05366 -25.493 < 2e-16 ***
## DELINQ_woe 0.93613 0.06108 15.326 < 2e-16 ***
## LOAN_woe 1.00109 0.10609 9.436 < 2e-16 ***
## DEROG_woe 0.67828 0.08693 7.802 6.08e-15 ***
## YOJ_woe 0.93461 0.18358 5.091 3.56e-07 ***
## CLNO_woe 0.71282 0.16785 4.247 2.17e-05 ***
## NINQ_woe 0.81889 0.13028 6.285 3.27e-10 ***
## JOB_woe 0.94456 0.16428 5.750 8.95e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2976.8 on 2979 degrees of freedom
## Residual deviance: 2340.7 on 2972 degrees of freedom
## AIC: 2356.7
##
## Number of Fisher Scoring iterations: 5
# Convert to binned data frame for test data:
df_test_iv <- df_test %>% select(names(df_train_iv))
df_test_woe2 <- woebin_ply(df_test_iv, bins_var)
# Caculate probabiliy of default (PD) for observations belonging test data:
test_pred2 <- predict(my_logistic2, df_test_woe2, type = "response")
# Model Performance for test data:
perf_eva(df_test_iv$BAD, test_pred2,
type = c("ks", "lift", "roc", "pr"),
title = "Test Data")

## $KS
## [1] 0.392
##
## $AUC
## [1] 0.7626
##
## $Gini
## [1] 0.5252
##
## $pic
## TableGrob (2 x 2) "arrange": 4 grobs
## z cells name grob
## pks 1 (1-1,1-1) arrange gtable[layout]
## plift 2 (1-1,2-2) arrange gtable[layout]
## proc 3 (2-2,1-1) arrange gtable[layout]
## ppr 4 (2-2,2-2) arrange gtable[layout]
We can also calculate scorecard points for all credit applications by using this scorecard model as follows:
# Calculate scorecard scores for variables based on the results from woebin and glm:
my_card2 <- scorecard(bins_var, my_logistic2, points0 = 600, odds0 = 1/19, pdo = 50)
# Scorecard Points for test data set:
my_points_test <- scorecard_ply(df_test_iv, my_card2, print_step = 0,
only_total_score = FALSE) %>% as.data.frame()
df_scored_test <- df_test_iv %>%
mutate(SCORE = my_points_test$score) %>%
mutate(BAD = case_when(BAD == 1 ~ "Default", TRUE ~ "NonDefault"))
df_scored_test %>%
group_by(BAD) %>%
summarise_each(funs(min, max, median, mean, n()), SCORE) %>%
mutate_if(is.numeric, function(x) {round(x, 0)}) %>%
knitr::kable(caption = "Table 4: Scorecad Points by Group for Test Data (Selection based on IV)")
Table 4: Scorecad Points by Group for Test Data (Selection based on IV)
Default |
209 |
669 |
461 |
452 |
595 |
NonDefault |
269 |
687 |
545 |
535 |
2385 |
df_scored_test %>%
group_by(BAD) %>%
summarise(tb = mean(SCORE)) %>%
ungroup() -> mean_score_test
df_scored_test %>%
ggplot(aes(SCORE, color = BAD, fill = BAD)) +
geom_density(alpha = 0.3) +
geom_vline(aes(xintercept = mean_score_test$tb[1]), linetype = "dashed", color = "red") +
geom_vline(aes(xintercept = mean_score_test$tb[2]), linetype = "dashed", color = "blue") +
geom_text(aes(x = 450 - 10, y = 0.005, label = mean_score_test$tb[1] %>% round(0)), color = "red", size = 4) +
geom_text(aes(x = 530 + 20, y = 0.005, label = mean_score_test$tb[2] %>% round(0)), color = "blue", size = 4) +
theme(legend.title = element_blank()) +
theme(legend.position = c(0.2, 0.8)) +
theme(plot.margin = unit(c(1, 1, 1, 1), "cm")) +
labs(x = NULL, y = NULL, title = "Figure 7: Scorecard Distribution by two Credit Groups for Test Data",
subtitle = "Variable Selection Used: IV Criterion")

Results show that this approach to variable selection results model performance is poor compared to the previous one (selection based on AIC).
Caution: Be Aware Of Limitations
Although credit scoring systems are being implemented and used by most banks nowadays, they do face a number of limitations. A first limitation concerns the data that is used to estimate credit scoring models. Since data is the major, and in most cases the only, ingredient to build these models, its quality and predictive ability is key to the models’ success.
The quality of the data refers, for example, to the num- ber of missing values and outliers, and to the recency and representativity of the data. Data quality issues can be difficult to detect without specific domain knowledge, but have an important impact on the scorecard development and resulting risk measures. The availability of high-quality data is a very important prerequisite for building good credit scoring models. However, not only does the data need to be of high quality, but it should be predictive as well, in the sense that the captured characteristics are related to the customer’s likelihood of defaulting.
In addition, before constructing a scorecard model, we need to thoroughly reflect on why a customer defaults and which characteristics could potentially be related to this. Customers may default because of unknown reasons or information not available to the financial institution, thereby posing another limitation to the performance of credit scoring models. The statistical techniques used in developing credit scoring models typically assume a data set of sufficient size containing enough defaults. This may not always be the case for specific types of portfolios where only limited data is available, or only a low number of defaults is observed. For these types of portfolios, one may have to rely on alternative risk assessment methods using, for example, expert judgment based on the five Cs, as discussed earlier.
Thank you message
I woould like to thank Mr.Dung for his praiseworthy work. His posts and references helped me improve my skills and knowledge a lot that I can not express by these simple words.
He’s defenitely my teacher!
References
Martens, D., B. Baesens, T. Van Gestel, and J. Vanthienen. 2007. “Comprehensible Credit Scoring Models Using Rule Extraction from Support Vector Machines.†European Journal of Operational Research 183:1466–1476.
Baesens, B., Roesch, D., & Scheule, H. (2016). Credit risk analytics: Measurement techniques, applications, and examples in SAS. John Wiley & Sons.
Siddiqi, N. (2012). Credit risk scorecards: developing and implementing intelligent credit scoring. John Wiley & Sons.
Anderson R (2007): The Credit Scoring Toolkit: Theory and Practice for Retail Credit Risk Management and Decision Automation. Oxford, Oxford University Press.
Hand DJ, Henley WE (1997): Statistical Classification Methods in Consumer Credit Scoring: a review. Journal. of the Royal Statistical Society, Series A, 160(3):523–541.
Thomas LC (2000): A survey of credit and behavioural scoring: forecasting financial risk of lending to consumers. International Journal of Forecasting, 16(2):149–172 .
Thomas LC (2009): Consumer Credit Models: Pricing, Profit, and Portfolio. Oxford, Oxford University Press.
Crook JN, Edelman DB, Thomas LC (2007): Recent developments in consumer credit risk assessment. European Journal of Operational Research, 183(3):1447–1465.
Van Gestel, T., B. Baesens, P. Van Dijcke, J. Suykens, J. Garcia, and T. Alderweireld. 2005. “Linear and Nonlinear Credit Scoring by Combining Logistic Regression and Support Vector Machines.†Journal of Credit Risk 1, no. 4.
https://github.com/chidungkr
LS0tDQp0aXRsZTogIkFwcHJvYWNoIHRvIFNjb3JlY2FyZCBNb2RlbCAiIA0KYXV0aG9yOiAiSmVubnkgTmd1eWVuIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgIyBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZmxhdGx5Ig0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KLS0tDQoNCmBgYHtyIHNldHVwLGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCiMjIyBJbnRyb2R1Y3Rpb24gdG8gQ3JlZGl0IFNjb3JpbmcgYW5kIFNjb3JlY2FyZCBBcHByb2FjaA0KDQpHcmFudGluZyBjcmVkaXQgdG8gYm90aCByZXRhaWwgYW5kIG5vbnJldGFpbCAoZS5nLiwgY29ycG9yYXRlKSBjdXN0b21lcnMgaXMgdGhlIGNvcmUNCmJ1c2luZXNzIG9mIGEgYmFuay4gSW4gZG9pbmcgc28sIGJhbmtzIG5lZWQgdG8gaGF2ZSBhZGVxdWF0ZSBzeXN0ZW1zIHRvIGRlY2lkZQ0KdG8gd2hvbSB0byBncmFudCBjcmVkaXQuIENyZWRpdCBzY29yaW5nIGlzIGEga2V5IHJpc2sgYXNzZXNzbWVudCB0ZWNobmlxdWUgdG8NCmFuYWx5emUgYW5kIHF1YW50aWZ5IGEgcG90ZW50aWFsIG9ibGlnb3LigJlzIGNyZWRpdCByaXNrLiBFc3NlbnRpYWxseSwgY3JlZGl0IHNjb3JpbmcgYWltcw0KYXQgcXVhbnRpZnlpbmcgdGhlIGxpa2VsaWhvb2QgdGhhdCBhbiBvYmxpZ29yIHdpbGwgcmVwYXkgdGhlIGRlYnQuIFRoZSBvdXRjb21lIG9mIHRoZQ0KY3JlZGl0IHNjb3JpbmcgZXhlcmNpc2UgaXMgYSBzY29yZSByZWZsZWN0aW5nIHRoZSBjcmVkaXR3b3J0aGluZXNzIG9mIHRoZSBvYmxpZ29yLg0KDQpUaHJvdWdob3V0IHRoZSBwYXN0IGZldyBkZWNhZGVzIGJhbmtzIGhhdmUNCmdhdGhlcmVkIHBsZW50eSBvZiBpbmZvcm1hdGlvbiBkZXNjcmliaW5nIHRoZSBkZWZhdWx0IGJlaGF2aW9yIG9mIHRoZWlyIGN1c3RvbWVycy4NCkV4YW1wbGVzIGFyZSBoaXN0b3JpY2FsIGluZm9ybWF0aW9uIGFib3V0IGEgY3VzdG9tZXLigJlzIGRhdGUgb2YgYmlydGgsIGdlbmRlciwgaW5jb21lLA0KZW1wbG95bWVudCBzdGF0dXMsIGFuZCBzbyBvbi4gQWxsIHRoaXMgZGF0YSBoYXMgYmVlbiBuaWNlbHkgc3RvcmVkIGludG8gaHVnZSAoZS5nLiwNCnJlbGF0aW9uYWwpIGRhdGFiYXNlcyBvciBkYXRhIHdhcmVob3VzZXMuIE9uIHRvcCBvZiB0aGlzLCBiYW5rcyBoYXZlIGFjY3VtdWxhdGVkDQpsb3RzIG9mIGJ1c2luZXNzIGV4cGVyaWVuY2UgYWJvdXQgdGhlaXIgY3JlZGl0IHByb2R1Y3RzLiBBcyBhbiBleGFtcGxlLCBtYW55IGNyZWRpdA0KZXhwZXJ0cyBkbyBhIHByZXR0eSBnb29kIGpvYiBvZiBkaXNjcmltaW5hdGluZyBiZXR3ZWVuIGxvdy1yaXNrIGFuZCBoaWdoLXJpc2sgbW9ydC0NCmdhZ2VzIHVzaW5nIHRoZWlyIGJ1c2luZXNzIGV4cGVydGlzZSBvbmx5LiBJdCBpcyBub3cgdGhlIGFpbSBvZiBjcmVkaXQgc2NvcmluZyB0byBhbmFseXplDQpib3RoIHNvdXJjZXMgb2YgZGF0YSBpbiBtb3JlIGRldGFpbCBhbmQgY29tZSB1cCB3aXRoIGEgc3RhdGlzdGljYWxseSBiYXNlZCBkZWNpc2lvbg0KbW9kZWwgdGhhdCBhbGxvd3Mgc2NvcmluZyBmdXR1cmUgY3JlZGl0IGFwcGxpY2F0aW9ucyBhbmQgdWx0aW1hdGVseSBkZWNpZGluZyB3aGljaA0Kb25lcyB0byBhY2NlcHQgYW5kIHdoaWNoIHRvIHJlamVjdC4NCg0KDQpGb3IgdGhlDQpoaXN0b3JpY2FsIGN1c3RvbWVycywgd2Uga25vdyB3aGljaCBvbmVzIHR1cm5lZCBvdXQgdG8gYmUgZ29vZCBwYXllcnMgYW5kIHdoaWNoDQpvbmVzIHR1cm5lZCBvdXQgdG8gYmUgYmFkIHBheWVycy4gVGhpcyBnb29kL2JhZCBzdGF0dXMgaXMgbm93IHRoZSBiaW5hcnkgdGFyZ2V0IHZhcmlhYmxlIFksIHdoaWNoIHdlIHdpbGwgcmVsYXRlIHRvIGFsbCBpbmZvcm1hdGlvbiBhdmFpbGFibGUgYXQgc2NvcmluZyB0aW1lIGFib3V0IG91cg0Kb2JsaWdvcnMuIFRoZSBnb2FsIG9mIGNyZWRpdCBzY29yaW5nIGlzIG5vdyB0byBxdWFudGlmeSB0aGlzIHJlbGF0aW9uc2hpcCBhcyBwcmVjaXNlbHkNCmFzIHBvc3NpYmxlIHRvIGFzc2lzdCBjcmVkaXQgZGVjaXNpb25zLCBtb25pdG9yaW5nLCBhbmQgbWFuYWdlbWVudC4gQmFua3Mgc2NvcmUgYm9yLQ0Kcm93ZXJzIGF0IGxvYW4gYXBwbGljYXRpb24sIGFzIHdlbGwgYXMgYXQgcmVndWxhciB0aW1lcyBkdXJpbmcgdGhlIHRlcm0gb2YgYSBmaW5hbmNpYWwNCmNvbnRyYWN0IChnZW5lcmFsbHkgbG9hbnMsIGxvYW4gY29tbWl0bWVudHMsIGFuZCBndWFyYW50ZWVzKS4NCg0KT25jZSB3ZSBoYXZlIG91ciBjcmVkaXQgc2NvcmluZyBtb2RlbCBidWlsdCwgd2UgY2FuIHRoZW4gdXNlIGl0IHRvIGRlY2lkZSB3aGV0aGVyDQp0aGUgY3JlZGl0IGFwcGxpY2F0aW9uIHNob3VsZCBiZSBhY2NlcHRlZCBvciByZWplY3RlZCwgb3IgdG8gZGVyaXZlIHRoZSBwcm9iYWJpbGl0eSBvZiBhDQpmdXR1cmUgZGVmYXVsdC4gVG8gc3VtbWFyaXplLCBjcmVkaXQgc2NvcmluZyBpcyBhIGtleSByaXNrIG1hbmFnZW1lbnQgdG9vbCBmb3IgYSBiYW5rDQp0byBvcHRpbWFsbHkgbWFuYWdlLCB1bmRlcnN0YW5kLCBhbmQgbW9kZWwgdGhlIGNyZWRpdCByaXNrIGl0IGlzIGV4cG9zZWQgdG8uDQoNCiMjIyBUaGUgVHdvIEF2YWlsYWJsZSBBcHByb2FjaGVzOiBKdWRnbWVudGFsIFZlcnN1cyBTdGF0aXN0aWNhbCBTY29yaW5nIA0KDQpUaGVyZSBhcmUgYmFzaWNhbGx5IHR3byBtYWluIGFwcHJvYWNoZXMgdG8gYXNzZXNzaW5nIGNyZWRpdCByaXNrOiAoMSkgdGhlIGp1ZGdtZW50YWwNCmFwcHJvYWNoLCBhbmQgKDIpIHRoZSBzdGF0aXN0aWNhbCBhcHByb2FjaC4gQm90aCByZWx5IG9uIGhpc3RvcmljYWwgaW5mb3JtYXRpb24sIGJ1dCB0aGUNCnR5cGUgb2YgaW5mb3JtYXRpb24gdGhleSB1c2UgaXMgZGlmZmVyZW50Lg0KDQoqKlRoZSBqdWRnbWVudGFsIGFwcHJvYWNoKiogaXMgYSBxdWFsaXRhdGl2ZSwgZXhwZXJ0LWJhc2VkIGFwcHJvYWNoIHdoZXJlYnksIGJhc2VkDQpvbiBidXNpbmVzcyBleHBlcmllbmNlIGFuZCBjb21tb24gc2Vuc2UsIHRoZSBjcmVkaXQgZXhwZXJ0IG9yIGNyZWRpdCBjb21taXR0ZWUsDQp3aGljaCBpcyBhIGdyb3VwIG9mIGNyZWRpdCBleHBlcnRzLCB3aWxsIG1ha2UgYSBkZWNpc2lvbiBhYm91dCB0aGUgY3JlZGl0IHJpc2suIFVzdWFsbHksDQp0aGlzIGlzIGRvbmUgYmFzZWQgb24gaW5zcGVjdGluZyB0aGUgZml2ZSBDcyBvZiB0aGUgYXBwbGljYW50IGFuZCBsb2FuOg0KDQotICoqQ2hhcmFjdGVyKiogbWVhc3VyZXMgdGhlIGJvcnJvd2Vy4oCZcyBjaGFyYWN0ZXIgYW5kIGludGVncml0eSAoZS5nLiwgcmVwdXRhdGlvbiwNCmhvbmVzdHksIGV0Yy4pLg0KDQotICoqQ2FwaXRhbCoqIG1lYXN1cmVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGJvcnJvd2Vy4oCZcyBhc3NldHMgKGUuZy4sIGNhciwNCmhvdXNlLCBldGMuKSBhbmQgbGlhYmlsaXRpZXMgKGUuZy4sIHJlbnRpbmcgZXhwZW5zZXMsIGV0Yy4pLg0KDQotICoqQ29sbGF0ZXJhbCoqIG1lYXN1cmVzIHRoZSBjb2xsYXRlcmFsIHByb3ZpZGVkIGluIGNhc2UgcGF5bWVudCBwcm9ibGVtcyBvY2N1cg0KKGUuZy4sIGhvdXNlLCBjYXIsIGV0Yy4pLg0KDQotICoqQ2FwYWNpdHkqKiBtZWFzdXJlcyB0aGUgYm9ycm93ZXLigJlzIGFiaWxpdHkgdG8gcGF5IChlLmcuLCBqb2Igc3RhdHVzLCBpbmNvbWUsDQpldGMuKS4NCg0KLSAqKkNvbmRpdGlvbioqIG1lYXN1cmVzIHRoZSBib3Jyb3dlcuKAmXMgY2lyY3Vtc3RhbmNlcyAoZS5nLiwgbWFya2V0IGNvbmRpdGlvbnMsDQpjb21wZXRpdGl2ZSBwcmVzc3VyZSwgc2Vhc29uYWwgY2hhcmFjdGVyLCBldGMuKS4NCg0KSW4gYW5hbHl6aW5nIHRoaXMgaW5mb3JtYXRpb24sIGEgcXVhbGl0YXRpdmUgb3Igc3ViamVjdGl2ZSBldmFsdWF0aW9uIG9mIHRoZSBjcmVkaXQNCnJpc2sgaXMgbWFkZS4gQWx0aG91Z2ggdGhlIGp1ZGdtZW50YWwgYXBwcm9hY2ggbWlnaHQgc2VlbSBzdWJqZWN0aXZlIGFuZCB0aHVzDQp1bnNvcGhpc3RpY2F0ZWQgYXQgZmlyc3Qgc2lnaHQsIGl0IGlzIHN0aWxsIHF1aXRlIGNvbW1vbmx5IHVzZWQgYnkgYmFua3MgZm9yIHZlcnkgc3BlY2lmaWMNCmNyZWRpdCBwb3J0Zm9saW9zIHN1Y2ggYXMgcHJvamVjdCBmaW5hbmNlIG9yIG5ldyBjcmVkaXQgcHJvZHVjdHMuDQoNCldpdGggdGhlIGVtZXJnZW5jZSBvZiBzdGF0aXN0aWNhbCBjbGFzc2lmaWNhdGlvbiB0ZWNobmlxdWVzIGF0IHRoZSBiZWdpbm5pbmcgb2YNCnRoZSAxOTgwcywgYmFua3MgYmVjYW1lIG1vcmUgYW5kIG1vcmUgaW50ZXJlc3RlZCBpbiBhYmFuZG9uaW5nIHRoZSBqdWRnbWVudGFsDQphcHByb2FjaCBhbmQgb3B0aW5nIGZvciBhIG1vcmUgZm9ybWFsICoqZGF0YS1iYXNlZCBzdGF0aXN0aWNhbCBhcHByb2FjaCoqLiANCg0KVGhlIHN0YXRpc3RpY2FsIGFwcHJvYWNoIGlzIGJhc2VkIG9uIHN0YXRpc3RpY2FsIGFuYWx5c2lzIG9mIGhpc3RvcmljYWwgZGF0YSB0byBmaW5kDQp0aGUgb3B0aW1hbCBtdWx0aXZhcmlhdGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBjdXN0b21lcuKAmXMgY2hhcmFjdGVyaXN0aWNzIGFuZCB0aGUNCmJpbmFyeSBnb29kL2JhZCB0YXJnZXQgdmFyaWFibGUgKEJhZXNlbnMgZXQgYWwuIDIwMDMpLiBJdCBpcyBsZXNzIHN1YmplY3RpdmUgdGhhbiB0aGUNCmp1ZGdtZW50YWwgYXBwcm9hY2ggc2luY2UgaXQgaXMgbm90IHRpZWQgdG8gYSBwYXJ0aWN1bGFyIGNyZWRpdCBleHBlcnTigJlzIGJhY2tncm91bmQNCmtub3dsZWRnZSBhbmQgZXhwZXJpZW5jZS4NCg0KVGhlIHN0YXRpc3RpY2FsIGFwcHJvYWNoIGFpbXMgYXQgYnVpbGRpbmcgc2NvcmVjYXJkcywgd2hpY2ggYXJlIGJhc2VkIG9uIG11bHRpdmFyaWF0ZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBpbnB1dHMgKHN1Y2ggYXMgYWdlLCBtYXJpdGFsIHN0YXR1cywgaW5jb21lLCBzYXZpbmdzDQphbW91bnQpIGFuZCBhIHRhcmdldCB2YXJpYWJsZSB0aGF0IHJlZmxlY3RzIHRoZSByaXNrIG9mIGRlZmF1bHQuIEluIG90aGVyIHdvcmRzLCBhIHNjb3JlY2FyZCB3aWxsIGFzc2lnbiBzY29yZXMgdG8gZWFjaCBvZiB0aG9zZSBpbnB1dHMuIEluIG91ciBleGFtcGxlLCBzY29yZXMgd2lsbCBiZSBhc3NpZ25lZA0KdG8gYWdlLCBtYXJpdGFsIHN0YXR1cywgaW5jb21lLCBhbmQgc2F2aW5ncyBhbW91bnQuIEFsbCB0aG9zZSBzY29yZXMgd2lsbCB0aGVuIGJlIGFkZGVkDQp1cCBhbmQgY29tcGFyZWQgd2l0aCB0aGUgY3JpdGljYWwgdGhyZXNob2xkLCB3aGljaCBzcGVjaWZpZXMgdGhlIG1pbmltdW0gbGV2ZWwgb2YNCnJlcXVpcmVkIGNyZWRpdCBxdWFsaXR5LiBJZiB0aGUgYWdncmVnYXRlZCBzY29yZSBleGNlZWRzIHRoZSB0aHJlc2hvbGQsIHRoZW4gY3JlZGl0IHdpbGwNCmJlIGdyYW50ZWQuIElmIGl0IGZhbGxzIGJlbG93IHRoZSB0aHJlc2hvbGQsIHRoZW4gY3JlZGl0IHdpbGwgYmUgd2l0aGhlbGQuDQoNCkluIHByYWN0aWNlLCBoeWJyaWQgYXBwcm9hY2hlcyBtYXkgYmUgYXBwbGllZC4gSW4gYSBmaXJzdCBzdGVwLCBhIGJhbmsgbWF5IGdlbmVyYXRlDQppbmZvcm1hdGlvbmFsIHZhbHVlcyBieSBqdWRnbWVudGFsIHNjb3JpbmcuIEFuIGV4YW1wbGUgbWF5IGJlIGFuIGV4cGVydCBvcGluaW9uDQpvZiBhIGNyZWRpdCBhbmFseXN0IG9uIHRoZSBwYXltZW50IGV0aGljcyBvZiBhIGJvcnJvd2VyIChlLmcuLCBhcyBhIGRpc2NyZXRlIG51bWJlcg0KYmV0d2VlbiAxIGFuZCA1KS4gSW4gYSBzZWNvbmQgc3RlcCwgdGhlIGJhbmsgbWF5IGFnZ3JlZ2F0ZSB0aGlzIGp1ZGdtZW50YWwgc2NvcmUNCmFuZCBvdGhlciBoYXJkIGluZm9ybWF0aW9uIGludG8gYSBzdGF0aXN0aWNhbCBzY29yZS4NCg0KIyMjIEFkdmFudGFnZXMgb2YgU3RhdGlzdGljYWwgQXBwcm9hY2ggdG8gQ3JlZGl0IFNjb3JpbmcgDQoNCkdlbmVyYWxseSBzcGVha2luZywgdGhlIHN0YXRpc3RpY2FsIGFwcHJvYWNoIHRvIGNyZWRpdCBzY29yaW5nIGhhcyBtYW55IGFkdmFudGFnZXMNCmNvbXBhcmVkIHdpdGggdGhlIGp1ZGdtZW50YWwgYXBwcm9hY2guIEZpcnN0LCBpdCBpcyBiZXR0ZXIgaW4gdGVybXMgb2Ygc3BlZWQgYW5kIGFjY3VyYWN5LiBXZSBjYW4gbm93IG1ha2UgZmFzdGVyIGRlY2lzaW9ucyB0aGFuIHdlIHdlcmUgYWJsZSB0byBkbyB3aXRoIHRoZSBqdWRnbWVudGFsDQphcHByb2FjaC4gVGhpcyBpcyBlc3BlY2lhbGx5IHJlbGV2YW50IHdoZW4gd29ya2luZyBpbiBhbiBvbmxpbmUgZW52aXJvbm1lbnQgd2hlcmUNCmNyZWRpdCBkZWNpc2lvbnMgbmVlZCB0byBiZSBtYWRlIHF1aWNrbHksIHBvc3NpYmx5IGluIHJlYWwgdGltZS4gQmVjYXVzZSBhIGNyZWRpdCBzY29yZWNhcmQgaXMgZXNzZW50aWFsbHkgYSBtYXRoZW1hdGljYWwgZm9ybXVsYSwgaXQgY2FuIGJlIGVhc2lseSBwcm9ncmFtbWVkIGFuZCBldmFsdWF0ZWQNCmluIGFuIGF1dG9tYXRlZCBhbmQgZmFzdCB3YXkuDQoNCkFub3RoZXIgYWR2YW50YWdlIG9mIGhhdmluZyBzdGF0aXN0aWNhbCBjcmVkaXQgc2NvcmluZyBtb2RlbHMgaXMgY29uc2lzdGVuY3kuIFdlDQpubyBsb25nZXIgaGF2ZSB0byByZWx5IHVwb24gdGhlIGV4cGVyaWVuY2UsIGludHVpdGlvbiwgb3IgY29tbW9uIHNlbnNlIG9mIG9uZSBvcg0KbXVsdGlwbGUgYnVzaW5lc3MgZXhwZXJ0cy4gTm93IGl04oCZcyBqdXN0IGEgbWF0aGVtYXRpY2FsIGZvcm11bGEsIGFuZCB0aGUgZm9ybXVsYSB3aWxsDQphbHdheXMgZXZhbHVhdGUgaW4gZXhhY3RseSB0aGUgc2FtZSB3YXkgaWYgZ2l2ZW4gdGhlIHNhbWUgc2V0IG9mIGlucHV0cywgbGlrZSBhZ2UsDQptYXJpdGFsIHN0YXR1cywgaW5jb21lLCBhbmQgc28gb24uDQoNCkZpbmFsbHksIHN0YXRpc3RpY2FsIGNyZWRpdCBzY29yaW5nIG1vZGVscyB3aWxsIHR5cGljYWxseSBhbHNvIGJlIG1vcmUgcG93ZXJmdWwgdGhhbg0KanVkZ21lbnRhbCBtb2RlbHMuIFRoaXMgcGVyZm9ybWFuY2UgYm9vc3Qgd2lsbCBhbGxvdyBhIHJlZHVjdGlvbiBvZiBiYWQgZGVidCBsb3NzDQphbmQgb3BlcmF0aW5nIGNvc3RzLCBhbmQgY29uc2VxdWVudGx5IGl0IHdpbGwgYWxzbyBpbXByb3ZlIHBvcnRmb2xpbyBtYW5hZ2VtZW50Lg0KDQojIyMgTG9naXN0aWMgUmVncmVzc2lvbiBmb3IgRGV2ZWxvcGluZyBhIFNjb3JlY2FyZCBNb2RlbA0KDQpMb2dpc3RpYyByZWdyZXNzaW9uIGlzIGEgdmVyeSBwb3B1bGFyIGNyZWRpdCBzY29yaW5nIGNsYXNzaWZpY2F0aW9uIHRlY2huaXF1ZSBkdWUgdG8gaXRzDQpzaW1wbGljaXR5IGFuZCBnb29kIHBlcmZvcm1hbmNlLiBKdXN0IGFzIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24sIG9uY2UgdGhlIHBhcmFtZXRlcnMgaGF2ZSBiZWVuIGVzdGltYXRlZCwgdGhlIHJlZ3Jlc3Npb24gY2FuIGJlIGV2YWx1YXRlZCBpbiBhIHN0cmFpZ2h0Zm9yd2FyZCB3YXksDQpjb250cmlidXRpbmcgdG8gaXRzIG9wZXJhdGlvbmFsIGVmZmljaWVuY3kuIEZyb20gYW4gaW50ZXJwcmV0YWJpbGl0eSB2aWV3cG9pbnQsIGl0IGNhbg0KYmUgZWFzaWx5IHRyYW5zZm9ybWVkIGludG8gYW4gaW50ZXJwcmV0YWJsZSwgdXNlci1mcmllbmRseSwgcG9pbnRzLWJhc2VkIGNyZWRpdCBzY29yZWNhcmQuIA0KDQojIyMgQW4gSW1wb3J0YW50IFRlY2huaWNhbCBBc3BlY3Qgb2YgRGV2ZWxvcGluZyBMb2dpc3RpYyBSZWdyZXNzaW9uOiBWYXJpYWJsZSBTZWxlY3Rpb24NCg0KVmFyaWFibGUgc2VsZWN0aW9uIGFpbXMgYXQgcmVkdWNpbmcgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgaW4gYSBtb2RlbC4gSXQgd2lsbCBtYWtlDQp0aGUgbW9kZWwgbW9yZSBjb25jaXNlIGFuZCBmYXN0ZXIgdG8gZXZhbHVhdGUuIExvZ2lzdGljIHJlZ3Jlc3Npb24gaGFzIGEgYnVpbHQtaW4gcHJvY2VkdXJlIHRvIHBlcmZvcm0gdmFyaWFibGUgc2VsZWN0aW9uLiBJdCBpcyBiYXNlZCBvbiBhIHN0YXRpc3RpY2FsIGh5cG90aGVzaXMgdGVzdCB0byB2ZXJpZnkNCndoZXRoZXIgdGhlIGNvZWZmaWNpZW50IG9mIGEgdmFyaWFibGUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gemVyby4gDQoNCkluIGNyZWRpdCBzY29yaW5nLCBpdCBpcyB2ZXJ5IGltcG9ydGFudCB0byBiZSBhd2FyZSB0aGF0IHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBpcw0Kb25seSBvbmUgZXZhbHVhdGlvbiBjcml0ZXJpb24gdG8gY29uc2lkZXIgaW4gZG9pbmcgdmFyaWFibGUgc2VsZWN0aW9uLiBBcyBtZW50aW9uZWQNCmJlZm9yZSwgaW50ZXJwcmV0YWJpbGl0eSBpcyBhbHNvIGFuIGltcG9ydGFudCBjcml0ZXJpb24gKE1hcnRlbnMgZXQgYWwuIDIwMDcpLiBJbiBsb2dpc3RpYw0KcmVncmVzc2lvbiwgdGhpcyBjYW4gYmUgZWFzaWx5IGV2YWx1YXRlZCBieSBpbnNwZWN0aW5nIHRoZSBzaWduIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50LiBJdCBpcyBoaWdobHkgcHJlZmVyYWJsZSB0aGF0IGEgY29lZmZpY2llbnQgaGFzIHRoZSBzYW1lIHNpZ24gYXMgYW50aWNpcGF0ZWQgYnkgdGhlDQpjcmVkaXQgZXhwZXJ0OyBvdGhlcndpc2UgaGUgb3Igc2hlIHdpbGwgYmUgcmVsdWN0YW50IHRvIHVzZSB0aGUgbW9kZWwuIENvZWZmaWNpZW50cyBjYW4NCmhhdmUgdW5leHBlY3RlZCBzaWducyBkdWUgdG8gbXVsdGljb2xsaW5lYXJpdHkgaXNzdWVzLCBub2lzZSwgb3Igc21hbGwgc2FtcGxlIGVmZmVjdHMuDQpTaWduIHJlc3RyaWN0aW9ucyBjYW4gYmUgZWFzaWx5IGVuZm9yY2VkIGluIGEgZm9yd2FyZCByZWdyZXNzaW9uIHNldHVwIGJ5IHByZXZlbnRpbmcNCnZhcmlhYmxlcyB3aXRoIHRoZSB3cm9uZyBzaWduIGZyb20gZW50ZXJpbmcgdGhlIG1vZGVsLg0KDQpMZWdhbCBpc3N1ZXMgYWxzbyBuZWVkIHRvIGJlIHByb3Blcmx5IHRha2VuIGludG8gYWNjb3VudC4gRm9yIGV4YW1wbGUsIGluIHRoZSBVbml0ZWQgU3RhdGVzLA0KdGhlcmUgaXMgdGhlIEVxdWFsIENyZWRpdCBPcHBvcnR1bml0eSBBY3QsIHdoaWNoIHN0YXRlcyB0aGF0IG5vIG9uZSBpcyBhbGxvd2VkIHRvIGRpcy0NCmNyaW1pbmF0ZSBiYXNlZCBvbiBnZW5kZXIsIGFnZSwgZXRobmljIG9yaWdpbiwgbmF0aW9uYWxpdHksIGJlbGllZnMsIGFuZCBzbyBvbi4gVGhlc2UNCnZhcmlhYmxlcyBtdXN0IG5vdCBiZSBpbmNsdWRlZCBpbiBhIGNyZWRpdCBzY29yZWNhcmQuIE90aGVyIGNvdW50cmllcyBoYXZlIG90aGVyIHJlZ3VsYXRpb25zLCBhbmQgaXQgaXMgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mIHRoaXMuDQoNCkN1cnJlbnRseSwgc29tZSBtZXRob2RzIGZvciBjaG9vc2luZyByZWxldmFudCB2YXJpYWJsZXMgYXJlOiANCg0KLSBCYXNlZCBvbiAqKkFJQyBDcml0ZXJpb24qKiBmb3Igc2VsZWN0aW9uLiANCg0KLSAqKkluZm9ybWF0aW9uIFZhbHVlKiogKElWKSBDcml0ZXJpb24uIA0KDQotIEFwcHJvYWNoZXMgYmFzZWQgb24gKipNYWNoaW5lIExlYXJuaW5nIEFsZ29yaXRobXMqKi4gDQoNCg0KIyMjIE1vcmUgRGV0YWlscyBvbiBXZWlnaHQgb2YgRXZpZGVuY2UgKFdPRSkgYW5kIEluZm9ybWF0aW9uIFZhbHVlIChJVikNCg0KUHJpb3IgdG8gYnVpbGRpbmcgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gbW9kZWwgKGUuZy4sIExvZ2lzdGljIFJlZ3Jlc3Npb24sIGV0Yy4pLCBhIGNvbW1vbiBzdGVwIGlzIHRvIHBlcmZvcm0gdmFyaWFibGUgc2NyZWVuaW5nIGFuZCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLiBUaGlzIGlzIHRoZSBzdGVwIHdoZXJlIHdlIGdldCB0byBrbm93IHRoZSBkYXRhIGFuZCB3ZWVkIG91dCB2YXJpYWJsZXMgdGhhdCBhcmUgZWl0aGVyIGlsbC1jb25kaXRpb25lZCBvciBzaW1wbHkgY29udGFpbiBubyBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgaGVscCB1cyBwcmVkaWN0IHRoZSBhY3Rpb24gb2YgaW50ZXJlc3QuIE5vdGUgdGhhdCB0aGUgcHVycG9zZSBvZiB0aGlzIHN0ZXAgc2hvdWxkIG5vdCB0byBiZSBjb25mdXNlZCB3aXRoIHRoYXQgb2YgbXVsdGlwbGUtdmFyaWFibGUgc2VsZWN0aW9uIHRlY2huaXF1ZXMsIHN1Y2ggYXMgc3RlcHdpc2UgcmVncmVzc2lvbiwgd2hlcmUgdGhlIHZhcmlhYmxlcyB0aGF0IGdvIGludG8gdGhlIGZpbmFsIG1vZGVsIGFyZSBzZWxlY3RlZC4gUmF0aGVyLCB0aGlzIGlzIGEgcHJlY3Vyc29yeSBzdGVwIGRlc2lnbmVkIHRvIGVuc3VyZSB0aGF0IHRoZSBhcHByb2FjaGVzIGRlcGxveWVkIGR1cmluZyB0aGUgZmluYWwgbW9kZWxpbmcgcGhhc2VzIGFyZSBzZXQgdXAgZm9yIHN1Y2Nlc3MuDQoNClRoZSB3ZWlnaHQgb2YgZXZpZGVuY2UgKFdPRSkgYW5kIGluZm9ybWF0aW9uIHZhbHVlIChJVikgcHJvdmlkZSBhIGdyZWF0IGZyYW1ld29yayBmb3IgZm9yIGV4cGxvcmF0b3J5IGFuYWx5c2lzIGFuZCB2YXJpYWJsZSBzY3JlZW5pbmcgZm9yIGJpbmFyeSBjbGFzc2lmaWVycy4gV09FIGFuZCBJViBoYXZlIGJlZW4gdXNlZCBleHRlbnNpdmVseSBpbiB0aGUgY3JlZGl0IHJpc2sgd29ybGQgZm9yIHNldmVyYWwgZGVjYWRlcywgYW5kIHRoZSB1bmRlcmx5aW5nIHRoZW9yeSBkYXRlcyBiYWNrIHRvIHRoZSAxOTUwcy4gDQoNCldPRSBhbmQgIElWIGFyZSBzaW1wbGUsIHlldCBwb3dlcmZ1bCB0ZWNobmlxdWVzIHRvIHBlcmZvcm0gdmFyaWFibGUgdHJhbnNmb3JtYXRpb24gYW5kIHNlbGVjdGlvbi4gVGhlc2UgY29uY2VwdHMgaGF2ZSBodWdlIGNvbm5lY3Rpb24gd2l0aCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbGluZyB0ZWNobmlxdWUuIEl0IGlzIHdpZGVseSB1c2VkIGluIGNyZWRpdCBzY29yaW5nIHRvIG1lYXN1cmUgdGhlIHNlcGFyYXRpb24gb2YgZ29vZCB2cyBiYWQgY3VzdG9tZXJzLiBJbiBhZGRpdGlvbiwgdGhlIGFkdmFudGFnZXMgb2YgV09FIHRyYW5zZm9ybWF0aW9uIGFyZToNCg0KDQotIEhhbmRsZXMgbWlzc2luZyB2YWx1ZXMuIA0KDQotIEhhbmRsZXMgb3V0bGllcnMuIA0KDQotIFRoZSB0cmFuc2Zvcm1hdGlvbiBpcyBiYXNlZCBvbiBsb2dhcml0aG1pYyB2YWx1ZSBvZiBkaXN0cmlidXRpb25zLiBUaGlzIGlzIGFsaWduZWQgd2l0aCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBvdXRwdXQgZnVuY3Rpb24NCk5vIG5lZWQgZm9yIGR1bW15IHZhcmlhYmxlcy4gDQoNCi0gQnkgdXNpbmcgcHJvcGVyIGJpbm5pbmcgdGVjaG5pcXVlLCBpdCBjYW4gZXN0YWJsaXNoIG1vbm90b25pYyByZWxhdGlvbnNoaXAgKGVpdGhlciBpbmNyZWFzZSBvciBkZWNyZWFzZSkgYmV0d2VlbiB0aGUgaW5kZXBlbmRlbnQgYW5kIGRlcGVuZGVudCB2YXJpYWJsZQ0KDQpBY2NvcmRpbmcgdG8gQmFlc2VucyBldCBhbC4gKDIwMTYpIGFuZCBTaWRkaXFpICgyMDEyKSwgV09FIGFuZCBJViBhbmFseXNpcyBlbmFibGUgb25lIHRvOg0KDQotIENvbnNpZGVyIGVhY2ggdmFyaWFibGXigJlzIGluZGVwZW5kZW50IGNvbnRyaWJ1dGlvbiB0byB0aGUgb3V0Y29tZS4NCg0KLSBEZXRlY3QgbGluZWFyIGFuZCBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMuDQoNCi0gUmFuayB2YXJpYWJsZXMgaW4gdGVybXMgb2Yg4oCcdW5pdmFyaWF0ZeKAnSBwcmVkaWN0aXZlIHN0cmVuZ3RoLg0KDQotIFZpc3VhbGl6ZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIHByZWRpY3RpdmUgdmFyaWFibGVzIGFuZCB0aGUgYmluYXJ5IG91dGNvbWUuDQoNCi0gU2VhbWxlc3NseSBjb21wYXJlIHRoZSBzdHJlbmd0aCBvZiBjb250aW51b3VzIGFuZCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aG91dCBjcmVhdGluZyBkdW1teSB2YXJpYWJsZXMuDQoNCi0gU2VhbWxlc3NseSBoYW5kbGUgbWlzc2luZyB2YWx1ZXMgd2l0aG91dCBpbXB1dGF0aW9uLg0KDQotIEFzc2VzcyB0aGUgcHJlZGljdGl2ZSBwb3dlciBvZiBtaXNzaW5nIHZhbHVlcy4NCg0KQnkgY29udmVudGlvbiB0aGUgdmFsdWVzIG9mIHRoZSBJViBzdGF0aXN0aWMgZm9yIHZhcmlhYmxlIHNlbGVjIHRpb24gY2FuIGJlIHVzZWQgYXMgZm9sbG93czogDQoNCi0gTGVzcyB0aGFuIDAuMDI6IHRoZSBwcmVkaWN0b3IgaXMgbm90IHVzZWZ1bCBmb3IgbW9kZWxpbmcgKHNlcGFyYXRpbmcgdGhlIEdvb2RzIGZyb20gdGhlIEJhZHMpLiANCg0KLSBGcm9tIDAuMDIgdG8gMC4xOiB0aGUgcHJlZGljdG9yIGhhcyBvbmx5IGEgd2VhayByZWxhdGlvbnNoaXAgdG8gdGhlIEdvb2RzL0JhZHMgb2RkcyByYXRpby4gDQoNCi0gRnJvbSAwLjEgdG8gMC4zOiB0aGUgcHJlZGljdG9yIGhhcyBhIG1lZGl1bSBzdHJlbmd0aCByZWxhdGlvbnNoaXAgdG8gdGhlIEdvb2RzL0JhZHMgb2RkcyByYXRpby4gDQoNCi0gRnJvbSAwLjMgdG8gMC41OiB0aGUgcHJlZGljdG9yIGhhcyBhIHN0cm9uZyByZWxhdGlvbnNoaXAgdG8gdGhlIEdvb2RzL0JhZHMgb2RkcyByYXRpby4NCg0KLSBIaWdoZXIgdGhhbiAwLjU6IHdlIHNob3VsZCBjaGVjayBjYXJyZWZ1bGx5IHdoZW4gc2VsZWN0aW5nIHZhcmlhYmxlcyBoYXZlIElWIGhpZ2hlciB0aGFuIDAuNSBiZWNhdXNlIG9mIHN1c3BpY2lvdXMgcmVsYXRpb25zaGlwLiANCg0KDQojIyMgS2V5IENoYXJhY3RlcmlzdGljcyBvZiBhIFVzZWZ1bCBTY29yZWNhcmQgTW9kZWwNCg0KQmVmb3JlIGJyaW5naW5nIGEgc2NvcmVjYXJkIGludG8gcHJvZHVjdGlvbiwgaXQgbmVlZHMgdG8gYmUgdGhvcm91Z2hseSBldmFsdWF0ZWQuDQpEZXBlbmRpbmcgb24gdGhlIGV4YWN0IHNldHRpbmcgYW5kIHVzYWdlIG9mIHRoZSBtb2RlbCwgZGlmZmVyZW50IGFzcGVjdHMgbWF5IG5lZWQNCnRvIGJlIGFzc2Vzc2VkIGR1cmluZyBldmFsdWF0aW9uIGluIG9yZGVyIHRvIGVuc3VyZSB0aGUgbW9kZWwgaXMgYWNjZXB0YWJsZSBmb3IgaW1wbGVtZW50YXRpb24uIA0KS2V5IGNoYXJhY3RlcmlzdGljcyBvZiBzdWNjZXNzZnVsIHNjb3JlY2FyZCBtb2RlbCBhcmU6IA0KDQotICoqSW50ZXJwcmV0YWJpbGl0eSoqOiBBIHNjb3JlY2FyZCBuZWVkcyB0byBiZSBpbnRlcnByZXRhYmxlLiBJbiBvdGhlciB3b3JkcywgYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGV0ZWN0ZWQgZGVmYXVsdCBiZWhhdmlvciBpcyByZXF1aXJlZCwgZm9yIGluc3RhbmNlIHRvIHZhbGlkYXRlIHRoZSBzY29yZWNhcmQgYmVmb3JlIGl0IGNhbiBiZSB1c2VkLiBUaGlzIGFzcGVjdCBpbnZvbHZlcyBhIGNlcnRhaW4gZGVncmVlIG9mIHN1YmplY3RpdmlzbSwgc2luY2UgaW50ZXJwcmV0YWJpbGl0eSBtYXkgZGVwZW5kIG9uIHRoZSBjcmVkaXQgZXhwZXJ04oCZcyBrbm93bGVkZ2UuIFRoZSBpbnRlcnByZXRhYmlsaXR5IG9mIGEgbW9kZWwgZGVwZW5kcyBvbiBpdHMgZm9ybWF0LCB3aGljaCBpbiB0dXJuIGlzIGRldGVybWluZWQgYnkgdGhlIGFkb3B0ZWQgYW5hbHl0aWNhbCB0ZWNobmlxdWUuIE1vZGVscyB0aGF0IGFsbG93IHRoZSB1c2VyIHRvIHVuZGVyc3RhbmQgdGhlIHVuZGVybHlpbmcgcmVhc29ucyB3aHkgdGhlIG1vZGVsIHNpZ25hbHMgYSBjdXN0b21lciB0byBiZSBhIGRlZmF1bHRlciBhcmUgY2FsbGVkIHdoaXRlIGJveCBtb2RlbHMsIHdoZXJlYXMgY29tcGxleCwgaW5jb21wcmVoZW5zaWJsZSwgbWF0aGVtYXRpY2FsIG1vZGVscyBhcmUgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgYmxhY2sgYm94IG1vZGVscy4NCg0KLSAqKlN0YXRpc3RpY2FsIGFjY3VyYWN5Kio6IFJlZmVycyB0byB0aGUgZGV0ZWN0aW9uIHBvd2VyIGFuZCB0aGUgY29ycmVjdG5lc3Mgb2YgdGhlIHNjb3JlY2FyZCBpbiBsYWJlbGluZyBjdXN0b21lcnMgYXMgZGVmYXVsdGVycy4gU2V2ZXJhbCBzdGF0aXN0aWNhbCBldmFsdWF0aW9uIGNyaXRlcmlhIGV4aXN0IGFuZCBtYXkgYmUgYXBwbGllZCB0byBldmFsdWF0ZSB0aGlzIGFzcGVjdCwgc3VjaCBhcyB0aGUgaGl0IHJhdGUsIGxpZnQgY3VydmVzLCBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSAoQVVDKSwgYW5kIHNvIG9uLiBTdGF0aXN0aWNhbCBhY2N1cmFjeSBtYXkgYWxzbyByZWZlciB0byBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIG1lYW5pbmcgdGhhdCB0aGUgcGF0dGVybnMgdGhhdCBoYXZlIGJlZW4gZm91bmQgaW4gdGhlIGRhdGEgaGF2ZSB0byBiZSB2YWxpZCBhbmQgbm90IHRoZSBjb25zZXF1ZW5jZSBvZiBub2lzZS4gSW4gb3RoZXIgd29yZHMsIHdlIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIG1vZGVsIGdlbmVyYWxpemVzIHdlbGwgYW5kIGlzIG5vdCBvdmVyZml0dGVkIHRvIHRoZSBoaXN0b3JpY2FsIGRhdGEgc2V0Lg0KDQotICoqRWNvbm9taWNhbCBjb3N0Kio6IERldmVsb3BpbmcgYW5kIGltcGxlbWVudGluZyBhIHNjb3JlY2FyZCBpbnZvbHZlcyBhIHNpZ25pZmljYW50Y29zdCB0byBhbiBvcmdhbml6YXRpb24uIFRoZSB0b3RhbCBjb3N0IGluY2x1ZGVzIHRoZSBjb3N0cyB0b2dhdGhlciwgcHJlcHJvY2VzcywgYW5kIGFuYWx5emUgdGhlIGRhdGEsIGFuZCB0aGUgY29zdHMgdG8gcHV0dGhlIHJlc3VsdGluZyBzY29yZWNhcmRzIGludG8gcHJvZHVjdGlvbi4gSW4gYWRkaXRpb24sIHRoZSBzb2Z0d2FyZWNvc3RzIGFzIHdlbGwgYXMgaHVtYW4gYW5kIGNvbXB1dGluZyByZXNvdXJjZXMgc2hvdWxkIGJldGFrZW4gaW50byBhY2NvdW50LiBQb3NzaWJseSBhbHNvIGV4dGVybmFsIChlLmcuLCBjcmVkaXQgYnVyZWF1KWRhdGEgaGFzIHRvIGJlIGJvdWdodCB0byBlbnJpY2ggdGhlIGF2YWlsYWJsZSBpbi1ob3VzZSBkYXRhLkNsZWFybHkgaXQgaXMgaW1wb3J0YW50IHRvIHBlcmZvcm0gYSB0aG9yb3VnaCBjb3N0LWJlbmVmaXQgYW5hbHlzaXNhdCB0aGUgc3RhcnQgb2YgdGhlIGNyZWRpdCBzY29yaW5nIHByb2plY3QsIGFuZCB0byBnYWluIGluc2lnaHQgaW50b3RoZSBjb25zdGl0dWVudCBmYWN0b3JzIG9mIHRoZSByZXR1cm4gb24gaW52ZXN0bWVudCBvZiBidWlsZGluZyBhc2NvcmVjYXJkIHN5c3RlbS4NCg0KLSAqKlJlZ3VsYXRvcnkgY29tcGxpYW5jZSoqOiBBIHNjb3JlY2FyZCBzaG91bGQgYmUgaW4gbGluZSBhbmQgY29tcGxpYW50IHdpdGggYWxsIGFwcGxpY2FibGUgcmVndWxhdGlvbnMgYW5kIGxlZ2lzbGF0aW9uLiBJbiBhIGNyZWRpdCBzY29yaW5nIHNldHRpbmcsIHRoZSBCYXNlbCBBY2NvcmRzIHNwZWNpZnkgd2hhdCBpbmZvcm1hdGlvbiBjYW4gb3IgY2Fubm90IGJlIHVzZWQgYW5kIGhvdyB0aGUgdGFyZ2V0IChpLmUuLCBkZWZhdWx0KSBzaG91bGQgYmUgZGVmaW5lZC4gT3RoZXIgcmVndWxhdGlvbnMgKGUuZy4sIHdpdGggcmVzcGVjdCB0byBwcml2YWN5IGFuZC9vciBkaXNjcmltaW5hdGlvbikgc2hvdWxkIGFsc28gYmUgcmVzcGVjdGVkLg0KDQoNCiMjIyBTb21lIFByYWN0aWNhbCBBc3BlY3RzIG9mIFVzaW5nIFNjb3JlY2FyZCBNb2RlbCBhdCBCYW5rcw0KDQpUaGUgbW9zdCBpbXBvcnRhbnQgdXNhZ2Ugb2YgYXBwbGljYXRpb24gc2NvcmVzIGlzIHRvIGRlY2lkZSBvbiBsb2FuIGFwcHJvdmFsLiBUaGUNCnNjb3JlcyBjYW4gYWxzbyBiZSB1c2VkIGZvciBwcmljaW5nIHB1cnBvc2VzLiBSaXNrLWJhc2VkIHByaWNpbmcgKHNvbWV0aW1lcyBhbHNvDQpyZWZlcnJlZCB0byBhcyByaXNrLWFkanVzdGVkIHByaWNpbmcpIHNldHMgdGhlIHByaWNlIG9yIG90aGVyIGNoYXJhY3RlcmlzdGljcyAoZS5nLiwNCmxvYW4gdGVybSwgY29sbGF0ZXJhbCkgb2YgdGhlIGxvYW4gYmFzZWQgb24gdGhlIHBlcmNlaXZlZCByaXNrIGFzIG1lYXN1cmVkIGJ5IHRoZQ0KYXBwbGljYXRpb24gc2NvcmUuIEEgbG93ZXIgc2NvcmUgd2lsbCBpbXBseSBhIGhpZ2hlciBpbnRlcmVzdCByYXRlIGFuZCB2aWNlIHZlcnNhLiANCg0KDQojIyMgUiBDb2RlcyBmb3IgRGV2ZWxvcGluZyBTY29yZWNhcmQgTW9kZWwgdXNpbmcgTG9naXN0aWMgUmVncmVzc2lvbg0KDQpJbiB0aGlzIHNlY3Rpb24gdGhlIGF1dGhvciB3aWxsIHByZXNlbnQgUiBDb2RlcyBmb3IgZGV2ZWxvcGluZyBTY29yZWNhcmQgTW9kZWwgd2l0aCBhIGZvY3VzIG9uOiANCg0KLSBQcm9ibGVtIG9mIHByb2Nlc3NpbmcgZGF0YSAobWlzc2luZyBkYXRhKS4gDQoNCi0gVmFyaWFibGUgU2VsZWN0aW9uIGJhc2VkIG9uOiAoMSkgQklDIENyaXRlcmlvbiwgYW5kICgyKSBJViBWYWx1ZS4gDQoNCi0gRmluZGluZyBvcHRpbWFsIHRocmVzaG9sZCBmb3IgY2xhc3NpZnlpbmcgYW5kIHNlbGVjdGluZyBieSB1c2luZyBzaW11bGF0aW9ucy4gDQoNCi0gQ29tcGFyaW5nIHJlc3VsdHMgZnJvbSBhbHRlcm5hdGl2ZSBhcHByb2FjaGVzLiANCg0KLSBEZXBsb3lpbmcgbW9kZWwgc2VsZWN0ZWQgaW4gcHJhY3RpY2UgYnkgdXNpbmcgYSBSIGZ1bmN0aW9uIHRoYXQgdGFrZXMgaW5wdXRzIGFuZCByZXR1cm5zIGZpbmFsIHNjb3JlIG91dHB1dHMuIA0KDQpEYXRhIHVzZWQgaW4gdGhpcyBwb3N0IGNhbiBiZSBkb3dubG9hZCBmcm9tOiBodHRwOi8vd3d3LmNyZWRpdHJpc2thbmFseXRpY3MubmV0L3VwbG9hZHMvMS85LzUvMS8xOTUxMTYwMS9obWVxLmNzdi4gDQoNCmBgYHtyfQ0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgU3RhdGUgMTogRGF0YSBQcmUtcHJvY2Vzc2luZw0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIExvYWQgc29tZSBwYWNrYWdlcyBmb3IgZGF0YSBtYW5pcHVsYXRpb246IA0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KDQojIENsZWFyIHdvcmtzcGFjZTogDQpybShsaXN0ID0gbHMoKSkNCg0KIyBJbXBvcnQgZGF0YTogDQpobWVxIDwtIHJlYWQuY3N2KCJodHRwOi8vd3d3LmNyZWRpdHJpc2thbmFseXRpY3MubmV0L3VwbG9hZHMvMS85LzUvMS8xOTUxMTYwMS9obWVxLmNzdiIpDQoNCiMgRnVuY3Rpb24gZm9yIGRldGVjdGluZyBOQSBvYnNlcnZhdGlvbnM6IA0KbmFfcmF0ZSA8LSBmdW5jdGlvbih4KSB7eCAlPiUgaXMubmEoKSAlPiUgc3VtKCkgLyBsZW5ndGgoeCl9DQpzYXBwbHkoaG1lcSwgbmFfcmF0ZSkgJT4lIHJvdW5kKDIpDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBGdW5jdGlvbiByZXBsYWNlcyBOQSBieSBtZWFuOiANCnJlcGxhY2VfYnlfbWVhbiA8LSBmdW5jdGlvbih4KSB7DQogIHhbaXMubmEoeCldIDwtIG1lYW4oeCwgbmEucm0gPSBUUlVFKQ0KICByZXR1cm4oeCkNCn0NCg0KIyBBIGZ1bmN0aW9uIGltcHV0ZXMgTkEgb2JzZXJ2YXRpb25zIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXM6IA0KDQpyZXBsYWNlX25hX2NhdGVnb3JpY2FsIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCAlPiUgDQogICAgdGFibGUoKSAlPiUgDQogICAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgICBhcnJhbmdlKC1GcmVxKSAtPj4gbXlfZGYNCiAgDQogIG5fb2JzIDwtIHN1bShteV9kZiRGcmVxKQ0KICBwb3AgPC0gbXlfZGYkLiAlPiUgYXMuY2hhcmFjdGVyKCkNCiAgc2V0LnNlZWQoMjkpDQogIHhbaXMubmEoeCldIDwtIHNhbXBsZShwb3AsIHN1bShpcy5uYSh4KSksIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gbXlfZGYkRnJlcSkNCiAgcmV0dXJuKHgpDQp9DQoNCiMgVXNlIHRoZSB0d28gZnVuY3Rpb25zOiANCmRmIDwtIGhtZXEgJT4lIA0KICBtdXRhdGVfaWYoaXMuZmFjdG9yLCBhcy5jaGFyYWN0ZXIpICU+JSANCiAgbXV0YXRlKFJFQVNPTiA9IGNhc2Vfd2hlbihSRUFTT04gPT0gIiIgfiBOQV9jaGFyYWN0ZXJfLCBUUlVFIH4gUkVBU09OKSwgDQogICAgICAgICBKT0IgPSBjYXNlX3doZW4oSk9CID09ICIiIH4gTkFfY2hhcmFjdGVyXywgVFJVRSB+IEpPQikpICU+JQ0KICBtdXRhdGVfaWYoaXNfY2hhcmFjdGVyLCBhcy5mYWN0b3IpICU+JSANCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIHJlcGxhY2VfYnlfbWVhbikgJT4lIA0KICBtdXRhdGVfaWYoaXMuZmFjdG9yLCByZXBsYWNlX25hX2NhdGVnb3JpY2FsKQ0KDQojIFNwbGl0IG91ciBkYXRhOiANCg0KZGZfdHJhaW4gPC0gZGYgJT4lIA0KICBncm91cF9ieShCQUQpICU+JSANCiAgc2FtcGxlX2ZyYWMoMC41KSAlPiUgDQogIHVuZ3JvdXAoKSAjIFVzZSA1MCUgZGF0YSBzZXQgZm9yIHRyYWluaW5nIG1vZGVsLiANCiANCmRmX3Rlc3QgPC0gZHBseXI6OnNldGRpZmYoZGYsIGRmX3RyYWluKSAjIFVzZSA1MCUgZGF0YSBzZXQgZm9yIHZhbGlkYXRpb24uIA0KDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgU2NlbmFyaW8gMTogU2NvcmVjYXJkIG1vZGVsIHdpdGggdmFyaWFibGVzIHNlbGVjdGVkIGJhc2VkIG9uIEFJQyBDcml0ZXJpb24gDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIExvYWQgcHVycnIgcGFja2FnZSBmb3IgbG9vcGluZzogDQpsaWJyYXJ5KHB1cnJyKQ0KDQojIEZ1bmN0aW9uIGxpc3RzIGFsbCBjb21iaW5hdGlvbnMgb2YgdmFyaWFibGVzOiANCg0KYWxsX2NvbWJpbmF0aW9ucyA8LSBmdW5jdGlvbih5b3VyX3ByZWRpY3RvcnMpIHsNCg0KICBuIDwtIGxlbmd0aCh5b3VyX3ByZWRpY3RvcnMpDQogIG1hcCgxOm4sIGZ1bmN0aW9uKHgpIHtjb21ibih2YXJpYWJsZXMsIHgpfSkgJT4lICANCiAgICBtYXAoYXMuZGF0YS5mcmFtZSkgLT4gaw0KICANCiAgYWxsX3ZlYyA8LSBjKCkNCiAgDQogIGZvciAoaSBpbiAxOm4pIHsNCiAgICBkZiA8LSBrW1tpXV0NCiAgICBuX2NvbCA8LSBuY29sKGRmKQ0KICAgIA0KICAgIGZvciAoaiBpbiAxOm5fY29sKSB7DQogICAgICBteV92ZWMgPC0gZGZbLCBqXSAlPiUgYXMuY2hhcmFjdGVyKCkgJT4lIGxpc3QoKQ0KICAgICAgYWxsX3ZlYyA8LSBjKGFsbF92ZWMsIG15X3ZlYykNCiAgICB9DQogIH0NCiAgDQogIHJldHVybihhbGxfdmVjKQ0KICANCn0NCg0KDQojIEFsbCBwb3RlbnRpYWwgdmFyaWFibGVzIGNhbiBiZSB1c2VkIGZvciBtb2RlbGxpbmcgTG9naXN0aWMgUmVncmVzc2lvbjogIA0KdmFyaWFibGVzIDwtIGRmICU+JSBzZWxlY3QoLUJBRCkgJT4lIG5hbWVzKCkNCg0KIyBBSUNzIGZvciBhbGwgbW9kZWxzLiBOb3RlIHRoYXQgdGhlcmUgd2lsbCBiZSA0MDk1IExvZ2lzdGljIE1vZGVscyB0aHVzIA0KIyB0cmFpbmluZyBhbGwgbW9kZWxzIG1heSBiZSBhIHRpbWUtY29uc3VtaW5nIHByb2Nlc3M6IA0KDQpzeXN0ZW0udGltZSgNCiAgYWxsX2NvbWJpbmF0aW9ucyh2YXJpYWJsZXMpICU+JSANCiAgbWFwKGZ1bmN0aW9uKHgpIHthcy5mb3JtdWxhKHBhc3RlKCJCQUQgfiIsIHBhc3RlKHgsIGNvbGxhcHNlID0gIiArICIpKSl9KSAlPiUgDQogIG1hcChmdW5jdGlvbihmb3JtdWxhcikge2dsbShmb3JtdWxhciwgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSBkZl90cmFpbil9KSAlPiUgDQogIG1hcF9kYmwoImFpYyIpIC0+IGFpY192YWx1ZXMNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KDQoNCiMgIFRoZSBjb25iaW5hdGlvbiBvZiBQcmVkaWN0b3JzIHRoYXQgcmVzdWx0cyBtaW4gQUlDOiANCg0KYWxsX2NvbWJpbmF0aW9uX29mX3ZhcnMgPC0gdmFyaWFibGVzICU+JSBhbGxfY29tYmluYXRpb25zKCkNCnByZWRpY3RvcnNfc2VsZWN0ZWQgPC0gYWxsX2NvbWJpbmF0aW9uX29mX3ZhcnNbW3doaWNoLm1pbihhaWNfdmFsdWVzKV1dIA0KDQojIFRodXMgdGhlcmUgYXJlIDEwIHByZWRpY3RvcnMgc2VsZWN0ZWQgYmFzZWQgb24gQUlDIGNyaXRlcmlvbjogIA0KcHJlZGljdG9yc19zZWxlY3RlZA0KYGBgDQoNCmBgYHtyfQ0KDQoNCiMgRGF0YSBmcmFtZSBmb3IgdHJhaW5pbmcgTG9naXN0aWMgUmVncmVzc2lvbjogDQpkZl90cmFpbl9haWMgPC0gZGZfdHJhaW4gJT4lIHNlbGVjdChwcmVkaWN0b3JzX3NlbGVjdGVkLCAiQkFEIikNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIERldmVsb3AgYSBzY29yZWNhcmQgTW9kZWwgZm9yIHZhcmlhYmxlcyBiYXNlZCBvbiB0aGUgcmVzdWx0cyBmcm9tIFdPRSBhbmQgSVYgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KbGlicmFyeShzY29yZWNhcmQpDQoNCiMgR2VuZXJhdGVzIG9wdGltYWwgYmlubmluZyBmb3IgbnVtZXJpY2FsLCBmYWN0b3IgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlczogDQpiaW5zX3ZhciA8LSB3b2ViaW4oZGZfdHJhaW5fYWljLCB5ID0gIkJBRCIsIG5vX2NvcmVzID0gMjAsIHBvc2l0aXZlID0gIkJBRHwxIikNCg0KIyBDcmVhdGVzIGEgZGF0YSBmcmFtZSBvZiBiaW5uZWQgdmFyaWFibGVzIGZvciBMb2dpc3RpYyBSZWdyZXNzaW9uOiANCmRmX3RyYWluX3dvZSA8LSB3b2ViaW5fcGx5KGRmX3RyYWluX2FpYywgYmluc192YXIpDQoNCiMgTG9naXN0aWMgUmVncmVzc2lvbjoNCm15X2xvZ2lzdGljIDwtIGdsbShCQUQgfiAuLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IGRmX3RyYWluX3dvZSkNCg0KIyBTaG93IHJlc3VsdHM6IA0KbXlfbG9naXN0aWMgJT4lIHN1bW1hcnkoKQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KDQojIENhbGN1bGF0ZSBzY29yZWNhcmQgc2NvcmVzIGZvciB2YXJpYWJsZXMgYmFzZWQgb24gdGhlIHJlc3VsdHMgZnJvbSB3b2ViaW4gYW5kIGdsbTogDQpteV9jYXJkIDwtIHNjb3JlY2FyZChiaW5zX3ZhciwgbXlfbG9naXN0aWMsIHBvaW50czAgPSA2MDAsIG9kZHMwID0gMS8xOSwgcGRvID0gNTApDQoNCiMgU2hvdyBSZXN1bHRzOiANCg0KbGlicmFyeShzdHJpbmdyKQ0KZG8uY2FsbCgiYmluZF9yb3dzIiwgbXlfY2FyZCkgJT4lIA0KICBzbGljZSgtMSkgJT4lIA0KICBzZWxlY3QoLWJyZWFrcywgLWlzX3NwZWNpYWxfdmFsdWVzLCAtY291bnQsIC1jb3VudF9kaXN0ciwgLWdvb2QsIC1iYWQsIC1iYWRwcm9iKSAlPiUgDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7cm91bmQoeCwgMyl9KSAlPiUgDQogIG11dGF0ZShiaW4gPSBiaW4gJT4lIA0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIlxcWyIsICJGcm9tICIpICU+JSANCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJcXCwiLCAiIHRvICIpICU+JSANCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJcXCkiLCAiIikpIC0+IGl2X2Zvcl9wcmVkaWN0b3JzX3BvaW50DQoNCml2X2Zvcl9wcmVkaWN0b3JzX3BvaW50ICU+JSANCiAga25pdHI6OmthYmxlKGNvbC5uYW1lcyA9IGMoIlByZWRpY3RvciIsICJHcm91cCIsICJXT0UiLCAiU2NvcmVjYXJkIiwgIkJpbiBJViIsICJUb3RhbCBJViIpKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCg0KDQoNCiMgSW5mb3JtYXRpb24gVmFsdWVzIGZvciBwcmVkaWN0b3JzOiANCg0KaXZfZm9yX3ByZWRpY3RvcnNfcG9pbnQgJT4lIA0KICBncm91cF9ieSh2YXJpYWJsZSkgJT4lIA0KICBzdW1tYXJpc2UoaXZfdmFyID0gbWVhbih0b3RhbF9pdikpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgYXJyYW5nZShpdl92YXIpICU+JSANCiAgbXV0YXRlKHZhcmlhYmxlID0gZmFjdG9yKHZhcmlhYmxlLCBsZXZlbHMgPSB2YXJpYWJsZSkpIC0+IGl2X3ZhbHVlcw0KDQoNCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpDQppdl92YWx1ZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHZhcmlhYmxlLCBpdl92YXIpKSArIA0KICBnZW9tX2NvbChmaWxsID0gIiMzNzdlYjgiKSArIA0KICBjb29yZF9mbGlwKCkgKyANCiAgZ2VvbV9jb2woZGF0YSA9IGl2X3ZhbHVlcyAlPiUgZmlsdGVyKGl2X3ZhciA8IDAuMSksIGFlcyh2YXJpYWJsZSwgaXZfdmFyKSwgZmlsbCA9ICJncmV5NjAiKSArIA0KICBnZW9tX3RleHQoZGF0YSA9IGl2X3ZhbHVlcyAlPiUgZmlsdGVyKGl2X3ZhciA8IDAuMSksIGFlcyhsYWJlbCA9IHJvdW5kKGl2X3ZhciwgMykpLCANCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMSwgc2l6ZSA9IDUsIGNvbG9yID0gImdyZXk0MCIpICsgDQogIGdlb21fdGV4dChkYXRhID0gaXZfdmFsdWVzICU+JSBmaWx0ZXIoaXZfdmFyID49IDAuMSksIGFlcyhsYWJlbCA9IHJvdW5kKGl2X3ZhciwgMykpLCANCiAgICAgICAgICAgIGhqdXN0ID0gLS4xLCBzaXplID0gNSwgY29sb3IgPSAiIzM3N2ViOCIpICsgDQogIGxhYnModGl0bGUgPSAiRmlndXJlIDE6IEluZm9ybWF0aW9uIFZhbHVlIChJVikgZm9yIFZhcmlhYmxlcyIsIA0KICAgICAgIHggPSBOVUxMLCB5ID0gIkluZm9ybWF0aW9uIFZhbHVlIChJVikiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAyKSkgKyANCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArIA0KICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAxLCAxLCAxKSwgImNtIikpDQoNCmBgYA0KDQpgYGB7cn0NCg0KDQojIFNjb3JlY2FyZCBwb2ludCBmb3IgYWxsIG9ic2VydmF0aW9ucyBmcm9tIHRyYWluIGRhdGEgc2V0OiANCm15X3BvaW50c190cmFpbiA8LSBzY29yZWNhcmRfcGx5KGRmX3RyYWluX2FpYywgbXlfY2FyZCwgb25seV90b3RhbF9zY29yZSA9IEZBTFNFLCBwcmludF9zdGVwID0gMCkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KDQojIFNvbWUgc3RhdGlzdGljcyBzY29yZWNhcmQgYnkgZ3JvdXA6IA0KZGZfc2NvcmVkX3RyYWluIDwtIGRmX3RyYWluX2FpYyAlPiUgDQogIG11dGF0ZShTQ09SRSA9IG15X3BvaW50c190cmFpbiRzY29yZSkgJT4lIA0KICBtdXRhdGUoQkFEID0gY2FzZV93aGVuKEJBRCA9PSAxIH4gIkRlZmF1bHQiLCBUUlVFIH4gIk5vbkRlZmF1bHQiKSkgDQoNCmRmX3Njb3JlZF90cmFpbiAlPiUgDQogIGdyb3VwX2J5KEJBRCkgJT4lIA0KICBzdW1tYXJpc2VfZWFjaChmdW5zKG1pbiwgbWF4LCBtZWRpYW4sIG1lYW4sIG4oKSksIFNDT1JFKSAlPiUgDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7cm91bmQoeCwgMCl9KSAlPiUgDQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIlRhYmxlIDE6IFNjb3JlY2FkIFBvaW50cyBieSBHcm91cCBmb3IgVHJhaW4gRGF0YSIpDQoNCmBgYA0KDQpgYGB7cn0NCg0KDQpkZl9zY29yZWRfdHJhaW4gJT4lIA0KICBncm91cF9ieShCQUQpICU+JSANCiAgc3VtbWFyaXNlKHRiID0gbWVhbihTQ09SRSkpICU+JSANCiAgdW5ncm91cCgpIC0+IG1lYW5fc2NvcmVfdHJhaW4NCg0KZGZfc2NvcmVkX3RyYWluICU+JSANCiAgZ2dwbG90KGFlcyhTQ09SRSwgY29sb3IgPSBCQUQsIGZpbGwgPSBCQUQpKSArIA0KICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpICsgDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuX3Njb3JlX3RyYWluJHRiWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fc2NvcmVfdHJhaW4kdGJbMl0pLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKyANCiAgZ2VvbV90ZXh0KGFlcyh4ID0gNDAwIC0gMTUsIHkgPSAwLjAwNDIsIGxhYmVsID0gbWVhbl9zY29yZV90cmFpbiR0YlsxXSAlPiUgcm91bmQoMCkpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gNCkgKyANCiAgZ2VvbV90ZXh0KGFlcyh4ID0gNTY1LCB5ID0gMC4wMDQyLCBsYWJlbCA9IG1lYW5fc2NvcmVfdHJhaW4kdGJbMl0gJT4lIHJvdW5kKDApKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSA0KSArIA0KICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjgpKSArIA0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiRmlndXJlIDI6IFNjb3JlY2FyZCBEaXN0cmlidXRpb24gYnkgdHdvIENyZWRpdCBHcm91cHMgZm9yIFRyYWluIERhdGEiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJUaGUgc2NvcmVjYXJkIHBvaW50IGlzIGEgbnVtZXJpYyBleHByZXNzaW9uIG1lYXN1cmluZyBjcmVkaXR3b3J0aGluZXNzLiBDb21tZXJjaWFsIEJhbmtzXG51c3VhbGx5IHV0aWxpemUgaXQgYXMgYSBtZXRob2QgdG8gc3VwcG9ydCB0aGUgZGVjaXNpb24tbWFraW5nIGFib3V0IGNyZWRpdCBhcHBsaWNhdGlvbnMuIikNCg0KYGBgDQoNCmBgYHtyfQ0KDQoNCiMgU2NvcmVjYXJkIFBvaW50cyBmb3IgdGVzdCBkYXRhIHNldDogDQoNCmRmX3Rlc3RfYWljIDwtIGRmX3Rlc3QgJT4lIHNlbGVjdChwcmVkaWN0b3JzX3NlbGVjdGVkLCAiQkFEIikNCg0KbXlfcG9pbnRzX3Rlc3QgPC0gc2NvcmVjYXJkX3BseShkZl90ZXN0X2FpYywgbXlfY2FyZCwgcHJpbnRfc3RlcCA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5X3RvdGFsX3Njb3JlID0gRkFMU0UpICU+JSBhcy5kYXRhLmZyYW1lKCkNCg0KDQpkZl9zY29yZWRfdGVzdCA8LSBkZl90ZXN0X2FpYyAlPiUgDQogIG11dGF0ZShTQ09SRSA9IG15X3BvaW50c190ZXN0JHNjb3JlKSAlPiUgDQogIG11dGF0ZShCQUQgPSBjYXNlX3doZW4oQkFEID09IDEgfiAiRGVmYXVsdCIsIFRSVUUgfiAiTm9uRGVmYXVsdCIpKSANCg0KZGZfc2NvcmVkX3Rlc3QgJT4lIA0KICBncm91cF9ieShCQUQpICU+JSANCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtaW4sIG1heCwgbWVkaWFuLCBtZWFuLCBuKCkpLCBTQ09SRSkgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDApfSkgJT4lIA0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJUYWJsZSAyOiBTY29yZWNhZCBQb2ludHMgYnkgR3JvdXAgZm9yIFRlc3QgRGF0YSIpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KDQoNCg0KZGZfc2NvcmVkX3Rlc3QgJT4lIA0KICBncm91cF9ieShCQUQpICU+JSANCiAgc3VtbWFyaXNlKHRiID0gbWVhbihTQ09SRSkpICU+JSANCiAgdW5ncm91cCgpIC0+IG1lYW5fc2NvcmVfdGVzdA0KDQpkZl9zY29yZWRfdGVzdCAlPiUgDQogIGdncGxvdChhZXMoU0NPUkUsIGNvbG9yID0gQkFELCBmaWxsID0gQkFEKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9zY29yZV90ZXN0JHRiWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsyXSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArIA0KICBnZW9tX3RleHQoYWVzKHggPSA0MTIsIHkgPSAwLjAwNDIsIGxhYmVsID0gbWVhbl9zY29yZV90ZXN0JHRiWzFdICU+JSByb3VuZCgwKSksIGNvbG9yID0gInJlZCIsIHNpemUgPSA0KSArIA0KICBnZW9tX3RleHQoYWVzKHggPSA1NzAsIHkgPSAwLjAwNDIsIGxhYmVsID0gbWVhbl9zY29yZV90ZXN0JHRiWzJdICU+JSByb3VuZCgwKSksIGNvbG9yID0gImJsdWUiLCBzaXplID0gNCkgKyANCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44KSkgKyANCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHRpdGxlID0gIkZpZ3VyZSAzOiBTY29yZWNhcmQgRGlzdHJpYnV0aW9uIGJ5IHR3byBDcmVkaXQgR3JvdXBzIGZvciBUZXN0IERhdGEiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJUaGUgc2NvcmVjYXJkIHBvaW50IGlzIGEgbnVtZXJpYyBleHByZXNzaW9uIG1lYXN1cmluZyBjcmVkaXR3b3J0aGluZXNzLiBDb21tZXJjaWFsIEJhbmtzXG51c3VhbGx5IHV0aWxpemUgaXQgYXMgYSBtZXRob2QgdG8gc3VwcG9ydCB0aGUgZGVjaXNpb24tbWFraW5nIGFib3V0IGNyZWRpdCBhcHBsaWNhdGlvbnMuIikNCmBgYA0KDQojIyMgU29tZSBDcml0ZXJpYSBmb3IgTW9kZWwgRXZhbHVhdGlvbiBpbiBDb250ZXh0IG9mIENyZXNpdCBTY29yaW5nDQoNCkl0IGlzIGltcG9zc2libGUgdG8gdXNlIGEgc2NvcmluZyBtb2RlbCBlZmZlY3RpdmVseSB3aXRob3V0IGtub3dpbmcgaG93IGFjY3VyYXRlIGl0IGlzLiBGaXJzdCwgb25lIG5lZWRzIHRvIHNlbGVjdCB0aGUgYmVzdCBtb2RlbCBhY2NvcmRpbmcgdG8gc29tZSBjcml0ZXJpYSBmb3IgZXZhbHVhdGluZyBtb2RlbCBwZXJmb3JtYW5jZS4gVGhlIG1ldGhvZG9sb2d5IG9mIGNyZWRpdCBzY29yaW5nIG1vZGVscw0KYW5kIHNvbWUgbWVhc3VyZXMgb2YgdGhlaXIgcXVhbGl0eSBoYXZlIGJlZW4gZGlzY3Vzc2VkIGluIHN1cnZleXMgY29uZHVjdGVkIGJ5DQpIYW5kIGFuZCBIZW5sZXkgKDE5OTcpLCBUaG9tYXMgKDIwMDApLCBhbmQgQ3Jvb2sgYXQgYWwuICgyMDA3KS4gSG93ZXZlciwgdW50aWwganVzdA0KdGVuIHllYXJzIGFnbywgdGhlIGdlbmVyYWwgbGl0ZXJhdHVyZSBkZXZvdGVkIHRvIHRoZSBpc3N1ZSBvZiBjcmVkaXQgc2NvcmluZyB3YXMgbm90IHN1YnN0YW50aWFsLiBGb3J0dW5hdGVseSwgdGhlIHNpdHVhdGlvbiBoYXMgaW1wcm92ZWQgaW4gdGhlIGxhc3QgZGVjYWRlIHdpdGggdGhlIHB1YmxpY2F0aW9uIG9mIHdvcmtzIGJ5IEFuZGVyc29uICgyMDA3KSwgQ3Jvb2sgZXQgYWwuICgyMDA3KSwgU2lkZGlxaSAoMjAwNiksIFRob21hcyBldCBhbC4gKDIwMDIpLCBhbmQgVGhvbWFzICgyMDA5KSwgYWxsIG9mIHdoaWNoIGFkZHJlc3MgdGhlIHRvcGljIG9mIGNyZWRpdCBzY29yaW5nLiBUaGUgbW9zdCB1c2VkIGNyaXRlcmlhIGluIGNvbnRleHQgb2YgY3JlZGl0IHNjb3JpbmcgYXJlOiANCg0KLSBHYWluIG9yICoqbGlmdCoqIGlzIGEgbWVhc3VyZSBvZiB0aGUgZWZmZWN0aXZlbmVzcyBvZiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsIGNhbGN1bGF0ZWQgYXMgdGhlIHJhdGlvIGJldHdlZW4gdGhlIHJlc3VsdHMgb2J0YWluZWQgd2l0aCBhbmQgd2l0aG91dCB0aGUgbW9kZWwuIEdhaW4gYW5kIGxpZnQgY2hhcnRzIGFyZSB2aXN1YWwgYWlkcyBmb3IgZXZhbHVhdGluZyBwZXJmb3JtYW5jZSBvZiBjbGFzc2lmaWNhdGlvbiBtb2RlbHMuIEhvd2V2ZXIsIGluIGNvbnRyYXN0IHRvIHRoZSBjb25mdXNpb24gbWF0cml4IHRoYXQgZXZhbHVhdGVzIG1vZGVscyBvbiB0aGUgd2hvbGUgcG9wdWxhdGlvbiBnYWluIG9yIGxpZnQgY2hhcnQgZXZhbHVhdGVzIG1vZGVsIHBlcmZvcm1hbmNlIGluIGEgcG9ydGlvbiBvZiB0aGUgcG9wdWxhdGlvbi4gDQoNCi0gSy1TIG9yICoqS29sbW9nb3Jvdi1TbWlybm92IGNoYXJ0KiogbWVhc3VyZXMgcGVyZm9ybWFuY2Ugb2YgY2xhc3NpZmljYXRpb24gbW9kZWxzLiBNb3JlIGFjY3VyYXRlbHksIEstUyBpcyBhIG1lYXN1cmUgb2YgdGhlIGRlZ3JlZSBvZiBzZXBhcmF0aW9uIGJldHdlZW4gdGhlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBkaXN0cmlidXRpb25zLiBUaGUgSy1TIGlzIDEwMCBpZiB0aGUgc2NvcmVzIHBhcnRpdGlvbiB0aGUgcG9wdWxhdGlvbiBpbnRvIHR3byBzZXBhcmF0ZSBncm91cHMgaW4gd2hpY2ggb25lIGdyb3VwIGNvbnRhaW5zIGFsbCB0aGUgcG9zaXRpdmVzIGFuZCB0aGUgb3RoZXIgYWxsIHRoZSBuZWdhdGl2ZXMuIE9uIHRoZSBvdGhlciBoYW5kLCBJZiB0aGUgbW9kZWwgY2Fubm90IGRpZmZlcmVudGlhdGUgYmV0d2VlbiBwb3NpdGl2ZXMgYW5kIG5lZ2F0aXZlcywgdGhlbiBpdCBpcyBhcyBpZiB0aGUgbW9kZWwgc2VsZWN0cyBjYXNlcyByYW5kb21seSBmcm9tIHRoZSBwb3B1bGF0aW9uLiBUaGUgSy1TIHdvdWxkIGJlIDAuIEluIG1vc3QgY2xhc3NpZmljYXRpb24gbW9kZWxzIHRoZSBLLVMgd2lsbCBmYWxsIGJldHdlZW4gMCBhbmQgMTAwLCBhbmQgdGhhdCB0aGUgaGlnaGVyIHRoZSB2YWx1ZSB0aGUgYmV0dGVyIHRoZSBtb2RlbCBpcyBhdCBzZXBhcmF0aW5nIHRoZSBwb3NpdGl2ZSBmcm9tIG5lZ2F0aXZlIGNhc2VzLg0KDQotICoqQXJlYSB1bmRlciBST0MgY3VydmUqKiBpcyBvZnRlbiB1c2VkIGFzIGEgbWVhc3VyZSBvZiBxdWFsaXR5IG9mIHRoZSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMuIEEgcmFuZG9tIGNsYXNzaWZpZXIgaGFzIGFuIGFyZWEgdW5kZXIgdGhlIGN1cnZlIG9mIDAuNSwgd2hpbGUgQVVDIGZvciBhIHBlcmZlY3QgY2xhc3NpZmllciBpcyBlcXVhbCB0byAxLiBJbiBwcmFjdGljZSwgbW9zdCBvZiB0aGUgY2xhc3NpZmljYXRpb24gbW9kZWxzIGhhdmUgYW4gQVVDIGJldHdlZW4gMC41IGFuZCAxLg0KDQotICoqUHJlY2lzaW9uKiogYW5kICoqUmVjYWxsKiogKGZvciBtb3JlIGRldGFpbDogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUHJlY2lzaW9uX2FuZF9yZWNhbGwpLiANCg0KTW9kZWwgcGVyZm9ybWFuY2UgY3JpdGVyaWEgd2lsbCBiZSBwcmVzZW50ZWQgaW4gdGhlIGZvbGxvd2luZyBjb2RlIGNodW5rLiANCg0KDQpgYGB7cn0NCiMgQ29udmVydCB0byBiaW5uZWQgZGF0YSBmcmFtZSBmb3IgdGVzdCBkYXRhOiANCmRmX3Rlc3Rfd29lIDwtIHdvZWJpbl9wbHkoZGZfdGVzdF9haWMsIGJpbnNfdmFyKQ0KDQojIENhY3VsYXRlIHByb2JhYmlsaXkgb2YgZGVmYXVsdCAoUEQpIGZvciBvYnNlcnZhdGlvbnMgYmVsb25naW5nIHRlc3QgZGF0YTogDQp0ZXN0X3ByZWQgPC0gcHJlZGljdChteV9sb2dpc3RpYywgZGZfdGVzdF93b2UsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIE1vZGVsIFBlcmZvcm1hbmNlIGZvciB0ZXN0IGRhdGE6IA0KcGVyZl9ldmEoZGZfdGVzdF9haWMkQkFELCB0ZXN0X3ByZWQsIA0KICAgICAgICAgdHlwZSA9IGMoImtzIiwgImxpZnQiLCAicm9jIiwgInByIiksIA0KICAgICAgICAgdGl0bGUgPSAiVGVzdCBEYXRhIikNCmBgYA0KDQojIyMgUG9iYWJpbGl0aWVzIG9mIERlZmF1bHQNCg0KU2NvcmVjYXJkcyBiYXNlZCBvbiBvdXIgbW9kZWwgcHJvdmlkZSBzY29yZXMuIEEgc2NvcmUgaXMgYSBtZWFzdXJlIHRoYXQgYWxsb3dzIGxlbmRlcnMNCnRvIHJhbmsgY3VzdG9tZXJzIGZyb20gaGlnaCByaXNrIChsb3cgc2NvcmUpIHRvIGxvdyByaXNrIChoaWdoIHNjb3JlKSBhbmQgYXMgc3VjaA0KcHJvdmlkZXMgYSByZWxhdGl2ZSBtZWFzdXJlIG9mIGNyZWRpdCByaXNrLiBTY29yZXMgYXJlIHVubGltaXRlZCBhbmQgY2FuIGJlIG1lYXN1cmVkDQp3aXRoaW4gYW55IHJhbmdlOyB0aGV5IGNhbiBldmVuIGJlIG5lZ2F0aXZlLiBBIHNjb3JlIGlzIG5vdCB0aGUgc2FtZSBhcyBhIHByb2JhYmlsaXR5Lg0KQSBwcm9iYWJpbGl0eSBhbHNvIGFsbG93cyB1cyB0byByYW5rLCBidXQgb24gdG9wIG9mIHRoYXQsIHNpbmNlIGl0IGlzIGxpbWl0ZWQgYmV0d2Vlbg0KMCBhbmQgMSwgaXQgYWxzbyBnaXZlcyBhbiBhYnNvbHV0ZSBpbnRlcnByZXRhdGlvbiBvZiBjcmVkaXQgcmlzay4gSGVuY2UsIHByb2JhYmlsaXRpZXMNCnByb3ZpZGUgbW9yZSBpbmZvcm1hdGlvbiB0aGFuIHNjb3JlcyBkby4gRm9yIGFwcGxpY2F0aW9uIHNjb3JpbmcsIG9uZSBkb2VzIG5vdCBuZWVkDQp3ZWxsLWNhbGlicmF0ZWQgcHJvYmFiaWxpdGllcyBvZiBkZWZhdWx0LiBIb3dldmVyLCBmb3Igb3RoZXIgYXBwbGljYXRpb24gYXJlYXMgc3VjaCBhcw0KcmVndWxhdG9yeSBjYXBpdGFsIGNhbGN1bGF0aW9uIGluIGEgQmFzZWwgc2V0dGluZywgYXMgd2Ugd2lsbCBkaXNjdXNzIGxhdGVyLCBjYWxpYnJhdGVkDQpkZWZhdWx0IHByb2JhYmlsaXRpZXMgYXJlIG5lZWRlZCAoVmFuIEdlc3RlbCBhbmQgQmFlc2VucyAyMDA5KS4NCg0KQXQgdGhpcyBzdGF0ZSB3ZSBjYW4gZXZhbHVhdGUgcHJvYmFiaWxpdGllcyBvZiBkZWZhdWx0IChQRCkgZm9yIGFsbCBvYnNlcnZhdGlvbnMgYnkgZ3JvdXAgYXMgZm9sbG93czogDQoNCmBgYHtyfQ0KDQojIEN0ZWF0ZSBhIGRhdGEgZnJhbWUgb2YgUEQ6IA0KZGZfdGVzdF9haWMgJT4lIA0KICBtdXRhdGUocHJvYl9kZWZhdWx0ID0gdGVzdF9wcmVkLCANCiAgICAgICAgIEJBRCA9IGNhc2Vfd2hlbihCQUQgPT0gMSB+ICJEZWZhdWx0IiwgVFJVRSB+ICJOb25EZWZhdWx0IikpIC0+IHByb2JfZGVmYXVsdF9kZiANCg0KIyBTaG93IHNvbWUgc3RhdGlzdGljczogDQoNCnByb2JfZGVmYXVsdF9kZiAlPiUgDQogIGdyb3VwX2J5KEJBRCkgJT4lIA0KICBzdW1tYXJpc2VfZWFjaChmdW5zKG1pbiwgbWF4LCBtZWRpYW4sIG1lYW4sIG4oKSksIHByb2JfZGVmYXVsdCkgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDQpfSkgJT4lIA0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJUYWJsZSAzOiBQcm9iYWJpbGl0aWVzIG9mIERlZmF1bHQgYnkgR3JvdXAgZm9yIFRlc3QgRGF0YSIpDQpgYGANCg0KYGBge3J9DQoNCg0KDQojIFBEIERpc3RyaWJ1dGlvbiBieSBncm91cDogDQoNCnByb2JfZGVmYXVsdF9kZiAlPiUgDQogIGdyb3VwX2J5KEJBRCkgJT4lIA0KICBzdW1tYXJpc2UodGIgPSBtZWFuKHByb2JfZGVmYXVsdCkpICU+JSANCiAgdW5ncm91cCgpIC0+IG1lYW5fcHJvYl90ZXN0DQoNCnByb2JfZGVmYXVsdF9kZiAlPiUgDQogIGdncGxvdChhZXMocHJvYl9kZWZhdWx0LCBjb2xvciA9IEJBRCwgZmlsbCA9IEJBRCkpICsgDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fcHJvYl90ZXN0JHRiWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fcHJvYl90ZXN0JHRiWzJdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIpICsgDQogIGdlb21fdGV4dChhZXMoeCA9IDAuNDQsIHkgPSA5LjIsIGxhYmVsID0gbWVhbl9wcm9iX3Rlc3QkdGJbMV0gJT4lIHJvdW5kKDQpKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDQpICsgDQogIGdlb21fdGV4dChhZXMoeCA9IDAuMSArIDAuMDcsIHkgPSA5LjIsIGxhYmVsID0gbWVhbl9wcm9iX3Rlc3QkdGJbMl0gJT4lIHJvdW5kKDQpKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSA0KSArIA0KICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44OCwgMC44KSkgKyANCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMSwgMSwgMSksICJjbSIpKSArIA0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiRmlndXJlIDQ6IFBEIERpc3RyaWJ1dGlvbiBieSB0d28gQ3JlZGl0IEdyb3VwcyBmb3IgVGVzdCBEYXRhIikgDQoNCmBgYA0KDQoNCiMjIyBTZWFyY2ggT3B0aW1hbCBUaHJlc2hvbGQgb2YgUHJvYmFiaWxpdHkgdGhhdCBtYXhpbWl6ZSBBY2N1cmFjeQ0KDQpBY2N1cmFjeSBjcml0ZXJpb24gaXMgd2lkZWx5IHVzZWQgZm9yIGV2YWx1YXRpbmcgbW9kZWwgcGVyZm9ybWFuY2UgaW4gY29udGV4dCBvZiBjcmVkaXQgc2NvcmluZy4gSG93ZXZlciB0aGlzIG1lYXN1cmUgaXMgdG90YWxseSBhZmZlY3RlZCBieSB0aHJlc2hvbGQgb2YgcHJvYmFiaWxpdHkgdGhhdCB3ZSBzZWxlY3QgZm9yIGNsYXNzaWZpZXIuIEluIHRoaXMgc2VjdGlvbiBJIHdpbGwgcHJlc2VudCBhcHByb2FjaCB0byBmaW5kaW5nIG9wdGltYWwgdGhyZXNob2xkIHRoYXQgbWF4aW1pemVzIGFjY3VyYWN5IGJ5IHVzaW5nIHNpbXVsYXRpb24gbWV0aG9kLiANCg0KYGBge3J9DQoNCiMgRnVuY3Rpb24gY2FsY3VsYXRlcyBtb2RlbCBjcml0ZXJpYSBmb3IgTG9naXN0aWMgQ2xhc3NpZmllcjogDQpsaWJyYXJ5KGNhcmV0KQ0KDQpldmFsX2Z1biA8LSBmdW5jdGlvbih0aHJlKSB7DQogIGxhcHBseSgxOjMwLCBmdW5jdGlvbih4KSB7DQogICAgDQogICAgc2V0LnNlZWQoeCkNCiAgICBpZCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkZl90ZXN0X2FpYyRCQUQsIHAgPSAxMDAwIC8gbnJvdyhkZl90ZXN0X2FpYyksIGxpc3QgPSBGQUxTRSkNCiAgICANCiAgICB0ZXN0X2RmX21pbmkgPC0gZGZfdGVzdF9haWNbaWQsIF0NCiAgICANCiAgICBkZl90ZXN0X3dvZV9taW5pIDwtIHdvZWJpbl9wbHkodGVzdF9kZl9taW5pLCBiaW5zX3ZhcikNCiAgICB0ZXN0X3ByZWQgPC0gcHJlZGljdChteV9sb2dpc3RpYywgZGZfdGVzdF93b2VfbWluaSwgdHlwZSA9ICJyZXNwb25zZSIpDQogICAgDQogICAgcHJlZGljdCA8LSBjYXNlX3doZW4odGVzdF9wcmVkID49IHRocmUgfiAiRGVmYXVsdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdGVzdF9wcmVkIDwgdGhyZSB+ICJOb25EZWZhdWx0IikNCiAgICANCiAgICBjbSA8LSBjb25mdXNpb25NYXRyaXgoY2FzZV93aGVuKHRlc3RfZGZfbWluaSRCQUQgPT0gMSB+ICJEZWZhdWx0IiwgVFJVRSB+ICJOb25EZWZhdWx0IikgJT4lIGFzLmZhY3RvcigpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdCAlPiUgYXMuZmFjdG9yKCkpDQogICAgDQogICAgYmdfZ2cgPC0gY20kdGFibGUgJT4lIA0KICAgICAgYXMudmVjdG9yKCkgJT4lIA0KICAgICAgbWF0cml4KG5jb2wgPSA0KSAlPiUgDQogICAgICBhcy5kYXRhLmZyYW1lKCkNCiAgICANCiAgICBuYW1lcyhiZ19nZykgPC0gYygiQkIiLCAiR0IiLCAiQkciLCAiR0ciKQ0KICAgIHJlc3VsdHMgPC0gYyhjbSRvdmVyYWxsLCBjbSRieUNsYXNzKSANCiAgICANCiAgICBuYW1lX3Jlc3VsdHMgPC0gcmVzdWx0cyAlPiUgDQogICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgcm93Lm5hbWVzKCkNCiAgICANCiAgICByZXN1bHRzICU+JSANCiAgICAgIGFzLnZlY3RvcigpICU+JSANCiAgICAgIG1hdHJpeChuY29sID0gMTgpICU+JSANCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAtPiBhbGxfZGYNCiAgICANCiAgICBuYW1lcyhhbGxfZGYpIDwtIG5hbWVfcmVzdWx0cw0KICAgIGFsbF9kZiA8LSBiaW5kX2NvbHMoYWxsX2RmLCBiZ19nZykNCiAgICByZXR1cm4oYWxsX2RmKQ0KICB9KQ0KfQ0KDQojIFVzZSBhYm92ZSBmdW5jdGlvbiAobWF5IGJlIGEgdGltZS1jb25zdW1pbmcgcHJvY2Vzcyk6IA0Kc3lzdGVtLnRpbWUoY29tcGFyZWRfZGYgPC0gbGFwcGx5KHNlcSgwLjEsIDAuOSwgYnkgPSAwLjA1KSwgZXZhbF9mdW4pKQ0KDQpgYGANCg0KYGBge3J9DQoNCg0KIyBDb252ZXJ0IHRvIGRhdGEgZnJhbWU6IA0KY29tcGFyZWRfZGYgPC0gZG8uY2FsbCgiYmluZF9yb3dzIiwgY29tcGFyZWRfZGYpIA0KDQpjb21wYXJlZF9kZiAlPD4lIA0KICBtdXRhdGUoVGhyZXNob2xkID0gbGFwcGx5KHNlcSgwLjEsIDAuOSwgYnkgPSAwLjA1KSwgZnVuY3Rpb24oeCkge3JlcCh4LCAzMCl9KSAlPiUgdW5saXN0KCkpDQoNCm5hbWVzKGNvbXBhcmVkX2RmKSA8LSBuYW1lcyhjb21wYXJlZF9kZikgJT4lIHN0cl9yZXBsYWNlX2FsbCgiICIsICIiKQ0KDQojIEF2ZXJhZ2UgQWNjdXJhY3kgdnMgVGhyZXNob2xkOiANCmNvbXBhcmVkX2RmICU+JSANCiAgZ3JvdXBfYnkoVGhyZXNob2xkKSAlPiUgDQogIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVkaWFuKSwgQWNjdXJhY3kpIC0+IGFjYw0KDQojIFRocmVzaG9sZCBtYXhpbWl6ZSBBY2N1cmFjeTogDQphY2MgJT4lIHNsaWNlKHdoaWNoLm1heCguJEFjY3VyYWN5KSkNCg0KYGBgDQpgYGB7cn0NCg0KDQojIFRocmVzaG9sZCBtYXhpbWl6ZSBBY2N1cmFjeSBmb3IgRGVmYXVsdCBjYXNlczogDQpjb21wYXJlZF9kZiAlPiUgDQogIGdyb3VwX2J5KFRocmVzaG9sZCkgJT4lIA0KICBzdW1tYXJpc2VfZWFjaChmdW5zKG1lZGlhbiksIE5lZ1ByZWRWYWx1ZSkgLT4gbm9uZGVmYXVsdF9hY2MNCg0Kbm9uZGVmYXVsdF9hY2MgJT4lIHNsaWNlKHdoaWNoLm1heCguJE5lZ1ByZWRWYWx1ZSkpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KDQoNCiMgVGhyZXNob2xkIG1heGltaXplIEFjY3VyYWN5IGZvciBOb25EZWZhdWx0IGNhc2VzOiANCg0KY29tcGFyZWRfZGYgJT4lIA0KICBncm91cF9ieShUaHJlc2hvbGQpICU+JSANCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWRpYW4pLCBQb3NQcmVkVmFsdWUpIC0+IGRlZmF1bHRfYWNjDQoNCmRlZmF1bHRfYWNjICU+JSBzbGljZSh3aGljaC5tYXgoLiRQb3NQcmVkVmFsdWUpKQ0KDQpgYGANCg0KYGBge3J9DQoNCg0KIyBWaXN1YWxpemUgb3VyIHJlc3VsdHM6IA0KY29tcGFyZWRfZGYgJT4lIA0KICBncm91cF9ieShUaHJlc2hvbGQpICU+JSANCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWRpYW4pLCBBY2N1cmFjeSwgTmVnUHJlZFZhbHVlLCBQb3NQcmVkVmFsdWUsIEthcHBhLCBTZW5zaXRpdml0eSwgU3BlY2lmaWNpdHkpICU+JSANCiAgZ2F0aGVyKE1ldHJpYywgYiwgLVRocmVzaG9sZCkgJT4lIA0KICBnZ3Bsb3QoYWVzKFRocmVzaG9sZCwgYiwgY29sb3IgPSBNZXRyaWMpKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAuMSwgMC45LCBieSA9IDAuMDUpKSArIA0KICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIGxhYnMoeSA9ICJBY2N1cmFjeSBSYXRlIiwgDQogICAgICAgdGl0bGUgPSAiRmlndXJlIDU6IFZhcmlhdGlvbiBvZiBMb2dpc3RpYyBDbGFzc2lmaWVyJ3MgTWV0cmljcyBieSBUaHJlc2hvbGQgb2YgUHJvYmFiaWxpdHkiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRhIFVzZWQ6IERlbGlucXVlbmN5IERhdGEgZm9yIDUsOTYwIEhvbWUgTG9hbnMiLCANCiAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBodHRwOi8vd3d3LmNyZWRpdHJpc2thbmFseXRpY3MubmV0L3VwbG9hZHMvMS85LzUvMS8xOTUxMTYwMS9obWVxLmNzdiIpDQoNCmBgYA0KDQpUaGVzZSByZXN1bHRzIHJldmVhbHMgdGhhdCBvcHRpbWFsIHRocmVzaG9sZCBvZiBwcm9iYWJpbGl0eSBtYXhpbWl6aW5nIEFjY3VyYWN5IGlzIDAuNS4gSG93ZXZlciBub3RlIHRoYXQgQWNjdXJhY3kgc2hvdWxkIG5vdCBiZSB0aGUgbW9zdCB2aXRhbCBnb2FsIGZvciBmb3ItcHJvZml0IGJ1c2luZXNzZXMuIA0KDQojIyMgU2VhcmNoIE9wdGltYWwgVGhyZXNob2xkIG9mIFNjb3JlIHRoYXQgbWF4aW1pemUgQWNjdXJhY3kNCg0KSW4gY2FzZSBvZiB1c2luZyBzY29yZWNhcmQgcG9pbnRzIGZvciBkZWNpc2lvbi1tYWtpbmcgKGFjY2VwdCBvciByZWplY3QgYW4gY3JlZGl0IGFwcGxpY2F0aW9uKSB3ZSBtdXN0IGRldGVybWluZSBvcHRpbWFsIHRocmVzaG9sZCAob3IgY3V0b2ZmKSBvZiBzY29yZSwgd2hpY2ggcmVwcmVzZW50cyB0aGUgbWluaW11bSBzY29yZSByZXF1aXJlZCBieSBiYW5rcyBmb3IgYWNjZXB0aW5nLiBJbiB0aGlzIHNlY3Rpb24gSSB3aWxsIHByZXNlbnQgYXBwcm9hY2ggdG8gZmluZGluZyBvcHRpbWFsIHRocmVzaG9sZCBvZiBzY29yZS4NCg0KYGBge3J9DQoNCiMgRnVuY3Rpb24gY2FsY3VsYXRlcyBtb2RlbCBtZWFzdXJlcyB3aXRoIGlucHV0IGlzIGEgc2NvcmUgc2VsZWN0ZWQ6IA0KDQp2YWxpZGF0ZV9zY29yZSA8LSBmdW5jdGlvbihzY29yZV9zZWxlY3RlZCkgew0KICBsYXBwbHkoMTozMCwgZnVuY3Rpb24oeCkgew0KICAgIA0KICAgIHNldC5zZWVkKHgpDQogICAgaWQgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGZfdGVzdF9haWMkQkFELCBwID0gMTAwMCAvIG5yb3coZGZfdGVzdF9haWMpLCBsaXN0ID0gRkFMU0UpDQogIA0KICAgIHRlc3RfZGZfbWluaSA8LSBkZl90ZXN0X2FpY1tpZCwgXQ0KICAgIA0KICAgIG15X3BvaW50c190ZXN0X21pbmkgPC0gc2NvcmVjYXJkX3BseSh0ZXN0X2RmX21pbmksIG15X2NhcmQsIG9ubHlfdG90YWxfc2NvcmUgPSBGQUxTRSwgcHJpbnRfc3RlcCA9IDApICU+JSANCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogICAgICBwdWxsKHNjb3JlKQ0KICAgIA0KICAgIGR1X2JhbyA8LSBjYXNlX3doZW4obXlfcG9pbnRzX3Rlc3RfbWluaSA+PSBzY29yZV9zZWxlY3RlZCB+ICJEZWZhdWx0IiwgDQogICAgICAgICAgICAgICAgICAgICAgICBteV9wb2ludHNfdGVzdF9taW5pIDwgc2NvcmVfc2VsZWN0ZWQgfiAiTm9uRGVmYXVsdCIpDQogICAgDQogICAgY20gPC0gY29uZnVzaW9uTWF0cml4KGNhc2Vfd2hlbih0ZXN0X2RmX21pbmkkQkFEID09IDEgfiAiRGVmYXVsdCIsIFRSVUUgfiAiTm9uRGVmYXVsdCIpICU+JSBhcy5mYWN0b3IoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGR1X2JhbyAlPiUgYXMuZmFjdG9yKCkpDQogICAgDQogICAgYmdfZ2cgPC0gY20kdGFibGUgJT4lIA0KICAgICAgYXMudmVjdG9yKCkgJT4lIA0KICAgICAgbWF0cml4KG5jb2wgPSA0KSAlPiUgDQogICAgICBhcy5kYXRhLmZyYW1lKCkNCiAgICANCiAgICBuYW1lcyhiZ19nZykgPC0gYygiQkIiLCAiR0IiLCAiQkciLCAiR0ciKQ0KICAgIHJlc3VsdHMgPC0gYyhjbSRvdmVyYWxsLCBjbSRieUNsYXNzKSANCiAgICANCiAgICBuYW1lX3Jlc3VsdHMgPC0gcmVzdWx0cyAlPiUgDQogICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgcm93Lm5hbWVzKCkNCiAgICANCiAgICByZXN1bHRzICU+JSANCiAgICAgIGFzLnZlY3RvcigpICU+JSANCiAgICAgIG1hdHJpeChuY29sID0gMTgpICU+JSANCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAtPiBhbGxfZGYNCiAgICANCiAgICBuYW1lcyhhbGxfZGYpIDwtIG5hbWVfcmVzdWx0cw0KICAgIGFsbF9kZiA8LSBiaW5kX2NvbHMoYWxsX2RmLCBiZ19nZykNCiAgICByZXR1cm4oYWxsX2RmKQ0KICB9KQ0KfQ0KDQojIFVzZSB0aGUgZnVuY3Rpb246IA0Kc3lzdGVtLnRpbWUoY29tcGFyZWRfZGZfc2NvcmUgPC0gbGFwcGx5KHNlcSg1MDAsIDcwMCwgYnkgPSAxMCksIHZhbGlkYXRlX3Njb3JlKSkNCmNvbXBhcmVkX2RmX3Njb3JlIDwtIGRvLmNhbGwoImJpbmRfcm93cyIsIGNvbXBhcmVkX2RmX3Njb3JlKQ0KDQpjb21wYXJlZF9kZl9zY29yZSAlPD4lIA0KICBtdXRhdGUoVGhyZXNob2xkID0gbGFwcGx5KHNlcSg1MDAsIDcwMCwgYnkgPSAxMCksIGZ1bmN0aW9uKHgpIHtyZXAoeCwgMzApfSkgJT4lIHVubGlzdCgpKQ0KDQpuYW1lcyhjb21wYXJlZF9kZl9zY29yZSkgPC0gbmFtZXMoY29tcGFyZWRfZGZfc2NvcmUpICU+JSBzdHJfcmVwbGFjZV9hbGwoIiAiLCAiIikNCmNvbXBhcmVkX2RmX3Njb3JlJEFjY3VyYWN5ICU+JSBzdW1tYXJ5KCkNCg0KY29tcGFyZWRfZGZfc2NvcmUgJT4lIA0KICBncm91cF9ieShUaHJlc2hvbGQpICU+JSANCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWRpYW4pLCBBY2N1cmFjeSwgTmVnUHJlZFZhbHVlLCBQb3NQcmVkVmFsdWUsIEthcHBhLCBTZW5zaXRpdml0eSwgU3BlY2lmaWNpdHkpICU+JSANCiAgZ2F0aGVyKE1ldHJpYywgYiwgLVRocmVzaG9sZCkgJT4lIA0KICBnZ3Bsb3QoYWVzKFRocmVzaG9sZCwgYiwgY29sb3IgPSBNZXRyaWMpKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDUwMCwgNzAwLCBieSA9IDUwKSkgKyANCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArIA0KICBsYWJzKHkgPSAiQWNjdXJhY3kgUmF0ZSIsIA0KICAgICAgIHRpdGxlID0gIkZpZ3VyZSA2OiBWYXJpYXRpb24gb2YgTG9naXN0aWMgQ2xhc3NpZmllcidzIE1ldHJpY3MgYnkgVGhyZXNob2xkIG9mIFNjb3JlY2FyZCIsIA0KICAgICAgIHN1YnRpdGxlID0gIkRhdGEgVXNlZDogRGVsaW5xdWVuY3kgRGF0YSBmb3IgNSw5NjAgSG9tZSBMb2FucyIsIA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IGh0dHA6Ly93d3cuY3JlZGl0cmlza2FuYWx5dGljcy5uZXQvdXBsb2Fkcy8xLzkvNS8xLzE5NTExNjAxL2htZXEuY3N2IikNCg0KYGBgDQoNCg0KIyMjIFZhcmlhYmxlIFNlbGVjdGlvbiB1c2luZyBJViBDcml0ZXJpb24NCg0KSW5zdGVhZCBvZiB1c2luZyBBSUMgYXMgYSBjcml0ZXJpb24gZm9yIHZhcmlhYmxlIHNlbGVjdGluIHdlIGNhbiB1c2UgSVYgYXMgZm9sbG93czogDQoNCg0KYGBge3J9DQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgU2NlbmFyaW8gMjogU2NvcmVjYXJkIG1vZGVsIHdpdGggdmFyaWFibGVzIHNlbGVjdGVkIGJhc2VkIG9uIElWIENyaXRlcmlvbiANCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgQ2FsY3VsYXRlIGluZm9ybWF0aW9uIHZhbHVlczogDQppbmZvX3ZhbHVlcyA8LSBpdihkZl90cmFpbiwgeSA9ICJCQUQiLCBwb3NpdGl2ZSA9ICJCQUR8MSIpDQoNCiMgU2hvdyBJVnMgYnkgZ3JhcGg6IA0KaW5mb192YWx1ZXMgJT4lIA0KICBhcnJhbmdlKGluZm9fdmFsdWUpICU+JSANCiAgbXV0YXRlKGluZm9fdmFsdWUgPSByb3VuZChpbmZvX3ZhbHVlLCAzKSwgdmFyaWFibGUgPSBmYWN0b3IodmFyaWFibGUsIGxldmVscyA9IHZhcmlhYmxlKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHZhcmlhYmxlLCBpbmZvX3ZhbHVlKSkgKyANCiAgZ2VvbV9jb2woZmlsbCA9ICIjMzc3ZWI4IikgKyANCiAgY29vcmRfZmxpcCgpICsgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBpbmZvX3ZhbHVlKSwgaGp1c3QgPSAtLjEsIHNpemUgPSA1LCBjb2xvciA9ICIjMzc3ZWI4IikgKyANCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNzogSW5mb3JtYXRpb24gVmFsdWUgKElWKSBmb3IgQWxsIFZhcmlhYmxlcyIsIA0KICAgICAgIHggPSBOVUxMLCB5ID0gIkluZm9ybWF0aW9uIFZhbHVlIChJVikiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAwLjkpKSArIA0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEsIDEsIDEsIDEpLCAiY20iKSkgDQpgYGANCg0KYGBge3J9DQoNCg0KDQojIFdlIG9ubHkgdXNlIHZhcmlhYmxlcyB3aXRoIElWID49IDAuMSBmb3IgdHJhaW5pbmcgTG9naXN0aWMgUmVncmVzc2lvbjogDQp2YXJpYWJsZXNfc2VsZWN0ZWRfaXYgPC0gaW5mb192YWx1ZXMgJT4lIA0KICBmaWx0ZXIoaW5mb192YWx1ZSA+PSAwLjEpICU+JSANCiAgcHVsbCgxKQ0KDQojIERhdGEgZnJhbWUgZm9yIHRyYWluaW5nIExvZ2lzdGljIFJlZ3Jlc3Npb246IA0KDQpkZl90cmFpbl9pdiA8LSBkZl90cmFpbiAlPiUgc2VsZWN0KHZhcmlhYmxlc19zZWxlY3RlZF9pdiwgIkJBRCIpDQoNCiMgQmluIG91ciBkYXRhIHNldCB0aGF0IHdpbGwgYmUgdXNlZCBsYXRlciBmb3IgTG9naXN0aWMgUmVncmVzc2lvbjogDQpiaW5zX3ZhciA8LSB3b2ViaW4oZGZfdHJhaW5faXYsIHkgPSAiQkFEIiwgbm9fY29yZXMgPSA0LCBwb3NpdGl2ZSA9ICJCQUR8MSIpDQoNCiMgQ3JlYXRlcyBhIGRhdGEgZnJhbWUgb2YgYmlubmVkIHZhcmlhYmxlcyBmb3IgTG9naXN0aWMgUmVncmVzc2lvbjogDQpkZl90cmFpbl93b2UyIDwtIHdvZWJpbl9wbHkoZGZfdHJhaW5faXYsIGJpbnNfdmFyKQ0KDQojIExvZ2lzdGljIFJlZ3Jlc3Npb246DQpteV9sb2dpc3RpYzIgPC0gZ2xtKEJBRCB+IC4sIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gZGZfdHJhaW5fd29lMikNCg0KIyBTaG93IHJlc3VsdHM6IA0KbXlfbG9naXN0aWMyICU+JSBzdW1tYXJ5KCkNCg0KIyBDb252ZXJ0IHRvIGJpbm5lZCBkYXRhIGZyYW1lIGZvciB0ZXN0IGRhdGE6IA0KZGZfdGVzdF9pdiA8LSBkZl90ZXN0ICU+JSBzZWxlY3QobmFtZXMoZGZfdHJhaW5faXYpKQ0KZGZfdGVzdF93b2UyIDwtIHdvZWJpbl9wbHkoZGZfdGVzdF9pdiwgYmluc192YXIpDQoNCiMgQ2FjdWxhdGUgcHJvYmFiaWxpeSBvZiBkZWZhdWx0IChQRCkgZm9yIG9ic2VydmF0aW9ucyBiZWxvbmdpbmcgdGVzdCBkYXRhOiANCnRlc3RfcHJlZDIgPC0gcHJlZGljdChteV9sb2dpc3RpYzIsIGRmX3Rlc3Rfd29lMiwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgTW9kZWwgUGVyZm9ybWFuY2UgZm9yIHRlc3QgZGF0YTogDQpwZXJmX2V2YShkZl90ZXN0X2l2JEJBRCwgdGVzdF9wcmVkMiwgDQogICAgICAgICB0eXBlID0gYygia3MiLCAibGlmdCIsICJyb2MiLCAicHIiKSwgDQogICAgICAgICB0aXRsZSA9ICJUZXN0IERhdGEiKQ0KDQpgYGANCg0KV2UgY2FuIGFsc28gY2FsY3VsYXRlIHNjb3JlY2FyZCBwb2ludHMgZm9yIGFsbCBjcmVkaXQgYXBwbGljYXRpb25zIGJ5IHVzaW5nIHRoaXMgc2NvcmVjYXJkIG1vZGVsIGFzIGZvbGxvd3M6IA0KDQpgYGB7cn0NCg0KIyBDYWxjdWxhdGUgc2NvcmVjYXJkIHNjb3JlcyBmb3IgdmFyaWFibGVzIGJhc2VkIG9uIHRoZSByZXN1bHRzIGZyb20gd29lYmluIGFuZCBnbG06IA0KbXlfY2FyZDIgPC0gc2NvcmVjYXJkKGJpbnNfdmFyLCBteV9sb2dpc3RpYzIsIHBvaW50czAgPSA2MDAsIG9kZHMwID0gMS8xOSwgcGRvID0gNTApDQoNCiMgU2NvcmVjYXJkIFBvaW50cyBmb3IgdGVzdCBkYXRhIHNldDogDQpteV9wb2ludHNfdGVzdCA8LSBzY29yZWNhcmRfcGx5KGRmX3Rlc3RfaXYsIG15X2NhcmQyLCBwcmludF9zdGVwID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHlfdG90YWxfc2NvcmUgPSBGQUxTRSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KDQpkZl9zY29yZWRfdGVzdCA8LSBkZl90ZXN0X2l2ICU+JSANCiAgbXV0YXRlKFNDT1JFID0gbXlfcG9pbnRzX3Rlc3Qkc2NvcmUpICU+JSANCiAgbXV0YXRlKEJBRCA9IGNhc2Vfd2hlbihCQUQgPT0gMSB+ICJEZWZhdWx0IiwgVFJVRSB+ICJOb25EZWZhdWx0IikpIA0KDQpkZl9zY29yZWRfdGVzdCAlPiUgDQogIGdyb3VwX2J5KEJBRCkgJT4lIA0KICBzdW1tYXJpc2VfZWFjaChmdW5zKG1pbiwgbWF4LCBtZWRpYW4sIG1lYW4sIG4oKSksIFNDT1JFKSAlPiUgDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7cm91bmQoeCwgMCl9KSAlPiUgDQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIlRhYmxlIDQ6IFNjb3JlY2FkIFBvaW50cyBieSBHcm91cCBmb3IgVGVzdCBEYXRhIChTZWxlY3Rpb24gYmFzZWQgb24gSVYpIikNCg0KZGZfc2NvcmVkX3Rlc3QgJT4lIA0KICBncm91cF9ieShCQUQpICU+JSANCiAgc3VtbWFyaXNlKHRiID0gbWVhbihTQ09SRSkpICU+JSANCiAgdW5ncm91cCgpIC0+IG1lYW5fc2NvcmVfdGVzdA0KDQpkZl9zY29yZWRfdGVzdCAlPiUgDQogIGdncGxvdChhZXMoU0NPUkUsIGNvbG9yID0gQkFELCBmaWxsID0gQkFEKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9zY29yZV90ZXN0JHRiWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsyXSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArIA0KICBnZW9tX3RleHQoYWVzKHggPSA0NTAgLSAxMCwgeSA9IDAuMDA1LCBsYWJlbCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsxXSAlPiUgcm91bmQoMCkpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gNCkgKyANCiAgZ2VvbV90ZXh0KGFlcyh4ID0gNTMwICsgMjAsIHkgPSAwLjAwNSwgbGFiZWwgPSBtZWFuX3Njb3JlX3Rlc3QkdGJbMl0gJT4lIHJvdW5kKDApKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSA0KSArIA0KICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjgpKSArIA0KICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAxLCAxLCAxKSwgImNtIikpICsgDQogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCB0aXRsZSA9ICJGaWd1cmUgNzogU2NvcmVjYXJkIERpc3RyaWJ1dGlvbiBieSB0d28gQ3JlZGl0IEdyb3VwcyBmb3IgVGVzdCBEYXRhIiwgDQogICAgICAgc3VidGl0bGUgPSAiVmFyaWFibGUgU2VsZWN0aW9uIFVzZWQ6IElWIENyaXRlcmlvbiIpDQpgYGANCg0KUmVzdWx0cyBzaG93IHRoYXQgdGhpcyBhcHByb2FjaCB0byB2YXJpYWJsZSBzZWxlY3Rpb24gcmVzdWx0cyBtb2RlbCBwZXJmb3JtYW5jZSBpcyBwb29yIGNvbXBhcmVkIHRvIHRoZSBwcmV2aW91cyBvbmUgKHNlbGVjdGlvbiBiYXNlZCBvbiBBSUMpLiANCg0KDQojIyMgQ2F1dGlvbjogQmUgQXdhcmUgT2YgTGltaXRhdGlvbnMNCg0KQWx0aG91Z2ggY3JlZGl0IHNjb3Jpbmcgc3lzdGVtcyBhcmUgYmVpbmcgaW1wbGVtZW50ZWQgYW5kIHVzZWQgYnkgbW9zdCBiYW5rcw0Kbm93YWRheXMsIHRoZXkgZG8gZmFjZSBhIG51bWJlciBvZiBsaW1pdGF0aW9ucy4gQSBmaXJzdCBsaW1pdGF0aW9uIGNvbmNlcm5zIHRoZSBkYXRhDQp0aGF0IGlzIHVzZWQgdG8gZXN0aW1hdGUgY3JlZGl0IHNjb3JpbmcgbW9kZWxzLiBTaW5jZSBkYXRhIGlzIHRoZSBtYWpvciwgYW5kIGluIG1vc3QNCmNhc2VzIHRoZSBvbmx5LCBpbmdyZWRpZW50IHRvIGJ1aWxkIHRoZXNlIG1vZGVscywgaXRzIHF1YWxpdHkgYW5kIHByZWRpY3RpdmUgYWJpbGl0eSBpcw0Ka2V5IHRvIHRoZSBtb2RlbHPigJkgc3VjY2Vzcy4NCg0KVGhlIHF1YWxpdHkgb2YgdGhlIGRhdGEgcmVmZXJzLCBmb3IgZXhhbXBsZSwgdG8gdGhlIG51bS0NCmJlciBvZiBtaXNzaW5nIHZhbHVlcyBhbmQgb3V0bGllcnMsIGFuZCB0byB0aGUgcmVjZW5jeSBhbmQgcmVwcmVzZW50YXRpdml0eSBvZiB0aGUNCmRhdGEuIERhdGEgcXVhbGl0eSBpc3N1ZXMgY2FuIGJlIGRpZmZpY3VsdCB0byBkZXRlY3Qgd2l0aG91dCBzcGVjaWZpYyBkb21haW4ga25vd2xlZGdlLCBidXQgaGF2ZSBhbiBpbXBvcnRhbnQgaW1wYWN0IG9uIHRoZSBzY29yZWNhcmQgZGV2ZWxvcG1lbnQgYW5kIHJlc3VsdGluZyByaXNrDQptZWFzdXJlcy4gVGhlIGF2YWlsYWJpbGl0eSBvZiBoaWdoLXF1YWxpdHkgZGF0YSBpcyBhIHZlcnkgaW1wb3J0YW50IHByZXJlcXVpc2l0ZSBmb3INCmJ1aWxkaW5nIGdvb2QgY3JlZGl0IHNjb3JpbmcgbW9kZWxzLiBIb3dldmVyLCBub3Qgb25seSBkb2VzIHRoZSBkYXRhIG5lZWQgdG8gYmUgb2YNCmhpZ2ggcXVhbGl0eSwgYnV0IGl0IHNob3VsZCBiZSBwcmVkaWN0aXZlIGFzIHdlbGwsIGluIHRoZSBzZW5zZSB0aGF0IHRoZSBjYXB0dXJlZCBjaGFyYWN0ZXJpc3RpY3MgYXJlIHJlbGF0ZWQgdG8gdGhlIGN1c3RvbWVy4oCZcyBsaWtlbGlob29kIG9mIGRlZmF1bHRpbmcuDQoNCkluIGFkZGl0aW9uLCBiZWZvcmUgY29uc3RydWN0aW5nIGEgc2NvcmVjYXJkIG1vZGVsLCB3ZSBuZWVkIHRvIHRob3JvdWdobHkgcmVmbGVjdCBvbiB3aHkgYSBjdXN0b21lciBkZWZhdWx0cyBhbmQgd2hpY2gNCmNoYXJhY3RlcmlzdGljcyBjb3VsZCBwb3RlbnRpYWxseSBiZSByZWxhdGVkIHRvIHRoaXMuIEN1c3RvbWVycyBtYXkgZGVmYXVsdCBiZWNhdXNlIG9mDQp1bmtub3duIHJlYXNvbnMgb3IgaW5mb3JtYXRpb24gbm90IGF2YWlsYWJsZSB0byB0aGUgZmluYW5jaWFsIGluc3RpdHV0aW9uLCB0aGVyZWJ5DQpwb3NpbmcgYW5vdGhlciBsaW1pdGF0aW9uIHRvIHRoZSBwZXJmb3JtYW5jZSBvZiBjcmVkaXQgc2NvcmluZyBtb2RlbHMuIFRoZSBzdGF0aXN0aWNhbA0KdGVjaG5pcXVlcyB1c2VkIGluIGRldmVsb3BpbmcgY3JlZGl0IHNjb3JpbmcgbW9kZWxzIHR5cGljYWxseSBhc3N1bWUgYSBkYXRhIHNldCBvZg0Kc3VmZmljaWVudCBzaXplIGNvbnRhaW5pbmcgZW5vdWdoIGRlZmF1bHRzLiBUaGlzIG1heSBub3QgYWx3YXlzIGJlIHRoZSBjYXNlIGZvciBzcGVjaWZpYyB0eXBlcyBvZiBwb3J0Zm9saW9zIHdoZXJlIG9ubHkgbGltaXRlZCBkYXRhIGlzIGF2YWlsYWJsZSwgb3Igb25seSBhIGxvdyBudW1iZXIgb2YNCmRlZmF1bHRzIGlzIG9ic2VydmVkLiBGb3IgdGhlc2UgdHlwZXMgb2YgcG9ydGZvbGlvcywgb25lIG1heSBoYXZlIHRvIHJlbHkgb24gYWx0ZXJuYXRpdmUNCnJpc2sgYXNzZXNzbWVudCBtZXRob2RzIHVzaW5nLCBmb3IgZXhhbXBsZSwgZXhwZXJ0IGp1ZGdtZW50IGJhc2VkIG9uIHRoZSBmaXZlIENzLA0KYXMgZGlzY3Vzc2VkIGVhcmxpZXIuDQoNCiMjIyBUaGFuayB5b3UgbWVzc2FnZQ0KDQpJIHdvb3VsZCBsaWtlIHRvIHRoYW5rIE1yLkR1bmcgZm9yIGhpcyBwcmFpc2V3b3J0aHkgd29yay4gSGlzIHBvc3RzIGFuZCByZWZlcmVuY2VzIGhlbHBlZCBtZSBpbXByb3ZlIG15IHNraWxscyBhbmQga25vd2xlZGdlIGEgbG90IHRoYXQgSSBjYW4gbm90IGV4cHJlc3MgYnkgdGhlc2Ugc2ltcGxlIHdvcmRzLg0KDQpIZSdzIGRlZmVuaXRlbHkgbXkgdGVhY2hlciENCg0KIyMjIFJlZmVyZW5jZXMNCg0KMS4gTWFydGVucywgRC4sIEIuIEJhZXNlbnMsIFQuIFZhbiBHZXN0ZWwsIGFuZCBKLiBWYW50aGllbmVuLiAyMDA3LiDigJxDb21wcmVoZW5zaWJsZSBDcmVkaXQgU2NvcmluZyBNb2RlbHMgVXNpbmcgUnVsZSBFeHRyYWN0aW9uIGZyb20gU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMu4oCdIEV1cm9wZWFuIEpvdXJuYWwgb2YgT3BlcmF0aW9uYWwgUmVzZWFyY2ggMTgzOjE0NjbigJMxNDc2Lg0KDQoyLiBCYWVzZW5zLCBCLiwgUm9lc2NoLCBELiwgJiBTY2hldWxlLCBILiAoMjAxNikuIENyZWRpdCByaXNrIGFuYWx5dGljczogTWVhc3VyZW1lbnQgdGVjaG5pcXVlcywgYXBwbGljYXRpb25zLCBhbmQgZXhhbXBsZXMgaW4gU0FTLiBKb2huIFdpbGV5ICYgU29ucy4NCg0KMy4gU2lkZGlxaSwgTi4gKDIwMTIpLiBDcmVkaXQgcmlzayBzY29yZWNhcmRzOiBkZXZlbG9waW5nIGFuZCBpbXBsZW1lbnRpbmcgaW50ZWxsaWdlbnQgY3JlZGl0IHNjb3JpbmcuIEpvaG4gV2lsZXkgJiBTb25zLg0KDQo0LiBBbmRlcnNvbiBSICgyMDA3KTogVGhlIENyZWRpdCBTY29yaW5nIFRvb2xraXQ6IFRoZW9yeSBhbmQgUHJhY3RpY2UgZm9yIFJldGFpbCBDcmVkaXQgUmlzayBNYW5hZ2VtZW50IGFuZCBEZWNpc2lvbiBBdXRvbWF0aW9uLiBPeGZvcmQsIE94Zm9yZCBVbml2ZXJzaXR5IFByZXNzLiANCg0KNS4gSGFuZCBESiwgSGVubGV5IFdFICgxOTk3KTogU3RhdGlzdGljYWwgQ2xhc3NpZmljYXRpb24gTWV0aG9kcyBpbiBDb25zdW1lciBDcmVkaXQgU2NvcmluZzoNCmEgcmV2aWV3LiBKb3VybmFsLiBvZiB0aGUgUm95YWwgU3RhdGlzdGljYWwgU29jaWV0eSwgU2VyaWVzIEEsIDE2MCgzKTo1MjPigJM1NDEuIA0KDQo2LiBUaG9tYXMgTEMgKDIwMDApOiBBIHN1cnZleSBvZiBjcmVkaXQgYW5kIGJlaGF2aW91cmFsIHNjb3Jpbmc6IGZvcmVjYXN0aW5nIGZpbmFuY2lhbCByaXNrIG9mIGxlbmRpbmcgdG8gY29uc3VtZXJzLiBJbnRlcm5hdGlvbmFsIEpvdXJuYWwgb2YgRm9yZWNhc3RpbmcsIDE2KDIpOjE0OeKAkzE3MiAuDQoNCjcuIFRob21hcyBMQyAoMjAwOSk6IENvbnN1bWVyIENyZWRpdCBNb2RlbHM6IFByaWNpbmcsIFByb2ZpdCwgYW5kIFBvcnRmb2xpby4gT3hmb3JkLCBPeGZvcmQgVW5pdmVyc2l0eSBQcmVzcy4gDQoNCjguIENyb29rIEpOLCBFZGVsbWFuIERCLCBUaG9tYXMgTEMgKDIwMDcpOiBSZWNlbnQgZGV2ZWxvcG1lbnRzIGluIGNvbnN1bWVyIGNyZWRpdCByaXNrIGFzc2Vzc21lbnQuIEV1cm9wZWFuIEpvdXJuYWwgb2YgT3BlcmF0aW9uYWwgUmVzZWFyY2gsIDE4MygzKToxNDQ34oCTMTQ2NS4gDQoNCjkuIFZhbiBHZXN0ZWwsIFQuLCBCLiBCYWVzZW5zLCBQLiBWYW4gRGlqY2tlLCBKLiBTdXlrZW5zLCBKLiBHYXJjaWEsIGFuZCBULiBBbGRlcndlaXJlbGQuIDIwMDUuIOKAnExpbmVhciBhbmQgTm9ubGluZWFyIENyZWRpdCBTY29yaW5nIGJ5IENvbWJpbmluZyBMb2dpc3RpYyBSZWdyZXNzaW9uIGFuZCBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcy7igJ0gSm91cm5hbCBvZiBDcmVkaXQgUmlzayAxLCBuby4gNC4NCg0KMTAuIGh0dHBzOi8vZ2l0aHViLmNvbS9jaGlkdW5na3INCg==