Introduction

This dataset is An Adidas sales dataset that have information on the sales of Adidas products, number of units sold, the total sales revenue, the location of the sales, the sales outlets and method of sales.

This data is based on the Adidas dataset, it is very granular and each row in that dataset represents a line item for a purchase at one of 6 Retailers outlet spread across all the states in the five Region of USA. there is no missing value in this dataset. this project has two part to it, part one seeks to answer project statement using Residual analysis and part two seeks to use Dummy variables in regression to explain the project statement

Data Source

The data used in this analysis is available publicly on Kaggle.com. https://www.kaggle.com/datasets/heemalichaudhari/adidas-sales-dataset

Project Statement:

“How are quarterly sales affected by quarter of the year, region, and by product?

Step 1

Load the Libraries

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.1     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.2     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(corrplot)
corrplot 0.92 loaded
library(rmarkdown)
library(jtools) # For tabulating and visualizing results from multiple regression models
library(ggstance)

Attaching package: 'ggstance'

The following objects are masked from 'package:ggplot2':

    geom_errorbarh, GeomErrorbarh
library(huxtable)

Attaching package: 'huxtable'

The following object is masked from 'package:dplyr':

    add_rownames

The following object is masked from 'package:ggplot2':

    theme_grey

Descriptive Analysis

Data Preparation (ETL)

load dataset as a dataframe object and checking the dimension with dim(), it has 9648 rows and 12 columns.

Adidias <- read.csv('C:/Users/ebene/Documents/Adidias.csv')
# Save the data frame as an RDS file
saveRDS(Adidias, file = "C:/Users/ebene/Documents/Adidias.rds")
Adidias <- readRDS("C:/Users/ebene/Documents/Adidias.rds")
dim(Adidias)
[1] 9648   12

Getting to Know the Dataset and checking for missing values

str(Adidias)
'data.frame':   9648 obs. of  12 variables:
 $ Retailer        : chr  "Foot Locker" "Foot Locker" "Foot Locker" "Foot Locker" ...
 $ Retailer_ID     : int  1185732 1185732 1185732 1185732 1185732 1185732 1185732 1185732 1185732 1185732 ...
 $ Invoice_Date    : chr  "01/01/2020" "02/01/2020" "03/01/2020" "04/01/2020" ...
 $ Region          : chr  "Northeast" "Northeast" "Northeast" "Northeast" ...
 $ State           : chr  "New York" "New York" "New York" "New York" ...
 $ City            : chr  "New York" "New York" "New York" "New York" ...
 $ Product         : chr  "Men's Street Footwear" "Men's Athletic Footwear" "Women's Street Footwear" "Women's Athletic Footwear" ...
 $ Price_per_Unit  : int  50 50 40 45 60 50 50 50 40 45 ...
 $ Units_Sold      : int  1200 1000 1000 850 900 1000 1250 900 950 825 ...
 $ Total_Sales     : int  600000 500000 400000 382500 540000 500000 625000 450000 380000 371250 ...
 $ Operating_Profit: int  300000 150000 140000 133875 162000 125000 312500 135000 133000 129938 ...
 $ Sales_Method    : chr  "In-store" "In-store" "In-store" "In-store" ...
colSums(is.na(Adidias))
        Retailer      Retailer_ID     Invoice_Date           Region 
               0                0                0                0 
           State             City          Product   Price_per_Unit 
               0                0                0                0 
      Units_Sold      Total_Sales Operating_Profit     Sales_Method 
               0                0                0                0 

From the data structure above, there are columns data, Retailer_ID and Invoice_Date. These variable do not provide enough information to perform linear regression analysis, which can be removed, but we need to get quarterly sales from date, hence the date will be useful,

Summary of dataset.

rmarkdown::paged_table(Adidias)
Adidias<- Adidias %>% 
select(-Retailer_ID)# romoves the column Retiasler
summary(Adidias)## Step 3.1 converting to date using lubridate function
   Retailer         Invoice_Date          Region             State          
 Length:9648        Length:9648        Length:9648        Length:9648       
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                            
                                                                            
                                                                            
     City             Product          Price_per_Unit     Units_Sold    
 Length:9648        Length:9648        Min.   :  7.00   Min.   :   0.0  
 Class :character   Class :character   1st Qu.: 35.00   1st Qu.: 106.0  
 Mode  :character   Mode  :character   Median : 45.00   Median : 176.0  
                                       Mean   : 45.22   Mean   : 256.9  
                                       3rd Qu.: 55.00   3rd Qu.: 350.0  
                                       Max.   :110.00   Max.   :1275.0  
  Total_Sales     Operating_Profit Sales_Method      
 Min.   :     0   Min.   :     0   Length:9648       
 1st Qu.:  4254   1st Qu.:  1922   Class :character  
 Median :  9576   Median :  4372   Mode  :character  
 Mean   : 93273   Mean   : 34425                     
 3rd Qu.:150000   3rd Qu.: 52063                     
 Max.   :825000   Max.   :390000                     

Converting Date to Datetype

creating a column for quarter, I had to break date into quarter then concatenate date and quarter and quarter_char to be able to answer the project statement

library(lubridate)
# Convert 'Invoice_Date' column to Date type
Adidias$Invoice_Date <- dmy(Adidias$Invoice_Date)

# Get quarters
Adidias$quarter <- quarter(Adidias$Invoice_Date)
# Create a new column 'date_quarter'
Adidias$date_quater <- paste(Adidias$Invoice_Date, Adidias$quarter, sep = "-")
# Create a new column 'quarter_char' representing quarters as characters
Adidias <- Adidias %>%
  mutate(quarter_char = case_when(
    quarter == 1 ~ 'First',
    quarter == 2 ~ 'Second',
    quarter == 3 ~ 'Third',
    quarter == 4 ~ 'Fourth',
    TRUE ~ as.character(quarter)
  ))

# View the updated data frame
#print(Adidias)

Renaming my data columns

Adidias <- rename(Adidias, total_sales = 'Total_Sales',
                     operating_profit = 'Operating_Profit',
                     invoice_date = 'Invoice_Date',
                     price_per_unit = 'Price_per_Unit',
                     units_sold = 'Units_Sold',
                     sales_method = 'Sales_Method',
                     region = Region,
                     retailer = Retailer,
                     state = State,
                     city = City,
                     product = Product)

The number of transaction or number of sales unit made for each product that was sold in one hand and knowing the retailer with the most product unit sold in other hand: Foot Locker had the most unit sold and was followed by West Gear. For retailers with the least number of sales transactions is Walmart. Men’s Street Footwear and Men’s Athletic Footwear had the most sales unite. Men’s Apparel and Women’s Athletic Footwear had the lowest sold unit

#Examine PROD_NAME
library(data.table)

Attaching package: 'data.table'
The following objects are masked from 'package:lubridate':

    hour, isoweek, mday, minute, month, quarter, second, wday, week,
    yday, year
The following objects are masked from 'package:dplyr':

    between, first, last
The following object is masked from 'package:purrr':

    transpose
setDT(Adidias)
Adidias[, .N , "product"]
sort(table(Adidias$retailer), decreasing = T)

  Foot Locker     West Gear Sports Direct        Kohl's        Amazon 
         2637          2374          2032          1030           949 
      Walmart 
          626 

Period of Analysis

c(min(Adidias$invoice_date), max(Adidias$invoice_date))
[1] "2020-01-01" "2021-12-31"

data used from 1 January 2020 up to 31 December 2021

Region Unit Sold

West Region has the most Adidas purchase transactions and Southeast Region is the area with the fewest adidas purchase transactions

sort(table(Adidias$region), decreasing = T)

     West Northeast   Midwest     South Southeast 
     2448      2376      1872      1728      1224 

from the summary() function, there were days no unite was sold per day and there were days that highest average unite sold per day was 1,275

Days No Product Was Sold

zero_sales <-Adidias %>%
    filter(units_sold == "0")
zero_sales

On June 5, 2021 and June 11, 2021 in Omaha, Midwest region, state of Nebraska in second quarter, Woman’s Athletic Footwear products did not sell at Foot Locker. it should be investigated what happend in the month of June to avoid future occurrence.

Sales Method Performance

Adidas Online sales method performed better and in-store has the leaset performance

  sort(table(Adidias$sales_method), decreasing = T)

  Online   Outlet In-store 
    4889     3019     1740 

Exploratory Analysis

Statistical Analysis

Correlation relationship on Unit Sold and opration_profit, the first code gives the output of the correlation between the two variables with out the correlation matrix,the code snippets gives the correlation matrix. At 0.89, the relationship between oprational_profit and units_sold is directly proportional.

cor(Adidias$units_sold, Adidias$operating_profit)
[1] 0.8923794
cor(Adidias[,c('operating_profit', 'units_sold')])
                 operating_profit units_sold
operating_profit        1.0000000  0.8923794
units_sold              0.8923794  1.0000000

Regression Opreation_profit On units_sold With A Scatter Plot

the stat_smooth function from the ggplot2 package was used to add regression function on the plot. The “lm” value for the method parameter indicates that I am using a linear model to add a trend line to the scatter plot.

