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')



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)

  1. Thanksgiving is the holiday that brought in well above the normal day’s average
  2. Meanwhile both the Super_Bowl and Labour_Day each brought in revenue that was just above the normal day’s average revenue
  3. 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=