1 Background

Researchers are studying the impact of protein sources and protein levels in the diet on the weight of rats. They feed the rats with diets of beef, cereal and pork and use a low and high protein level for each diet type. The researchers can include 60 rats in the experiment. Prior to the experiment, the rats were divided in 10 homogeneous groups of 6 rats based on characteristics such as initial weight, appetite, etc.

Within each group a rat is randomly assigned to a diet. The rats are fed during a month and the weight gain in grams is recorded for each rat.

2 Experimental design

  • There are three explanatory variables in the experiment: the factor diet type with three levels (beef, cereal and pork), factor protein level with levels (low and high) and a group blocking factor with 10 levels.

  • There are 6 treatments: beef-high, cereal-high, pork-high, beef-low, cereal-low, pork-low protein.

  • The rats are the experimental units (the unit to which a treatment is applied): in this design, there is a randomisation restriction: Within a block, a rat is randomly assigned to a diet.

  • The rats are the observational units (the unit on which the response is measured): The weight is weighted for each rat.

  • The weight gain is the response variable.

  • The experiment is a randomized complete block (RCB) design

Load libraries

library(tidyverse)

3 Data import

diet <- read.table("https://raw.githubusercontent.com/statOmics/PSLS21/data/dietRats.txt",
                   header=TRUE)
head(diet)

4 Tidy data

diet <- diet %>%
  mutate(block = as.factor(block),
         protSource = as.factor(protSource),
         protLevel = as.factor(protLevel)) %>%
  mutate(protLevel = fct_relevel(protLevel, "l"))

5 Data exploration

  • Boxplot of the weight gain against protein source, protein level with coloring according to block
diet %>% 
  ggplot(aes(x=protLevel, y=weightGain)) + 
    scale_fill_brewer(palette="RdGy") +
    theme_bw() +
    geom_boxplot(outlier.shape=NA) + 
    geom_jitter(aes(color=block)) +
    ggtitle("Weight gain dependence on protein source") +
    ylab("Weigth gain (g)") + 
    #stat_summary(fun = mean, geom="point", shape=5, size=3, color="black", fill="black") +
    facet_wrap(.~protSource)

  • Lineplot of the weight gain against protein source, protein level with coloring and grouping according to block
diet %>% 
  ggplot(aes(x=protSource:protLevel, y=weightGain)) + 
    scale_fill_brewer(palette="RdGy") +
    theme_bw() +
    geom_line(aes(group=block, color=block)) +
    geom_point(aes(color=block))+
    ggtitle("Weight gain dependence on protein source") +
    ylab("Weigth gain (g)") 

    #stat_summary(fun = mean, geom="point", shape=5, size=3, color="black", fill="black")
  • An increase in the weight of the rats seems to depend on the protein source received in the diet.

  • The increase in the weight of the rats seems to depend on the level of protein received in the diet

  • There also seems to be an interaction effect between the protein level and the protein source on the gain in weight of the rats. For the beef and the pork diets the effect of high protein levels in the data seems to be much stronger than in the cereal diet.

  • There is also a strong effect of the block. Blocking implies a randomisation restriction, hence, we will have to include the block effect anyway.

6 Multivariate linear regression analysis

6.1 Assumptions

List assumptions:

  1. The observations are independent
  2. Linearity between the response and predictor variable
  3. The residuals of the model must be normally distributed
  4. Homoscedasticity of the data

The first assumption is met if we correct for block in the model because the rats were randomized to the treatment within block. The other three assumptions can be assessed by fitting the linear model and calling the plot() function as follows.

lm1 <- lm(weightGain ~ block + protSource*protLevel, data=diet)
plot(lm1)

All assumptions are met for this dataset.

6.2 Hypothesis testing

We here fit a linear model with a blocking factor for block and main and interaction effects for protein source and protein level.

summary(lm1)
## 
## Call:
## lm(formula = weightGain ~ block + protSource * protLevel, data = diet)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -21.017  -5.183  -1.017   4.092  18.983 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)             88.3500     4.1728  21.173  < 2e-16 ***
## block2                  -9.1667     4.8183  -1.902 0.063520 .  
## block3                  -4.8333     4.8183  -1.003 0.321166    
## block4                 -20.3333     4.8183  -4.220 0.000117 ***
## block5                  -9.3333     4.8183  -1.937 0.059031 .  
## block6                   0.6667     4.8183   0.138 0.890572    
## block7                 -31.3333     4.8183  -6.503 5.58e-08 ***
## block8                 -10.1667     4.8183  -2.110 0.040450 *  
## block9                 -19.8333     4.8183  -4.116 0.000162 ***
## block10                 -0.1667     4.8183  -0.035 0.972559    
## protSourcec              1.1000     3.7322   0.295 0.769556    
## protSourcep              1.3000     3.7322   0.348 0.729226    
## protLevelh              20.0000     3.7322   5.359 2.76e-06 ***
## protSourcec:protLevelh -18.2000     5.2782  -3.448 0.001236 ** 
## protSourcep:protLevelh  -1.7000     5.2782  -0.322 0.748883    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 8.346 on 45 degrees of freedom
## Multiple R-squared:  0.7712, Adjusted R-squared:    0.7 
## F-statistic: 10.83 on 14 and 45 DF,  p-value: 3.726e-10

6.3 Interpretation of the regression parameters

library(ExploreModelMatrix)
ExploreModelMatrix::VisualizeDesign(diet, ~ block + protSource * protLevel)$plotlist
## $`block = 1`

## 
## $`block = 10`

## 
## $`block = 2`

## 
## $`block = 3`

## 
## $`block = 4`

## 
## $`block = 5`

## 
## $`block = 6`

## 
## $`block = 7`

## 
## $`block = 8`

## 
## $`block = 9`

There are 3 levels for protein source, 2 levels for protein level and 10 levels for the blocking variable. We will have one reference level for each respective variable: beef, low, block 1. So we need 2, 1 and 9 dummy variables to introduce the factors protein source, protein level and block in the linear model, respectively.

Hence, we can write down the linear model as follows:

\(y_i=\beta_0+\beta_cx_{i,c} +\beta_px_{i,p}+\beta_hx_{i,h}+\beta_{ch}x_{i,c}x_{i,h}+\beta_{ph}x_{i,p}x_{i,h}+\beta_{b2}x_{i,b2}+\ldots+\beta_{b10}x_{i,b10}+\epsilon_{i}\)

with: \(y_i\) the observed weight gain for rat i, \(x_{i,h}\) a dummy variable which is 1 if rat i receives a high protein diet and is 0 otherwise,
\(x_{i,c}\) a dummy variable which is 1 if rat i receives a cereal diet and is 0 otherwise,
\(x_{i,p}\) a dummy variable which is 1 if rat i receives a pork diet and is 0 otherwise and \(x_{i,bk}\) is a dummy variable which is 1 if rat i belongs to block \(bk\) and is 0 otherwise, with \(k \in 2,\ldots, 10\), and \(\epsilon_i\) an error term which is normally distributed with mean 0 and variance \(\sigma^2\), i.e. \(\epsilon_i \sim N(0,\sigma^2)\).

  • Rats that are assigned to block \(k\) and receive a beef based low protein diet have a covariate pattern \(x_{i,h}=0\), \(x_{i,c}=0\), \(x_{i,p}=0\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). Their mean weight gain is thus equal to \(\mu_{l,b,bk}=\beta_0+\beta_{bk}\)

  • Rats that are assigned to block \(k\) and receive a beef based high protein diet have a covariate pattern \(x_{i,h}=1\), \(x_{i,c}=0\), \(x_{i,p}=0\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). Their mean weight gain is thus equal to \(\mu_{h,b,bk}=\beta_0+\beta_h+\beta_{bk}\)

  • Rats that are assigned to block \(k\) and receive a cereal based low protein diet have a covariate pattern \(x_{i,h}=0\), \(x_{i,c}=1\), \(x_{i,p}=0\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). Their mean weight gain is thus equal to \(\mu_{h,c,bk}=\beta_0+\beta_c+\beta_{bk}\)

  • Rats that are assigned to block \(k\) and receive a cereal based heigh protein diet have a covariate pattern \(x_{i,h}=1\), \(x_{i,c}=1\), \(x_{i,p}=0\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). Their mean weight gain is thus equal to \(\mu_{h,c,bk}=\beta_0+\beta_h+\beta_c+\beta_{ch}+\beta_{bk}\)

  • Rats that are assigned to block \(k\) and receive a pork based low protein diet have a covariate pattern \(x_{i,h}=0\), \(x_{i,c}=0\), \(x_{i,p}=1\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). Their mean weight gain is thus equal to \(\mu_{l,p,bk}=\beta_0+\beta_p+\beta_{bk}\)

  • Rats that are assigned to block \(k\) and receive a pork based heigh protein diet have a covariate pattern \(x_{i,h}=1\), \(x_{i,c}=0\), \(x_{i,p}=1\), \(x_{i,bm}=0\) with \(m\neq k\) and \(x_{i,bk}=1\). There mean weight gain is thus equal to \(\mu_{h,p,bk}=\beta_0+\beta_h+\beta_p+\beta_{ph}+\beta_{bk}\)

