Objective & Introduction:

It is observed that the critic score earned by a movie is based on various factors such as Actors, Directors, Plot of movie, revenue, etc. The report deals with all these kinds of factors and highlighting the important ones and developing a regression model based on the imdb_d to predict the average critic score earned by a movie.

Dataset and its source:

The dataset in this project is extracted from Kaggle.com open datasets: IMDB New Dataset. The dataset was acquired using BeautifulSoup4 from the search page of Imdb.

Source:- Kaggle.com. 2021. IMDB New Dataset. [online] Available at: https://www.kaggle.com/wrandrall/imdb-new-dataset?select=imdb_db [Accessed 4 June 2021].

The dataset has 189900 rows and 14 columns. Each observation represents a movie. The movies are ranked by the number of votes from high to low. The movie listed are taken from 1900-2020.

Descriptive features of dataset:

There are 6 numerical and 8 categorical variables present in the dataset. They are listed below: The target Feature is:

Score: The mean score allotted to the movie between 1-10 by the journalists/critics.

Movie Name: the name of the movie/Series.

Movie date: The year when the movie released.

Series Name: the name of the series season (if any)

Series date: the year when the series released.

Movie type: genre of movie (action, drama, sci-fi..)

Number of votes: number of people who voted for the meta score.

Movie revenue in million $: Box-office revenue made by the movie in million $

Meta score: the mean score allotted to the movie/series from 1-100 by viewers.

Time Duration: the duration of the movie in minutes.

Director: list of directors that directed the movie/series.

Actors: list of main actors that played in the movie/series.

Restrictions: Age restriction and warning (all public, all public with warning, 12 ,12 with warnings, 16, ….)

Description: A short summary of the movie.

Report

Importing Libraries.

To start with our report, we have to import necessary R packages:

library(readr)
library(dplyr)
library(tidyr)
library(plotly)
library(car)

Importing the dataset.

After importing the neccessary packages, we have to import the imdb databaset/ We imported the dataset using read_csv function and named the dataset as imdb_db and have a glimpse at our datatset.

imdb_db <- read_csv("imdb_db.csv")
imdb_db

Data Pre-processing.

Our dataset has alot of observations. So, we will take top 1500 observations which represents top score of 1500 movies and name them as imdb.

imdb <- imdb_db %>% slice(1:1500)
imdb

Now, let’s check for any misng values in our dataset.

colSums(is.na(imdb))
         Movie Name          Movie Date          Serie Name          Serie Date 
                  0                   0                1500                1500 
         Movie Type     Number of Votes  Movie Revenue (M$)               Score 
                  0                   0                  60                   0 
          Metascore Time Duration (min)            Director              Actors 
                 60                   0                   0                   0 
        Restriction         Description 
                  0                   0 

As we can see that our dataset has missing values in columns Serie Name, Serie data, Meta score and Movie Revenue(M$). So, before moving further we have remove them. We will remove them by using drop.na() and select functions.

imdb <- drop_na(imdb,`Movie Revenue (M$)`,Metascore)
imdb <- select(imdb,-c(3,4))

We have removed the missing values and just checking if we still have any missing values present in the imdb dataset.

colSums(is.na(imdb))
         Movie Name          Movie Date          Movie Type     Number of Votes 
                  0                   0                   0                   0 
 Movie Revenue (M$)               Score           Metascore Time Duration (min) 
                  0                   0                   0                   0 
           Director              Actors         Restriction         Description 
                  0                   0                   0                   0 

As we can see that there are no missing values present in our imdb dataset. Now , we will check if our datase contains any special values such as NAN,infinite values etc.

is.special <- function(x){ if (is.numeric(x)) (is.infinite(x) | is.nan(x))}
sapply(imdb, function(x) sum( is.special(x)))
         Movie Name          Movie Date          Movie Type     Number of Votes 
                  0                   0                   0                   0 
 Movie Revenue (M$)               Score           Metascore Time Duration (min) 
                  0                   0                   0                   0 
           Director              Actors         Restriction         Description 
                  0                   0                   0                   0 

Descriptive statistics of dataset.

As we can see that our dataset doesn’t contain any special values. Now, we will move forward to see some descriptive stats of our dataset.

glimpse(imdb)
Rows: 1,440
Columns: 12
$ `Movie Name`          <chr> "Les évadés", "The Dark Knight: Le chevalier noir", "Inception~
$ `Movie Date`          <dbl> 1994, 2008, 2010, 1999, 1994, 1994, 1999, 2001, 2003, 1972, 20~
$ `Movie Type`          <chr> "['Drama']", "['Action', 'Crime', 'Drama']", "['Action', 'Adve~
$ `Number of Votes`     <dbl> 2294987, 2259829, 2021865, 1819635, 1792272, 1768611, 1643323,~
$ `Movie Revenue (M$)`  <dbl> 28341469, 534858444, 292576195, 37030102, 107928762, 330252182~
$ Score                 <dbl> 9.3, 9.0, 8.8, 8.8, 8.9, 8.8, 8.7, 8.8, 8.9, 9.2, 8.4, 8.6, 8.~
$ Metascore             <dbl> 80, 84, 74, 66, 94, 82, 73, 92, 94, 100, 78, 74, 87, 65, 81, 6~
$ `Time Duration (min)` <dbl> 142, 152, 148, 139, 154, 142, 136, 178, 201, 175, 164, 169, 17~
$ Director              <chr> "['Frank Darabont']", "['Christopher Nolan']", "['Christopher ~
$ Actors                <chr> "['Tim Robbins', 'Morgan Freeman', 'Bob Gunton', 'William Sadl~
$ Restriction           <chr> "Tous publics", "Tous publics", "Tous publics", "16", "12", "T~
$ Description           <chr> "Two imprisoned men bond over a number of years, finding solac~
class(imdb)
[1] "tbl_df"     "tbl"        "data.frame"
 imdb %>% summary()   
  Movie Name          Movie Date    Movie Type        Number of Votes   Movie Revenue (M$) 
 Length:1440        Min.   :1972   Length:1440        Min.   : 933448   Min.   :  6719864  
 Class :character   1st Qu.:1994   Class :character   1st Qu.:1026328   1st Qu.: 91574114  
 Mode  :character   Median :2000   Mode  :character   Median :1166181   Median :167142682  
                    Mean   :1999                      Mean   :1284147   Mean   :213513229  
                    3rd Qu.:2006                      3rd Qu.:1460896   3rd Qu.:310730244  
                    Max.   :2014                      Max.   :2294987   Max.   :760507625  
     Score         Metascore      Time Duration (min)   Director            Actors         
 Min.   :7.800   Min.   : 58.00   Min.   : 98.0       Length:1440        Length:1440       
 1st Qu.:8.300   1st Qu.: 68.75   1st Qu.:123.5       Class :character   Class :character  
 Median :8.500   Median : 77.50   Median :142.5       Mode  :character   Mode  :character  
 Mean   :8.504   Mean   : 77.90   Mean   :146.2                                            
 3rd Qu.:8.700   3rd Qu.: 87.00   3rd Qu.:166.0                                            
 Max.   :9.300   Max.   :100.00   Max.   :202.0                                            
 Restriction        Description       
 Length:1440        Length:1440       
 Class :character   Class :character  
 Mode  :character   Mode  :character  
                                      
                                      
                                      

From above, we can see that our dataset have 1440 rows and 12 columns and a summary of our entire dataset is presented above.

Relationship between Regressor and Response Variable.

Here, we are showing a possible relationship between regressor and the response variable by plotting the graph and correlation.

The first graph we are witnessing is showing the relationship between our Response variable Score and regressor Movie Revenue. As we can see that there is not a clear but visible positive linear trend between our Regressor and response variable.

# relation between variables
fig1 <- plot_ly(data = imdb, x = ~Score, y = ~imdb$`Movie Revenue (M$)`)
fig1

Let’s confirm our relationship between variable by performing correlation.

cormovie <- cor.test(imdb$Score, imdb$`Movie Revenue (M$)`, 
                method = "pearson")
cormovie

    Pearson's product-moment correlation

data:  imdb$Score and imdb$`Movie Revenue (M$)`
t = -14.754, df = 1438, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.4066409 -0.3168782
sample estimates:
       cor 
-0.3626002 

From correlation we see that:

T value is -14.754(t-test statistic)

df is 1438(degrees of freedom)

p-value is the significance level of the t-test (p-value < 2.21).

conf.int is the confidence interval of the correlation coefficient at 95% (conf.int = [-0.4066, -0.3168]).

sample estimates is the correlation coefficient (Cor.coeff = -0.36).

So, we can say that our regressor and response variable are somewhere shows correlation but is not very strong.

Now, second we will show the relationship between Metascore and Score variable by displaying a scatter plot.

fig2 <- plot_ly(data = imdb, x = ~Score, y = ~imdb$Metascore)
fig2

The Second graph we are witnessing is showing the relationship between our Response variable Score and regressor Metascore. As we can see that there is not a clear but visible positive linear trend between our Regressor and response variable.

Let’s confirm our relationship between variable by performing correlation.

cormeta <- cor.test(imdb$Score, imdb$Metascore, 
                     method = "pearson")
cormeta

    Pearson's product-moment correlation

data:  imdb$Score and imdb$Metascore
t = 18.529, df = 1438, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3963395 0.4797856
sample estimates:
      cor 
0.4390088 

From correlation we see that:

T value is 18.529(t-test statistic)

df is 1438(degrees of freedom)

p-value is the significance level of the t-test (p-value < 2.22).

conf.int is the confidence interval of the correlation coefficient at 95% (conf.int = [0.3963, 0.4797]).

sample estimates is the correlation coefficient (Cor.coeff = 0.4390).

So, we can say that our regressor and response variable are somewhere shows correlation but is not very strong.

Now, we are going to represent the relationship between Restriction and Score variable by plotting a boxplot.

fig3 <- plot_ly(data = imdb, x = ~Restriction, y = ~Score, type = "box")
fig3 <- fig3 %>% layout(boxmode = "group")
fig3

From the above boxplot, we can see the average Score of the movie as per different age retrictions such as for Age group of 12, the average score is 8.5 while for Age group of 16 the average score is 8.65 and so on.

Regression Model Fitting:

Now, we will fit various regression model and perform tests to check the assumptions and transform the data.

First, we are fitting Multiple Linear Regression on our imdb Dataset.

Multiple Linear Regression

Model1 <- lm(Score~ imdb$`Movie Date`+imdb$`Number of Votes`+imdb$`Movie Revenue (M$)`
                    +imdb$Metascore+imdb$`Time Duration (min)`, imdb)
summary(Model1)

Call:
lm(formula = Score ~ imdb$`Movie Date` + imdb$`Number of Votes` + 
    imdb$`Movie Revenue (M$)` + imdb$Metascore + imdb$`Time Duration (min)`, 
    data = imdb)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.255330 -0.080766 -0.008307  0.086434  0.198411 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 3.014e+01  6.231e-01   48.37   <2e-16 ***
imdb$`Movie Date`          -1.154e-02  3.087e-04  -37.39   <2e-16 ***
imdb$`Number of Votes`      6.572e-07  8.967e-09   73.29   <2e-16 ***
imdb$`Movie Revenue (M$)`  -7.935e-10  1.785e-11  -44.46   <2e-16 ***
imdb$Metascore              7.087e-03  2.906e-04   24.39   <2e-16 ***
imdb$`Time Duration (min)`  1.461e-03  1.111e-04   13.16   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1083 on 1434 degrees of freedom
Multiple R-squared:  0.8943,    Adjusted R-squared:  0.8939 
F-statistic:  2425 on 5 and 1434 DF,  p-value: < 2.2e-16

Here, we have fitted a Multiple Linear Regression model with five variables and name the model as Multiple Model. We can see that Multiple R2 value is 0.8943 and Adj R2 Value is 0.8939. We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

To check the overall fit, we will use Anova function.

anova(Model1)
Analysis of Variance Table

Response: Score
                             Df Sum Sq Mean Sq F value    Pr(>F)    
imdb$`Movie Date`             1 34.100  34.100 2908.92 < 2.2e-16 ***
imdb$`Number of Votes`        1 81.233  81.233 6929.59 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 16.680  16.680 1422.92 < 2.2e-16 ***
imdb$Metascore                1  8.122   8.122  692.88 < 2.2e-16 ***
imdb$`Time Duration (min)`    1  2.029   2.029  173.10 < 2.2e-16 ***
Residuals                  1434 16.810   0.012                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant and the model is a good fit.

Now, we will display the graphs to test the assumptions.

par(mfrow = c(2,2))
plot(Model1)

we can see 4 Diagnostic plots known as Residuals plot, QQ plots, Scale-location plot and leverage plots.

In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end.

The next two shows that the regression is non-linear, non-constant variance.

To test further, if any assumptions are violated or not. We will perform some tests.

durbinWatsonTest(Model1)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2078972       2.41538       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is violated.

shapiro.test(Model1$residuals)

    Shapiro-Wilk normality test

data:  Model1$residuals
W = 0.97392, p-value = 1.766e-15

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that normality error assumption is violated.

ncvTest(Model1)
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 8.627698, Df = 1, p = 0.0033109

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value is less then 0.05. therefore, we can say that the constant variance assumptions is violated here.

Observation of Multiple Model.

After performing the test, we found out that few assumptions were violated here. So, we will try to transform the data using Log Transformation.

Transformation via Log:

We will make a new model and name it as log1 and fit the transformed value.

# log transformation
log1 <-lm(log(imdb$Score)~ imdb$`Movie Date`+imdb$`Number of Votes`+imdb$`Movie Revenue (M$)`
            +imdb$Metascore+imdb$`Time Duration (min)`, imdb)     
summary(log1)

Call:
lm(formula = log(imdb$Score) ~ imdb$`Movie Date` + imdb$`Number of Votes` + 
    imdb$`Movie Revenue (M$)` + imdb$Metascore + imdb$`Time Duration (min)`, 
    data = imdb)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0315958 -0.0106862 -0.0003744  0.0107547  0.0240024 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 4.651e+00  7.567e-02   61.47   <2e-16 ***