ggplot(Adidias, aes(x =units_sold , y = operating_profit)) +
  geom_point() +
  stat_smooth(method = 'lm')
`geom_smooth()` using formula = 'y ~ x'

conceptually, this algorithm calculates the slope and intercept of a line that mathematically reduces the sum of the distance between all of the data points and the line.

Residuals Analysis

created a linear model to predict operational by regressing units_sold from the Adidas data frame, and then look at a summary of the model.

lm_a <- lm(operating_profit  ~ units_sold, data = Adidias)
summary(lm_a)

Call:
lm(formula = operating_profit ~ units_sold, data = Adidias)

Residuals:
    Min      1Q  Median      3Q     Max 
-108758  -12327     263   10588  225529 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -23568.799    388.809  -60.62   <2e-16 ***
units_sold     225.719      1.162  194.21   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 24460 on 9646 degrees of freedom
Multiple R-squared:  0.7963,    Adjusted R-squared:  0.7963 
F-statistic: 3.772e+04 on 1 and 9646 DF,  p-value: < 2.2e-16

Residuals are simply the difference between the actual observations of opration_revenue that were used to create the model and the values of unit_sales that are fitted from the model.

Creating Residual DataFrame

To look at some specific observations I will first creating a dataframe that has the actual values of Unit_sales, the opreating_revenue, and two new columns: One fittedOpreating_profit, this will be created using the coefficient estimates from the linear model. The second column will be created, residuals, by subtracting the values from the fittedOpreating_profit, column from the opreating_profit.

resid <- Adidias %>%
  select(operating_profit, units_sold) %>%
  mutate(fittedOpreating_profit = -23568.799 + 225.719*units_sold
         , residuals = operating_profit - fittedOpreating_profit)
head(resid)

The first row indicates that when the value of Unit_sold is equal to 1,200, the fitted value of opreating_profit is 247,294.0. However, the actual oprating_profit of 300,000 was above the amount by 52,706. In other words, the actual value of opreating_profit is above the line created by the linear model.

looking at the second row, which indicates that when the value of unit_sold is equal to 1000, the fitted value of opreating_profit is 202150.2. This time, the actual value of opreating_profit of 150000 is below that amount by 52150.20 meaning that it falls below the line created by the linear model.

Business Management Application of Residuals

As a Business manager of Adidas Sales that wants to see “How are quarterly sales affected by quarter of the year, region, and by product? can have a clue from residual analysis If we think of the fitted values as a target, or expectation of what opreating_profit should be, then the residual tells us whether that revenue is more or less than expected. In other words, it can be thought of as a variance.

Residual Analysis Explaining the project statement

# Create a dataframe with residuals and identifying information
resids2 <- Adidias %>%
  select(retailer,product,region, date_quater, units_sold, operating_profit)
resids2$fittedOpreating_profit = lm_a$fitted.values
resids2$residuals = lm_a$residuals

# Get the five best performing store/quarter combinations
best <- resids2 %>%
  arrange(desc(residuals)) %>%
  .[1:4,]
# Get the five worst performing store/quarter combinations
worst <- resids2 %>%
  arrange(residuals) %>%
  .[1:4,] %>%
  arrange(desc(residuals))

# Combine the five best and worst into one dataframe and display them
bestWorst <- bind_rows(best,worst)
bestWorst

Recommendation

The manager, may want to look into the two stores that under performed during 2021 to work on improving their performance. In contrast, He may want to look into the two stores that outperformed during 2021 to find out if their best practices can be replicated in other locations.

Part Two

‘Residual Analysis: An R Project ON How Adidas Quarterly Sales Are Affected By Quarter Of The year, Region, And By Product’ Part 2 Using Dummy variables to analyse Adidas quarter of the year product performances

Project Statement:

“How are quarterly sales affected by quarter of the year, region, and by product?

Qualitative Variables

I will be using Qualitative variable quarter of the year, however, machine learning algorithms, including regression, rely on numeric values. So we have to convert qualitative variables to numeric variables.

Dummy Variables

What is often done is a series of binary variables is used to capture the different levels of the qualitative variable. Specifically, we would replace the quarter of the year variable, quarter_NoYear, with three variables: Second, Third, and Fourth. The values in these columns take on a value of 1 if the observation fits into that category, and a value of zero otherwise. We only need three columns because if they all have a value of 0, then that means the observation fits into the first quarter.

Here’s a dataframe to illustrate that idea with a bit more detail:

data.frame('quarter_char' = c('First', 'Second', 'Third', 'Fourth'),
                 'quarterSecond' = c(0, 1, 0, 0),
                 'quarterThird' = c(0, 0, 1, 0),
                 'quarterFourth' = c(0, 0, 0, 1))

Relationship between perating_profit on quarter_char column

Regression of operating_profit on quarter_char column, which as a data type of factor.

lm_b<- lm(operating_profit  ~ quarter_char, data = Adidias)
summary(lm_b)

Call:
lm(formula = operating_profit ~ quarter_char, data = Adidias)

Residuals:
   Min     1Q Median     3Q    Max 
-40494 -33080 -26092  18040 355040 

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)           27460       1093  25.115  < 2e-16 ***
quarter_charFourth     6925       1558   4.446 8.86e-06 ***
quarter_charSecond     7779       1554   5.005 5.68e-07 ***
quarter_charThird     13142       1544   8.510  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 54000 on 9644 degrees of freedom
Multiple R-squared:  0.007533,  Adjusted R-squared:  0.007225 
F-statistic:  24.4 on 3 and 9644 DF,  p-value: 1.006e-15

There is a coefficient estimate for the second through fourth quarters, but not for the first quarter. The intercept represents the estimate of operating_profit for the first quarter, and the coefficient estimates for the other variables represent the difference between that quarter from the first quarter. the operating_profit for third quarter is 13,142 higher than the baseline estimated and 7,779 higher than the baseline estimated in second quarter.

Manual Comparison of Mean

Let’s create a manual comparison by calculating the mean value of operating_profit for each quarter.

Adidias %>%
  group_by(quarter_char) %>%
  summarize(meanProfit = mean(operating_profit)) %>%
  ungroup()

The value of meanProfit for the second quarter, 35,238.62 is higher than the value of meanProfit for the first quarter by 7,778.78, which is represented by the coefficient estimate on quarter_cherSecond in the regression model.

There’s a similar relationship for the other two quarters and the difference from the first quarter and their coefficient estimates.

In terms of statistical significance, all the coefficients are highly significant (p-values < 0.001). This means that the quarterly variables have a significant impact on operating profit.

Comparing two simple regression model

lm_a <- lm(operating_profit  ~ units_sold, data = Adidias)

lm_b<- lm(operating_profit  ~ quarter_char, data = Adidias)
export_summs(lm_a, lm_b) # Create a nice looking table for comparing the regression results
Registered S3 methods overwritten by 'broom':
  method            from  
  tidy.glht         jtools
  tidy.summary.glht jtools
Model 1Model 2
(Intercept)-23568.80 ***27459.84 ***
(388.81)   (1093.36)   
units_sold225.72 ***       
(1.16)          
quarter_charFourth       6925.40 ***
       (1557.79)   
quarter_charSecond       7778.78 ***
       (1554.15)   
quarter_charThird       13142.38 ***
       (1544.35)   
N9648       9648       
R20.80    0.01    
*** p < 0.001; ** p < 0.01; * p < 0.05.

The table indicates the key takeaways from both linear models: * Coefficient estimates: * Model 1, the coefficient estimate is -23568.80 for the intercept and is 225.72 for Unit_sold. * For model 2, the coefficient estimate is 27459.84 for the intercept and is 6925.40 for quarter_charFourth, 7778.78 for quarter_charSecond and 13142.38 for quarter_charThird * The standard errors are in parentheses below these coefficient estimates. * N stands for number of observations, and they are both based on 9648 observations. * R2 is the R-squared, which is much larger for model 1, 80%, than for model 2, 1%. This means that model 1 explains more variation in opratiing_profit than model 2, and is better for making predictions.

Visualizing coefficients and standard errors

Let’s use the jtools package to plot the coefficients and standard errors to help visualize the results.

library(jtools)
plot_summs(lm_a, lm_b)
Loading required namespace: broom.mixed
Loading required namespace: broom.mixed

This visualization is excellent. It clearly shows that the coefficient on Unit_sold from model 1 is positive, and has a much smaller standard error relative to the positive coefficient on quarter_char from model 2. The long orange whiskers and the short, barely visible blue whiskers represent the standard error, or the range in which the actual value could be.

The Unique Effect of Quarter of the Year

Quarter of the year may have a significant effect on quarterly oprating_profit after controlling for the percentage of sales that come from other products. Let’s test this out by including it with the unit_sold variables that we have already investigated.

lm_c <- lm(operating_profit  ~ units_sold + quarter_char,data = Adidias)
export_summs(lm_a, lm_b, lm_c)
Model 1Model 2Model 3
(Intercept)-23568.80 ***27459.84 ***-29406.31 ***
(388.81)   (1093.36)   (567.75)   
units_sold225.72 ***       226.42 ***
(1.16)          (1.15)   
quarter_charFourth       6925.40 ***11268.92 ***
       (1557.79)   (696.44)   
