Data exploration with the dplyr, tidyr and stringr libraries. - Column subsetting - Filtering of rows - Boolean operators, Boolean algebra, de Morgan’s laws - Creating new columns (1x Challenge) - Missing values - Manipulating text (3x Challenge) - Aggregating data (1x Challenge) - Pivot tables, data in long and wide format - Merging tables

Useful resources:
- dplyr cheatsheet
- tidyr cheatsheet
- stringr cheatsheet
- ggplot2 cheatsheet
- A. Kassambara - Guide to Create Beautiful Graphics in R.

The data comes from https://flixgem.com/ (dataset version as of March 12, 2021). The data contains information on 9425 movies and series available on Netlix.

Data exploration with dplyr and tidyr libraries

Subset of columns

We select columns by their names using the select() function. We can also delete columns by preceding the name of a column with the - symbol.

dane %>%
  select(Title, Runtime, IMDb.Score, Release.Date) %>%
  head(5)
dane %>%
  select(-Netflix.Link, -IMDb.Link, -Image, -Poster, -TMDb.Trailer)%>%
  head(5)
dane %>%
  select(1:10)%>%
  head(5)
dane %>%
  select(Title:Runtime)%>%
  head(5)

Useful functions when selecting/deleting columns: - starts_with() - select or delete columns starting with a given string. - ends_with() - select or delete columns ending with a given string - contains() - select or delete columns containing the given string.

dane %>%
  select(starts_with('IMDb'))%>% 
  head(10)
dane %>%
  select(ends_with('Score'))%>% 
  head(10)
dane %>%
  select(contains('Date'))%>% 
  head(10)

Use the matches() function to select or remove columns containing a given regular expression. A useful tool in building and testing regular expressions is at the link https://regex101.com/.

dane %>%
  select(matches('^[a-z]{5,6}$')) %>% 
  head(10)
dane %>%
  select(-matches('\\.'))%>% 
  head(10)

The select() function always returns a data frame, while we also have the option of returning a vector using the pull() function.

dane %>%
  select(IMDb.Score)%>% 
  head(10)

# dane %>%
#   select(IMDb.Score) %>%
#   unlist(use.names = FALSE)
dane %>%
  pull(IMDb.Score)%>% 
  head(10)
dane %>%
  pull(IMDb.Score, Title)%>% 
  head(10)

Row filtering

We filter the rows with the filter() function using the ==, !=, >, >=, <, <=, between() operators.

dane %>%
  filter(Series.or.Movie == "Series")%>% 
  head(10)
dane %>%
  filter(IMDb.Score > 8)%>% 
  head(10)

Boolean operators, Boolean algebra, de Morgan’s laws

The logical operator AND denoted by the symbol &. - FALSE & FALSE = FALSE - FALSE & TRUE = FALSE - TRUE & FALSE = FALSE - TRUE & TRUE = TRUE

dane %>%
  filter(IMDb.Score >= 8 & Series.or.Movie == 'Series')%>% 
  head(10)

The logical operator OR denoted by the symbol |. - FALSE | FALSE = FALSE - FALSE | TRUE = TRUE - TRUE | FALSE = TRUE - TRUE | TRUE = TRUE

dane %>%
  filter(IMDb.Score >= 9 | IMDb.Votes < 1000)%>% 
  head(10)

De Morgan’s laws say that when we enter with a negation under the parenthesis, OR turns into AND (and vice versa).

not (A & B) = (not A) | (not B) not (A | B) = (not A) & (not B)

dane %>%
  filter(!(IMDb.Score >= 9 | IMDb.Votes < 1000))%>% 
  head(10)
dane %>%
  filter(!(IMDb.Score >= 9) & !(IMDb.Votes < 1000))%>% 
  head(10)

Create new columns

Use the mutate() function to either add new columns to the data frame or edit existing columns.

dane %>%
  mutate(score_category = if_else(IMDb.Score >= 5, 'Good', 'Poor')) %>%
  select(Title, IMDb.Score, score_category)%>% 
  head(10)
dane %>%
  transmute(
    Release = Release.Date %>% as.Date(format = '%y/%m/%d')
    ,Netflix.Release = Netflix.Release.Date %>% as.Date(format = '%y/%m/%d')
  )

Challenge 1.

CHALLENGE 1: What is the oldest Woody Allen film available on Netflix?

dane %>%
  select(Title,Director, Release.Date) %>%
  filter(Director == "Woody Allen") %>%
  mutate(Release = as.numeric(format(as.Date(Release.Date,format = "%m/%d/%Y"),"%Y"))) %>%
  arrange(Release) %>%
head(1)


  

In the case of the *case_when()* function, we do not need to write conditions that create mutually disjoint sets. The evaluation occurs when the first condition is met, followed immediately by another iteration.


dane %>%
  mutate(score_category = case_when(
    IMDb.Score <= 2 ~ 'Very Poor'
    ,IMDb.Score <= 4 ~ 'Poor'
    ,IMDb.Score <= 6 ~ 'Medium'
    ,IMDb.Score <= 8 ~ 'Good'
    ,IMDb.Score <= 10 ~ 'Very Good'
    )) %>%
  select(Title, IMDb.Score, score_category)%>% 
  head(10)

We perform mathematical operations for each row and based on several columns using the rowwise() function.

dane %>%
  mutate(avg_score = mean(c(IMDb.Score * 10
                            ,Hidden.Gem.Score * 10
                            ,Rotten.Tomatoes.Score
                            ,Metacritic.Score)
                          ,na.rm = TRUE) %>%
           round(2)) %>%
  select(Title, avg_score)%>% 
  head(10)
dane %>% 
  rowwise() %>%
  mutate(avg_score = mean(c(IMDb.Score * 10
                            ,Hidden.Gem.Score * 10
                            ,Rotten.Tomatoes.Score
                            ,Metacritic.Score)
                          ,na.rm = TRUE) %>%
           round(2)) %>%
  select(Title, avg_score)%>% 
  head(10)

By default, the columns created with mutate() are at the end of the table. With relocate() we can change the positions of individual columns in the table.

dane %>%
  mutate(Popularity = if_else(IMDb.Votes > quantile(IMDb.Votes, 0.90, na.rm = TRUE), 'High', 'Not High')) %>%
  relocate(Popularity, .after = Title)

We rename the columns using the rename() function.

dane %>%
  rename(
    Tytul = Title
    ,Gatunek = Genre
  )

Missing Values

Using functions from the tidyr library, we can tame missing values: - drop_na() - we remove rows containing missing values in the indicated columns. - replace_na() - we replace missing values with the specified constant. - fill() - we replace missing values with the previous or next available value.

dane %>%
  sapply(function(x) is.na(x) %>% sum())
dane %>%
  drop_na(Hidden.Gem.Score)
dane %>%
  mutate(Hidden.Gem.Score = replace_na(Hidden.Gem.Score, median(Hidden.Gem.Score, na.rm = TRUE))) %>%
  sapply(function(x) is.na(x) %>% sum())
dane %>%
  replace_na(list(Hidden.Gem.Score = median(dane$Hidden.Gem.Score, na.rm = TRUE))) %>%
  sapply(function(x) is.na(x) %>% sum())

Text manipulation

The stringr library contains a lot of useful functions for manipulating text and regular expressions. Most of the functions in this library start with str_.