imdb$`Movie Date`          -1.340e-03  3.749e-05  -35.74   <2e-16 ***
imdb$`Number of Votes`      7.667e-08  1.089e-09   70.41   <2e-16 ***
imdb$`Movie Revenue (M$)`  -9.587e-11  2.167e-12  -44.24   <2e-16 ***
imdb$Metascore              8.350e-04  3.529e-05   23.66   <2e-16 ***
imdb$`Time Duration (min)`  1.663e-04  1.349e-05   12.33   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01315 on 1434 degrees of freedom
Multiple R-squared:  0.8875,    Adjusted R-squared:  0.8871 
F-statistic:  2263 on 5 and 1434 DF,  p-value: < 2.2e-16

Here, we have fitted a transformed Multiple Linear Regression model with five variables and name the model as log1 . We can see that Multiple R2 value is 0.8875 and Adj R2 Value is 0.8871 We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

To check the overall fit, we will use Anova function.

anova(log1)
Analysis of Variance Table

Response: log(imdb$Score)
                             Df  Sum Sq Mean Sq F value    Pr(>F)    
imdb$`Movie Date`             1 0.46903 0.46903 2713.65 < 2.2e-16 ***
imdb$`Number of Votes`        1 1.10096 1.10096 6369.82 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 0.24749 0.24749 1431.89 < 2.2e-16 ***
imdb$Metascore                1 0.11226 0.11226  649.49 < 2.2e-16 ***
imdb$`Time Duration (min)`    1 0.02628 0.02628  152.03 < 2.2e-16 ***
Residuals                  1434 0.24785 0.00017                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant and the model is a good fit.

Now, we will display the graphs to test the assumptions.

par(mfrow = c(2,2))
plot(log1)

After the transformation, we can say that:

In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is still violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end. It is almost same as before transformation.

The next two shows that the regression is non-linear, non-constant variance. To test further, if any assumptions are violated or not. We will perform some tests.

To Test for Auto correlated Errors, we will perform DW-test

durbinWatsonTest(log1)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2085987      2.416514       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is violated.

shapiro.test(log1$residuals)

    Shapiro-Wilk normality test

data:  log1$residuals
W = 0.97352, p-value = 1.295e-15

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we have enough evidence to reject H0.

This implies that normality error assumption is violated.

ncvTest(log1)
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 8.671053, Df = 1, p = 0.003233

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value is less then 0.05. therefore, we can say that the constant variance assumptions is still violated here.

Comments:

As we can see that before transformation, few assumptions were violated. So we transform the data using log transformation and after transformation also, these assumptions were still violated.

Stepwise Regression.

Now, we will perform another regression analysis on our dataset named Stepwise Regression Below we have fitted a stepwise model.

step(Model1, data=imdb, direction="both")
Start:  AIC=-6396.59
Score ~ imdb$`Movie Date` + imdb$`Number of Votes` + imdb$`Movie Revenue (M$)` + 
    imdb$Metascore + imdb$`Time Duration (min)`

                             Df Sum of Sq    RSS     AIC
<none>                                    16.810 -6396.6
- imdb$`Time Duration (min)`  1     2.029 18.839 -6234.5
- imdb$Metascore              1     6.971 23.781 -5899.0
- imdb$`Movie Date`           1    16.391 33.201 -5418.5
- imdb$`Movie Revenue (M$)`   1    23.174 39.984 -5150.8
- imdb$`Number of Votes`      1    62.960 79.770 -4156.3

Call:
lm(formula = Score ~ imdb$`Movie Date` + imdb$`Number of Votes` + 
    imdb$`Movie Revenue (M$)` + imdb$Metascore + imdb$`Time Duration (min)`, 
    data = imdb)

Coefficients:
               (Intercept)           imdb$`Movie Date`      imdb$`Number of Votes`  
                 3.014e+01                  -1.154e-02                   6.572e-07  
 imdb$`Movie Revenue (M$)`              imdb$Metascore  imdb$`Time Duration (min)`  
                -7.935e-10                   7.087e-03                   1.461e-03  

After performing the step wise regression model, we found out that AIC is -6396.59 and our first model is the best fitted model with the lowest AIC. The variables included in that model with their coefficients are:

-Intercept: 3.014

-Movie Data: -1.154

-Number of votes:6.572

-Movie Revenue: -7.935

-Metascore:7.087

-Time Duration:1.461.

After evaluating our best fit model, we fit the model and perform analysis on it.

Model2 = lm(Score ~ Metascore + imdb$`Movie Revenue (M$)` + 
               imdb$`Number of Votes` + imdb$`Movie Date`+imdb$`Time Duration (min)`, data = imdb)
summary(Model2 )

Call:
lm(formula = Score ~ Metascore + imdb$`Movie Revenue (M$)` + 
    imdb$`Number of Votes` + imdb$`Movie Date` + imdb$`Time Duration (min)`, 
    data = imdb)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.255330 -0.080766 -0.008307  0.086434  0.198411 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 3.014e+01  6.231e-01   48.37   <2e-16 ***
Metascore                   7.087e-03  2.906e-04   24.39   <2e-16 ***
imdb$`Movie Revenue (M$)`  -7.935e-10  1.785e-11  -44.46   <2e-16 ***
imdb$`Number of Votes`      6.572e-07  8.967e-09   73.29   <2e-16 ***
imdb$`Movie Date`          -1.154e-02  3.087e-04  -37.39   <2e-16 ***
imdb$`Time Duration (min)`  1.461e-03  1.111e-04   13.16   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1083 on 1434 degrees of freedom
Multiple R-squared:  0.8943,    Adjusted R-squared:  0.8939 
F-statistic:  2425 on 5 and 1434 DF,  p-value: < 2.2e-16

Here, we have fitted a AIC model with the best model. We can see that Multiple R2 value is 0.8943 and Adj R2 Value is 0.8939. We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

To check the overall fit, we will use Anova function.

anova(Model2)
Analysis of Variance Table

Response: Score
                             Df Sum Sq Mean Sq F value    Pr(>F)    
Metascore                     1 30.639  30.639  2613.7 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 30.624  30.624  2612.4 < 2.2e-16 ***
imdb$`Number of Votes`        1 62.372  62.372  5320.7 < 2.2e-16 ***
imdb$`Movie Date`             1 16.500  16.500  1407.6 < 2.2e-16 ***
imdb$`Time Duration (min)`    1  2.029   2.029   173.1 < 2.2e-16 ***
Residuals                  1434 16.810   0.012                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant and the model is a good fit.

Now, we will check if any assumptions is violated or not.

par(mfrow=c(2,2))
plot(Model2 )

we can see 4 Diagnostic plots known as Residuals plot, QQ plots, Scale-location plot and leverage plots. In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end.

The next two shows that the regression is non-linear, non-constant variance. To test further, if any assumptions are violated or not. We will perform some tests.

Now to move further in our Analysis, we will perform some statiscal test.

ncvTest(Model2 )
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 8.627698, Df = 1, p = 0.0033109

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value is less then 0.05. therefore, we can say that the constant variance assumptions is violated here.

durbinWatsonTest(Model2)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2078972       2.41538       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is violated.

shapiro.test(Model2 $residuals) 

    Shapiro-Wilk normality test

data:  Model2$residuals
W = 0.97392, p-value = 1.766e-15

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that normality error assumption is violated.

Observation of Model2 Model:

After performing the test, we found out that few assumptions are violated here. So, we will try to transform the data using Square-root Transformation.

Sqrt Transformation:

We will make a new model and name it as sqrt and fit the transformed value. We will transform both the values of Regressor and predictor.

#sqrt Transformation
sqrt <- lm(sqrt(Score) ~ (Metascore + imdb$`Movie Revenue (M$)` + 
             imdb$`Number of Votes` + imdb$`Movie Date`+imdb$`Time Duration (min)`), data = imdb)
summary(sqrt)

Call:
lm(formula = sqrt(Score) ~ (Metascore + imdb$`Movie Revenue (M$)` + 
    imdb$`Number of Votes` + imdb$`Movie Date` + imdb$`Time Duration (min)`), 
    data = imdb)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.044910 -0.014569 -0.001016  0.015326  0.034504 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 6.601e+00  1.085e-01   60.82   <2e-16 ***
Metascore                   1.216e-03  5.061e-05   24.03   <2e-16 ***
imdb$`Movie Revenue (M$)`  -1.379e-10  3.108e-12  -44.36   <2e-16 ***
imdb$`Number of Votes`      1.122e-07  1.562e-09   71.85   <2e-16 ***
imdb$`Movie Date`          -1.966e-03  5.376e-05  -36.57   <2e-16 ***
imdb$`Time Duration (min)`  2.465e-04  1.934e-05   12.74   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01886 on 1434 degrees of freedom
Multiple R-squared:  0.891, Adjusted R-squared:  0.8906 
F-statistic:  2344 on 5 and 1434 DF,  p-value: < 2.2e-16

Here, we have fitted our transformed model. We can see that Multiple R2 value is 0.891 and Adj R2 Value is 0.8906

We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

To check the overall fit, we will use Anova function.

anova(sqrt)
Analysis of Variance Table

Response: sqrt(Score)
                             Df  Sum Sq Mean Sq F value    Pr(>F)    
Metascore                     1 0.89098 0.89098 2505.96 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 0.92234 0.92234 2594.16 < 2.2e-16 ***
imdb$`Number of Votes`        1 1.81745 1.81745 5111.72 < 2.2e-16 ***
imdb$`Movie Date`             1 0.47862 0.47862 1346.15 < 2.2e-16 ***
imdb$`Time Duration (min)`    1 0.05775 0.05775  162.42 < 2.2e-16 ***
Residuals                  1434 0.50985 0.00036                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant as well as the model and the model is a good fit.

Now, we will check for assumptions by visualizing the model.

par(mfrow=c(2,2))
plot(sqrt)

After the transformation, we can say that:

In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is still violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end. It is almost same as before transformation.

The next two shows that the regression is non-linear, non-constant variance. To test further, if any assumptions are violated or not. We will perform some tests.

durbinWatsonTest(sqrt)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2087192      2.416894       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is still violated.

shapiro.test(sqrt$residuals)

    Shapiro-Wilk normality test

data:  sqrt$residuals
W = 0.97359, p-value = 1.373e-15

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that normality error assumption is violated.

ncvTest(sqrt)
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 8.728842, Df = 1, p = 0.0031322

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value is less then 0.05. therefore, we can say that the constant variance assumptions is violated here.

Comments

As we can see that before transformation, few assumptions were violated. So we transform the data using square-root transformation and after transformation also, these assumptions were still violated.

Multiple Linear Regression using indicator variable:

Here, we have used Restriction variables and converted it into four category(1,2,3,4) and then build a multiple linear regression by including that variable and considering that as indicator variable using factor function.

imdb$Restriction[imdb$Restriction=="12"]<-"1"
imdb$Restriction[imdb$Restriction=="16"]<-"2"
imdb$Restriction[imdb$Restriction=="Tous publics"]<-"3"
imdb$Restriction[imdb$Restriction=="Tous publics avec avertissement"]<-"4"
imdb$Restriction <- factor(imdb$Restriction)

Here, we have build the linear regression model and named it as Model3 and now we will perform our analysis on this model.

Model3 <- lm(Score~ imdb$`Movie Date`+imdb$`Number of Votes`+imdb$`Movie Revenue (M$)`
                    +imdb$Metascore+imdb$`Time Duration (min)`+Restriction, imdb)
summary(Model3)

Call:
lm(formula = Score ~ imdb$`Movie Date` + imdb$`Number of Votes` + 
    imdb$`Movie Revenue (M$)` + imdb$Metascore + imdb$`Time Duration (min)` + 
    Restriction, data = imdb)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.243150 -0.078436  0.002359  0.096447  0.203974 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 3.019e+01  6.159e-01  49.013  < 2e-16 ***
imdb$`Movie Date`          -1.156e-02  3.052e-04 -37.879  < 2e-16 ***
imdb$`Number of Votes`      6.590e-07  8.818e-09  74.735  < 2e-16 ***
imdb$`Movie Revenue (M$)`  -8.425e-10  1.992e-11 -42.298  < 2e-16 ***
imdb$Metascore              7.010e-03  2.913e-04  24.060  < 2e-16 ***
imdb$`Time Duration (min)`  1.392e-03  1.124e-04  12.377  < 2e-16 ***
Restriction2               -6.255e-02  1.168e-02  -5.355 9.95e-08 ***
Restriction3                2.057e-02  7.889e-03   2.608  0.00921 ** 
Restriction4                5.482e-03  1.055e-02   0.520  0.60346    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1063 on 1431 degrees of freedom
Multiple R-squared:  0.8982,    Adjusted R-squared:  0.8976 
F-statistic:  1579 on 8 and 1431 DF,  p-value: < 2.2e-16

Here, we have fitted our model by considering restriction as our indicator variable. We can see that Multiple R2 value is 0.8982 and Adj R2 Value is 0.8976.

We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

Only Variable restriction 4 has insignificant values as it’s p-value is greater then 0.05, rest all the variables have significant values.

To check the overall fit, we will use Anova function.

anova(Model3)
Analysis of Variance Table

Response: Score
                             Df Sum Sq Mean Sq  F value    Pr(>F)    
imdb$`Movie Date`             1 34.100  34.100 3015.704 < 2.2e-16 ***
imdb$`Number of Votes`        1 81.233  81.233 7183.978 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 16.680  16.680 1475.158 < 2.2e-16 ***
imdb$Metascore                1  8.122   8.122  718.318 < 2.2e-16 ***
imdb$`Time Duration (min)`    1  2.029   2.029  179.455 < 2.2e-16 ***
Restriction                   3  0.629   0.210   18.547  8.33e-12 ***
Residuals                  1431 16.181   0.011                       
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant and the model is a good fit.

Now, we will check for assumptions by visualizing the model.

par(mfrow=c(2,2))
plot(Model3)

In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end.

The next two shows that the regression is non-linear, non-constant variance.

To test further, if any assumptions are violated or not. We will perform some tests.

durbinWatsonTest(Model3)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2398101      2.478892       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is violated.

shapiro.test(Model3$residuals)

    Shapiro-Wilk normality test

data:  Model3$residuals
W = 0.97762, p-value = 3.558e-14

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we enough evidence to reject H0. This implies that normality error assumption is violated.

ncvTest(Model3)
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 3.45498, Df = 1, p = 0.063061

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value > 0.05. therefore, we can say that the constant variance assumptions is violated here.