quarter_charSecond       7778.78 ***6660.84 ***
       (1554.15)   (694.48)   
quarter_charThird       13142.38 ***4882.99 ***
       (1544.35)   (691.36)   
N9648       9648       9648       
R20.80    0.01    0.80    
*** p < 0.001; ** p < 0.01; * p < 0.05.

regression analysis with the addition of the “units_sold” variable yields the following results:

The coefficient for the “units_sold” variable is 226.424 with a standard error of 1.152. This means that for each additional unit sold, the operating profit is estimated to increase by 226.424. The coefficient is highly significant (p-value < 0.001), indicating a strong positive relationship between units sold and operating profit.

The coefficient for the “quarter_charFourth” variable is 11,268.915 with a standard error of 696.438. This coefficient represents the difference in operating profit between the fourth quarter and the baseline quarter, while holding the number of units sold constant. The positive coefficient suggests that the fourth quarter tends to have higher operating profit compared to the baseline quarter.

All coefficients are highly significant (p-values < 0.001), indicating their strong relationship with operating profit.

The multiple R-squared value is 0.8019, indicating that the model with “units_sold” and quarterly variables explains approximately 80.19% of the variability in operating profit. The adjusted R-squared value is 0.8018, which takes into account the number of predictors in the model.

The F-statistic is 9756 with a p-value < 2.2e-16, indicating that the overall model is highly significant.

This change is effectively communicated by visualizing the coefficients from all three models.

plot_summs(lm_a, lm_b, lm_c)
Loading required namespace: broom.mixed
Loading required namespace: broom.mixed
Loading required namespace: broom.mixed

Conclusion

Based on Model 2 and Model 3, it appears that the quarter_charThird variable had the largest positive coefficient and was statistically significant in both models. This suggests that the third quarter (quarter_charThird) had the most significant positive impact on the dependent variable compared to the other quarters (quarter_charFourth and quarter_charSecond). However, the practical significance and implications may vary depending on the specific situation or domain being studied.

