Data

Synthesized Pop

# Synthetic Population dataset 
syn_ind <- read.csv("data/01_synthetic_population_dataset/00_0831_final_synthesized_individual.csv")
syn_hh <- read.csv("data/01_synthetic_population_dataset/00_0831_final_synthesized_household.csv")

Census data

CB2000CBP All Sectors: County Business Patterns: https://data.census.gov/cedsci/table?q=business%20establishment&g=0500000US36013&tid=CBP2020.CB2000CBP

B08007 SEX OF WORKERS BY PLACE OF WORK–STATE AND COUNTY LEVEL https://data.census.gov/cedsci/table?q=employment&g=0500000US36013%241500000&tid=ACSDT5Y2020.B08007

S2301 EMPLOYMENT STATUS (Chautauqua county) https://data.census.gov/cedsci/table?q=S2301&g=0500000US36013&tid=ACSST5Y2020.S2301

S1401 SCHOOL ENROLLMENT https://data.census.gov/cedsci/table?t=School%20Enrollment&g=0500000US36013&tid=ACSST5Y2020.S1401

Educational Institution Dataset: https://edg.epa.gov/metadata/catalog/main/home.page

NY GIS Clearinghouse (Public Schools K-12): http://gis.ny.gov/gisdata/inventories/details.cfm?DSID=1326

# ----- Size of business of all sections in Chau 
css_business_size <- read.csv("data/ACSDT5Y2020_chau_block_group_cleaned/Chautauqua_county_business_patterns.csv")

# -----  Employment status at bg 
ttt <- read.csv("data/ACSDT5Y2020_chau_block_group_cleaned/Chautauqua_bg_ACSDT5Y2020.B08007-2022-08-31T160100.csv")
ttt <- cln_census_geoid_2(ttt, idx_cbgroup)
ttt$n_wok_out_county_state <- ttt$n_wok_out_county + ttt$n_wok_out_state
ttt$p_wok_in_county <- round(ttt$n_wok_in_county / ttt$ttl_wokers,4)
ttt$p_wok_out_county_state <- 1 - ttt$p_wok_in_county
css_emp_place <- ttt

# -----  Employment_ratio county
ttt <- read.csv("data/ACSDT5Y2020_chau_block_group_cleaned/Chautauqua_county_ACSST5Y2020.S2301-2022-08-31T180418.csv")
css_emp_ratio <- ttt

# -----  Public K-12 in NYS ---------
tt <- st_read("data/NY_Public_K_12/Public_K_12.shp")
tt <- tt[lengths(st_intersects(tt, c_county)) >0, ]
c_pub_k12 <- tt

# -----  School district ---------
tt <- st_read("data/NY_SchDist/SchDist_2019_v3.shp")
tt <- tt[lengths(st_intersects(tt, c_county))>0, ]
c_school_dist <- tt

# -----  School enrollment ---------
tt <- read.csv("data/ACSDT5Y2020_chau_block_group_cleaned/Chautauqua_county_ACSST5Y2020.S1401-2022-09-06T221723.csv")
css_age_school_enroll <- tt

Urban, Suburban, Rural cbgroup

## This cell can validate the rural/urban data collected from the Census
tt <- css_age_gender[c("GEOID","ttl")]
tt <- left_join(c_bgroup["GEOID"], tt)
tt$area <- st_area(tt)
tt$density <- tt$ttl * 1000**2/ as.numeric(tt$area) 
tt$cat <- cut(tt$density, breaks = c(0,100,10000),
              labels = c("rural","suburban"))
tm_shape(tt) + tm_polygons(col = "cat",alpha = 0.5) + 
  tm_shape(c_urban) + tm_polygons(alpha = 0.5, col="red")

Residence

Family Network

# Unique id of household with (n_member > 1) 
lt_hhid <- syn_hh %>% filter(hh_size > 1) %>% pull(hh_id) %>% unique()
# Network data container 
df_network <- data.frame(matrix(ncol = 4)) %>% setnames(c("Source","Target","Type","Relation"))

foo <- syn_ind   # individual 
tt <- syn_hh  # household 
nn <- df_network  # network 

for (i in 1:length(lt_hhid)) {
  # unique household id 
  hh_idx <- lt_hhid[[i]]
  
  # All individuals in a particular household 
  data <- foo %>% filter(hh_id == hh_idx)
  a <- combn(data$ind_id,2)
  data <- data.frame(Source = a[1,],
                     Target = a[2,],
                     Type = "Undirected",
                     Relation = "Family")
  nn <- rbind(nn, data)
}

# # Save network result 
# nn <- na.omit(nn)
# write.csv(nn,"data/02_network_dataset/XX_step1_family_network.csv", row.names = F)

Residence

Prepare a list of resident

# (Projected CRS: NAD83 / UTM zone 18N)
# datasets: c_county, c_bgroup (id:GEOID), c_parcel_r (id: PRINT_KEY)
# Join residential parcel with census block group 
df <- c_parcel_r %>% st_centroid()
df <- st_join(df, c_bgroup, join = st_intersects)
df <- df[!duplicated(df$PRINT_KEY),]
st_geometry(df) <- NULL

# Prepare a df of residential parcel
tt <- df[c("PRINT_KEY","GEOID","PROP_CLASS")]
# remove recreational use
tt <- tt[!tt$PROP_CLASS %in% c("200","242","260"), ]
# two_family * 2
a <- tt[tt$PROP_CLASS=="220",]
tt <- rbind(tt,a)
# three family * 3 
a <- tt[tt$PROP_CLASS=="230",]
a <- a[rep(seq_len(nrow(a)), each = 2),]
tt <- rbind(tt, a)
# rural residence
a <- tt[tt$PROP_CLASS %in% c ("240","241"),] # primary residential 
a <- a[rep(seq_len(nrow(a)), each = 2),]
tt <- rbind(tt, a)
# estate and residence 
a <- tt[tt$PROP_CLASS %in% c("250","280","281"),]
a <- a[rep(seq_len(nrow(a)), each = 29),]
tt <- rbind(tt, a)

rownames(tt) <- NULL
tt$prcl_idx <- sprintf("R_prcl_%06d",1:nrow(tt))
df_residence_list <- tt

Household Allocation

# set random seed 
set.seed(rm_seed)
# datasets 
foo <- syn_ind             # individual 
tt <- syn_hh               # household 
zz <- df_residence_list    # residential parcel list 

# Initialize 
tt$prcl_idx <- NA
zz$if_pop <- NA

for (i in 1:length(lt_geoid_bg)) {
  idx <- lt_geoid_bg[[i]]
  # GEOID: Individual Data
  data <- foo %>% filter(GEOID == idx)
  # GEOID: Household Data 
  df <- tt %>% filter(GEOID == idx)
  # GEOID: residential parcel data 
  zzz <- zz %>% filter(GEOID == idx)
  
  # Threshold
  n <- min(nrow(zzz),    # available residence 
           nrow(df))   # all household 
  
  for (j in 1:n) {
    # randomly select a household : id
    a <- df %>% filter(is.na(prcl_idx)) %>% slice_sample(n=1) %>% pull(hh_id)
    # randomly select a residential parcel : id
    b <- zzz %>% filter(is.na(if_pop)) %>% slice_sample(n=1) %>% pull(prcl_idx)
    
    df[df$hh_id == a,]$prcl_idx <- b
    zzz[zzz$prcl_idx == b, ]$if_pop <- a
  }
  
  # Save result 
  tt <- tt %>% left_join(df[c("hh_id","prcl_idx")], by = "hh_id") %>% 
    mutate(prcl_idx = coalesce(prcl_idx.x, prcl_idx.y)) %>% 
    select(-prcl_idx.x, -prcl_idx.y)
    
  zz <- zz %>% left_join(zzz[c("prcl_idx","if_pop")], by="prcl_idx") %>% 
    mutate(if_pop  = coalesce (if_pop.x, if_pop.y)) %>% 
    select(-if_pop.x, -if_pop.y)
  
  print(i)
}


# populate rest households 
# extract "household without parcel" and "parcel without household" 
lt_temp <- tt[is.na(tt$prcl_idx),]
zzz <- zz[is.na(zzz$if_pop),]
for (i in 1:nrow(lt_temp)) {
  a <- lt_temp %>% filter(is.na(prcl_idx)) %>% slice_sample(n=1) %>% pull(hh_id)
  b <- zzz %>% filter(is.na(if_pop)) %>% slice_sample(n=1) %>% pull(prcl_idx)
  
  lt_temp[lt_temp$hh_id == a, ]$prcl_idx <- b
  zzz[zzz$prcl_idx == b,]$if_pop <- a
}

# Fill original dataset
tt <- tt %>% left_join(lt_temp[c("hh_id","prcl_idx")], by = "hh_id") %>% 
  mutate(prcl_idx = coalesce(prcl_idx.x, prcl_idx.y)) %>%
  select(-prcl_idx.x, -prcl_idx.y)
zz <- zz %>% left_join(zzz[c("prcl_idx","if_pop")], by="prcl_idx") %>%
  mutate(if_pop  = coalesce (if_pop.x, if_pop.y)) %>%
  select(-if_pop.x, -if_pop.y)
zz <- zz[!is.na(zz$if_pop),] # only keep occupied parcel 
tt <- left_join(tt, zz[c("prcl_idx","PRINT_KEY","PROP_CLASS")])

# -----  Urban Residential Parcel ---------
lt_urban_parcel_r <- c_parcel_r[lengths(st_intersects(c_parcel_r, c_urban))>0,] %>% pull("PRINT_KEY")
tt$urban_rural <- "rural"
tt[tt$PRINT_KEY %in% lt_urban_parcel_r,]$urban_rural <- "urban"

# EXPORT 
# write.csv(tt, "data/01_synthetic_population_dataset/XX_1014_household_parcel_idx_urban_rural.csv", row.names = F)
# syn_hh_prcl <- tt


# # ----- Plot ---------
# aa <- left_join(tt, c_parcel_r["PRINT_KEY"]) %>% st_as_sf()
# tmap_mode("plot")
# pdf("plot/xx_Parcel_residential_urban_rural.pdf")
# tm_shape(aa) + tm_polygons(col = "urban_rural", border.alpha = 0)
# dev.off()

Group Quarter Allocation

# Prepare parcels for gq 
df <- c_parcel_gq
df <- st_join(df, c_bgroup, join = st_intersects)
df <- df[!duplicated(df$PRINT_KEY),]
# urban/rural group quarter 
df$urban_rural <- "rural"
df[lengths(st_intersects(df, c_urban))>0,]$urban_rural <- "urban"
st_geometry(df) <- NULL
df <- df[c("PRINT_KEY","GEOID","PROP_CLASS","urban_rural")]
df$prcl_idx <- sprintf("R_gq_%03d",1:nrow(df))
df_res_gq_list <- df

# Identify individuals in gq 
foo <- syn_ind   # individual 
foo <- foo[foo$hh_role=="in_gq",]
foo$hh_id_2 <- paste(foo$hh_id, foo$GEOID, sep="_")

lt_gq <- foo[c("GEOID","hh_id","hh_id_2")] %>% mutate(act_size = 1)
lt_gq <- aggregate(act_size~hh_id + GEOID + hh_id_2, data = lt_gq, FUN =sum)

# different gq types 
df <- df_res_gq_list
lt_temp <- lt_gq$hh_id %>% unique()
df[lt_temp] <- 0
df[df$PROP_CLASS %in% c("613","615"),]$gq_college_housing <- 1
df[df$PROP_CLASS %in% c("633"),c("gq_others_adult_over64","gq_others_adult")] <- 1
df[df$PROP_CLASS %in% c("641","642"),c("gq_others_adult_over64")] <- 1
df[df$PROP_CLASS %in% c("670"),c("gq_Juvenile_facility","gq_others_adult")] <- 1
df$filled <- 0

# Populate 
row.names(lt_gq) <- NULL
lt_gq[c("PRINT_KEY","prcl_idx")] <- NA
for (i in 1:nrow(lt_gq)) {
  idx <- lt_gq[i,]$GEOID
  a <- lt_gq[i,]$hh_id
  
  b <- df %>% filter(GEOID == idx) %>% filter(get(a) == 1)
  if(nrow(b)>0){
    b <- b %>% slice_sample(n=1)
    df[df$PRINT_KEY == b$PRINT_KEY,]$filled <- 1
    lt_gq[i, c("PRINT_KEY","prcl_idx")] <- b[c("PRINT_KEY","prcl_idx")]
  }
}

zz <- lt_gq[is.na(lt_gq$prcl_idx),]
zzz <- lt_gq[!is.na(lt_gq$prcl_idx),]

for (i in 1:nrow(zz)) {
  a <- zz[i,]$hh_id
  b <- df %>% filter(get(a) == 1 & filled==0)
  if(nrow(b) == 0){
    b <- df %>% filter(get(a) == 1)
  } 
  if(nrow(b) > 0){
    b <- b %>% slice_sample(n=1)
    df[df$PRINT_KEY == b$PRINT_KEY,]$filled <- 1
    zz[i, c("PRINT_KEY","prcl_idx")] <- b[c("PRINT_KEY","prcl_idx")]
  }

}

lt_gq <- rbind(zz,zzz)
lt_gq$type <- "in_gq"
lt_gq <- left_join(lt_gq, df_res_gq_list[c("prcl_idx","urban_rural","PROP_CLASS")])

setnames(lt_gq, old="act_size", new="hh_size")
rownames(lt_gq) <- NULL

Individual Urban/Rural

# # export
# syn_hh_gq_prcl <- rbind(syn_hh_prcl, lt_gq[c("GEOID","hh_id","type","hh_size","prcl_idx","PRINT_KEY","PROP_CLASS","urban_rural")])
# write.csv(syn_hh_gq_prcl, "data/01_synthetic_population_dataset/XX_1014_household_gq_parcel_idx_urban_rural.csv", row.names = F)

# Individual - urban/rural
tt <- left_join(syn_ind, syn_hh_gq_prcl[c("GEOID","hh_id","urban_rural")], by=c("GEOID"="GEOID","hh_id"="hh_id"))
# syn_ind_urb <- tt
# write.csv(syn_ind_urb, "data/01_synthetic_population_dataset/XX_1014_individual_urban_rural.csv", row.names = F)

Working Location

List of County Business

df <- data.frame(matrix(ncol = 5)) %>% setnames(new = c("business_id",
                                                        "Meaning_of_Employment_size_of_establishments_code",
                                                        "employment_size_min","employment_size_max","act_size"))
tt <- css_business_size[2:10, 4:7]
rownames(tt) <- NULL

for (i in 1:nrow(tt)) {
  a <- tt[i, 1:3]
  b <- tt[i,4]
  data <- a[rep(seq_len(nrow(a)), each = b),]
  data$business_id <- NA
  data$act_size <- 0
  df <- rbind(df, data)
}

df <- df[!is.na(df$Meaning_of_Employment_size_of_establishments_code),]
rownames(df) <- NULL
df$business_id <- sprintf("Org_%05d", 1:nrow(df))

df_business <- df

County Labor Force Pool

1: Who is employed based on employment-population ratio 2. Who is working in county

Labor Force Participation Rate includes the numbers of people with a job as well as the number actively looking for work. We used employment-population ratio

# set random seed 
set.seed(rm_seed)

# datasets 
foo <- syn_ind_urb   # individual 

# initialize 
foo$if_employed <- 0
foo$wok_place <- 0

# randomly select employeed individuals at COUNTY level
for (i in 1:nrow(css_emp_ratio)) {
  a <- css_emp_ratio[i,]
  data <- foo %>% filter(age >= a$age_lo & age <= a$age_hi)

  # calculate # of employeed
  m <- round(nrow(data) * a$Employment_Population_Ratio,0)
  b <- data %>% slice_sample(n = m) %>% pull(ind_id)    # random selection
  foo[foo$ind_id %in% b,]$if_employed <- 1
}

# decide their working place (in/out county) at block groups level
for (i in 1:length(lt_geoid_bg)) {
  idx <- lt_geoid_bg[[i]]
  m <- css_emp_place %>% filter(GEOID == idx)
  data <- foo %>% filter(GEOID == idx & if_employed == 1)
  
  # "in_county", "out_county_or_state"
  n <- round(nrow(data) * m$p_wok_in_county, 0)
  a <- data %>% slice_sample(n=n) %>% pull(ind_id)
  foo[foo$ind_id %in% data$ind_id, ]$wok_place <- "out_county_or_state"
  foo[foo$ind_id %in% a,]$wok_place <- "in_county"
  
  # CHECK
  # print(paste(idx, (m$ttl_wokers - nrow(data)) / m$ttl_wokers, sep = "-----------------------"))
}

# # EXPORT 
# write.csv(foo, "data/01_synthetic_population_dataset/XX_1014_individual_urban_rural_emp_place.csv", row.names = F)
# syn_ind_urb_emp <- foo

Allocation in-county Work business

# set random seed 
set.seed(rm_seed)

# datasets 
foo <- syn_ind_urb_emp   # individual 
zz <- data.frame(matrix(ncol = 5, nrow = 0)) %>% setnames(new = names(df_business))

# initialize & extract only individuals working "in_county"
foo$business_id <- NA
data <- foo %>% filter(wok_place == "in_county")

# create a list of positions 
for (i in 1:nrow(df_business)) {
  a <- df_business[i,]
  a <- a[rep(seq_len(nrow(a)), each = a$employment_size_max),]
  zz <- rbind(zz, a)
}

rownames(zz) <- NULL
zz$position_idx <- sprintf("P_%05d",1:nrow(zz))

# connect employees with positions
for (i in 1:nrow(data)) {
  # Randomly select an in-county worker & a company
  a <- data %>% filter(is.na(business_id)) %>% slice_sample(n=1) %>% pull(ind_id)
  b <- zz %>% filter(act_size < 1) %>% slice_sample(n=1)
  
  # assign value back 
  data[data$ind_id == a, ]$business_id <- b$business_id
  zz[zz$position_idx == b$position_idx,]$act_size <- 1
}

zzz <- zz[zz$act_size ==1,]
zzz <- table(zzz$business_id) %>% as.data.frame()
zzz <- left_join(df_business, zzz, by=c("business_id"="Var1")) %>% mutate(act_size = Freq) %>% select(-Freq)

foo <- left_join(syn_ind_urb_emp, data[c("ind_id","business_id")])

# # EXPORT
# write.csv(foo,"data/01_synthetic_population_dataset/XX_1014_individual_urban_rural_emp_place_orgID.csv", row.names = F)
# syn_ind_urb_empID <- foo
# 
# df_business_pop <- zzz[!is.na(zzz$act_size),]
# write.csv(df_business_pop,"data/01_synthetic_population_dataset/XX_1014_business_in_county_act_size.csv", row.names = F)

