1 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)

2 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.

3 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.

4 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.

5 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.

6 Study Population

study.pop <- loan %>%
  filter(BankRegion != "Unknown")

kable(t(table(study.pop$BankRegion)))
Midwest Northeast South West
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.

7 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.

8 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))
Size Var.count
32724 30

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.

9 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))
Size Var.count
33239 30

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.

10 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)) 
Midwest Northeast South West
9841 5490 10055 7338

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))  
LoanNr_ChkDgt Name City State Zip Bank BankState NAICS ApprovalDate ApprovalFY Term NoEmp NewExist CreateJob RetainedJob FranchiseCode UrbanRural RevLineCr LowDoc ChgOffDate DisbursementDate DisbursementGross BalanceGross MIS_Status ChgOffPrinGr GrAppv SBA_Appv BankRegion GrAppv_Cat sampling.frame add.id
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

11 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))
Size Var.count
32305 29
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.

12 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==