Q: What can be improved in the following code so that the tidyverse style convention is followed?

gatunki = dane$Genre %>%
  paste0(collapse = ', ') %>%
  str_extract_all('[A-Za-z]+') %>%
  unlist() %>%
  table() %>%
  as.data.frame()

gatunki %>%
  arrange(-Freq)
dane %>%
  mutate(poland_available = str_detect(Country.Availability, 'Poland')) %>%
  filter(poland_available == TRUE) %>%
  pull(Title)%>% 
  head(10)

Using separate(), we can split one column into several, and combine several columns into one using the unite() function.

dane %>%
  unite(
    col = 'Scores'
    ,c('Hidden.Gem.Score', 'IMDb.Score', 'Rotten.Tomatoes.Score', 'Metacritic.Score')
    ,sep = ', '
  ) %>%
  select(Title, Scores)%>% 
  head(10)

Challenge 2.

CHALLENGE 2: What are the three highest rated comedies available in Polish?

dane %>% 
  rowwise() %>%
  mutate(avg_score = mean(c(IMDb.Score * 10
                            ,Hidden.Gem.Score * 10
                            ,Rotten.Tomatoes.Score
                            ,Metacritic.Score)
                          ,na.rm = TRUE) %>%
           round(2)) %>%
  select(Title, Country.Availability, avg_score)%>% 
  filter(Country.Availability == "Poland") %>%
  head(3)

Challenge 3.

CHALLENGE 3: For 2019 and 2020 productions, what is the average time between release and appearance on Netflix?

  dane$Release <- as.numeric(format(as.Date(dane$Release.Date,format = "%m/%d/%Y"),"%Y"))
  dane$Netflix.Release <- as.numeric(format(as.Date(dane$Netflix.Release.Date,format = "%m/%d/%Y"),"%Y"))
 dane$avg_time_movie <- as.numeric(as.Date(dane$Netflix.Release) - as.Date(dane$Release)) 
  dane %>%
    filter(Release == '2019' | Release == '2020') %>%
  select(Title, avg_time_movie, Release, Netflix.Release)

Challenge 4.

CHALLENGE 4: What are the most popular tags for productions available in Polish?

dane %>%
  group_by(Tags, Languages) %>%
  filter(Languages == "Polish") %>%
  summarise(Count = n()) %>%
  arrange(desc(Count))

Data aggregation

Using the group_by() and summarize() functions, we perform operations on aggregated data.

dane %>%
  group_by(Series.or.Movie) %>%
  summarize(
    count = n()
    ,avg_imdb_score = mean(IMDb.Score, na.rm = TRUE) %>% round(2)
    ,avg_imdb_votes = mean(IMDb.Votes, na.rm = TRUE) %>% round(0)
    ,sum_awards = sum(Awards.Received, na.rm = TRUE)
  )
dane %>%
  group_by(Series.or.Movie, Runtime) %>%
  summarize(n = n()) %>%
  arrange(-n)

Challenge 5.

CHALLENGE 5: What are the average ratings of films produced in each decade (i.e., 1960s, 1970s, 1980s, 1990s, etc.)?

dane$Date_Col <- as.Date(dane$Release.Date, format = "%d/%m/%Y")
dane$Decade <- cut(dane$Date_Col, breaks = seq(as.Date("1959-12-31"), as.Date("2020-12-31"), by = "10 years"), labels = c("1960s", "1970s", "1980s", "1990s", "2000s", "2010s", "2020s"))
dane %>%
  rowwise() %>%
  mutate(avg_score = mean(c(IMDb.Score * 10
                            ,Hidden.Gem.Score * 10
                            ,Rotten.Tomatoes.Score
                            ,Metacritic.Score)
                          ,na.rm = TRUE) %>%
           round(2)) %>%
  summarise(Title, avg_score, Decade) %>%
  arrange(Decade)

Pivot tables, long and wide format data

Data in wide format: - rows represent individual observations - columns represent attributes of these observations - cells represent values of individual attributes for individual observations.

Data in long format: - in the first column we have observations (the observation key can also consist of more than one column) - in the second column we have attributes - in the third column we have values.

The long format is useful, among other things, when creating charts in the ggplot2 library.

dane_pivot = dane %>%
  select(Title, ends_with('Score'))
dane_pivot = dane_pivot %>%
  pivot_longer(
    cols = 2:5
    ,names_to = 'Attribute'
    ,values_to = 'Value'
  )
dane_pivot = dane_pivot %>%
  pivot_wider(
    id_cols = 1
    ,names_from = 'Attribute'
    ,values_from = 'Value'
  )
## Warning: Values from `Value` are not uniquely identified; output will contain list-cols.
## • Use `values_fn = list` to suppress this warning.
## • Use `values_fn = {summary_fun}` to summarise duplicates.
## • Use the following dplyr code to identify duplicates.
##   {data} %>%
##   dplyr::group_by(Title, Attribute) %>%
##   dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
##   dplyr::filter(n > 1L)

Joining tables

oceny_metacritic = dane %>%
  select(Title, Metacritic.Score) %>%
  .[1:100,] %>%
  drop_na()

oceny_rotten_tomatoes = dane %>%
  select(Title, Rotten.Tomatoes.Score) %>%
  .[1:100,] %>%
  drop_na()

We join tables by their corresponding keys just as we do in SQL.

oceny_metacritic %>%
  left_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
##                       Title Metacritic.Score Rotten.Tomatoes.Score
## 1          Lets Fight Ghost               82                    98
## 2       HOW TO BUILD A GIRL               69                    79
## 3             The Invisible               36                    20
## 4                     Joker               59                    68
## 5                         I               51                    52
## 6          Harrys Daughters               85                    96
## 7                The Closet               72                    85
## 8             Trial by Fire               51                    61
## 9           Dilili in Paris               37                    NA
## 10    Framing John DeLorean               67                    90
## 11                    Alice               67                    75
## 12          Ordinary People               86                    89
## 13        Paths of the Soul               90                    94
## 14         Rebel in the Rye               46                    30
## 15               The Return               82                    NA
## 16                    Stray               54                    56
## 17              Stand by Me               75                    91
## 18             Wonderstruck               71                    68
## 19       Intimate Strangers               71                    86
## 20    The Girl on the Train               48                    44
## 21           Ride Your Wave               63                    93
## 22                   Capone               46                    NA
## 23          Above Suspicion               57                    NA
## 24            A Call to Spy               65                    NA
## 25                      Red               60                    72
## 26           The Mole Agent               69                    NA
## 27             I Care a Lot               67                    NA
## 28                   Burden               57                    97
## 29               Collective               95                    NA
## 30                     Love               51                    40
## 31                   Amanda               63                    NA
## 32           Corpus Christi               77                    NA
## 33               The Shadow               50                    35
## 34                Aftermath               44                    42
## 35                 Unhinged               40                    NA
## 36 John Lewis: Good Trouble               70                    NA
## 37                 Repo Man               82                    98
## 38     For Love of the Game               43                    46
## 39  The Replacement Killers               42                    36
oceny_metacritic %>%
  right_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
