Assignment Objectives

  • Comprehend the likelihood function and its properties.

  • Master the maximum likelihood estimation framework and required components.

  • Understand the plug-in principle underlying MLE.

  • Implement maximum likelihood estimation procedures in R.


Policies of Using AI Tools

Policy on AI Tool Use: Students must adhere to the AI tool policy specified in the course syllabus. The direct copying of AI-generated content is strictly prohibited. All submitted work must reflect your own understanding; where external tools are consulted, content must be thoroughly rephrased and synthesized in your own words.

Code Inclusion Requirement: Any code included in your essay must be properly commented to explain the purpose and/or expected output of key code lines. Submitting AI-generated code without meaningful, student-added comments will not be accepted.


Gamma Distribution Revisited

Let \(X\) be the two parameter Gamma random variable with density function

\[ f(x \mid \alpha, \beta) = \frac{1}{\Gamma(\alpha)\beta^\alpha} x^{\alpha-1} e^{-x/\beta} \ \ \text{for} \ \ x > 0, \]

where with \(\alpha > 0\) (shape), \(\beta>0\) (scale), and

\[ \Gamma(\alpha) = \int_{0}^{\infty} t^{\alpha-1} e^{-t} \, dt, \quad \alpha > 0. \]

\(\Gamma(x)\) can be computed in R using the gamma() function. The derivative of \(\Gamma(x)\) are given respectively by

\[ \Gamma^\prime(z) = \Gamma (z)\psi_0(z) \]

where \(\psi_0(z) = \frac{d}{dz} \ln \Gamma{z}\). In R, the digamma function \(\psi_0(z)\) is evaluated by digamma().


This assignment focuses on finding maximum likelihood estimate of parameters \(\alpha\) and \(\beta\) based on a real-world application data set.


Question 1: Derive gradient (first order partial derivative) of likelihood function

Assume that \(\{x_1, x_2, \cdots, x_n \} \to \text{ gamma }(\alpha, \beta)\) with density function given by

\[ f(x \mid \alpha, \beta) = \frac{1}{\Gamma(\alpha)\beta^\alpha} x^{\alpha-1} e^{-x/\beta} \ \ \text{for} \ \ x > 0, \]

Derive the gradient of the log-likelihood function with respect to the gamma distribution parameters \(\alpha\) and \(\beta\). To this end,

a). Write out the full log-likelihood function based on the given data and the density function provided above.

true.alpha <- 2.5
true.beta <- 1.8


sample.data= rgamma(1000, shape = true.alpha, scale = true.beta )


# Log-Likelihood Function (to simplify computation)
gammas.loglik = function(params, data) 
  {
  alpha_shape = params[1] #shape parameter
  beta_scale = params[2] # scale parameter
  n = length(data)
  
  # giving a log to the gamma distribution function and simplifying the equation
  #replacing log(alpha)or log(gamma(alpha)) with lgamma()
  loglik_gamma = -n * lgamma(alpha_shape) - 
                 n * alpha_shape * log(beta_scale) + 
                 (alpha_shape - 1) * sum(log(data)) - 
                 (1/beta_scale) * sum(data)
  
  return(loglik_gamma) 
}

Expanding the log and crafting the summation \[ \ell(\alpha, \beta)= \sum [ln(1)-ln\Gamma(\alpha)-ln(\beta^\alpha) + ln(x_i^{\alpha-1})+ln(e^{-x_i/\beta})] \]

The log likelihood function of the gamma distribution function: \[ \ell(x \mid \alpha, \beta)= -n \ln \Gamma(\alpha) - n \alpha \ln(\beta) + (\alpha - 1) \sum_{i=1}^{n} \ln(x_i) - \frac{1}{\beta} \sum_{i=1}^{n} x_i \]

b). Derive the score functions (the gradient of the log-likelihood) from the full log-likelihood function in part (a).

# Corrected Score Function
gamma.score = function(params, data) 
  {
  alpha_shape = params[1]
  beta_scale = params[2]
  n = length(data)

  # Getting the gradient parameters, replacing with digamma 
  gradient_alpha = -n * digamma(alpha_shape) - n * log(beta_scale) + sum(log(data)) #Partial derivative to alpha #The first derivative of log gamma function which you need for the partial derivatives mentioned
  gradient_beta = -(n * alpha_shape) / beta_scale + sum(data) / (beta_scale^2) 
#Partial derivative with respect to Beta

  return(c(gradient_alpha, gradient_beta))
}

Performing the log likelihood partial derivatives for the Gamma distribution.

Taking the partial derivative in respect to the parameter \(\alpha\).

\[ \frac{\partial \ell}{\partial \alpha} = \frac{\partial}{\partial \alpha} \left[ -n \ln \Gamma(\alpha) - n \alpha \ln(\beta) + (\alpha - 1) \sum_{i=1}^{n} \ln(x_i) - \frac{1}{\beta} \sum_{i=1}^{n} x_i \right] \]

\[ \frac{\partial}{\partial \alpha} [-n \ln \Gamma(\alpha)] = -n \psi_0(\alpha) \]

\[ \frac{\partial}{\partial \alpha} [-n \alpha \ln(\beta)] = -n \ln(\beta) \]

\[ \frac{\partial}{\partial \alpha} [(\alpha - 1) \sum \ln(x_i)] = \sum \ln(x_i) \]

\[ \frac{\partial}{\partial \alpha} [-\frac{1}{\beta} \sum x_i] = 0 \]

\[ U_\alpha = -n \psi_0(\alpha) - n \ln(\beta) + \sum_{i=1}^{n} \ln(x_i) \]

Beta

Taking the partial derivative in respect to the parameter \(\beta\).

$$

[-n ()] = 0

$$

The Partial derivative of the Beta parameter is:

\[ \frac{\partial}{\partial \beta} [-n\alpha \ln(\beta)] = -\frac{n \alpha}{\beta} \]

  1. Differentiate and set the beta section to 0:

\[ \frac{\partial}{\partial \beta} [(\alpha - 1) \sum \ln(x_i)] = 0 \] Remove line below: \[ \frac{\partial\ell}{\partial \beta}=\frac{\partial}{\partial \beta} [-n\alpha \ln(\beta) +(\alpha-1) \sum^n_{i=1}(x_i) - \frac{1}{\beta} \sum^n_{i=1}x_i \]

\[ \frac{\partial}{\partial \beta} [-\frac{1}{\beta} \sum x_i] = \frac{\sum x_i}{\beta^2} \]

The score function for the scale \(\beta\) parameter is:

\[ U_\beta = -\frac{n \alpha}{\beta} + \frac{\sum_{i=1}^{n} x_i}{\beta^2} \]

The gradient vector with Alpha (Shape) and Beta (Scale) Parameters:

\[ U_\alpha = -n \psi_0(\alpha) - n \ln(\beta) + \sum_{i=1}^{n} \ln(x_i) .........(A) \]

\[ U_\beta = -\frac{n \alpha}{\beta} + \frac{\sum_{i=1}^{n} x_i}{\beta^2} .......... (B) \]


Question 2: Birth data set

The following R code reads in a data set containing, for each of 7 days, the lengths of time in hours spent by women in the delivery suite while giving birth (without a ceasarian section) at John Radcliffe Hospital in Oxford, England. The data are taken from Davison (Statistical Models. Cambridge University Press, 2003).

2.1, 3.4, 4.25, 5.6, 6.4, 7.3, 8.5, 8.75, 8.9, 9.5, 9.75, 10, 10.4, 10.4, 16, 19,
4, 4.1, 5, 5.5, 5.7, 6.5, 7.25, 7.3, 7.5, 8.2, 8.5, 9.75, 11, 11.2, 15, 16.5, 2.6, 
3.6, 3.6, 6.4, 6.8, 7.5, 7.5, 8.25, 8.5, 10.4, 10.75, 14.25, 14.5, 1.5, 4.7, 4.7, 
7.2, 7.25, 8.1, 8.5, 9.2, 9.5, 10.7, 11.5, 2.5, 2.5, 3.4, 4.2, 5.9, 6.25, 7.3, 7.5, 
7.8, 8.3, 8.3, 10.25, 12.9, 14.3, 4, 4, 5.25, 6.1, 6.5, 6.9, 7, 8.45, 9.25, 10.1, 
10.2, 12.75, 14.6, 2, 2.7, 2.75, 3.4, 4.2, 4.3, 4.9, 6.25, 7, 9, 9.25, 10.7

Assume the data are generated from a gamma distribution. The objective is to use these data and the designated algorithm to find the maximum likelihood estimates (MLEs) of the parameters \(\alpha\) and \(\beta\).

a). Find the MLEs of \(\alpha\) and \(\beta\), denoted by \(\hat{\alpha}\) and \(\hat{\beta}\), using gradient-based optimization via the R function optim() with the gradient vector derived in Question 1.

