–

The job is to perform data cleaning and data preprocessing techniques in R programming, analyze, visualize, and conclude our analysis based on our research and results which we find after applying data preprocessing techniques and analysis.

Data Cleaning:

library(tidyverse) # Load CSV files
df <- read_csv("sales_data.csv")

-- Column specification -------------------------------------------------------------------------------------------------------------------------------------
cols(
  `Invoice ID` = col_character(),
  Branch = col_character(),
  City = col_character(),
  `Customer type` = col_character(),
  Gender = col_character(),
  `Product line` = col_character(),
  `Unit price` = col_double(),
  Quantity = col_double(),
  `Tax 5%` = col_double(),
  Total = col_double(),
  Date = col_character(),
  Time = col_time(format = ""),
  Payment = col_character(),
  cogs = col_double(),
  `gross margin percentage` = col_double(),
  `gross income` = col_double(),
  Rating = col_double()
)
# Get to know the data
head(df)
df[order(df$Date ),]
dim(df) # To check number of rows and columns
[1] 1000   17
summary(df) # description of the data 
  Invoice ID           Branch              City           Customer type         Gender          Product line         Unit price       Quantity    
 Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Min.   :10.08   Min.   : 1.00  
 Class :character   Class :character   Class :character   Class :character   Class :character   Class :character   1st Qu.:32.88   1st Qu.: 3.00  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :55.23   Median : 5.00  
                                                                                                                   Mean   :55.67   Mean   : 5.51  
                                                                                                                   3rd Qu.:77.94   3rd Qu.: 8.00  
                                                                                                                   Max.   :99.96   Max.   :10.00  
                                                                                                                                                  
     Tax 5%            Total             Date               Time            Payment               cogs        gross margin percentage  gross income    
 Min.   : 0.5085   Min.   :  10.68   Length:1000        Length:1000       Length:1000        Min.   : 10.17   Min.   :4.762           Min.   : 0.5085  
 1st Qu.: 5.8948   1st Qu.: 124.42   Class :character   Class1:hms        Class :character   1st Qu.:118.50   1st Qu.:4.762           1st Qu.: 5.9249  
 Median :12.0960   Median : 253.85   Mode  :character   Class2:difftime   Mode  :character   Median :241.76   Median :4.762           Median :12.0880  
 Mean   :15.3855   Mean   : 322.97                      Mode  :numeric                       Mean   :307.59   Mean   :4.762           Mean   :15.3794  
 3rd Qu.:22.4605   3rd Qu.: 471.35                                                           3rd Qu.:448.90   3rd Qu.:4.762           3rd Qu.:22.4453  
 Max.   :49.6500   Max.   :1042.65                                                           Max.   :993.00   Max.   :4.762           Max.   :49.6500  
 NA's   :1                                                                                                                                             
     Rating      
 Min.   : 4.000  
 1st Qu.: 5.500  
 Median : 7.000  
 Mean   : 6.973  
 3rd Qu.: 8.500  
 Max.   :10.000  
                 

Here, we need to make sure that date is converted to date time

# Converting date to Datetime
library(lubridate)
df$Date <- parse_date_time(x = df$Date,
                orders = c("d m y", "d B Y", "m/d/y"),
                locale = "eng")

View the data again to verify the DATE data type:

df[order(df$Date ),]

Checking the null values:  
===========================

summary(is.na(df))  # To check null values
 Invoice ID        Branch           City         Customer type     Gender        Product line    Unit price       Quantity         Tax 5%       
 Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical  
 FALSE:1000      FALSE:1000      FALSE:998       FALSE:1000      FALSE:998       FALSE:1000      FALSE:1000      FALSE:1000      FALSE:999      
                                 TRUE :2                         TRUE :2                                                         TRUE :1        
   Total            Date            Time          Payment           cogs         gross margin percentage gross income      Rating       
 Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical   Mode :logical           Mode :logical   Mode :logical  
 FALSE:1000      FALSE:999       FALSE:1000      FALSE:1000      FALSE:1000      FALSE:1000              FALSE:1000      FALSE:1000     
                 TRUE :1                                                                                                                

This means that we have:
- 2 missing inputs in column cities,
- 2 missing inputs in column gender,
- 1 missing input in column Tax 5%,
- 1 missing input in column Date

we use the library naniar to count the remaining missing values in our attributes

#install.packages("naniar")
library(naniar)
gg_miss_var(df)

Fixing the null values:  
===========================
Handling missing taxes by using the mean

library(tidyr)
df$`Tax 5%` <- replace_na(df$`Tax 5%`,mean(df$`Tax 5%`, na.rm = TRUE) )
summary(is.na(df$`Tax 5%`))
   Mode   FALSE 
logical    1000 

=> Missing iput on taxes has been solved

Catching up with a summary overview about the missing values

miss_var_summary(df)

Again we are still having to handles these:

Now handling the missing Date by using the complete function from the library ‘Tidyr’   complete(Date = seq.Date(<>, <>, by=<>))

then remove all the remaining null values:

 x <- df[complete.cases(df), ]
 str(x)
tibble [996 x 17] (S3: tbl_df/tbl/data.frame)
 $ Invoice ID             : chr [1:996] "750-67-8428" "226-31-3081" "631-41-3108" "123-19-1176" ...
 $ Branch                 : chr [1:996] "A" "C" "A" "A" ...
 $ City                   : chr [1:996] "Yangon" "Naypyitaw" "Yangon" "Yangon" ...
 $ Customer type          : chr [1:996] "Member" "Normal" "Normal" "Member" ...
 $ Gender                 : chr [1:996] "Female" "Female" "Male" "Male" ...
 $ Product line           : chr [1:996] "Health and beauty" "Electronic accessories" "Home and lifestyle" "Health and beauty" ...
 $ Unit price             : num [1:996] 74.7 15.3 46.3 58.2 86.3 ...
 $ Quantity               : num [1:996] 7 5 7 8 7 7 6 10 2 3 ...
 $ Tax 5%                 : num [1:996] 26.14 3.82 16.22 23.29 30.21 ...
 $ Total                  : num [1:996] 549 80.2 340.5 489 634.4 ...
 $ Date                   : POSIXct[1:996], format: "2019-01-05" "2019-03-08" "2019-03-03" "2019-01-27" ...
 $ Time                   : 'hms' num [1:996] 13:08:00 10:29:00 13:23:00 20:33:00 ...
  ..- attr(*, "units")= chr "secs"
 $ Payment                : chr [1:996] "Ewallet" "Cash" "Credit card" "Ewallet" ...
 $ cogs                   : num [1:996] 522.8 76.4 324.3 465.8 604.2 ...
 $ gross margin percentage: num [1:996] 4.76 4.76 4.76 4.76 4.76 ...
 $ gross income           : num [1:996] 26.14 3.82 16.22 23.29 30.21 ...
 $ Rating                 : num [1:996] 9.1 9.6 7.4 8.4 5.3 4.1 5.8 8 7.2 5.9 ...
