options(scipen = 999)
library(tidyverse)
library(DataExplorer)
library(forecast)
library(flextable)
library(explore)
library(plotly)
Walmart Sales Analysis (2010-2012)
Introduction Dataset Info This is the historical data that covers
sales from 2010-02-05 to 2012-11-01, in the file Walmart_Store_sales.
Within this file you will find the following fields:
Store - the store number Date - the week of sales Weekly_Sales -
sales for the given store Holiday_Flag - whether the week is a special
holiday week 1 – Holiday week 0 – Non-holiday week Temperature -
Temperature on the day of sale Fuel_Price - Cost of fuel in the region
CPI – Prevailing consumer price index Unemployment - Prevailing
unemployment rate Holiday Events Super Bowl: 12-Feb-10, 11-Feb-11,
10-Feb-12, 8-Feb-13 Labour Day: 10-Sep-10, 9-Sep-11, 7-Sep-12, 6-Sep-13
Thanksgiving: 26-Nov-10, 25-Nov-11, 23-Nov-12, 29-Nov-13 Christmas:
31-Dec-10, 30-Dec-11, 28-Dec-12, 27-Dec-13
Objectives Data exploration Manipulation data (only if is necessary).
statistical Exploration Data Analysis (EDA). the focus is to see
distribution
what store did best vs worst how much was the difference between the
best and least selling store annual sales monthly sales Store quarterly
growth for 2012
what holidays were better in average sales than normal day
2012 to 2011 year over year 2012 month over month 2012 q3 QoQ
walmart = readr::read_csv("C:/Users/thepy/Downloads/Walmart.csv")
walmart_eda = walmart
#check NA
# and duplicate
# as outliers
sum(is.na(walmart_eda)) # no NA
[1] 0
sum(duplicated(walmart_eda)) # no duplicate
[1] 0
the data has nulls or duplicate values
# count NAs
apply(X=is.na(walmart_eda), MARGIN=2, FUN = sum)
Store Date Weekly_Sales Holiday_Flag Temperature Fuel_Price CPI
0 0 0 0 0 0 0
Unemployment
0
looking at each of the columns we see that there are no NA
#count unique values
sort(sapply(walmart_eda,function(x) length(unique(x))))
Holiday_Flag Store Date Unemployment Fuel_Price CPI Temperature
2 45 143 349 892 2145 3528
Weekly_Sales
6435
dim(walmart_eda)
str(walmart_eda)
walmart_eda_2 <-
walmart_eda %>%
mutate(Date=dmy(Date) ,
Year=year(Date),
Month=month(Date),
Quarterly=quarter(Date),
weekly=week(Date),
Store = as.integer(Store)) %>%
# Holiday_Flag=as.factor(Holiday_Flag)) %>%
rename(Holiday_Y_N=Holiday_Flag) %>%
mutate(Weekly_Sales2=ifelse(Weekly_Sales>1046965,'above_average','below_average'),
Weekly_Sales2=as.factor(Weekly_Sales2)) %>%
relocate(Weekly_Sales2,.after = Weekly_Sales) %>%
mutate(Fuel_Price2=ifelse(Fuel_Price<3.359,'Low','High' ),
Fuel_Price2=as.factor(Fuel_Price2)) %>%
relocate(Fuel_Price2,.after = Fuel_Price) %>%
mutate(Temperature_range= case_when(
Temperature <= quantile(walmart_eda$Temperature, c(0.25)) ~ "Cold",
Temperature > quantile(walmart_eda$Temperature, c(0.50)) &
Temperature <= quantile(walmart_eda$Temperature, c(0.75)) ~ "Cool",
T ~ "Hot")) %>%
mutate(Temperature_range=as.factor(Temperature_range)) %>%
relocate(Temperature_range,.after = Temperature) %>%
mutate(CPI2 = ifelse(CPI>mean(CPI),'Yes','No'),
CPI2= as.factor(CPI2)) %>%
relocate(CPI2,.after=CPI) %>%
mutate(Unemployment2=ifelse(Unemployment<mean(Unemployment),'Belowavg','Aboveavg' ),
Unemployment2=as.factor(Unemployment2))%>%
relocate(Unemployment2,.after=Unemployment) %>%
mutate(Holidays = case_when(Date=='2010-02-10' ~ 'Super_Bowl',
Date=='2011-02-11' ~ 'Super_Bowl',
Date=='2012-02-12' ~ 'Super_Bowl',
Date=='2010-09-10' ~ 'Labour_Day',
Date=='2011-09-09' ~ 'Labour_Day',
Date=='2012-09-07' ~ 'Labour_Day',
Date=='2010-11-26' ~ 'Thanksgiving',
Date=='2011-11-25' ~ 'Thanksgiving',
Date=='2010-12-31' ~ 'Christmas',
Date== '2011-12-30' ~ 'Christmas',
TRUE ~ 'Non-Holiday' )) %>%
relocate(Holidays,.after = Holiday_Y_N)
summary(walmart_eda_2)
Store Date Weekly_Sales Weekly_Sales2 Holiday_Y_N
Min. : 1 Min. :2010-02-05 Min. : 209986 above_average:2876 Min. :0.00000
1st Qu.:12 1st Qu.:2010-10-08 1st Qu.: 553350 below_average:3559 1st Qu.:0.00000
Median :23 Median :2011-06-17 Median : 960746 Median :0.00000
Mean :23 Mean :2011-06-17 Mean :1046965 Mean :0.06993
3rd Qu.:34 3rd Qu.:2012-02-24 3rd Qu.:1420159 3rd Qu.:0.00000
Max. :45 Max. :2012-10-26 Max. :3818686 Max. :1.00000
Holidays Temperature Temperature_range Fuel_Price Fuel_Price2 CPI
Length:6435 Min. : -2.06 Cold:1609 Min. :2.472 High:3464 Min. :126.1
Class :character 1st Qu.: 47.46 Cool:1608 1st Qu.:2.933 Low :2971 1st Qu.:131.7
Mode :character Median : 62.67 Hot :3218 Median :3.445 Median :182.6
Mean : 60.66 Mean :3.359 Mean :171.6
3rd Qu.: 74.94 3rd Qu.:3.735 3rd Qu.:212.7
Max. :100.14 Max. :4.468 Max. :227.2
CPI2 Unemployment Unemployment2 Year Month Quarterly
No :3146 Min. : 3.879 Aboveavg:3022 Min. :2010 Min. : 1.000 Min. :1.000
Yes:3289 1st Qu.: 6.891 Belowavg:3413 1st Qu.:2010 1st Qu.: 4.000 1st Qu.:2.000
Median : 7.874 Median :2011 Median : 6.000 Median :2.000
Mean : 7.999 Mean :2011 Mean : 6.448 Mean :2.483
3rd Qu.: 8.622 3rd Qu.:2012 3rd Qu.: 9.000 3rd Qu.:3.000
Max. :14.313 Max. :2012 Max. :12.000 Max. :4.000
weekly
Min. : 1.00
1st Qu.:14.00
Median :26.00
Mean :26.15
3rd Qu.:38.00
Max. :53.00
minimum store is store 1,max store is 45, range of stores is 44
walmart_eda %>%
mutate(Date=dmy(Date)) %>%
summarise(Last_recored_weeklysale=max(Date),
First_recored_weeklysale=min(Date))
walmart_eda %>%
select(Weekly_Sales) %>%
summarise(minimum_sales=round(min(Weekly_Sales),2),
average_sales=round(mean(Weekly_Sales),2),
maximum_sales=round(max(Weekly_Sales),2))
walmart_eda %>%
select(Temperature) %>%
summarise(minimum_Temperature=round(min(Temperature),2),
average_Temperature=round(mean(Temperature),2),
maximum_Temperature=round(max(Temperature),2))
walmart_eda %>%
select(Fuel_Price) %>%
summarise(minimum_Fuel_Price=round(min(Fuel_Price),2),
average_Fuel_Price=round(mean(Fuel_Price),2),
maximum_Fuel_Price=round(max(Fuel_Price),2))
walmart_eda %>%
select(CPI) %>%
summarise(minimum_CPI=round(min(CPI),2),
average_CPI=round(mean(CPI),2),
maximum_CPI=round(max(CPI),2))
walmart_eda %>%
select(Unemployment) %>%
summarise(minimum_Unemployment=round(min(Unemployment),2),
average_Unemployment=round(mean(Unemployment),2),
maximum_Unemployment=round(max(Unemployment),2))
Data Analysis
which store has max sales?
walmart_eda_2 %>%
group_by(Store) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
arrange(desc(totalsales))
visualize
walmart_eda_2 %>%
group_by(Store) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
arrange(desc(totalsales)) %>%
ggplot(aes(reorder(Store,totalsales),totalsales,fill=totalsales))+geom_col(col='black')+scale_fill_gradient(high='green',low = 'darkred')+theme(axis.text.x = element_text(size=7.2))+xlab('Stores')+ggtitle('Revenue by Store Number')