School Location

# Unique School district code: c_school_dist["SDLCODE"]; c_pub_k12["SDL_CODE"]
# Unique School id: c_pub_k12["SDL_CODE"]
# tm_shape(c_school_dist["SDLCODE"]) + tm_polygons(alpha = 0, border.col = "red") + 
#   tm_shape(c_pub_k12["SDL_CODE"]) + tm_dots(col="blue")

data <- c_pub_k12[,c(3,41:59)]   # unique id: SED_CODE
st_geometry(data) <- NULL
# Create age range 
lt <- sprintf("age_%02d", 3:19)
data[,lt] <- NA
data[is.na(data)] <- 0
# Decide age range for each grade 
data[data$GRADE_PK == "Y", c("age_03","age_04")] <- "Y"
data[data$GRADE_FK == "Y", c("age_04","age_05","age_06")] <- "Y"
data[data$GRADE_1 == "Y", c("age_06","age_07")] <- "Y"
data[data$GRADE_2 == "Y", c("age_07","age_08")] <- "Y"
data[data$GRADE_3 == "Y", c("age_08","age_09")] <- "Y"
data[data$GRADE_4 == "Y", c("age_09","age_10")] <- "Y"
data[data$GRADE_5 == "Y", c("age_10","age_11")] <- "Y"
data[data$GRADE_6 == "Y", c("age_11","age_12")] <- "Y" 
data[data$GRADE_7 == "Y", c("age_12","age_13")] <- "Y"
data[data$GRADE_8 == "Y", c("age_13","age_14")] <- "Y"
data[data$GRADE_9 == "Y", c("age_14","age_15")] <- "Y"
data[data$GRADE_10 == "Y", c("age_15","age_16")] <- "Y"
data[data$GRADE_11 == "Y", c("age_16","age_17")] <- "Y"
data[data$GRADE_12 == "Y", c("age_17","age_18","age_19")] <- "Y"

data <- left_join(c_pub_k12[,c("SED_CODE","SDL_CODE")], data[,c(1,21:37)])
c_pub_k12_cln <- data
c_pub_k12_cln <- st_as_sf(c_pub_k12_cln)
# Identify Child age between 2-19 who enrolls in school 
# css_age_school_enroll
data <- syn_ind_urb_empID
data$if_school <- 0

for (i in 1:nrow(css_age_school_enroll)) {
  a <- css_age_school_enroll[i,2:5] 
  age_min <- as.numeric(a$age_min)
  age_max <- as.numeric(a$age_max)
  
  m <- data %>% filter(age >= age_min & age <= age_max & hh_role != "in_gq")
  n <- round(nrow(m) * a$Enrol_school / a$Total, 0)
  
  df <- m %>% slice_sample(n=n) %>% pull(ind_id)
  data[data$ind_id %in% df,]$if_school <- 1 }

# Join schoolers with parcel index 
data <- left_join(data, syn_hh_gq_prcl[c("hh_id","GEOID","PRINT_KEY")], by=c("hh_id"="hh_id","GEOID"="GEOID")) %>% 
  filter(if_school == 1 & hh_role != "in_gq")
data$school_id <- 0

# convert to sf object and get school district ID 
data <- left_join(data, c_parcel_r["PRINT_KEY"]) %>% st_as_sf() %>% st_centroid()
data <- st_join(data, c_school_dist["SDLCODE"], join=st_intersects)
# c_pub_k12_cln - age vector 
lt <- names(c_pub_k12_cln)[3:19]    # age range: 3 - 19 yrs

for (i in 1:nrow(data)) {
  a <- data[i, ]         # child enrolled in school 
  b <- c_pub_k12_cln %>% filter(get(lt[a$age - 2])=="Y" & SDL_CODE == a$SDLCODE)
  if(nrow(b)>0){
    b <- b %>% slice_sample(n=1) %>% pull("SED_CODE")
  } else {
    b <- c_pub_k12_cln %>% filter(get(lt[a$age - 2])=="Y") %>% st_as_sf()
    b <- st_join(a, b["SED_CODE"], join = st_nearest_feature) %>% pull("SED_CODE")
  }
  
  data[i,]$school_id <- b
  print(i)
}

# # save result
# df <- data
# st_geometry(df) <- NULL
# df <- left_join(syn_ind_urb_empID, df[c("ind_id","if_school","school_id","SDLCODE")])
# 
# write.csv(df, "data/01_synthetic_population_dataset/XX_1014_individual_urban_rural_emp_place_orgID_sch.csv", row.names = F)
# syn_ind_urb_empID_sch <- df

Network

Working Network

# set random seed 
set.seed(rm_seed)

# Network data containner 
df_network <- data.frame(matrix(ncol = 4)) %>% setnames(c("Source","Target","Type","Relation"))
nn <- df_network

foo <- syn_ind_urb_empID_sch   # individual 
zz <- df_business_pop[!is.na(df_business_pop$act_size),] # business 
threhd <- 10    # the threshold of network size to generate a full-connected network 
zzz <- zz[zz$act_size > threhd,]
zz <- zz[(zz$act_size <= threhd & zz$act_size > 1),]

# full connected working network 
for (i in 1:nrow(zz)) {
  idx <- zz[i,]$business_id
  # All individuals in a particular company
  data <- foo %>% filter(business_id == idx)
  a <- combn(data$ind_id,2)
  
  data <- data.frame(Source = a[1,],
                     Target = a[2,],
                     Type = "Undirected",
                     Relation = "Work")
  nn <- rbind(nn, data)
  print(i)
}

# Scale free working network with n >10
mm <- df_network

lt <- c("avg_degree","diameter")
zzz[lt] <- NA
for (i in 1:nrow(zzz)) {
  idx <- zzz[i,]$business_id
  n <- zzz[i,]$act_size
  
  # All individuals in a particular company
  data <- foo %>% filter(business_id == idx) %>% pull(ind_id) %>% sample()
  
  # Generate Network 
  g <- sample_pa(n, m = 6, directed =F, power = 1)
  zzz[i, lt] <- c(mean(degree(g)), igraph::diameter(g))
  
  g <- as.data.frame(get.edgelist(g)) %>% setnames(new=c("Source","Target"))
  g$Source <- mapvalues(g$Source, from=c(1:n), to=data, warn_missing = F)
  g$Target <- mapvalues(g$Target, from=c(1:n), to=data, warn_missing = F)
  g$Type = "Undirected"
  g$Relation = "Work"
  
  mm <- rbind(mm, g)
  print(i)
}

nn <- rbind(nn, mm)
nn <- nn[!is.na(nn$Source),]

# export
# ntwk_work <- nn # average degree = 10:025
# write.csv(ntwk_work, "data/02_network_dataset/XX_step2_1015_working_network.csv", row.names = F)

Education Network

# set random seed 
set.seed(rm_seed)

# Network data containner 
df_network <- data.frame(matrix(ncol = 4)) %>% setnames(c("Source","Target","Type","Relation"))
nn <- df_network

foo <- syn_ind_urb_empID_sch   # individual 
zz <- c_pub_k12_cln   # public K-12 

tt <- table(foo$school_id) %>% as.data.frame() %>% setnames(new=c("SED_CODE","n_child"))
zz <- left_join(zz, tt)
lt <- c("avg_degree","diameter")
zz[lt] <- NA

for (i in 1:nrow(zz)) {
  idx <- zz[i,]$SED_CODE
  n <- zz[i,]$n_child
  
  # All child in a particular school
  data <- foo %>% filter(school_id == idx) %>% pull(ind_id) %>% sample()
  
  # Generate network based on PA
  g <- sample_pa(n, m = 3, directed =F, power = 1)
  zz[i, lt] <- c(mean(degree(g)), igraph::diameter(g))
  
  g <- as.data.frame(get.edgelist(g)) %>% setnames(new=c("Source","Target"))
  g$Source <- mapvalues(g$Source, from=c(1:n), to=data, warn_missing = F)
  g$Target <- mapvalues(g$Target, from=c(1:n), to=data, warn_missing = F)
  g$Type = "Undirected"
  g$Relation = "School"
  
  nn <- rbind(nn, g)
  print(i)
}

c_pub_k12_cln_ntwk <- zz
nn <- nn[!is.na(nn$Source),]

# #  export
# ntwk_school <- nn
# write.csv(ntwk_school, "data/02_network_dataset/XX_step3_1015_k12_school_network.csv", row.names = F)

Group Quarter Network

# individual living in group quarter
foo <- syn_ind_urb_empID_sch %>% filter(hh_role=="in_gq") %>% 
  mutate(hh_id2 = paste(hh_id, GEOID, sep="_"))  

tt <- foo["hh_id2"] %>% mutate(act_size = 1)
tt <- aggregate(act_size~hh_id2, data = tt, FUN =sum)

threhd <- 5     # the threshold of network size to generate a full-connected network 
zzz <- tt[tt$act_size > threhd,]
zz <- tt[(tt$act_size <= threhd & tt$act_size > 1),]

# full connected gq network 
nn <- df_network
for (i in 1:nrow(zz)) {
  idx <- zz[i,]$hh_id2
  # All individuals in a particular gq
  data <- foo %>% filter(hh_id2 == idx)
  a <- combn(data$ind_id,2)
  
  data <- data.frame(Source = a[1,],
                     Target = a[2,],
                     Type = "Undirected",
                     Relation = "gq")
  nn <- rbind(nn, data)
  print(i)
}

# scale-free gq network 
mm <- df_network
lt <- c("avg_degree","diameter")
zzz[lt] <- NA
for (i in 1:nrow(zzz)) {
  idx <- zzz[i,]$hh_id2
  n <- zzz[i,]$act_size
  
  # All individuals in a particular gq
  data <- foo %>% filter(hh_id2 == idx) %>% pull(ind_id) %>% sample()
  
  # Generate Network 
  g <- sample_pa(n, m = 3, directed =F, power = 1)
  zzz[i, lt] <- c(mean(degree(g)), igraph::diameter(g))
  
  g <- as.data.frame(get.edgelist(g)) %>% setnames(new=c("Source","Target"))
  g$Source <- mapvalues(g$Source, from=c(1:n), to=data, warn_missing = F)
  g$Target <- mapvalues(g$Target, from=c(1:n), to=data, warn_missing = F)
  g$Type = "Undirected"
  g$Relation = "gq"
  
  mm <- rbind(mm, g)
  print(i)
}

nn <- rbind(nn, mm)
nn <- nn[!is.na(nn$Source),]
# # #  export
# ntwk_gq <- nn
# write.csv(ntwk_gq, "data/02_network_dataset/XX_step4_1015_gq_network.csv", row.names = F)

Social Media Usage - Facebook

ADULT USER [18,∞)

# Identify FB users; urban (70%), rural (67%); 
# male (61%); female (77%); 
# age 18-29 (70%); age 30-49 (77%); age 50-64 (73%); age 60+ (50%)

foo <- syn_ind_urb_empID_sch[c("ind_id","urban_rural","gender","age")] %>% filter(age >= 18)
brks <- c(17,29,49,64,100)
labs <- c("age_18_29","age_30_49","age_50_64","age_65over")
foo$age_cat <- cut(foo$age, breaks=brks, labels = labs)

data <- foo %>% mutate(n=1)
data <- aggregate(n~urban_rural + gender + age_cat, data = data[c("urban_rural","gender","age_cat","n")], FUN = sum)

data <- data[order(data$urban_rural, data$gender, data$age_cat),]
row.names(data) <- NULL

data$idx <- sprintf("x_%02d", 1:nrow(data))
data$value <- 1

for (i in 1:3) {
  tt <- data[,c(i,5,6)]
  colnames(tt) <- c("var","idx","value")
  tt <- dcast(tt, idx ~ var, fill = 0, value.var = "value")
  data <- left_join(data, tt, by=c("idx"="idx"))
}

## Solve an lsei problem   ||Ax-B||^2
## Least Squares with Equalities and Inequalities

# A: coefficient matrix. 
mat1 <- as.matrix(data[7:14]) %>% t()

# B: numeric vector containing the right-hand side. variables in order: rural, urban, female, male,a18-29, a30-49, a50-64, a65+
tt <- data
tt[,7:14] <- tt[,7:14] * tt$n
tt <- apply(tt[,7:14], 2, sum) 
ttt <- matrix(c(0.67, 0.7, 0.77, 0.61, 0.7, 0.77, 0.73, 0.5), 
              nrow = 1)
mat2 <- tt * ttt %>% t()

# Inequality constraints, Gx >= H
mat3 <-  rbind(diag(16),-1 * diag(16))
mat4 <- rbind(matrix(0, nrow = 16), as.matrix(data$n) * -1)

a <- lsei(A = mat1, B = mat2, G=mat3, H=mat4)
a <- matrix(a$X, ncol = 1)
data$result <- a

(mat1 %*% a - mat2)
(mat1 %*% a - mat2) / mat2

df_smedia_user <- data
data <- df_smedia_user
zz <- foo
zz$if_socialmedia <- 0

for (i in 1:nrow(data)) {
  aa <- data[i,]
  lt_temp <- zz %>% filter(urban_rural == aa$urban_rural,
                           gender == aa$gender,
                           age_cat == aa$age_cat) %>% 
    slice_sample(n=aa$result) %>% pull(ind_id)
  zz[zz$ind_id %in% lt_temp,]$if_socialmedia = 1
}

# check 
nrow(zz[zz$urban_rural=="rural" & zz$if_socialmedia==1,]) / nrow(zz[zz$urban_rural=="rural",]) 
nrow(zz[zz$urban_rural=="urban" & zz$if_socialmedia==1,]) / nrow(zz[zz$urban_rural=="urban",])
nrow(zz[zz$gender=="Male" & zz$if_socialmedia==1,]) / nrow(zz[zz$gender=="Male",])
nrow(zz[zz$gender=="Female" & zz$if_socialmedia==1,]) / nrow(zz[zz$gender=="Female",])
nrow(zz[zz$age_cat=="age_18_29" & zz$if_socialmedia==1,]) / nrow(zz[zz$age_cat=="age_18_29",])
nrow(zz[zz$age_cat=="age_30_49" & zz$if_socialmedia==1,]) / nrow(zz[zz$age_cat=="age_30_49",])
nrow(zz[zz$age_cat=="age_50_64" & zz$if_socialmedia==1,]) / nrow(zz[zz$age_cat=="age_50_64",])
nrow(zz[zz$age_cat=="age_65over" & zz$if_socialmedia==1,]) / nrow(zz[zz$age_cat=="age_65over",])
zz <- left_join(syn_ind_urb_empID_sch, zz[c("ind_id","if_socialmedia")])
zz[is.na(zz$if_socialmedia),]$if_socialmedia <- 0

# # export
# write.csv(zz, "data/01_synthetic_population_dataset/XX_1014_individual_urban_rural_emp_place_orgID_sch_smedia.csv", row.names = F)
# syn_ind_urb_empID_sch_smedia <- zz

Adult: Social Media Network

average degree in county: 64

# set random seed 
set.seed(rm_seed)

# Network data containner 
df_network <- data.frame(matrix(ncol = 4)) %>% setnames(c("Source","Target","Type","Relation"))
nn <- df_network

# Filter adults using social media
data <- syn_ind_urb_empID_sch_smedia %>% filter(if_socialmedia==1) %>% pull(ind_id) %>% sample()  # individual 
n <- length(data)
# Scale free social media network. avg = 64
# Generate Network 
g <- sample_pa(n, m = 25, directed =F, power = 1)
mean(degree(g))

g <- as.data.frame(get.edgelist(g)) %>% setnames(new=c("Source","Target"))
g$Source <- mapvalues(g$Source, from=c(1:n), to=data, warn_missing = F)
g$Target <- mapvalues(g$Target, from=c(1:n), to=data, warn_missing = F)
g$Type = "Undirected"
g$Relation = "SocialMedia"

# # export
# ntwk_fb <- g
# write.csv(ntwk_fb, "data/02_network_dataset/XX_step5_1015_social_media_network_adult.csv", row.names = F)

TEENS USER[13, 17]

# Teen FB Users: urban (40%), rural (43%)
#                boy (31%), girl (34%)
#                age 13-14 (23%), age 15-17 (39%)

## Filter out teenagers 
foo <- syn_ind_urb_empID_sch_smedia[c("ind_id","urban_rural","gender","age" )] %>% 
  filter(age>=13 & age <=17)
foo$age_cut <- cut(foo$age, breaks = c(12,14,20), labels = c("age_13_14","age_15_17"))

## Aggregate teens into classes based on different age/gender/urban-rural combinations
data <- foo[c("urban_rural","gender","age_cut")] %>% mutate(n=1)
data <- aggregate(n~urban_rural + gender + age_cut, data = data, FUN=sum)
data <- data %>% mutate(idx = sprintf("X_%02d", 1:nrow(data)), value = 1)

for (i in 1:3) {
  a <- data[, c(i,5,6)]
  colnames(a) <- c("var","idx","value")
  a <- dcast(a, idx ~ var, fill = 0, value.var = "value")
  data <- left_join(data, a, by=c("idx" = "idx"))
}

## Coefficient Matrix
mat1 <- as.matrix(data[,7:12]) %>% t()
## Right-hand
tt <- data[,7:12] * data$n
tt <- apply(tt, 2, sum)
# ttt <- matrix(data=c(0.43, 0.4, 0.34, 0.31, 0.23, 0.39), nrow = 1)  # facebook
ttt <- matrix(data=c(0.62, 0.58, 0.64, 0.54, 0.51, 0.65), nrow = 1)  # snapchat
mat2 <- tt * ttt %>% t()
## Inequality constraints
mat3 <- rbind(diag(8), -1*diag(8))
mat4 <- rbind(matrix(0, nrow = 8), as.matrix(data$n) * -1)

# resolve
a <- lsei(A=mat1, B=mat2, G=mat3, H=mat4)
a <- matrix(a$X, ncol=1)

(mat1 %*% a - mat2)/mat2 

data$result <- a
df_smedia_teen_user <- data
data <- df_smedia_teen_user
zz <- foo
zz$if_socialmedia_teen <- 0

for (i in 1:nrow(data)) {
  aa <- data[i,]
  lt_temp <- zz %>% filter(urban_rural == aa$urban_rural,
                           gender == aa$gender,
                           age_cut == aa$age_cut) %>% 
    slice_sample(n=aa$result) %>% pull(ind_id)
  zz[zz$ind_id %in% lt_temp,]$if_socialmedia_teen <- 1
}

