1 Introduction and Background

Here we have a dataset originally composed of over 5,000 songs that were available on the music-streaming service Spotify over the course of 2024. These tracks stem from a wide array of vastly different genres and styles of creation. The dataset is publicly available on the platform Kaggle and is provided in the References section below. Although the data originally contained 29 variables, only 11 play a particularly relevant role in the analysis I wish to do here. A breakdown of those variables, their meanings and data types is below:

Name Meaning Data_Type
track_popularity song’s rating on the 0 - 100 metric double
popular how popular the track is (more on that later) character
track_name name of the song character
track_artist artist’s stage name character
playlist_genre genre that Spotify classifies the song in character
playlist_subgenre subgenre that Spotify classifies the song in character
tempo the speed of a track, measured in beats per minute (BPM) double
danceability A score describing how suitable a track is for dancing based on tempo, rhythm stability, beat strength and overall regularity (0 to 1 scale) double
speechiness the presence of audible words (0 to 1 scale) double
instrumentalness likelihood that the track contains instrumentals (0 to 1 scale) double
duration_mins length of track in minutes double

Many of the other variables in the dataset I chose to disregard as they either pertained to unnecessary technical information (such as the algorithmic identifier for the particular song or its album), or extremely niche musical topics that the average listener or introductory critic would most likely not pick up on (such as mode and key).

1.1 Objective for my Analysis

Using this dataset, I wish to quantifiably examine what musical qualities and characteristics lead to a song (or “track”) becoming extremely successful (hence popular) via Spotify. As of October of 2025, Spotify has a market cap of over 139 billion dollars and close to 700 million users. This means that for any aspiring musician, “making it big” on Spotify would serve as a monumental and life-altering milestone.

Spotify has a metric known as the popularity index which serves to numerically represent the popularity of tracks on its platform. While the exact formula behind the score is kept confidential, critics in the music industry and those with strong technical backgrounds have theorized that the primary factors which influence a track’s score are the following; its total number of streams, its recent number of streams, its number of downloads and saves, and its month-to-month growth in all three of said metrics.

This popularity index operates on a scale ranging from 0 to 100, with only the most well-established stars reaching the 90th percentile and higher. That being said, any track with a rating of 75 or higher is largely considered a “top track” by industry experts, so that is the interpretation I will apply with my analysis. I created a categorical binary variable “popular”, which is valued as “yes” for tracks with a popularity index rating of 75 or higher, and “no” for tracks with a lower rating. I will perform associative logistic regression to assess which, if any, of the factors mentioned in my variable breakdown above contribute to a track being deemed popular or not.

Finally, after performing simple and multiple linear regression processes to analyze the association between a track’s popularity and its status of the variables I have laid out, I will then use predictive modeling techniques in the effort to create a model which can accurately predict a particular track’s likelihood of being popular on Spotify.

2 Simple Logistic Regression Model

2.1 Checking for Pairwise Relationships

Before jumping into model creation, I created a correlation matrix for the quantitative predictor variables I am going to use in my analysis. This is because variables with very high correlations (r >= 0.8) often can function as almost multiples of one another, leading to inaccurate conclusions, especially when performing multiple regression.

cor_matrix = cor(Quantitative_Variables)
  # No high correlations between the variables

kable(cor_matrix, caption = "Correlation Matrix")
Correlation Matrix
tempo danceability speechiness instrumentalness duration_mins
tempo 1.0000000 0.0338179 0.0576956 -0.1518469 0.0397516
danceability 0.0338179 1.0000000 0.2840300 -0.3205070 -0.1374377
speechiness 0.0576956 0.2840300 1.0000000 -0.1903130 -0.1050720
instrumentalness -0.1518469 -0.3205070 -0.1903130 1.0000000 -0.2218689
duration_mins 0.0397516 -0.1374377 -0.1050720 -0.2218689 1.0000000

After creating a correlation matrix, we can see that none of the quantitative predictor variables that we are examining have a high correlation with one another. Practically speaking, this is not very surprising as even “similar” songs by the same artist or within the same genre can have wildly different characteristics when it comes to tempo or anything else we are looking at.

2.2 Picking Variable for Simple Model

Below I created histograms to represent the distributions of all five quantitative variables I am going to use in this analysis. The purpose for doing such is to determine if any sort of variable manipulation is necessary before using the variable in our logistic model.

par(mfrow = c(2, 3), mar = c(4, 4, 2, 1))  # mar controls margins 
  
  
ylimit = max(density(Work_Data$tempo)$y)
hist(Work_Data$tempo, probability = TRUE, main = "Tempo Distribution", xlab="Tempo", 
       col = "azure1", border="lightseagreen")
  lines(density(Work_Data$tempo, adjust=2), col="blue") 

ylimit = max(density(Work_Data$danceability)$y)
hist(Work_Data$danceability, probability = TRUE, main = "Danceability Distribution", xlab="Danceability", 
       col = "azure1", border="lightseagreen")
  lines(density(Work_Data$danceability, adjust=2), col="blue") 
  
ylimit = max(density(Work_Data$speechiness)$y)
hist(Work_Data$speechiness, probability = TRUE, main = "Speechiness Distribution", xlab="Speechiness", 
       col = "azure1", border="lightseagreen")
  lines(density(Work_Data$speechiness, adjust=2), col="blue") 
  
  
ylimit = max(density(Work_Data$instrumentalness)$y)
hist(Work_Data$instrumentalness, probability = TRUE, main = "Instrumentalness Distribution", xlab="Instrumentalness", 
       col = "azure1", border="lightseagreen")
  lines(density(Work_Data$instrumentalness, adjust=2), col="blue") 
  
  
ylimit = max(density(Work_Data$duration_mins)$y)
hist(Work_Data$duration_mins, probability = TRUE, main = "Length (mins) Distribution", xlab="Length (mins)", 
       col = "azure1", border="lightseagreen")
  lines(density(Work_Data$duration_mins, adjust=2), col="blue") 

We can see from the histograms that there is an extreme skew in speechiness, instrumentalness and length. While there are slight skews in tempo and danceability, they are far more moderate. Since tempo appears to be the least skewed of the variables, and it’s calculation is the most straightforward (a simple measure of beats per minute), I will use tempo to perform my simple logistic regression.

2.3 Creating the Simple Model

Using built-in R functions, I created a simple binary logistic regression model, in which the tempo of a song is meant to predict whether or not that track is popular or not (popularity score of 75 or not). In the calculations, the base level, or 0, was stored for instances of a track’s popularity being marked as “no”. While the instance level, or 1, was stored for instances of a track’s popularity being marked as “yes”.

Simple_Log_Model = glm(formula = popular ~ tempo, family = binomial(link = "logit"), 
    data = Work_Data)

invisible(summary(Simple_Log_Model))

Simple_Log_Model_Coef_Summary = summary(Simple_Log_Model)$coef       # output stats of coefficients
Simple_Confidence_Interval = confint(Simple_Log_Model)                     # confidence intervals of betas
Simple_Summary_Stats = cbind(Simple_Log_Model_Coef_Summary, Simple_Confidence_Interval.95=Simple_Confidence_Interval)   # rounding off decimals
kable(Simple_Summary_Stats,caption = "Simple Logistic Regression Model Summary")  
Simple Logistic Regression Model Summary
Estimate Std. Error z value Pr(>|z|) 2.5 % 97.5 %
(Intercept) -2.2643674 0.2126810 -10.646777 0.000000 -2.6844577 -1.8504312
tempo 0.0060146 0.0017127 3.511866 0.000445 0.0026561 0.0093727

The summary of our model above leads us to believe that a track’s tempo does have a positive association with the likelihood that a track is popular on Spotify, however that association is very small in practical effect. Despite a statistically significant p value, \(\beta\)1 has an estimated association with the probability of a track being deemed popular of .006, and a 95% confidence interval deeming the association to fall between the interval [.003, .009].

2.4 Odds Ratio Summary

While the direct interpretation of a logistic regression model’s summary statistics can be helpful, it is sometimes more practical to examine our model’s odds ratio. An odds ratio represents the likelihood of success divided by the likelihood of failure. For example, an odds ratio of 2 would mean that whatever instance we are considering a “success” occurs twice the number of times than it fails to occur.

# Odds ratio
Simple_Log_Model_Coef_Summary = summary(Simple_Log_Model)$coef
Odds_Ratio = exp(coef(Simple_Log_Model))
out.stats = cbind(Simple_Log_Model_Coef_Summary, odds.ratio = Odds_Ratio)                 
kable(out.stats,caption = "Simple Regression Model Coefficients W/ Odds Ratios")
Simple Regression Model Coefficients W/ Odds Ratios
Estimate Std. Error z value Pr(>|z|) odds.ratio
(Intercept) -2.2643674 0.2126810 -10.646777 0.000000 0.1038957
tempo 0.0060146 0.0017127 3.511866 0.000445 1.0060327

The odds ratio between a track’s tempo and it’s popularity status is ~ 1.006. Since tempo is measured in beats per minute (BPM), this means that for every additional beat per minute that a song is recorded in, it’s probability of having a popularity score above 75 increases by about 0.6%.

2.5 Goodness-of-fit measures

Below I calculated the goodness-of-fit (GOF) measures for the logistic regression model. Generally speaking, it is beneficial to find a model’s GOF features as they serve as relative comparisons of model efficiency when comparing multiple models. In this instance, we only created one model so far, so the measures are not of practical use at the moment.

Residual_Deviance = Simple_Log_Model$deviance
Null_Residual_Deviance = Simple_Log_Model$null.deviance
aic = Simple_Log_Model$aic
Goodness_Of_Fit = cbind(Deviance.residual = Residual_Deviance, Null.Deviance.Residual = Null_Residual_Deviance,
      AIC = aic)
pander(Goodness_Of_Fit)
Deviance.residual Null.Deviance.Residual AIC
2451 2464 2455

2.6 Success Probability Curve

Below I created a visual of the the model’s S curve (also called success probability curve or logistic curve). The S curve helps us see the change in probability of an event happening (a track being popular or not on Spotify), in relation to a change in the value of a predictor variable (tempo in BPM in this instance).

###

tempo_range = range(Work_Data$tempo)
x = seq(tempo_range[1], tempo_range[2], length = 200)
beta.x = coef(Simple_Log_Model)[1] + coef(Simple_Log_Model)[2]*x
success.prob = exp(beta.x)/(1+exp(beta.x))
failure.prob = 1/(1+exp(beta.x))
ylimit = max(success.prob, failure.prob)
##
beta1 = coef(Simple_Log_Model)[2]
success.prob.rate = beta1*exp(beta.x)/(1+exp(beta.x))^2
##
##
par(mfrow = c(1,1))
plot(x, success.prob, type = "l", lwd = 2, col = "black",
     main = "The Probability of a Track Being \n Considered Popular on Spotify", 
     ylim=c(0, 1.1*ylimit),
     xlab = "Tempo (BPM)",
     ylab = "probability",
     axes = FALSE,
     col.main = "black",
     cex.main = 1.1)
# lines(x, failure.prob,lwd = 2, col = "darkred")
axis(1, pos = 0)
axis(2)

##
y.rate = max(success.prob.rate)
plot(x, success.prob.rate, type = "l", lwd = 2, col = "black",
     main = "The Rate of Change in Probability of a Track Being \n Considered Popular on Spotify", 
     xlab = "Tempo (BPM)",
     ylab = "Rate of Change",
     ylim=c(0,1.1*y.rate),
     axes = FALSE,
     col.main = "black",
     cex.main = 1.1
     )
axis(1, pos = 0)
axis(2)

The S Curve provides visual confirmation of the conclusions we were able to draw from analyzing our coefficient summary, confidence intervals and odds ratio. That is, that there is a positive association between a track’s tempo and it’s likelihood of being a popular track on Spotify, but that association is very minimal in practical effect.

We can see from our second plot, that looked at the relationship between the rate of change in said probability and a track’s tempo, that from 50 to 200 beats per minute, the probability of a track being popular’s positive rate of change only increases from 0.0006 to 0.0012.

2.7 Takeaways from Simple Logistic Regression Model

To conclude, we can say that although there is a statistically significant and positive relationship between a track’s tempo and the probability that it is popular on Spotify, that association is of such a small magnitude that it is relatively a non-factor in practical sense. Therefore, it would be overly simplistic and hyperbolic for music labels or agents to aggressively push their artists towards a more up-tempo production, as the return on such a decision would likely not be worth the cost of souring the relationship with the artist at hand.

3 Multiple Logistic Regression Model

As we saw earlier when creating our simple regression model, there is no significant risk of multicollinearity with the data we are working with. However, we also saw there is an extreme rightward skew in the distribution of speechiness, instrumentalness and length. Below I take a closer look at the distributions of those three variables.

## Remember in the HW instructions, include not just the numerical variables but two categorical ones too (let's use playlist_genre and playlist_subgenre)

par(mfrow=c(1,2))
hist(speechiness, xlab="Speechiness", main = "")
hist(instrumentalness, xlab = "Instrumentalness", main = "")

hist(duration_mins, xlab = "Length (Mins)", main = "")

To combat the skew of all three variables, I will perform discretization on each of them. The coding for such can be seen below.

  # Speechiness = "presence of audible words on 0 to 1 scale"
grp.speechiness = speechiness
grp.speechiness[speechiness < 0.5] = ": s under 0.5"
grp.speechiness[speechiness > 0.5] = ": s over 0.5"

  # sum(speechiness > 0.5) = 14
  # sum(speechiness < 0.5) = 2641


  # Instrumentalness = "likelihood that the track contains instrumentals (0 to 1 scale)"
grp.instrumentalness = instrumentalness
grp.instrumentalness[instrumentalness < 0.1] = ": s under 0.1"
grp.instrumentalness[instrumentalness > 0.1 & instrumentalness <= 0.7] = ": s from 0.1 to 0.7"
grp.instrumentalness[instrumentalness > 0.7] = ": s over 0.7"
 
 #  sum(instrumentalness < 0.1) = 1802
 #  sum(instrumentalness > 0.1 & instrumentalness < 0.7) = 193
 #  sum(instrumentalness > 0.7) = 659
  

  # duration_mins below
grp.duration_mins = duration_mins
grp.duration_mins[duration_mins < 5] = ": Under 5 mins"
grp.duration_mins[duration_mins >= 5 & duration_mins <= 10] = ": Between 5 and 10 mins"
grp.duration_mins[duration_mins > 10] = ": Over 10 mins"
  
# sum(duration_mins < 5) = 2424
# sum(duration_mins >= 5 & duration_mins <= 10) = 211
# sum(duration_mins > 10) = 20


Work_Data$grp.speechiness = grp.speechiness
Work_Data$grp.instrumentalness = grp.instrumentalness
Work_Data$grp.duration_mins = grp.duration_mins

3.1 Building Full Multiple Logistic Model

For the full multiple logistic model, I will initially include the continuous numerical variables tempo and danceability in their original form, as well as the discretized transformations of instrumentalness, length in minutes and speechiness. After coding this model in, I will examine its summary statistics.

Multiple_Full_Log_Model = glm(formula = popular ~ tempo + danceability + grp.speechiness + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = Work_Data)

invisible(summary(Multiple_Full_Log_Model))

Multiple_Full_Log_Model_Coef_Summary = summary(Multiple_Full_Log_Model)$coef
Multiple_Full_Confidence_Interval = confint(Multiple_Full_Log_Model)  

Multiple_Full_Summary_Stats = cbind(Multiple_Full_Log_Model_Coef_Summary, Multiple_Full_Confidence_Interval.95=Multiple_Full_Confidence_Interval)   # rounding off decimals

kable(Multiple_Full_Summary_Stats,caption = "Full Multiple Logistic Regression Model Summary") 
Full Multiple Logistic Regression Model Summary
Estimate Std. Error z value Pr(>|z|) 2.5 % 97.5 %
(Intercept) -17.3380444 375.7154226 -0.0461467 0.9631933 NA 7.5447416
tempo 0.0038700 0.0018965 2.0405532 0.0412953 0.0001487 0.0075874
danceability 0.3230690 0.3274150 0.9867263 0.3237768 -0.3156955 0.9684832
grp.speechiness: s under 0.5 14.2760281 375.7151570 0.0379969 0.9696901 -10.9252242 NA
grp.instrumentalness: s over 0.7 -2.1650529 0.3613366 -5.9917905 0.0000000 -2.9070614 -1.4777880
grp.instrumentalness: s under 0.1 0.6024645 0.2205999 2.7310277 0.0063137 0.1872867 1.0554459
grp.duration_mins: Over 10 mins -13.2932711 310.2840235 -0.0428423 0.9658273 NA 4.4755962
grp.duration_mins: Under 5 mins 0.6782754 0.2321971 2.9211189 0.0034878 0.2431306 1.1574116
invisible(table(Work_Data$popular, Work_Data$grp.speechiness))
invisible(table(Work_Data$popular, Work_Data$grp.instrumentalness))
invisible(table(Work_Data$popular, Work_Data$grp.duration_mins))
  #NA's in kable function display are due to "perfect" results between certain levels of our discretized variables and the response variable of popular or not. If all observations in a certain level result in one of the two options for the response variable, then that coefficient will trend towards negative or positive infinity per R's calculations.

We can see the following from our model summary. First, just like the results from our simple regression model, there is once again a statistically significant association between a track’s tempo and its likelihood of being popular on Spotify (p ~ 0.0413). However that association is so small that it is of little practical importance. Additionally, it appears that a track’s instrumentalness and length absolutely play a part in its probability of being popular on Spotify.

If we look at the discretized breakdown of instrumentalness, we see that there is an significant difference in tracks whose instrumentalness rating is of 0.7 and higher, or less than 0.1, when compared to tracks whose rating is somewhere in the middle. For track length, there also appears to be significant evidence that tracks less than 5 minutes long have a greater probability of achieving popularity on Spotify than those of any other lengths in our dataset.

On the other hand, there is not evidence to suggest that a track’s danceability or speechiness have such an association with its popularity.

After examining the model summary, I also chose to run VIF on this full model, which can be seen below. Given that all GVIF are below 1.1, we can safely say that multicollinearity is not pertinent to this dataset. This finding is in line with the takeaway from our pairwise scatterplot earlier on.

vif(Multiple_Full_Log_Model)
                         GVIF Df GVIF^(1/(2*Df))
tempo                1.018314  1        1.009116
danceability         1.048916  1        1.024166
grp.speechiness      1.000000  1        1.000000
grp.instrumentalness 1.035527  2        1.008766
grp.duration_mins    1.012616  2        1.003139

3.2 Manually Reduced Multiple Logistic Model

Since parsimony (having as least amount of variables as possible without losing significant accuracy in association examination or prediction accuracy) is an important aspect of statistical modeling, I also then chose to create a reduced version of my multiple logistic model above. In this model, I did not include predictors whose impact was very clearly insignificant as per the original full model’s summary (danceability and speechiness). The summary for this new reduced model is below.

Multiple_Reduced_Log_Model = glm(formula = popular ~ tempo + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = Work_Data)

invisible(summary(Multiple_Reduced_Log_Model))

Multiple_Reduced_Log_Model_Coef_Summary = summary(Multiple_Reduced_Log_Model)$coef
Multiple_Reduced_Log_Model_Confidence_Interval = confint(Multiple_Reduced_Log_Model)  

Multiple_Reduced_Summary_Stats = cbind(Multiple_Reduced_Log_Model_Coef_Summary, Multiple_Reduced_Confidence_Interval.95=Multiple_Reduced_Log_Model_Confidence_Interval)   # rounding off decimals

