Last update: 21 March, 2021

Please notice that this report refers to birth to three only

1 load package

pacman::p_load(tidyverse, janitor)

2 Data handling

3 get all ASQ4 data

read_excel_allsheets <- function(filename) {
    sheets <- readxl::excel_sheets(filename) #get all sheet names
    x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X))
    names(x) <- sheets #get names only
    x #return
}
#get the excel file
excel_list <- read_excel_allsheets("C:/Users/luisf/Dropbox/ASQ4_AEPS Data for Luis 2.2021/Luis Feb 2021/Final ALL ASQ4 for AEPS 2019_2020.xlsx")
#transform into vectors
list2env(setNames(excel_list, #list
                  paste0("ds_",janitor::make_clean_names(names(excel_list)))),  #fixing different names and other patterns
         envir=.GlobalEnv) #where?

4 Create a backup

backup_asq4_demo <- ds_asq4_demo 
backup_asq4_items <- ds_asq4_items

#remove all attributes

#ds_asq4_demo[] <- lapply(ds_asq4_demo, function(x) { attributes(x) <- NULL; x })
#backup_asq4_items[] <- lapply(backup_asq4_items, function(x) { attributes(x) <- NULL; x })

5 clean names

ds_asq4_demo <- clean_names(ds_asq4_demo)
ds_asq4_items <- clean_names(ds_asq4_items)

6 merge datasets

ds_asq <- left_join(ds_asq4_demo, ds_asq4_items, by = c("asq4_id"))

7 Compute totals

7.1 Change variable computational level

ds_asq <- ds_asq %>% 
  mutate_at(vars(c1:c6, gm1:gm6, fm1:fm6, ps1:ps6, p1:p6), ~as.numeric(.))

7.2 Compute totals for each ASQ-4 domain

ds_asq<-ds_asq %>% 
  mutate(com_sum = rowSums(select(.,c1:c6))) %>% 
  mutate(gm_sum = rowSums(select(.,gm1:gm6))) %>% 
  mutate(fm_sum = rowSums(select(.,fm1:fm6))) %>% 
  mutate(ps_sum = rowSums(select(.,ps1:ps6))) %>% 
  mutate(per_sum = rowSums(select(.,p1:p6)))

8 AEPS Demographics

aeps_1_demo <- readxl::read_excel("C:/Users/luisf/Dropbox/ASQ4_AEPS Data for Luis 2.2021/Luis Feb 2021/copy KM Cleaned AEPS 3.5.21/1.Cleaned AEPSBirthToThreeFormNoN 2 Grace 9.13.2019.xlsx", sheet = 2)
#fix names
aeps_1_demo <- clean_names(aeps_1_demo)
#remove empty columns and rows
aeps_1_demo <- remove_empty(aeps_1_demo, which = c("rows", "cols"), quiet = TRUE)
#remove useless rows
aeps_1_demo <- aeps_1_demo %>% filter(!is.na(childs_id))

#add new features to merge
aeps_1_demo <- aeps_1_demo %>% 
  mutate(aeps_file_number = 1) %>% #same as ds_asq
  mutate(spread_sheet = spread_sheet_id_1) %>% #same as full dataset!
  mutate(aeps_sprdsheet_id_number = spread_sheet_id_1) #same as ds_asq

9 1.AEPSBirthToThreeFormNoN 2 Grace 9.13.2019

10 First file 1 - Fine motor (1)

10.1 get data

aeps_1_fine <- readxl::read_excel("C:/Users/luisf/Dropbox/ASQ4_AEPS Data for Luis 2.2021/Luis Feb 2021/copy KM Cleaned AEPS 3.5.21/1.Cleaned AEPSBirthToThreeFormNoN 2 Grace 9.13.2019.xlsx", sheet = 3)
#clear excel attributes
aeps_1_fine[] <- lapply(aeps_1_fine, function(x) { attributes(x) <- NULL; x })
#fix names
aeps_1_fine <- clean_names(aeps_1_fine)
#remove empty columns and rows
aeps_1_fine <- remove_empty(aeps_1_fine, which = c("rows", "cols"), quiet = TRUE)
#create a skill
aeps_1_fine <- aeps_1_fine %>% 
  mutate(skill = .[[1]]) %>% #this is the first column )in this case -- fine motor (sub)domain
  select(1,skill, everything())
#With this new variable (skill), just keep if is a letter
aeps_1_fine <- aeps_1_fine %>% 
  mutate(skill = if_else(str_detect(skill, "[a-z]"), .[[1]],  NA_character_)) %>% #if skill is a letter, otherwise missing
  fill(skill)
#remove first line (it's almost all na)
aeps_1_fine <- aeps_1_fine %>% 
  filter(!str_detect(.[[1]], "[a-z]"))
#transform to numeric
aeps_1_fine <- aeps_1_fine %>% mutate(!! names(.)[1] := as.numeric(!! rlang::sym(names(.)[1]))) 
#aeps_1_fine[[1]] <- as.numeric(aeps_1_fine[[1]])

10.2 Tranform it to long format

spec <- tibble(`.name` = names(aeps_1_fine)) %>%
  slice(-c(1:2)) %>%
  mutate(`.value` = case_when(
           `.name` %>% str_detect("spread") ~ "spread_sheet",
           `.name` %>% str_detect("score")  ~ "score",
           `.name` %>% str_detect("confirm") ~ "confirm")
  )
# apply the spec
aeps_1_fine_long <- aeps_1_fine %>%
  pivot_longer_spec(spec)

10.3 Add domain

This chunk will get all domains and number skills. It will be useful to merge all ds in the future.

aeps_1_fine_long <- aeps_1_fine_long %>% 
  mutate(domain = names(.)[[1]]) %>% 
  rename(number_skill = names(.)[[1]]) %>% 
  select(domain, number_skill, everything())