We can now relate this to the output of the lm function:

  • The intercept \(\beta_0\) is thus the average weight increase in the low beef diet for rats in block 1.

  • The parameter \(\beta_c\): the average difference in weight gain between cereal-low and beef-low diet is 1.1g.

  • The parameter \(\beta_p\): the average difference in weight gain between pork-low and beef-low diet is 1.3g.

  • The parameter \(\beta_h\): the average difference in weight gain between beef-high and beef-Low diet is 20g.

  • The parameter \(\beta_{ch}\) is the difference in the average weight gain difference due to the high protein level as compared to the low protein level for cereal diets as compared to the weight gain difference that occurs due to the protein level in the reference class (here beef diet). Here this is negative, i.e. -18.2g, thus the weight gain for the cereal protein source increases on average less between high and low protein diets than in beef based diets.

  • The parameter \(\beta_{ph}\) is the difference in the average weight gain difference due to the high protein level as compared to the low protein level for pork diets as compared to the weight gain difference that occurs due to the protein level in the reference class (here beef diet). Here this is negative, i.e. -1.7g, thus the weight gain for the proc protein source increases on average less between high and low protein diets than in beef based diets.

6.4 Testing the overall (combined) effect of diet

Because there are multiple factors with different levels in the model, we can first assess the effect of the diet (protein Level, protein source and the interaction) by using anova. With this test we will assess the null hypothesis that the average weight gain in each treatment is equal: i.e. \(H_0: \mu_{b,l}=\mu_{b,h}=\mu_{c,h}=\mu_{c,l}=\mu_{p,h}=\mu_{p,h}\) versus the alternative hyptohesis \(H_1:\) that at least two treatment means are different.

lm0 <- lm(weightGain ~ block, data=diet)
anova(lm0, lm1)

We can conclude that there is an extremely significant effect of the diet type (protein source and/or protein level and/or protein source-protein level interaction) on the weight gain of rats (p << 0.001).

6.5 Assessing the interaction effect between protein source and protein level

library(car)
Anova(lm1, type="III")

There is a very significant interaction between the protein source and the protein level (p = 2e-03). This indicates that the average weight increase due to the protein level differs according to the protein source. Hence, we cannot assess the effect of the protein source and/or protein level independently because there effects of the protein source vary according to the protein level.

6.6 Assessing specific contrasts

Imagine that we are interested in assessing if there is an effect of

  1. protein source in the low protein diets
    • \(\mu_{c,l}-\mu_{b,l} = \beta_c\)
    • \(\mu_{p,l}-\mu_{b,l} = \beta_p\)
    • \(\mu_{c,l}-\mu_{p,l}= \beta_c-\beta_p\))
  2. protein source in high protein diets
    • \(\mu_{c,h}-\mu_{b,h}=\beta_c+\beta_{ch}\)
    • \(\mu_{p,h}-\mu_{b,h}=\beta_p+\beta_{ph}\)
    • \(\mu_{p,h}-\mu_{c,h}=(\beta_c+\beta_{ch})-(\beta_p+\beta_{ph})\)
  3. protein level for
    • beef diets (\(\mu_{b,h}-\mu_{b,l}=\beta_h\)),
    • cereal diets (\(\mu_{c,h}-\mu_{c,l}=\beta_h+\beta_{ch}\)) and
    • pork diets (\(\mu_{p,h}-\mu_{p,l}=\beta_h+\beta_{ph}\)).
  4. If the effect of the protein level differs between
    • beef and cereal \((\mu_{c,h}-\mu_{c,l}) - (\mu_{b,h}-\mu_{b,l})=\beta_{ch}\)
    • beef and pork \((\mu_{p,h}-\mu_{p,l}) - (\mu_{c,h}-\mu_{c,l})=\beta_{ph}\) and
    • cereal and pork diets \((\mu_{c,h}-\mu_{c,l}) - (\mu_{p,h}-\mu_{p,l})=\beta_{ch}-\beta_{ph}\).

These effects of interest are so-called contrasts, i.e. linear combinations of the parameters.

We can define the contrasts and assess the significance of the contrasts with the code below. The contrasts are given as input in the form of symbolic descriptions to the linfct argument of the glht function.