Observation

After performing the test, we found out that few assumptions were violated here. So, we will try to transform the data using Log Transformation.

Log Transformation

We will make a new model and name it as log2 and fit the transformed value

log2 <- lm(log(Score)~ imdb$`Movie Date`+imdb$`Number of Votes`+imdb$`Movie Revenue (M$)`
                    +imdb$Metascore+imdb$`Time Duration (min)`+Restriction, imdb)
summary(log2)

Call:
lm(formula = log(Score) ~ imdb$`Movie Date` + imdb$`Number of Votes` + 
    imdb$`Movie Revenue (M$)` + imdb$Metascore + imdb$`Time Duration (min)` + 
    Restriction, data = imdb)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0301303 -0.0091947  0.0004506  0.0113366  0.0247214 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 4.660e+00  7.484e-02  62.265  < 2e-16 ***
imdb$`Movie Date`          -1.343e-03  3.708e-05 -36.228  < 2e-16 ***
imdb$`Number of Votes`      7.689e-08  1.071e-09  71.771  < 2e-16 ***
imdb$`Movie Revenue (M$)`  -1.016e-10  2.420e-12 -41.964  < 2e-16 ***
imdb$Metascore              8.265e-04  3.540e-05  23.349  < 2e-16 ***
imdb$`Time Duration (min)`  1.575e-04  1.366e-05  11.527  < 2e-16 ***
Restriction2               -7.515e-03  1.419e-03  -5.295 1.38e-07 ***
Restriction3                2.402e-03  9.586e-04   2.506   0.0123 *  
Restriction4                1.061e-03  1.282e-03   0.827   0.4082    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01292 on 1431 degrees of freedom
Multiple R-squared:  0.8916,    Adjusted R-squared:  0.891 
F-statistic:  1471 on 8 and 1431 DF,  p-value: < 2.2e-16

Here, we have fitted a transformed Multiple Linear Regression model . We can see that Multiple R2 value is 0.8916 and Adj R2 Value is 0.891. We can also see that the P-value is smaller then 0.05, therefore we can say that the model is significant and we can reject H0.

To check the overall fit, we will use Anova function.

anova(log2)
Analysis of Variance Table

Response: log(Score)
                             Df  Sum Sq Mean Sq  F value    Pr(>F)    
imdb$`Movie Date`             1 0.46903 0.46903 2809.836 < 2.2e-16 ***
imdb$`Number of Votes`        1 1.10096 1.10096 6595.595 < 2.2e-16 ***
imdb$`Movie Revenue (M$)`     1 0.24749 0.24749 1482.646 < 2.2e-16 ***
imdb$Metascore                1 0.11226 0.11226  672.509 < 2.2e-16 ***
imdb$`Time Duration (min)`    1 0.02628 0.02628  157.422 < 2.2e-16 ***
Restriction                   3 0.00899 0.00300   17.943 1.965e-11 ***
Residuals                  1431 0.23887 0.00017                       
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From Anova Test, we found out that P value for all the variables are 0.001 which is less than 0.005. Therefore,we can say that all the variables are statiscally significant and the model is a good fit.

Now, we will display the graphs to test the assumptions.

par(mfrow = c(2,2))
plot(log2)

we can see 4 Diagnostic plots known as Residuals plot, QQ plots, Scale-location plot and leverage plots.

In the 1st graph we can see that on X axis has predicted value known as Y^(y hat) and on y axis there are residuals and errors. Here we can the line is not flat and the points are looking as a bunch of clouds which means that linearity assumption is violated here.

In the next Graph (QQ plot), here y axis is ordered, observed and Standardized residuals and on X axis it has ordered theoretical residuals. In the graph we can see that residuals are truly normally distributed as mostly points are falling on the line but some were falling out of line in the end.

The next two shows that the regression is non-linear, non-constant variance.

To test further, if any assumptions are violated or not. We will perform some tests.

durbinWatsonTest(log2)
 lag Autocorrelation D-W Statistic p-value
   1      -0.2412033      2.481343       0
 Alternative hypothesis: rho != 0

H0: Errors are uncorrelated

H1: Errors are correlated

From the Durbin Waston test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that uncorrelated error assumption is not violated.

shapiro.test(log2$residuals)

    Shapiro-Wilk normality test

data:  log2$residuals
W = 0.9772, p-value = 2.482e-14

H0: Errors are normally distributed

H1: Errors are not normally distributed

From the Shapiro-wilk normality test, Since the p-value is < 0.05 we have enough evidence to reject H0. This implies that normality error assumption is violated.

ncvTest(log2)
Non-constant Variance Score Test 
Variance formula: ~ fitted.values 
Chisquare = 2.913237, Df = 1, p = 0.087855

H0: Errors have a constant variance

H1: Errors have a non-constant variance

Here, we can see that p value > 0.05. therefore, we can say that the constant variance assumptions is violated here.

Comments:

As we can see that before transformation,few assumptions were violated. So we transform the data using log transformation and after transformation also, these assumption is still violated.

Choosing best Model:

After building three different, we have to choose the best model. We will choose the most appropriate model on the basis of their Multiple R2 values.

Multiple R2 value of different models are:

1.) Model1: 0.8943, Transformed Model1: 0.8875

2.) Model2: 0.8943, Transformed Model2: 0.891

3.) Model3: 0.8982, Transformed Model3: 0.8916

So from the above values, we can say that Model3 is the best model if we observed as per their Multiple R2 values while Model 3 is still the best model if we see the Multiple r2 values of Transformed models.

Predicting Target variable:

Now we will perform prediction on the best model using different variables.

First we will use Metascore Variable.

prediction1  <- lm(Score~Metascore,imdb)
summary(prediction1)

Call:
lm(formula = Score ~ Metascore, data = imdb)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77185 -0.13538  0.02097  0.18229  0.76793 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 7.4712626  0.0562996  132.71   <2e-16 ***
Metascore   0.0132601  0.0007157   18.53   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2987 on 1438 degrees of freedom
Multiple R-squared:  0.1927,    Adjusted R-squared:  0.1922 
F-statistic: 343.3 on 1 and 1438 DF,  p-value: < 2.2e-16
predict(prediction1,data.frame(Metascore = 100),interval="prediction", level = 0.95)
       fit      lwr      upr
1 8.797269 8.210231 9.384308

The prediction interval using Metascore is [8.797,9.384].

References:

  1. Sthda.com. 2021. Correlation Test Between Two Variables in R - Easy Guides - Wiki - STHDA. [online] Available at: http://www.sthda.com/english/wiki/correlation-test-between-two-variables-in-r [Accessed 3 June 2021].

  2. R, B., 2021. Best Subsets Regression Essentials in R - Articles - STHDA. [online] Sthda.com. Available at: http://www.sthda.com/english/articles/37-model-selection-essentials-in-r/155-best-subsets-regression-essentials-in-r/ [Accessed 3 June 2021].

  3. ListenData. 2021. 15 Types of Regression in Data Science. [online] Available at: https://www.listendata.com/2018/03/regression-analysis.html [Accessed 3 June 2021].


  1. -16↩︎

  2. -16↩︎

