The code demonstrated here are from Charlotte Wickham’s purrr-tutorial. She gave a two day tutorial at the 2017 UseR, YouTube Solving iteration problems with purrr day1 and YouTube Solving iteration problems with purrr II day2.

Here is the link to the http://bit.ly/purrr-slides.

I also find that Sharon Machlis purrr videos are quite useful. map_df

Day 1 of Charlotte’s purrr tutorial

library(pacman)
p_load(tidyverse, repurrrsive, nycflights13, fs, glue, babynames, Lahman, tidymodels)
data(sw_films)
View(sw_films)

data(sw_people)
View(sw_people)

data(sw_planets)
View(sw_planets)

data(sw_species)
View(sw_species)

data(sw_starships)
View(sw_starships)

data(sw_vehicles)
View(sw_vehicles)
sw_people %>% map_chr(~ length(.x[["starships"]]))
 [1] "2" "0" "0" "1" "0" "0" "0" "0" "1" "5" "3" "0" "2" "2" "0" "0" "1" "1"
[19] "0" "0" "1" "0" "0" "1" "0" "0" "0" "1" "0" "1" "0" "0" "0" "0" "0" "0"
[37] "1" "0" "0" "0" "0" "1" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
[55] "1" "0" "1" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
[73] "0" "0" "0" "0" "1" "0" "0" "0" "0" "0" "0" "1" "0" "0" "3"
sw_people %>% map_chr(~ .x[["hair_color"]])
 [1] "blond"         "n/a"           "n/a"           "none"         
 [5] "brown"         "brown, grey"   "brown"         "n/a"          
 [9] "black"         "auburn, white" "blond"         "auburn, grey" 