11 First file 1 - Gross motor (2)

11.1 get data

aeps_1_gross <- readxl::read_excel("C:/Users/luisf/Dropbox/ASQ4_AEPS Data for Luis 2.2021/Luis Feb 2021/copy KM Cleaned AEPS 3.5.21/1.Cleaned AEPSBirthToThreeFormNoN 2 Grace 9.13.2019.xlsx", sheet = 4)
#clear excel attributes
aeps_1_gross[] <- lapply(aeps_1_gross, function(x) { attributes(x) <- NULL; x })
#fix names
aeps_1_gross <- clean_names(aeps_1_gross)
#remove empty columns and rows
aeps_1_gross <- remove_empty(aeps_1_gross, which = c("rows", "cols"), quiet = TRUE)
#create a skill
aeps_1_gross <- aeps_1_gross %>% 
  mutate(skill = .[[1]]) %>% #this is the first column )in this case -- fine motor (sub)domain
  select(1,skill, everything())
#With this new variable (skill), just keep if is a letter
aeps_1_gross <- aeps_1_gross %>% 
  mutate(skill = if_else(str_detect(skill, "[a-z]"), .[[1]],  NA_character_)) %>% #if skill is a letter, otherwise missing
  fill(skill)
#remove first line (it's almost all na)
aeps_1_gross <- aeps_1_gross %>% 
  filter(!str_detect(.[[1]], "[a-z]"))
#transform to numeric
aeps_1_gross <- aeps_1_gross %>% mutate(!! names(.)[1] := as.numeric(!! rlang::sym(names(.)[1]))) 
#aeps_1_gross[[1]] <- as.numeric(aeps_1_gross[[1]])

11.2 Tranform it to long format

spec <- tibble(`.name` = names(aeps_1_gross)) %>%
  slice(-c(1:2)) %>%
  mutate(`.value` = case_when(
    `.name` %>% str_detect("spread") ~ "spread_sheet",
    `.name` %>% str_detect("score")  ~ "score",
    `.name` %>% str_detect("confirm") ~ "confirm")
  )
# apply the spec
aeps_1_gross_long <- aeps_1_gross %>%
  pivot_longer_spec(spec)

11.3 Add domain

This chunk will get all domains and number skills. It will be useful to merge all ds in the future.

aeps_1_gross_long <- aeps_1_gross_long %>% 
  mutate(domain = names(.)[[1]]) %>% 
  rename(number_skill = names(.)[[1]]) %>% 
  select(domain, number_skill, everything())

12 First file 1 - Adaptive (3)

12.1 get data

aeps_1_adaptive <- readxl::read_excel("C:/Users/luisf/Dropbox/ASQ4_AEPS Data for Luis 2.2021/Luis Feb 2021/copy KM Cleaned AEPS 3.5.21/1.Cleaned AEPSBirthToThreeFormNoN 2 Grace 9.13.2019.xlsx", sheet = 5)
#clear excel attributes
aeps_1_adaptive[] <- lapply(aeps_1_adaptive, function(x) { attributes(x) <- NULL; x })
#fix names
aeps_1_adaptive <- clean_names(aeps_1_adaptive)
#remove empty columns and rows
aeps_1_adaptive <- remove_empty(aeps_1_adaptive, which = c("rows", "cols"), quiet = TRUE)
#create a skill
aeps_1_adaptive <- aeps_1_adaptive %>% 
  mutate(skill = .[[1]]) %>% #this is the first column )in this case -- fine motor (sub)domain
  select(1,skill, everything())
#With this new variable (skill), just keep if is a letter
aeps_1_adaptive <- aeps_1_adaptive %>% 
  mutate(skill = if_else(str_detect(skill, "[a-z]"), .[[1]],  NA_character_)) %>% #if skill is a letter, otherwise missing
  fill(skill)
#remove first line (it's almost all na)
aeps_1_adaptive <- aeps_1_adaptive %>% 
  filter(!str_detect(.[[1]], "[a-z]"))
#transform to numeric
aeps_1_adaptive <- aeps_1_adaptive %>% mutate(!! names(.)[1] := as.numeric(!! rlang::sym(names(.)[1]))) 
#aeps_1_adaptive[[1]] <- as.numeric(aeps_1_adaptive[[1]])

12.2 Tranform it to long format

spec <- tibble(`.name` = names(aeps_1_adaptive)) %>%
  slice(-c(1:2)) %>%
  mutate(`.value` = case_when(
    `.name` %>% str_detect("spread") ~ "spread_sheet",
    `.name` %>% str_detect("score")  ~ "score",
    `.name` %>% str_detect("confirm") ~ "confirm")
  )
# apply the spec
aeps_1_adaptive_long <- aeps_1_adaptive %>%
  pivot_longer_spec(spec)

12.3 Add domain

This chunk will get all domains and number skills. It will be useful to merge all ds in the future.

aeps_1_adaptive_long <- aeps_1_adaptive_long %>% 
  mutate(domain = names(.)[[1]]) %>% 
  rename(number_skill = names(.)[[1]]) %>% 
  select(domain, number_skill, everything())

13 Merge AEPS spreadsheets datasets

Just checking if we have 22 children in each dataset

aeps_1_fine_long %>% count(spread_sheet)
aeps_1_gross_long %>% count(spread_sheet)
aeps_1_adaptive_long %>% count(spread_sheet)

I’ll use bind_rows to put each dataset on top of the another. First, fine long with gross long

ds_aeps_birth_three <- bind_rows(
  aeps_1_fine_long,
  aeps_1_gross_long)

Now, this resultant ds with adaptive