kable(Multiple_Reduced_Summary_Stats,caption = "Reduced Multiple Logistic Regression Model Summary") 
Reduced Multiple Logistic Regression Model Summary
Estimate Std. Error z value Pr(>|z|) 2.5 % 97.5 %
(Intercept) -2.8580203 0.3686009 -7.7536985 0.0000000 -3.5991921 -2.1520692
tempo 0.0035527 0.0018681 1.9017792 0.0572000 -0.0001176 0.0072091
grp.instrumentalness: s over 0.7 -2.1920495 0.3605499 -6.0797397 0.0000000 -2.9326652 -1.5064166
grp.instrumentalness: s under 0.1 0.6152133 0.2195892 2.8016558 0.0050841 0.2022035 1.0664024
grp.duration_mins: Over 10 mins -13.3332333 310.6582027 -0.0429193 0.9657659 NA 4.4642172
grp.duration_mins: Under 5 mins 0.6999423 0.2308747 3.0316973 0.0024318 0.2676486 1.1767544

The results from this new reduced model largely align with the findings from the original multiple model. However, just as overfitting a model with needless information is a poor practice, so is underfitting a model by not including everything that is truly important. To ensure validity of this reduced model’s structure, below I ran automatic stepwwise selection via R’s AIC function. The model’s summary statistics have been printed.

3.3 Algorithmically Reduced Multiple Logistic Model

library(MASS)
AIC_Model = stepAIC(Multiple_Reduced_Log_Model, 
                      scope = list(lower=formula(Multiple_Reduced_Log_Model),upper=formula(Multiple_Full_Log_Model)),
                      direction = "forward",   # forward selection
                      trace = 0   # do not show the details
                      )

kable(Multiple_Reduced_Summary_Stats,caption ="AIC-Created Multiple Logistic Regression Model Summary")
AIC-Created Multiple Logistic Regression Model Summary
Estimate Std. Error z value Pr(>|z|) 2.5 % 97.5 %
(Intercept) -2.8580203 0.3686009 -7.7536985 0.0000000 -3.5991921 -2.1520692
tempo 0.0035527 0.0018681 1.9017792 0.0572000 -0.0001176 0.0072091
grp.instrumentalness: s over 0.7 -2.1920495 0.3605499 -6.0797397 0.0000000 -2.9326652 -1.5064166
grp.instrumentalness: s under 0.1 0.6152133 0.2195892 2.8016558 0.0050841 0.2022035 1.0664024
grp.duration_mins: Over 10 mins -13.3332333 310.6582027 -0.0429193 0.9657659 NA 4.4642172
grp.duration_mins: Under 5 mins 0.6999423 0.2308747 3.0316973 0.0024318 0.2676486 1.1767544

We can see from our model summary that the AIC-created model and the reduced model I coded in via manual variable selection are identical. This is not surprising due to how clearly significant or insignificant the predictors were in the original full model. Below I will perform global GOF measures on the model to have record of a formal comparison of each one’s accuracy.

3.4 Model Comparison and Selection

 # GOF Full Multiple Model
Mult_Full_Residual_Deviance = Multiple_Full_Log_Model$deviance
Mult_Full_Null_Residual_Deviance = Multiple_Full_Log_Model$null.deviance
Mult_Full_aic = Multiple_Full_Log_Model$aic
Mult_Full_Goodness_Of_Fit = cbind(Mult_Full_Residual_Deviance,
                                  Mult_Full_Null_Residual_Deviance, Mult_Full_aic)

 # GOF Manually Reduced Multiple Model
Mult_Reduced_Residual_Deviance = Multiple_Reduced_Log_Model$deviance
Mult_Reduced_Null_Residual_Deviance = Multiple_Reduced_Log_Model$null.deviance
Mult_Reduced_aic = Multiple_Reduced_Log_Model$aic
Mult_Reduced_Goodness_Of_Fit = cbind(Mult_Reduced_Residual_Deviance,
                                  Mult_Reduced_Null_Residual_Deviance, Mult_Reduced_aic)

 # GOF AIC Reduced Model
Mult_AIC_Residual_Deviance = AIC_Model$deviance
Mult_AIC_Null_Residual_Deviance = AIC_Model$null.deviance
Mult_AIC_aic = AIC_Model$aic
Mult_AIC_Goodness_Of_Fit = cbind(Mult_AIC_Residual_Deviance,
                                  Mult_AIC_Null_Residual_Deviance, Mult_AIC_aic)

Model_GOFs = rbind(Mult_Full_Goodness_Of_Fit, Mult_Reduced_Goodness_Of_Fit,
                   Mult_AIC_Goodness_Of_Fit)
rownames(Model_GOFs) = c("Full Model", "Manually Reduced Model", "AIC Reduced Model")
colnames(Model_GOFs) = c("Residual Deviance", "Null Deviance", "AIC")

kable(Model_GOFs, caption ="GOF Summary of Each Model")
GOF Summary of Each Model
Residual Deviance Null Deviance AIC
Full Model 2216.519 2463.55 2232.519
Manually Reduced Model 2224.352 2463.55 2236.352
AIC Reduced Model 2217.497 2463.55 2231.497

As we can see from the table above, all three models perform extremely similar. Since our AIC-created model has the lowest AIC, and its residual deviance is only a fraction greater than that of our full model, we will accept the AIC-created model as our final choice to use for interpetation of this dataset.

3.5 Takeaways from Multiple Logistic Regression Modeling Process

After the simple logistic regression modeling process earlier, we came away with the conclusion that a track’s tempo was ‘technically’ significant in it’s likelihood of being popular on Spotify, but the practical magnitude of its impact was so small that it was not a factor that deserves a great deal of emphasis in the real world. Our AIC-created model for the most part confirms this finding, (p ~ .0572).

The most important findings from our multiple logistic regression process come from what was discovered by our discretized variables. Tracks with very high instrumentalness (over 0.7 on this dataset’s scale) have a lesser likelihood of being popular on Spotify than those that fall in between 0.1 and 0.7 on said scale. Additionally, tracks under 0.1 on the scale actually have a slighly higher chance of being popular.

Regarding a track’s length, tracks being under 5 minutes does have a significant association with increased likelihood of popularity when compared to tracks ranging from 5 to 10 minutes and tracks beyond 10 minutes. However, there is not sufficient evidence that tracks going above 10 minutes significantly reduces their chance of being popular when compared to those in the 5 to 10 minute category.

Below is a visual displaying the odds ratios for relevant variables from my AIC-created model, they will be referenced in my final conclusion. The extreme estimate, standard error and odds ratio for speechiness can be disregarded and attributed largely to the vast majority of observations falling below 0.5 on the speechiness scale. That along with the variable’s extremely high p value, and standard error being practically identical to the standard error of the model’s null intercept lead us to believe that the summary statistics for speechiness can be chalked up to random chance, not true logistic association.

AIC_Model_Coef_Summary = summary(AIC_Model)$coef
AIC_Odds_Ratio = exp(coef(AIC_Model))
AIC_out.stats = cbind(AIC_Model_Coef_Summary, odds.ratio = AIC_Odds_Ratio)                 
kable(AIC_out.stats,caption = "AIC-Created Multiple Regression Model Coefficients W/ Odds Ratios")
AIC-Created Multiple Regression Model Coefficients W/ Odds Ratios
Estimate Std. Error z value Pr(>|z|) odds.ratio
(Intercept) -17.1140371 375.3481554 -0.0455951 0.9636330 0.0000000
tempo 0.0036649 0.0018745 1.9551582 0.0505644 1.0036716
grp.instrumentalness: s over 0.7 -2.1888224 0.3605641 -6.0705491 0.0000000 0.1120486
grp.instrumentalness: s under 0.1 0.6234236 0.2196071 2.8388131 0.0045282 1.8653032
grp.duration_mins: Over 10 mins -13.3387936 310.5685700 -0.0429496 0.9657417 0.0000016
grp.duration_mins: Under 5 mins 0.7018358 0.2310153 3.0380492 0.0023812 2.0174530
grp.speechiness: s under 0.5 14.2414898 375.3479579 0.0379421 0.9697338 1531088.9917716

3.6 Final Association Modeling Conclusion

To conclude, in the most practical sense there are two factors that seem to have the greatest association on a track’s chance of being popular on Spotify. Those being its instrumentalness and length. Tracks that are under 5 minutes long have about twice the likelihood of being popular than not, while tracks that are longer than 10 minutes do not have nearly as great a chance. Tracks that are not very instrumental (s under 0.1) also have close to twice as great a chance of being popular than not. Much better odds than their very instrumental (s over 0.7) and mildly instrumental (0.1 < s < 0.7) counterparts.

4 Predictive Binary Logistic Model

Now that we have deeply studied the association of a track’s likelihood of popularity with some of the track’s characteristics, I am going to take our analysis one step further, away from the realm of strict observation and into the realm of educated and predictive speculation. For this process, I will utilize the models constructed in sections 3.1 through 3.3 above, as well as standard predictive modeling techniques like data splitting and ROC visualization.

4.1 Data Splitting

Below I split the data we have been analyzing into two subsets, one for training purposes and one for assessment purposes, at a rate of 70% to 30% respectively. This procedure is done to counteract the risk of overfitting a model and to ensure that the true rate of inaccurate prediction (both false positives and false negatives) is known.

n = nrow(Work_Data) # Sample size of data we are working with
Training_n =round(0.7*n) # Sample size of training data
Training_ID = sample(1:n, Training_n, replace = FALSE) 
  # The number of rows to choose from is 1:n, the number that will be selected is Training_n, replacement is set to FALSE since we are taking a subset of the data, so do not want to take the same observation(s) more than once.

Training_Data = Work_Data[Training_ID, ]
  # Creates a subset of Work_Data, assigning only the observations from the dataset that were marked with Training_ID to be in the Training_Data subset

Testing_Data = Work_Data[-Training_ID, ]
  # Creates an alternate subset of Work_Data, assigning only the observations from the dataset that were NOT marked with Training_ID to be in the Testing_Data subset

4.2 Cross Validation (CV) Process and Model Selection

Now I will use the aforementioned models, created in my multiple regression association analysis, in the CV process. I will create 3 folds (distinct groups) and set the cut-off probability (what constitutes “yes” or “no” as the value of our binary response variable) to the standard 0.5. The results and interpretation can be seen below.

# Multiple_Full_Log_Model = First multiple log model
# Multiple_Reduced_Log_Model = Manually reduced one
# AIC_Model = The final Algorithmically Reduced one we used

# 3 fold cross-validation
k = 3

fold_size = floor(dim(Training_Data)[1]/k)

## PE vectors for candidate models
PE1 = rep(0,5)
PE2 = rep(0,5)
PE3 = rep(0,5)
for(i in 1:k){  # Start of big FOR loop
  ## Training and testing folds
  valid.id = (fold_size*(i-1)+1):(fold_size*i)
  valid = Training_Data[valid.id, ]
  train.dat = Training_Data[-valid.id,]

  # Full Log Model
CVModel1 = glm(formula = popular ~ tempo + danceability + grp.speechiness + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = train.dat)
table(Training_Data$popular)


  # Manually Reduced Log Model
CVModel2 = glm(formula = popular ~ tempo + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = train.dat)

  
  # AIC Reduced (Final) Log Model
  CVModel3 = stepAIC(CVModel1, 
          scope = list(lower=formula(CVModel2),upper=formula(CVModel1)),
                      direction = "forward",   # forward selection
                      trace = 0 )

   ##  predicted probabilities of each candidate model
   pred01 = predict(CVModel1, newdata = valid, type="response")
   pred02 = predict(CVModel2, newdata = valid, type="response")
   pred03 = predict(CVModel3, newdata = valid, type="response")
   
   pre.outcome01 = ifelse(as.vector(pred01) > 0.5, "yes", "no")
   pre.outcome02 = ifelse(as.vector(pred02) > 0.5, "yes", "no")
   pre.outcome03 = ifelse(as.vector(pred03) > 0.5, "yes", "no")
      # Have our cut-off probability set to 0.5
        # HAVE to set responses as "yes" and "no", earlier had them set as "pos" and "neg" to mimic the case study code, which is why outcome was illogical
   
   PE1[i] = 1-sum(pre.outcome01 == valid$popular )/length(pred01)
   PE2[i] = 1-sum(pre.outcome02 == valid$popular )/length(pred02)
   PE3[i] = 1-sum(pre.outcome02 == valid$popular)/length(pred03)
   # valid$popular = “From the validation subset valid, take the column named popular.”
   
  } # End of big FOR loop
 
 # Display the output of the loop
Avg.Prediction_Error = cbind(PE1 = mean(PE1), PE2 = mean(PE2), PE3 = mean(PE3))
kable(Avg.Prediction_Error, digits = 4, caption = "Average Prediction Errors of the CV Models")
Average Prediction Errors of the CV Models
PE1 PE2 PE3
0.104 0.104 0.104
pred02 = predict(CVModel2, newdata = Testing_Data, type="response")
pred02.outcome = ifelse(as.vector(pred02)>0.5, "yes", "no")

accuracy = sum(pred02.outcome == Testing_Data$popular)/length(pred02)
kable(accuracy, caption="The actual accuracy of the final model")
The actual accuracy of the final model
x
0.8205772

We can see from above that, at a cut-off probability of 0.5, all three of our models used for associative analysis function with an average prediction error of ~ 0.1057 (this number can vary by about .01 either way depending on which run of the simulation we are on). This is not too surprising given the shared associative findings of each model (check Multiple Regression section). Given that I previously determined the AIC-produced model was the most ideal, that is the one I will consider as the ultimate or “final” predictive model. This model’s rate of accuracy, which was found through applying it’s structure to the testing dataset that we subsetted earlier, is about 82.67% (like prediction error, can have slight variation).

4.3 ROC Visualization

To assist with understanding, I also constructed a ROC (Receiver Operating Characteristics) curve. This image allows us to interpret the relationship between our different models’ specificity (rate of producing false positives) on the x axis VS sensitivity (rate of producing true positives) on the y axis.

We can see from the curve below that all three models are extremely similar in their ratio of sensitivity to specificity, and hence have almost identical AUC values. As mentioned above, we will utilize the AIC-reduced model given that it was deemed the most accurate for our previous association analysis.

######### Using a function to extract false positive and false negative values
TPR.FPR=function(pred){
  prob.seq = seq(0,1, length=50)  # 50 equally spaced cut-off probabilities
  pn=length(prob.seq)
  true.lab=as.vector(Training_Data$popular)
  TPR = NULL
  FPR = NULL
  ##
  for (i in 1:pn){
   pred.lab = as.vector(ifelse(pred >prob.seq[i],"yes", "no"))
   TPR[i] = length(which(true.lab=="yes" & pred.lab=="yes"))/length(which(true.lab=="yes"))
   FPR[i] = length(which(true.lab=="no" & pred.lab=="yes"))/length(which(true.lab=="no"))
  }
 cbind(FPR = FPR, TPR = TPR)
}

######### Calculating the AUC
if (!require("pROC")) {
   install.packages("pROC")
   library(pROC)
}
###  Candidate Models
 # Full Log Model
CVModel1 = glm(formula = popular ~ tempo + danceability + grp.speechiness + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = train.dat)

  # Manually Reduced Log Model
CVModel2 = glm(formula = popular ~ tempo + grp.instrumentalness + grp.duration_mins, family = binomial(link = "logit"), 
    data = train.dat)

  # AIC Reduced (Final) Log Model
  CVModel3 = stepAIC(CVModel1, 
          scope = list(lower=formula(CVModel2),upper=formula(CVModel1)),
                      direction = "forward",   # forward selection
                      trace = 0 )
##  predicted probabilities
pred01 = predict.glm(CVModel1, newdata = Training_Data, type="response") 
pred02 = predict.glm(CVModel2, newdata = Training_Data, type="response")
pred03 = predict.glm(CVModel3, newdata = Training_Data, type="response")
####
## ROC curve
 plot(TPR.FPR(pred01)[,1], TPR.FPR(pred01)[,2], 
      type="l", col="red", lty=1, lwd = 2, xlim=c(0,1), ylim=c(0,1),
      xlab = "False Positive Rate (FPR)",
      ylab ="True Positive Rate (TPR)",
      main = "ROC curves of the three candidate models",
      cex.main = 1.1,
      col.main = "black")
 lines(TPR.FPR(pred02)[,1], TPR.FPR(pred02)[,2],  col="darkblue", lty=2, lwd = 2)
 lines(TPR.FPR(pred03)[,1], TPR.FPR(pred03)[,2],  col="black", lty=3, lwd = 2)    

  ##
  category = Training_Data$popular == "yes"
  ROCobj01 <- roc(category, as.vector(pred01))
  ROCobj02 <- roc(category, as.vector(pred02))
  ROCobj03 <- roc(category, as.vector(pred03))
  AUC01 = round(auc(ROCobj01),4)
  AUC02 = round(auc(ROCobj02),4)
  AUC03 = round(auc(ROCobj03),4)
  ##
  legend("bottomright", c(paste("Full Log Model AUC = ",AUC01), 
                         paste("Man Reduced Log Model AUC = ",AUC02),
                         paste("AIC Reduced Log Model AUC = ", AUC03)),
        col=c("red","darkblue", "black"), lty=1:3, cex = 0.85, bty="n")

4.4 Predictive Modeling Takeaways

To conclude, if someone had interest in predicting a song’s chance of being popular given its length, instrumentalness and (to a minor extent) tempo, they should utilize the AIC-reduced model that was formed in section 3.3 above. I could see those most interested in this predictive model being agents, record label executives and aspiring artists, all attempting to “fix” their music to fit the mold of what tends to commercially succeed on the world’s largest streaming platform.