df <- na.omit(df)

Lasdt check on null values if they still exist:

miss_var_summary(df)
df
gg_miss_var(df)

Now, clearly, there is no missing inputs, and now we can dive deeper into the exploratory data analysis

Data Analysis and Exploration: 
_________________________________ 

We’ll start by looking for the most correlated variables using the polychoric correlation:

# selecting numerical columns 
library(magrittr) # needs to be run every time you start R and want to use %>%
library(dplyr)    # alternatively, this also loads %>%
my_data <- df %>% select(7, 8, 9, 10, 14, 15, 16, 17)
# Correlations with significance levels
# polychoric correlation
library(polycor)
polychor(my_data)
NaNs producedNaNs produced
[1] -0.004516063
head(my_data)

Now we’ll use plotly dash to sketch the heatmap of the sales data to look for the best correlated variables. For this we will need to install the following libraries:
- library(tidyverse)
- library(hrbrthemes)
- library(viridis)
- library(plotly)
- library(d3heatmap)

#install.packages("tidyverse")
#install.packages("hrbrthemes")
#install.packages("viridis")
#install.packages("plotly")
#install.packages("d3heatmap")
#data.matrix(my_data, rownames.force = NA)
# round to two decimal places:
round_df <- function(x, digits) {
    # round all numeric variables
    # x: data frame 
    # digits: number of digits to round
    numeric_columns <- sapply(x, mode) == 'numeric'
    x[numeric_columns] <-  round(x[numeric_columns], digits)
    x
}
cleaned_df <- round_df(my_data, 2)
cleaned_df
# The sales dataset:
Sales_data <- as.matrix(cleaned_df)

Plotting a heatmap for the cleaned data we have worked on:

#########################################################
### A) Installing and loading required packages
#########################################################
if (!require("gplots")) {
   install.packages("gplots", dependencies = TRUE)
   library(gplots)
   }
if (!require("RColorBrewer")) {
   install.packages("RColorBrewer", dependencies = TRUE)
   library(RColorBrewer)
   }
#########################################################
### B) Reading in data and transform it into matrix format
#########################################################
rnames <- Sales_data[,1]                            # assign labels in column 1 to "rnames"
mat_data <- Sales_data  # transform column 2-5 into a matrix
rownames(mat_data) <- rnames                  # assign row names
#########################################################
### C) Customizing and plotting the heat map
#########################################################
# creates a own color palette from red to green
my_palette <- colorRampPalette(c("red", "yellow", "green"))(n = 299)
# (optional) defines the color breaks manually for a "skewed" color transition
col_breaks = c(seq(-1,0,length=100),  # for red
  seq(0.01,0.8,length=100),           # for yellow
  seq(0.81,1,length=100))             # for green
# creates a 5 x 5 inch image
png("./image/reda.png",    # create PNG for the heat map        
  width = 5*300,        # 5 x 300 pixels
  height = 5*300,
  res = 300,            # 300 pixels per inch
  pointsize = 8)        # smaller font size
heatmap.2(mat_data,
  cellnote = mat_data,  # same data set for cell labels
  main = "Correlation", # heat map title
  notecol="black",      # change font color of cell labels to black
  density.info="none",  # turns off density plot inside color legend
  trace="none",         # turns off trace lines inside the heat map
  margins =c(12,9),     # widens margins around plot
  col=my_palette,       # use on color palette defined earlier
  breaks=col_breaks,    # enable color transition at specified limits
  dendrogram="row",     # only draw a row dendrogram
  Colv="NA")            # turn off column clustering
dev.off()               # close the PNG device
null device 
          1 
heatmap.2(mat_data)

The best correlated are Tax 5%, Total, Gross Income and cogs i.e Cost of Goods sold with a correlation of 1.

Also, the above mentioned all has a good correlation of 0.71 with Quantity

x <-  df$`Tax 5%`
y <- df$`gross income`
plot(x, y, col = "red", main="Tax 5% as a function of the Gross Income",
  xlab="Tax 5%", ylab="Gross Income")

x <-  df$Quantity
y <- df$cogs
plot(x, y, col = "Dark Green", main="Quantity Vs Cost of Goods Sale",
  xlab="Quantity", ylab="Cost of Goods Sale")

x <-  df$`Unit price`
y <- df$`gross income`
plot(x, y, col = "Blue", main="Unit Price Vs Gross Income",
  xlab="Unit Price", ylab="Gross Income")

Rating <- df$Rating
hist(Rating, col = 'Gold')

Let’s find the mean rating now

Rating <- df$Rating
avg <- mean(Rating)
hist(Rating, col = 'Gold')
abline(v= avg, col='Blue', lwd =2)

Plotting histogram for all the dataset

library(Hmisc)
hist.data.frame(cleaned_df)

Analysis of Branch, City and Product Type: 
_________________________________________ 

#Counting the branches 
counts <- df %>% count(Branch)
counts
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
7: In readChar(file, size, TRUE) : truncating string with embedded nuls
8: In readChar(file, size, TRUE) : truncating string with embedded nuls
9: In readChar(file, size, TRUE) : truncating string with embedded nuls
10: In readChar(file, size, TRUE) : truncating string with embedded nuls
# Import ggplot2 package
library(ggplot2)
# Build the classification graph
ggplot(data = df, aes(x = Branch)) +
  geom_bar(aes(fill = Branch)) + 
  labs(title = "Branch Analysis",
       subtitle = "Which branch is the busiest?.",
       x = "Branches",
       y = "Count")

Now let’s check the payment methods:

# Build the payment methid graph
ggplot(data = df, aes(x = Payment)) +
  geom_bar(aes(fill = Payment)) + 
  labs(title = "Payment Method Analysis",
       subtitle = "Which Payment method is most used?.",
       x = "Payment methods",
       y = "Count")

Showcasing results:

# Print results
summary <- count(df, Payment)
summary$percentage <- summary$n / sum(summary$n) * 100
summary

Now checking the busiest city:

# Build the cities graph
ggplot(data = df, aes(x = City)) +
  geom_bar(aes(fill = City)) + 
  labs(title = "Geo Analysis",
       subtitle = "Which city is most busy?.",
       x = "City",
       y = "Count")

Percentage outcomes per busiest cities is the following:

# Print results
summary <- count(df, City)
summary$percentage <- summary$n / sum(summary$n) * 100
summary

Gender:

# Build the gender graph
ggplot(data = df, aes(x = Gender)) +
  geom_bar(aes(fill = Gender)) + 
  labs(title = "Gender Analysis",
       subtitle = "Gender Count",
       x = "Gender",
       y = "Count")

Showcasing results per gender:

# Print results
summary <- count(df, Gender)
summary$percentage <- summary$n / sum(summary$n) * 100
summary