ds_aeps_birth_three <- bind_rows(
  ds_aeps_birth_three,
  aeps_1_adaptive_long)

14 Merge AEPS with AEPS demographics

I’ll get aeps_1_demo to add to this partially full dataset the child’ ID

ds_aeps_birth_three <- left_join(ds_aeps_birth_three, aeps_1_demo)

First, i’ll add two key variables present in the ASQ-4 dataset to guarantee the merging will be correct

ds_aeps_birth_three <- ds_aeps_birth_three %>% 
  mutate(aeps_file_number = 1) %>% 
  mutate(aeps_sprdsheet_id_number = spread_sheet) %>% 
  rename(asq4_id = childs_id) %>% 
  mutate(asq4_id = as.numeric(asq4_id)) #in ASQ4 dataset, this variable is numeric

Create a full dataset with ASQ-4 and AEPS

ds_birth_three_aeps_asq <- left_join(
  ds_aeps_birth_three,
  ds_asq,
  by = "asq4_id"
)

14.1 Manual check

ds_birth_three_aeps_asq %>% 
  filter(spread_sheet == "15") %>% View()

14.2 Save as excel

write.csv(ds_birth_three_aeps_asq, file = "ds_birth_three_aeps_asq.csv", row.names = F)

It seems everything worked! (Saturday, 13 March, 2021)
Ask Kimberly