library(multcomp)
set.seed(75468) # to get reproducible results (small effect if removed)
lm1MultComp <- glht(
    model = lm1,
    linfct = c("protSourcec = 0",
               "protSourcep = 0",
               "protSourcec- protSourcep = 0",
               "protSourcec + protSourcec:protLevelh = 0",
               "protSourcep + protSourcep:protLevelh = 0",
               "(protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) = 0",
               "protLevelh = 0",
               "protLevelh +  protSourcec:protLevelh = 0",
               "protLevelh +  protSourcep:protLevelh = 0",
               "protSourcec:protLevelh = 0",
               "protSourcep:protLevelh = 0",
               "protSourcec:protLevelh - protSourcep:protLevelh = 0")
)
summary(lm1MultComp)
## 
##   Simultaneous Tests for General Linear Hypotheses
## 
## Fit: lm(formula = weightGain ~ block + protSource * protLevel, data = diet)
## 
## Linear Hypotheses:
##                                                                                      Estimate
## protSourcec == 0                                                                        1.100
## protSourcep == 0                                                                        1.300
## protSourcec - protSourcep == 0                                                         -0.200
## protSourcec + protSourcec:protLevelh == 0                                             -17.100
## protSourcep + protSourcep:protLevelh == 0                                              -0.400
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0  -16.700
## protLevelh == 0                                                                        20.000
## protLevelh + protSourcec:protLevelh == 0                                                1.800
## protLevelh + protSourcep:protLevelh == 0                                               18.300
## protSourcec:protLevelh == 0                                                           -18.200
## protSourcep:protLevelh == 0                                                            -1.700
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                  -16.500
##                                                                                      Std. Error
## protSourcec == 0                                                                          3.732
## protSourcep == 0                                                                          3.732
## protSourcec - protSourcep == 0                                                            3.732
## protSourcec + protSourcec:protLevelh == 0                                                 3.732
## protSourcep + protSourcep:protLevelh == 0                                                 3.732
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0      3.732
## protLevelh == 0                                                                           3.732
## protLevelh + protSourcec:protLevelh == 0                                                  3.732
## protLevelh + protSourcep:protLevelh == 0                                                  3.732
## protSourcec:protLevelh == 0                                                               5.278
## protSourcep:protLevelh == 0                                                               5.278
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                      5.278
##                                                                                      t value
## protSourcec == 0                                                                       0.295
## protSourcep == 0                                                                       0.348
## protSourcec - protSourcep == 0                                                        -0.054
## protSourcec + protSourcec:protLevelh == 0                                             -4.582
## protSourcep + protSourcep:protLevelh == 0                                             -0.107
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0  -4.475
## protLevelh == 0                                                                        5.359
## protLevelh + protSourcec:protLevelh == 0                                               0.482
## protLevelh + protSourcep:protLevelh == 0                                               4.903
## protSourcec:protLevelh == 0                                                           -3.448
## protSourcep:protLevelh == 0                                                           -0.322
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                  -3.126
##                                                                                      Pr(>|t|)
## protSourcec == 0                                                                       0.9995
## protSourcep == 0                                                                       0.9989
## protSourcec - protSourcep == 0                                                         1.0000
## protSourcec + protSourcec:protLevelh == 0                                              <0.001
## protSourcep + protSourcep:protLevelh == 0                                              1.0000
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0   <0.001
## protLevelh == 0                                                                        <0.001
## protLevelh + protSourcec:protLevelh == 0                                               0.9949
## protLevelh + protSourcep:protLevelh == 0                                               <0.001
## protSourcec:protLevelh == 0                                                            0.0118
## protSourcep:protLevelh == 0                                                            0.9993
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                   0.0265
##                                                                                         
## protSourcec == 0                                                                        
## protSourcep == 0                                                                        
## protSourcec - protSourcep == 0                                                          
## protSourcec + protSourcec:protLevelh == 0                                            ***
## protSourcep + protSourcep:protLevelh == 0                                               
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0 ***
## protLevelh == 0                                                                      ***
## protLevelh + protSourcec:protLevelh == 0                                                
## protLevelh + protSourcep:protLevelh == 0                                             ***
## protSourcec:protLevelh == 0                                                          *  
## protSourcep:protLevelh == 0                                                             
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)
confint(lm1MultComp)
## 
##   Simultaneous Confidence Intervals
## 
## Fit: lm(formula = weightGain ~ block + protSource * protLevel, data = diet)
## 
## Quantile = 2.8765
## 95% family-wise confidence level
##  
## 
## Linear Hypotheses:
##                                                                                      Estimate
## protSourcec == 0                                                                       1.1000
## protSourcep == 0                                                                       1.3000
## protSourcec - protSourcep == 0                                                        -0.2000
## protSourcec + protSourcec:protLevelh == 0                                            -17.1000
## protSourcep + protSourcep:protLevelh == 0                                             -0.4000
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0 -16.7000
## protLevelh == 0                                                                       20.0000
## protLevelh + protSourcec:protLevelh == 0                                               1.8000
## protLevelh + protSourcep:protLevelh == 0                                              18.3000
## protSourcec:protLevelh == 0                                                          -18.2000
## protSourcep:protLevelh == 0                                                           -1.7000
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                 -16.5000
##                                                                                      lwr     
## protSourcec == 0                                                                      -9.6358
## protSourcep == 0                                                                      -9.4358
## protSourcec - protSourcep == 0                                                       -10.9358
## protSourcec + protSourcec:protLevelh == 0                                            -27.8358
## protSourcep + protSourcep:protLevelh == 0                                            -11.1358
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0 -27.4358
## protLevelh == 0                                                                        9.2642
## protLevelh + protSourcec:protLevelh == 0                                              -8.9358
## protLevelh + protSourcep:protLevelh == 0                                               7.5642
## protSourcec:protLevelh == 0                                                          -33.3827
## protSourcep:protLevelh == 0                                                          -16.8827
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                 -31.6827
##                                                                                      upr     
## protSourcec == 0                                                                      11.8358
## protSourcep == 0                                                                      12.0358
## protSourcec - protSourcep == 0                                                        10.5358
## protSourcec + protSourcec:protLevelh == 0                                             -6.3642
## protSourcep + protSourcep:protLevelh == 0                                             10.3358
## (protSourcec + protSourcec:protLevelh) - (protSourcep + protSourcep:protLevelh) == 0  -5.9642
## protLevelh == 0                                                                       30.7358
## protLevelh + protSourcec:protLevelh == 0                                              12.5358
## protLevelh + protSourcep:protLevelh == 0                                              29.0358
## protSourcec:protLevelh == 0                                                           -3.0173
## protSourcep:protLevelh == 0                                                           13.4827
## protSourcec:protLevelh - protSourcep:protLevelh == 0                                  -1.3173

Note that the p-values and the confidence intervals are automatically corrected for multiple testing.

7 Conclusion

  • There is an extremely significant effect of the type of protein diet on the weight gain of rats (p<<1e-6). The interaction between protein type and protein source is also very significant (p=0.0019).

  • The average weight gain does not vary significantly according to protein source in the diets with low protein levels (all p>0.99).

  • The weight gain in the cereal diet at high protein concentration is on average 17.1g and 16.7g g lower than in the high protein beef and pork diet, respectively (95% CI [6.4, 27.8] and [6, 27.4]) and the differences are extremely significant (p<0.001). The average weight gains after meat based diets at high protein levels, however, do not differ significantly (p>0.99).

  • We also discovered an extremely significant difference in weight gain according to the protein level for beef and pork based diets (p<0.001).
    The weight gain on average increases with 20g and 18.3g in the high protein level as compared to the low protein beef and pork diet, respectively (95%CI [9.3, 30.7] and [7.6, 29]). The protein level effect is not significant for the cereal diet (p>0.99).

  • Finally significant interactions between protein level and protein source were found (p<0.05), i.e. the increase in weight gain due to protein level in cereal based diets was 18.2g and 16.5g lower than that of beef and pork based diets, respectively (95% CI [3g,33.4g] and [1.3g,31.7g]). The average difference in increase in weight gain due to protein level among the meat based diets was not significant (p>0.99).

All reported p-values and confidence intervals were corrected for multiple testing