## Validate
zz %>% filter(if_socialmedia_teen==1 & gender=="Male") %>% nrow() / nrow(zz %>% filter(gender=="Male"))
zz %>% filter(if_socialmedia_teen==1 & gender=="Female") %>% nrow() / nrow(zz %>% filter(gender=="Female"))
zz %>% filter(if_socialmedia_teen==1 & age_cut=="age_13_14") %>% nrow() / nrow(zz %>% filter(age_cut=="age_13_14"))
zz %>% filter(if_socialmedia_teen==1 & age_cut=="age_15_17") %>% nrow() / nrow(zz %>% filter(age_cut=="age_15_17"))
zz %>% filter(if_socialmedia_teen==1 & urban_rural=="urban") %>% nrow() / nrow(zz %>% filter(urban_rural=="urban"))
zz %>% filter(if_socialmedia_teen==1 & urban_rural=="rural") %>% nrow() / nrow(zz %>% filter(urban_rural=="rural"))
zz <- left_join(syn_ind_urb_empID_sch_smedia, zz[c("ind_id","if_socialmedia_teen")])
zz[is.na(zz$if_socialmedia_teen),]$if_socialmedia_teen <- 0

# # export
# write.csv(zz, "data/01_synthetic_population_dataset/XX_1014_individual_urban_rural_emp_place_orgID_sch_smedia_teen.csv", row.names = F)
# syn_ind_urb_empID_sch_smedia_teen <- zz

Teen: Social Networks

set.seed(rm_seed)

# Network data containner 
df_network <- data.frame(matrix(ncol = 4)) %>% setnames(c("Source","Target","Type","Relation"))
nn <- df_network

# Filter teens with social media access
data <- syn_ind_urb_empID_sch_smedia_teen %>% filter(if_socialmedia_teen==1) %>% pull(ind_id) %>% sample()  # individual 
n <- length(data)

# Generate Network (avg. degree = 50)
g <- sample_pa(n, m = 25, directed =F, power = 1)
# mean(degree(g))
g <- as.data.frame(get.edgelist(g)) %>% setnames(new=c("Source","Target"))
g$Source <- mapvalues(g$Source, from=c(1:n), to=data, warn_missing = F)
g$Target <- mapvalues(g$Target, from=c(1:n), to=data, warn_missing = F)
g$Type = "Undirected"
g$Relation = "SocialMedia_teen"

# # export
# ntwk_smedia_teen <- g
# write.csv(ntwk_smedia_teen, "data/02_network_dataset/XX_step6_1015_social_media_network_teen.csv", row.names = F)

Summary_Network

Assign individual with parcel (coordinates)

# join individual with parcels 
df <- left_join(syn_ind_urb_empID_sch_smedia_teen, 
                syn_hh_gq_prcl[c("GEOID","hh_id","prcl_idx","PRINT_KEY")], by=c("GEOID"="GEOID","hh_id"="hh_id"))
df <- left_join(df, c_parcel["PRINT_KEY"]) %>% st_as_sf()

# # get centroid of parcel and transform to wgs84 (4326)
# zz <- df %>% st_centroid() %>%  st_as_sf() %>% st_transform(crs = 4326)

# keep project 
zz <- df %>% st_centroid() %>% st_as_sf()
zzz <- zz %>% mutate(long = unlist(map(zz$geometry,1)), lat = unlist(map(zz$geometry,2)))
st_geometry(zzz) <- NULL

# Add randomness to centroid
# aa <- 0.001 # wgs 84
aa <- 0.05  # NAD 83
zzz$long <- zzz$long + runif(nrow(zzz), -aa, aa)
zzz$lat <- zzz$lat+ runif(nrow(zzz), -aa, aa)
rownames(zzz) <- NULL
zzz["ind_new_id"] <- 1:nrow(zzz) - 1
# Python export 
write.csv(zzz, "data/01_synthetic_population_dataset/XX_999_model_individual_urban_rural_emp_place_orgID_sch_smedia_teen_NAD83.csv", row.names = F)



# tt <- st_as_sf(zzz, coords = c("long","lat"), crs=4326)
# 
# tmap_mode("plot")
# tm_shape(tt) + tm_dots()

# # Export
# write.csv(zzz, "data/01_synthetic_population_dataset/XX_1311_individual_urban_rural_emp_place_orgID_sch_smedia_teen_coord.csv", row.names = F)
# syn_ind_urb_empID_sch_smedia_teen_coord <- zzz

COMBINE & SAVE all networks

ntwk_family <- read.csv("data/02_network_dataset/step1_0831_family_network.csv")[,2:5]
ntwk_all <- rbind(ntwk_family,
                  ntwk_work,
                  ntwk_school,
                  ntwk_gq,
                  ntwk_fb,
                  ntwk_smedia_teen)

# export network - save
# write.csv(ntwk_all, "data/02_network_dataset/xx_step_finl_all_network.csv", row.names = F)

Network export

data <- ntwk_all
data <- left_join(data, zzz[c("ind_id","ind_new_id")], by=c("Source"="ind_id"))
names(data)[[5]] <- "source_reindex"
data <- left_join(data, zzz[c("ind_id","ind_new_id")], by=c("Target"="ind_id"))
names(data)[[6]] <- "target_reindex"

write.csv(data, "data/02_network_dataset/step999_model_step_finl_all_network.csv", row.names = F)

Network attributes

# 1. physical space: nodes & edges 
length(unique(c(ntwk_family$Source, ntwk_family$Target, ntwk_gq$Source, ntwk_gq$Target)))
nrow(ntwk_family) + nrow(ntwk_gq)

# 2.1 relational space - school: nodes & edges 
length(unique(c(ntwk_school$Source, ntwk_school$Target)))
nrow(ntwk_school)

# 2.2 relational space - work: nodes & edges
length(unique(c(ntwk_work$Source, ntwk_work$Target)))
nrow(ntwk_work)

# 3. cyber space - social media 
length(unique(c(ntwk_fb$Source, ntwk_fb$Target, ntwk_smedia_teen$Source, ntwk_smedia_teen$Target)))
nrow(ntwk_fb) + nrow(ntwk_smedia_teen)

Export

tt <- syn_ind_urb_empID_sch_smedia_teen_coord
tt[tt$hh_role=="in_gq",]$hh_id <- paste(tt[tt$hh_role=="in_gq",]$hh_id,
                                        tt[tt$hh_role=="in_gq",]$GEOID,
                                        sep="_")
colnames(tt)[1] <- "Id"
nodes <- tt[tt$Id %in% c(ntwk_all$Source, ntwk_all$Target),]

