For weeks, we’ve built analytical capacity using two core models: Linear Regression (Normal Distribution) and Logistic Regression (Binomial Distribution). Today, we look under the hood of the glm() function in R to see how these models are just two members of a much larger, incredibly flexible family: Generalized Linear Models (GLMs).

Your knowledge of Linear and Logistic Regression is the foundation. If you can identify the type of data you have, you can select the correct GLM family, and the rest of the process (fitting, interpreting, diagnosing) follows familiar rules.

The GLM Framework: The Three Pillars

Every GLM is defined by three fundamental components. In glm(), you only need to specify the Random Component (via family).

Component Description Linear Regression (Normal) Logistic Regression (Binomial) New GLMs
1. Random The probability distribution of the response variable (\(Y\)). Normal (family = gaussian) Binomial (family = binomial) Poisson, Gamma, etc.
2. Systematic The linear predictor: \(X\beta\) (the familiar part). \(\beta_0 + \beta_1 X_1 + \dots\) \(\beta_0 + \beta_1 X_1 + \dots\) Same!
3. Link The function that links the mean of \(Y\) to the linear predictor. Identity (\(\mu = \eta\)) Logit (\(\log(\frac{\mu}{1-\mu}) = \eta\)) Log, Inverse, etc.

For any new GLM, we only need to learn:

  • Which family to use (based on the data type).
  • What the link function does to the mean \(\mu\).

Modeling Counts with Poisson Regression

Call Center Incident Reports: A company wants to model the number of customer support incidents reported per week for different product lines. Since the response variable is a count (0, 1, 2, 3, …), the Normal (Gaussian) distribution is inappropriate, and the Binomial (Logistic) distribution is for proportions. Count data is exactly applicable to the Poisson GLM Family.

R Code & Interpretation

We’ll simulate some data for demonstration.

library(tidyverse)

# 1. Simulate data (Incident Reports)
set.seed(42)
data_poisson <- tibble(
  Product_Age_Years = runif(100, 1, 10),
  Support_Level = factor(sample(c("Basic", "Premium"), 100, replace = TRUE, prob = c(0.7, 0.3))),
  # Generate counts (rate depends on age and support)
  lambda = exp(1.5 - 0.2 * Product_Age_Years + 0.5 * (Support_Level == "Premium")),
  Incidents_Weekly = rpois(100, lambda)
)

# 2. Fit the Poisson GLM
poisson_model <- glm(
  Incidents_Weekly ~ Product_Age_Years + Support_Level,
  family = poisson(link = "log"), # The only change from lm() or glm(..., family=binomial)
  data = data_poisson
)

summary(poisson_model)

Call:
glm(formula = Incidents_Weekly ~ Product_Age_Years + Support_Level, 
    family = poisson(link = "log"), data = data_poisson)

Coefficients:
                     Estimate Std. Error z value Pr(>|z|)    
(Intercept)           1.38469    0.16897   8.195 2.51e-16 ***
Product_Age_Years    -0.19174    0.02863  -6.698 2.11e-11 ***
Support_LevelPremium  0.38595    0.15029   2.568   0.0102 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for poisson family taken to be 1)

    Null deviance: 183.54  on 99  degrees of freedom
Residual deviance: 124.62  on 97  degrees of freedom
AIC: 326.7

Number of Fisher Scoring iterations: 5

Interpretation of Coefficients:

Just like in Logistic Regression, we must exponentiate the coefficients to return them to the original scale of the response variable (counts). Exponentiated coefficients are interpreted as Rate Ratios.

# Exponentiate the coefficients
exp(coef(poisson_model))
         (Intercept)    Product_Age_Years Support_LevelPremium 
            3.993592             0.825518             1.471006 
Coefficient Estimate Exp(Estimate) Interpretation
Product_Age_Years -0.19 0.82 A 1-year increase in product age is associated with an 18% decrease (\(\approx 1 - 0.82\)) in the expected weekly incident rate.
Support_LevelPremium 0.39 1.47 Premium support products have a 47% higher expected weekly incident rate compared to Basic support products, holding age constant.

Assumption Check: Overdispersion

The core assumption of Poisson is that the mean equals the variance (\(\text{Var}(Y) = \mu\)). If the variance is much larger than the mean, we have overdispersion, which leads to deflated standard errors (and inflated significance).

  • Diagnostic: Check the Residual Deviance against the Degrees of Freedom (DoF) from the summary().
# Check for Overdispersion: Ratio of Residual Deviance / Residual DoF
deviance_ratio <- summary(poisson_model)$deviance / summary(poisson_model)$df.residual
print(paste("Deviance Ratio:", round(deviance_ratio, 2)))
[1] "Deviance Ratio: 1.28"
# Rule of Thumb: If ratio > 1.5, consider switching to a Quasi-Poisson or Negative Binomial model.

Graphical Summary

A key visualization is comparing the predicted rates to the observed counts.

# Add predictions (expected counts) back to the data
data_poisson <- data_poisson %>%
  mutate(Expected_Incidents = predict(poisson_model, type = "response"))

# Plot Expected vs. Observed Counts
ggplot(data_poisson, aes(x = Expected_Incidents, y = Incidents_Weekly, color = Support_Level)) +
  geom_point(alpha = 0.6) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey") +
  labs(
    title = "Poisson Model Fit: Expected vs. Observed Incident Counts",
    x = "Expected Weekly Incidents (Model Prediction)",
    y = "Observed Weekly Incidents",
    color = "Support Level"
  ) +
  theme_minimal()

