library(tidyr)
library(dplyr)

Introduction

The goal of this assignment is to practice in preparing different datasets for downstream analysis work.

We were tasked with choosing three wide datasets identified in the Week 5 Discussion items.

The datasets I chose were:

  1. NYC Subway Ridership by Cameron Smith
  2. US Gross Domestic Product by Quarter (2020 mostly)

NYC Subway Ridership

Cameron Identified a dataset located in the MTA Web Site listing annual ridership at all stations from 2013-2018.

He also suggested “An example of analysis that can be done is the change in ridership per station, or perhaps by borough, from one year to the next.”

Getting the Data I copied/pasted the data (minus the borough totals at the end) from that site to an excel spreadsheet and subsequently saved it to a csv located at my DS607 github repo.

subway_raw <- read.csv(file = 'https://raw.githubusercontent.com/georg4re/DS607/master/data/subway-ridership.csv')
head(subway_raw)
##                   ï..Station..alphabetical.by.borough.     X2013     X2014
## 1                                            The Bronx                    
## 2           138 St-Grand Concourse 4 subway 5 subway   957,984 1,033,559
## 3 149 St-Grand Concourse 2 subway 4 subway 5 subway 4,427,399 4,536,888
## 4  161 St-Yankee Stadium B subway D subway 4 subway 8,766,012 8,961,029
## 5                                     167 St 4 subway 3,081,534 3,067,345
## 6                             167 St B subwayD subway 3,091,289 3,245,977
##       X2015     X2016     X2017     X2018 X2017.2018.Change       X X2018.Rank
## 1                                                                           NA
## 2 1,056,380 1,070,024 1,036,746   944,598           -92,148  -8.90%        365
## 3 4,424,754 4,381,900 4,255,015 3,972,763          -282,252  -6.60%        121
## 4 8,922,188 8,784,407 8,596,506 8,392,290          -204,216  -2.40%         38
## 5 3,180,274 3,179,087 2,954,228 2,933,140           -21,088  -0.70%        165
## 6 3,295,032 3,365,748 3,293,451 2,022,919        -1,270,532 -38.60%        231
summary(subway_raw)
##  ï..Station..alphabetical.by.borough.    X2013              X2014          
##  Length:428                           Length:428         Length:428        
##  Class :character                     Class :character   Class :character  
##  Mode  :character                     Mode  :character   Mode  :character  
##                                                                            
##                                                                            
##                                                                            
##                                                                            
##     X2015              X2016              X2017              X2018          
##  Length:428         Length:428         Length:428         Length:428        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  X2017.2018.Change       X               X2018.Rank   
##  Length:428         Length:428         Min.   :  1.0  
##  Class :character   Class :character   1st Qu.:106.8  
##  Mode  :character   Mode  :character   Median :212.5  
##                                        Mean   :212.5  
##                                        3rd Qu.:318.2  
##                                        Max.   :424.0  
##                                        NA's   :4

As we can see, the dataset is “wide”, it presents several other challenges like:

  • column names are weird
  • borough information is presented isolated in a row prior to the stations for that borough
  • the ridership values are treated as strings not numeric variables.
  • for our analysis, we don’t need the last three columns (2017-2018 change, pct or rank)

Cleaning the Data 1: Change column Names

#First column name is tough to rename so using basic R
names(subway_raw)[1] = 'station'

#using dplyr to rename other columns
subway_data <- subway_raw %>% 
  rename( '2013'= 'X2013',
    '2014' = 'X2014', 
    '2015' = 'X2015',
    '2016' = 'X2016',
    '2017' = 'X2017',
    '2018' = 'X2018', 
    '2017-2018 Change' = X2017.2018.Change, 
    'Pct Change' = X, 
    '2018 Rank' = X2018.Rank
  )
colnames(subway_data)
##  [1] "station"          "2013"             "2014"             "2015"            
##  [5] "2016"             "2017"             "2018"             "2017-2018 Change"
##  [9] "Pct Change"       "2018 Rank"

Cleaning the Data 2: Assign the Borough name to each row

#Find the rows containing the Borough names
#I noticed these are the only ones that have NA in the 2018 rank
borough_names <- subway_data %>% 
  dplyr::filter(is.na(subway_data$`2018 Rank`)) %>%
  select(station)

# Loop thru the subway_data and assign the proper borough to the data
subway_data$borough <- ''
idx <- 0 #begin with the first borough
for (i in 1:nrow(subway_data)) {
  if (is.na(subway_data$`2018 Rank`[i])) { #it's the boroug's row
    idx <- idx + 1
  } else { 
    subway_data$borough[i] = borough_names$station[idx]
  }
}

#I notice I could have avoided extracting the borough's name into 
# a vector and just use the previous name within the for loop

#I will now remove the rows with only borough names from the data
subway_data <- subway_data %>%
  drop_na(`2018 Rank`)
  
head(subway_data)
##                                                station      2013      2014
## 1           138 St-Grand Concourse 4 subway 5 subway   957,984 1,033,559
## 2 149 St-Grand Concourse 2 subway 4 subway 5 subway 4,427,399 4,536,888
## 3  161 St-Yankee Stadium B subway D subway 4 subway 8,766,012 8,961,029
## 4                                     167 St 4 subway 3,081,534 3,067,345
## 5                             167 St B subwayD subway 3,091,289 3,245,977
## 6                                     170 St 4 subway 2,961,575 2,941,958
##        2015      2016      2017      2018 2017-2018 Change Pct Change 2018 Rank
## 1 1,056,380 1,070,024 1,036,746   944,598          -92,148     -8.90%       365
## 2 4,424,754 4,381,900 4,255,015 3,972,763         -282,252     -6.60%       121
## 3 8,922,188 8,784,407 8,596,506 8,392,290         -204,216     -2.40%        38
## 4 3,180,274 3,179,087 2,954,228 2,933,140          -21,088     -0.70%       165
## 5 3,295,032 3,365,748 3,293,451 2,022,919       -1,270,532    -38.60%       231
## 6 3,045,205 3,038,777 2,785,331 2,562,443         -222,888     -8.00%       183
##     borough
## 1 The Bronx
## 2 The Bronx
## 3 The Bronx
## 4 The Bronx
## 5 The Bronx
## 6 The Bronx