databirth= sort(c(2.1, 3.4, 4.25, 5.6, 6.4, 7.3, 8.5, 8.75, 8.9, 9.5, 9.75, 10, 10.4, 10.4, 16, 19,
4, 4.1, 5, 5.5, 5.7, 6.5, 7.25, 7.3, 7.5, 8.2, 8.5, 9.75, 11, 11.2, 15, 16.5, 2.6, 
3.6, 3.6, 6.4, 6.8, 7.5, 7.5, 8.25, 8.5, 10.4, 10.75, 14.25, 14.5, 1.5, 4.7, 4.7, 
7.2, 7.25, 8.1, 8.5, 9.2, 9.5, 10.7, 11.5, 2.5, 2.5, 3.4, 4.2, 5.9, 6.25, 7.3, 7.5, 
7.8, 8.3, 8.3, 10.25, 12.9, 14.3, 4, 4, 5.25, 6.1, 6.5, 6.9, 7, 8.45, 9.25, 10.1, 
10.2, 12.75, 14.6, 2, 2.7, 2.75, 3.4, 4.2, 4.3, 4.9, 6.25, 7, 9, 9.25, 10.7))

#gammas.loglik(params = params, data = databirth)


# Score Function to reach the gradient function of alpha and beta
gamma.score = function(params, data) 
  {
  alpha_shape = params[1]
  beta_scale = params[2]
  n = length(data)

  # Math Fix: Combined into one expression
  gradient_alpha = -n * digamma(alpha_shape) - n * log(beta_scale) + sum(log(data))
  gradient_beta = -(n * alpha_shape) / beta_scale + sum(data) / (beta_scale^2)

  return(c(gradient_alpha, gradient_beta))
}

initial.params = c(alpha_shape = 5 , beta_scale= 2 ) #change later

#Using optim() to reach gradient, gamma is a nonlinear, nonclosed function. We use optim() to get more accurate parameters
mle.result <- optim(
  par = initial.params,
  fn = gammas.loglik, # fn, maps the territory of the function to follow/find the height of the function(here it is the gamma function)
  gr = gamma.score,     # scored gamma  function --> needed for gradient
  data = databirth,           
  method = "L-BFGS-B",  # Best for gradients + bounds
  hessian = TRUE,  # A matrix second order partial derivatives. If the Gradient is the first derivative (slope), the hessian is the second derivative (curvature)
  control = list(trace = FALSE,
                 fnscale = -1, # We want to MAXIMIZE the log-likelihood, so choose -1
                 maxit = 500,
                 abstol = 1e-8)
)


mle.result
$par
alpha_shape  beta_scale 
   4.387853    1.760123 

$value
[1] -251.1173

$counts
function gradient 
      13       13 

$convergence
[1] 0

$message
[1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH"

$hessian
            alpha_shape beta_scale
alpha_shape   -24.30334  -53.97352
beta_scale    -53.97352 -134.55199

The predicted shape parameter in the birth times is $$ = 4.387853. The predicted scale parameter \(\hat\beta\) = 1.7601226.

b). Apply the method of moments to obtain estimators for \(\alpha\) and \(\beta\). Denote these moment estimators as \(\tilde{\alpha}\) and \(\tilde{\beta}\).

#MME: Method of Moments (Scale Parameter)
m1 <- mean(databirth) 
s2 <- var(databirth)  # Sample Variance (s^2)

# Scale parameter MoM formulas:
mom_beta  <- s2 / m1          # This is the Scale Beta
mom_alpha <- m1^2 / s2        # This is the Shape Alpha

#mom_beta
#mom_alpha

initial.params = c(alpha_shape = mom_alpha, beta_scale= mom_beta ) #change later

#Using optim like above 2a to optimze() the nonlinear and nonclosed function of the method of moment estimators mme
mle.result <- optim(
  par = initial.params,
  fn = gammas.loglik,   # gamma function
  gr = gamma.score,     # scored gamma  function
  data = databirth,           
  method = "L-BFGS-B",  # Best for gradients + bounds
  hessian = TRUE,
  control = list(trace = FALSE,
                 fnscale = -1, # We want to MAXIMIZE the log-likelihood, so choose -1
                 maxit = 500,
                 abstol = 1e-8)
)


mle.result
$par
alpha_shape  beta_scale 
   4.387854    1.760122 

$value
[1] -251.1173

$counts
function gradient 
       8        8 

$convergence
[1] 0

$message
[1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH"

$hessian
            alpha_shape beta_scale
alpha_shape   -24.30333  -53.97352
beta_scale    -53.97352 -134.55199

The method of moments estimators for \(\tilde{\alpha}\) is 4.6850734 and \(\tilde{\beta}\) is 1.6484604.

c). Conduct a brief literature review comparing the method of moments estimation (MME) and maximum likelihood estimation (MLE). Synthesize the key advantages and limitations of each, concluding with a practical recommendation.

Method of moments estimation (MME) and Maximum likelihood estimation (MLE) are both methods to connect distributions with estimated parameters using a random sample. The method of moment estimation (MME) is a simpler test than the Maximum likelihood estimation (MLE).

Methods of Moments Estimation

Method of moments estimation is a simpler estimator, using the population moments (information from the population) and setting it equal to the known sample moments (known sample information) to solve for what the population parameters would be. Parameters are numerical population descriptions that summarize characteristics about our population, for example, mean or variance. The goal of the likelihood estimation is to measure how well parameter values explains the observed data.

The major advantage to the Moment of methods estimation is its simplicity. MME does not require much computational power, allowing for fast computations that can be used as general estimators. The downside of thee Method of moment’s simplicity, is its lack of specify may yield estimators with wider variance and less accuracy- also known as efficiency.

When we set up two equations together and solve for the unknown population parameter of interest, sometimes, depending on the complexity of the distribution, we may not have a nonlinear equation. In other words, solving for the unknown parameter, cannot be done using simple algebraic expressions and require more computational rigor often requiring a background application in calculus or linear algebra. When an expression is non-linear we can no longer get defined values for the parameters of interest, but approximations. Connecting the higher variance of MME calculations with a possible added noise or bias with a non-linear approximation, the parameters calculated using MME have a greater chance to being less accurate in comparison to other tests, such as the Maximum Likelihood Estimation (MLE).

The higher chance of bias places importance of pairing the MME with a Fisher Information test. The Fisher Information test measures how well the sample carries information about the population parameter. With its simpler calculations, the MME is best suited to provide general starting point information about the parameter from the sample, in fact it is often used as a starting point prior to MLE analysis.

Maximum Likelihood Estimation

Maximum likelihood estimation is an estimator process that assumes the collected data is fixed. It performs a series of tests to find at for the greatest point in the distribution to calculate the parameter that is most likely that summarizes the sample.

In retrospect, the likelihood estimation function calculates the likelihood of a parameter across the entire distribution while each point in the distribution has its own unique parameter value. The maximum likelihood estimator finds the region in the distribution with the greatest likelihood to provide a reasonable estimate for the parameter that maximizes the likelihood for the observed data giving a statistically probable estimate for the parameters.

Unlike the MME, the Maximum Likelihood Estimation is a more computationally involved process.

Additionally the MME cannot be checked with quality metrics that require data outside of the collected sample, such as hypothesized data such as a p-value test.

Comparison of Method of Moments Estimation and Maximum Likelihood Estimation

The MLE is more accurate than the MME, instead of approximating the highest points of frequency in the distribution we directly calculate the highest maximia via partial derivative calculus . The MLE has lower variance than the MLE leading to lower bias.

Both the MLE and MME have the chance to have non-linear, non-closed form equations. As the MLE is already often a more complex test, this only slows the computational time, and often results in computational approximations, rather than defined parameter answers.

Due to accuracy, if time is alloted the Maximum Likelihood Estimation is the best test to use. Ultimately the MLE shares some methodology from the MME, making it a more refined test of the MLE. Often the MLE and MME can be used together. The MME can act as a less computationally intensive starting point for the MLE as the MLE becomes more complex.

Comparison MLE and MME with Birthing Time Data

In observing the difference in the Maximum Likelihood Estimation and Methods of Moments Estimation directly on the birthing time dataset, we see the gamma alpha parameter calculated using the MME is \(\tilde{\alpha}_{MME}\) = 4.6850734 and the beta parameter is \(\tilde{\beta}_{MME}\) = 1.6484604. The MLE gamma alpha parameter is \(\hat{\alpha}_{MLE}\) = 4.3878544 and the beta parameter is \(\hat{\beta}_{MLE}\) = 1.7601224. Although the calculated parameter values are similar, it is understood that the MME parameters have more variance than their MLE counterparts.

# Defining the range for the x-axis based on birth data
x_range <- seq(0, max(databirth) + 5, length.out = 200)

# Calculate the Density curves
# Using  MME values
mom_dens <- dgamma(x_range, shape = mom_alpha, scale = mom_beta)

# Using your MLE values from optim
mle_dens <- dgamma(x_range, shape = mle.result$par[1], scale = mle.result$par[2])

# Create the data frame for ggplot
plot_data <- data.frame(
  x = rep(x_range, 2),
  density = c(mom_dens, mle_dens),
  Method = rep(c("Method of Moment Estimator", "Maximum Likelihood Estimator"), each = length(x_range))
)

# Building plot
gamma_plt <- ggplot() +
  # Add the actual data as a histogram or density area
  #gray is the birthdata distribution
  geom_density(data = data.frame(x = databirth), aes(x = x), 
               fill = "gray90", color = "gray60", alpha = 0.5) +
  # Add the two theoretical lines
  geom_line(data = plot_data, aes(x = x, y = density, color = Method, linetype = Method), size = 1) +
  labs(title = "Method of Moment Estimators Compared to 
       Maximum Likelihood Estimators for Birth Times",
       subtitle = "Comparing estimator densities against observed data in a gamma distribution",
       x = "Hours in Delivery Suite", 
       y = "Density") +
  theme_minimal() +
  scale_color_manual(values = c("Method of Moment Estimator" = "blue", 
                                "Maximum Likelihood Estimator" = "red")) + #The estimator's distribution for the MME and the MLE in different colors