##                                    Title Metacritic.Score Rotten.Tomatoes.Score
## 1                       Lets Fight Ghost               82                    98
## 2                    HOW TO BUILD A GIRL               69                    79
## 3                          The Invisible               36                    20
## 4                                  Joker               59                    68
## 5                                      I               51                    52
## 6                       Harrys Daughters               85                    96
## 7                             The Closet               72                    85
## 8                          Trial by Fire               51                    61
## 9                  Framing John DeLorean               67                    90
## 10                                 Alice               67                    75
## 11                       Ordinary People               86                    89
## 12                     Paths of the Soul               90                    94
## 13                      Rebel in the Rye               46                    30
## 14                                 Stray               54                    56
## 15                           Stand by Me               75                    91
## 16                          Wonderstruck               71                    68
## 17                    Intimate Strangers               71                    86
## 18                 The Girl on the Train               48                    44
## 19                        Ride Your Wave               63                    93
## 20                                   Red               60                    72
## 21                                Burden               57                    97
## 22                                  Love               51                    40
## 23                            The Shadow               50                    35
## 24                             Aftermath               44                    42
## 25                              Repo Man               82                    98
## 26                  For Love of the Game               43                    46
## 27               The Replacement Killers               42                    36
## 28            The Simple Minded Murderer               NA                    92
## 29         Comrades: Almost a Love Story               NA                    89
## 30                        The Mysterians               NA                    51
## 31                                Repast               NA                    87
## 32                                  Sway               NA                    86
## 33       When a Woman Ascends the Stairs               NA                   100
## 34                              Yearning               NA                    88
## 35                       Ginza Cosmetics               NA                    45
## 36                       Floating Clouds               NA                    83
## 37                  Life and Nothing But               NA                    86
## 38                 Let Joy Reign Supreme               NA                    79
## 39                       Coup de Torchon               NA                    83
## 40                     Keys To The Heart               NA                    77
## 41               Gonjiam: Haunted Asylum               NA                    91
## 42                        Golden Slumber               NA                    75
## 43                           Extreme Job               NA                    82
## 44                               Default               NA                    78
## 45 The Accidental Detective 2: In Action               NA                    73
## 46              1987: When the Day Comes               NA                    82
## 47                       Ten Years Japan               NA                   100
## 48                            Overcoming               NA                    88
## 49                  Awara Paagal Deewana               NA                    54
oceny_metacritic %>%
  inner_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
##                      Title Metacritic.Score Rotten.Tomatoes.Score
## 1         Lets Fight Ghost               82                    98
## 2      HOW TO BUILD A GIRL               69                    79
## 3            The Invisible               36                    20
## 4                    Joker               59                    68
## 5                        I               51                    52
## 6         Harrys Daughters               85                    96
## 7               The Closet               72                    85
## 8            Trial by Fire               51                    61
## 9    Framing John DeLorean               67                    90
## 10                   Alice               67                    75
## 11         Ordinary People               86                    89
## 12       Paths of the Soul               90                    94
## 13        Rebel in the Rye               46                    30
## 14                   Stray               54                    56
## 15             Stand by Me               75                    91
## 16            Wonderstruck               71                    68
## 17      Intimate Strangers               71                    86
## 18   The Girl on the Train               48                    44
## 19          Ride Your Wave               63                    93
## 20                     Red               60                    72
## 21                  Burden               57                    97
## 22                    Love               51                    40
## 23              The Shadow               50                    35
## 24               Aftermath               44                    42
## 25                Repo Man               82                    98
## 26    For Love of the Game               43                    46
## 27 The Replacement Killers               42                    36
oceny_metacritic %>%
  full_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
##                                    Title Metacritic.Score Rotten.Tomatoes.Score
## 1                       Lets Fight Ghost               82                    98
## 2                    HOW TO BUILD A GIRL               69                    79
## 3                          The Invisible               36                    20
## 4                                  Joker               59                    68
## 5                                      I               51                    52
## 6                       Harrys Daughters               85                    96
## 7                             The Closet               72                    85
## 8                          Trial by Fire               51                    61
## 9                        Dilili in Paris               37                    NA
## 10                 Framing John DeLorean               67                    90
## 11                                 Alice               67                    75
## 12                       Ordinary People               86                    89
## 13                     Paths of the Soul               90                    94
## 14                      Rebel in the Rye               46                    30
## 15                            The Return               82                    NA
## 16                                 Stray               54                    56
## 17                           Stand by Me               75                    91
## 18                          Wonderstruck               71                    68
## 19                    Intimate Strangers               71                    86
## 20                 The Girl on the Train               48                    44
## 21                        Ride Your Wave               63                    93
## 22                                Capone               46                    NA
## 23                       Above Suspicion               57                    NA
## 24                         A Call to Spy               65                    NA
## 25                                   Red               60                    72
## 26                        The Mole Agent               69                    NA
## 27                          I Care a Lot               67                    NA
## 28                                Burden               57                    97
## 29                            Collective               95                    NA
## 30                                  Love               51                    40
## 31                                Amanda               63                    NA
## 32                        Corpus Christi               77                    NA
## 33                            The Shadow               50                    35
## 34                             Aftermath               44                    42
## 35                              Unhinged               40                    NA
## 36              John Lewis: Good Trouble               70                    NA
## 37                              Repo Man               82                    98
## 38                  For Love of the Game               43                    46
## 39               The Replacement Killers               42                    36
## 40            The Simple Minded Murderer               NA                    92
## 41         Comrades: Almost a Love Story               NA                    89
## 42                        The Mysterians               NA                    51
## 43                                Repast               NA                    87
## 44                                  Sway               NA                    86
## 45       When a Woman Ascends the Stairs               NA                   100
## 46                              Yearning               NA                    88
## 47                       Ginza Cosmetics               NA                    45
## 48                       Floating Clouds               NA                    83
## 49                  Life and Nothing But               NA                    86
## 50                 Let Joy Reign Supreme               NA                    79
## 51                       Coup de Torchon               NA                    83
## 52                     Keys To The Heart               NA                    77
## 53               Gonjiam: Haunted Asylum               NA                    91
## 54                        Golden Slumber               NA                    75
## 55                           Extreme Job               NA                    82
## 56                               Default               NA                    78
## 57 The Accidental Detective 2: In Action               NA                    73
## 58              1987: When the Day Comes               NA                    82
## 59                       Ten Years Japan               NA                   100
## 60                            Overcoming               NA                    88
## 61                  Awara Paagal Deewana               NA                    54
oceny_metacritic %>%
  anti_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
##                       Title Metacritic.Score
## 1           Dilili in Paris               37
## 2                The Return               82
## 3                    Capone               46
## 4           Above Suspicion               57
## 5             A Call to Spy               65
## 6            The Mole Agent               69
## 7              I Care a Lot               67
## 8                Collective               95
## 9                    Amanda               63
## 10           Corpus Christi               77
## 11                 Unhinged               40
## 12 John Lewis: Good Trouble               70
oceny_rotten_tomatoes %>%
  anti_join(oceny_metacritic, by = c('Title' = 'Title'))