Finish cleaning data: Changing the numeric column types + remove unneeded columns

subway_data <- subway_data %>%
  mutate('2013' = as.numeric(gsub(',', '', subway_data$`2013`)),
         '2014' = as.numeric(gsub(',', '', subway_data$`2014`)),
         '2015' = as.numeric(gsub(',', '', subway_data$`2015`)),
         '2016' = as.numeric(gsub(',', '', subway_data$`2016`)),
         '2017' = as.numeric(gsub(',', '', subway_data$`2017`)),
         '2018' = as.numeric(gsub(',', '', subway_data$`2018`))) %>%
  select(borough, station, '2013', '2014', '2015', '2016', '2017', '2018')
head(subway_data)
##     borough                                              station    2013
## 1 The Bronx           138 St-Grand Concourse 4 subway 5 subway  957984
## 2 The Bronx 149 St-Grand Concourse 2 subway 4 subway 5 subway 4427399
## 3 The Bronx  161 St-Yankee Stadium B subway D subway 4 subway 8766012
## 4 The Bronx                                     167 St 4 subway 3081534
## 5 The Bronx                             167 St B subwayD subway 3091289
## 6 The Bronx                                     170 St 4 subway 2961575
##      2014    2015    2016    2017    2018
## 1 1033559 1056380 1070024 1036746  944598
## 2 4536888 4424754 4381900 4255015 3972763
## 3 8961029 8922188 8784407 8596506 8392290
## 4 3067345 3180274 3179087 2954228 2933140
## 5 3245977 3295032 3365748 3293451 2022919
## 6 2941958 3045205 3038777 2785331 2562443

Pivoting Let’s pivot from wide to long so as to have one single observation per row.

subway_clean <- subway_data %>%
  pivot_longer(
    3:8,
    names_to="year",
    values_to="riders",
    values_drop_na = TRUE
    )

head(subway_clean)
## # A tibble: 6 x 4
##   borough   station                                    year   riders
##   <chr>     <chr>                                      <chr>   <dbl>
## 1 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2013   957984
## 2 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2014  1033559
## 3 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2015  1056380
## 4 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2016  1070024
## 5 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2017  1036746
## 6 The Bronx 138 St-Grand Concourse 4 subway 5 subway 2018   944598

Let’s Analyze this data

Now that we have a long, focused data set, we can go ahead and try to perform the requested analysis. Because of the many stations, I will forgo analyzing a particular station’s number of riders but this analysis can be performed using the subway_clean data set.

Ridership per Borough per Year

riders_per_borough <- subway_clean %>%
  group_by(borough, year) %>%
  summarize(avg_riders = mean(riders, na.rm = TRUE), .groups = "drop")

riders_per_borough
## # A tibble: 24 x 3
##    borough   year  avg_riders
##    <chr>     <chr>      <dbl>
##  1 Brooklyn  2013    2371925.
##  2 Brooklyn  2014    2437738.
##  3 Brooklyn  2015    2468462.
##  4 Brooklyn  2016    2449301.
##  5 Brooklyn  2017    2416386.
##  6 Brooklyn  2018    2358161.
##  7 Manhattan 2013    8037119.
##  8 Manhattan 2014    8250454.
##  9 Manhattan 2015    8223157.
## 10 Manhattan 2016    8189785.
## # ... with 14 more rows
library(ggplot2)
library(scales)

p <- ggplot(data = riders_per_borough, aes(x = year, y = avg_riders, group = borough, colour=borough)) + 
     geom_line() +
     geom_point() +
     scale_x_discrete(breaks = riders_per_borough$year, labels = riders_per_borough$year)

#Ensure a clean display 
p + scale_y_continuous(labels = comma)

Conclusion

All boroughs show a decline on ridership within the last few years. This is noticeable from 2016. There was an increase in ridership in 2013 followed by slight increases until 2016. Manhattan, The Bronx and Queens show a steeper decline than Brooklyn. Let’s take a look at the overall ridership:

all_riders <-  subway_clean %>%
  group_by(year) %>%
  summarize(avg_riders = mean(riders, na.rm = TRUE))
## `summarise()` ungrouping output (override with `.groups` argument)
p <- ggplot(data = all_riders) + 
     geom_bar(mapping = aes(x = year, y = avg_riders, fill=year), stat='identity') + 
     scale_y_continuous(labels = comma)

p

This graph shows that the average number of riders has steadily declined in NY since 2016.

US Gross Domestic Product by Quarter (2020 mostly)

Arushi Arora Identified a dataset located in the Fred Economic Data Website listing annual US Gross Domestic product for 2019 and 2020

Arora suggested that “It might be helpful specially during the pandemic to identify sectors that are doing great and other that have been greatly impacted.”

Getting the Data I saved the data provided by Aroro as a tab delimited file located at my DS607 github repo.

gdp_raw <- read.table(file= 'https://raw.githubusercontent.com/georg4re/DS607/master/data/2019-2020-gdp.txt',
                      sep="\t", header=TRUE)
head(gdp_raw)
##   Line                              Name    Q2.2020    Q1.2020    Q2.2019
## 1    1            Gross domestic product 19,520.114 21,561.139 21,329.877
## 2    2 Personal consumption expenditures 13,097.348 14,545.460 14,497.320
## 3    3                             Goods  4,361.518  4,552.919  4,517.679
## 4    4                     Durable goods  1,478.299  1,496.444  1,535.984
## 5    5                  Nondurable goods  2,883.219  3,056.474  2,981.695
## 6    6                          Services  8,735.830  9,992.541  9,979.641
summary(gdp_raw)
##       Line           Name             Q2.2020            Q1.2020         
##  Min.   : 1.00   Length:26          Length:26          Length:26         
##  1st Qu.: 7.25   Class :character   Class :character   Class :character  
##  Median :13.50   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :13.50                                                           
##  3rd Qu.:19.75                                                           
##  Max.   :26.00                                                           
##    Q2.2019         
##  Length:26         
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