LS0tDQp0aXRsZTogIkRpYW5lIC0gQUVQUyAmIEFTUS00IERBVEEgUFJPQ0VTU0lORyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge3IgZ2xvYmFsIG9wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlcyA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gImhpZGUiKQ0KYGBgDQoNCg0KTGFzdCB1cGRhdGU6ICBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYA0KDQoqKlBsZWFzZSBub3RpY2UgdGhhdCB0aGlzIHJlcG9ydCByZWZlcnMgdG8gYmlydGggdG8gdGhyZWUgb25seSoqICAgIA0KDQojIGxvYWQgcGFja2FnZQ0KYGBge3J9DQpwYWNtYW46OnBfbG9hZCh0aWR5dmVyc2UsIGphbml0b3IpDQpgYGANCg0KIyBEYXRhIGhhbmRsaW5nDQoNCiMgZ2V0IGFsbCBBU1E0IGRhdGENCg0KYGBge3J9DQpyZWFkX2V4Y2VsX2FsbHNoZWV0cyA8LSBmdW5jdGlvbihmaWxlbmFtZSkgew0KICAgIHNoZWV0cyA8LSByZWFkeGw6OmV4Y2VsX3NoZWV0cyhmaWxlbmFtZSkgI2dldCBhbGwgc2hlZXQgbmFtZXMNCiAgICB4IDwtIGxhcHBseShzaGVldHMsIGZ1bmN0aW9uKFgpIHJlYWR4bDo6cmVhZF9leGNlbChmaWxlbmFtZSwgc2hlZXQgPSBYKSkNCiAgICBuYW1lcyh4KSA8LSBzaGVldHMgI2dldCBuYW1lcyBvbmx5DQogICAgeCAjcmV0dXJuDQp9DQojZ2V0IHRoZSBleGNlbCBmaWxlDQpleGNlbF9saXN0IDwtIHJlYWRfZXhjZWxfYWxsc2hlZXRzKCJDOi9Vc2Vycy9sdWlzZi9Ecm9wYm94L0FTUTRfQUVQUyBEYXRhIGZvciBMdWlzIDIuMjAyMS9MdWlzIEZlYiAyMDIxL0ZpbmFsIEFMTCBBU1E0IGZvciBBRVBTIDIwMTlfMjAyMC54bHN4IikNCiN0cmFuc2Zvcm0gaW50byB2ZWN0b3JzDQpsaXN0MmVudihzZXROYW1lcyhleGNlbF9saXN0LCAjbGlzdA0KICAgICAgICAgICAgICAgICAgcGFzdGUwKCJkc18iLGphbml0b3I6Om1ha2VfY2xlYW5fbmFtZXMobmFtZXMoZXhjZWxfbGlzdCkpKSksICAjZml4aW5nIGRpZmZlcmVudCBuYW1lcyBhbmQgb3RoZXIgcGF0dGVybnMNCiAgICAgICAgIGVudmlyPS5HbG9iYWxFbnYpICN3aGVyZT8NCmBgYA0KDQojIENyZWF0ZSBhIGJhY2t1cA0KDQpgYGB7cn0NCmJhY2t1cF9hc3E0X2RlbW8gPC0gZHNfYXNxNF9kZW1vIA0KYmFja3VwX2FzcTRfaXRlbXMgPC0gZHNfYXNxNF9pdGVtcw0KYGBgDQoNCg0KI3JlbW92ZSBhbGwgYXR0cmlidXRlcyANCg0KYGBge3J9DQojZHNfYXNxNF9kZW1vW10gPC0gbGFwcGx5KGRzX2FzcTRfZGVtbywgZnVuY3Rpb24oeCkgeyBhdHRyaWJ1dGVzKHgpIDwtIE5VTEw7IHggfSkNCiNiYWNrdXBfYXNxNF9pdGVtc1tdIDwtIGxhcHBseShiYWNrdXBfYXNxNF9pdGVtcywgZnVuY3Rpb24oeCkgeyBhdHRyaWJ1dGVzKHgpIDwtIE5VTEw7IHggfSkNCmBgYA0KDQojIGNsZWFuIG5hbWVzIA0KDQpgYGB7cn0NCmRzX2FzcTRfZGVtbyA8LSBjbGVhbl9uYW1lcyhkc19hc3E0X2RlbW8pDQpkc19hc3E0X2l0ZW1zIDwtIGNsZWFuX25hbWVzKGRzX2FzcTRfaXRlbXMpDQpgYGANCg0KIyBtZXJnZSBkYXRhc2V0cyANCg0KYGBge3J9DQpkc19hc3EgPC0gbGVmdF9qb2luKGRzX2FzcTRfZGVtbywgZHNfYXNxNF9pdGVtcywgYnkgPSBjKCJhc3E0X2lkIikpDQpgYGANCg0KIyBDb21wdXRlIHRvdGFscw0KDQojIyBDaGFuZ2UgdmFyaWFibGUgY29tcHV0YXRpb25hbCBsZXZlbCANCg0KYGBge3J9DQpkc19hc3EgPC0gZHNfYXNxICU+JSANCiAgbXV0YXRlX2F0KHZhcnMoYzE6YzYsIGdtMTpnbTYsIGZtMTpmbTYsIHBzMTpwczYsIHAxOnA2KSwgfmFzLm51bWVyaWMoLikpDQpgYGANCg0KIyMgQ29tcHV0ZSB0b3RhbHMgZm9yIGVhY2ggQVNRLTQgZG9tYWluDQpgYGB7cn0NCmRzX2FzcTwtZHNfYXNxICU+JSANCiAgbXV0YXRlKGNvbV9zdW0gPSByb3dTdW1zKHNlbGVjdCguLGMxOmM2KSkpICU+JSANCiAgbXV0YXRlKGdtX3N1bSA9IHJvd1N1bXMoc2VsZWN0KC4sZ20xOmdtNikpKSAlPiUgDQogIG11dGF0ZShmbV9zdW0gPSByb3dTdW1zKHNlbGVjdCguLGZtMTpmbTYpKSkgJT4lIA0KICBtdXRhdGUocHNfc3VtID0gcm93U3VtcyhzZWxlY3QoLixwczE6cHM2KSkpICU+JSANCiAgbXV0YXRlKHBlcl9zdW0gPSByb3dTdW1zKHNlbGVjdCguLHAxOnA2KSkpDQpgYGANCg0KDQojIEFFUFMgRGVtb2dyYXBoaWNzDQoNCmBgYHtyfQ0KYWVwc18xX2RlbW8gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCJDOi9Vc2Vycy9sdWlzZi9Ecm9wYm94L0FTUTRfQUVQUyBEYXRhIGZvciBMdWlzIDIuMjAyMS9MdWlzIEZlYiAyMDIxL2NvcHkgS00gQ2xlYW5lZCBBRVBTIDMuNS4yMS8xLkNsZWFuZWQgQUVQU0JpcnRoVG9UaHJlZUZvcm1Ob04gMiBHcmFjZSA5LjEzLjIwMTkueGxzeCIsIHNoZWV0ID0gMikNCmBgYA0KDQpgYGB7cn0NCiNmaXggbmFtZXMNCmFlcHNfMV9kZW1vIDwtIGNsZWFuX25hbWVzKGFlcHNfMV9kZW1vKQ0KI3JlbW92ZSBlbXB0eSBjb2x1bW5zIGFuZCByb3dzDQphZXBzXzFfZGVtbyA8LSByZW1vdmVfZW1wdHkoYWVwc18xX2RlbW8sIHdoaWNoID0gYygicm93cyIsICJjb2xzIiksIHF1aWV0ID0gVFJVRSkNCiNyZW1vdmUgdXNlbGVzcyByb3dzDQphZXBzXzFfZGVtbyA8LSBhZXBzXzFfZGVtbyAlPiUgZmlsdGVyKCFpcy5uYShjaGlsZHNfaWQpKQ0KDQojYWRkIG5ldyBmZWF0dXJlcyB0byBtZXJnZQ0KYWVwc18xX2RlbW8gPC0gYWVwc18xX2RlbW8gJT4lIA0KICBtdXRhdGUoYWVwc19maWxlX251bWJlciA9IDEpICU+JSAjc2FtZSBhcyBkc19hc3ENCiAgbXV0YXRlKHNwcmVhZF9zaGVldCA9IHNwcmVhZF9zaGVldF9pZF8xKSAlPiUgI3NhbWUgYXMgZnVsbCBkYXRhc2V0IQ0KICBtdXRhdGUoYWVwc19zcHJkc2hlZXRfaWRfbnVtYmVyID0gc3ByZWFkX3NoZWV0X2lkXzEpICNzYW1lIGFzIGRzX2FzcQ0KYGBgDQoNCg0KDQojIDEuQUVQU0JpcnRoVG9UaHJlZUZvcm1Ob04gMiBHcmFjZSA5LjEzLjIwMTkNCg0KIyBGaXJzdCBmaWxlIDEgLSBGaW5lIG1vdG9yICgxKQ0KDQojIyBnZXQgZGF0YQ0KDQpgYGB7cn0NCmFlcHNfMV9maW5lIDwtIHJlYWR4bDo6cmVhZF9leGNlbCgiQzovVXNlcnMvbHVpc2YvRHJvcGJveC9BU1E0X0FFUFMgRGF0YSBmb3IgTHVpcyAyLjIwMjEvTHVpcyBGZWIgMjAyMS9jb3B5IEtNIENsZWFuZWQgQUVQUyAzLjUuMjEvMS5DbGVhbmVkIEFFUFNCaXJ0aFRvVGhyZWVGb3JtTm9OIDIgR3JhY2UgOS4xMy4yMDE5Lnhsc3giLCBzaGVldCA9IDMpDQpgYGANCmBgYHtyfQ0KI2NsZWFyIGV4Y2VsIGF0dHJpYnV0ZXMNCmFlcHNfMV9maW5lW10gPC0gbGFwcGx5KGFlcHNfMV9maW5lLCBmdW5jdGlvbih4KSB7IGF0dHJpYnV0ZXMoeCkgPC0gTlVMTDsgeCB9KQ0KI2ZpeCBuYW1lcw0KYWVwc18xX2ZpbmUgPC0gY2xlYW5fbmFtZXMoYWVwc18xX2ZpbmUpDQojcmVtb3ZlIGVtcHR5IGNvbHVtbnMgYW5kIHJvd3MNCmFlcHNfMV9maW5lIDwtIHJlbW92ZV9lbXB0eShhZXBzXzFfZmluZSwgd2hpY2ggPSBjKCJyb3dzIiwgImNvbHMiKSwgcXVpZXQgPSBUUlVFKQ0KI2NyZWF0ZSBhIHNraWxsDQphZXBzXzFfZmluZSA8LSBhZXBzXzFfZmluZSAlPiUgDQogIG11dGF0ZShza2lsbCA9IC5bWzFdXSkgJT4lICN0aGlzIGlzIHRoZSBmaXJzdCBjb2x1bW4gKWluIHRoaXMgY2FzZSAtLSBmaW5lIG1vdG9yIChzdWIpZG9tYWluDQogIHNlbGVjdCgxLHNraWxsLCBldmVyeXRoaW5nKCkpDQojV2l0aCB0aGlzIG5ldyB2YXJpYWJsZSAoc2tpbGwpLCBqdXN0IGtlZXAgaWYgaXMgYSBsZXR0ZXINCmFlcHNfMV9maW5lIDwtIGFlcHNfMV9maW5lICU+JSANCiAgbXV0YXRlKHNraWxsID0gaWZfZWxzZShzdHJfZGV0ZWN0KHNraWxsLCAiW2Etel0iKSwgLltbMV1dLCAgTkFfY2hhcmFjdGVyXykpICU+JSAjaWYgc2tpbGwgaXMgYSBsZXR0ZXIsIG90aGVyd2lzZSBtaXNzaW5nDQogIGZpbGwoc2tpbGwpDQojcmVtb3ZlIGZpcnN0IGxpbmUgKGl0J3MgYWxtb3N0IGFsbCBuYSkNCmFlcHNfMV9maW5lIDwtIGFlcHNfMV9maW5lICU+JSANCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KC5bWzFdXSwgIlthLXpdIikpDQojdHJhbnNmb3JtIHRvIG51bWVyaWMNCmFlcHNfMV9maW5lIDwtIGFlcHNfMV9maW5lICU+JSBtdXRhdGUoISEgbmFtZXMoLilbMV0gOj0gYXMubnVtZXJpYyghISBybGFuZzo6c3ltKG5hbWVzKC4pWzFdKSkpIA0KI2FlcHNfMV9maW5lW1sxXV0gPC0gYXMubnVtZXJpYyhhZXBzXzFfZmluZVtbMV1dKQ0KYGBgDQoNCg0KIyMgVHJhbmZvcm0gaXQgdG8gbG9uZyBmb3JtYXQNCg0KYGBge3J9DQpzcGVjIDwtIHRpYmJsZShgLm5hbWVgID0gbmFtZXMoYWVwc18xX2ZpbmUpKSAlPiUNCiAgc2xpY2UoLWMoMToyKSkgJT4lDQogIG11dGF0ZShgLnZhbHVlYCA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgic3ByZWFkIikgfiAic3ByZWFkX3NoZWV0IiwNCiAgICAgICAgICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgic2NvcmUiKSAgfiAic2NvcmUiLA0KICAgICAgICAgICBgLm5hbWVgICU+JSBzdHJfZGV0ZWN0KCJjb25maXJtIikgfiAiY29uZmlybSIpDQogICkNCiMgYXBwbHkgdGhlIHNwZWMNCmFlcHNfMV9maW5lX2xvbmcgPC0gYWVwc18xX2ZpbmUgJT4lDQogIHBpdm90X2xvbmdlcl9zcGVjKHNwZWMpDQpgYGANCg0KIyMgQWRkIGRvbWFpbg0KDQpUaGlzIGNodW5rIHdpbGwgZ2V0IGFsbCBkb21haW5zIGFuZCBudW1iZXIgc2tpbGxzLiBJdCB3aWxsIGJlIHVzZWZ1bCB0byBtZXJnZSBhbGwgZHMgaW4gdGhlIGZ1dHVyZS4gIA0KDQpgYGB7cn0NCmFlcHNfMV9maW5lX2xvbmcgPC0gYWVwc18xX2ZpbmVfbG9uZyAlPiUgDQogIG11dGF0ZShkb21haW4gPSBuYW1lcyguKVtbMV1dKSAlPiUgDQogIHJlbmFtZShudW1iZXJfc2tpbGwgPSBuYW1lcyguKVtbMV1dKSAlPiUgDQogIHNlbGVjdChkb21haW4sIG51bWJlcl9za2lsbCwgZXZlcnl0aGluZygpKQ0KYGBgDQoNCg0KIyBGaXJzdCBmaWxlIDEgLSBHcm9zcyBtb3RvciAoMikNCg0KIyMgZ2V0IGRhdGENCg0KYGBge3J9DQphZXBzXzFfZ3Jvc3MgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCJDOi9Vc2Vycy9sdWlzZi9Ecm9wYm94L0FTUTRfQUVQUyBEYXRhIGZvciBMdWlzIDIuMjAyMS9MdWlzIEZlYiAyMDIxL2NvcHkgS00gQ2xlYW5lZCBBRVBTIDMuNS4yMS8xLkNsZWFuZWQgQUVQU0JpcnRoVG9UaHJlZUZvcm1Ob04gMiBHcmFjZSA5LjEzLjIwMTkueGxzeCIsIHNoZWV0ID0gNCkNCmBgYA0KYGBge3J9DQojY2xlYXIgZXhjZWwgYXR0cmlidXRlcw0KYWVwc18xX2dyb3NzW10gPC0gbGFwcGx5KGFlcHNfMV9ncm9zcywgZnVuY3Rpb24oeCkgeyBhdHRyaWJ1dGVzKHgpIDwtIE5VTEw7IHggfSkNCiNmaXggbmFtZXMNCmFlcHNfMV9ncm9zcyA8LSBjbGVhbl9uYW1lcyhhZXBzXzFfZ3Jvc3MpDQojcmVtb3ZlIGVtcHR5IGNvbHVtbnMgYW5kIHJvd3MNCmFlcHNfMV9ncm9zcyA8LSByZW1vdmVfZW1wdHkoYWVwc18xX2dyb3NzLCB3aGljaCA9IGMoInJvd3MiLCAiY29scyIpLCBxdWlldCA9IFRSVUUpDQojY3JlYXRlIGEgc2tpbGwNCmFlcHNfMV9ncm9zcyA8LSBhZXBzXzFfZ3Jvc3MgJT4lIA0KICBtdXRhdGUoc2tpbGwgPSAuW1sxXV0pICU+JSAjdGhpcyBpcyB0aGUgZmlyc3QgY29sdW1uIClpbiB0aGlzIGNhc2UgLS0gZmluZSBtb3RvciAoc3ViKWRvbWFpbg0KICBzZWxlY3QoMSxza2lsbCwgZXZlcnl0aGluZygpKQ0KI1dpdGggdGhpcyBuZXcgdmFyaWFibGUgKHNraWxsKSwganVzdCBrZWVwIGlmIGlzIGEgbGV0dGVyDQphZXBzXzFfZ3Jvc3MgPC0gYWVwc18xX2dyb3NzICU+JSANCiAgbXV0YXRlKHNraWxsID0gaWZfZWxzZShzdHJfZGV0ZWN0KHNraWxsLCAiW2Etel0iKSwgLltbMV1dLCAgTkFfY2hhcmFjdGVyXykpICU+JSAjaWYgc2tpbGwgaXMgYSBsZXR0ZXIsIG90aGVyd2lzZSBtaXNzaW5nDQogIGZpbGwoc2tpbGwpDQojcmVtb3ZlIGZpcnN0IGxpbmUgKGl0J3MgYWxtb3N0IGFsbCBuYSkNCmFlcHNfMV9ncm9zcyA8LSBhZXBzXzFfZ3Jvc3MgJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QoLltbMV1dLCAiW2Etel0iKSkNCiN0cmFuc2Zvcm0gdG8gbnVtZXJpYw0KYWVwc18xX2dyb3NzIDwtIGFlcHNfMV9ncm9zcyAlPiUgbXV0YXRlKCEhIG5hbWVzKC4pWzFdIDo9IGFzLm51bWVyaWMoISEgcmxhbmc6OnN5bShuYW1lcyguKVsxXSkpKSANCiNhZXBzXzFfZ3Jvc3NbWzFdXSA8LSBhcy5udW1lcmljKGFlcHNfMV9ncm9zc1tbMV1dKQ0KYGBgDQoNCg0KIyMgVHJhbmZvcm0gaXQgdG8gbG9uZyBmb3JtYXQNCg0KYGBge3J9DQpzcGVjIDwtIHRpYmJsZShgLm5hbWVgID0gbmFtZXMoYWVwc18xX2dyb3NzKSkgJT4lDQogIHNsaWNlKC1jKDE6MikpICU+JQ0KICBtdXRhdGUoYC52YWx1ZWAgPSBjYXNlX3doZW4oDQogICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgic3ByZWFkIikgfiAic3ByZWFkX3NoZWV0IiwNCiAgICBgLm5hbWVgICU+JSBzdHJfZGV0ZWN0KCJzY29yZSIpICB+ICJzY29yZSIsDQogICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgiY29uZmlybSIpIH4gImNvbmZpcm0iKQ0KICApDQojIGFwcGx5IHRoZSBzcGVjDQphZXBzXzFfZ3Jvc3NfbG9uZyA8LSBhZXBzXzFfZ3Jvc3MgJT4lDQogIHBpdm90X2xvbmdlcl9zcGVjKHNwZWMpDQpgYGANCg0KIyMgQWRkIGRvbWFpbg0KDQpUaGlzIGNodW5rIHdpbGwgZ2V0IGFsbCBkb21haW5zIGFuZCBudW1iZXIgc2tpbGxzLiBJdCB3aWxsIGJlIHVzZWZ1bCB0byBtZXJnZSBhbGwgZHMgaW4gdGhlIGZ1dHVyZS4gIA0KDQpgYGB7cn0NCmFlcHNfMV9ncm9zc19sb25nIDwtIGFlcHNfMV9ncm9zc19sb25nICU+JSANCiAgbXV0YXRlKGRvbWFpbiA9IG5hbWVzKC4pW1sxXV0pICU+JSANCiAgcmVuYW1lKG51bWJlcl9za2lsbCA9IG5hbWVzKC4pW1sxXV0pICU+JSANCiAgc2VsZWN0KGRvbWFpbiwgbnVtYmVyX3NraWxsLCBldmVyeXRoaW5nKCkpDQpgYGANCg0KDQoNCiMgRmlyc3QgZmlsZSAxIC0gQWRhcHRpdmUgKDMpDQoNCiMjIGdldCBkYXRhDQoNCmBgYHtyfQ0KYWVwc18xX2FkYXB0aXZlIDwtIHJlYWR4bDo6cmVhZF9leGNlbCgiQzovVXNlcnMvbHVpc2YvRHJvcGJveC9BU1E0X0FFUFMgRGF0YSBmb3IgTHVpcyAyLjIwMjEvTHVpcyBGZWIgMjAyMS9jb3B5IEtNIENsZWFuZWQgQUVQUyAzLjUuMjEvMS5DbGVhbmVkIEFFUFNCaXJ0aFRvVGhyZWVGb3JtTm9OIDIgR3JhY2UgOS4xMy4yMDE5Lnhsc3giLCBzaGVldCA9IDUpDQpgYGANCg0KYGBge3J9DQojY2xlYXIgZXhjZWwgYXR0cmlidXRlcw0KYWVwc18xX2FkYXB0aXZlW10gPC0gbGFwcGx5KGFlcHNfMV9hZGFwdGl2ZSwgZnVuY3Rpb24oeCkgeyBhdHRyaWJ1dGVzKHgpIDwtIE5VTEw7IHggfSkNCiNmaXggbmFtZXMNCmFlcHNfMV9hZGFwdGl2ZSA8LSBjbGVhbl9uYW1lcyhhZXBzXzFfYWRhcHRpdmUpDQojcmVtb3ZlIGVtcHR5IGNvbHVtbnMgYW5kIHJvd3MNCmFlcHNfMV9hZGFwdGl2ZSA8LSByZW1vdmVfZW1wdHkoYWVwc18xX2FkYXB0aXZlLCB3aGljaCA9IGMoInJvd3MiLCAiY29scyIpLCBxdWlldCA9IFRSVUUpDQojY3JlYXRlIGEgc2tpbGwNCmFlcHNfMV9hZGFwdGl2ZSA8LSBhZXBzXzFfYWRhcHRpdmUgJT4lIA0KICBtdXRhdGUoc2tpbGwgPSAuW1sxXV0pICU+JSAjdGhpcyBpcyB0aGUgZmlyc3QgY29sdW1uIClpbiB0aGlzIGNhc2UgLS0gZmluZSBtb3RvciAoc3ViKWRvbWFpbg0KICBzZWxlY3QoMSxza2lsbCwgZXZlcnl0aGluZygpKQ0KI1dpdGggdGhpcyBuZXcgdmFyaWFibGUgKHNraWxsKSwganVzdCBrZWVwIGlmIGlzIGEgbGV0dGVyDQphZXBzXzFfYWRhcHRpdmUgPC0gYWVwc18xX2FkYXB0aXZlICU+JSANCiAgbXV0YXRlKHNraWxsID0gaWZfZWxzZShzdHJfZGV0ZWN0KHNraWxsLCAiW2Etel0iKSwgLltbMV1dLCAgTkFfY2hhcmFjdGVyXykpICU+JSAjaWYgc2tpbGwgaXMgYSBsZXR0ZXIsIG90aGVyd2lzZSBtaXNzaW5nDQogIGZpbGwoc2tpbGwpDQojcmVtb3ZlIGZpcnN0IGxpbmUgKGl0J3MgYWxtb3N0IGFsbCBuYSkNCmFlcHNfMV9hZGFwdGl2ZSA8LSBhZXBzXzFfYWRhcHRpdmUgJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QoLltbMV1dLCAiW2Etel0iKSkNCiN0cmFuc2Zvcm0gdG8gbnVtZXJpYw0KYWVwc18xX2FkYXB0aXZlIDwtIGFlcHNfMV9hZGFwdGl2ZSAlPiUgbXV0YXRlKCEhIG5hbWVzKC4pWzFdIDo9IGFzLm51bWVyaWMoISEgcmxhbmc6OnN5bShuYW1lcyguKVsxXSkpKSANCiNhZXBzXzFfYWRhcHRpdmVbWzFdXSA8LSBhcy5udW1lcmljKGFlcHNfMV9hZGFwdGl2ZVtbMV1dKQ0KYGBgDQoNCg0KIyMgVHJhbmZvcm0gaXQgdG8gbG9uZyBmb3JtYXQNCg0KYGBge3J9DQpzcGVjIDwtIHRpYmJsZShgLm5hbWVgID0gbmFtZXMoYWVwc18xX2FkYXB0aXZlKSkgJT4lDQogIHNsaWNlKC1jKDE6MikpICU+JQ0KICBtdXRhdGUoYC52YWx1ZWAgPSBjYXNlX3doZW4oDQogICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgic3ByZWFkIikgfiAic3ByZWFkX3NoZWV0IiwNCiAgICBgLm5hbWVgICU+JSBzdHJfZGV0ZWN0KCJzY29yZSIpICB+ICJzY29yZSIsDQogICAgYC5uYW1lYCAlPiUgc3RyX2RldGVjdCgiY29uZmlybSIpIH4gImNvbmZpcm0iKQ0KICApDQojIGFwcGx5IHRoZSBzcGVjDQphZXBzXzFfYWRhcHRpdmVfbG9uZyA8LSBhZXBzXzFfYWRhcHRpdmUgJT4lDQogIHBpdm90X2xvbmdlcl9zcGVjKHNwZWMpDQpgYGANCg0KIyMgQWRkIGRvbWFpbg0KDQpUaGlzIGNodW5rIHdpbGwgZ2V0IGFsbCBkb21haW5zIGFuZCBudW1iZXIgc2tpbGxzLiBJdCB3aWxsIGJlIHVzZWZ1bCB0byBtZXJnZSBhbGwgZHMgaW4gdGhlIGZ1dHVyZS4gIA0KDQpgYGB7cn0NCmFlcHNfMV9hZGFwdGl2ZV9sb25nIDwtIGFlcHNfMV9hZGFwdGl2ZV9sb25nICU+JSANCiAgbXV0YXRlKGRvbWFpbiA9IG5hbWVzKC4pW1sxXV0pICU+JSANCiAgcmVuYW1lKG51bWJlcl9za2lsbCA9IG5hbWVzKC4pW1sxXV0pICU+JSANCiAgc2VsZWN0KGRvbWFpbiwgbnVtYmVyX3NraWxsLCBldmVyeXRoaW5nKCkpDQpgYGANCg0KDQojIE1lcmdlIEFFUFMgc3ByZWFkc2hlZXRzIGRhdGFzZXRzDQoNCkp1c3QgY2hlY2tpbmcgaWYgd2UgaGF2ZSAyMiBjaGlsZHJlbiBpbiBlYWNoIGRhdGFzZXQgIA0KYGBge3J9DQphZXBzXzFfZmluZV9sb25nICU+JSBjb3VudChzcHJlYWRfc2hlZXQpDQphZXBzXzFfZ3Jvc3NfbG9uZyAlPiUgY291bnQoc3ByZWFkX3NoZWV0KQ0KYWVwc18xX2FkYXB0aXZlX2xvbmcgJT4lIGNvdW50KHNwcmVhZF9zaGVldCkNCmBgYA0KDQpJJ2xsIHVzZSBgYmluZF9yb3dzYCB0byBwdXQgZWFjaCBkYXRhc2V0IG9uIHRvcCBvZiB0aGUgYW5vdGhlci4gDQpGaXJzdCwgZmluZSBsb25nIHdpdGggZ3Jvc3MgbG9uZw0KDQoNCmBgYHtyfQ0KZHNfYWVwc19iaXJ0aF90aHJlZSA8LSBiaW5kX3Jvd3MoDQogIGFlcHNfMV9maW5lX2xvbmcsDQogIGFlcHNfMV9ncm9zc19sb25nKQ0KYGBgDQoNCk5vdywgdGhpcyByZXN1bHRhbnQgZHMgd2l0aCBhZGFwdGl2ZQ0KDQpgYGB7cn0NCmRzX2FlcHNfYmlydGhfdGhyZWUgPC0gYmluZF9yb3dzKA0KICBkc19hZXBzX2JpcnRoX3RocmVlLA0KICBhZXBzXzFfYWRhcHRpdmVfbG9uZykNCmBgYA0KDQoNCiMgTWVyZ2UgQUVQUyB3aXRoIEFFUFMgZGVtb2dyYXBoaWNzDQoNCkknbGwgZ2V0IGBhZXBzXzFfZGVtb2AgdG8gYWRkIHRvIHRoaXMgcGFydGlhbGx5IGZ1bGwgZGF0YXNldCB0aGUgY2hpbGQnIElEDQoNCmBgYHtyfQ0KZHNfYWVwc19iaXJ0aF90aHJlZSA8LSBsZWZ0X2pvaW4oZHNfYWVwc19iaXJ0aF90aHJlZSwgYWVwc18xX2RlbW8pDQpgYGANCg0KRmlyc3QsIGknbGwgYWRkIHR3byBrZXkgdmFyaWFibGVzIHByZXNlbnQgaW4gdGhlIEFTUS00IGRhdGFzZXQgdG8gZ3VhcmFudGVlIHRoZSBtZXJnaW5nIHdpbGwgYmUgY29ycmVjdA0KDQpgYGB7cn0NCmRzX2FlcHNfYmlydGhfdGhyZWUgPC0gZHNfYWVwc19iaXJ0aF90aHJlZSAlPiUgDQogIG11dGF0ZShhZXBzX2ZpbGVfbnVtYmVyID0gMSkgJT4lIA0KICBtdXRhdGUoYWVwc19zcHJkc2hlZXRfaWRfbnVtYmVyID0gc3ByZWFkX3NoZWV0KSAlPiUgDQogIHJlbmFtZShhc3E0X2lkID0gY2hpbGRzX2lkKSAlPiUgDQogIG11dGF0ZShhc3E0X2lkID0gYXMubnVtZXJpYyhhc3E0X2lkKSkgI2luIEFTUTQgZGF0YXNldCwgdGhpcyB2YXJpYWJsZSBpcyBudW1lcmljDQpgYGANCg0KDQpDcmVhdGUgYSBmdWxsIGRhdGFzZXQgd2l0aCBBU1EtNCBhbmQgQUVQUw0KDQpgYGB7cn0NCmRzX2JpcnRoX3RocmVlX2FlcHNfYXNxIDwtIGxlZnRfam9pbigNCiAgZHNfYWVwc19iaXJ0aF90aHJlZSwNCiAgZHNfYXNxLA0KICBieSA9ICJhc3E0X2lkIg0KKQ0KYGBgDQoNCg0KIyMgTWFudWFsIGNoZWNrDQoNCmBgYHtyfQ0KZHNfYmlydGhfdGhyZWVfYWVwc19hc3EgJT4lIA0KICBmaWx0ZXIoc3ByZWFkX3NoZWV0ID09ICIxNSIpICU+JSBWaWV3KCkNCmBgYA0KDQoNCiMjIFNhdmUgYXMgZXhjZWwNCg0KYGBge3IsIGV2YWwgPSBGQUxTRSB9DQp3cml0ZS5jc3YoZHNfYmlydGhfdGhyZWVfYWVwc19hc3EsIGZpbGUgPSAiZHNfYmlydGhfdGhyZWVfYWVwc19hc3EuY3N2Iiwgcm93Lm5hbWVzID0gRikNCmBgYA0KDQoNCj4gSXQgc2VlbXMgZXZlcnl0aGluZyB3b3JrZWQhIChTYXR1cmRheSwgMTMgTWFyY2gsIDIwMjEpICANCkFzayBLaW1iZXJseQ0KDQo=