Visualizing a Gender based comparison related to Product Type

# Build  Gender based comparison related to Product Type graph
#plot(df$`Unit price`, df$`Product line`, pch = 16, col = c('cornflowerblue', 'springgreen')[df$Gender])
library(ggplot2)
#Use loess (local fiting as smooth method)
ggplot() + geom_point(data = df, aes(x = `Product line`, y = `Unit price`, color = Gender)) 

Using countplot method to better showcase the result per gender

ggplot(df, aes(y=`Product line`)) + 
geom_bar(aes(fill = Gender)) + 
  labs(title = "Gender Analysis per Product Type",
       x = "Count",
       y = "Product Type")

Showcasing results per gender:

# Print results
summary <- count(df, `Product line`)
summary$percentage <- summary$n / sum(summary$n) * 100
summary

Well, In Health & Beauty, Males are much more than Females whereas in Fashion accessories , Food & beverages and Sports & Travel Females are more and in the rest there is not much significant difference.

Visualizing a City based comparison related to Product Type

Now let’s focus more on the cities that lead the product lines per genre

# Build  Gender based comparison related to Product Type graph
There were 15 warnings (use warnings() to see them)
library(ggplot2)
#Use loess (local fiting as smooth method)
ggplot() + geom_point(data = df, aes(x = `Product line`, y = `Unit price`, color = City)) 

Let’s re-adjust the graph using a barplot

ggplot(df, aes(y=`Product line`)) + 
geom_bar(aes(fill = City)) + 
  labs(title = "Gender Analysis per Product Type",
       x = "Count",
       y = "Product Type")

Conslusion:
- Mandalay leads Sports and Travels and also Health and Beauty,
- Naypyitaw leads Foods and Beverage and also Fashion and accessories,
- Yangon leads Home and Liofestyle and and also Electronic accessories,

Visualizing the most used payment method for Product Type, Branch and City

Now let’s focus more on the product type that are mostly purchased using specific payment methods:

ggplot(df, aes(y=`Product line`)) + 
geom_bar(aes(fill = Payment)) + 
  labs(title = "payment method for Product Type",
       x = "Count",
       y = "Product Type")

And here’s, we check the branches that mostly promote a specific type of payment

ggplot(df, aes(y=Branch)) + 
geom_bar(aes(fill = Payment)) + 
  labs(title = "payment method for Branch Type",
       x = "Count",
       y = "Branch")

These cities in which we mostly use a specific type of paymnent:

ggplot(df, aes(y=City)) + 
geom_bar(aes(fill = Payment)) + 
  labs(title = "payment method for City Type",
       x = "Count",
       y = "City")

Finding Which Branch has better sale for a particular product type
__________________________________________________________________

ggplot(df, aes(y=`Product line`)) + 
geom_bar(aes(fill = Branch)) + 
  labs(title = "Braches having better sales for Product Type",
       x = "Count",
       y = "Product Type")

Boxen Plot for Rating and Quantity
__________________________________

couleur <- c("gold","darkgreen","red", "pink", "yellow", "brown","orange","gray", "green", "silver")
boxplot(Rating~Quantity,data=df, main="Boxen Plot for Quantity and Rating",notch=TRUE,
   xlab="Quantity", ylab="Rating")

Now implementing the wordcloud to showcase the most sold product

#install.packages("wordcloud")
#library(wordcloud)
#install.packages("wordcloud2")
#library(wordcloud2)
#install.packages("tm")
library(tm)
#Create a vector containing only the text
text <- df$`Product line`
# Create a corpus  
docs <- Corpus(VectorSource(text))
dtm <- TermDocumentMatrix(docs) 
matrix <- as.matrix(dtm) 
words <- sort(rowSums(matrix),decreasing=TRUE) 
df <- data.frame(word = names(words),freq=words)
wordcloud2(data=df, size=1.6, color='random-dark')
set.seed(1234) # for reproducibility 
wordcloud(words = df$word, freq = df$freq, min.freq = 1,           
          max.words=200, random.order=FALSE, rot.per=0.35,            
          colors=brewer.pal(8, "Dark2"))