As we can see, the dataset is “wide”, it also treats the values as characters and not as numeric values. Several sub section data also have the same names, we will drop all repeated subsections with the same name for a smaller dataset without losing important data.

####Cleaning data: Changing the numeric column types

gdp_data <- gdp_raw %>%
  mutate('Q2.2020' = as.numeric(gsub(',', '', gdp_raw$`Q2.2020`)) * 1000,
         'Q1.2020' = as.numeric(gsub(',', '', gdp_raw$`Q1.2020`)) * 1000,
         'Q2.2019' = as.numeric(gsub(',', '', gdp_raw$`Q2.2019`)) * 1000) %>%
  rename( '2020.2'= 'Q2.2020',
    '2020.1' = 'Q1.2020', 
    '2019.2' = 'Q2.2019') %>%
  distinct(Name, .keep_all = TRUE)

#I multiplied by 1000 to get rid of the decimal point in the notation 
head(gdp_data)
##   Line                              Name   2020.2   2020.1   2019.2
## 1    1            Gross domestic product 19520114 21561139 21329877
## 2    2 Personal consumption expenditures 13097348 14545460 14497320
## 3    3                             Goods  4361518  4552919  4517679
## 4    4                     Durable goods  1478299  1496444  1535984
## 5    5                  Nondurable goods  2883219  3056474  2981695
## 6    6                          Services  8735830  9992541  9979641

Pivoting Let’s pivot from wide to long so as to have one single observation per row.

gdp_clean <- gdp_data %>%
  pivot_longer(
    3:5,
    names_to="Quarter",
    values_to="Gdp",
    values_drop_na = TRUE
    ) %>%
  select(Name, Quarter, Gdp)%>%
  arrange(Name, Quarter, by_group = TRUE)

head(gdp_clean)
## # A tibble: 6 x 3
##   Name                          Quarter     Gdp
##   <chr>                         <chr>     <dbl>
## 1 Change in private inventories 2019.2    53060
## 2 Change in private inventories 2020.1   -52117
## 3 Change in private inventories 2020.2  -298356
## 4 Durable goods                 2019.2  1535984
## 5 Durable goods                 2020.1  1496444
## 6 Durable goods                 2020.2  1478299

Analyzing Data **

Now that we have a long, focused data set, we can go ahead and try to perform the requested analysis. We can pay special attention to the second quarter of 2020 to see the areas more greatly affected by the pandemic

GDP Per Quarter

all_gdp <- gdp_clean %>%
  group_by(Quarter) %>%
  summarise(avg_gdp = mean(Gdp), median_gdp = median(Gdp))
## `summarise()` ungrouping output (override with `.groups` argument)
all_gdp
## # A tibble: 3 x 3
##   Quarter  avg_gdp median_gdp
##   <chr>      <dbl>      <dbl>
## 1 2019.2  3764302    2420831 
## 2 2020.1  3779190.   2410136.
## 3 2020.2  3416684.   2060748.

Based on the overall mean gdp we can see that, as we could probably presume, the average GDP figure fell significantly during the 2nd quarter of 2020.

We’ll add a percent change column to our clean data to identify gains/losses by quarter

gdp_by_pct_change <-mutate(gdp_clean, Row = 1:n()) %>%
  group_by(Name) %>%
  mutate(pct_change = Gdp/lag(Gdp) * 100) %>%
  ungroup %>%
  select(Name, Quarter, pct_change) %>%
  arrange(pct_change)
head(gdp_by_pct_change)
## # A tibble: 6 x 3
##   Name                              Quarter pct_change
##   <chr>                             <chr>        <dbl>
## 1 Change in private inventories     2020.1       -98.2
## 2 Exports                           2020.2        73.3
## 3 Net exports of goods and services 2020.1        76.7
## 4 Imports                           2020.2        79.6
## 5 Gross private domestic investment 2020.2        85.1
## 6 Services                          2020.2        87.4

By looking at this data, we can see that Exports and Imports suffered the most during the Second Quarter as they decreased the most among the categories we have.

p <- ggplot(data = gdp_clean, aes(x = Quarter, y = Gdp, group=Name, color=Name )) +  
     geom_line() +
     geom_point() +
     scale_y_continuous(labels = comma)
p <- p + theme(legend.position="bottom")
p <- p + guides(fill=guide_legend(nrow=5, byrow=TRUE))

p

We see a general decline in values, let’s take a look at this same graph by percent change:

#Assign 100 to all NA assuming we start at 100% on 2019.2
gdp_by_pct_change[is.na(gdp_by_pct_change)] <- 100
p <- ggplot(data = gdp_by_pct_change, aes(x = Quarter, y = pct_change, group=Name, color=Name )) +  
     geom_line() +
     geom_point() +
     scale_y_continuous(labels = comma)
p <- p + theme(legend.position="bottom")
p <- p + guides(fill=guide_legend(nrow=10, byrow=TRUE))

p

Let’s take a look at the bigger changes in the 2nd Quarter:

gdp_by_pct_change %>% 
    filter(Quarter=="2020.2") %>%
    arrange(desc(pct_change)) %>%
    slice(1:5) %>%
  ggplot(data = .) + 
     geom_bar(mapping = aes(x = Name, y = pct_change, fill=Name), stat='identity') +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank()
        )

Based on this, the biggest gain was in Change in private inventories with a near 600% increase. This is also the same variable decreased almost 100% between 2019 and the 1st quarter in 2020

Most affected

gdp_by_pct_change %>% 
    filter(Quarter == '2020.2') %>%
    arrange(pct_change) %>%
    slice(1:5) %>%
  ggplot(data = .) + 
     geom_bar(mapping = aes(x = Name, y = pct_change, fill=Name), stat='identity') +
    theme(axis.title.x=element_blank(),
        axis.text.x=element_blank()
        )