LS0tCnRpdGxlOiAiRXhlcmNpc2UgOC54OiBCbG9ja2luZyBvbiB0aGUgcmF0IGRpZXQgZGF0YXNldCAtIHNvbHV0aW9uIiAgIAphdXRob3I6ICJMaWV2ZW4gQ2xlbWVudCBhbmQgSmVyb2VuIEdpbGlzIgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIiAgCm91dHB1dDoKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgICAgCiAgICAgIHRoZW1lOiBjb3NtbwogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBwZGZfZG9jdW1lbnQ6CiAgICAgIHRvYzogdHJ1ZQogICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4Ci0tLQoKIyBCYWNrZ3JvdW5kIAoKUmVzZWFyY2hlcnMgYXJlIHN0dWR5aW5nIHRoZSBpbXBhY3Qgb2YgcHJvdGVpbiBzb3VyY2VzIGFuZCBwcm90ZWluIGxldmVscyBpbiAKdGhlIGRpZXQgb24gdGhlIHdlaWdodCBvZiByYXRzLiBUaGV5IGZlZWQgdGhlIHJhdHMgd2l0aCBkaWV0cyBvZiBiZWVmLCBjZXJlYWwgCmFuZCBwb3JrIGFuZCB1c2UgYSBsb3cgYW5kIGhpZ2ggcHJvdGVpbiBsZXZlbCBmb3IgZWFjaCBkaWV0IHR5cGUuIApUaGUgcmVzZWFyY2hlcnMgY2FuIGluY2x1ZGUgNjAgcmF0cyBpbiB0aGUgZXhwZXJpbWVudC4gUHJpb3IgdG8gdGhlIGV4cGVyaW1lbnQsCnRoZSByYXRzIHdlcmUgZGl2aWRlZCBpbiAxMCBob21vZ2VuZW91cyBncm91cHMgb2YgNiByYXRzIGJhc2VkIG9uIApjaGFyYWN0ZXJpc3RpY3Mgc3VjaCBhcyBpbml0aWFsIHdlaWdodCwgYXBwZXRpdGUsIGV0Yy4KCldpdGhpbiBlYWNoIGdyb3VwIGEgcmF0IGlzIHJhbmRvbWx5IGFzc2lnbmVkIHRvIGEgZGlldC4gVGhlIHJhdHMgYXJlIGZlZCBkdXJpbmcgCmEgbW9udGggYW5kIHRoZSB3ZWlnaHQgZ2FpbiBpbiBncmFtcyBpcyByZWNvcmRlZCBmb3IgZWFjaCByYXQuIAoKIyBFeHBlcmltZW50YWwgZGVzaWduIAoKLSBUaGVyZSBhcmUgdGhyZWUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGluIHRoZSBleHBlcmltZW50OiB0aGUgZmFjdG9yIGRpZXQgdHlwZQp3aXRoIHRocmVlIGxldmVscyAoYmVlZiwgY2VyZWFsIGFuZCBwb3JrKSwgZmFjdG9yIHByb3RlaW4gbGV2ZWwgd2l0aCBsZXZlbHMgCihsb3cgYW5kIGhpZ2gpIGFuZCBhIGdyb3VwIGJsb2NraW5nIGZhY3RvciB3aXRoIDEwIGxldmVscy4KCi0gVGhlcmUgYXJlIDYgdHJlYXRtZW50czogYmVlZi1oaWdoLCBjZXJlYWwtaGlnaCwgcG9yay1oaWdoLCBiZWVmLWxvdywgCmNlcmVhbC1sb3csIHBvcmstbG93IHByb3RlaW4uIAoKLSBUaGUgcmF0cyBhcmUgdGhlIGV4cGVyaW1lbnRhbCB1bml0cyAodGhlIHVuaXQgdG8gd2hpY2ggYSB0cmVhdG1lbnQgaXMgYXBwbGllZCk6IGluIHRoaXMgZGVzaWduLCB0aGVyZSBpcyBhIHJhbmRvbWlzYXRpb24gcmVzdHJpY3Rpb246IFdpdGhpbiBhIGJsb2NrLCBhIHJhdCBpcyByYW5kb21seSBhc3NpZ25lZCB0byBhIGRpZXQuCgotIFRoZSByYXRzIGFyZSB0aGUgb2JzZXJ2YXRpb25hbCB1bml0cyAodGhlIHVuaXQgb24gd2hpY2ggdGhlIHJlc3BvbnNlIGlzIG1lYXN1cmVkKTogVGhlIHdlaWdodCBpcyB3ZWlnaHRlZCBmb3IgZWFjaCByYXQuCgotIFRoZSB3ZWlnaHQgZ2FpbiBpcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIAoKLSBUaGUgZXhwZXJpbWVudCBpcyBhIHJhbmRvbWl6ZWQgY29tcGxldGUgYmxvY2sgKFJDQikgZGVzaWduCgpMb2FkIGxpYnJhcmllcwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgRGF0YSBpbXBvcnQKCmBgYHtyfQpkaWV0IDwtIHJlYWQudGFibGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvUFNMUzIxL2RhdGEvZGlldFJhdHMudHh0IiwKICAgICAgICAgICAgICAgICAgIGhlYWRlcj1UUlVFKQpoZWFkKGRpZXQpCmBgYAoKIyBUaWR5IGRhdGEKCmBgYHtyfQpkaWV0IDwtIGRpZXQgJT4lCiAgbXV0YXRlKGJsb2NrID0gYXMuZmFjdG9yKGJsb2NrKSwKICAgICAgICAgcHJvdFNvdXJjZSA9IGFzLmZhY3Rvcihwcm90U291cmNlKSwKICAgICAgICAgcHJvdExldmVsID0gYXMuZmFjdG9yKHByb3RMZXZlbCkpICU+JQogIG11dGF0ZShwcm90TGV2ZWwgPSBmY3RfcmVsZXZlbChwcm90TGV2ZWwsICJsIikpCmBgYAoKIyBEYXRhIGV4cGxvcmF0aW9uCgotIEJveHBsb3Qgb2YgdGhlIHdlaWdodCBnYWluIGFnYWluc3QgcHJvdGVpbiBzb3VyY2UsIHByb3RlaW4gbGV2ZWwgd2l0aCBjb2xvcmluZyBhY2NvcmRpbmcgdG8gYmxvY2sKCmBgYHtyfQpkaWV0ICU+JSAKICBnZ3Bsb3QoYWVzKHg9cHJvdExldmVsLCB5PXdlaWdodEdhaW4pKSArIAogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmRHeSIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsgCiAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9YmxvY2spKSArCiAgICBnZ3RpdGxlKCJXZWlnaHQgZ2FpbiBkZXBlbmRlbmNlIG9uIHByb3RlaW4gc291cmNlIikgKwogICAgeWxhYigiV2VpZ3RoIGdhaW4gKGcpIikgKyAKICAgICNzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZT01LCBzaXplPTMsIGNvbG9yPSJibGFjayIsIGZpbGw9ImJsYWNrIikgKwogICAgZmFjZXRfd3JhcCgufnByb3RTb3VyY2UpCmBgYAoKLSBMaW5lcGxvdCBvZiB0aGUgd2VpZ2h0IGdhaW4gYWdhaW5zdCBwcm90ZWluIHNvdXJjZSwgcHJvdGVpbiBsZXZlbCB3aXRoIGNvbG9yaW5nIGFuZCBncm91cGluZyBhY2NvcmRpbmcgdG8gYmxvY2sKCmBgYHtyfQpkaWV0ICU+JSAKICBnZ3Bsb3QoYWVzKHg9cHJvdFNvdXJjZTpwcm90TGV2ZWwsIHk9d2VpZ2h0R2FpbikpICsgCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJSZEd5IikgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwPWJsb2NrLCBjb2xvcj1ibG9jaykpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPWJsb2NrKSkrCiAgICBnZ3RpdGxlKCJXZWlnaHQgZ2FpbiBkZXBlbmRlbmNlIG9uIHByb3RlaW4gc291cmNlIikgKwogICAgeWxhYigiV2VpZ3RoIGdhaW4gKGcpIikgCiAgICAjc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb209InBvaW50Iiwgc2hhcGU9NSwgc2l6ZT0zLCBjb2xvcj0iYmxhY2siLCBmaWxsPSJibGFjayIpCmBgYAoKLSBBbiBpbmNyZWFzZSBpbiB0aGUgd2VpZ2h0IG9mIHRoZSByYXRzIHNlZW1zIHRvIGRlcGVuZCBvbiB0aGUgcHJvdGVpbiBzb3VyY2UKcmVjZWl2ZWQgaW4gdGhlIGRpZXQuCgotIFRoZSBpbmNyZWFzZSBpbiB0aGUgd2VpZ2h0IG9mIHRoZSByYXRzIHNlZW1zIHRvIGRlcGVuZCBvbiB0aGUgbGV2ZWwgb2YgcHJvdGVpbgpyZWNlaXZlZCBpbiB0aGUgZGlldAoKLSBUaGVyZSBhbHNvIHNlZW1zIHRvIGJlIGFuIGludGVyYWN0aW9uIGVmZmVjdCBiZXR3ZWVuIHRoZSBwcm90ZWluIGxldmVsIGFuZCB0aGUKcHJvdGVpbiBzb3VyY2Ugb24gdGhlIGdhaW4gaW4gd2VpZ2h0IG9mIHRoZSByYXRzLiBGb3IgdGhlIGJlZWYgYW5kIHRoZSBwb3JrIApkaWV0cyB0aGUgZWZmZWN0IG9mIGhpZ2ggcHJvdGVpbiBsZXZlbHMgaW4gdGhlIGRhdGEgc2VlbXMgdG8gYmUgbXVjaCBzdHJvbmdlcgp0aGFuIGluIHRoZSBjZXJlYWwgZGlldC4KCi0gVGhlcmUgaXMgYWxzbyBhIHN0cm9uZyBlZmZlY3Qgb2YgdGhlIGJsb2NrLiBCbG9ja2luZyBpbXBsaWVzIGEgcmFuZG9taXNhdGlvbiAKcmVzdHJpY3Rpb24sIGhlbmNlLCB3ZSB3aWxsIGhhdmUgdG8gaW5jbHVkZSB0aGUgYmxvY2sgZWZmZWN0IGFueXdheS4gCgojIE11bHRpdmFyaWF0ZSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcwoKIyMgQXNzdW1wdGlvbnMgCgpMaXN0IGFzc3VtcHRpb25zOgoKMS4gVGhlIG9ic2VydmF0aW9ucyBhcmUgaW5kZXBlbmRlbnQgCjIuIExpbmVhcml0eSBiZXR3ZWVuIHRoZSByZXNwb25zZSBhbmQgcHJlZGljdG9yIHZhcmlhYmxlCjMuIFRoZSByZXNpZHVhbHMgb2YgdGhlIG1vZGVsIG11c3QgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKNC4gSG9tb3NjZWRhc3RpY2l0eSBvZiB0aGUgZGF0YQoKVGhlIGZpcnN0IGFzc3VtcHRpb24gaXMgbWV0IGlmIHdlIGNvcnJlY3QgZm9yIGJsb2NrIGluIHRoZSBtb2RlbCBiZWNhdXNlIHRoZSByYXRzIHdlcmUgcmFuZG9taXplZCB0byB0aGUgdHJlYXRtZW50IHdpdGhpbiBibG9jay4gVGhlIG90aGVyIHRocmVlIGFzc3VtcHRpb25zIGNhbiBiZSBhc3Nlc3NlZCBieQpmaXR0aW5nIHRoZSBsaW5lYXIgbW9kZWwgYW5kIGNhbGxpbmcgdGhlIGBwbG90KClgIGZ1bmN0aW9uIGFzIGZvbGxvd3MuCgpgYGB7cn0KbG0xIDwtIGxtKHdlaWdodEdhaW4gfiBibG9jayArIHByb3RTb3VyY2UqcHJvdExldmVsLCBkYXRhPWRpZXQpCnBsb3QobG0xKQpgYGAKCkFsbCBhc3N1bXB0aW9ucyBhcmUgbWV0IGZvciB0aGlzIGRhdGFzZXQuCgojIyBIeXBvdGhlc2lzIHRlc3RpbmcKCldlIGhlcmUgZml0IGEgbGluZWFyIG1vZGVsIHdpdGggYSBibG9ja2luZyBmYWN0b3IgZm9yIGBibG9ja2AgYW5kIG1haW4gYW5kIAppbnRlcmFjdGlvbiBlZmZlY3RzIGZvciBwcm90ZWluIHNvdXJjZSBhbmQgcHJvdGVpbiBsZXZlbC4KCmBgYHtyfQpzdW1tYXJ5KGxtMSkKYGBgCgojIyBJbnRlcnByZXRhdGlvbiBvZiB0aGUgcmVncmVzc2lvbiBwYXJhbWV0ZXJzCgpgYGB7cn0KbGlicmFyeShFeHBsb3JlTW9kZWxNYXRyaXgpCkV4cGxvcmVNb2RlbE1hdHJpeDo6VmlzdWFsaXplRGVzaWduKGRpZXQsIH4gYmxvY2sgKyBwcm90U291cmNlICogcHJvdExldmVsKSRwbG90bGlzdApgYGAKClRoZXJlIGFyZSAzIGxldmVscyBmb3IgcHJvdGVpbiBzb3VyY2UsIDIgbGV2ZWxzIGZvciBwcm90ZWluIGxldmVsIGFuZCAxMCBsZXZlbHMKZm9yIHRoZSBibG9ja2luZyB2YXJpYWJsZS4gV2Ugd2lsbCBoYXZlIG9uZSByZWZlcmVuY2UgbGV2ZWwgZm9yIGVhY2ggcmVzcGVjdGl2ZSAKdmFyaWFibGU6IGJlZWYsIGxvdywgYmxvY2sgMS4gU28gd2UgbmVlZCAyLCAxIGFuZCA5IGR1bW15IHZhcmlhYmxlcyB0byBpbnRyb2R1Y2UgCnRoZSBmYWN0b3JzIHByb3RlaW4gc291cmNlLCBwcm90ZWluIGxldmVsIGFuZCBibG9jayBpbiB0aGUgbGluZWFyIG1vZGVsLCAKcmVzcGVjdGl2ZWx5LgoKSGVuY2UsIHdlIGNhbiB3cml0ZSBkb3duIHRoZSBsaW5lYXIgbW9kZWwgYXMgZm9sbG93czoKCiR5X2k9XGJldGFfMCtcYmV0YV9jeF97aSxjfSArXGJldGFfcHhfe2kscH0rXGJldGFfaHhfe2ksaH0rXGJldGFfe2NofXhfe2ksY314X3tpLGh9K1xiZXRhX3twaH14X3tpLHB9eF97aSxofStcYmV0YV97YjJ9eF97aSxiMn0rXGxkb3RzK1xiZXRhX3tiMTB9eF97aSxiMTB9K1xlcHNpbG9uX3tpfSQKCndpdGg6CiR5X2kkIHRoZSBvYnNlcnZlZCB3ZWlnaHQgZ2FpbiBmb3IgcmF0IGksIAokeF97aSxofSQgYSBkdW1teSB2YXJpYWJsZSB3aGljaCBpcyAxIGlmIHJhdCBpIHJlY2VpdmVzIGEgaGlnaCBwcm90ZWluIGRpZXQgCmFuZCBpcyAwIG90aGVyd2lzZSwgIAokeF97aSxjfSQgYSBkdW1teSB2YXJpYWJsZSB3aGljaCBpcyAxIGlmIHJhdCBpIHJlY2VpdmVzIGEgY2VyZWFsIGRpZXQgCmFuZCBpcyAwIG90aGVyd2lzZSwgIAokeF97aSxwfSQgYSBkdW1teSB2YXJpYWJsZSB3aGljaCBpcyAxIGlmIHJhdCBpIHJlY2VpdmVzIGEgcG9yayBkaWV0IAphbmQgaXMgMCBvdGhlcndpc2UgYW5kIAokeF97aSxia30kIGlzIGEgZHVtbXkgdmFyaWFibGUgd2hpY2ggaXMgMSBpZiByYXQgaSBiZWxvbmdzIHRvIGJsb2NrICRiayQgCmFuZCBpcyAwIG90aGVyd2lzZSwgd2l0aCAkayBcaW4gMixcbGRvdHMsIDEwJCwgCmFuZCAkXGVwc2lsb25faSQgYW4gZXJyb3IgdGVybSB3aGljaCBpcyBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIG1lYW4gMCBhbmQgCnZhcmlhbmNlICRcc2lnbWFeMiQsIGkuZS4gJFxlcHNpbG9uX2kgXHNpbSBOKDAsXHNpZ21hXjIpJC4KCi0gUmF0cyB0aGF0IGFyZSBhc3NpZ25lZCB0byBibG9jayAkayQgYW5kIHJlY2VpdmUgYSBiZWVmIGJhc2VkIGxvdyBwcm90ZWluIGRpZXQgaGF2ZQoJYSBjb3ZhcmlhdGUgcGF0dGVybiAkeF97aSxofT0wJCwgJHhfe2ksY309MCQsICR4X3tpLHB9PTAkLCAkeF97aSxibX09MCQgd2l0aCAkbVxuZXEgayQgYW5kICR4X3tpLGJrfT0xJC4gCglUaGVpciBtZWFuIHdlaWdodCBnYWluIGlzIHRodXMgZXF1YWwgdG8gJFxtdV97bCxiLGJrfT1cYmV0YV8wK1xiZXRhX3tia30kCgotIFJhdHMgdGhhdCBhcmUgYXNzaWduZWQgdG8gYmxvY2sgJGskIGFuZCByZWNlaXZlIGEgYmVlZiBiYXNlZCBoaWdoIHByb3RlaW4gZGlldCBoYXZlCglhIGNvdmFyaWF0ZSBwYXR0ZXJuICR4X3tpLGh9PTEkLCAkeF97aSxjfT0wJCwgJHhfe2kscH09MCQsICR4X3tpLGJtfT0wJCB3aXRoICRtXG5lcSBrJCBhbmQgJHhfe2ksYmt9PTEkLiAKCVRoZWlyIG1lYW4gd2VpZ2h0IGdhaW4gaXMgdGh1cyBlcXVhbCB0byAkXG11X3toLGIsYmt9PVxiZXRhXzArXGJldGFfaCtcYmV0YV97Ymt9JAoKLSBSYXRzIHRoYXQgYXJlIGFzc2lnbmVkIHRvIGJsb2NrICRrJCBhbmQgcmVjZWl2ZSBhIGNlcmVhbCBiYXNlZCBsb3cgcHJvdGVpbiBkaWV0IGhhdmUKCWEgY292YXJpYXRlIHBhdHRlcm4gJHhfe2ksaH09MCQsICR4X3tpLGN9PTEkLCAkeF97aSxwfT0wJCwgJHhfe2ksYm19PTAkIHdpdGggJG1cbmVxIGskIGFuZCAkeF97aSxia309MSQuIAoJVGhlaXIgbWVhbiB3ZWlnaHQgZ2FpbiBpcyB0aHVzIGVxdWFsIHRvICRcbXVfe2gsYyxia309XGJldGFfMCtcYmV0YV9jK1xiZXRhX3tia30kCgotIFJhdHMgdGhhdCBhcmUgYXNzaWduZWQgdG8gYmxvY2sgJGskIGFuZCByZWNlaXZlIGEgY2VyZWFsIGJhc2VkIGhlaWdoIHByb3RlaW4gZGlldCBoYXZlCglhIGNvdmFyaWF0ZSBwYXR0ZXJuICR4X3tpLGh9PTEkLCAkeF97aSxjfT0xJCwgJHhfe2kscH09MCQsICR4X3tpLGJtfT0wJCB3aXRoICRtXG5lcSBrJCBhbmQgJHhfe2ksYmt9PTEkLiAKCVRoZWlyIG1lYW4gd2VpZ2h0IGdhaW4gaXMgdGh1cyBlcXVhbCB0byAkXG11X3toLGMsYmt9PVxiZXRhXzArXGJldGFfaCtcYmV0YV9jK1xiZXRhX3tjaH0rXGJldGFfe2JrfSQKCi0gUmF0cyB0aGF0IGFyZSBhc3NpZ25lZCB0byBibG9jayAkayQgYW5kIHJlY2VpdmUgYSBwb3JrIGJhc2VkIGxvdyBwcm90ZWluIGRpZXQgaGF2ZQoJYSBjb3ZhcmlhdGUgcGF0dGVybiAkeF97aSxofT0wJCwgICR4X3tpLGN9PTAkLCAkeF97aSxwfT0xJCwgJHhfe2ksYm19PTAkIHdpdGggJG1cbmVxIGskIGFuZCAkeF97aSxia309MSQuIAoJVGhlaXIgbWVhbiB3ZWlnaHQgZ2FpbiBpcyB0aHVzIGVxdWFsIHRvICRcbXVfe2wscCxia309XGJldGFfMCtcYmV0YV9wK1xiZXRhX3tia30kCgotIFJhdHMgdGhhdCBhcmUgYXNzaWduZWQgdG8gYmxvY2sgJGskIGFuZCByZWNlaXZlIGEgcG9yayBiYXNlZCBoZWlnaCBwcm90ZWluIGRpZXQgaGF2ZQoJYSBjb3ZhcmlhdGUgcGF0dGVybiAkeF97aSxofT0xJCwgJHhfe2ksY309MCQsICR4X3tpLHB9PTEkLCAkeF97aSxibX09MCQgd2l0aCAkbVxuZXEgayQgYW5kICR4X3tpLGJrfT0xJC4gCglUaGVyZSBtZWFuIHdlaWdodCBnYWluIGlzIHRodXMgZXF1YWwgdG8gJFxtdV97aCxwLGJrfT1cYmV0YV8wK1xiZXRhX2grXGJldGFfcCtcYmV0YV97cGh9K1xiZXRhX3tia30kCgkKV2UgY2FuIG5vdyByZWxhdGUgdGhpcyB0byB0aGUgb3V0cHV0IG9mIHRoZSBgbG1gIGZ1bmN0aW9uOgoKLSBUaGUgaW50ZXJjZXB0ICRcYmV0YV8wJCBpcyB0aHVzIHRoZSBhdmVyYWdlIHdlaWdodCBpbmNyZWFzZSBpbiB0aGUgbG93IGJlZWYgCmRpZXQgZm9yIHJhdHMgaW4gYmxvY2sgMS4KCi0gVGhlIHBhcmFtZXRlciAkXGJldGFfYyQ6IHRoZSBhdmVyYWdlIGRpZmZlcmVuY2UgaW4gd2VpZ2h0IGdhaW4gYmV0d2VlbiBjZXJlYWwtbG93IGFuZCAKYmVlZi1sb3cgZGlldCBpcyBgciBsbTEkY29lZlsicHJvdFNvdXJjZWMiXWBnLgoKLSBUaGUgcGFyYW1ldGVyICRcYmV0YV9wJDogdGhlIGF2ZXJhZ2UgZGlmZmVyZW5jZSBpbiB3ZWlnaHQgZ2FpbiBiZXR3ZWVuIHBvcmstbG93IGFuZCAKYmVlZi1sb3cgZGlldCBpcyBgciBsbTEkY29lZlsicHJvdFNvdXJjZXAiXWBnLgoKLSBUaGUgcGFyYW1ldGVyICRcYmV0YV9oJDogdGhlIGF2ZXJhZ2UgZGlmZmVyZW5jZSBpbiB3ZWlnaHQgZ2FpbiBiZXR3ZWVuIGJlZWYtaGlnaCBhbmQgCmJlZWYtTG93IGRpZXQgaXMgYHIgbG0xJGNvZWZbInByb3RMZXZlbGgiXWBnLgoKLSBUaGUgcGFyYW1ldGVyICRcYmV0YV97Y2h9JCBpcyB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgYXZlcmFnZSB3ZWlnaHQgZ2FpbiBkaWZmZXJlbmNlIApkdWUgdG8gdGhlIGhpZ2ggcHJvdGVpbiBsZXZlbCBhcyBjb21wYXJlZCB0byB0aGUgbG93IHByb3RlaW4gbGV2ZWwgZm9yIGNlcmVhbCBkaWV0cyAKYXMgY29tcGFyZWQgdG8gdGhlIHdlaWdodCBnYWluIGRpZmZlcmVuY2UgdGhhdCBvY2N1cnMgZHVlIHRvIHRoZSBwcm90ZWluIGxldmVsCmluIHRoZSByZWZlcmVuY2UgY2xhc3MgKGhlcmUgYmVlZiBkaWV0KS4gSGVyZSB0aGlzIGlzIG5lZ2F0aXZlLCAKaS5lLiBgciBsbTEkY29lZlsicHJvdFNvdXJjZWM6cHJvdExldmVsaCJdYGcsIHRodXMgdGhlIHdlaWdodCBnYWluIApmb3IgdGhlIGNlcmVhbCBwcm90ZWluIHNvdXJjZSBpbmNyZWFzZXMgb24gYXZlcmFnZSBsZXNzIGJldHdlZW4gaGlnaCBhbmQgbG93IHByb3RlaW4gZGlldHMgCnRoYW4gaW4gYmVlZiBiYXNlZCBkaWV0cy4KCi0gVGhlIHBhcmFtZXRlciAkXGJldGFfe3BofSQgaXMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2Ugd2VpZ2h0IGdhaW4gZGlmZmVyZW5jZSAKZHVlIHRvIHRoZSBoaWdoIHByb3RlaW4gbGV2ZWwgYXMgY29tcGFyZWQgdG8gdGhlIGxvdyBwcm90ZWluIGxldmVsIGZvciBwb3JrIGRpZXRzIAphcyBjb21wYXJlZCB0byB0aGUgd2VpZ2h0IGdhaW4gZGlmZmVyZW5jZSB0aGF0IG9jY3VycyBkdWUgdG8gdGhlIHByb3RlaW4gbGV2ZWwKaW4gdGhlIHJlZmVyZW5jZSBjbGFzcyAoaGVyZSBiZWVmIGRpZXQpLiBIZXJlIHRoaXMgaXMgbmVnYXRpdmUsIAppLmUuIGByIGxtMSRjb2VmWyJwcm90U291cmNlcDpwcm90TGV2ZWxoIl1gZywgdGh1cyB0aGUgd2VpZ2h0IGdhaW4gCmZvciB0aGUgcHJvYyBwcm90ZWluIHNvdXJjZSBpbmNyZWFzZXMgb24gYXZlcmFnZSBsZXNzIGJldHdlZW4gaGlnaCBhbmQgbG93IHByb3RlaW4gZGlldHMgCnRoYW4gaW4gYmVlZiBiYXNlZCBkaWV0cy4KCiMjIFRlc3RpbmcgdGhlIG92ZXJhbGwgKGNvbWJpbmVkKSBlZmZlY3Qgb2YgZGlldAoKQmVjYXVzZSB0aGVyZSBhcmUgbXVsdGlwbGUgZmFjdG9ycyB3aXRoIGRpZmZlcmVudCBsZXZlbHMgaW4gdGhlIG1vZGVsLCAKd2UgY2FuIGZpcnN0IGFzc2VzcyB0aGUgZWZmZWN0IG9mIHRoZSBkaWV0IChwcm90ZWluIExldmVsLCBwcm90ZWluIHNvdXJjZSAKYW5kIHRoZSBpbnRlcmFjdGlvbikgYnkgdXNpbmcgYW5vdmEuIFdpdGggdGhpcyB0ZXN0IHdlIHdpbGwgYXNzZXNzIHRoZSBudWxsCmh5cG90aGVzaXMgdGhhdCB0aGUgYXZlcmFnZSB3ZWlnaHQgZ2FpbiBpbiBlYWNoIHRyZWF0bWVudCBpcyBlcXVhbDogaS5lLiAkSF8wOiBcbXVfe2IsbH09XG11X3tiLGh9PVxtdV97YyxofT1cbXVfe2MsbH09XG11X3twLGh9PVxtdV97cCxofSQgdmVyc3VzIAp0aGUgYWx0ZXJuYXRpdmUgaHlwdG9oZXNpcyAkSF8xOiQgdGhhdCBhdCBsZWFzdCB0d28gdHJlYXRtZW50IG1lYW5zIGFyZSAKZGlmZmVyZW50LiAKCmBgYHtyfQpsbTAgPC0gbG0od2VpZ2h0R2FpbiB+IGJsb2NrLCBkYXRhPWRpZXQpCmFub3ZhKGxtMCwgbG0xKQpgYGAKCldlIGNhbiBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIGFuIGV4dHJlbWVseSBzaWduaWZpY2FudCBlZmZlY3Qgb2YgdGhlIGRpZXQgdHlwZSAKKHByb3RlaW4gc291cmNlIGFuZC9vciBwcm90ZWluIGxldmVsIGFuZC9vciBwcm90ZWluIHNvdXJjZS1wcm90ZWluIGxldmVsIAppbnRlcmFjdGlvbikgb24gdGhlIHdlaWdodCBnYWluIG9mIHJhdHMgKHAgPDwgMC4wMDEpLgoKIyMgQXNzZXNzaW5nIHRoZSBpbnRlcmFjdGlvbiBlZmZlY3QgYmV0d2VlbiBwcm90ZWluIHNvdXJjZSBhbmQgcHJvdGVpbiBsZXZlbAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY2FyKQpBbm92YShsbTEsIHR5cGU9IklJSSIpCmBgYAoKVGhlcmUgaXMgYSB2ZXJ5IHNpZ25pZmljYW50IGludGVyYWN0aW9uIGJldHdlZW4gdGhlIHByb3RlaW4gc291cmNlIGFuZCAKdGhlIHByb3RlaW4gbGV2ZWwgKHAgPSBgciBmb3JtYXQoQW5vdmEobG0xLCB0eXBlPTMpJCJQcig+RikiLGRpZ2l0cz0xKVs1XWApLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBhdmVyYWdlIHdlaWdodCBpbmNyZWFzZSBkdWUgdG8gCnRoZSBwcm90ZWluIGxldmVsIGRpZmZlcnMgYWNjb3JkaW5nIHRvIHRoZSBwcm90ZWluIHNvdXJjZS4gCkhlbmNlLCB3ZSBjYW5ub3QgYXNzZXNzIHRoZSBlZmZlY3Qgb2YgdGhlIHByb3RlaW4gc291cmNlIGFuZC9vciBwcm90ZWluIGxldmVsIAppbmRlcGVuZGVudGx5IGJlY2F1c2UgdGhlcmUgZWZmZWN0cyBvZiB0aGUgcHJvdGVpbiBzb3VyY2UgdmFyeSBhY2NvcmRpbmcgdG8gCnRoZSBwcm90ZWluIGxldmVsLgoKIyMgQXNzZXNzaW5nIHNwZWNpZmljIGNvbnRyYXN0cwoKSW1hZ2luZSB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGFzc2Vzc2luZyBpZiB0aGVyZSBpcyBhbiBlZmZlY3Qgb2YKIAoxLiBwcm90ZWluIHNvdXJjZSBpbiB0aGUgbG93IHByb3RlaW4gZGlldHMgCgktICRcbXVfe2MsbH0tXG11X3tiLGx9ID0gXGJldGFfYyQKCS0gJFxtdV97cCxsfS1cbXVfe2IsbH0gPSBcYmV0YV9wJAoJLSAkXG11X3tjLGx9LVxtdV97cCxsfT0gXGJldGFfYy1cYmV0YV9wJCkKCgoyLiBwcm90ZWluIHNvdXJjZSBpbiBoaWdoIHByb3RlaW4gZGlldHMgCgktICRcbXVfe2MsaH0tXG11X3tiLGh9PVxiZXRhX2MrXGJldGFfe2NofSQKCS0gJFxtdV97cCxofS1cbXVfe2IsaH09XGJldGFfcCtcYmV0YV97cGh9JAoJLSAkXG11X3twLGh9LVxtdV97YyxofT0oXGJldGFfYytcYmV0YV97Y2h9KS0oXGJldGFfcCtcYmV0YV97cGh9KSQKCgozLiBwcm90ZWluIGxldmVsIGZvcgoJLSBiZWVmIGRpZXRzICgkXG11X3tiLGh9LVxtdV97YixsfT1cYmV0YV9oJCksIAoJLSBjZXJlYWwgZGlldHMgICgkXG11X3tjLGh9LVxtdV97YyxsfT1cYmV0YV9oK1xiZXRhX3tjaH0kKSBhbmQgCgktIHBvcmsgZGlldHMgICgkXG11X3twLGh9LVxtdV97cCxsfT1cYmV0YV9oK1xiZXRhX3twaH0kKS4KCgo0LiBJZiB0aGUgZWZmZWN0IG9mIHRoZSBwcm90ZWluIGxldmVsIGRpZmZlcnMgYmV0d2VlbgoJLSBiZWVmIGFuZCBjZXJlYWwgICQoXG11X3tjLGh9LVxtdV97YyxsfSkgLSAoXG11X3tiLGh9LVxtdV97YixsfSk9XGJldGFfe2NofSQKCS0gYmVlZiBhbmQgcG9yayAkKFxtdV97cCxofS1cbXVfe3AsbH0pIC0gKFxtdV97YyxofS1cbXVfe2MsbH0pPVxiZXRhX3twaH0kIGFuZAoJLSBjZXJlYWwgYW5kIHBvcmsgZGlldHMgJChcbXVfe2MsaH0tXG11X3tjLGx9KSAtIChcbXVfe3AsaH0tXG11X3twLGx9KT1cYmV0YV97Y2h9LVxiZXRhX3twaH0kLgoKVGhlc2UgZWZmZWN0cyBvZiBpbnRlcmVzdCBhcmUgc28tY2FsbGVkIAoqKmNvbnRyYXN0cywgaS5lLiBsaW5lYXIgY29tYmluYXRpb25zIG9mIHRoZSBwYXJhbWV0ZXJzKiouCgpXZSBjYW4gZGVmaW5lIHRoZSBjb250cmFzdHMgYW5kIGFzc2VzcyB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBjb250cmFzdHMgd2l0aCAKdGhlIGNvZGUgYmVsb3cuIFRoZSBjb250cmFzdHMgYXJlIGdpdmVuIGFzIGlucHV0IGluIHRoZSBmb3JtIG9mIHN5bWJvbGljCmRlc2NyaXB0aW9ucyB0byB0aGUgYGxpbmZjdGAgYXJndW1lbnQgb2YgdGhlIGBnbGh0YCBmdW5jdGlvbi4KCmBgYHtyLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobXVsdGNvbXApCnNldC5zZWVkKDc1NDY4KSAjIHRvIGdldCByZXByb2R1Y2libGUgcmVzdWx0cyAoc21hbGwgZWZmZWN0IGlmIHJlbW92ZWQpCmxtMU11bHRDb21wIDwtIGdsaHQoCiAgICBtb2RlbCA9IGxtMSwKICAgIGxpbmZjdCA9IGMoInByb3RTb3VyY2VjID0gMCIsCiAgICAgICAgICAgICAgICJwcm90U291cmNlcCA9IDAiLAogICAgICAgICAgICAgICAicHJvdFNvdXJjZWMtIHByb3RTb3VyY2VwID0gMCIsCiAgICAgICAgICAgICAgICJwcm90U291cmNlYyArIHByb3RTb3VyY2VjOnByb3RMZXZlbGggPSAwIiwKICAgICAgICAgICAgICAgInByb3RTb3VyY2VwICsgcHJvdFNvdXJjZXA6cHJvdExldmVsaCA9IDAiLAogICAgICAgICAgICAgICAiKHByb3RTb3VyY2VjICsgcHJvdFNvdXJjZWM6cHJvdExldmVsaCkgLSAocHJvdFNvdXJjZXAgKyBwcm90U291cmNlcDpwcm90TGV2ZWxoKSA9IDAiLAogICAgICAgICAgICAgICAicHJvdExldmVsaCA9IDAiLAogICAgICAgICAgICAgICAicHJvdExldmVsaCArICBwcm90U291cmNlYzpwcm90TGV2ZWxoID0gMCIsCiAgICAgICAgICAgICAgICJwcm90TGV2ZWxoICsgIHByb3RTb3VyY2VwOnByb3RMZXZlbGggPSAwIiwKICAgICAgICAgICAgICAgInByb3RTb3VyY2VjOnByb3RMZXZlbGggPSAwIiwKICAgICAgICAgICAgICAgInByb3RTb3VyY2VwOnByb3RMZXZlbGggPSAwIiwKICAgICAgICAgICAgICAgInByb3RTb3VyY2VjOnByb3RMZXZlbGggLSBwcm90U291cmNlcDpwcm90TGV2ZWxoID0gMCIpCikKYGBgCgpgYGB7cn0Kc3VtbWFyeShsbTFNdWx0Q29tcCkKYGBgCgpgYGB7cn0KY29uZmludChsbTFNdWx0Q29tcCkKYGBgCgpOb3RlIHRoYXQgdGhlIHAtdmFsdWVzIGFuZCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIGF1dG9tYXRpY2FsbHkgY29ycmVjdGVkIApmb3IgbXVsdGlwbGUgdGVzdGluZy4KCiMgQ29uY2x1c2lvbgoKLSBUaGVyZSBpcyBhbiBleHRyZW1lbHkgc2lnbmlmaWNhbnQgZWZmZWN0IG9mIHRoZSB0eXBlIG9mIHByb3RlaW4gZGlldCBvbiB0aGUgCndlaWdodCBnYWluIG9mIHJhdHMgIChwPDwxZS02KS4gVGhlIGludGVyYWN0aW9uIGJldHdlZW4gcHJvdGVpbiB0eXBlIGFuZCBwcm90ZWluIApzb3VyY2UgaXMgYWxzbyB2ZXJ5IHNpZ25pZmljYW50IChwPWByIHJvdW5kKEFub3ZhKGxtMSx0eXBlPTMpJFByWzVdLDQpYCkuIAoKLSBUaGUgYXZlcmFnZSB3ZWlnaHQgZ2FpbiBkb2VzIG5vdCB2YXJ5IHNpZ25pZmljYW50bHkgYWNjb3JkaW5nIHRvIHByb3RlaW4gCnNvdXJjZSBpbiB0aGUgZGlldHMgd2l0aCBsb3cgcHJvdGVpbiBsZXZlbHMgKGFsbCBwPjAuOTkpLiAKCi0gVGhlIHdlaWdodCBnYWluIGluIHRoZSBjZXJlYWwgZGlldCBhdCBoaWdoIHByb3RlaW4gY29uY2VudHJhdGlvbiBpcyBvbiAKYXZlcmFnZSBgciBjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyJwcm90U291cmNlYyArIHByb3RTb3VyY2VjOnByb3RMZXZlbGgiLDFdICU+JSBhYnMgJT4lIHJvdW5kKC4sMSlgZyBhbmQgYHIgY29uZmludChsbTFNdWx0Q29tcCkkY29uZmludFsiKHByb3RTb3VyY2VjICsgcHJvdFNvdXJjZWM6cHJvdExldmVsaCkgLSAocHJvdFNvdXJjZXAgKyBwcm90U291cmNlcDpwcm90TGV2ZWxoKSIsMV0gJT4lIGFicyAlPiUgcm91bmQoLiwxKWBnIGcgbG93ZXIgdGhhbiBpbiB0aGUgaGlnaCBwcm90ZWluIGJlZWYgYW5kIHBvcmsgZGlldCwgCnJlc3BlY3RpdmVseSAoOTUlIENJIFtgciBjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyJwcm90U291cmNlYyArIHByb3RTb3VyY2VjOnByb3RMZXZlbGgiLC0xXSAlPiUgYWJzICU+JSByb3VuZCguLDEpICU+JSBzb3J0YF0gYW5kIFtgciBjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyIocHJvdFNvdXJjZWMgKyBwcm90U291cmNlYzpwcm90TGV2ZWxoKSAtIChwcm90U291cmNlcCArIHByb3RTb3VyY2VwOnByb3RMZXZlbGgpIiwtMV0gJT4lIGFicyAlPiUgcm91bmQoLiwxKSAlPiUgc29ydGBdKSBhbmQgdGhlIGRpZmZlcmVuY2VzIGFyZSAKZXh0cmVtZWx5IHNpZ25pZmljYW50IChwPDAuMDAxKS4gVGhlIGF2ZXJhZ2Ugd2VpZ2h0IGdhaW5zIGFmdGVyIG1lYXQgYmFzZWQgCmRpZXRzIGF0IGhpZ2ggcHJvdGVpbiBsZXZlbHMsIGhvd2V2ZXIsIGRvIG5vdCBkaWZmZXIgc2lnbmlmaWNhbnRseSAKKHA+MC45OSkuCgotIFdlIGFsc28gZGlzY292ZXJlZCBhbiBleHRyZW1lbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiB3ZWlnaHQgZ2FpbiAKYWNjb3JkaW5nIHRvIHRoZSBwcm90ZWluIGxldmVsIGZvciBiZWVmIGFuZCBwb3JrIGJhc2VkIGRpZXRzIChwPDAuMDAxKS4gIApUaGUgd2VpZ2h0IGdhaW4gb24gYXZlcmFnZSBpbmNyZWFzZXMgd2l0aCBgciByb3VuZChjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyJwcm90TGV2ZWxoIiwxXSwxKWBnIGFuZCBgciByb3VuZChjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyJwcm90TGV2ZWxoICsgcHJvdFNvdXJjZXA6cHJvdExldmVsaCIsMV0sMSlgZyBpbiB0aGUgaGlnaCBwcm90ZWluIApsZXZlbCBhcyBjb21wYXJlZCB0byB0aGUgbG93IHByb3RlaW4gYmVlZiBhbmQgcG9yayBkaWV0LCByZXNwZWN0aXZlbHkgCig5NSVDSSBbYHIgcm91bmQoY29uZmludChsbTFNdWx0Q29tcCkkY29uZmludFsicHJvdExldmVsaCIsLTFdLDEpYF0gYW5kIFtgciByb3VuZChjb25maW50KGxtMU11bHRDb21wKSRjb25maW50WyJwcm90TGV2ZWxoICsgcHJvdFNvdXJjZXA6cHJvdExldmVsaCIsLTFdLDEpYF0pLiBUaGUgcHJvdGVpbiBsZXZlbCBlZmZlY3QgaXMgbm90IHNpZ25pZmljYW50CmZvciB0aGUgY2VyZWFsIGRpZXQgKHA+MC45OSkuIAoKLSBGaW5hbGx5IHNpZ25pZmljYW50IGludGVyYWN0aW9ucyBiZXR3ZWVuIHByb3RlaW4gbGV2ZWwgYW5kIHByb3RlaW4gc291cmNlIHdlcmUKZm91bmQgKHA8MC4wNSksIGkuZS4gdGhlIGluY3JlYXNlIGluIHdlaWdodCBnYWluIGR1ZSB0byBwcm90ZWluIGxldmVsIGluIGNlcmVhbApiYXNlZCBkaWV0cyB3YXMgMTguMmcgYW5kIDE2LjVnIGxvd2VyIHRoYW4gdGhhdCBvZiBiZWVmIGFuZCBwb3JrIGJhc2VkIGRpZXRzLApyZXNwZWN0aXZlbHkgKDk1JSBDSSBbM2csMzMuNGddIGFuZCBbMS4zZywzMS43Z10pLiBUaGUgYXZlcmFnZSBkaWZmZXJlbmNlIGluIAppbmNyZWFzZSBpbiB3ZWlnaHQgZ2FpbiBkdWUgdG8gcHJvdGVpbiBsZXZlbCBhbW9uZyB0aGUgbWVhdCBiYXNlZCBkaWV0cyB3YXMgbm90IApzaWduaWZpY2FudCAocD4wLjk5KS4KCkFsbCByZXBvcnRlZCBwLXZhbHVlcyBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgd2VyZSBjb3JyZWN0ZWQgZm9yIG11bHRpcGxlIAp0ZXN0aW5nCg==