LS0tCnRpdGxlOiAiQmluYXJ5IExvZ2lzdGljIFByZWRpY3RpdmUgTW9kZWxpbmcgb2YgYSBUcmFjaydzIExpa2VsaWhvb2QgdG8gYmUgUG9wdWxhciBvbiBTcG90aWZ5IgphdXRob3I6ICJDaHJpcyBCYWhtIgpkYXRlOiAiMjAyNS0xMC0yMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHRvY19kZXB0aDogNAogICAgZmlnX3dpZHRoOiA2CiAgICBmaWdfaGVpZ2h0OiA0CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBsdW1lbgogICAgaGlnaGxpZ2h0OiB0YW5nbwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA0Ci0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpkaXYjVE9DIGxpIHsgICAgIC8qIHRhYmxlIG9mIGNvbnRlbnQgICovCiAgICBsaXN0LXN0eWxlOnVwcGVyLXJvbWFuOwogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOwogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsKICAgIGJhY2tncm91bmQtcG9zaXRpb246MDsKfQoKaDEudGl0bGUgeyAgICAvKiBsZXZlbCAxIGhlYWRlciBvZiB0aXRsZSAgKi8KICBmb250LXNpemU6IDI0cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoMSB7IC8qIEhlYWRlciAxIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTRweDsKICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovCi5oZWFkZXItc2VjdGlvbi1udW1iZXI6OmFmdGVyIHsKICBjb250ZW50OiAiLiI7Cn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KCmlmICghcmVxdWlyZSgia25pdHIiKSkgeyAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBjb25kaXRpb25hbCBzdGF0ZW1lbnQgdG8gZGV0ZWN0CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikgICAgICAgICAgICAgICAgICAjIHdoZXRoZXIgYSBwYWNrYWdlIHdhcyBpbnN0YWxsZWQgaW4KICAgbGlicmFyeShrbml0cikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91ciBtYWNoaW5lLiBJZiBub3QsIGluc3RhbGwgaXQgYW5kCn0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGxvYWQgaXQgdG8gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgoKaWYgKCFyZXF1aXJlKHRpZHl2ZXJzZSkpIHtsaWJyYXJ5KHRpZHl2c2Vyc2UpfSAKCmlmICghcmVxdWlyZShHR2FsbHkpKSB7bGlicmFyeShHR2FsbHkpfSAKCmlmICghcmVxdWlyZShrYWJsZUV4dHJhKSkge2xpYnJhcnkoa2FibGVFeHRyYSl9IAoKaWYgKCFyZXF1aXJlKGdncGxvdDIpKSB7bGlicmFyeShnZ3Bsb3QyKX0gCgppZiAoIXJlcXVpcmUoY2FyKSkge2xpYnJhcnkoY2FyKX0gCgppZiAoIXJlcXVpcmUoZHBseXIpKSB7bGlicmFyeShkcGx5cil9IAoKaWYgKCFyZXF1aXJlKHBhbmRlcikpIHtsaWJyYXJ5KHBhbmRlcil9IAoKaWYgKCFyZXF1aXJlKGNhcikpIHtsaWJyYXJ5KGNhcil9IAoKaWYgKCFyZXF1aXJlKCJzY2FsZXMiKSkgewppbnN0YWxsLnBhY2thZ2VzKCJzY2FsZXMiKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKbGlicmFyeSgic2NhbGVzIikgCn0KCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKCWVjaG8gPSBUUlVFLAoJbWVzc2FnZSA9IEZBTFNFLAoJd2FybmluZyA9IEZBTFNFLAoJY29tbWVudCA9IE5BLAoJcmVzdWx0cyA9IFRSVUUKKQogICAKYGBgCgojIEludHJvZHVjdGlvbiBhbmQgQmFja2dyb3VuZAoKSGVyZSB3ZSBoYXZlIGEgZGF0YXNldCBvcmlnaW5hbGx5IGNvbXBvc2VkIG9mIG92ZXIgNSwwMDAgc29uZ3MgdGhhdCB3ZXJlIGF2YWlsYWJsZSBvbiB0aGUgbXVzaWMtc3RyZWFtaW5nIHNlcnZpY2UgU3BvdGlmeSBvdmVyIHRoZSBjb3Vyc2Ugb2YgMjAyNC4gVGhlc2UgdHJhY2tzIHN0ZW0gZnJvbSBhIHdpZGUgYXJyYXkgb2YgdmFzdGx5IGRpZmZlcmVudCBnZW5yZXMgYW5kIHN0eWxlcyBvZiBjcmVhdGlvbi4gVGhlIGRhdGFzZXQgaXMgcHVibGljbHkgYXZhaWxhYmxlIG9uIHRoZSBwbGF0Zm9ybSBLYWdnbGUgYW5kIGlzIHByb3ZpZGVkIGluIHRoZSBSZWZlcmVuY2VzIHNlY3Rpb24gYmVsb3cuIEFsdGhvdWdoIHRoZSBkYXRhIG9yaWdpbmFsbHkgY29udGFpbmVkIDI5IHZhcmlhYmxlcywgb25seSAxMSBwbGF5IGEgcGFydGljdWxhcmx5IHJlbGV2YW50IHJvbGUgaW4gdGhlIGFuYWx5c2lzIEkgd2lzaCB0byBkbyBoZXJlLiBBIGJyZWFrZG93biBvZiB0aG9zZSB2YXJpYWJsZXMsIHRoZWlyIG1lYW5pbmdzIGFuZCBkYXRhIHR5cGVzIGlzIGJlbG93OgoKYGBge3IgVmFyaWFibGUgVGFibGUsIGVjaG89RkFMU0V9CmxpYnJhcnkoa25pdHIpCgpWYXJfVGFibGUgPSBkYXRhLmZyYW1lKAogIE5hbWUgPSBjKCJ0cmFja19wb3B1bGFyaXR5IiwgInBvcHVsYXIiLCAidHJhY2tfbmFtZSIsICJ0cmFja19hcnRpc3QiLCAicGxheWxpc3RfZ2VucmUiLCAicGxheWxpc3Rfc3ViZ2VucmUiLCAidGVtcG8iLCAiZGFuY2VhYmlsaXR5Iiwic3BlZWNoaW5lc3MiLCAiaW5zdHJ1bWVudGFsbmVzcyIsICJkdXJhdGlvbl9taW5zIiksCiAgCiAgTWVhbmluZyA9IGMoInNvbmcncyByYXRpbmcgb24gdGhlIDAgLSAxMDAgbWV0cmljIiwgImhvdyBwb3B1bGFyIHRoZSB0cmFjayBpcyAobW9yZSBvbiB0aGF0IGxhdGVyKSIsCiAgICAgICAgICAgICAgIm5hbWUgb2YgdGhlIHNvbmciLAogICAgICAgICAgICAgICJhcnRpc3QncyBzdGFnZSBuYW1lIiwgCiAgICAgICAgICAgICAgImdlbnJlIHRoYXQgU3BvdGlmeSBjbGFzc2lmaWVzIHRoZSBzb25nIGluIiwgCiAgICAgICAgICAgICAgInN1YmdlbnJlIHRoYXQgU3BvdGlmeSBjbGFzc2lmaWVzIHRoZSBzb25nIGluIiwgIAogICAgICAgICAgICAgICJ0aGUgc3BlZWQgb2YgYSB0cmFjaywgbWVhc3VyZWQgaW4gYmVhdHMgcGVyIG1pbnV0ZSAoQlBNKSIsIAogICAgICAgICAgICAgICJBIHNjb3JlIGRlc2NyaWJpbmcgaG93IHN1aXRhYmxlIGEgdHJhY2sgaXMgZm9yIGRhbmNpbmcgYmFzZWQgb24gdGVtcG8sIHJoeXRobSBzdGFiaWxpdHksIGJlYXQgc3RyZW5ndGggYW5kIG92ZXJhbGwgcmVndWxhcml0eSAoMCB0byAxIHNjYWxlKSIsIAogICAgICAgICAgICAgICJ0aGUgcHJlc2VuY2Ugb2YgYXVkaWJsZSB3b3JkcyAoMCB0byAxIHNjYWxlKSIsIAogICAgICAgICAgICAgICJsaWtlbGlob29kIHRoYXQgdGhlIHRyYWNrIGNvbnRhaW5zIGluc3RydW1lbnRhbHMgKDAgdG8gMSBzY2FsZSkgIiwgCiAgICAgICAgICAgICAgImxlbmd0aCBvZiB0cmFjayBpbiBtaW51dGVzIiksCiAgCiAgRGF0YV9UeXBlID0gYygiZG91YmxlIiwgImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiZG91YmxlIiwgImRvdWJsZSIsICJkb3VibGUiLCAiZG91YmxlIiwgImRvdWJsZSIpCikKCmthYmxlKFZhcl9UYWJsZSkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIikKYGBgCgpNYW55IG9mIHRoZSBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQgSSBjaG9zZSB0byBkaXNyZWdhcmQgYXMgdGhleSBlaXRoZXIgcGVydGFpbmVkIHRvIHVubmVjZXNzYXJ5IHRlY2huaWNhbCBpbmZvcm1hdGlvbiAoc3VjaCBhcyB0aGUgYWxnb3JpdGhtaWMgaWRlbnRpZmllciBmb3IgdGhlIHBhcnRpY3VsYXIgc29uZyBvciBpdHMgYWxidW0pLCBvciBleHRyZW1lbHkgbmljaGUgbXVzaWNhbCB0b3BpY3MgdGhhdCB0aGUgYXZlcmFnZSBsaXN0ZW5lciBvciBpbnRyb2R1Y3RvcnkgY3JpdGljIHdvdWxkIG1vc3QgbGlrZWx5IG5vdCBwaWNrIHVwIG9uIChzdWNoIGFzIG1vZGUgYW5kIGtleSkuCgojIyBPYmplY3RpdmUgZm9yIG15IEFuYWx5c2lzClVzaW5nIHRoaXMgZGF0YXNldCwgSSB3aXNoIHRvIHF1YW50aWZpYWJseSBleGFtaW5lIHdoYXQgbXVzaWNhbCBxdWFsaXRpZXMgYW5kIGNoYXJhY3RlcmlzdGljcyBsZWFkIHRvIGEgc29uZyAob3IgInRyYWNrIikgYmVjb21pbmcgZXh0cmVtZWx5IHN1Y2Nlc3NmdWwgKGhlbmNlIHBvcHVsYXIpIHZpYSBTcG90aWZ5LiBBcyBvZiBPY3RvYmVyIG9mIDIwMjUsIFNwb3RpZnkgaGFzIGEgbWFya2V0IGNhcCBvZiBvdmVyIDEzOSBiaWxsaW9uIGRvbGxhcnMgYW5kIGNsb3NlIHRvIDcwMCBtaWxsaW9uIHVzZXJzLiBUaGlzIG1lYW5zIHRoYXQgZm9yIGFueSBhc3BpcmluZyBtdXNpY2lhbiwgIm1ha2luZyBpdCBiaWciIG9uIFNwb3RpZnkgd291bGQgc2VydmUgYXMgYSBtb251bWVudGFsIGFuZCBsaWZlLWFsdGVyaW5nIG1pbGVzdG9uZS4gCgpTcG90aWZ5IGhhcyBhIG1ldHJpYyBrbm93biBhcyB0aGUgcG9wdWxhcml0eSBpbmRleCB3aGljaCBzZXJ2ZXMgdG8gbnVtZXJpY2FsbHkgcmVwcmVzZW50IHRoZSBwb3B1bGFyaXR5IG9mIHRyYWNrcyBvbiBpdHMgcGxhdGZvcm0uIFdoaWxlIHRoZSBleGFjdCBmb3JtdWxhIGJlaGluZCB0aGUgc2NvcmUgaXMga2VwdCBjb25maWRlbnRpYWwsIGNyaXRpY3MgaW4gdGhlIG11c2ljIGluZHVzdHJ5IGFuZCB0aG9zZSB3aXRoIHN0cm9uZyB0ZWNobmljYWwgYmFja2dyb3VuZHMgaGF2ZSB0aGVvcml6ZWQgdGhhdCB0aGUgcHJpbWFyeSBmYWN0b3JzIHdoaWNoIGluZmx1ZW5jZSBhIHRyYWNrJ3Mgc2NvcmUgYXJlIHRoZSBmb2xsb3dpbmc7IGl0cyB0b3RhbCBudW1iZXIgb2Ygc3RyZWFtcywgaXRzIHJlY2VudCBudW1iZXIgb2Ygc3RyZWFtcywgaXRzIG51bWJlciBvZiBkb3dubG9hZHMgYW5kIHNhdmVzLCBhbmQgaXRzIG1vbnRoLXRvLW1vbnRoIGdyb3d0aCBpbiBhbGwgdGhyZWUgb2Ygc2FpZCBtZXRyaWNzLgoKVGhpcyBwb3B1bGFyaXR5IGluZGV4IG9wZXJhdGVzIG9uIGEgc2NhbGUgcmFuZ2luZyBmcm9tIDAgdG8gMTAwLCB3aXRoIG9ubHkgdGhlIG1vc3Qgd2VsbC1lc3RhYmxpc2hlZCBzdGFycyByZWFjaGluZyB0aGUgOTB0aCBwZXJjZW50aWxlIGFuZCBoaWdoZXIuIFRoYXQgYmVpbmcgc2FpZCwgYW55IHRyYWNrIHdpdGggYSByYXRpbmcgb2YgNzUgb3IgaGlnaGVyIGlzIGxhcmdlbHkgY29uc2lkZXJlZCBhICJ0b3AgdHJhY2siIGJ5IGluZHVzdHJ5IGV4cGVydHMsIHNvIHRoYXQgaXMgdGhlIGludGVycHJldGF0aW9uIEkgd2lsbCBhcHBseSB3aXRoIG15IGFuYWx5c2lzLiBJIGNyZWF0ZWQgYSBjYXRlZ29yaWNhbCBiaW5hcnkgdmFyaWFibGUgInBvcHVsYXIiLCB3aGljaCBpcyB2YWx1ZWQgYXMgInllcyIgZm9yIHRyYWNrcyB3aXRoIGEgcG9wdWxhcml0eSBpbmRleCByYXRpbmcgb2YgNzUgb3IgaGlnaGVyLCBhbmQgIm5vIiBmb3IgdHJhY2tzIHdpdGggYSBsb3dlciByYXRpbmcuIEkgd2lsbCBwZXJmb3JtIGFzc29jaWF0aXZlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gYXNzZXNzIHdoaWNoLCBpZiBhbnksIG9mIHRoZSBmYWN0b3JzIG1lbnRpb25lZCBpbiBteSB2YXJpYWJsZSBicmVha2Rvd24gYWJvdmUgY29udHJpYnV0ZSB0byBhIHRyYWNrIGJlaW5nIGRlZW1lZCBwb3B1bGFyIG9yIG5vdC4gCgpGaW5hbGx5LCBhZnRlciBwZXJmb3JtaW5nIHNpbXBsZSBhbmQgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gcHJvY2Vzc2VzIHRvIGFuYWx5emUgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gYSB0cmFjaydzIHBvcHVsYXJpdHkgYW5kIGl0cyBzdGF0dXMgb2YgdGhlIHZhcmlhYmxlcyBJIGhhdmUgbGFpZCBvdXQsIEkgd2lsbCB0aGVuIHVzZSBwcmVkaWN0aXZlIG1vZGVsaW5nIHRlY2huaXF1ZXMgaW4gdGhlIGVmZm9ydCB0byBjcmVhdGUgYSBtb2RlbCB3aGljaCBjYW4gYWNjdXJhdGVseSBwcmVkaWN0IGEgcGFydGljdWxhciB0cmFjaydzIGxpa2VsaWhvb2Qgb2YgYmVpbmcgcG9wdWxhciBvbiBTcG90aWZ5LgoKYGBge3IgRGF0YSBSZWFkIGluIGFuZCBDbGVhbmluZywgaW5jbHVkZT1GQUxTRX0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICMgT3V0IG9mIHNjaWVudGlmaWMgbm90YXRpb24sIHBlcnNvbmFsIHByZWZlcmVuY2UKCkRhdGFfVXJsX0EgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NocmlzQjIzMjMvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9oaWdoX3BvcHVsYXJpdHlfc3BvdGlmeV9kYXRhLmNzdiIKQWJvdmVfNjggPSByZWFkX2RlbGltKERhdGFfVXJsX0EsIGRlbGltID0gIiwiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKRGF0YV9VcmxfQiA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ2hyaXNCMjMyMy9TVEEzMjEvcmVmcy9oZWFkcy9tYWluL2xvd19wb3B1bGFyaXR5X3Nwb3RpZnlfZGF0YS5jc3YiCkJlbG93XzY4ID0gcmVhZF9kZWxpbShEYXRhX1VybF9CLCBkZWxpbSA9ICIsIiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCgojIyMgTGluZSBiZWxvdyB3YXMgdXNlZCBmb3IgaW5pdGlhbCB2aXN1YWwgcHVycG9zZXMgCiNBYm92ZV82OCA9IEFib3ZlXzY4WywgYygidHJhY2tfbmFtZSIsICJ0cmFja19hbGJ1bV9uYW1lIiwgInRyYWNrX2FydGlzdCIsICJwbGF5bGlzdF9uYW1lIiwgInBsYXlsaXN0X2dlbnJlIiwgInBsYXlsaXN0X3N1YmdlbnJlIiwgInRyYWNrX3BvcHVsYXJpdHkiLCAiZW5lcmd5IiwgInRlbXBvIiwgImRhbmNlYWJpbGl0eSIsICJsb3VkbmVzcyIsICJsaXZlbmVzcyIsICJ2YWxlbmNlIiwgInNwZWVjaGluZXNzIiwgImluc3RydW1lbnRhbG5lc3MiLCAia2V5IiwgImR1cmF0aW9uX21zIiwgInRpbWVfc2lnbmF0dXJlIiwgInRyYWNrX2hyZWYiLCAibW9kZSIsICJ1cmkiLCAidHlwZSIsICJ0cmFja19hbGJ1bV9yZWxlYXNlX2RhdGUiLCAiYW5hbHlzaXNfdXJsIiwgImlkIiwgInRyYWNrX2FsYnVtX2lkIiwgInBsYXlsaXN0X2lkIiwgInRyYWNrX2lkIiwgImFjb3VzdGljbmVzcyIpXQoKCkRhdGEgPSByYmluZChBYm92ZV82OCwgQmVsb3dfNjgpCiAgRGF0YSRkdXJhdGlvbl9taW5zID0gRGF0YSRkdXJhdGlvbl9tcy82MDAwMApEYXRhJHBvcHVsYXIgPSBpZmVsc2UoRGF0YSR0cmFja19wb3B1bGFyaXR5ID4gNzUsICJ5ZXMiLCAibm8iKQoKRGF0YSA9IGRwbHlyOjpzZWxlY3QoRGF0YSwgdHJhY2tfcG9wdWxhcml0eSwgcG9wdWxhciwgdHJhY2tfbmFtZSwgdHJhY2tfYWxidW1fbmFtZSwgdHJhY2tfYXJ0aXN0LCBwbGF5bGlzdF9uYW1lLCBwbGF5bGlzdF9nZW5yZSwgcGxheWxpc3Rfc3ViZ2VucmUsIGVuZXJneSwgdGVtcG8sIGRhbmNlYWJpbGl0eSwgbG91ZG5lc3MsIGxpdmVuZXNzLCB2YWxlbmNlLCBzcGVlY2hpbmVzcywgaW5zdHJ1bWVudGFsbmVzcywga2V5LCBkdXJhdGlvbl9taW5zKQoKdW5pcXVlKERhdGEkcGxheWxpc3RfZ2VucmUpCgojIEJyZWFrZG93biBvZiBWYXJpYWJsZXMgS2VwdAogICAjIElkZW50aWZ5aW5nIFZhcmlhYmxlczoKICAgICAgICMgdHJhY2tfbmFtZQogICAgICAgIyB0cmFja19hbGJ1bV9uYW1lCiAgICAgICAjIHRyYWNrX2FydGlzdAogICAjIENhdGVnb3JpY2FsIEluZm86CiAgICAgICAjIHBsYXlsaXN0X25hbWUKICAgICAgICMgcGxheWxpc3RfZ2VucmUKICAgICAgICMgcGxheWxpc3Rfc3ViZ2VucmUKICAgICAgICMgKioqKiBwb3B1bGFyICoqKiogYmluYXJ5IHJlc3BvbnNlIHZhcmlhYmxlIHNlbGYtY3JlYXRlZAogICAjIFF1YW50aXRhdGl2ZSBJbmZvOgogICAgICAgIyB0cmFja19wb3B1bGFyaXR5CiAgICAgICAjIGVuZXJneQogICAgICAgIyB0ZW1wbwogICAgICAgIyBkYW5jZWFiaWxpdHkKICAgICAgICMgbG91ZG5lc3MKICAgICAgICMgbGl2ZW5lc3MKICAgICAgICMgdmFsZW5jZQogICAgICAgIyBzcGVlY2hpbmVzcwogICAgICAgIyBpbnN0cnVtZW50YWxuZXNzCiAgICAgICAjIGtleQogICAgICAgIyBkdXJhdGlvbl9tcwogICAKICAgICMgMTcgc2VsZWN0ZWQgYWJvdmUgZnJvbSBvcmlnaW5hbCBkYXRhIHNldCwgcGx1cyBwb3B1bGFyCiAgIAogICMgVmFyaWFibGVzIHdlIGFyZSBub3QgaW5jbHVkaW5nIGluIHJlZHVjZWQgZGF0YSBzZXQgYXJlIGxhcmdlbHkgdGVjaG5pY2FsIHRyYWNraW5nIHZhcmlhYmxlcyBzdWNoIGFzIGlkIGFuZCBhbmFseXNpc191cmwsIG9yIHZlcnkgbmljaGUgbXVzaWNhbCBtZXRyaWNzIHRoYXQgdGhlIGF2ZXJhZ2UgbGlzdGVuZXIgd291bGQgYmUgdW5hd2FyZSBvZiBzdWNoIGFzIG1vZGUuCgoKIyMjIyBDaGVja2luZyBmb3IgRHVwbGljYXRlczoKCkR1cGxpY2F0ZV9Tb25ncyA9IERhdGFbZHVwbGljYXRlZChEYXRhJHRyYWNrX25hbWUpIHwgZHVwbGljYXRlZChEYXRhJHRyYWNrX25hbWUsIGZyb21MYXN0ID0gVFJVRSksIF0KRHVwbGljYXRlX1NvbmdzX1JlZHVjZWQgPSBkcGx5cjo6c2VsZWN0KER1cGxpY2F0ZV9Tb25ncywgdHJhY2tfbmFtZSwgdHJhY2tfYXJ0aXN0LCBwbGF5bGlzdF9uYW1lLCBwbGF5bGlzdF9nZW5yZSwgcGxheWxpc3Rfc3ViZ2VucmUpCgpEdXBsaWNhdGVfU29uZ19Db3VudHMgPSBhcy5kYXRhLmZyYW1lKHRhYmxlKER1cGxpY2F0ZV9Tb25ncyR0cmFja19uYW1lKSkgICAKICAjIDgzNyBpbnN0YW5jZXMgb2YgdGhlIHNhbWUgc29uZyBiZWluZyBsaXN0ZWQgbW9yZSB0aGFuIG9uY2UKCm5hbWVzKER1cGxpY2F0ZV9Tb25nX0NvdW50cykgPSBjKCJ0cmFja19uYW1lIiwgImNvdW50IikKICAjIDM3OCBzb25ncyBsaXN0ZWQgbW9yZSB0aGFuIG9uY2UKCnN1bShEdXBsaWNhdGVfU29uZ19Db3VudHMkY291bnQpICAgIyBTdW0gZnVuY3Rpb24gY29uZmlybXMgdGhlcmUgYXJlIDgzNyB0b3RhbCByZXBlYXRzCgogIAojIEdvaW5nIHRvIHJlZHVjZSBtYWluIGRhdGFzZXQgdG8gb25seSBvbmUgb2JzZXJ2YXRpb24gcGVyIHNvbmcsIHVzZWQgYmFzZSBSIGZ1bmN0aW9uIHRvIGtlZXAgZmlyc3Qgb2NjdXJlbmNlIG9mIGVhY2ggc29uZwoKIyMjIyBOZXdfRGF0YSA9IG5vIGR1cGxpY2F0ZXMKTmV3X0RhdGEgPSBEYXRhWyFkdXBsaWNhdGVkKERhdGEkdHJhY2tfbmFtZSksIF0KCiMgR29pbmcgdG8gZWxpbWluYXRlIGdlbnJlcyB0aGF0IGFyZSBub3QgcHJldmFsZW50IGluIE5vcnRoIEFtZXJpY2EgYW5kIEV1cm9wZSwgYXMgbXkgYW5hbHlzaXMgd2lsbCBhcHBseSBsYXJnZWx5IHRvIGF1ZGllbmNlIGluIFdlc3Rlcm4gd29ybGQKCnVuaXF1ZShOZXdfRGF0YSRwbGF5bGlzdF9nZW5yZSkKCk5ld19EYXRhJFdlc3QgPSBpZmVsc2UoCiAgTmV3X0RhdGEkcGxheWxpc3RfZ2VucmUgJWluJSBjKCJicmF6aWxpYW4iLCAiYXJhYmljIiwgImluZGlhbiIsICJtYW5kb3BvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYW50b3BvcCIsICJrLXBvcCIsICJ0dXJraXNoIiwgImtvcmVhbiIsICJqLXBvcCIsICJ3b3JsZCIsICJsYXRpbiIsICJlbGVjdHJvbmljIiksCiAgIm5vIiwKICAieWVzIgopCgp0YWJsZShOZXdfRGF0YSRXZXN0KQoKIyMjIyBOZXdfRGF0YTIgPSBDb25maW5lZCB0byBXZXN0ZXJuIG11c2ljIGdlbnJlcwpOZXdfRGF0YTIgPSBOZXdfRGF0YVtOZXdfRGF0YSRXZXN0IT0gIm5vIiwgXQpjb2xuYW1lcyhOZXdfRGF0YTIpCgojIyMjIFdvcmtfRGF0YSA9IFJlZHVjZWQgbnVtYmVyIG9mIHZhcmlhYmxlcyB0byBwZXJmb3JtIGFuYWx5c2lzIG9uLCBtaXNzaW5nIHZhbHVlIGNoZWNrcyBwZXJmb3JtZWQKV29ya19EYXRhID0gZHBseXI6OnNlbGVjdChOZXdfRGF0YTIsIHRyYWNrX3BvcHVsYXJpdHksIHBvcHVsYXIsIHRyYWNrX25hbWUsIHRyYWNrX2FydGlzdCwgcGxheWxpc3RfZ2VucmUsIHBsYXlsaXN0X3N1YmdlbnJlLCB0ZW1wbywgZGFuY2VhYmlsaXR5LCBzcGVlY2hpbmVzcywgaW5zdHJ1bWVudGFsbmVzcywgZHVyYXRpb25fbWlucykKCldvcmtfRGF0YSA9IHNvcnRfYnkoV29ya19EYXRhLCBXb3JrX0RhdGEkdHJhY2tfcG9wdWxhcml0eSwgZGVjcmVhc2luZz1UUlVFKQoKc3VtKGlzLm5hKFdvcmtfRGF0YSkpICMgNSBtaXNzaW5nIHZhbHVlcyBpbiBkYXRhc2V0CgpNaXNzaW5nX1Jvd3MgPSBXb3JrX0RhdGFbIWNvbXBsZXRlLmNhc2VzKFdvcmtfRGF0YSksIF0KICMgQWxsIDUgbWlzc2luZyB2YWx1ZXMgY29tZSBmcm9tIG9uZSBvYnNlcnZhdGlvbiwgdGhlIHNvbmcgaXMgIk1ha2UgSXQiIGJ5IEJlcmhhbmlvLiBJIHdpbGwganVzdCByZW1vdmUgdGhpcyBzb25nIGZyb20gdGhlIGRhdGFzZXQgdXNpbmcgZmlsdGVyIGZ1bmN0aW9uIGJlbG93LgoKV29ya19EYXRhID0gZmlsdGVyKFdvcmtfRGF0YSwgdHJhY2tfbmFtZSAhPSAiTWFrZSBJdCIgKQoKICB0cmFja19wb3B1bGFyaXR5ID0gV29ya19EYXRhJHRyYWNrX3BvcHVsYXJpdHkgCiAgcG9wdWxhciA9IFdvcmtfRGF0YSRwb3B1bGFyCiAgdHJhY2tfbmFtZSA9IFdvcmtfRGF0YSR0cmFja19uYW1lCiAgdHJhY2tfYXJ0aXN0ID0gV29ya19EYXRhJHRyYWNrX2FydGlzdAogIHBsYXlsaXN0X2dlbnJlID0gV29ya19EYXRhJHBsYXlsaXN0X2dlbnJlCiAgcGxheWxpc3Rfc3ViZ2VucmUgPSBXb3JrX0RhdGEkcGxheWxpc3Rfc3ViZ2VucmUKICB0ZW1wbyA9IFdvcmtfRGF0YSR0ZW1wbwogIGRhbmNlYWJpbGl0eSA9IFdvcmtfRGF0YSRkYW5jZWFiaWxpdHkKICBzcGVlY2hpbmVzcyA9IFdvcmtfRGF0YSRzcGVlY2hpbmVzcwogIGluc3RydW1lbnRhbG5lc3MgPSBXb3JrX0RhdGEkaW5zdHJ1bWVudGFsbmVzcwogIGR1cmF0aW9uX21pbnMgPSBXb3JrX0RhdGEkZHVyYXRpb25fbWlucwogIApRdWFudGl0YXRpdmVfVmFyaWFibGVzID0gY2JpbmQodGVtcG8sIGRhbmNlYWJpbGl0eSwgc3BlZWNoaW5lc3MsIGluc3RydW1lbnRhbG5lc3MsIGR1cmF0aW9uX21pbnMpCgpDYXRlZ29yaWNhbF9WYXJpYWJsZXMgPSBjYmluZChwbGF5bGlzdF9nZW5yZSwgcGxheWxpc3Rfc3ViZ2VucmUpCiAKYGBgCgojIFNpbXBsZSBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsCgojIyBDaGVja2luZyBmb3IgUGFpcndpc2UgUmVsYXRpb25zaGlwcwpCZWZvcmUganVtcGluZyBpbnRvIG1vZGVsIGNyZWF0aW9uLCBJIGNyZWF0ZWQgYSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSBxdWFudGl0YXRpdmUgcHJlZGljdG9yIHZhcmlhYmxlcyBJIGFtIGdvaW5nIHRvIHVzZSBpbiBteSBhbmFseXNpcy4gVGhpcyBpcyBiZWNhdXNlIHZhcmlhYmxlcyB3aXRoIHZlcnkgaGlnaCBjb3JyZWxhdGlvbnMgKHIgPj0gMC44KSBvZnRlbiBjYW4gZnVuY3Rpb24gYXMgYWxtb3N0IG11bHRpcGxlcyBvZiBvbmUgYW5vdGhlciwgbGVhZGluZyB0byBpbmFjY3VyYXRlIGNvbmNsdXNpb25zLCBlc3BlY2lhbGx5IHdoZW4gcGVyZm9ybWluZyBtdWx0aXBsZSByZWdyZXNzaW9uLgpgYGB7cn0KY29yX21hdHJpeCA9IGNvcihRdWFudGl0YXRpdmVfVmFyaWFibGVzKQogICMgTm8gaGlnaCBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgdmFyaWFibGVzCgprYWJsZShjb3JfbWF0cml4LCBjYXB0aW9uID0gIkNvcnJlbGF0aW9uIE1hdHJpeCIpCmBgYAoKQWZ0ZXIgY3JlYXRpbmcgYSBjb3JyZWxhdGlvbiBtYXRyaXgsIHdlIGNhbiBzZWUgdGhhdCBub25lIG9mIHRoZSBxdWFudGl0YXRpdmUgcHJlZGljdG9yIHZhcmlhYmxlcyB0aGF0IHdlIGFyZSBleGFtaW5pbmcgaGF2ZSBhIGhpZ2ggY29ycmVsYXRpb24gd2l0aCBvbmUgYW5vdGhlci4gUHJhY3RpY2FsbHkgc3BlYWtpbmcsIHRoaXMgaXMgbm90IHZlcnkgc3VycHJpc2luZyBhcyBldmVuICJzaW1pbGFyIiBzb25ncyBieSB0aGUgc2FtZSBhcnRpc3Qgb3Igd2l0aGluIHRoZSBzYW1lIGdlbnJlIGNhbiBoYXZlIHdpbGRseSBkaWZmZXJlbnQgY2hhcmFjdGVyaXN0aWNzIHdoZW4gaXQgY29tZXMgdG8gdGVtcG8gb3IgYW55dGhpbmcgZWxzZSB3ZSBhcmUgbG9va2luZyBhdC4KCgojIyBQaWNraW5nIFZhcmlhYmxlIGZvciBTaW1wbGUgTW9kZWwKQmVsb3cgSSBjcmVhdGVkIGhpc3RvZ3JhbXMgdG8gcmVwcmVzZW50IHRoZSBkaXN0cmlidXRpb25zIG9mIGFsbCBmaXZlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgSSBhbSBnb2luZyB0byB1c2UgaW4gdGhpcyBhbmFseXNpcy4gVGhlIHB1cnBvc2UgZm9yIGRvaW5nIHN1Y2ggaXMgdG8gZGV0ZXJtaW5lIGlmIGFueSBzb3J0IG9mIHZhcmlhYmxlIG1hbmlwdWxhdGlvbiBpcyBuZWNlc3NhcnkgYmVmb3JlIHVzaW5nIHRoZSB2YXJpYWJsZSBpbiBvdXIgbG9naXN0aWMgbW9kZWwuCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDMpLCBtYXIgPSBjKDQsIDQsIDIsIDEpKSAgIyBtYXIgY29udHJvbHMgbWFyZ2lucyAKICAKICAKeWxpbWl0ID0gbWF4KGRlbnNpdHkoV29ya19EYXRhJHRlbXBvKSR5KQpoaXN0KFdvcmtfRGF0YSR0ZW1wbywgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIlRlbXBvIERpc3RyaWJ1dGlvbiIsIHhsYWI9IlRlbXBvIiwgCiAgICAgICBjb2wgPSAiYXp1cmUxIiwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikKICBsaW5lcyhkZW5zaXR5KFdvcmtfRGF0YSR0ZW1wbywgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKSAKCnlsaW1pdCA9IG1heChkZW5zaXR5KFdvcmtfRGF0YSRkYW5jZWFiaWxpdHkpJHkpCmhpc3QoV29ya19EYXRhJGRhbmNlYWJpbGl0eSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIkRhbmNlYWJpbGl0eSBEaXN0cmlidXRpb24iLCB4bGFiPSJEYW5jZWFiaWxpdHkiLCAKICAgICAgIGNvbCA9ICJhenVyZTEiLCBib3JkZXI9ImxpZ2h0c2VhZ3JlZW4iKQogIGxpbmVzKGRlbnNpdHkoV29ya19EYXRhJGRhbmNlYWJpbGl0eSwgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKSAKICAKeWxpbWl0ID0gbWF4KGRlbnNpdHkoV29ya19EYXRhJHNwZWVjaGluZXNzKSR5KQpoaXN0KFdvcmtfRGF0YSRzcGVlY2hpbmVzcywgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIlNwZWVjaGluZXNzIERpc3RyaWJ1dGlvbiIsIHhsYWI9IlNwZWVjaGluZXNzIiwgCiAgICAgICBjb2wgPSAiYXp1cmUxIiwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikKICBsaW5lcyhkZW5zaXR5KFdvcmtfRGF0YSRzcGVlY2hpbmVzcywgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKSAKICAKICAKeWxpbWl0ID0gbWF4KGRlbnNpdHkoV29ya19EYXRhJGluc3RydW1lbnRhbG5lc3MpJHkpCmhpc3QoV29ya19EYXRhJGluc3RydW1lbnRhbG5lc3MsIHByb2JhYmlsaXR5ID0gVFJVRSwgbWFpbiA9ICJJbnN0cnVtZW50YWxuZXNzIERpc3RyaWJ1dGlvbiIsIHhsYWI9Ikluc3RydW1lbnRhbG5lc3MiLCAKICAgICAgIGNvbCA9ICJhenVyZTEiLCBib3JkZXI9ImxpZ2h0c2VhZ3JlZW4iKQogIGxpbmVzKGRlbnNpdHkoV29ya19EYXRhJGluc3RydW1lbnRhbG5lc3MsIGFkanVzdD0yKSwgY29sPSJibHVlIikgCiAgCiAgCnlsaW1pdCA9IG1heChkZW5zaXR5KFdvcmtfRGF0YSRkdXJhdGlvbl9taW5zKSR5KQpoaXN0KFdvcmtfRGF0YSRkdXJhdGlvbl9taW5zLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiTGVuZ3RoIChtaW5zKSBEaXN0cmlidXRpb24iLCB4bGFiPSJMZW5ndGggKG1pbnMpIiwgCiAgICAgICBjb2wgPSAiYXp1cmUxIiwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikKICBsaW5lcyhkZW5zaXR5KFdvcmtfRGF0YSRkdXJhdGlvbl9taW5zLCBhZGp1c3Q9MiksIGNvbD0iYmx1ZSIpIAoKCmBgYAoKV2UgY2FuIHNlZSBmcm9tIHRoZSBoaXN0b2dyYW1zIHRoYXQgdGhlcmUgaXMgYW4gZXh0cmVtZSBza2V3IGluIHNwZWVjaGluZXNzLCBpbnN0cnVtZW50YWxuZXNzIGFuZCBsZW5ndGguIFdoaWxlIHRoZXJlIGFyZSBzbGlnaHQgc2tld3MgaW4gdGVtcG8gYW5kIGRhbmNlYWJpbGl0eSwgdGhleSBhcmUgKmZhciogbW9yZSBtb2RlcmF0ZS4gU2luY2UgdGVtcG8gYXBwZWFycyB0byBiZSB0aGUgbGVhc3Qgc2tld2VkIG9mIHRoZSB2YXJpYWJsZXMsIGFuZCBpdCdzIGNhbGN1bGF0aW9uIGlzIHRoZSBtb3N0IHN0cmFpZ2h0Zm9yd2FyZCAoYSBzaW1wbGUgbWVhc3VyZSBvZiBiZWF0cyBwZXIgbWludXRlKSwgSSB3aWxsIHVzZSB0ZW1wbyB0byBwZXJmb3JtIG15IHNpbXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uLgoKCiMjIENyZWF0aW5nIHRoZSBTaW1wbGUgTW9kZWwKVXNpbmcgYnVpbHQtaW4gUiBmdW5jdGlvbnMsIEkgY3JlYXRlZCBhIHNpbXBsZSBiaW5hcnkgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCwgaW4gd2hpY2ggdGhlIHRlbXBvIG9mIGEgc29uZyBpcyBtZWFudCB0byBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IHRoYXQgdHJhY2sgaXMgcG9wdWxhciBvciBub3QgKHBvcHVsYXJpdHkgc2NvcmUgb2YgNzUgb3Igbm90KS4gSW4gdGhlIGNhbGN1bGF0aW9ucywgdGhlIGJhc2UgbGV2ZWwsIG9yIDAsIHdhcyBzdG9yZWQgZm9yIGluc3RhbmNlcyBvZiBhIHRyYWNrJ3MgcG9wdWxhcml0eSBiZWluZyBtYXJrZWQgYXMgIm5vIi4gV2hpbGUgdGhlIGluc3RhbmNlIGxldmVsLCBvciAxLCB3YXMgc3RvcmVkIGZvciBpbnN0YW5jZXMgb2YgYSB0cmFjaydzIHBvcHVsYXJpdHkgYmVpbmcgbWFya2VkIGFzICJ5ZXMiLgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KIyBDb252ZXJ0IHRoZSBjaGFyYWN0ZXIgdmFsdWVzIHRvIGZhY3RvcnMsIG5lY2Vzc2FyeSBmb3IgZ2xtIGZ1bmN0aW9uIHRvIHdvcmsKV29ya19EYXRhJHBvcHVsYXIgPSBmYWN0b3IoV29ya19EYXRhJHBvcHVsYXIsIGxldmVscyA9IGMoIm5vIiwgInllcyIpKQogIGludmlzaWJsZShzdHIoV29ya19EYXRhJHBvcHVsYXIpKQogIGludmlzaWJsZSh1bmlxdWUoV29ya19EYXRhJHBvcHVsYXIpKQpgYGAKYGBge3J9ClNpbXBsZV9Mb2dfTW9kZWwgPSBnbG0oZm9ybXVsYSA9IHBvcHVsYXIgfiB0ZW1wbywgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLCAKICAgIGRhdGEgPSBXb3JrX0RhdGEpCgppbnZpc2libGUoc3VtbWFyeShTaW1wbGVfTG9nX01vZGVsKSkKClNpbXBsZV9Mb2dfTW9kZWxfQ29lZl9TdW1tYXJ5ID0gc3VtbWFyeShTaW1wbGVfTG9nX01vZGVsKSRjb2VmICAgICAgICMgb3V0cHV0IHN0YXRzIG9mIGNvZWZmaWNpZW50cwpTaW1wbGVfQ29uZmlkZW5jZV9JbnRlcnZhbCA9IGNvbmZpbnQoU2ltcGxlX0xvZ19Nb2RlbCkgICAgICAgICAgICAgICAgICAgICAjIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9mIGJldGFzClNpbXBsZV9TdW1tYXJ5X1N0YXRzID0gY2JpbmQoU2ltcGxlX0xvZ19Nb2RlbF9Db2VmX1N1bW1hcnksIFNpbXBsZV9Db25maWRlbmNlX0ludGVydmFsLjk1PVNpbXBsZV9Db25maWRlbmNlX0ludGVydmFsKSAgICMgcm91bmRpbmcgb2ZmIGRlY2ltYWxzCmthYmxlKFNpbXBsZV9TdW1tYXJ5X1N0YXRzLGNhcHRpb24gPSAiU2ltcGxlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwgU3VtbWFyeSIpICAKYGBgClRoZSBzdW1tYXJ5IG9mIG91ciBtb2RlbCBhYm92ZSBsZWFkcyB1cyB0byBiZWxpZXZlIHRoYXQgYSB0cmFjaydzIHRlbXBvIGRvZXMgaGF2ZSBhIHBvc2l0aXZlIGFzc29jaWF0aW9uIHdpdGggdGhlIGxpa2VsaWhvb2QgdGhhdCBhIHRyYWNrIGlzIHBvcHVsYXIgb24gU3BvdGlmeSwgaG93ZXZlciB0aGF0IGFzc29jaWF0aW9uIGlzIHZlcnkgc21hbGwgaW4gcHJhY3RpY2FsIGVmZmVjdC4gRGVzcGl0ZSBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcCB2YWx1ZSwgJFxiZXRhJH4xfiBoYXMgYW4gZXN0aW1hdGVkIGFzc29jaWF0aW9uIHdpdGggdGhlIHByb2JhYmlsaXR5IG9mIGEgdHJhY2sgYmVpbmcgZGVlbWVkIHBvcHVsYXIgb2YgLjAwNiwgYW5kIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZGVlbWluZyB0aGUgYXNzb2NpYXRpb24gdG8gZmFsbCBiZXR3ZWVuIHRoZSBpbnRlcnZhbCBbLjAwMywgLjAwOV0uCgoKIyMgT2RkcyBSYXRpbyBTdW1tYXJ5CldoaWxlIHRoZSBkaXJlY3QgaW50ZXJwcmV0YXRpb24gb2YgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsJ3Mgc3VtbWFyeSBzdGF0aXN0aWNzIGNhbiBiZSBoZWxwZnVsLCBpdCBpcyBzb21ldGltZXMgbW9yZSBwcmFjdGljYWwgdG8gZXhhbWluZSBvdXIgbW9kZWwncyBvZGRzIHJhdGlvLiBBbiBvZGRzIHJhdGlvIHJlcHJlc2VudHMgdGhlIGxpa2VsaWhvb2Qgb2Ygc3VjY2VzcyBkaXZpZGVkIGJ5IHRoZSBsaWtlbGlob29kIG9mIGZhaWx1cmUuIEZvciBleGFtcGxlLCBhbiBvZGRzIHJhdGlvIG9mIDIgd291bGQgbWVhbiB0aGF0IHdoYXRldmVyIGluc3RhbmNlIHdlIGFyZSBjb25zaWRlcmluZyBhICJzdWNjZXNzIiBvY2N1cnMgdHdpY2UgdGhlIG51bWJlciBvZiB0aW1lcyB0aGFuIGl0IGZhaWxzIHRvIG9jY3VyLgpgYGB7cn0KIyBPZGRzIHJhdGlvClNpbXBsZV9Mb2dfTW9kZWxfQ29lZl9TdW1tYXJ5ID0gc3VtbWFyeShTaW1wbGVfTG9nX01vZGVsKSRjb2VmCk9kZHNfUmF0aW8gPSBleHAoY29lZihTaW1wbGVfTG9nX01vZGVsKSkKb3V0LnN0YXRzID0gY2JpbmQoU2ltcGxlX0xvZ19Nb2RlbF9Db2VmX1N1bW1hcnksIG9kZHMucmF0aW8gPSBPZGRzX1JhdGlvKSAgICAgICAgICAgICAgICAgCmthYmxlKG91dC5zdGF0cyxjYXB0aW9uID0gIlNpbXBsZSBSZWdyZXNzaW9uIE1vZGVsIENvZWZmaWNpZW50cyBXLyBPZGRzIFJhdGlvcyIpCmBgYApUaGUgb2RkcyByYXRpbyBiZXR3ZWVuIGEgdHJhY2sncyB0ZW1wbyBhbmQgaXQncyBwb3B1bGFyaXR5IHN0YXR1cyBpcyB+IDEuMDA2LiBTaW5jZSB0ZW1wbyBpcyBtZWFzdXJlZCBpbiBiZWF0cyBwZXIgbWludXRlIChCUE0pLCB0aGlzIG1lYW5zIHRoYXQgZm9yIGV2ZXJ5IGFkZGl0aW9uYWwgYmVhdCBwZXIgbWludXRlIHRoYXQgYSBzb25nIGlzIHJlY29yZGVkIGluLCBpdCdzIHByb2JhYmlsaXR5IG9mIGhhdmluZyBhIHBvcHVsYXJpdHkgc2NvcmUgYWJvdmUgNzUgaW5jcmVhc2VzIGJ5IGFib3V0IDAuNiUuCgojIyBHb29kbmVzcy1vZi1maXQgbWVhc3VyZXMKQmVsb3cgSSBjYWxjdWxhdGVkIHRoZSBnb29kbmVzcy1vZi1maXQgKEdPRikgbWVhc3VyZXMgZm9yIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBHZW5lcmFsbHkgc3BlYWtpbmcsIGl0IGlzIGJlbmVmaWNpYWwgdG8gZmluZCBhIG1vZGVsJ3MgR09GIGZlYXR1cmVzIGFzIHRoZXkgc2VydmUgYXMgcmVsYXRpdmUgY29tcGFyaXNvbnMgb2YgbW9kZWwgZWZmaWNpZW5jeSB3aGVuIGNvbXBhcmluZyBtdWx0aXBsZSBtb2RlbHMuIEluIHRoaXMgaW5zdGFuY2UsIHdlIG9ubHkgY3JlYXRlZCBvbmUgbW9kZWwgc28gZmFyLCBzbyB0aGUgbWVhc3VyZXMgYXJlIG5vdCBvZiBwcmFjdGljYWwgdXNlIGF0IHRoZSBtb21lbnQuCmBgYHtyfQpSZXNpZHVhbF9EZXZpYW5jZSA9IFNpbXBsZV9Mb2dfTW9kZWwkZGV2aWFuY2UKTnVsbF9SZXNpZHVhbF9EZXZpYW5jZSA9IFNpbXBsZV9Mb2dfTW9kZWwkbnVsbC5kZXZpYW5jZQphaWMgPSBTaW1wbGVfTG9nX01vZGVsJGFpYwpHb29kbmVzc19PZl9GaXQgPSBjYmluZChEZXZpYW5jZS5yZXNpZHVhbCA9IFJlc2lkdWFsX0RldmlhbmNlLCBOdWxsLkRldmlhbmNlLlJlc2lkdWFsID0gTnVsbF9SZXNpZHVhbF9EZXZpYW5jZSwKICAgICAgQUlDID0gYWljKQpwYW5kZXIoR29vZG5lc3NfT2ZfRml0KQoKYGBgCgojIyBTdWNjZXNzIFByb2JhYmlsaXR5IEN1cnZlCkJlbG93IEkgY3JlYXRlZCBhIHZpc3VhbCBvZiB0aGUgdGhlIG1vZGVsJ3MgUyBjdXJ2ZSAoYWxzbyBjYWxsZWQgc3VjY2VzcyBwcm9iYWJpbGl0eSBjdXJ2ZSBvciBsb2dpc3RpYyBjdXJ2ZSkuIFRoZSBTIGN1cnZlIGhlbHBzIHVzIHNlZSB0aGUgY2hhbmdlIGluIHByb2JhYmlsaXR5IG9mIGFuIGV2ZW50IGhhcHBlbmluZyAoYSB0cmFjayBiZWluZyBwb3B1bGFyIG9yIG5vdCBvbiBTcG90aWZ5KSwgaW4gcmVsYXRpb24gdG8gYSBjaGFuZ2UgaW4gdGhlIHZhbHVlIG9mIGEgcHJlZGljdG9yIHZhcmlhYmxlICh0ZW1wbyBpbiBCUE0gaW4gdGhpcyBpbnN0YW5jZSkuCmBgYHtyfQojIyMKCnRlbXBvX3JhbmdlID0gcmFuZ2UoV29ya19EYXRhJHRlbXBvKQp4ID0gc2VxKHRlbXBvX3JhbmdlWzFdLCB0ZW1wb19yYW5nZVsyXSwgbGVuZ3RoID0gMjAwKQpiZXRhLnggPSBjb2VmKFNpbXBsZV9Mb2dfTW9kZWwpWzFdICsgY29lZihTaW1wbGVfTG9nX01vZGVsKVsyXSp4CnN1Y2Nlc3MucHJvYiA9IGV4cChiZXRhLngpLygxK2V4cChiZXRhLngpKQpmYWlsdXJlLnByb2IgPSAxLygxK2V4cChiZXRhLngpKQp5bGltaXQgPSBtYXgoc3VjY2Vzcy5wcm9iLCBmYWlsdXJlLnByb2IpCiMjCmJldGExID0gY29lZihTaW1wbGVfTG9nX01vZGVsKVsyXQpzdWNjZXNzLnByb2IucmF0ZSA9IGJldGExKmV4cChiZXRhLngpLygxK2V4cChiZXRhLngpKV4yCiMjCiMjCnBhcihtZnJvdyA9IGMoMSwxKSkKcGxvdCh4LCBzdWNjZXNzLnByb2IsIHR5cGUgPSAibCIsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsCiAgICAgbWFpbiA9ICJUaGUgUHJvYmFiaWxpdHkgb2YgYSBUcmFjayBCZWluZyBcbiBDb25zaWRlcmVkIFBvcHVsYXIgb24gU3BvdGlmeSIsIAogICAgIHlsaW09YygwLCAxLjEqeWxpbWl0KSwKICAgICB4bGFiID0gIlRlbXBvIChCUE0pIiwKICAgICB5bGFiID0gInByb2JhYmlsaXR5IiwKICAgICBheGVzID0gRkFMU0UsCiAgICAgY29sLm1haW4gPSAiYmxhY2siLAogICAgIGNleC5tYWluID0gMS4xKQojIGxpbmVzKHgsIGZhaWx1cmUucHJvYixsd2QgPSAyLCBjb2wgPSAiZGFya3JlZCIpCmF4aXMoMSwgcG9zID0gMCkKYXhpcygyKQoKIyMKeS5yYXRlID0gbWF4KHN1Y2Nlc3MucHJvYi5yYXRlKQpwbG90KHgsIHN1Y2Nlc3MucHJvYi5yYXRlLCB0eXBlID0gImwiLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLAogICAgIG1haW4gPSAiVGhlIFJhdGUgb2YgQ2hhbmdlIGluIFByb2JhYmlsaXR5IG9mIGEgVHJhY2sgQmVpbmcgXG4gQ29uc2lkZXJlZCBQb3B1bGFyIG9uIFNwb3RpZnkiLCAKICAgICB4bGFiID0gIlRlbXBvIChCUE0pIiwKICAgICB5bGFiID0gIlJhdGUgb2YgQ2hhbmdlIiwKICAgICB5bGltPWMoMCwxLjEqeS5yYXRlKSwKICAgICBheGVzID0gRkFMU0UsCiAgICAgY29sLm1haW4gPSAiYmxhY2siLAogICAgIGNleC5tYWluID0gMS4xCiAgICAgKQpheGlzKDEsIHBvcyA9IDApCmF4aXMoMikKYGBgCgpUaGUgUyBDdXJ2ZSBwcm92aWRlcyB2aXN1YWwgY29uZmlybWF0aW9uIG9mIHRoZSBjb25jbHVzaW9ucyB3ZSB3ZXJlIGFibGUgdG8gZHJhdyBmcm9tIGFuYWx5emluZyBvdXIgY29lZmZpY2llbnQgc3VtbWFyeSwgY29uZmlkZW5jZSBpbnRlcnZhbHMgYW5kIG9kZHMgcmF0aW8uIFRoYXQgaXMsIHRoYXQgdGhlcmUgaXMgYSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGEgdHJhY2sncyB0ZW1wbyBhbmQgaXQncyBsaWtlbGlob29kIG9mIGJlaW5nIGEgcG9wdWxhciB0cmFjayBvbiBTcG90aWZ5LCAqKmJ1dCoqIHRoYXQgYXNzb2NpYXRpb24gaXMgdmVyeSBtaW5pbWFsIGluIHByYWN0aWNhbCBlZmZlY3QuCgpXZSBjYW4gc2VlIGZyb20gb3VyIHNlY29uZCBwbG90LCB0aGF0IGxvb2tlZCBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHJhdGUgb2YgY2hhbmdlIGluIHNhaWQgcHJvYmFiaWxpdHkgYW5kIGEgdHJhY2sncyB0ZW1wbywgdGhhdCBmcm9tIDUwIHRvIDIwMCBiZWF0cyBwZXIgbWludXRlLCB0aGUgcHJvYmFiaWxpdHkgb2YgYSB0cmFjayBiZWluZyBwb3B1bGFyJ3MgcG9zaXRpdmUgcmF0ZSBvZiBjaGFuZ2Ugb25seSBpbmNyZWFzZXMgZnJvbSAwLjAwMDYgdG8gMC4wMDEyLgoKIyMgVGFrZWF3YXlzIGZyb20gU2ltcGxlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwKVG8gY29uY2x1ZGUsIHdlIGNhbiBzYXkgdGhhdCBhbHRob3VnaCB0aGVyZSBpcyBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYW5kIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgdHJhY2sncyB0ZW1wbyBhbmQgdGhlIHByb2JhYmlsaXR5IHRoYXQgaXQgaXMgcG9wdWxhciBvbiBTcG90aWZ5LCB0aGF0IGFzc29jaWF0aW9uIGlzIG9mIHN1Y2ggYSBzbWFsbCBtYWduaXR1ZGUgdGhhdCBpdCBpcyByZWxhdGl2ZWx5IGEgbm9uLWZhY3RvciBpbiBwcmFjdGljYWwgc2Vuc2UuIFRoZXJlZm9yZSwgaXQgd291bGQgYmUgb3Zlcmx5IHNpbXBsaXN0aWMgYW5kIGh5cGVyYm9saWMgZm9yIG11c2ljIGxhYmVscyBvciBhZ2VudHMgdG8gYWdncmVzc2l2ZWx5IHB1c2ggdGhlaXIgYXJ0aXN0cyB0b3dhcmRzIGEgbW9yZSB1cC10ZW1wbyBwcm9kdWN0aW9uLCBhcyB0aGUgcmV0dXJuIG9uIHN1Y2ggYSBkZWNpc2lvbiB3b3VsZCBsaWtlbHkgbm90IGJlIHdvcnRoIHRoZSBjb3N0IG9mIHNvdXJpbmcgdGhlIHJlbGF0aW9uc2hpcCB3aXRoIHRoZSBhcnRpc3QgYXQgaGFuZC4gCgojIE11bHRpcGxlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwKQXMgd2Ugc2F3IGVhcmxpZXIgd2hlbiBjcmVhdGluZyBvdXIgc2ltcGxlIHJlZ3Jlc3Npb24gbW9kZWwsIHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IHJpc2sgb2YgbXVsdGljb2xsaW5lYXJpdHkgd2l0aCB0aGUgZGF0YSB3ZSBhcmUgd29ya2luZyB3aXRoLiBIb3dldmVyLCB3ZSBhbHNvIHNhdyB0aGVyZSBpcyBhbiBleHRyZW1lIHJpZ2h0d2FyZCBza2V3IGluIHRoZSBkaXN0cmlidXRpb24gb2Ygc3BlZWNoaW5lc3MsIGluc3RydW1lbnRhbG5lc3MgYW5kIGxlbmd0aC4gQmVsb3cgSSB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhvc2UgdGhyZWUgdmFyaWFibGVzLgpgYGB7cn0KIyMgUmVtZW1iZXIgaW4gdGhlIEhXIGluc3RydWN0aW9ucywgaW5jbHVkZSBub3QganVzdCB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBidXQgdHdvIGNhdGVnb3JpY2FsIG9uZXMgdG9vIChsZXQncyB1c2UgcGxheWxpc3RfZ2VucmUgYW5kIHBsYXlsaXN0X3N1YmdlbnJlKQoKcGFyKG1mcm93PWMoMSwyKSkKaGlzdChzcGVlY2hpbmVzcywgeGxhYj0iU3BlZWNoaW5lc3MiLCBtYWluID0gIiIpCmhpc3QoaW5zdHJ1bWVudGFsbmVzcywgeGxhYiA9ICJJbnN0cnVtZW50YWxuZXNzIiwgbWFpbiA9ICIiKQpoaXN0KGR1cmF0aW9uX21pbnMsIHhsYWIgPSAiTGVuZ3RoIChNaW5zKSIsIG1haW4gPSAiIikKYGBgCgpUbyBjb21iYXQgdGhlIHNrZXcgb2YgYWxsIHRocmVlIHZhcmlhYmxlcywgSSB3aWxsIHBlcmZvcm0gZGlzY3JldGl6YXRpb24gb24gZWFjaCBvZiB0aGVtLiBUaGUgY29kaW5nIGZvciBzdWNoIGNhbiBiZSBzZWVuIGJlbG93LgpgYGB7cn0KICAjIFNwZWVjaGluZXNzID0gInByZXNlbmNlIG9mIGF1ZGlibGUgd29yZHMgb24gMCB0byAxIHNjYWxlIgpncnAuc3BlZWNoaW5lc3MgPSBzcGVlY2hpbmVzcwpncnAuc3BlZWNoaW5lc3Nbc3BlZWNoaW5lc3MgPCAwLjVdID0gIjogcyB1bmRlciAwLjUiCmdycC5zcGVlY2hpbmVzc1tzcGVlY2hpbmVzcyA+IDAuNV0gPSAiOiBzIG92ZXIgMC41IgoKICAjIHN1bShzcGVlY2hpbmVzcyA+IDAuNSkgPSAxNAogICMgc3VtKHNwZWVjaGluZXNzIDwgMC41KSA9IDI2NDEKCgogICMgSW5zdHJ1bWVudGFsbmVzcyA9ICJsaWtlbGlob29kIHRoYXQgdGhlIHRyYWNrIGNvbnRhaW5zIGluc3RydW1lbnRhbHMgKDAgdG8gMSBzY2FsZSkiCmdycC5pbnN0cnVtZW50YWxuZXNzID0gaW5zdHJ1bWVudGFsbmVzcwpncnAuaW5zdHJ1bWVudGFsbmVzc1tpbnN0cnVtZW50YWxuZXNzIDwgMC4xXSA9ICI6IHMgdW5kZXIgMC4xIgpncnAuaW5zdHJ1bWVudGFsbmVzc1tpbnN0cnVtZW50YWxuZXNzID4gMC4xICYgaW5zdHJ1bWVudGFsbmVzcyA8PSAwLjddID0gIjogcyBmcm9tIDAuMSB0byAwLjciCmdycC5pbnN0cnVtZW50YWxuZXNzW2luc3RydW1lbnRhbG5lc3MgPiAwLjddID0gIjogcyBvdmVyIDAuNyIKIAogIyAgc3VtKGluc3RydW1lbnRhbG5lc3MgPCAwLjEpID0gMTgwMgogIyAgc3VtKGluc3RydW1lbnRhbG5lc3MgPiAwLjEgJiBpbnN0cnVtZW50YWxuZXNzIDwgMC43KSA9IDE5MwogIyAgc3VtKGluc3RydW1lbnRhbG5lc3MgPiAwLjcpID0gNjU5CiAgCgogICMgZHVyYXRpb25fbWlucyBiZWxvdwpncnAuZHVyYXRpb25fbWlucyA9IGR1cmF0aW9uX21pbnMKZ3JwLmR1cmF0aW9uX21pbnNbZHVyYXRpb25fbWlucyA8IDVdID0gIjogVW5kZXIgNSBtaW5zIgpncnAuZHVyYXRpb25fbWluc1tkdXJhdGlvbl9taW5zID49IDUgJiBkdXJhdGlvbl9taW5zIDw9IDEwXSA9ICI6IEJldHdlZW4gNSBhbmQgMTAgbWlucyIKZ3JwLmR1cmF0aW9uX21pbnNbZHVyYXRpb25fbWlucyA+IDEwXSA9ICI6IE92ZXIgMTAgbWlucyIKICAKIyBzdW0oZHVyYXRpb25fbWlucyA8IDUpID0gMjQyNAojIHN1bShkdXJhdGlvbl9taW5zID49IDUgJiBkdXJhdGlvbl9taW5zIDw9IDEwKSA9IDIxMQojIHN1bShkdXJhdGlvbl9taW5zID4gMTApID0gMjAKCgpXb3JrX0RhdGEkZ3JwLnNwZWVjaGluZXNzID0gZ3JwLnNwZWVjaGluZXNzCldvcmtfRGF0YSRncnAuaW5zdHJ1bWVudGFsbmVzcyA9IGdycC5pbnN0cnVtZW50YWxuZXNzCldvcmtfRGF0YSRncnAuZHVyYXRpb25fbWlucyA9IGdycC5kdXJhdGlvbl9taW5zCmBgYAoKCiMjIEJ1aWxkaW5nIEZ1bGwgTXVsdGlwbGUgTG9naXN0aWMgTW9kZWwKRm9yIHRoZSBmdWxsIG11bHRpcGxlIGxvZ2lzdGljIG1vZGVsLCBJIHdpbGwgaW5pdGlhbGx5IGluY2x1ZGUgdGhlIGNvbnRpbnVvdXMgbnVtZXJpY2FsIHZhcmlhYmxlcyB0ZW1wbyBhbmQgZGFuY2VhYmlsaXR5IGluIHRoZWlyIG9yaWdpbmFsIGZvcm0sIGFzIHdlbGwgYXMgdGhlIGRpc2NyZXRpemVkIHRyYW5zZm9ybWF0aW9ucyBvZiBpbnN0cnVtZW50YWxuZXNzLCBsZW5ndGggaW4gbWludXRlcyBhbmQgc3BlZWNoaW5lc3MuIEFmdGVyIGNvZGluZyB0aGlzIG1vZGVsIGluLCBJIHdpbGwgZXhhbWluZSBpdHMgc3VtbWFyeSBzdGF0aXN0aWNzLgoKYGBge3J9Ck11bHRpcGxlX0Z1bGxfTG9nX01vZGVsID0gZ2xtKGZvcm11bGEgPSBwb3B1bGFyIH4gdGVtcG8gKyBkYW5jZWFiaWxpdHkgKyBncnAuc3BlZWNoaW5lc3MgKyBncnAuaW5zdHJ1bWVudGFsbmVzcyArIGdycC5kdXJhdGlvbl9taW5zLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksIAogICAgZGF0YSA9IFdvcmtfRGF0YSkKCmludmlzaWJsZShzdW1tYXJ5KE11bHRpcGxlX0Z1bGxfTG9nX01vZGVsKSkKCk11bHRpcGxlX0Z1bGxfTG9nX01vZGVsX0NvZWZfU3VtbWFyeSA9IHN1bW1hcnkoTXVsdGlwbGVfRnVsbF9Mb2dfTW9kZWwpJGNvZWYKTXVsdGlwbGVfRnVsbF9Db25maWRlbmNlX0ludGVydmFsID0gY29uZmludChNdWx0aXBsZV9GdWxsX0xvZ19Nb2RlbCkgIAoKTXVsdGlwbGVfRnVsbF9TdW1tYXJ5X1N0YXRzID0gY2JpbmQoTXVsdGlwbGVfRnVsbF9Mb2dfTW9kZWxfQ29lZl9TdW1tYXJ5LCBNdWx0aXBsZV9GdWxsX0NvbmZpZGVuY2VfSW50ZXJ2YWwuOTU9TXVsdGlwbGVfRnVsbF9Db25maWRlbmNlX0ludGVydmFsKSAgICMgcm91bmRpbmcgb2ZmIGRlY2ltYWxzCgprYWJsZShNdWx0aXBsZV9GdWxsX1N1bW1hcnlfU3RhdHMsY2FwdGlvbiA9ICJGdWxsIE11bHRpcGxlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwgU3VtbWFyeSIpIAoKaW52aXNpYmxlKHRhYmxlKFdvcmtfRGF0YSRwb3B1bGFyLCBXb3JrX0RhdGEkZ3JwLnNwZWVjaGluZXNzKSkKaW52aXNpYmxlKHRhYmxlKFdvcmtfRGF0YSRwb3B1bGFyLCBXb3JrX0RhdGEkZ3JwLmluc3RydW1lbnRhbG5lc3MpKQppbnZpc2libGUodGFibGUoV29ya19EYXRhJHBvcHVsYXIsIFdvcmtfRGF0YSRncnAuZHVyYXRpb25fbWlucykpCiAgI05BJ3MgaW4ga2FibGUgZnVuY3Rpb24gZGlzcGxheSBhcmUgZHVlIHRvICJwZXJmZWN0IiByZXN1bHRzIGJldHdlZW4gY2VydGFpbiBsZXZlbHMgb2Ygb3VyIGRpc2NyZXRpemVkIHZhcmlhYmxlcyBhbmQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIG9mIHBvcHVsYXIgb3Igbm90LiBJZiBhbGwgb2JzZXJ2YXRpb25zIGluIGEgY2VydGFpbiBsZXZlbCByZXN1bHQgaW4gb25lIG9mIHRoZSB0d28gb3B0aW9ucyBmb3IgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLCB0aGVuIHRoYXQgY29lZmZpY2llbnQgd2lsbCB0cmVuZCB0b3dhcmRzIG5lZ2F0aXZlIG9yIHBvc2l0aXZlIGluZmluaXR5IHBlciBSJ3MgY2FsY3VsYXRpb25zLgpgYGAKCldlIGNhbiBzZWUgdGhlIGZvbGxvd2luZyBmcm9tIG91ciBtb2RlbCBzdW1tYXJ5LiBGaXJzdCwganVzdCBsaWtlIHRoZSByZXN1bHRzIGZyb20gb3VyIHNpbXBsZSByZWdyZXNzaW9uIG1vZGVsLCB0aGVyZSBpcyBvbmNlIGFnYWluIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhc3NvY2lhdGlvbiBiZXR3ZWVuIGEgdHJhY2sncyB0ZW1wbyBhbmQgaXRzIGxpa2VsaWhvb2Qgb2YgYmVpbmcgcG9wdWxhciBvbiBTcG90aWZ5IChwIH4gMC4wNDEzKS4gSG93ZXZlciB0aGF0IGFzc29jaWF0aW9uIGlzIHNvIHNtYWxsIHRoYXQgaXQgaXMgb2YgbGl0dGxlICpwcmFjdGljYWwqIGltcG9ydGFuY2UuIEFkZGl0aW9uYWxseSwgaXQgYXBwZWFycyB0aGF0IGEgdHJhY2sncyBpbnN0cnVtZW50YWxuZXNzIGFuZCBsZW5ndGggYWJzb2x1dGVseSBwbGF5IGEgcGFydCBpbiBpdHMgcHJvYmFiaWxpdHkgb2YgYmVpbmcgcG9wdWxhciBvbiBTcG90aWZ5LiAKCklmIHdlIGxvb2sgYXQgdGhlIGRpc2NyZXRpemVkIGJyZWFrZG93biBvZiBpbnN0cnVtZW50YWxuZXNzLCB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBhbiBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHRyYWNrcyB3aG9zZSBpbnN0cnVtZW50YWxuZXNzIHJhdGluZyBpcyBvZiAwLjcgYW5kIGhpZ2hlciwgb3IgbGVzcyB0aGFuIDAuMSwgd2hlbiBjb21wYXJlZCB0byB0cmFja3Mgd2hvc2UgcmF0aW5nIGlzIHNvbWV3aGVyZSBpbiB0aGUgbWlkZGxlLiBGb3IgdHJhY2sgbGVuZ3RoLCB0aGVyZSBhbHNvIGFwcGVhcnMgdG8gYmUgc2lnbmlmaWNhbnQgZXZpZGVuY2UgdGhhdCB0cmFja3MgbGVzcyB0aGFuIDUgbWludXRlcyBsb25nIGhhdmUgYSBncmVhdGVyIHByb2JhYmlsaXR5IG9mIGFjaGlldmluZyBwb3B1bGFyaXR5IG9uIFNwb3RpZnkgdGhhbiB0aG9zZSBvZiBhbnkgb3RoZXIgbGVuZ3RocyBpbiBvdXIgZGF0YXNldC4KCk9uIHRoZSBvdGhlciBoYW5kLCB0aGVyZSBpcyBub3QgZXZpZGVuY2UgdG8gc3VnZ2VzdCB0aGF0IGEgdHJhY2sncyBkYW5jZWFiaWxpdHkgb3Igc3BlZWNoaW5lc3MgaGF2ZSBzdWNoIGFuIGFzc29jaWF0aW9uIHdpdGggaXRzIHBvcHVsYXJpdHkuCgpBZnRlciBleGFtaW5pbmcgdGhlIG1vZGVsIHN1bW1hcnksIEkgYWxzbyBjaG9zZSB0byBydW4gVklGIG9uIHRoaXMgZnVsbCBtb2RlbCwgd2hpY2ggY2FuIGJlIHNlZW4gYmVsb3cuIEdpdmVuIHRoYXQgYWxsIEdWSUYgYXJlIGJlbG93IDEuMSwgd2UgY2FuIHNhZmVseSBzYXkgdGhhdCBtdWx0aWNvbGxpbmVhcml0eSBpcyBub3QgcGVydGluZW50IHRvIHRoaXMgZGF0YXNldC4gVGhpcyBmaW5kaW5nIGlzIGluIGxpbmUgd2l0aCB0aGUgdGFrZWF3YXkgZnJvbSBvdXIgcGFpcndpc2Ugc2NhdHRlcnBsb3QgZWFybGllciBvbi4KYGBge3J9CnZpZihNdWx0aXBsZV9GdWxsX0xvZ19Nb2RlbCkKYGBgCgojIyBNYW51YWxseSBSZWR1Y2VkIE11bHRpcGxlIExvZ2lzdGljIE1vZGVsClNpbmNlIHBhcnNpbW9ueSAoaGF2aW5nIGFzIGxlYXN0IGFtb3VudCBvZiB2YXJpYWJsZXMgYXMgcG9zc2libGUgd2l0aG91dCBsb3Npbmcgc2lnbmlmaWNhbnQgYWNjdXJhY3kgaW4gYXNzb2NpYXRpb24gZXhhbWluYXRpb24gb3IgcHJlZGljdGlvbiBhY2N1cmFjeSkgaXMgYW4gaW1wb3J0YW50IGFzcGVjdCBvZiBzdGF0aXN0aWNhbCBtb2RlbGluZywgSSBhbHNvIHRoZW4gY2hvc2UgdG8gY3JlYXRlIGEgcmVkdWNlZCB2ZXJzaW9uIG9mIG15IG11bHRpcGxlIGxvZ2lzdGljIG1vZGVsIGFib3ZlLiBJbiB0aGlzIG1vZGVsLCBJIGRpZCBub3QgaW5jbHVkZSBwcmVkaWN0b3JzIHdob3NlIGltcGFjdCB3YXMgdmVyeSBjbGVhcmx5IGluc2lnbmlmaWNhbnQgYXMgcGVyIHRoZSBvcmlnaW5hbCBmdWxsIG1vZGVsJ3Mgc3VtbWFyeSAoZGFuY2VhYmlsaXR5IGFuZCBzcGVlY2hpbmVzcykuIFRoZSBzdW1tYXJ5IGZvciB0aGlzIG5ldyByZWR1Y2VkIG1vZGVsIGlzIGJlbG93LgoKYGBge3J9Ck11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsID0gZ2xtKGZvcm11bGEgPSBwb3B1bGFyIH4gdGVtcG8gKyBncnAuaW5zdHJ1bWVudGFsbmVzcyArIGdycC5kdXJhdGlvbl9taW5zLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksIAogICAgZGF0YSA9IFdvcmtfRGF0YSkKCmludmlzaWJsZShzdW1tYXJ5KE11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsKSkKCk11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsX0NvZWZfU3VtbWFyeSA9IHN1bW1hcnkoTXVsdGlwbGVfUmVkdWNlZF9Mb2dfTW9kZWwpJGNvZWYKTXVsdGlwbGVfUmVkdWNlZF9Mb2dfTW9kZWxfQ29uZmlkZW5jZV9JbnRlcnZhbCA9IGNvbmZpbnQoTXVsdGlwbGVfUmVkdWNlZF9Mb2dfTW9kZWwpICAKCk11bHRpcGxlX1JlZHVjZWRfU3VtbWFyeV9TdGF0cyA9IGNiaW5kKE11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsX0NvZWZfU3VtbWFyeSwgTXVsdGlwbGVfUmVkdWNlZF9Db25maWRlbmNlX0ludGVydmFsLjk1PU11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsX0NvbmZpZGVuY2VfSW50ZXJ2YWwpICAgIyByb3VuZGluZyBvZmYgZGVjaW1hbHMKCmthYmxlKE11bHRpcGxlX1JlZHVjZWRfU3VtbWFyeV9TdGF0cyxjYXB0aW9uID0gIlJlZHVjZWQgTXVsdGlwbGUgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCBTdW1tYXJ5IikgCmBgYAoKVGhlIHJlc3VsdHMgZnJvbSB0aGlzIG5ldyByZWR1Y2VkIG1vZGVsIGxhcmdlbHkgYWxpZ24gd2l0aCB0aGUgZmluZGluZ3MgZnJvbSB0aGUgb3JpZ2luYWwgbXVsdGlwbGUgbW9kZWwuIEhvd2V2ZXIsIGp1c3QgYXMgb3ZlcmZpdHRpbmcgYSBtb2RlbCB3aXRoIG5lZWRsZXNzIGluZm9ybWF0aW9uIGlzIGEgcG9vciBwcmFjdGljZSwgc28gaXMgdW5kZXJmaXR0aW5nIGEgbW9kZWwgYnkgbm90IGluY2x1ZGluZyBldmVyeXRoaW5nIHRoYXQgaXMgdHJ1bHkgaW1wb3J0YW50LiBUbyBlbnN1cmUgdmFsaWRpdHkgb2YgdGhpcyByZWR1Y2VkIG1vZGVsJ3Mgc3RydWN0dXJlLCBiZWxvdyBJIHJhbiBhdXRvbWF0aWMgc3RlcHd3aXNlIHNlbGVjdGlvbiB2aWEgUidzIEFJQyBmdW5jdGlvbi4gVGhlIG1vZGVsJ3Mgc3VtbWFyeSBzdGF0aXN0aWNzIGhhdmUgYmVlbiBwcmludGVkLgoKIyMgQWxnb3JpdGhtaWNhbGx5IFJlZHVjZWQgTXVsdGlwbGUgTG9naXN0aWMgTW9kZWwKYGBge3J9CmxpYnJhcnkoTUFTUykKQUlDX01vZGVsID0gc3RlcEFJQyhNdWx0aXBsZV9SZWR1Y2VkX0xvZ19Nb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICBzY29wZSA9IGxpc3QobG93ZXI9Zm9ybXVsYShNdWx0aXBsZV9SZWR1Y2VkX0xvZ19Nb2RlbCksdXBwZXI9Zm9ybXVsYShNdWx0aXBsZV9GdWxsX0xvZ19Nb2RlbCkpLAogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLCAgICMgZm9yd2FyZCBzZWxlY3Rpb24KICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gMCAgICMgZG8gbm90IHNob3cgdGhlIGRldGFpbHMKICAgICAgICAgICAgICAgICAgICAgICkKCmthYmxlKE11bHRpcGxlX1JlZHVjZWRfU3VtbWFyeV9TdGF0cyxjYXB0aW9uID0iQUlDLUNyZWF0ZWQgTXVsdGlwbGUgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCBTdW1tYXJ5IikKYGBgCgpXZSBjYW4gc2VlIGZyb20gb3VyIG1vZGVsIHN1bW1hcnkgdGhhdCB0aGUgQUlDLWNyZWF0ZWQgbW9kZWwgYW5kIHRoZSByZWR1Y2VkIG1vZGVsIEkgY29kZWQgaW4gdmlhIG1hbnVhbCB2YXJpYWJsZSBzZWxlY3Rpb24gYXJlIGlkZW50aWNhbC4gVGhpcyBpcyBub3Qgc3VycHJpc2luZyBkdWUgdG8gaG93IGNsZWFybHkgc2lnbmlmaWNhbnQgb3IgaW5zaWduaWZpY2FudCB0aGUgcHJlZGljdG9ycyB3ZXJlIGluIHRoZSBvcmlnaW5hbCBmdWxsIG1vZGVsLiBCZWxvdyBJIHdpbGwgcGVyZm9ybSBnbG9iYWwgR09GIG1lYXN1cmVzIG9uIHRoZSBtb2RlbCB0byBoYXZlIHJlY29yZCBvZiBhIGZvcm1hbCBjb21wYXJpc29uIG9mIGVhY2ggb25lJ3MgYWNjdXJhY3kuCgojIyBNb2RlbCBDb21wYXJpc29uIGFuZCBTZWxlY3Rpb24KYGBge3J9CiAjIEdPRiBGdWxsIE11bHRpcGxlIE1vZGVsCk11bHRfRnVsbF9SZXNpZHVhbF9EZXZpYW5jZSA9IE11bHRpcGxlX0Z1bGxfTG9nX01vZGVsJGRldmlhbmNlCk11bHRfRnVsbF9OdWxsX1Jlc2lkdWFsX0RldmlhbmNlID0gTXVsdGlwbGVfRnVsbF9Mb2dfTW9kZWwkbnVsbC5kZXZpYW5jZQpNdWx0X0Z1bGxfYWljID0gTXVsdGlwbGVfRnVsbF9Mb2dfTW9kZWwkYWljCk11bHRfRnVsbF9Hb29kbmVzc19PZl9GaXQgPSBjYmluZChNdWx0X0Z1bGxfUmVzaWR1YWxfRGV2aWFuY2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNdWx0X0Z1bGxfTnVsbF9SZXNpZHVhbF9EZXZpYW5jZSwgTXVsdF9GdWxsX2FpYykKCiAjIEdPRiBNYW51YWxseSBSZWR1Y2VkIE11bHRpcGxlIE1vZGVsCk11bHRfUmVkdWNlZF9SZXNpZHVhbF9EZXZpYW5jZSA9IE11bHRpcGxlX1JlZHVjZWRfTG9nX01vZGVsJGRldmlhbmNlCk11bHRfUmVkdWNlZF9OdWxsX1Jlc2lkdWFsX0RldmlhbmNlID0gTXVsdGlwbGVfUmVkdWNlZF9Mb2dfTW9kZWwkbnVsbC5kZXZpYW5jZQpNdWx0X1JlZHVjZWRfYWljID0gTXVsdGlwbGVfUmVkdWNlZF9Mb2dfTW9kZWwkYWljCk11bHRfUmVkdWNlZF9Hb29kbmVzc19PZl9GaXQgPSBjYmluZChNdWx0X1JlZHVjZWRfUmVzaWR1YWxfRGV2aWFuY2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNdWx0X1JlZHVjZWRfTnVsbF9SZXNpZHVhbF9EZXZpYW5jZSwgTXVsdF9SZWR1Y2VkX2FpYykKCiAjIEdPRiBBSUMgUmVkdWNlZCBNb2RlbApNdWx0X0FJQ19SZXNpZHVhbF9EZXZpYW5jZSA9IEFJQ19Nb2RlbCRkZXZpYW5jZQpNdWx0X0FJQ19OdWxsX1Jlc2lkdWFsX0RldmlhbmNlID0gQUlDX01vZGVsJG51bGwuZGV2aWFuY2UKTXVsdF9BSUNfYWljID0gQUlDX01vZGVsJGFpYwpNdWx0X0FJQ19Hb29kbmVzc19PZl9GaXQgPSBjYmluZChNdWx0X0FJQ19SZXNpZHVhbF9EZXZpYW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE11bHRfQUlDX051bGxfUmVzaWR1YWxfRGV2aWFuY2UsIE11bHRfQUlDX2FpYykKCk1vZGVsX0dPRnMgPSByYmluZChNdWx0X0Z1bGxfR29vZG5lc3NfT2ZfRml0LCBNdWx0X1JlZHVjZWRfR29vZG5lc3NfT2ZfRml0LAogICAgICAgICAgICAgICAgICAgTXVsdF9BSUNfR29vZG5lc3NfT2ZfRml0KQpyb3duYW1lcyhNb2RlbF9HT0ZzKSA9IGMoIkZ1bGwgTW9kZWwiLCAiTWFudWFsbHkgUmVkdWNlZCBNb2RlbCIsICJBSUMgUmVkdWNlZCBNb2RlbCIpCmNvbG5hbWVzKE1vZGVsX0dPRnMpID0gYygiUmVzaWR1YWwgRGV2aWFuY2UiLCAiTnVsbCBEZXZpYW5jZSIsICJBSUMiKQoKa2FibGUoTW9kZWxfR09GcywgY2FwdGlvbiA9IkdPRiBTdW1tYXJ5IG9mIEVhY2ggTW9kZWwiKQpgYGAKCkFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgdGFibGUgYWJvdmUsIGFsbCB0aHJlZSBtb2RlbHMgcGVyZm9ybSBleHRyZW1lbHkgc2ltaWxhci4gU2luY2Ugb3VyIEFJQy1jcmVhdGVkIG1vZGVsIGhhcyB0aGUgbG93ZXN0IEFJQywgYW5kIGl0cyByZXNpZHVhbCBkZXZpYW5jZSBpcyBvbmx5IGEgZnJhY3Rpb24gZ3JlYXRlciB0aGFuIHRoYXQgb2Ygb3VyIGZ1bGwgbW9kZWwsIHdlIHdpbGwgYWNjZXB0IHRoZSBBSUMtY3JlYXRlZCBtb2RlbCBhcyBvdXIgZmluYWwgY2hvaWNlIHRvIHVzZSBmb3IgaW50ZXJwZXRhdGlvbiBvZiB0aGlzIGRhdGFzZXQuCgojIyBUYWtlYXdheXMgZnJvbSBNdWx0aXBsZSBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsaW5nIFByb2Nlc3MKQWZ0ZXIgdGhlIHNpbXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsaW5nIHByb2Nlc3MgZWFybGllciwgd2UgY2FtZSBhd2F5IHdpdGggdGhlIGNvbmNsdXNpb24gdGhhdCBhIHRyYWNrJ3MgdGVtcG8gd2FzICd0ZWNobmljYWxseScgc2lnbmlmaWNhbnQgaW4gaXQncyBsaWtlbGlob29kIG9mIGJlaW5nIHBvcHVsYXIgb24gU3BvdGlmeSwgYnV0IHRoZSBwcmFjdGljYWwgbWFnbml0dWRlIG9mIGl0cyBpbXBhY3Qgd2FzIHNvIHNtYWxsIHRoYXQgaXQgd2FzIG5vdCBhIGZhY3RvciB0aGF0IGRlc2VydmVzIGEgZ3JlYXQgZGVhbCBvZiBlbXBoYXNpcyBpbiB0aGUgcmVhbCB3b3JsZC4gT3VyIEFJQy1jcmVhdGVkIG1vZGVsIGZvciB0aGUgbW9zdCBwYXJ0IGNvbmZpcm1zIHRoaXMgZmluZGluZywgKHAgfiAuMDU3MikuIAoKVGhlIG1vc3QgaW1wb3J0YW50IGZpbmRpbmdzIGZyb20gb3VyIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcHJvY2VzcyBjb21lIGZyb20gd2hhdCB3YXMgZGlzY292ZXJlZCBieSBvdXIgZGlzY3JldGl6ZWQgdmFyaWFibGVzLiBUcmFja3Mgd2l0aCB2ZXJ5IGhpZ2ggaW5zdHJ1bWVudGFsbmVzcyAob3ZlciAwLjcgb24gdGhpcyBkYXRhc2V0J3Mgc2NhbGUpIGhhdmUgYSBsZXNzZXIgbGlrZWxpaG9vZCBvZiBiZWluZyBwb3B1bGFyIG9uIFNwb3RpZnkgdGhhbiB0aG9zZSB0aGF0IGZhbGwgaW4gYmV0d2VlbiAwLjEgYW5kIDAuNyBvbiBzYWlkIHNjYWxlLiBBZGRpdGlvbmFsbHksIHRyYWNrcyB1bmRlciAwLjEgb24gdGhlIHNjYWxlIGFjdHVhbGx5IGhhdmUgYSBzbGlnaGx5IGhpZ2hlciBjaGFuY2Ugb2YgYmVpbmcgcG9wdWxhci4KClJlZ2FyZGluZyBhIHRyYWNrJ3MgbGVuZ3RoLCB0cmFja3MgYmVpbmcgdW5kZXIgNSBtaW51dGVzIGRvZXMgaGF2ZSBhIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIHdpdGggaW5jcmVhc2VkIGxpa2VsaWhvb2Qgb2YgcG9wdWxhcml0eSB3aGVuIGNvbXBhcmVkIHRvIHRyYWNrcyByYW5naW5nIGZyb20gNSB0byAxMCBtaW51dGVzIGFuZCB0cmFja3MgYmV5b25kIDEwIG1pbnV0ZXMuIEhvd2V2ZXIsIHRoZXJlIGlzIG5vdCBzdWZmaWNpZW50IGV2aWRlbmNlIHRoYXQgdHJhY2tzIGdvaW5nIGFib3ZlIDEwIG1pbnV0ZXMgc2lnbmlmaWNhbnRseSByZWR1Y2VzIHRoZWlyIGNoYW5jZSBvZiBiZWluZyBwb3B1bGFyIHdoZW4gY29tcGFyZWQgdG8gdGhvc2UgaW4gdGhlIDUgdG8gMTAgbWludXRlIGNhdGVnb3J5LgoKQmVsb3cgaXMgYSB2aXN1YWwgZGlzcGxheWluZyB0aGUgb2RkcyByYXRpb3MgZm9yIHJlbGV2YW50IHZhcmlhYmxlcyBmcm9tIG15IEFJQy1jcmVhdGVkIG1vZGVsLCB0aGV5IHdpbGwgYmUgcmVmZXJlbmNlZCBpbiBteSBmaW5hbCBjb25jbHVzaW9uLiBUaGUgZXh0cmVtZSBlc3RpbWF0ZSwgc3RhbmRhcmQgZXJyb3IgYW5kIG9kZHMgcmF0aW8gZm9yIHNwZWVjaGluZXNzIGNhbiBiZSBkaXNyZWdhcmRlZCBhbmQgYXR0cmlidXRlZCBsYXJnZWx5IHRvIHRoZSB2YXN0IG1ham9yaXR5IG9mIG9ic2VydmF0aW9ucyBmYWxsaW5nIGJlbG93IDAuNSBvbiB0aGUgc3BlZWNoaW5lc3Mgc2NhbGUuIFRoYXQgYWxvbmcgd2l0aCB0aGUgdmFyaWFibGUncyBleHRyZW1lbHkgaGlnaCBwIHZhbHVlLCBhbmQgc3RhbmRhcmQgZXJyb3IgYmVpbmcgcHJhY3RpY2FsbHkgaWRlbnRpY2FsIHRvIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbW9kZWwncyBudWxsIGludGVyY2VwdCBsZWFkIHVzIHRvIGJlbGlldmUgdGhhdCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBzcGVlY2hpbmVzcyBjYW4gYmUgY2hhbGtlZCB1cCB0byByYW5kb20gY2hhbmNlLCBub3QgdHJ1ZSBsb2dpc3RpYyBhc3NvY2lhdGlvbi4KYGBge3J9CkFJQ19Nb2RlbF9Db2VmX1N1bW1hcnkgPSBzdW1tYXJ5KEFJQ19Nb2RlbCkkY29lZgpBSUNfT2Rkc19SYXRpbyA9IGV4cChjb2VmKEFJQ19Nb2RlbCkpCkFJQ19vdXQuc3RhdHMgPSBjYmluZChBSUNfTW9kZWxfQ29lZl9TdW1tYXJ5LCBvZGRzLnJhdGlvID0gQUlDX09kZHNfUmF0aW8pICAgICAgICAgICAgICAgICAKa2FibGUoQUlDX291dC5zdGF0cyxjYXB0aW9uID0gIkFJQy1DcmVhdGVkIE11bHRpcGxlIFJlZ3Jlc3Npb24gTW9kZWwgQ29lZmZpY2llbnRzIFcvIE9kZHMgUmF0aW9zIikKYGBgCgojIyBGaW5hbCBBc3NvY2lhdGlvbiBNb2RlbGluZyBDb25jbHVzaW9uClRvIGNvbmNsdWRlLCBpbiB0aGUgbW9zdCBwcmFjdGljYWwgc2Vuc2UgdGhlcmUgYXJlIHR3byBmYWN0b3JzIHRoYXQgc2VlbSB0byBoYXZlIHRoZSBncmVhdGVzdCBhc3NvY2lhdGlvbiBvbiBhIHRyYWNrJ3MgY2hhbmNlIG9mIGJlaW5nIHBvcHVsYXIgb24gU3BvdGlmeS4gVGhvc2UgYmVpbmcgaXRzIGluc3RydW1lbnRhbG5lc3MgYW5kIGxlbmd0aC4gVHJhY2tzIHRoYXQgYXJlIHVuZGVyIDUgbWludXRlcyBsb25nIGhhdmUgYWJvdXQgKip0d2ljZSoqIHRoZSBsaWtlbGlob29kIG9mIGJlaW5nIHBvcHVsYXIgdGhhbiBub3QsIHdoaWxlIHRyYWNrcyB0aGF0IGFyZSBsb25nZXIgdGhhbiAxMCBtaW51dGVzIGRvIG5vdCBoYXZlIG5lYXJseSBhcyBncmVhdCBhIGNoYW5jZS4gVHJhY2tzIHRoYXQgYXJlIG5vdCB2ZXJ5IGluc3RydW1lbnRhbCAocyB1bmRlciAwLjEpIGFsc28gaGF2ZSBjbG9zZSB0byB0d2ljZSBhcyBncmVhdCBhIGNoYW5jZSBvZiBiZWluZyBwb3B1bGFyIHRoYW4gbm90LiBNdWNoIGJldHRlciBvZGRzIHRoYW4gdGhlaXIgdmVyeSBpbnN0cnVtZW50YWwgKHMgb3ZlciAwLjcpIGFuZCBtaWxkbHkgaW5zdHJ1bWVudGFsICgwLjEgPCBzIDwgMC43KSBjb3VudGVycGFydHMuCgojIFByZWRpY3RpdmUgQmluYXJ5IExvZ2lzdGljIE1vZGVsCk5vdyB0aGF0IHdlIGhhdmUgZGVlcGx5IHN0dWRpZWQgdGhlIGFzc29jaWF0aW9uIG9mIGEgdHJhY2sncyBsaWtlbGlob29kIG9mIHBvcHVsYXJpdHkgd2l0aCBzb21lIG9mIHRoZSB0cmFjaydzIGNoYXJhY3RlcmlzdGljcywgSSBhbSBnb2luZyB0byB0YWtlIG91ciBhbmFseXNpcyBvbmUgc3RlcCBmdXJ0aGVyLCBhd2F5IGZyb20gdGhlIHJlYWxtIG9mIHN0cmljdCBvYnNlcnZhdGlvbiBhbmQgaW50byB0aGUgcmVhbG0gb2YgZWR1Y2F0ZWQgYW5kIHByZWRpY3RpdmUgc3BlY3VsYXRpb24uIEZvciB0aGlzIHByb2Nlc3MsIEkgd2lsbCB1dGlsaXplIHRoZSBtb2RlbHMgY29uc3RydWN0ZWQgaW4gc2VjdGlvbnMgMy4xIHRocm91Z2ggMy4zIGFib3ZlLCBhcyB3ZWxsIGFzIHN0YW5kYXJkIHByZWRpY3RpdmUgbW9kZWxpbmcgdGVjaG5pcXVlcyBsaWtlIGRhdGEgc3BsaXR0aW5nIGFuZCBST0MgdmlzdWFsaXphdGlvbi4KCiMjIERhdGEgU3BsaXR0aW5nCkJlbG93IEkgc3BsaXQgdGhlIGRhdGEgd2UgaGF2ZSBiZWVuIGFuYWx5emluZyBpbnRvIHR3byBzdWJzZXRzLCBvbmUgZm9yIHRyYWluaW5nIHB1cnBvc2VzIGFuZCBvbmUgZm9yIGFzc2Vzc21lbnQgcHVycG9zZXMsIGF0IGEgcmF0ZSBvZiA3MCUgdG8gMzAlIHJlc3BlY3RpdmVseS4gVGhpcyBwcm9jZWR1cmUgaXMgZG9uZSB0byBjb3VudGVyYWN0IHRoZSByaXNrIG9mIG92ZXJmaXR0aW5nIGEgbW9kZWwgYW5kIHRvIGVuc3VyZSB0aGF0IHRoZSB0cnVlIHJhdGUgb2YgaW5hY2N1cmF0ZSBwcmVkaWN0aW9uIChib3RoIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzKSBpcyBrbm93bi4KCmBgYHtyIFN1YnNldHRpbmcgRGF0YSBmb3IgVGVzdGluZyBhbmQgVHJhaW5pbmd9Cm4gPSBucm93KFdvcmtfRGF0YSkgIyBTYW1wbGUgc2l6ZSBvZiBkYXRhIHdlIGFyZSB3b3JraW5nIHdpdGgKVHJhaW5pbmdfbiA9cm91bmQoMC43Km4pICMgU2FtcGxlIHNpemUgb2YgdHJhaW5pbmcgZGF0YQpUcmFpbmluZ19JRCA9IHNhbXBsZSgxOm4sIFRyYWluaW5nX24sIHJlcGxhY2UgPSBGQUxTRSkgCiAgIyBUaGUgbnVtYmVyIG9mIHJvd3MgdG8gY2hvb3NlIGZyb20gaXMgMTpuLCB0aGUgbnVtYmVyIHRoYXQgd2lsbCBiZSBzZWxlY3RlZCBpcyBUcmFpbmluZ19uLCByZXBsYWNlbWVudCBpcyBzZXQgdG8gRkFMU0Ugc2luY2Ugd2UgYXJlIHRha2luZyBhIHN1YnNldCBvZiB0aGUgZGF0YSwgc28gZG8gbm90IHdhbnQgdG8gdGFrZSB0aGUgc2FtZSBvYnNlcnZhdGlvbihzKSBtb3JlIHRoYW4gb25jZS4KClRyYWluaW5nX0RhdGEgPSBXb3JrX0RhdGFbVHJhaW5pbmdfSUQsIF0KICAjIENyZWF0ZXMgYSBzdWJzZXQgb2YgV29ya19EYXRhLCBhc3NpZ25pbmcgb25seSB0aGUgb2JzZXJ2YXRpb25zIGZyb20gdGhlIGRhdGFzZXQgdGhhdCB3ZXJlIG1hcmtlZCB3aXRoIFRyYWluaW5nX0lEIHRvIGJlIGluIHRoZSBUcmFpbmluZ19EYXRhIHN1YnNldAoKVGVzdGluZ19EYXRhID0gV29ya19EYXRhWy1UcmFpbmluZ19JRCwgXQogICMgQ3JlYXRlcyBhbiBhbHRlcm5hdGUgc3Vic2V0IG9mIFdvcmtfRGF0YSwgYXNzaWduaW5nIG9ubHkgdGhlIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBkYXRhc2V0IHRoYXQgd2VyZSBOT1QgbWFya2VkIHdpdGggVHJhaW5pbmdfSUQgdG8gYmUgaW4gdGhlIFRlc3RpbmdfRGF0YSBzdWJzZXQKYGBgCgojIyBDcm9zcyBWYWxpZGF0aW9uIChDVikgUHJvY2VzcyBhbmQgTW9kZWwgU2VsZWN0aW9uCk5vdyBJIHdpbGwgdXNlIHRoZSBhZm9yZW1lbnRpb25lZCBtb2RlbHMsIGNyZWF0ZWQgaW4gbXkgbXVsdGlwbGUgcmVncmVzc2lvbiBhc3NvY2lhdGlvbiBhbmFseXNpcywgaW4gdGhlIENWIHByb2Nlc3MuIEkgd2lsbCBjcmVhdGUgMyBmb2xkcyAoZGlzdGluY3QgZ3JvdXBzKSBhbmQgc2V0IHRoZSBjdXQtb2ZmIHByb2JhYmlsaXR5ICh3aGF0IGNvbnN0aXR1dGVzIOKAnHllc+KAnSBvciDigJxub+KAnSBhcyB0aGUgdmFsdWUgb2Ygb3VyIGJpbmFyeSByZXNwb25zZSB2YXJpYWJsZSkgdG8gdGhlIHN0YW5kYXJkIDAuNS4gVGhlIHJlc3VsdHMgYW5kIGludGVycHJldGF0aW9uIGNhbiBiZSBzZWVuIGJlbG93LgoKYGBge3J9CiMgTXVsdGlwbGVfRnVsbF9Mb2dfTW9kZWwgPSBGaXJzdCBtdWx0aXBsZSBsb2cgbW9kZWwKIyBNdWx0aXBsZV9SZWR1Y2VkX0xvZ19Nb2RlbCA9IE1hbnVhbGx5IHJlZHVjZWQgb25lCiMgQUlDX01vZGVsID0gVGhlIGZpbmFsIEFsZ29yaXRobWljYWxseSBSZWR1Y2VkIG9uZSB3ZSB1c2VkCgojIDMgZm9sZCBjcm9zcy12YWxpZGF0aW9uCmsgPSAzCgpmb2xkX3NpemUgPSBmbG9vcihkaW0oVHJhaW5pbmdfRGF0YSlbMV0vaykKCiMjIFBFIHZlY3RvcnMgZm9yIGNhbmRpZGF0ZSBtb2RlbHMKUEUxID0gcmVwKDAsNSkKUEUyID0gcmVwKDAsNSkKUEUzID0gcmVwKDAsNSkKZm9yKGkgaW4gMTprKXsgICMgU3RhcnQgb2YgYmlnIEZPUiBsb29wCiAgIyMgVHJhaW5pbmcgYW5kIHRlc3RpbmcgZm9sZHMKICB2YWxpZC5pZCA9IChmb2xkX3NpemUqKGktMSkrMSk6KGZvbGRfc2l6ZSppKQogIHZhbGlkID0gVHJhaW5pbmdfRGF0YVt2YWxpZC5pZCwgXQogIHRyYWluLmRhdCA9IFRyYWluaW5nX0RhdGFbLXZhbGlkLmlkLF0KCiAgIyBGdWxsIExvZyBNb2RlbApDVk1vZGVsMSA9IGdsbShmb3JtdWxhID0gcG9wdWxhciB+IHRlbXBvICsgZGFuY2VhYmlsaXR5ICsgZ3JwLnNwZWVjaGluZXNzICsgZ3JwLmluc3RydW1lbnRhbG5lc3MgKyBncnAuZHVyYXRpb25fbWlucywgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLCAKICAgIGRhdGEgPSB0cmFpbi5kYXQpCnRhYmxlKFRyYWluaW5nX0RhdGEkcG9wdWxhcikKCgogICMgTWFudWFsbHkgUmVkdWNlZCBMb2cgTW9kZWwKQ1ZNb2RlbDIgPSBnbG0oZm9ybXVsYSA9IHBvcHVsYXIgfiB0ZW1wbyArIGdycC5pbnN0cnVtZW50YWxuZXNzICsgZ3JwLmR1cmF0aW9uX21pbnMsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgCiAgICBkYXRhID0gdHJhaW4uZGF0KQoKICAKICAjIEFJQyBSZWR1Y2VkIChGaW5hbCkgTG9nIE1vZGVsCiAgQ1ZNb2RlbDMgPSBzdGVwQUlDKENWTW9kZWwxLCAKICAgICAgICAgIHNjb3BlID0gbGlzdChsb3dlcj1mb3JtdWxhKENWTW9kZWwyKSx1cHBlcj1mb3JtdWxhKENWTW9kZWwxKSksCiAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiZm9yd2FyZCIsICAgIyBmb3J3YXJkIHNlbGVjdGlvbgogICAgICAgICAgICAgICAgICAgICAgdHJhY2UgPSAwICkKCiAgICMjICBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBvZiBlYWNoIGNhbmRpZGF0ZSBtb2RlbAogICBwcmVkMDEgPSBwcmVkaWN0KENWTW9kZWwxLCBuZXdkYXRhID0gdmFsaWQsIHR5cGU9InJlc3BvbnNlIikKICAgcHJlZDAyID0gcHJlZGljdChDVk1vZGVsMiwgbmV3ZGF0YSA9IHZhbGlkLCB0eXBlPSJyZXNwb25zZSIpCiAgIHByZWQwMyA9IHByZWRpY3QoQ1ZNb2RlbDMsIG5ld2RhdGEgPSB2YWxpZCwgdHlwZT0icmVzcG9uc2UiKQogICAKICAgcHJlLm91dGNvbWUwMSA9IGlmZWxzZShhcy52ZWN0b3IocHJlZDAxKSA+IDAuNSwgInllcyIsICJubyIpCiAgIHByZS5vdXRjb21lMDIgPSBpZmVsc2UoYXMudmVjdG9yKHByZWQwMikgPiAwLjUsICJ5ZXMiLCAibm8iKQogICBwcmUub3V0Y29tZTAzID0gaWZlbHNlKGFzLnZlY3RvcihwcmVkMDMpID4gMC41LCAieWVzIiwgIm5vIikKICAgICAgIyBIYXZlIG91ciBjdXQtb2ZmIHByb2JhYmlsaXR5IHNldCB0byAwLjUKICAgICAgICAjIEhBVkUgdG8gc2V0IHJlc3BvbnNlcyBhcyAieWVzIiBhbmQgIm5vIiwgZWFybGllciBoYWQgdGhlbSBzZXQgYXMgInBvcyIgYW5kICJuZWciIHRvIG1pbWljIHRoZSBjYXNlIHN0dWR5IGNvZGUsIHdoaWNoIGlzIHdoeSBvdXRjb21lIHdhcyBpbGxvZ2ljYWwKICAgCiAgIFBFMVtpXSA9IDEtc3VtKHByZS5vdXRjb21lMDEgPT0gdmFsaWQkcG9wdWxhciApL2xlbmd0aChwcmVkMDEpCiAgIFBFMltpXSA9IDEtc3VtKHByZS5vdXRjb21lMDIgPT0gdmFsaWQkcG9wdWxhciApL2xlbmd0aChwcmVkMDIpCiAgIFBFM1tpXSA9IDEtc3VtKHByZS5vdXRjb21lMDIgPT0gdmFsaWQkcG9wdWxhcikvbGVuZ3RoKHByZWQwMykKICAgIyB2YWxpZCRwb3B1bGFyID0g4oCcRnJvbSB0aGUgdmFsaWRhdGlvbiBzdWJzZXQgdmFsaWQsIHRha2UgdGhlIGNvbHVtbiBuYW1lZCBwb3B1bGFyLuKAnQogICAKICB9ICMgRW5kIG9mIGJpZyBGT1IgbG9vcAogCiAjIERpc3BsYXkgdGhlIG91dHB1dCBvZiB0aGUgbG9vcApBdmcuUHJlZGljdGlvbl9FcnJvciA9IGNiaW5kKFBFMSA9IG1lYW4oUEUxKSwgUEUyID0gbWVhbihQRTIpLCBQRTMgPSBtZWFuKFBFMykpCmthYmxlKEF2Zy5QcmVkaWN0aW9uX0Vycm9yLCBkaWdpdHMgPSA0LCBjYXB0aW9uID0gIkF2ZXJhZ2UgUHJlZGljdGlvbiBFcnJvcnMgb2YgdGhlIENWIE1vZGVscyIpCgpwcmVkMDIgPSBwcmVkaWN0KENWTW9kZWwyLCBuZXdkYXRhID0gVGVzdGluZ19EYXRhLCB0eXBlPSJyZXNwb25zZSIpCnByZWQwMi5vdXRjb21lID0gaWZlbHNlKGFzLnZlY3RvcihwcmVkMDIpPjAuNSwgInllcyIsICJubyIpCgphY2N1cmFjeSA9IHN1bShwcmVkMDIub3V0Y29tZSA9PSBUZXN0aW5nX0RhdGEkcG9wdWxhcikvbGVuZ3RoKHByZWQwMikKa2FibGUoYWNjdXJhY3ksIGNhcHRpb249IlRoZSBhY3R1YWwgYWNjdXJhY3kgb2YgdGhlIGZpbmFsIG1vZGVsIikKYGBgCgpXZSBjYW4gc2VlIGZyb20gYWJvdmUgdGhhdCwgYXQgYSBjdXQtb2ZmIHByb2JhYmlsaXR5IG9mIDAuNSwgYWxsIHRocmVlIG9mIG91ciBtb2RlbHMgdXNlZCBmb3IgYXNzb2NpYXRpdmUgYW5hbHlzaXMgZnVuY3Rpb24gd2l0aCBhbiBhdmVyYWdlIHByZWRpY3Rpb24gZXJyb3Igb2YgfiAwLjEwNTcgKHRoaXMgbnVtYmVyIGNhbiB2YXJ5IGJ5IGFib3V0IC4wMSBlaXRoZXIgd2F5IGRlcGVuZGluZyBvbiB3aGljaCBydW4gb2YgdGhlIHNpbXVsYXRpb24gd2UgYXJlIG9uKS4gVGhpcyBpcyBub3QgdG9vIHN1cnByaXNpbmcgZ2l2ZW4gdGhlIHNoYXJlZCBhc3NvY2lhdGl2ZSBmaW5kaW5ncyBvZiBlYWNoIG1vZGVsIChjaGVjayBNdWx0aXBsZSBSZWdyZXNzaW9uIHNlY3Rpb24pLiBHaXZlbiB0aGF0IEkgcHJldmlvdXNseSBkZXRlcm1pbmVkIHRoZSAqQUlDLXByb2R1Y2VkIG1vZGVsKiB3YXMgdGhlIG1vc3QgaWRlYWwsIHRoYXQgaXMgdGhlIG9uZSBJIHdpbGwgY29uc2lkZXIgYXMgdGhlIHVsdGltYXRlIG9yICJmaW5hbCIgcHJlZGljdGl2ZSBtb2RlbC4gKlRoaXMqIG1vZGVsJ3MgcmF0ZSBvZiBhY2N1cmFjeSwgd2hpY2ggd2FzIGZvdW5kIHRocm91Z2ggYXBwbHlpbmcgaXQncyBzdHJ1Y3R1cmUgdG8gdGhlIHRlc3RpbmcgZGF0YXNldCB0aGF0IHdlIHN1YnNldHRlZCBlYXJsaWVyLCBpcyBhYm91dCA4Mi42NyUgKGxpa2UgcHJlZGljdGlvbiBlcnJvciwgY2FuIGhhdmUgc2xpZ2h0IHZhcmlhdGlvbikuCgojIyBST0MgVmlzdWFsaXphdGlvbgpUbyBhc3Npc3Qgd2l0aCB1bmRlcnN0YW5kaW5nLCBJIGFsc28gY29uc3RydWN0ZWQgYSBST0MgKFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpY3MpIGN1cnZlLiBUaGlzIGltYWdlIGFsbG93cyB1cyB0byBpbnRlcnByZXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG91ciBkaWZmZXJlbnQgbW9kZWxzJyBzcGVjaWZpY2l0eSAocmF0ZSBvZiBwcm9kdWNpbmcgZmFsc2UgcG9zaXRpdmVzKSBvbiB0aGUgeCBheGlzIFZTIHNlbnNpdGl2aXR5IChyYXRlIG9mIHByb2R1Y2luZyB0cnVlIHBvc2l0aXZlcykgb24gdGhlIHkgYXhpcy4gCgpXZSBjYW4gc2VlIGZyb20gdGhlIGN1cnZlIGJlbG93IHRoYXQgYWxsIHRocmVlIG1vZGVscyBhcmUgZXh0cmVtZWx5IHNpbWlsYXIgaW4gdGhlaXIgcmF0aW8gb2Ygc2Vuc2l0aXZpdHkgdG8gc3BlY2lmaWNpdHksIGFuZCBoZW5jZSBoYXZlIGFsbW9zdCBpZGVudGljYWwgQVVDIHZhbHVlcy4gQXMgbWVudGlvbmVkIGFib3ZlLCB3ZSB3aWxsIHV0aWxpemUgdGhlIEFJQy1yZWR1Y2VkIG1vZGVsIGdpdmVuIHRoYXQgaXQgd2FzIGRlZW1lZCB0aGUgbW9zdCBhY2N1cmF0ZSBmb3Igb3VyIHByZXZpb3VzIGFzc29jaWF0aW9uIGFuYWx5c2lzLgpgYGB7cn0KIyMjIyMjIyMjIFVzaW5nIGEgZnVuY3Rpb24gdG8gZXh0cmFjdCBmYWxzZSBwb3NpdGl2ZSBhbmQgZmFsc2UgbmVnYXRpdmUgdmFsdWVzClRQUi5GUFI9ZnVuY3Rpb24ocHJlZCl7CiAgcHJvYi5zZXEgPSBzZXEoMCwxLCBsZW5ndGg9NTApICAjIDUwIGVxdWFsbHkgc3BhY2VkIGN1dC1vZmYgcHJvYmFiaWxpdGllcwogIHBuPWxlbmd0aChwcm9iLnNlcSkKICB0cnVlLmxhYj1hcy52ZWN0b3IoVHJhaW5pbmdfRGF0YSRwb3B1bGFyKQogIFRQUiA9IE5VTEwKICBGUFIgPSBOVUxMCiAgIyMKICBmb3IgKGkgaW4gMTpwbil7CiAgIHByZWQubGFiID0gYXMudmVjdG9yKGlmZWxzZShwcmVkID5wcm9iLnNlcVtpXSwieWVzIiwgIm5vIikpCiAgIFRQUltpXSA9IGxlbmd0aCh3aGljaCh0cnVlLmxhYj09InllcyIgJiBwcmVkLmxhYj09InllcyIpKS9sZW5ndGgod2hpY2godHJ1ZS5sYWI9PSJ5ZXMiKSkKICAgRlBSW2ldID0gbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0ibm8iICYgcHJlZC5sYWI9PSJ5ZXMiKSkvbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0ibm8iKSkKICB9CiBjYmluZChGUFIgPSBGUFIsIFRQUiA9IFRQUikKfQoKIyMjIyMjIyMjIENhbGN1bGF0aW5nIHRoZSBBVUMKaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpCiAgIGxpYnJhcnkocFJPQykKfQojIyMgIENhbmRpZGF0ZSBNb2RlbHMKICMgRnVsbCBMb2cgTW9kZWwKQ1ZNb2RlbDEgPSBnbG0oZm9ybXVsYSA9IHBvcHVsYXIgfiB0ZW1wbyArIGRhbmNlYWJpbGl0eSArIGdycC5zcGVlY2hpbmVzcyArIGdycC5pbnN0cnVtZW50YWxuZXNzICsgZ3JwLmR1cmF0aW9uX21pbnMsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgCiAgICBkYXRhID0gdHJhaW4uZGF0KQoKICAjIE1hbnVhbGx5IFJlZHVjZWQgTG9nIE1vZGVsCkNWTW9kZWwyID0gZ2xtKGZvcm11bGEgPSBwb3B1bGFyIH4gdGVtcG8gKyBncnAuaW5zdHJ1bWVudGFsbmVzcyArIGdycC5kdXJhdGlvbl9taW5zLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksIAogICAgZGF0YSA9IHRyYWluLmRhdCkKCiAgIyBBSUMgUmVkdWNlZCAoRmluYWwpIExvZyBNb2RlbAogIENWTW9kZWwzID0gc3RlcEFJQyhDVk1vZGVsMSwgCiAgICAgICAgICBzY29wZSA9IGxpc3QobG93ZXI9Zm9ybXVsYShDVk1vZGVsMiksdXBwZXI9Zm9ybXVsYShDVk1vZGVsMSkpLAogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLCAgICMgZm9yd2FyZCBzZWxlY3Rpb24KICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gMCApCiMjICBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcwpwcmVkMDEgPSBwcmVkaWN0LmdsbShDVk1vZGVsMSwgbmV3ZGF0YSA9IFRyYWluaW5nX0RhdGEsIHR5cGU9InJlc3BvbnNlIikgCnByZWQwMiA9IHByZWRpY3QuZ2xtKENWTW9kZWwyLCBuZXdkYXRhID0gVHJhaW5pbmdfRGF0YSwgdHlwZT0icmVzcG9uc2UiKQpwcmVkMDMgPSBwcmVkaWN0LmdsbShDVk1vZGVsMywgbmV3ZGF0YSA9IFRyYWluaW5nX0RhdGEsIHR5cGU9InJlc3BvbnNlIikKIyMjIwojIyBST0MgY3VydmUKIHBsb3QoVFBSLkZQUihwcmVkMDEpWywxXSwgVFBSLkZQUihwcmVkMDEpWywyXSwgCiAgICAgIHR5cGU9ImwiLCBjb2w9InJlZCIsIGx0eT0xLCBsd2QgPSAyLCB4bGltPWMoMCwxKSwgeWxpbT1jKDAsMSksCiAgICAgIHhsYWIgPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSAoRlBSKSIsCiAgICAgIHlsYWIgPSJUcnVlIFBvc2l0aXZlIFJhdGUgKFRQUikiLAogICAgICBtYWluID0gIlJPQyBjdXJ2ZXMgb2YgdGhlIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMiLAogICAgICBjZXgubWFpbiA9IDEuMSwKICAgICAgY29sLm1haW4gPSAiYmxhY2siKQogbGluZXMoVFBSLkZQUihwcmVkMDIpWywxXSwgVFBSLkZQUihwcmVkMDIpWywyXSwgIGNvbD0iZGFya2JsdWUiLCBsdHk9MiwgbHdkID0gMikKIGxpbmVzKFRQUi5GUFIocHJlZDAzKVssMV0sIFRQUi5GUFIocHJlZDAzKVssMl0sICBjb2w9ImJsYWNrIiwgbHR5PTMsIGx3ZCA9IDIpICAgIAoKICAjIwogIGNhdGVnb3J5ID0gVHJhaW5pbmdfRGF0YSRwb3B1bGFyID09ICJ5ZXMiCiAgUk9Db2JqMDEgPC0gcm9jKGNhdGVnb3J5LCBhcy52ZWN0b3IocHJlZDAxKSkKICBST0NvYmowMiA8LSByb2MoY2F0ZWdvcnksIGFzLnZlY3RvcihwcmVkMDIpKQogIFJPQ29iajAzIDwtIHJvYyhjYXRlZ29yeSwgYXMudmVjdG9yKHByZWQwMykpCiAgQVVDMDEgPSByb3VuZChhdWMoUk9Db2JqMDEpLDQpCiAgQVVDMDIgPSByb3VuZChhdWMoUk9Db2JqMDIpLDQpCiAgQVVDMDMgPSByb3VuZChhdWMoUk9Db2JqMDMpLDQpCiAgIyMKICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYyhwYXN0ZSgiRnVsbCBMb2cgTW9kZWwgQVVDID0gIixBVUMwMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIk1hbiBSZWR1Y2VkIExvZyBNb2RlbCBBVUMgPSAiLEFVQzAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJBSUMgUmVkdWNlZCBMb2cgTW9kZWwgQVVDID0gIiwgQVVDMDMpKSwKICAgICAgICBjb2w9YygicmVkIiwiZGFya2JsdWUiLCAiYmxhY2siKSwgbHR5PTE6MywgY2V4ID0gMC44NSwgYnR5PSJuIikKYGBgCgojIyBQcmVkaWN0aXZlIE1vZGVsaW5nIFRha2Vhd2F5cyAKVG8gY29uY2x1ZGUsIGlmIHNvbWVvbmUgaGFkIGludGVyZXN0IGluIHByZWRpY3RpbmcgYSBzb25nJ3MgY2hhbmNlIG9mIGJlaW5nIHBvcHVsYXIgZ2l2ZW4gaXRzIGxlbmd0aCwgaW5zdHJ1bWVudGFsbmVzcyBhbmQgKHRvIGEgbWlub3IgZXh0ZW50KSB0ZW1wbywgdGhleSBzaG91bGQgdXRpbGl6ZSB0aGUgQUlDLXJlZHVjZWQgbW9kZWwgdGhhdCB3YXMgZm9ybWVkIGluIHNlY3Rpb24gMy4zIGFib3ZlLiBJIGNvdWxkIHNlZSB0aG9zZSBtb3N0IGludGVyZXN0ZWQgaW4gdGhpcyBwcmVkaWN0aXZlIG1vZGVsIGJlaW5nIGFnZW50cywgcmVjb3JkIGxhYmVsIGV4ZWN1dGl2ZXMgYW5kIGFzcGlyaW5nIGFydGlzdHMsIGFsbCBhdHRlbXB0aW5nIHRvICJmaXgiIHRoZWlyIG11c2ljIHRvIGZpdCB0aGUgbW9sZCBvZiB3aGF0IHRlbmRzIHRvIGNvbW1lcmNpYWxseSBzdWNjZWVkIG9uIHRoZSB3b3JsZCdzIGxhcmdlc3Qgc3RyZWFtaW5nIHBsYXRmb3JtLiAKCiMgUmVmZXJlbmNlczoKCk9yaWdpbmFsIERhdGFzZXQgU291cmNlOgoKLSBodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL3NvbG9tb25hbWVoL3Nwb3RpZnktbXVzaWMtZGF0YXNldD9zZWxlY3Q9aGlnaF9wb3B1bGFyaXR5X3Nwb3RpZnlfZGF0YS5jc3YKCkRhdGFzZXQgRG93bmxvYWQgTGlua3MgdmlhIEdpdGh1YjoKCi0gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NocmlzQjIzMjMvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9oaWdoX3BvcHVsYXJpdHlfc3BvdGlmeV9kYXRhLmNzdgoKLSBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ2hyaXNCMjMyMy9TVEEzMjEvcmVmcy9oZWFkcy9tYWluL2xvd19wb3B1bGFyaXR5X3Nwb3RpZnlfZGF0YS5jc3YK