LS0tDQp0aXRsZTogIk1BVEgxMzEyIFJlZ3Jlc3Npb24gQW5hbHlzaXMgU2VtZXN0ZXIgMSwgMjAyMSINCmF1dGhvcjogIlN1bm55IEt1bWFyIFZhaXNobm92KHMzODIyMjk1KSINClN0dWRlbnQgbm8uOiBTMzgyMjI5NQ0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQpzdWJ0aXRsZTogTWF0aCAxMzEyIFJlZ3Jlc3Npb24gQW5hbHlzaXMgRmluYWwgUmVwb3J0DQotLS0NCg0KIyBPYmplY3RpdmUgJiBJbnRyb2R1Y3Rpb246IA0KDQpJdCBpcyBvYnNlcnZlZCB0aGF0IHRoZSBjcml0aWMgc2NvcmUgZWFybmVkIGJ5IGEgbW92aWUgaXMgYmFzZWQgb24gdmFyaW91cyBmYWN0b3JzIHN1Y2ggYXMgQWN0b3JzLCBEaXJlY3RvcnMsIFBsb3Qgb2YgbW92aWUsIHJldmVudWUsIGV0Yy4gVGhlIHJlcG9ydCBkZWFscyB3aXRoIGFsbCB0aGVzZSBraW5kcyBvZiBmYWN0b3JzIGFuZCBoaWdobGlnaHRpbmcgdGhlIGltcG9ydGFudCBvbmVzIGFuZCBkZXZlbG9waW5nIGEgcmVncmVzc2lvbiBtb2RlbCBiYXNlZCBvbiB0aGUgKippbWRiX2QqKiB0byBwcmVkaWN0IHRoZSBhdmVyYWdlIGNyaXRpYyBzY29yZSBlYXJuZWQgYnkgYSBtb3ZpZS4NCg0KIyBEYXRhc2V0IGFuZCBpdHMgc291cmNlOg0KDQpUaGUgZGF0YXNldCBpbiB0aGlzIHByb2plY3QgaXMgZXh0cmFjdGVkIGZyb20gS2FnZ2xlLmNvbSBvcGVuIGRhdGFzZXRzOiBJTURCIE5ldyBEYXRhc2V0LiBUaGUgZGF0YXNldCB3YXMgYWNxdWlyZWQgdXNpbmcgQmVhdXRpZnVsU291cDQgZnJvbSB0aGUgc2VhcmNoIHBhZ2Ugb2YgSW1kYi4gDQoNClNvdXJjZTotIEthZ2dsZS5jb20uIDIwMjEuIElNREIgTmV3IERhdGFzZXQuIFtvbmxpbmVdIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vd3JhbmRyYWxsL2ltZGItbmV3LWRhdGFzZXQ/c2VsZWN0PWltZGJfZGI+IFtBY2Nlc3NlZCA0IEp1bmUgMjAyMV0uDQoNClRoZSBkYXRhc2V0IGhhcyAxODk5MDAgcm93cyBhbmQgMTQgY29sdW1ucy4gRWFjaCBvYnNlcnZhdGlvbiByZXByZXNlbnRzIGEgbW92aWUuIFRoZSBtb3ZpZXMgYXJlIHJhbmtlZCBieSB0aGUgbnVtYmVyIG9mIHZvdGVzIGZyb20gaGlnaCB0byBsb3cuDQpUaGUgbW92aWUgbGlzdGVkIGFyZSB0YWtlbiBmcm9tIDE5MDAtMjAyMC4NCg0KIyBEZXNjcmlwdGl2ZSBmZWF0dXJlcyBvZiBkYXRhc2V0Og0KDQpUaGVyZSBhcmUgNiBudW1lcmljYWwgYW5kIDggY2F0ZWdvcmljYWwgdmFyaWFibGVzIHByZXNlbnQgaW4gdGhlIGRhdGFzZXQuIFRoZXkgYXJlIGxpc3RlZCBiZWxvdzoNClRoZSB0YXJnZXQgRmVhdHVyZSBpczoNCg0KKipTY29yZToqKiBUaGUgbWVhbiBzY29yZSBhbGxvdHRlZCB0byB0aGUgbW92aWUgYmV0d2VlbiAxLTEwIGJ5IHRoZSBqb3VybmFsaXN0cy9jcml0aWNzLg0KDQoqKk1vdmllIE5hbWU6KiogdGhlIG5hbWUgb2YgdGhlIG1vdmllL1Nlcmllcy4NCg0KKipNb3ZpZSBkYXRlOioqIFRoZSB5ZWFyIHdoZW4gdGhlIG1vdmllIHJlbGVhc2VkLg0KDQoqKlNlcmllcyBOYW1lOioqIHRoZSBuYW1lIG9mIHRoZSBzZXJpZXMgc2Vhc29uIChpZiBhbnkpDQoNCioqU2VyaWVzIGRhdGU6KiogdGhlIHllYXIgd2hlbiB0aGUgc2VyaWVzIHJlbGVhc2VkLg0KDQoqKk1vdmllIHR5cGU6KiogZ2VucmUgb2YgbW92aWUgKGFjdGlvbiwgZHJhbWEsIHNjaS1maS4uKQ0KDQoqKk51bWJlciBvZiB2b3RlczoqKiBudW1iZXIgb2YgcGVvcGxlIHdobyB2b3RlZCBmb3IgdGhlIG1ldGEgc2NvcmUuDQoNCioqTW92aWUgcmV2ZW51ZSBpbiBtaWxsaW9uICQ6KiogQm94LW9mZmljZSByZXZlbnVlIG1hZGUgYnkgdGhlIG1vdmllIGluIG1pbGxpb24gJA0KDQoqKk1ldGEgc2NvcmU6KiogdGhlIG1lYW4gc2NvcmUgYWxsb3R0ZWQgdG8gdGhlIG1vdmllL3NlcmllcyBmcm9tIDEtMTAwIGJ5IHZpZXdlcnMuDQoNCioqVGltZSBEdXJhdGlvbjoqKiB0aGUgZHVyYXRpb24gb2YgdGhlIG1vdmllIGluIG1pbnV0ZXMuDQoNCioqRGlyZWN0b3I6KiogbGlzdCBvZiBkaXJlY3RvcnMgdGhhdCBkaXJlY3RlZCB0aGUgbW92aWUvc2VyaWVzLg0KDQoqKkFjdG9yczoqKiBsaXN0IG9mIG1haW4gYWN0b3JzIHRoYXQgcGxheWVkIGluIHRoZSBtb3ZpZS9zZXJpZXMuDQoNCioqUmVzdHJpY3Rpb25zOioqIEFnZSByZXN0cmljdGlvbiBhbmQgd2FybmluZyAoYWxsIHB1YmxpYywgYWxsIHB1YmxpYyB3aXRoIHdhcm5pbmcsIDEyICwxMiB3aXRoIHdhcm5pbmdzLCAxNiwg4oCmLikNCg0KKipEZXNjcmlwdGlvbjoqKiBBIHNob3J0IHN1bW1hcnkgb2YgdGhlIG1vdmllLg0KDQojIFJlcG9ydA0KDQojIyBJbXBvcnRpbmcgTGlicmFyaWVzLg0KDQpUbyBzdGFydCB3aXRoIG91ciByZXBvcnQsIHdlIGhhdmUgdG8gaW1wb3J0IG5lY2Vzc2FyeSBSIHBhY2thZ2VzOg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShjYXIpDQpgYGANCg0KIyMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0Lg0KDQpBZnRlciBpbXBvcnRpbmcgdGhlIG5lY2Nlc3NhcnkgcGFja2FnZXMsIHdlIGhhdmUgdG8gaW1wb3J0IHRoZSAqKmltZGIgZGF0YWJhc2V0KiovDQpXZSBpbXBvcnRlZCB0aGUgZGF0YXNldCB1c2luZyAqKnJlYWRfY3N2KiogZnVuY3Rpb24gYW5kIG5hbWVkIHRoZSBkYXRhc2V0IGFzICoqaW1kYl9kYioqIGFuZCBoYXZlIGEgZ2xpbXBzZSBhdCBvdXIgZGF0YXRzZXQuDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KaW1kYl9kYiA8LSByZWFkX2NzdigiaW1kYl9kYi5jc3YiKQ0KaW1kYl9kYg0KYGBgDQoNCiMjIERhdGEgUHJlLXByb2Nlc3NpbmcuDQoNCk91ciBkYXRhc2V0IGhhcyBhbG90IG9mIG9ic2VydmF0aW9ucy4gU28sIHdlIHdpbGwgdGFrZSB0b3AgMTUwMCBvYnNlcnZhdGlvbnMgd2hpY2ggcmVwcmVzZW50cyB0b3Agc2NvcmUgb2YgMTUwMCBtb3ZpZXMgYW5kIG5hbWUgdGhlbSBhcyAqKmltZGIqKi4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KaW1kYiA8LSBpbWRiX2RiICU+JSBzbGljZSgxOjE1MDApDQppbWRiDQpgYGANCg0KTm93LCBsZXQncyBjaGVjayBmb3IgYW55IG1pc25nIHZhbHVlcyBpbiBvdXIgZGF0YXNldC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KY29sU3Vtcyhpcy5uYShpbWRiKSkNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlIHRoYXQgb3VyIGRhdGFzZXQgaGFzIG1pc3NpbmcgdmFsdWVzIGluIGNvbHVtbnMgKipTZXJpZSBOYW1lKiosICoqU2VyaWUgZGF0YSoqLCAqKk1ldGEgc2NvcmUqKiBhbmQgKipNb3ZpZSBSZXZlbnVlKE0kKSoqLiBTbywgYmVmb3JlIG1vdmluZyBmdXJ0aGVyIHdlIGhhdmUgcmVtb3ZlIHRoZW0uIFdlIHdpbGwgcmVtb3ZlIHRoZW0gYnkgdXNpbmcgKipkcm9wLm5hKCkqKiBhbmQgKipzZWxlY3QqKiBmdW5jdGlvbnMuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmltZGIgPC0gZHJvcF9uYShpbWRiLGBNb3ZpZSBSZXZlbnVlIChNJClgLE1ldGFzY29yZSkNCmltZGIgPC0gc2VsZWN0KGltZGIsLWMoMyw0KSkNCmBgYA0KDQpXZSBoYXZlIHJlbW92ZWQgdGhlIG1pc3NpbmcgdmFsdWVzIGFuZCBqdXN0IGNoZWNraW5nIGlmIHdlIHN0aWxsIGhhdmUgYW55IG1pc3NpbmcgdmFsdWVzIHByZXNlbnQgaW4gdGhlICoqaW1kYioqIGRhdGFzZXQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmNvbFN1bXMoaXMubmEoaW1kYikpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBwcmVzZW50IGluIG91ciAqKmltZGIqKiBkYXRhc2V0Lg0KTm93ICwgd2Ugd2lsbCBjaGVjayBpZiBvdXIgZGF0YXNlIGNvbnRhaW5zIGFueSBzcGVjaWFsIHZhbHVlcyBzdWNoIGFzICoqTkFOKiosKippbmZpbml0ZSB2YWx1ZXMqKiBldGMuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmlzLnNwZWNpYWwgPC0gZnVuY3Rpb24oeCl7IGlmIChpcy5udW1lcmljKHgpKSAoaXMuaW5maW5pdGUoeCkgfCBpcy5uYW4oeCkpfQ0Kc2FwcGx5KGltZGIsIGZ1bmN0aW9uKHgpIHN1bSggaXMuc3BlY2lhbCh4KSkpDQpgYGANCg0KIyMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvZiBkYXRhc2V0Lg0KDQpBcyB3ZSBjYW4gc2VlIHRoYXQgb3VyIGRhdGFzZXQgZG9lc24ndCBjb250YWluIGFueSBzcGVjaWFsIHZhbHVlcy4gTm93LCB3ZSB3aWxsIG1vdmUgZm9yd2FyZCB0byBzZWUgc29tZSBkZXNjcmlwdGl2ZSBzdGF0cyBvZiBvdXIgZGF0YXNldC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KZ2xpbXBzZShpbWRiKQ0KY2xhc3MoaW1kYikNCiBpbWRiICU+JSBzdW1tYXJ5KCkgICANCmBgYA0KDQpGcm9tIGFib3ZlLCB3ZSBjYW4gc2VlIHRoYXQgb3VyIGRhdGFzZXQgaGF2ZSAqKjE0NDAgcm93cyBhbmQgMTIgY29sdW1ucyoqIGFuZCBhIHN1bW1hcnkgb2Ygb3VyIGVudGlyZSBkYXRhc2V0IGlzIHByZXNlbnRlZCBhYm92ZS4NCg0KIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gUmVncmVzc29yIGFuZCBSZXNwb25zZSBWYXJpYWJsZS4NCg0KSGVyZSwgd2UgYXJlIHNob3dpbmcgYSBwb3NzaWJsZSByZWxhdGlvbnNoaXAgYmV0d2VlbiByZWdyZXNzb3IgYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSBieSBwbG90dGluZyB0aGUgZ3JhcGggYW5kIGNvcnJlbGF0aW9uLg0KDQpUaGUgZmlyc3QgZ3JhcGggd2UgYXJlIHdpdG5lc3NpbmcgaXMgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb3VyIFJlc3BvbnNlIHZhcmlhYmxlICoqU2NvcmUqKiBhbmQgcmVncmVzc29yICoqTW92aWUgUmV2ZW51ZSoqLiBBcyB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgaXMgbm90IGEgY2xlYXIgYnV0IHZpc2libGUgcG9zaXRpdmUgbGluZWFyIHRyZW5kIGJldHdlZW4gb3VyIFJlZ3Jlc3NvciBhbmQgcmVzcG9uc2UgdmFyaWFibGUuIA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCiMgcmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMNCmZpZzEgPC0gcGxvdF9seShkYXRhID0gaW1kYiwgeCA9IH5TY29yZSwgeSA9IH5pbWRiJGBNb3ZpZSBSZXZlbnVlIChNJClgKQ0KZmlnMQ0KYGBgDQpMZXQncyBjb25maXJtIG91ciByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZSBieSBwZXJmb3JtaW5nICoqY29ycmVsYXRpb24qKi4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KY29ybW92aWUgPC0gY29yLnRlc3QoaW1kYiRTY29yZSwgaW1kYiRgTW92aWUgUmV2ZW51ZSAoTSQpYCwgDQogICAgICAgICAgICAgICAgbWV0aG9kID0gInBlYXJzb24iKQ0KY29ybW92aWUNCmBgYA0KDQpGcm9tICoqY29ycmVsYXRpb24qKiB3ZSBzZWUgdGhhdDoNCg0KVCB2YWx1ZSBpcyAtMTQuNzU0KHQtdGVzdCBzdGF0aXN0aWMpDQoNCmRmIGlzIDE0MzgoZGVncmVlcyBvZiBmcmVlZG9tKQ0KDQpwLXZhbHVlIGlzIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgb2YgdGhlIHQtdGVzdCAocC12YWx1ZSA8IDIuMl5bLTE2XSkuDQoNCmNvbmYuaW50IGlzIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBhdCA5NSUgKGNvbmYuaW50ID0gWy0wLjQwNjYsIC0wLjMxNjhdKS4NCg0Kc2FtcGxlIGVzdGltYXRlcyBpcyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgKENvci5jb2VmZiA9IC0wLjM2KS4NCg0KU28sIHdlIGNhbiBzYXkgdGhhdCBvdXIgcmVncmVzc29yIGFuZCByZXNwb25zZSB2YXJpYWJsZSBhcmUgc29tZXdoZXJlIHNob3dzIGNvcnJlbGF0aW9uIGJ1dCBpcyBub3QgdmVyeSBzdHJvbmcuDQogDQogDQpOb3csIHNlY29uZCB3ZSB3aWxsIHNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICoqTWV0YXNjb3JlKiogYW5kICoqU2NvcmUqKiB2YXJpYWJsZSBieSBkaXNwbGF5aW5nIGEgc2NhdHRlciBwbG90Lg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPX0NCmZpZzIgPC0gcGxvdF9seShkYXRhID0gaW1kYiwgeCA9IH5TY29yZSwgeSA9IH5pbWRiJE1ldGFzY29yZSkNCmZpZzINCmBgYA0KVGhlIFNlY29uZCBncmFwaCB3ZSBhcmUgd2l0bmVzc2luZyBpcyBzaG93aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvdXIgUmVzcG9uc2UgdmFyaWFibGUgKipTY29yZSoqIGFuZCByZWdyZXNzb3IgKipNZXRhc2NvcmUqKi4gQXMgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIG5vdCBhIGNsZWFyIGJ1dCB2aXNpYmxlIHBvc2l0aXZlIGxpbmVhciB0cmVuZCBiZXR3ZWVuIG91ciBSZWdyZXNzb3IgYW5kIHJlc3BvbnNlIHZhcmlhYmxlLiANCg0KTGV0J3MgY29uZmlybSBvdXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFibGUgYnkgcGVyZm9ybWluZyAqKmNvcnJlbGF0aW9uKiouDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KY29ybWV0YSA8LSBjb3IudGVzdChpbWRiJFNjb3JlLCBpbWRiJE1ldGFzY29yZSwgDQogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIpDQpjb3JtZXRhDQpgYGANCkZyb20gKipjb3JyZWxhdGlvbioqIHdlIHNlZSB0aGF0Og0KDQpUIHZhbHVlIGlzIDE4LjUyOSh0LXRlc3Qgc3RhdGlzdGljKQ0KDQpkZiBpcyAxNDM4KGRlZ3JlZXMgb2YgZnJlZWRvbSkNCg0KcC12YWx1ZSBpcyB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mIHRoZSB0LXRlc3QgKHAtdmFsdWUgPCAyLjJeWy0xNl0pLg0KDQpjb25mLmludCBpcyB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYXQgOTUlIChjb25mLmludCA9IFswLjM5NjMsIDAuNDc5N10pLg0KDQpzYW1wbGUgZXN0aW1hdGVzIGlzIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCAoQ29yLmNvZWZmID0gMC40MzkwKS4NCg0KU28sIHdlIGNhbiBzYXkgdGhhdCBvdXIgcmVncmVzc29yIGFuZCByZXNwb25zZSB2YXJpYWJsZSBhcmUgc29tZXdoZXJlIHNob3dzIGNvcnJlbGF0aW9uIGJ1dCBpcyBub3QgdmVyeSBzdHJvbmcuDQogDQogDQpOb3csIHdlIGFyZSBnb2luZyB0byByZXByZXNlbnQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICoqUmVzdHJpY3Rpb24qKiBhbmQgKipTY29yZSoqIHZhcmlhYmxlIGJ5IHBsb3R0aW5nIGEgYm94cGxvdC4NCiANCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KZmlnMyA8LSBwbG90X2x5KGRhdGEgPSBpbWRiLCB4ID0gflJlc3RyaWN0aW9uLCB5ID0gflNjb3JlLCB0eXBlID0gImJveCIpDQpmaWczIDwtIGZpZzMgJT4lIGxheW91dChib3htb2RlID0gImdyb3VwIikNCmZpZzMNCmBgYA0KDQpGcm9tIHRoZSBhYm92ZSBib3hwbG90LCB3ZSBjYW4gc2VlIHRoZSBhdmVyYWdlIFNjb3JlIG9mIHRoZSBtb3ZpZSBhcyBwZXIgZGlmZmVyZW50IGFnZSByZXRyaWN0aW9ucyBzdWNoIGFzIGZvciBBZ2UgZ3JvdXAgb2YgMTIsIHRoZSBhdmVyYWdlIHNjb3JlIGlzIDguNSB3aGlsZSBmb3IgQWdlIGdyb3VwIG9mIDE2IHRoZSBhdmVyYWdlIHNjb3JlIGlzIDguNjUgYW5kIHNvIG9uLg0KDQojIFJlZ3Jlc3Npb24gTW9kZWwgRml0dGluZzoNCg0KTm93LCB3ZSB3aWxsIGZpdCB2YXJpb3VzIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIHBlcmZvcm0gdGVzdHMgdG8gY2hlY2sgdGhlIGFzc3VtcHRpb25zIGFuZCB0cmFuc2Zvcm0gdGhlIGRhdGEuDQoNCkZpcnN0LCB3ZSBhcmUgZml0dGluZyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBvbiBvdXIgKippbWRiKiogRGF0YXNldC4NCg0KIyMgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KTW9kZWwxIDwtIGxtKFNjb3JlfiBpbWRiJGBNb3ZpZSBEYXRlYCtpbWRiJGBOdW1iZXIgb2YgVm90ZXNgK2ltZGIkYE1vdmllIFJldmVudWUgKE0kKWANCiAgICAgICAgICAgICAgICAgICAgK2ltZGIkTWV0YXNjb3JlK2ltZGIkYFRpbWUgRHVyYXRpb24gKG1pbilgLCBpbWRiKQ0Kc3VtbWFyeShNb2RlbDEpDQpgYGANCg0KSGVyZSwgd2UgaGF2ZSBmaXR0ZWQgYSBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBtb2RlbCB3aXRoIGZpdmUgdmFyaWFibGVzIGFuZCBuYW1lIHRoZSBtb2RlbCBhcyAqKk11bHRpcGxlIE1vZGVsKiouIFdlIGNhbiBzZWUgdGhhdCAqKk11bHRpcGxlIFIyKiogdmFsdWUgaXMgMC44OTQzIGFuZCAqKkFkaiBSMioqIFZhbHVlIGlzIDAuODkzOS4NCldlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBQLXZhbHVlIGlzIHNtYWxsZXIgdGhlbiAwLjA1LCB0aGVyZWZvcmUgd2UgY2FuIHNheSB0aGF0IHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudCBhbmQgd2UgY2FuIHJlamVjdCBIMC4gDQoNClRvIGNoZWNrIHRoZSBvdmVyYWxsIGZpdCwgd2Ugd2lsbCB1c2UgKipBbm92YSoqIGZ1bmN0aW9uLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQphbm92YShNb2RlbDEpDQpgYGANCg0KRnJvbSAqKkFub3ZhKiogVGVzdCwgd2UgZm91bmQgb3V0IHRoYXQgUCB2YWx1ZSBmb3IgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIDAuMDAxIHdoaWNoIGlzIGxlc3MgdGhhbiAwLjAwNS4gVGhlcmVmb3JlLHdlIGNhbiBzYXkgdGhhdCBhbGwgdGhlIHZhcmlhYmxlcyBhcmUgc3RhdGlzY2FsbHkgc2lnbmlmaWNhbnQgYW5kIHRoZSBtb2RlbCBpcyBhIGdvb2QgZml0Lg0KDQpOb3csIHdlIHdpbGwgZGlzcGxheSB0aGUgZ3JhcGhzIHRvIHRlc3QgdGhlIGFzc3VtcHRpb25zLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpwYXIobWZyb3cgPSBjKDIsMikpDQpwbG90KE1vZGVsMSkNCmBgYA0KDQp3ZSBjYW4gc2VlIDQgRGlhZ25vc3RpYyBwbG90cyBrbm93biBhcyBSZXNpZHVhbHMgcGxvdCwgUVEgcGxvdHMsIFNjYWxlLWxvY2F0aW9uIHBsb3QgYW5kIGxldmVyYWdlIHBsb3RzLg0KDQpJbiB0aGUgMXN0IGdyYXBoIHdlIGNhbiBzZWUgdGhhdCBvbiBYIGF4aXMgaGFzIHByZWRpY3RlZCB2YWx1ZSBrbm93biBhcyBZXih5IGhhdCkgYW5kIG9uIHkgYXhpcyB0aGVyZSBhcmUgcmVzaWR1YWxzIGFuZCBlcnJvcnMuIEhlcmUgd2UgY2FuIHRoZSBsaW5lIGlzIG5vdCBmbGF0IGFuZCB0aGUgcG9pbnRzIGFyZSBsb29raW5nIGFzIGEgYnVuY2ggb2YgY2xvdWRzIHdoaWNoIG1lYW5zIHRoYXQgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQgaGVyZS4NCg0KSW4gdGhlIG5leHQgR3JhcGggKFFRIHBsb3QpLCBoZXJlIHkgYXhpcyBpcyBvcmRlcmVkLCBvYnNlcnZlZCBhbmQgU3RhbmRhcmRpemVkIHJlc2lkdWFscyBhbmQgb24gWCBheGlzIGl0IGhhcyBvcmRlcmVkIHRoZW9yZXRpY2FsIHJlc2lkdWFscy4gSW4gdGhlIGdyYXBoIHdlIGNhbiBzZWUgdGhhdCByZXNpZHVhbHMgYXJlIHRydWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIG1vc3RseSBwb2ludHMgYXJlIGZhbGxpbmcgb24gdGhlIGxpbmUgYnV0IHNvbWUgd2VyZSBmYWxsaW5nIG91dCBvZiBsaW5lIGluIHRoZSBlbmQuDQoNClRoZSBuZXh0IHR3byBzaG93cyB0aGF0IHRoZSByZWdyZXNzaW9uIGlzIG5vbi1saW5lYXIsIG5vbi1jb25zdGFudCB2YXJpYW5jZS4NCg0KVG8gdGVzdCBmdXJ0aGVyLCBpZiBhbnkgYXNzdW1wdGlvbnMgYXJlIHZpb2xhdGVkIG9yIG5vdC4gV2Ugd2lsbCBwZXJmb3JtIHNvbWUgdGVzdHMuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmR1cmJpbldhdHNvblRlc3QoTW9kZWwxKQ0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIHVuY29ycmVsYXRlZA0KDQpIMTogRXJyb3JzIGFyZSBjb3JyZWxhdGVkDQoNCkZyb20gdGhlIER1cmJpbiBXYXN0b24gdGVzdCwgU2luY2UgdGhlIHAtdmFsdWUgaXMgPCAwLjA1IHdlIGhhdmUgZW5vdWdoIGV2aWRlbmNlIHRvIHJlamVjdCBIMC4gVGhpcyBpbXBsaWVzIHRoYXQgdW5jb3JyZWxhdGVkIGVycm9yIGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnNoYXBpcm8udGVzdChNb2RlbDEkcmVzaWR1YWxzKQ0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIA0KDQpIMTogRXJyb3JzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KRnJvbSB0aGUgU2hhcGlyby13aWxrIG5vcm1hbGl0eSB0ZXN0LCBTaW5jZSB0aGUgcC12YWx1ZSBpcyA8IDAuMDUgd2UgaGF2ZSBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IEgwLiBUaGlzIGltcGxpZXMgdGhhdCBub3JtYWxpdHkgZXJyb3IgYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KbmN2VGVzdChNb2RlbDEpDQpgYGANCg0KSDA6IEVycm9ycyBoYXZlIGEgY29uc3RhbnQgdmFyaWFuY2UgDQoNCkgxOiBFcnJvcnMgaGF2ZSBhIG5vbi1jb25zdGFudCB2YXJpYW5jZQ0KDQpIZXJlLCB3ZSBjYW4gc2VlIHRoYXQgcCB2YWx1ZSBpcyBsZXNzIHRoZW4gMC4wNS4gdGhlcmVmb3JlLCB3ZSBjYW4gc2F5IHRoYXQgdGhlIGNvbnN0YW50IHZhcmlhbmNlIGFzc3VtcHRpb25zIGlzIHZpb2xhdGVkIGhlcmUuIA0KDQojIyBPYnNlcnZhdGlvbiBvZiBNdWx0aXBsZSBNb2RlbC4NCkFmdGVyIHBlcmZvcm1pbmcgdGhlIHRlc3QsIHdlIGZvdW5kIG91dCB0aGF0IGZldyBhc3N1bXB0aW9ucyB3ZXJlIHZpb2xhdGVkIGhlcmUuIFNvLCB3ZSB3aWxsIHRyeSB0byB0cmFuc2Zvcm0gdGhlIGRhdGEgdXNpbmcgKipMb2cgVHJhbnNmb3JtYXRpb24qKi4NCg0KDQojIyBUcmFuc2Zvcm1hdGlvbiB2aWEgTG9nOg0KDQpXZSB3aWxsIG1ha2UgYSBuZXcgbW9kZWwgYW5kIG5hbWUgaXQgYXMgKipsb2cxKiogYW5kIGZpdCB0aGUgdHJhbnNmb3JtZWQgdmFsdWUuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCiMgbG9nIHRyYW5zZm9ybWF0aW9uDQpsb2cxIDwtbG0obG9nKGltZGIkU2NvcmUpfiBpbWRiJGBNb3ZpZSBEYXRlYCtpbWRiJGBOdW1iZXIgb2YgVm90ZXNgK2ltZGIkYE1vdmllIFJldmVudWUgKE0kKWANCiAgICAgICAgICAgICtpbWRiJE1ldGFzY29yZStpbWRiJGBUaW1lIER1cmF0aW9uIChtaW4pYCwgaW1kYikgICAgIA0Kc3VtbWFyeShsb2cxKQ0KYGBgDQoNCkhlcmUsIHdlIGhhdmUgZml0dGVkIGEgdHJhbnNmb3JtZWQgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBmaXZlIHZhcmlhYmxlcyBhbmQgbmFtZSB0aGUgbW9kZWwgYXMgKipsb2cxKiogLiBXZSBjYW4gc2VlIHRoYXQgKipNdWx0aXBsZSBSMioqIHZhbHVlIGlzIDAuODg3NSBhbmQgKipBZGogUjIqKiBWYWx1ZSBpcyAwLjg4NzENCldlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBQLXZhbHVlIGlzIHNtYWxsZXIgdGhlbiAwLjA1LCB0aGVyZWZvcmUgd2UgY2FuIHNheSB0aGF0IHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudCBhbmQgd2UgY2FuIHJlamVjdCBIMC4gDQoNClRvIGNoZWNrIHRoZSBvdmVyYWxsIGZpdCwgd2Ugd2lsbCB1c2UgKipBbm92YSoqIGZ1bmN0aW9uLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQphbm92YShsb2cxKQ0KYGBgDQoNCkZyb20gKipBbm92YSoqIFRlc3QsIHdlIGZvdW5kIG91dCB0aGF0IFAgdmFsdWUgZm9yIGFsbCB0aGUgdmFyaWFibGVzIGFyZSAwLjAwMSB3aGljaCBpcyBsZXNzIHRoYW4gMC4wMDUuIFRoZXJlZm9yZSx3ZSBjYW4gc2F5IHRoYXQgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIHN0YXRpc2NhbGx5IHNpZ25pZmljYW50IGFuZCB0aGUgbW9kZWwgaXMgYSBnb29kIGZpdC4NCg0KTm93LCB3ZSB3aWxsIGRpc3BsYXkgdGhlIGdyYXBocyB0byB0ZXN0IHRoZSBhc3N1bXB0aW9ucy4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChsb2cxKQ0KYGBgDQoNCkFmdGVyIHRoZSB0cmFuc2Zvcm1hdGlvbiwgd2UgY2FuIHNheSB0aGF0Og0KDQpJbiB0aGUgMXN0IGdyYXBoIHdlIGNhbiBzZWUgdGhhdCBvbiBYIGF4aXMgaGFzIHByZWRpY3RlZCB2YWx1ZSBrbm93biBhcyBZXih5IGhhdCkgYW5kIG9uIHkgYXhpcyB0aGVyZSBhcmUgcmVzaWR1YWxzIGFuZCBlcnJvcnMuIEhlcmUgd2UgY2FuIHRoZSBsaW5lIGlzIG5vdCBmbGF0IGFuZCB0aGUgcG9pbnRzIGFyZSBsb29raW5nIGFzIGEgYnVuY2ggb2YgY2xvdWRzIHdoaWNoIG1lYW5zIHRoYXQgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgc3RpbGwgdmlvbGF0ZWQgaGVyZS4NCg0KSW4gdGhlIG5leHQgR3JhcGggKFFRIHBsb3QpLCBoZXJlIHkgYXhpcyBpcyBvcmRlcmVkLCBvYnNlcnZlZCBhbmQgU3RhbmRhcmRpemVkIHJlc2lkdWFscyBhbmQgb24gWCBheGlzIGl0IGhhcyBvcmRlcmVkIHRoZW9yZXRpY2FsIHJlc2lkdWFscy4gSW4gdGhlIGdyYXBoIHdlIGNhbiBzZWUgdGhhdCByZXNpZHVhbHMgYXJlIHRydWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIG1vc3RseSBwb2ludHMgYXJlIGZhbGxpbmcgb24gdGhlIGxpbmUgYnV0IHNvbWUgd2VyZSBmYWxsaW5nIG91dCBvZiBsaW5lIGluIHRoZSBlbmQuIEl0IGlzIGFsbW9zdCBzYW1lIGFzIGJlZm9yZSB0cmFuc2Zvcm1hdGlvbi4NCg0KVGhlIG5leHQgdHdvIHNob3dzIHRoYXQgdGhlIHJlZ3Jlc3Npb24gaXMgbm9uLWxpbmVhciwgbm9uLWNvbnN0YW50IHZhcmlhbmNlLg0KVG8gdGVzdCBmdXJ0aGVyLCBpZiBhbnkgYXNzdW1wdGlvbnMgYXJlIHZpb2xhdGVkIG9yIG5vdC4gV2Ugd2lsbCBwZXJmb3JtIHNvbWUgdGVzdHMuDQoNClRvIFRlc3QgZm9yIEF1dG8gY29ycmVsYXRlZCBFcnJvcnMsIHdlIHdpbGwgcGVyZm9ybSAqKkRXLXRlc3QqKg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpkdXJiaW5XYXRzb25UZXN0KGxvZzEpDQpgYGANCg0KDQpIMDogRXJyb3JzIGFyZSB1bmNvcnJlbGF0ZWQNCg0KSDE6IEVycm9ycyBhcmUgY29ycmVsYXRlZA0KDQpGcm9tIHRoZSBEdXJiaW4gV2FzdG9uIHRlc3QsIFNpbmNlIHRoZSBwLXZhbHVlIGlzIDwgMC4wNSB3ZSBoYXZlIGVub3VnaCBldmlkZW5jZSB0byByZWplY3QgSDAuIFRoaXMgaW1wbGllcyB0aGF0IHVuY29ycmVsYXRlZCBlcnJvciBhc3N1bXB0aW9uIGlzIHZpb2xhdGVkLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpzaGFwaXJvLnRlc3QobG9nMSRyZXNpZHVhbHMpDQpgYGANCg0KSDA6IEVycm9ycyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgDQoNCkgxOiBFcnJvcnMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZA0KDQpGcm9tIHRoZSBTaGFwaXJvLXdpbGsgbm9ybWFsaXR5IHRlc3QsIFNpbmNlIHRoZSBwLXZhbHVlIGlzIDwgMC4wNSB3ZSBoYXZlIGVub3VnaCBldmlkZW5jZSB0byByZWplY3QgSDAuIA0KDQpUaGlzIGltcGxpZXMgdGhhdCBub3JtYWxpdHkgZXJyb3IgYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KbmN2VGVzdChsb2cxKQ0KYGBgDQoNCkgwOiBFcnJvcnMgaGF2ZSBhIGNvbnN0YW50IHZhcmlhbmNlIA0KDQpIMTogRXJyb3JzIGhhdmUgYSBub24tY29uc3RhbnQgdmFyaWFuY2UNCg0KSGVyZSwgd2UgY2FuIHNlZSB0aGF0IHAgdmFsdWUgaXMgbGVzcyB0aGVuIDAuMDUuIHRoZXJlZm9yZSwgd2UgY2FuIHNheSB0aGF0IHRoZSBjb25zdGFudCB2YXJpYW5jZSBhc3N1bXB0aW9ucyBpcyBzdGlsbCB2aW9sYXRlZCBoZXJlLiANCg0KIyMgQ29tbWVudHM6DQoNCkFzIHdlIGNhbiBzZWUgdGhhdCBiZWZvcmUgdHJhbnNmb3JtYXRpb24sIGZldyBhc3N1bXB0aW9ucyB3ZXJlIHZpb2xhdGVkLiBTbyB3ZSB0cmFuc2Zvcm0gdGhlIGRhdGEgdXNpbmcgKipsb2cqKiB0cmFuc2Zvcm1hdGlvbiBhbmQgYWZ0ZXIgdHJhbnNmb3JtYXRpb24gYWxzbywgdGhlc2UgYXNzdW1wdGlvbnMgd2VyZSBzdGlsbCB2aW9sYXRlZC4NCg0KDQojIyBTdGVwd2lzZSBSZWdyZXNzaW9uLg0KDQpOb3csIHdlIHdpbGwgcGVyZm9ybSBhbm90aGVyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgb24gb3VyIGRhdGFzZXQgbmFtZWQgKipTdGVwd2lzZSBSZWdyZXNzaW9uKioNCkJlbG93IHdlIGhhdmUgZml0dGVkIGEgc3RlcHdpc2UgbW9kZWwuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnN0ZXAoTW9kZWwxLCBkYXRhPWltZGIsIGRpcmVjdGlvbj0iYm90aCIpDQpgYGANCkFmdGVyIHBlcmZvcm1pbmcgdGhlIHN0ZXAgd2lzZSByZWdyZXNzaW9uIG1vZGVsLCB3ZSBmb3VuZCBvdXQgdGhhdCBBSUMgaXMgLTYzOTYuNTkgYW5kIG91ciBmaXJzdCBtb2RlbCBpcyB0aGUgYmVzdCBmaXR0ZWQgbW9kZWwgd2l0aCB0aGUgbG93ZXN0IEFJQy4gVGhlIHZhcmlhYmxlcyBpbmNsdWRlZCBpbiB0aGF0IG1vZGVsIHdpdGggdGhlaXIgY29lZmZpY2llbnRzIGFyZToNCg0KLSoqSW50ZXJjZXB0OiAzLjAxNCoqDQoNCi0qKk1vdmllIERhdGE6IC0xLjE1NCoqDQoNCi0qKk51bWJlciBvZiB2b3Rlczo2LjU3MioqDQoNCi0qKk1vdmllIFJldmVudWU6IC03LjkzNSoqDQoNCi0qKk1ldGFzY29yZTo3LjA4NyoqDQoNCi0qKlRpbWUgRHVyYXRpb246MS40NjEqKi4NCg0KQWZ0ZXIgZXZhbHVhdGluZyBvdXIgYmVzdCBmaXQgbW9kZWwsIHdlIGZpdCB0aGUgbW9kZWwgYW5kIHBlcmZvcm0gYW5hbHlzaXMgb24gaXQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCk1vZGVsMiA9IGxtKFNjb3JlIH4gTWV0YXNjb3JlICsgaW1kYiRgTW92aWUgUmV2ZW51ZSAoTSQpYCArIA0KICAgICAgICAgICAgICAgaW1kYiRgTnVtYmVyIG9mIFZvdGVzYCArIGltZGIkYE1vdmllIERhdGVgK2ltZGIkYFRpbWUgRHVyYXRpb24gKG1pbilgLCBkYXRhID0gaW1kYikNCnN1bW1hcnkoTW9kZWwyICkNCmBgYA0KDQoNCkhlcmUsIHdlIGhhdmUgZml0dGVkIGEgQUlDIG1vZGVsIHdpdGggdGhlIGJlc3QgbW9kZWwuIFdlIGNhbiBzZWUgdGhhdCAqKk11bHRpcGxlIFIyKiogdmFsdWUgaXMgMC44OTQzIGFuZCAqKkFkaiBSMioqIFZhbHVlIGlzIDAuODkzOS4NCldlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBQLXZhbHVlIGlzIHNtYWxsZXIgdGhlbiAwLjA1LCB0aGVyZWZvcmUgd2UgY2FuIHNheSB0aGF0IHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudCBhbmQgd2UgY2FuIHJlamVjdCBIMC4gDQoNClRvIGNoZWNrIHRoZSBvdmVyYWxsIGZpdCwgd2Ugd2lsbCB1c2UgKipBbm92YSoqIGZ1bmN0aW9uLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQphbm92YShNb2RlbDIpDQpgYGANCkZyb20gQW5vdmEgVGVzdCwgd2UgZm91bmQgb3V0IHRoYXQgUCB2YWx1ZSBmb3IgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIDAuMDAxIHdoaWNoIGlzIGxlc3MgdGhhbiAwLjAwNS4gVGhlcmVmb3JlLHdlIGNhbiBzYXkgdGhhdCBhbGwgdGhlIHZhcmlhYmxlcyBhcmUgc3RhdGlzY2FsbHkgc2lnbmlmaWNhbnQgYW5kIHRoZSBtb2RlbCBpcyBhIGdvb2QgZml0Lg0KDQpOb3csIHdlIHdpbGwgY2hlY2sgaWYgYW55IGFzc3VtcHRpb25zIGlzIHZpb2xhdGVkIG9yIG5vdC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoTW9kZWwyICkNCmBgYA0KDQp3ZSBjYW4gc2VlIDQgRGlhZ25vc3RpYyBwbG90cyBrbm93biBhcyBSZXNpZHVhbHMgcGxvdCwgUVEgcGxvdHMsIFNjYWxlLWxvY2F0aW9uIHBsb3QgYW5kIGxldmVyYWdlIHBsb3RzLg0KSW4gdGhlIDFzdCBncmFwaCB3ZSBjYW4gc2VlIHRoYXQgb24gWCBheGlzIGhhcyBwcmVkaWN0ZWQgdmFsdWUga25vd24gYXMgWV4oeSBoYXQpIGFuZCBvbiB5IGF4aXMgdGhlcmUgYXJlIHJlc2lkdWFscyBhbmQgZXJyb3JzLiBIZXJlIHdlIGNhbiB0aGUgbGluZSBpcyBub3QgZmxhdCBhbmQgdGhlIHBvaW50cyBhcmUgbG9va2luZyBhcyBhIGJ1bmNoIG9mIGNsb3VkcyB3aGljaCBtZWFucyB0aGF0IGxpbmVhcml0eSBhc3N1bXB0aW9uIGlzIHZpb2xhdGVkIGhlcmUuDQoNCkluIHRoZSBuZXh0IEdyYXBoIChRUSBwbG90KSwgaGVyZSB5IGF4aXMgaXMgb3JkZXJlZCwgb2JzZXJ2ZWQgYW5kIFN0YW5kYXJkaXplZCByZXNpZHVhbHMgYW5kIG9uIFggYXhpcyBpdCBoYXMgb3JkZXJlZCB0aGVvcmV0aWNhbCByZXNpZHVhbHMuIEluIHRoZSBncmFwaCB3ZSBjYW4gc2VlIHRoYXQgcmVzaWR1YWxzIGFyZSB0cnVseSBub3JtYWxseSBkaXN0cmlidXRlZCBhcyBtb3N0bHkgcG9pbnRzIGFyZSBmYWxsaW5nIG9uIHRoZSBsaW5lIGJ1dCBzb21lIHdlcmUgZmFsbGluZyBvdXQgb2YgbGluZSBpbiB0aGUgZW5kLg0KDQpUaGUgbmV4dCB0d28gc2hvd3MgdGhhdCB0aGUgcmVncmVzc2lvbiBpcyBub24tbGluZWFyLCBub24tY29uc3RhbnQgdmFyaWFuY2UuDQpUbyB0ZXN0IGZ1cnRoZXIsIGlmIGFueSBhc3N1bXB0aW9ucyBhcmUgdmlvbGF0ZWQgb3Igbm90LiBXZSB3aWxsIHBlcmZvcm0gc29tZSB0ZXN0cy4NCg0KTm93IHRvIG1vdmUgZnVydGhlciBpbiBvdXIgQW5hbHlzaXMsIHdlIHdpbGwgcGVyZm9ybSBzb21lIHN0YXRpc2NhbCB0ZXN0Lg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpuY3ZUZXN0KE1vZGVsMiApDQpgYGANCkgwOiBFcnJvcnMgaGF2ZSBhIGNvbnN0YW50IHZhcmlhbmNlIA0KDQpIMTogRXJyb3JzIGhhdmUgYSBub24tY29uc3RhbnQgdmFyaWFuY2UNCg0KSGVyZSwgd2UgY2FuIHNlZSB0aGF0IHAgdmFsdWUgaXMgbGVzcyB0aGVuIDAuMDUuIHRoZXJlZm9yZSwgd2UgY2FuIHNheSB0aGF0IHRoZSBjb25zdGFudCB2YXJpYW5jZSBhc3N1bXB0aW9ucyBpcyB2aW9sYXRlZCBoZXJlLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpkdXJiaW5XYXRzb25UZXN0KE1vZGVsMikNCmBgYA0KDQpIMDogRXJyb3JzIGFyZSB1bmNvcnJlbGF0ZWQNCg0KSDE6IEVycm9ycyBhcmUgY29ycmVsYXRlZA0KDQpGcm9tIHRoZSBEdXJiaW4gV2FzdG9uIHRlc3QsIFNpbmNlIHRoZSBwLXZhbHVlIGlzIDwgMC4wNSB3ZSBoYXZlIGVub3VnaCBldmlkZW5jZSB0byByZWplY3QgSDAuIFRoaXMgaW1wbGllcyB0aGF0IHVuY29ycmVsYXRlZCBlcnJvciBhc3N1bXB0aW9uIGlzIHZpb2xhdGVkLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpzaGFwaXJvLnRlc3QoTW9kZWwyICRyZXNpZHVhbHMpIA0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIA0KDQpIMTogRXJyb3JzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KRnJvbSB0aGUgU2hhcGlyby13aWxrIG5vcm1hbGl0eSB0ZXN0LCBTaW5jZSB0aGUgcC12YWx1ZSBpcyA8IDAuMDUgd2UgaGF2ZSBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IEgwLiBUaGlzIGltcGxpZXMgdGhhdCBub3JtYWxpdHkgZXJyb3IgYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4NCg0KIyMgT2JzZXJ2YXRpb24gb2YgTW9kZWwyIE1vZGVsOg0KDQpBZnRlciBwZXJmb3JtaW5nIHRoZSB0ZXN0LCB3ZSBmb3VuZCBvdXQgdGhhdCBmZXcgYXNzdW1wdGlvbnMgYXJlIHZpb2xhdGVkIGhlcmUuIFNvLCB3ZSB3aWxsIHRyeSB0byB0cmFuc2Zvcm0gdGhlIGRhdGEgdXNpbmcgKipTcXVhcmUtcm9vdCBUcmFuc2Zvcm1hdGlvbioqLg0KDQojIyBTcXJ0IFRyYW5zZm9ybWF0aW9uOg0KDQpXZSB3aWxsIG1ha2UgYSBuZXcgbW9kZWwgYW5kIG5hbWUgaXQgYXMgKipzcXJ0KiogYW5kIGZpdCB0aGUgdHJhbnNmb3JtZWQgdmFsdWUuIFdlIHdpbGwgdHJhbnNmb3JtIGJvdGggdGhlIHZhbHVlcyBvZiBSZWdyZXNzb3IgYW5kIHByZWRpY3Rvci4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KI3NxcnQgVHJhbnNmb3JtYXRpb24NCnNxcnQgPC0gbG0oc3FydChTY29yZSkgfiAoTWV0YXNjb3JlICsgaW1kYiRgTW92aWUgUmV2ZW51ZSAoTSQpYCArIA0KICAgICAgICAgICAgIGltZGIkYE51bWJlciBvZiBWb3Rlc2AgKyBpbWRiJGBNb3ZpZSBEYXRlYCtpbWRiJGBUaW1lIER1cmF0aW9uIChtaW4pYCksIGRhdGEgPSBpbWRiKQ0Kc3VtbWFyeShzcXJ0KQ0KYGBgDQoNCkhlcmUsIHdlIGhhdmUgZml0dGVkIG91ciB0cmFuc2Zvcm1lZCBtb2RlbC4gV2UgY2FuIHNlZSB0aGF0ICoqTXVsdGlwbGUgUjIqKiB2YWx1ZSBpcyAwLjg5MSBhbmQgKipBZGogUjIqKiBWYWx1ZSBpcyAwLjg5MDYNCg0KV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIFAtdmFsdWUgaXMgc21hbGxlciB0aGVuIDAuMDUsIHRoZXJlZm9yZSB3ZSBjYW4gc2F5IHRoYXQgdGhlIG1vZGVsIGlzIHNpZ25pZmljYW50IGFuZCB3ZSBjYW4gcmVqZWN0IEgwLiANCg0KVG8gY2hlY2sgdGhlIG92ZXJhbGwgZml0LCB3ZSB3aWxsIHVzZSAqKkFub3ZhKiogZnVuY3Rpb24uDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmFub3ZhKHNxcnQpDQpgYGANCg0KRnJvbSBBbm92YSBUZXN0LCB3ZSBmb3VuZCBvdXQgdGhhdCBQIHZhbHVlIGZvciBhbGwgdGhlIHZhcmlhYmxlcyBhcmUgMC4wMDEgd2hpY2ggaXMgbGVzcyB0aGFuIDAuMDA1LiBUaGVyZWZvcmUsd2UgY2FuIHNheSB0aGF0IGFsbCB0aGUgdmFyaWFibGVzIGFyZSBzdGF0aXNjYWxseSBzaWduaWZpY2FudCBhcyB3ZWxsIGFzIHRoZSBtb2RlbCBhbmQgdGhlIG1vZGVsIGlzIGEgZ29vZCBmaXQuDQoNCk5vdywgd2Ugd2lsbCBjaGVjayBmb3IgYXNzdW1wdGlvbnMgYnkgdmlzdWFsaXppbmcgdGhlIG1vZGVsLg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChzcXJ0KQ0KYGBgDQoNCkFmdGVyIHRoZSB0cmFuc2Zvcm1hdGlvbiwgd2UgY2FuIHNheSB0aGF0Og0KDQpJbiB0aGUgMXN0IGdyYXBoIHdlIGNhbiBzZWUgdGhhdCBvbiBYIGF4aXMgaGFzIHByZWRpY3RlZCB2YWx1ZSBrbm93biBhcyBZXih5IGhhdCkgYW5kIG9uIHkgYXhpcyB0aGVyZSBhcmUgcmVzaWR1YWxzIGFuZCBlcnJvcnMuIEhlcmUgd2UgY2FuIHRoZSBsaW5lIGlzIG5vdCBmbGF0IGFuZCB0aGUgcG9pbnRzIGFyZSBsb29raW5nIGFzIGEgYnVuY2ggb2YgY2xvdWRzIHdoaWNoIG1lYW5zIHRoYXQgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgc3RpbGwgdmlvbGF0ZWQgaGVyZS4NCg0KSW4gdGhlIG5leHQgR3JhcGggKFFRIHBsb3QpLCBoZXJlIHkgYXhpcyBpcyBvcmRlcmVkLCBvYnNlcnZlZCBhbmQgU3RhbmRhcmRpemVkIHJlc2lkdWFscyBhbmQgb24gWCBheGlzIGl0IGhhcyBvcmRlcmVkIHRoZW9yZXRpY2FsIHJlc2lkdWFscy4gSW4gdGhlIGdyYXBoIHdlIGNhbiBzZWUgdGhhdCByZXNpZHVhbHMgYXJlIHRydWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIG1vc3RseSBwb2ludHMgYXJlIGZhbGxpbmcgb24gdGhlIGxpbmUgYnV0IHNvbWUgd2VyZSBmYWxsaW5nIG91dCBvZiBsaW5lIGluIHRoZSBlbmQuIEl0IGlzIGFsbW9zdCBzYW1lIGFzIGJlZm9yZSB0cmFuc2Zvcm1hdGlvbi4NCg0KVGhlIG5leHQgdHdvIHNob3dzIHRoYXQgdGhlIHJlZ3Jlc3Npb24gaXMgbm9uLWxpbmVhciwgbm9uLWNvbnN0YW50IHZhcmlhbmNlLiBUbyB0ZXN0IGZ1cnRoZXIsIGlmIGFueSBhc3N1bXB0aW9ucyBhcmUgdmlvbGF0ZWQgb3Igbm90LiBXZSB3aWxsIHBlcmZvcm0gc29tZSB0ZXN0cy4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KZHVyYmluV2F0c29uVGVzdChzcXJ0KQ0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIHVuY29ycmVsYXRlZA0KDQpIMTogRXJyb3JzIGFyZSBjb3JyZWxhdGVkDQoNCkZyb20gdGhlIER1cmJpbiBXYXN0b24gdGVzdCwgU2luY2UgdGhlIHAtdmFsdWUgaXMgPCAwLjA1IHdlIGhhdmUgZW5vdWdoIGV2aWRlbmNlIHRvIHJlamVjdCBIMC4gVGhpcyBpbXBsaWVzIHRoYXQgdW5jb3JyZWxhdGVkIGVycm9yIGFzc3VtcHRpb24gaXMgc3RpbGwgdmlvbGF0ZWQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnNoYXBpcm8udGVzdChzcXJ0JHJlc2lkdWFscykNCmBgYA0KDQpIMDogRXJyb3JzIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZA0KDQpIMTogRXJyb3JzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KRnJvbSB0aGUgU2hhcGlyby13aWxrIG5vcm1hbGl0eSB0ZXN0LCBTaW5jZSB0aGUgcC12YWx1ZSBpcyA8IDAuMDUgd2UgaGF2ZSBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IEgwLiBUaGlzIGltcGxpZXMgdGhhdCBub3JtYWxpdHkgZXJyb3IgYXNzdW1wdGlvbiBpcyAgdmlvbGF0ZWQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCm5jdlRlc3Qoc3FydCkNCmBgYA0KDQpIMDogRXJyb3JzIGhhdmUgYSBjb25zdGFudCB2YXJpYW5jZQ0KDQpIMTogRXJyb3JzIGhhdmUgYSBub24tY29uc3RhbnQgdmFyaWFuY2UNCg0KSGVyZSwgd2UgY2FuIHNlZSB0aGF0IHAgdmFsdWUgaXMgbGVzcyB0aGVuIDAuMDUuIHRoZXJlZm9yZSwgd2UgY2FuIHNheSB0aGF0IHRoZSBjb25zdGFudCB2YXJpYW5jZSBhc3N1bXB0aW9ucyBpcyB2aW9sYXRlZCBoZXJlLg0KDQojIyBDb21tZW50cw0KDQpBcyB3ZSBjYW4gc2VlIHRoYXQgYmVmb3JlIHRyYW5zZm9ybWF0aW9uLCBmZXcgYXNzdW1wdGlvbnMgd2VyZSB2aW9sYXRlZC4gU28gd2UgdHJhbnNmb3JtIHRoZSBkYXRhIHVzaW5nIHNxdWFyZS1yb290IHRyYW5zZm9ybWF0aW9uIGFuZCBhZnRlciB0cmFuc2Zvcm1hdGlvbiBhbHNvLCB0aGVzZSBhc3N1bXB0aW9ucyB3ZXJlIHN0aWxsIHZpb2xhdGVkLg0KDQojIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiB1c2luZyBpbmRpY2F0b3IgdmFyaWFibGU6DQoNCkhlcmUsIHdlIGhhdmUgdXNlZCAqKlJlc3RyaWN0aW9uKiogdmFyaWFibGVzIGFuZCBjb252ZXJ0ZWQgaXQgaW50byBmb3VyIGNhdGVnb3J5KDEsMiwzLDQpIGFuZCB0aGVuIGJ1aWxkIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gYnkgaW5jbHVkaW5nIHRoYXQgdmFyaWFibGUgYW5kIGNvbnNpZGVyaW5nIHRoYXQgYXMgaW5kaWNhdG9yIHZhcmlhYmxlIHVzaW5nICoqZmFjdG9yKiogZnVuY3Rpb24uDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KaW1kYiRSZXN0cmljdGlvbltpbWRiJFJlc3RyaWN0aW9uPT0iMTIiXTwtIjEiDQppbWRiJFJlc3RyaWN0aW9uW2ltZGIkUmVzdHJpY3Rpb249PSIxNiJdPC0iMiINCmltZGIkUmVzdHJpY3Rpb25baW1kYiRSZXN0cmljdGlvbj09IlRvdXMgcHVibGljcyJdPC0iMyINCmltZGIkUmVzdHJpY3Rpb25baW1kYiRSZXN0cmljdGlvbj09IlRvdXMgcHVibGljcyBhdmVjIGF2ZXJ0aXNzZW1lbnQiXTwtIjQiDQppbWRiJFJlc3RyaWN0aW9uIDwtIGZhY3RvcihpbWRiJFJlc3RyaWN0aW9uKQ0KYGBgDQoNCkhlcmUsIHdlIGhhdmUgYnVpbGQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGFuZCBuYW1lZCBpdCBhcyAqKk1vZGVsMyoqIGFuZCBub3cgd2Ugd2lsbCBwZXJmb3JtIG91ciBhbmFseXNpcyBvbiB0aGlzIG1vZGVsLg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCk1vZGVsMyA8LSBsbShTY29yZX4gaW1kYiRgTW92aWUgRGF0ZWAraW1kYiRgTnVtYmVyIG9mIFZvdGVzYCtpbWRiJGBNb3ZpZSBSZXZlbnVlIChNJClgDQogICAgICAgICAgICAgICAgICAgICtpbWRiJE1ldGFzY29yZStpbWRiJGBUaW1lIER1cmF0aW9uIChtaW4pYCtSZXN0cmljdGlvbiwgaW1kYikNCnN1bW1hcnkoTW9kZWwzKQ0KDQpgYGANCg0KSGVyZSwgd2UgaGF2ZSBmaXR0ZWQgb3VyIG1vZGVsIGJ5IGNvbnNpZGVyaW5nICoqcmVzdHJpY3Rpb24qKiBhcyBvdXIgaW5kaWNhdG9yIHZhcmlhYmxlLiBXZSBjYW4gc2VlIHRoYXQgTXVsdGlwbGUgUjIgdmFsdWUgaXMgMC44OTgyIGFuZCBBZGogUjIgVmFsdWUgaXMgMC44OTc2LiANCg0KV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIFAtdmFsdWUgaXMgc21hbGxlciB0aGVuIDAuMDUsIHRoZXJlZm9yZSB3ZSBjYW4gc2F5IHRoYXQgdGhlIG1vZGVsIGlzIHNpZ25pZmljYW50IGFuZCB3ZSBjYW4gcmVqZWN0IEgwLg0KDQpPbmx5IFZhcmlhYmxlICoqcmVzdHJpY3Rpb24gNCoqIGhhcyBpbnNpZ25pZmljYW50IHZhbHVlcyBhcyBpdCdzIHAtdmFsdWUgaXMgZ3JlYXRlciB0aGVuIDAuMDUsIHJlc3QgYWxsIHRoZSB2YXJpYWJsZXMgaGF2ZSBzaWduaWZpY2FudCB2YWx1ZXMuDQoNClRvIGNoZWNrIHRoZSBvdmVyYWxsIGZpdCwgd2Ugd2lsbCB1c2UgQW5vdmEgZnVuY3Rpb24uDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCmFub3ZhKE1vZGVsMykNCmBgYA0KDQpGcm9tIEFub3ZhIFRlc3QsIHdlIGZvdW5kIG91dCB0aGF0IFAgdmFsdWUgZm9yIGFsbCB0aGUgdmFyaWFibGVzIGFyZSAwLjAwMSB3aGljaCBpcyBsZXNzIHRoYW4gMC4wMDUuIFRoZXJlZm9yZSx3ZSBjYW4gc2F5IHRoYXQgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIHN0YXRpc2NhbGx5IHNpZ25pZmljYW50IGFuZCB0aGUgbW9kZWwgaXMgYSBnb29kIGZpdC4NCg0KTm93LCB3ZSB3aWxsIGNoZWNrIGZvciBhc3N1bXB0aW9ucyBieSB2aXN1YWxpemluZyB0aGUgbW9kZWwuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KE1vZGVsMykNCmBgYA0KDQpJbiB0aGUgMXN0IGdyYXBoIHdlIGNhbiBzZWUgdGhhdCBvbiBYIGF4aXMgaGFzIHByZWRpY3RlZCB2YWx1ZSBrbm93biBhcyBZXih5IGhhdCkgYW5kIG9uIHkgYXhpcyB0aGVyZSBhcmUgcmVzaWR1YWxzIGFuZCBlcnJvcnMuIEhlcmUgd2UgY2FuIHRoZSBsaW5lIGlzIG5vdCBmbGF0IGFuZCB0aGUgcG9pbnRzIGFyZSBsb29raW5nIGFzIGEgYnVuY2ggb2YgY2xvdWRzIHdoaWNoIG1lYW5zIHRoYXQgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQgaGVyZS4NCg0KSW4gdGhlIG5leHQgR3JhcGggKFFRIHBsb3QpLCBoZXJlIHkgYXhpcyBpcyBvcmRlcmVkLCBvYnNlcnZlZCBhbmQgU3RhbmRhcmRpemVkIHJlc2lkdWFscyBhbmQgb24gWCBheGlzIGl0IGhhcyBvcmRlcmVkIHRoZW9yZXRpY2FsIHJlc2lkdWFscy4gSW4gdGhlIGdyYXBoIHdlIGNhbiBzZWUgdGhhdCByZXNpZHVhbHMgYXJlIHRydWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIG1vc3RseSBwb2ludHMgYXJlIGZhbGxpbmcgb24gdGhlIGxpbmUgYnV0IHNvbWUgd2VyZSBmYWxsaW5nIG91dCBvZiBsaW5lIGluIHRoZSBlbmQuDQoNClRoZSBuZXh0IHR3byBzaG93cyB0aGF0IHRoZSByZWdyZXNzaW9uIGlzIG5vbi1saW5lYXIsIG5vbi1jb25zdGFudCB2YXJpYW5jZS4NCg0KVG8gdGVzdCBmdXJ0aGVyLCBpZiBhbnkgYXNzdW1wdGlvbnMgYXJlIHZpb2xhdGVkIG9yIG5vdC4gV2Ugd2lsbCBwZXJmb3JtIHNvbWUgdGVzdHMuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCmR1cmJpbldhdHNvblRlc3QoTW9kZWwzKQ0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIHVuY29ycmVsYXRlZA0KDQpIMTogRXJyb3JzIGFyZSBjb3JyZWxhdGVkDQoNCkZyb20gdGhlIER1cmJpbiBXYXN0b24gdGVzdCwgU2luY2UgdGhlIHAtdmFsdWUgaXMgPCAwLjA1IHdlIGhhdmUgZW5vdWdoIGV2aWRlbmNlIHRvIHJlamVjdCBIMC4gVGhpcyBpbXBsaWVzIHRoYXQgdW5jb3JyZWxhdGVkIGVycm9yIGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnNoYXBpcm8udGVzdChNb2RlbDMkcmVzaWR1YWxzKQ0KYGBgDQoNCkgwOiBFcnJvcnMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkDQoNCkgxOiBFcnJvcnMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZA0KDQpGcm9tIHRoZSBTaGFwaXJvLXdpbGsgbm9ybWFsaXR5IHRlc3QsIFNpbmNlIHRoZSBwLXZhbHVlIGlzIDwgMC4wNSB3ZSBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IEgwLiBUaGlzIGltcGxpZXMgdGhhdCBub3JtYWxpdHkgZXJyb3IgYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KbmN2VGVzdChNb2RlbDMpDQpgYGANCg0KSDA6IEVycm9ycyBoYXZlIGEgY29uc3RhbnQgdmFyaWFuY2UNCg0KSDE6IEVycm9ycyBoYXZlIGEgbm9uLWNvbnN0YW50IHZhcmlhbmNlDQoNCkhlcmUsIHdlIGNhbiBzZWUgdGhhdCBwIHZhbHVlID4gMC4wNS4gdGhlcmVmb3JlLCB3ZSBjYW4gc2F5IHRoYXQgdGhlIGNvbnN0YW50IHZhcmlhbmNlIGFzc3VtcHRpb25zIGlzIHZpb2xhdGVkIGhlcmUuDQoNCiMjIE9ic2VydmF0aW9uDQoNCkFmdGVyIHBlcmZvcm1pbmcgdGhlIHRlc3QsIHdlIGZvdW5kIG91dCB0aGF0IGZldyBhc3N1bXB0aW9ucyB3ZXJlIHZpb2xhdGVkIGhlcmUuIFNvLCB3ZSB3aWxsIHRyeSB0byB0cmFuc2Zvcm0gdGhlIGRhdGEgdXNpbmcgKipMb2cgVHJhbnNmb3JtYXRpb24qKi4NCg0KIyMgTG9nIFRyYW5zZm9ybWF0aW9uDQoNCldlIHdpbGwgbWFrZSBhIG5ldyBtb2RlbCBhbmQgbmFtZSBpdCBhcyAqKmxvZzIqKiBhbmQgZml0IHRoZSB0cmFuc2Zvcm1lZCB2YWx1ZQ0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCmxvZzIgPC0gbG0obG9nKFNjb3JlKX4gaW1kYiRgTW92aWUgRGF0ZWAraW1kYiRgTnVtYmVyIG9mIFZvdGVzYCtpbWRiJGBNb3ZpZSBSZXZlbnVlIChNJClgDQogICAgICAgICAgICAgICAgICAgICtpbWRiJE1ldGFzY29yZStpbWRiJGBUaW1lIER1cmF0aW9uIChtaW4pYCtSZXN0cmljdGlvbiwgaW1kYikNCnN1bW1hcnkobG9nMikNCmBgYA0KDQpIZXJlLCB3ZSBoYXZlIGZpdHRlZCBhIHRyYW5zZm9ybWVkIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIG1vZGVsIC4gV2UgY2FuIHNlZSB0aGF0ICoqTXVsdGlwbGUgUjIqKiB2YWx1ZSBpcyAwLjg5MTYgYW5kICoqQWRqIFIyKiogVmFsdWUgaXMgMC44OTEuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBQLXZhbHVlIGlzIHNtYWxsZXIgdGhlbiAwLjA1LCB0aGVyZWZvcmUgd2UgY2FuIHNheSB0aGF0IHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudCBhbmQgd2UgY2FuIHJlamVjdCBIMC4NCg0KVG8gY2hlY2sgdGhlIG92ZXJhbGwgZml0LCB3ZSB3aWxsIHVzZSBBbm92YSBmdW5jdGlvbi4NCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KYW5vdmEobG9nMikNCmBgYA0KDQpGcm9tIEFub3ZhIFRlc3QsIHdlIGZvdW5kIG91dCB0aGF0IFAgdmFsdWUgZm9yIGFsbCB0aGUgdmFyaWFibGVzIGFyZSAwLjAwMSB3aGljaCBpcyBsZXNzIHRoYW4gMC4wMDUuIFRoZXJlZm9yZSx3ZSBjYW4gc2F5IHRoYXQgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIHN0YXRpc2NhbGx5IHNpZ25pZmljYW50IGFuZCB0aGUgbW9kZWwgaXMgYSBnb29kIGZpdC4NCg0KTm93LCB3ZSB3aWxsIGRpc3BsYXkgdGhlIGdyYXBocyB0byB0ZXN0IHRoZSBhc3N1bXB0aW9ucy4NCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCB3YXJuaW5ncz19DQpwYXIobWZyb3cgPSBjKDIsMikpDQpwbG90KGxvZzIpDQpgYGANCg0Kd2UgY2FuIHNlZSA0IERpYWdub3N0aWMgcGxvdHMga25vd24gYXMgUmVzaWR1YWxzIHBsb3QsIFFRIHBsb3RzLCBTY2FsZS1sb2NhdGlvbiBwbG90IGFuZCBsZXZlcmFnZSBwbG90cy4NCg0KSW4gdGhlIDFzdCBncmFwaCB3ZSBjYW4gc2VlIHRoYXQgb24gWCBheGlzIGhhcyBwcmVkaWN0ZWQgdmFsdWUga25vd24gYXMgWV4oeSBoYXQpIGFuZCBvbiB5IGF4aXMgdGhlcmUgYXJlIHJlc2lkdWFscyBhbmQgZXJyb3JzLiBIZXJlIHdlIGNhbiB0aGUgbGluZSBpcyBub3QgZmxhdCBhbmQgdGhlIHBvaW50cyBhcmUgbG9va2luZyBhcyBhIGJ1bmNoIG9mIGNsb3VkcyB3aGljaCBtZWFucyB0aGF0IGxpbmVhcml0eSBhc3N1bXB0aW9uIGlzIHZpb2xhdGVkIGhlcmUuDQoNCkluIHRoZSBuZXh0IEdyYXBoIChRUSBwbG90KSwgaGVyZSB5IGF4aXMgaXMgb3JkZXJlZCwgb2JzZXJ2ZWQgYW5kIFN0YW5kYXJkaXplZCByZXNpZHVhbHMgYW5kIG9uIFggYXhpcyBpdCBoYXMgb3JkZXJlZCB0aGVvcmV0aWNhbCByZXNpZHVhbHMuIEluIHRoZSBncmFwaCB3ZSBjYW4gc2VlIHRoYXQgcmVzaWR1YWxzIGFyZSB0cnVseSBub3JtYWxseSBkaXN0cmlidXRlZCBhcyBtb3N0bHkgcG9pbnRzIGFyZSBmYWxsaW5nIG9uIHRoZSBsaW5lIGJ1dCBzb21lIHdlcmUgZmFsbGluZyBvdXQgb2YgbGluZSBpbiB0aGUgZW5kLg0KDQpUaGUgbmV4dCB0d28gc2hvd3MgdGhhdCB0aGUgcmVncmVzc2lvbiBpcyBub24tbGluZWFyLCBub24tY29uc3RhbnQgdmFyaWFuY2UuDQoNClRvIHRlc3QgZnVydGhlciwgaWYgYW55IGFzc3VtcHRpb25zIGFyZSB2aW9sYXRlZCBvciBub3QuIFdlIHdpbGwgcGVyZm9ybSBzb21lIHRlc3RzLg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCmR1cmJpbldhdHNvblRlc3QobG9nMikNCmBgYA0KDQpIMDogRXJyb3JzIGFyZSB1bmNvcnJlbGF0ZWQNCg0KSDE6IEVycm9ycyBhcmUgY29ycmVsYXRlZA0KDQpGcm9tIHRoZSBEdXJiaW4gV2FzdG9uIHRlc3QsIFNpbmNlIHRoZSBwLXZhbHVlIGlzIDwgMC4wNSB3ZSBoYXZlIGVub3VnaCBldmlkZW5jZSB0byByZWplY3QgSDAuIFRoaXMgaW1wbGllcyB0aGF0IHVuY29ycmVsYXRlZCBlcnJvciBhc3N1bXB0aW9uIGlzIG5vdCB2aW9sYXRlZC4NCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCB3YXJuaW5ncz19DQpzaGFwaXJvLnRlc3QobG9nMiRyZXNpZHVhbHMpDQpgYGANCg0KSDA6IEVycm9ycyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KSDE6IEVycm9ycyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkDQoNCkZyb20gdGhlIFNoYXBpcm8td2lsayBub3JtYWxpdHkgdGVzdCwgU2luY2UgdGhlIHAtdmFsdWUgaXMgPCAwLjA1IHdlIGhhdmUgZW5vdWdoIGV2aWRlbmNlIHRvIHJlamVjdCBIMC4gVGhpcyBpbXBsaWVzIHRoYXQgbm9ybWFsaXR5IGVycm9yIGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQuDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KbmN2VGVzdChsb2cyKQ0KYGBgDQoNCkgwOiBFcnJvcnMgaGF2ZSBhIGNvbnN0YW50IHZhcmlhbmNlDQoNCkgxOiBFcnJvcnMgaGF2ZSBhIG5vbi1jb25zdGFudCB2YXJpYW5jZQ0KDQpIZXJlLCB3ZSBjYW4gc2VlIHRoYXQgcCB2YWx1ZSA+IDAuMDUuIHRoZXJlZm9yZSwgd2UgY2FuIHNheSB0aGF0IHRoZSBjb25zdGFudCB2YXJpYW5jZSBhc3N1bXB0aW9ucyBpcyB2aW9sYXRlZCBoZXJlLg0KDQojIyBDb21tZW50czoNCg0KQXMgd2UgY2FuIHNlZSB0aGF0IGJlZm9yZSB0cmFuc2Zvcm1hdGlvbixmZXcgYXNzdW1wdGlvbnMgd2VyZSB2aW9sYXRlZC4gU28gd2UgdHJhbnNmb3JtIHRoZSBkYXRhIHVzaW5nIGxvZyB0cmFuc2Zvcm1hdGlvbiBhbmQgYWZ0ZXIgdHJhbnNmb3JtYXRpb24gYWxzbywgdGhlc2UgYXNzdW1wdGlvbiBpcyBzdGlsbCB2aW9sYXRlZC4NCg0KIyMgQ2hvb3NpbmcgYmVzdCBNb2RlbDoNCg0KQWZ0ZXIgYnVpbGRpbmcgdGhyZWUgZGlmZmVyZW50LCB3ZSBoYXZlIHRvIGNob29zZSB0aGUgYmVzdCBtb2RlbC4gV2Ugd2lsbCBjaG9vc2UgdGhlIG1vc3QgYXBwcm9wcmlhdGUgbW9kZWwgb24gdGhlIGJhc2lzIG9mIHRoZWlyICoqTXVsdGlwbGUgUjIqKiB2YWx1ZXMuDQoNCk11bHRpcGxlIFIyIHZhbHVlIG9mIGRpZmZlcmVudCBtb2RlbHMgYXJlOg0KDQoxLikgTW9kZWwxOiAwLjg5NDMsIFRyYW5zZm9ybWVkIE1vZGVsMTogMC44ODc1DQoNCjIuKSBNb2RlbDI6IDAuODk0MywgVHJhbnNmb3JtZWQgTW9kZWwyOiAwLjg5MQ0KDQozLikgTW9kZWwzOiAwLjg5ODIsIFRyYW5zZm9ybWVkIE1vZGVsMzogMC44OTE2DQoNClNvIGZyb20gdGhlIGFib3ZlIHZhbHVlcywgd2UgY2FuIHNheSB0aGF0ICoqTW9kZWwzKiogaXMgdGhlIGJlc3QgbW9kZWwgaWYgd2Ugb2JzZXJ2ZWQgYXMgcGVyIHRoZWlyICoqTXVsdGlwbGUgUjIqKiB2YWx1ZXMgd2hpbGUgKipNb2RlbCAzKiogaXMgc3RpbGwgdGhlIGJlc3QgbW9kZWwgaWYgd2Ugc2VlIHRoZSAqKk11bHRpcGxlIHIyKiogdmFsdWVzIG9mIFRyYW5zZm9ybWVkIG1vZGVscy4NCg0KIyMgUHJlZGljdGluZyBUYXJnZXQgdmFyaWFibGU6DQoNCk5vdyB3ZSB3aWxsIHBlcmZvcm0gcHJlZGljdGlvbiBvbiB0aGUgYmVzdCBtb2RlbCB1c2luZyBkaWZmZXJlbnQgdmFyaWFibGVzLg0KDQpGaXJzdCB3ZSB3aWxsIHVzZSAqKk1ldGFzY29yZSoqIFZhcmlhYmxlLg0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHdhcm5pbmdzPX0NCnByZWRpY3Rpb24xICA8LSBsbShTY29yZX5NZXRhc2NvcmUsaW1kYikNCnN1bW1hcnkocHJlZGljdGlvbjEpDQpgYGANCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgd2FybmluZ3M9fQ0KcHJlZGljdChwcmVkaWN0aW9uMSxkYXRhLmZyYW1lKE1ldGFzY29yZSA9IDEwMCksaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbCA9IDAuOTUpDQpgYGANCg0KVGhlIHByZWRpY3Rpb24gaW50ZXJ2YWwgdXNpbmcgKipNZXRhc2NvcmUqKiBpcyBbOC43OTcsOS4zODRdLg0KDQojIFJlZmVyZW5jZXM6DQoNCjEuIFN0aGRhLmNvbS4gMjAyMS4gQ29ycmVsYXRpb24gVGVzdCBCZXR3ZWVuIFR3byBWYXJpYWJsZXMgaW4gUiAtIEVhc3kgR3VpZGVzIC0gV2lraSAtIFNUSERBLiBbb25saW5lXSBBdmFpbGFibGUgYXQ6IDxodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL3dpa2kvY29ycmVsYXRpb24tdGVzdC1iZXR3ZWVuLXR3by12YXJpYWJsZXMtaW4tcj4gW0FjY2Vzc2VkIDMgSnVuZSAyMDIxXS4NCg0KMi4gUiwgQi4sIDIwMjEuIEJlc3QgU3Vic2V0cyBSZWdyZXNzaW9uIEVzc2VudGlhbHMgaW4gUiAtIEFydGljbGVzIC0gU1RIREEuIFtvbmxpbmVdIFN0aGRhLmNvbS4gQXZhaWxhYmxlIGF0OiA8aHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy8zNy1tb2RlbC1zZWxlY3Rpb24tZXNzZW50aWFscy1pbi1yLzE1NS1iZXN0LXN1YnNldHMtcmVncmVzc2lvbi1lc3NlbnRpYWxzLWluLXIvPiBbQWNjZXNzZWQgMyBKdW5lIDIwMjFdLg0KDQozLiBMaXN0ZW5EYXRhLiAyMDIxLiAxNSBUeXBlcyBvZiBSZWdyZXNzaW9uIGluIERhdGEgU2NpZW5jZS4gW29ubGluZV0gQXZhaWxhYmxlIGF0OiA8aHR0cHM6Ly93d3cubGlzdGVuZGF0YS5jb20vMjAxOC8wMy9yZWdyZXNzaW9uLWFuYWx5c2lzLmh0bWw+IFtBY2Nlc3NlZCAzIEp1bmUgMjAyMV0uDQog