Again, this graph shows that exports, imports and Gross private domestic investments were the most affected sectors in the 2nd Quarter.

Conclusion

Although there is a general decline in values, some sectors like Private inventories and Defense reported gains in their GDP during the 2nd quarter of 2020.

LS0tDQp0aXRsZTogIkRTNjA3IC0gUHJvamVjdDIiDQphdXRob3I6ICJCaGFyYW5pIE5pdHRhbGEsIEdlb3JnZSBDcnV6IERlc2NoYW1wcyINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0DQotLS0NCg0KYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCiMjIyBJbnRyb2R1Y3Rpb24NClRoZSBnb2FsIG9mIHRoaXMgYXNzaWdubWVudCBpcyB0byBwcmFjdGljZSBpbiBwcmVwYXJpbmcgZGlmZmVyZW50IGRhdGFzZXRzIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzIHdvcmsuIA0KDQpXZSB3ZXJlIHRhc2tlZCB3aXRoIGNob29zaW5nIHRocmVlIHdpZGUgZGF0YXNldHMgaWRlbnRpZmllZCBpbiB0aGUgV2VlayA1IERpc2N1c3Npb24gaXRlbXMuIA0KDQpUaGUgZGF0YXNldHMgSSBjaG9zZSB3ZXJlOiANCg0KMS4gKipOWUMgU3Vid2F5IFJpZGVyc2hpcCoqIGJ5ICpDYW1lcm9uIFNtaXRoKg0KMi4gKipVUyBHcm9zcyBEb21lc3RpYyBQcm9kdWN0IGJ5IFF1YXJ0ZXIgKDIwMjAgbW9zdGx5KSoqDQoNCg0KIyMjIE5ZQyBTdWJ3YXkgUmlkZXJzaGlwDQpDYW1lcm9uIElkZW50aWZpZWQgYSBkYXRhc2V0IGxvY2F0ZWQgaW4gdGhlIFtNVEEgV2ViIFNpdGVdKGh0dHA6Ly93ZWIubXRhLmluZm8vbnljdC9mYWN0cy9yaWRlcnNoaXAvcmlkZXJzaGlwX3N1Yl9hbm51YWwuaHRtKSBsaXN0aW5nIGFubnVhbCByaWRlcnNoaXAgYXQgYWxsIHN0YXRpb25zIGZyb20gMjAxMy0yMDE4Lg0KDQpIZSBhbHNvIHN1Z2dlc3RlZCAiQW4gZXhhbXBsZSBvZiBhbmFseXNpcyB0aGF0IGNhbiBiZSBkb25lIGlzIHRoZSBjaGFuZ2UgaW4gcmlkZXJzaGlwIHBlciBzdGF0aW9uLCBvciBwZXJoYXBzIGJ5IGJvcm91Z2gsIGZyb20gb25lIHllYXIgdG8gdGhlIG5leHQuIg0KDQoqKkdldHRpbmcgdGhlIERhdGEqKg0KSSBjb3BpZWQvcGFzdGVkIHRoZSBkYXRhIChtaW51cyB0aGUgYm9yb3VnaCB0b3RhbHMgYXQgdGhlIGVuZCkgZnJvbSB0aGF0IHNpdGUgdG8gYW4gZXhjZWwgc3ByZWFkc2hlZXQgYW5kIHN1YnNlcXVlbnRseSBzYXZlZCBpdCB0byBhIGNzdiBsb2NhdGVkIGF0IG15IERTNjA3IGdpdGh1YiByZXBvLiANCg0KYGBge3J9DQpzdWJ3YXlfcmF3IDwtIHJlYWQuY3N2KGZpbGUgPSAnaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlb3JnNHJlL0RTNjA3L21hc3Rlci9kYXRhL3N1YndheS1yaWRlcnNoaXAuY3N2JykNCmhlYWQoc3Vid2F5X3JhdykNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkoc3Vid2F5X3JhdykNCmBgYA0KDQoNCkFzIHdlIGNhbiBzZWUsIHRoZSBkYXRhc2V0IGlzICJ3aWRlIiwgaXQgcHJlc2VudHMgc2V2ZXJhbCBvdGhlciBjaGFsbGVuZ2VzIGxpa2U6IA0KDQotIGNvbHVtbiBuYW1lcyBhcmUgd2VpcmQNCi0gYm9yb3VnaCBpbmZvcm1hdGlvbiBpcyBwcmVzZW50ZWQgaXNvbGF0ZWQgaW4gYSByb3cgcHJpb3IgdG8gdGhlIHN0YXRpb25zIGZvciB0aGF0IGJvcm91Z2gNCi0gdGhlIHJpZGVyc2hpcCB2YWx1ZXMgYXJlIHRyZWF0ZWQgYXMgc3RyaW5ncyBub3QgbnVtZXJpYyB2YXJpYWJsZXMuDQotIGZvciBvdXIgYW5hbHlzaXMsIHdlIGRvbid0IG5lZWQgdGhlIGxhc3QgdGhyZWUgY29sdW1ucyAoMjAxNy0yMDE4IGNoYW5nZSwgcGN0IG9yIHJhbmspDQoNCioqQ2xlYW5pbmcgdGhlIERhdGEgMTogQ2hhbmdlIGNvbHVtbiBOYW1lcyAqKg0KYGBge3J9DQojRmlyc3QgY29sdW1uIG5hbWUgaXMgdG91Z2ggdG8gcmVuYW1lIHNvIHVzaW5nIGJhc2ljIFINCm5hbWVzKHN1YndheV9yYXcpWzFdID0gJ3N0YXRpb24nDQoNCiN1c2luZyBkcGx5ciB0byByZW5hbWUgb3RoZXIgY29sdW1ucw0Kc3Vid2F5X2RhdGEgPC0gc3Vid2F5X3JhdyAlPiUgDQogIHJlbmFtZSggJzIwMTMnPSAnWDIwMTMnLA0KICAgICcyMDE0JyA9ICdYMjAxNCcsIA0KICAgICcyMDE1JyA9ICdYMjAxNScsDQogICAgJzIwMTYnID0gJ1gyMDE2JywNCiAgICAnMjAxNycgPSAnWDIwMTcnLA0KICAgICcyMDE4JyA9ICdYMjAxOCcsIA0KICAgICcyMDE3LTIwMTggQ2hhbmdlJyA9IFgyMDE3LjIwMTguQ2hhbmdlLCANCiAgICAnUGN0IENoYW5nZScgPSBYLCANCiAgICAnMjAxOCBSYW5rJyA9IFgyMDE4LlJhbmsNCiAgKQ0KY29sbmFtZXMoc3Vid2F5X2RhdGEpDQoNCmBgYA0KDQoNCioqQ2xlYW5pbmcgdGhlIERhdGEgMjogQXNzaWduIHRoZSBCb3JvdWdoIG5hbWUgdG8gZWFjaCByb3cgKioNCg0KYGBge3J9DQojRmluZCB0aGUgcm93cyBjb250YWluaW5nIHRoZSBCb3JvdWdoIG5hbWVzDQojSSBub3RpY2VkIHRoZXNlIGFyZSB0aGUgb25seSBvbmVzIHRoYXQgaGF2ZSBOQSBpbiB0aGUgMjAxOCByYW5rDQpib3JvdWdoX25hbWVzIDwtIHN1YndheV9kYXRhICU+JSANCiAgZHBseXI6OmZpbHRlcihpcy5uYShzdWJ3YXlfZGF0YSRgMjAxOCBSYW5rYCkpICU+JQ0KICBzZWxlY3Qoc3RhdGlvbikNCg0KIyBMb29wIHRocnUgdGhlIHN1YndheV9kYXRhIGFuZCBhc3NpZ24gdGhlIHByb3BlciBib3JvdWdoIHRvIHRoZSBkYXRhDQpzdWJ3YXlfZGF0YSRib3JvdWdoIDwtICcnDQppZHggPC0gMCAjYmVnaW4gd2l0aCB0aGUgZmlyc3QgYm9yb3VnaA0KZm9yIChpIGluIDE6bnJvdyhzdWJ3YXlfZGF0YSkpIHsNCiAgaWYgKGlzLm5hKHN1YndheV9kYXRhJGAyMDE4IFJhbmtgW2ldKSkgeyAjaXQncyB0aGUgYm9yb3VnJ3Mgcm93DQogICAgaWR4IDwtIGlkeCArIDENCiAgfSBlbHNlIHsgDQogICAgc3Vid2F5X2RhdGEkYm9yb3VnaFtpXSA9IGJvcm91Z2hfbmFtZXMkc3RhdGlvbltpZHhdDQogIH0NCn0NCg0KI0kgbm90aWNlIEkgY291bGQgaGF2ZSBhdm9pZGVkIGV4dHJhY3RpbmcgdGhlIGJvcm91Z2gncyBuYW1lIGludG8gDQojIGEgdmVjdG9yIGFuZCBqdXN0IHVzZSB0aGUgcHJldmlvdXMgbmFtZSB3aXRoaW4gdGhlIGZvciBsb29wDQoNCiNJIHdpbGwgbm93IHJlbW92ZSB0aGUgcm93cyB3aXRoIG9ubHkgYm9yb3VnaCBuYW1lcyBmcm9tIHRoZSBkYXRhDQpzdWJ3YXlfZGF0YSA8LSBzdWJ3YXlfZGF0YSAlPiUNCiAgZHJvcF9uYShgMjAxOCBSYW5rYCkNCiAgDQpoZWFkKHN1YndheV9kYXRhKQ0KYGBgDQoNCg0KKipGaW5pc2ggY2xlYW5pbmcgZGF0YTogQ2hhbmdpbmcgdGhlIG51bWVyaWMgY29sdW1uIHR5cGVzICsgcmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMgKioNCmBgYHtyfQ0Kc3Vid2F5X2RhdGEgPC0gc3Vid2F5X2RhdGEgJT4lDQogIG11dGF0ZSgnMjAxMycgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMTNgKSksDQogICAgICAgICAnMjAxNCcgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMTRgKSksDQogICAgICAgICAnMjAxNScgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMTVgKSksDQogICAgICAgICAnMjAxNicgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMTZgKSksDQogICAgICAgICAnMjAxNycgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMTdgKSksDQogICAgICAgICAnMjAxOCcgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgc3Vid2F5X2RhdGEkYDIwMThgKSkpICU+JQ0KICBzZWxlY3QoYm9yb3VnaCwgc3RhdGlvbiwgJzIwMTMnLCAnMjAxNCcsICcyMDE1JywgJzIwMTYnLCAnMjAxNycsICcyMDE4JykNCmhlYWQoc3Vid2F5X2RhdGEpDQpgYGANCg0KDQoqKlBpdm90aW5nICoqDQpMZXQncyBwaXZvdCBmcm9tIHdpZGUgdG8gbG9uZyBzbyBhcyB0byBoYXZlIG9uZSBzaW5nbGUgb2JzZXJ2YXRpb24gcGVyIHJvdy4gDQoNCmBgYHtyfQ0Kc3Vid2F5X2NsZWFuIDwtIHN1YndheV9kYXRhICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgMzo4LA0KICAgIG5hbWVzX3RvPSJ5ZWFyIiwNCiAgICB2YWx1ZXNfdG89InJpZGVycyIsDQogICAgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFDQogICAgKQ0KDQpoZWFkKHN1YndheV9jbGVhbikNCmBgYA0KDQoNCioqTGV0J3MgQW5hbHl6ZSB0aGlzIGRhdGEgKioNCg0KTm93IHRoYXQgd2UgaGF2ZSBhIGxvbmcsIGZvY3VzZWQgZGF0YSBzZXQsIHdlIGNhbiBnbyBhaGVhZCBhbmQgdHJ5IHRvIHBlcmZvcm0gdGhlIHJlcXVlc3RlZCBhbmFseXNpcy4gIEJlY2F1c2Ugb2YgdGhlIG1hbnkgc3RhdGlvbnMsIEkgd2lsbCBmb3JnbyBhbmFseXppbmcgYSBwYXJ0aWN1bGFyIHN0YXRpb24ncyBudW1iZXIgb2YgcmlkZXJzIGJ1dCB0aGlzIGFuYWx5c2lzIGNhbiBiZSBwZXJmb3JtZWQgdXNpbmcgdGhlIHN1YndheV9jbGVhbiBkYXRhIHNldC4NCg0KIyMjIyBSaWRlcnNoaXAgcGVyIEJvcm91Z2ggcGVyIFllYXIgIyMjDQoNCmBgYHtyfQ0KcmlkZXJzX3Blcl9ib3JvdWdoIDwtIHN1YndheV9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYm9yb3VnaCwgeWVhcikgJT4lDQogIHN1bW1hcml6ZShhdmdfcmlkZXJzID0gbWVhbihyaWRlcnMsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpDQoNCnJpZGVyc19wZXJfYm9yb3VnaA0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzY2FsZXMpDQoNCnAgPC0gZ2dwbG90KGRhdGEgPSByaWRlcnNfcGVyX2Jvcm91Z2gsIGFlcyh4ID0geWVhciwgeSA9IGF2Z19yaWRlcnMsIGdyb3VwID0gYm9yb3VnaCwgY29sb3VyPWJvcm91Z2gpKSArIA0KICAgICBnZW9tX2xpbmUoKSArDQogICAgIGdlb21fcG9pbnQoKSArDQogICAgIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gcmlkZXJzX3Blcl9ib3JvdWdoJHllYXIsIGxhYmVscyA9IHJpZGVyc19wZXJfYm9yb3VnaCR5ZWFyKQ0KDQojRW5zdXJlIGEgY2xlYW4gZGlzcGxheSANCnAgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpDQoNCmBgYA0KDQojIyMjIENvbmNsdXNpb24gDQoNCkFsbCBib3JvdWdocyBzaG93IGEgZGVjbGluZSBvbiByaWRlcnNoaXAgd2l0aGluIHRoZSBsYXN0IGZldyB5ZWFycy4gIFRoaXMgaXMgbm90aWNlYWJsZSBmcm9tIDIwMTYuICBUaGVyZSB3YXMgYW4gaW5jcmVhc2UgaW4gcmlkZXJzaGlwIGluIDIwMTMgZm9sbG93ZWQgYnkgc2xpZ2h0IGluY3JlYXNlcyB1bnRpbCAyMDE2LiAgTWFuaGF0dGFuLCBUaGUgQnJvbnggYW5kIFF1ZWVucyBzaG93IGEgc3RlZXBlciBkZWNsaW5lIHRoYW4gQnJvb2tseW4uIExldCdzIHRha2UgYSBsb29rIGF0IHRoZSBvdmVyYWxsIHJpZGVyc2hpcDoNCg0KYGBge3J9DQphbGxfcmlkZXJzIDwtICBzdWJ3YXlfY2xlYW4gJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpemUoYXZnX3JpZGVycyA9IG1lYW4ocmlkZXJzLCBuYS5ybSA9IFRSVUUpKQ0KDQpwIDwtIGdncGxvdChkYXRhID0gYWxsX3JpZGVycykgKyANCiAgICAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0geWVhciwgeSA9IGF2Z19yaWRlcnMsIGZpbGw9eWVhciksIHN0YXQ9J2lkZW50aXR5JykgKyANCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KDQpwDQpgYGANCg0KVGhpcyBncmFwaCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIG51bWJlciBvZiByaWRlcnMgaGFzIHN0ZWFkaWx5IGRlY2xpbmVkIGluIE5ZIHNpbmNlIDIwMTYuIA0KDQojIyMgVVMgR3Jvc3MgRG9tZXN0aWMgUHJvZHVjdCBieSBRdWFydGVyICgyMDIwIG1vc3RseSkNCg0KQXJ1c2hpIEFyb3JhIElkZW50aWZpZWQgYSBkYXRhc2V0IGxvY2F0ZWQgaW4gdGhlIFtGcmVkIEVjb25vbWljIERhdGEgV2Vic2l0ZV0oaHR0cHM6Ly9mcmVkLnN0bG91aXNmZWQub3JnL3Nlcmllcy9HRFApIGxpc3RpbmcgYW5udWFsIFVTIEdyb3NzIERvbWVzdGljIHByb2R1Y3QgZm9yIDIwMTkgYW5kIDIwMjAgDQoNCkFyb3JhIHN1Z2dlc3RlZCB0aGF0ICJJdCBtaWdodCBiZSBoZWxwZnVsIHNwZWNpYWxseSBkdXJpbmcgdGhlIHBhbmRlbWljIHRvIGlkZW50aWZ5IHNlY3RvcnMgdGhhdCBhcmUgZG9pbmcgZ3JlYXQgYW5kIG90aGVyIHRoYXQgaGF2ZSBiZWVuIGdyZWF0bHkgaW1wYWN0ZWQuIg0KDQoNCioqR2V0dGluZyB0aGUgRGF0YSoqDQpJIHNhdmVkIHRoZSBkYXRhIHByb3ZpZGVkIGJ5IEFyb3JvIGFzIGEgdGFiIGRlbGltaXRlZCBmaWxlIGxvY2F0ZWQgYXQgbXkgRFM2MDcgZ2l0aHViIHJlcG8uIA0KDQpgYGB7cn0NCmdkcF9yYXcgPC0gcmVhZC50YWJsZShmaWxlPSAnaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlb3JnNHJlL0RTNjA3L21hc3Rlci9kYXRhLzIwMTktMjAyMC1nZHAudHh0JywNCiAgICAgICAgICAgICAgICAgICAgICBzZXA9Ilx0IiwgaGVhZGVyPVRSVUUpDQpoZWFkKGdkcF9yYXcpDQpgYGANCmBgYHtyfQ0Kc3VtbWFyeShnZHBfcmF3KQ0KDQpgYGANCg0KQXMgd2UgY2FuIHNlZSwgdGhlIGRhdGFzZXQgaXMgIndpZGUiLCBpdCBhbHNvIHRyZWF0cyB0aGUgdmFsdWVzIGFzIGNoYXJhY3RlcnMgYW5kIG5vdCBhcyBudW1lcmljIHZhbHVlcy4gIFNldmVyYWwgc3ViIHNlY3Rpb24gZGF0YSBhbHNvIGhhdmUgdGhlIHNhbWUgbmFtZXMsIHdlIHdpbGwgZHJvcCBhbGwgcmVwZWF0ZWQgc3Vic2VjdGlvbnMgd2l0aCB0aGUgc2FtZSBuYW1lIGZvciBhIHNtYWxsZXIgZGF0YXNldCB3aXRob3V0IGxvc2luZyBpbXBvcnRhbnQgZGF0YS4NCg0KIyMjI0NsZWFuaW5nIGRhdGE6IENoYW5naW5nIHRoZSBudW1lcmljIGNvbHVtbiB0eXBlcw0KDQpgYGB7cn0NCmdkcF9kYXRhIDwtIGdkcF9yYXcgJT4lDQogIG11dGF0ZSgnUTIuMjAyMCcgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgZ2RwX3JhdyRgUTIuMjAyMGApKSAqIDEwMDAsDQogICAgICAgICAnUTEuMjAyMCcgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgZ2RwX3JhdyRgUTEuMjAyMGApKSAqIDEwMDAsDQogICAgICAgICAnUTIuMjAxOScgPSBhcy5udW1lcmljKGdzdWIoJywnLCAnJywgZ2RwX3JhdyRgUTIuMjAxOWApKSAqIDEwMDApICU+JQ0KICByZW5hbWUoICcyMDIwLjInPSAnUTIuMjAyMCcsDQogICAgJzIwMjAuMScgPSAnUTEuMjAyMCcsIA0KICAgICcyMDE5LjInID0gJ1EyLjIwMTknKSAlPiUNCiAgZGlzdGluY3QoTmFtZSwgLmtlZXBfYWxsID0gVFJVRSkNCg0KI0kgbXVsdGlwbGllZCBieSAxMDAwIHRvIGdldCByaWQgb2YgdGhlIGRlY2ltYWwgcG9pbnQgaW4gdGhlIG5vdGF0aW9uIA0KaGVhZChnZHBfZGF0YSkNCmBgYA0KDQoqKlBpdm90aW5nICoqDQpMZXQncyBwaXZvdCBmcm9tIHdpZGUgdG8gbG9uZyBzbyBhcyB0byBoYXZlIG9uZSBzaW5nbGUgb2JzZXJ2YXRpb24gcGVyIHJvdy4gDQoNCmBgYHtyfQ0KZ2RwX2NsZWFuIDwtIGdkcF9kYXRhICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgMzo1LA0KICAgIG5hbWVzX3RvPSJRdWFydGVyIiwNCiAgICB2YWx1ZXNfdG89IkdkcCIsDQogICAgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFDQogICAgKSAlPiUNCiAgc2VsZWN0KE5hbWUsIFF1YXJ0ZXIsIEdkcCklPiUNCiAgYXJyYW5nZShOYW1lLCBRdWFydGVyLCBieV9ncm91cCA9IFRSVUUpDQoNCmhlYWQoZ2RwX2NsZWFuKQ0KYGBgDQoNCg0KIyMjIyBBbmFseXppbmcgRGF0YSAqKg0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgbG9uZywgZm9jdXNlZCBkYXRhIHNldCwgd2UgY2FuIGdvIGFoZWFkIGFuZCB0cnkgdG8gcGVyZm9ybSB0aGUgcmVxdWVzdGVkIGFuYWx5c2lzLiAgV2UgY2FuIHBheSBzcGVjaWFsIGF0dGVudGlvbiB0byB0aGUgc2Vjb25kIHF1YXJ0ZXIgb2YgMjAyMCB0byBzZWUgdGhlIGFyZWFzIG1vcmUgZ3JlYXRseSBhZmZlY3RlZCBieSB0aGUgcGFuZGVtaWMNCg0KIyMjIyBHRFAgUGVyIFF1YXJ0ZXIgIyMjDQoNCmBgYHtyfQ0KDQphbGxfZ2RwIDwtIGdkcF9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoUXVhcnRlcikgJT4lDQogIHN1bW1hcmlzZShhdmdfZ2RwID0gbWVhbihHZHApLCBtZWRpYW5fZ2RwID0gbWVkaWFuKEdkcCkpDQoNCmFsbF9nZHANCg0KYGBgDQoNCkJhc2VkIG9uIHRoZSBvdmVyYWxsIG1lYW4gZ2RwIHdlIGNhbiBzZWUgdGhhdCwgYXMgd2UgY291bGQgcHJvYmFibHkgcHJlc3VtZSwgdGhlIGF2ZXJhZ2UgR0RQIGZpZ3VyZSBmZWxsIHNpZ25pZmljYW50bHkgZHVyaW5nIHRoZSAybmQgcXVhcnRlciBvZiAyMDIwLg0KDQpXZSdsbCBhZGQgYSBwZXJjZW50IGNoYW5nZSBjb2x1bW4gdG8gb3VyIGNsZWFuIGRhdGEgdG8gaWRlbnRpZnkgZ2FpbnMvbG9zc2VzIGJ5IHF1YXJ0ZXINCmBgYHtyfQ0KZ2RwX2J5X3BjdF9jaGFuZ2UgPC1tdXRhdGUoZ2RwX2NsZWFuLCBSb3cgPSAxOm4oKSkgJT4lDQogIGdyb3VwX2J5KE5hbWUpICU+JQ0KICBtdXRhdGUocGN0X2NoYW5nZSA9IEdkcC9sYWcoR2RwKSAqIDEwMCkgJT4lDQogIHVuZ3JvdXAgJT4lDQogIHNlbGVjdChOYW1lLCBRdWFydGVyLCBwY3RfY2hhbmdlKSAlPiUNCiAgYXJyYW5nZShwY3RfY2hhbmdlKQ0KaGVhZChnZHBfYnlfcGN0X2NoYW5nZSkNCmBgYA0KDQpCeSBsb29raW5nIGF0IHRoaXMgZGF0YSwgd2UgY2FuIHNlZSB0aGF0IEV4cG9ydHMgYW5kIEltcG9ydHMgc3VmZmVyZWQgdGhlIG1vc3QgZHVyaW5nIHRoZSBTZWNvbmQgUXVhcnRlciBhcyB0aGV5IGRlY3JlYXNlZCB0aGUgbW9zdCBhbW9uZyB0aGUgY2F0ZWdvcmllcyB3ZSBoYXZlLiANCg0KYGBge3J9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnZHBfY2xlYW4sIGFlcyh4ID0gUXVhcnRlciwgeSA9IEdkcCwgZ3JvdXA9TmFtZSwgY29sb3I9TmFtZSApKSArICANCiAgICAgZ2VvbV9saW5lKCkgKw0KICAgICBnZW9tX3BvaW50KCkgKw0KICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpDQpwIDwtIHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQpwIDwtIHAgKyBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz01LCBieXJvdz1UUlVFKSkNCg0KcA0KYGBgDQoNCg0KV2Ugc2VlIGEgZ2VuZXJhbCBkZWNsaW5lIGluIHZhbHVlcywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhpcyBzYW1lIGdyYXBoIGJ5IHBlcmNlbnQgY2hhbmdlOg0KYGBge3J9DQojQXNzaWduIDEwMCB0byBhbGwgTkEgYXNzdW1pbmcgd2Ugc3RhcnQgYXQgMTAwJSBvbiAyMDE5LjINCmdkcF9ieV9wY3RfY2hhbmdlW2lzLm5hKGdkcF9ieV9wY3RfY2hhbmdlKV0gPC0gMTAwDQpwIDwtIGdncGxvdChkYXRhID0gZ2RwX2J5X3BjdF9jaGFuZ2UsIGFlcyh4ID0gUXVhcnRlciwgeSA9IHBjdF9jaGFuZ2UsIGdyb3VwPU5hbWUsIGNvbG9yPU5hbWUgKSkgKyAgDQogICAgIGdlb21fbGluZSgpICsNCiAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KcCA8LSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KcCA8LSBwICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MTAsIGJ5cm93PVRSVUUpKQ0KDQpwDQoNCmBgYA0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgYmlnZ2VyIGNoYW5nZXMgaW4gdGhlIDJuZCBRdWFydGVyOiANCmBgYHtyfQ0KZ2RwX2J5X3BjdF9jaGFuZ2UgJT4lIA0KICAgIGZpbHRlcihRdWFydGVyPT0iMjAyMC4yIikgJT4lDQogICAgYXJyYW5nZShkZXNjKHBjdF9jaGFuZ2UpKSAlPiUNCiAgICBzbGljZSgxOjUpICU+JQ0KICBnZ3Bsb3QoZGF0YSA9IC4pICsgDQogICAgIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IE5hbWUsIHkgPSBwY3RfY2hhbmdlLCBmaWxsPU5hbWUpLCBzdGF0PSdpZGVudGl0eScpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpDQogICAgICAgICkNCmBgYA0KDQpCYXNlZCBvbiB0aGlzLCB0aGUgYmlnZ2VzdCBnYWluIHdhcyBpbiBDaGFuZ2UgaW4gcHJpdmF0ZSBpbnZlbnRvcmllcyB3aXRoIGEgbmVhciA2MDAlIGluY3JlYXNlLiAgVGhpcyBpcyBhbHNvIHRoZSBzYW1lIHZhcmlhYmxlIGRlY3JlYXNlZCBhbG1vc3QgMTAwJSBiZXR3ZWVuIDIwMTkgYW5kIHRoZSAxc3QgcXVhcnRlciBpbiAyMDIwDQoNCioqTW9zdCBhZmZlY3RlZCoqDQoNCmBgYHtyfQ0KZ2RwX2J5X3BjdF9jaGFuZ2UgJT4lIA0KICAgIGZpbHRlcihRdWFydGVyID09ICcyMDIwLjInKSAlPiUNCiAgICBhcnJhbmdlKHBjdF9jaGFuZ2UpICU+JQ0KICAgIHNsaWNlKDE6NSkgJT4lDQogIGdncGxvdChkYXRhID0gLikgKyANCiAgICAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gTmFtZSwgeSA9IHBjdF9jaGFuZ2UsIGZpbGw9TmFtZSksIHN0YXQ9J2lkZW50aXR5JykgKw0KICAgIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKQ0KICAgICAgICApDQpgYGANCg0KDQpBZ2FpbiwgdGhpcyBncmFwaCBzaG93cyB0aGF0IGV4cG9ydHMsIGltcG9ydHMgYW5kIEdyb3NzIHByaXZhdGUgZG9tZXN0aWMgaW52ZXN0bWVudHMgd2VyZSB0aGUgbW9zdCBhZmZlY3RlZCBzZWN0b3JzIGluIHRoZSAybmQgUXVhcnRlci4gDQoNCiMjIyMgQ29uY2x1c2lvbiANCg0KQWx0aG91Z2ggdGhlcmUgaXMgYSBnZW5lcmFsIGRlY2xpbmUgaW4gdmFsdWVzLCBzb21lIHNlY3RvcnMgbGlrZSBQcml2YXRlIGludmVudG9yaWVzIGFuZCBEZWZlbnNlIHJlcG9ydGVkIGdhaW5zIGluIHRoZWlyIEdEUCBkdXJpbmcgdGhlIDJuZCBxdWFydGVyIG9mIDIwMjAu