# # export edges - gephi
# write.csv(ntwk_all, "data/02_network_dataset/network/2023xxxx_network_edge.csv", row.names = F)
# # export nodes - gephi
# write.csv(nodes, "data/02_network_dataset/network/2023xxxx_network_nodes.csv", row.names = F)
## Plot 
pdf("plot/geo_school_district.pdf")
tm_shape(c_county) + tm_polygons(alpha = 0, border.col = "red") + tm_shape(c_school_dist) + tm_polygons(alpha = 0)
dev.off()
LS0tDQp0aXRsZTogIlIgKDIvMykgQUJNIElucHV0IERhdGEgLSBIeWJyaWQgU3BhY2UgTmV0d29yayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgRGF0YQ0KIyMgU3ludGhlc2l6ZWQgUG9wDQpgYGB7cn0NCiMgU3ludGhldGljIFBvcHVsYXRpb24gZGF0YXNldCANCnN5bl9pbmQgPC0gcmVhZC5jc3YoImRhdGEvMDFfc3ludGhldGljX3BvcHVsYXRpb25fZGF0YXNldC8wMF8wODMxX2ZpbmFsX3N5bnRoZXNpemVkX2luZGl2aWR1YWwuY3N2IikNCnN5bl9oaCA8LSByZWFkLmNzdigiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0LzAwXzA4MzFfZmluYWxfc3ludGhlc2l6ZWRfaG91c2Vob2xkLmNzdiIpDQpgYGANCg0KIyMgQ2Vuc3VzIGRhdGEgDQpDQjIwMDBDQlAgIEFsbCBTZWN0b3JzOiBDb3VudHkgQnVzaW5lc3MgUGF0dGVybnM6IGh0dHBzOi8vZGF0YS5jZW5zdXMuZ292L2NlZHNjaS90YWJsZT9xPWJ1c2luZXNzJTIwZXN0YWJsaXNobWVudCZnPTA1MDAwMDBVUzM2MDEzJnRpZD1DQlAyMDIwLkNCMjAwMENCUA0KDQpCMDgwMDcgIFNFWCBPRiBXT1JLRVJTIEJZIFBMQUNFIE9GIFdPUkstLVNUQVRFIEFORCBDT1VOVFkgTEVWRUwNCmh0dHBzOi8vZGF0YS5jZW5zdXMuZ292L2NlZHNjaS90YWJsZT9xPWVtcGxveW1lbnQmZz0wNTAwMDAwVVMzNjAxMyUyNDE1MDAwMDAmdGlkPUFDU0RUNVkyMDIwLkIwODAwNw0KDQpTMjMwMSAgRU1QTE9ZTUVOVCBTVEFUVVMgKENoYXV0YXVxdWEgY291bnR5KQ0KaHR0cHM6Ly9kYXRhLmNlbnN1cy5nb3YvY2Vkc2NpL3RhYmxlP3E9UzIzMDEmZz0wNTAwMDAwVVMzNjAxMyZ0aWQ9QUNTU1Q1WTIwMjAuUzIzMDENCg0KUzE0MDEgU0NIT09MIEVOUk9MTE1FTlQNCmh0dHBzOi8vZGF0YS5jZW5zdXMuZ292L2NlZHNjaS90YWJsZT90PVNjaG9vbCUyMEVucm9sbG1lbnQmZz0wNTAwMDAwVVMzNjAxMyZ0aWQ9QUNTU1Q1WTIwMjAuUzE0MDENCg0KRWR1Y2F0aW9uYWwgSW5zdGl0dXRpb24gRGF0YXNldDogaHR0cHM6Ly9lZGcuZXBhLmdvdi9tZXRhZGF0YS9jYXRhbG9nL21haW4vaG9tZS5wYWdlDQoNCk5ZIEdJUyBDbGVhcmluZ2hvdXNlIChQdWJsaWMgU2Nob29scyBLLTEyKTogaHR0cDovL2dpcy5ueS5nb3YvZ2lzZGF0YS9pbnZlbnRvcmllcy9kZXRhaWxzLmNmbT9EU0lEPTEzMjYNCmBgYHtyfQ0KIyAtLS0tLSBTaXplIG9mIGJ1c2luZXNzIG9mIGFsbCBzZWN0aW9ucyBpbiBDaGF1IA0KY3NzX2J1c2luZXNzX3NpemUgPC0gcmVhZC5jc3YoImRhdGEvQUNTRFQ1WTIwMjBfY2hhdV9ibG9ja19ncm91cF9jbGVhbmVkL0NoYXV0YXVxdWFfY291bnR5X2J1c2luZXNzX3BhdHRlcm5zLmNzdiIpDQoNCiMgLS0tLS0gIEVtcGxveW1lbnQgc3RhdHVzIGF0IGJnIA0KdHR0IDwtIHJlYWQuY3N2KCJkYXRhL0FDU0RUNVkyMDIwX2NoYXVfYmxvY2tfZ3JvdXBfY2xlYW5lZC9DaGF1dGF1cXVhX2JnX0FDU0RUNVkyMDIwLkIwODAwNy0yMDIyLTA4LTMxVDE2MDEwMC5jc3YiKQ0KdHR0IDwtIGNsbl9jZW5zdXNfZ2VvaWRfMih0dHQsIGlkeF9jYmdyb3VwKQ0KdHR0JG5fd29rX291dF9jb3VudHlfc3RhdGUgPC0gdHR0JG5fd29rX291dF9jb3VudHkgKyB0dHQkbl93b2tfb3V0X3N0YXRlDQp0dHQkcF93b2tfaW5fY291bnR5IDwtIHJvdW5kKHR0dCRuX3dva19pbl9jb3VudHkgLyB0dHQkdHRsX3dva2Vycyw0KQ0KdHR0JHBfd29rX291dF9jb3VudHlfc3RhdGUgPC0gMSAtIHR0dCRwX3dva19pbl9jb3VudHkNCmNzc19lbXBfcGxhY2UgPC0gdHR0DQoNCiMgLS0tLS0gIEVtcGxveW1lbnRfcmF0aW8gY291bnR5DQp0dHQgPC0gcmVhZC5jc3YoImRhdGEvQUNTRFQ1WTIwMjBfY2hhdV9ibG9ja19ncm91cF9jbGVhbmVkL0NoYXV0YXVxdWFfY291bnR5X0FDU1NUNVkyMDIwLlMyMzAxLTIwMjItMDgtMzFUMTgwNDE4LmNzdiIpDQpjc3NfZW1wX3JhdGlvIDwtIHR0dA0KDQojIC0tLS0tICBQdWJsaWMgSy0xMiBpbiBOWVMgLS0tLS0tLS0tDQp0dCA8LSBzdF9yZWFkKCJkYXRhL05ZX1B1YmxpY19LXzEyL1B1YmxpY19LXzEyLnNocCIpDQp0dCA8LSB0dFtsZW5ndGhzKHN0X2ludGVyc2VjdHModHQsIGNfY291bnR5KSkgPjAsIF0NCmNfcHViX2sxMiA8LSB0dA0KDQojIC0tLS0tICBTY2hvb2wgZGlzdHJpY3QgLS0tLS0tLS0tDQp0dCA8LSBzdF9yZWFkKCJkYXRhL05ZX1NjaERpc3QvU2NoRGlzdF8yMDE5X3YzLnNocCIpDQp0dCA8LSB0dFtsZW5ndGhzKHN0X2ludGVyc2VjdHModHQsIGNfY291bnR5KSk+MCwgXQ0KY19zY2hvb2xfZGlzdCA8LSB0dA0KDQojIC0tLS0tICBTY2hvb2wgZW5yb2xsbWVudCAtLS0tLS0tLS0NCnR0IDwtIHJlYWQuY3N2KCJkYXRhL0FDU0RUNVkyMDIwX2NoYXVfYmxvY2tfZ3JvdXBfY2xlYW5lZC9DaGF1dGF1cXVhX2NvdW50eV9BQ1NTVDVZMjAyMC5TMTQwMS0yMDIyLTA5LTA2VDIyMTcyMy5jc3YiKQ0KY3NzX2FnZV9zY2hvb2xfZW5yb2xsIDwtIHR0DQpgYGANCiMjIFVyYmFuLCBTdWJ1cmJhbiwgUnVyYWwgY2Jncm91cA0KYGBge3J9DQojIyBUaGlzIGNlbGwgY2FuIHZhbGlkYXRlIHRoZSBydXJhbC91cmJhbiBkYXRhIGNvbGxlY3RlZCBmcm9tIHRoZSBDZW5zdXMNCnR0IDwtIGNzc19hZ2VfZ2VuZGVyW2MoIkdFT0lEIiwidHRsIildDQp0dCA8LSBsZWZ0X2pvaW4oY19iZ3JvdXBbIkdFT0lEIl0sIHR0KQ0KdHQkYXJlYSA8LSBzdF9hcmVhKHR0KQ0KdHQkZGVuc2l0eSA8LSB0dCR0dGwgKiAxMDAwKioyLyBhcy5udW1lcmljKHR0JGFyZWEpIA0KdHQkY2F0IDwtIGN1dCh0dCRkZW5zaXR5LCBicmVha3MgPSBjKDAsMTAwLDEwMDAwKSwNCiAgICAgICAgICAgICAgbGFiZWxzID0gYygicnVyYWwiLCJzdWJ1cmJhbiIpKQ0KdG1fc2hhcGUodHQpICsgdG1fcG9seWdvbnMoY29sID0gImNhdCIsYWxwaGEgPSAwLjUpICsgDQogIHRtX3NoYXBlKGNfdXJiYW4pICsgdG1fcG9seWdvbnMoYWxwaGEgPSAwLjUsIGNvbD0icmVkIikNCmBgYA0KDQoNCiMgUmVzaWRlbmNlIA0KIyMgRmFtaWx5IE5ldHdvcmsgDQpgYGB7cn0NCiMgVW5pcXVlIGlkIG9mIGhvdXNlaG9sZCB3aXRoIChuX21lbWJlciA+IDEpIA0KbHRfaGhpZCA8LSBzeW5faGggJT4lIGZpbHRlcihoaF9zaXplID4gMSkgJT4lIHB1bGwoaGhfaWQpICU+JSB1bmlxdWUoKQ0KIyBOZXR3b3JrIGRhdGEgY29udGFpbmVyIA0KZGZfbmV0d29yayA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCkpICU+JSBzZXRuYW1lcyhjKCJTb3VyY2UiLCJUYXJnZXQiLCJUeXBlIiwiUmVsYXRpb24iKSkNCg0KZm9vIDwtIHN5bl9pbmQgICAjIGluZGl2aWR1YWwgDQp0dCA8LSBzeW5faGggICMgaG91c2Vob2xkIA0Kbm4gPC0gZGZfbmV0d29yayAgIyBuZXR3b3JrIA0KDQpmb3IgKGkgaW4gMTpsZW5ndGgobHRfaGhpZCkpIHsNCiAgIyB1bmlxdWUgaG91c2Vob2xkIGlkIA0KICBoaF9pZHggPC0gbHRfaGhpZFtbaV1dDQogIA0KICAjIEFsbCBpbmRpdmlkdWFscyBpbiBhIHBhcnRpY3VsYXIgaG91c2Vob2xkIA0KICBkYXRhIDwtIGZvbyAlPiUgZmlsdGVyKGhoX2lkID09IGhoX2lkeCkNCiAgYSA8LSBjb21ibihkYXRhJGluZF9pZCwyKQ0KICBkYXRhIDwtIGRhdGEuZnJhbWUoU291cmNlID0gYVsxLF0sDQogICAgICAgICAgICAgICAgICAgICBUYXJnZXQgPSBhWzIsXSwNCiAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSAiVW5kaXJlY3RlZCIsDQogICAgICAgICAgICAgICAgICAgICBSZWxhdGlvbiA9ICJGYW1pbHkiKQ0KICBubiA8LSByYmluZChubiwgZGF0YSkNCn0NCg0KIyAjIFNhdmUgbmV0d29yayByZXN1bHQgDQojIG5uIDwtIG5hLm9taXQobm4pDQojIHdyaXRlLmNzdihubiwiZGF0YS8wMl9uZXR3b3JrX2RhdGFzZXQvWFhfc3RlcDFfZmFtaWx5X25ldHdvcmsuY3N2Iiwgcm93Lm5hbWVzID0gRikNCmBgYA0KDQojIyBSZXNpZGVuY2UNCiMjIyBQcmVwYXJlIGEgbGlzdCBvZiByZXNpZGVudA0KYGBge3J9DQojIChQcm9qZWN0ZWQgQ1JTOiBOQUQ4MyAvIFVUTSB6b25lIDE4TikNCiMgZGF0YXNldHM6IGNfY291bnR5LCBjX2Jncm91cCAoaWQ6R0VPSUQpLCBjX3BhcmNlbF9yIChpZDogUFJJTlRfS0VZKQ0KIyBKb2luIHJlc2lkZW50aWFsIHBhcmNlbCB3aXRoIGNlbnN1cyBibG9jayBncm91cCANCmRmIDwtIGNfcGFyY2VsX3IgJT4lIHN0X2NlbnRyb2lkKCkNCmRmIDwtIHN0X2pvaW4oZGYsIGNfYmdyb3VwLCBqb2luID0gc3RfaW50ZXJzZWN0cykNCmRmIDwtIGRmWyFkdXBsaWNhdGVkKGRmJFBSSU5UX0tFWSksXQ0Kc3RfZ2VvbWV0cnkoZGYpIDwtIE5VTEwNCg0KIyBQcmVwYXJlIGEgZGYgb2YgcmVzaWRlbnRpYWwgcGFyY2VsDQp0dCA8LSBkZltjKCJQUklOVF9LRVkiLCJHRU9JRCIsIlBST1BfQ0xBU1MiKV0NCiMgcmVtb3ZlIHJlY3JlYXRpb25hbCB1c2UNCnR0IDwtIHR0WyF0dCRQUk9QX0NMQVNTICVpbiUgYygiMjAwIiwiMjQyIiwiMjYwIiksIF0NCiMgdHdvX2ZhbWlseSAqIDINCmEgPC0gdHRbdHQkUFJPUF9DTEFTUz09IjIyMCIsXQ0KdHQgPC0gcmJpbmQodHQsYSkNCiMgdGhyZWUgZmFtaWx5ICogMyANCmEgPC0gdHRbdHQkUFJPUF9DTEFTUz09IjIzMCIsXQ0KYSA8LSBhW3JlcChzZXFfbGVuKG5yb3coYSkpLCBlYWNoID0gMiksXQ0KdHQgPC0gcmJpbmQodHQsIGEpDQojIHJ1cmFsIHJlc2lkZW5jZQ0KYSA8LSB0dFt0dCRQUk9QX0NMQVNTICVpbiUgYyAoIjI0MCIsIjI0MSIpLF0gIyBwcmltYXJ5IHJlc2lkZW50aWFsIA0KYSA8LSBhW3JlcChzZXFfbGVuKG5yb3coYSkpLCBlYWNoID0gMiksXQ0KdHQgPC0gcmJpbmQodHQsIGEpDQojIGVzdGF0ZSBhbmQgcmVzaWRlbmNlIA0KYSA8LSB0dFt0dCRQUk9QX0NMQVNTICVpbiUgYygiMjUwIiwiMjgwIiwiMjgxIiksXQ0KYSA8LSBhW3JlcChzZXFfbGVuKG5yb3coYSkpLCBlYWNoID0gMjkpLF0NCnR0IDwtIHJiaW5kKHR0LCBhKQ0KDQpyb3duYW1lcyh0dCkgPC0gTlVMTA0KdHQkcHJjbF9pZHggPC0gc3ByaW50ZigiUl9wcmNsXyUwNmQiLDE6bnJvdyh0dCkpDQpkZl9yZXNpZGVuY2VfbGlzdCA8LSB0dA0KYGBgDQoNCiMjIyBIb3VzZWhvbGQgQWxsb2NhdGlvbg0KYGBge3J9DQojIHNldCByYW5kb20gc2VlZCANCnNldC5zZWVkKHJtX3NlZWQpDQojIGRhdGFzZXRzIA0KZm9vIDwtIHN5bl9pbmQgICAgICAgICAgICAgIyBpbmRpdmlkdWFsIA0KdHQgPC0gc3luX2hoICAgICAgICAgICAgICAgIyBob3VzZWhvbGQgDQp6eiA8LSBkZl9yZXNpZGVuY2VfbGlzdCAgICAjIHJlc2lkZW50aWFsIHBhcmNlbCBsaXN0IA0KDQojIEluaXRpYWxpemUgDQp0dCRwcmNsX2lkeCA8LSBOQQ0KenokaWZfcG9wIDwtIE5BDQoNCmZvciAoaSBpbiAxOmxlbmd0aChsdF9nZW9pZF9iZykpIHsNCiAgaWR4IDwtIGx0X2dlb2lkX2JnW1tpXV0NCiAgIyBHRU9JRDogSW5kaXZpZHVhbCBEYXRhDQogIGRhdGEgPC0gZm9vICU+JSBmaWx0ZXIoR0VPSUQgPT0gaWR4KQ0KICAjIEdFT0lEOiBIb3VzZWhvbGQgRGF0YSANCiAgZGYgPC0gdHQgJT4lIGZpbHRlcihHRU9JRCA9PSBpZHgpDQogICMgR0VPSUQ6IHJlc2lkZW50aWFsIHBhcmNlbCBkYXRhIA0KICB6enogPC0genogJT4lIGZpbHRlcihHRU9JRCA9PSBpZHgpDQogIA0KICAjIFRocmVzaG9sZA0KICBuIDwtIG1pbihucm93KHp6eiksICAgICMgYXZhaWxhYmxlIHJlc2lkZW5jZSANCiAgICAgICAgICAgbnJvdyhkZikpICAgIyBhbGwgaG91c2Vob2xkIA0KICANCiAgZm9yIChqIGluIDE6bikgew0KICAgICMgcmFuZG9tbHkgc2VsZWN0IGEgaG91c2Vob2xkIDogaWQNCiAgICBhIDwtIGRmICU+JSBmaWx0ZXIoaXMubmEocHJjbF9pZHgpKSAlPiUgc2xpY2Vfc2FtcGxlKG49MSkgJT4lIHB1bGwoaGhfaWQpDQogICAgIyByYW5kb21seSBzZWxlY3QgYSByZXNpZGVudGlhbCBwYXJjZWwgOiBpZA0KICAgIGIgPC0genp6ICU+JSBmaWx0ZXIoaXMubmEoaWZfcG9wKSkgJT4lIHNsaWNlX3NhbXBsZShuPTEpICU+JSBwdWxsKHByY2xfaWR4KQ0KICAgIA0KICAgIGRmW2RmJGhoX2lkID09IGEsXSRwcmNsX2lkeCA8LSBiDQogICAgenp6W3p6eiRwcmNsX2lkeCA9PSBiLCBdJGlmX3BvcCA8LSBhDQogIH0NCiAgDQogICMgU2F2ZSByZXN1bHQgDQogIHR0IDwtIHR0ICU+JSBsZWZ0X2pvaW4oZGZbYygiaGhfaWQiLCJwcmNsX2lkeCIpXSwgYnkgPSAiaGhfaWQiKSAlPiUgDQogICAgbXV0YXRlKHByY2xfaWR4ID0gY29hbGVzY2UocHJjbF9pZHgueCwgcHJjbF9pZHgueSkpICU+JSANCiAgICBzZWxlY3QoLXByY2xfaWR4LngsIC1wcmNsX2lkeC55KQ0KICAgIA0KICB6eiA8LSB6eiAlPiUgbGVmdF9qb2luKHp6eltjKCJwcmNsX2lkeCIsImlmX3BvcCIpXSwgYnk9InByY2xfaWR4IikgJT4lIA0KICAgIG11dGF0ZShpZl9wb3AgID0gY29hbGVzY2UgKGlmX3BvcC54LCBpZl9wb3AueSkpICU+JSANCiAgICBzZWxlY3QoLWlmX3BvcC54LCAtaWZfcG9wLnkpDQogIA0KICBwcmludChpKQ0KfQ0KDQoNCiMgcG9wdWxhdGUgcmVzdCBob3VzZWhvbGRzIA0KIyBleHRyYWN0ICJob3VzZWhvbGQgd2l0aG91dCBwYXJjZWwiIGFuZCAicGFyY2VsIHdpdGhvdXQgaG91c2Vob2xkIiANCmx0X3RlbXAgPC0gdHRbaXMubmEodHQkcHJjbF9pZHgpLF0NCnp6eiA8LSB6eltpcy5uYSh6enokaWZfcG9wKSxdDQpmb3IgKGkgaW4gMTpucm93KGx0X3RlbXApKSB7DQogIGEgPC0gbHRfdGVtcCAlPiUgZmlsdGVyKGlzLm5hKHByY2xfaWR4KSkgJT4lIHNsaWNlX3NhbXBsZShuPTEpICU+JSBwdWxsKGhoX2lkKQ0KICBiIDwtIHp6eiAlPiUgZmlsdGVyKGlzLm5hKGlmX3BvcCkpICU+JSBzbGljZV9zYW1wbGUobj0xKSAlPiUgcHVsbChwcmNsX2lkeCkNCiAgDQogIGx0X3RlbXBbbHRfdGVtcCRoaF9pZCA9PSBhLCBdJHByY2xfaWR4IDwtIGINCiAgenp6W3p6eiRwcmNsX2lkeCA9PSBiLF0kaWZfcG9wIDwtIGENCn0NCg0KIyBGaWxsIG9yaWdpbmFsIGRhdGFzZXQNCnR0IDwtIHR0ICU+JSBsZWZ0X2pvaW4obHRfdGVtcFtjKCJoaF9pZCIsInByY2xfaWR4IildLCBieSA9ICJoaF9pZCIpICU+JSANCiAgbXV0YXRlKHByY2xfaWR4ID0gY29hbGVzY2UocHJjbF9pZHgueCwgcHJjbF9pZHgueSkpICU+JQ0KICBzZWxlY3QoLXByY2xfaWR4LngsIC1wcmNsX2lkeC55KQ0KenogPC0genogJT4lIGxlZnRfam9pbih6enpbYygicHJjbF9pZHgiLCJpZl9wb3AiKV0sIGJ5PSJwcmNsX2lkeCIpICU+JQ0KICBtdXRhdGUoaWZfcG9wICA9IGNvYWxlc2NlIChpZl9wb3AueCwgaWZfcG9wLnkpKSAlPiUNCiAgc2VsZWN0KC1pZl9wb3AueCwgLWlmX3BvcC55KQ0KenogPC0genpbIWlzLm5hKHp6JGlmX3BvcCksXSAjIG9ubHkga2VlcCBvY2N1cGllZCBwYXJjZWwgDQp0dCA8LSBsZWZ0X2pvaW4odHQsIHp6W2MoInByY2xfaWR4IiwiUFJJTlRfS0VZIiwiUFJPUF9DTEFTUyIpXSkNCg0KIyAtLS0tLSAgVXJiYW4gUmVzaWRlbnRpYWwgUGFyY2VsIC0tLS0tLS0tLQ0KbHRfdXJiYW5fcGFyY2VsX3IgPC0gY19wYXJjZWxfcltsZW5ndGhzKHN0X2ludGVyc2VjdHMoY19wYXJjZWxfciwgY191cmJhbikpPjAsXSAlPiUgcHVsbCgiUFJJTlRfS0VZIikNCnR0JHVyYmFuX3J1cmFsIDwtICJydXJhbCINCnR0W3R0JFBSSU5UX0tFWSAlaW4lIGx0X3VyYmFuX3BhcmNlbF9yLF0kdXJiYW5fcnVyYWwgPC0gInVyYmFuIg0KDQojIEVYUE9SVCANCiMgd3JpdGUuY3N2KHR0LCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaG91c2Vob2xkX3BhcmNlbF9pZHhfdXJiYW5fcnVyYWwuY3N2Iiwgcm93Lm5hbWVzID0gRikNCiMgc3luX2hoX3ByY2wgPC0gdHQNCg0KDQojICMgLS0tLS0gUGxvdCAtLS0tLS0tLS0NCiMgYWEgPC0gbGVmdF9qb2luKHR0LCBjX3BhcmNlbF9yWyJQUklOVF9LRVkiXSkgJT4lIHN0X2FzX3NmKCkNCiMgdG1hcF9tb2RlKCJwbG90IikNCiMgcGRmKCJwbG90L3h4X1BhcmNlbF9yZXNpZGVudGlhbF91cmJhbl9ydXJhbC5wZGYiKQ0KIyB0bV9zaGFwZShhYSkgKyB0bV9wb2x5Z29ucyhjb2wgPSAidXJiYW5fcnVyYWwiLCBib3JkZXIuYWxwaGEgPSAwKQ0KIyBkZXYub2ZmKCkNCmBgYA0KIyMjIEdyb3VwIFF1YXJ0ZXIgQWxsb2NhdGlvbiANCmBgYHtyfQ0KIyBQcmVwYXJlIHBhcmNlbHMgZm9yIGdxIA0KZGYgPC0gY19wYXJjZWxfZ3ENCmRmIDwtIHN0X2pvaW4oZGYsIGNfYmdyb3VwLCBqb2luID0gc3RfaW50ZXJzZWN0cykNCmRmIDwtIGRmWyFkdXBsaWNhdGVkKGRmJFBSSU5UX0tFWSksXQ0KIyB1cmJhbi9ydXJhbCBncm91cCBxdWFydGVyIA0KZGYkdXJiYW5fcnVyYWwgPC0gInJ1cmFsIg0KZGZbbGVuZ3RocyhzdF9pbnRlcnNlY3RzKGRmLCBjX3VyYmFuKSk+MCxdJHVyYmFuX3J1cmFsIDwtICJ1cmJhbiINCnN0X2dlb21ldHJ5KGRmKSA8LSBOVUxMDQpkZiA8LSBkZltjKCJQUklOVF9LRVkiLCJHRU9JRCIsIlBST1BfQ0xBU1MiLCJ1cmJhbl9ydXJhbCIpXQ0KZGYkcHJjbF9pZHggPC0gc3ByaW50ZigiUl9ncV8lMDNkIiwxOm5yb3coZGYpKQ0KZGZfcmVzX2dxX2xpc3QgPC0gZGYNCg0KIyBJZGVudGlmeSBpbmRpdmlkdWFscyBpbiBncSANCmZvbyA8LSBzeW5faW5kICAgIyBpbmRpdmlkdWFsIA0KZm9vIDwtIGZvb1tmb28kaGhfcm9sZT09ImluX2dxIixdDQpmb28kaGhfaWRfMiA8LSBwYXN0ZShmb28kaGhfaWQsIGZvbyRHRU9JRCwgc2VwPSJfIikNCg0KbHRfZ3EgPC0gZm9vW2MoIkdFT0lEIiwiaGhfaWQiLCJoaF9pZF8yIildICU+JSBtdXRhdGUoYWN0X3NpemUgPSAxKQ0KbHRfZ3EgPC0gYWdncmVnYXRlKGFjdF9zaXplfmhoX2lkICsgR0VPSUQgKyBoaF9pZF8yLCBkYXRhID0gbHRfZ3EsIEZVTiA9c3VtKQ0KDQojIGRpZmZlcmVudCBncSB0eXBlcyANCmRmIDwtIGRmX3Jlc19ncV9saXN0DQpsdF90ZW1wIDwtIGx0X2dxJGhoX2lkICU+JSB1bmlxdWUoKQ0KZGZbbHRfdGVtcF0gPC0gMA0KZGZbZGYkUFJPUF9DTEFTUyAlaW4lIGMoIjYxMyIsIjYxNSIpLF0kZ3FfY29sbGVnZV9ob3VzaW5nIDwtIDENCmRmW2RmJFBST1BfQ0xBU1MgJWluJSBjKCI2MzMiKSxjKCJncV9vdGhlcnNfYWR1bHRfb3ZlcjY0IiwiZ3Ffb3RoZXJzX2FkdWx0IildIDwtIDENCmRmW2RmJFBST1BfQ0xBU1MgJWluJSBjKCI2NDEiLCI2NDIiKSxjKCJncV9vdGhlcnNfYWR1bHRfb3ZlcjY0IildIDwtIDENCmRmW2RmJFBST1BfQ0xBU1MgJWluJSBjKCI2NzAiKSxjKCJncV9KdXZlbmlsZV9mYWNpbGl0eSIsImdxX290aGVyc19hZHVsdCIpXSA8LSAxDQpkZiRmaWxsZWQgPC0gMA0KDQojIFBvcHVsYXRlIA0Kcm93Lm5hbWVzKGx0X2dxKSA8LSBOVUxMDQpsdF9ncVtjKCJQUklOVF9LRVkiLCJwcmNsX2lkeCIpXSA8LSBOQQ0KZm9yIChpIGluIDE6bnJvdyhsdF9ncSkpIHsNCiAgaWR4IDwtIGx0X2dxW2ksXSRHRU9JRA0KICBhIDwtIGx0X2dxW2ksXSRoaF9pZA0KICANCiAgYiA8LSBkZiAlPiUgZmlsdGVyKEdFT0lEID09IGlkeCkgJT4lIGZpbHRlcihnZXQoYSkgPT0gMSkNCiAgaWYobnJvdyhiKT4wKXsNCiAgICBiIDwtIGIgJT4lIHNsaWNlX3NhbXBsZShuPTEpDQogICAgZGZbZGYkUFJJTlRfS0VZID09IGIkUFJJTlRfS0VZLF0kZmlsbGVkIDwtIDENCiAgICBsdF9ncVtpLCBjKCJQUklOVF9LRVkiLCJwcmNsX2lkeCIpXSA8LSBiW2MoIlBSSU5UX0tFWSIsInByY2xfaWR4IildDQogIH0NCn0NCg0KenogPC0gbHRfZ3FbaXMubmEobHRfZ3EkcHJjbF9pZHgpLF0NCnp6eiA8LSBsdF9ncVshaXMubmEobHRfZ3EkcHJjbF9pZHgpLF0NCg0KZm9yIChpIGluIDE6bnJvdyh6eikpIHsNCiAgYSA8LSB6eltpLF0kaGhfaWQNCiAgYiA8LSBkZiAlPiUgZmlsdGVyKGdldChhKSA9PSAxICYgZmlsbGVkPT0wKQ0KICBpZihucm93KGIpID09IDApew0KICAgIGIgPC0gZGYgJT4lIGZpbHRlcihnZXQoYSkgPT0gMSkNCiAgfSANCiAgaWYobnJvdyhiKSA+IDApew0KICAgIGIgPC0gYiAlPiUgc2xpY2Vfc2FtcGxlKG49MSkNCiAgICBkZltkZiRQUklOVF9LRVkgPT0gYiRQUklOVF9LRVksXSRmaWxsZWQgPC0gMQ0KICAgIHp6W2ksIGMoIlBSSU5UX0tFWSIsInByY2xfaWR4IildIDwtIGJbYygiUFJJTlRfS0VZIiwicHJjbF9pZHgiKV0NCiAgfQ0KDQp9DQoNCmx0X2dxIDwtIHJiaW5kKHp6LHp6eikNCmx0X2dxJHR5cGUgPC0gImluX2dxIg0KbHRfZ3EgPC0gbGVmdF9qb2luKGx0X2dxLCBkZl9yZXNfZ3FfbGlzdFtjKCJwcmNsX2lkeCIsInVyYmFuX3J1cmFsIiwiUFJPUF9DTEFTUyIpXSkNCg0Kc2V0bmFtZXMobHRfZ3EsIG9sZD0iYWN0X3NpemUiLCBuZXc9ImhoX3NpemUiKQ0Kcm93bmFtZXMobHRfZ3EpIDwtIE5VTEwNCmBgYA0KDQojIyMgSW5kaXZpZHVhbCBVcmJhbi9SdXJhbA0KYGBge3J9DQojICMgZXhwb3J0DQojIHN5bl9oaF9ncV9wcmNsIDwtIHJiaW5kKHN5bl9oaF9wcmNsLCBsdF9ncVtjKCJHRU9JRCIsImhoX2lkIiwidHlwZSIsImhoX3NpemUiLCJwcmNsX2lkeCIsIlBSSU5UX0tFWSIsIlBST1BfQ0xBU1MiLCJ1cmJhbl9ydXJhbCIpXSkNCiMgd3JpdGUuY3N2KHN5bl9oaF9ncV9wcmNsLCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaG91c2Vob2xkX2dxX3BhcmNlbF9pZHhfdXJiYW5fcnVyYWwuY3N2Iiwgcm93Lm5hbWVzID0gRikNCg0KIyBJbmRpdmlkdWFsIC0gdXJiYW4vcnVyYWwNCnR0IDwtIGxlZnRfam9pbihzeW5faW5kLCBzeW5faGhfZ3FfcHJjbFtjKCJHRU9JRCIsImhoX2lkIiwidXJiYW5fcnVyYWwiKV0sIGJ5PWMoIkdFT0lEIj0iR0VPSUQiLCJoaF9pZCI9ImhoX2lkIikpDQojIHN5bl9pbmRfdXJiIDwtIHR0DQojIHdyaXRlLmNzdihzeW5faW5kX3VyYiwgImRhdGEvMDFfc3ludGhldGljX3BvcHVsYXRpb25fZGF0YXNldC9YWF8xMDE0X2luZGl2aWR1YWxfdXJiYW5fcnVyYWwuY3N2Iiwgcm93Lm5hbWVzID0gRikNCmBgYA0KDQoNCiMgV29ya2luZyBMb2NhdGlvbiANCiMjIExpc3Qgb2YgQ291bnR5IEJ1c2luZXNzDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSA1KSkgJT4lIHNldG5hbWVzKG5ldyA9IGMoImJ1c2luZXNzX2lkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lYW5pbmdfb2ZfRW1wbG95bWVudF9zaXplX29mX2VzdGFibGlzaG1lbnRzX2NvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW1wbG95bWVudF9zaXplX21pbiIsImVtcGxveW1lbnRfc2l6ZV9tYXgiLCJhY3Rfc2l6ZSIpKQ0KdHQgPC0gY3NzX2J1c2luZXNzX3NpemVbMjoxMCwgNDo3XQ0Kcm93bmFtZXModHQpIDwtIE5VTEwNCg0KZm9yIChpIGluIDE6bnJvdyh0dCkpIHsNCiAgYSA8LSB0dFtpLCAxOjNdDQogIGIgPC0gdHRbaSw0XQ0KICBkYXRhIDwtIGFbcmVwKHNlcV9sZW4obnJvdyhhKSksIGVhY2ggPSBiKSxdDQogIGRhdGEkYnVzaW5lc3NfaWQgPC0gTkENCiAgZGF0YSRhY3Rfc2l6ZSA8LSAwDQogIGRmIDwtIHJiaW5kKGRmLCBkYXRhKQ0KfQ0KDQpkZiA8LSBkZlshaXMubmEoZGYkTWVhbmluZ19vZl9FbXBsb3ltZW50X3NpemVfb2ZfZXN0YWJsaXNobWVudHNfY29kZSksXQ0Kcm93bmFtZXMoZGYpIDwtIE5VTEwNCmRmJGJ1c2luZXNzX2lkIDwtIHNwcmludGYoIk9yZ18lMDVkIiwgMTpucm93KGRmKSkNCg0KZGZfYnVzaW5lc3MgPC0gZGYNCmBgYA0KDQojIyBDb3VudHkgTGFib3IgRm9yY2UgUG9vbA0KMTogV2hvIGlzIGVtcGxveWVkIGJhc2VkIG9uICplbXBsb3ltZW50LXBvcHVsYXRpb24gcmF0aW8qDQoyLiBXaG8gaXMgd29ya2luZyBpbiBjb3VudHkgDQoNCkxhYm9yIEZvcmNlIFBhcnRpY2lwYXRpb24gUmF0ZSBpbmNsdWRlcyB0aGUgbnVtYmVycyBvZiBwZW9wbGUgd2l0aCBhIGpvYiBhcyB3ZWxsIGFzIHRoZSBudW1iZXIgYWN0aXZlbHkgKmxvb2tpbmcgZm9yKiB3b3JrLg0KV2UgdXNlZCAqZW1wbG95bWVudC1wb3B1bGF0aW9uIHJhdGlvKg0KYGBge3J9DQojIHNldCByYW5kb20gc2VlZCANCnNldC5zZWVkKHJtX3NlZWQpDQoNCiMgZGF0YXNldHMgDQpmb28gPC0gc3luX2luZF91cmIgICAjIGluZGl2aWR1YWwgDQoNCiMgaW5pdGlhbGl6ZSANCmZvbyRpZl9lbXBsb3llZCA8LSAwDQpmb28kd29rX3BsYWNlIDwtIDANCg0KIyByYW5kb21seSBzZWxlY3QgZW1wbG95ZWVkIGluZGl2aWR1YWxzIGF0IENPVU5UWSBsZXZlbA0KZm9yIChpIGluIDE6bnJvdyhjc3NfZW1wX3JhdGlvKSkgew0KICBhIDwtIGNzc19lbXBfcmF0aW9baSxdDQogIGRhdGEgPC0gZm9vICU+JSBmaWx0ZXIoYWdlID49IGEkYWdlX2xvICYgYWdlIDw9IGEkYWdlX2hpKQ0KDQogICMgY2FsY3VsYXRlICMgb2YgZW1wbG95ZWVkDQogIG0gPC0gcm91bmQobnJvdyhkYXRhKSAqIGEkRW1wbG95bWVudF9Qb3B1bGF0aW9uX1JhdGlvLDApDQogIGIgPC0gZGF0YSAlPiUgc2xpY2Vfc2FtcGxlKG4gPSBtKSAlPiUgcHVsbChpbmRfaWQpICAgICMgcmFuZG9tIHNlbGVjdGlvbg0KICBmb29bZm9vJGluZF9pZCAlaW4lIGIsXSRpZl9lbXBsb3llZCA8LSAxDQp9DQoNCiMgZGVjaWRlIHRoZWlyIHdvcmtpbmcgcGxhY2UgKGluL291dCBjb3VudHkpIGF0IGJsb2NrIGdyb3VwcyBsZXZlbA0KZm9yIChpIGluIDE6bGVuZ3RoKGx0X2dlb2lkX2JnKSkgew0KICBpZHggPC0gbHRfZ2VvaWRfYmdbW2ldXQ0KICBtIDwtIGNzc19lbXBfcGxhY2UgJT4lIGZpbHRlcihHRU9JRCA9PSBpZHgpDQogIGRhdGEgPC0gZm9vICU+JSBmaWx0ZXIoR0VPSUQgPT0gaWR4ICYgaWZfZW1wbG95ZWQgPT0gMSkNCiAgDQogICMgImluX2NvdW50eSIsICJvdXRfY291bnR5X29yX3N0YXRlIg0KICBuIDwtIHJvdW5kKG5yb3coZGF0YSkgKiBtJHBfd29rX2luX2NvdW50eSwgMCkNCiAgYSA8LSBkYXRhICU+JSBzbGljZV9zYW1wbGUobj1uKSAlPiUgcHVsbChpbmRfaWQpDQogIGZvb1tmb28kaW5kX2lkICVpbiUgZGF0YSRpbmRfaWQsIF0kd29rX3BsYWNlIDwtICJvdXRfY291bnR5X29yX3N0YXRlIg0KICBmb29bZm9vJGluZF9pZCAlaW4lIGEsXSR3b2tfcGxhY2UgPC0gImluX2NvdW50eSINCiAgDQogICMgQ0hFQ0sNCiAgIyBwcmludChwYXN0ZShpZHgsIChtJHR0bF93b2tlcnMgLSBucm93KGRhdGEpKSAvIG0kdHRsX3dva2Vycywgc2VwID0gIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIikpDQp9DQoNCiMgIyBFWFBPUlQgDQojIHdyaXRlLmNzdihmb28sICJkYXRhLzAxX3N5bnRoZXRpY19wb3B1bGF0aW9uX2RhdGFzZXQvWFhfMTAxNF9pbmRpdmlkdWFsX3VyYmFuX3J1cmFsX2VtcF9wbGFjZS5jc3YiLCByb3cubmFtZXMgPSBGKQ0KIyBzeW5faW5kX3VyYl9lbXAgPC0gZm9vDQpgYGANCg0KIyMgQWxsb2NhdGlvbiBpbi1jb3VudHkgV29yayBidXNpbmVzcw0KYGBge3J9DQojIHNldCByYW5kb20gc2VlZCANCnNldC5zZWVkKHJtX3NlZWQpDQoNCiMgZGF0YXNldHMgDQpmb28gPC0gc3luX2luZF91cmJfZW1wICAgIyBpbmRpdmlkdWFsIA0KenogPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDUsIG5yb3cgPSAwKSkgJT4lIHNldG5hbWVzKG5ldyA9IG5hbWVzKGRmX2J1c2luZXNzKSkNCg0KIyBpbml0aWFsaXplICYgZXh0cmFjdCBvbmx5IGluZGl2aWR1YWxzIHdvcmtpbmcgImluX2NvdW50eSINCmZvbyRidXNpbmVzc19pZCA8LSBOQQ0KZGF0YSA8LSBmb28gJT4lIGZpbHRlcih3b2tfcGxhY2UgPT0gImluX2NvdW50eSIpDQoNCiMgY3JlYXRlIGEgbGlzdCBvZiBwb3NpdGlvbnMgDQpmb3IgKGkgaW4gMTpucm93KGRmX2J1c2luZXNzKSkgew0KICBhIDwtIGRmX2J1c2luZXNzW2ksXQ0KICBhIDwtIGFbcmVwKHNlcV9sZW4obnJvdyhhKSksIGVhY2ggPSBhJGVtcGxveW1lbnRfc2l6ZV9tYXgpLF0NCiAgenogPC0gcmJpbmQoenosIGEpDQp9DQoNCnJvd25hbWVzKHp6KSA8LSBOVUxMDQp6eiRwb3NpdGlvbl9pZHggPC0gc3ByaW50ZigiUF8lMDVkIiwxOm5yb3coenopKQ0KDQojIGNvbm5lY3QgZW1wbG95ZWVzIHdpdGggcG9zaXRpb25zDQpmb3IgKGkgaW4gMTpucm93KGRhdGEpKSB7DQogICMgUmFuZG9tbHkgc2VsZWN0IGFuIGluLWNvdW50eSB3b3JrZXIgJiBhIGNvbXBhbnkNCiAgYSA8LSBkYXRhICU+JSBmaWx0ZXIoaXMubmEoYnVzaW5lc3NfaWQpKSAlPiUgc2xpY2Vfc2FtcGxlKG49MSkgJT4lIHB1bGwoaW5kX2lkKQ0KICBiIDwtIHp6ICU+JSBmaWx0ZXIoYWN0X3NpemUgPCAxKSAlPiUgc2xpY2Vfc2FtcGxlKG49MSkNCiAgDQogICMgYXNzaWduIHZhbHVlIGJhY2sgDQogIGRhdGFbZGF0YSRpbmRfaWQgPT0gYSwgXSRidXNpbmVzc19pZCA8LSBiJGJ1c2luZXNzX2lkDQogIHp6W3p6JHBvc2l0aW9uX2lkeCA9PSBiJHBvc2l0aW9uX2lkeCxdJGFjdF9zaXplIDwtIDENCn0NCg0Kenp6IDwtIHp6W3p6JGFjdF9zaXplID09MSxdDQp6enogPC0gdGFibGUoenp6JGJ1c2luZXNzX2lkKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQp6enogPC0gbGVmdF9qb2luKGRmX2J1c2luZXNzLCB6enosIGJ5PWMoImJ1c2luZXNzX2lkIj0iVmFyMSIpKSAlPiUgbXV0YXRlKGFjdF9zaXplID0gRnJlcSkgJT4lIHNlbGVjdCgtRnJlcSkNCg0KZm9vIDwtIGxlZnRfam9pbihzeW5faW5kX3VyYl9lbXAsIGRhdGFbYygiaW5kX2lkIiwiYnVzaW5lc3NfaWQiKV0pDQoNCiMgIyBFWFBPUlQNCiMgd3JpdGUuY3N2KGZvbywiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaW5kaXZpZHVhbF91cmJhbl9ydXJhbF9lbXBfcGxhY2Vfb3JnSUQuY3N2Iiwgcm93Lm5hbWVzID0gRikNCiMgc3luX2luZF91cmJfZW1wSUQgPC0gZm9vDQojIA0KIyBkZl9idXNpbmVzc19wb3AgPC0genp6WyFpcy5uYSh6enokYWN0X3NpemUpLF0NCiMgd3JpdGUuY3N2KGRmX2J1c2luZXNzX3BvcCwiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfYnVzaW5lc3NfaW5fY291bnR5X2FjdF9zaXplLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpgYGANCg0KIyBTY2hvb2wgTG9jYXRpb24gDQpgYGB7cn0NCiMgVW5pcXVlIFNjaG9vbCBkaXN0cmljdCBjb2RlOiBjX3NjaG9vbF9kaXN0WyJTRExDT0RFIl07IGNfcHViX2sxMlsiU0RMX0NPREUiXQ0KIyBVbmlxdWUgU2Nob29sIGlkOiBjX3B1Yl9rMTJbIlNETF9DT0RFIl0NCiMgdG1fc2hhcGUoY19zY2hvb2xfZGlzdFsiU0RMQ09ERSJdKSArIHRtX3BvbHlnb25zKGFscGhhID0gMCwgYm9yZGVyLmNvbCA9ICJyZWQiKSArIA0KIyAgIHRtX3NoYXBlKGNfcHViX2sxMlsiU0RMX0NPREUiXSkgKyB0bV9kb3RzKGNvbD0iYmx1ZSIpDQoNCmRhdGEgPC0gY19wdWJfazEyWyxjKDMsNDE6NTkpXSAgICMgdW5pcXVlIGlkOiBTRURfQ09ERQ0Kc3RfZ2VvbWV0cnkoZGF0YSkgPC0gTlVMTA0KIyBDcmVhdGUgYWdlIHJhbmdlIA0KbHQgPC0gc3ByaW50ZigiYWdlXyUwMmQiLCAzOjE5KQ0KZGF0YVssbHRdIDwtIE5BDQpkYXRhW2lzLm5hKGRhdGEpXSA8LSAwDQojIERlY2lkZSBhZ2UgcmFuZ2UgZm9yIGVhY2ggZ3JhZGUgDQpkYXRhW2RhdGEkR1JBREVfUEsgPT0gIlkiLCBjKCJhZ2VfMDMiLCJhZ2VfMDQiKV0gPC0gIlkiDQpkYXRhW2RhdGEkR1JBREVfRksgPT0gIlkiLCBjKCJhZ2VfMDQiLCJhZ2VfMDUiLCJhZ2VfMDYiKV0gPC0gIlkiDQpkYXRhW2RhdGEkR1JBREVfMSA9PSAiWSIsIGMoImFnZV8wNiIsImFnZV8wNyIpXSA8LSAiWSINCmRhdGFbZGF0YSRHUkFERV8yID09ICJZIiwgYygiYWdlXzA3IiwiYWdlXzA4IildIDwtICJZIg0KZGF0YVtkYXRhJEdSQURFXzMgPT0gIlkiLCBjKCJhZ2VfMDgiLCJhZ2VfMDkiKV0gPC0gIlkiDQpkYXRhW2RhdGEkR1JBREVfNCA9PSAiWSIsIGMoImFnZV8wOSIsImFnZV8xMCIpXSA8LSAiWSINCmRhdGFbZGF0YSRHUkFERV81ID09ICJZIiwgYygiYWdlXzEwIiwiYWdlXzExIildIDwtICJZIg0KZGF0YVtkYXRhJEdSQURFXzYgPT0gIlkiLCBjKCJhZ2VfMTEiLCJhZ2VfMTIiKV0gPC0gIlkiIA0KZGF0YVtkYXRhJEdSQURFXzcgPT0gIlkiLCBjKCJhZ2VfMTIiLCJhZ2VfMTMiKV0gPC0gIlkiDQpkYXRhW2RhdGEkR1JBREVfOCA9PSAiWSIsIGMoImFnZV8xMyIsImFnZV8xNCIpXSA8LSAiWSINCmRhdGFbZGF0YSRHUkFERV85ID09ICJZIiwgYygiYWdlXzE0IiwiYWdlXzE1IildIDwtICJZIg0KZGF0YVtkYXRhJEdSQURFXzEwID09ICJZIiwgYygiYWdlXzE1IiwiYWdlXzE2IildIDwtICJZIg0KZGF0YVtkYXRhJEdSQURFXzExID09ICJZIiwgYygiYWdlXzE2IiwiYWdlXzE3IildIDwtICJZIg0KZGF0YVtkYXRhJEdSQURFXzEyID09ICJZIiwgYygiYWdlXzE3IiwiYWdlXzE4IiwiYWdlXzE5IildIDwtICJZIg0KDQpkYXRhIDwtIGxlZnRfam9pbihjX3B1Yl9rMTJbLGMoIlNFRF9DT0RFIiwiU0RMX0NPREUiKV0sIGRhdGFbLGMoMSwyMTozNyldKQ0KY19wdWJfazEyX2NsbiA8LSBkYXRhDQpjX3B1Yl9rMTJfY2xuIDwtIHN0X2FzX3NmKGNfcHViX2sxMl9jbG4pDQpgYGANCg0KYGBge3J9DQojIElkZW50aWZ5IENoaWxkIGFnZSBiZXR3ZWVuIDItMTkgd2hvIGVucm9sbHMgaW4gc2Nob29sIA0KIyBjc3NfYWdlX3NjaG9vbF9lbnJvbGwNCmRhdGEgPC0gc3luX2luZF91cmJfZW1wSUQNCmRhdGEkaWZfc2Nob29sIDwtIDANCg0KZm9yIChpIGluIDE6bnJvdyhjc3NfYWdlX3NjaG9vbF9lbnJvbGwpKSB7DQogIGEgPC0gY3NzX2FnZV9zY2hvb2xfZW5yb2xsW2ksMjo1XSANCiAgYWdlX21pbiA8LSBhcy5udW1lcmljKGEkYWdlX21pbikNCiAgYWdlX21heCA8LSBhcy5udW1lcmljKGEkYWdlX21heCkNCiAgDQogIG0gPC0gZGF0YSAlPiUgZmlsdGVyKGFnZSA+PSBhZ2VfbWluICYgYWdlIDw9IGFnZV9tYXggJiBoaF9yb2xlICE9ICJpbl9ncSIpDQogIG4gPC0gcm91bmQobnJvdyhtKSAqIGEkRW5yb2xfc2Nob29sIC8gYSRUb3RhbCwgMCkNCiAgDQogIGRmIDwtIG0gJT4lIHNsaWNlX3NhbXBsZShuPW4pICU+JSBwdWxsKGluZF9pZCkNCiAgZGF0YVtkYXRhJGluZF9pZCAlaW4lIGRmLF0kaWZfc2Nob29sIDwtIDEgfQ0KDQojIEpvaW4gc2Nob29sZXJzIHdpdGggcGFyY2VsIGluZGV4IA0KZGF0YSA8LSBsZWZ0X2pvaW4oZGF0YSwgc3luX2hoX2dxX3ByY2xbYygiaGhfaWQiLCJHRU9JRCIsIlBSSU5UX0tFWSIpXSwgYnk9YygiaGhfaWQiPSJoaF9pZCIsIkdFT0lEIj0iR0VPSUQiKSkgJT4lIA0KICBmaWx0ZXIoaWZfc2Nob29sID09IDEgJiBoaF9yb2xlICE9ICJpbl9ncSIpDQpkYXRhJHNjaG9vbF9pZCA8LSAwDQoNCiMgY29udmVydCB0byBzZiBvYmplY3QgYW5kIGdldCBzY2hvb2wgZGlzdHJpY3QgSUQgDQpkYXRhIDwtIGxlZnRfam9pbihkYXRhLCBjX3BhcmNlbF9yWyJQUklOVF9LRVkiXSkgJT4lIHN0X2FzX3NmKCkgJT4lIHN0X2NlbnRyb2lkKCkNCmRhdGEgPC0gc3Rfam9pbihkYXRhLCBjX3NjaG9vbF9kaXN0WyJTRExDT0RFIl0sIGpvaW49c3RfaW50ZXJzZWN0cykNCmBgYA0KDQpgYGB7cn0NCiMgY19wdWJfazEyX2NsbiAtIGFnZSB2ZWN0b3IgDQpsdCA8LSBuYW1lcyhjX3B1Yl9rMTJfY2xuKVszOjE5XSAgICAjIGFnZSByYW5nZTogMyAtIDE5IHlycw0KDQpmb3IgKGkgaW4gMTpucm93KGRhdGEpKSB7DQogIGEgPC0gZGF0YVtpLCBdICAgICAgICAgIyBjaGlsZCBlbnJvbGxlZCBpbiBzY2hvb2wgDQogIGIgPC0gY19wdWJfazEyX2NsbiAlPiUgZmlsdGVyKGdldChsdFthJGFnZSAtIDJdKT09IlkiICYgU0RMX0NPREUgPT0gYSRTRExDT0RFKQ0KICBpZihucm93KGIpPjApew0KICAgIGIgPC0gYiAlPiUgc2xpY2Vfc2FtcGxlKG49MSkgJT4lIHB1bGwoIlNFRF9DT0RFIikNCiAgfSBlbHNlIHsNCiAgICBiIDwtIGNfcHViX2sxMl9jbG4gJT4lIGZpbHRlcihnZXQobHRbYSRhZ2UgLSAyXSk9PSJZIikgJT4lIHN0X2FzX3NmKCkNCiAgICBiIDwtIHN0X2pvaW4oYSwgYlsiU0VEX0NPREUiXSwgam9pbiA9IHN0X25lYXJlc3RfZmVhdHVyZSkgJT4lIHB1bGwoIlNFRF9DT0RFIikNCiAgfQ0KICANCiAgZGF0YVtpLF0kc2Nob29sX2lkIDwtIGINCiAgcHJpbnQoaSkNCn0NCg0KIyAjIHNhdmUgcmVzdWx0DQojIGRmIDwtIGRhdGENCiMgc3RfZ2VvbWV0cnkoZGYpIDwtIE5VTEwNCiMgZGYgPC0gbGVmdF9qb2luKHN5bl9pbmRfdXJiX2VtcElELCBkZltjKCJpbmRfaWQiLCJpZl9zY2hvb2wiLCJzY2hvb2xfaWQiLCJTRExDT0RFIildKQ0KIyANCiMgd3JpdGUuY3N2KGRmLCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaW5kaXZpZHVhbF91cmJhbl9ydXJhbF9lbXBfcGxhY2Vfb3JnSURfc2NoLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQojIHN5bl9pbmRfdXJiX2VtcElEX3NjaCA8LSBkZg0KYGBgDQoNCiMgTmV0d29yayANCiMjIFdvcmtpbmcgTmV0d29yayANCmBgYHtyfQ0KIyBzZXQgcmFuZG9tIHNlZWQgDQpzZXQuc2VlZChybV9zZWVkKQ0KDQojIE5ldHdvcmsgZGF0YSBjb250YWlubmVyIA0KZGZfbmV0d29yayA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCkpICU+JSBzZXRuYW1lcyhjKCJTb3VyY2UiLCJUYXJnZXQiLCJUeXBlIiwiUmVsYXRpb24iKSkNCm5uIDwtIGRmX25ldHdvcmsNCg0KZm9vIDwtIHN5bl9pbmRfdXJiX2VtcElEX3NjaCAgICMgaW5kaXZpZHVhbCANCnp6IDwtIGRmX2J1c2luZXNzX3BvcFshaXMubmEoZGZfYnVzaW5lc3NfcG9wJGFjdF9zaXplKSxdICMgYnVzaW5lc3MgDQp0aHJlaGQgPC0gMTAgICAgIyB0aGUgdGhyZXNob2xkIG9mIG5ldHdvcmsgc2l6ZSB0byBnZW5lcmF0ZSBhIGZ1bGwtY29ubmVjdGVkIG5ldHdvcmsgDQp6enogPC0genpbenokYWN0X3NpemUgPiB0aHJlaGQsXQ0KenogPC0genpbKHp6JGFjdF9zaXplIDw9IHRocmVoZCAmIHp6JGFjdF9zaXplID4gMSksXQ0KDQojIGZ1bGwgY29ubmVjdGVkIHdvcmtpbmcgbmV0d29yayANCmZvciAoaSBpbiAxOm5yb3coenopKSB7DQogIGlkeCA8LSB6eltpLF0kYnVzaW5lc3NfaWQNCiAgIyBBbGwgaW5kaXZpZHVhbHMgaW4gYSBwYXJ0aWN1bGFyIGNvbXBhbnkNCiAgZGF0YSA8LSBmb28gJT4lIGZpbHRlcihidXNpbmVzc19pZCA9PSBpZHgpDQogIGEgPC0gY29tYm4oZGF0YSRpbmRfaWQsMikNCiAgDQogIGRhdGEgPC0gZGF0YS5mcmFtZShTb3VyY2UgPSBhWzEsXSwNCiAgICAgICAgICAgICAgICAgICAgIFRhcmdldCA9IGFbMixdLA0KICAgICAgICAgICAgICAgICAgICAgVHlwZSA9ICJVbmRpcmVjdGVkIiwNCiAgICAgICAgICAgICAgICAgICAgIFJlbGF0aW9uID0gIldvcmsiKQ0KICBubiA8LSByYmluZChubiwgZGF0YSkNCiAgcHJpbnQoaSkNCn0NCg0KIyBTY2FsZSBmcmVlIHdvcmtpbmcgbmV0d29yayB3aXRoIG4gPjEwDQptbSA8LSBkZl9uZXR3b3JrDQoNCmx0IDwtIGMoImF2Z19kZWdyZWUiLCJkaWFtZXRlciIpDQp6enpbbHRdIDwtIE5BDQpmb3IgKGkgaW4gMTpucm93KHp6eikpIHsNCiAgaWR4IDwtIHp6eltpLF0kYnVzaW5lc3NfaWQNCiAgbiA8LSB6enpbaSxdJGFjdF9zaXplDQogIA0KICAjIEFsbCBpbmRpdmlkdWFscyBpbiBhIHBhcnRpY3VsYXIgY29tcGFueQ0KICBkYXRhIDwtIGZvbyAlPiUgZmlsdGVyKGJ1c2luZXNzX2lkID09IGlkeCkgJT4lIHB1bGwoaW5kX2lkKSAlPiUgc2FtcGxlKCkNCiAgDQogICMgR2VuZXJhdGUgTmV0d29yayANCiAgZyA8LSBzYW1wbGVfcGEobiwgbSA9IDYsIGRpcmVjdGVkID1GLCBwb3dlciA9IDEpDQogIHp6eltpLCBsdF0gPC0gYyhtZWFuKGRlZ3JlZShnKSksIGlncmFwaDo6ZGlhbWV0ZXIoZykpDQogIA0KICBnIDwtIGFzLmRhdGEuZnJhbWUoZ2V0LmVkZ2VsaXN0KGcpKSAlPiUgc2V0bmFtZXMobmV3PWMoIlNvdXJjZSIsIlRhcmdldCIpKQ0KICBnJFNvdXJjZSA8LSBtYXB2YWx1ZXMoZyRTb3VyY2UsIGZyb209YygxOm4pLCB0bz1kYXRhLCB3YXJuX21pc3NpbmcgPSBGKQ0KICBnJFRhcmdldCA8LSBtYXB2YWx1ZXMoZyRUYXJnZXQsIGZyb209YygxOm4pLCB0bz1kYXRhLCB3YXJuX21pc3NpbmcgPSBGKQ0KICBnJFR5cGUgPSAiVW5kaXJlY3RlZCINCiAgZyRSZWxhdGlvbiA9ICJXb3JrIg0KICANCiAgbW0gPC0gcmJpbmQobW0sIGcpDQogIHByaW50KGkpDQp9DQoNCm5uIDwtIHJiaW5kKG5uLCBtbSkNCm5uIDwtIG5uWyFpcy5uYShubiRTb3VyY2UpLF0NCg0KIyBleHBvcnQNCiMgbnR3a193b3JrIDwtIG5uICMgYXZlcmFnZSBkZWdyZWUgPSAxMDowMjUNCiMgd3JpdGUuY3N2KG50d2tfd29yaywgImRhdGEvMDJfbmV0d29ya19kYXRhc2V0L1hYX3N0ZXAyXzEwMTVfd29ya2luZ19uZXR3b3JrLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpgYGANCg0KIyMgRWR1Y2F0aW9uIE5ldHdvcmsgDQpgYGB7cn0NCiMgc2V0IHJhbmRvbSBzZWVkIA0Kc2V0LnNlZWQocm1fc2VlZCkNCg0KIyBOZXR3b3JrIGRhdGEgY29udGFpbm5lciANCmRmX25ldHdvcmsgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDQpKSAlPiUgc2V0bmFtZXMoYygiU291cmNlIiwiVGFyZ2V0IiwiVHlwZSIsIlJlbGF0aW9uIikpDQpubiA8LSBkZl9uZXR3b3JrDQoNCmZvbyA8LSBzeW5faW5kX3VyYl9lbXBJRF9zY2ggICAjIGluZGl2aWR1YWwgDQp6eiA8LSBjX3B1Yl9rMTJfY2xuICAgIyBwdWJsaWMgSy0xMiANCg0KdHQgPC0gdGFibGUoZm9vJHNjaG9vbF9pZCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgc2V0bmFtZXMobmV3PWMoIlNFRF9DT0RFIiwibl9jaGlsZCIpKQ0KenogPC0gbGVmdF9qb2luKHp6LCB0dCkNCmx0IDwtIGMoImF2Z19kZWdyZWUiLCJkaWFtZXRlciIpDQp6eltsdF0gPC0gTkENCg0KZm9yIChpIGluIDE6bnJvdyh6eikpIHsNCiAgaWR4IDwtIHp6W2ksXSRTRURfQ09ERQ0KICBuIDwtIHp6W2ksXSRuX2NoaWxkDQogIA0KICAjIEFsbCBjaGlsZCBpbiBhIHBhcnRpY3VsYXIgc2Nob29sDQogIGRhdGEgPC0gZm9vICU+JSBmaWx0ZXIoc2Nob29sX2lkID09IGlkeCkgJT4lIHB1bGwoaW5kX2lkKSAlPiUgc2FtcGxlKCkNCiAgDQogICMgR2VuZXJhdGUgbmV0d29yayBiYXNlZCBvbiBQQQ0KICBnIDwtIHNhbXBsZV9wYShuLCBtID0gMywgZGlyZWN0ZWQgPUYsIHBvd2VyID0gMSkNCiAgenpbaSwgbHRdIDwtIGMobWVhbihkZWdyZWUoZykpLCBpZ3JhcGg6OmRpYW1ldGVyKGcpKQ0KICANCiAgZyA8LSBhcy5kYXRhLmZyYW1lKGdldC5lZGdlbGlzdChnKSkgJT4lIHNldG5hbWVzKG5ldz1jKCJTb3VyY2UiLCJUYXJnZXQiKSkNCiAgZyRTb3VyY2UgPC0gbWFwdmFsdWVzKGckU291cmNlLCBmcm9tPWMoMTpuKSwgdG89ZGF0YSwgd2Fybl9taXNzaW5nID0gRikNCiAgZyRUYXJnZXQgPC0gbWFwdmFsdWVzKGckVGFyZ2V0LCBmcm9tPWMoMTpuKSwgdG89ZGF0YSwgd2Fybl9taXNzaW5nID0gRikNCiAgZyRUeXBlID0gIlVuZGlyZWN0ZWQiDQogIGckUmVsYXRpb24gPSAiU2Nob29sIg0KICANCiAgbm4gPC0gcmJpbmQobm4sIGcpDQogIHByaW50KGkpDQp9DQoNCmNfcHViX2sxMl9jbG5fbnR3ayA8LSB6eg0Kbm4gPC0gbm5bIWlzLm5hKG5uJFNvdXJjZSksXQ0KDQojICMgIGV4cG9ydA0KIyBudHdrX3NjaG9vbCA8LSBubg0KIyB3cml0ZS5jc3YobnR3a19zY2hvb2wsICJkYXRhLzAyX25ldHdvcmtfZGF0YXNldC9YWF9zdGVwM18xMDE1X2sxMl9zY2hvb2xfbmV0d29yay5jc3YiLCByb3cubmFtZXMgPSBGKQ0KYGBgDQoNCg0KIyMgR3JvdXAgUXVhcnRlciBOZXR3b3JrIA0KYGBge3J9DQojIGluZGl2aWR1YWwgbGl2aW5nIGluIGdyb3VwIHF1YXJ0ZXINCmZvbyA8LSBzeW5faW5kX3VyYl9lbXBJRF9zY2ggJT4lIGZpbHRlcihoaF9yb2xlPT0iaW5fZ3EiKSAlPiUgDQogIG11dGF0ZShoaF9pZDIgPSBwYXN0ZShoaF9pZCwgR0VPSUQsIHNlcD0iXyIpKSAgDQoNCnR0IDwtIGZvb1siaGhfaWQyIl0gJT4lIG11dGF0ZShhY3Rfc2l6ZSA9IDEpDQp0dCA8LSBhZ2dyZWdhdGUoYWN0X3NpemV+aGhfaWQyLCBkYXRhID0gdHQsIEZVTiA9c3VtKQ0KDQp0aHJlaGQgPC0gNSAgICAgIyB0aGUgdGhyZXNob2xkIG9mIG5ldHdvcmsgc2l6ZSB0byBnZW5lcmF0ZSBhIGZ1bGwtY29ubmVjdGVkIG5ldHdvcmsgDQp6enogPC0gdHRbdHQkYWN0X3NpemUgPiB0aHJlaGQsXQ0KenogPC0gdHRbKHR0JGFjdF9zaXplIDw9IHRocmVoZCAmIHR0JGFjdF9zaXplID4gMSksXQ0KDQojIGZ1bGwgY29ubmVjdGVkIGdxIG5ldHdvcmsgDQpubiA8LSBkZl9uZXR3b3JrDQpmb3IgKGkgaW4gMTpucm93KHp6KSkgew0KICBpZHggPC0genpbaSxdJGhoX2lkMg0KICAjIEFsbCBpbmRpdmlkdWFscyBpbiBhIHBhcnRpY3VsYXIgZ3ENCiAgZGF0YSA8LSBmb28gJT4lIGZpbHRlcihoaF9pZDIgPT0gaWR4KQ0KICBhIDwtIGNvbWJuKGRhdGEkaW5kX2lkLDIpDQogIA0KICBkYXRhIDwtIGRhdGEuZnJhbWUoU291cmNlID0gYVsxLF0sDQogICAgICAgICAgICAgICAgICAgICBUYXJnZXQgPSBhWzIsXSwNCiAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSAiVW5kaXJlY3RlZCIsDQogICAgICAgICAgICAgICAgICAgICBSZWxhdGlvbiA9ICJncSIpDQogIG5uIDwtIHJiaW5kKG5uLCBkYXRhKQ0KICBwcmludChpKQ0KfQ0KDQojIHNjYWxlLWZyZWUgZ3EgbmV0d29yayANCm1tIDwtIGRmX25ldHdvcmsNCmx0IDwtIGMoImF2Z19kZWdyZWUiLCJkaWFtZXRlciIpDQp6enpbbHRdIDwtIE5BDQpmb3IgKGkgaW4gMTpucm93KHp6eikpIHsNCiAgaWR4IDwtIHp6eltpLF0kaGhfaWQyDQogIG4gPC0genp6W2ksXSRhY3Rfc2l6ZQ0KICANCiAgIyBBbGwgaW5kaXZpZHVhbHMgaW4gYSBwYXJ0aWN1bGFyIGdxDQogIGRhdGEgPC0gZm9vICU+JSBmaWx0ZXIoaGhfaWQyID09IGlkeCkgJT4lIHB1bGwoaW5kX2lkKSAlPiUgc2FtcGxlKCkNCiAgDQogICMgR2VuZXJhdGUgTmV0d29yayANCiAgZyA8LSBzYW1wbGVfcGEobiwgbSA9IDMsIGRpcmVjdGVkID1GLCBwb3dlciA9IDEpDQogIHp6eltpLCBsdF0gPC0gYyhtZWFuKGRlZ3JlZShnKSksIGlncmFwaDo6ZGlhbWV0ZXIoZykpDQogIA0KICBnIDwtIGFzLmRhdGEuZnJhbWUoZ2V0LmVkZ2VsaXN0KGcpKSAlPiUgc2V0bmFtZXMobmV3PWMoIlNvdXJjZSIsIlRhcmdldCIpKQ0KICBnJFNvdXJjZSA8LSBtYXB2YWx1ZXMoZyRTb3VyY2UsIGZyb209YygxOm4pLCB0bz1kYXRhLCB3YXJuX21pc3NpbmcgPSBGKQ0KICBnJFRhcmdldCA8LSBtYXB2YWx1ZXMoZyRUYXJnZXQsIGZyb209YygxOm4pLCB0bz1kYXRhLCB3YXJuX21pc3NpbmcgPSBGKQ0KICBnJFR5cGUgPSAiVW5kaXJlY3RlZCINCiAgZyRSZWxhdGlvbiA9ICJncSINCiAgDQogIG1tIDwtIHJiaW5kKG1tLCBnKQ0KICBwcmludChpKQ0KfQ0KDQpubiA8LSByYmluZChubiwgbW0pDQpubiA8LSBublshaXMubmEobm4kU291cmNlKSxdDQojICMgIyAgZXhwb3J0DQojIG50d2tfZ3EgPC0gbm4NCiMgd3JpdGUuY3N2KG50d2tfZ3EsICJkYXRhLzAyX25ldHdvcmtfZGF0YXNldC9YWF9zdGVwNF8xMDE1X2dxX25ldHdvcmsuY3N2Iiwgcm93Lm5hbWVzID0gRikNCmBgYA0KDQojIyBTb2NpYWwgTWVkaWEgVXNhZ2UgLSBGYWNlYm9vaw0KIyMjIEFEVUxUIFVTRVIgWzE4LOKInikNCmBgYHtyfQ0KIyBJZGVudGlmeSBGQiB1c2VyczsgdXJiYW4gKDcwJSksIHJ1cmFsICg2NyUpOyANCiMgbWFsZSAoNjElKTsgZmVtYWxlICg3NyUpOyANCiMgYWdlIDE4LTI5ICg3MCUpOyBhZ2UgMzAtNDkgKDc3JSk7IGFnZSA1MC02NCAoNzMlKTsgYWdlIDYwKyAoNTAlKQ0KDQpmb28gPC0gc3luX2luZF91cmJfZW1wSURfc2NoW2MoImluZF9pZCIsInVyYmFuX3J1cmFsIiwiZ2VuZGVyIiwiYWdlIildICU+JSBmaWx0ZXIoYWdlID49IDE4KQ0KYnJrcyA8LSBjKDE3LDI5LDQ5LDY0LDEwMCkNCmxhYnMgPC0gYygiYWdlXzE4XzI5IiwiYWdlXzMwXzQ5IiwiYWdlXzUwXzY0IiwiYWdlXzY1b3ZlciIpDQpmb28kYWdlX2NhdCA8LSBjdXQoZm9vJGFnZSwgYnJlYWtzPWJya3MsIGxhYmVscyA9IGxhYnMpDQoNCmRhdGEgPC0gZm9vICU+JSBtdXRhdGUobj0xKQ0KZGF0YSA8LSBhZ2dyZWdhdGUobn51cmJhbl9ydXJhbCArIGdlbmRlciArIGFnZV9jYXQsIGRhdGEgPSBkYXRhW2MoInVyYmFuX3J1cmFsIiwiZ2VuZGVyIiwiYWdlX2NhdCIsIm4iKV0sIEZVTiA9IHN1bSkNCg0KZGF0YSA8LSBkYXRhW29yZGVyKGRhdGEkdXJiYW5fcnVyYWwsIGRhdGEkZ2VuZGVyLCBkYXRhJGFnZV9jYXQpLF0NCnJvdy5uYW1lcyhkYXRhKSA8LSBOVUxMDQoNCmRhdGEkaWR4IDwtIHNwcmludGYoInhfJTAyZCIsIDE6bnJvdyhkYXRhKSkNCmRhdGEkdmFsdWUgPC0gMQ0KDQpmb3IgKGkgaW4gMTozKSB7DQogIHR0IDwtIGRhdGFbLGMoaSw1LDYpXQ0KICBjb2xuYW1lcyh0dCkgPC0gYygidmFyIiwiaWR4IiwidmFsdWUiKQ0KICB0dCA8LSBkY2FzdCh0dCwgaWR4IH4gdmFyLCBmaWxsID0gMCwgdmFsdWUudmFyID0gInZhbHVlIikNCiAgZGF0YSA8LSBsZWZ0X2pvaW4oZGF0YSwgdHQsIGJ5PWMoImlkeCI9ImlkeCIpKQ0KfQ0KDQojIyBTb2x2ZSBhbiBsc2VpIHByb2JsZW0gICB8fEF4LUJ8fF4yDQojIyBMZWFzdCBTcXVhcmVzIHdpdGggRXF1YWxpdGllcyBhbmQgSW5lcXVhbGl0aWVzDQoNCiMgQTogY29lZmZpY2llbnQgbWF0cml4LiANCm1hdDEgPC0gYXMubWF0cml4KGRhdGFbNzoxNF0pICU+JSB0KCkNCg0KIyBCOiBudW1lcmljIHZlY3RvciBjb250YWluaW5nIHRoZSByaWdodC1oYW5kIHNpZGUuIHZhcmlhYmxlcyBpbiBvcmRlcjogcnVyYWwsIHVyYmFuLCBmZW1hbGUsIG1hbGUsYTE4LTI5LCBhMzAtNDksIGE1MC02NCwgYTY1Kw0KdHQgPC0gZGF0YQ0KdHRbLDc6MTRdIDwtIHR0Wyw3OjE0XSAqIHR0JG4NCnR0IDwtIGFwcGx5KHR0Wyw3OjE0XSwgMiwgc3VtKSANCnR0dCA8LSBtYXRyaXgoYygwLjY3LCAwLjcsIDAuNzcsIDAuNjEsIDAuNywgMC43NywgMC43MywgMC41KSwgDQogICAgICAgICAgICAgIG5yb3cgPSAxKQ0KbWF0MiA8LSB0dCAqIHR0dCAlPiUgdCgpDQoNCiMgSW5lcXVhbGl0eSBjb25zdHJhaW50cywgR3ggPj0gSA0KbWF0MyA8LSAgcmJpbmQoZGlhZygxNiksLTEgKiBkaWFnKDE2KSkNCm1hdDQgPC0gcmJpbmQobWF0cml4KDAsIG5yb3cgPSAxNiksIGFzLm1hdHJpeChkYXRhJG4pICogLTEpDQoNCmEgPC0gbHNlaShBID0gbWF0MSwgQiA9IG1hdDIsIEc9bWF0MywgSD1tYXQ0KQ0KYSA8LSBtYXRyaXgoYSRYLCBuY29sID0gMSkNCmRhdGEkcmVzdWx0IDwtIGENCg0KKG1hdDEgJSolIGEgLSBtYXQyKQ0KKG1hdDEgJSolIGEgLSBtYXQyKSAvIG1hdDINCg0KZGZfc21lZGlhX3VzZXIgPC0gZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YSA8LSBkZl9zbWVkaWFfdXNlcg0KenogPC0gZm9vDQp6eiRpZl9zb2NpYWxtZWRpYSA8LSAwDQoNCmZvciAoaSBpbiAxOm5yb3coZGF0YSkpIHsNCiAgYWEgPC0gZGF0YVtpLF0NCiAgbHRfdGVtcCA8LSB6eiAlPiUgZmlsdGVyKHVyYmFuX3J1cmFsID09IGFhJHVyYmFuX3J1cmFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZGVyID09IGFhJGdlbmRlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9jYXQgPT0gYWEkYWdlX2NhdCkgJT4lIA0KICAgIHNsaWNlX3NhbXBsZShuPWFhJHJlc3VsdCkgJT4lIHB1bGwoaW5kX2lkKQ0KICB6elt6eiRpbmRfaWQgJWluJSBsdF90ZW1wLF0kaWZfc29jaWFsbWVkaWEgPSAxDQp9DQoNCiMgY2hlY2sgDQpucm93KHp6W3p6JHVyYmFuX3J1cmFsPT0icnVyYWwiICYgenokaWZfc29jaWFsbWVkaWE9PTEsXSkgLyBucm93KHp6W3p6JHVyYmFuX3J1cmFsPT0icnVyYWwiLF0pIA0KbnJvdyh6elt6eiR1cmJhbl9ydXJhbD09InVyYmFuIiAmIHp6JGlmX3NvY2lhbG1lZGlhPT0xLF0pIC8gbnJvdyh6elt6eiR1cmJhbl9ydXJhbD09InVyYmFuIixdKQ0KbnJvdyh6elt6eiRnZW5kZXI9PSJNYWxlIiAmIHp6JGlmX3NvY2lhbG1lZGlhPT0xLF0pIC8gbnJvdyh6elt6eiRnZW5kZXI9PSJNYWxlIixdKQ0KbnJvdyh6elt6eiRnZW5kZXI9PSJGZW1hbGUiICYgenokaWZfc29jaWFsbWVkaWE9PTEsXSkgLyBucm93KHp6W3p6JGdlbmRlcj09IkZlbWFsZSIsXSkNCm5yb3coenpbenokYWdlX2NhdD09ImFnZV8xOF8yOSIgJiB6eiRpZl9zb2NpYWxtZWRpYT09MSxdKSAvIG5yb3coenpbenokYWdlX2NhdD09ImFnZV8xOF8yOSIsXSkNCm5yb3coenpbenokYWdlX2NhdD09ImFnZV8zMF80OSIgJiB6eiRpZl9zb2NpYWxtZWRpYT09MSxdKSAvIG5yb3coenpbenokYWdlX2NhdD09ImFnZV8zMF80OSIsXSkNCm5yb3coenpbenokYWdlX2NhdD09ImFnZV81MF82NCIgJiB6eiRpZl9zb2NpYWxtZWRpYT09MSxdKSAvIG5yb3coenpbenokYWdlX2NhdD09ImFnZV81MF82NCIsXSkNCm5yb3coenpbenokYWdlX2NhdD09ImFnZV82NW92ZXIiICYgenokaWZfc29jaWFsbWVkaWE9PTEsXSkgLyBucm93KHp6W3p6JGFnZV9jYXQ9PSJhZ2VfNjVvdmVyIixdKQ0KYGBgDQoNCmBgYHtyfQ0KenogPC0gbGVmdF9qb2luKHN5bl9pbmRfdXJiX2VtcElEX3NjaCwgenpbYygiaW5kX2lkIiwiaWZfc29jaWFsbWVkaWEiKV0pDQp6eltpcy5uYSh6eiRpZl9zb2NpYWxtZWRpYSksXSRpZl9zb2NpYWxtZWRpYSA8LSAwDQoNCiMgIyBleHBvcnQNCiMgd3JpdGUuY3N2KHp6LCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaW5kaXZpZHVhbF91cmJhbl9ydXJhbF9lbXBfcGxhY2Vfb3JnSURfc2NoX3NtZWRpYS5jc3YiLCByb3cubmFtZXMgPSBGKQ0KIyBzeW5faW5kX3VyYl9lbXBJRF9zY2hfc21lZGlhIDwtIHp6DQpgYGANCiMjIyBBZHVsdDogU29jaWFsIE1lZGlhIE5ldHdvcmsgDQphdmVyYWdlIGRlZ3JlZSBpbiBjb3VudHk6IDY0DQpgYGB7cn0NCiMgc2V0IHJhbmRvbSBzZWVkIA0Kc2V0LnNlZWQocm1fc2VlZCkNCg0KIyBOZXR3b3JrIGRhdGEgY29udGFpbm5lciANCmRmX25ldHdvcmsgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDQpKSAlPiUgc2V0bmFtZXMoYygiU291cmNlIiwiVGFyZ2V0IiwiVHlwZSIsIlJlbGF0aW9uIikpDQpubiA8LSBkZl9uZXR3b3JrDQoNCiMgRmlsdGVyIGFkdWx0cyB1c2luZyBzb2NpYWwgbWVkaWENCmRhdGEgPC0gc3luX2luZF91cmJfZW1wSURfc2NoX3NtZWRpYSAlPiUgZmlsdGVyKGlmX3NvY2lhbG1lZGlhPT0xKSAlPiUgcHVsbChpbmRfaWQpICU+JSBzYW1wbGUoKSAgIyBpbmRpdmlkdWFsIA0KbiA8LSBsZW5ndGgoZGF0YSkNCiMgU2NhbGUgZnJlZSBzb2NpYWwgbWVkaWEgbmV0d29yay4gYXZnID0gNjQNCiMgR2VuZXJhdGUgTmV0d29yayANCmcgPC0gc2FtcGxlX3BhKG4sIG0gPSAyNSwgZGlyZWN0ZWQgPUYsIHBvd2VyID0gMSkNCm1lYW4oZGVncmVlKGcpKQ0KDQpnIDwtIGFzLmRhdGEuZnJhbWUoZ2V0LmVkZ2VsaXN0KGcpKSAlPiUgc2V0bmFtZXMobmV3PWMoIlNvdXJjZSIsIlRhcmdldCIpKQ0KZyRTb3VyY2UgPC0gbWFwdmFsdWVzKGckU291cmNlLCBmcm9tPWMoMTpuKSwgdG89ZGF0YSwgd2Fybl9taXNzaW5nID0gRikNCmckVGFyZ2V0IDwtIG1hcHZhbHVlcyhnJFRhcmdldCwgZnJvbT1jKDE6biksIHRvPWRhdGEsIHdhcm5fbWlzc2luZyA9IEYpDQpnJFR5cGUgPSAiVW5kaXJlY3RlZCINCmckUmVsYXRpb24gPSAiU29jaWFsTWVkaWEiDQoNCiMgIyBleHBvcnQNCiMgbnR3a19mYiA8LSBnDQojIHdyaXRlLmNzdihudHdrX2ZiLCAiZGF0YS8wMl9uZXR3b3JrX2RhdGFzZXQvWFhfc3RlcDVfMTAxNV9zb2NpYWxfbWVkaWFfbmV0d29ya19hZHVsdC5jc3YiLCByb3cubmFtZXMgPSBGKQ0KYGBgDQoNCiMjIyBURUVOUyBVU0VSWzEzLCAxN10NCmBgYHtyfQ0KIyBUZWVuIEZCIFVzZXJzOiB1cmJhbiAoNDAlKSwgcnVyYWwgKDQzJSkNCiMgICAgICAgICAgICAgICAgYm95ICgzMSUpLCBnaXJsICgzNCUpDQojICAgICAgICAgICAgICAgIGFnZSAxMy0xNCAoMjMlKSwgYWdlIDE1LTE3ICgzOSUpDQoNCiMjIEZpbHRlciBvdXQgdGVlbmFnZXJzIA0KZm9vIDwtIHN5bl9pbmRfdXJiX2VtcElEX3NjaF9zbWVkaWFbYygiaW5kX2lkIiwidXJiYW5fcnVyYWwiLCJnZW5kZXIiLCJhZ2UiICldICU+JSANCiAgZmlsdGVyKGFnZT49MTMgJiBhZ2UgPD0xNykNCmZvbyRhZ2VfY3V0IDwtIGN1dChmb28kYWdlLCBicmVha3MgPSBjKDEyLDE0LDIwKSwgbGFiZWxzID0gYygiYWdlXzEzXzE0IiwiYWdlXzE1XzE3IikpDQoNCiMjIEFnZ3JlZ2F0ZSB0ZWVucyBpbnRvIGNsYXNzZXMgYmFzZWQgb24gZGlmZmVyZW50IGFnZS9nZW5kZXIvdXJiYW4tcnVyYWwgY29tYmluYXRpb25zDQpkYXRhIDwtIGZvb1tjKCJ1cmJhbl9ydXJhbCIsImdlbmRlciIsImFnZV9jdXQiKV0gJT4lIG11dGF0ZShuPTEpDQpkYXRhIDwtIGFnZ3JlZ2F0ZShufnVyYmFuX3J1cmFsICsgZ2VuZGVyICsgYWdlX2N1dCwgZGF0YSA9IGRhdGEsIEZVTj1zdW0pDQpkYXRhIDwtIGRhdGEgJT4lIG11dGF0ZShpZHggPSBzcHJpbnRmKCJYXyUwMmQiLCAxOm5yb3coZGF0YSkpLCB2YWx1ZSA9IDEpDQoNCmZvciAoaSBpbiAxOjMpIHsNCiAgYSA8LSBkYXRhWywgYyhpLDUsNildDQogIGNvbG5hbWVzKGEpIDwtIGMoInZhciIsImlkeCIsInZhbHVlIikNCiAgYSA8LSBkY2FzdChhLCBpZHggfiB2YXIsIGZpbGwgPSAwLCB2YWx1ZS52YXIgPSAidmFsdWUiKQ0KICBkYXRhIDwtIGxlZnRfam9pbihkYXRhLCBhLCBieT1jKCJpZHgiID0gImlkeCIpKQ0KfQ0KDQojIyBDb2VmZmljaWVudCBNYXRyaXgNCm1hdDEgPC0gYXMubWF0cml4KGRhdGFbLDc6MTJdKSAlPiUgdCgpDQojIyBSaWdodC1oYW5kDQp0dCA8LSBkYXRhWyw3OjEyXSAqIGRhdGEkbg0KdHQgPC0gYXBwbHkodHQsIDIsIHN1bSkNCiMgdHR0IDwtIG1hdHJpeChkYXRhPWMoMC40MywgMC40LCAwLjM0LCAwLjMxLCAwLjIzLCAwLjM5KSwgbnJvdyA9IDEpICAjIGZhY2Vib29rDQp0dHQgPC0gbWF0cml4KGRhdGE9YygwLjYyLCAwLjU4LCAwLjY0LCAwLjU0LCAwLjUxLCAwLjY1KSwgbnJvdyA9IDEpICAjIHNuYXBjaGF0DQptYXQyIDwtIHR0ICogdHR0ICU+JSB0KCkNCiMjIEluZXF1YWxpdHkgY29uc3RyYWludHMNCm1hdDMgPC0gcmJpbmQoZGlhZyg4KSwgLTEqZGlhZyg4KSkNCm1hdDQgPC0gcmJpbmQobWF0cml4KDAsIG5yb3cgPSA4KSwgYXMubWF0cml4KGRhdGEkbikgKiAtMSkNCg0KIyByZXNvbHZlDQphIDwtIGxzZWkoQT1tYXQxLCBCPW1hdDIsIEc9bWF0MywgSD1tYXQ0KQ0KYSA8LSBtYXRyaXgoYSRYLCBuY29sPTEpDQoNCihtYXQxICUqJSBhIC0gbWF0MikvbWF0MiANCg0KZGF0YSRyZXN1bHQgPC0gYQ0KZGZfc21lZGlhX3RlZW5fdXNlciA8LSBkYXRhDQpgYGANCg0KYGBge3J9DQpkYXRhIDwtIGRmX3NtZWRpYV90ZWVuX3VzZXINCnp6IDwtIGZvbw0KenokaWZfc29jaWFsbWVkaWFfdGVlbiA8LSAwDQoNCmZvciAoaSBpbiAxOm5yb3coZGF0YSkpIHsNCiAgYWEgPC0gZGF0YVtpLF0NCiAgbHRfdGVtcCA8LSB6eiAlPiUgZmlsdGVyKHVyYmFuX3J1cmFsID09IGFhJHVyYmFuX3J1cmFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZGVyID09IGFhJGdlbmRlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9jdXQgPT0gYWEkYWdlX2N1dCkgJT4lIA0KICAgIHNsaWNlX3NhbXBsZShuPWFhJHJlc3VsdCkgJT4lIHB1bGwoaW5kX2lkKQ0KICB6elt6eiRpbmRfaWQgJWluJSBsdF90ZW1wLF0kaWZfc29jaWFsbWVkaWFfdGVlbiA8LSAxDQp9DQoNCiMjIFZhbGlkYXRlDQp6eiAlPiUgZmlsdGVyKGlmX3NvY2lhbG1lZGlhX3RlZW49PTEgJiBnZW5kZXI9PSJNYWxlIikgJT4lIG5yb3coKSAvIG5yb3coenogJT4lIGZpbHRlcihnZW5kZXI9PSJNYWxlIikpDQp6eiAlPiUgZmlsdGVyKGlmX3NvY2lhbG1lZGlhX3RlZW49PTEgJiBnZW5kZXI9PSJGZW1hbGUiKSAlPiUgbnJvdygpIC8gbnJvdyh6eiAlPiUgZmlsdGVyKGdlbmRlcj09IkZlbWFsZSIpKQ0KenogJT4lIGZpbHRlcihpZl9zb2NpYWxtZWRpYV90ZWVuPT0xICYgYWdlX2N1dD09ImFnZV8xM18xNCIpICU+JSBucm93KCkgLyBucm93KHp6ICU+JSBmaWx0ZXIoYWdlX2N1dD09ImFnZV8xM18xNCIpKQ0KenogJT4lIGZpbHRlcihpZl9zb2NpYWxtZWRpYV90ZWVuPT0xICYgYWdlX2N1dD09ImFnZV8xNV8xNyIpICU+JSBucm93KCkgLyBucm93KHp6ICU+JSBmaWx0ZXIoYWdlX2N1dD09ImFnZV8xNV8xNyIpKQ0KenogJT4lIGZpbHRlcihpZl9zb2NpYWxtZWRpYV90ZWVuPT0xICYgdXJiYW5fcnVyYWw9PSJ1cmJhbiIpICU+JSBucm93KCkgLyBucm93KHp6ICU+JSBmaWx0ZXIodXJiYW5fcnVyYWw9PSJ1cmJhbiIpKQ0KenogJT4lIGZpbHRlcihpZl9zb2NpYWxtZWRpYV90ZWVuPT0xICYgdXJiYW5fcnVyYWw9PSJydXJhbCIpICU+JSBucm93KCkgLyBucm93KHp6ICU+JSBmaWx0ZXIodXJiYW5fcnVyYWw9PSJydXJhbCIpKQ0KYGBgDQoNCg0KYGBge3J9DQp6eiA8LSBsZWZ0X2pvaW4oc3luX2luZF91cmJfZW1wSURfc2NoX3NtZWRpYSwgenpbYygiaW5kX2lkIiwiaWZfc29jaWFsbWVkaWFfdGVlbiIpXSkNCnp6W2lzLm5hKHp6JGlmX3NvY2lhbG1lZGlhX3RlZW4pLF0kaWZfc29jaWFsbWVkaWFfdGVlbiA8LSAwDQoNCiMgIyBleHBvcnQNCiMgd3JpdGUuY3N2KHp6LCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzEwMTRfaW5kaXZpZHVhbF91cmJhbl9ydXJhbF9lbXBfcGxhY2Vfb3JnSURfc2NoX3NtZWRpYV90ZWVuLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQojIHN5bl9pbmRfdXJiX2VtcElEX3NjaF9zbWVkaWFfdGVlbiA8LSB6eg0KYGBgDQojIyMgVGVlbjogU29jaWFsIE5ldHdvcmtzIA0KYGBge3J9DQpzZXQuc2VlZChybV9zZWVkKQ0KDQojIE5ldHdvcmsgZGF0YSBjb250YWlubmVyIA0KZGZfbmV0d29yayA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCkpICU+JSBzZXRuYW1lcyhjKCJTb3VyY2UiLCJUYXJnZXQiLCJUeXBlIiwiUmVsYXRpb24iKSkNCm5uIDwtIGRmX25ldHdvcmsNCg0KIyBGaWx0ZXIgdGVlbnMgd2l0aCBzb2NpYWwgbWVkaWEgYWNjZXNzDQpkYXRhIDwtIHN5bl9pbmRfdXJiX2VtcElEX3NjaF9zbWVkaWFfdGVlbiAlPiUgZmlsdGVyKGlmX3NvY2lhbG1lZGlhX3RlZW49PTEpICU+JSBwdWxsKGluZF9pZCkgJT4lIHNhbXBsZSgpICAjIGluZGl2aWR1YWwgDQpuIDwtIGxlbmd0aChkYXRhKQ0KDQojIEdlbmVyYXRlIE5ldHdvcmsgKGF2Zy4gZGVncmVlID0gNTApDQpnIDwtIHNhbXBsZV9wYShuLCBtID0gMjUsIGRpcmVjdGVkID1GLCBwb3dlciA9IDEpDQojIG1lYW4oZGVncmVlKGcpKQ0KZyA8LSBhcy5kYXRhLmZyYW1lKGdldC5lZGdlbGlzdChnKSkgJT4lIHNldG5hbWVzKG5ldz1jKCJTb3VyY2UiLCJUYXJnZXQiKSkNCmckU291cmNlIDwtIG1hcHZhbHVlcyhnJFNvdXJjZSwgZnJvbT1jKDE6biksIHRvPWRhdGEsIHdhcm5fbWlzc2luZyA9IEYpDQpnJFRhcmdldCA8LSBtYXB2YWx1ZXMoZyRUYXJnZXQsIGZyb209YygxOm4pLCB0bz1kYXRhLCB3YXJuX21pc3NpbmcgPSBGKQ0KZyRUeXBlID0gIlVuZGlyZWN0ZWQiDQpnJFJlbGF0aW9uID0gIlNvY2lhbE1lZGlhX3RlZW4iDQoNCiMgIyBleHBvcnQNCiMgbnR3a19zbWVkaWFfdGVlbiA8LSBnDQojIHdyaXRlLmNzdihudHdrX3NtZWRpYV90ZWVuLCAiZGF0YS8wMl9uZXR3b3JrX2RhdGFzZXQvWFhfc3RlcDZfMTAxNV9zb2NpYWxfbWVkaWFfbmV0d29ya190ZWVuLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpgYGANCg0KDQojIFN1bW1hcnlfTmV0d29yaw0KIyMgQXNzaWduIGluZGl2aWR1YWwgd2l0aCBwYXJjZWwgKGNvb3JkaW5hdGVzKQ0KYGBge3J9DQojIGpvaW4gaW5kaXZpZHVhbCB3aXRoIHBhcmNlbHMgDQpkZiA8LSBsZWZ0X2pvaW4oc3luX2luZF91cmJfZW1wSURfc2NoX3NtZWRpYV90ZWVuLCANCiAgICAgICAgICAgICAgICBzeW5faGhfZ3FfcHJjbFtjKCJHRU9JRCIsImhoX2lkIiwicHJjbF9pZHgiLCJQUklOVF9LRVkiKV0sIGJ5PWMoIkdFT0lEIj0iR0VPSUQiLCJoaF9pZCI9ImhoX2lkIikpDQpkZiA8LSBsZWZ0X2pvaW4oZGYsIGNfcGFyY2VsWyJQUklOVF9LRVkiXSkgJT4lIHN0X2FzX3NmKCkNCg0KIyAjIGdldCBjZW50cm9pZCBvZiBwYXJjZWwgYW5kIHRyYW5zZm9ybSB0byB3Z3M4NCAoNDMyNikNCiMgenogPC0gZGYgJT4lIHN0X2NlbnRyb2lkKCkgJT4lICBzdF9hc19zZigpICU+JSBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNikNCg0KIyBrZWVwIHByb2plY3QgDQp6eiA8LSBkZiAlPiUgc3RfY2VudHJvaWQoKSAlPiUgc3RfYXNfc2YoKQ0Kenp6IDwtIHp6ICU+JSBtdXRhdGUobG9uZyA9IHVubGlzdChtYXAoenokZ2VvbWV0cnksMSkpLCBsYXQgPSB1bmxpc3QobWFwKHp6JGdlb21ldHJ5LDIpKSkNCnN0X2dlb21ldHJ5KHp6eikgPC0gTlVMTA0KDQojIEFkZCByYW5kb21uZXNzIHRvIGNlbnRyb2lkDQojIGFhIDwtIDAuMDAxICMgd2dzIDg0DQphYSA8LSAwLjA1ICAjIE5BRCA4Mw0Kenp6JGxvbmcgPC0genp6JGxvbmcgKyBydW5pZihucm93KHp6eiksIC1hYSwgYWEpDQp6enokbGF0IDwtIHp6eiRsYXQrIHJ1bmlmKG5yb3coenp6KSwgLWFhLCBhYSkNCnJvd25hbWVzKHp6eikgPC0gTlVMTA0Kenp6WyJpbmRfbmV3X2lkIl0gPC0gMTpucm93KHp6eikgLSAxDQojIFB5dGhvbiBleHBvcnQgDQp3cml0ZS5jc3Yoenp6LCAiZGF0YS8wMV9zeW50aGV0aWNfcG9wdWxhdGlvbl9kYXRhc2V0L1hYXzk5OV9tb2RlbF9pbmRpdmlkdWFsX3VyYmFuX3J1cmFsX2VtcF9wbGFjZV9vcmdJRF9zY2hfc21lZGlhX3RlZW5fTkFEODMuY3N2Iiwgcm93Lm5hbWVzID0gRikNCg0KDQoNCiMgdHQgPC0gc3RfYXNfc2Yoenp6LCBjb29yZHMgPSBjKCJsb25nIiwibGF0IiksIGNycz00MzI2KQ0KIyANCiMgdG1hcF9tb2RlKCJwbG90IikNCiMgdG1fc2hhcGUodHQpICsgdG1fZG90cygpDQoNCiMgIyBFeHBvcnQNCiMgd3JpdGUuY3N2KHp6eiwgImRhdGEvMDFfc3ludGhldGljX3BvcHVsYXRpb25fZGF0YXNldC9YWF8xMzExX2luZGl2aWR1YWxfdXJiYW5fcnVyYWxfZW1wX3BsYWNlX29yZ0lEX3NjaF9zbWVkaWFfdGVlbl9jb29yZC5jc3YiLCByb3cubmFtZXMgPSBGKQ0KIyBzeW5faW5kX3VyYl9lbXBJRF9zY2hfc21lZGlhX3RlZW5fY29vcmQgPC0genp6DQpgYGANCg0KIyMgQ09NQklORSAmIFNBVkUgYWxsIG5ldHdvcmtzIA0KYGBge3J9DQpudHdrX2ZhbWlseSA8LSByZWFkLmNzdigiZGF0YS8wMl9uZXR3b3JrX2RhdGFzZXQvc3RlcDFfMDgzMV9mYW1pbHlfbmV0d29yay5jc3YiKVssMjo1XQ0KbnR3a19hbGwgPC0gcmJpbmQobnR3a19mYW1pbHksDQogICAgICAgICAgICAgICAgICBudHdrX3dvcmssDQogICAgICAgICAgICAgICAgICBudHdrX3NjaG9vbCwNCiAgICAgICAgICAgICAgICAgIG50d2tfZ3EsDQogICAgICAgICAgICAgICAgICBudHdrX2ZiLA0KICAgICAgICAgICAgICAgICAgbnR3a19zbWVkaWFfdGVlbikNCg0KIyBleHBvcnQgbmV0d29yayAtIHNhdmUNCiMgd3JpdGUuY3N2KG50d2tfYWxsLCAiZGF0YS8wMl9uZXR3b3JrX2RhdGFzZXQveHhfc3RlcF9maW5sX2FsbF9uZXR3b3JrLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpgYGANCg0KIyMjIE5ldHdvcmsgZXhwb3J0IA0KYGBge3J9DQpkYXRhIDwtIG50d2tfYWxsDQpkYXRhIDwtIGxlZnRfam9pbihkYXRhLCB6enpbYygiaW5kX2lkIiwiaW5kX25ld19pZCIpXSwgYnk9YygiU291cmNlIj0iaW5kX2lkIikpDQpuYW1lcyhkYXRhKVtbNV1dIDwtICJzb3VyY2VfcmVpbmRleCINCmRhdGEgPC0gbGVmdF9qb2luKGRhdGEsIHp6eltjKCJpbmRfaWQiLCJpbmRfbmV3X2lkIildLCBieT1jKCJUYXJnZXQiPSJpbmRfaWQiKSkNCm5hbWVzKGRhdGEpW1s2XV0gPC0gInRhcmdldF9yZWluZGV4Ig0KDQp3cml0ZS5jc3YoZGF0YSwgImRhdGEvMDJfbmV0d29ya19kYXRhc2V0L3N0ZXA5OTlfbW9kZWxfc3RlcF9maW5sX2FsbF9uZXR3b3JrLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpgYGANCg0KDQojIyMgTmV0d29yayBhdHRyaWJ1dGVzIA0KYGBge3J9DQojIDEuIHBoeXNpY2FsIHNwYWNlOiBub2RlcyAmIGVkZ2VzIA0KbGVuZ3RoKHVuaXF1ZShjKG50d2tfZmFtaWx5JFNvdXJjZSwgbnR3a19mYW1pbHkkVGFyZ2V0LCBudHdrX2dxJFNvdXJjZSwgbnR3a19ncSRUYXJnZXQpKSkNCm5yb3cobnR3a19mYW1pbHkpICsgbnJvdyhudHdrX2dxKQ0KDQojIDIuMSByZWxhdGlvbmFsIHNwYWNlIC0gc2Nob29sOiBub2RlcyAmIGVkZ2VzIA0KbGVuZ3RoKHVuaXF1ZShjKG50d2tfc2Nob29sJFNvdXJjZSwgbnR3a19zY2hvb2wkVGFyZ2V0KSkpDQpucm93KG50d2tfc2Nob29sKQ0KDQojIDIuMiByZWxhdGlvbmFsIHNwYWNlIC0gd29yazogbm9kZXMgJiBlZGdlcw0KbGVuZ3RoKHVuaXF1ZShjKG50d2tfd29yayRTb3VyY2UsIG50d2tfd29yayRUYXJnZXQpKSkNCm5yb3cobnR3a193b3JrKQ0KDQojIDMuIGN5YmVyIHNwYWNlIC0gc29jaWFsIG1lZGlhIA0KbGVuZ3RoKHVuaXF1ZShjKG50d2tfZmIkU291cmNlLCBudHdrX2ZiJFRhcmdldCwgbnR3a19zbWVkaWFfdGVlbiRTb3VyY2UsIG50d2tfc21lZGlhX3RlZW4kVGFyZ2V0KSkpDQpucm93KG50d2tfZmIpICsgbnJvdyhudHdrX3NtZWRpYV90ZWVuKQ0KYGBgDQoNCg0KIyMgRXhwb3J0IA0KYGBge3J9DQp0dCA8LSBzeW5faW5kX3VyYl9lbXBJRF9zY2hfc21lZGlhX3RlZW5fY29vcmQNCnR0W3R0JGhoX3JvbGU9PSJpbl9ncSIsXSRoaF9pZCA8LSBwYXN0ZSh0dFt0dCRoaF9yb2xlPT0iaW5fZ3EiLF0kaGhfaWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHRbdHQkaGhfcm9sZT09ImluX2dxIixdJEdFT0lELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iXyIpDQpjb2xuYW1lcyh0dClbMV0gPC0gIklkIg0Kbm9kZXMgPC0gdHRbdHQkSWQgJWluJSBjKG50d2tfYWxsJFNvdXJjZSwgbnR3a19hbGwkVGFyZ2V0KSxdDQoNCiMgIyBleHBvcnQgZWRnZXMgLSBnZXBoaQ0KIyB3cml0ZS5jc3YobnR3a19hbGwsICJkYXRhLzAyX25ldHdvcmtfZGF0YXNldC9uZXR3b3JrLzIwMjN4eHh4X25ldHdvcmtfZWRnZS5jc3YiLCByb3cubmFtZXMgPSBGKQ0KIyAjIGV4cG9ydCBub2RlcyAtIGdlcGhpDQojIHdyaXRlLmNzdihub2RlcywgImRhdGEvMDJfbmV0d29ya19kYXRhc2V0L25ldHdvcmsvMjAyM3h4eHhfbmV0d29ya19ub2Rlcy5jc3YiLCByb3cubmFtZXMgPSBGKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMjIFBsb3QgDQpwZGYoInBsb3QvZ2VvX3NjaG9vbF9kaXN0cmljdC5wZGYiKQ0KdG1fc2hhcGUoY19jb3VudHkpICsgdG1fcG9seWdvbnMoYWxwaGEgPSAwLCBib3JkZXIuY29sID0gInJlZCIpICsgdG1fc2hhcGUoY19zY2hvb2xfZGlzdCkgKyB0bV9wb2x5Z29ucyhhbHBoYSA9IDApDQpkZXYub2ZmKCkNCmBgYA0KDQo=