#The distribution is drawn we need both the parameters alpha and beta the draw the distribution, so they are both represented
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5))

# Make it interactive
ggplotly(gamma_plt)

Using the delivery suite birthing times data, we see that both the Method of Moment Estimator (blue) and the Maximum Likelihood Estimator (red) capture the distribution similarly, although the MLE capture the distribution closer to the true distribution (gray). These sample estimations would then be used to best parameterize the population.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgNDogTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24iDQphdXRob3I6ICJFemFuYSBSaXZlcnMiDQpkYXRlOiAiIER1ZTogMDMtMDMtMjAyNiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQojVE9DOjpiZWZvcmUgew0KICBjb250ZW50OiAiVGFibGUgb2YgQ29udGVudHMiOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAxLjJlbTsNCiAgZGlzcGxheTogYmxvY2s7DQogIGNvbG9yOiBuYXZ5Ow0KICBtYXJnaW4tYm90dG9tOiAxMHB4Ow0KfQ0KDQoNCmRpdiNUT0MgbGkgeyAgICAgLyogdGFibGUgb2YgY29udGVudCAgKi8NCiAgICBsaXN0LXN0eWxlOnVwcGVyLXJvbWFuOw0KICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXJlcGVhdDpub25lOw0KICAgIGJhY2tncm91bmQtcG9zaXRpb246MDsNCn0NCg0KaDEudGl0bGUgeyAgICAvKiBsZXZlbCAxIGhlYWRlciBvZiB0aXRsZSAgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KDQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICBmb250LXNpemU6IDE1cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDEgeyAvKiBIZWFkZXIgMSAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTZweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovDQouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7DQogIGNvbnRlbnQ6ICIuIjsNCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KfQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICBsaWJyYXJ5KHBsb3RseSkNCn0NCiMjIyMNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQogDQpcDQogDQojIyAqKkFzc2lnbm1lbnQgT2JqZWN0aXZlcyoqIA0KDQoqIENvbXByZWhlbmQgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gYW5kIGl0cyBwcm9wZXJ0aWVzLg0KDQoqIE1hc3RlciB0aGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gZnJhbWV3b3JrIGFuZCByZXF1aXJlZCBjb21wb25lbnRzLg0KDQoqIFVuZGVyc3RhbmQgdGhlIHBsdWctaW4gcHJpbmNpcGxlIHVuZGVybHlpbmcgTUxFLg0KDQoqIEltcGxlbWVudCBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiBwcm9jZWR1cmVzIGluIFIuDQoNClwNCg0KIyMgKipQb2xpY2llcyBvZiBVc2luZyBBSSBUb29scyoqDQoNCioqUG9saWN5IG9uIEFJIFRvb2wgVXNlKio6IFN0dWRlbnRzIG11c3QgYWRoZXJlIHRvIHRoZSBBSSB0b29sIHBvbGljeSBzcGVjaWZpZWQgaW4gdGhlIGNvdXJzZSBzeWxsYWJ1cy4gVGhlIGRpcmVjdCBjb3B5aW5nIG9mIEFJLWdlbmVyYXRlZCBjb250ZW50IGlzIHN0cmljdGx5IHByb2hpYml0ZWQuIEFsbCBzdWJtaXR0ZWQgd29yayBtdXN0IHJlZmxlY3QgeW91ciBvd24gdW5kZXJzdGFuZGluZzsgd2hlcmUgZXh0ZXJuYWwgdG9vbHMgYXJlIGNvbnN1bHRlZCwgY29udGVudCBtdXN0IGJlIHRob3JvdWdobHkgcmVwaHJhc2VkIGFuZCBzeW50aGVzaXplZCBpbiB5b3VyIG93biB3b3Jkcy4NCg0KKipDb2RlIEluY2x1c2lvbiBSZXF1aXJlbWVudCoqOiBBbnkgY29kZSBpbmNsdWRlZCBpbiB5b3VyIGVzc2F5IG11c3QgYmUgcHJvcGVybHkgY29tbWVudGVkIHRvIGV4cGxhaW4gdGhlIHB1cnBvc2UgYW5kL29yIGV4cGVjdGVkIG91dHB1dCBvZiBrZXkgY29kZSBsaW5lcy4gU3VibWl0dGluZyBBSS1nZW5lcmF0ZWQgY29kZSB3aXRob3V0IG1lYW5pbmdmdWwsIHN0dWRlbnQtYWRkZWQgY29tbWVudHMgd2lsbCBub3QgYmUgYWNjZXB0ZWQuDQoNClwNCg0KKipHYW1tYSBEaXN0cmlidXRpb24gUmV2aXNpdGVkKioNCg0KTGV0ICRYJCBiZSB0aGUgdHdvIHBhcmFtZXRlciBHYW1tYSByYW5kb20gdmFyaWFibGUgd2l0aCBkZW5zaXR5IGZ1bmN0aW9uDQoNCiQkDQpmKHggXG1pZCBcYWxwaGEsIFxiZXRhKSA9IFxmcmFjezF9e1xHYW1tYShcYWxwaGEpXGJldGFeXGFscGhhfSB4XntcYWxwaGEtMX0gZV57LXgvXGJldGF9ICBcIFwgXHRleHR7Zm9yfSBcIFwgIHggPiAwLA0KJCQNCg0Kd2hlcmUgd2l0aCAkXGFscGhhID4gMCQgKHNoYXBlKSwgJFxiZXRhPjAkIChzY2FsZSksIGFuZA0KDQokJA0KXEdhbW1hKFxhbHBoYSkgPSBcaW50X3swfV57XGluZnR5fSB0XntcYWxwaGEtMX0gZV57LXR9IFwsIGR0LCBccXVhZCBcYWxwaGEgPiAwLg0KJCQNCg0KJFxHYW1tYSh4KSQgY2FuIGJlIGNvbXB1dGVkIGluIFIgdXNpbmcgdGhlIGBnYW1tYSgpYCBmdW5jdGlvbi4gVGhlIGRlcml2YXRpdmUgb2YgJFxHYW1tYSh4KSQgYXJlIGdpdmVuIHJlc3BlY3RpdmVseSBieQ0KDQokJA0KXEdhbW1hXlxwcmltZSh6KSA9IFxHYW1tYSAoeilccHNpXzAoeikNCiQkDQoNCndoZXJlICRccHNpXzAoeikgPSBcZnJhY3tkfXtken0gXGxuIFxHYW1tYXt6fSQuIEluIFIsIHRoZSBkaWdhbW1hIGZ1bmN0aW9uICRccHNpXzAoeikkIGlzIGV2YWx1YXRlZCBieSBgZGlnYW1tYSgpYC4NCg0KDQoNClwNCg0KPGZvbnQgY29sb3IgPSAiYmx1ZSI+VGhpcyBhc3NpZ25tZW50IGZvY3VzZXMgb24gZmluZGluZyBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUgb2YgcGFyYW1ldGVycyAkXGFscGhhJCBhbmQgJFxiZXRhJCBiYXNlZCBvbiBhIHJlYWwtd29ybGQgYXBwbGljYXRpb24gZGF0YSBzZXQuPC9mb250Pg0KDQoNClwNCg0KIyMgKipRdWVzdGlvbiAxOiBEZXJpdmUgZ3JhZGllbnQgKGZpcnN0IG9yZGVyIHBhcnRpYWwgZGVyaXZhdGl2ZSkgb2YgbGlrZWxpaG9vZCBmdW5jdGlvbioqDQoNCkFzc3VtZSB0aGF0ICRce3hfMSwgeF8yLCBcY2RvdHMsIHhfbiBcfSBcdG8gXHRleHR7IGdhbW1hIH0oXGFscGhhLCBcYmV0YSkkIHdpdGggZGVuc2l0eSBmdW5jdGlvbiBnaXZlbiBieQ0KDQokJA0KZih4IFxtaWQgXGFscGhhLCBcYmV0YSkgPSBcZnJhY3sxfXtcR2FtbWEoXGFscGhhKVxiZXRhXlxhbHBoYX0geF57XGFscGhhLTF9IGVeey14L1xiZXRhfSAgXCBcIFx0ZXh0e2Zvcn0gXCBcICB4ID4gMCwNCiQkDQoNCkRlcml2ZSB0aGUgZ3JhZGllbnQgb2YgdGhlICoqbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24qKiB3aXRoIHJlc3BlY3QgdG8gdGhlIGdhbW1hIGRpc3RyaWJ1dGlvbiBwYXJhbWV0ZXJzICRcYWxwaGEkIGFuZCAkXGJldGEkLiBUbyB0aGlzIGVuZCwNCg0KDQphKS4gV3JpdGUgb3V0IHRoZSBmdWxsICoqbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24qKiBiYXNlZCBvbiB0aGUgZ2l2ZW4gZGF0YSBhbmQgdGhlIGRlbnNpdHkgZnVuY3Rpb24gcHJvdmlkZWQgYWJvdmUuDQpgYGB7cn0NCg0KdHJ1ZS5hbHBoYSA8LSAyLjUNCnRydWUuYmV0YSA8LSAxLjgNCg0KDQpzYW1wbGUuZGF0YT0gcmdhbW1hKDEwMDAsIHNoYXBlID0gdHJ1ZS5hbHBoYSwgc2NhbGUgPSB0cnVlLmJldGEgKQ0KDQoNCiMgTG9nLUxpa2VsaWhvb2QgRnVuY3Rpb24gKHRvIHNpbXBsaWZ5IGNvbXB1dGF0aW9uKQ0KZ2FtbWFzLmxvZ2xpayA9IGZ1bmN0aW9uKHBhcmFtcywgZGF0YSkgDQogIHsNCiAgYWxwaGFfc2hhcGUgPSBwYXJhbXNbMV0gI3NoYXBlIHBhcmFtZXRlcg0KICBiZXRhX3NjYWxlID0gcGFyYW1zWzJdICMgc2NhbGUgcGFyYW1ldGVyDQogIG4gPSBsZW5ndGgoZGF0YSkNCiAgDQogICMgZ2l2aW5nIGEgbG9nIHRvIHRoZSBnYW1tYSBkaXN0cmlidXRpb24gZnVuY3Rpb24gYW5kIHNpbXBsaWZ5aW5nIHRoZSBlcXVhdGlvbg0KICAjcmVwbGFjaW5nIGxvZyhhbHBoYSlvciBsb2coZ2FtbWEoYWxwaGEpKSB3aXRoIGxnYW1tYSgpDQogIGxvZ2xpa19nYW1tYSA9IC1uICogbGdhbW1hKGFscGhhX3NoYXBlKSAtIA0KICAgICAgICAgICAgICAgICBuICogYWxwaGFfc2hhcGUgKiBsb2coYmV0YV9zY2FsZSkgKyANCiAgICAgICAgICAgICAgICAgKGFscGhhX3NoYXBlIC0gMSkgKiBzdW0obG9nKGRhdGEpKSAtIA0KICAgICAgICAgICAgICAgICAoMS9iZXRhX3NjYWxlKSAqIHN1bShkYXRhKQ0KICANCiAgcmV0dXJuKGxvZ2xpa19nYW1tYSkgDQp9DQoNCmBgYA0KDQoNCg0KRXhwYW5kaW5nIHRoZSBsb2cgYW5kIGNyYWZ0aW5nIHRoZSBzdW1tYXRpb24NCiQkIA0KIFxlbGwoXGFscGhhLCBcYmV0YSk9IFxzdW0gW2xuKDEpLWxuXEdhbW1hKFxhbHBoYSktbG4oXGJldGFeXGFscGhhKSArIGxuKHhfaV57XGFscGhhLTF9KStsbihlXnsteF9pL1xiZXRhfSldDQokJA0KDQoNClRoZSBsb2cgbGlrZWxpaG9vZCBmdW5jdGlvbiBvZiB0aGUgZ2FtbWEgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uOg0KJCQNClxlbGwoeCBcbWlkIFxhbHBoYSwgXGJldGEpPSAtbiBcbG4gXEdhbW1hKFxhbHBoYSkgLSBuIFxhbHBoYSBcbG4oXGJldGEpICsgKFxhbHBoYSAtIDEpIFxzdW1fe2k9MX1ee259IFxsbih4X2kpIC0gXGZyYWN7MX17XGJldGF9IFxzdW1fe2k9MX1ee259IHhfaQ0KJCQNCg0KDQpiKS4gRGVyaXZlIHRoZSBzY29yZSBmdW5jdGlvbnMgKHRoZSBncmFkaWVudCBvZiB0aGUgbG9nLWxpa2VsaWhvb2QpIGZyb20gdGhlIGZ1bGwgKipsb2ctbGlrZWxpaG9vZCBmdW5jdGlvbioqIGluIHBhcnQgKGEpLg0KDQoNCmBgYHtyfQ0KDQoNCiMgQ29ycmVjdGVkIFNjb3JlIEZ1bmN0aW9uDQpnYW1tYS5zY29yZSA9IGZ1bmN0aW9uKHBhcmFtcywgZGF0YSkgDQogIHsNCiAgYWxwaGFfc2hhcGUgPSBwYXJhbXNbMV0NCiAgYmV0YV9zY2FsZSA9IHBhcmFtc1syXQ0KICBuID0gbGVuZ3RoKGRhdGEpDQoNCiAgIyBHZXR0aW5nIHRoZSBncmFkaWVudCBwYXJhbWV0ZXJzLCByZXBsYWNpbmcgd2l0aCBkaWdhbW1hIA0KICBncmFkaWVudF9hbHBoYSA9IC1uICogZGlnYW1tYShhbHBoYV9zaGFwZSkgLSBuICogbG9nKGJldGFfc2NhbGUpICsgc3VtKGxvZyhkYXRhKSkgI1BhcnRpYWwgZGVyaXZhdGl2ZSB0byBhbHBoYSAjVGhlIGZpcnN0IGRlcml2YXRpdmUgb2YgbG9nIGdhbW1hIGZ1bmN0aW9uIHdoaWNoIHlvdSBuZWVkIGZvciB0aGUgcGFydGlhbCBkZXJpdmF0aXZlcyBtZW50aW9uZWQNCiAgZ3JhZGllbnRfYmV0YSA9IC0obiAqIGFscGhhX3NoYXBlKSAvIGJldGFfc2NhbGUgKyBzdW0oZGF0YSkgLyAoYmV0YV9zY2FsZV4yKSANCiNQYXJ0aWFsIGRlcml2YXRpdmUgd2l0aCByZXNwZWN0IHRvIEJldGENCg0KICByZXR1cm4oYyhncmFkaWVudF9hbHBoYSwgZ3JhZGllbnRfYmV0YSkpDQp9DQoNCg0KYGBgDQoNCg0KUGVyZm9ybWluZyB0aGUgbG9nIGxpa2VsaWhvb2QgcGFydGlhbCBkZXJpdmF0aXZlcyBmb3IgdGhlIEdhbW1hIGRpc3RyaWJ1dGlvbi4gDQoNClRha2luZyB0aGUgcGFydGlhbCBkZXJpdmF0aXZlIGluIHJlc3BlY3QgdG8gdGhlIHBhcmFtZXRlciAkXGFscGhhJC4gDQoNCiQkDQpcZnJhY3tccGFydGlhbCBcZWxsfXtccGFydGlhbCBcYWxwaGF9ID0gXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxhbHBoYX0gXGxlZnRbIC1uIFxsbiBcR2FtbWEoXGFscGhhKSAtIG4gXGFscGhhIFxsbihcYmV0YSkgKyAoXGFscGhhIC0gMSkgXHN1bV97aT0xfV57bn0gXGxuKHhfaSkgLSBcZnJhY3sxfXtcYmV0YX0gXHN1bV97aT0xfV57bn0geF9pIFxyaWdodF0NCiQkDQoNCg0KJCQNClxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCBcYWxwaGF9IFstbiBcbG4gXEdhbW1hKFxhbHBoYSldID0gLW4gXHBzaV8wKFxhbHBoYSkNCiQkDQoNCg0KDQokJA0KXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxhbHBoYX0gWy1uIFxhbHBoYSBcbG4oXGJldGEpXSA9IC1uIFxsbihcYmV0YSkNCiQkDQoNCg0KJCQNClxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCBcYWxwaGF9IFsoXGFscGhhIC0gMSkgXHN1bSBcbG4oeF9pKV0gPSBcc3VtIFxsbih4X2kpDQokJA0KDQoNCiQkDQpcZnJhY3tccGFydGlhbH17XHBhcnRpYWwgXGFscGhhfSBbLVxmcmFjezF9e1xiZXRhfSBcc3VtIHhfaV0gPSAwDQokJA0KDQokJA0KVV9cYWxwaGEgPSAtbiBccHNpXzAoXGFscGhhKSAtIG4gXGxuKFxiZXRhKSArIFxzdW1fe2k9MX1ee259IFxsbih4X2kpDQokJA0KDQoNCiMjIEJldGEgDQoNClRha2luZyB0aGUgcGFydGlhbCBkZXJpdmF0aXZlIGluIHJlc3BlY3QgdG8gdGhlIHBhcmFtZXRlciAkXGJldGEkLg0KDQokJA0KDQpcZnJhY3tccGFydGlhbH17XHBhcnRpYWwgXGJldGF9IFstbiBcbG4gXEdhbW1hKFxhbHBoYSldID0gMA0KDQokJA0KDQoNClRoZSBQYXJ0aWFsIGRlcml2YXRpdmUgb2YgdGhlIEJldGEgcGFyYW1ldGVyIGlzOg0KDQokJA0KXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxiZXRhfSBbLW5cYWxwaGEgXGxuKFxiZXRhKV0gPSAtXGZyYWN7biBcYWxwaGF9e1xiZXRhfQ0KJCQNCg0KDQozLiBEaWZmZXJlbnRpYXRlIGFuZCBzZXQgdGhlIGJldGEgc2VjdGlvbiB0byAwOg0KDQokJA0KXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxiZXRhfSBbKFxhbHBoYSAtIDEpIFxzdW0gXGxuKHhfaSldID0gMA0KJCQNClJlbW92ZSBsaW5lIGJlbG93Og0KJCQNClxmcmFje1xwYXJ0aWFsXGVsbH17XHBhcnRpYWwgXGJldGF9PVxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCBcYmV0YX0gWy1uXGFscGhhIFxsbihcYmV0YSkgKyhcYWxwaGEtMSkgXHN1bV5uX3tpPTF9KHhfaSkgLSBcZnJhY3sxfXtcYmV0YX0gXHN1bV5uX3tpPTF9eF9pDQokJA0KDQoNCiQkDQpcZnJhY3tccGFydGlhbH17XHBhcnRpYWwgXGJldGF9IFstXGZyYWN7MX17XGJldGF9IFxzdW0geF9pXSA9IFxmcmFje1xzdW0geF9pfXtcYmV0YV4yfQ0KJCQNCg0KDQpUaGUgc2NvcmUgZnVuY3Rpb24gZm9yIHRoZSBzY2FsZSAkXGJldGEkIHBhcmFtZXRlciBpczoNCg0KJCQNClVfXGJldGEgPSAtXGZyYWN7biBcYWxwaGF9e1xiZXRhfSArIFxmcmFje1xzdW1fe2k9MX1ee259IHhfaX17XGJldGFeMn0NCiQkDQoNClRoZSBncmFkaWVudCB2ZWN0b3Igd2l0aCBBbHBoYSAoU2hhcGUpIGFuZCBCZXRhIChTY2FsZSkgUGFyYW1ldGVyczoNCg0KDQokJA0KVV9cYWxwaGEgPSAtbiBccHNpXzAoXGFscGhhKSAtIG4gXGxuKFxiZXRhKSArIFxzdW1fe2k9MX1ee259IFxsbih4X2kpIC4uLi4uLi4uLihBKQ0KJCQNCg0KJCQNClVfXGJldGEgPSAtXGZyYWN7biBcYWxwaGF9e1xiZXRhfSArIFxmcmFje1xzdW1fe2k9MX1ee259IHhfaX17XGJldGFeMn0gLi4uLi4uLi4uLiAoQikNCiQkDQoNCg0KXA0KDQoNCg0KIyMgKipRdWVzdGlvbiAyOiBCaXJ0aCBkYXRhIHNldCoqDQoNClRoZSBmb2xsb3dpbmcgUiBjb2RlIHJlYWRzIGluIGEgZGF0YSBzZXQgY29udGFpbmluZywgZm9yIGVhY2ggb2YgNyBkYXlzLCB0aGUgbGVuZ3RocyBvZiB0aW1lIGluIGhvdXJzIHNwZW50IGJ5DQp3b21lbiBpbiB0aGUgZGVsaXZlcnkgc3VpdGUgd2hpbGUgZ2l2aW5nIGJpcnRoICh3aXRob3V0IGEgY2Vhc2FyaWFuIHNlY3Rpb24pIGF0IEpvaG4gUmFkY2xpZmZlIEhvc3BpdGFsIGluDQpPeGZvcmQsIEVuZ2xhbmQuIFRoZSBkYXRhIGFyZSB0YWtlbiBmcm9tIERhdmlzb24gKFN0YXRpc3RpY2FsIE1vZGVscy4gQ2FtYnJpZGdlIFVuaXZlcnNpdHkgUHJlc3MsIDIwMDMpLg0KDQpgYGANCjIuMSwgMy40LCA0LjI1LCA1LjYsIDYuNCwgNy4zLCA4LjUsIDguNzUsIDguOSwgOS41LCA5Ljc1LCAxMCwgMTAuNCwgMTAuNCwgMTYsIDE5LA0KNCwgNC4xLCA1LCA1LjUsIDUuNywgNi41LCA3LjI1LCA3LjMsIDcuNSwgOC4yLCA4LjUsIDkuNzUsIDExLCAxMS4yLCAxNSwgMTYuNSwgMi42LCANCjMuNiwgMy42LCA2LjQsIDYuOCwgNy41LCA3LjUsIDguMjUsIDguNSwgMTAuNCwgMTAuNzUsIDE0LjI1LCAxNC41LCAxLjUsIDQuNywgNC43LCANCjcuMiwgNy4yNSwgOC4xLCA4LjUsIDkuMiwgOS41LCAxMC43LCAxMS41LCAyLjUsIDIuNSwgMy40LCA0LjIsIDUuOSwgNi4yNSwgNy4zLCA3LjUsIA0KNy44LCA4LjMsIDguMywgMTAuMjUsIDEyLjksIDE0LjMsIDQsIDQsIDUuMjUsIDYuMSwgNi41LCA2LjksIDcsIDguNDUsIDkuMjUsIDEwLjEsIA0KMTAuMiwgMTIuNzUsIDE0LjYsIDIsIDIuNywgMi43NSwgMy40LCA0LjIsIDQuMywgNC45LCA2LjI1LCA3LCA5LCA5LjI1LCAxMC43DQpgYGANCg0KQXNzdW1lIHRoZSBkYXRhIGFyZSBnZW5lcmF0ZWQgZnJvbSBhIGdhbW1hIGRpc3RyaWJ1dGlvbi4gVGhlIG9iamVjdGl2ZSBpcyB0byB1c2UgdGhlc2UgZGF0YSBhbmQgdGhlIGRlc2lnbmF0ZWQgYWxnb3JpdGhtIHRvIGZpbmQgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0ZXMgKE1MRXMpIG9mIHRoZSBwYXJhbWV0ZXJzICRcYWxwaGEkIGFuZCAkXGJldGEkLg0KDQoNCmEpLiBGaW5kIHRoZSBNTEVzIG9mICRcYWxwaGEkIGFuZCAkXGJldGEkLCBkZW5vdGVkIGJ5ICRcaGF0e1xhbHBoYX0kIGFuZCAkXGhhdHtcYmV0YX0kLCAgdXNpbmcgZ3JhZGllbnQtYmFzZWQgb3B0aW1pemF0aW9uIHZpYSB0aGUgUiBmdW5jdGlvbiBgb3B0aW0oKWAgd2l0aCB0aGUgZ3JhZGllbnQgdmVjdG9yIGRlcml2ZWQgaW4gUXVlc3Rpb24gMS4NCg0KDQpgYGB7cn0NCg0KZGF0YWJpcnRoPSBzb3J0KGMoMi4xLCAzLjQsIDQuMjUsIDUuNiwgNi40LCA3LjMsIDguNSwgOC43NSwgOC45LCA5LjUsIDkuNzUsIDEwLCAxMC40LCAxMC40LCAxNiwgMTksDQo0LCA0LjEsIDUsIDUuNSwgNS43LCA2LjUsIDcuMjUsIDcuMywgNy41LCA4LjIsIDguNSwgOS43NSwgMTEsIDExLjIsIDE1LCAxNi41LCAyLjYsIA0KMy42LCAzLjYsIDYuNCwgNi44LCA3LjUsIDcuNSwgOC4yNSwgOC41LCAxMC40LCAxMC43NSwgMTQuMjUsIDE0LjUsIDEuNSwgNC43LCA0LjcsIA0KNy4yLCA3LjI1LCA4LjEsIDguNSwgOS4yLCA5LjUsIDEwLjcsIDExLjUsIDIuNSwgMi41LCAzLjQsIDQuMiwgNS45LCA2LjI1LCA3LjMsIDcuNSwgDQo3LjgsIDguMywgOC4zLCAxMC4yNSwgMTIuOSwgMTQuMywgNCwgNCwgNS4yNSwgNi4xLCA2LjUsIDYuOSwgNywgOC40NSwgOS4yNSwgMTAuMSwgDQoxMC4yLCAxMi43NSwgMTQuNiwgMiwgMi43LCAyLjc1LCAzLjQsIDQuMiwgNC4zLCA0LjksIDYuMjUsIDcsIDksIDkuMjUsIDEwLjcpKQ0KDQojZ2FtbWFzLmxvZ2xpayhwYXJhbXMgPSBwYXJhbXMsIGRhdGEgPSBkYXRhYmlydGgpDQoNCg0KIyBTY29yZSBGdW5jdGlvbiB0byByZWFjaCB0aGUgZ3JhZGllbnQgZnVuY3Rpb24gb2YgYWxwaGEgYW5kIGJldGENCmdhbW1hLnNjb3JlID0gZnVuY3Rpb24ocGFyYW1zLCBkYXRhKSANCiAgew0KICBhbHBoYV9zaGFwZSA9IHBhcmFtc1sxXQ0KICBiZXRhX3NjYWxlID0gcGFyYW1zWzJdDQogIG4gPSBsZW5ndGgoZGF0YSkNCg0KICAjIE1hdGggRml4OiBDb21iaW5lZCBpbnRvIG9uZSBleHByZXNzaW9uDQogIGdyYWRpZW50X2FscGhhID0gLW4gKiBkaWdhbW1hKGFscGhhX3NoYXBlKSAtIG4gKiBsb2coYmV0YV9zY2FsZSkgKyBzdW0obG9nKGRhdGEpKQ0KICBncmFkaWVudF9iZXRhID0gLShuICogYWxwaGFfc2hhcGUpIC8gYmV0YV9zY2FsZSArIHN1bShkYXRhKSAvIChiZXRhX3NjYWxlXjIpDQoNCiAgcmV0dXJuKGMoZ3JhZGllbnRfYWxwaGEsIGdyYWRpZW50X2JldGEpKQ0KfQ0KDQppbml0aWFsLnBhcmFtcyA9IGMoYWxwaGFfc2hhcGUgPSA1ICwgYmV0YV9zY2FsZT0gMiApICNjaGFuZ2UgbGF0ZXINCg0KI1VzaW5nIG9wdGltKCkgdG8gcmVhY2ggZ3JhZGllbnQsIGdhbW1hIGlzIGEgbm9ubGluZWFyLCBub25jbG9zZWQgZnVuY3Rpb24uIFdlIHVzZSBvcHRpbSgpIHRvIGdldCBtb3JlIGFjY3VyYXRlIHBhcmFtZXRlcnMNCm1sZS5yZXN1bHQgPC0gb3B0aW0oDQogIHBhciA9IGluaXRpYWwucGFyYW1zLA0KICBmbiA9IGdhbW1hcy5sb2dsaWssICMgZm4sIG1hcHMgdGhlIHRlcnJpdG9yeSBvZiB0aGUgZnVuY3Rpb24gdG8gZm9sbG93L2ZpbmQgdGhlIGhlaWdodCBvZiB0aGUgZnVuY3Rpb24oaGVyZSBpdCBpcyB0aGUgZ2FtbWEgZnVuY3Rpb24pDQogIGdyID0gZ2FtbWEuc2NvcmUsICAgICAjIHNjb3JlZCBnYW1tYSAgZnVuY3Rpb24gLS0+IG5lZWRlZCBmb3IgZ3JhZGllbnQNCiAgZGF0YSA9IGRhdGFiaXJ0aCwgICAgICAgICAgIA0KICBtZXRob2QgPSAiTC1CRkdTLUIiLCAgIyBCZXN0IGZvciBncmFkaWVudHMgKyBib3VuZHMNCiAgaGVzc2lhbiA9IFRSVUUsICAjIEEgbWF0cml4IHNlY29uZCBvcmRlciBwYXJ0aWFsIGRlcml2YXRpdmVzLiBJZiB0aGUgR3JhZGllbnQgaXMgdGhlIGZpcnN0IGRlcml2YXRpdmUgKHNsb3BlKSwgdGhlIGhlc3NpYW4gaXMgdGhlIHNlY29uZCBkZXJpdmF0aXZlIChjdXJ2YXR1cmUpDQogIGNvbnRyb2wgPSBsaXN0KHRyYWNlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIGZuc2NhbGUgPSAtMSwgIyBXZSB3YW50IHRvIE1BWElNSVpFIHRoZSBsb2ctbGlrZWxpaG9vZCwgc28gY2hvb3NlIC0xDQogICAgICAgICAgICAgICAgIG1heGl0ID0gNTAwLA0KICAgICAgICAgICAgICAgICBhYnN0b2wgPSAxZS04KQ0KKQ0KDQoNCm1sZS5yZXN1bHQNCg0KYGBgDQoNCg0KVGhlIHByZWRpY3RlZCBzaGFwZSBwYXJhbWV0ZXIgaW4gdGhlIGJpcnRoIHRpbWVzIGlzICRcaGF0XGFscGhhICQgPSAgYHIgbWxlLnJlc3VsdCRwYXJbMV1gLiBUaGUgcHJlZGljdGVkIHNjYWxlIHBhcmFtZXRlciAkXGhhdFxiZXRhJCA9IGByIG1sZS5yZXN1bHQkcGFyWzJdYC4NCg0KDQpiKS4gQXBwbHkgdGhlIG1ldGhvZCBvZiBtb21lbnRzIHRvIG9idGFpbiBlc3RpbWF0b3JzIGZvciAkXGFscGhhJCBhbmQgJFxiZXRhJC4gRGVub3RlIHRoZXNlIG1vbWVudCBlc3RpbWF0b3JzIGFzICRcdGlsZGV7XGFscGhhfSQgYW5kICRcdGlsZGV7XGJldGF9JC4NCg0KDQpgYGB7ciB0MmIyfQ0KDQoNCiNNTUU6IE1ldGhvZCBvZiBNb21lbnRzIChTY2FsZSBQYXJhbWV0ZXIpDQptMSA8LSBtZWFuKGRhdGFiaXJ0aCkgDQpzMiA8LSB2YXIoZGF0YWJpcnRoKSAgIyBTYW1wbGUgVmFyaWFuY2UgKHNeMikNCg0KIyBTY2FsZSBwYXJhbWV0ZXIgTW9NIGZvcm11bGFzOg0KbW9tX2JldGEgIDwtIHMyIC8gbTEgICAgICAgICAgIyBUaGlzIGlzIHRoZSBTY2FsZSBCZXRhDQptb21fYWxwaGEgPC0gbTFeMiAvIHMyICAgICAgICAjIFRoaXMgaXMgdGhlIFNoYXBlIEFscGhhDQoNCiNtb21fYmV0YQ0KI21vbV9hbHBoYQ0KDQppbml0aWFsLnBhcmFtcyA9IGMoYWxwaGFfc2hhcGUgPSBtb21fYWxwaGEsIGJldGFfc2NhbGU9IG1vbV9iZXRhICkgI2NoYW5nZSBsYXRlcg0KDQojVXNpbmcgb3B0aW0gbGlrZSBhYm92ZSAyYSB0byBvcHRpbXplKCkgdGhlIG5vbmxpbmVhciBhbmQgbm9uY2xvc2VkIGZ1bmN0aW9uIG9mIHRoZSBtZXRob2Qgb2YgbW9tZW50IGVzdGltYXRvcnMgbW1lDQptbGUucmVzdWx0IDwtIG9wdGltKA0KICBwYXIgPSBpbml0aWFsLnBhcmFtcywNCiAgZm4gPSBnYW1tYXMubG9nbGlrLCAgICMgZ2FtbWEgZnVuY3Rpb24NCiAgZ3IgPSBnYW1tYS5zY29yZSwgICAgICMgc2NvcmVkIGdhbW1hICBmdW5jdGlvbg0KICBkYXRhID0gZGF0YWJpcnRoLCAgICAgICAgICAgDQogIG1ldGhvZCA9ICJMLUJGR1MtQiIsICAjIEJlc3QgZm9yIGdyYWRpZW50cyArIGJvdW5kcw0KICBoZXNzaWFuID0gVFJVRSwNCiAgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgZm5zY2FsZSA9IC0xLCAjIFdlIHdhbnQgdG8gTUFYSU1JWkUgdGhlIGxvZy1saWtlbGlob29kLCBzbyBjaG9vc2UgLTENCiAgICAgICAgICAgICAgICAgbWF4aXQgPSA1MDAsDQogICAgICAgICAgICAgICAgIGFic3RvbCA9IDFlLTgpDQopDQoNCg0KbWxlLnJlc3VsdA0KYGBgDQoNCg0KDQpUaGUgbWV0aG9kIG9mIG1vbWVudHMgZXN0aW1hdG9ycyBmb3IgJFx0aWxkZXtcYWxwaGF9JCBpcyBgciBtb21fYWxwaGFgIGFuZCAkXHRpbGRle1xiZXRhfSQgaXMgYHIgbW9tX2JldGFgLg0KDQoNCg0KYykuIENvbmR1Y3QgYSBicmllZiBsaXRlcmF0dXJlIHJldmlldyBjb21wYXJpbmcgdGhlIG1ldGhvZCBvZiBtb21lbnRzIGVzdGltYXRpb24gKE1NRSkgYW5kIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uIChNTEUpLiBTeW50aGVzaXplIHRoZSBrZXkgYWR2YW50YWdlcyBhbmQgbGltaXRhdGlvbnMgb2YgZWFjaCwgY29uY2x1ZGluZyB3aXRoIGEgcHJhY3RpY2FsIHJlY29tbWVuZGF0aW9uLg0KDQpNZXRob2Qgb2YgbW9tZW50cyBlc3RpbWF0aW9uIChNTUUpIGFuZCBNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAoTUxFKSBhcmUgYm90aCBtZXRob2RzIHRvIGNvbm5lY3QgZGlzdHJpYnV0aW9ucyB3aXRoIGVzdGltYXRlZCBwYXJhbWV0ZXJzIHVzaW5nIGEgcmFuZG9tIHNhbXBsZS4gVGhlIG1ldGhvZCBvZiBtb21lbnQgZXN0aW1hdGlvbiAoTU1FKSBpcyBhIHNpbXBsZXIgdGVzdCB0aGFuIHRoZSBNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAoTUxFKS4NCg0KIyBNZXRob2RzIG9mIE1vbWVudHMgRXN0aW1hdGlvbg0KDQpNZXRob2Qgb2YgbW9tZW50cyBlc3RpbWF0aW9uIGlzIGEgc2ltcGxlciBlc3RpbWF0b3IsIHVzaW5nIHRoZSBwb3B1bGF0aW9uIG1vbWVudHMgKGluZm9ybWF0aW9uIGZyb20gdGhlIHBvcHVsYXRpb24pIGFuZCBzZXR0aW5nIGl0IGVxdWFsIHRvIHRoZSBrbm93biBzYW1wbGUgIG1vbWVudHMgKGtub3duIHNhbXBsZSBpbmZvcm1hdGlvbikgdG8gc29sdmUgZm9yIHdoYXQgdGhlIHBvcHVsYXRpb24gcGFyYW1ldGVycyB3b3VsZCBiZS4gUGFyYW1ldGVycyBhcmUgbnVtZXJpY2FsIHBvcHVsYXRpb24gZGVzY3JpcHRpb25zIHRoYXQgc3VtbWFyaXplIGNoYXJhY3RlcmlzdGljcyBhYm91dCBvdXIgcG9wdWxhdGlvbiwgZm9yIGV4YW1wbGUsIG1lYW4gb3IgdmFyaWFuY2UuIFRoZSBnb2FsIG9mIHRoZSBsaWtlbGlob29kIGVzdGltYXRpb24gaXMgdG8gbWVhc3VyZSBob3cgd2VsbCBwYXJhbWV0ZXIgdmFsdWVzIGV4cGxhaW5zIHRoZSBvYnNlcnZlZCBkYXRhLg0KDQoNClRoZSBtYWpvciBhZHZhbnRhZ2UgdG8gdGhlIE1vbWVudCBvZiBtZXRob2RzIGVzdGltYXRpb24gaXMgaXRzIHNpbXBsaWNpdHkuIE1NRSBkb2VzIG5vdCByZXF1aXJlIG11Y2ggY29tcHV0YXRpb25hbCBwb3dlciwgYWxsb3dpbmcgZm9yIGZhc3QgY29tcHV0YXRpb25zIHRoYXQgY2FuIGJlIHVzZWQgYXMgZ2VuZXJhbCBlc3RpbWF0b3JzLiANClRoZSBkb3duc2lkZSBvZiB0aGVlIE1ldGhvZCBvZiBtb21lbnQncyBzaW1wbGljaXR5LCBpcyBpdHMgbGFjayBvZiBzcGVjaWZ5IG1heSB5aWVsZCBlc3RpbWF0b3JzIHdpdGggd2lkZXIgdmFyaWFuY2UgYW5kIGxlc3MgYWNjdXJhY3ktIGFsc28ga25vd24gYXMgZWZmaWNpZW5jeS4gDQoNCldoZW4gd2Ugc2V0IHVwIHR3byBlcXVhdGlvbnMgdG9nZXRoZXIgYW5kIHNvbHZlIGZvciB0aGUgdW5rbm93biBwb3B1bGF0aW9uIHBhcmFtZXRlciBvZiBpbnRlcmVzdCwgc29tZXRpbWVzLCBkZXBlbmRpbmcgb24gdGhlIGNvbXBsZXhpdHkgb2YgdGhlIGRpc3RyaWJ1dGlvbiwgd2UgbWF5IG5vdCBoYXZlIGEgbm9ubGluZWFyIGVxdWF0aW9uLiBJbiBvdGhlciB3b3Jkcywgc29sdmluZyBmb3IgdGhlIHVua25vd24gcGFyYW1ldGVyLCBjYW5ub3QgYmUgZG9uZSB1c2luZyBzaW1wbGUgYWxnZWJyYWljIGV4cHJlc3Npb25zIGFuZCByZXF1aXJlIG1vcmUgY29tcHV0YXRpb25hbCByaWdvciBvZnRlbiByZXF1aXJpbmcgYSBiYWNrZ3JvdW5kIGFwcGxpY2F0aW9uIGluIGNhbGN1bHVzIG9yIGxpbmVhciBhbGdlYnJhLiBXaGVuIGFuIGV4cHJlc3Npb24gaXMgbm9uLWxpbmVhciB3ZSBjYW4gbm8gbG9uZ2VyIGdldCBkZWZpbmVkIHZhbHVlcyBmb3IgdGhlIHBhcmFtZXRlcnMgb2YgaW50ZXJlc3QsIGJ1dCBhcHByb3hpbWF0aW9ucy4gQ29ubmVjdGluZyB0aGUgaGlnaGVyIHZhcmlhbmNlIG9mIE1NRSBjYWxjdWxhdGlvbnMgd2l0aCBhIHBvc3NpYmxlIGFkZGVkIG5vaXNlIG9yIGJpYXMgd2l0aCBhIG5vbi1saW5lYXIgYXBwcm94aW1hdGlvbiwgdGhlIHBhcmFtZXRlcnMgY2FsY3VsYXRlZCB1c2luZyBNTUUgaGF2ZSBhIGdyZWF0ZXIgY2hhbmNlIHRvIGJlaW5nIGxlc3MgYWNjdXJhdGUgaW4gY29tcGFyaXNvbiB0byBvdGhlciB0ZXN0cywgc3VjaCBhcyB0aGUgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gKE1MRSkuIA0KDQpUaGUgaGlnaGVyIGNoYW5jZSBvZiBiaWFzIHBsYWNlcyBpbXBvcnRhbmNlIG9mIHBhaXJpbmcgdGhlIE1NRSB3aXRoIGEgRmlzaGVyIEluZm9ybWF0aW9uIHRlc3QuIFRoZSBGaXNoZXIgSW5mb3JtYXRpb24gdGVzdCBtZWFzdXJlcyBob3cgd2VsbCB0aGUgc2FtcGxlIGNhcnJpZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHBvcHVsYXRpb24gcGFyYW1ldGVyLiBXaXRoIGl0cyBzaW1wbGVyIGNhbGN1bGF0aW9ucywgdGhlIE1NRSBpcyBiZXN0IHN1aXRlZCB0byBwcm92aWRlIGdlbmVyYWwgc3RhcnRpbmcgcG9pbnQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHBhcmFtZXRlciBmcm9tIHRoZSBzYW1wbGUsIGluIGZhY3QgaXQgaXMgb2Z0ZW4gdXNlZCBhcyBhIHN0YXJ0aW5nIHBvaW50IHByaW9yIHRvIE1MRSBhbmFseXNpcy4NCg0KDQojIyBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGlvbg0KDQpNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiBpcyBhbiBlc3RpbWF0b3IgcHJvY2VzcyB0aGF0IGFzc3VtZXMgdGhlIGNvbGxlY3RlZCBkYXRhIGlzIGZpeGVkLiBJdCBwZXJmb3JtcyBhIHNlcmllcyBvZiB0ZXN0cyB0byBmaW5kICBhdCBmb3IgdGhlIGdyZWF0ZXN0IHBvaW50IGluIHRoZSBkaXN0cmlidXRpb24gdG8gY2FsY3VsYXRlIHRoZSBwYXJhbWV0ZXIgdGhhdCBpcyBtb3N0IGxpa2VseSB0aGF0IHN1bW1hcml6ZXMgdGhlIHNhbXBsZS4gIA0KDQpJbiByZXRyb3NwZWN0LCB0aGUgbGlrZWxpaG9vZCBlc3RpbWF0aW9uIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIGxpa2VsaWhvb2Qgb2YgYSBwYXJhbWV0ZXIgYWNyb3NzIHRoZSBlbnRpcmUgZGlzdHJpYnV0aW9uIHdoaWxlIGVhY2ggcG9pbnQgaW4gdGhlIGRpc3RyaWJ1dGlvbiBoYXMgaXRzIG93biB1bmlxdWUgcGFyYW1ldGVyIHZhbHVlLiBUaGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRvciBmaW5kcyB0aGUgcmVnaW9uIGluIHRoZSBkaXN0cmlidXRpb24gd2l0aCB0aGUgZ3JlYXRlc3QgbGlrZWxpaG9vZCB0byBwcm92aWRlIGEgcmVhc29uYWJsZSBlc3RpbWF0ZSBmb3IgdGhlIHBhcmFtZXRlciB0aGF0IG1heGltaXplcyB0aGUgbGlrZWxpaG9vZCBmb3IgdGhlIG9ic2VydmVkIGRhdGEgZ2l2aW5nIGEgc3RhdGlzdGljYWxseSBwcm9iYWJsZSBlc3RpbWF0ZSBmb3IgdGhlIHBhcmFtZXRlcnMuIA0KDQpVbmxpa2UgdGhlIE1NRSwgdGhlIE1heGltdW0gTGlrZWxpaG9vZCBFc3RpbWF0aW9uIGlzIGEgbW9yZSBjb21wdXRhdGlvbmFsbHkgaW52b2x2ZWQgcHJvY2Vzcy4NCg0KQWRkaXRpb25hbGx5IHRoZSBNTUUgY2Fubm90IGJlIGNoZWNrZWQgd2l0aCBxdWFsaXR5IG1ldHJpY3MgdGhhdCByZXF1aXJlIGRhdGEgb3V0c2lkZSBvZiB0aGUgY29sbGVjdGVkIHNhbXBsZSwgc3VjaCBhcyBoeXBvdGhlc2l6ZWQgZGF0YSBzdWNoIGFzIGEgcC12YWx1ZSB0ZXN0LiANCg0KDQojIENvbXBhcmlzb24gb2YgTWV0aG9kIG9mIE1vbWVudHMgRXN0aW1hdGlvbiBhbmQgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24NCg0KVGhlIE1MRSBpcyBtb3JlIGFjY3VyYXRlIHRoYW4gdGhlIE1NRSwgaW5zdGVhZCBvZiBhcHByb3hpbWF0aW5nIHRoZSBoaWdoZXN0IHBvaW50cyBvZiBmcmVxdWVuY3kgaW4gdGhlIGRpc3RyaWJ1dGlvbiB3ZSBkaXJlY3RseSBjYWxjdWxhdGUgdGhlIGhpZ2hlc3QgbWF4aW1pYSB2aWEgcGFydGlhbCBkZXJpdmF0aXZlIGNhbGN1bHVzIC4gVGhlIE1MRSBoYXMgbG93ZXIgdmFyaWFuY2UgdGhhbiB0aGUgTUxFIGxlYWRpbmcgdG8gbG93ZXIgYmlhcy4NCg0KQm90aCB0aGUgTUxFIGFuZCBNTUUgaGF2ZSB0aGUgY2hhbmNlIHRvIGhhdmUgbm9uLWxpbmVhciwgbm9uLWNsb3NlZCBmb3JtIGVxdWF0aW9ucy4gQXMgdGhlIE1MRSBpcyBhbHJlYWR5IG9mdGVuIGEgbW9yZSBjb21wbGV4IHRlc3QsIHRoaXMgb25seSBzbG93cyB0aGUgY29tcHV0YXRpb25hbCB0aW1lLCBhbmQgb2Z0ZW4gcmVzdWx0cyBpbiBjb21wdXRhdGlvbmFsIGFwcHJveGltYXRpb25zLCByYXRoZXIgdGhhbiBkZWZpbmVkIHBhcmFtZXRlciBhbnN3ZXJzLiANCg0KDQpEdWUgdG8gYWNjdXJhY3ksIGlmIHRpbWUgaXMgYWxsb3RlZCB0aGUgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gaXMgdGhlIGJlc3QgdGVzdCB0byB1c2UuIFVsdGltYXRlbHkgdGhlIE1MRSBzaGFyZXMgc29tZSBtZXRob2RvbG9neSBmcm9tIHRoZSBNTUUsIG1ha2luZyBpdCBhIG1vcmUgcmVmaW5lZCB0ZXN0IG9mIHRoZSBNTEUuIE9mdGVuIHRoZSBNTEUgYW5kIE1NRSBjYW4gYmUgdXNlZCB0b2dldGhlci4gVGhlIE1NRSBjYW4gYWN0IGFzIGEgbGVzcyBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgTUxFIGFzIHRoZSBNTEUgYmVjb21lcyBtb3JlIGNvbXBsZXguIA0KDQojIyBDb21wYXJpc29uIE1MRSBhbmQgTU1FIHdpdGggQmlydGhpbmcgVGltZSBEYXRhDQpJbiBvYnNlcnZpbmcgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIE1heGltdW0gTGlrZWxpaG9vZCBFc3RpbWF0aW9uIGFuZCBNZXRob2RzIG9mIE1vbWVudHMgRXN0aW1hdGlvbiBkaXJlY3RseSBvbiB0aGUgYmlydGhpbmcgdGltZSBkYXRhc2V0LCB3ZSBzZWUgdGhlIGdhbW1hIGFscGhhIHBhcmFtZXRlciBjYWxjdWxhdGVkIHVzaW5nIHRoZSBNTUUgaXMgJFx0aWxkZXtcYWxwaGF9X3tNTUV9JCA9IGByIG1vbV9hbHBoYWAgYW5kIHRoZSBiZXRhIHBhcmFtZXRlciBpcyAkXHRpbGRle1xiZXRhfV97TU1FfSQgPSBgciBtb21fYmV0YWAuIFRoZSBNTEUgZ2FtbWEgYWxwaGEgcGFyYW1ldGVyIGlzICRcaGF0e1xhbHBoYX1fe01MRX0kID0gYHIgbWxlLnJlc3VsdCRwYXJbMV1gIGFuZCB0aGUgYmV0YSBwYXJhbWV0ZXIgaXMgJFxoYXR7XGJldGF9X3tNTEV9JCA9IGByIG1sZS5yZXN1bHQkcGFyWzJdYC4gQWx0aG91Z2ggdGhlIGNhbGN1bGF0ZWQgcGFyYW1ldGVyIHZhbHVlcyBhcmUgc2ltaWxhciwgaXQgaXMgdW5kZXJzdG9vZCB0aGF0IHRoZSBNTUUgcGFyYW1ldGVycyBoYXZlIG1vcmUgdmFyaWFuY2UgdGhhbiB0aGVpciBNTEUgY291bnRlcnBhcnRzLg0KDQoNCmBgYHtyfQ0KDQojIERlZmluaW5nIHRoZSByYW5nZSBmb3IgdGhlIHgtYXhpcyBiYXNlZCBvbiBiaXJ0aCBkYXRhDQp4X3JhbmdlIDwtIHNlcSgwLCBtYXgoZGF0YWJpcnRoKSArIDUsIGxlbmd0aC5vdXQgPSAyMDApDQoNCiMgQ2FsY3VsYXRlIHRoZSBEZW5zaXR5IGN1cnZlcw0KIyBVc2luZyAgTU1FIHZhbHVlcw0KbW9tX2RlbnMgPC0gZGdhbW1hKHhfcmFuZ2UsIHNoYXBlID0gbW9tX2FscGhhLCBzY2FsZSA9IG1vbV9iZXRhKQ0KDQojIFVzaW5nIHlvdXIgTUxFIHZhbHVlcyBmcm9tIG9wdGltDQptbGVfZGVucyA8LSBkZ2FtbWEoeF9yYW5nZSwgc2hhcGUgPSBtbGUucmVzdWx0JHBhclsxXSwgc2NhbGUgPSBtbGUucmVzdWx0JHBhclsyXSkNCg0KIyBDcmVhdGUgdGhlIGRhdGEgZnJhbWUgZm9yIGdncGxvdA0KcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoDQogIHggPSByZXAoeF9yYW5nZSwgMiksDQogIGRlbnNpdHkgPSBjKG1vbV9kZW5zLCBtbGVfZGVucyksDQogIE1ldGhvZCA9IHJlcChjKCJNZXRob2Qgb2YgTW9tZW50IEVzdGltYXRvciIsICJNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdG9yIiksIGVhY2ggPSBsZW5ndGgoeF9yYW5nZSkpDQopDQoNCiMgQnVpbGRpbmcgcGxvdA0KZ2FtbWFfcGx0IDwtIGdncGxvdCgpICsNCiAgIyBBZGQgdGhlIGFjdHVhbCBkYXRhIGFzIGEgaGlzdG9ncmFtIG9yIGRlbnNpdHkgYXJlYQ0KICAjZ3JheSBpcyB0aGUgYmlydGhkYXRhIGRpc3RyaWJ1dGlvbg0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGRhdGFiaXJ0aCksIGFlcyh4ID0geCksIA0KICAgICAgICAgICAgICAgZmlsbCA9ICJncmF5OTAiLCBjb2xvciA9ICJncmF5NjAiLCBhbHBoYSA9IDAuNSkgKw0KICAjIEFkZCB0aGUgdHdvIHRoZW9yZXRpY2FsIGxpbmVzDQogIGdlb21fbGluZShkYXRhID0gcGxvdF9kYXRhLCBhZXMoeCA9IHgsIHkgPSBkZW5zaXR5LCBjb2xvciA9IE1ldGhvZCwgbGluZXR5cGUgPSBNZXRob2QpLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIk1ldGhvZCBvZiBNb21lbnQgRXN0aW1hdG9ycyBDb21wYXJlZCB0byANCiAgICAgICBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdG9ycyBmb3IgQmlydGggVGltZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkNvbXBhcmluZyBlc3RpbWF0b3IgZGVuc2l0aWVzIGFnYWluc3Qgb2JzZXJ2ZWQgZGF0YSBpbiBhIGdhbW1hIGRpc3RyaWJ1dGlvbiIsDQogICAgICAgeCA9ICJIb3VycyBpbiBEZWxpdmVyeSBTdWl0ZSIsIA0KICAgICAgIHkgPSAiRGVuc2l0eSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk1ldGhvZCBvZiBNb21lbnQgRXN0aW1hdG9yIiA9ICJibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdG9yIiA9ICJyZWQiKSkgKyAjVGhlIGVzdGltYXRvcidzIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIE1NRSBhbmQgdGhlIE1MRSBpbiBkaWZmZXJlbnQgY29sb3JzDQoNCiNUaGUgZGlzdHJpYnV0aW9uIGlzIGRyYXduIHdlIG5lZWQgYm90aCB0aGUgcGFyYW1ldGVycyBhbHBoYSBhbmQgYmV0YSB0aGUgZHJhdyB0aGUgZGlzdHJpYnV0aW9uLCBzbyB0aGV5IGFyZSBib3RoIHJlcHJlc2VudGVkDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBNYWtlIGl0IGludGVyYWN0aXZlDQpnZ3Bsb3RseShnYW1tYV9wbHQpDQoNCmBgYA0KVXNpbmcgdGhlIGRlbGl2ZXJ5IHN1aXRlIGJpcnRoaW5nIHRpbWVzIGRhdGEsIHdlIHNlZSB0aGF0IGJvdGggdGhlIE1ldGhvZCBvZiBNb21lbnQgRXN0aW1hdG9yIChibHVlKSBhbmQgdGhlIE1heGltdW0gTGlrZWxpaG9vZCBFc3RpbWF0b3IgKHJlZCkgY2FwdHVyZSB0aGUgZGlzdHJpYnV0aW9uIHNpbWlsYXJseSwgYWx0aG91Z2ggdGhlIE1MRSBjYXB0dXJlIHRoZSBkaXN0cmlidXRpb24gY2xvc2VyIHRvIHRoZSB0cnVlIGRpc3RyaWJ1dGlvbiAoZ3JheSkuIFRoZXNlIHNhbXBsZSBlc3RpbWF0aW9ucyB3b3VsZCB0aGVuIGJlIHVzZWQgdG8gYmVzdCBwYXJhbWV0ZXJpemUgdGhlIHBvcHVsYXRpb24u