-
Store 20 had the most with 301,397,792
-
Store 33 had the least with 37,160,222
What was the difference in revenue between the highest
selling store and the least selling store?
walmart_eda_2 %>%
# filter(Store > 32, Store<34, Store>19, Store<21) %>%
group_by(Store) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Store,values_from = totalsales) %>%
transmute(store_maxsale_to_leastsale_difference= `20` - `33`)
NA
Store 20 which was the store with the overall most sales
(301,397,792) had a revenue that was 264,237,570 greater than
that of store 33 which happened to bring in the least overall revenue
(37,160,222)
what is the percent of sales per year?
annualproportion <- walmart_eda_2 %>%
group_by(Year) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
mutate(saleproportion=round((totalsales/sum(totalsales)),2)) %>%
arrange(desc(totalsales))
webr::PieDonut(annualproportion,aes(saleproportion,Year),title = "Percent of Walmart's annual sales",showPieName = T,pieLabelSize = 5,showRatioPie = F,showRatioDonut = F)

which Holidays are better than the average day sales?
walmart_eda_2 %>%
group_by(Holidays) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
arrange(desc(totalsales))
walmart_eda_2 %>%
group_by(Holidays) %>%
summarise(avgsales=mean(Weekly_Sales)) %>%
arrange(desc(avgsales)) %>%
ggplot(aes(fct_infreq(Holidays,avgsales),avgsales,fill=avgsales))+geom_col(col='black')+scale_fill_gradient(high='green3',low='darkred')+xlab('')+ggtitle('Holidays better than Average')+theme(axis.text.x = element_text(size=7.7))+scale_y_continuous(label=scales::dollar)

-
Thanksgiving is the holiday that brought in well above the normal day’s
average
-
Meanwhile both the Super_Bowl and Labour_Day each brought in revenue
that was just above the normal day’s average revenue
-
while sales on Christmas sat below the normal day’s average revenue
since all christmas revenue is made prior to Christmas and anything
during or after is most associated with returns which would be seen as a
loss in revenue
quarter 3 progress 2012 for stores average sales
walmart_eda_2 %>%
filter(Year==2012) %>%
group_by(Store,Quarterly) %>%
summarise(avgsales=mean(Weekly_Sales)) %>%
pivot_wider(names_from = Quarterly,values_from = avgsales) %>%
mutate(QoQ= (((`3`- `2`)*100)/`2`) ) %>%
select(Store,QoQ) %>%
arrange(desc(QoQ))
`summarise()` has grouped output by 'Store'. You can override using the `.groups` argument.
walmart_eda_2 %>%
filter(Year==2012) %>%
group_by(Store,Quarterly) %>%
summarise(avgsales=mean(Weekly_Sales)) %>%
pivot_wider(names_from = Quarterly,values_from = avgsales) %>%
mutate(QoQ= round((((`3`- `2`)*100)/`2`),2) ) %>%
select(Store,QoQ) %>%
arrange(desc(QoQ)) %>%
ggplot(aes(reorder(Store,QoQ),QoQ,fill=QoQ))+geom_col(col='black')+scale_fill_gradient(high='green',low='darkred')+xlab('Store')+ggtitle('Quarterly Growth by Store 2012' )+theme(axis.text.x = element_text(size=7.7))
`summarise()` has grouped output by 'Store'. You can override using the `.groups` argument.

in quarter 3 of 2012, we can see that the vast majority of
store say negative growth while only a handful saw positive growth with
store 14 seeing the most negative growth (-15.77% growth) and store 7
the most positive growth (13.33% growth)
Monthly Sales
#monthly sales
walmart_eda_2 %>%
#filter(Year==2012) %>%
group_by(Month,Year) %>%
mutate(Year=as.factor(Year)) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
arrange(desc(totalsales))
-
Dec 2010 ( 288,760,533)
-
Dec 2011 (28,8078,102)
-
June 2012 (240,610,329)
-
December of 2010 saw the most sales given all the shopping that comes
with preparing for Christmas gifts
-
September of 2010 saw the least sales
-
December of 2011 also saw the most sales given all the shopping that
comes with preparing for Christmas gifts
-
January of 2011 saw the fewest sales
-
the current data did not provide for the last 2 months of 2012 June of
2012 saw the most sales followed closely by august and march January
brought in the least revenue
Semester sales (biannual)
# creating new dataframes for semester revenue (total revenue by 6 month or biannual) for each year
semesterrev <- walmart_eda_2 %>%
filter(Year==2010,Month >1, Month < 7) %>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester1= round(`2`+`3`+`4`+`5`+`6`),2) %>%
select(Year,Semester1)
semesterrev2 <-walmart_eda_2 %>%
filter(Year==2010,Month >6) %>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester2= round(`7`+`8`+`9`+`10`+`11`+`12`),2) %>%
select(Year,Semester2)
semesterrev3 <- walmart_eda_2 %>%
filter(Year==2011,Month >=1, Month < 7)%>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester3= round(`1`+`2`+`3`+`4`+`5`+`6`),2) %>%
select(Year,Semester3)
semesterrev4 <- walmart_eda_2 %>%
filter(Year==2011,Month >6)%>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester4= round(`7`+`8`+`9`+`10`+`11`+`12`)) %>%
select(Year,Semester4)
semesterrev5 <-walmart_eda_2 %>%
filter(Year==2012,Month >=1, Month < 7)%>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester5= round(`1`+`2`+`3`+`4`+`5`+`6`),2) %>%
select(Year,Semester5)
semesterrev6 <-walmart_eda_2 %>%
filter(Year==2012,Month >6)%>%
group_by(Year,Month) %>%
summarise(totalsale=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsale) %>%
mutate(Semester6= round(`7`+`8`+`9`+`10`),2) %>%
select(Year,Semester6)
# joining semester tables with inner join
semester_revenues <- semesterrev %>%
inner_join(semesterrev2,by='Year')
semester_revenues2011 <- semesterrev3 %>%
inner_join(semesterrev4,by='Year')
semester_revenues2012 <- semesterrev5 %>%
inner_join(semesterrev6,by='Year')
# bring all joined tables together into new dataframe with rbind
all_semesters <- rbind(semester_revenues,semester_revenues2011,semester_revenues2012)
all_semesters%>%
pivot_longer(col=-Year,names_to = 'Semesters',values_to = 'Revenue') %>%
filter(Revenue!='NA') %>% # filter out Nulls
arrange(desc(Revenue))
#creating visual
all_semesters%>%
pivot_longer(col=-Year,names_to = 'Semesters',values_to = 'Revenue') %>%
filter(Revenue!='NA') %>%
ggplot(aes(reorder(Semesters,Revenue),Revenue,col=Revenue))+geom_col(aes(fill=Year),linewidth=1.2)+coord_flip()+xlab('Semester2010to2012')+scale_color_gradient(high='green3',low='darkred')

-
Semester 4 of 2011 ( months July through December) had the greatest
Revenue for the six semesters
-
Semester 6 being months (July through October) had the least Revenue of
the six Semesters
all_semesters%>%
pivot_longer(col=-Year,names_to = 'Semesters',values_to = 'Revenue') %>%
filter(Revenue!='NA') %>% # filter out Nulls
#group_by(Year,Semesters) %>%
# summarise(totalRev=sum(Revenue)) %>%
mutate(Revenueproportion=round(Revenue/sum(Revenue),2)) %>%
arrange(desc(Revenue))
NA
all_semester_proportions <-all_semesters%>%
pivot_longer(col=-Year,names_to = 'Semesters',values_to = 'Revenue') %>%
filter(Revenue!='NA') %>% # filter out Nulls
group_by(Semesters) %>%
summarise(totalRev=sum(Revenue)) %>%
mutate(Revenueproportion=round(totalRev/sum(totalRev),3)*100) %>%
arrange(desc(totalRev))
PieDonut(all_semester_proportions,aes(Semesters,Revenueproportion),title = 'Percent of Total Weekly Sales by Semester',ratioByGroup = F,donutLabelSize=4,pieLabelSize=4,showPieName=F,showRatioDonut=F,showRatioPie=F,explodeDonut = T)