Points on the graph above the line indicate where our model underestimates the count, points below the line indicate where our model overestimates the count, and points exactly on the line are perfect predictions.

Modeling Skewed Continuous Data with Gamma Regression

Insurance Claim Amounts: An insurance company wants to model the size of financial claims paid out to customers. Claim amounts are continuous and positive (cannot be negative). They are highly right-skewed (most claims are small, but a few are very large). Positive, continuous, skewed data types can be modeled with the Gamma GLM Family.

R Code & Interpretation

# 3. Simulate Gamma data (Claim Amounts)
set.seed(101)
data_gamma <- tibble(
  Customer_Score = runif(100, 500, 800), # e.g., credit/risk score
  Claim_Type = factor(sample(c("Minor", "Major"), 100, replace = TRUE, prob = c(0.7, 0.3))),
  # Generate claim amounts (mean depends on score and claim type)
  mu = exp(7 - 0.005 * Customer_Score + 0.5 * (Claim_Type == "Major")),
  Claim_Amount = rgamma(100, shape = 2, rate = 2/mu) # Gamma parameterization
)

# 4. Fit the Gamma GLM
gamma_model <- glm(
  Claim_Amount ~ Customer_Score + Claim_Type,
  family = Gamma(link = "log"), # Family changes, link stays 'log'
  data = data_gamma
)

summary(gamma_model)

Call:
glm(formula = Claim_Amount ~ Customer_Score + Claim_Type, family = Gamma(link = "log"), 
    data = data_gamma)

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      7.3717659  0.6912186  10.665  < 2e-16 ***
Customer_Score  -0.0048348  0.0009964  -4.852 4.66e-06 ***
Claim_TypeMinor -0.5678030  0.1919092  -2.959  0.00388 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for Gamma family taken to be 0.7735794)

    Null deviance: 84.850  on 99  degrees of freedom
Residual deviance: 63.418  on 97  degrees of freedom
AIC: 953.49

Number of Fisher Scoring iterations: 5

Interpretation of Coefficients (Rate Ratios Again!):

Since we used the Log Link again, the interpretation template is the same as Poisson and Logistic Regression—exponentiate the coefficient for a multiplicative effect.

# Exponentiate the coefficients
exp(coef(gamma_model))
    (Intercept)  Customer_Score Claim_TypeMinor 
   1590.4398662       0.9951769       0.5667693 

Since the Gamma model used the Log Link (family = Gamma(link = "log")), we interpret the exponentiated coefficients (\(\exp(\beta)\)) as a multiplicative ratio relative to the expected mean (claim amount).

Coefficient Estimate \((\beta)\) \(\exp(\beta)\) Interpretation (Multiplicative)
(Intercept) 7.3717659 1590.44 This is the expected claim amount (in currency units, e.g., dollars) when all predictors are zero (i.e., Customer Score = 0 and Claim Type = Major). This value is usually theoretical since a score of 0 is unrealistic.
Customer_Score -0.0048348 0.9951769 For every one-unit increase in the Customer Score, the expected claim amount is multiplied by 0.995, or decreases by approximately 0.48% (\(1 - 0.9951769 = 0.00482\)). This suggests higher customer scores are associated with slightly lower claim amounts.
Claim_TypeMinor -0.5678030 0.5667693 The baseline (omitted) category is Claim_TypeMajor. Therefore, the expected claim amount for a Minor claim is 0.567 times the expected claim amount for a Major claim, holding Customer Score constant. In other words, Minor claims are expected to be about 43.3% lower (\(1 - 0.5667693\)) than Major claims.

Assumption Check: Homoscedasticity vs. Variance Function

Unlike Linear Regression where variance is constant (homoscedasticity), GLMs allow variance to be a function of the mean (\(\text{Var}(Y) = V(\mu)\)).

  • For Normal, \(V(\mu) = \sigma^2\) (Constant variance).
  • For Poisson, \(V(\mu) = \mu\) (Variance increases with the mean).
  • For Gamma, \(V(\mu) = \phi \mu^2\) (Variance increases with the square of the mean).

Diagnostic: Plotting the standardized residuals against the fitted values (mean).

plot(
  fitted(gamma_model),
  resid(gamma_model, type = "deviance"),
  xlab = "Fitted Values (Expected Claim Amount)",
  ylab = "Deviance Residuals",
  main = "Gamma Model Residuals vs. Fitted Values"
)
abline(h = 0, col = "grey", lty = 2)

We look for random scatter around zero. We are not looking for constant variance (as in Linear Regression), but rather ensuring there is no obvious pattern (like a cone shape, which would indicate a poor fit for the chosen variance function, \(V(\mu) = \phi \mu^2\)).

Conclusion

The Transferable Analytical Capacity

You now know three different GLMs, and yet, the core skills you use are identical:

Task Linear/Logistic/Poisson/Gamma
Model Specification Y ~ X1 + X2 (The systematic component is the same)
P-Value Interpretation A high Wald \(\chi^2\) statistic (t-statistic for Linear) results in a low p-value, indicating the predictor is relevant to the link function of the mean.
Coefficient Interpretation Identity Link (Linear): Additive effect. \(\beta_1\) is the change in \(Y\) per unit \(X_1\).
Log Link (Logistic, Poisson, Gamma): Multiplicative effect. \(\exp(\beta_1)\) is the ratio of means/rates/odds per unit \(X_1\).

