Combined Data
loan01 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational01.csv", header = TRUE)[, -1]
loan02 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational02.csv", header = TRUE)[, -1]
loan03 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational03.csv", header = TRUE)[, -1]
loan04 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational04.csv", header = TRUE)[, -1]
loan05 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational05.csv", header = TRUE)[, -1]
loan06 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational06.csv", header = TRUE)[, -1]
loan07 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational07.csv", header = TRUE)[, -1]
loan08 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational08.csv", header = TRUE)[, -1]
loan09 = read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/w06-SBAnational09.csv", header = TRUE)[, -1]
loan = rbind(loan01, loan02, loan03, loan04, loan05, loan06, loan07, loan08, loan09)
# dim(bankLoan)
#names(bankLoan)
Mis_Status Missing
loan <- loan[!is.na(loan$MIS_Status), ]
Mis_Status is the status of the persons loan. This is one of the most
important variables in our dataset. It ensures us that when we do our
analysis we have a complete observation telling us if the loan was
approved, paid, or defaulted. So we remove any observations with loan
status missing for clarity.
Convert Variables
currency_vars <- c("DisbursementGross", "BalanceGross", "ChgOffPrinGr", "GrAppv", "SBA_Appv")
loan[currency_vars] <- lapply(loan[currency_vars], function(x) as.numeric(gsub("[$,]", "", x)))
Since DisbursementGross, BalanceGross, ChgOffPrinGr, GrAppv, and
SBA_Appv are important measures for our analysis we want to be able to
use them more methodically. So we convert these categorical variables to
numeric variables and remove any character values. Working with numbers
as opposed to charterers is better for statistical analysis,
visualization, and overall modeling. ## Categorical Variable rework
loan <- loan %>%
mutate(BankRegion = case_when(
BankState %in% c("CT", "ME", "MA", "NH", "NJ", "NY", "PA", "RI", "VT") ~ "Northeast",
BankState %in% c("IL", "IN", "IA", "KS", "MI", "MN", "MO", "NE", "ND", "OH", "SD", "WI") ~ "Midwest",
BankState %in% c("AL", "AR", "DE", "DC", "FL", "GA", "KY", "LA", "MD", "MS", "NC", "OK", "SC", "TN", "TX", "VA", "WV") ~ "South",
BankState %in% c("AK", "AZ", "CA", "CO", "HI", "ID", "MT", "NV", "NM", "OR", "UT", "WA", "WY") ~ "West",
TRUE ~ "Unknown"
))
table(loan$BankRegion)
Midwest Northeast South Unknown West
269885 150560 275751 1730 201238
I decided to make 4 regional categories of the states. These
categories being Northeast, Midwest, South, West and also an unknown. By
doing this we can see if there are any patterns for any specific region.
We can use this to compare approval rates, default rates, or average
loan amounts by region. This also makes it easier for data visualization
to again see if there is any disparities by region.
Default Rates based on
Region
default_rates <- loan %>%
group_by(BankRegion) %>%
summarise(
Total_Loans = n(),
Defaults = sum(MIS_Status == "CHGOFF", na.rm = TRUE),
Default_Rate = Defaults / Total_Loans * 100
)
print(default_rates)
# A tibble: 5 × 4
BankRegion Total_Loans Defaults Default_Rate
<chr> <int> <int> <dbl>
1 Midwest 269885 42537 15.8
2 Northeast 150560 21021 14.0
3 South 275751 58751 21.3
4 Unknown 1730 106 6.13
5 West 201238 35143 17.5
This code calculates the default rate of SBA-backed loans for each
region. With this code it helps us to compare which regions have higher
or lower default rates. It also gives insight into regional economic
trends and lending practices. We see that in the South the default rate
is the highest at 21% meaning that more loans go unpaid here than in any
other region.
Discretize GrApprv
loan <- loan %>%
mutate(GrAppv_Cat = cut(GrAppv,
breaks = quantile(GrAppv, probs = seq(0, 1, by = 0.2), na.rm = TRUE),
include.lowest = TRUE,
labels = c("Very Low", "Low", "Medium", "High", "Very High")))
table(loan$GrAppv_Cat)
Very Low Low Medium High Very High
180997 179102 179575 179677 179813
In the code above we categorize the SBA guaranteed approval amount
into five groups. By categorizing and making this variable into small
intervals we convert it from a continuous variable we now have a
discretized variable. Discretized variables are easier to interpret. We
see from the output above that they are evenly distributed meaning our
approval spans a wide range of amounts.
Study Population
study.pop <- loan %>%
filter(BankRegion != "Unknown")
kable(t(table(study.pop$BankRegion)))
269885 |
150560 |
275751 |
201238 |
For the study population we remove the unknown group because there is
only 1730 observations which is too small and does not match up with our
other categories. We also cannot draw conclusions about the region if we
do not know where these observations are coming from. So now we have
defined our study population.
Sample Calculation
std_dev <- sd(loan$SBA_Appv, na.rm = TRUE)
Z_alpha <- 1.96
Z_beta <- 0.84
delta <- 5000
n <- (2 * (Z_alpha + Z_beta)^2 * std_dev^2) / delta^2
n_rounded <- ceiling(n)
cat("The required sample size per group is:", n_rounded, "\n")
The required sample size per group is: 32724
After making a calculation for our sample we get a sample size of
32724 which is exactly where we need the size to be. It is less than 5%
of our dataset but is also a relatively big number.
Simple Random
Sample
study.pop$sampling.frame = 1:length(study.pop$GrAppv)
sampled.list = sample(1:length(study.pop$GrAppv), 32724)
SRS.sample = study.pop[sampled.list,]
dimension.SRS = dim(SRS.sample)
names(dimension.SRS) = c("Size", "Var.count")
kable(t(dimension.SRS))
We performed a simple random sample. First we assigned a unique index
to each observation using sampling.frame. Then we randomly selected
32,724 observations from the dataset without replacement. Finally we
extract the sampled observations into SRS.sample to make a table.
Systematic
Sampling
jump.size = dim(study.pop)[1]%/% 32724
rand.starting.pt=sample(1:jump.size,1)
sampling.id = seq(rand.starting.pt, dim(study.pop)[1], jump.size)
sys.sample=study.pop[sampling.id,]
sys.Sample.dim = dim(sys.sample)
names(sys.Sample.dim) = c("Size", "Var.count")
kable(t(sys.Sample.dim))
We performed a systematic sample. First we calculated the jump size
by dividing the total number of observations by 32,724, determining the
interval at which samples will be selected. Then we randomly picked a
starting point within the first interval. Using this starting point we
generate a sequence of indices at regular intervals to select
observations.
Stratefied
Sampling
freq.table = table(study.pop$BankRegion)
rel.freq = freq.table/sum(freq.table)
strata.size = round(rel.freq*32724)
strata.names=names(strata.size)
kable(t(strata.size))
We performed a Stratified Sampling by first creating a frequency
table of the BankRegion variable, which represents different categories.
Then we calculate the relative frequency of each category by dividing
the counts by the total number of observations. Next we determine the
sample size for each categories by multiplying the relative frequencies
by 32,724 and rounding the values.
strata.sample = study.pop[0,]
for (i in 1:length(strata.names)) {
ith.strata.names = strata.names[i]
ith.strata.size = strata.size[i]
ith.sampling.id = which(study.pop$BankRegion == ith.strata.names)
if (length(ith.sampling.id) > 0 && ith.strata.size > 0) {
ith.strata = study.pop[ith.sampling.id,]
ith.strata$add.id = 1:nrow(ith.strata)
ith.sampling.id = sample(1:nrow(ith.strata), min(ith.strata.size, nrow(ith.strata)))
ith.sample = ith.strata[ith.sampling.id, ]
strata.sample = rbind(strata.sample, ith.sample)
}
}
strat.sample.final = strata.sample
kable(head(strat.sample.final))
545534 |
5363724001 |
GOOD FEET STORE |
DENVER |
CO |
80231 |
WELLS FARGO BANK NATL ASSOC |
MN |
448210 |
26-Apr-02 |
2002 |
72 |
6 |
1 |
4 |
6 |
1 |
1 |
0 |
N |
|
30-Jun-02 |
130000 |
0 |
P I F |
0 |
130000 |
110500 |
Midwest |
High |
545534 |
168642 |
838741 |
9008854009 |
JERRE ALLYN LLC |
DENVER |
CO |
80202 |
WELLS FARGO BANK NATL ASSOC |
SD |
522310 |
27-Jul-05 |
2005 |
12 |
2 |
1 |
2 |
2 |
1 |
1 |
Y |
N |
6-Feb-10 |
31-Aug-05 |
60103 |
0 |
CHGOFF |
25596 |
30000 |
15000 |
Midwest |
Low |
838741 |
250316 |
699908 |
7286233005 |
SUNCREST EXTERMINATING, INC. |
ANAHEIM |
CA |
92806 |
WELLS FARGO BANK NATL ASSOC |
SD |
0 |
13-Jul-94 |
1994 |
300 |
32 |
1 |
0 |
0 |
1 |
0 |
N |
N |
|
31-Oct-94 |
440000 |
0 |
P I F |
0 |
440000 |
308000 |
Midwest |
Very High |
699908 |
208883 |
815096 |
8754793005 |
BOAT LIFT MARINE CENTER |
OSAGE BEACH |
MO |
65065 |
CENTRAL BK OF LAKE OF OZARKS |
MO |
333923 |
31-Oct-95 |
1996 |
120 |
6 |
1 |
0 |
0 |
1 |
0 |
N |
N |
|
31-Jan-96 |
285000 |
0 |
P I F |
0 |
285000 |
213750 |
Midwest |
High |
815096 |
242436 |
812631 |
8727094008 |
KING’S UPHOLSTERY |
JOELTON |
TN |
37080 |
U.S. BANK NATIONAL ASSOCIATION |
OH |
811420 |
23-Mar-05 |
2005 |
84 |
3 |
1 |
0 |
3 |
1 |
1 |
Y |
N |
|
30-Apr-05 |
24584 |
0 |
P I F |
0 |
10000 |
5000 |
Midwest |
Very Low |
812631 |
241609 |
298982 |
3069575000 |
DAVIDOVICH BAGEL & LOX FACTORY |
JAMAICA |
NY |
11435 |
JPMORGAN CHASE BANK NATL ASSOC |
IL |
311812 |
29-Apr-08 |
2008 |
84 |
9 |
2 |
1 |
9 |
0 |
1 |
N |
N |
|
31-May-08 |
101300 |
0 |
P I F |
0 |
101300 |
50650 |
Midwest |
Medium |
298982 |
88047 |
Cluster Sampling
unique_zip_codes = unique(loan$Zip)
avg_loans_per_zip = mean(table(loan$Zip))
num_zip_codes = ceiling(32724 / avg_loans_per_zip)
set.seed(123)
sampled_zip_codes = sample(unique_zip_codes, size = num_zip_codes, replace = FALSE)
cluster_sample = loan[loan$Zip %in% sampled_zip_codes, ]
cluster_sample_size = nrow(cluster_sample)
cluster_sample_dim = dim(cluster_sample)
names(cluster_sample_dim) = c("Size", "Var.count")
kable(t(cluster_sample_dim))
cluster_sample_size
[1] 32305
For cluster sampling we select a random set of zip codes and includes
all loans from those zip codes to create a cluster sample. We first
calculate the average number of loans per zip code and determines how
many zip codes are needed to reach the target sample size of 32,724.
Finally we filter the dataset to keep only loans from the selected zip
codes and check the final sample size.
SBA_Appr Curves
ggplot(loan, aes(x = SBA_Appv, fill = GrAppv_Cat, color = GrAppv_Cat)) +
geom_density(alpha = 0.3) +
labs(title = "Density Curves of SBA Approval Amount by Loan Size",
x = "SBA Approval Amount (SBA_Appv)",
y = "Density") +
theme_minimal() +
theme(legend.title = element_blank()) +
coord_cartesian(xlim = c(0, 500000))