[13] "brown"         "brown"         "n/a"           "n/a"          
[17] "brown"         "brown"         "white"         "grey"         
[21] "black"         "none"          "none"          "black"        
[25] "none"          "none"          "auburn"        "brown"        
[29] "brown"         "none"          "brown"         "none"         
[33] "blond"         "none"          "none"          "none"         
[37] "brown"         "black"         "none"          "black"        
[41] "black"         "none"          "none"          "none"         
[45] "none"          "none"          "none"          "none"         
[49] "white"         "none"          "black"         "none"         
[53] "none"          "none"          "none"          "none"         
[57] "black"         "brown"         "brown"         "none"         
[61] "black"         "black"         "brown"         "white"        
[65] "black"         "black"         "blonde"        "none"         
[69] "none"          "none"          "white"         "none"         
[73] "none"          "none"          "none"          "none"         
[77] "none"          "brown"         "brown"         "none"         
[81] "none"          "black"         "brown"         "brown"        
[85] "none"          "unknown"       "brown"        
sw_people %>% map_lgl(~ .x[["gender"]] == "male")
 [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
[13]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
[25]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[37]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
[49]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
[61] FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE
[73] FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE
[85] FALSE FALSE FALSE
sw_people %>% map_dbl(~ as.numeric(.x[["mass"]]))
 [1]  77.0  75.0  32.0 136.0  49.0 120.0  75.0  32.0  84.0  77.0  84.0    NA
[13] 112.0  80.0  74.0    NA  77.0 110.0  17.0  75.0  78.2 140.0 113.0  79.0
[25]  79.0  83.0    NA    NA  20.0  68.0  89.0  90.0    NA  66.0  82.0    NA
[37]    NA    NA  40.0    NA    NA  80.0    NA  55.0  45.0    NA  65.0  84.0
[49]  82.0  87.0    NA  50.0    NA    NA  80.0    NA  85.0    NA    NA  80.0
[61]  56.2  50.0    NA  80.0    NA  79.0  55.0 102.0  88.0    NA    NA  15.0
[73]    NA  48.0    NA  57.0 159.0 136.0  79.0  48.0  80.0    NA    NA    NA
[85]    NA    NA  45.0
sw_people %>% map_dbl(~ parse_number(.x[["mass"]]))
 [1]   77.0   75.0   32.0  136.0   49.0  120.0   75.0   32.0   84.0   77.0
[11]   84.0     NA  112.0   80.0   74.0 1358.0   77.0  110.0   17.0   75.0
[21]   78.2  140.0  113.0   79.0   79.0   83.0     NA     NA   20.0   68.0
[31]   89.0   90.0     NA   66.0   82.0     NA     NA     NA   40.0     NA
[41]     NA   80.0     NA   55.0   45.0     NA   65.0   84.0   82.0   87.0
[51]     NA   50.0     NA     NA   80.0     NA   85.0     NA     NA   80.0
[61]   56.2   50.0     NA   80.0     NA   79.0   55.0  102.0   88.0     NA
[71]     NA   15.0     NA   48.0     NA   57.0  159.0  136.0   79.0   48.0
[81]   80.0     NA     NA     NA     NA     NA   45.0
sw_people %>% map("starships") %>%
  map_int(length)
 [1] 2 0 0 1 0 0 0 0 1 5 3 0 2 2 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1
[38] 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[75] 0 0 1 0 0 0 0 0 0 1 0 0 3
planet_lookup <- sw_planets %>% map_chr("name") %>%
  set_names(map_chr(sw_planets, "url"))
planet_lookup
 http://swapi.co/api/planets/2/  http://swapi.co/api/planets/3/ 
                     "Alderaan"                      "Yavin IV" 
 http://swapi.co/api/planets/4/  http://swapi.co/api/planets/5/ 
                         "Hoth"                       "Dagobah" 
 http://swapi.co/api/planets/6/  http://swapi.co/api/planets/7/ 
                       "Bespin"                         "Endor" 
 http://swapi.co/api/planets/8/  http://swapi.co/api/planets/9/ 
                        "Naboo"                     "Coruscant" 
http://swapi.co/api/planets/10/ http://swapi.co/api/planets/11/ 
                       "Kamino"                      "Geonosis" 
http://swapi.co/api/planets/12/ http://swapi.co/api/planets/13/ 
                       "Utapau"                      "Mustafar" 
http://swapi.co/api/planets/14/ http://swapi.co/api/planets/15/ 
                     "Kashyyyk"                   "Polis Massa" 
http://swapi.co/api/planets/16/ http://swapi.co/api/planets/17/ 
                      "Mygeeto"                       "Felucia" 
http://swapi.co/api/planets/18/ http://swapi.co/api/planets/19/ 
               "Cato Neimoidia"                     "Saleucami" 
http://swapi.co/api/planets/20/ http://swapi.co/api/planets/21/ 
                      "Stewjon"                        "Eriadu" 
http://swapi.co/api/planets/22/ http://swapi.co/api/planets/23/ 
                     "Corellia"                         "Rodia" 
http://swapi.co/api/planets/24/ http://swapi.co/api/planets/25/ 
                    "Nal Hutta"                     "Dantooine" 
http://swapi.co/api/planets/26/ http://swapi.co/api/planets/27/ 
                   "Bestine IV"                   "Ord Mantell" 
http://swapi.co/api/planets/28/ http://swapi.co/api/planets/29/ 
                      "unknown"                     "Trandosha" 
http://swapi.co/api/planets/30/ http://swapi.co/api/planets/31/ 
                      "Socorro"                      "Mon Cala" 
http://swapi.co/api/planets/32/ http://swapi.co/api/planets/33/ 
                    "Chandrila"                       "Sullust" 
http://swapi.co/api/planets/34/ http://swapi.co/api/planets/35/ 
                     "Toydaria"                     "Malastare" 
http://swapi.co/api/planets/36/ http://swapi.co/api/planets/37/ 
                     "Dathomir"                        "Ryloth" 
http://swapi.co/api/planets/38/ http://swapi.co/api/planets/39/ 
                  "Aleen Minor"                       "Vulpter" 
http://swapi.co/api/planets/40/ http://swapi.co/api/planets/41/ 
                      "Troiken"                          "Tund" 
http://swapi.co/api/planets/42/ http://swapi.co/api/planets/43/ 
                   "Haruun Kal"                         "Cerea" 
http://swapi.co/api/planets/44/ http://swapi.co/api/planets/45/ 
                  "Glee Anselm"                      "Iridonia" 
http://swapi.co/api/planets/46/ http://swapi.co/api/planets/47/ 
                      "Tholoth"                       "Iktotch" 
http://swapi.co/api/planets/48/ http://swapi.co/api/planets/49/ 
                      "Quermia"                         "Dorin" 
http://swapi.co/api/planets/50/ http://swapi.co/api/planets/51/ 
                     "Champala"                        "Mirial" 
http://swapi.co/api/planets/52/ http://swapi.co/api/planets/53/ 
                      "Serenno"                  "Concord Dawn" 
http://swapi.co/api/planets/54/ http://swapi.co/api/planets/55/ 
                        "Zolan"                          "Ojom" 
http://swapi.co/api/planets/56/ http://swapi.co/api/planets/57/ 
                        "Skako"                    "Muunilinst" 
http://swapi.co/api/planets/58/ http://swapi.co/api/planets/59/ 
                        "Shili"                         "Kalee" 
http://swapi.co/api/planets/60/  http://swapi.co/api/planets/1/ 
                       "Umbara"                      "Tatooine" 
http://swapi.co/api/planets/61/ 
                        "Jakku" 

Challenges:

  1. Which film (see sw_films) has the most characters?

Answers:

  1. Which sw_species has the most possible eye colors?

Answers:

  1. Which sw_planets do we know the least about (i.e. have the most “unknown” entries)?

Answers:

Solutions:

  1. Which film (see sw_films) has the most characters?

Answers: Attack of the Clones

# do one.
length(sw_films[[1]]$characters)
[1] 18
sw_films %>% map("characters") %>%
  map_int(~ length(.x)) %>%
  set_names(map_chr(sw_films, "title"))
             A New Hope    Attack of the Clones      The Phantom Menace 
                     18                      40                      34 
    Revenge of the Sith      Return of the Jedi The Empire Strikes Back 
                     34                      20                      16 
      The Force Awakens 
                     11 
sw_films %>% map("characters") %>%
  map_int(length) %>%
  set_names(map_chr(sw_films, "title")) %>%
  sort(decreasing = TRUE)
   Attack of the Clones      The Phantom Menace     Revenge of the Sith 
                     40                      34                      34 
     Return of the Jedi              A New Hope The Empire Strikes Back 
                     20                      18                      16 
      The Force Awakens 
                     11 
  1. Which sw_species has the most possible eye colors?

Answers: Mirialan, Togruta, Wookiee, Human

# do one
length(unlist(str_split(sw_species[[1]]$"eye_colors", ", ")))
[1] 2
sw_species %>% map_chr("eye_colors") %>%
  stringr::str_split(", ") %>%
  map_int(length) %>%
  set_names(map_chr(sw_species, "name")) %>%
  sort(decreasing = TRUE)
      Mirialan        Togruta        Wookiee          Human        Twi'lek 
             6              6              6              6              4 
Yoda's species           Hutt     Trandoshan           Ewok      Neimodian 
             3              2              2              2              2 
           Dug         Zabrak     Tholothian        Kel Dor      Geonosian 
             2              2              2              2              2 
  Mon Calamari      Sullustan         Gungan      Toydarian         Aleena 
             1              1              1              1              1 
    Vulptereen          Xexto          Toong         Cerean       Nautolan 
             1              1              1              1              1 
      Iktotchi       Quermian       Chagrian       Clawdite       Besalisk 
             1              1              1              1              1 
      Kaminoan        Skakoan           Muun        Kaleesh         Pau'an 
             1              1              1              1              1 
         Droid         Rodian 
             1              1 
  1. Which sw_planets do we know the least about (i.e. have the most “unknown” entries)?

Answers: Aleen Minor, Tholoth, Quermia, Zolan, Umbara

sw_planets %>% map_int( ~ map_lgl(.x, ~ "unknown" %in% .x) %>%
              sum() ) %>%
  set_names(map_chr(sw_planets, "name")) %>%
  sort(decreasing = TRUE)
   Aleen Minor        Tholoth        Quermia          Zolan         Umbara 
             8              8              8              8              8 
       Troiken         Mirial        Serenno   Concord Dawn          Jakku 
             7              7              7              7              7 
       unknown       Iridonia          Shili        Stewjon           Ojom 
             6              5              5              4              4 
          Tund        Iktotch          Dorin      Saleucami       Champala 
             3              3              3              2              2 
         Skako           Hoth        Dagobah      Coruscant        Mygeeto 
             2              1              1              1              1 
       Felucia Cato Neimoidia         Eriadu      Nal Hutta      Dantooine 
             1              1              1              1              1 
    Bestine IV      Trandosha        Socorro       Toydaria      Malastare 
             1              1              1              1              1 
      Dathomir        Vulpter     Haruun Kal          Cerea          Kalee 
             1              1              1              1              1 
      Alderaan       Yavin IV         Bespin          Endor          Naboo 
             0              0              0              0              0 
        Kamino       Geonosis         Utapau       Mustafar       Kashyyyk 
             0              0              0              0              0 
   Polis Massa       Corellia          Rodia    Ord Mantell       Mon Cala 
             0              0              0              0              0 
     Chandrila        Sullust         Ryloth    Glee Anselm     Muunilinst 
             0              0              0              0              0 
      Tatooine 
             0 

Try Sharon’s first Example

Read .csv files using map.

First, write come .csv files.

flights %>% filter(origin == "LGA") %>%
  write_csv("flights_LGA.csv")
flights %>% filter(origin == "EWR") %>%
  write_csv("flights_EWR.csv")
flights %>% filter(origin == "JFK") %>%
  write_csv("flights_JFK.csv")
myfiles <- list.files(pattern = ".csv")
myfiles
[1] "flights_EWR.csv" "flights_JFK.csv" "flights_LGA.csv"
mydata <- myfiles %>% map_df(read_csv)
Parsed with column specification:
cols(
  year = col_double(),
  month = col_double(),
  day = col_double(),
  dep_time = col_double(),
  sched_dep_time = col_double(),
  dep_delay = col_double(),
  arr_time = col_double(),
  sched_arr_time = col_double(),
  arr_delay = col_double(),
  carrier = col_character(),
  flight = col_double(),
  tailnum = col_character(),
  origin = col_character(),
  dest = col_character(),
  air_time = col_double(),
  distance = col_double(),
  hour = col_double(),
  minute = col_double(),
  time_hour = col_datetime(format = "")
)
Parsed with column specification:
cols(
  year = col_double(),
  month = col_double(),
  day = col_double(),
  dep_time = col_double(),
  sched_dep_time = col_double(),
  dep_delay = col_double(),
  arr_time = col_double(),
  sched_arr_time = col_double(),
  arr_delay = col_double(),
  carrier = col_character(),
  flight = col_double(),
  tailnum = col_character(),
  origin = col_character(),
  dest = col_character(),
  air_time = col_double(),
  distance = col_double(),
  hour = col_double(),
  minute = col_double(),
  time_hour = col_datetime(format = "")
)
Parsed with column specification:
cols(
  year = col_double(),
  month = col_double(),
  day = col_double(),
  dep_time = col_double(),
  sched_dep_time = col_double(),
  dep_delay = col_double(),
  arr_time = col_double(),
  sched_arr_time = col_double(),
  arr_delay = col_double(),
  carrier = col_character(),
  flight = col_double(),
  tailnum = col_character(),
  origin = col_character(),
  dest = col_character(),
  air_time = col_double(),
  distance = col_double(),
  hour = col_double(),
  minute = col_double(),
  time_hour = col_datetime(format = "")
)
dim(flights)
[1] 336776     19
dim(mydata)
[1] 336776     19
file_delete(glue("{myfiles}"))

Day 2 of Charlotte’s purrr tutorial

Try rerun().

x <- 3 %>% rerun(rnorm(100)) %>%
  map(mean)
x
[[1]]
[1] -0.01373121

[[2]]
[1] -0.1657158

[[3]]
[1] 0.1884074
x <- 1000 %>% rerun( tibble( x = rnorm(100), 
                             y = rexp(100) ) 
                     )

x_cor <- x %>% map_df(~ tibble(x_cor = as.vector(cor(.x))[2]) ) 


x_cor %>% ggplot(aes(x = x_cor)) +
  geom_histogram()


x_cor <- x_cor %>% mutate( index = 1:length(x_cor))

x_cor %>% ggplot(aes(x = index, y = x_cor)) +
  geom_line()

Back to the tutorial.

Try to use the gap_split dataset to make plots for each country.

Make a list of ggplots.

Try walk().

data(gap_split)
View(gap_split)

countries <- names(gap_split)
countries
  [1] "Afghanistan"              "Albania"                 
  [3] "Algeria"                  "Angola"                  
  [5] "Argentina"                "Australia"               
  [7] "Austria"                  "Bahrain"                 
  [9] "Bangladesh"               "Belgium"                 
 [11] "Benin"                    "Bolivia"                 
 [13] "Bosnia and Herzegovina"   "Botswana"                
 [15] "Brazil"                   "Bulgaria"                
 [17] "Burkina Faso"             "Burundi"                 
 [19] "Cambodia"                 "Cameroon"                
 [21] "Canada"                   "Central African Republic"
 [23] "Chad"                     "Chile"                   
 [25] "China"                    "Colombia"                
 [27] "Comoros"                  "Congo, Dem. Rep."        
 [29] "Congo, Rep."              "Costa Rica"              
 [31] "Cote d'Ivoire"            "Croatia"                 
 [33] "Cuba"                     "Czech Republic"          
 [35] "Denmark"                  "Djibouti"                
 [37] "Dominican Republic"       "Ecuador"                 
 [39] "Egypt"                    "El Salvador"             
 [41] "Equatorial Guinea"        "Eritrea"                 
 [43] "Ethiopia"                 "Finland"                 
 [45] "France"                   "Gabon"                   
 [47] "Gambia"                   "Germany"                 
 [49] "Ghana"                    "Greece"                  
 [51] "Guatemala"                "Guinea"                  
 [53] "Guinea-Bissau"            "Haiti"                   
 [55] "Honduras"                 "Hong Kong, China"        
 [57] "Hungary"                  "Iceland"                 
 [59] "India"                    "Indonesia"               
 [61] "Iran"                     "Iraq"                    
 [63] "Ireland"                  "Israel"                  
 [65] "Italy"                    "Jamaica"                 
 [67] "Japan"                    "Jordan"                  
 [69] "Kenya"                    "Korea, Dem. Rep."        
 [71] "Korea, Rep."              "Kuwait"                  
 [73] "Lebanon"                  "Lesotho"                 
 [75] "Liberia"                  "Libya"                   
 [77] "Madagascar"               "Malawi"                  
 [79] "Malaysia"                 "Mali"                    
 [81] "Mauritania"               "Mauritius"               
 [83] "Mexico"                   "Mongolia"                
 [85] "Montenegro"               "Morocco"                 
 [87] "Mozambique"               "Myanmar"                 
 [89] "Namibia"                  "Nepal"                   
 [91] "Netherlands"              "New Zealand"             
 [93] "Nicaragua"                "Niger"                   
 [95] "Nigeria"                  "Norway"                  
 [97] "Oman"                     "Pakistan"                
 [99] "Panama"                   "Paraguay"                
[101] "Peru"                     "Philippines"             
[103] "Poland"                   "Portugal"                
[105] "Puerto Rico"              "Reunion"                 
[107] "Romania"                  "Rwanda"                  
[109] "Sao Tome and Principe"    "Saudi Arabia"            
[111] "Senegal"                  "Serbia"                  
[113] "Sierra Leone"             "Singapore"               
[115] "Slovak Republic"          "Slovenia"                
[117] "Somalia"                  "South Africa"            
[119] "Spain"                    "Sri Lanka"               
[121] "Sudan"                    "Swaziland"               
[123] "Sweden"                   "Switzerland"             
[125] "Syria"                    "Taiwan"                  
[127] "Tanzania"                 "Thailand"                
[129] "Togo"                     "Trinidad and Tobago"     
[131] "Tunisia"                  "Turkey"                  
[133] "Uganda"                   "United Kingdom"          
[135] "United States"            "Uruguay"                 
[137] "Venezuela"                "Vietnam"                 
[139] "West Bank and Gaza"       "Yemen, Rep."             
[141] "Zambia"                   "Zimbabwe"                
plots <- map2( gap_split, countries,
                    ~ ggplot(.x, aes(x = year, y = lifeExp)) 
                      + geom_line()
                      + labs(title = .y))

plots
$Afghanistan

$Albania

$Algeria

$Angola

$Argentina

$Australia

$Austria

$Bahrain

$Bangladesh

$Belgium

$Benin

$Bolivia

$`Bosnia and Herzegovina`

$Botswana

$Brazil

$Bulgaria

$`Burkina Faso`

$Burundi

$Cambodia

$Cameroon

$Canada

$`Central African Republic`

$Chad

$Chile

$China

$Colombia

$Comoros

$`Congo, Dem. Rep.`

$`Congo, Rep.`

$`Costa Rica`

$`Cote d'Ivoire`

$Croatia

$Cuba

$`Czech Republic`

$Denmark

$Djibouti

$`Dominican Republic`

$Ecuador

$Egypt

$`El Salvador`

$`Equatorial Guinea`

$Eritrea

$Ethiopia

$Finland

$France

$Gabon

$Gambia

$Germany

$Ghana

$Greece

$Guatemala

$Guinea

$`Guinea-Bissau`

$Haiti

$Honduras

$`Hong Kong, China`

$Hungary

$Iceland

$India

$Indonesia

$Iran

$Iraq

$Ireland

$Israel

$Italy

$Jamaica

$Japan

$Jordan

$Kenya

$`Korea, Dem. Rep.`

$`Korea, Rep.`

$Kuwait

$Lebanon

$Lesotho

$Liberia

$Libya

$Madagascar

$Malawi

$Malaysia

$Mali

$Mauritania

$Mauritius

$Mexico

$Mongolia

$Montenegro

$Morocco

$Mozambique

$Myanmar

$Namibia

$Nepal

$Netherlands

$`New Zealand`

$Nicaragua

$Niger

$Nigeria

$Norway

$Oman

$Pakistan

$Panama

$Paraguay

$Peru

$Philippines

$Poland

$Portugal

$`Puerto Rico`

$Reunion

$Romania

$Rwanda

$`Sao Tome and Principe`

$`Saudi Arabia`

$Senegal

$Serbia

$`Sierra Leone`

$Singapore

$`Slovak Republic`

$Slovenia

$Somalia

$`South Africa`

$Spain

$`Sri Lanka`

$Sudan

$Swaziland

$Sweden

$Switzerland

$Syria

$Taiwan

$Tanzania

$Thailand

$Togo

$`Trinidad and Tobago`

$Tunisia

$Turkey

$Uganda

$`United Kingdom`

$`United States`

$Uruguay

$Venezuela

$Vietnam

$`West Bank and Gaza`

$`Yemen, Rep.`

$Zambia

$Zimbabwe

dir_create("plots")

walk2(.x = plots, .y = countries, 
  ~ ggsave(filename = glue("plots/{.y}.pdf"), plot = .x))
Saving 7.29 x 4.5 in image
  
file_delete(glue("plots/{countries}.pdf"))

walk2(.x = plots, .y = countries, 
  ~ ggsave(filename = paste0("plots/",.y,".pdf"), plot = .x))
Saving 7.29 x 4.5 in image

  
file.remove(paste0("plots/",countries, ".pdf"))
  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [15] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [29] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [43] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [57] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [71] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [85] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [99] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[113] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[127] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[141] TRUE TRUE

Chapter 5 from mdsr

What is tidy data?

The babynames data has been updated.

babynames %>% group_by(sex, name) %>%
  summarise( total_births = sum(n)) %>%
  arrange(desc(total_births))
`summarise()` regrouping output by 'sex' (override with `.groups` argument)
babynames %>% filter(name == "Sue") %>%
  group_by(sex, name) %>%
  summarise( total = sum(n)) 
`summarise()` regrouping output by 'sex' (override with `.groups` argument)
babynames %>% filter(name == "Robin") %>%
  group_by(sex, name) %>%
  summarise( total = sum(n)) 
`summarise()` regrouping output by 'sex' (override with `.groups` argument)
babynames %>% filter(name %in% c("Sue", "Robin", "Leslie")) %>%
  group_by(sex, name) %>%
  summarise( total = sum(n)) %>% 
  pivot_wider(names_from = "sex", values_from = "total")
`summarise()` regrouping output by 'sex' (override with `.groups` argument)
BabyWide <- babynames %>% 
  group_by(sex, name) %>%
  summarise( total = sum(n)) %>% 
  pivot_wider(names_from = sex, values_from = total)
`summarise()` regrouping output by 'sex' (override with `.groups` argument)
head(BabyWide, 10)
BabyWide %>% filter( M > 50000, F > 50000) %>%
  mutate( ratio = pmin(M/F, F/M) ) %>%
  arrange(desc(ratio))

Automation and iteration

The Lahman data has been updated.

names(Teams)
 [1] "yearID"         "lgID"           "teamID"         "franchID"      
 [5] "divID"          "Rank"           "G"              "Ghome"         
 [9] "W"              "L"              "DivWin"         "WCWin"         
[13] "LgWin"          "WSWin"          "R"              "AB"            
[17] "H"              "X2B"            "X3B"            "HR"            
[21] "BB"             "SO"             "SB"             "CS"            
[25] "HBP"            "SF"             "RA"             "ER"            
[29] "ERA"            "CG"             "SHO"            "SV"            
[33] "IPouts"         "HA"             "HRA"            "BBA"           
[37] "SOA"            "E"              "DP"             "FP"            
[41] "name"           "park"           "attendance"     "BPF"           
[45] "PPF"            "teamIDBR"       "teamIDlahman45" "teamIDretro"   

Replace the apply() function with map()

Teams %>% select(15:40) %>%
  map_df(mean, na.rm = TRUE) 
angles <- Teams %>% filter(franchID == "ANA") %>%
  group_by(teamID, name) %>%
  summarize( began = first(yearID), end = last(yearID) ) %>%
  arrange(began)
`summarise()` regrouping output by 'teamID' (override with `.groups` argument)
angles
angles_names <- angles$name

angles_names
[1] "Los Angeles Angels"            "California Angels"            
[3] "Anaheim Angels"                "Los Angeles Angels of Anaheim"
nchar(angles_names[1])
[1] 18
nchar(angles_names[2])
[1] 17
nchar(angles_names[3])
[1] 14
nchar(angles_names[4])
[1] 29
angles_names %>% map(nchar)
[[1]]
[1] 18

[[2]]
[1] 17

[[3]]
[1] 14

[[4]]
[1] 29
top5 <- function(x, teamname){
  x %>%
    filter(name == teamname) %>%
    select(teamID, yearID,   W, L, name) %>%
    arrange(desc(W)) %>%
    head(5)
}
top5(Teams, angles_names[1])
top5(Teams, angles_names[2])
top5(Teams, angles_names[3])
top5(Teams, angles_names[4])

Compare lapply and map

angles_list <- lapply(angles_names, FUN = top5, x = Teams)
angles_list
[[1]]

[[2]]

[[3]]

[[4]]
angles_list2 <- angles_names %>% map(~ top5(Teams, .x))
angles_list2
[[1]]

[[2]]

[[3]]

[[4]]
NA

angles_df2 <- angles_names %>% map_df(~ top5(Teams, .x))
angles_df2

angles_df2 %>% group_by(teamID, name) %>%
  summarise(N = n(), mean_wins = mean(W)) %>%
  arrange(desc(mean_wins))
`summarise()` regrouping output by 'teamID' (override with `.groups` argument)

Iteration over subgroups, update with map

exp_wpct <- function(x){
  return( 1 / (1 + (1/x)^2))
}
TeamRuns <- Teams %>%
  filter(yearID >= 1954) %>%
  rename(RS = R) %>%
  mutate(WPct  = W / (W+L), run_ratio = RS/RA) %>%
  select(yearID, teamID, lgID, WPct, run_ratio)
TeamRuns %>% ggplot(aes(x = run_ratio, y = WPct)) +
  geom_vline(xintercept = 1, color ="darkgrey", linetype = 2) +
  geom_hline(yintercept = 0.5, color = "darkgrey", linetype = 2 ) +
  geom_point(alpha = 0.3) +
  stat_function(fun = exp_wpct, size = 2, color = "blue") +
  xlab("Ratio of Runs Scored to Runs Allowed") +
  ylab("Winning Percentage")

library(mosaic)
exWpct <- fitModel( WPct ~ 1/(1 + (1/run_ratio)^k), data = TeamRuns)
coef(exWpct)
       k 
1.836508 
exWpct <- nls( WPct ~ 1/(1 + (1/run_ratio)^k), data = TeamRuns)
No starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart model
coef(exWpct)
       k 
1.836508 
fit_k <- function(x){
  mod <- nls( formula = WPct ~ 1/(1 + (1/run_ratio)^k), data = x )
  return(data.frame(k = coef(mod)))
}
fit_k(TeamRuns)
No starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart model
TeamRuns2 <- TeamRuns %>%
  mutate(decade = yearID %/% 10 * 10) 
TeamRuns2

n_TeamRuns2 <- TeamRuns2 %>% group_by(decade) %>% 
  nest()
n_TeamRuns2

m_TeamRuns2 <- n_TeamRuns2 %>% 
  mutate(decade, model = map(data, fit_k))
No starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart model
m_TeamRuns2

m_TeamRuns2 %>% mutate( model = unlist(model) ) %>%
  select(-data)

Make the homerun leaders plot by league

hr_leader <- function(x){
  x %>%
    select(teamID, HR) %>%
    arrange(desc(HR)) %>%
    head(n=1)
}
Teams %>%
  filter(yearID == 1961 & lgID == "AL") %>%
  arrange(desc(HR)) %>%
  head(1)
n_TeamRuns2 <- Teams %>% group_by(yearID, lgID) %>% 
  nest()

n_TeamRuns2
hr_leaders <- n_TeamRuns2 %>%
    mutate(yearID, lgID, hr = map(data, hr_leader) ) %>%
    arrange(yearID) %>%
    select(-data) %>%
    unnest(hr)
    
head(hr_leaders, 10)
NA
hr_leaders %>% group_by(lgID) %>%
  summarize(homerun_mean = mean(HR))
`summarise()` ungrouping output (override with `.groups` argument)
mean(HR ~ lgID, data = hr_leaders)
      AA       AL       FL       NA       NL       PL       UA 
 40.5000 157.1092  51.0000  13.8000 129.2917  66.0000  32.0000 
hr_leaders %>% filter(yearID >= 1916) %>%
  group_by(lgID) %>%
  summarize(homerun_mean = mean(HR))
`summarise()` ungrouping output (override with `.groups` argument)
mean(HR ~ lgID, data = hr_leaders)
      AA       AL       FL       NA       NL       PL       UA 
 40.5000 157.1092  51.0000  13.8000 129.2917  66.0000  32.0000 
hr_leaders %>%
  filter(yearID >= 1916) %>%
  ggplot(aes(x = yearID, y = HR, color = lgID)) +
  geom_line() +
  geom_point() +
  geom_smooth(se=0) +
  geom_vline(xintercept = 1973) +
  annotate("text", x=1974, y=25, label = "AL adopts DH", hjust="left")

Interation for the Bootstrap using rerun

n_TeamRuns3 <- TeamRuns %>%
  group_by(yearID) %>%
  nest()
n_TeamRuns3 

k_actual <- n_TeamRuns3 %>%                 # m_TeamRuns3
  mutate(model = map(data, fit_k))
No starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart modelNo starting values specified for some parameters.
Initializing ‘k’ to '1.'.
Consider specifying 'start' or using a selfStart model
k_actual <- k_actual %>% select(-data) %>%
  unnest(model) 

k_actual %>% ungroup() %>%
  summarize(
    min = min(k),
    median = median(k),
    max = max(k),
    mean = mean(k),
    sd = sd(k),
    n = n()
  )
NA
NA
NA
NA
k_actual %>% ggplot(aes(x = k)) +
  geom_density() + 
  xlab("Best fit exponent for a single season.")

Bootstrap

Use moderndive

library(moderndive)

virtual_resamples <- k_actual %>% 
  rep_sample_n(size = 66, replace = TRUE, reps = 1000)

virtual_resampled_means <- virtual_resamples %>% 
  group_by(replicate) %>% 
  summarize(mean_k = mean(k))
`summarise()` ungrouping output (override with `.groups` argument)
virtual_resampled_means %>% ggplot( aes(x = mean_k)) +
  geom_histogram(color = "white") +
  labs(x = "Resample mean k")


virtual_resampled_means %>% ggplot( aes(x = mean_k)) +
  geom_density() +
  labs(x = "Resample mean k")

library(infer)

bootstrap_distribution <- k_actual %>% 
  specify(response = k) %>% 
  generate(reps = 1000, type = "bootstrap") %>%
  calculate(stat = "mean") 

bootstrap_distribution %>%
  visualise()


percentile_ci <- bootstrap_distribution %>% 
  get_confidence_interval(level = 0.95, type = "percentile")
percentile_ci
NA

To do: Add further Boostrapping code.

Use tidymodels to implement this.

From the purrr cheatsheet.

n_iris <- iris %>% group_by(Species) %>% 
  nest()
n_iris
mod_fun <- function(df){
  lm(Sepal.Length ~ ., data = df)
}
m_iris <- n_iris %>% 
  mutate(model = map(data, mod_fun))
m_iris
b_fun <- function(mod){
  coefficients(mod)[[1]]
}
m_iris %>% transmute( Species, beta = map_dbl(model, b_fun) )
LS0tCnRpdGxlOiAiQ2hhcmxvdHRlIFdpY2toYW0ncyBwdXJyciB0dXRvcmlhbCIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpUaGUgY29kZSBkZW1vbnN0cmF0ZWQgaGVyZSBhcmUgZnJvbSBDaGFybG90dGUgV2lja2hhbSdzIFtwdXJyci10dXRvcmlhbF0oaHR0cHM6Ly9naXRodWIuY29tL2N3aWNraGFtL3B1cnJyLXR1dG9yaWFsKS4gIFNoZSBnYXZlIGEgdHdvIGRheSB0dXRvcmlhbCBhdCB0aGUgWzIwMTcgVXNlUl0oKSwgW1lvdVR1YmUgU29sdmluZyBpdGVyYXRpb24gcHJvYmxlbXMgd2l0aCBwdXJyciBkYXkxXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PTdVbFdKV2ZaTzlNKSBhbmQgW1lvdVR1YmUgU29sdmluZyBpdGVyYXRpb24gcHJvYmxlbXMgd2l0aCBwdXJyciBJSSBkYXkyXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PWIwb3pLVFVobzBBKS4KCkhlcmUgaXMgdGhlIGxpbmsgdG8gdGhlIFtodHRwOi8vYml0Lmx5L3B1cnJyLXNsaWRlc10oaHR0cDovL2JpdC5seS9wdXJyci1zbGlkZXMpLgoKSSBhbHNvIGZpbmQgdGhhdCBbU2hhcm9uIE1hY2hsaXNdKGh0dHBzOi8vd3d3LmNvbXB1dGVyd29ybGQuY29tL2F1dGhvci9TaGFyb24tTWFjaGxpcy8pIHB1cnJyIHZpZGVvcyBhcmUgcXVpdGUgdXNlZnVsLiBbbWFwX2RmXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PWdZSkJLMDlodmc0Jmxpc3Q9UEw3RDJSTVNtUk85Sk92UEMxZ2JBOE1jM2F6dlNmbThWdiZpbmRleD00OSkKCgojIyBEYXkgMSBvZiBDaGFybG90dGUncyBwdXJyciB0dXRvcmlhbAoKYGBge3J9CmxpYnJhcnkocGFjbWFuKQpwX2xvYWQodGlkeXZlcnNlLCByZXB1cnJyc2l2ZSwgbnljZmxpZ2h0czEzLCBmcywgZ2x1ZSwgYmFieW5hbWVzLCBMYWhtYW4sIHRpZHltb2RlbHMpCmBgYAoKYGBge3J9CmRhdGEoc3dfZmlsbXMpCiMgVmlldyhzd19maWxtcykKCmRhdGEoc3dfcGVvcGxlKQojIFZpZXcoc3dfcGVvcGxlKQoKZGF0YShzd19wbGFuZXRzKQojIFZpZXcoc3dfcGxhbmV0cykKCmRhdGEoc3dfc3BlY2llcykKIyBWaWV3KHN3X3NwZWNpZXMpCgpkYXRhKHN3X3N0YXJzaGlwcykKIyBWaWV3KHN3X3N0YXJzaGlwcykKCmRhdGEoc3dfdmVoaWNsZXMpCiMgVmlldyhzd192ZWhpY2xlcykKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpzd19wZW9wbGUgJT4lIG1hcF9jaHIofiBsZW5ndGgoLnhbWyJzdGFyc2hpcHMiXV0pKQoKc3dfcGVvcGxlICU+JSBtYXBfY2hyKH4gLnhbWyJoYWlyX2NvbG9yIl1dKQoKc3dfcGVvcGxlICU+JSBtYXBfbGdsKH4gLnhbWyJnZW5kZXIiXV0gPT0gIm1hbGUiKQoKc3dfcGVvcGxlICU+JSBtYXBfZGJsKH4gYXMubnVtZXJpYygueFtbIm1hc3MiXV0pKQoKc3dfcGVvcGxlICU+JSBtYXBfZGJsKH4gcGFyc2VfbnVtYmVyKC54W1sibWFzcyJdXSkpCmBgYAoKCmBgYHtyfQpzd19wZW9wbGUgJT4lIG1hcCgic3RhcnNoaXBzIikgJT4lCiAgbWFwX2ludChsZW5ndGgpCgpwbGFuZXRfbG9va3VwIDwtIHN3X3BsYW5ldHMgJT4lIG1hcF9jaHIoIm5hbWUiKSAlPiUKICBzZXRfbmFtZXMobWFwX2Nocihzd19wbGFuZXRzLCAidXJsIikpCnBsYW5ldF9sb29rdXAKYGBgCgojIyBDaGFsbGVuZ2VzOgoKMS4gV2hpY2ggZmlsbSAoc2VlIHN3X2ZpbG1zKSBoYXMgdGhlIG1vc3QgY2hhcmFjdGVycz8KCioqQW5zd2VyczoqKgoKMi4gV2hpY2ggc3dfc3BlY2llcyBoYXMgdGhlIG1vc3QgcG9zc2libGUgZXllIGNvbG9ycz8KCioqQW5zd2VyczoqKgoKMy4gV2hpY2ggc3dfcGxhbmV0cyBkbyB3ZSBrbm93IHRoZSBsZWFzdCBhYm91dCAoaS5lLiBoYXZlIHRoZSBtb3N0ICJ1bmtub3duIiBlbnRyaWVzKT8KCioqQW5zd2VyczoqKgoKCgojIyBTb2x1dGlvbnM6CgoxLiBXaGljaCBmaWxtIChzZWUgc3dfZmlsbXMpIGhhcyB0aGUgbW9zdCBjaGFyYWN0ZXJzPwoKKipBbnN3ZXJzOioqICBBdHRhY2sgb2YgdGhlIENsb25lcyAKCmBgYHtyfQojIGRvIG9uZS4KbGVuZ3RoKHN3X2ZpbG1zW1sxXV0kY2hhcmFjdGVycykKCnN3X2ZpbG1zICU+JSBtYXAoImNoYXJhY3RlcnMiKSAlPiUKICBtYXBfaW50KH4gbGVuZ3RoKC54KSkgJT4lCiAgc2V0X25hbWVzKG1hcF9jaHIoc3dfZmlsbXMsICJ0aXRsZSIpKQoKc3dfZmlsbXMgJT4lIG1hcCgiY2hhcmFjdGVycyIpICU+JQogIG1hcF9pbnQobGVuZ3RoKSAlPiUKICBzZXRfbmFtZXMobWFwX2Nocihzd19maWxtcywgInRpdGxlIikpICU+JQogIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpCmBgYAoyLiBXaGljaCBzd19zcGVjaWVzIGhhcyB0aGUgbW9zdCBwb3NzaWJsZSBleWUgY29sb3JzPwoKKipBbnN3ZXJzOioqIE1pcmlhbGFuLCBUb2dydXRhLCBXb29raWVlLCBIdW1hbgoKYGBge3J9CiMgZG8gb25lCmxlbmd0aCh1bmxpc3Qoc3RyX3NwbGl0KHN3X3NwZWNpZXNbWzFdXSQiZXllX2NvbG9ycyIsICIsICIpKSkKCnN3X3NwZWNpZXMgJT4lIG1hcF9jaHIoImV5ZV9jb2xvcnMiKSAlPiUKICBzdHJpbmdyOjpzdHJfc3BsaXQoIiwgIikgJT4lCiAgbWFwX2ludChsZW5ndGgpICU+JQogIHNldF9uYW1lcyhtYXBfY2hyKHN3X3NwZWNpZXMsICJuYW1lIikpICU+JQogIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpCmBgYAoKMy4gV2hpY2ggc3dfcGxhbmV0cyBkbyB3ZSBrbm93IHRoZSBsZWFzdCBhYm91dCAoaS5lLiBoYXZlIHRoZSBtb3N0ICJ1bmtub3duIiBlbnRyaWVzKT8KCioqQW5zd2VyczoqKiBBbGVlbiBNaW5vciwgVGhvbG90aCwgUXVlcm1pYSwgWm9sYW4sIFVtYmFyYSAKCmBgYHtyfQpzd19wbGFuZXRzICU+JSBtYXBfaW50KCB+IG1hcF9sZ2woLngsIH4gInVua25vd24iICVpbiUgLngpICU+JQogICAgICAgICAgICAgIHN1bSgpICkgJT4lCiAgc2V0X25hbWVzKG1hcF9jaHIoc3dfcGxhbmV0cywgIm5hbWUiKSkgJT4lCiAgc29ydChkZWNyZWFzaW5nID0gVFJVRSkKYGBgCgojIyBUcnkgU2hhcm9uJ3MgZmlyc3QgRXhhbXBsZQoKUmVhZCAuY3N2IGZpbGVzIHVzaW5nIG1hcC4KCkZpcnN0LCB3cml0ZSBjb21lIC5jc3YgZmlsZXMuCgpgYGB7cn0KZmxpZ2h0cyAlPiUgZmlsdGVyKG9yaWdpbiA9PSAiTEdBIikgJT4lCiAgd3JpdGVfY3N2KCJmbGlnaHRzX0xHQS5jc3YiKQpmbGlnaHRzICU+JSBmaWx0ZXIob3JpZ2luID09ICJFV1IiKSAlPiUKICB3cml0ZV9jc3YoImZsaWdodHNfRVdSLmNzdiIpCmZsaWdodHMgJT4lIGZpbHRlcihvcmlnaW4gPT0gIkpGSyIpICU+JQogIHdyaXRlX2NzdigiZmxpZ2h0c19KRksuY3N2IikKYGBgCgpgYGB7cn0KbXlmaWxlcyA8LSBsaXN0LmZpbGVzKHBhdHRlcm4gPSAiLmNzdiIpCm15ZmlsZXMKYGBgCgpgYGB7cn0KbXlkYXRhIDwtIG15ZmlsZXMgJT4lIG1hcF9kZihyZWFkX2NzdikKCmRpbShmbGlnaHRzKQpkaW0obXlkYXRhKQpgYGAKCmBgYHtyfQpmaWxlX2RlbGV0ZShnbHVlKCJ7bXlmaWxlc30iKSkKYGBgCgoKIyMgRGF5IDIgb2YgQ2hhcmxvdHRlJ3MgcHVycnIgdHV0b3JpYWwKClRyeSByZXJ1bigpLgoKYGBge3J9CnggPC0gMyAlPiUgcmVydW4ocm5vcm0oMTAwKSkgJT4lCiAgbWFwKG1lYW4pCngKYGBgCgpgYGB7cn0KeCA8LSAxMDAwICU+JSByZXJ1biggdGliYmxlKCB4ID0gcm5vcm0oMTAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJleHAoMTAwKSApIAogICAgICAgICAgICAgICAgICAgICApCgp4X2NvciA8LSB4ICU+JSBtYXBfZGYofiB0aWJibGUoeF9jb3IgPSBhcy52ZWN0b3IoY29yKC54KSlbMl0pICkgCgoKeF9jb3IgJT4lIGdncGxvdChhZXMoeCA9IHhfY29yKSkgKwogIGdlb21faGlzdG9ncmFtKCkKCnhfY29yIDwtIHhfY29yICU+JSBtdXRhdGUoIGluZGV4ID0gMTpsZW5ndGgoeF9jb3IpKQoKeF9jb3IgJT4lIGdncGxvdChhZXMoeCA9IGluZGV4LCB5ID0geF9jb3IpKSArCiAgZ2VvbV9saW5lKCkKCmBgYAoKCkJhY2sgdG8gdGhlIHR1dG9yaWFsLgoKVHJ5IHRvIHVzZSB0aGUgZ2FwX3NwbGl0IGRhdGFzZXQgdG8gbWFrZSBwbG90cyBmb3IgZWFjaCBjb3VudHJ5LgoKTWFrZSBhIGxpc3Qgb2YgZ2dwbG90cy4KClRyeSB3YWxrKCkuCgpgYGB7cn0KZGF0YShnYXBfc3BsaXQpCiMgVmlldyhnYXBfc3BsaXQpCgpjb3VudHJpZXMgPC0gbmFtZXMoZ2FwX3NwbGl0KQpjb3VudHJpZXMKCnBsb3RzIDwtIG1hcDIoIGdhcF9zcGxpdCwgY291bnRyaWVzLAogICAgICAgICAgICAgICAgICAgIH4gZ2dwbG90KC54LCBhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwKSkgCiAgICAgICAgICAgICAgICAgICAgICArIGdlb21fbGluZSgpCiAgICAgICAgICAgICAgICAgICAgICArIGxhYnModGl0bGUgPSAueSkpCgpwbG90cwoKZGlyX2NyZWF0ZSgicGxvdHMiKQoKd2FsazIoLnggPSBwbG90cywgLnkgPSBjb3VudHJpZXMsIAogIH4gZ2dzYXZlKGZpbGVuYW1lID0gZ2x1ZSgicGxvdHMvey55fS5wZGYiKSwgcGxvdCA9IC54KSkKICAKZmlsZV9kZWxldGUoZ2x1ZSgicGxvdHMve2NvdW50cmllc30ucGRmIikpCgp3YWxrMigueCA9IHBsb3RzLCAueSA9IGNvdW50cmllcywgCiAgfiBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoInBsb3RzLyIsLnksIi5wZGYiKSwgcGxvdCA9IC54KSkKICAKZmlsZS5yZW1vdmUocGFzdGUwKCJwbG90cy8iLGNvdW50cmllcywgIi5wZGYiKSkKYGBgCgojIENoYXB0ZXIgNSBmcm9tIG1kc3IKCiMjIFdoYXQgaXMgdGlkeSBkYXRhPwoKVGhlIGJhYnluYW1lcyBkYXRhIGhhcyBiZWVuIHVwZGF0ZWQuCgpgYGB7cn0KYmFieW5hbWVzICU+JSBncm91cF9ieShzZXgsIG5hbWUpICU+JQogIHN1bW1hcmlzZSggdG90YWxfYmlydGhzID0gc3VtKG4pKSAlPiUKICBhcnJhbmdlKGRlc2ModG90YWxfYmlydGhzKSkKYGBgCmBgYHtyfQpiYWJ5bmFtZXMgJT4lIGZpbHRlcihuYW1lID09ICJTdWUiKSAlPiUKICBncm91cF9ieShzZXgsIG5hbWUpICU+JQogIHN1bW1hcmlzZSggdG90YWwgPSBzdW0obikpIApgYGAKCmBgYHtyfQpiYWJ5bmFtZXMgJT4lIGZpbHRlcihuYW1lID09ICJSb2JpbiIpICU+JQogIGdyb3VwX2J5KHNleCwgbmFtZSkgJT4lCiAgc3VtbWFyaXNlKCB0b3RhbCA9IHN1bShuKSkgCmBgYAoKYGBge3J9CmJhYnluYW1lcyAlPiUgZmlsdGVyKG5hbWUgJWluJSBjKCJTdWUiLCAiUm9iaW4iLCAiTGVzbGllIikpICU+JQogIGdyb3VwX2J5KHNleCwgbmFtZSkgJT4lCiAgc3VtbWFyaXNlKCB0b3RhbCA9IHN1bShuKSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAic2V4IiwgdmFsdWVzX2Zyb20gPSAidG90YWwiKQpgYGAKCmBgYHtyfQpCYWJ5V2lkZSA8LSBiYWJ5bmFtZXMgJT4lIAogIGdyb3VwX2J5KHNleCwgbmFtZSkgJT4lCiAgc3VtbWFyaXNlKCB0b3RhbCA9IHN1bShuKSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZXgsIHZhbHVlc19mcm9tID0gdG90YWwpCgpoZWFkKEJhYnlXaWRlLCAxMCkKYGBgCgpgYGB7cn0KQmFieVdpZGUgJT4lIGZpbHRlciggTSA+IDUwMDAwLCBGID4gNTAwMDApICU+JQogIG11dGF0ZSggcmF0aW8gPSBwbWluKE0vRiwgRi9NKSApICU+JQogIGFycmFuZ2UoZGVzYyhyYXRpbykpCmBgYAoKIyMgQXV0b21hdGlvbiBhbmQgaXRlcmF0aW9uCgpUaGUgTGFobWFuIGRhdGEgaGFzIGJlZW4gdXBkYXRlZC4KCmBgYHtyfQpuYW1lcyhUZWFtcykKYGBgCgojIyBSZXBsYWNlIHRoZSBhcHBseSgpIGZ1bmN0aW9uIHdpdGggbWFwKCkKCmBgYHtyfQpUZWFtcyAlPiUgc2VsZWN0KDE1OjQwKSAlPiUKICBtYXBfZGYobWVhbiwgbmEucm0gPSBUUlVFKSAKYGBgCgpgYGB7cn0KYW5nbGVzIDwtIFRlYW1zICU+JSBmaWx0ZXIoZnJhbmNoSUQgPT0gIkFOQSIpICU+JQogIGdyb3VwX2J5KHRlYW1JRCwgbmFtZSkgJT4lCiAgc3VtbWFyaXplKCBiZWdhbiA9IGZpcnN0KHllYXJJRCksIGVuZCA9IGxhc3QoeWVhcklEKSApICU+JQogIGFycmFuZ2UoYmVnYW4pCgphbmdsZXMKYGBgCgpgYGB7cn0KYW5nbGVzX25hbWVzIDwtIGFuZ2xlcyRuYW1lCgphbmdsZXNfbmFtZXMKYGBgCmBgYHtyfQpuY2hhcihhbmdsZXNfbmFtZXNbMV0pCm5jaGFyKGFuZ2xlc19uYW1lc1syXSkKbmNoYXIoYW5nbGVzX25hbWVzWzNdKQpuY2hhcihhbmdsZXNfbmFtZXNbNF0pCmBgYAoKYGBge3J9CmFuZ2xlc19uYW1lcyAlPiUgbWFwKG5jaGFyKQpgYGAKCmBgYHtyfQp0b3A1IDwtIGZ1bmN0aW9uKHgsIHRlYW1uYW1lKXsKICB4ICU+JQogICAgZmlsdGVyKG5hbWUgPT0gdGVhbW5hbWUpICU+JQogICAgc2VsZWN0KHRlYW1JRCwgeWVhcklELCAgIFcsIEwsIG5hbWUpICU+JQogICAgYXJyYW5nZShkZXNjKFcpKSAlPiUKICAgIGhlYWQoNSkKfQpgYGAKCmBgYHtyfQp0b3A1KFRlYW1zLCBhbmdsZXNfbmFtZXNbMV0pCnRvcDUoVGVhbXMsIGFuZ2xlc19uYW1lc1syXSkKdG9wNShUZWFtcywgYW5nbGVzX25hbWVzWzNdKQp0b3A1KFRlYW1zLCBhbmdsZXNfbmFtZXNbNF0pCmBgYAoKQ29tcGFyZSBsYXBwbHkgYW5kIG1hcAoKYGBge3J9CmFuZ2xlc19saXN0IDwtIGxhcHBseShhbmdsZXNfbmFtZXMsIEZVTiA9IHRvcDUsIHggPSBUZWFtcykKYW5nbGVzX2xpc3QKCmFuZ2xlc19saXN0MiA8LSBhbmdsZXNfbmFtZXMgJT4lIG1hcCh+IHRvcDUoVGVhbXMsIC54KSkKYW5nbGVzX2xpc3QyCmBgYAoKYGBge3J9CgphbmdsZXNfZGYyIDwtIGFuZ2xlc19uYW1lcyAlPiUgbWFwX2RmKH4gdG9wNShUZWFtcywgLngpKQphbmdsZXNfZGYyCgphbmdsZXNfZGYyICU+JSBncm91cF9ieSh0ZWFtSUQsIG5hbWUpICU+JQogIHN1bW1hcmlzZShOID0gbigpLCBtZWFuX3dpbnMgPSBtZWFuKFcpKSAlPiUKICBhcnJhbmdlKGRlc2MobWVhbl93aW5zKSkKCmBgYAoKIyMgSXRlcmF0aW9uIG92ZXIgc3ViZ3JvdXBzLCB1cGRhdGUgd2l0aCBtYXAKCmBgYHtyfQpleHBfd3BjdCA8LSBmdW5jdGlvbih4KXsKICByZXR1cm4oIDEgLyAoMSArICgxL3gpXjIpKQp9CmBgYAoKYGBge3J9ClRlYW1SdW5zIDwtIFRlYW1zICU+JQogIGZpbHRlcih5ZWFySUQgPj0gMTk1NCkgJT4lCiAgcmVuYW1lKFJTID0gUikgJT4lCiAgbXV0YXRlKFdQY3QgID0gVyAvIChXK0wpLCBydW5fcmF0aW8gPSBSUy9SQSkgJT4lCiAgc2VsZWN0KHllYXJJRCwgdGVhbUlELCBsZ0lELCBXUGN0LCBydW5fcmF0aW8pCmBgYAoKYGBge3J9ClRlYW1SdW5zICU+JSBnZ3Bsb3QoYWVzKHggPSBydW5fcmF0aW8sIHkgPSBXUGN0KSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEsIGNvbG9yID0iZGFya2dyZXkiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGNvbG9yID0gImRhcmtncmV5IiwgbGluZXR5cGUgPSAyICkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGV4cF93cGN0LCBzaXplID0gMiwgY29sb3IgPSAiYmx1ZSIpICsKICB4bGFiKCJSYXRpbyBvZiBSdW5zIFNjb3JlZCB0byBSdW5zIEFsbG93ZWQiKSArCiAgeWxhYigiV2lubmluZyBQZXJjZW50YWdlIikKYGBgCgpgYGB7cn0KbGlicmFyeShtb3NhaWMpCmV4V3BjdCA8LSBmaXRNb2RlbCggV1BjdCB+IDEvKDEgKyAoMS9ydW5fcmF0aW8pXmspLCBkYXRhID0gVGVhbVJ1bnMpCmNvZWYoZXhXcGN0KQpgYGAKYGBge3J9CmV4V3BjdCA8LSBubHMoIFdQY3QgfiAxLygxICsgKDEvcnVuX3JhdGlvKV5rKSwgZGF0YSA9IFRlYW1SdW5zKQpjb2VmKGV4V3BjdCkKYGBgCgpgYGB7cn0KZml0X2sgPC0gZnVuY3Rpb24oeCl7CiAgbW9kIDwtIG5scyggZm9ybXVsYSA9IFdQY3QgfiAxLygxICsgKDEvcnVuX3JhdGlvKV5rKSwgZGF0YSA9IHggKQogIHJldHVybihkYXRhLmZyYW1lKGsgPSBjb2VmKG1vZCkpKQp9CmBgYAoKYGBge3J9CmZpdF9rKFRlYW1SdW5zKQpgYGAKCmBgYHtyfQpUZWFtUnVuczIgPC0gVGVhbVJ1bnMgJT4lCiAgbXV0YXRlKGRlY2FkZSA9IHllYXJJRCAlLyUgMTAgKiAxMCkgClRlYW1SdW5zMgoKbl9UZWFtUnVuczIgPC0gVGVhbVJ1bnMyICU+JSBncm91cF9ieShkZWNhZGUpICU+JSAKICBuZXN0KCkKbl9UZWFtUnVuczIKCm1fVGVhbVJ1bnMyIDwtIG5fVGVhbVJ1bnMyICU+JSAKICBtdXRhdGUoZGVjYWRlLCBtb2RlbCA9IG1hcChkYXRhLCBmaXRfaykpCm1fVGVhbVJ1bnMyCgptX1RlYW1SdW5zMiAlPiUgbXV0YXRlKCBtb2RlbCA9IHVubGlzdChtb2RlbCkgKSAlPiUKICBzZWxlY3QoLWRhdGEpCmBgYAoKIyMgTWFrZSB0aGUgaG9tZXJ1biBsZWFkZXJzIHBsb3QgYnkgbGVhZ3VlCgpgYGB7cn0KaHJfbGVhZGVyIDwtIGZ1bmN0aW9uKHgpewogIHggJT4lCiAgICBzZWxlY3QodGVhbUlELCBIUikgJT4lCiAgICBhcnJhbmdlKGRlc2MoSFIpKSAlPiUKICAgIGhlYWQobj0xKQp9CgpgYGAKCmBgYHtyfQpUZWFtcyAlPiUKICBmaWx0ZXIoeWVhcklEID09IDE5NjEgJiBsZ0lEID09ICJBTCIpICU+JQogIGFycmFuZ2UoZGVzYyhIUikpICU+JQogIGhlYWQoMSkKYGBgCgpgYGB7cn0Kbl9UZWFtUnVuczIgPC0gVGVhbXMgJT4lIGdyb3VwX2J5KHllYXJJRCwgbGdJRCkgJT4lIAogIG5lc3QoKQoKbl9UZWFtUnVuczIKYGBgCgoKYGBge3J9CmhyX2xlYWRlcnMgPC0gbl9UZWFtUnVuczIgJT4lCiAgICBtdXRhdGUoeWVhcklELCBsZ0lELCBociA9IG1hcChkYXRhLCBocl9sZWFkZXIpICkgJT4lCiAgICBhcnJhbmdlKHllYXJJRCkgJT4lCiAgICBzZWxlY3QoLWRhdGEpICU+JQogICAgdW5uZXN0KGhyKQogICAgCmhlYWQoaHJfbGVhZGVycywgMTApCgpgYGAKCmBgYHtyfQpocl9sZWFkZXJzICU+JSBncm91cF9ieShsZ0lEKSAlPiUKICBzdW1tYXJpemUoaG9tZXJ1bl9tZWFuID0gbWVhbihIUikpCgptZWFuKEhSIH4gbGdJRCwgZGF0YSA9IGhyX2xlYWRlcnMpCmBgYAoKYGBge3J9CmhyX2xlYWRlcnMgJT4lIGZpbHRlcih5ZWFySUQgPj0gMTkxNikgJT4lCiAgZ3JvdXBfYnkobGdJRCkgJT4lCiAgc3VtbWFyaXplKGhvbWVydW5fbWVhbiA9IG1lYW4oSFIpKQoKbWVhbihIUiB+IGxnSUQsIGRhdGEgPSBocl9sZWFkZXJzKQpgYGAKCmBgYHtyfQpocl9sZWFkZXJzICU+JQogIGZpbHRlcih5ZWFySUQgPj0gMTkxNikgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhcklELCB5ID0gSFIsIGNvbG9yID0gbGdJRCkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChzZT0wKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTk3MykgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD0xOTc0LCB5PTI1LCBsYWJlbCA9ICJBTCBhZG9wdHMgREgiLCBoanVzdD0ibGVmdCIpCmBgYAoKIyMgSW50ZXJhdGlvbiBmb3IgdGhlIEJvb3RzdHJhcCB1c2luZyByZXJ1bgoKYGBge3J9Cm5fVGVhbVJ1bnMzIDwtIFRlYW1SdW5zICU+JQogIGdyb3VwX2J5KHllYXJJRCkgJT4lCiAgbmVzdCgpCm5fVGVhbVJ1bnMzIAoKa19hY3R1YWwgPC0gbl9UZWFtUnVuczMgJT4lICAgICAgICAgICAgICAgICAjIG1fVGVhbVJ1bnMzCiAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZpdF9rKSkKCmtfYWN0dWFsIDwtIGtfYWN0dWFsICU+JSBzZWxlY3QoLWRhdGEpICU+JQogIHVubmVzdChtb2RlbCkgCgprX2FjdHVhbCAlPiUgdW5ncm91cCgpICU+JQogIHN1bW1hcml6ZSgKICAgIG1pbiA9IG1pbihrKSwKICAgIG1lZGlhbiA9IG1lZGlhbihrKSwKICAgIG1heCA9IG1heChrKSwKICAgIG1lYW4gPSBtZWFuKGspLAogICAgc2QgPSBzZChrKSwKICAgIG4gPSBuKCkKICApCiAgCgoKCmBgYAoKYGBge3J9CmtfYWN0dWFsICU+JSBnZ3Bsb3QoYWVzKHggPSBrKSkgKwogIGdlb21fZGVuc2l0eSgpICsgCiAgeGxhYigiQmVzdCBmaXQgZXhwb25lbnQgZm9yIGEgc2luZ2xlIHNlYXNvbi4iKQpgYGAKCiMjIEJvb3RzdHJhcAoKVXNlIFttb2Rlcm5kaXZlXShodHRwczovL21vZGVybmRpdmUuY29tLzgtY29uZmlkZW5jZS1pbnRlcnZhbHMuaHRtbCkKCmBgYHtyfQpsaWJyYXJ5KG1vZGVybmRpdmUpCgp2aXJ0dWFsX3Jlc2FtcGxlcyA8LSBrX2FjdHVhbCAlPiUgCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA2NiwgcmVwbGFjZSA9IFRSVUUsIHJlcHMgPSAxMDAwKQoKdmlydHVhbF9yZXNhbXBsZWRfbWVhbnMgPC0gdmlydHVhbF9yZXNhbXBsZXMgJT4lIAogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lIAogIHN1bW1hcml6ZShtZWFuX2sgPSBtZWFuKGspKQoKdmlydHVhbF9yZXNhbXBsZWRfbWVhbnMgJT4lIGdncGxvdCggYWVzKHggPSBtZWFuX2spKSArCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAid2hpdGUiKSArCiAgbGFicyh4ID0gIlJlc2FtcGxlIG1lYW4gayIpCgp2aXJ0dWFsX3Jlc2FtcGxlZF9tZWFucyAlPiUgZ2dwbG90KCBhZXMoeCA9IG1lYW5faykpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgbGFicyh4ID0gIlJlc2FtcGxlIG1lYW4gayIpCmBgYAoKYGBge3J9CmxpYnJhcnkoaW5mZXIpCgpib290c3RyYXBfZGlzdHJpYnV0aW9uIDwtIGtfYWN0dWFsICU+JSAKICBzcGVjaWZ5KHJlc3BvbnNlID0gaykgJT4lIAogIGdlbmVyYXRlKHJlcHMgPSAxMDAwLCB0eXBlID0gImJvb3RzdHJhcCIpICU+JQogIGNhbGN1bGF0ZShzdGF0ID0gIm1lYW4iKSAKCmJvb3RzdHJhcF9kaXN0cmlidXRpb24gJT4lCiAgdmlzdWFsaXNlKCkKCnBlcmNlbnRpbGVfY2kgPC0gYm9vdHN0cmFwX2Rpc3RyaWJ1dGlvbiAlPiUgCiAgZ2V0X2NvbmZpZGVuY2VfaW50ZXJ2YWwobGV2ZWwgPSAwLjk1LCB0eXBlID0gInBlcmNlbnRpbGUiKQpwZXJjZW50aWxlX2NpCgpgYGAKClRvIGRvOiAgQWRkIGZ1cnRoZXIgQm9vc3RyYXBwaW5nIGNvZGUuCgpVc2UgW3RpZHltb2RlbHNdKGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnL2xlYXJuLykgdG8gaW1wbGVtZW50IHRoaXMuCgpGcm9tIHRoZSBwdXJyciBjaGVhdHNoZWV0LgoKYGBge3J9Cm5faXJpcyA8LSBpcmlzICU+JSBncm91cF9ieShTcGVjaWVzKSAlPiUgCiAgbmVzdCgpCm5faXJpcwpgYGAKCmBgYHtyfQptb2RfZnVuIDwtIGZ1bmN0aW9uKGRmKXsKICBsbShTZXBhbC5MZW5ndGggfiAuLCBkYXRhID0gZGYpCn0KYGBgCgpgYGB7cn0KbV9pcmlzIDwtIG5faXJpcyAlPiUgCiAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIG1vZF9mdW4pKQptX2lyaXMKYGBgCgpgYGB7cn0KYl9mdW4gPC0gZnVuY3Rpb24obW9kKXsKICBjb2VmZmljaWVudHMobW9kKVtbMV1dCn0KYGBgCgpgYGB7cn0KbV9pcmlzICU+JSB0cmFuc211dGUoIFNwZWNpZXMsIGJldGEgPSBtYXBfZGJsKG1vZGVsLCBiX2Z1bikgKQpgYGAKCgoKCgo=