-
Semester 4 being the second half of 2011 saw the greatest percent 19.6%
of revenue intake
-
Semester 6 being the second half of 2012 saw the smallest percent 11.7%
of revenue intake and we can note that an obvious reason for this is
that we did not have sale records for the holiday season in addition to
the fact that we know from hindsight that the economy in 2012 began to
take a downwards direction.
Lastly, I will include the general year over year for 2011 into 2012,
2012 quarter over quarter, 2012 month over month as well as 2012 monthly
revenue
walmart_eda_2 %>%
filter(Year>2010) %>%
group_by(Year) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Year,values_from = totalsales) %>%
transmute(YoY=round((((`2012`-`2011`)*100)/`2011`),2))
for 2011 into 2012 Walmart saw a -18.3% year over year drop in
revenue
walmart_eda_2 %>%
filter(Year>2011) %>%
group_by(Quarterly) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Quarterly,values_from = totalsales) %>%
transmute(QoQ=round((((`3`-`2`)*100)/`2`),2))
from quarter 2 to quarter 3 of 2012
Walmart saw an overall -2.15% drop in quarter over quarter growth
walmart_eda_2 %>%
filter(Year>2011) %>%
group_by(Month) %>%
summarise(totalsales=sum(Weekly_Sales)) %>%
pivot_wider(names_from = Month,values_from = totalsales) %>%
transmute(MoM=round((((`10`-`9`)*100)/`9`),2))
from September to October of 2012
Walmart saw an overall 2.06% rise in Month over Month growth
walmart_eda_2 %>%
filter(Year>2011,Month>9,) %>%
group_by(Month) %>%
summarise(totalsales=sum(Weekly_Sales))
In October of 2012 Walmart had a monthly revenue of 184,361,680
LS0tDQp0aXRsZTogIldhbG1hcnQgRURBIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6IEhhcnJpc29uDQotLS0NCg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShEYXRhRXhwbG9yZXIpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShmbGV4dGFibGUpDQpsaWJyYXJ5KGV4cGxvcmUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkod2VicikNCmBgYA0KDQpXYWxtYXJ0IFNhbGVzIEFuYWx5c2lzICgyMDEwLTIwMTIpDQoNCkludHJvZHVjdGlvbg0KRGF0YXNldCBJbmZvDQpUaGlzIGlzIHRoZSBoaXN0b3JpY2FsIGRhdGEgdGhhdCBjb3ZlcnMgc2FsZXMgZnJvbSAyMDEwLTAyLTA1IHRvIDIwMTItMTEtMDEsIGluIHRoZSBmaWxlIFdhbG1hcnRfU3RvcmVfc2FsZXMuIFdpdGhpbiB0aGlzIGZpbGUgeW91IHdpbGwgZmluZCB0aGUgZm9sbG93aW5nIGZpZWxkczoNCg0KU3RvcmUgLSB0aGUgc3RvcmUgbnVtYmVyDQpEYXRlIC0gdGhlIHdlZWsgb2Ygc2FsZXMNCldlZWtseV9TYWxlcyAtIHNhbGVzIGZvciB0aGUgZ2l2ZW4gc3RvcmUNCkhvbGlkYXlfRmxhZyAtIHdoZXRoZXIgdGhlIHdlZWsgaXMgYSBzcGVjaWFsIGhvbGlkYXkgd2VlayAxIOKAkyBIb2xpZGF5IHdlZWsgMCDigJMgTm9uLWhvbGlkYXkgd2Vlaw0KVGVtcGVyYXR1cmUgLSBUZW1wZXJhdHVyZSBvbiB0aGUgZGF5IG9mIHNhbGUNCkZ1ZWxfUHJpY2UgLSBDb3N0IG9mIGZ1ZWwgaW4gdGhlIHJlZ2lvbg0KQ1BJIOKAkyBQcmV2YWlsaW5nIGNvbnN1bWVyIHByaWNlIGluZGV4DQpVbmVtcGxveW1lbnQgLSBQcmV2YWlsaW5nIHVuZW1wbG95bWVudCByYXRlDQpIb2xpZGF5IEV2ZW50cyBTdXBlciBCb3dsOiAxMi1GZWItMTAsIDExLUZlYi0xMSwgMTAtRmViLTEyLCA4LUZlYi0xMyBMYWJvdXIgRGF5OiAxMC1TZXAtMTAsIDktU2VwLTExLCA3LVNlcC0xMiwgNi1TZXAtMTMgVGhhbmtzZ2l2aW5nOiAyNi1Ob3YtMTAsIDI1LU5vdi0xMSwgMjMtTm92LTEyLCAyOS1Ob3YtMTMgQ2hyaXN0bWFzOiAzMS1EZWMtMTAsIDMwLURlYy0xMSwgMjgtRGVjLTEyLCAyNy1EZWMtMTMNCg0KDQoNCk9iamVjdGl2ZXMNCkRhdGEgZXhwbG9yYXRpb24NCk1hbmlwdWxhdGlvbiBkYXRhIChvbmx5IGlmIGlzIG5lY2Vzc2FyeSkuDQpzdGF0aXN0aWNhbCBFeHBsb3JhdGlvbiBEYXRhIEFuYWx5c2lzIChFREEpLg0KdGhlIGZvY3VzIGlzIHRvIHNlZSBkaXN0cmlidXRpb24NCg0Kd2hhdCBzdG9yZSBkaWQgYmVzdCB2cyB3b3JzdA0KaG93IG11Y2ggd2FzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGJlc3QgYW5kIGxlYXN0IHNlbGxpbmcgc3RvcmUNCmFubnVhbCBzYWxlcw0KbW9udGhseSBzYWxlcyANClN0b3JlIHF1YXJ0ZXJseSBncm93dGggZm9yIDIwMTIgDQoNCg0Kd2hhdCBob2xpZGF5cyB3ZXJlIGJldHRlciBpbiBhdmVyYWdlIHNhbGVzIHRoYW4gbm9ybWFsIGRheQ0KDQoyMDEyIHRvIDIwMTEgeWVhciBvdmVyIHllYXINCjIwMTIgbW9udGggb3ZlciBtb250aA0KMjAxMiBxMyBRb1ENCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd2FsbWFydCA9IHJlYWRyOjpyZWFkX2NzdigiQzovVXNlcnMvdGhlcHkvRG93bmxvYWRzL1dhbG1hcnQuY3N2IikNCmBgYA0KDQoNCg0KYGBge3J9DQoNCndhbG1hcnRfZWRhID0gd2FsbWFydA0KYGBgDQoNCg0KYGBge3J9DQojY2hlY2sgTkENCiMgYW5kIGR1cGxpY2F0ZQ0KIyBhcyBvdXRsaWVycw0Kc3VtKGlzLm5hKHdhbG1hcnRfZWRhKSkgIyBubyBOQQ0Kc3VtKGR1cGxpY2F0ZWQod2FsbWFydF9lZGEpKSAjIG5vIGR1cGxpY2F0ZQ0KYGBgDQoNCnRoZSBkYXRhIGhhcyBudWxscyBvciBkdXBsaWNhdGUgdmFsdWVzDQpgYGB7cn0NCmBgYA0KDQpgYGB7cn0NCiMgY291bnQgTkFzDQphcHBseShYPWlzLm5hKHdhbG1hcnRfZWRhKSwgTUFSR0lOPTIsIEZVTiA9IHN1bSkNCmBgYA0KbG9va2luZyBhdCBlYWNoIG9mIHRoZSBjb2x1bW5zIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBubyBOQQ0KDQpgYGB7cn0NCiNjb3VudCB1bmlxdWUgdmFsdWVzDQpzb3J0KHNhcHBseSh3YWxtYXJ0X2VkYSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHgpKSkpDQpgYGANCg0KDQpgYGB7cn0NCg0KZGltKHdhbG1hcnRfZWRhKQ0Kc3RyKHdhbG1hcnRfZWRhKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCndhbG1hcnRfZWRhXzIgPC0gDQp3YWxtYXJ0X2VkYSAlPiUgDQogIG11dGF0ZShEYXRlPWRteShEYXRlKSAsDQogICAgICAgICBZZWFyPXllYXIoRGF0ZSksDQogICAgICAgICBNb250aD1tb250aChEYXRlKSwNCiAgICAgICAgIFF1YXJ0ZXJseT1xdWFydGVyKERhdGUpLA0KICAgICAgICAgd2Vla2x5PXdlZWsoRGF0ZSksIA0KICAgICAgICAgU3RvcmUgPSBhcy5pbnRlZ2VyKFN0b3JlKSkgJT4lIA0KICAgICAgICAjIEhvbGlkYXlfRmxhZz1hcy5mYWN0b3IoSG9saWRheV9GbGFnKSkgJT4lIA0KICByZW5hbWUoSG9saWRheV9ZX049SG9saWRheV9GbGFnKSAlPiUgDQogIG11dGF0ZShXZWVrbHlfU2FsZXMyPWlmZWxzZShXZWVrbHlfU2FsZXM+MTA0Njk2NSwnYWJvdmVfYXZlcmFnZScsJ2JlbG93X2F2ZXJhZ2UnKSwNCiAgICAgICAgIFdlZWtseV9TYWxlczI9YXMuZmFjdG9yKFdlZWtseV9TYWxlczIpKSAlPiUgDQogIHJlbG9jYXRlKFdlZWtseV9TYWxlczIsLmFmdGVyID0gV2Vla2x5X1NhbGVzKSAlPiUgDQogIG11dGF0ZShGdWVsX1ByaWNlMj1pZmVsc2UoRnVlbF9QcmljZTwzLjM1OSwnTG93JywnSGlnaCcgKSwNCiAgICAgICAgIEZ1ZWxfUHJpY2UyPWFzLmZhY3RvcihGdWVsX1ByaWNlMikpICU+JSANCiAgcmVsb2NhdGUoRnVlbF9QcmljZTIsLmFmdGVyID0gRnVlbF9QcmljZSkgJT4lDQogIG11dGF0ZShUZW1wZXJhdHVyZV9yYW5nZT0gY2FzZV93aGVuKCANCiAgICBUZW1wZXJhdHVyZSA8PSBxdWFudGlsZSh3YWxtYXJ0X2VkYSRUZW1wZXJhdHVyZSwgYygwLjI1KSkgfiAiQ29sZCIsDQogICAgVGVtcGVyYXR1cmUgPiBxdWFudGlsZSh3YWxtYXJ0X2VkYSRUZW1wZXJhdHVyZSwgYygwLjUwKSkgJiANCiAgICAgVGVtcGVyYXR1cmUgPD0gcXVhbnRpbGUod2FsbWFydF9lZGEkVGVtcGVyYXR1cmUsIGMoMC43NSkpIH4gIkNvb2wiLCANCiAgICBUIH4gIkhvdCIpKSAlPiUNCiAgbXV0YXRlKFRlbXBlcmF0dXJlX3JhbmdlPWFzLmZhY3RvcihUZW1wZXJhdHVyZV9yYW5nZSkpICU+JSANCiAgcmVsb2NhdGUoVGVtcGVyYXR1cmVfcmFuZ2UsLmFmdGVyID0gVGVtcGVyYXR1cmUpICU+JSANCiAgbXV0YXRlKENQSTIgPSBpZmVsc2UoQ1BJPm1lYW4oQ1BJKSwnWWVzJywnTm8nKSwNCiAgICAgICAgIENQSTI9IGFzLmZhY3RvcihDUEkyKSkgJT4lIA0KICByZWxvY2F0ZShDUEkyLC5hZnRlcj1DUEkpICU+JSANCiBtdXRhdGUoVW5lbXBsb3ltZW50Mj1pZmVsc2UoVW5lbXBsb3ltZW50PG1lYW4oVW5lbXBsb3ltZW50KSwnQmVsb3dhdmcnLCdBYm92ZWF2ZycgKSwNCiAgICAgICAgIFVuZW1wbG95bWVudDI9YXMuZmFjdG9yKFVuZW1wbG95bWVudDIpKSU+JSANCiAgcmVsb2NhdGUoVW5lbXBsb3ltZW50MiwuYWZ0ZXI9VW5lbXBsb3ltZW50KSAlPiUgDQogIG11dGF0ZShIb2xpZGF5cyA9IGNhc2Vfd2hlbihEYXRlPT0nMjAxMC0wMi0xMCcgfiAnU3VwZXJfQm93bCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlPT0nMjAxMS0wMi0xMScgfiAnU3VwZXJfQm93bCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlPT0nMjAxMi0wMi0xMicgfiAnU3VwZXJfQm93bCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGU9PScyMDEwLTA5LTEwJyB+ICdMYWJvdXJfRGF5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGU9PScyMDExLTA5LTA5JyB+ICdMYWJvdXJfRGF5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGU9PScyMDEyLTA5LTA3JyB+ICdMYWJvdXJfRGF5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGU9PScyMDEwLTExLTI2JyB+ICdUaGFua3NnaXZpbmcnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZT09JzIwMTEtMTEtMjUnIH4gJ1RoYW5rc2dpdmluZycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlPT0nMjAxMC0xMi0zMScgfiAnQ2hyaXN0bWFzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGU9PSAnMjAxMS0xMi0zMCcgfiAnQ2hyaXN0bWFzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ05vbi1Ib2xpZGF5JyApKSAlPiUgDQogIHJlbG9jYXRlKEhvbGlkYXlzLC5hZnRlciA9IEhvbGlkYXlfWV9OKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeSh3YWxtYXJ0X2VkYV8yKQ0KYGBgDQptaW5pbXVtIHN0b3JlIGlzIHN0b3JlIDEsbWF4IHN0b3JlIGlzIDQ1LCByYW5nZSBvZiBzdG9yZXMgaXMgNDQNCg0KYGBge3J9DQoNCmBgYA0KDQpgYGB7cn0NCndhbG1hcnRfZWRhICU+JSANCiAgbXV0YXRlKERhdGU9ZG15KERhdGUpKSAlPiUgDQogIHN1bW1hcmlzZShMYXN0X3JlY29yZWRfd2Vla2x5c2FsZT1tYXgoRGF0ZSksDQogICAgICAgICBGaXJzdF9yZWNvcmVkX3dlZWtseXNhbGU9bWluKERhdGUpKQ0KYGBgDQoNCg0KYGBge3J9DQp3YWxtYXJ0X2VkYSAlPiUgDQpzZWxlY3QoV2Vla2x5X1NhbGVzKSAlPiUgDQogIHN1bW1hcmlzZShtaW5pbXVtX3NhbGVzPXJvdW5kKG1pbihXZWVrbHlfU2FsZXMpLDIpLA0KICAgICAgICAgYXZlcmFnZV9zYWxlcz1yb3VuZChtZWFuKFdlZWtseV9TYWxlcyksMiksDQogICAgICAgICBtYXhpbXVtX3NhbGVzPXJvdW5kKG1heChXZWVrbHlfU2FsZXMpLDIpKQ0KYGBgDQoNCg0KYGBge3J9DQp3YWxtYXJ0X2VkYSAlPiUgDQpzZWxlY3QoVGVtcGVyYXR1cmUpICU+JSANCiAgc3VtbWFyaXNlKG1pbmltdW1fVGVtcGVyYXR1cmU9cm91bmQobWluKFRlbXBlcmF0dXJlKSwyKSwNCiAgICAgICAgIGF2ZXJhZ2VfVGVtcGVyYXR1cmU9cm91bmQobWVhbihUZW1wZXJhdHVyZSksMiksDQogICAgICAgICBtYXhpbXVtX1RlbXBlcmF0dXJlPXJvdW5kKG1heChUZW1wZXJhdHVyZSksMikpDQpgYGANCmBgYHtyfQ0Kd2FsbWFydF9lZGEgJT4lIA0Kc2VsZWN0KEZ1ZWxfUHJpY2UpICU+JSANCiAgc3VtbWFyaXNlKG1pbmltdW1fRnVlbF9QcmljZT1yb3VuZChtaW4oRnVlbF9QcmljZSksMiksDQogICAgICAgICBhdmVyYWdlX0Z1ZWxfUHJpY2U9cm91bmQobWVhbihGdWVsX1ByaWNlKSwyKSwNCiAgICAgICAgIG1heGltdW1fRnVlbF9QcmljZT1yb3VuZChtYXgoRnVlbF9QcmljZSksMikpDQpgYGANCg0KDQpgYGB7cn0NCndhbG1hcnRfZWRhICU+JSANCnNlbGVjdChDUEkpICU+JSANCiAgc3VtbWFyaXNlKG1pbmltdW1fQ1BJPXJvdW5kKG1pbihDUEkpLDIpLA0KICAgICAgICAgYXZlcmFnZV9DUEk9cm91bmQobWVhbihDUEkpLDIpLA0KICAgICAgICAgbWF4aW11bV9DUEk9cm91bmQobWF4KENQSSksMikpDQpgYGANCg0KDQpgYGB7cn0NCndhbG1hcnRfZWRhICU+JSANCnNlbGVjdChVbmVtcGxveW1lbnQpICU+JSANCiAgc3VtbWFyaXNlKG1pbmltdW1fVW5lbXBsb3ltZW50PXJvdW5kKG1pbihVbmVtcGxveW1lbnQpLDIpLA0KICAgICAgICAgYXZlcmFnZV9VbmVtcGxveW1lbnQ9cm91bmQobWVhbihVbmVtcGxveW1lbnQpLDIpLA0KICAgICAgICAgbWF4aW11bV9VbmVtcGxveW1lbnQ9cm91bmQobWF4KFVuZW1wbG95bWVudCksMikpDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCg0KYGBge3J9DQpgYGANCg0KRGF0YSBBbmFseXNpcw0KPGJyPg0KPGJyPg0KPGI+d2hpY2ggc3RvcmUgaGFzIG1heCBzYWxlcz88L2I+DQoNCmBgYHtyfQ0Kd2FsbWFydF9lZGFfMiAlPiUgDQogIGdyb3VwX2J5KFN0b3JlKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbHNhbGVzPXN1bShXZWVrbHlfU2FsZXMpKSAlPiUgDQogIGFycmFuZ2UoZGVzYyh0b3RhbHNhbGVzKSkgDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQp2aXN1YWxpemUNCg0KYGBge3J9DQp3YWxtYXJ0X2VkYV8yICU+JSANCiAgZ3JvdXBfYnkoU3RvcmUpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsc2FsZXM9c3VtKFdlZWtseV9TYWxlcykpICU+JSANCiAgYXJyYW5nZShkZXNjKHRvdGFsc2FsZXMpKSAlPiUgDQogIGdncGxvdChhZXMocmVvcmRlcihTdG9yZSx0b3RhbHNhbGVzKSx0b3RhbHNhbGVzLGZpbGw9dG90YWxzYWxlcykpK2dlb21fY29sKGNvbD0nYmxhY2snKStzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9J2dyZWVuJyxsb3cgPSAnZGFya3JlZCcpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9Ny4yKSkreGxhYignU3RvcmVzJykrZ2d0aXRsZSgnUmV2ZW51ZSBieSBTdG9yZSBOdW1iZXInKQ0KYGBgDQo8dWw+PGxpPg0KU3RvcmUgMjAgaGFkIHRoZSBtb3N0IHdpdGggMzAxLDM5Nyw3OTINCjwvbGk+PGxpPlN0b3JlIDMzIGhhZCB0aGUgbGVhc3Qgd2l0aCAzNywxNjAsMjIyDQo8L2xpPjwvdWw+DQo8YnI+DQo8YnI+DQo8Yj5XaGF0IHdhcyB0aGUgZGlmZmVyZW5jZSBpbiByZXZlbnVlIGJldHdlZW4gdGhlIGhpZ2hlc3Qgc2VsbGluZyBzdG9yZSBhbmQgdGhlIGxlYXN0IHNlbGxpbmcgc3RvcmU/PGI+DQpgYGB7cn0NCndhbG1hcnRfZWRhXzIgJT4lIA0KIyBmaWx0ZXIoU3RvcmUgPiAzMiwgU3RvcmU8MzQsIFN0b3JlPjE5LCBTdG9yZTwyMSkgJT4lIA0KICBncm91cF9ieShTdG9yZSkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gU3RvcmUsdmFsdWVzX2Zyb20gPSB0b3RhbHNhbGVzKSAlPiUgDQogIHRyYW5zbXV0ZShzdG9yZV9tYXhzYWxlX3RvX2xlYXN0c2FsZV9kaWZmZXJlbmNlPSBgMjBgIC0gYDMzYCkgDQogIA0KYGBgDQoNCjxiPjxpPlN0b3JlIDIwIHdoaWNoIHdhcyB0aGUgc3RvcmUgd2l0aCB0aGUgb3ZlcmFsbCBtb3N0IHNhbGVzICgzMDEsMzk3LDc5MikgaGFkIGEgcmV2ZW51ZSB0aGF0IHdhcyA8aT4yNjQsMjM3LDU3MCBncmVhdGVyPC9pPiB0aGFuIHRoYXQgb2Ygc3RvcmUgMzMgd2hpY2ggaGFwcGVuZWQgdG8gYnJpbmcgaW4gdGhlIGxlYXN0IG92ZXJhbGwgcmV2ZW51ZSAoMzcsMTYwLDIyMikNCjwvYj48L2k+DQpgYGB7cn0NCmBgYA0KDQo8Yj53aGF0IGlzIHRoZSBwZXJjZW50IG9mIHNhbGVzIHBlciB5ZWFyPzwvYj4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphbm51YWxwcm9wb3J0aW9uIDwtIHdhbG1hcnRfZWRhXzIgJT4lIA0KICBncm91cF9ieShZZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbHNhbGVzPXN1bShXZWVrbHlfU2FsZXMpKSAlPiUgDQogbXV0YXRlKHNhbGVwcm9wb3J0aW9uPXJvdW5kKCh0b3RhbHNhbGVzL3N1bSh0b3RhbHNhbGVzKSksMikpICU+JSANCiAgIGFycmFuZ2UoZGVzYyh0b3RhbHNhbGVzKSkNCndlYnI6OlBpZURvbnV0KGFubnVhbHByb3BvcnRpb24sYWVzKHNhbGVwcm9wb3J0aW9uLFllYXIpLHRpdGxlID0gIlBlcmNlbnQgb2YgV2FsbWFydCdzIGFubnVhbCBzYWxlcyIsc2hvd1BpZU5hbWUgPSBULHBpZUxhYmVsU2l6ZSA9IDUsc2hvd1JhdGlvUGllID0gRixzaG93UmF0aW9Eb251dCA9IEYpDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCjxiPndoaWNoIEhvbGlkYXlzIGFyZSBiZXR0ZXIgdGhhbiB0aGUgYXZlcmFnZSBkYXkgc2FsZXM/PC9iPg0KYGBge3J9DQp3YWxtYXJ0X2VkYV8yICU+JSANCiAgZ3JvdXBfYnkoSG9saWRheXMpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsc2FsZXM9c3VtKFdlZWtseV9TYWxlcykpICU+JSANCiAgYXJyYW5nZShkZXNjKHRvdGFsc2FsZXMpKQ0KYGBgDQoNCmBgYHtyfQ0Kd2FsbWFydF9lZGFfMiAlPiUgDQogIGdyb3VwX2J5KEhvbGlkYXlzKSAlPiUgDQogIHN1bW1hcmlzZShhdmdzYWxlcz1tZWFuKFdlZWtseV9TYWxlcykpICU+JSANCiAgYXJyYW5nZShkZXNjKGF2Z3NhbGVzKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKGZjdF9pbmZyZXEoSG9saWRheXMsYXZnc2FsZXMpLGF2Z3NhbGVzLGZpbGw9YXZnc2FsZXMpKStnZW9tX2NvbChjb2w9J2JsYWNrJykrc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSdncmVlbjMnLGxvdz0nZGFya3JlZCcpK3hsYWIoJycpK2dndGl0bGUoJ0hvbGlkYXlzIGJldHRlciB0aGFuIEF2ZXJhZ2UnKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTcuNykpK3NjYWxlX3lfY29udGludW91cyhsYWJlbD1zY2FsZXM6OmRvbGxhcikNCmBgYA0KPG9sPjxsaT4NClRoYW5rc2dpdmluZyBpcyB0aGUgaG9saWRheSB0aGF0IGJyb3VnaHQgaW4gd2VsbCBhYm92ZSB0aGUgbm9ybWFsIGRheSdzIGF2ZXJhZ2UNCjwvbGk+PGxpPg0KTWVhbndoaWxlIGJvdGggdGhlIFN1cGVyX0Jvd2wgYW5kIExhYm91cl9EYXkgZWFjaCBicm91Z2h0IGluIHJldmVudWUgdGhhdCB3YXMganVzdCBhYm92ZSB0aGUgbm9ybWFsIGRheSdzIGF2ZXJhZ2UgcmV2ZW51ZTwvbGk+PGxpPiANCndoaWxlIHNhbGVzIG9uIENocmlzdG1hcyBzYXQgYmVsb3cgdGhlIG5vcm1hbCBkYXkncyBhdmVyYWdlIHJldmVudWUgc2luY2UgYWxsIGNocmlzdG1hcyByZXZlbnVlIGlzIG1hZGUgcHJpb3IgdG8gQ2hyaXN0bWFzIGFuZCBhbnl0aGluZyBkdXJpbmcgb3IgYWZ0ZXIgaXMgbW9zdCBhc3NvY2lhdGVkIHdpdGggcmV0dXJucyB3aGljaCB3b3VsZCBiZSBzZWVuIGFzIGEgbG9zcyBpbiByZXZlbnVlIDwvbGk+PC9vbD4NCg0KDQoNCg0KDQo8YnI+DQo8YnI+DQo8YnI+DQoNCnF1YXJ0ZXIgMyBwcm9ncmVzcyAyMDEyIGZvciBzdG9yZXMgYXZlcmFnZSBzYWxlcw0KYGBge3J9DQp3YWxtYXJ0X2VkYV8yICU+JQ0KICBmaWx0ZXIoWWVhcj09MjAxMikgJT4lIA0KICBncm91cF9ieShTdG9yZSxRdWFydGVybHkpICU+JSANCiAgc3VtbWFyaXNlKGF2Z3NhbGVzPW1lYW4oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gUXVhcnRlcmx5LHZhbHVlc19mcm9tID0gYXZnc2FsZXMpICU+JSANCiAgbXV0YXRlKFFvUT0gKCgoYDNgLSBgMmApKjEwMCkvYDJgKSApICU+JSANCiAgc2VsZWN0KFN0b3JlLFFvUSkgJT4lIA0KICBhcnJhbmdlKGRlc2MoUW9RKSkNCmBgYA0KDQpgYGB7cn0NCndhbG1hcnRfZWRhXzIgJT4lDQogIGZpbHRlcihZZWFyPT0yMDEyKSAlPiUgDQogIGdyb3VwX2J5KFN0b3JlLFF1YXJ0ZXJseSkgJT4lIA0KICBzdW1tYXJpc2UoYXZnc2FsZXM9bWVhbihXZWVrbHlfU2FsZXMpKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBRdWFydGVybHksdmFsdWVzX2Zyb20gPSBhdmdzYWxlcykgJT4lIA0KICBtdXRhdGUoUW9RPSByb3VuZCgoKChgM2AtIGAyYCkqMTAwKS9gMmApLDIpICkgJT4lIA0KICBzZWxlY3QoU3RvcmUsUW9RKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhRb1EpKSAlPiUgDQogIGdncGxvdChhZXMocmVvcmRlcihTdG9yZSxRb1EpLFFvUSxmaWxsPVFvUSkpK2dlb21fY29sKGNvbD0nYmxhY2snKStzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9J2dyZWVuJyxsb3c9J2RhcmtyZWQnKSt4bGFiKCdTdG9yZScpK2dndGl0bGUoJ1F1YXJ0ZXJseSBHcm93dGggYnkgU3RvcmUgMjAxMicgKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTcuNykpDQpgYGANCg0KPGI+PGk+aW4gcXVhcnRlciAzIG9mIDIwMTIsIHdlIGNhbiBzZWUgdGhhdCB0aGUgdmFzdCBtYWpvcml0eSBvZiBzdG9yZSBzYXkgbmVnYXRpdmUgZ3Jvd3RoIHdoaWxlIG9ubHkgYSBoYW5kZnVsIHNhdyBwb3NpdGl2ZSBncm93dGggd2l0aCBzdG9yZSAxNCBzZWVpbmcgdGhlIG1vc3QgbmVnYXRpdmUgZ3Jvd3RoICgtMTUuNzclIGdyb3d0aCkgYW5kIHN0b3JlIDcgdGhlIG1vc3QgcG9zaXRpdmUgZ3Jvd3RoICgxMy4zMyUgZ3Jvd3RoKTwvYj48L2k+DQoNCmBgYHtyfQ0KYGBgDQoNCg0KDQoNCg0KTW9udGhseSBTYWxlcw0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KI21vbnRobHkgc2FsZXMNCg0Kd2FsbWFydF9lZGFfMiAlPiUgDQogICNmaWx0ZXIoWWVhcj09MjAxMikgJT4lIA0KICBncm91cF9ieShNb250aCxZZWFyKSAlPiUgDQogIG11dGF0ZShZZWFyPWFzLmZhY3RvcihZZWFyKSkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBhcnJhbmdlKGRlc2ModG90YWxzYWxlcykpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNtb250aGx5IHNhbGVzDQoNCmxtb250aGx5c2FsZXBlcnllYXIgPC0gd2FsbWFydF9lZGFfMiAlPiUgDQogICNmaWx0ZXIoWWVhcj09MjAxMikgJT4lIA0KICBncm91cF9ieShNb250aCxZZWFyKSAlPiUgDQogIG11dGF0ZShZZWFyPWFzLmZhY3RvcihZZWFyKSkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBhcnJhbmdlKGRlc2ModG90YWxzYWxlcykpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90bHkoZ2dwbG90KGxtb250aGx5c2FsZXBlcnllYXIsYWVzKE1vbnRoLHRvdGFsc2FsZXMsY29sPVllYXIpKStnZW9tX2xpbmUobGluZXdpZHRoPS45MCkrZ2VvbV9wb2ludChzaXplPTEuNyxjb2w9J2JsYWNrJykrDQogICMgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9J2dyZWVuJyxsb3c9J2RhcmtyZWQnKSsNCiAgeGxhYignTW9udGhzJykrZ2d0aXRsZSgnTW9udGhseSBTYWxlcyBwZXIgWWVhcicpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWw9c2NhbGVzOjpkb2xsYXIpK3NjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSwxMixieT0xKSkpICMrZmFjZXRfd3JhcCh+WWVhcixucm93PTMpDQoNCmBgYA0KPHVsPg0KPGxpPg0KRGVjIDIwMTAgKCAyODgsNzYwLDUzMyk8L2xpPjxsaT4NCkRlYyAyMDExICgyOCw4MDc4LDEwMik8L2xpPjxsaT4NCkp1bmUgMjAxMiAoMjQwLDYxMCwzMjkpDQo8L2xpPjxsaT4NCkRlY2VtYmVyIG9mIDIwMTAgc2F3IHRoZSBtb3N0IHNhbGVzIGdpdmVuIGFsbCB0aGUgc2hvcHBpbmcgdGhhdCBjb21lcyB3aXRoIHByZXBhcmluZyBmb3IgQ2hyaXN0bWFzIGdpZnRzDQo8L2xpPjxsaT4NClNlcHRlbWJlciBvZiAyMDEwIHNhdyB0aGUgbGVhc3Qgc2FsZXMgDQo8L2xpPg0KPGxpPg0KRGVjZW1iZXIgb2YgMjAxMSBhbHNvIHNhdyB0aGUgbW9zdCBzYWxlcyBnaXZlbiBhbGwgdGhlIHNob3BwaW5nIHRoYXQgY29tZXMgd2l0aCBwcmVwYXJpbmcgZm9yIENocmlzdG1hcyBnaWZ0cw0KPC9saT48bGk+DQpKYW51YXJ5IG9mIDIwMTEgc2F3IHRoZSBmZXdlc3Qgc2FsZXMgDQo8L2xpPg0KPGxpPg0KdGhlIGN1cnJlbnQgZGF0YSBkaWQgbm90IHByb3ZpZGUgZm9yIHRoZSBsYXN0IDIgbW9udGhzIG9mIDIwMTINCkp1bmUgb2YgMjAxMiAgc2F3IHRoZSBtb3N0IHNhbGVzIGZvbGxvd2VkIGNsb3NlbHkgYnkgYXVndXN0IGFuZCBtYXJjaA0KSmFudWFyeSBicm91Z2h0IGluIHRoZSBsZWFzdCByZXZlbnVlDQo8L2xpPg0KPC91bD4NCjxicj4NCg0KDQpTZW1lc3RlciBzYWxlcyAoYmlhbm51YWwpDQpgYGB7cn0NCg0KIyBjcmVhdGluZyBuZXcgZGF0YWZyYW1lcyBmb3Igc2VtZXN0ZXIgcmV2ZW51ZSAodG90YWwgcmV2ZW51ZSBieSA2IG1vbnRoIG9yIGJpYW5udWFsKSBmb3IgZWFjaCB5ZWFyDQpzZW1lc3RlcnJldiA8LSB3YWxtYXJ0X2VkYV8yICU+JSANCiAgZmlsdGVyKFllYXI9PTIwMTAsTW9udGggPjEsIE1vbnRoIDwgNykgJT4lIA0KICBncm91cF9ieShZZWFyLE1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbHNhbGU9c3VtKFdlZWtseV9TYWxlcykpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IE1vbnRoLHZhbHVlc19mcm9tID0gdG90YWxzYWxlKSAlPiUgDQogIG11dGF0ZShTZW1lc3RlcjE9IHJvdW5kKGAyYCtgM2ArYDRgK2A1YCtgNmApLDIpICU+JSANCiAgc2VsZWN0KFllYXIsU2VtZXN0ZXIxKQ0KDQoNCnNlbWVzdGVycmV2MiA8LXdhbG1hcnRfZWRhXzIgJT4lIA0KICBmaWx0ZXIoWWVhcj09MjAxMCxNb250aCA+NikgJT4lIA0KICBncm91cF9ieShZZWFyLE1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbHNhbGU9c3VtKFdlZWtseV9TYWxlcykpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IE1vbnRoLHZhbHVlc19mcm9tID0gdG90YWxzYWxlKSAlPiUgDQogIG11dGF0ZShTZW1lc3RlcjI9IHJvdW5kKGA3YCtgOGArYDlgK2AxMGArYDExYCtgMTJgKSwyKSAlPiUgDQogIHNlbGVjdChZZWFyLFNlbWVzdGVyMikNCg0Kc2VtZXN0ZXJyZXYzIDwtIHdhbG1hcnRfZWRhXzIgJT4lIA0KICBmaWx0ZXIoWWVhcj09MjAxMSxNb250aCA+PTEsIE1vbnRoIDwgNyklPiUgDQogIGdyb3VwX2J5KFllYXIsTW9udGgpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsc2FsZT1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gTW9udGgsdmFsdWVzX2Zyb20gPSB0b3RhbHNhbGUpICU+JSANCiAgbXV0YXRlKFNlbWVzdGVyMz0gcm91bmQoYDFgK2AyYCtgM2ArYDRgK2A1YCtgNmApLDIpICU+JSANCiAgc2VsZWN0KFllYXIsU2VtZXN0ZXIzKQ0KDQpzZW1lc3RlcnJldjQgPC0gd2FsbWFydF9lZGFfMiAlPiUgDQogIGZpbHRlcihZZWFyPT0yMDExLE1vbnRoID42KSU+JSANCiAgZ3JvdXBfYnkoWWVhcixNb250aCkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlPXN1bShXZWVrbHlfU2FsZXMpKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBNb250aCx2YWx1ZXNfZnJvbSA9IHRvdGFsc2FsZSkgJT4lIA0KICBtdXRhdGUoU2VtZXN0ZXI0PSByb3VuZChgN2ArYDhgK2A5YCtgMTBgK2AxMWArYDEyYCkpICU+JSANCiAgc2VsZWN0KFllYXIsU2VtZXN0ZXI0KQ0KDQoNCg0Kc2VtZXN0ZXJyZXY1IDwtd2FsbWFydF9lZGFfMiAlPiUgDQogIGZpbHRlcihZZWFyPT0yMDEyLE1vbnRoID49MSwgTW9udGggPCA3KSU+JSANCiAgZ3JvdXBfYnkoWWVhcixNb250aCkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlPXN1bShXZWVrbHlfU2FsZXMpKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBNb250aCx2YWx1ZXNfZnJvbSA9IHRvdGFsc2FsZSkgJT4lIA0KICBtdXRhdGUoU2VtZXN0ZXI1PSByb3VuZChgMWArYDJgK2AzYCtgNGArYDVgK2A2YCksMikgJT4lIA0KICBzZWxlY3QoWWVhcixTZW1lc3RlcjUpDQoNCnNlbWVzdGVycmV2NiA8LXdhbG1hcnRfZWRhXzIgJT4lIA0KICBmaWx0ZXIoWWVhcj09MjAxMixNb250aCA+NiklPiUgDQogIGdyb3VwX2J5KFllYXIsTW9udGgpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsc2FsZT1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gTW9udGgsdmFsdWVzX2Zyb20gPSB0b3RhbHNhbGUpICU+JSANCiAgbXV0YXRlKFNlbWVzdGVyNj0gcm91bmQoYDdgK2A4YCtgOWArYDEwYCksMikgJT4lIA0KICBzZWxlY3QoWWVhcixTZW1lc3RlcjYpDQpgYGANCg0KDQpgYGB7cn0NCiMgam9pbmluZyBzZW1lc3RlciB0YWJsZXMgd2l0aCBpbm5lciBqb2luDQpzZW1lc3Rlcl9yZXZlbnVlcyA8LSBzZW1lc3RlcnJldiAlPiUgDQogIGlubmVyX2pvaW4oc2VtZXN0ZXJyZXYyLGJ5PSdZZWFyJykNCg0KDQpzZW1lc3Rlcl9yZXZlbnVlczIwMTEgPC0gc2VtZXN0ZXJyZXYzICU+JSANCiAgaW5uZXJfam9pbihzZW1lc3RlcnJldjQsYnk9J1llYXInKQ0KDQpzZW1lc3Rlcl9yZXZlbnVlczIwMTIgPC0gc2VtZXN0ZXJyZXY1ICU+JSANCiAgaW5uZXJfam9pbihzZW1lc3RlcnJldjYsYnk9J1llYXInKQ0KYGBgDQoNCg0KYGBge3J9DQojIGJyaW5nIGFsbCBqb2luZWQgdGFibGVzIHRvZ2V0aGVyIGludG8gbmV3IGRhdGFmcmFtZSB3aXRoIHJiaW5kDQphbGxfc2VtZXN0ZXJzIDwtIHJiaW5kKHNlbWVzdGVyX3JldmVudWVzLHNlbWVzdGVyX3JldmVudWVzMjAxMSxzZW1lc3Rlcl9yZXZlbnVlczIwMTIpDQpgYGANCg0KPGJyPg0KYGBge3J9DQphbGxfc2VtZXN0ZXJzJT4lDQogIHBpdm90X2xvbmdlcihjb2w9LVllYXIsbmFtZXNfdG8gPSAnU2VtZXN0ZXJzJyx2YWx1ZXNfdG8gPSAnUmV2ZW51ZScpICU+JSANCiAgZmlsdGVyKFJldmVudWUhPSdOQScpICU+JSAjIGZpbHRlciBvdXQgTnVsbHMNCiAgYXJyYW5nZShkZXNjKFJldmVudWUpKQ0KYGBgDQoNCg0KYGBge3J9DQojY3JlYXRpbmcgdmlzdWFsDQphbGxfc2VtZXN0ZXJzJT4lDQogIHBpdm90X2xvbmdlcihjb2w9LVllYXIsbmFtZXNfdG8gPSAnU2VtZXN0ZXJzJyx2YWx1ZXNfdG8gPSAnUmV2ZW51ZScpICU+JSANCiAgZmlsdGVyKFJldmVudWUhPSdOQScpICU+JSANCiAgZ2dwbG90KGFlcyhyZW9yZGVyKFNlbWVzdGVycyxSZXZlbnVlKSxSZXZlbnVlLGNvbD1SZXZlbnVlKSkrZ2VvbV9jb2woYWVzKGZpbGw9WWVhciksbGluZXdpZHRoPTEuMikrY29vcmRfZmxpcCgpK3hsYWIoJ1NlbWVzdGVyMjAxMHRvMjAxMicpK3NjYWxlX2NvbG9yX2dyYWRpZW50KGhpZ2g9J2dyZWVuMycsbG93PSdkYXJrcmVkJykNCmBgYA0KPHVsPjxsaT5TZW1lc3RlciA0IG9mIDIwMTEgKCBtb250aHMgSnVseSB0aHJvdWdoIERlY2VtYmVyKSBoYWQgdGhlIGdyZWF0ZXN0IFJldmVudWUgZm9yIHRoZSBzaXggc2VtZXN0ZXJzDQo8L2xpPg0KPGxpPg0KU2VtZXN0ZXIgNiBiZWluZyBtb250aHMgKEp1bHkgdGhyb3VnaCBPY3RvYmVyKSBoYWQgdGhlIGxlYXN0IFJldmVudWUgb2YgdGhlIHNpeCBTZW1lc3RlcnMNCjwvbGk+PC91bD4NCg0KDQpgYGB7cn0NCmFsbF9zZW1lc3RlcnMlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbD0tWWVhcixuYW1lc190byA9ICdTZW1lc3RlcnMnLHZhbHVlc190byA9ICdSZXZlbnVlJykgJT4lIA0KICBmaWx0ZXIoUmV2ZW51ZSE9J05BJykgJT4lICMgZmlsdGVyIG91dCBOdWxscw0KICAjZ3JvdXBfYnkoWWVhcixTZW1lc3RlcnMpICU+JSANCiAjIHN1bW1hcmlzZSh0b3RhbFJldj1zdW0oUmV2ZW51ZSkpICU+JSANCiAgbXV0YXRlKFJldmVudWVwcm9wb3J0aW9uPXJvdW5kKFJldmVudWUvc3VtKFJldmVudWUpLDIpKSAlPiUgDQogICAgYXJyYW5nZShkZXNjKFJldmVudWUpKSAgDQoNCmBgYA0KDQpgYGB7cn0NCmFsbF9zZW1lc3Rlcl9wcm9wb3J0aW9ucyA8LWFsbF9zZW1lc3RlcnMlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbD0tWWVhcixuYW1lc190byA9ICdTZW1lc3RlcnMnLHZhbHVlc190byA9ICdSZXZlbnVlJykgJT4lIA0KICBmaWx0ZXIoUmV2ZW51ZSE9J05BJykgJT4lICMgZmlsdGVyIG91dCBOdWxscw0KICBncm91cF9ieShTZW1lc3RlcnMpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsUmV2PXN1bShSZXZlbnVlKSkgJT4lIA0KICBtdXRhdGUoUmV2ZW51ZXByb3BvcnRpb249cm91bmQodG90YWxSZXYvc3VtKHRvdGFsUmV2KSwzKSoxMDApICU+JSANCiAgICBhcnJhbmdlKGRlc2ModG90YWxSZXYpKSAgDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClBpZURvbnV0KGFsbF9zZW1lc3Rlcl9wcm9wb3J0aW9ucyxhZXMoU2VtZXN0ZXJzLFJldmVudWVwcm9wb3J0aW9uKSx0aXRsZSA9ICdQZXJjZW50IG9mIFRvdGFsIFdlZWtseSBTYWxlcyBieSBTZW1lc3RlcicscmF0aW9CeUdyb3VwID0gRixkb251dExhYmVsU2l6ZT00LHBpZUxhYmVsU2l6ZT00LHNob3dQaWVOYW1lPUYsc2hvd1JhdGlvRG9udXQ9RixzaG93UmF0aW9QaWU9RixleHBsb2RlRG9udXQgPSBUKQ0KYGBgDQo8dWw+PGxpPg0KU2VtZXN0ZXIgNCBiZWluZyB0aGUgc2Vjb25kIGhhbGYgb2YgMjAxMSBzYXcgdGhlIGdyZWF0ZXN0IHBlcmNlbnQgMTkuNiUgb2YgcmV2ZW51ZSBpbnRha2UNCjwvbGk+PGxpPg0KU2VtZXN0ZXIgNiBiZWluZyB0aGUgc2Vjb25kIGhhbGYgb2YgMjAxMiBzYXcgdGhlIHNtYWxsZXN0IHBlcmNlbnQgMTEuNyUgb2YgcmV2ZW51ZSBpbnRha2UgYW5kIHdlIGNhbiBub3RlIHRoYXQgYW4gb2J2aW91cyByZWFzb24gZm9yIHRoaXMgaXMgdGhhdCB3ZSBkaWQgbm90IGhhdmUgc2FsZSByZWNvcmRzIGZvciB0aGUgaG9saWRheSBzZWFzb24gaW4gYWRkaXRpb24gdG8gdGhlIGZhY3QgdGhhdCB3ZSBrbm93IGZyb20gaGluZHNpZ2h0IHRoYXQgdGhlIGVjb25vbXkgaW4gMjAxMiBiZWdhbiB0byB0YWtlIGEgZG93bndhcmRzIGRpcmVjdGlvbi4NCjwvbGk+PC91bD4NCjxicj4NCjxicj4NCg0KDQpMYXN0bHksIEkgd2lsbCBpbmNsdWRlIHRoZSBnZW5lcmFsIHllYXIgb3ZlciB5ZWFyIGZvciAyMDExIGludG8gMjAxMiwgMjAxMiBxdWFydGVyIG92ZXIgcXVhcnRlciwgMjAxMiBtb250aCBvdmVyIG1vbnRoIGFzIHdlbGwgYXMgMjAxMiBtb250aGx5IHJldmVudWUNCg0KDQoNCmBgYHtyfQ0Kd2FsbWFydF9lZGFfMiAlPiUgDQogIGZpbHRlcihZZWFyPjIwMTApICU+JSANCiAgZ3JvdXBfYnkoWWVhcikgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gWWVhcix2YWx1ZXNfZnJvbSA9IHRvdGFsc2FsZXMpICU+JSANCiAgdHJhbnNtdXRlKFlvWT1yb3VuZCgoKChgMjAxMmAtYDIwMTFgKSoxMDApL2AyMDExYCksMikpDQpgYGANCmZvciAyMDExIGludG8gMjAxMg0KV2FsbWFydCBzYXcgYSAtMTguMyUgeWVhciBvdmVyIHllYXIgZHJvcCBpbiByZXZlbnVlDQoNCg0KYGBge3J9DQoNCmBgYA0KDQpgYGB7cn0NCndhbG1hcnRfZWRhXzIgJT4lIA0KICBmaWx0ZXIoWWVhcj4yMDExKSAlPiUgDQogIGdyb3VwX2J5KFF1YXJ0ZXJseSkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gUXVhcnRlcmx5LHZhbHVlc19mcm9tID0gdG90YWxzYWxlcykgJT4lIA0KICB0cmFuc211dGUoUW9RPXJvdW5kKCgoKGAzYC1gMmApKjEwMCkvYDJgKSwyKSkNCmBgYA0KZnJvbSBxdWFydGVyIDIgdG8gcXVhcnRlciAzIG9mIDIwMTIgDQoNCldhbG1hcnQgc2F3IGFuIG92ZXJhbGwgLTIuMTUlIGRyb3AgaW4gcXVhcnRlciBvdmVyIHF1YXJ0ZXIgZ3Jvd3RoDQoNCg0KYGBge3J9DQp3YWxtYXJ0X2VkYV8yICU+JSANCiAgZmlsdGVyKFllYXI+MjAxMSkgJT4lIA0KICBncm91cF9ieShNb250aCkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxzYWxlcz1zdW0oV2Vla2x5X1NhbGVzKSkgJT4lIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gTW9udGgsdmFsdWVzX2Zyb20gPSB0b3RhbHNhbGVzKSAlPiUgDQogIHRyYW5zbXV0ZShNb009cm91bmQoKCgoYDEwYC1gOWApKjEwMCkvYDlgKSwyKSkNCmBgYA0KZnJvbSBTZXB0ZW1iZXIgdG8gT2N0b2JlciBvZiAyMDEyIA0KDQpXYWxtYXJ0IHNhdyBhbiBvdmVyYWxsIDIuMDYlIHJpc2UgaW4gTW9udGggb3ZlciBNb250aCBncm93dGgNCg0KDQoNCmBgYHtyfQ0Kd2FsbWFydF9lZGFfMiAlPiUgDQogIGZpbHRlcihZZWFyPjIwMTEsTW9udGg+OSwpICU+JSANCiAgZ3JvdXBfYnkoTW9udGgpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsc2FsZXM9c3VtKFdlZWtseV9TYWxlcykpIA0KYGBgDQpJbiBPY3RvYmVyIG9mIDIwMTIgV2FsbWFydCBoYWQgYSBtb250aGx5IHJldmVudWUgb2YgMTg0LDM2MSw2ODANCg0KYGBge3J9DQpgYGANCg0KDQo=