LS0tDQp0aXRsZTogIlJlc2lkdWFsIEFuYWx5c2lzOiBBbiBSIFByb2plY3QgT04gSG93IEFkaWRhcyBRdWFydGVybHkgU2FsZXMgQXJlIEFmZmVjdGVkDQogICAgICAgIEJ5IFF1YXJ0ZXIgT2YgVGhlIHllYXIsIFJlZ2lvbiwgQW5kIEJ5IFByb2R1Y3QgUGFydCAxIg0Kb3V0cHV0OiAnaHRtbF9ub3RlYm9vaycNCkF1dGhvcjogRWJlbmV6ZXIgQWtwYXRpDQpEYXRlOiAyNS8wNS8yMw0KLS0tDQoNCiMjIEludHJvZHVjdGlvbg0KVGhpcyBkYXRhc2V0IGlzIEFuIEFkaWRhcyBzYWxlcyBkYXRhc2V0IHRoYXQgaGF2ZSBpbmZvcm1hdGlvbiBvbiB0aGUgc2FsZXMgb2YgQWRpZGFzIHByb2R1Y3RzLCBudW1iZXIgb2YgdW5pdHMgc29sZCwgdGhlIHRvdGFsIHNhbGVzIHJldmVudWUsIHRoZSBsb2NhdGlvbiBvZiB0aGUgc2FsZXMsIHRoZSBzYWxlcyBvdXRsZXRzIGFuZCBtZXRob2Qgb2Ygc2FsZXMuDQoNClRoaXMgZGF0YSBpcyBiYXNlZCBvbiB0aGUgQWRpZGFzIGRhdGFzZXQsIGl0IGlzIHZlcnkgZ3JhbnVsYXIgYW5kIGVhY2ggcm93IGluIHRoYXQgZGF0YXNldCByZXByZXNlbnRzIGEgbGluZSBpdGVtIGZvciBhIHB1cmNoYXNlIGF0IG9uZSBvZiA2IFJldGFpbGVycyBvdXRsZXQgc3ByZWFkIGFjcm9zcyBhbGwgdGhlIHN0YXRlcyBpbiB0aGUgZml2ZSBSZWdpb24gb2YgVVNBLiB0aGVyZSBpcyBubyBtaXNzaW5nIHZhbHVlIGluIHRoaXMgZGF0YXNldC4gdGhpcyBwcm9qZWN0IGhhcyB0d28gcGFydCB0byBpdCwgcGFydCBvbmUgc2Vla3MgdG8gYW5zd2VyIHByb2plY3Qgc3RhdGVtZW50IHVzaW5nICpSZXNpZHVhbCBhbmFseXNpcyogYW5kIHBhcnQgdHdvIHNlZWtzIHRvIHVzZSBEdW1teSB2YXJpYWJsZXMgaW4gcmVncmVzc2lvbiB0byBleHBsYWluIHRoZSBwcm9qZWN0IHN0YXRlbWVudCANCg0KIyMgRGF0YSBTb3VyY2UNCg0KVGhlIGRhdGEgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGlzIGF2YWlsYWJsZSBwdWJsaWNseSBvbiBLYWdnbGUuY29tLiA8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9oZWVtYWxpY2hhdWRoYXJpL2FkaWRhcy1zYWxlcy1kYXRhc2V0Pg0KDQojIyBQcm9qZWN0IFN0YXRlbWVudDoNCg0KIkhvdyBhcmUgcXVhcnRlcmx5IHNhbGVzIGFmZmVjdGVkIGJ5IHF1YXJ0ZXIgb2YgdGhlIHllYXIsIHJlZ2lvbiwgYW5kIGJ5IHByb2R1Y3Q/DQoNCiMjIFN0ZXAgMQ0KDQpMb2FkIHRoZSBMaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KHJtYXJrZG93bikNCmxpYnJhcnkoanRvb2xzKSAjIEZvciB0YWJ1bGF0aW5nIGFuZCB2aXN1YWxpemluZyByZXN1bHRzIGZyb20gbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbHMNCmxpYnJhcnkoZ2dzdGFuY2UpDQpsaWJyYXJ5KGh1eHRhYmxlKQ0KYGBgDQoNCiMjIERlc2NyaXB0aXZlIEFuYWx5c2lzDQpEYXRhIFByZXBhcmF0aW9uIChFVEwpDQoNCmxvYWQgZGF0YXNldCBhcyBhIGRhdGFmcmFtZSBvYmplY3QgYW5kIGNoZWNraW5nIHRoZSBkaW1lbnNpb24gd2l0aCBkaW0oKSwgaXQgaGFzIDk2NDggcm93cyBhbmQgMTIgY29sdW1ucy4NCg0KYGBge3J9DQpBZGlkaWFzIDwtIHJlYWQuY3N2KCdDOi9Vc2Vycy9lYmVuZS9Eb2N1bWVudHMvQWRpZGlhcy5jc3YnKQ0KIyBTYXZlIHRoZSBkYXRhIGZyYW1lIGFzIGFuIFJEUyBmaWxlDQpzYXZlUkRTKEFkaWRpYXMsIGZpbGUgPSAiQzovVXNlcnMvZWJlbmUvRG9jdW1lbnRzL0FkaWRpYXMucmRzIikNCkFkaWRpYXMgPC0gcmVhZFJEUygiQzovVXNlcnMvZWJlbmUvRG9jdW1lbnRzL0FkaWRpYXMucmRzIikNCmRpbShBZGlkaWFzKQ0KYGBgDQoNCkdldHRpbmcgdG8gS25vdyB0aGUgRGF0YXNldCBhbmQgY2hlY2tpbmcgZm9yIG1pc3NpbmcgdmFsdWVzDQoNCmBgYHtyfQ0Kc3RyKEFkaWRpYXMpDQpjb2xTdW1zKGlzLm5hKEFkaWRpYXMpKQ0KYGBgDQoNCkZyb20gdGhlIGRhdGEgc3RydWN0dXJlIGFib3ZlLCB0aGVyZSBhcmUgY29sdW1ucyBkYXRhLCBSZXRhaWxlcl9JRCBhbmQgSW52b2ljZV9EYXRlLiBUaGVzZSB2YXJpYWJsZSBkbyBub3QgcHJvdmlkZSBlbm91Z2ggaW5mb3JtYXRpb24gdG8gcGVyZm9ybSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcywgd2hpY2ggY2FuIGJlIHJlbW92ZWQsIGJ1dCB3ZSBuZWVkIHRvIGdldCBxdWFydGVybHkgc2FsZXMgZnJvbSBkYXRlLCBoZW5jZSB0aGUgZGF0ZSB3aWxsIGJlIHVzZWZ1bCwNCg0KIyMgU3VtbWFyeSBvZiBkYXRhc2V0Lg0KDQpgYGB7cn0NCnJtYXJrZG93bjo6cGFnZWRfdGFibGUoQWRpZGlhcykNCkFkaWRpYXM8LSBBZGlkaWFzICU+JSANCnNlbGVjdCgtUmV0YWlsZXJfSUQpIyByb21vdmVzIHRoZSBjb2x1bW4gUmV0aWFzbGVyDQpzdW1tYXJ5KEFkaWRpYXMpIyMgU3RlcCAzLjEgY29udmVydGluZyB0byBkYXRlIHVzaW5nIGx1YnJpZGF0ZSBmdW5jdGlvbg0KYGBgDQojIyBDb252ZXJ0aW5nIERhdGUgdG8gIERhdGV0eXBlIA0KY3JlYXRpbmcgYSBjb2x1bW4gZm9yIHF1YXJ0ZXIsIEkgaGFkIHRvIGJyZWFrIGRhdGUgaW50byBxdWFydGVyIHRoZW4gY29uY2F0ZW5hdGUgZGF0ZSBhbmQgcXVhcnRlciBhbmQgcXVhcnRlcl9jaGFyIHRvIGJlIGFibGUgdG8gYW5zd2VyIHRoZSBwcm9qZWN0IHN0YXRlbWVudA0KDQpgYGB7cn0NCmxpYnJhcnkobHVicmlkYXRlKQ0KIyBDb252ZXJ0ICdJbnZvaWNlX0RhdGUnIGNvbHVtbiB0byBEYXRlIHR5cGUNCkFkaWRpYXMkSW52b2ljZV9EYXRlIDwtIGRteShBZGlkaWFzJEludm9pY2VfRGF0ZSkNCg0KIyBHZXQgcXVhcnRlcnMNCkFkaWRpYXMkcXVhcnRlciA8LSBxdWFydGVyKEFkaWRpYXMkSW52b2ljZV9EYXRlKQ0KIyBDcmVhdGUgYSBuZXcgY29sdW1uICdkYXRlX3F1YXJ0ZXInDQpBZGlkaWFzJGRhdGVfcXVhdGVyIDwtIHBhc3RlKEFkaWRpYXMkSW52b2ljZV9EYXRlLCBBZGlkaWFzJHF1YXJ0ZXIsIHNlcCA9ICItIikNCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiAncXVhcnRlcl9jaGFyJyByZXByZXNlbnRpbmcgcXVhcnRlcnMgYXMgY2hhcmFjdGVycw0KQWRpZGlhcyA8LSBBZGlkaWFzICU+JQ0KICBtdXRhdGUocXVhcnRlcl9jaGFyID0gY2FzZV93aGVuKA0KICAgIHF1YXJ0ZXIgPT0gMSB+ICdGaXJzdCcsDQogICAgcXVhcnRlciA9PSAyIH4gJ1NlY29uZCcsDQogICAgcXVhcnRlciA9PSAzIH4gJ1RoaXJkJywNCiAgICBxdWFydGVyID09IDQgfiAnRm91cnRoJywNCiAgICBUUlVFIH4gYXMuY2hhcmFjdGVyKHF1YXJ0ZXIpDQogICkpDQoNCiMgVmlldyB0aGUgdXBkYXRlZCBkYXRhIGZyYW1lDQojcHJpbnQoQWRpZGlhcykNCg0KYGBgDQoNCiMjIFJlbmFtaW5nIG15IGRhdGEgY29sdW1ucw0KDQpgYGB7cn0NCkFkaWRpYXMgPC0gcmVuYW1lKEFkaWRpYXMsIHRvdGFsX3NhbGVzID0gJ1RvdGFsX1NhbGVzJywNCiAgICAgICAgICAgICAgICAgICAgIG9wZXJhdGluZ19wcm9maXQgPSAnT3BlcmF0aW5nX1Byb2ZpdCcsDQogICAgICAgICAgICAgICAgICAgICBpbnZvaWNlX2RhdGUgPSAnSW52b2ljZV9EYXRlJywNCiAgICAgICAgICAgICAgICAgICAgIHByaWNlX3Blcl91bml0ID0gJ1ByaWNlX3Blcl9Vbml0JywNCiAgICAgICAgICAgICAgICAgICAgIHVuaXRzX3NvbGQgPSAnVW5pdHNfU29sZCcsDQogICAgICAgICAgICAgICAgICAgICBzYWxlc19tZXRob2QgPSAnU2FsZXNfTWV0aG9kJywNCiAgICAgICAgICAgICAgICAgICAgIHJlZ2lvbiA9IFJlZ2lvbiwNCiAgICAgICAgICAgICAgICAgICAgIHJldGFpbGVyID0gUmV0YWlsZXIsDQogICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9IFN0YXRlLA0KICAgICAgICAgICAgICAgICAgICAgY2l0eSA9IENpdHksDQogICAgICAgICAgICAgICAgICAgICBwcm9kdWN0ID0gUHJvZHVjdCkNCmBgYA0KDQpUaGUgbnVtYmVyIG9mIHRyYW5zYWN0aW9uIG9yIG51bWJlciBvZiBzYWxlcyB1bml0IG1hZGUgZm9yIGVhY2ggcHJvZHVjdCB0aGF0IHdhcyBzb2xkIGluIG9uZSBoYW5kIGFuZCBrbm93aW5nIHRoZSByZXRhaWxlciB3aXRoIHRoZSBtb3N0IHByb2R1Y3QgdW5pdCBzb2xkIGluIG90aGVyIGhhbmQ6IEZvb3QgTG9ja2VyIGhhZCB0aGUgbW9zdCB1bml0IHNvbGQgYW5kIHdhcyBmb2xsb3dlZCBieSBXZXN0IEdlYXIuIEZvciByZXRhaWxlcnMgd2l0aCB0aGUgbGVhc3QgbnVtYmVyIG9mIHNhbGVzIHRyYW5zYWN0aW9ucyBpcyBXYWxtYXJ0LiBNZW4ncyBTdHJlZXQgRm9vdHdlYXIgYW5kIE1lbidzIEF0aGxldGljIEZvb3R3ZWFyIGhhZCB0aGUgbW9zdCBzYWxlcyB1bml0ZS4gTWVuJ3MgQXBwYXJlbCBhbmQgV29tZW4ncyBBdGhsZXRpYyBGb290d2VhciBoYWQgdGhlIGxvd2VzdCBzb2xkIHVuaXQNCg0KYGBge3J9DQojRXhhbWluZSBQUk9EX05BTUUNCmxpYnJhcnkoZGF0YS50YWJsZSkNCnNldERUKEFkaWRpYXMpDQpBZGlkaWFzWywgLk4gLCAicHJvZHVjdCJdDQpzb3J0KHRhYmxlKEFkaWRpYXMkcmV0YWlsZXIpLCBkZWNyZWFzaW5nID0gVCkNCmBgYA0KDQojIyBQZXJpb2Qgb2YgQW5hbHlzaXMNCg0KYGBge3J9DQpjKG1pbihBZGlkaWFzJGludm9pY2VfZGF0ZSksIG1heChBZGlkaWFzJGludm9pY2VfZGF0ZSkpDQpgYGANCg0KZGF0YSB1c2VkIGZyb20gMSBKYW51YXJ5IDIwMjAgdXAgdG8gMzEgRGVjZW1iZXIgMjAyMQ0KDQojIyBSZWdpb24gVW5pdCBTb2xkDQoNCldlc3QgUmVnaW9uIGhhcyB0aGUgbW9zdCBBZGlkYXMgcHVyY2hhc2UgdHJhbnNhY3Rpb25zIGFuZCBTb3V0aGVhc3QgUmVnaW9uIGlzIHRoZSBhcmVhIHdpdGggdGhlIGZld2VzdCBhZGlkYXMgcHVyY2hhc2UgdHJhbnNhY3Rpb25zDQoNCmBgYHtyfQ0Kc29ydCh0YWJsZShBZGlkaWFzJHJlZ2lvbiksIGRlY3JlYXNpbmcgPSBUKQ0KYGBgDQoNCmZyb20gdGhlIHN1bW1hcnkoKSBmdW5jdGlvbiwgdGhlcmUgd2VyZSBkYXlzIG5vIHVuaXRlIHdhcyBzb2xkIHBlciBkYXkgYW5kIHRoZXJlIHdlcmUgZGF5cyB0aGF0IGhpZ2hlc3QgYXZlcmFnZSB1bml0ZSBzb2xkIHBlciBkYXkgd2FzIDEsMjc1DQoNCiMjIERheXMgTm8gUHJvZHVjdCBXYXMgU29sZA0KDQpgYGB7cn0NCnplcm9fc2FsZXMgPC1BZGlkaWFzICU+JQ0KICAgIGZpbHRlcih1bml0c19zb2xkID09ICIwIikNCnplcm9fc2FsZXMNCmBgYA0KDQpPbiBKdW5lIDUsIDIwMjEgYW5kIEp1bmUgMTEsIDIwMjEgaW4gT21haGEsIE1pZHdlc3QgcmVnaW9uLCBzdGF0ZSBvZiBOZWJyYXNrYSBpbiBzZWNvbmQgcXVhcnRlciwgV29tYW4ncyBBdGhsZXRpYyBGb290d2VhciBwcm9kdWN0cyBkaWQgbm90IHNlbGwgYXQgRm9vdCBMb2NrZXIuIGl0IHNob3VsZCBiZSBpbnZlc3RpZ2F0ZWQgd2hhdCBoYXBwZW5kIGluIHRoZSBtb250aCBvZiBKdW5lIHRvIGF2b2lkIGZ1dHVyZSBvY2N1cnJlbmNlLg0KDQojIyBTYWxlcyBNZXRob2QgUGVyZm9ybWFuY2UNCg0KQWRpZGFzIE9ubGluZSBzYWxlcyBtZXRob2QgcGVyZm9ybWVkIGJldHRlciBhbmQgaW4tc3RvcmUgaGFzIHRoZSBsZWFzZXQgcGVyZm9ybWFuY2UNCg0KYGBge3J9DQogIHNvcnQodGFibGUoQWRpZGlhcyRzYWxlc19tZXRob2QpLCBkZWNyZWFzaW5nID0gVCkNCmBgYA0KDQojIyBFeHBsb3JhdG9yeSBBbmFseXNpcw0KU3RhdGlzdGljYWwgQW5hbHlzaXMNCg0KQ29ycmVsYXRpb24gcmVsYXRpb25zaGlwIG9uIFVuaXQgU29sZCBhbmQgb3ByYXRpb25fcHJvZml0LCB0aGUgZmlyc3QgY29kZSBnaXZlcyB0aGUgb3V0cHV0IG9mIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzIHdpdGggb3V0IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgsdGhlIGNvZGUgc25pcHBldHMgZ2l2ZXMgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4gQXQgMC44OSwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9wcmF0aW9uYWxfcHJvZml0IGFuZCB1bml0c19zb2xkIGlzIGRpcmVjdGx5IHByb3BvcnRpb25hbC4NCg0KDQpgYGB7cn0NCmNvcihBZGlkaWFzJHVuaXRzX3NvbGQsIEFkaWRpYXMkb3BlcmF0aW5nX3Byb2ZpdCkNCmNvcihBZGlkaWFzWyxjKCdvcGVyYXRpbmdfcHJvZml0JywgJ3VuaXRzX3NvbGQnKV0pDQpgYGANCg0KIyMgUmVncmVzc2lvbiBPcHJlYXRpb25fcHJvZml0IE9uIHVuaXRzX3NvbGQgV2l0aCBBIFNjYXR0ZXIgUGxvdA0KDQp0aGUgc3RhdF9zbW9vdGggZnVuY3Rpb24gZnJvbSB0aGUgZ2dwbG90MiBwYWNrYWdlIHdhcyB1c2VkIHRvIGFkZCByZWdyZXNzaW9uIGZ1bmN0aW9uIG9uIHRoZSBwbG90LiBUaGUgImxtIiB2YWx1ZSBmb3IgdGhlIG1ldGhvZCBwYXJhbWV0ZXIgaW5kaWNhdGVzIHRoYXQgSSBhbSB1c2luZyBhIGxpbmVhciBtb2RlbCB0byBhZGQgYSB0cmVuZCBsaW5lIHRvIHRoZSBzY2F0dGVyIHBsb3QuDQoNCmBgYHtyfQ0KZ2dwbG90KEFkaWRpYXMsIGFlcyh4ID11bml0c19zb2xkICwgeSA9IG9wZXJhdGluZ19wcm9maXQpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICdsbScpDQpgYGANCg0KY29uY2VwdHVhbGx5LCB0aGlzIGFsZ29yaXRobSBjYWxjdWxhdGVzIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIGEgbGluZSB0aGF0IG1hdGhlbWF0aWNhbGx5IHJlZHVjZXMgdGhlIHN1bSBvZiB0aGUgZGlzdGFuY2UgYmV0d2VlbiBhbGwgb2YgdGhlIGRhdGEgcG9pbnRzIGFuZCB0aGUgbGluZS4NCg0KIyMgUmVzaWR1YWxzIEFuYWx5c2lzDQoNCmNyZWF0ZWQgYSBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCBvcGVyYXRpb25hbCBieSByZWdyZXNzaW5nIHVuaXRzX3NvbGQgZnJvbSB0aGUgQWRpZGFzIGRhdGEgZnJhbWUsIGFuZCB0aGVuIGxvb2sgYXQgYSBzdW1tYXJ5IG9mIHRoZSBtb2RlbC4NCg0KYGBge3J9DQpsbV9hIDwtIGxtKG9wZXJhdGluZ19wcm9maXQgIH4gdW5pdHNfc29sZCwgZGF0YSA9IEFkaWRpYXMpDQpzdW1tYXJ5KGxtX2EpDQoNCmBgYA0KDQpSZXNpZHVhbHMgYXJlIHNpbXBseSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBhY3R1YWwgb2JzZXJ2YXRpb25zIG9mIG9wcmF0aW9uX3JldmVudWUgdGhhdCB3ZXJlIHVzZWQgdG8gY3JlYXRlIHRoZSBtb2RlbCBhbmQgdGhlIHZhbHVlcyBvZiB1bml0X3NhbGVzIHRoYXQgYXJlIGZpdHRlZCBmcm9tIHRoZSBtb2RlbC4NCg0KIyMgQ3JlYXRpbmcgUmVzaWR1YWwgRGF0YUZyYW1lIA0KVG8gbG9vayBhdCBzb21lIHNwZWNpZmljIG9ic2VydmF0aW9ucyBJIHdpbGwgZmlyc3QgY3JlYXRpbmcgYSBkYXRhZnJhbWUgdGhhdCBoYXMgdGhlIGFjdHVhbCB2YWx1ZXMgb2YgVW5pdF9zYWxlcywgdGhlIG9wcmVhdGluZ19yZXZlbnVlLCBhbmQgdHdvIG5ldyBjb2x1bW5zOiBPbmUgZml0dGVkT3ByZWF0aW5nX3Byb2ZpdCwgdGhpcyB3aWxsIGJlIGNyZWF0ZWQgdXNpbmcgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcyBmcm9tIHRoZSBsaW5lYXIgbW9kZWwuIFRoZSBzZWNvbmQgY29sdW1uIHdpbGwgYmUgY3JlYXRlZCwgcmVzaWR1YWxzLCBieSBzdWJ0cmFjdGluZyB0aGUgdmFsdWVzIGZyb20gdGhlIGZpdHRlZE9wcmVhdGluZ19wcm9maXQsIGNvbHVtbiBmcm9tIHRoZSBvcHJlYXRpbmdfcHJvZml0Lg0KDQpgYGB7cn0NCnJlc2lkIDwtIEFkaWRpYXMgJT4lDQogIHNlbGVjdChvcGVyYXRpbmdfcHJvZml0LCB1bml0c19zb2xkKSAlPiUNCiAgbXV0YXRlKGZpdHRlZE9wcmVhdGluZ19wcm9maXQgPSAtMjM1NjguNzk5ICsgMjI1LjcxOSp1bml0c19zb2xkDQogICAgICAgICAsIHJlc2lkdWFscyA9IG9wZXJhdGluZ19wcm9maXQgLSBmaXR0ZWRPcHJlYXRpbmdfcHJvZml0KQ0KaGVhZChyZXNpZCkNCmBgYA0KDQpUaGUgZmlyc3Qgcm93IGluZGljYXRlcyB0aGF0IHdoZW4gdGhlIHZhbHVlIG9mIFVuaXRfc29sZCBpcyBlcXVhbCB0byAxLDIwMCwgdGhlIGZpdHRlZCB2YWx1ZSBvZiBvcHJlYXRpbmdfcHJvZml0IGlzIDI0NywyOTQuMC4gSG93ZXZlciwgdGhlIGFjdHVhbCBvcHJhdGluZ19wcm9maXQgb2YgMzAwLDAwMCB3YXMgYWJvdmUgdGhlIGFtb3VudCBieSA1Miw3MDYuIEluIG90aGVyIHdvcmRzLCB0aGUgYWN0dWFsIHZhbHVlIG9mIG9wcmVhdGluZ19wcm9maXQgaXMgKmFib3ZlKiB0aGUgbGluZSBjcmVhdGVkIGJ5IHRoZSBsaW5lYXIgbW9kZWwuDQoNCmxvb2tpbmcgYXQgdGhlIHNlY29uZCByb3csIHdoaWNoIGluZGljYXRlcyB0aGF0IHdoZW4gdGhlIHZhbHVlIG9mIHVuaXRfc29sZCBpcyBlcXVhbCB0byAxMDAwLCB0aGUgZml0dGVkIHZhbHVlIG9mIG9wcmVhdGluZ19wcm9maXQgaXMgMjAyMTUwLjIuIFRoaXMgdGltZSwgdGhlIGFjdHVhbCB2YWx1ZSBvZiBvcHJlYXRpbmdfcHJvZml0IG9mIDE1MDAwMCBpcyBiZWxvdyB0aGF0IGFtb3VudCBieSA1MjE1MC4yMCBtZWFuaW5nIHRoYXQgaXQgZmFsbHMgKmJlbG93KiB0aGUgbGluZSBjcmVhdGVkIGJ5IHRoZSBsaW5lYXIgbW9kZWwuDQoNCiMjIEJ1c2luZXNzIE1hbmFnZW1lbnQgQXBwbGljYXRpb24gb2YgUmVzaWR1YWxzDQoNCkFzIGEgQnVzaW5lc3MgbWFuYWdlciBvZiBBZGlkYXMgU2FsZXMgdGhhdCB3YW50cyB0byBzZWUgIkhvdyBhcmUgcXVhcnRlcmx5IHNhbGVzIGFmZmVjdGVkIGJ5IHF1YXJ0ZXIgb2YgdGhlIHllYXIsIHJlZ2lvbiwgYW5kIGJ5IHByb2R1Y3Q/IGNhbiBoYXZlIGEgY2x1ZSBmcm9tIHJlc2lkdWFsIGFuYWx5c2lzIElmIHdlIHRoaW5rIG9mIHRoZSBmaXR0ZWQgdmFsdWVzIGFzIGEgdGFyZ2V0LCBvciBleHBlY3RhdGlvbiBvZiB3aGF0IG9wcmVhdGluZ19wcm9maXQgc2hvdWxkIGJlLCB0aGVuIHRoZSByZXNpZHVhbCB0ZWxscyB1cyB3aGV0aGVyIHRoYXQgcmV2ZW51ZSBpcyBtb3JlIG9yIGxlc3MgdGhhbiBleHBlY3RlZC4gSW4gb3RoZXIgd29yZHMsIGl0IGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgdmFyaWFuY2UuDQoNCiMjIFJlc2lkdWFsIEFuYWx5c2lzICBFeHBsYWluaW5nIHRoZSBwcm9qZWN0IHN0YXRlbWVudA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggcmVzaWR1YWxzIGFuZCBpZGVudGlmeWluZyBpbmZvcm1hdGlvbg0KcmVzaWRzMiA8LSBBZGlkaWFzICU+JQ0KICBzZWxlY3QocmV0YWlsZXIscHJvZHVjdCxyZWdpb24sIGRhdGVfcXVhdGVyLCB1bml0c19zb2xkLCBvcGVyYXRpbmdfcHJvZml0KQ0KcmVzaWRzMiRmaXR0ZWRPcHJlYXRpbmdfcHJvZml0ID0gbG1fYSRmaXR0ZWQudmFsdWVzDQpyZXNpZHMyJHJlc2lkdWFscyA9IGxtX2EkcmVzaWR1YWxzDQoNCiMgR2V0IHRoZSBmaXZlIGJlc3QgcGVyZm9ybWluZyBzdG9yZS9xdWFydGVyIGNvbWJpbmF0aW9ucw0KYmVzdCA8LSByZXNpZHMyICU+JQ0KICBhcnJhbmdlKGRlc2MocmVzaWR1YWxzKSkgJT4lDQogIC5bMTo0LF0NCiMgR2V0IHRoZSBmaXZlIHdvcnN0IHBlcmZvcm1pbmcgc3RvcmUvcXVhcnRlciBjb21iaW5hdGlvbnMNCndvcnN0IDwtIHJlc2lkczIgJT4lDQogIGFycmFuZ2UocmVzaWR1YWxzKSAlPiUNCiAgLlsxOjQsXSAlPiUNCiAgYXJyYW5nZShkZXNjKHJlc2lkdWFscykpDQoNCiMgQ29tYmluZSB0aGUgZml2ZSBiZXN0IGFuZCB3b3JzdCBpbnRvIG9uZSBkYXRhZnJhbWUgYW5kIGRpc3BsYXkgdGhlbQ0KYmVzdFdvcnN0IDwtIGJpbmRfcm93cyhiZXN0LHdvcnN0KQ0KYmVzdFdvcnN0DQpgYGANCg0KKiBCZXN0IHN0b3JlcyBhcmUgV2FsbWFydCBhbmQgU3BvcnRzIERpcmVjdCwgYmVzdCBwcm9kdWN0IGlzIFdvbWVuJ3MgQXBwYXJlbA0KICB0aGUgc3RvcmUgYXQgU291dGhlYXN0IGJlYXQgdGhlaXIgZ29hbCBkdXJpbmcgc2Vjb25kIGFuZCB0aGlyZCBxdWFydGVycyBmb3IgMjAyMSAgIGJ5IGF0IGxlYXN0ICQxOTgsNjAxLjINCiogVGhlIHN0b3JlIGF0IFNvdXRoIGFsc28gYmVhdCBpdHMgZXhwZWN0YXRpb24gZHVyaW5nIHRoaXJkIGFuZCBmb3J0IHF1YXJ0ZXIgYnkgYXQgICBsZWFzdCAkMTU0LDk5My4zLg0KKiBXb3JzdCBzdG9yZXMgYXJlIFdlc3QgR2VhciBhbmQgU3BvcnRzIERpcmVjdCBpbiB0aGUgV2VzdCBhbmQgU291dGggZm9yIE1lbidzICAgICAgIEF0aGxldGljIEZvb3R3ZWFyIGFuZCBNZW4ncyBTdHJlZXQgRm9vdHdlYXIuDQoqIFRoZSBzdG9yZSBhdCBXZXN0IG1pc3NlZCBpdHMgZXhwZWN0ZWQgcXVhcnRlcmx5IG9wcmF0aW5nX3Byb2ZpdCBmb3IgZmlyc3QgICAgICAgICAgcXVhcnRlcnMgb2YgMjAyMSBieSBhdCBsZWFzdCAkMTA4LDc1Ny42Lg0KKiBUaGUgc3RvcmUgaW4gdGhlIFNvdXRoIGFsc28gbWlzc2VkIGl0cyBleHBlY3RlZCBxdWFydGVybHkgb3ByYXRpbmdfcHJvZml0cyBmb3IgICAgIHRoaXJkIHF1YXJ0ZXIgaW4gMjAyMSBieSBhYm91dCAkMTAyLDkzNC43Lg0KDQojIyBSZWNvbW1lbmRhdGlvbg0KDQpUaGUgbWFuYWdlciwgbWF5IHdhbnQgdG8gbG9vayBpbnRvIHRoZSB0d28gc3RvcmVzIHRoYXQgdW5kZXIgcGVyZm9ybWVkIGR1cmluZyAyMDIxIHRvIHdvcmsgb24gaW1wcm92aW5nIHRoZWlyIHBlcmZvcm1hbmNlLiBJbiBjb250cmFzdCwgSGUgbWF5IHdhbnQgdG8gbG9vayBpbnRvIHRoZSB0d28gc3RvcmVzIHRoYXQgb3V0cGVyZm9ybWVkIGR1cmluZyAyMDIxIHRvIGZpbmQgb3V0IGlmIHRoZWlyIGJlc3QgcHJhY3RpY2VzIGNhbiBiZSByZXBsaWNhdGVkIGluIG90aGVyIGxvY2F0aW9ucy4NCg0KIyMgUGFydCBUd28NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCidSZXNpZHVhbCBBbmFseXNpczogQW4gUiBQcm9qZWN0IE9OIEhvdyBBZGlkYXMgUXVhcnRlcmx5IFNhbGVzIEFyZSBBZmZlY3RlZCBCeSBRdWFydGVyIE9mIFRoZSB5ZWFyLCBSZWdpb24sIEFuZCBCeSBQcm9kdWN0JyBQYXJ0IDIgVXNpbmcgRHVtbXkgdmFyaWFibGVzIHRvIGFuYWx5c2UgQWRpZGFzIHF1YXJ0ZXIgb2YgdGhlIHllYXIgcHJvZHVjdCBwZXJmb3JtYW5jZXMNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFByb2plY3QgU3RhdGVtZW50Og0KDQoiSG93IGFyZSBxdWFydGVybHkgc2FsZXMgYWZmZWN0ZWQgYnkgcXVhcnRlciBvZiB0aGUgeWVhciwgcmVnaW9uLCBhbmQgYnkgcHJvZHVjdD8NCg0KIyMgUXVhbGl0YXRpdmUgVmFyaWFibGVzDQoNCkkgd2lsbCBiZSB1c2luZyBRdWFsaXRhdGl2ZSB2YXJpYWJsZSBxdWFydGVyIG9mIHRoZSB5ZWFyLCBob3dldmVyLCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMsIGluY2x1ZGluZyByZWdyZXNzaW9uLCByZWx5IG9uIG51bWVyaWMgdmFsdWVzLiBTbyB3ZSBoYXZlIHRvIGNvbnZlcnQgcXVhbGl0YXRpdmUgdmFyaWFibGVzIHRvIG51bWVyaWMgdmFyaWFibGVzLg0KDQojIyBEdW1teSBWYXJpYWJsZXMNCg0KV2hhdCBpcyBvZnRlbiBkb25lIGlzIGEgc2VyaWVzIG9mIGJpbmFyeSB2YXJpYWJsZXMgaXMgdXNlZCB0byBjYXB0dXJlIHRoZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRoZSBxdWFsaXRhdGl2ZSB2YXJpYWJsZS4gU3BlY2lmaWNhbGx5LCB3ZSB3b3VsZCByZXBsYWNlIHRoZSBxdWFydGVyIG9mIHRoZSB5ZWFyIHZhcmlhYmxlLCBxdWFydGVyX05vWWVhciwgd2l0aCB0aHJlZSB2YXJpYWJsZXM6IFNlY29uZCwgVGhpcmQsIGFuZCBGb3VydGguIFRoZSB2YWx1ZXMgaW4gdGhlc2UgY29sdW1ucyB0YWtlIG9uIGEgdmFsdWUgb2YgMSBpZiB0aGUgb2JzZXJ2YXRpb24gZml0cyBpbnRvIHRoYXQgY2F0ZWdvcnksIGFuZCBhIHZhbHVlIG9mIHplcm8gb3RoZXJ3aXNlLiBXZSBvbmx5IG5lZWQgdGhyZWUgY29sdW1ucyBiZWNhdXNlIGlmIHRoZXkgYWxsIGhhdmUgYSB2YWx1ZSBvZiAwLCB0aGVuIHRoYXQgbWVhbnMgdGhlIG9ic2VydmF0aW9uIGZpdHMgaW50byB0aGUgZmlyc3QgcXVhcnRlci4NCg0KSGVyZSdzIGEgZGF0YWZyYW1lIHRvIGlsbHVzdHJhdGUgdGhhdCBpZGVhIHdpdGggYSBiaXQgbW9yZSBkZXRhaWw6DQoNCmBgYHtyfQ0KDQoNCmRhdGEuZnJhbWUoJ3F1YXJ0ZXJfY2hhcicgPSBjKCdGaXJzdCcsICdTZWNvbmQnLCAnVGhpcmQnLCAnRm91cnRoJyksDQogICAgICAgICAgICAgICAgICdxdWFydGVyU2Vjb25kJyA9IGMoMCwgMSwgMCwgMCksDQogICAgICAgICAgICAgICAgICdxdWFydGVyVGhpcmQnID0gYygwLCAwLCAxLCAwKSwNCiAgICAgICAgICAgICAgICAgJ3F1YXJ0ZXJGb3VydGgnID0gYygwLCAwLCAwLCAxKSkNCmBgYA0KDQojIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBwZXJhdGluZ19wcm9maXQgb24gcXVhcnRlcl9jaGFyIGNvbHVtbg0KDQpSZWdyZXNzaW9uIG9mIG9wZXJhdGluZ19wcm9maXQgb24gcXVhcnRlcl9jaGFyIGNvbHVtbiwgd2hpY2ggYXMgYSBkYXRhIHR5cGUgb2YgZmFjdG9yLg0KDQpgYGB7cn0NCmxtX2I8LSBsbShvcGVyYXRpbmdfcHJvZml0ICB+IHF1YXJ0ZXJfY2hhciwgZGF0YSA9IEFkaWRpYXMpDQpzdW1tYXJ5KGxtX2IpDQpgYGANCg0KVGhlcmUgaXMgYSBjb2VmZmljaWVudCBlc3RpbWF0ZSBmb3IgdGhlIHNlY29uZCB0aHJvdWdoIGZvdXJ0aCBxdWFydGVycywgYnV0IG5vdCBmb3IgdGhlIGZpcnN0IHF1YXJ0ZXIuIFRoZSBpbnRlcmNlcHQgcmVwcmVzZW50cyB0aGUgZXN0aW1hdGUgb2Ygb3BlcmF0aW5nX3Byb2ZpdCBmb3IgdGhlIGZpcnN0IHF1YXJ0ZXIsIGFuZCB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzIGZvciB0aGUgb3RoZXIgdmFyaWFibGVzIHJlcHJlc2VudCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoYXQgcXVhcnRlciBmcm9tIHRoZSBmaXJzdCBxdWFydGVyLiB0aGUgb3BlcmF0aW5nX3Byb2ZpdCBmb3IgdGhpcmQgcXVhcnRlciBpcyAxMywxNDIgaGlnaGVyIHRoYW4gdGhlIGJhc2VsaW5lIGVzdGltYXRlZCBhbmQgNyw3NzkgaGlnaGVyIHRoYW4gdGhlIGJhc2VsaW5lIGVzdGltYXRlZCBpbiBzZWNvbmQgcXVhcnRlci4NCg0KIyMgTWFudWFsIENvbXBhcmlzb24gb2YgTWVhbg0KTGV0J3MgY3JlYXRlIGEgbWFudWFsIGNvbXBhcmlzb24gYnkgY2FsY3VsYXRpbmcgdGhlIG1lYW4gdmFsdWUgb2Ygb3BlcmF0aW5nX3Byb2ZpdCBmb3IgZWFjaCBxdWFydGVyLg0KDQpgYGB7cn0NCkFkaWRpYXMgJT4lDQogIGdyb3VwX2J5KHF1YXJ0ZXJfY2hhcikgJT4lDQogIHN1bW1hcml6ZShtZWFuUHJvZml0ID0gbWVhbihvcGVyYXRpbmdfcHJvZml0KSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNClRoZSB2YWx1ZSBvZiBtZWFuUHJvZml0IGZvciB0aGUgc2Vjb25kIHF1YXJ0ZXIsIDM1LDIzOC42MiBpcyBoaWdoZXIgdGhhbiB0aGUgdmFsdWUgb2YgbWVhblByb2ZpdCBmb3IgdGhlIGZpcnN0IHF1YXJ0ZXIgYnkgNyw3NzguNzgsIHdoaWNoIGlzIHJlcHJlc2VudGVkIGJ5IHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZSBvbiBxdWFydGVyX2NoZXJTZWNvbmQgaW4gdGhlIHJlZ3Jlc3Npb24gbW9kZWwuDQoNClRoZXJlJ3MgYSBzaW1pbGFyIHJlbGF0aW9uc2hpcCBmb3IgdGhlIG90aGVyIHR3byBxdWFydGVycyBhbmQgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgZmlyc3QgcXVhcnRlciBhbmQgdGhlaXIgY29lZmZpY2llbnQgZXN0aW1hdGVzLg0KDQpJbiB0ZXJtcyBvZiBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIGFsbCB0aGUgY29lZmZpY2llbnRzIGFyZSBoaWdobHkgc2lnbmlmaWNhbnQgKHAtdmFsdWVzIDwgMC4wMDEpLiBUaGlzIG1lYW5zIHRoYXQgdGhlIHF1YXJ0ZXJseSB2YXJpYWJsZXMgaGF2ZSBhIHNpZ25pZmljYW50IGltcGFjdCBvbiBvcGVyYXRpbmcgcHJvZml0Lg0KDQojIyBDb21wYXJpbmcgdHdvIHNpbXBsZSByZWdyZXNzaW9uIG1vZGVsDQoNCmBgYHtyfQ0KbG1fYSA8LSBsbShvcGVyYXRpbmdfcHJvZml0ICB+IHVuaXRzX3NvbGQsIGRhdGEgPSBBZGlkaWFzKQ0KDQpsbV9iPC0gbG0ob3BlcmF0aW5nX3Byb2ZpdCAgfiBxdWFydGVyX2NoYXIsIGRhdGEgPSBBZGlkaWFzKQ0KZXhwb3J0X3N1bW1zKGxtX2EsIGxtX2IpICMgQ3JlYXRlIGEgbmljZSBsb29raW5nIHRhYmxlIGZvciBjb21wYXJpbmcgdGhlIHJlZ3Jlc3Npb24gcmVzdWx0cw0KYGBgDQoNClRoZSB0YWJsZSBpbmRpY2F0ZXMgdGhlIGtleSB0YWtlYXdheXMgZnJvbSBib3RoIGxpbmVhciBtb2RlbHM6DQoqIENvZWZmaWNpZW50IGVzdGltYXRlczoNCiogTW9kZWwgMSwgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlIGlzICAtMjM1NjguODAgZm9yIHRoZSBpbnRlcmNlcHQgYW5kIGlzICAyMjUuNzIgICBmb3IgVW5pdF9zb2xkLg0KKiBGb3IgbW9kZWwgMiwgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlIGlzIDI3NDU5Ljg0IGZvciB0aGUgaW50ZXJjZXB0IGFuZCBpcyAgICAgICAgIDY5MjUuNDAgZm9yIHF1YXJ0ZXJfY2hhckZvdXJ0aCwgIDc3NzguNzggZm9yICBxdWFydGVyX2NoYXJTZWNvbmQgYW5kIDEzMTQyLjM4IGZvciAgIHF1YXJ0ZXJfY2hhclRoaXJkIA0KKiBUaGUgc3RhbmRhcmQgZXJyb3JzIGFyZSBpbiBwYXJlbnRoZXNlcyBiZWxvdyB0aGVzZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMuDQoqIE4gc3RhbmRzIGZvciBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLCBhbmQgdGhleSBhcmUgYm90aCBiYXNlZCBvbiA5NjQ4ICAgICAgICAgICAgICAgb2JzZXJ2YXRpb25zLg0KKiBSMiBpcyB0aGUgUi1zcXVhcmVkLCB3aGljaCBpcyBtdWNoIGxhcmdlciBmb3IgbW9kZWwgMSwgODAlLCB0aGFuIGZvciBtb2RlbCAyLCAxJS4gICBUaGlzIG1lYW5zIHRoYXQgbW9kZWwgMSBleHBsYWlucyBtb3JlIHZhcmlhdGlvbiBpbiBvcHJhdGlpbmdfcHJvZml0IHRoYW4gbW9kZWwgMiwgICBhbmQgaXMgYmV0dGVyIGZvciBtYWtpbmcgcHJlZGljdGlvbnMuDQoNCiMjIFZpc3VhbGl6aW5nIGNvZWZmaWNpZW50cyBhbmQgc3RhbmRhcmQgZXJyb3JzDQoNCkxldCdzIHVzZSB0aGUganRvb2xzIHBhY2thZ2UgdG8gcGxvdCB0aGUgY29lZmZpY2llbnRzIGFuZCBzdGFuZGFyZCBlcnJvcnMgdG8gaGVscCB2aXN1YWxpemUgdGhlIHJlc3VsdHMuDQoNCmBgYHtyfQ0KbGlicmFyeShqdG9vbHMpDQpwbG90X3N1bW1zKGxtX2EsIGxtX2IpDQpgYGANClRoaXMgdmlzdWFsaXphdGlvbiBpcyBleGNlbGxlbnQuIEl0IGNsZWFybHkgc2hvd3MgdGhhdCB0aGUgY29lZmZpY2llbnQgb24gVW5pdF9zb2xkIGZyb20gbW9kZWwgMSBpcyBwb3NpdGl2ZSwgYW5kIGhhcyBhIG11Y2ggc21hbGxlciBzdGFuZGFyZCBlcnJvciByZWxhdGl2ZSB0byB0aGUgcG9zaXRpdmUgY29lZmZpY2llbnQgb24gcXVhcnRlcl9jaGFyIGZyb20gbW9kZWwgMi4gVGhlIGxvbmcgb3JhbmdlIHdoaXNrZXJzIGFuZCB0aGUgc2hvcnQsIGJhcmVseSB2aXNpYmxlIGJsdWUgd2hpc2tlcnMgcmVwcmVzZW50IHRoZSBzdGFuZGFyZCBlcnJvciwgb3IgdGhlIHJhbmdlIGluIHdoaWNoIHRoZSBhY3R1YWwgdmFsdWUgY291bGQgYmUuDQoNCiMjIFRoZSBVbmlxdWUgRWZmZWN0IG9mIFF1YXJ0ZXIgb2YgdGhlIFllYXINClF1YXJ0ZXIgb2YgdGhlIHllYXIgbWF5IGhhdmUgYSBzaWduaWZpY2FudCBlZmZlY3Qgb24gcXVhcnRlcmx5IG9wcmF0aW5nX3Byb2ZpdCBhZnRlciBjb250cm9sbGluZyBmb3IgdGhlIHBlcmNlbnRhZ2Ugb2Ygc2FsZXMgdGhhdCBjb21lIGZyb20gb3RoZXIgcHJvZHVjdHMuIExldCdzIHRlc3QgdGhpcyBvdXQgYnkgaW5jbHVkaW5nIGl0IHdpdGggdGhlIHVuaXRfc29sZCB2YXJpYWJsZXMgdGhhdCB3ZSBoYXZlIGFscmVhZHkgaW52ZXN0aWdhdGVkLg0KDQpgYGB7cn0NCmxtX2MgPC0gbG0ob3BlcmF0aW5nX3Byb2ZpdCAgfiB1bml0c19zb2xkICsgcXVhcnRlcl9jaGFyLGRhdGEgPSBBZGlkaWFzKQ0KZXhwb3J0X3N1bW1zKGxtX2EsIGxtX2IsIGxtX2MpDQpgYGANCg0KcmVncmVzc2lvbiBhbmFseXNpcyB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgInVuaXRzX3NvbGQiIHZhcmlhYmxlIHlpZWxkcyB0aGUgZm9sbG93aW5nIHJlc3VsdHM6DQoNClRoZSBjb2VmZmljaWVudCBmb3IgdGhlICJ1bml0c19zb2xkIiB2YXJpYWJsZSBpcyAyMjYuNDI0IHdpdGggYSBzdGFuZGFyZCBlcnJvciBvZiAxLjE1Mi4gVGhpcyBtZWFucyB0aGF0IGZvciBlYWNoIGFkZGl0aW9uYWwgdW5pdCBzb2xkLCB0aGUgb3BlcmF0aW5nIHByb2ZpdCBpcyBlc3RpbWF0ZWQgdG8gaW5jcmVhc2UgYnkgMjI2LjQyNC4gVGhlIGNvZWZmaWNpZW50IGlzIGhpZ2hseSBzaWduaWZpY2FudCAocC12YWx1ZSA8IDAuMDAxKSwgaW5kaWNhdGluZyBhIHN0cm9uZyBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB1bml0cyBzb2xkIGFuZCBvcGVyYXRpbmcgcHJvZml0Lg0KDQpUaGUgY29lZmZpY2llbnQgZm9yIHRoZSAicXVhcnRlcl9jaGFyRm91cnRoIiB2YXJpYWJsZSBpcyAxMSwyNjguOTE1IHdpdGggYSBzdGFuZGFyZCBlcnJvciBvZiA2OTYuNDM4LiBUaGlzIGNvZWZmaWNpZW50IHJlcHJlc2VudHMgdGhlIGRpZmZlcmVuY2UgaW4gb3BlcmF0aW5nIHByb2ZpdCBiZXR3ZWVuIHRoZSBmb3VydGggcXVhcnRlciBhbmQgdGhlIGJhc2VsaW5lIHF1YXJ0ZXIsIHdoaWxlIGhvbGRpbmcgdGhlIG51bWJlciBvZiB1bml0cyBzb2xkIGNvbnN0YW50LiBUaGUgcG9zaXRpdmUgY29lZmZpY2llbnQgc3VnZ2VzdHMgdGhhdCB0aGUgZm91cnRoIHF1YXJ0ZXIgdGVuZHMgdG8gaGF2ZSBoaWdoZXIgb3BlcmF0aW5nIHByb2ZpdCBjb21wYXJlZCB0byB0aGUgYmFzZWxpbmUgcXVhcnRlci4NCg0KQWxsIGNvZWZmaWNpZW50cyBhcmUgaGlnaGx5IHNpZ25pZmljYW50IChwLXZhbHVlcyA8IDAuMDAxKSwgaW5kaWNhdGluZyB0aGVpciBzdHJvbmcgcmVsYXRpb25zaGlwIHdpdGggb3BlcmF0aW5nIHByb2ZpdC4NCg0KVGhlIG11bHRpcGxlIFItc3F1YXJlZCB2YWx1ZSBpcyAwLjgwMTksIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgd2l0aCAidW5pdHNfc29sZCIgYW5kIHF1YXJ0ZXJseSB2YXJpYWJsZXMgZXhwbGFpbnMgYXBwcm94aW1hdGVseSA4MC4xOSUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIG9wZXJhdGluZyBwcm9maXQuIFRoZSBhZGp1c3RlZCBSLXNxdWFyZWQgdmFsdWUgaXMgMC44MDE4LCB3aGljaCB0YWtlcyBpbnRvIGFjY291bnQgdGhlIG51bWJlciBvZiBwcmVkaWN0b3JzIGluIHRoZSBtb2RlbC4NCg0KVGhlIEYtc3RhdGlzdGljIGlzIDk3NTYgd2l0aCBhIHAtdmFsdWUgPCAyLjJlLTE2LCBpbmRpY2F0aW5nIHRoYXQgdGhlIG92ZXJhbGwgbW9kZWwgaXMgaGlnaGx5IHNpZ25pZmljYW50Lg0KDQpUaGlzIGNoYW5nZSBpcyBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZWQgYnkgdmlzdWFsaXppbmcgdGhlIGNvZWZmaWNpZW50cyBmcm9tIGFsbCB0aHJlZSBtb2RlbHMuDQpgYGB7cn0NCnBsb3Rfc3VtbXMobG1fYSwgbG1fYiwgbG1fYykNCmBgYA0KIyMgQ29uY2x1c2lvbg0KQmFzZWQgb24gTW9kZWwgMiBhbmQgTW9kZWwgMywgaXQgYXBwZWFycyB0aGF0IHRoZSBxdWFydGVyX2NoYXJUaGlyZCB2YXJpYWJsZSBoYWQgdGhlIGxhcmdlc3QgcG9zaXRpdmUgY29lZmZpY2llbnQgYW5kIHdhcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGluIGJvdGggbW9kZWxzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIHRoaXJkIHF1YXJ0ZXIgKHF1YXJ0ZXJfY2hhclRoaXJkKSBoYWQgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgcG9zaXRpdmUgaW1wYWN0IG9uIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgY29tcGFyZWQgdG8gdGhlIG90aGVyIHF1YXJ0ZXJzIChxdWFydGVyX2NoYXJGb3VydGggYW5kIHF1YXJ0ZXJfY2hhclNlY29uZCkuIEhvd2V2ZXIsIHRoZSBwcmFjdGljYWwgc2lnbmlmaWNhbmNlIGFuZCBpbXBsaWNhdGlvbnMgbWF5IHZhcnkgZGVwZW5kaW5nIG9uIHRoZSBzcGVjaWZpYyBzaXR1YXRpb24gb3IgZG9tYWluIGJlaW5nIHN0dWRpZWQuDQoNCg0KDQo=