The code above shows a density plot of the distribution of SBA
approval amounts, categorized by loan size. From the visualization above
we can see which loan sizes are most common and helps us see if certain
loan categories are higher. The density peaks tell us where most SBA
approval amounts fall.
LS0tDQp0aXRsZTogIkVEQSBCYW5rIExvYW4iDQphdXRob3I6ICdUeWxlciBCYXR0YWdsaW5pJw0KZGF0ZTogIjIwMjUtMy0yIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZmlnX3dpZHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIERldGVjdCwgaW5zdGFsbCBhbmQgbG9hZCBwYWNrYWdlcyBpZiBuZWVkZWQuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQogICBsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoIm5sZXFzbHYiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibmxlcXNsdiIpDQogICBsaWJyYXJ5KG5sZXFzbHYpDQp9DQojDQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQoNCmlmICghcmVxdWlyZSgicHN5Y2giKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpDQogICBsaWJyYXJ5KHBzeWNoKQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQogICBsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCiAgIGxpYnJhcnkoR0dhbGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXIiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KICAgbGlicmFyeShjYXIpDQp9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KICAgbGlicmFyeShkcGx5cikNCn0NCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQogICBsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJyZWFkeGwiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KICAgbGlicmFyeShyZWFkeGwpDQp9DQppZiAoIXJlcXVpcmUoIm9wZW54bHN4IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygib3Blbnhsc3giKQ0KICAgbGlicmFyeShvcGVueGxzeCkNCn0NCmlmICghcmVxdWlyZSgiZm9yZWNhc3QiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpDQogICBsaWJyYXJ5KGZvcmVjYXN0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJwd3IiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJwd3IiKQ0KICAgbGlicmFyeShwd3IpDQp9DQojIHNwZWNpZmljYXRpb25zIG9mIG91dHB1dHMgb2YgY29kZSBpbiBjb2RlIGNodW5rcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmdzID0gRkFMU0UsICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlcyA9IEZBTFNFLCAgIw0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSAgICAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgKSAgIA0KYGBgDQoNCg0KIyMgQ29tYmluZWQgRGF0YQ0KDQpgYGB7cn0NCmxvYW4wMSA9IHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vVHlsZXJCYXR0YWdsaW5pL1NUQS0zMjEvcmVmcy9oZWFkcy9tYWluL3cwNi1TQkFuYXRpb25hbDAxLmNzdiIsIGhlYWRlciA9IFRSVUUpWywgLTFdDQpsb2FuMDIgPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1R5bGVyQmF0dGFnbGluaS9TVEEtMzIxL3JlZnMvaGVhZHMvbWFpbi93MDYtU0JBbmF0aW9uYWwwMi5jc3YiLCBoZWFkZXIgPSBUUlVFKVssIC0xXQ0KbG9hbjAzID0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9UeWxlckJhdHRhZ2xpbmkvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vdzA2LVNCQW5hdGlvbmFsMDMuY3N2IiwgaGVhZGVyID0gVFJVRSlbLCAtMV0NCmxvYW4wNCA9IHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vVHlsZXJCYXR0YWdsaW5pL1NUQS0zMjEvcmVmcy9oZWFkcy9tYWluL3cwNi1TQkFuYXRpb25hbDA0LmNzdiIsIGhlYWRlciA9IFRSVUUpWywgLTFdDQpsb2FuMDUgPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1R5bGVyQmF0dGFnbGluaS9TVEEtMzIxL3JlZnMvaGVhZHMvbWFpbi93MDYtU0JBbmF0aW9uYWwwNS5jc3YiLCBoZWFkZXIgPSBUUlVFKVssIC0xXQ0KbG9hbjA2ID0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9UeWxlckJhdHRhZ2xpbmkvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vdzA2LVNCQW5hdGlvbmFsMDYuY3N2IiwgaGVhZGVyID0gVFJVRSlbLCAtMV0NCmxvYW4wNyA9IHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vVHlsZXJCYXR0YWdsaW5pL1NUQS0zMjEvcmVmcy9oZWFkcy9tYWluL3cwNi1TQkFuYXRpb25hbDA3LmNzdiIsIGhlYWRlciA9IFRSVUUpWywgLTFdDQpsb2FuMDggPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1R5bGVyQmF0dGFnbGluaS9TVEEtMzIxL3JlZnMvaGVhZHMvbWFpbi93MDYtU0JBbmF0aW9uYWwwOC5jc3YiLCBoZWFkZXIgPSBUUlVFKVssIC0xXQ0KbG9hbjA5ID0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9UeWxlckJhdHRhZ2xpbmkvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vdzA2LVNCQW5hdGlvbmFsMDkuY3N2IiwgaGVhZGVyID0gVFJVRSlbLCAtMV0NCmxvYW4gPSByYmluZChsb2FuMDEsIGxvYW4wMiwgbG9hbjAzLCBsb2FuMDQsIGxvYW4wNSwgbG9hbjA2LCBsb2FuMDcsIGxvYW4wOCwgbG9hbjA5KQ0KIyBkaW0oYmFua0xvYW4pDQojbmFtZXMoYmFua0xvYW4pDQoNCg0KDQpgYGANCg0KIyMgTWlzX1N0YXR1cyBNaXNzaW5nDQoNCmBgYHtyfQ0KbG9hbiA8LSBsb2FuWyFpcy5uYShsb2FuJE1JU19TdGF0dXMpLCBdDQoNCmBgYA0KDQpNaXNfU3RhdHVzIGlzIHRoZSBzdGF0dXMgb2YgdGhlIHBlcnNvbnMgbG9hbi4gVGhpcyBpcyBvbmUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyBpbiBvdXIgZGF0YXNldC4gSXQgZW5zdXJlcyB1cyB0aGF0IHdoZW4gd2UgZG8gb3VyIGFuYWx5c2lzIHdlIGhhdmUgYSBjb21wbGV0ZSBvYnNlcnZhdGlvbiB0ZWxsaW5nIHVzIGlmIHRoZSBsb2FuIHdhcyBhcHByb3ZlZCwgcGFpZCwgb3IgZGVmYXVsdGVkLiBTbyB3ZSByZW1vdmUgYW55IG9ic2VydmF0aW9ucyB3aXRoIGxvYW4gc3RhdHVzIG1pc3NpbmcgZm9yIGNsYXJpdHkuDQoNCiMjIENvbnZlcnQgVmFyaWFibGVzDQoNCmBgYHtyfQ0KY3VycmVuY3lfdmFycyA8LSBjKCJEaXNidXJzZW1lbnRHcm9zcyIsICJCYWxhbmNlR3Jvc3MiLCAiQ2hnT2ZmUHJpbkdyIiwgIkdyQXBwdiIsICJTQkFfQXBwdiIpDQoNCmxvYW5bY3VycmVuY3lfdmFyc10gPC0gbGFwcGx5KGxvYW5bY3VycmVuY3lfdmFyc10sIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoZ3N1YigiWyQsXSIsICIiLCB4KSkpDQoNCmBgYA0KDQpTaW5jZSBEaXNidXJzZW1lbnRHcm9zcywgQmFsYW5jZUdyb3NzLCBDaGdPZmZQcmluR3IsIEdyQXBwdiwgYW5kIFNCQV9BcHB2IGFyZSBpbXBvcnRhbnQgbWVhc3VyZXMgZm9yIG91ciBhbmFseXNpcyB3ZSB3YW50IHRvIGJlIGFibGUgdG8gdXNlIHRoZW0gbW9yZSBtZXRob2RpY2FsbHkuIFNvIHdlIGNvbnZlcnQgdGhlc2UgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIG51bWVyaWMgdmFyaWFibGVzIGFuZCByZW1vdmUgYW55IGNoYXJhY3RlciB2YWx1ZXMuIFdvcmtpbmcgd2l0aCBudW1iZXJzIGFzIG9wcG9zZWQgdG8gY2hhcnRlcmVycyBpcyBiZXR0ZXIgZm9yIHN0YXRpc3RpY2FsIGFuYWx5c2lzLCB2aXN1YWxpemF0aW9uLCBhbmQgb3ZlcmFsbCBtb2RlbGluZy4NCiMjIENhdGVnb3JpY2FsIFZhcmlhYmxlIHJld29yaw0KDQpgYGB7cn0NCmxvYW4gPC0gbG9hbiAlPiUNCiAgbXV0YXRlKEJhbmtSZWdpb24gPSBjYXNlX3doZW4oDQogICAgQmFua1N0YXRlICVpbiUgYygiQ1QiLCAiTUUiLCAiTUEiLCAiTkgiLCAiTkoiLCAiTlkiLCAiUEEiLCAiUkkiLCAiVlQiKSB+ICJOb3J0aGVhc3QiLA0KICAgIEJhbmtTdGF0ZSAlaW4lIGMoIklMIiwgIklOIiwgIklBIiwgIktTIiwgIk1JIiwgIk1OIiwgIk1PIiwgIk5FIiwgIk5EIiwgIk9IIiwgIlNEIiwgIldJIikgfiAiTWlkd2VzdCIsDQogICAgQmFua1N0YXRlICVpbiUgYygiQUwiLCAiQVIiLCAiREUiLCAiREMiLCAiRkwiLCAiR0EiLCAiS1kiLCAiTEEiLCAiTUQiLCAiTVMiLCAiTkMiLCAiT0siLCAiU0MiLCAiVE4iLCAiVFgiLCAiVkEiLCAiV1YiKSB+ICJTb3V0aCIsDQogICAgQmFua1N0YXRlICVpbiUgYygiQUsiLCAiQVoiLCAiQ0EiLCAiQ08iLCAiSEkiLCAiSUQiLCAiTVQiLCAiTlYiLCAiTk0iLCAiT1IiLCAiVVQiLCAiV0EiLCAiV1kiKSB+ICJXZXN0IiwNCiAgICBUUlVFIH4gIlVua25vd24iDQogICkpDQoNCnRhYmxlKGxvYW4kQmFua1JlZ2lvbikNCmBgYA0KDQpJIGRlY2lkZWQgdG8gbWFrZSA0IHJlZ2lvbmFsIGNhdGVnb3JpZXMgb2YgdGhlIHN0YXRlcy4gVGhlc2UgY2F0ZWdvcmllcyBiZWluZyBOb3J0aGVhc3QsIE1pZHdlc3QsIFNvdXRoLCBXZXN0IGFuZCBhbHNvIGFuIHVua25vd24uIEJ5IGRvaW5nIHRoaXMgd2UgY2FuIHNlZSBpZiB0aGVyZSBhcmUgYW55IHBhdHRlcm5zIGZvciBhbnkgc3BlY2lmaWMgcmVnaW9uLiBXZSBjYW4gdXNlIHRoaXMgdG8gY29tcGFyZSBhcHByb3ZhbCByYXRlcywgZGVmYXVsdCByYXRlcywgb3IgYXZlcmFnZSBsb2FuIGFtb3VudHMgYnkgcmVnaW9uLiBUaGlzIGFsc28gbWFrZXMgaXQgZWFzaWVyIGZvciBkYXRhIHZpc3VhbGl6YXRpb24gdG8gYWdhaW4gc2VlIGlmIHRoZXJlIGlzIGFueSBkaXNwYXJpdGllcyBieSByZWdpb24uIA0KDQojIyBEZWZhdWx0IFJhdGVzIGJhc2VkIG9uIFJlZ2lvbg0KDQpgYGB7cn0NCmRlZmF1bHRfcmF0ZXMgPC0gbG9hbiAlPiUNCiAgZ3JvdXBfYnkoQmFua1JlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBUb3RhbF9Mb2FucyA9IG4oKSwNCiAgICBEZWZhdWx0cyA9IHN1bShNSVNfU3RhdHVzID09ICJDSEdPRkYiLCBuYS5ybSA9IFRSVUUpLA0KICAgIERlZmF1bHRfUmF0ZSA9IERlZmF1bHRzIC8gVG90YWxfTG9hbnMgKiAxMDANCiAgKQ0KDQpwcmludChkZWZhdWx0X3JhdGVzKQ0KYGBgDQoNClRoaXMgY29kZSBjYWxjdWxhdGVzIHRoZSBkZWZhdWx0IHJhdGUgb2YgU0JBLWJhY2tlZCBsb2FucyBmb3IgZWFjaCByZWdpb24uIFdpdGggdGhpcyBjb2RlIGl0IGhlbHBzIHVzIHRvIGNvbXBhcmUgd2hpY2ggcmVnaW9ucyBoYXZlIGhpZ2hlciBvciBsb3dlciBkZWZhdWx0IHJhdGVzLiBJdCBhbHNvIGdpdmVzIGluc2lnaHQgaW50byByZWdpb25hbCBlY29ub21pYyB0cmVuZHMgYW5kIGxlbmRpbmcgcHJhY3RpY2VzLiBXZSBzZWUgdGhhdCBpbiB0aGUgU291dGggdGhlIGRlZmF1bHQgcmF0ZSBpcyB0aGUgaGlnaGVzdCBhdCAyMSUgbWVhbmluZyB0aGF0IG1vcmUgbG9hbnMgZ28gdW5wYWlkIGhlcmUgdGhhbiBpbiBhbnkgb3RoZXIgcmVnaW9uLiANCg0KIyMgRGlzY3JldGl6ZSBHckFwcHJ2DQoNCmBgYHtyfQ0KbG9hbiA8LSBsb2FuICU+JQ0KICBtdXRhdGUoR3JBcHB2X0NhdCA9IGN1dChHckFwcHYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBxdWFudGlsZShHckFwcHYsIHByb2JzID0gc2VxKDAsIDEsIGJ5ID0gMC4yKSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKQ0KDQp0YWJsZShsb2FuJEdyQXBwdl9DYXQpDQpgYGANCg0KSW4gdGhlIGNvZGUgYWJvdmUgd2UgY2F0ZWdvcml6ZSB0aGUgU0JBIGd1YXJhbnRlZWQgYXBwcm92YWwgYW1vdW50IGludG8gZml2ZSBncm91cHMuIEJ5IGNhdGVnb3JpemluZyBhbmQgbWFraW5nIHRoaXMgdmFyaWFibGUgaW50byBzbWFsbCBpbnRlcnZhbHMgd2UgY29udmVydCBpdCBmcm9tIGEgY29udGludW91cyB2YXJpYWJsZSB3ZSBub3cgaGF2ZSBhIGRpc2NyZXRpemVkIHZhcmlhYmxlLiBEaXNjcmV0aXplZCB2YXJpYWJsZXMgYXJlIGVhc2llciB0byBpbnRlcnByZXQuIFdlIHNlZSBmcm9tIHRoZSBvdXRwdXQgYWJvdmUgdGhhdCB0aGV5IGFyZSBldmVubHkgZGlzdHJpYnV0ZWQgbWVhbmluZyBvdXIgYXBwcm92YWwgc3BhbnMgYSB3aWRlIHJhbmdlIG9mIGFtb3VudHMuIA0KDQojIyBTdHVkeSBQb3B1bGF0aW9uDQoNCmBgYHtyfQ0KDQpzdHVkeS5wb3AgPC0gbG9hbiAlPiUNCiAgZmlsdGVyKEJhbmtSZWdpb24gIT0gIlVua25vd24iKQ0KDQprYWJsZSh0KHRhYmxlKHN0dWR5LnBvcCRCYW5rUmVnaW9uKSkpDQoNCmBgYA0KDQpGb3IgdGhlIHN0dWR5IHBvcHVsYXRpb24gd2UgcmVtb3ZlIHRoZSB1bmtub3duIGdyb3VwIGJlY2F1c2UgdGhlcmUgaXMgb25seSAxNzMwIG9ic2VydmF0aW9ucyB3aGljaCBpcyB0b28gc21hbGwgYW5kIGRvZXMgbm90IG1hdGNoIHVwIHdpdGggb3VyIG90aGVyIGNhdGVnb3JpZXMuIFdlIGFsc28gY2Fubm90IGRyYXcgY29uY2x1c2lvbnMgYWJvdXQgdGhlIHJlZ2lvbiBpZiB3ZSBkbyBub3Qga25vdyB3aGVyZSB0aGVzZSBvYnNlcnZhdGlvbnMgYXJlIGNvbWluZyBmcm9tLiBTbyBub3cgd2UgaGF2ZSBkZWZpbmVkIG91ciBzdHVkeSBwb3B1bGF0aW9uLg0KDQojIyBTYW1wbGUgQ2FsY3VsYXRpb24NCg0KYGBge3J9DQpzdGRfZGV2IDwtIHNkKGxvYW4kU0JBX0FwcHYsIG5hLnJtID0gVFJVRSkNCg0KWl9hbHBoYSA8LSAxLjk2ICANClpfYmV0YSA8LSAwLjg0ICAgDQpkZWx0YSA8LSA1MDAwICAgIA0KDQpuIDwtICgyICogKFpfYWxwaGEgKyBaX2JldGEpXjIgKiBzdGRfZGV2XjIpIC8gZGVsdGFeMg0KDQpuX3JvdW5kZWQgPC0gY2VpbGluZyhuKQ0KDQpjYXQoIlRoZSByZXF1aXJlZCBzYW1wbGUgc2l6ZSBwZXIgZ3JvdXAgaXM6Iiwgbl9yb3VuZGVkLCAiXG4iKQ0KDQpgYGANCg0KQWZ0ZXIgbWFraW5nIGEgY2FsY3VsYXRpb24gZm9yIG91ciBzYW1wbGUgd2UgZ2V0IGEgc2FtcGxlIHNpemUgb2YgMzI3MjQgd2hpY2ggaXMgZXhhY3RseSB3aGVyZSB3ZSBuZWVkIHRoZSBzaXplIHRvIGJlLiBJdCBpcyBsZXNzIHRoYW4gNSUgb2Ygb3VyIGRhdGFzZXQgYnV0IGlzIGFsc28gYSByZWxhdGl2ZWx5IGJpZyBudW1iZXIuDQoNCiMjIFNpbXBsZSBSYW5kb20gU2FtcGxlDQoNCmBgYHtyfQ0Kc3R1ZHkucG9wJHNhbXBsaW5nLmZyYW1lID0gMTpsZW5ndGgoc3R1ZHkucG9wJEdyQXBwdikgICANCg0Kc2FtcGxlZC5saXN0ID0gc2FtcGxlKDE6bGVuZ3RoKHN0dWR5LnBvcCRHckFwcHYpLCAzMjcyNCkgDQoNClNSUy5zYW1wbGUgPSBzdHVkeS5wb3Bbc2FtcGxlZC5saXN0LF0gICAgICAgICAgICAgICAgICANCg0KZGltZW5zaW9uLlNSUyA9IGRpbShTUlMuc2FtcGxlKQ0KbmFtZXMoZGltZW5zaW9uLlNSUykgPSBjKCJTaXplIiwgIlZhci5jb3VudCIpDQprYWJsZSh0KGRpbWVuc2lvbi5TUlMpKQ0KDQpgYGANCg0KV2UgcGVyZm9ybWVkIGEgc2ltcGxlIHJhbmRvbSBzYW1wbGUuIEZpcnN0IHdlIGFzc2lnbmVkIGEgdW5pcXVlIGluZGV4IHRvIGVhY2ggb2JzZXJ2YXRpb24gdXNpbmcgc2FtcGxpbmcuZnJhbWUuIFRoZW4gd2UgcmFuZG9tbHkgc2VsZWN0ZWQgMzIsNzI0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSBkYXRhc2V0IHdpdGhvdXQgcmVwbGFjZW1lbnQuIEZpbmFsbHkgd2UgIGV4dHJhY3QgdGhlIHNhbXBsZWQgb2JzZXJ2YXRpb25zIGludG8gU1JTLnNhbXBsZSB0byBtYWtlIGEgdGFibGUuIA0KDQojIyBTeXN0ZW1hdGljIFNhbXBsaW5nDQoNCmBgYHtyfQ0KanVtcC5zaXplID0gZGltKHN0dWR5LnBvcClbMV0lLyUgMzI3MjQNCg0KcmFuZC5zdGFydGluZy5wdD1zYW1wbGUoMTpqdW1wLnNpemUsMSkNCnNhbXBsaW5nLmlkID0gc2VxKHJhbmQuc3RhcnRpbmcucHQsIGRpbShzdHVkeS5wb3ApWzFdLCBqdW1wLnNpemUpDQoNCnN5cy5zYW1wbGU9c3R1ZHkucG9wW3NhbXBsaW5nLmlkLF0gICAgDQoNCnN5cy5TYW1wbGUuZGltID0gZGltKHN5cy5zYW1wbGUpDQpuYW1lcyhzeXMuU2FtcGxlLmRpbSkgPSBjKCJTaXplIiwgIlZhci5jb3VudCIpDQprYWJsZSh0KHN5cy5TYW1wbGUuZGltKSkNCg0KYGBgDQoNCldlIHBlcmZvcm1lZCBhIHN5c3RlbWF0aWMgc2FtcGxlLiBGaXJzdCB3ZSBjYWxjdWxhdGVkIHRoZSBqdW1wIHNpemUgYnkgZGl2aWRpbmcgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgYnkgMzIsNzI0LCBkZXRlcm1pbmluZyB0aGUgaW50ZXJ2YWwgYXQgd2hpY2ggc2FtcGxlcyB3aWxsIGJlIHNlbGVjdGVkLiBUaGVuIHdlIHJhbmRvbWx5IHBpY2tlZCBhIHN0YXJ0aW5nIHBvaW50IHdpdGhpbiB0aGUgZmlyc3QgaW50ZXJ2YWwuIFVzaW5nIHRoaXMgc3RhcnRpbmcgcG9pbnQgd2UgZ2VuZXJhdGUgYSBzZXF1ZW5jZSBvZiBpbmRpY2VzIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzIHRvIHNlbGVjdCBvYnNlcnZhdGlvbnMuDQoNCg0KDQojIyBTdHJhdGVmaWVkIFNhbXBsaW5nDQoNCmBgYHtyfQ0KZnJlcS50YWJsZSA9IHRhYmxlKHN0dWR5LnBvcCRCYW5rUmVnaW9uKSAgDQpyZWwuZnJlcSA9IGZyZXEudGFibGUvc3VtKGZyZXEudGFibGUpICANCnN0cmF0YS5zaXplID0gcm91bmQocmVsLmZyZXEqMzI3MjQpICAgICANCnN0cmF0YS5uYW1lcz1uYW1lcyhzdHJhdGEuc2l6ZSkgICAgICAgIA0Ka2FibGUodChzdHJhdGEuc2l6ZSkpIA0KDQpgYGANCg0KV2UgcGVyZm9ybWVkIGEgU3RyYXRpZmllZCBTYW1wbGluZyBieSBmaXJzdCBjcmVhdGluZyBhIGZyZXF1ZW5jeSB0YWJsZSBvZiB0aGUgQmFua1JlZ2lvbiB2YXJpYWJsZSwgd2hpY2ggcmVwcmVzZW50cyBkaWZmZXJlbnQgY2F0ZWdvcmllcy4gVGhlbiB3ZSBjYWxjdWxhdGUgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBvZiBlYWNoIGNhdGVnb3J5IGJ5IGRpdmlkaW5nIHRoZSBjb3VudHMgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIE5leHQgd2UgZGV0ZXJtaW5lIHRoZSBzYW1wbGUgc2l6ZSBmb3IgZWFjaCBjYXRlZ29yaWVzIGJ5IG11bHRpcGx5aW5nIHRoZSByZWxhdGl2ZSBmcmVxdWVuY2llcyBieSAzMiw3MjQgYW5kIHJvdW5kaW5nIHRoZSB2YWx1ZXMuDQoNCmBgYHtyfQ0Kc3RyYXRhLnNhbXBsZSA9IHN0dWR5LnBvcFswLF0gIA0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoc3RyYXRhLm5hbWVzKSkgew0KICAgaXRoLnN0cmF0YS5uYW1lcyA9IHN0cmF0YS5uYW1lc1tpXSAgDQogICBpdGguc3RyYXRhLnNpemUgPSBzdHJhdGEuc2l6ZVtpXSAgIA0KDQogICBpdGguc2FtcGxpbmcuaWQgPSB3aGljaChzdHVkeS5wb3AkQmFua1JlZ2lvbiA9PSBpdGguc3RyYXRhLm5hbWVzKSANCiAgIA0KICAgaWYgKGxlbmd0aChpdGguc2FtcGxpbmcuaWQpID4gMCAmJiBpdGguc3RyYXRhLnNpemUgPiAwKSB7DQogICAgICAgaXRoLnN0cmF0YSA9IHN0dWR5LnBvcFtpdGguc2FtcGxpbmcuaWQsXSANCiAgICAgICBpdGguc3RyYXRhJGFkZC5pZCA9IDE6bnJvdyhpdGguc3RyYXRhKSAgDQogICAgICAgDQogICAgICAgaXRoLnNhbXBsaW5nLmlkID0gc2FtcGxlKDE6bnJvdyhpdGguc3RyYXRhKSwgbWluKGl0aC5zdHJhdGEuc2l6ZSwgbnJvdyhpdGguc3RyYXRhKSkpIA0KICAgICAgIGl0aC5zYW1wbGUgPSBpdGguc3RyYXRhW2l0aC5zYW1wbGluZy5pZCwgXQ0KICAgICAgIA0KICAgICAgIHN0cmF0YS5zYW1wbGUgPSByYmluZChzdHJhdGEuc2FtcGxlLCBpdGguc2FtcGxlKSANCiAgIH0NCn0NCg0Kc3RyYXQuc2FtcGxlLmZpbmFsID0gc3RyYXRhLnNhbXBsZQ0KDQprYWJsZShoZWFkKHN0cmF0LnNhbXBsZS5maW5hbCkpICANCg0KYGBgDQoNCiMjIENsdXN0ZXIgU2FtcGxpbmcNCg0KYGBge3J9DQp1bmlxdWVfemlwX2NvZGVzID0gdW5pcXVlKGxvYW4kWmlwKQ0KDQphdmdfbG9hbnNfcGVyX3ppcCA9IG1lYW4odGFibGUobG9hbiRaaXApKQ0KDQpudW1femlwX2NvZGVzID0gY2VpbGluZygzMjcyNCAvIGF2Z19sb2Fuc19wZXJfemlwKQ0KDQpzZXQuc2VlZCgxMjMpDQpzYW1wbGVkX3ppcF9jb2RlcyA9IHNhbXBsZSh1bmlxdWVfemlwX2NvZGVzLCBzaXplID0gbnVtX3ppcF9jb2RlcywgcmVwbGFjZSA9IEZBTFNFKQ0KDQpjbHVzdGVyX3NhbXBsZSA9IGxvYW5bbG9hbiRaaXAgJWluJSBzYW1wbGVkX3ppcF9jb2RlcywgXQ0KDQpjbHVzdGVyX3NhbXBsZV9zaXplID0gbnJvdyhjbHVzdGVyX3NhbXBsZSkNCmNsdXN0ZXJfc2FtcGxlX2RpbSA9IGRpbShjbHVzdGVyX3NhbXBsZSkNCm5hbWVzKGNsdXN0ZXJfc2FtcGxlX2RpbSkgPSBjKCJTaXplIiwgIlZhci5jb3VudCIpDQoNCmthYmxlKHQoY2x1c3Rlcl9zYW1wbGVfZGltKSkNCg0KY2x1c3Rlcl9zYW1wbGVfc2l6ZQ0KYGBgDQoNCkZvciBjbHVzdGVyIHNhbXBsaW5nIHdlIHNlbGVjdCBhIHJhbmRvbSBzZXQgb2YgemlwIGNvZGVzIGFuZCBpbmNsdWRlcyBhbGwgbG9hbnMgZnJvbSB0aG9zZSB6aXAgY29kZXMgdG8gY3JlYXRlIGEgY2x1c3RlciBzYW1wbGUuIFdlIGZpcnN0IGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBudW1iZXIgb2YgbG9hbnMgcGVyIHppcCBjb2RlIGFuZCBkZXRlcm1pbmVzIGhvdyBtYW55IHppcCBjb2RlcyBhcmUgbmVlZGVkIHRvIHJlYWNoIHRoZSB0YXJnZXQgc2FtcGxlIHNpemUgb2YgMzIsNzI0LiBGaW5hbGx5IHdlIGZpbHRlciB0aGUgZGF0YXNldCB0byBrZWVwIG9ubHkgbG9hbnMgZnJvbSB0aGUgc2VsZWN0ZWQgemlwIGNvZGVzIGFuZCBjaGVjayB0aGUgZmluYWwgc2FtcGxlIHNpemUuDQoNCg0KIyMgU0JBX0FwcHIgQ3VydmVzDQoNCmBgYHtyfQ0KZ2dwbG90KGxvYW4sIGFlcyh4ID0gU0JBX0FwcHYsIGZpbGwgPSBHckFwcHZfQ2F0LCBjb2xvciA9IEdyQXBwdl9DYXQpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKyANCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IEN1cnZlcyBvZiBTQkEgQXBwcm92YWwgQW1vdW50IGJ5IExvYW4gU2l6ZSIsDQogICAgICAgeCA9ICJTQkEgQXBwcm92YWwgQW1vdW50IChTQkFfQXBwdikiLCANCiAgICAgICB5ID0gIkRlbnNpdHkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA1MDAwMDApKQ0KYGBgDQoNClRoZSBjb2RlIGFib3ZlIHNob3dzIGEgZGVuc2l0eSBwbG90IG9mIHRoZSBkaXN0cmlidXRpb24gb2YgU0JBIGFwcHJvdmFsIGFtb3VudHMsIGNhdGVnb3JpemVkIGJ5IGxvYW4gc2l6ZS4gRnJvbSB0aGUgdmlzdWFsaXphdGlvbiBhYm92ZSB3ZSBjYW4gc2VlIHdoaWNoIGxvYW4gc2l6ZXMgYXJlIG1vc3QgY29tbW9uIGFuZCBoZWxwcyB1cyBzZWUgaWYgY2VydGFpbiBsb2FuIGNhdGVnb3JpZXMgYXJlIGhpZ2hlci4gVGhlIGRlbnNpdHkgcGVha3MgdGVsbCB1cyB3aGVyZSBtb3N0IFNCQSBhcHByb3ZhbCBhbW91bnRzIGZhbGwuIA0KDQoNCg0KDQoNCg==