Whether you are dealing with Log Odds (Logistic), Log Rates (Poisson), or Log Means (Gamma), the process of exponentiating the coefficient to get a multiplicative ratio is the same. You just need to adjust your language (odds, rate, or mean) to match the data type!

LS0tDQp0aXRsZTogIkdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgKEdMTXMpIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0aGVtZTogY2VydWxlYW4NCi0tLQ0KDQpGb3Igd2Vla3MsIHdl4oCZdmUgYnVpbHQgYW5hbHl0aWNhbCBjYXBhY2l0eSB1c2luZyB0d28gY29yZSBtb2RlbHM6ICoqTGluZWFyIFJlZ3Jlc3Npb24gKE5vcm1hbCBEaXN0cmlidXRpb24pKiogYW5kICoqTG9naXN0aWMgUmVncmVzc2lvbiAoQmlub21pYWwgRGlzdHJpYnV0aW9uKSoqLiBUb2RheSwgd2UgbG9vayB1bmRlciB0aGUgaG9vZCBvZiB0aGUgYGdsbSgpYCBmdW5jdGlvbiBpbiBgUmAgdG8gc2VlIGhvdyB0aGVzZSBtb2RlbHMgYXJlIGp1c3QgdHdvIG1lbWJlcnMgb2YgYSBtdWNoIGxhcmdlciwgaW5jcmVkaWJseSBmbGV4aWJsZSBmYW1pbHk6ICoqR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVscyAoR0xNcykqKi4NCg0KWW91ciBrbm93bGVkZ2Ugb2YgTGluZWFyIGFuZCBMb2dpc3RpYyBSZWdyZXNzaW9uIGlzIHRoZSBmb3VuZGF0aW9uLiBJZiB5b3UgY2FuIGlkZW50aWZ5IHRoZSB0eXBlIG9mIGRhdGEgeW91IGhhdmUsIHlvdSBjYW4gc2VsZWN0IHRoZSBjb3JyZWN0IEdMTSBmYW1pbHksIGFuZCB0aGUgcmVzdCBvZiB0aGUgcHJvY2VzcyAoZml0dGluZywgaW50ZXJwcmV0aW5nLCBkaWFnbm9zaW5nKSBmb2xsb3dzIGZhbWlsaWFyIHJ1bGVzLg0KDQoNCiMjIFRoZSBHTE0gRnJhbWV3b3JrOiBUaGUgVGhyZWUgUGlsbGFycyANCg0KRXZlcnkgR0xNIGlzIGRlZmluZWQgYnkgdGhyZWUgZnVuZGFtZW50YWwgY29tcG9uZW50cy4gSW4gYGdsbSgpYCwgeW91IG9ubHkgbmVlZCB0byBzcGVjaWZ5IHRoZSAqKlJhbmRvbSBDb21wb25lbnQqKiAodmlhIGBmYW1pbHlgKS4NCg0KfCBDb21wb25lbnQgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IExpbmVhciBSZWdyZXNzaW9uIChOb3JtYWwpICAgICAgfCBMb2dpc3RpYyBSZWdyZXNzaW9uIChCaW5vbWlhbCkgICAgICAgICAgIHwgTmV3IEdMTXMgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAxLiAqKlJhbmRvbSoqICAgICB8IFRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlICgkWSQpLiAgICAgfCAqTm9ybWFsKiAoYGZhbWlseSA9IGdhdXNzaWFuYCkgICAgICB8ICpCaW5vbWlhbCogKGBmYW1pbHkgPSBiaW5vbWlhbGApICAgICAgICAgICAgIHwgKlBvaXNzb24qLCAqR2FtbWEqLCBldGMuIHwNCnwgMi4gKipTeXN0ZW1hdGljKiogfCBUaGUgbGluZWFyIHByZWRpY3RvcjogJFhcYmV0YSQgKHRoZSBmYW1pbGlhciBwYXJ0KS4gICAgICAgICAgICAgIHwgJFxiZXRhXzAgKyBcYmV0YV8xIFhfMSArIFxkb3RzJCB8ICRcYmV0YV8wICsgXGJldGFfMSBYXzEgKyBcZG90cyQgICAgICAgICAgfCAqKlNhbWUhKiogICAgICAgICAgICAgICAgfA0KfCAzLiAqKkxpbmsqKiAgICAgICB8IFRoZSBmdW5jdGlvbiB0aGF0IGxpbmtzIHRoZSBtZWFuIG9mICRZJCB0byB0aGUgbGluZWFyIHByZWRpY3Rvci4gfCAqSWRlbnRpdHkqICgkXG11ID0gXGV0YSQpICAgICAgICAgfCAqTG9naXQqICgkXGxvZyhcZnJhY3tcbXV9ezEtXG11fSkgPSBcZXRhJCkgfCAqTG9nKiwgKkludmVyc2UqLCBldGMuICAgfA0KDQoNCkZvciBhbnkgbmV3IEdMTSwgd2Ugb25seSBuZWVkIHRvIGxlYXJuOg0KDQogLSBXaGljaCBgZmFtaWx5YCB0byB1c2UgKGJhc2VkIG9uIHRoZSBkYXRhIHR5cGUpLiAgDQogLSBXaGF0IHRoZSBgbGlua2AgZnVuY3Rpb24gZG9lcyB0byB0aGUgbWVhbiAkXG11JC4NCiANCiMjIE1vZGVsaW5nIENvdW50cyB3aXRoIFBvaXNzb24gUmVncmVzc2lvbiANCg0KKipDYWxsIENlbnRlciBJbmNpZGVudCBSZXBvcnRzOioqIEEgY29tcGFueSB3YW50cyB0byBtb2RlbCB0aGUgbnVtYmVyIG9mIGN1c3RvbWVyIHN1cHBvcnQgaW5jaWRlbnRzIHJlcG9ydGVkIHBlciB3ZWVrIGZvciBkaWZmZXJlbnQgcHJvZHVjdCBsaW5lcy4gU2luY2UgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIGEgY291bnQgKDAsIDEsIDIsIDMsIC4uLiksIHRoZSBOb3JtYWwgKEdhdXNzaWFuKSBkaXN0cmlidXRpb24gaXMgaW5hcHByb3ByaWF0ZSwgYW5kIHRoZSBCaW5vbWlhbCAoTG9naXN0aWMpIGRpc3RyaWJ1dGlvbiBpcyBmb3IgcHJvcG9ydGlvbnMuIENvdW50IGRhdGEgaXMgZXhhY3RseSBhcHBsaWNhYmxlIHRvIHRoZSBQb2lzc29uIEdMTSBGYW1pbHkuDQoNCiMjIyBUaGUgUG9pc3NvbiBMaW5rIEZ1bmN0aW9uDQoNClRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiByZXF1aXJlcyB0aGUgbWVhbiAoJFxtdSQpIHRvIGJlIHBvc2l0aXZlLiBUaGUgc3RhbmRhcmQgbGluayBmdW5jdGlvbiBpcyB0aGUgTG9nIExpbms6DQoNCiQkXGxvZyhcbXUpID0gXGJldGFfMCArIFxiZXRhXzEgWF8xICsgXGRvdHMkJA0KDQpUaGlzIGlzIGlkZW50aWNhbCB0byB0aGUgTG9naXQgbGluayBpbiBMb2dpc3RpYyBSZWdyZXNzaW9uISBUaGlzIG1lYW5zIHRoZSBjb2VmZmljaWVudHMgd2lsbCBiZSBpbnRlcnByZXRlZCBzaW1pbGFybHk6IG11bHRpcGxpY2F0aXZlbHkuDQoNCiMjIyBSIENvZGUgJiBJbnRlcnByZXRhdGlvbg0KDQpXZSdsbCBzaW11bGF0ZSBzb21lIGRhdGEgZm9yIGRlbW9uc3RyYXRpb24uDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMgMS4gU2ltdWxhdGUgZGF0YSAoSW5jaWRlbnQgUmVwb3J0cykNCnNldC5zZWVkKDQyKQ0KZGF0YV9wb2lzc29uIDwtIHRpYmJsZSgNCiAgUHJvZHVjdF9BZ2VfWWVhcnMgPSBydW5pZigxMDAsIDEsIDEwKSwNCiAgU3VwcG9ydF9MZXZlbCA9IGZhY3RvcihzYW1wbGUoYygiQmFzaWMiLCAiUHJlbWl1bSIpLCAxMDAsIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gYygwLjcsIDAuMykpKSwNCiAgIyBHZW5lcmF0ZSBjb3VudHMgKHJhdGUgZGVwZW5kcyBvbiBhZ2UgYW5kIHN1cHBvcnQpDQogIGxhbWJkYSA9IGV4cCgxLjUgLSAwLjIgKiBQcm9kdWN0X0FnZV9ZZWFycyArIDAuNSAqIChTdXBwb3J0X0xldmVsID09ICJQcmVtaXVtIikpLA0KICBJbmNpZGVudHNfV2Vla2x5ID0gcnBvaXMoMTAwLCBsYW1iZGEpDQopDQoNCiMgMi4gRml0IHRoZSBQb2lzc29uIEdMTQ0KcG9pc3Nvbl9tb2RlbCA8LSBnbG0oDQogIEluY2lkZW50c19XZWVrbHkgfiBQcm9kdWN0X0FnZV9ZZWFycyArIFN1cHBvcnRfTGV2ZWwsDQogIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSwgIyBUaGUgb25seSBjaGFuZ2UgZnJvbSBsbSgpIG9yIGdsbSguLi4sIGZhbWlseT1iaW5vbWlhbCkNCiAgZGF0YSA9IGRhdGFfcG9pc3Nvbg0KKQ0KDQpzdW1tYXJ5KHBvaXNzb25fbW9kZWwpDQpgYGANCg0KIyMjIEludGVycHJldGF0aW9uIG9mIENvZWZmaWNpZW50czoNCg0KSnVzdCBsaWtlIGluIExvZ2lzdGljIFJlZ3Jlc3Npb24sIHdlIG11c3QgZXhwb25lbnRpYXRlIHRoZSBjb2VmZmljaWVudHMgdG8gcmV0dXJuIHRoZW0gdG8gdGhlIG9yaWdpbmFsIHNjYWxlIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZSAoY291bnRzKS4gRXhwb25lbnRpYXRlZCBjb2VmZmljaWVudHMgYXJlIGludGVycHJldGVkIGFzIFJhdGUgUmF0aW9zLg0KDQpgYGB7cn0NCiMgRXhwb25lbnRpYXRlIHRoZSBjb2VmZmljaWVudHMNCmV4cChjb2VmKHBvaXNzb25fbW9kZWwpKQ0KYGBgDQoNCnwgQ29lZmZpY2llbnQgICAgICAgICAgfCBFc3RpbWF0ZSB8IEV4cChFc3RpbWF0ZSkgfCBJbnRlcnByZXRhdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYFByb2R1Y3RfQWdlX1llYXJzYCAgICB8IC0wLjE5ICAgIHwgX18wLjgyX18gICAgICAgICAgfCBBIDEteWVhciBpbmNyZWFzZSBpbiBwcm9kdWN0IGFnZSBpcyBhc3NvY2lhdGVkIHdpdGggYW4gX18xOCUgZGVjcmVhc2VfXyAoJFxhcHByb3ggMSAtIDAuODIkKSBpbiB0aGUgZXhwZWN0ZWQgd2Vla2x5IGluY2lkZW50IHJhdGUuICAgICB8DQp8IGBTdXBwb3J0X0xldmVsUHJlbWl1bWAgfCAwLjM5ICAgICB8IF9fMS40N19fICAgICAgICAgIHwgUHJlbWl1bSBzdXBwb3J0IHByb2R1Y3RzIGhhdmUgYSBfXzQ3JSBoaWdoZXJfXyBleHBlY3RlZCB3ZWVrbHkgaW5jaWRlbnQgcmF0ZSBjb21wYXJlZCB0byBCYXNpYyBzdXBwb3J0IHByb2R1Y3RzLCBob2xkaW5nIGFnZSBjb25zdGFudC4gfA0KDQoNCiMjIyBBc3N1bXB0aW9uIENoZWNrOiBPdmVyZGlzcGVyc2lvbg0KDQpUaGUgY29yZSBhc3N1bXB0aW9uIG9mIFBvaXNzb24gaXMgdGhhdCB0aGUgbWVhbiBlcXVhbHMgdGhlIHZhcmlhbmNlICgkXHRleHR7VmFyfShZKSA9IFxtdSQpLiBJZiB0aGUgdmFyaWFuY2UgaXMgbXVjaCBsYXJnZXIgdGhhbiB0aGUgbWVhbiwgd2UgaGF2ZSBvdmVyZGlzcGVyc2lvbiwgd2hpY2ggbGVhZHMgdG8gZGVmbGF0ZWQgc3RhbmRhcmQgZXJyb3JzIChhbmQgaW5mbGF0ZWQgc2lnbmlmaWNhbmNlKS4gDQoNCiAtIF9fRGlhZ25vc3RpYzpfXyBDaGVjayB0aGUgUmVzaWR1YWwgRGV2aWFuY2UgYWdhaW5zdCB0aGUgRGVncmVlcyBvZiBGcmVlZG9tIChEb0YpIGZyb20gdGhlIGBzdW1tYXJ5KClgLg0KIA0KYGBge3J9DQojIENoZWNrIGZvciBPdmVyZGlzcGVyc2lvbjogUmF0aW8gb2YgUmVzaWR1YWwgRGV2aWFuY2UgLyBSZXNpZHVhbCBEb0YNCmRldmlhbmNlX3JhdGlvIDwtIHN1bW1hcnkocG9pc3Nvbl9tb2RlbCkkZGV2aWFuY2UgLyBzdW1tYXJ5KHBvaXNzb25fbW9kZWwpJGRmLnJlc2lkdWFsDQpwcmludChwYXN0ZSgiRGV2aWFuY2UgUmF0aW86Iiwgcm91bmQoZGV2aWFuY2VfcmF0aW8sIDIpKSkNCg0KIyBSdWxlIG9mIFRodW1iOiBJZiByYXRpbyA+IDEuNSwgY29uc2lkZXIgc3dpdGNoaW5nIHRvIGEgUXVhc2ktUG9pc3NvbiBvciBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbC4NCmBgYA0KDQojIyMgR3JhcGhpY2FsIFN1bW1hcnkNCg0KQSBrZXkgdmlzdWFsaXphdGlvbiBpcyBjb21wYXJpbmcgdGhlIHByZWRpY3RlZCByYXRlcyB0byB0aGUgb2JzZXJ2ZWQgY291bnRzLg0KDQpgYGB7cn0NCiMgQWRkIHByZWRpY3Rpb25zIChleHBlY3RlZCBjb3VudHMpIGJhY2sgdG8gdGhlIGRhdGENCmRhdGFfcG9pc3NvbiA8LSBkYXRhX3BvaXNzb24gJT4lDQogIG11dGF0ZShFeHBlY3RlZF9JbmNpZGVudHMgPSBwcmVkaWN0KHBvaXNzb25fbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKSkNCg0KIyBQbG90IEV4cGVjdGVkIHZzLiBPYnNlcnZlZCBDb3VudHMNCmdncGxvdChkYXRhX3BvaXNzb24sIGFlcyh4ID0gRXhwZWN0ZWRfSW5jaWRlbnRzLCB5ID0gSW5jaWRlbnRzX1dlZWtseSwgY29sb3IgPSBTdXBwb3J0X0xldmVsKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQb2lzc29uIE1vZGVsIEZpdDogRXhwZWN0ZWQgdnMuIE9ic2VydmVkIEluY2lkZW50IENvdW50cyIsDQogICAgeCA9ICJFeHBlY3RlZCBXZWVrbHkgSW5jaWRlbnRzIChNb2RlbCBQcmVkaWN0aW9uKSIsDQogICAgeSA9ICJPYnNlcnZlZCBXZWVrbHkgSW5jaWRlbnRzIiwNCiAgICBjb2xvciA9ICJTdXBwb3J0IExldmVsIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANClBvaW50cyBvbiB0aGUgZ3JhcGggYWJvdmUgdGhlIGxpbmUgaW5kaWNhdGUgd2hlcmUgb3VyIG1vZGVsIHVuZGVyZXN0aW1hdGVzIHRoZSBjb3VudCwgcG9pbnRzIGJlbG93IHRoZSBsaW5lIGluZGljYXRlIHdoZXJlIG91ciBtb2RlbCBvdmVyZXN0aW1hdGVzIHRoZSBjb3VudCwgYW5kIHBvaW50cyBleGFjdGx5IG9uIHRoZSBsaW5lIGFyZSBwZXJmZWN0IHByZWRpY3Rpb25zLiANCg0KIyMgTW9kZWxpbmcgU2tld2VkIENvbnRpbnVvdXMgRGF0YSB3aXRoIEdhbW1hIFJlZ3Jlc3Npb24gDQoNCl9fSW5zdXJhbmNlIENsYWltIEFtb3VudHNfXzogQW4gaW5zdXJhbmNlIGNvbXBhbnkgd2FudHMgdG8gbW9kZWwgdGhlIHNpemUgb2YgZmluYW5jaWFsIGNsYWltcyBwYWlkIG91dCB0byBjdXN0b21lcnMuIENsYWltIGFtb3VudHMgYXJlICpjb250aW51b3VzKiBhbmQgKnBvc2l0aXZlKiAoY2Fubm90IGJlIG5lZ2F0aXZlKS4gVGhleSBhcmUgKmhpZ2hseSByaWdodC1za2V3ZWQqIChtb3N0IGNsYWltcyBhcmUgc21hbGwsIGJ1dCBhIGZldyBhcmUgdmVyeSBsYXJnZSkuIFBvc2l0aXZlLCBjb250aW51b3VzLCBza2V3ZWQgZGF0YSB0eXBlcyBjYW4gYmUgbW9kZWxlZCB3aXRoIHRoZSBfX0dhbW1hX18gR0xNIEZhbWlseS4NCg0KIyMjIFRoZSBHYW1tYSBMaW5rIEZ1bmN0aW9uDQoNClRoZSBHYW1tYSBkaXN0cmlidXRpb24gY29tbW9ubHkgdXNlcyB0aGUgKipMb2cgTGluayoqIChvciBzb21ldGltZXMgdGhlICoqSW52ZXJzZSBsaW5rKiopLiBXZSdsbCBzdGljayB3aXRoIExvZyBhcyBpdCBhbGxvd3MgZm9yIHRoZSBtdWx0aXBsaWNhdGl2ZSBpbnRlcnByZXRhdGlvbiB3ZSBqdXN0IGxlYXJuZWQuDQoNCiQkXGxvZyhcbXUpID0gXGJldGFfMCArIFxiZXRhXzEgWF8xICsgXGRvdHMkJA0KDQojIyMgUiBDb2RlICYgSW50ZXJwcmV0YXRpb24NCmBgYHtyfQ0KIyAzLiBTaW11bGF0ZSBHYW1tYSBkYXRhIChDbGFpbSBBbW91bnRzKQ0Kc2V0LnNlZWQoMTAxKQ0KZGF0YV9nYW1tYSA8LSB0aWJibGUoDQogIEN1c3RvbWVyX1Njb3JlID0gcnVuaWYoMTAwLCA1MDAsIDgwMCksICMgZS5nLiwgY3JlZGl0L3Jpc2sgc2NvcmUNCiAgQ2xhaW1fVHlwZSA9IGZhY3RvcihzYW1wbGUoYygiTWlub3IiLCAiTWFqb3IiKSwgMTAwLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC43LCAwLjMpKSksDQogICMgR2VuZXJhdGUgY2xhaW0gYW1vdW50cyAobWVhbiBkZXBlbmRzIG9uIHNjb3JlIGFuZCBjbGFpbSB0eXBlKQ0KICBtdSA9IGV4cCg3IC0gMC4wMDUgKiBDdXN0b21lcl9TY29yZSArIDAuNSAqIChDbGFpbV9UeXBlID09ICJNYWpvciIpKSwNCiAgQ2xhaW1fQW1vdW50ID0gcmdhbW1hKDEwMCwgc2hhcGUgPSAyLCByYXRlID0gMi9tdSkgIyBHYW1tYSBwYXJhbWV0ZXJpemF0aW9uDQopDQoNCiMgNC4gRml0IHRoZSBHYW1tYSBHTE0NCmdhbW1hX21vZGVsIDwtIGdsbSgNCiAgQ2xhaW1fQW1vdW50IH4gQ3VzdG9tZXJfU2NvcmUgKyBDbGFpbV9UeXBlLA0KICBmYW1pbHkgPSBHYW1tYShsaW5rID0gImxvZyIpLCAjIEZhbWlseSBjaGFuZ2VzLCBsaW5rIHN0YXlzICdsb2cnDQogIGRhdGEgPSBkYXRhX2dhbW1hDQopDQoNCnN1bW1hcnkoZ2FtbWFfbW9kZWwpDQpgYGANCg0KIyMjIEludGVycHJldGF0aW9uIG9mIENvZWZmaWNpZW50cyAoUmF0ZSBSYXRpb3MgQWdhaW4hKToNCg0KU2luY2Ugd2UgdXNlZCB0aGUgTG9nIExpbmsgYWdhaW4sIHRoZSBpbnRlcnByZXRhdGlvbiB0ZW1wbGF0ZSBpcyB0aGUgc2FtZSBhcyBQb2lzc29uIGFuZCBMb2dpc3RpYyBSZWdyZXNzaW9u4oCUZXhwb25lbnRpYXRlIHRoZSBjb2VmZmljaWVudCBmb3IgYSBtdWx0aXBsaWNhdGl2ZSBlZmZlY3QuDQoNCmBgYHtyfQ0KIyBFeHBvbmVudGlhdGUgdGhlIGNvZWZmaWNpZW50cw0KZXhwKGNvZWYoZ2FtbWFfbW9kZWwpKQ0KYGBgDQoNClNpbmNlIHRoZSBHYW1tYSBtb2RlbCB1c2VkIHRoZSBMb2cgTGluayAoYGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIilgKSwgd2UgaW50ZXJwcmV0IHRoZSBleHBvbmVudGlhdGVkIGNvZWZmaWNpZW50cyAoJFxleHAoXGJldGEpJCkgYXMgYSAqbXVsdGlwbGljYXRpdmUgcmF0aW8qIHJlbGF0aXZlIHRvIHRoZSBleHBlY3RlZCBtZWFuIChjbGFpbSBhbW91bnQpLg0KDQp8IENvZWZmaWNpZW50ICAgICB8IEVzdGltYXRlICQoXGJldGEpJCB8ICRcZXhwKFxiZXRhKSQgICAgfCBJbnRlcnByZXRhdGlvbiAoTXVsdGlwbGljYXRpdmUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYChJbnRlcmNlcHQpYCAgICAgfCA3LjM3MTc2NTkgICAgfCAxNTkwLjQ0ICAgfCBUaGlzIGlzIHRoZSBleHBlY3RlZCBjbGFpbSBhbW91bnQgKGluIGN1cnJlbmN5IHVuaXRzLCBlLmcuLCBkb2xsYXJzKSB3aGVuIGFsbCBwcmVkaWN0b3JzIGFyZSB6ZXJvIChpLmUuLCBDdXN0b21lciBTY29yZSA9IDAgYW5kIENsYWltIFR5cGUgPSBgTWFqb3JgKS4gVGhpcyB2YWx1ZSBpcyB1c3VhbGx5IHRoZW9yZXRpY2FsIHNpbmNlIGEgc2NvcmUgb2YgMCBpcyB1bnJlYWxpc3RpYy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYEN1c3RvbWVyX1Njb3JlYCAgfCAtMC4wMDQ4MzQ4ICAgfCAqKjAuOTk1MTc2OSoqIHwgRm9yIGV2ZXJ5ICoqb25lLXVuaXQgaW5jcmVhc2UqKiBpbiB0aGUgQ3VzdG9tZXIgU2NvcmUsIHRoZSBleHBlY3RlZCBjbGFpbSBhbW91bnQgaXMgbXVsdGlwbGllZCBieSAwLjk5NSwgb3IgZGVjcmVhc2VzIGJ5IGFwcHJveGltYXRlbHkgKiowLjQ4JSoqICgkMSAtIDAuOTk1MTc2OSA9IDAuMDA0ODIkKS4gVGhpcyBzdWdnZXN0cyBoaWdoZXIgY3VzdG9tZXIgc2NvcmVzIGFyZSBhc3NvY2lhdGVkIHdpdGggc2xpZ2h0bHkgbG93ZXIgY2xhaW0gYW1vdW50cy4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBDbGFpbV9UeXBlTWlub3JgIHwgLTAuNTY3ODAzMCAgIHwgKiowLjU2Njc2OTMqKiB8IFRoZSBiYXNlbGluZSAob21pdHRlZCkgY2F0ZWdvcnkgaXMgYENsYWltX1R5cGVNYWpvcmAuIFRoZXJlZm9yZSwgdGhlIGV4cGVjdGVkIGNsYWltIGFtb3VudCBmb3IgYSBgTWlub3JgIGNsYWltIGlzICoqMC41NjcgdGltZXMqKiB0aGUgZXhwZWN0ZWQgY2xhaW0gYW1vdW50IGZvciBhIGBNYWpvcmAgY2xhaW0sIGhvbGRpbmcgQ3VzdG9tZXIgU2NvcmUgY29uc3RhbnQuIEluIG90aGVyIHdvcmRzLCBNaW5vciBjbGFpbXMgYXJlIGV4cGVjdGVkIHRvIGJlIGFib3V0ICoqNDMuMyUgbG93ZXIqKiAoJDEgLSAwLjU2Njc2OTMkKSB0aGFuIE1ham9yIGNsYWltcy4gfA0KDQoNCg0KIyMjIEFzc3VtcHRpb24gQ2hlY2s6IEhvbW9zY2VkYXN0aWNpdHkgdnMuIFZhcmlhbmNlIEZ1bmN0aW9uDQoNClVubGlrZSBMaW5lYXIgUmVncmVzc2lvbiB3aGVyZSB2YXJpYW5jZSBpcyBjb25zdGFudCAoaG9tb3NjZWRhc3RpY2l0eSksIEdMTXMgYWxsb3cgdmFyaWFuY2UgdG8gYmUgYSBmdW5jdGlvbiBvZiB0aGUgbWVhbiAoJFx0ZXh0e1Zhcn0oWSkgPSBWKFxtdSkkKS4gDQoNCiAtIEZvciBOb3JtYWwsICRWKFxtdSkgPSBcc2lnbWFeMiQgKENvbnN0YW50IHZhcmlhbmNlKS4gDQogLSBGb3IgUG9pc3NvbiwgJFYoXG11KSA9IFxtdSQgKFZhcmlhbmNlIGluY3JlYXNlcyB3aXRoIHRoZSBtZWFuKS4gIA0KIC0gRm9yIEdhbW1hLCAkVihcbXUpID0gXHBoaSBcbXVeMiQgKFZhcmlhbmNlIGluY3JlYXNlcyB3aXRoIHRoZSBzcXVhcmUgb2YgdGhlIG1lYW4pLg0KIA0KX19EaWFnbm9zdGljOl9fIFBsb3R0aW5nIHRoZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIGFnYWluc3QgdGhlIGZpdHRlZCB2YWx1ZXMgKG1lYW4pLg0KDQpgYGB7cn0NCnBsb3QoDQogIGZpdHRlZChnYW1tYV9tb2RlbCksDQogIHJlc2lkKGdhbW1hX21vZGVsLCB0eXBlID0gImRldmlhbmNlIiksDQogIHhsYWIgPSAiRml0dGVkIFZhbHVlcyAoRXhwZWN0ZWQgQ2xhaW0gQW1vdW50KSIsDQogIHlsYWIgPSAiRGV2aWFuY2UgUmVzaWR1YWxzIiwNCiAgbWFpbiA9ICJHYW1tYSBNb2RlbCBSZXNpZHVhbHMgdnMuIEZpdHRlZCBWYWx1ZXMiDQopDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJncmV5IiwgbHR5ID0gMikNCmBgYA0KDQpXZSBsb29rIGZvciByYW5kb20gc2NhdHRlciBhcm91bmQgemVyby4gV2UgYXJlIG5vdCBsb29raW5nIGZvciBjb25zdGFudCB2YXJpYW5jZSAoYXMgaW4gTGluZWFyIFJlZ3Jlc3Npb24pLCBidXQgcmF0aGVyIGVuc3VyaW5nIHRoZXJlIGlzIG5vIG9idmlvdXMgcGF0dGVybiAobGlrZSBhIGNvbmUgc2hhcGUsIHdoaWNoIHdvdWxkIGluZGljYXRlIGEgcG9vciBmaXQgZm9yIHRoZSBjaG9zZW4gdmFyaWFuY2UgZnVuY3Rpb24sICRWKFxtdSkgPSBccGhpIFxtdV4yJCkuDQoNCiMjIENvbmNsdXNpb24NCg0KVGhlIFRyYW5zZmVyYWJsZSBBbmFseXRpY2FsIENhcGFjaXR5DQoNCllvdSBub3cga25vdyB0aHJlZSBkaWZmZXJlbnQgR0xNcywgYW5kIHlldCwgdGhlIGNvcmUgc2tpbGxzIHlvdSB1c2UgYXJlIGlkZW50aWNhbDoNCg0KfCBUYXNrICAgICAgICAgICAgICAgICAgICAgICB8IExpbmVhci9Mb2dpc3RpYy9Qb2lzc29uL0dhbW1hICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IE1vZGVsIFNwZWNpZmljYXRpb24gICAgICAgIHwgYFkgfiBYMSArIFgyYCAoVGhlIHN5c3RlbWF0aWMgY29tcG9uZW50IGlzIHRoZSBzYW1lKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICpQKi1WYWx1ZSBJbnRlcnByZXRhdGlvbiAgICAgfCBBIGhpZ2ggV2FsZCAkXGNoaV4yJCBzdGF0aXN0aWMgKHQtc3RhdGlzdGljIGZvciBMaW5lYXIpIHJlc3VsdHMgaW4gYSBsb3cgcC12YWx1ZSwgaW5kaWNhdGluZyB0aGUgcHJlZGljdG9yIGlzIHJlbGV2YW50IHRvIHRoZSBsaW5rIGZ1bmN0aW9uIG9mIHRoZSBtZWFuLiB8DQp8IENvZWZmaWNpZW50IEludGVycHJldGF0aW9uIHwgSWRlbnRpdHkgTGluayAoTGluZWFyKTogQWRkaXRpdmUgZWZmZWN0LiAkXGJldGFfMSQgaXMgdGhlIGNoYW5nZSBpbiAkWSQgcGVyIHVuaXQgJFhfMSQuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IExvZyBMaW5rIChMb2dpc3RpYywgUG9pc3NvbiwgR2FtbWEpOiBNdWx0aXBsaWNhdGl2ZSBlZmZlY3QuICRcZXhwKFxiZXRhXzEpJCBpcyB0aGUgcmF0aW8gb2YgbWVhbnMvcmF0ZXMvb2RkcyBwZXIgdW5pdCAkWF8xJC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KV2hldGhlciB5b3UgYXJlIGRlYWxpbmcgd2l0aCBMb2cgT2RkcyAoTG9naXN0aWMpLCBMb2cgUmF0ZXMgKFBvaXNzb24pLCBvciBMb2cgTWVhbnMgKEdhbW1hKSwgdGhlIHByb2Nlc3Mgb2YgZXhwb25lbnRpYXRpbmcgdGhlIGNvZWZmaWNpZW50IHRvIGdldCBhIG11bHRpcGxpY2F0aXZlIHJhdGlvIGlzIHRoZSBzYW1lLiBZb3UganVzdCBuZWVkIHRvIGFkanVzdCB5b3VyIGxhbmd1YWdlIChvZGRzLCByYXRlLCBvciBtZWFuKSB0byBtYXRjaCB0aGUgZGF0YSB0eXBlIQ0KDQo=