##                                    Title Rotten.Tomatoes.Score
## 1             The Simple Minded Murderer                    92
## 2          Comrades: Almost a Love Story                    89
## 3                         The Mysterians                    51
## 4                                 Repast                    87
## 5                                   Sway                    86
## 6        When a Woman Ascends the Stairs                   100
## 7                               Yearning                    88
## 8                        Ginza Cosmetics                    45
## 9                        Floating Clouds                    83
## 10                  Life and Nothing But                    86
## 11                 Let Joy Reign Supreme                    79
## 12                       Coup de Torchon                    83
## 13                     Keys To The Heart                    77
## 14               Gonjiam: Haunted Asylum                    91
## 15                        Golden Slumber                    75
## 16                           Extreme Job                    82
## 17                               Default                    78
## 18 The Accidental Detective 2: In Action                    73
## 19              1987: When the Day Comes                    82
## 20                       Ten Years Japan                   100
## 21                            Overcoming                    88
## 22                  Awara Paagal Deewana                    54
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAxLiIKYXV0aG9yOiAiVGVhbSBtZW1iZXJzOiBadXphbm5hIFJ1bWFrLCBXaWt0b3JpYSBQdXJnaWVsLCBNYXJsZW5hIFBhd8WCb3dza2EiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCiAgICBmb250c2l6ZTogMTBwdAogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDQKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCkRhdGEgZXhwbG9yYXRpb24gd2l0aCB0aGUgKmRwbHlyKiwgKnRpZHlyKiBhbmQgKnN0cmluZ3IqIGxpYnJhcmllcy4gLQpDb2x1bW4gc3Vic2V0dGluZyAtIEZpbHRlcmluZyBvZiByb3dzIC0gQm9vbGVhbiBvcGVyYXRvcnMsIEJvb2xlYW4KYWxnZWJyYSwgZGUgTW9yZ2FuJ3MgbGF3cyAtIENyZWF0aW5nIG5ldyBjb2x1bW5zICgxeCBDaGFsbGVuZ2UpIC0KTWlzc2luZyB2YWx1ZXMgLSBNYW5pcHVsYXRpbmcgdGV4dCAoM3ggQ2hhbGxlbmdlKSAtIEFnZ3JlZ2F0aW5nIGRhdGEgKDF4CkNoYWxsZW5nZSkgLSBQaXZvdCB0YWJsZXMsIGRhdGEgaW4gbG9uZyBhbmQgd2lkZSBmb3JtYXQgLSBNZXJnaW5nIHRhYmxlcwoKVXNlZnVsIHJlc291cmNlczpcCi0gW2RwbHlyCmNoZWF0c2hlZXRdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL21haW4vZGF0YS10cmFuc2Zvcm1hdGlvbi5wZGYpXAotIFt0aWR5cgpjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL3RpZHlyLnBkZilcCi0gW3N0cmluZ3IKY2hlYXRzaGVldF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9zdHJpbmdzLnBkZilcCi0gW2dncGxvdDIKY2hlYXRzaGVldF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24ucGRmKVwKLSBbQS4gS2Fzc2FtYmFyYSAtIEd1aWRlIHRvIENyZWF0ZSBCZWF1dGlmdWwgR3JhcGhpY3MgaW4KUl0oaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9kb3dubG9hZC8zLWVib29rcy81LWd1aWRlLXRvLWNyZWF0ZS1iZWF1dGlmdWwtZ3JhcGhpY3MtaW4tci1ib29rLykuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZighcmVxdWlyZSgndGlkeXZlcnNlJykpIGluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClRoZSBkYXRhIGNvbWVzIGZyb20gPGh0dHBzOi8vZmxpeGdlbS5jb20vPiAoZGF0YXNldCB2ZXJzaW9uIGFzIG9mIE1hcmNoCjEyLCAyMDIxKS4gVGhlIGRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gb24gOTQyNSBtb3ZpZXMgYW5kIHNlcmllcwphdmFpbGFibGUgb24gTmV0bGl4LgoKYGBge3IgbG9hZC1kYXRhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9rZmxpc2lrb3dza2kvZHMvbWFzdGVyL25ldGZsaXgtZGF0YXNldC5jc3Y/cmF3PXRydWUiLCBkZXN0ZmlsZSA9ImRhbmUuY3N2Iixtb2RlPSJ3YiIpCmRhbmU8LXJlYWQuY3N2KGZpbGU9ImRhbmUuY3N2IixlbmNvZGluZyA9IlVURi04IixoZWFkZXI9VFJVRSxzZXAgPSAiLCIpCmF0dGFjaChkYW5lKQpgYGAKCiMjIERhdGEgZXhwbG9yYXRpb24gd2l0aCBkcGx5ciBhbmQgdGlkeXIgbGlicmFyaWVzCgojIyMgU3Vic2V0IG9mIGNvbHVtbnMKCldlIHNlbGVjdCBjb2x1bW5zIGJ5IHRoZWlyIG5hbWVzIHVzaW5nIHRoZSAqc2VsZWN0KCkqIGZ1bmN0aW9uLiBXZSBjYW4KYWxzbyBkZWxldGUgY29sdW1ucyBieSBwcmVjZWRpbmcgdGhlIG5hbWUgb2YgYSBjb2x1bW4gd2l0aCB0aGUgKi0qCnN5bWJvbC4KCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHNlbGVjdChUaXRsZSwgUnVudGltZSwgSU1EYi5TY29yZSwgUmVsZWFzZS5EYXRlKSAlPiUKICBoZWFkKDUpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgc2VsZWN0KC1OZXRmbGl4LkxpbmssIC1JTURiLkxpbmssIC1JbWFnZSwgLVBvc3RlciwgLVRNRGIuVHJhaWxlciklPiUKICBoZWFkKDUpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgc2VsZWN0KDE6MTApJT4lCiAgaGVhZCg1KQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHNlbGVjdChUaXRsZTpSdW50aW1lKSU+JQogIGhlYWQoNSkKYGBgCgpVc2VmdWwgZnVuY3Rpb25zIHdoZW4gc2VsZWN0aW5nL2RlbGV0aW5nIGNvbHVtbnM6IC0gKnN0YXJ0c193aXRoKCkqIC0Kc2VsZWN0IG9yIGRlbGV0ZSBjb2x1bW5zIHN0YXJ0aW5nIHdpdGggYSBnaXZlbiBzdHJpbmcuIC0gKmVuZHNfd2l0aCgpKiAtCnNlbGVjdCBvciBkZWxldGUgY29sdW1ucyBlbmRpbmcgd2l0aCBhIGdpdmVuIHN0cmluZyAtICpjb250YWlucygpKiAtCnNlbGVjdCBvciBkZWxldGUgY29sdW1ucyBjb250YWluaW5nIHRoZSBnaXZlbiBzdHJpbmcuCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBzZWxlY3Qoc3RhcnRzX3dpdGgoJ0lNRGInKSklPiUgCiAgaGVhZCgxMCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBzZWxlY3QoZW5kc193aXRoKCdTY29yZScpKSU+JSAKICBoZWFkKDEwKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHNlbGVjdChjb250YWlucygnRGF0ZScpKSU+JSAKICBoZWFkKDEwKQpgYGAKClVzZSB0aGUgKm1hdGNoZXMoKSogZnVuY3Rpb24gdG8gc2VsZWN0IG9yIHJlbW92ZSBjb2x1bW5zIGNvbnRhaW5pbmcgYQpnaXZlbiByZWd1bGFyIGV4cHJlc3Npb24uIEEgdXNlZnVsIHRvb2wgaW4gYnVpbGRpbmcgYW5kIHRlc3RpbmcgcmVndWxhcgpleHByZXNzaW9ucyBpcyBhdCB0aGUgbGluayA8aHR0cHM6Ly9yZWdleDEwMS5jb20vPi4KCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHNlbGVjdChtYXRjaGVzKCdeW2Etel17NSw2fSQnKSkgJT4lIAogIGhlYWQoMTApCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgc2VsZWN0KC1tYXRjaGVzKCdcXC4nKSklPiUgCiAgaGVhZCgxMCkKYGBgCgpUaGUgKnNlbGVjdCgpKiBmdW5jdGlvbiBhbHdheXMgcmV0dXJucyBhIGRhdGEgZnJhbWUsIHdoaWxlIHdlIGFsc28gaGF2ZQp0aGUgb3B0aW9uIG9mIHJldHVybmluZyBhIHZlY3RvciB1c2luZyB0aGUgKnB1bGwoKSogZnVuY3Rpb24uCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBzZWxlY3QoSU1EYi5TY29yZSklPiUgCiAgaGVhZCgxMCkKCiMgZGFuZSAlPiUKIyAgIHNlbGVjdChJTURiLlNjb3JlKSAlPiUKIyAgIHVubGlzdCh1c2UubmFtZXMgPSBGQUxTRSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBwdWxsKElNRGIuU2NvcmUpJT4lIAogIGhlYWQoMTApCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgcHVsbChJTURiLlNjb3JlLCBUaXRsZSklPiUgCiAgaGVhZCgxMCkKYGBgCgojIyMgUm93IGZpbHRlcmluZwoKV2UgZmlsdGVyIHRoZSByb3dzIHdpdGggdGhlICpmaWx0ZXIoKSogZnVuY3Rpb24gdXNpbmcgdGhlICo9PSosICohPSosCipcPiosICpcPj0qLCAqXDwqLCAqXDw9KiwgKmJldHdlZW4oKSogb3BlcmF0b3JzLgoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgZmlsdGVyKFNlcmllcy5vci5Nb3ZpZSA9PSAiU2VyaWVzIiklPiUgCiAgaGVhZCgxMCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBmaWx0ZXIoSU1EYi5TY29yZSA+IDgpJT4lIAogIGhlYWQoMTApCmBgYAoKIyMjIEJvb2xlYW4gb3BlcmF0b3JzLCBCb29sZWFuIGFsZ2VicmEsIGRlIE1vcmdhbidzIGxhd3MKClRoZSBsb2dpY2FsIG9wZXJhdG9yICpBTkQqIGRlbm90ZWQgYnkgdGhlIHN5bWJvbCAqJiouIC0gKkZBTFNFICYgRkFMU0UgPQpGQUxTRSogLSAqRkFMU0UgJiBUUlVFID0gRkFMU0UqIC0gKlRSVUUgJiBGQUxTRSA9IEZBTFNFKiAtICpUUlVFICYgVFJVRQo9IFRSVUUqCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBmaWx0ZXIoSU1EYi5TY29yZSA+PSA4ICYgU2VyaWVzLm9yLk1vdmllID09ICdTZXJpZXMnKSU+JSAKICBoZWFkKDEwKQpgYGAKClRoZSBsb2dpY2FsIG9wZXJhdG9yICpPUiogZGVub3RlZCBieSB0aGUgc3ltYm9sICpcfCouIC0gKkZBTFNFIFx8IEZBTFNFCj0gRkFMU0UqIC0gKkZBTFNFIFx8IFRSVUUgPSBUUlVFKiAtICpUUlVFIFx8IEZBTFNFID0gVFJVRSogLSAqVFJVRSBcfApUUlVFID0gVFJVRSoKCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIGZpbHRlcihJTURiLlNjb3JlID49IDkgfCBJTURiLlZvdGVzIDwgMTAwMCklPiUgCiAgaGVhZCgxMCkKYGBgCgpEZSBNb3JnYW4ncyBsYXdzIHNheSB0aGF0IHdoZW4gd2UgZW50ZXIgd2l0aCBhIG5lZ2F0aW9uIHVuZGVyIHRoZQpwYXJlbnRoZXNpcywgT1IgdHVybnMgaW50byBBTkQgKGFuZCB2aWNlIHZlcnNhKS4KCipub3QgKEEgJiBCKSA9IChub3QgQSkgXHwgKG5vdCBCKSogKm5vdCAoQSBcfCBCKSA9IChub3QgQSkgJiAobm90IEIpKgoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgZmlsdGVyKCEoSU1EYi5TY29yZSA+PSA5IHwgSU1EYi5Wb3RlcyA8IDEwMDApKSU+JSAKICBoZWFkKDEwKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIGZpbHRlcighKElNRGIuU2NvcmUgPj0gOSkgJiAhKElNRGIuVm90ZXMgPCAxMDAwKSklPiUgCiAgaGVhZCgxMCkKYGBgCgojIyMgQ3JlYXRlIG5ldyBjb2x1bW5zCgpVc2UgdGhlICptdXRhdGUoKSogZnVuY3Rpb24gdG8gZWl0aGVyIGFkZCBuZXcgY29sdW1ucyB0byB0aGUgZGF0YSBmcmFtZQpvciBlZGl0IGV4aXN0aW5nIGNvbHVtbnMuCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBtdXRhdGUoc2NvcmVfY2F0ZWdvcnkgPSBpZl9lbHNlKElNRGIuU2NvcmUgPj0gNSwgJ0dvb2QnLCAnUG9vcicpKSAlPiUKICBzZWxlY3QoVGl0bGUsIElNRGIuU2NvcmUsIHNjb3JlX2NhdGVnb3J5KSU+JSAKICBoZWFkKDEwKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHRyYW5zbXV0ZSgKICAgIFJlbGVhc2UgPSBSZWxlYXNlLkRhdGUgJT4lIGFzLkRhdGUoZm9ybWF0ID0gJyV5LyVtLyVkJykKICAgICxOZXRmbGl4LlJlbGVhc2UgPSBOZXRmbGl4LlJlbGVhc2UuRGF0ZSAlPiUgYXMuRGF0ZShmb3JtYXQgPSAnJXkvJW0vJWQnKQogICkKYGBgCgojIyMjIENoYWxsZW5nZSAxLgoKKipDSEFMTEVOR0UgMToqKiBXaGF0IGlzIHRoZSBvbGRlc3QgV29vZHkgQWxsZW4gZmlsbSBhdmFpbGFibGUgb24KTmV0ZmxpeD8KCmBgYHtyIGNoYWxsZW5nZTEsIGV2YWw9RkFMU0V9CgpkYW5lICU+JQogIHNlbGVjdChUaXRsZSxEaXJlY3RvciwgUmVsZWFzZS5EYXRlKSAlPiUKICBmaWx0ZXIoRGlyZWN0b3IgPT0gIldvb2R5IEFsbGVuIikgJT4lCiAgbXV0YXRlKFJlbGVhc2UgPSBhcy5udW1lcmljKGZvcm1hdChhcy5EYXRlKFJlbGVhc2UuRGF0ZSxmb3JtYXQgPSAiJW0vJWQvJVkiKSwiJVkiKSkpICU+JQogIGFycmFuZ2UoUmVsZWFzZSkgJT4lCmhlYWQoMSkKCgogIAoKSW4gdGhlIGNhc2Ugb2YgdGhlICpjYXNlX3doZW4oKSogZnVuY3Rpb24sIHdlIGRvIG5vdCBuZWVkIHRvIHdyaXRlIGNvbmRpdGlvbnMgdGhhdCBjcmVhdGUgbXV0dWFsbHkgZGlzam9pbnQgc2V0cy4gVGhlIGV2YWx1YXRpb24gb2NjdXJzIHdoZW4gdGhlIGZpcnN0IGNvbmRpdGlvbiBpcyBtZXQsIGZvbGxvd2VkIGltbWVkaWF0ZWx5IGJ5IGFub3RoZXIgaXRlcmF0aW9uLgoKCmRhbmUgJT4lCiAgbXV0YXRlKHNjb3JlX2NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgSU1EYi5TY29yZSA8PSAyIH4gJ1ZlcnkgUG9vcicKICAgICxJTURiLlNjb3JlIDw9IDQgfiAnUG9vcicKICAgICxJTURiLlNjb3JlIDw9IDYgfiAnTWVkaXVtJwogICAgLElNRGIuU2NvcmUgPD0gOCB+ICdHb29kJwogICAgLElNRGIuU2NvcmUgPD0gMTAgfiAnVmVyeSBHb29kJwogICAgKSkgJT4lCiAgc2VsZWN0KFRpdGxlLCBJTURiLlNjb3JlLCBzY29yZV9jYXRlZ29yeSklPiUgCiAgaGVhZCgxMCkKYGBgCgpXZSBwZXJmb3JtIG1hdGhlbWF0aWNhbCBvcGVyYXRpb25zIGZvciBlYWNoIHJvdyBhbmQgYmFzZWQgb24gc2V2ZXJhbApjb2x1bW5zIHVzaW5nIHRoZSAqcm93d2lzZSgpKiBmdW5jdGlvbi4KCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIG11dGF0ZShhdmdfc2NvcmUgPSBtZWFuKGMoSU1EYi5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGlkZGVuLkdlbS5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUm90dGVuLlRvbWF0b2VzLlNjb3JlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTWV0YWNyaXRpYy5TY29yZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAsbmEucm0gPSBUUlVFKSAlPiUKICAgICAgICAgICByb3VuZCgyKSkgJT4lCiAgc2VsZWN0KFRpdGxlLCBhdmdfc2NvcmUpJT4lIAogIGhlYWQoMTApCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lIAogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoYXZnX3Njb3JlID0gbWVhbihjKElNRGIuU2NvcmUgKiAxMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhpZGRlbi5HZW0uU2NvcmUgKiAxMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLFJvdHRlbi5Ub21hdG9lcy5TY29yZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLE1ldGFjcml0aWMuU2NvcmUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgLG5hLnJtID0gVFJVRSkgJT4lCiAgICAgICAgICAgcm91bmQoMikpICU+JQogIHNlbGVjdChUaXRsZSwgYXZnX3Njb3JlKSU+JSAKICBoZWFkKDEwKQpgYGAKCkJ5IGRlZmF1bHQsIHRoZSBjb2x1bW5zIGNyZWF0ZWQgd2l0aCAqbXV0YXRlKCkqIGFyZSBhdCB0aGUgZW5kIG9mIHRoZQp0YWJsZS4gV2l0aCAqcmVsb2NhdGUoKSogd2UgY2FuIGNoYW5nZSB0aGUgcG9zaXRpb25zIG9mIGluZGl2aWR1YWwKY29sdW1ucyBpbiB0aGUgdGFibGUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBtdXRhdGUoUG9wdWxhcml0eSA9IGlmX2Vsc2UoSU1EYi5Wb3RlcyA+IHF1YW50aWxlKElNRGIuVm90ZXMsIDAuOTAsIG5hLnJtID0gVFJVRSksICdIaWdoJywgJ05vdCBIaWdoJykpICU+JQogIHJlbG9jYXRlKFBvcHVsYXJpdHksIC5hZnRlciA9IFRpdGxlKQpgYGAKCldlIHJlbmFtZSB0aGUgY29sdW1ucyB1c2luZyB0aGUgKnJlbmFtZSgpKiBmdW5jdGlvbi4KCmBgYHtyLCBldmFsPUZBTFNFfQpkYW5lICU+JQogIHJlbmFtZSgKICAgIFR5dHVsID0gVGl0bGUKICAgICxHYXR1bmVrID0gR2VucmUKICApCmBgYAoKIyMjIE1pc3NpbmcgVmFsdWVzCgpVc2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgKnRpZHlyKiBsaWJyYXJ5LCB3ZSBjYW4gdGFtZSBtaXNzaW5nIHZhbHVlczogLQoqZHJvcF9uYSgpKiAtIHdlIHJlbW92ZSByb3dzIGNvbnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGluZGljYXRlZApjb2x1bW5zLiAtICpyZXBsYWNlX25hKCkqIC0gd2UgcmVwbGFjZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSBzcGVjaWZpZWQKY29uc3RhbnQuIC0gKmZpbGwoKSogLSB3ZSByZXBsYWNlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHByZXZpb3VzIG9yCm5leHQgYXZhaWxhYmxlIHZhbHVlLgoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgc2FwcGx5KGZ1bmN0aW9uKHgpIGlzLm5hKHgpICU+JSBzdW0oKSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBkcm9wX25hKEhpZGRlbi5HZW0uU2NvcmUpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgbXV0YXRlKEhpZGRlbi5HZW0uU2NvcmUgPSByZXBsYWNlX25hKEhpZGRlbi5HZW0uU2NvcmUsIG1lZGlhbihIaWRkZW4uR2VtLlNjb3JlLCBuYS5ybSA9IFRSVUUpKSkgJT4lCiAgc2FwcGx5KGZ1bmN0aW9uKHgpIGlzLm5hKHgpICU+JSBzdW0oKSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICByZXBsYWNlX25hKGxpc3QoSGlkZGVuLkdlbS5TY29yZSA9IG1lZGlhbihkYW5lJEhpZGRlbi5HZW0uU2NvcmUsIG5hLnJtID0gVFJVRSkpKSAlPiUKICBzYXBwbHkoZnVuY3Rpb24oeCkgaXMubmEoeCkgJT4lIHN1bSgpKQpgYGAKCiMjIyBUZXh0IG1hbmlwdWxhdGlvbgoKVGhlICpzdHJpbmdyKiBsaWJyYXJ5IGNvbnRhaW5zIGEgbG90IG9mIHVzZWZ1bCBmdW5jdGlvbnMgZm9yCm1hbmlwdWxhdGluZyB0ZXh0IGFuZCByZWd1bGFyIGV4cHJlc3Npb25zLiBNb3N0IG9mIHRoZSBmdW5jdGlvbnMgaW4gdGhpcwpsaWJyYXJ5IHN0YXJ0IHdpdGggKnN0clxfKi4KClE6IFdoYXQgY2FuIGJlIGltcHJvdmVkIGluIHRoZSBmb2xsb3dpbmcgY29kZSBzbyB0aGF0IHRoZSAqdGlkeXZlcnNlKgpzdHlsZSBjb252ZW50aW9uIGlzIGZvbGxvd2VkPwoKYGBge3IsIGV2YWw9RkFMU0V9CmdhdHVua2kgPSBkYW5lJEdlbnJlICU+JQogIHBhc3RlMChjb2xsYXBzZSA9ICcsICcpICU+JQogIHN0cl9leHRyYWN0X2FsbCgnW0EtWmEtel0rJykgJT4lCiAgdW5saXN0KCkgJT4lCiAgdGFibGUoKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKCmdhdHVua2kgJT4lCiAgYXJyYW5nZSgtRnJlcSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBtdXRhdGUocG9sYW5kX2F2YWlsYWJsZSA9IHN0cl9kZXRlY3QoQ291bnRyeS5BdmFpbGFiaWxpdHksICdQb2xhbmQnKSkgJT4lCiAgZmlsdGVyKHBvbGFuZF9hdmFpbGFibGUgPT0gVFJVRSkgJT4lCiAgcHVsbChUaXRsZSklPiUgCiAgaGVhZCgxMCkKYGBgCgpVc2luZyAqc2VwYXJhdGUoKSosIHdlIGNhbiBzcGxpdCBvbmUgY29sdW1uIGludG8gc2V2ZXJhbCwgYW5kIGNvbWJpbmUKc2V2ZXJhbCBjb2x1bW5zIGludG8gb25lIHVzaW5nIHRoZSAqdW5pdGUoKSogZnVuY3Rpb24uCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICB1bml0ZSgKICAgIGNvbCA9ICdTY29yZXMnCiAgICAsYygnSGlkZGVuLkdlbS5TY29yZScsICdJTURiLlNjb3JlJywgJ1JvdHRlbi5Ub21hdG9lcy5TY29yZScsICdNZXRhY3JpdGljLlNjb3JlJykKICAgICxzZXAgPSAnLCAnCiAgKSAlPiUKICBzZWxlY3QoVGl0bGUsIFNjb3JlcyklPiUgCiAgaGVhZCgxMCkKYGBgCgojIyMjIENoYWxsZW5nZSAyLgoKKipDSEFMTEVOR0UgMjoqKiBXaGF0IGFyZSB0aGUgdGhyZWUgaGlnaGVzdCByYXRlZCBjb21lZGllcyBhdmFpbGFibGUgaW4KUG9saXNoPwoKYGBge3IgY2hhbGxlbmdlMiwgZXZhbD1GQUxTRX0KZGFuZSAlPiUgCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShhdmdfc2NvcmUgPSBtZWFuKGMoSU1EYi5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGlkZGVuLkdlbS5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUm90dGVuLlRvbWF0b2VzLlNjb3JlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTWV0YWNyaXRpYy5TY29yZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAsbmEucm0gPSBUUlVFKSAlPiUKICAgICAgICAgICByb3VuZCgyKSkgJT4lCiAgc2VsZWN0KFRpdGxlLCBDb3VudHJ5LkF2YWlsYWJpbGl0eSwgYXZnX3Njb3JlKSU+JSAKICBmaWx0ZXIoQ291bnRyeS5BdmFpbGFiaWxpdHkgPT0gIlBvbGFuZCIpICU+JQogIGhlYWQoMykKCgoKYGBgCgojIyMjIENoYWxsZW5nZSAzLgoKKipDSEFMTEVOR0UgMzoqKiBGb3IgMjAxOSBhbmQgMjAyMCBwcm9kdWN0aW9ucywgd2hhdCBpcyB0aGUgYXZlcmFnZSB0aW1lCmJldHdlZW4gcmVsZWFzZSBhbmQgYXBwZWFyYW5jZSBvbiBOZXRmbGl4PwoKYGBge3IgY2hhbGxlbmdlMywgZXZhbD1GQUxTRX0KICBkYW5lJFJlbGVhc2UgPC0gYXMubnVtZXJpYyhmb3JtYXQoYXMuRGF0ZShkYW5lJFJlbGVhc2UuRGF0ZSxmb3JtYXQgPSAiJW0vJWQvJVkiKSwiJVkiKSkKICBkYW5lJE5ldGZsaXguUmVsZWFzZSA8LSBhcy5udW1lcmljKGZvcm1hdChhcy5EYXRlKGRhbmUkTmV0ZmxpeC5SZWxlYXNlLkRhdGUsZm9ybWF0ID0gIiVtLyVkLyVZIiksIiVZIikpCiBkYW5lJGF2Z190aW1lX21vdmllIDwtIGFzLm51bWVyaWMoYXMuRGF0ZShkYW5lJE5ldGZsaXguUmVsZWFzZSkgLSBhcy5EYXRlKGRhbmUkUmVsZWFzZSkpIAogIGRhbmUgJT4lCiAgICBmaWx0ZXIoUmVsZWFzZSA9PSAnMjAxOScgfCBSZWxlYXNlID09ICcyMDIwJykgJT4lCiAgc2VsZWN0KFRpdGxlLCBhdmdfdGltZV9tb3ZpZSwgUmVsZWFzZSwgTmV0ZmxpeC5SZWxlYXNlKQogIApgYGAKCiMjIyMgQ2hhbGxlbmdlIDQuCgoqKkNIQUxMRU5HRSA0OioqIFdoYXQgYXJlIHRoZSBtb3N0IHBvcHVsYXIgdGFncyBmb3IgcHJvZHVjdGlvbnMKYXZhaWxhYmxlIGluIFBvbGlzaD8KCmBgYHtyIGNoYWxsZW5nZTQsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgZ3JvdXBfYnkoVGFncywgTGFuZ3VhZ2VzKSAlPiUKICBmaWx0ZXIoTGFuZ3VhZ2VzID09ICJQb2xpc2giKSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhDb3VudCkpCgoKYGBgCgojIyMgRGF0YSBhZ2dyZWdhdGlvbgoKVXNpbmcgdGhlICpncm91cF9ieSgpKiBhbmQgKnN1bW1hcml6ZSgpKiBmdW5jdGlvbnMsIHdlIHBlcmZvcm0Kb3BlcmF0aW9ucyBvbiBhZ2dyZWdhdGVkIGRhdGEuCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFuZSAlPiUKICBncm91cF9ieShTZXJpZXMub3IuTW92aWUpICU+JQogIHN1bW1hcml6ZSgKICAgIGNvdW50ID0gbigpCiAgICAsYXZnX2ltZGJfc2NvcmUgPSBtZWFuKElNRGIuU2NvcmUsIG5hLnJtID0gVFJVRSkgJT4lIHJvdW5kKDIpCiAgICAsYXZnX2ltZGJfdm90ZXMgPSBtZWFuKElNRGIuVm90ZXMsIG5hLnJtID0gVFJVRSkgJT4lIHJvdW5kKDApCiAgICAsc3VtX2F3YXJkcyA9IHN1bShBd2FyZHMuUmVjZWl2ZWQsIG5hLnJtID0gVFJVRSkKICApCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmRhbmUgJT4lCiAgZ3JvdXBfYnkoU2VyaWVzLm9yLk1vdmllLCBSdW50aW1lKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lCiAgYXJyYW5nZSgtbikKYGBgCgojIyMjIENoYWxsZW5nZSA1LgoKKipDSEFMTEVOR0UgNToqKiBXaGF0IGFyZSB0aGUgYXZlcmFnZSByYXRpbmdzIG9mIGZpbG1zIHByb2R1Y2VkIGluIGVhY2gKZGVjYWRlIChpLmUuLCAxOTYwcywgMTk3MHMsIDE5ODBzLCAxOTkwcywgZXRjLik/CgpgYGB7ciBjaGFsbGVuZ2U1LCBldmFsPUZBTFNFfQpkYW5lJERhdGVfQ29sIDwtIGFzLkRhdGUoZGFuZSRSZWxlYXNlLkRhdGUsIGZvcm1hdCA9ICIlZC8lbS8lWSIpCmRhbmUkRGVjYWRlIDwtIGN1dChkYW5lJERhdGVfQ29sLCBicmVha3MgPSBzZXEoYXMuRGF0ZSgiMTk1OS0xMi0zMSIpLCBhcy5EYXRlKCIyMDIwLTEyLTMxIiksIGJ5ID0gIjEwIHllYXJzIiksIGxhYmVscyA9IGMoIjE5NjBzIiwgIjE5NzBzIiwgIjE5ODBzIiwgIjE5OTBzIiwgIjIwMDBzIiwgIjIwMTBzIiwgIjIwMjBzIikpCmRhbmUgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShhdmdfc2NvcmUgPSBtZWFuKGMoSU1EYi5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGlkZGVuLkdlbS5TY29yZSAqIDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUm90dGVuLlRvbWF0b2VzLlNjb3JlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTWV0YWNyaXRpYy5TY29yZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAsbmEucm0gPSBUUlVFKSAlPiUKICAgICAgICAgICByb3VuZCgyKSkgJT4lCiAgc3VtbWFyaXNlKFRpdGxlLCBhdmdfc2NvcmUsIERlY2FkZSkgJT4lCiAgYXJyYW5nZShEZWNhZGUpCgoKYGBgCgojIyMgUGl2b3QgdGFibGVzLCBsb25nIGFuZCB3aWRlIGZvcm1hdCBkYXRhCgpEYXRhIGluICp3aWRlKiBmb3JtYXQ6IC0gcm93cyByZXByZXNlbnQgaW5kaXZpZHVhbCBvYnNlcnZhdGlvbnMgLQpjb2x1bW5zIHJlcHJlc2VudCBhdHRyaWJ1dGVzIG9mIHRoZXNlIG9ic2VydmF0aW9ucyAtIGNlbGxzIHJlcHJlc2VudAp2YWx1ZXMgb2YgaW5kaXZpZHVhbCBhdHRyaWJ1dGVzIGZvciBpbmRpdmlkdWFsIG9ic2VydmF0aW9ucy4KCkRhdGEgaW4gKmxvbmcqIGZvcm1hdDogLSBpbiB0aGUgZmlyc3QgY29sdW1uIHdlIGhhdmUgb2JzZXJ2YXRpb25zICh0aGUKb2JzZXJ2YXRpb24ga2V5IGNhbiBhbHNvIGNvbnNpc3Qgb2YgbW9yZSB0aGFuIG9uZSBjb2x1bW4pIC0gaW4gdGhlCnNlY29uZCBjb2x1bW4gd2UgaGF2ZSBhdHRyaWJ1dGVzIC0gaW4gdGhlIHRoaXJkIGNvbHVtbiB3ZSBoYXZlIHZhbHVlcy4KClRoZSAqbG9uZyogZm9ybWF0IGlzIHVzZWZ1bCwgYW1vbmcgb3RoZXIgdGhpbmdzLCB3aGVuIGNyZWF0aW5nIGNoYXJ0cyBpbgp0aGUgKmdncGxvdDIqIGxpYnJhcnkuCgpgYGB7cn0KZGFuZV9waXZvdCA9IGRhbmUgJT4lCiAgc2VsZWN0KFRpdGxlLCBlbmRzX3dpdGgoJ1Njb3JlJykpCmBgYAoKYGBge3J9CmRhbmVfcGl2b3QgPSBkYW5lX3Bpdm90ICU+JQogIHBpdm90X2xvbmdlcigKICAgIGNvbHMgPSAyOjUKICAgICxuYW1lc190byA9ICdBdHRyaWJ1dGUnCiAgICAsdmFsdWVzX3RvID0gJ1ZhbHVlJwogICkKYGBgCgpgYGB7cn0KZGFuZV9waXZvdCA9IGRhbmVfcGl2b3QgJT4lCiAgcGl2b3Rfd2lkZXIoCiAgICBpZF9jb2xzID0gMQogICAgLG5hbWVzX2Zyb20gPSAnQXR0cmlidXRlJwogICAgLHZhbHVlc19mcm9tID0gJ1ZhbHVlJwogICkKYGBgCgojIyMgSm9pbmluZyB0YWJsZXMKCmBgYHtyfQpvY2VueV9tZXRhY3JpdGljID0gZGFuZSAlPiUKICBzZWxlY3QoVGl0bGUsIE1ldGFjcml0aWMuU2NvcmUpICU+JQogIC5bMToxMDAsXSAlPiUKICBkcm9wX25hKCkKCm9jZW55X3JvdHRlbl90b21hdG9lcyA9IGRhbmUgJT4lCiAgc2VsZWN0KFRpdGxlLCBSb3R0ZW4uVG9tYXRvZXMuU2NvcmUpICU+JQogIC5bMToxMDAsXSAlPiUKICBkcm9wX25hKCkKYGBgCgpXZSBqb2luIHRhYmxlcyBieSB0aGVpciBjb3JyZXNwb25kaW5nIGtleXMganVzdCBhcyB3ZSBkbyBpbiBTUUwuCgpgYGB7cn0Kb2NlbnlfbWV0YWNyaXRpYyAlPiUKICBsZWZ0X2pvaW4ob2Nlbnlfcm90dGVuX3RvbWF0b2VzLCBieSA9IGMoJ1RpdGxlJyA9ICdUaXRsZScpKQpgYGAKCmBgYHtyfQpvY2VueV9tZXRhY3JpdGljICU+JQogIHJpZ2h0X2pvaW4ob2Nlbnlfcm90dGVuX3RvbWF0b2VzLCBieSA9IGMoJ1RpdGxlJyA9ICdUaXRsZScpKQpgYGAKCmBgYHtyfQpvY2VueV9tZXRhY3JpdGljICU+JQogIGlubmVyX2pvaW4ob2Nlbnlfcm90dGVuX3RvbWF0b2VzLCBieSA9IGMoJ1RpdGxlJyA9ICdUaXRsZScpKQpgYGAKCmBgYHtyfQpvY2VueV9tZXRhY3JpdGljICU+JQogIGZ1bGxfam9pbihvY2VueV9yb3R0ZW5fdG9tYXRvZXMsIGJ5ID0gYygnVGl0bGUnID0gJ1RpdGxlJykpCmBgYAoKYGBge3J9Cm9jZW55X21ldGFjcml0aWMgJT4lCiAgYW50aV9qb2luKG9jZW55X3JvdHRlbl90b21hdG9lcywgYnkgPSBjKCdUaXRsZScgPSAnVGl0bGUnKSkKYGBgCgpgYGB7cn0Kb2Nlbnlfcm90dGVuX3RvbWF0b2VzICU+JQogIGFudGlfam9pbihvY2VueV9tZXRhY3JpdGljLCBieSA9IGMoJ1RpdGxlJyA9ICdUaXRsZScpKQpgYGAK