LS0tDQp0aXRsZTogIkV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgaW4gUiBwcm9ncmFtbWluZyBmb3IgU2FsZXMgaW4gU3VwZXJtYXJrZXQiDQpvdXRwdXQ6DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCi0tDQoNClRoZSBqb2IgaXMgdG8gcGVyZm9ybSBkYXRhIGNsZWFuaW5nIGFuZCBkYXRhIHByZXByb2Nlc3NpbmcgdGVjaG5pcXVlcyBpbiBSIHByb2dyYW1taW5nLCBhbmFseXplLCB2aXN1YWxpemUsIGFuZCBjb25jbHVkZSBvdXIgYW5hbHlzaXMgYmFzZWQgb24gb3VyIHJlc2VhcmNoIGFuZCByZXN1bHRzIHdoaWNoIHdlIGZpbmQgYWZ0ZXIgYXBwbHlpbmcgZGF0YSBwcmVwcm9jZXNzaW5nIHRlY2huaXF1ZXMgYW5kIGFuYWx5c2lzLg0KDQoNCioqRGF0YSBDbGVhbmluZzoqKg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIExvYWQgQ1NWIGZpbGVzDQpgYGANCg0KYGBge3J9DQpkZiA8LSByZWFkX2Nzdigic2FsZXNfZGF0YS5jc3YiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgR2V0IHRvIGtub3cgdGhlIGRhdGENCmhlYWQoZGYpDQpkZltvcmRlcihkZiREYXRlICksXQ0KYGBgDQpgYGB7cn0NCmRpbShkZikgIyBUbyBjaGVjayBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucw0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZikgIyBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YSANCmBgYA0KSGVyZSwgd2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCBkYXRlIGlzIGNvbnZlcnRlZCB0byBkYXRlIHRpbWUgDQoNCmBgYHtyfQ0KIyBDb252ZXJ0aW5nIGRhdGUgdG8gRGF0ZXRpbWUNCmxpYnJhcnkobHVicmlkYXRlKQ0KZGYkRGF0ZSA8LSBwYXJzZV9kYXRlX3RpbWUoeCA9IGRmJERhdGUsDQogICAgICAgICAgICAgICAgb3JkZXJzID0gYygiZCBtIHkiLCAiZCBCIFkiLCAibS9kL3kiKSwNCiAgICAgICAgICAgICAgICBsb2NhbGUgPSAiZW5nIikNCg0KYGBgDQoNClZpZXcgdGhlIGRhdGEgYWdhaW4gdG8gdmVyaWZ5IHRoZSBEQVRFIGRhdGEgdHlwZToNCg0KYGBge3J9DQoNCmRmW29yZGVyKGRmJERhdGUgKSxdDQoNCmBgYA0KDQoqKipDaGVja2luZyB0aGUgbnVsbCB2YWx1ZXM6KioqIFwgXA0KKio9PT09PT09PT09PT09PT09PT09PT09PT09PT0qKiBcDQoNCmBgYHtyfQ0Kc3VtbWFyeShpcy5uYShkZikpICAjIFRvIGNoZWNrIG51bGwgdmFsdWVzDQpgYGANCg0KVGhpcyBtZWFucyB0aGF0IHdlIGhhdmU6XA0KLSAqKjIqKiBtaXNzaW5nIGlucHV0cyBpbiBjb2x1bW4gY2l0aWVzLCAgXA0KLSAqKjIqKiBtaXNzaW5nIGlucHV0cyBpbiBjb2x1bW4gZ2VuZGVyLCBcDQotICoqMSoqIG1pc3NpbmcgaW5wdXQgaW4gY29sdW1uIFRheCA1JSxcDQotICoqMSoqIG1pc3NpbmcgaW5wdXQgaW4gY29sdW1uIERhdGUgXA0KDQp3ZSB1c2UgdGhlIGxpYnJhcnkgbmFuaWFyIHRvIGNvdW50IHRoZSByZW1haW5pbmcgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGF0dHJpYnV0ZXMNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygibmFuaWFyIikNCmxpYnJhcnkobmFuaWFyKQ0KZ2dfbWlzc192YXIoZGYpDQpgYGANCg0KKioqRml4aW5nIHRoZSBudWxsIHZhbHVlczoqKiogXCBcDQoqKj09PT09PT09PT09PT09PT09PT09PT09PT09PSoqIFwNCkhhbmRsaW5nIG1pc3NpbmcgdGF4ZXMgYnkgdXNpbmcgdGhlIG1lYW4gDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQpkZiRgVGF4IDUlYCA8LSByZXBsYWNlX25hKGRmJGBUYXggNSVgLG1lYW4oZGYkYFRheCA1JWAsIG5hLnJtID0gVFJVRSkgKQ0Kc3VtbWFyeShpcy5uYShkZiRgVGF4IDUlYCkpDQpgYGANCj0+IE1pc3NpbmcgaXB1dCBvbiB0YXhlcyBoYXMgYmVlbiBzb2x2ZWQgICBcDQoNCkNhdGNoaW5nIHVwIHdpdGggYSBzdW1tYXJ5IG92ZXJ2aWV3IGFib3V0IHRoZSBtaXNzaW5nIHZhbHVlcyANCmBgYHtyfQ0KbWlzc192YXJfc3VtbWFyeShkZikNCmBgYA0KQWdhaW4gd2UgYXJlIHN0aWxsIGhhdmluZyB0byBoYW5kbGVzIHRoZXNlOiBcDQoNCi0gKioyKiogbWlzc2luZyBpbnB1dHMgaW4gY29sdW1uIGNpdGllcywgIFwNCi0gKioyKiogbWlzc2luZyBpbnB1dHMgaW4gY29sdW1uIGdlbmRlciwgXA0KLSAqKjEqKiBtaXNzaW5nIGlucHV0IGluIGNvbHVtbiBEYXRlXA0KDQpOb3cgaGFuZGxpbmcgdGhlIG1pc3NpbmcgRGF0ZSBieSB1c2luZyB0aGUgY29tcGxldGUgZnVuY3Rpb24gZnJvbSB0aGUgbGlicmFyeSAnVGlkeXInIFwgDQoqKipjb21wbGV0ZShEYXRlID0gc2VxLkRhdGUoPDxzdGFydF9kYXRlPj4sIDw8ZW5kX2RhdGU+PiwgYnk9PDxkYXRlX3VuaXQ+PikpKioqXA0KDQp0aGVuIHJlbW92ZSBhbGwgdGhlIHJlbWFpbmluZyBudWxsIHZhbHVlczoNCmBgYHtyfQ0KIHggPC0gZGZbY29tcGxldGUuY2FzZXMoZGYpLCBdDQogc3RyKHgpDQpkZiA8LSBuYS5vbWl0KGRmKQ0KYGBgDQpMYXNkdCBjaGVjayBvbiBudWxsIHZhbHVlcyBpZiB0aGV5IHN0aWxsIGV4aXN0OiBcDQoNCmBgYHtyfQ0KbWlzc192YXJfc3VtbWFyeShkZikNCmRmDQpnZ19taXNzX3ZhcihkZikNCmBgYA0KDQpOb3csIGNsZWFybHksIHRoZXJlIGlzIG5vIG1pc3NpbmcgaW5wdXRzLCBhbmQgbm93IHdlIGNhbiBkaXZlIGRlZXBlciBpbnRvIHRoZSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzXA0KDQoqKkRhdGEgQW5hbHlzaXMgYW5kIEV4cGxvcmF0aW9uOioqXCBcDQoqKipfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qKipcIFwNCg0KDQpXZSdsbCBzdGFydCBieSBsb29raW5nIGZvciB0aGUgbW9zdCBjb3JyZWxhdGVkIHZhcmlhYmxlcyB1c2luZyB0aGUgcG9seWNob3JpYyBjb3JyZWxhdGlvbjogXA0KYGBge3J9DQoNCiMgc2VsZWN0aW5nIG51bWVyaWNhbCBjb2x1bW5zIA0KbGlicmFyeShtYWdyaXR0cikgIyBuZWVkcyB0byBiZSBydW4gZXZlcnkgdGltZSB5b3Ugc3RhcnQgUiBhbmQgd2FudCB0byB1c2UgJT4lDQpsaWJyYXJ5KGRwbHlyKSAgICAjIGFsdGVybmF0aXZlbHksIHRoaXMgYWxzbyBsb2FkcyAlPiUNCm15X2RhdGEgPC0gZGYgJT4lIHNlbGVjdCg3LCA4LCA5LCAxMCwgMTQsIDE1LCAxNiwgMTcpDQoNCg0KIyBDb3JyZWxhdGlvbnMgd2l0aCBzaWduaWZpY2FuY2UgbGV2ZWxzDQojIHBvbHljaG9yaWMgY29ycmVsYXRpb24NCmxpYnJhcnkocG9seWNvcikNCnBvbHljaG9yKG15X2RhdGEpDQoNCmhlYWQobXlfZGF0YSkNCmBgYA0KDQoNCk5vdyB3ZSdsbCB1c2UgcGxvdGx5IGRhc2ggdG8gc2tldGNoIHRoZSBoZWF0bWFwIG9mIHRoZSBzYWxlcyBkYXRhIHRvIGxvb2sgZm9yIHRoZSBiZXN0IGNvcnJlbGF0ZWQgdmFyaWFibGVzLiBGb3IgdGhpcyB3ZSB3aWxsIG5lZWQgdG8gaW5zdGFsbCB0aGUgZm9sbG93aW5nIGxpYnJhcmllczogIFwNCi0gbGlicmFyeSh0aWR5dmVyc2UpIFwNCi0gbGlicmFyeShocmJydGhlbWVzKSBcDQotIGxpYnJhcnkodmlyaWRpcykgXA0KLSBsaWJyYXJ5KHBsb3RseSkgXA0KLSBsaWJyYXJ5KGQzaGVhdG1hcCkgXA0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJocmJydGhlbWVzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ2aXJpZGlzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KI2luc3RhbGwucGFja2FnZXMoImQzaGVhdG1hcCIpDQojZGF0YS5tYXRyaXgobXlfZGF0YSwgcm93bmFtZXMuZm9yY2UgPSBOQSkNCiMgcm91bmQgdG8gdHdvIGRlY2ltYWwgcGxhY2VzOg0Kcm91bmRfZGYgPC0gZnVuY3Rpb24oeCwgZGlnaXRzKSB7DQogICAgIyByb3VuZCBhbGwgbnVtZXJpYyB2YXJpYWJsZXMNCiAgICAjIHg6IGRhdGEgZnJhbWUgDQogICAgIyBkaWdpdHM6IG51bWJlciBvZiBkaWdpdHMgdG8gcm91bmQNCiAgICBudW1lcmljX2NvbHVtbnMgPC0gc2FwcGx5KHgsIG1vZGUpID09ICdudW1lcmljJw0KICAgIHhbbnVtZXJpY19jb2x1bW5zXSA8LSAgcm91bmQoeFtudW1lcmljX2NvbHVtbnNdLCBkaWdpdHMpDQogICAgeA0KfQ0KDQpjbGVhbmVkX2RmIDwtIHJvdW5kX2RmKG15X2RhdGEsIDIpDQpjbGVhbmVkX2RmDQojIFRoZSBzYWxlcyBkYXRhc2V0Og0KU2FsZXNfZGF0YSA8LSBhcy5tYXRyaXgoY2xlYW5lZF9kZikNCg0KYGBgDQpQbG90dGluZyBhIGhlYXRtYXAgZm9yIHRoZSBjbGVhbmVkIGRhdGEgd2UgaGF2ZSB3b3JrZWQgb246IFwNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMgQSkgSW5zdGFsbGluZyBhbmQgbG9hZGluZyByZXF1aXJlZCBwYWNrYWdlcw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmlmICghcmVxdWlyZSgiZ3Bsb3RzIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImdwbG90cyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KGdwbG90cykNCiAgIH0NCmlmICghcmVxdWlyZSgiUkNvbG9yQnJld2VyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KFJDb2xvckJyZXdlcikNCiAgIH0NCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyBCKSBSZWFkaW5nIGluIGRhdGEgYW5kIHRyYW5zZm9ybSBpdCBpbnRvIG1hdHJpeCBmb3JtYXQNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCnJuYW1lcyA8LSBTYWxlc19kYXRhWywxXSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFzc2lnbiBsYWJlbHMgaW4gY29sdW1uIDEgdG8gInJuYW1lcyINCm1hdF9kYXRhIDwtIFNhbGVzX2RhdGEgICMgdHJhbnNmb3JtIGNvbHVtbiAyLTUgaW50byBhIG1hdHJpeA0Kcm93bmFtZXMobWF0X2RhdGEpIDwtIHJuYW1lcyAgICAgICAgICAgICAgICAgICMgYXNzaWduIHJvdyBuYW1lcw0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIEMpIEN1c3RvbWl6aW5nIGFuZCBwbG90dGluZyB0aGUgaGVhdCBtYXANCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIGNyZWF0ZXMgYSBvd24gY29sb3IgcGFsZXR0ZSBmcm9tIHJlZCB0byBncmVlbg0KbXlfcGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoInJlZCIsICJ5ZWxsb3ciLCAiZ3JlZW4iKSkobiA9IDI5OSkNCg0KIyAob3B0aW9uYWwpIGRlZmluZXMgdGhlIGNvbG9yIGJyZWFrcyBtYW51YWxseSBmb3IgYSAic2tld2VkIiBjb2xvciB0cmFuc2l0aW9uDQpjb2xfYnJlYWtzID0gYyhzZXEoLTEsMCxsZW5ndGg9MTAwKSwgICMgZm9yIHJlZA0KICBzZXEoMC4wMSwwLjgsbGVuZ3RoPTEwMCksICAgICAgICAgICAjIGZvciB5ZWxsb3cNCiAgc2VxKDAuODEsMSxsZW5ndGg9MTAwKSkgICAgICAgICAgICAgIyBmb3IgZ3JlZW4NCg0KIyBjcmVhdGVzIGEgNSB4IDUgaW5jaCBpbWFnZQ0KcG5nKCIuL2ltYWdlL3JlZGEucG5nIiwgICAgIyBjcmVhdGUgUE5HIGZvciB0aGUgaGVhdCBtYXAgICAgICAgIA0KICB3aWR0aCA9IDUqMzAwLCAgICAgICAgIyA1IHggMzAwIHBpeGVscw0KICBoZWlnaHQgPSA1KjMwMCwNCiAgcmVzID0gMzAwLCAgICAgICAgICAgICMgMzAwIHBpeGVscyBwZXIgaW5jaA0KICBwb2ludHNpemUgPSA4KSAgICAgICAgIyBzbWFsbGVyIGZvbnQgc2l6ZQ0KDQpoZWF0bWFwLjIobWF0X2RhdGEsDQogIGNlbGxub3RlID0gbWF0X2RhdGEsICAjIHNhbWUgZGF0YSBzZXQgZm9yIGNlbGwgbGFiZWxzDQogIG1haW4gPSAiQ29ycmVsYXRpb24iLCAjIGhlYXQgbWFwIHRpdGxlDQogIG5vdGVjb2w9ImJsYWNrIiwgICAgICAjIGNoYW5nZSBmb250IGNvbG9yIG9mIGNlbGwgbGFiZWxzIHRvIGJsYWNrDQogIGRlbnNpdHkuaW5mbz0ibm9uZSIsICAjIHR1cm5zIG9mZiBkZW5zaXR5IHBsb3QgaW5zaWRlIGNvbG9yIGxlZ2VuZA0KICB0cmFjZT0ibm9uZSIsICAgICAgICAgIyB0dXJucyBvZmYgdHJhY2UgbGluZXMgaW5zaWRlIHRoZSBoZWF0IG1hcA0KICBtYXJnaW5zID1jKDEyLDkpLCAgICAgIyB3aWRlbnMgbWFyZ2lucyBhcm91bmQgcGxvdA0KICBjb2w9bXlfcGFsZXR0ZSwgICAgICAgIyB1c2Ugb24gY29sb3IgcGFsZXR0ZSBkZWZpbmVkIGVhcmxpZXINCiAgYnJlYWtzPWNvbF9icmVha3MsICAgICMgZW5hYmxlIGNvbG9yIHRyYW5zaXRpb24gYXQgc3BlY2lmaWVkIGxpbWl0cw0KICBkZW5kcm9ncmFtPSJyb3ciLCAgICAgIyBvbmx5IGRyYXcgYSByb3cgZGVuZHJvZ3JhbQ0KICBDb2x2PSJOQSIpICAgICAgICAgICAgIyB0dXJuIG9mZiBjb2x1bW4gY2x1c3RlcmluZw0KDQpkZXYub2ZmKCkgICAgICAgICAgICAgICAjIGNsb3NlIHRoZSBQTkcgZGV2aWNlDQpoZWF0bWFwLjIobWF0X2RhdGEpDQpgYGANClRoZSBiZXN0IGNvcnJlbGF0ZWQgYXJlIFRheCA1JSwgVG90YWwsIEdyb3NzIEluY29tZSBhbmQgY29ncyBpLmUgQ29zdCBvZiBHb29kcyBzb2xkIHdpdGggYSBjb3JyZWxhdGlvbiBvZiAxLg0KDQpBbHNvLCB0aGUgYWJvdmUgbWVudGlvbmVkIGFsbCBoYXMgYSBnb29kIGNvcnJlbGF0aW9uIG9mIDAuNzEgd2l0aCBRdWFudGl0eQ0KDQoNCg0KYGBge3J9DQp4IDwtICBkZiRgVGF4IDUlYA0KeSA8LSBkZiRgZ3Jvc3MgaW5jb21lYA0KcGxvdCh4LCB5LCBjb2wgPSAicmVkIiwgbWFpbj0iVGF4IDUlIGFzIGEgZnVuY3Rpb24gb2YgdGhlIEdyb3NzIEluY29tZSIsDQogIHhsYWI9IlRheCA1JSIsIHlsYWI9Ikdyb3NzIEluY29tZSIpDQpgYGANCmBgYHtyfQ0KeCA8LSAgZGYkUXVhbnRpdHkNCnkgPC0gZGYkY29ncw0KcGxvdCh4LCB5LCBjb2wgPSAiRGFyayBHcmVlbiIsIG1haW49IlF1YW50aXR5IFZzIENvc3Qgb2YgR29vZHMgU2FsZSIsDQogIHhsYWI9IlF1YW50aXR5IiwgeWxhYj0iQ29zdCBvZiBHb29kcyBTYWxlIikNCmBgYA0KYGBge3J9DQp4IDwtICBkZiRgVW5pdCBwcmljZWANCnkgPC0gZGYkYGdyb3NzIGluY29tZWANCnBsb3QoeCwgeSwgY29sID0gIkJsdWUiLCBtYWluPSJVbml0IFByaWNlIFZzIEdyb3NzIEluY29tZSIsDQogIHhsYWI9IlVuaXQgUHJpY2UiLCB5bGFiPSJHcm9zcyBJbmNvbWUiKQ0KYGBgDQoNCmBgYHtyfQ0KUmF0aW5nIDwtIGRmJFJhdGluZw0KaGlzdChSYXRpbmcsIGNvbCA9ICdHb2xkJykNCg0KYGBgDQpMZXQncyBmaW5kIHRoZSBtZWFuIHJhdGluZyBub3cNCmBgYHtyfQ0KUmF0aW5nIDwtIGRmJFJhdGluZw0KYXZnIDwtIG1lYW4oUmF0aW5nKQ0KaGlzdChSYXRpbmcsIGNvbCA9ICdHb2xkJykNCmFibGluZSh2PSBhdmcsIGNvbD0nQmx1ZScsIGx3ZCA9MikNCmBgYA0KUGxvdHRpbmcgaGlzdG9ncmFtIGZvciBhbGwgdGhlIGRhdGFzZXQgDQpgYGB7cn0NCmxpYnJhcnkoSG1pc2MpDQpoaXN0LmRhdGEuZnJhbWUoY2xlYW5lZF9kZikNCmBgYA0KKipBbmFseXNpcyBvZiBCcmFuY2gsIENpdHkgYW5kIFByb2R1Y3QgVHlwZToqKlwgXA0KKioqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qKipcIFwNCmBgYHtyfQ0KI0NvdW50aW5nIHRoZSBicmFuY2hlcyANCmNvdW50cyA8LSBkZiAlPiUgY291bnQoQnJhbmNoKQ0KY291bnRzDQojT1INCiNzdW1tYXJ5IDwtIGNvdW50KGRmLCBCcmFuY2gpDQojc3VtbWFyeQ0KYGBgDQoNCmBgYHtyfQ0KDQojIEltcG9ydCBnZ3Bsb3QyIHBhY2thZ2UNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBCdWlsZCB0aGUgYnJhbmNoIGdyYXBoDQpnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IEJyYW5jaCkpICsNCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBCcmFuY2gpKSArIA0KICBsYWJzKHRpdGxlID0gIkJyYW5jaCBBbmFseXNpcyIsDQogICAgICAgc3VidGl0bGUgPSAiV2hpY2ggYnJhbmNoIGlzIHRoZSBidXNpZXN0Py4iLA0KICAgICAgIHggPSAiQnJhbmNoZXMiLA0KICAgICAgIHkgPSAiQ291bnQiKQ0KDQpgYGANCk5vdyBsZXQncyBjaGVjayB0aGUgcGF5bWVudCBtZXRob2RzOg0KYGBge3J9DQojIEJ1aWxkIHRoZSBwYXltZW50IG1ldGhvZCBncmFwaA0KZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBQYXltZW50KSkgKw0KICBnZW9tX2JhcihhZXMoZmlsbCA9IFBheW1lbnQpKSArIA0KICBsYWJzKHRpdGxlID0gIlBheW1lbnQgTWV0aG9kIEFuYWx5c2lzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJXaGljaCBQYXltZW50IG1ldGhvZCBpcyBtb3N0IHVzZWQ/LiIsDQogICAgICAgeCA9ICJQYXltZW50IG1ldGhvZHMiLA0KICAgICAgIHkgPSAiQ291bnQiKQ0KYGBgDQoNClNob3djYXNpbmcgcmVzdWx0czoNCmBgYHtyfQ0KIyBQcmludCByZXN1bHRzDQpzdW1tYXJ5IDwtIGNvdW50KGRmLCBQYXltZW50KQ0Kc3VtbWFyeSRwZXJjZW50YWdlIDwtIHN1bW1hcnkkbiAvIHN1bShzdW1tYXJ5JG4pICogMTAwDQpzdW1tYXJ5DQpgYGANCg0KTm93IGNoZWNraW5nIHRoZSBidXNpZXN0IGNpdHk6DQpgYGB7cn0NCiMgQnVpbGQgdGhlIGNpdGllcyBncmFwaA0KZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBDaXR5KSkgKw0KICBnZW9tX2JhcihhZXMoZmlsbCA9IENpdHkpKSArIA0KICBsYWJzKHRpdGxlID0gIkdlbyBBbmFseXNpcyIsDQogICAgICAgc3VidGl0bGUgPSAiV2hpY2ggY2l0eSBpcyBtb3N0IGJ1c3k/LiIsDQogICAgICAgeCA9ICJDaXR5IiwNCiAgICAgICB5ID0gIkNvdW50IikNCmBgYA0KUGVyY2VudGFnZSBvdXRjb21lcyBwZXIgYnVzaWVzdCBjaXRpZXMgaXMgdGhlIGZvbGxvd2luZzoNCmBgYHtyfQ0KIyBQcmludCByZXN1bHRzDQpzdW1tYXJ5IDwtIGNvdW50KGRmLCBDaXR5KQ0Kc3VtbWFyeSRwZXJjZW50YWdlIDwtIHN1bW1hcnkkbiAvIHN1bShzdW1tYXJ5JG4pICogMTAwDQpzdW1tYXJ5DQpgYGANCkdlbmRlcjogDQpgYGB7cn0NCiMgQnVpbGQgdGhlIGdlbmRlciBncmFwaA0KZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBHZW5kZXIpKSArDQogIGdlb21fYmFyKGFlcyhmaWxsID0gR2VuZGVyKSkgKyANCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgQW5hbHlzaXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkdlbmRlciBDb3VudCIsDQogICAgICAgeCA9ICJHZW5kZXIiLA0KICAgICAgIHkgPSAiQ291bnQiKQ0KYGBgDQpTaG93Y2FzaW5nIHJlc3VsdHMgcGVyIGdlbmRlcjoNCmBgYHtyfQ0KIyBQcmludCByZXN1bHRzDQpzdW1tYXJ5IDwtIGNvdW50KGRmLCBHZW5kZXIpDQpzdW1tYXJ5JHBlcmNlbnRhZ2UgPC0gc3VtbWFyeSRuIC8gc3VtKHN1bW1hcnkkbikgKiAxMDANCnN1bW1hcnkNCmBgYA0KDQpWaXN1YWxpemluZyBhIEdlbmRlciBiYXNlZCBjb21wYXJpc29uIHJlbGF0ZWQgdG8gUHJvZHVjdCBUeXBlDQpgYGB7cn0NCiMgQnVpbGQgIEdlbmRlciBiYXNlZCBjb21wYXJpc29uIHJlbGF0ZWQgdG8gUHJvZHVjdCBUeXBlIGdyYXBoDQpsaWJyYXJ5KGdncGxvdDIpDQojVXNlIGxvZXNzIChsb2NhbCBmaXRpbmcgYXMgc21vb3RoIG1ldGhvZCkNCmdncGxvdCgpICsgZ2VvbV9wb2ludChkYXRhID0gZGYsIGFlcyh4ID0gYFByb2R1Y3QgbGluZWAsIHkgPSBgVW5pdCBwcmljZWAsIGNvbG9yID0gR2VuZGVyKSkgDQoNCmBgYA0KVXNpbmcgY291bnRwbG90IG1ldGhvZCB0byBiZXR0ZXIgc2hvd2Nhc2UgdGhlIHJlc3VsdCBwZXIgZ2VuZGVyDQoNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeT1gUHJvZHVjdCBsaW5lYCkpICsgDQpnZW9tX2JhcihhZXMoZmlsbCA9IEdlbmRlcikpICsgDQogIGxhYnModGl0bGUgPSAiR2VuZGVyIEFuYWx5c2lzIHBlciBQcm9kdWN0IFR5cGUiLA0KICAgICAgIHggPSAiQ291bnQiLA0KICAgICAgIHkgPSAiUHJvZHVjdCBUeXBlIikNCg0KYGBgDQpTaG93Y2FzaW5nIHJlc3VsdHMgcGVyIGdlbmRlcjoNCmBgYHtyfQ0KIyBQcmludCByZXN1bHRzDQpzdW1tYXJ5IDwtIGNvdW50KGRmLCBgUHJvZHVjdCBsaW5lYCkNCnN1bW1hcnkkcGVyY2VudGFnZSA8LSBzdW1tYXJ5JG4gLyBzdW0oc3VtbWFyeSRuKSAqIDEwMA0Kc3VtbWFyeQ0KYGBgDQpXZWxsLCBJbiBIZWFsdGggJiBCZWF1dHksICoqKk1hbGVzKioqIGFyZSBtdWNoIG1vcmUgdGhhbiAqKipGZW1hbGVzKioqIHdoZXJlYXMgaW4gRmFzaGlvbiBhY2Nlc3NvcmllcyAsIEZvb2QgJiBiZXZlcmFnZXMgYW5kIFNwb3J0cyAmIFRyYXZlbCBGZW1hbGVzIGFyZSBtb3JlIGFuZCBpbiB0aGUgcmVzdCB0aGVyZSBpcyBub3QgbXVjaCBzaWduaWZpY2FudCBkaWZmZXJlbmNlLiBcDQoNCioqVmlzdWFsaXppbmcgYSBDaXR5IGJhc2VkIGNvbXBhcmlzb24gcmVsYXRlZCB0byBQcm9kdWN0IFR5cGUqKiBcDQoNCk5vdyBsZXQncyBmb2N1cyBtb3JlIG9uIHRoZSBjaXRpZXMgdGhhdCBsZWFkIHRoZSBwcm9kdWN0IGxpbmVzIHBlciBnZW5yZSANCmBgYHtyfQ0KIyBCdWlsZCAgR2VuZGVyIGJhc2VkIGNvbXBhcmlzb24gcmVsYXRlZCB0byBQcm9kdWN0IFR5cGUgZ3JhcGgNCmxpYnJhcnkoZ2dwbG90MikNCiNVc2UgbG9lc3MgKGxvY2FsIGZpdGluZyBhcyBzbW9vdGggbWV0aG9kKQ0KZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBkZiwgYWVzKHggPSBgUHJvZHVjdCBsaW5lYCwgeSA9IGBVbml0IHByaWNlYCwgY29sb3IgPSBDaXR5KSkgDQoNCmBgYA0KDQpMZXQncyByZS1hZGp1c3QgdGhlIGdyYXBoIHVzaW5nIGEgYmFycGxvdA0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHk9YFByb2R1Y3QgbGluZWApKSArIA0KZ2VvbV9iYXIoYWVzKGZpbGwgPSBDaXR5KSkgKyANCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgQW5hbHlzaXMgcGVyIFByb2R1Y3QgVHlwZSIsDQogICAgICAgeCA9ICJDb3VudCIsDQogICAgICAgeSA9ICJQcm9kdWN0IFR5cGUiKQ0KDQpgYGANCkNvbnNsdXNpb246IFwNCi0gKipNYW5kYWxheSoqIGxlYWRzIFNwb3J0cyBhbmQgVHJhdmVscyBhbmQgYWxzbyBIZWFsdGggYW5kIEJlYXV0eSwgIFwNCi0gKipOYXlweWl0YXcqKiBsZWFkcyBGb29kcyBhbmQgQmV2ZXJhZ2UgYW5kIGFsc28gRmFzaGlvbiBhbmQgYWNjZXNzb3JpZXMsIFwNCi0gKipZYW5nb24qKiBsZWFkcyBIb21lIGFuZCBMaW9mZXN0eWxlIGFuZCBhbmQgYWxzbyBFbGVjdHJvbmljIGFjY2Vzc29yaWVzLFwNCg0KKipWaXN1YWxpemluZyB0aGUgbW9zdCB1c2VkIHBheW1lbnQgbWV0aG9kIGZvciBQcm9kdWN0IFR5cGUsIEJyYW5jaCBhbmQgQ2l0eSoqIFwNCg0KTm93IGxldCdzIGZvY3VzIG1vcmUgb24gdGhlIHByb2R1Y3QgdHlwZSB0aGF0IGFyZSBtb3N0bHkgcHVyY2hhc2VkIHVzaW5nIHNwZWNpZmljIHBheW1lbnQgbWV0aG9kczoNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh5PWBQcm9kdWN0IGxpbmVgKSkgKyANCmdlb21fYmFyKGFlcyhmaWxsID0gUGF5bWVudCkpICsgDQogIGxhYnModGl0bGUgPSAicGF5bWVudCBtZXRob2QgZm9yIFByb2R1Y3QgVHlwZSIsDQogICAgICAgeCA9ICJDb3VudCIsDQogICAgICAgeSA9ICJQcm9kdWN0IFR5cGUiKQ0KDQpgYGANCkFuZCBoZXJlJ3MsIHdlIGNoZWNrIHRoZSBicmFuY2hlcyB0aGF0IG1vc3RseSBwcm9tb3RlIGEgc3BlY2lmaWMgdHlwZSBvZiBwYXltZW50IA0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHk9QnJhbmNoKSkgKyANCmdlb21fYmFyKGFlcyhmaWxsID0gUGF5bWVudCkpICsgDQogIGxhYnModGl0bGUgPSAicGF5bWVudCBtZXRob2QgZm9yIEJyYW5jaCBUeXBlIiwNCiAgICAgICB4ID0gIkNvdW50IiwNCiAgICAgICB5ID0gIkJyYW5jaCIpDQoNCmBgYA0KVGhlc2UgY2l0aWVzIGluIHdoaWNoIHdlIG1vc3RseSB1c2UgYSBzcGVjaWZpYyB0eXBlIG9mIHBheW1uZW50Og0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh5PUNpdHkpKSArIA0KZ2VvbV9iYXIoYWVzKGZpbGwgPSBQYXltZW50KSkgKyANCiAgbGFicyh0aXRsZSA9ICJwYXltZW50IG1ldGhvZCBmb3IgQ2l0eSBUeXBlIiwNCiAgICAgICB4ID0gIkNvdW50IiwNCiAgICAgICB5ID0gIkNpdHkiKQ0KDQpgYGANCioqRmluZGluZyBXaGljaCBCcmFuY2ggaGFzIGJldHRlciBzYWxlIGZvciBhIHBhcnRpY3VsYXIgcHJvZHVjdCB0eXBlKiogIFwNCioqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiogXA0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh5PWBQcm9kdWN0IGxpbmVgKSkgKyANCmdlb21fYmFyKGFlcyhmaWxsID0gQnJhbmNoKSkgKyANCiAgbGFicyh0aXRsZSA9ICJCcmFjaGVzIGhhdmluZyBiZXR0ZXIgc2FsZXMgZm9yIFByb2R1Y3QgVHlwZSIsDQogICAgICAgeCA9ICJDb3VudCIsDQogICAgICAgeSA9ICJQcm9kdWN0IFR5cGUiKQ0KDQpgYGANCioqQm94ZW4gUGxvdCBmb3IgUmF0aW5nIGFuZCBRdWFudGl0eSoqXA0KKipfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKipcDQpgYGB7cn0NCmNvdWxldXIgPC0gYygiZ29sZCIsImRhcmtncmVlbiIsInJlZCIsICJwaW5rIiwgInllbGxvdyIsICJicm93biIsIm9yYW5nZSIsImdyYXkiLCAiZ3JlZW4iLCAic2lsdmVyIikNCmJveHBsb3QoUmF0aW5nflF1YW50aXR5LGRhdGE9ZGYsIG1haW49IkJveGVuIFBsb3QgZm9yIFF1YW50aXR5IGFuZCBSYXRpbmciLG5vdGNoPVRSVUUsDQogICB4bGFiPSJRdWFudGl0eSIsIHlsYWI9IlJhdGluZyIpDQoNCmBgYA0KTm93IGltcGxlbWVudGluZyB0aGUgd29yZGNsb3VkIHRvIHNob3djYXNlIHRoZSBtb3N0IHNvbGQgcHJvZHVjdCANCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIndvcmRjbG91ZCIpDQojbGlicmFyeSh3b3JkY2xvdWQpDQojaW5zdGFsbC5wYWNrYWdlcygid29yZGNsb3VkMiIpDQojbGlicmFyeSh3b3JkY2xvdWQyKQ0KYGBgDQoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygidG0iKQ0KbGlicmFyeSh0bSkNCiNDcmVhdGUgYSB2ZWN0b3IgY29udGFpbmluZyBvbmx5IHRoZSB0ZXh0DQp0ZXh0IDwtIGRmJGBQcm9kdWN0IGxpbmVgDQojIENyZWF0ZSBhIGNvcnB1cyAgDQpkb2NzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dCkpDQpgYGANCmBgYHtyfQ0KZHRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChkb2NzKSANCm1hdHJpeCA8LSBhcy5tYXRyaXgoZHRtKSANCndvcmRzIDwtIHNvcnQocm93U3VtcyhtYXRyaXgpLGRlY3JlYXNpbmc9VFJVRSkgDQpkZiA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh3b3JkcyksZnJlcT13b3JkcykNCmBgYA0KDQpgYGB7cn0NCndvcmRjbG91ZDIoZGF0YT1kZiwgc2l6ZT0xLjYsIGNvbG9yPSdyYW5kb20tZGFyaycpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgxMjM0KSAjIGZvciByZXByb2R1Y2liaWxpdHkgDQp3b3JkY2xvdWQod29yZHMgPSBkZiR3b3JkLCBmcmVxID0gZGYkZnJlcSwgbWluLmZyZXEgPSAxLCAgICAgICAgICAgDQogICAgICAgICAgbWF4LndvcmRzPTIwMCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuMzUsICAgICAgICAgICAgDQogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQpgYGANCg0KDQoNCg0KDQo=