1 General information

We analyze a dataset published on Kaggle. It refers to french employment, salaries, population per town. The aim is to evaluate equality/inequalities in France, and geographical distribution of business according to their size.

Such data are collected by the INSEE. Information regarding the number of firms in every french town, categorized by size can be found here. This dataset contains about 35000 units/per town.

Information about salaries around french town per job categories, age and sex (expressed in average net amount per hour in euro) can be found here. This dataset contains about 5000 units/per town.

Demographic information in France per town, age, sex and living mode can be found here. This dataset contains about 8 million units/per town. Additional info about Population Data can be found here.

These datasets have been pre-processed and put together. The final dataset contains 58 variables and 5022 observations.

1.1 Aim of the study

This project aims to explore structure of French labour market. In particular, we are interested in:

  • evaluating possible inequalities: per towns/region, sex, age, job categories etc.;
  • discover geographical distribution of business according to their size
  • predicting the … using a regression model;
  • reduce the dimensionality of … performing a PCA;
  • explore different algorithms to cluster male/females using …

1.2 Plan for the study

  1. Unsupervised learning:
  • PCA
  • Clustering methods (K-means/Hierarchical)
  1. Supervised learning:
  • GLM
  • Linear/Quadratic Discriminant Analysis
  • KNN
  • Cross-validation
  • Bootstrap
  • Subset selection
  • Shrinkage methods
  • Dimension reduction methods
  1. Description of population demographics in France
  2. Structure of the french labour market
  3. Future works

1.3 Loading tools

Loading all libraries needed throughout the notebook

Import the datasets

setwd("./data")
firms       <- read.csv("base_etablissement_par_tranche_effectif.csv", encoding = "UTF-8")
geo         <- read.csv("name_geographic_information.csv", encoding = "UTF-8")
salary      <- read.csv("net_salary_per_town_categories.csv", encoding = "UTF-8")
population  <- read.csv("population.csv", encoding = "UTF-8")
educ        <- read.csv("level_education.csv", sep =";", encoding = "UTF-8")
categ_socio <- read.csv("Categorie_socioprofessionnelle.csv", sep =";", encoding = "UTF-8")
status_work <- read.csv("Emplois_lieu_travail.csv", sep =";", encoding = "UTF-8")
ineq        <- read.csv("Comparateur_territoires.csv", sep =";", encoding = "UTF-8")
commune     <- read.csv("insee_commune.csv", encoding = "UTF-8")

2 Pre-processing

2.1 Firms data

Assign meaningful names and check the modified data:

names(firms) 
 [1] "CODGEO"   "LIBGEO"   "REG"      "DEP"      "E14TST"   "E14TS0ND" "E14TS1"  
 [8] "E14TS6"   "E14TS10"  "E14TS20"  "E14TS50"  "E14TS100" "E14TS200" "E14TS500"
names(firms)[2:ncol(firms)] <-
  c("town", 
    "regNum",
    "deptNum",
    "total",
    "null",
    "firmsEmpl_1_5",
    "firmsEmpl_6_9",
    "firmsEmpl_10_19",
    "firmsEmpl_20_49",
    "firmsEmpl_50_99",
    "firmsEmpl_100_199",
    "firmsEmpl_200_499",
    "firmsEmpl_500plus")
# preliminary checks
names(firms)
 [1] "CODGEO"            "town"              "regNum"            "deptNum"          
 [5] "total"             "null"              "firmsEmpl_1_5"     "firmsEmpl_6_9"    
 [9] "firmsEmpl_10_19"   "firmsEmpl_20_49"   "firmsEmpl_50_99"   "firmsEmpl_100_199"
[13] "firmsEmpl_200_499" "firmsEmpl_500plus"
head(firms)
str(firms)
'data.frame':   36681 obs. of  14 variables:
 $ CODGEO           : Factor w/ 36681 levels "01001","01002",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ town             : Factor w/ 34142 levels "Aast","Abainville",..: 13659 13661 442 444 460 480 484 581 638 802 ...
 $ regNum           : int  82 82 82 82 82 82 82 82 82 82 ...
 $ deptNum          : Factor w/ 101 levels "01","02","03",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ total            : int  25 10 996 99 4 124 48 22 33 14 ...
 $ null             : int  22 9 577 73 4 87 28 17 23 11 ...
 $ firmsEmpl_1_5    : int  1 1 272 20 0 20 15 4 8 2 ...
 $ firmsEmpl_6_9    : int  2 0 63 3 0 10 2 1 1 1 ...
 $ firmsEmpl_10_19  : int  0 0 46 1 0 5 3 0 0 0 ...
 $ firmsEmpl_20_49  : int  0 0 24 2 0 2 0 0 0 0 ...
 $ firmsEmpl_50_99  : int  0 0 9 0 0 0 0 0 0 0 ...
 $ firmsEmpl_100_199: int  0 0 3 0 0 0 0 0 1 0 ...
 $ firmsEmpl_200_499: int  0 0 2 0 0 0 0 0 0 0 ...
 $ firmsEmpl_500plus: int  0 0 0 0 0 0 0 0 0 0 ...
summary(firms)
     CODGEO                  town           regNum         deptNum     
 01001  :    1   Sainte-Colombe:   14   Min.   : 1.00   62     :  895  
 01002  :    1   Saint-Sauveur :   12   1st Qu.:25.00   02     :  816  
 01004  :    1   Beaulieu      :   11   Median :43.00   80     :  782  
 01005  :    1   Sainte-Marie  :   11   Mean   :49.42   76     :  745  
 01006  :    1   Le Pin        :   10   3rd Qu.:73.00   57     :  730  
 01007  :    1   Saint-Aubin   :   10   Max.   :94.00   14     :  706  
 (Other):36675   (Other)       :36613                   (Other):32007  
     total               null          firmsEmpl_1_5      firmsEmpl_6_9      
 Min.   :     0.0   Min.   :     0.0   Min.   :    0.00   Min.   :    0.000  
 1st Qu.:     8.0   1st Qu.:     6.0   1st Qu.:    1.00   1st Qu.:    0.000  
 Median :    19.0   Median :    14.0   Median :    3.00   Median :    0.000  
 Mean   :   123.5   Mean   :    83.6   Mean   :   27.29   Mean   :    5.221  
 3rd Qu.:    54.0   3rd Qu.:    39.0   3rd Qu.:   11.00   3rd Qu.:    2.000  
 Max.   :427385.0   Max.   :316603.0   Max.   :76368.00   Max.   :14836.000  
                                                                             
 firmsEmpl_10_19   firmsEmpl_20_49    firmsEmpl_50_99     firmsEmpl_100_199 
 Min.   :    0.0   Min.   :   0.000   Min.   :   0.0000   Min.   :  0.0000  
 1st Qu.:    0.0   1st Qu.:   0.000   1st Qu.:   0.0000   1st Qu.:  0.0000  
 Median :    0.0   Median :   0.000   Median :   0.0000   Median :  0.0000  
 Mean   :    3.8   Mean   :   2.296   Mean   :   0.7383   Mean   :  0.3324  
 3rd Qu.:    1.0   3rd Qu.:   1.000   3rd Qu.:   0.0000   3rd Qu.:  0.0000  
 Max.   :10829.0   Max.   :5643.000   Max.   :1658.0000   Max.   :812.0000  
                                                                            
 firmsEmpl_200_499  firmsEmpl_500plus  
 Min.   :  0.0000   Min.   :  0.00000  
 1st Qu.:  0.0000   1st Qu.:  0.00000  
 Median :  0.0000   Median :  0.00000  
 Mean   :  0.1728   Mean   :  0.04842  
 3rd Qu.:  0.0000   3rd Qu.:  0.00000  
 Max.   :456.0000   Max.   :180.00000  
                                       
# Check for duplicated data: there is no
sum(duplicated.data.frame(firms))
[1] 0

Categorize firms’ size according to EU standard, but in a slightly different form for medium and large firms (i.e., medium firms have <200 instead of <250 employees):

# merge variables
firms$micro   <- firms$firmsEmpl_1_5 + firms$firmsEmpl_6_9
firms$small   <- firms$firmsEmpl_10_19 + firms$firmsEmpl_20_49
firms$medium  <- firms$firmsEmpl_50_99 + firms$firmsEmpl_100_199
firms$large   <- firms$firmsEmpl_200_499 + firms$firmsEmpl_500plus
# Drop unnecessary (at the moment) columns 
firms <- subset(firms, select = c(CODGEO, town, total, micro, small, medium, large, null))
# check
summary(firms)
     CODGEO                  town           total              micro         
 01001  :    1   Sainte-Colombe:   14   Min.   :     0.0   Min.   :    0.00  
 01002  :    1   Saint-Sauveur :   12   1st Qu.:     8.0   1st Qu.:    1.00  
 01004  :    1   Beaulieu      :   11   Median :    19.0   Median :    4.00  
 01005  :    1   Sainte-Marie  :   11   Mean   :   123.5   Mean   :   32.51  
 01006  :    1   Le Pin        :   10   3rd Qu.:    54.0   3rd Qu.:   13.00  
 01007  :    1   Saint-Aubin   :   10   Max.   :427385.0   Max.   :91204.00  
 (Other):36675   (Other)       :36613                                        
     small               medium             large               null         
 Min.   :    0.000   Min.   :   0.000   Min.   :  0.0000   Min.   :     0.0  
 1st Qu.:    0.000   1st Qu.:   0.000   1st Qu.:  0.0000   1st Qu.:     6.0  
 Median :    0.000   Median :   0.000   Median :  0.0000   Median :    14.0  
 Mean   :    6.097   Mean   :   1.071   Mean   :  0.2212   Mean   :    83.6  
 3rd Qu.:    2.000   3rd Qu.:   0.000   3rd Qu.:  0.0000   3rd Qu.:    39.0  
 Max.   :16472.000   Max.   :2470.000   Max.   :636.0000   Max.   :316603.0  
                                                                             
# there is an obs with more than 316K null data: we check if it is plausible
# get the highest 20 null values
str_firms <- sort(firms$null, decreasing = T)[1:20]
# get their indexes
str_firms_ind <- match(str_firms, firms$null)
# get the corresponding city
firms$town[str_firms_ind]
 [1] Paris                Marseille            Lyon                 Nice                
 [5] Toulouse             Bordeaux             Montpellier          Nantes              
 [9] Strasbourg           Lille                Aix-en-Provence      Boulogne-Billancourt
[13] Fort-de-France       Rennes               Grenoble             Toulon              
[17] Cannes               Saint-Denis          Neuilly-sur-Seine    Nîmes               
34142 Levels: Aast Abainville Abancourt Abaucourt Abaucourt-Hautecourt ... Zuytpeene
# they are the largest cities, hence it seems reasonable..

2.2 Geographical data

Assign names and remove some variables:

names(geo)
 [1] "EU_circo"               "code_région"            "nom_région"            
 [4] "chef.lieu_région"       "numéro_département"     "nom_département"       
 [7] "préfecture"             "numéro_circonscription" "nom_commune"           
[10] "codes_postaux"          "code_insee"             "latitude"              
[13] "longitude"              "éloignement"           
names(geo)[c(2:11, 14)] =
  c("code_region",
    "region", 
    "region_capital",
    "number_depart",
    "department", 
    "prefecture",
    "circons",
    "town_name", 
    "postal_code", 
    "CODGEO",
    "eloignement")
# drop unnecessary columns (code/num and name represents same thing) 
geo <- subset(geo, select = -c(EU_circo, code_region, number_depart, prefecture, circons, eloignement, town_name))
# preliminary checks
names(geo)
[1] "region"         "region_capital" "department"     "postal_code"    "CODGEO"        
[6] "latitude"       "longitude"     
head(geo)
str(geo)
'data.frame':   36840 obs. of  7 variables:
 $ region        : Factor w/ 28 levels "Alsace","Aquitaine",..: 27 27 27 27 27 27 27 27 27 27 ...
 $ region_capital: Factor w/ 28 levels "Ajaccio","Amiens",..: 14 14 14 14 14 14 14 14 14 14 ...
 $ department    : Factor w/ 102 levels "Ain","Aisne",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ postal_code   : Factor w/ 6106 levels "01000","01090",..: 26 19 29 26 17 1 23 16 17 17 ...
 $ CODGEO        : int  1024 1029 1038 1040 1245 1053 1065 1069 1072 1095 ...
 $ latitude      : num  46.3 46.4 46.3 46.4 46.1 ...
 $ longitude     : Factor w/ 1151 levels "","-","-0,75",..: 874 880 881 864 891 877 872 880 885 892 ...
summary(geo)
           region       region_capital           department     postal_code   
 Midi-Pyrénées: 3028   Toulouse: 3028   Pas-de-Calais :  898   51300  :   46  
 Rhône-Alpes  : 2890   Lyon    : 2890   Aisne         :  816   51800  :   44  
 Lorraine     : 2336   Metz    : 2336   Somme         :  783   70000  :   42  
 Aquitaine    : 2300   Bordeaux: 2300   Seine-Maritime:  747   88500  :   42  
 Picardie     : 2295   Amiens  : 2295   Moselle       :  732   80140  :   40  
 Bourgogne    : 2050   Dijon   : 2050   Côte-d'Or     :  709   02160  :   38  
 (Other)      :21941   (Other) :21941   (Other)       :32155   (Other):36588  
     CODGEO         latitude        longitude    
 Min.   : 1001   Min.   :41.39           : 2841  
 1st Qu.:24577   1st Qu.:45.22   2.433333:  105  
 Median :48191   Median :47.43   2.333333:  100  
 Mean   :46298   Mean   :47.00   1.833333:   99  
 3rd Qu.:67043   3rd Qu.:48.85   2.116667:   98  
 Max.   :97617   Max.   :51.08   2.25    :   94  
                 NA's   :2929    (Other) :33503  

Correct typos for longitude data and keep just the unique CODGEO to avoid towns with multiple postal codes:

# spot "," instead of "." in longitude
newLong       <- as.character(geo$longitude)    # copy the vector
sum(grep(",", newLong))                         # total commas
[1] 994302
ind_long_err  <- grep(",", newLong)             # indexing them
newLong       <- gsub(",", ".", newLong)        # substituting them with dots
indNA_Long    <- is.na(as.numeric((newLong)))   # spot NA
NAs introduced by coercion
# geo$longitude[indNA_Long]                       # verify that they were actually missing
geo$longitude <- as.numeric(newLong)            # overwrite the longitude variable with the new one
NAs introduced by coercion
# Check for duplicated data (e.g., cities with different postal codes, that we dropped):
  # e.g., to verify it,  try on the initial dataset:
  # sum(geo$nom_commune == "Paris")
  # ind_duplic <- geo$nom_commune == "Paris"
  # geo[ind_duplic,]
sum(duplicated.data.frame(geo)) 
[1] 118
# retaing unique postal cities
geo <- geo[!duplicated(geo$CODGEO),]
# check again
summary(geo)
           region       region_capital           department     postal_code   
 Midi-Pyrénées: 3020   Toulouse: 3020   Pas-de-Calais :  894   51300  :   46  
 Rhône-Alpes  : 2879   Lyon    : 2879   Aisne         :  816   51800  :   44  
 Lorraine     : 2333   Metz    : 2333   Somme         :  782   70000  :   42  
 Aquitaine    : 2296   Bordeaux: 2296   Seine-Maritime:  745   88500  :   42  
 Picardie     : 2291   Amiens  : 2291   Moselle       :  730   80140  :   40  
 Bourgogne    : 2046   Dijon   : 2046   Côte-d'Or     :  707   02160  :   38  
 (Other)      :21828   (Other) :21828   (Other)       :32019   (Other):36441  
     CODGEO         latitude       longitude      
 Min.   : 1001   Min.   :41.39   Min.   :-5.1000  
 1st Qu.:24561   1st Qu.:45.22   1st Qu.: 0.6833  
 Median :48172   Median :47.43   Median : 2.6167  
 Mean   :46264   Mean   :47.00   Mean   : 2.7324  
 3rd Qu.:67020   3rd Qu.:48.85   3rd Qu.: 4.8500  
 Max.   :97617   Max.   :51.08   Max.   : 9.5167  
                 NA's   :2923    NA's   :2902     

Assign latitude and longitude values for missing data (almost 3000). The code used to retrieve them using Google API has been commented and its result is loaded.

# index of NAs and their total
indNA_coord = is.na(geo$latitude) | is.na(geo$longitude)
sum(indNA_coord)
[1] 2987
# code used to retrieve the NA using Google API, which have been saved in a csv file
# 
# # initialize variables
# city_search = 0
# res = as.data.frame(matrix(c(0, 0, 0), 1, 3))
# names(res) = c("lon", "lat", "address")
# 
# # retrieve lat and long (Google API = 2500 request per day)
# # my_iter = floor(sum(indNA_coord)/3)
# for (i in 1:sum(indNA_coord)){
# 
#   # city searched
#   city_search[i] = paste(c(as.character(NA_coord$town_name[i]), as.character(NA_coord$postal_code[i]), as.character(NA_coord$department[i]), "France"), sep=" ", collapse = ", ")
#   
#   # solution
#   res[i,] = geocode(city_search[i], output = "latlona", source = c("google", "dsk"), messaging = FALSE)
# 
#   # retrieve still missing data, because of existing problems with API (up to 15 trials)
#   j = 0
#   while (any(is.na(res[i,])) & j < 25){
#     res[i,] = geocode(city_search[i], output = "latlona", source = c("google", "dsk"), messaging = FALSE)
#     j = j + 1
#   }
# }
# # check the solution
# sol = cbind(searched = city_search, res)
# # save it as a csv file to save time
# write.csv(retrieved_geo_NA[,2:3], "geo_NA_Final.csv", quote = FALSE, row.names=FALSE, fileEncoding = "UTF-8")
    
# read the created csv
setwd("./data")
retrieved_geo_NA = read.csv("geo_NA_Final.csv", header = T, encoding = "UTF-8")
# get only long and lat and assign to original NA 
geo$latitude[indNA_coord] = retrieved_geo_NA[,2]
geo$longitude[indNA_coord] = retrieved_geo_NA[,1]
# there are 37 still missing units, which are towns located in old colonies far from Europe
indNA_coord = is.na(geo$latitude) | is.na(geo$longitude)
sum(indNA_coord)
[1] 35
# exclude those towns
geo = geo[!indNA_coord,]
summary(geo)
           region       region_capital           department     postal_code   
 Midi-Pyrénées: 3020   Toulouse: 3020   Pas-de-Calais :  894   51300  :   46  
 Rhône-Alpes  : 2879   Lyon    : 2879   Aisne         :  816   51800  :   44  
 Lorraine     : 2333   Metz    : 2333   Somme         :  782   70000  :   42  
 Aquitaine    : 2296   Bordeaux: 2296   Seine-Maritime:  745   88500  :   42  
 Picardie     : 2291   Amiens  : 2291   Moselle       :  730   80140  :   40  
 Bourgogne    : 2046   Dijon   : 2046   Côte-d'Or     :  707   02160  :   38  
 (Other)      :21793   (Other) :21793   (Other)       :31984   (Other):36406  
     CODGEO         latitude        longitude       
 Min.   : 1001   Min.   :-21.38   Min.   :-63.0885  
 1st Qu.:24551   1st Qu.: 45.15   1st Qu.:  0.6585  
 Median :48154   Median : 47.38   Median :  2.6333  
 Mean   :46215   Mean   : 46.87   Mean   :  2.6611  
 3rd Qu.:66225   3rd Qu.: 48.83   3rd Qu.:  4.8667  
 Max.   :97613   Max.   : 51.08   Max.   : 55.8250  
                                                    

Remove DOM-TOM towns (i.e. old colonies far from Europe)

# delete non-European countries
ind_nonEur = geo$latitude < 30 | geo$latitude > 70 |geo$longitude < -20 | geo$longitude > 20
sum(ind_nonEur)
[1] 92
geo = geo[!ind_nonEur,]

2.3 Salary data

Assign meaningful names and check the data:

names(salary)
 [1] "CODGEO"    "LIBGEO"    "SNHM14"    "SNHMC14"   "SNHMP14"   "SNHME14"   "SNHMO14"  
 [8] "SNHMF14"   "SNHMFC14"  "SNHMFP14"  "SNHMFE14"  "SNHMFO14"  "SNHMH14"   "SNHMHC14" 
[15] "SNHMHP14"  "SNHMHE14"  "SNHMHO14"  "SNHM1814"  "SNHM2614"  "SNHM5014"  "SNHMF1814"
[22] "SNHMF2614" "SNHMF5014" "SNHMH1814" "SNHMH2614" "SNHMH5014"
names(salary)[2:ncol(salary)] <-
  c("town",
    "sal_general",    
    "sal_executive",
    "sal_midManager",
    "sal_employee",
    "sal_worker",
    "sal_Females",
    "sal_F_executive",
    "sal_F_midManager",
    "sal_F_employee",
    "sal_F_worker",
    "sal_Males",
    "sal_M_executive",
    "sal_M_midManager",
    "sal_M_employee",
    "sal_M_worker",
    "sal_18_25",
    "sal_26_50",
    "sal_51plus",
    "sal_F_18_25",
    "sal_F_26_50",
    "sal_F_51plus",
    "sal_M_18_25",
    "sal_M_26_50",
    "sal_M_51plus")
# preliminary checks
names(salary)
 [1] "CODGEO"           "town"             "sal_general"      "sal_executive"   
 [5] "sal_midManager"   "sal_employee"     "sal_worker"       "sal_Females"     
 [9] "sal_F_executive"  "sal_F_midManager" "sal_F_employee"   "sal_F_worker"    
[13] "sal_Males"        "sal_M_executive"  "sal_M_midManager" "sal_M_employee"  
[17] "sal_M_worker"     "sal_18_25"        "sal_26_50"        "sal_51plus"      
[21] "sal_F_18_25"      "sal_F_26_50"      "sal_F_51plus"     "sal_M_18_25"     
[25] "sal_M_26_50"      "sal_M_51plus"    
head(salary)
str(salary)
'data.frame':   5136 obs. of  26 variables:
 $ CODGEO          : Factor w/ 5136 levels "01004","01007",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ town            : Factor w/ 5085 levels "Abbeville","Ablis",..: 73 79 133 192 275 298 407 395 400 406 ...
 $ sal_general     : num  13.7 13.5 13.5 12.9 13 13.9 12.4 14 11.5 12.4 ...
 $ sal_executive   : num  24.2 22.1 27.6 21.8 22.8 22.2 24 23.1 21.2 23.4 ...
 $ sal_midManager  : num  15.5 14.7 15.6 14.1 14.1 15.1 13.1 15.3 13.5 14.1 ...
 $ sal_employee    : num  10.3 10.7 11.1 11 10.5 11 10.5 10.9 9.9 10.3 ...
 $ sal_worker      : num  11.2 11.4 11.1 11.3 11.1 11.4 10.4 11.3 10.5 10.5 ...
 $ sal_Females     : num  11.6 11.9 10.9 11.4 11.6 12.5 10.9 12.4 10.3 11 ...
 $ sal_F_executive : num  19.1 19 19.5 19 19.4 20.3 20.7 20.5 20.8 21.5 ...
 $ sal_F_midManager: num  13.2 13.3 11.7 13 13.6 14 11.8 13.9 12.3 13 ...
 $ sal_F_employee  : num  10.1 10.6 10.8 10.3 10.2 10.9 10.4 10.7 9.8 9.9 ...
 $ sal_F_worker    : num  9.6 10 9.5 9.9 9.8 10.5 9.3 10.3 9 9.5 ...
 $ sal_Males       : num  15 14.7 15.3 13.8 13.8 15.2 13.4 15.4 12.3 13.2 ...
 $ sal_M_executive : num  26.4 23.3 30.2 23 24.1 23.1 25.2 24.4 21.3 24 ...
 $ sal_M_midManager: num  16.7 15.8 17.2 14.7 14.4 15.9 13.8 16.3 14.2 14.9 ...
 $ sal_M_employee  : num  11 11.3 12.4 13.2 11.7 12.1 10.8 11.8 10.5 11.6 ...
 $ sal_M_worker    : num  11.6 11.7 11.8 11.6 11.4 11.7 10.8 11.6 11 10.9 ...
 $ sal_18_25       : num  10.5 9.8 9.3 9.6 9.4 9.7 9.3 9.7 9.6 9.7 ...
 $ sal_26_50       : num  13.7 13.8 13.3 12.9 12.8 14.1 12.5 13.9 11.5 12.3 ...
 $ sal_51plus      : num  16.1 14.6 16 14.2 15.2 15.4 13.3 16.7 12.7 13.7 ...
 $ sal_F_18_25     : num  9.7 9.2 8.9 9.3 9 9.5 8.9 9.7 9.2 9.3 ...
 $ sal_F_26_50     : num  11.8 12.2 10.6 11.4 11.8 12.8 11 12.4 10.3 11.2 ...
 $ sal_F_51plus    : num  12.5 12.5 12.5 12.2 12.3 13 11.5 13.8 11.3 11.4 ...
 $ sal_M_18_25     : num  11 10.2 9.6 9.7 9.7 9.9 9.6 9.6 10 9.9 ...
 $ sal_M_26_50     : num  14.9 14.9 15.1 13.8 13.4 15.3 13.3 15 12.3 13 ...
 $ sal_M_51plus    : num  18.6 16.4 18.6 15.9 16.9 17.2 14.9 19.3 13.9 15.4 ...
summary(salary)
     CODGEO               town       sal_general    sal_executive  sal_midManager 
 01004  :   1   Sainte-Marie:   4   Min.   :10.20   Min.   :16.0   Min.   :11.60  
 01007  :   1   Saint-Ouen  :   3   1st Qu.:12.10   1st Qu.:21.9   1st Qu.:13.80  
 01014  :   1   Allonnes    :   2   Median :13.00   Median :23.2   Median :14.40  
 01024  :   1   Andilly     :   2   Mean   :13.71   Mean   :23.7   Mean   :14.58  
 01025  :   1   Bassens     :   2   3rd Qu.:14.40   3rd Qu.:24.9   3rd Qu.:15.10  
 01027  :   1   Beaumont    :   2   Max.   :43.30   Max.   :51.5   Max.   :54.60  
 (Other):5130   (Other)     :5121                                                 
  sal_employee     sal_worker     sal_Females    sal_F_executive sal_F_midManager
 Min.   : 8.70   Min.   : 8.30   Min.   : 9.30   Min.   :12.00   Min.   :10.60   
 1st Qu.:10.00   1st Qu.:10.60   1st Qu.:10.90   1st Qu.:18.80   1st Qu.:12.60   
 Median :10.40   Median :11.00   Median :11.50   Median :20.00   Median :13.10   
 Mean   :10.56   Mean   :11.24   Mean   :12.04   Mean   :20.22   Mean   :13.27   
 3rd Qu.:10.90   3rd Qu.:11.60   3rd Qu.:12.70   3rd Qu.:21.40   3rd Qu.:13.80   
 Max.   :17.50   Max.   :46.30   Max.   :26.70   Max.   :35.50   Max.   :19.00   
                                                                                 
 sal_F_employee   sal_F_worker      sal_Males     sal_M_executive sal_M_midManager
 Min.   : 8.70   Min.   : 6.100   Min.   :10.40   Min.   :13.8    Min.   :11.80   
 1st Qu.: 9.80   1st Qu.: 9.200   1st Qu.:12.90   1st Qu.:23.1    1st Qu.:14.50   
 Median :10.10   Median : 9.700   Median :14.10   Median :24.6    Median :15.20   
 Mean   :10.31   Mean   : 9.827   Mean   :14.85   Mean   :25.2    Mean   :15.49   
 3rd Qu.:10.60   3rd Qu.:10.200   3rd Qu.:15.80   3rd Qu.:26.6    3rd Qu.:16.00   
 Max.   :16.10   Max.   :28.100   Max.   :52.40   Max.   :58.0    Max.   :93.40   
                                                                                  
 sal_M_employee   sal_M_worker    sal_18_25       sal_26_50      sal_51plus   
 Min.   : 8.00   Min.   : 8.9   Min.   : 7.90   Min.   : 9.7   Min.   :10.50  
 1st Qu.:10.50   1st Qu.:10.8   1st Qu.: 9.20   1st Qu.:12.0   1st Qu.:13.70  
 Median :11.10   Median :11.3   Median : 9.50   Median :12.9   Median :15.00  
 Mean   :11.27   Mean   :11.5   Mean   : 9.55   Mean   :13.5   Mean   :15.88  
 3rd Qu.:11.80   3rd Qu.:11.9   3rd Qu.: 9.70   3rd Qu.:14.3   3rd Qu.:16.90  
 Max.   :23.50   Max.   :53.2   Max.   :60.60   Max.   :38.1   Max.   :56.90  
                                                                              
  sal_F_18_25      sal_F_26_50     sal_F_51plus    sal_M_18_25      sal_M_26_50   
 Min.   : 7.500   Min.   : 9.10   Min.   : 9.50   Min.   : 7.800   Min.   : 9.60  
 1st Qu.: 8.900   1st Qu.:10.90   1st Qu.:11.70   1st Qu.: 9.400   1st Qu.:12.70  
 Median : 9.100   Median :11.60   Median :12.60   Median : 9.700   Median :13.80  
 Mean   : 9.162   Mean   :12.06   Mean   :13.17   Mean   : 9.821   Mean   :14.49  
 3rd Qu.: 9.400   3rd Qu.:12.70   3rd Qu.:14.00   3rd Qu.:10.000   3rd Qu.:15.50  
 Max.   :12.000   Max.   :26.60   Max.   :31.00   Max.   :93.300   Max.   :45.40  
                                                                                  
  sal_M_51plus  
 Min.   :10.80  
 1st Qu.:14.90  
 Median :16.60  
 Mean   :17.68  
 3rd Qu.:19.00  
 Max.   :68.60  
                
# Check for duplicated data: there are no
sum(duplicated.data.frame(salary))
[1] 0
# drop unnecessary variable
salary <-subset(salary, select = -c(town))

2.4 Population data

Rename the variables for population and exclude the unnecessary ones:

#names(population)
names(population)[5:7] <-
  c("ageCateg5",
    "sex",
    "peopleCategNum")
# drop unnecessary columns (NIVGEO is the same for all)
population <- subset(population, select = -c(NIVGEO, LIBGEO))
# Refactor sex and MOCO
population$MOCO <- factor(population$MOCO, levels = c(11,12,21,22,23,31,32),
                          labels = c("children_living_with_two_parents", 
                                     "children_living_with_one_parent",
                                     "adults_living_in_couple_without_child",
                                     "adults_living_in_couple_with_children",
                                     "adults_living_alone_with_children",
                                     "persons_not_from_family_living_in_the_home",
                                     "persons_living_alone"))
population$sex <- factor(population$sex, levels = c(1,2), labels = c("Male", "Female"))
# check again
summary(population)
     CODGEO                                                MOCO           ageCateg5 
 01001  :    238   children_living_with_two_parents          :1219512   Min.   : 0  
 01002  :    238   children_living_with_one_parent           :1219512   1st Qu.:20  
 01004  :    238   adults_living_in_couple_without_child     :1219512   Median :40  
 01005  :    238   adults_living_in_couple_with_children     :1219512   Mean   :40  
 01006  :    238   adults_living_alone_with_children         :1219512   3rd Qu.:60  
 01007  :    238   persons_not_from_family_living_in_the_home:1219512   Max.   :80  
 (Other):8535156   persons_living_alone                      :1219512               
     sex          peopleCategNum    
 Male  :4268292   Min.   :    0.00  
 Female:4268292   1st Qu.:    0.00  
                  Median :    0.00  
                  Mean   :    7.45  
                  3rd Qu.:    3.00  
                  Max.   :48873.00  
                                    

Understand the distribution of population according to different age categories using population pyramide (performed now because later the dataset will be modified):

# need the initial shape of data to construct the pyramide 
# Population pyramide
population_data2 <- ddply(population, .(sex, ageCateg5), function(population) {
  data.frame(total_population = sum(population$peopleCategNum))
  })
pop_pyramid <- ggplot(data = population_data2,
       mapping = aes(x = ageCateg5, fill = sex,
                     y = ifelse(test = sex == "Male",
                                yes = -total_population, no = total_population))) +
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(population_data2$total_population) * c(-1,1)) + 
  ggtitle("Pyramid of Population") + theme(plot.title = element_text(hjust = 0.5)) + 
  labs(x= "Age")+ labs(y = "Population") + coord_flip() # + scale_fill_brewer(palette = "Set1")
pop_pyramid

Re-organize population dataset and set CODGEO as units and create new variables using the available variables.

# Re-organize the data by creating new variables: "total population", "male", "female", "child", "elderly" and "workforce".
population <- ddply(population, .(CODGEO), function(population) {
  data.frame(total_population = sum(population$peopleCategNum),
             male = sum(population[population$sex == "Male",]$peopleCategNum),
             female = sum(population[population$sex == "Female",]$peopleCategNum),
             child = sum(population[population$ageCateg5 %in% seq(0, 10, by=5),]$peopleCategNum),
             elderly = sum(population[population$ageCateg5 %in% seq(65, 80, by=5),]$peopleCategNum),
             workforce = sum(population[population$ageCateg5 %in% seq(15, 60, by=5),]$peopleCategNum) 
  )})
# Calculate ratios using the existing variables
population$dependent <- population$child + population$elderly
population$sex_ratio <- ifelse(population$female==0, 0, population$male / population$female)
population$dependency_ratio <- ifelse(population$workforce==0, 0, population$dependent / population$workforce)
population$aged_dependency_ratio <- ifelse(population$workforce==0, 0, population$elderly / population$workforce)
population$child_dependency_ratio <- ifelse(population$workforce==0, 0, population$child / population$workforce)
# total population=65mln, which is reasonable
sum(population$total_population)
[1] 63569750
# create dummy variable for urban city
# taking the top 20% in terms of population
population$urbanDummy = population$total_population > quantile(population$total_population, 0.90)

2.5 Education data

Assign meaningful names and build some useful indicators:

names(educ)[3:ncol(educ)] <-
  c("Age15_NoDip_M","Age15_NoDip_F", "Age15_Sec_M", "Age15_Sec_F", "Age15_Hi_M","Age15_Hi_F", "Age15_Univ_M",
    "Age15_Univ_F", "Age20_NoDip_M", "Age20_NoDip_F", "Age20_Sec_M","Age20_Sec_F", "Age20_Hi_M", "Age20_Hi_F",
    "Age20_Univ_M", "Age20_Univ_F","Age25_NoDip_M", "Age25_NoDip_F","Age25_Sec_M","Age25_Sec_F", "Age25_Hi_M",
    "Age25_Hi_F","Age25_Univ_M","Age25_Univ_F", "Age30_NoDip_M", "Age30_NoDip_F", "Age30_Sec_M", "Age30_Sec_F",
    "Age30_Hi_M", "Age30_Hi_F","Age30_Univ_M","Age30_Univ_F", "Age35_NoDip_M", "Age35_NoDip_F", "Age35_Sec_M",
    "Age35_Sec_F", "Age35_Hi_M", "Age35_Hi_F", "Age35_Univ_M","Age35_Univ_F", "Age40_NoDip_M", "Age40_NoDip_F",
    "Age40_Sec_M","Age40_Sec_F", "Age40_Hi_M", "Age40_Hi_F", "Age40_Univ_M","Age40_Univ_F", "Age45_NoDip_M",
    "Age45_NoDip_F","Age45_Sec_M","Age45_Sec_F", "Age45_Hi_M", "Age45_Hi_F", "Age45_Univ_M","Age45_Univ_F",
    "Age50_NoDip_M", "Age50_NoDip_F","Age50_Sec_M","Age50_Sec_F", "Age50_Hi_M", "Age50_Hi_F", "Age50_Univ_M",
    "Age50_Univ_F", "Age55_NoDip_M", "Age55_NoDip_F","Age55_Sec_M", "Age55_Sec_F", "Age55_Hi_M", "Age55_Hi_F",
    "Age55_Univ_M", "Age55_Univ_F", "Age60_NoDip_M", "Age60_NoDip_F","Age60_Sec_M","Age60_Sec_F", "Age60_Hi_M",
    "Age60_Hi_F", "Age60_Univ_M", "Age60_Univ_F", "Age65_NoDip_M", "Age65_NoDip_F","Age65_Sec_M","Age65_Sec_F",
    "Age65_Hi_M", "Age65_Hi_F", "Age65_Univ_M", "Age65_Univ_F")
# Aggregate variables by grouping all the education level categories by gender
# total number of females with no diploma
educ$female_NoDip <- rowSums(cbind(educ$Age15_NoDip_F, educ$Age20_NoDip_F, educ$Age25_NoDip_F, educ$Age30_NoDip_F,
                                   educ$Age35_NoDip_F,educ$Age40_NoDip_F,educ$Age45_NoDip_F, educ$Age50_NoDip_F,
                                   educ$Age55_NoDip_F, educ$Age60_NoDip_F, educ$Age65_NoDip_F))
# total number of males with no diploma
educ$male_NoDip <- rowSums(cbind(educ$Age15_NoDip_M, educ$Age20_NoDip_M, educ$Age25_NoDip_M, educ$Age30_NoDip_M,
                                 educ$Age35_NoDip_M,educ$Age40_NoDip_M,educ$Age45_NoDip_M, educ$Age50_NoDip_M,
                                 educ$Age55_NoDip_M, educ$Age60_NoDip_M, educ$Age65_NoDip_M))
# total number of females with secondary education level
educ$female_Sec <- rowSums(cbind(educ$Age15_Sec_F, educ$Age20_Sec_F, educ$Age25_Sec_F, educ$Age30_Sec_F,
                                 educ$Age35_Sec_F,educ$Age40_Sec_F,educ$Age45_Sec_F, educ$Age50_Sec_F, 
                                 educ$Age55_Sec_F, educ$Age60_Sec_F, educ$Age65_Sec_F))
# total number of males with secondary education level
educ$male_Sec <- rowSums(cbind(educ$Age15_Sec_M, educ$Age20_Sec_M, educ$Age25_Sec_M, educ$Age30_Sec_M,
                               educ$Age35_Sec_M,educ$Age40_Sec_M,educ$Age45_Sec_M, educ$Age50_Sec_M, 
                               educ$Age55_Sec_M, educ$Age60_Sec_M, educ$Age65_Sec_M))
# total number of females with high-school education level
educ$female_Hi <- rowSums(cbind(educ$Age15_Hi_F, educ$Age20_Hi_F, educ$Age25_Hi_F, educ$Age30_Hi_F,
                                educ$Age35_Hi_F,educ$Age40_Hi_F,educ$Age45_Hi_F, educ$Age50_Hi_F, 
                                educ$Age55_Hi_F, educ$Age60_Hi_F, educ$Age65_Hi_F))
# total number of males with high-school education level
educ$male_Hi <- rowSums(cbind(educ$Age15_Hi_M, educ$Age20_Hi_M, educ$Age25_Hi_M, educ$Age30_Hi_M,
                              educ$Age35_Hi_M,educ$Age40_Hi_M,educ$Age45_Hi_M, educ$Age50_Hi_M, 
                              educ$Age55_Hi_M, educ$Age60_Hi_M, educ$Age65_Hi_M))
# total number of females with university degree
educ$female_Univ <- rowSums(cbind(educ$Age15_Univ_F, educ$Age20_Univ_F, educ$Age25_Univ_F, educ$Age30_Univ_F,
                                  educ$Age35_Univ_F,educ$Age40_Univ_F,educ$Age45_Univ_F, educ$Age50_Univ_F,
                                  educ$Age55_Univ_F, educ$Age60_Univ_F, educ$Age65_Univ_F))
# total number of males with university degree
educ$male_Univ <- rowSums(cbind(educ$Age15_Univ_M, educ$Age20_Univ_M, educ$Age25_Univ_M, educ$Age30_Univ_M,
                                educ$Age35_Univ_M,educ$Age40_Univ_M,educ$Age45_Univ_M, educ$Age50_Univ_M,
                                educ$Age55_Univ_M, educ$Age60_Univ_M, educ$Age65_Univ_M))
# drop all the unnecessary variables 
educ <- subset(educ, select= c(CODGEO,
                               female_NoDip, male_NoDip, 
                               female_Sec, male_Sec, 
                               female_Hi, male_Hi, 
                               female_Univ, male_Univ))
# total number of population without a diploma
educ$nodip <-rowSums(cbind(educ$female_NoDip, educ$male_NoDip))
# total number of population with secondary level of education
educ$sec <-rowSums(cbind(educ$female_Sec, educ$male_NoDip))
# total number of population with highschool diploma
educ$high <-rowSums(cbind(educ$female_Hi, educ$male_Hi))
# total number of poopulation with university diploma
educ$univ <- rowSums(cbind(educ$female_Univ, educ$male_Univ))

2.6 Demographic/social profiles data

Rename the original variables

# rename variables
names(categ_socio)[3:ncol(categ_socio)] <-
  c("M_immi_agri",
    "F_immi_agri",
    "M_NoImmi_agri",
    "F_NoImmi_agri",
    "M_immi_comm",
    "F_immi_comm",
    "M_NoImmi_comm",
    "F_NoImmi_comm",
    "M_immi_exec",
    "F_immi_exec",
    "M_NoImmi_exec",
    "F_NoImmi_exec",
    "M_immi_midman",
    "F_immi_midman",
    "M_NoImmi_midman",
    "F_NoImmi_midman",
    "M_immi_emp",
    "F_immi_emp",
    "M_NoImmi_emp",
    "F_NoImmi_emp",
    "M_immi_worker",
    "F_immi_worker",
    "M_NoImmi_worker",
    "F_NoImmi_worker",
    "M_immi_retired",
    "F_immi_retired",
    "M_NoImmi_retired",
    "F_NoImmi_retired",
    "M_immi_noAct",
    "F_immi_noAct",
    "M_NoImmi_noAct",
    "F_NoImmi_noAct")
# drop LIBGEO
categ_socio <- subset(categ_socio, select=-c(LIBGEO))

Create the total number of immigrants and natives per town, also separeted by gender.

# total number of male immigrants per town 
categ_socio$male_immig <-rowSums(cbind(categ_socio$M_immi_agri, categ_socio$M_immi_comm, categ_socio$M_immi_emp,  
                                       categ_socio$M_immi_exec, categ_socio$M_immi_midman,categ_socio$M_immi_noAct, 
                                       categ_socio$M_immi_retired, categ_socio$M_immi_worker))
 
# total number of female immigrants per town
categ_socio$female_immig <- rowSums(cbind(categ_socio$F_immi_agri,categ_socio$F_immi_comm, categ_socio$F_immi_emp, 
                                          categ_socio$F_immi_exec, categ_socio$F_immi_midman, 
                                          categ_socio$F_immi_noAct, categ_socio$F_immi_retired, categ_socio$F_immi_worker))
# total number immigrants per town
categ_socio$total_immig <- categ_socio$male_immig + categ_socio$female_immig
# working male immigrants per town 
categ_socio$male_working_immig <- categ_socio$male_immig - categ_socio$M_immi_retired 
 
# working female immigrants per town  
categ_socio$female_working_immig <- categ_socio$female_immig - categ_socio$F_immi_retired 
 
# total number of male natives per town 
categ_socio$male_native <- rowSums(cbind(categ_socio$M_NoImmi_agri, categ_socio$M_NoImmi_comm, categ_socio$M_NoImmi_exec, categ_socio$M_NoImmi_emp, categ_socio$M_NoImmi_midman, categ_socio$M_NoImmi_noAct, categ_socio$M_NoImmi_retired, categ_socio$M_NoImmi_worker))
# total number of female natives per town 
categ_socio$female_native <- rowSums(cbind(categ_socio$F_NoImmi_agri, categ_socio$F_NoImmi_comm, categ_socio$F_NoImmi_exec, categ_socio$F_NoImmi_emp, categ_socio$F_NoImmi_midman, categ_socio$F_NoImmi_noAct, categ_socio$F_NoImmi_retired, categ_socio$F_NoImmi_worker))
# total number of natives per town 
categ_socio$total_native <- categ_socio$male_native + categ_socio$female_native
# drop unnecessary variables 
categ_socio <- subset(categ_socio, select = c(CODGEO, male_immig, female_immig, total_immig, male_working_immig, 
                                               female_working_immig, male_native, female_native, total_native)) 

2.7 Work status data

Rename the original variables

# rename variables
names(status_work)[3:ncol(status_work)] <-
  c("Less20_M_wagearner_Full", "Less20_M_wagearner_Half", "Less20_M_Indp1_Full", "Less20_M_Indp1_Half",
    "Less20_M_empl_Full", "Less20_M_empl_Half", "Less20_M_trans_Full", "Less20_M_trans_Half",
    "Less20_F_wagearner_Full", "Less20_F_wagearner_Half", "Less20_F_Indp1_Full", "Less20_F_Indp1_Half",
    "Less20_F_empl_Full", "Less20_F_empl_Half", "Less20_F_trans_Full", "Less20_F_trans_Half",
    "t4_M_wagearner_Full", "t4_M_wagearner_Half", "t4_M_Indp1_Full", "t4_M_Indp1_Half", "t4_M_empl_Full",
    "t4_M_empl_Half", "t4_M_trans_Full", "t4_M_trans_Half", "t4_F_wagearner_Full", "t4_F_wagearner_Half",
    "t4_F_Indp1_Full", "t4_F_Indp1_Half", "t4_F_empl_Full", "t4_F_empl_Half", "t4_F_trans_Full", "t4_F_trans_Half",
    "t9_M_wagearner_Full", "t9_M_wagearner_Half", "t9_M_Indp1_Full", "t9_M_Indp1_Half", "t9_M_empl_Full",
    "t9_M_empl_Half", "t9_M_trans_Full", "t9_M_trans_Half", "t9_F_wagearner_Full", "t9_F_wagearner_Half",
    "t9_F_Indp1_Full", "t9_F_Indp1_Half", "t9_F_empl_Full", "t9_F_empl_Half", "t9_F_trans_Full", "t9_F_trans_Half",
    "T4_M_wagearner_Full", "T4_M_wagearner_Half", "T4_M_Indp1_Full", "T4_M_Indp1_Half", "T4_M_empl_Full",
    "T4_M_empl_Half", "T4_M_trans_Full", "T4_M_trans_Half", "T4_F_wagearner_Full", "T4_F_wagearner_Half",
    "T4_F_Indp1_Full", "T4_F_Indp1_Half", "T4_F_empl_Full", "T4_F_empl_Half", "T4_F_trans_Full", "T4_F_trans_Half",
    "T9_M_wagearner_Full", "T9_M_wagearner_Half", "T9_M_Indp1_Full", "T9_M_Indp1_Half", "T9_M_empl_Full",
    "T9_M_empl_Half", "T9_M_trans_Full", "T9_M_trans_Half", "T9_F_wagearner_Full", "T9_F_wagearner_Half",
    "T9_F_Indp1_Full", "T9_F_Indp1_Half", "T9_F_empl_Full", "T9_F_empl_Half", "T9_F_trans_Full", "T9_F_trans_Half",
    "FF_M_wagearner_Full", "FF_M_wagearner_Half", "FF_M_Indp1_Full", "FF_M_Indp1_Half", "FF_M_empl_Full",
    "FF_M_empl_Half", "FF_M_trans_Full", "FF_M_trans_Half", "FF_F_wagearner_Full", "FF_F_wagearner_Half",
    "FF_F_Indp1_Full", "FF_F_Indp1_Half", "FF_F_empl_Full", "FF_F_empl_Half", "FF_F_trans_Full", "FF_F_trans_Half",
    "F9_M_wagearner_Full", "F9_M_wagearner_Half", "F9_M_Indp1_Full", "F9_M_Indp1_Half", "F9_M_empl_Full",
    "F9_M_empl_Half", "F9_M_trans_Full", "F9_M_trans_Half", "F9_F_wagearner_Full", "F9_F_wagearner_Half",
    "F9_F_Indp1_Full", "F9_F_Indp1_Half", "F9_F_empl_Full", "F9_F_empl_Half", "F9_F_trans_Full", "F9_F_trans_Half",
    "f4_M_wagearner_Full", "f4_M_wagearner_Half", "f4_M_Indp1_Full", "f4_M_Indp1_Half", "f4_M_empl_Full",
    "f4_M_empl_Half", "f4_M_trans_Full", "f4_M_trans_Half", "f4_F_wagearner_Full", "f4_F_wagearner_Half",
    "f4_F_Indp1_Full", "f4_F_Indp1_Half", "f4_F_empl_Full", "f4_F_empl_Half", "f4_F_trans_Full", "f4_F_trans_Half",
    "f9_M_wagearner_Full", "f9_M_wagearner_Half", "f9_M_Indp1_Full", "f9_M_Indp1_Half", "f9_M_empl_Full",
    "f9_M_empl_Half", "f9_M_trans_Full", "f9_M_trans_Half", "f9_F_wagearner_Full", "f9_F_wagearner_Half",
    "f9_F_Indp1_Full", "f9_F_Indp1_Half", "f9_F_empl_Full", "f9_F_empl_Half", "f9_F_trans_Full", "f9_F_trans_Half",
    "s4_M_wagearner_Full", "s4_M_wagearner_Half", "s4_M_Indp1_Full", "s4_M_Indp1_Half", "s4_M_empl_Full",
    "s4_M_empl_Half", "s4_M_trans_Full", "s4_M_trans_Half", "s4_F_wagearner_Full", "s4_F_wagearner_Half",
    "s4_F_Indp1_Full", "s4_F_Indp1_Half", "s4_F_empl_Full", "s4_F_empl_Half", "s4_F_trans_Full", "s4_F_trans_Half",
    "s9_M_wagearner_Full", "s9_M_wagearner_Half", "s9_M_Indp1_Full", "s9_M_Indp1_Half", "s9_M_empl_Full",
    "s9_M_empl_Half", "s9_M_trans_Full", "s9_M_trans_Half", "s9_F_wagearner_Full", "s9_F_wagearner_Half",
    "s9_F_Indp1_Full", "s9_F_Indp1_Half", "s9_F_empl_Full", "s9_F_empl_Half", "s9_F_trans_Full", "s9_F_trans_Half")

Create aggregate statistics and drop unnecessary variables

# Simplify the variables by summing up by categories
# total number of wage earners for males and females.
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('wagearner', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
# names(status_work[nam.M])
# names(status_work[nam.F])
status_work$wagearner_M=rowSums(status_work[,nam.M])
status_work$wagearner_F=rowSums(status_work[,nam.F]) 
                                               
# same but for independent workers by gender
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('Indp1', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
# names(status_work[nam.M])
# names(status_work[nam.F])
status_work$independent_M=rowSums(status_work[,nam.M]) 
status_work$independent_F=rowSums(status_work[,nam.F])
# same but for government transfer receivers
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('trans', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
# names(status_work[nam.M])
# names(status_work[nam.F])
status_work$transfer_M=rowSums(status_work[,nam.M])  
status_work$transfer_F=rowSums(status_work[,nam.F])
# same but for employers
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('empl', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
# names(status_work[nam.M])
# names(status_work[nam.F])
status_work$employer_M=rowSums(status_work[,nam.M])
status_work$employer_F=rowSums(status_work[,nam.F])
 
# same but for full-time contracts
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('Full', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
# names(status_work[nam.M])
# names(status_work[nam.F])
status_work$full_M=rowSums(status_work[,nam.M])
status_work$full_F=rowSums(status_work[,nam.F])
# same but for half-time contracts
nam.M = rep(0, ncol(status_work))
nam.F = rep(0, ncol(status_work))
for (i in 1:ncol(status_work)){
  sol = grepl('Half', names(status_work)[i])
  if (sol==1){
    if (grepl('M', names(status_work)[i])){
      nam.M[i] = i  
    }else{nam.F[i]=i}
  }else{
    nam.F[i]=0
    nam.M[i]=0}
}
#names(status_work[nam.M])
#names(status_work[nam.F])
status_work$half_M=rowSums(status_work[,nam.M])
status_work$half_F=rowSums(status_work[,nam.F])
# total of each variables
status_work$full <- status_work$full_M + status_work$full_F
status_work$half <- status_work$half_M + status_work$half_F
status_work$wagearner <- status_work$wagearner_M + status_work$wagearner_F
status_work$independent <- status_work$independent_M + status_work$independent_F
status_work$transfer <- status_work$transfer_F + status_work$transfer_M 
status_work$employer <- status_work$employer_F + status_work$employer_M 
# drop unnecessary variables
status_work <- subset(status_work, select = c(CODGEO,
                                              wagearner_M, wagearner_F, 
                                              independent_M, independent_F, 
                                              transfer_M, transfer_F, 
                                              employer_M, employer_F, 
                                              full_M, full_F,
                                              half_M, half_F,
                                              full, half,
                                              wagearner, independent, transfer, employer))
# Full variable means individuals with full time contracts. Half means individuals with part time contract. 
# "transfer" is individuals receiving transfer payments. 
# "independent" is like auto-entrepreneurs. 

2.8 Inequality data

Drop unnecessary variables and rename the other ones

names(ineq)
 [1] "CODGEO"        "LIBGEO"        "REG"           "DEP"           "P14_POP"      
 [6] "P09_POP"       "SUPERF"        "NAIS0914"      "DECE0914"      "P14_MEN"      
[11] "NAISD16"       "DECESD16"      "P14_LOG"       "P14_RP"        "P14_RSECOCC"  
[16] "P14_LOGVAC"    "P14_RP_PROP"   "NBMENFISC14"   "PIMP14"        "MED14"        
[21] "TP6014"        "P14_EMPLT"     "P14_EMPLT_SAL" "P09_EMPLT"     "P14_POP1564"  
[26] "P14_CHOM1564"  "P14_ACT1564"   "ETTOT15"       "ETAZ15"        "ETBE15"       
[31] "ETFZ15"        "ETGU15"        "ETGZ15"        "ETOQ15"        "ETTEF115"     
[36] "ETTEFP1015"   
# drop unnecessary variables
ineq <- subset(ineq, select = -c(LIBGEO, P09_POP, NAISD16, DECESD16, P14_RP_PROP, P09_EMPLT,ETTOT15, ETAZ15, ETBE15, ETFZ15, ETGU15, ETGZ15, ETOQ15, ETTEF115, ETTEFP1015))
# rename variables 
names(ineq)[4:ncol(ineq)] <-
  c("pop_2014",
    "Superficie",
    "birth09_14",
    "death09_14",
    "households14",
    "housing14",
    "princ_resid14",
    "sec_resid14",
    "vac_resid14",
    "tax_house14",
    "shared_tax_house14",
    "median_living14",
    "lev_ineq14",
    "empl",
    "emp_sal",
    "pop15_64",
    "unemp15_64",
    "act15_64")
# drop more unnecessary variables 
ineq <- subset(ineq, select = -c(princ_resid14, sec_resid14, vac_resid14, pop_2014, households14, birth09_14, 
                                death09_14, tax_house14, shared_tax_house14, lev_ineq14)) 
 
# unemployment rate 
ineq$unemp_rate <- ineq$unemp15_64/ineq$pop15_64 
# impute NA using median (only 3)
ineq$median_living14[is.na(ineq$median_living14)] = median(ineq$median_living14, na.rm = T)

2.9 Commune data

Drop unnecessary variables and rename the other ones

names(commune)
 [1] "codgeo"                           "orientationeconomique"           
 [3] "indicefiscalpartiel"              "scorefiscal"                     
 [5] "libgeo"                           "reg"                             
 [7] "dep"                              "indicedmographique"              
 [9] "population"                       "evolutionpop"                    
[11] "nbmnages"                         "nbrsidencesprincipales"          
[13] "nbpropritaire"                    "nblogement"                      
[15] "nboccupantsrsidenceprincipale"    "nbfemme"                         
[17] "nbhomme"                          "nbmineurs"                       
[19] "nbmajeurs"                        "nbetudiants"                     
[21] "nbentreprisessecteurservices"     "nbentreprisessecteurcommerce"    
[23] "nbentreprisessecteurconstruction" "nbentreprisessecteurindustrie"   
[25] "nbcrationenteprises"              "nbcrationindustrielles"          
[27] "nbcrationconstruction"            "nbcrationcommerces"              
[29] "nbcrationservices"                "moyennerevenusfiscauxdpartementa"
[31] "moyennerevenusfiscauxrgionaux"    "depmoyennesalaireshoraires"      
[33] "depmoyennesalairescadrehoraires"  "depmoyennesalairesprofintermdiai"
[35] "depmoyennesalairesemployhoraires" "depmoyennesalairesouvrihoraires" 
[37] "regmoyennesalaireshoraires"       "regmoyennesalairescadrehoraires" 
[39] "regmoyennesalairesprofintermdiai" "regmoyennesalairesemployhoraires"
[41] "regmoyennesalairesouvrihoraires"  "valeurajoutergionale"            
[43] "urbanitruralit"                   "scoreurbanit"                    
[45] "nbatifs"                          "nbactifssalaris"                 
[47] "nbactifsnonsalaris"               "nblogementsecondaireetoccasionne"
[49] "dynamiquedmographiquebv"          "tauxtudiants"                    
[51] "tauxproprit"                      "dynamiquedmographiqueinsee"      
[53] "capacitfisc"                      "capacitfiscale"                  
[55] "moyennerevnusfiscaux"             "nbindustriesdesbiensintermdiaire"
[57] "nbdecommerce"                     "nbdeservicesauxparticuliers"     
[59] "nbinstitutiondeeducationsantacti" "pibrgionnal"                     
[61] "segenvironnementdmographiqueobso" "scorecroissancepopulation"       
[63] "scorecroissanceentrepreneuriale"  "scorevargion"                    
[65] "scorepib"                         "environnementdmographique"       
[67] "fidlit"                          
#Drop unnecessary variables
commune <-subset(commune, select=-c(libgeo, scorefiscal, dep, reg, evolutionpop, nbpropritaire, 
                                    nblogementsecondaireetoccasionne, scoreurbanit, scorevargion, scorepib,  
                                    nbrsidencesprincipales, indicefiscalpartiel, nboccupantsrsidenceprincipale, 
                                    depmoyennesalaireshoraires, depmoyennesalairescadrehoraires, 
                                    depmoyennesalairesprofintermdiai, depmoyennesalairesemployhoraires, 
                                    depmoyennesalairesouvrihoraires, valeurajoutergionale, 
                                    nbindustriesdesbiensintermdiaire, nbdecommerce, nbdeservicesauxparticuliers, 
                                    nbinstitutiondeeducationsantacti, scorevargion, scorepib, 
                                    moyennerevenusfiscauxdpartementa, moyennerevenusfiscauxrgionaux, 
                                    regmoyennesalaireshoraires, regmoyennesalairescadrehoraires, 
                                    regmoyennesalairesprofintermdiai, regmoyennesalairesemployhoraires, 
                                    regmoyennesalairesouvrihoraires, tauxtudiants, dynamiquedmographiqueinsee, 
                                    capacitfisc, capacitfiscale, moyennerevnusfiscaux, nblogement, fidlit)) 
# rename variables  
names(commune) <-c("CODGEO", "orient_econ", "demo_index", "population", "nb_household", 
                    "nb_female", "nb_male", "nb_minor", "nb_major", "nb_students",  
                    "nb_firms_service", "nb_firms_commerce", "nb_firms_construction", 
                    "nb_firms_ind", "nb_created_firms", "nb_created_ind", 
                    "nb_created_construction", "nb_created_commerce",
                    "nb_created_services", "urbanRural", "nb_active", "nb_active_employees",
                    "nb_active_non_employees", "dymanic_demo_prof", "profitRate", 
                    "regional_GDP", "demo_environment", "evol_pop_score", 
                    "evol_entrepreneurial_score", "demo_envir2") 
# drop more variables 
commune <-subset(commune, select=-c(demo_index, population, profitRate, demo_envir2, demo_environment)) 

3 Produce consistent datasets

The CODGEO variables have to be merged. However, for different reasons already identified by other kaggle users they need some pre-processing. To do so, some already known mistakes are corrected:

firms$CODGEO       <- sub("A", "0", firms$CODGEO)
firms$CODGEO       <- sub("B", "0", firms$CODGEO)
salary$CODGEO      <- sub("A", "0", salary$CODGEO)
salary$CODGEO      <- sub("B", "0", salary$CODGEO)
population$CODGEO  <- sub("A", "0", population$CODGEO)
population$CODGEO  <- sub("B", "0", population$CODGEO)
geo$CODGEO         <- sub("A", "0", geo$CODGEO)
geo$CODGEO         <- sub("B", "0", geo$CODGEO)
educ$CODGEO        <- sub("A", "0", educ$CODGEO)
educ$CODGEO        <- sub("B", "0", educ$CODGEO)
categ_socio$CODGEO <- sub("A", "0", categ_socio$CODGEO)
categ_socio$CODGEO <- sub("B", "0", categ_socio$CODGEO)
ineq$CODGEO        <- sub("A", "0", ineq$CODGEO)
ineq$CODGEO        <- sub("B", "0", ineq$CODGEO)
status_work$CODGEO <- sub("A", "0", status_work$CODGEO)
status_work$CODGEO <- sub("B", "0", status_work$CODGEO)
commune$CODGEO     <- sub("A", "0", commune$CODGEO)
commune$CODGEO     <- sub("B", "0", commune$CODGEO)
# Then all CODGEO are trasformed to integers and four new datasets are created retaining only the common CODGEO:
# use only integer values
geo$CODGEO         <- as.integer(geo$CODGEO)  
population$CODGEO  <- as.integer(population$CODGEO)
firms$CODGEO       <- as.integer(firms$CODGEO)
salary$CODGEO      <- as.integer(salary$CODGEO)
status_work$CODGEO <- as.integer(status_work$CODGEO)  
ineq$CODGEO        <- as.integer(ineq$CODGEO)
educ$CODGEO        <- as.integer(educ$CODGEO)
categ_socio$CODGEO <- as.integer(categ_socio$CODGEO)
commune$CODGEO     <- as.integer(commune$CODGEO)
# store datasets' names to loop on them
dataset = c("population", "salary", "firms", "geo", "ineq", "commune", "educ", "categ_socio", "status_work")
# obtain sommon IDs for all datasets
for (i in dataset){
  # get i-th name and create a new variable concateneting "NEW" at the end
  nam <- paste(i, "NEW", sep = "")
  # initialize counter to identify the number of iteration in j
  iter = 1
  for (j in dataset){
    if (j != i){
      # for each dataset different from the i-th
      if (iter == 1){
        # 1st iteration: use the original dataset (e.g., geo)
        assign(nam, semi_join(get(i), get(j), by = "CODGEO"))
      } else{
        # successive iteration: use the new dataset (e.g., geoNEW)
        assign(nam, semi_join(get(nam), get(j), by = "CODGEO"))
      }
      iter = iter + 1
    }
  }
}
# check how many observation have been deleted
for (i in dataset){
  del_rows = nrow(get(i)) - nrow(get(paste(i, "NEW", sep = "")))
  del_prop = del_rows / nrow(get(i))
  del_obs = paste("For", i, del_rows, "units have been deleted.",
                  "They were the", round(del_prop*100, digits=2), "% of the total.", sep = " ")
  print(paste(del_obs))
}
[1] "For population 30843 units have been deleted. They were the 85.99 % of the total."
[1] "For salary 111 units have been deleted. They were the 2.16 % of the total."
[1] "For firms 31656 units have been deleted. They were the 86.3 % of the total."
[1] "For geo 31541 units have been deleted. They were the 86.26 % of the total."
[1] "For ineq 31664 units have been deleted. They were the 86.3 % of the total."
[1] "For commune 31652 units have been deleted. They were the 86.3 % of the total."
[1] "For educ 30843 units have been deleted. They were the 85.99 % of the total."
[1] "For categ_socio 30843 units have been deleted. They were the 85.99 % of the total."
[1] "For status_work 30843 units have been deleted. They were the 85.99 % of the total."
print(paste("The new dataset has", nrow(salaryNEW), "units and", 
      ncol(salaryNEW)+ncol(populationNEW)+ncol(firmsNEW)+ncol(geoNEW)+
        ncol(status_workNEW)+ncol(ineqNEW)+ncol(educNEW)+ncol(categ_socioNEW)+ncol(communeNEW), "features."))
[1] "The new dataset has 5025 units and 131 features."

Overwrite new datesets into the old ones and remove the latter ones from memory

firms       <- firmsNEW
geo         <- geoNEW
salary      <- salaryNEW
population  <- populationNEW
educ        <- educNEW
categ_socio <- categ_socioNEW
status_work <- status_workNEW
ineq        <- ineqNEW
commune     <- communeNEW
rm(list=c("firmsNEW", "geoNEW", "salaryNEW", "populationNEW", "educNEW", "categ_socioNEW", "status_workNEW", "ineqNEW", "communeNEW"))

4 Descriptive statistics

4.1 Firms data

Check the distribution of the null firms (i.e., unknown sizes) and analyze firms’ distribution per town:

# check the ratio of null firms for each town
summary(firms$null/firms$total)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.3000  0.5937  0.6549  0.6549  0.7152  1.0000 
# a lot of information is missing, should we remove these data?
ggplot(data=firms, aes(firms$null/firms$total)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Null firms/total firms", y="Count") +
  ggtitle("Ratio between the number of null firms and total firms per town") +
  theme(plot.title = element_text(hjust = 0.5)) 

# distribution for the firms with unknown size 
ggplot(data=firms, aes(log(firms$null))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms of unknown size (log scale)", y="Density") +
  ggtitle("Number of firms of unknown size per town") +
  theme(plot.title = element_text(hjust = 0.5))

# distribution for the total number of firms 
ggplot(data=firms, aes(log(firms$total))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms (log scale)", y="Density") +
  ggtitle("Total number of firms per town") +
  theme(plot.title = element_text(hjust = 0.5)) 

# distribution for the number of micro size firms 
ggplot(data=firms, aes(log(firms$micro))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms of micro size (log scale)", y="Density") +
  ggtitle("Number of firms of micro size per town") +
  theme(plot.title = element_text(hjust = 0.5))

# distribution for the number of small size firms 
ggplot(data=firms, aes(log(firms$small))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms of small size (log scale)", y="Density") +
  ggtitle("Number of firms of small size per town") +
  theme(plot.title = element_text(hjust = 0.5))

# distribution for the number of medium size firms 
ggplot(data=firms, aes(log(firms$medium))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms of medium size (log scale)", y="Density") +
  ggtitle("Number of firms of medium size per town") +
  theme(plot.title = element_text(hjust = 0.5))

# distribution for the number of large size firms 
ggplot(data=firms, aes(log(firms$large))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Number of firms of large size (log scale)", y="Density") +
  ggtitle("Number of firms of large size per town") +
  theme(plot.title = element_text(hjust = 0.5))

Check correlation among the variables:

corrplot(cor(firms[,3:8]))

4.1.1 What have learned

Solved problems:

  • Use the EU firms’ categorization.

Some problematic aspects:

  • Some towns have 100% of null firms, should we remove these data?
  • These variables are strongly correlated, should we use just the total amount in the following parts?

We could use these data for the following tasks:

  • predict the salaries using such information as proxy for the competition in the job market;
  • predict the total number of firms, using salary data;
  • geo-spatial plot for firms’ size

4.2 Geographic data

Plot available towns on a map:

# center of France, obtained using:
# fra_center = as.numeric(geocode("France"))
fra_center = c(2.213749, 46.227638)
# plot all European towns available
geo_pos = as.data.frame(cbind(lon = geo$longitude, lat = geo$latitude))
geo_pos = geo_pos[complete.cases(geo_pos),]
ggmap(get_googlemap(center=fra_center, scale=2, zoom=5), extent="normal") +
  geom_point(aes(x=lon, y=lat), data=geo_pos, col="orange", alpha=0.2, size=0.01) 
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=5&size=640x640&scale=2&maptype=terrain&sensor=false

# Try to cll google API until there is no error
# 
# FraMap = NA
# while (any(is.na(FraMap))){
#   FraMap = tryCatch({
#       ggmap(get_googlemap(center=fra_center, scale=2, zoom=5), extent="normal")
#   }, error = function(e) {
#     message("cannot open URL")  
#     FraMap = NA
#   })
# }

4.2.1 What we have learned

Solved problems:

  • Missing latitude and longitude;
  • Duplications due to multiple postal codes in the same town;
  • Exclude non-European towns;

These information are useful to plot any future dataset/analysis. In addition, they could be useful to compare European towns vs. old colonies.

4.3 Salary data

Univariate analysis comparing salaries for both genders among various job categories:

#  number of units
n_sex <- length(salary$sal_Females)
# vector representing males and females
Label <- c(rep("M", n_sex*5), rep("F", n_sex*5))
# vector representing the variable considered
Variable <- c(rep("General", n_sex), 
             rep("Executive", n_sex),
             rep("MidManager", n_sex),
             rep("Employee", n_sex),
             rep("Worker",n_sex),
             rep("General", n_sex), 
             rep("Executive", n_sex),
             rep("MidManager", n_sex),
             rep("Employee", n_sex),
             rep("Worker",n_sex))
# merge these data
sal_sex = cbind.data.frame(Label = Label, 
             value = c(salary$sal_Males, salary$sal_M_executive, salary$sal_M_midManager, salary$sal_M_employee, salary$sal_M_worker,
                       salary$sal_Females, salary$sal_F_executive, salary$sal_F_midManager, salary$sal_F_employee, salary$sal_F_worker),
             Variable = Variable)
# plotting phase
ggplot(data = sal_sex, aes(x=Label, y=value)) +
  geom_boxplot(aes(fill = Label)) +
  # not color points replacing colour = group instead of colour=Label
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Sex") + ylab("Mean net salary per hour") + ggtitle("Gender comparison for different job positions") +
  theme(plot.title = element_text(hjust = 0.5)) +      stat_boxplot(geom = "errorbar", width = 0.5)

  # + guides(fill=guide_legend(title="Legend"))
# the same but excluding outliers
ggplot(data = sal_sex, aes(x=Label, y=value)) +
  scale_y_continuous(limits = quantile(sal_sex$value, c(0, 0.9))) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Sex") + ylab("Mean net salary per hour") + 
  ggtitle("Gender comparison for different job positions excluding the last decile") +
  theme(plot.title = element_text(hjust = 0.5)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

Univariate analysis comparing salaries for both genders among various ages:

# vector representing males and females
Label <- c(rep("M", n_sex*3), rep("F", n_sex*3))
# vector representing the variable considered
Variable <- c(rep("18-25", n_sex), 
              rep("26-50", n_sex),
              rep("51+", n_sex),
              rep("18-25", n_sex), 
              rep("26-50", n_sex),
              rep("51+", n_sex))
# merge these data
sal_sex <- cbind.data.frame(Label = Label, 
                           value = c(salary$sal_M_18_25, salary$sal_M_26_50, salary$sal_M_51plus, 
                                     salary$sal_F_18_25, salary$sal_F_26_50, salary$sal_F_51plus),
                           Variable = Variable)
# plotting phase
ggplot(data = sal_sex, aes(x=Label, y=value)) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Sex") + ylab("Mean net salary per hour") + ggtitle("Gender comparison for different ages") +
  theme(plot.title = element_text(hjust = 0.5)) + ylim(c(5, 100)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

The income inequality between genders, age groups and working positions is clear. In the following analyses the focus is on the salary ratio between women and men among different job positions:

# Gender salary ratio and general level of income
# Overall mean salary: The higher the net mean income, the more skewed the ratio of salary between female and male is. Only 2 towns have a ratio>1
# create overall F vs M ratio
salary$salary_ratio_FvsM <- salary$sal_Females / salary$sal_Males
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="Overall salary ratio (females/males)", y="Density") + 
  labs(title = "Overall salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs overall mean salary
ggplot(salary, aes(x= sal_general, y=salary_ratio_FvsM)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="Overall salary ratio(females/males)") + 
  labs(title = "Overall salary ratio between females and males vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm") 

# Executives mean salary: a bit better the situation for females in this case and less skewed
# create Executives F vs M ratio
salary$salary_ratio_FvsM_Exec <- salary$sal_F_executive / salary$sal_M_executive
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_Exec)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="Executives salary ratio (females/males)", y="Density") + 
  labs(title = "Executives salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs executives mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_Exec)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="Executives salary ratio (females/males)") + 
  labs(title = "Executives salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

# Middle managers mean salary: ....
# create Middle managers F vs M ratio
salary$salary_ratio_FvsM_midManag <- salary$sal_F_midManager / salary$sal_M_midManager
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_Exec)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="Middle managers salary ratio (females/males)", y="Density") + 
  labs(title = "Middle managers salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs executives mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_midManag)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="Middle managers salary ratio (females/males)") + 
  labs(title = "Middle managers salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

# Workers mean salary: ...
# create workers F vs M ratio
salary$salary_ratio_FvsM_worker <- salary$sal_F_worker / salary$sal_M_worker
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_worker)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="Workers salary ratio (females/males)", y="Density") + 
  labs(title = "Workers salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs workers mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_worker)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="Workers salary ratio (females/males)") + 
  labs(title = "Workers salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

# Employee mean salary: ...
# create Employee F vs M ratio
salary$salary_ratio_FvsM_employee <- salary$sal_F_employee / salary$sal_M_employee
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_employee)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="Employee salary ratio (females/males)", y="Density") + 
  labs(title = "Employee salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs Employee mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_employee)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="Employee salary ratio (females/males)") + 
  labs(title = "Employee salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

Now the focus is on the salary ratio between women and men among different age groups:

# 18-25 mean salary: are quite equal apart from some outliers and a quadratic trend
# create 18-25 F vs M ratio
salary$salary_ratio_FvsM_18_25 <- salary$sal_F_18_25 / salary$sal_M_18_25
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_18_25)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="18-25 salary ratio (females/males)", y="Density") + 
  labs(title = "18-25 salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs 18-25 mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_18_25)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="18-25 salary ratio (females/males)") + 
  labs(title = "18-25 salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

# scatter plot vs 18-25 mean salary for them
ggplot(salary, aes(x= sal_18_25, y= salary_ratio_FvsM_18_25)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="18-25 salary", y="18-25 salary ratio (females/males)") + 
  labs(title = "18-25 salary ratio between females and males \n vs. overall 18-25 salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "loess")

# 26-50 mean salary: ...
# create 26-50 F vs M ratio
salary$salary_ratio_FvsM_26_50 <- salary$sal_F_26_50 / salary$sal_M_26_50
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_26_50)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="26-50 salary ratio (females/males)", y="Density") + 
  labs(title = "26-50 salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs 26-50 mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_26_50)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="26-50 salary ratio (females/males)") + 
  labs(title = "26-50 salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

# 51+ mean salary: ...
# create 51+ F vs M ratio
salary$salary_ratio_FvsM_51plus <- salary$sal_F_51plus / salary$sal_M_51plus
# histogram
ggplot(data=salary, aes(salary$salary_ratio_FvsM_51plus)) + 
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) + 
  geom_density(col="black") + 
  labs(x="51+ salary ratio (females/males)", y="Density") + 
  labs(title = "51+ salary ratio between females and males") + 
  theme(plot.title = element_text(hjust = 0.5)) 

# scatter plot vs 26-50 mean salary
ggplot(salary, aes(x= sal_general, y= salary_ratio_FvsM_51plus)) +  
  geom_point(size = 0.5, colour = "#0091ff")+ 
  labs(x="Overall salary", y="51+ salary ratio (females/males)") + 
  labs(title = "51+ salary ratio between females and males \n vs. overall salary") + 
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_smooth(method = "lm")

Highlight bivariate relations:

# correlation matrix
corrplot(cor(salary[, 3:ncol(salary)]), method = "circle", title = "Correlation matrix for salary data", 
         diag = T, tl.cex=0.5, type="lower", 
         tl.col = "black", mar=c(0,0,1.5,0)) 

# most general pairs
pairs(salary[c(3:8, 13, 18:20)], gap=0, main = "Scatter matrix of the main variables in salary data", cex = 0.6)

# pairs highlighting genders' differences
pairs(salary[c(9:12, 14:17)], gap=0, main = "Scatter matrix of job categories for both genders", cex = 0.6)

4.3.1 What we have learned

….

4.4 Population data

Plot population data across different towns:

# Histogram of total population per town in log
ggplot(data=population, aes(ifelse(total_population!=0, log10(total_population), 0))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Log10 total population", y="Density") +
  ggtitle("Histogram of total population per town in log10 scale") +
  theme(plot.title = element_text(hjust = 0.5))

# Histogram of dependency ratio per town
ggplot(data=population, aes(ifelse(dependency_ratio!=0, log10(dependency_ratio), 0))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Log10 dependency ratio", y="Density") +
  ggtitle("Histogram of dependency ratio per town in log10 scale") +
  theme(plot.title = element_text(hjust = 0.5))

# Histogram of sex ratio per town
ggplot(data=population, aes(ifelse(sex_ratio!=0, log10(sex_ratio), 0))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Log10 sex ratio", y="Density") +
  ggtitle("Histogram of sex ratio per town in log10 scale") +
  theme(plot.title = element_text(hjust = 0.5))

Understand which towns have the highest concentration of population and ratios.

# Merge geography and population data
# geo_population <- merge(geo, population, by="CODGEO")
geo_population <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(population$CODGEO),],
                                   population[unique(population$CODGEO) %in% unique(geo$CODGEO),])
# France map
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
# Plot "Distribution of total population for each town" 
sc <- scale_colour_gradientn(colours =palette(rainbow(3)), limits=c(log10(min(geo_population$total_population)), 
                                                                    log10(max(geo_population$total_population)))) 
population_distribution <-
  FraMap +
  geom_point(aes(x=geo_population$longitude, y=geo_population$latitude,
                 colour=log10(geo_population$total_population)), 
             data=geo_population, alpha=0.2, size=0.01) +
             sc + labs(color='') + ggtitle("Distribution of Population for each town")
population_distribution
# Plot "Distribution of aged dependency ratio for each town"
sc <- scale_colour_gradientn(colours =palette(rainbow(4)), limits=c(min(geo_population$aged_dependency_ratio),
                                                                  max(geo_population$aged_dependency_ratio)))

aged_ratio_distribution <-
  FraMap +
  geom_point(aes(x=geo_population$longitude, y=geo_population$latitude,
                 colour=geo_population$aged_dependency_ratio),
             data=geo_population, alpha=1, size=1) +
             sc + labs(color='') + ggtitle("Aged dependency ratio per town")
aged_ratio_distribution

# which city has the highest/lowest aged dependency ratio 
geo_population[which.min(geo_population$aged_dependency_ratio),c('CODGEO')]  
[1] 77183
geo_population[which.max(geo_population$aged_dependency_ratio),c('CODGEO')]  
[1] 85003
 
# get list of top 10 highest aged dependency ratio 
Rank = sort(population$aged_dependency_ratio, TRUE) 
Rank10 = sort(Rank[1:10], TRUE) 
sol = rep(0, 10) 
for (i in 1:10){ 
    sol[i] = paste(geo_population$CODGEO[geo_population$aged_dependency_ratio %in% Rank10[i]] )
} 
# remove the dataset created to save memory
rm(list = c("geo_population", "Rank", "Rank10")) 

4.4.1 What we have learned

We can observe that in urban cities, the aged dependcy ratio is lower compared to non-urban cities. The city with the lowest aged dependency ratio is La Fert?-sous-Jouarre and the highest is L’Aiguillon-sur-Mer.

4.5 Education data

 
# Difference between male and female's education level (with University degrees) 
summary(educ$male_Univ) 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    42.0    206.0    329.0    960.3    656.0 440490.0 
summary(educ$female_Univ) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     57     255     395    1100     776  486406 
summary(educ$male_NoDip) 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    58.0    260.0    401.0    904.1    754.0 138888.0 
summary(educ$female_NoDip) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    107     356     567    1242    1081  177240 
 
# Merge geo and educ data to include longitude and latitude in the data. 
geo_educ <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(educ$CODGEO),], 
                                   educ[unique(educ$CODGEO) %in% unique(geo$CODGEO),]) 
 
# check the ratio of individuals with high and low education level for each town. 
summary(educ$univ/educ$nodip) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.1149  0.4989  0.7689  0.9438  1.1728  7.7004 
 
# Histogram of the ratio of individuals with high and low education level for each town 
ggplot(data=educ, aes(log(educ$univ/educ$nodip))) + 
  geom_histogram(aes(y=..density..), col="black", fill="blue", alpha=0.3, bins= 50)+ 
                   geom_density(col="black") + 
                   labs(x="log. University degree/No diploma", y="Density") + 
                   ggtitle("Ratio between the number of individuals with no diploma and  
                           individuals with University degree per town") + 
                   theme(plot.title =element_text(hjust= 0.5)) 

 
# Adjust scales before plotting 
sc <- scale_colour_gradientn(colours = c("#56ddc5", "#F8766D", "#00BA38"), limits=c(min(educ$univ/educ$nodip), max(educ$univ/educ$nodip))) 
 
# Map ratio  
educ_ratio_NoDipUniv <- 
  FraMap + 
  geom_point(aes(x=geo_educ$longitude, y=geo_educ$latitude, 
                 colour=educ$univ/educ$nodip), 
             data=geo_educ, alpha=1, size=1) + 
             sc + labs(color='Ratio') + ggtitle("Education level ratio for each town") 
educ_ratio_NoDipUniv 

 
# Statistics of shape for the data 
# library(psych) 
describe(educ$univ/educ$nodip) 
   vars    n mean   sd median trimmed  mad  min max range skew kurtosis   se
X1    1 5025 0.94 0.67   0.77    0.84 0.47 0.11 7.7  7.59  2.4     9.98 0.01
 
# remove the dataset created to save memory 
rm(list=c("geo_educ", "educ_ratio_NoDipUniv")) 
 

4.5.1 What we have learned

In average 960 males have university degree and 1100 females have university degree.
In average 904 males have no diploma and 1242 females have no diploma. Highest ratio in Paris=78251=Yvelines

4.6 Demographic/social profiles data

glimpse(categ_socio)
Observations: 5,025
Variables: 9
$ CODGEO               <int> 1004, 1007, 1014, 1024, 1025, 1027, 1031, 1032, 1033, 1...
$ male_immig           <dbl> 702, 55, 456, 55, 60, 63, 397, 72, 1304, 519, 115, 90, ...
$ female_immig         <dbl> 660, 45, 427, 65, 35, 38, 397, 92, 1337, 490, 145, 65, ...
$ total_immig          <dbl> 1362, 100, 883, 120, 95, 101, 794, 164, 2641, 1009, 260...
$ male_working_immig   <dbl> 568, 25, 334, 51, 45, 59, 308, 56, 1006, 400, 90, 70, 2...
$ female_working_immig <dbl> 562, 25, 333, 52, 30, 34, 348, 80, 1069, 387, 100, 45, ...
$ male_native          <dbl> 6076, 1174, 1325, 1581, 1548, 1798, 1391, 1524, 4448, 3...
$ female_native        <dbl> 6586, 1289, 1195, 1681, 1534, 1063, 1463, 1626, 4645, 4...
$ total_native         <dbl> 12662, 2463, 2520, 3262, 3082, 2861, 2854, 3150, 9093, ...
# merge geo and categ_socio
geo_categ_socio <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(categ_socio$CODGEO),],
                                   categ_socio[unique(categ_socio$CODGEO) %in% unique(geo$CODGEO),])
# find which cities have the most and least number of immigrants
categ_socio[which.max(categ_socio$total_immig),c('CODGEO')] 
[1] 75056
categ_socio[which.min(categ_socio$total_immig),c('CODGEO')] 
[1] 33154
# get list of top 10 cities with the most number of immigrants
Rank = sort(categ_socio$total_immig, TRUE)
Rank10 = sort(Rank[1:10], TRUE)
sol = rep(0, 10)
for (i in 1:10){
    sol[i] = paste(geo_categ_socio$region[categ_socio$total_immig %in% Rank10[i]] )
}
# Adjust scales before plotting 
sc <- scale_colour_gradientn(colours = c("#F8766D",  "#00BA38"), limits=c(min(geo_categ_socio$total_immig/population$total_population), max(geo_categ_socio$total_immig/population$total_population))) 
 
# Map ratio  
distribution_immig <- 
  FraMap + 
  geom_point(aes(x=geo_categ_socio$longitude, y=geo_categ_socio$latitude, 
                 colour=geo_categ_socio$total_immig/population$total_population), 
             data=geo_categ_socio, alpha=1, size=1) + 
             sc + labs(color='') + ggtitle("Immigrants for each town") 
distribution_immig

rm(geo_categ_socio)

4.6.1 What we have learned

City with the least number of immigrants is Rennes and the most is Paris. =“Île-de-France” “Provence-Alpes-Côte d’Azur” “Rhône-Alpes” “Midi-Pyrénées”
“Provence-Alpes-Côte d’Azur” “Alsace” “Languedoc-Roussillon” “Île-de-France”
“Île-de-France” “Île-de-France”

4.7 Work status data

summary(status_work)
     CODGEO       wagearner_M      wagearner_F     independent_M     independent_F     
 Min.   : 1004   Min.   :     0   Min.   :     0   Min.   :    0.0   Min.   :    0.00  
 1st Qu.:31455   1st Qu.:   274   1st Qu.:   299   1st Qu.:   37.0   1st Qu.:   23.00  
 Median :56017   Median :   589   Median :   594   Median :   62.0   Median :   40.00  
 Mean   :51625   Mean   :  1964   Mean   :  2011   Mean   :  149.8   Mean   :   99.83  
 3rd Qu.:73008   3rd Qu.:  1412   3rd Qu.:  1334   3rd Qu.:  112.0   3rd Qu.:   75.00  
 Max.   :97113   Max.   :751026   Max.   :828113   Max.   :78180.0   Max.   :56586.00  
   transfer_M        transfer_F        employer_M        employer_F      
 Min.   :  0.000   Min.   :  0.000   Min.   :    0.0   Min.   :    0.00  
 1st Qu.:  0.000   1st Qu.:  0.000   1st Qu.:   34.0   1st Qu.:   10.00  
 Median :  0.000   Median :  0.000   Median :   59.0   Median :   20.00  
 Mean   :  1.287   Mean   :  3.183   Mean   :  134.4   Mean   :   48.01  
 3rd Qu.:  0.000   3rd Qu.:  5.000   3rd Qu.:  110.0   3rd Qu.:   40.00  
 Max.   :761.000   Max.   :889.000   Max.   :54988.0   Max.   :20580.00  
     full_M           full_F           half_M            half_F        
 Min.   :     0   Min.   :     0   Min.   :    0.0   Min.   :     0.0  
 1st Qu.:   332   1st Qu.:   227   1st Qu.:   26.0   1st Qu.:   105.0  
 Median :   662   Median :   458   Median :   53.0   Median :   198.0  
 Mean   :  2071   Mean   :  1580   Mean   :  178.3   Mean   :   581.9  
 3rd Qu.:  1526   3rd Qu.:  1030   3rd Qu.:  118.0   3rd Qu.:   417.0  
 Max.   :790568   Max.   :712040   Max.   :94387.0   Max.   :194128.0  
      full              half            wagearner        independent      
 Min.   :      0   Min.   :     0.0   Min.   :      0   Min.   :     0.0  
 1st Qu.:    575   1st Qu.:   135.0   1st Qu.:    588   1st Qu.:    62.0  
 Median :   1126   Median :   252.0   Median :   1194   Median :   102.0  
 Mean   :   3651   Mean   :   760.2   Mean   :   3975   Mean   :   249.6  
 3rd Qu.:   2568   3rd Qu.:   536.0   3rd Qu.:   2772   3rd Qu.:   180.0  
 Max.   :1502608   Max.   :288515.0   Max.   :1579139   Max.   :134766.0  
    transfer           employer      
 Min.   :   0.000   Min.   :    0.0  
 1st Qu.:   0.000   1st Qu.:   45.0  
 Median :   0.000   Median :   79.0  
 Mean   :   4.471   Mean   :  182.4  
 3rd Qu.:   5.000   3rd Qu.:  148.0  
 Max.   :1650.000   Max.   :75568.0  
names(status_work) 
 [1] "CODGEO"        "wagearner_M"   "wagearner_F"   "independent_M" "independent_F"
 [6] "transfer_M"    "transfer_F"    "employer_M"    "employer_F"    "full_M"       
[11] "full_F"        "half_M"        "half_F"        "full"          "half"         
[16] "wagearner"     "independent"   "transfer"      "employer"     
# merge geo and status_work
# geo_status_work <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(status_work$CODGEO),],
#                                    status_work[unique(status_work$CODGEO) %in% unique(geo$CODGEO),])
geo_status_work <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(status_work$CODGEO),], 
                                    status_work[unique(status_work$CODGEO) %in% unique(geo$CODGEO),]) 
# which town has the most/least people receiving government transfers
status_work[which.max(status_work$transfer),c('CODGEO')] 
[1] 75056
status_work[which.min(status_work$transfer),c('CODGEO')] 
[1] 1007
# get list of top 10 cities with the people receiving government transfers
Rank = sort(status_work$transfer, TRUE)
Rank10 = sort(Rank[1:10], TRUE)
sol = rep(0, 10)
for (i in 1:10){
    sol[i] = paste(geo_status_work$CODGEO[status_work$transfer %in% Rank10[i]])
}
# which town has the most/least people with part time contract.
status_work[which.max(status_work$half),c('CODGEO')] 
[1] 75056
status_work[which.min(status_work$half),c('CODGEO')] 
[1] 76095
# get list of top 10 cities with the most part-time contract.
Rank = sort(status_work$half, TRUE)
Rank10 = sort(Rank[1:10], TRUE)
sol = rep(0, 10)
for (i in 1:10){
    sol[i] = paste(status_work$CODGEO[status_work$half %in% Rank10[i]] ) 
}
# Histogram (Females with full time contract)
describe(status_work$full_F)    # Skewness
   vars    n    mean       sd median trimmed    mad min    max  range  skew kurtosis
X1    1 5025 1580.12 11070.87    458  658.08 422.54   0 712040 712040 53.63  3381.11
       se
X1 156.18
ggplot(data=status_work, aes(ifelse(full_F!=0, log(full_F), 0))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="", y="Density") +
  ggtitle("Female workers with full time contract") + 
  theme(plot.title = element_text(hjust = 0.5))

# Histogram (males with full time contract) 
describe(status_work$full_M)    # Skewness 
   vars    n    mean       sd median trimmed    mad min    max  range  skew kurtosis
X1    1 5025 2070.78 12491.66    662  953.99 616.76   0 790568 790568 51.29  3167.63
       se
X1 176.22
ggplot(data=status_work, aes(ifelse(full_M!=0, log(full_M), 0))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="", y="Density") +
  ggtitle("Male workers with full time contract") + 
  theme(plot.title = element_text(hjust = 0.5))

# Histogram: Contract type ratio
describe(status_work$half/status_work$full)    # Skewness
   vars    n mean   sd median trimmed  mad  min  max range skew kurtosis se
X1    1 5024 0.24 0.09   0.23    0.23 0.07 0.04 0.82  0.78 1.24     3.45  0
ggplot(data=status_work, aes(status_work$half/status_work$full)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 50) +
  geom_density(col="black") +
  labs(x="Contract type ratio (temporary/full time)", y="Density") +
  ggtitle("Contract type ratio") +
  theme(plot.title = element_text(hjust = 0.5))

#  number of units
n_sex <- length(status_work$wagearner_F)
# vector representing males and females
Label <- c(rep("M", n_sex*6), rep("F", n_sex*6))
# vector representing the variable considered
Variable <- c(rep("Wagearner", n_sex), 
             rep("Independent", n_sex),
             rep("Transfer", n_sex),
             rep("Employer", n_sex),
             rep("Full",n_sex),
             rep("Half", n_sex))
             
# merge these data
status_sex = cbind.data.frame(Label = Label, 
             value = c(status_work$wagearner_M, status_work$wagearner_F, status_work$independent_M,
                       status_work$independent_F, status_work$transfer_M, status_work$transfer_F,
                       status_work$employer_M, status_work$employer_F, status_work$full_M, status_work$full_F,
                       status_work$half_M, status_work$half_F),
             Variable = Variable)
# plotting phase
ggplot(data = status_sex, aes(x=Label, y=value)) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Sex") + ylab("# of individuals") + ggtitle("Gender comparison") +
  theme(plot.title = element_text(hjust = 0.5)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

# the same but excluding outliers
ggplot(data = status_sex, aes(x=Label, y=value)) +
  scale_y_continuous(limits = quantile(status_sex$value, c(0, 0.9))) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Sex") + ylab("# of individuals") + 
  ggtitle("Gender comparison excluding the last decile") +
  theme(plot.title = element_text(hjust = 0.5)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

4.7.1 What we have learned

Paris is the city where they have the most individuals receiving government transfers. Lyon is the city where they have the least individuals receiving government transfers. =“Île-de-France” “Provence-Alpes-Côte d’Azur” “Provence-Alpes-Côte d’Azur” “Rhône-Alpes”
“Midi-Pyrénées” “Aquitaine” “Languedoc-Roussillon” “Pays de la Loire”
“Rhône-Alpes” “Alsace”

Paris has most individuals with half time contract. Rouen has the least. = “Île-de-France” “Rhône-Alpes” “Provence-Alpes-Côte d’Azur” “Midi-Pyrénées”
“Pays de la Loire” “Nord-Pas-de-Calais” “Alsace” “Languedoc-Roussillon”
“Aquitaine” “Bretagne”

In average, more females receive transfers than males. In average, more females have part-time contract than males. (In average, 581 females have part-time contract (in each town) and 178 males have part-time contract. (More males have full-time contract.)

More females work as wage-earners than males. =All these facts can be related to vulnerability of females in the labour market. Many studies show that females are mostly working as part-time workers and lower job categories. On top of this, more females receive transfers. We can also observe this in our data that more males are registered as independent workers or employers.

4.8 Inequality data

glimpse(ineq)
Observations: 5,025
Variables: 12
$ CODGEO          <int> 1004, 1007, 1014, 1024, 1025, 1027, 1031, 1032, 1033, 1034, ...
$ REG             <int> 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, ...
$ DEP             <fctr> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
$ Superficie      <int> 25, 34, 23, 19, 40, 18, 8, 13, 15, 22, 11, 9, 24, 6, 9, 33, ...
$ housing14       <int> 6838, 1160, 1453, 1362, 1242, 688, 1867, 1253, 5801, 4804, 1...
$ median_living14 <int> 19756, 21679, 19806, 21143, 21781, 22310, 18911, 20612, 1934...
$ empl            <int> 7453, 488, 2128, 852, 474, 2117, 2113, 1430, 4328, 5722, 335...
$ emp_sal         <int> 6743, 373, 1940, 738, 354, 1903, 1934, 1297, 3824, 5099, 303...
$ pop15_64        <int> 8963, 1614, 2217, 2163, 2043, 2361, 2426, 2059, 7271, 5286, ...
$ unemp15_64      <int> 1060, 108, 204, 121, 120, 69, 263, 165, 976, 647, 193, 111, ...
$ act15_64        <int> 6682, 1267, 1596, 1726, 1634, 2020, 1776, 1627, 5551, 3923, ...
$ unemp_rate      <dbl> 0.11826397, 0.06691450, 0.09201624, 0.05594082, 0.05873715, ...
# merge geo and ineq
geo_ineq <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(ineq$CODGEO),],
                                   ineq[unique(ineq$CODGEO) %in% unique(geo$CODGEO),])
# which city has the highest/lowest unemployment rate
ineq[which.max(ineq$unemp_rate),c('CODGEO')] 
[1] 33402
ineq[which.min(ineq$unemp_rate),c('CODGEO')] 
[1] 73257
# get list of top 10 cities with the highest unemployment rate.
Rank = sort(ineq$unemp_rate, TRUE)
Rank10 = sort(Rank[1:10], TRUE)
sol = rep(0, 10)
for (i in 1:10){
    sol[i] = paste(ineq$CODGEO[ineq$unemp_rate %in% Rank10[i]] ) 
}
# which is the smallest/biggest city (area)
ineq[which.max(ineq$Superficie),c('CODGEO')] 
[1] 13004
ineq[which.min(ineq$Superficie),c('CODGEO')] 
[1] 6011
# which city has the highest/lowest median living level
ineq[which.max(ineq$median_living14),c('CODGEO')] 
[1] 74016
ineq[which.min(ineq$median_living14),c('CODGEO')] 
[1] 93014
# Adjust scales before plotting
sc <- scale_colour_gradientn(colours = c("#56ddc5", "#ff3db7"), limits=c(min(ineq$unemp_rate), max(ineq$unemp_rate)))
# Map ratio 
ineq_unempl_distribution <-
  FraMap +
  geom_point(aes(x=geo_ineq$longitude, y=geo_ineq$latitude,
                 colour=geo_ineq$unemp_rate),
             data=geo_ineq, alpha=1, size=1) +
             sc + labs(color='Unemployment rate') + ggtitle("Unemployment rate for each town")
ineq_unempl_distribution

# remove the dataset created to save memory
rm(list=c("geo_ineq", "ineq_unempl_distribution"))

4.8.1 What we have learned

Bordeaux has the highest unemployment rate. Nantes has the lowest unemployment rate. =“Aquitaine” “Languedoc-Roussillon” “Nord-Pas-de-Calais” “Haute-Normandie” “Guadeloupe”
“Corse” “Nord-Pas-de-Calais” “Corse” “Rhône-Alpes” “Nord-Pas-de-Calais”

4.9 Commune data

glimpse(commune)
Observations: 5,025
Variables: 25
$ CODGEO                     <int> 1004, 1007, 1014, 1024, 1025, 1027, 1031, 1032, 1...
$ orient_econ                <fctr> Bassin Résidentiel, Bassin Résidentiel, Bassin...
$ nb_household               <int> 4640, 798, 1139, 719, 797, 482, 1484, 855, 4526, ...
$ nb_female                  <int> 11350, 2078, 3480, 1828, 2220, 1372, 3344, 2442, ...
$ nb_male                    <int> 10878, 2096, 3560, 1922, 2280, 1590, 3476, 2546, ...
$ nb_minor                   <int> 13624, 2586, 4537, 2295, 2886, 1871, 4184, 3233, ...
$ nb_major                   <int> 8604, 1588, 2503, 1455, 1614, 1091, 2636, 1755, 8...
$ nb_students                <int> 904, 173, 335, 159, 152, 133, 385, 190, 755, 519,...
$ nb_firms_service           <int> 342, 33, 63, 34, 17, 17, 70, 34, 264, 288, 108, 3...
$ nb_firms_commerce          <int> 301, 27, 96, 24, 22, 33, 54, 32, 270, 236, 140, 4...
$ nb_firms_construction      <int> 58, 17, 22, 20, 14, 9, 15, 22, 83, 36, 49, 21, 17...
$ nb_firms_ind               <int> 108, 24, 106, 22, 16, 26, 102, 34, 83, 103, 75, 3...
$ nb_created_firms           <int> 83, 14, 19, 7, 8, 5, 12, 14, 42, 54, 37, 9, 226, ...
$ nb_created_ind             <int> 4, 1, 1, 1, 0, 0, 2, 2, 3, 3, 0, 0, 17, 1, 1, 1, ...
$ nb_created_construction    <int> 14, 2, 3, 0, 1, 0, 2, 1, 15, 10, 6, 1, 31, 1, 3, ...
$ nb_created_commerce        <int> 27, 1, 7, 4, 2, 5, 3, 1, 6, 18, 7, 3, 63, 5, 2, 2...
$ nb_created_services        <int> 38, 10, 8, 2, 5, 0, 5, 10, 18, 23, 24, 5, 115, 3,...
$ urbanRural                 <fctr> Com < 50 m habts, Com rurale > 2 000 habts, Com ...
$ nb_active                  <int> 4556, 921, 1589, 912, 1070, 740, 1727, 1115, 4382...
$ nb_active_employees        <int> 4203, 798, 1418, 814, 906, 678, 1594, 1005, 4033,...
$ nb_active_non_employees    <int> 353, 123, 171, 98, 164, 62, 133, 110, 349, 320, 2...
$ dymanic_demo_prof          <fctr> 1.Accroissement par excédent naturel et migrato...
$ regional_GDP               <int> 173681, 173681, 173681, 173681, 173681, 173681, 1...
$ evol_pop_score             <fctr> 72,95082, 72,54098, 75, 72,54098, 72,54098, 74,5...
$ evol_entrepreneurial_score <fctr> 0,38471, 0,03314, 0,09539, 0,04784, 0,0317, 0,05...
# change the factor variables to numeric
# aaa = as.numeric(commune$urbanRural)
# Merge geo and commune data to include longitude and latitude in the data.
geo_commune <- cbind.data.frame(geo[unique(geo$CODGEO) %in% unique(commune$CODGEO),],
                                   commune[unique(commune$CODGEO) %in% unique(geo$CODGEO),])
# Adjust scales before plotting
#sc <- scale_colour_gradientn(colours = palette(rainbow(3)), limits=c(min(aaa), max(aaa)))
# Map ratio 
 # urbanrural<-
 #  FraMap +
 #  geom_point(aes(x=geo_commune$longitude, y=geo_commune$latitude,
 #                 colour=aaa),
 #             data=geo_commune, alpha=1, size=0.5) +
 #             sc + labs(color='') + ggtitle("Urban and rural cities")
# urbanrural
# boxplot
#  number of units
n_firms <- length(commune$nb_firms_commerce)
# vector 
Label <- c(rep("Firms", n_firms*4))
# vector representing the variable considered
Variable <- c(rep("Service", n_firms), 
             rep("Industry", n_firms),
             rep("Commerce", n_firms),
             rep("Construction", n_firms))
          
             
# merge these data
firms_sector = cbind.data.frame(Label = Label, 
             value = c(commune$nb_firms_commerce, commune$nb_firms_service, commune$nb_firms_ind, commune$nb_firms_construction),
             Variable = Variable)
# plotting phase
ggplot(data = firms_sector, aes(x=Label, y=value)) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Firms") + ylab("# of firms") + ggtitle("Sector comparison") +
  theme(plot.title = element_text(hjust = 0.5)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

# the same but excluding outliers
ggplot(data = firms_sector, aes(x=Label, y=value)) +
  scale_y_continuous(limits = quantile(firms_sector$value, c(0, 0.9))) +
  geom_boxplot(aes(fill = Label)) +
  geom_point(aes(y=value, colour=Label), position = position_dodge(width=0.75)) +
  facet_wrap( ~ Variable, scales="free") +
  xlab("Firms") + ylab("# of firms") + 
  ggtitle("Sector comparison excluding the last decile") +
  theme(plot.title = element_text(hjust = 0.5)) +
  stat_boxplot(geom = "errorbar", width = 0.5)

rm(list = c("geo_commune", "Rank", "Rank10", "aaa"))
object 'aaa' not found

5 Create an unique dataset

Create an unique dataset:

# merging
newDat = merge(firms, population, by="CODGEO")
newDat = merge(newDat, salary, by="CODGEO")
newDat = merge(newDat, geo, by="CODGEO")
newDat = merge(newDat, status_work, by="CODGEO")
newDat = merge(newDat, ineq, by="CODGEO")
newDat = merge(newDat, educ, by="CODGEO")
newDat = merge(newDat, categ_socio, by="CODGEO")
newDat = merge(newDat, commune, by="CODGEO")
# check
names(newDat)
  [1] "CODGEO"                     "town"                      
  [3] "total"                      "micro"                     
  [5] "small"                      "medium"                    
  [7] "large"                      "null"                      
  [9] "total_population"           "male"                      
 [11] "female"                     "child"                     
 [13] "elderly"                    "workforce"                 
 [15] "dependent"                  "sex_ratio"                 
 [17] "dependency_ratio"           "aged_dependency_ratio"     
 [19] "child_dependency_ratio"     "urbanDummy"                
 [21] "sal_general"                "sal_executive"             
 [23] "sal_midManager"             "sal_employee"              
 [25] "sal_worker"                 "sal_Females"               
 [27] "sal_F_executive"            "sal_F_midManager"          
 [29] "sal_F_employee"             "sal_F_worker"              
 [31] "sal_Males"                  "sal_M_executive"           
 [33] "sal_M_midManager"           "sal_M_employee"            
 [35] "sal_M_worker"               "sal_18_25"                 
 [37] "sal_26_50"                  "sal_51plus"                
 [39] "sal_F_18_25"                "sal_F_26_50"               
 [41] "sal_F_51plus"               "sal_M_18_25"               
 [43] "sal_M_26_50"                "sal_M_51plus"              
 [45] "salary_ratio_FvsM"          "salary_ratio_FvsM_Exec"    
 [47] "salary_ratio_FvsM_midManag" "salary_ratio_FvsM_worker"  
 [49] "salary_ratio_FvsM_employee" "salary_ratio_FvsM_18_25"   
 [51] "salary_ratio_FvsM_26_50"    "salary_ratio_FvsM_51plus"  
 [53] "region"                     "region_capital"            
 [55] "department"                 "postal_code"               
 [57] "latitude"                   "longitude"                 
 [59] "wagearner_M"                "wagearner_F"               
 [61] "independent_M"              "independent_F"             
 [63] "transfer_M"                 "transfer_F"                
 [65] "employer_M"                 "employer_F"                
 [67] "full_M"                     "full_F"                    
 [69] "half_M"                     "half_F"                    
 [71] "full"                       "half"                      
 [73] "wagearner"                  "independent"               
 [75] "transfer"                   "employer"                  
 [77] "REG"                        "DEP"                       
 [79] "Superficie"                 "housing14"                 
 [81] "median_living14"            "empl"                      
 [83] "emp_sal"                    "pop15_64"                  
 [85] "unemp15_64"                 "act15_64"                  
 [87] "unemp_rate"                 "female_NoDip"              
 [89] "male_NoDip"                 "female_Sec"                
 [91] "male_Sec"                   "female_Hi"                 
 [93] "male_Hi"                    "female_Univ"               
 [95] "male_Univ"                  "nodip"                     
 [97] "sec"                        "high"                      
 [99] "univ"                       "male_immig"                
[101] "female_immig"               "total_immig"               
[103] "male_working_immig"         "female_working_immig"      
[105] "male_native"                "female_native"             
[107] "total_native"               "orient_econ"               
[109] "nb_household"               "nb_female"                 
[111] "nb_male"                    "nb_minor"                  
[113] "nb_major"                   "nb_students"               
[115] "nb_firms_service"           "nb_firms_commerce"         
[117] "nb_firms_construction"      "nb_firms_ind"              
[119] "nb_created_firms"           "nb_created_ind"            
[121] "nb_created_construction"    "nb_created_commerce"       
[123] "nb_created_services"        "urbanRural"                
[125] "nb_active"                  "nb_active_employees"       
[127] "nb_active_non_employees"    "dymanic_demo_prof"         
[129] "regional_GDP"               "evol_pop_score"            
[131] "evol_entrepreneurial_score"
head(newDat)

Save/load final dataset (just to avoid running all the code)

## UNCOMMENT THESE LINE AND COMMENT THE NEXT ONES TO SAVE THE DATASET
## THEN MOVE THIS FILE IN THE data FOLDER
# #remove unused factor levels on newDat
# for (i in 1:ncol(newDat)){
#   if (is.factor(newDat[,i])){
#     newDat[,i] = droplevels.factor(newDat[,i])
#   }
# }
# # save the NewDat.csv file created (which has to be moved in the data folder)
# write.table(newDat, "newDat.csv", quote = TRUE, row.names=FALSE, col.names = TRUE,
#           fileEncoding = "UTF-8", sep = ",")
 
## LOADING DATA
# load the final dataset
setwd("./data")
newDat <- read.csv("newDat.csv", encoding = "UTF-8", header = TRUE)
# JUST A CHECK, DO NOT NEED TO RUN
# check (changing newDat loaded with newDat2), the only differences are in the order of 10e-15
# identical(newDat, newDat2)
 

Spatial plot of the data to spot possible patterns. A sub-sample will be retained when needed in order to reduce geo-spatial correlations.

# center of France, obtained using: 
# fra_center = as.numeric(geocode("France")) 
fra_center = c(2.213749, 46.227638) 
 
ggmap(get_googlemap(center=fra_center, scale=2, zoom=5), extent="normal") + 
  geom_point(aes(x=longitude, y=latitude), data=newDat, col="orange", alpha=1, size=0.05)  
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=5&size=640x640&scale=2&maptype=terrain&sensor=false

NA

6 Unsupervised Learning

6.1 Multi-Dimensional Scaling (MDS)

6.1.1 Preliminary multivariate data visualisation to better conduct the unsupervised learning

region <- geo$region
# add region variable to the salary dataset
salary_with_dep <- cbind(region, salary)
# extract continuous variables
salary_variables <- salary_with_dep[, 3:34]
#scatterplot to visually see the correlations between variables
pairs(salary_variables[1:8], 
      panel = function (x, y, ...) {
          points(x, y, ...)
          abline(lm(y ~ x), col = "red")
      }, pch = ".", cex = 0.5)

#scatterplot between variables of major interest
pairs(salary_variables[, c("salary_ratio_FvsM", "sal_general", "sal_Females","sal_Males")], 
      panel = function (x, y, ...) {
          points(x, y, ...)
          abline(lm(y ~ x), col = "red")
      }, pch = ".", cex = 0.5)

#scale the data
salary_variables_scaled <- as.data.frame(scale(salary_variables))
#divide sal_general into 4 parts
salary_levels <- with(salary_variables_scaled, 
                  equal.count(sal_general,4))
#plot sal_worker against sal_midManager given 3 partitions of sal_general
plot(xyplot(sal_worker ~ sal_midManager| cut(sal_general, 3), 
            data = salary_variables, 
            layout = c(3, 1), 
            xlab = "Worker Salary", 
            ylab = "Midmanager salary"))

#multidimesnional plot of midManager salary against worker salary, given 4 partitions of F/M salary ratio, and grayscale intensities of general salary
M_F_salary_ratio  <- with(salary_variables, equal.count(salary_ratio_FvsM, 4))
general_salary.ord <- with(salary_variables, rev(order(sal_general)))
salary_variables.ordered <- salary_variables[general_salary.ord,]
sal_general.breaks <- with(salary_variables.ordered, 
                     do.breaks(range(sal_general),50))
salary_variables.ordered$color<-level.colors(salary_variables.ordered$sal_general,
                                   at=sal_general.breaks,
                                   col.regions=grey.colors)
plot(xyplot(sal_worker ~ sal_midManager | M_F_salary_ratio, 
            data = salary_variables.ordered,
            aspect = "iso", 
            groups = color, 
            cex = 1, col = "black",
       panel = function(x, y, groups, ..., subscripts) {
           fill <- groups[subscripts]
           panel.grid(h = -1, v = -1)
           panel.xyplot(x, y, pch = 21, 
                        fill = fill, ...)
       },
       legend =
       list(right =
            list(fun = draw.colorkey,
                 args=list(key=list(col=gray.colors,
                                    at = sal_general.breaks),
                           draw = FALSE))),
            xlab = "Worker Salary", 
            ylab = "Midmanager salary"))

6.1.1.1 Preliminary multivariate firms data visualisation to better conduct the unsupervised learning

# add region variable to the salary dataset
firms_with_dep <- cbind(region, firms)
# extract continuous variables
firms_variables <- firms_with_dep[, 4:8]
#scatterplot to visually see the correlations between variables
pairs(firms_variables[, 2:5], 
      panel = function (x, y, ...) {
          points(x, y, ...)
          abline(lm(y ~ x), col = "red")
      }, pch = ".", cex = 0.5)

#scale the data
firms_variables_scaled <- as.data.frame(scale(firms_variables))
#divide firms total into 4 parts
firms_levels <- with(firms_variables_scaled, 
                  equal.count(total,4))
#plot sal_worker against sal_midManager given 3 partitions of sal_general
plot(xyplot(medium ~ large| cut(total, 3), 
            data = firms_variables, 
            layout = c(3, 1), 
            xlab = "medium firms", 
            ylab = "large firms"))

6.1.2 Multi-dimensional scaling (MDS)

6.1.2.1 Multi-dimensional scaling (MDS) on salary data

#Multi-dimensional scaling (MDS) on salary data
# produces a list with covariance matrix
# for each region as component:
salary_var <- tapply(1:nrow(salary_with_dep), salary_with_dep$region, 
                     function(i) var(salary_with_dep[i,3:7]))
#remove regions with null values
salary_var <- salary_var[-c(11, 12, 15, 19, 20, 28)]
#create dataframe with the counts of towns per region
regions_count <- data.frame(table(salary_with_dep$region))
regions_count_sub<-regions_count[!(regions_count$Freq==0 | regions_count$Freq==1),]
#check total sum
#row.names(regions_count_sub) <- 1:nrow(regions_count_sub)
#sum(regions_count_sub$Freq)
# initializes common covariance matrix var:
S <- regions_count_sub$Freq[1] * as.matrix(salary_var[[1]])
# creates common covariance matrix S
s <- 0
for (v in c(1:22)) S <- S + regions_count_sub$Freq[v] * as.matrix(salary_var[[v]]) 
S <- S / 5003
# finds center of each variable (sal_general, etc)
# for each region
salary_cen <- tapply(1:nrow(salary_with_dep), salary_with_dep$region, 
    function(i) apply(salary_with_dep[i,c(3:7)], 2, mean))
salary_cen <- salary_cen[-c(11, 12, 15, 19, 20, 28)]
# create a matrix out of each components, each
# mean measurement for all variables by region
salary_cen <- matrix(unlist(salary_cen), 
    nrow = length(salary_cen), byrow = TRUE)
#salary_cen
# compute the mahalanobis distances:
salary_mah <- apply(salary_cen, 1, 
    function(cen) mahalanobis(salary_cen, cen, S))
# run it on two dimensions
salary_mds <- cmdscale(salary_mah)
# draw a scatterplot of two-dimensional solution
# from classical MDS applied to Mahalanobis
# distances:
lim <- range(salary_mds) * 1.2
plot(salary_mds, xlab = "Coordinate 1", 
     ylab = "Coordinate 2",
     xlim = lim, ylim = lim, type = "n")
text(salary_mds, labels = levels(salary_with_dep$region), 
     cex = 0.5)

One can see that Languedoc Roussilon, the worst region in terms of GDP per capita, is further away from the center.

6.1.2.2 Multi-dimensional scaling (MDS) on firms data

firms_with_dep <- firms_with_dep[, -9]
firms_with_dep_normalized <- firms_with_dep[, 4:8]/population$total_population
firms_with_dep_normalized <- cbind(firms_with_dep$region, firms_with_dep_normalized)
names(firms_with_dep_normalized)[1] <- "region"
# produces a list with covariance matrix
# for each region as component:
firms_var <- tapply(1:nrow(firms_with_dep_normalized), firms_with_dep_normalized$region, 
                     function(i) var(firms_with_dep_normalized[i,2:6]))
#remove regions with null values
firms_var <- firms_var[-c(11, 12, 15, 19, 20, 28)]
#create dataframe with the counts of towns per region
regions_count <- data.frame(table(firms_with_dep_normalized$region))
regions_count_sub<-regions_count[!(regions_count$Freq==0 | regions_count$Freq==1),]
#check total sum
# row.names(regions_count_sub) <- 1:nrow(regions_count_sub)
# sum(regions_count_sub$Freq)
# initializes common covariance matrix var:
S <- regions_count_sub$Freq[1] * as.matrix(firms_var[[1]])
# creates common covariance matrix S
s <- 0
for (v in c(1:22)) S <- S + (regions_count_sub$Freq[v] - 1) * as.matrix(firms_var[[v]]) 
S <- S / 5003
# finds center of each variable (micro, etc)
# for each region
firms_cen <- tapply(1:nrow(firms_with_dep_normalized), firms_with_dep_normalized$region, 
    function(i) apply(firms_with_dep_normalized[i,-1], 2, mean))
firms_cen <- firms_cen[-c(11, 12, 15, 19, 20, 28)]
# create a matrix out of each components, each
# mean measurement for all variables by region
firms_cen <- matrix(unlist(firms_cen), 
    nrow = length(firms_cen), byrow = TRUE)
# compute the mahalanobis distances:
firms_mah <- apply(firms_cen, 1, 
    function(cen) mahalanobis(firms_cen, cen, S))
# run it on two dimensions
firms_mds <- cmdscale(firms_mah)
# draw a scatterplot of two-dimensional solution
# from classical MDS applied to Mahalanobis
# distances:
lim <- range(firms_mds) * 1.2
plot(firms_mds, xlab = "Coordinate 1", 
     ylab = "Coordinate 2",
     xlim = lim, ylim = lim, type = "n")
text(firms_mds, labels = levels(firms_with_dep_normalized$region), 
     cex = 0.5)

Since tha data is normalized according to population size, Haute Normandie and Midi Pyrenees are further away.

6.1.2.3 Multi-Dimensional Scaling for merged dataset

nums <- unlist(lapply(commune, is.numeric)) 
commune_selected <- commune[, nums]
commune_selected <- commune_selected[, 2:20]
commune_selected_normalized <- commune_selected[]/population$total_population
unemp_percent <- as.data.frame((ineq$unemp15_64/ineq$pop15_64)*100)
names(unemp_percent)[1] <- "unemp_percent"
firms_variables_normalized <- firms_variables[]/population$total_population
selected_variables <- cbind(firms_variables_normalized, salary_variables, commune_selected_normalized, unemp_percent)
selected_variables_with_dep <- cbind(geo$region, selected_variables)
names(selected_variables_with_dep)[1] <- "region"
pca_selected_variables <- prcomp(selected_variables)
selected_pca_scores<- as.data.frame(pca_selected_variables$x[,1:3])
selected_pca_with_dep <- cbind(geo$region, selected_pca_scores)
names(selected_pca_with_dep)[1] <- "region"
# produces a list with covariance matrix
# for each region as component:
selected_PCA_var <- tapply(1:nrow(selected_pca_with_dep), selected_pca_with_dep$region,
                     function(i) var(selected_pca_with_dep[i,2:4]))
#remove regions with null values
selected_PCA_var <- selected_PCA_var[-c(11, 12, 15, 19, 20, 28)]
#create dataframe with the counts of towns per region
regions_count <- data.frame(table(selected_pca_with_dep$region))
regions_count_sub<-regions_count[!(regions_count$Freq==0 | regions_count$Freq==1),]
#check total sum
# row.names(regions_count_sub) <- 1:nrow(regions_count_sub)
# sum(regions_count_sub$Freq)
# initializes common covariance matrix var:
S <- regions_count_sub$Freq[1] * as.matrix(selected_PCA_var[[1]])
# creates common covariance matrix S
s <- 0
for (v in c(1:22)) S <- S + (regions_count_sub$Freq[v] - 1) * as.matrix(selected_PCA_var[[v]])
S <- S / 5003
# finds center of each variable (micro, etc)
# for each region
selected_cen <- tapply(1:nrow(selected_pca_with_dep), selected_pca_with_dep$region,
    function(i) apply(selected_pca_with_dep[i,-1], 2, mean))
selected_cen <- selected_cen[-c(11, 12, 15, 19, 20, 28)]
# create a matrix out of each components, each
# mean measurement for all variables by region
selected_cen <- matrix(unlist(selected_cen),
    nrow = length(selected_cen), byrow = TRUE)
# compute the mahalanobis distances:
selected_mah <- apply(selected_cen, 1,
    function(cen) mahalanobis(selected_cen, cen, S))
# run it on two dimensions
selected_mds <- cmdscale(selected_mah)
# draw a scatterplot of two-dimensional solution
# from classical MDS applied to Mahalanobis
# distances:
lim <- range(selected_mds) * 1.2
plot(selected_mds, xlab = "Coordinate 1",
     ylab = "Coordinate 2",
     xlim = lim, ylim = lim, type = "n")
text(selected_mds, labels = levels(selected_pca_with_dep$region),
     cex = 0.5)

On the merged dataset one can Rhone-Alpes and Cote d’Azur, the two biggest economies after Ile de France.

6.2 PCA

6.2.0.1 PCA on salary data and delta M-F

pca_salary <- prcomp(salary_variables)
autoplot(pca_salary, data = salary_with_dep, colour = 'region',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

plot(pca_salary, type = "l")

#create dataset with M-F differences
salary_var_delta <- data.frame("M-F" = salary_variables$sal_Males -                                                           salary_variables$sal_Females,
                     "Executive M-F" = salary_variables$sal_M_executive -
                                         salary_variables$sal_F_executive,
                    "midManager M-F" = salary_variables$sal_M_midManager -
                                         salary_variables$sal_F_midManager,
                      "employee M-F" = salary_variables$sal_M_employee -
                                         salary_variables$sal_F_employee,
                        "worker M-F" = salary_variables$sal_M_worker -
                                         salary_variables$sal_F_worker,
                         "18-25 M-F" = salary_variables$sal_M_18_25 -
                                         salary_variables$sal_F_18_25,
                         "26-50 M-F" = salary_variables$sal_M_26_50 -
                                         salary_variables$sal_F_26_50,
                       "51 plus M-F" = salary_variables$sal_M_51plus -
                                         salary_variables$sal_F_51plus)
pca_salary_delta <- prcomp(salary_var_delta)
autoplot(pca_salary_delta, data = salary_with_dep, colour = 'region',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

plot(pca_salary_delta, type = "l")

There are three main directions of eigenvectors contributing to principal components’ scores: variables describing salaries of ordinary staff across different age categories, variables describing mid-manager and manager salaries, and salaries of executives.

PCA on delta M-F revealed three main directions of eigenvectors contributing to scores: male-female salary differences of mid-managers, executives, and 51 and older aged people.

6.2.0.2 PCA on firms data

firms_variables_normalized <- firms_variables[, -1]/population$total_population
pca_firms <- prcomp(firms_variables_normalized)
autoplot(pca_firms, data = firms_with_dep, colour = 'region',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

plot(pca_firms, type = "l")

Since there is a strong multicollinearity among the variables of the firms data, the conducted PCA results in the variable with considerably higher values, i.e. very small firms, to form a main eigenvector, leaving firms of other sizes to form a separate group of eigenvectors.

6.2.0.3 PCA on commune dataset

commune_trimmed <- commune[,c("nb_firms_service","nb_firms_commerce","nb_firms_construction")]
commune_trimmed$active_empl_prop <- commune$nb_active_employees/population$total_population
commune_trimmed$nb_firms_service <- commune$nb_firms_service/population$total_population
commune_trimmed$nb_firms_commerce <- commune$nb_firms_commerce/population$total_population
commune_trimmed$nb_firms_construction <- commune$nb_firms_construction/population$total_population
#commune_trimmed <- as.data.frame(scale(commune_trimmed))
names(which(sapply(commune_trimmed, anyNA)))
character(0)
str(commune_trimmed)
'data.frame':   5025 obs. of  4 variables:
 $ nb_firms_service     : num  0.02541 0.01288 0.0192 0.01021 0.00541 ...
 $ nb_firms_commerce    : num  0.02236 0.01053 0.02926 0.00721 0.007 ...
 $ nb_firms_construction: num  0.00431 0.00663 0.00671 0.00601 0.00446 ...
 $ active_empl_prop     : num  0.312 0.311 0.432 0.245 0.288 ...
commune_pca <- prcomp(commune_trimmed)
commune_pca
Standard deviations (1, .., p=4):
[1] 0.065193566 0.016941520 0.005599656 0.002815369

Rotation (n x k) = (4 x 4):
                               PC1         PC2         PC3         PC4
nb_firms_service      -0.025510236 -0.57990964 -0.81271473  0.05048565
nb_firms_commerce     -0.003487765 -0.81175562  0.57203205 -0.11755840
nb_firms_construction  0.011709990 -0.06688609  0.10896441  0.99172369
active_empl_prop      -0.999599890  0.01684835  0.02002141  0.01073949
commune_pca_scores<- as.data.frame(commune_pca$x[,1:3])
autoplot(commune_pca, data = geo, colour = 'region',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

plot(commune_pca, type = "l")

# plot3d(commune_pca[,1:2], col=cluster$cluster+1)
scatter3d(x = commune_pca_scores$PC1, y = commune_pca_scores$PC2, z = commune_pca_scores$PC3, groups = as.factor(kmeans(commune_pca_scores, 
                  centers = 6)$cluster),
          grid = FALSE, surface = FALSE)
Loading required namespace: mgcv

PCA conducted on the commune dataset revealed two main directions of eigenvectors: proportion of active employers in each town, and industry related variables.

6.2.0.4 PCA on selected variables

nums <- unlist(lapply(commune, is.numeric)) 
commune_selected <- commune[, nums]
commune_selected <- commune_selected[, 2:20]
commune_selected_normalized <- commune_selected[]/population$total_population
unemp_percent <- as.data.frame((ineq$unemp15_64/ineq$pop15_64)*100)
names(unemp_percent)[1] <- "unemp_percent"
selected_variables <- cbind(firms_variables_normalized, salary_variables, commune_selected_normalized, unemp_percent)
pca_selected_variables <- prcomp(selected_variables)
autoplot(pca_selected_variables, data = geo, colour = 'region',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

plot(pca_selected_variables, type = "l")

regional GDP is a striking important factor, comprising a distinctly oriented eigenvector. Another distinct eigenvector, though less substantial, is the unemployment rate.

6.3 Cluster Analysis

6.3.0.1 Cluster Analysis on salary

#create dataframe with first two principal components scores as variables
salary_pca_scores<- as.data.frame(pca_salary$x[,1:2])
salary_pca_scores <- as.data.frame(scale(salary_pca_scores))
sapply(salary_pca_scores, var)
PC1 PC2 
  1   1 
 
# K-means clustering
n <- nrow(salary_pca_scores)
wss <- rep(0, 22)
wss[1] <- (n - 1) * sum(sapply(salary_pca_scores, var))
for (i in 2:22)
    wss[i] <- sum(kmeans(salary_pca_scores,
                         centers = i)$withinss)
did not converge in 10 iterations
#we can see from the plot that optimal number of cluster is 5
plot(1:22, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

#plotting in PC space shows us that there are indeed distinguishable 5 groups 
plot(salary_pca_scores, 
     pch = kmeans(salary_pca_scores, 
                  centers = 5)$cluster)

#now we have to see whether these groups correspond to geographical regions...

6.3.0.2 clustering salary delta M-F

salary_delta_pca_scores <- as.data.frame(pca_salary_delta$x[,1:3])
n <- nrow(salary_delta_pca_scores)
wss <- rep(0, 15)
wss[1] <- (n - 1) * sum(sapply(salary_delta_pca_scores, var))
for (i in 2:15)
    wss[i] <- sum(kmeans(salary_delta_pca_scores,
                         centers = i)$withinss)
#we can see from the plot that optimal number of cluster is 5
plot(1:15, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

#plots
scatter3d(x = salary_delta_pca_scores$PC1, y = salary_delta_pca_scores$PC2, z = salary_delta_pca_scores$PC3, groups = as.factor(kmeans(salary_delta_pca_scores, 
                  centers = 6)$cluster),
          grid = FALSE, surface = FALSE)
clusters <- kmeans(salary_delta_pca_scores, 
                  centers = 6)
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(clusters$cluster)),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on male-female salary difference")
salary_geo

salary_delta_cluster <- cbind(salary_var_delta, clusters$cluster)
names(salary_delta_cluster)[9] <- "cluster"
sal_delta_by_cluster <- aggregate(salary_delta_cluster$M.F, by=list(salary_delta_cluster$cluster), FUN=mean)[2]
names(sal_delta_by_cluster)[1] <- "Salary_difference"
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= salary_variables$salary_ratio_FvsM),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Female-Male salary ratio")
salary_geo

6.3.0.3 Finding towns with large difference in male-female salaries

#detecting extreme cities in PCA
quantiles<-tapply(salary_delta_pca_scores$PC1,firms$town,quantile)
minq <- sapply(firms$town, function(x) quantiles[[x]]["25%"])
maxq <- sapply(firms$town, function(x) quantiles[[x]]["75%"])
towns <- as.data.frame(firms$town)
k <- salary_delta_pca_scores[which(salary_delta_pca_scores$PC1<minq | salary_delta_pca_scores$PC1>maxq), ]
#find towns with largedifference 
extreme_town_list <- towns[which(salary_delta_pca_scores$PC1<minq | salary_delta_pca_scores$PC1>maxq), ]
extreme_towns <- as.data.frame(extreme_town_list)
#detecting extreme cities in salary delta
quantiles<-tapply(salary_var_delta$M.F,firms$town,quantile)
minq <- sapply(firms$town, function(x) quantiles[[x]]["25%"])
maxq <- sapply(firms$town, function(x) quantiles[[x]]["75%"])
town <- towns[which(salary_var_delta$M.F<minq | 
                                    salary_var_delta$M.F>maxq), ]
extreme_towns_m_f <- as.data.frame(town)
geo <- as.data.frame(cbind(firms$town, geo))
colnames(geo)[colnames(geo)=="firms$town"] <- "town"
geo_extreme <- subset(geo, town %in% extreme_towns_m_f$town)
geo_extreme <- geo_extreme[, c("town", "latitude", "longitude")]
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo_extreme$longitude, y=geo_extreme$latitude),
             data=geo_extreme, col="red", alpha=0.5, size=0.5) + labs(color='') + ggtitle("Towns with large difference in male-female salaries")
salary_geo

#As you can see, the towns with large salry differences are concetrated around large cities such as Paris and Lyon.

6.3.0.4 clustering towns based on unemployed %

unemp_percent <- as.data.frame((ineq$unemp15_64/ineq$pop15_64)*100)
names(unemp_percent)[1] <- "unemp_percent"
n <- nrow(unemp_percent)
wss <- rep(0, 15)
wss[1] <- (n - 1) * sum(sapply(unemp_percent, var))
for (i in 2:15)
    wss[i] <- sum(kmeans(unemp_percent,
                         centers = i)$withinss)
#we can see from the plot that optimal number of cluster is 6
plot(1:15, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

clusters <- kmeans(unemp_percent, 6)
unemp_percent <- cbind(unemp_percent, clusters$cluster)
names(unemp_percent)[2] <- "cluster"
unemp_by_cluster <- aggregate(unemp_percent$unemp_percent, by=list(unemp_percent$cluster), FUN=mean)[2]
names(unemp_by_cluster)[1] <- "unemp_rate(%)" 
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(clusters$cluster), size = unemp_percent$unemp_percent), data=geo, alpha=0.5, size=0.5) + scale_fill_manual(name = "Unemployment rate", values = unemp_by_cluster$`unemp_rate(%)`) + labs(color='') + ggtitle("Clusters of towns based on unemployment rate")
salary_geo

6.3.0.5 Clustering based on Locational Gini Coefficient

industry <- commune[, c("nb_firms_service", "nb_firms_commerce", "nb_firms_construction")]
lq_service <- (industry$nb_firms_service/(industry$nb_firms_service+industry$nb_firms_commerce+industry$nb_firms_construction))/(sum(industry$nb_firms_service)/sum(industry))
lq_service <- as.data.frame((lq_service))
lq_commerce <- (industry$nb_firms_commerce/(industry$nb_firms_service+industry$nb_firms_commerce+industry$nb_firms_construction))/(sum(industry$nb_firms_commerce)/sum(industry))
lq_commerce <- as.data.frame((lq_commerce))
lq_construction <- (industry$nb_firms_construction/(industry$nb_firms_service+industry$nb_firms_commerce+industry$nb_firms_construction))/(sum(industry$nb_firms_construction)/sum(industry))
lq_construction <- as.data.frame((lq_construction))
industry_lq <- cbind(industry, lq_service, lq_commerce, lq_construction)
#create dataframe with the counts of towns per region
regions_count <- data.frame(table(salary_with_dep$region))
regions_count_sub<-regions_count[!(regions_count$Freq==0 | regions_count$Freq==1),]
#check total sum
row.names(regions_count_sub) <- 1:nrow(regions_count_sub)
sum(regions_count_sub$Freq)
[1] 5025
industry_lq <- cbind(firms$town, industry_lq)
names(industry_lq)[1] <- "town" 
names(industry_lq)[5] <- "lq_service"
names(industry_lq)[6] <- "lq_commerce"
names(industry_lq)[7] <- "lq_construction"
dm <- dist(industry_lq[, 5:7])
#round to two decimals
dm <- round(dm, 2)
 
# K-means clustering
n <- nrow(industry_lq)
wss <- rep(0, 22)
wss[1] <- (n - 1) * sum(sapply(industry_lq[, 5:7], var))
for (i in 2:22)
    wss[i] <- sum(kmeans(industry_lq[, 5:7],
                         centers = i)$withinss)
did not converge in 10 iterations
#we can see from the plot that optimal number of cluster is 5
plot(1:22, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

clusters <- kmeans(industry_lq[, 5:7], 5)
#Mapping the towns based on the locational Gini coefficient
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(clusters$cluster)),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on locational gini coefficient")
salary_geo

6.3.0.6 apping the towns based on the intensity of locational Gini coefficient

#Mapping the towns based on the intensity of locational Gini coefficient of the service industry
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= industry_lq$lq_service),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Towns based on service locational gini coefficient")
salary_geo

#Mapping the towns based on the intensity of locational Gini coefficient of the commerce industry
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= industry_lq$lq_commerce),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Towns based on commerce locational gini coefficient")
salary_geo

#Mapping the towns based on the intensity of locational Gini coefficient of the commerce industry
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= industry_lq$lq_construction),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Towns based on construction locational gini coefficient")
salary_geo

6.3.0.7 Salary clusters

#create distance matrix for hclust
dm <- dist(salary_variables)
#round to two decimals
dm <- round(dm, 2)
 
# K-means clustering
n <- nrow(salary_variables)
wss <- rep(0, 22)
wss[1] <- (n - 1) * sum(sapply(salary_variables, var))
for (i in 2:22)
    wss[i] <- sum(kmeans(salary_variables,
                         centers = i)$withinss)
Quick-TRANSfer stage steps exceeded maximum (= 251250)did not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterations
#we can see from the plot that optimal number of cluster is 5
plot(1:22, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

#now we have to see whether these groups correspond to geographical regions...

6.3.0.8 Mapping the salary clusters

 
#now we have to see whether these groups correspond to geographical regions...
clusters <- kmeans(salary_variables, 6)
salary_with_dep$cluster <- clusters$cluster
geo$cluster <- clusters$cluster
 
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(geo$cluster)),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on salary")
salary_geo

salary_cl <- cbind(salary_variables, clusters$cluster)
names(salary_cl)[33] <- "cluster"
salary_cluster <- aggregate(salary_cl$sal_general, by=list(salary_cl$cluster), FUN=mean)[2]
names(salary_cluster)[1] <- "salary" 
 

6.3.0.9 Clustering on selected variables

selected_pca_scores<- as.data.frame(pca_selected_variables$x[,1:3])
# K-means clustering
n <- nrow(selected_variables)
wss <- rep(0, 22)
wss[1] <- (n - 1) * sum(sapply(selected_variables, var))
for (i in 2:22)
    wss[i] <- sum(kmeans(selected_variables,
                         centers = i)$withinss)
did not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterationsdid not converge in 10 iterations
#we can see from the plot that optimal number of cluster is 7
plot(1:22, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

clusters <- kmeans(selected_variables, centers = 7)
scatter3d(x = selected_pca_scores$PC1, y = selected_pca_scores$PC2, z = selected_pca_scores$PC3, groups = as.factor(clusters$cluster),
          grid = FALSE, surface = FALSE)
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
 
# Plot "Distribution of total population for each town" 
salary_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(clusters$cluster)),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on selected variables")
salary_geo

6.3.0.10 Clustering in commune

#K means
n <- nrow(commune_trimmed)
wss <- rep(0, 15)
wss[1] <- (n - 1) * sum(sapply(commune_trimmed, var))
for (i in 2:15)
    wss[i] <- sum(kmeans(commune_trimmed,
                         centers = i)$withinss)
#we can see from the plot that optimal number of cluster is 11
plot(1:15, wss, type = "b",
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

6.3.0.11 Mapping the new clusters

n <- nrow(commune_trimmed)
wss <- rep(0, 22)
wss[1] <- (n - 1) * sum(sapply(commune_trimmed$active_empl_prop, var))
for (i in 2:22)
    wss[i] <- sum(kmeans(commune_trimmed$active_empl_prop,
                         centers = i)$withinss)
Quick-TRANSfer stage steps exceeded maximum (= 251250)Quick-TRANSfer stage steps exceeded maximum (= 251250)
#we can see from the plot that optimal number of cluster is 7
plot(1:22, wss, type = "b", 
     xlab = "Number of groups",
     ylab = "Within groups sum of squares")

#now we have to see whether these groups correspond to geographical regions...
FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
# Plot "Distribution of total population for each town"
newDat_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= factor(kmeans(commune_trimmed$active_empl_prop, 
                  centers = 6)$cluster)),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on active employees proportion")
newDat_geo

FraMap = ggmap(get_googlemap(center=fra_center, scale=2, zoom=6), extent="normal")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=46.227638,2.213749&zoom=6&size=640x640&scale=2&maptype=terrain&sensor=false
# Plot "Distribution of total population for each town"
newDat_geo <-
  FraMap +
  geom_point(aes(x=geo$longitude, y=geo$latitude, colour= commune_trimmed$active_empl_prop),
             data=geo, alpha=0.5, size=0.5) + labs(color='') + ggtitle("Clusters of towns based on active employees")
newDat_geo

7 Supervised Learning

7.1 ANOVA for salary

First the distribution of salary for both genders is considered, in original and log-scale

# original data
# male
ggplot(data=data.frame(newDat$sal_Males), aes(newDat$sal_Males)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 40) +
  geom_density(col="black") +
  labs(x="Male salaries", y="Density") +
  ggtitle("Distribution of salary for males") +
  theme(plot.title = element_text(hjust = 0.5))

# females
ggplot(data=data.frame(newDat$sal_Females), aes(newDat$sal_Females)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 40) +
  geom_density(col="black") +
  labs(x="Female salaries", y="Density") +
  ggtitle("Distribution of salary for females") +
  theme(plot.title = element_text(hjust = 0.5))

# log data
# male
ggplot(data=data.frame(log10(newDat$sal_Males)), aes(log10(newDat$sal_Males))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 40) +
  geom_density(col="black") +
  labs(x="Log10 male salaries", y="Density") +
  ggtitle("Distribution of salary for males in log scale") +
  theme(plot.title = element_text(hjust = 0.5))

# females
ggplot(data=data.frame(log10(newDat$sal_Females)), aes(log10(newDat$sal_Females))) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 40) +
  geom_density(col="black") +
  labs(x="Log10 female salaries", y="Density") +
  ggtitle("Distribution of salary for females in log scale") +
  theme(plot.title = element_text(hjust = 0.5))

# hist(c(log10(newDat$sal_Males), log10(newDat$sal_Females)), 30)

It is clear that the normality assumption, cannot hold.

At the same time, it is shown how to check if the difference in the mean in salary between males and females using a t-test

t.test(log10(newDat$sal_Males), log10(newDat$sal_Females), alternative = c("two.sided"))

    Welch Two Sample t-test

data:  log10(newDat$sal_Males) and log10(newDat$sal_Females)
t = 64.095, df = 9332.7, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.0857778 0.0911900
sample estimates:
mean of x mean of y 
 1.165991  1.077507 

This solution can also be otbained fitting a linear model with a dummy variable catching the sex effect, which corresponds to a one-way ANOVA model

# create the reponse attaching the salary for each sex
sal_y = c(log10(newDat$sal_Males), log10(newDat$sal_Females))
# create the dummy variable controlling for the corresponding sex
dummy = c(rep(1, length(newDat$sal_Males)), rep(0, length(newDat$sal_Females)))
# the t value is exactly the same as the one obtained in the previous t-test
sal_ANOVA = lm(sal_y ~ dummy)
summary(sal_ANOVA)

Call:
lm(formula = sal_y ~ dummy)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.14896 -0.04542 -0.01677  0.02970  0.55334 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.0775073  0.0009762 1103.81   <2e-16 ***
dummy       0.0884839  0.0013805   64.09   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.06918 on 10042 degrees of freedom
Multiple R-squared:  0.2903,    Adjusted R-squared:  0.2903 
F-statistic:  4108 on 1 and 10042 DF,  p-value: < 2.2e-16
# residuals plot
plot(sal_ANOVA$residuals)
abline (0, 0, col = "red")

To improve such analysis some more aspects are now considered. In particualar, the model considered in an ANOVA, where the predictors take in account each gender for different age and job levels, plus their interaction terms.

First of all the dataset is created and a subsample of it is considered, in order to avoid strong correlation among units

set.seed(300)
# create response variable
sal_y = c(newDat$sal_M_18_25, newDat$sal_M_26_50, newDat$sal_M_51plus,
          newDat$sal_M_executive, newDat$sal_M_midManager, newDat$sal_M_employee, newDat$sal_M_worker,
          newDat$sal_F_18_25, newDat$sal_F_26_50, newDat$sal_F_51plus,
          newDat$sal_F_executive, newDat$sal_F_midManager, newDat$sal_F_employee, newDat$sal_F_worker)
n_sal_y = length(sal_y)             # length response variable
n_cat = length(newDat$sal_M_18_25)  # length of each category (i.e., original vectors)
# create sex dummy variable, 1 for males and 0 for females
sal_sex = rep(0, n_sal_y)   # full regressors
sal_sex[1:n_sal_y/2] = 1    # assign males
# create age dummy variables, 18-25 years old is the base case
sal_age = cbind(rep(0, n_sal_y), rep(0, n_sal_y)) # full regressors
# 26-50 y.o.
sal_age[(n_cat+1):(n_cat*2), 1] = 1     # males
sal_age[(n_cat*8+1):(n_cat*9), 1] = 1   # females
# 51+ y.o.
sal_age[(n_cat*2+1):(n_cat*3), 2] = 1   # males
sal_age[(n_cat*9+1):(n_cat*10), 2] = 1  # females
# create job type dummy variables, worker is the base case
sal_job = cbind(rep(0, n_sal_y), rep(0, n_sal_y), rep(0, n_sal_y)) # full regressors
# executives
sal_job[(n_cat*3+1):(n_cat*4), 1] = 1     # males
sal_job[(n_cat*10+1):(n_cat*11), 1] = 1   # females
# middle managers
sal_job[(n_cat*4+1):(n_cat*5), 2] = 1     # males
sal_job[(n_cat*11+1):(n_cat*12), 2] = 1   # females
# employee
sal_job[(n_cat*5+1):(n_cat*6), 3] =   1   # males
sal_job[(n_cat*12+1):(n_cat*13), 3] = 1   # females
# final data set
data_ANOVA = cbind.data.frame(response = sal_y, sex = sal_sex, age = sal_age, job = sal_job)
# names(data_ANOVA)
# show regressors' shape
imagemat(data_ANOVA[,-1], xaxt = "n", main = "Factors for ANOVA")
axis(1, at=1:6, labels=c("Sex", "Age 26-50", "Age 51+", "Execut.", "Mid.Man.", "Empl."))
box()

# sub sample to avoid correlation, (unbalanced dataset)
# set.seed(20)
subs = sample(1:n_sal_y, size = round(n_sal_y*0.1))
data_ANOVA = data_ANOVA[subs,]
# show randomized data
imagemat(data_ANOVA[,-1], xaxt = "n", main = "Factors for ANOVA after sub-sampling")
axis(1, at=1:6, labels=c("Sex", "Age 26-50", "Age 51+", "Execut.", "Mid.Man.", "Empl."))
box()

Run the ANOVA model, tranforming the response variable using Box-Cox transformation

# plot the transformed response variable (suggested by Box-Cox transformation)
sal_y = data_ANOVA[,1]^-1
ggplot(data=data.frame(data_ANOVA[,1]), aes(sal_y)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 60) +
  geom_density(col="black") +
  labs(x="salary^-1", y="Density") +
  ggtitle("Transformation of salary found using Box-Cox transformation") +
  theme(plot.title = element_text(hjust = 0.5))

# take the identified subsample for dummy variables
sex = sal_sex[subs]
age = sal_age[subs,]
job = sal_job[subs,]
# ANOVA model
sal_ANOVA = lm(sal_y ~ sex + age + job + sex:age + sex:job)
summary(sal_ANOVA)

Call:
lm(formula = sal_y ~ sex + age + job + sex:age + sex:job)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.084391 -0.004587  0.000657  0.005446  0.050454 

Coefficients:
              Estimate Std. Error  t value Pr(>|t|)    
(Intercept)  0.1057963  0.0002737  386.559  < 2e-16 ***
sex         -0.0106870  0.0003870  -27.618  < 2e-16 ***
age1        -0.0212090  0.0004851  -43.723  < 2e-16 ***
age2        -0.0279090  0.0004717  -59.163  < 2e-16 ***
job1        -0.0555240  0.0004630 -119.912  < 2e-16 ***
job2        -0.0298464  0.0004690  -63.633  < 2e-16 ***
job3        -0.0081649  0.0004693  -17.397  < 2e-16 ***
sex:age1    -0.0023524  0.0006850   -3.434 0.000598 ***
sex:age2    -0.0083323  0.0006713  -12.412  < 2e-16 ***
sex:job1     0.0010803  0.0006723    1.607 0.108151    
sex:job2    -0.0002828  0.0006755   -0.419 0.675486    
sex:job3     0.0027415  0.0006671    4.109 4.01e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.008719 on 7019 degrees of freedom
Multiple R-squared:  0.8352,    Adjusted R-squared:  0.8349 
F-statistic:  3233 on 11 and 7019 DF,  p-value: < 2.2e-16
anova(sal_ANOVA)
Analysis of Variance Table

Response: sal_y
            Df  Sum Sq Mean Sq    F value    Pr(>F)    
sex          1 0.20446 0.20446  2689.2922 < 2.2e-16 ***
age          2 0.16187 0.08093  1064.5298 < 2.2e-16 ***
job          3 2.31845 0.77282 10164.8476 < 2.2e-16 ***
sex:age      2 0.01775 0.00887   116.7281 < 2.2e-16 ***
sex:job      3 0.00160 0.00053     7.0028 0.0001064 ***
Residuals 7019 0.53364 0.00008                         
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# aov(sal_y ~ sex + age + job + sex:age + sex:job)
plot(sal_ANOVA, 1)

# box-cox transformation suggested to use y^-1
boxcox(sal_ANOVA)
title("Box-Cox transformation of the response")

# comparison a full model with a reduced one excluding one interaction term
sal_ANOVA2 = lm(sal_y ~ sex + age + job + sex:age)
anova(sal_ANOVA, sal_ANOVA2)
Analysis of Variance Table

Model 1: sal_y ~ sex + age + job + sex:age + sex:job
Model 2: sal_y ~ sex + age + job + sex:age
  Res.Df     RSS Df  Sum of Sq      F    Pr(>F)    
1   7019 0.53364                                   
2   7022 0.53524 -3 -0.0015972 7.0028 0.0001064 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

To check if the model can be sparsified, considering groups of variables as a whole, the group-Lasso is applied. It shows that the interaction term sex:job is not so important.

# define the considered model, which includes interaction terms
form <- model.matrix(sal_y ~ (sex + age + job)^2)
# remove interaction age:job
# colnames(form)
form = form[,2:12]
# define groups' labels
grp = c(1,2,2,3,3,3,4,4,5,5,5)
#Group-Lasso with 10-folds Cross Validation and LAD cost function
fit.cv=cv.gglasso(x=form, y=sal_y, group=grp, nfolds=10, pred.loss = "L2")
# plot CV for Lambda
plot(fit.cv) 
mtext(expression("10-folds CV for group-Lasso in ANOVA"), outer=TRUE,  cex=1, line=-2.2)

# extract coefficients
# coef.mat = fit.cv$gglasso.fit$beta
# plot separately each coefficient
cols = brewer.pal(5,name="Set1")
plot(fit.cv$gglasso.fit, col = cols, lwd = 3, main = "Coefficients vs.Lambda values")
legend('bottomright', legend = paste("Group", 1:5), col=cols[1:5], cex = 0.65, pch = 1, lwd=4, bty ="n")

# plot coefficients' norms (not working), doing it manually
# https://www.rdocumentation.org/packages/gglasso/versions/1.3/topics/plot.gglasso
# https://code.google.com/archive/p/gglasso/issues
  # plot(fit.cv$gglasso.fit, group=TRUE, col =  cols, lwd = 2)
  # legend('bottomright', legend = paste("Group", 1:5), col=cols[1:5], cex = 0.65, pch = 1, lwd=4, bty ="n")
# initialize norms
coeffNorm = matrix(0, nrow =  max(fit.cv$gglasso.fit$group), length(fit.cv$gglasso.fit$lambda))
# compute norms group-wise
for (i in 1:max(fit.cv$gglasso.fit$group)){ 
  ind = fit.cv$gglasso.fit$group == i
  coeff = fit.cv$gglasso.fit$beta[ind,]
  if (sum(ind)>1){
    coeffNorm[i,] = apply(coeff, 2, function(x){sqrt(sum(x^2))})
  } else coeffNorm[i,] = coeff
} 
# coefficients' norm plot
plot(log(fit.cv$gglasso.fit$lambda), coeffNorm[1,], main="Coefficients' norm vs Lambda",
     ylab="Coefficients' norm",xlab="Ln Lambda values",
     col=cols[1],
     # xlim=c(-1,100),
     ylim=c(min(coeffNorm), max(coeffNorm)),
     type="l",lwd=4)
for (j in 2:nrow(coeffNorm)){
  lines(log(fit.cv$gglasso.fit$lambda), coeffNorm[j,], type="l",lwd=4, col=cols[j])
}
abline(0,0, lwd=3)
# plot legend once
# grid()
par(new=T)
legend('topright', legend = paste("group ", 1:5), lty=1, col=cols[1:5], cex = 0.6,lwd=2)

#Pick the best Lambda
lmbda=fit.cv$lambda.1se
(coefs=coef.gglasso(object=fit.cv$gglasso.fit,s=lmbda))
                       1
(Intercept)  0.103352890
sex         -0.009843253
age1        -0.018198974
age2        -0.025734448
job1        -0.051617852
job2        -0.027165667
job3        -0.004583826
sex:age1    -0.002729062
sex:age2    -0.006619003
sex:job1     0.000000000
sex:job2     0.000000000
sex:job3     0.000000000
#At best lambda get coefficients and fitted values
plt=sal_y-predict.gglasso(object=fit.cv$gglasso.fit,newx=form,s=lmbda,type='link')
plot(plt, ylab="residuals", xlab="index", main="Plot of residuals")
abline(0, 0, col= "red")

From this study it is possible to conclude that each factor considered is relevant to determine the salary level.

The least important factor results the interaction between sex and job, which is not statistically significant using group-Lasso. This means that there is no gender discrimination between various jobs, even if gender discrimination exists in general and for different age groups.

In the next section this topic will be investigated further.

7.2 Difference in salary: men vs. females

The focus of this section is on the on the difference on salary between males and females in various towns. Some classical approaches will be compared with robust ones, to diagnose the possible negative effect of the presence of noise or outliers. It is motivated by the assumptions that not all the towns come from the same data genereting process.

# These functions are used to create the plots used in the next chunks
# PLOTS FOR interactive regression diagnostic plots
RegressionPlots <- function(fit, textlabels, robust=F, out = NULL){
  
  # number of units
  n = length(fit$fitted.values)
  
  # original response
  y = fit$y
  
  # Extract fitted values from lm() object
  Fitted.Values <-  fit$fitted.values
  
  # Extract residuals from lm() object
  Residuals <-  fit$residuals
  
  if (robust == F){
    # Extract standardized residuals from lm() object
    Standardized.Residuals <- MASS::stdres(fit)  
    
    # Calculate Leverage
    Leverage <- lm.influence(fit)$hat
    
    # text for the labels of the plots
    tt = "OLS"
  } else{
    # standardized residuals based on a robust scale estimate
    Standardized.Residuals <- fit$residuals/fit$scale
    
    # get the robust distances based on mahalanobis distance from the robust output 
    # (it has to be specified in the call to lmrob using: control = lmrob.control(compute.rd = T))
    Leverage <- fit$MD
    
    # text for the labels of the plots
    tt = "Robust"
  }
  
  # Extract fitted values for lm() object
  Theoretical.Quantiles <- qqnorm(Residuals, plot.it = F)$x
  
  # Create data frame 
  # Will be used as input to plotly
  regMat <- data.frame(Fitted.Values, 
                       Residuals, 
                       Standardized.Residuals, 
                       Theoretical.Quantiles,
                       Leverage, 
                       textlabels)
  
  # Plot using Plotly
  
  # text info
  t <- list(
    family = "sans serif",
    size = 14,
    color = toRGB("grey50"))
  
  
  # Plot 1: Fitted vs Residuals
  # For scatter plot smoother
  LOESS <- loess.smooth(Fitted.Values, Residuals)
  
  # plt1 <- regMat %>% 
  #   plot_ly(x = Fitted.Values, y = Residuals, 
  #           type = "scatter", mode = "markers", hoverinfo = "text",
  #           color = out, name = "Data", marker = list(size = 10, opacity = 0.5),
  #           text = paste('</br> Unit: ', 1:n,
  #                        '</br> Town: ', as.character(textlabels),
  #                        '</br> x: ', Fitted.Values,
  #                        '</br> y: ', Residuals)) %>% 
  #   
  #   add_trace(x = LOESS$x, y = LOESS$y, type = "scatter", mode = "lines", name = "Smooth",
  #             inherit = F, line = list(width = 2)) %>% 
  # 
  #   layout(title = paste(tt, " Residuals vs Fitted Values"), plot_bgcolor = "#e6e6e6",
  #          xaxis = list(title = "Fitted Values"),
  #          yaxis = list(title = "Residuals"))
  
  # Plot 2: Fitted vs Standardized Residuals
  # For scatter plot smoother
  LOESS <- loess.smooth(Fitted.Values, Standardized.Residuals)
  
  plt2 <- regMat %>% 
    plot_ly(x = Fitted.Values, y = Standardized.Residuals, 
            type = "scatter", mode = "markers", hoverinfo = "text",
            color = out, name = "Data", marker = list(size = 10, opacity = 0.5),
            text = paste('</br> Unit: ', 1:n,
                         '</br> Town: ', as.character(textlabels),
                         '</br> x: ', Fitted.Values,
                         '</br> y: ', Standardized.Residuals)) %>% 
    
    add_trace(x = LOESS$x, y = LOESS$y, type = "scatter", mode = "lines", name = "Smooth",
              inherit = F, line = list(width = 2)) %>% 
    
    layout(title = paste(tt, " Standardized residuals vs Fitted Values"), plot_bgcolor = "#e6e6e6",
           xaxis = list(title = "Fitted Values"),
           yaxis = list(title = "Standardized residuals"))  
  # Plot 3: Residuals index
  plt3 <- regMat %>% 
    plot_ly(x = 1:n, y = Standardized.Residuals, 
            type = "scatter", mode = "markers", hoverinfo = "text",
            color = out, name = "Data", marker = list(size = 10, opacity = 0.5),
           text = paste('</br> Unit: ', 1:n,
                         '</br> Town: ', as.character(textlabels),
                         '</br> x: ', 1:n,
                         '</br> y: ', Standardized.Residuals)) %>% 
    
    add_segments(x = -5, xend = n+5, y = 0, yend = 0, mode = "line", inherit = F, 
                 name = "Null line", line = list(width = 2)) %>%
    
    layout(title = paste(tt, " Standardized Residuals' index"), plot_bgcolor = "#e6e6e6", 
             xaxis = list(title = "Index"),
             yaxis = list(title = "Standardized.Residuals"))
  # Plot 4: QQ Pot
  plt4 <- regMat %>% 
    plot_ly(x = Theoretical.Quantiles, y = Standardized.Residuals, 
            type = "scatter", mode = "markers", hoverinfo = "text", 
            color = out, name = "Data", marker = list(size = 10, opacity = 0.5),
           text = paste('</br> Unit: ', 1:n,
                         '</br> Town: ', as.character(textlabels),
                         '</br> x: ', Theoretical.Quantiles,
                         '</br> y: ', Standardized.Residuals)) %>% 
    
    add_trace(x = Theoretical.Quantiles, y = Theoretical.Quantiles, type = "scatter", 
              mode = "lines", name = "Theoretical", line = list(width = 2), 
              inherit = F) %>%
    
    layout(title = paste(tt, " Q-Q Plot"), plot_bgcolor = "#e6e6e6",
             xaxis = list(title = "Normal theoretical quantiles"),
             yaxis = list(title = "Data quantiles"))
  
  # Plot5: Residuals vs Leverage
  # For scatter plot smoother
  LOESS <- loess.smooth(Leverage, Residuals)
  
  plt5 <- regMat %>% 
    plot_ly(x = Leverage, y = Residuals, 
            type = "scatter", mode = "markers", hoverinfo = "text", 
            color = out, name = "Data", marker = list(size = 10, opacity = 0.5), 
           text = paste('</br> Unit: ', 1:n,
                         '</br> Town: ', as.character(textlabels),
                         '</br> x: ', Leverage,
                         '</br> y: ', Residuals)) %>% 
    
    add_trace(x = LOESS$x, y = LOESS$y, type = "scatter", mode = "lines", name = "Smooth",
              line = list(width = 2), inherit = F) %>%
    
    layout(title = paste(tt, " Leverage vs Residuals"), plot_bgcolor = "#e6e6e6", 
             xaxis = list(title = "Leverage values"),
             yaxis = list(title = "Residuals"))  
  # Plot 6: Fitted vs response
  # For scatter plot smoother
  LOESS <- loess.smooth(Fitted.Values, y)
  
  plt6 <- regMat %>% 
    plot_ly(x = Fitted.Values, y =y, 
            type = "scatter", mode = "markers", hoverinfo = "text",
            color = out, name = "Data", marker = list(size = 10, opacity = 0.5),
           text = paste('</br> Unit: ', 1:n,
                         '</br> Town: ', as.character(textlabels),
                         '</br> x: ', Fitted.Values,
                         '</br> y: ', y)) %>% 
    
    add_trace(x = LOESS$x, y = LOESS$y, type = "scatter", mode = "lines", name = "Smooth",
              inherit = F, line = list(width = 2)) %>% 
  
    layout(title = paste(tt, " Fitted Values vs response"), plot_bgcolor = "#e6e6e6",
             xaxis = list(title = "Fitted Values"),
             yaxis = list(title = "Response values"))
  # # Plot 7: MISSING: sqrt(abs(Residuals))
  # # For scatter plot smoother
  # LOESS2 <- loess.smooth(Fitted.Values, Root.Residuals)
  # 
  # plt3 <- regMat %>% 
  #   plot_ly(x = Fitted.Values, y = Root.Residuals, 
  #           type = "scatter", mode = "markers", hoverinfo = "text", 
  #           name = "Data", marker = list(size = 10, opacity = 0.5),
  #           text = paste('town: ', as.character(textlabels))) %>%
  #   
  #   add_trace(x = LOESS2$x, y = LOESS2$y, type = "scatter", mode = "lines", name = "Smooth",
  #             line = list(width = 2), inherit = F) %>% 
  #   
  #   layout(title = "Scale Location", plot_bgcolor = "#e6e6e6")
  plt = list(plt2, plt3, plt4, plt5, plt6) # plt1, 
  return(plt)
  # not working...to add in all plots
  # to avoid error with colorPalette<3
  # suppressWarnings(warning("RColorBrewer::brewer.pal"))
}
#
# PLOTS FOR BEST SUBSET REGRESSION
#
plot.regsubsets2 <-  
  function (x, labels = obj$xnames, main = NULL, scale = c("bic",  
     "Cp", "adjr2", "r2"), col = gray(seq(0, 0.9, length = 10)), ...)  
  { 
    obj <- x 
    lsum <- summary(obj) 
    par(mar = c(7, 5, 6, 3) + 0.1) 
    nmodels <- length(lsum$rsq) 
    np <- obj$np 
    propscale <- FALSE 
    sscale <- pmatch(scale[1], c("bic", "Cp", "adjr2", "r2"),  
                     nomatch = 0) 
    if (sscale == 0)  
      stop(paste("Unrecognised scale=", scale)) 
    if (propscale)  
      stop(paste("Proportional scaling only for probabilities")) 
    yscale <- switch(sscale, lsum$bic, lsum$cp, lsum$adjr2, lsum$rsq) 
    up <- switch(sscale, -1, -1, 1, 1) 
    index <- order(yscale * up) 
    colorscale <- switch(sscale, yscale, yscale, -log(pmax(yscale,  
                                                           1e-04)), -log(pmax(yscale, 1e-04))) 
    image(z = t(ifelse(lsum$which[index, ], colorscale[index],  
                       NA + max(colorscale) * 1.5)), xaxt = "n", yaxt = "n",  
          x = (1:np), y = 1:nmodels, xlab = "", ylab = scale[1],  
          col = col) 
    laspar <- par("las") 
    on.exit(par(las = laspar)) 
    par(las = 2) 
    axis(1, at = 1:np, labels = labels, ...) # I modified this line 
    axis(2, at = 1:nmodels, labels = signif(yscale[index], 2), ...) 
    # axis(2,cex.axis=0.01)
    if (!is.null(main))  
      title(main = main) 
    box() 
    invisible(NULL)
  } 

The considered model is built using all the variables that could explain the gap in salary. Some of them refers to the difference of some quantity between males and females for each town (e.g., diploma, univeristy, independent workers, etc.). The final model consist of 43 variables and 5025 units.

Additionally, some variables which would have had a huge positive impact on the prediction of the response variable have non been considered, because of their strong relation (e.g., gap in salary between males and females for various ages or occupations)

# response variable
# y = log10(newDat$sal_Males - newDat$sal_Females)
y = newDat$sal_Males - newDat$sal_Females
# original response variable histogram
ggplot(data=as.data.frame(y), aes(y)) +
  geom_histogram(aes(y =..density..), col="black", fill="blue", alpha = .3, bins = 35) +
  geom_density(col="black") +
  labs(x="Male-Female salary", y="Density") +
  ggtitle("Original response variable") +
  theme(plot.title = element_text(hjust = 0.5))

x = cbind.data.frame(pop = newDat$total_population, 
                     firms = newDat$total,
                     ratio_pop_firms = newDat$total/newDat$total_population,
                     # delta_exec = newDat$sal_M_executive - newDat$sal_F_executive,
                     # delta_midMan = newDat$sal_M_midManager - newDat$sal_F_midManager,
                     # delta_empl = newDat$sal_M_employee - newDat$sal_F_employee,
                     # delta_worker = newDat$sal_M_worker - newDat$sal_F_worker,
                     workforce = newDat$workforce,
                     dependency_ratio = newDat$dependency_ratio,
                     delta_wagearner = newDat$wagearner_M - newDat$wagearner_F,
                     delta_indep = newDat$independent_M - newDat$independent_F,
                     # delta_transfer = newDat$transfer_M - newDat$transfer_F, # 2000 zeros
                     delta_employer = newDat$employer_M - newDat$employer_F,
                     delta_full = newDat$full_M - newDat$full_F,
                     # delta_half = newDat$half_M - newDat$half_F, # OLS gives NA
                     superficie = newDat$Superficie,
                     housing14 = newDat$housing14,
                     median_living14 = newDat$median_living14,
                     empl = newDat$empl,
                     emp_sal = newDat$emp_sal,
                     pop15_64 = newDat$pop15_64,
                     unemp15_64 = newDat$unemp15_64,
                     act15_64 = newDat$act15_64,
                     unemp_rate = newDat$unemp_rate,
                     delta_NoDip = newDat$male_NoDip - newDat$female_NoDip,
                     delta_sec = newDat$male_Sec - newDat$female_Sec,
                     delta_hi = newDat$male_Hi - newDat$female_Hi,
                     delta_univ = newDat$male_Univ - newDat$female_Univ,
                     nodip = newDat$nodip,
                     delta_immig = newDat$male_immig - newDat$female_immig,
                     delta_workingImmig = newDat$male_working_immig - newDat$female_working_immig,
                     delta_native = newDat$male_native - newDat$female_native,
                     # evol_entrepr = newDat$evol_entrepreneurial_score,
                     # evol_pop = newDat$evol_pop_score,
                     regional_GDP = newDat$regional_GDP,
                     # dymanic_demo_prof = newDat$dymanic_demo_prof,
                     nb_active_employees = newDat$nb_active_employees,
                     nb_active_non_employees = newDat$nb_active_non_employees,
                     # nb_active = newDat$nb_active,   # OLS gives NA
                     nb_created_services = newDat$nb_created_services,
                     nb_created_commerce = newDat$nb_created_commerce,
                     nb_created_construction = newDat$nb_created_construction,
                     nb_created_ind = newDat$nb_created_ind,
                     # nb_created_firms = newDat$nb_created_firms, # OLS gives  
                     nb_firms_service = newDat$nb_firms_service,
                     nb_firms_commerce = newDat$nb_firms_commerce,
                     nb_firms_construction = newDat$nb_firms_construction,
                     nb_firms_ind = newDat$nb_firms_ind,
                     nb_major = newDat$nb_major,
                     nb_minor = newDat$nb_minor,
                     delta_num_sex = newDat$nb_male - newDat$nb_female,
                     nb_household = newDat$nb_household)
Error in data.frame(..., check.names = FALSE) : 
  arguments imply differing number of rows: 5022, 0

These plots shows that:

  • the response is long tailed, which also motivates the robust framework
  • there is strong evidence of collinearity

The final model is built considering a random sample of observations (70% of them) in order to reduce spatial correlation among units

In the following analyses to towns will be often highlighted and they have been found ex-post. They are considered as 2 typical outliers but for different reasons. One of them is Paris, which is an outlier for obvious reasons. The other (unexpected) one is Chambourcy, which has been spotted by the following analysis and it is a very small town.

All the coefficients’ estimates obtained in the following will be saved in a dataframe to allow an easy ex-post comparison. For fitting methods that provide significance levels, only variables significant at 0.01% will be kept.

7.2.1 Naive approaches

7.2.1.1 Ordinary least squares

A classical linear model based on OLS is first fitted

It shows that:

  • there is etoreskadasticity
  • Paris has a levarage equal to 1

Also, high collinearity and robustness are not taken into account. At the same time, running different times OLS didn’t produce any coefficients’ instability, even if VIF values are very high.

7.2.1.2 Best subset selection

Model selection by exhaustive search (suffering the same drawbacks as OLS) is now applied, which is also a very heavy approach in terms of computing time.

7.2.1.3 Stepwise selection

Model selection by sequential replacement (suffering the same drawbacks as OLS) is now applied and it is very fast compared to the exhaustive search

This approach results in a sparser model compared to the exhaustive search, and the cost (e.g., in terms of BIC) is higher.

7.2.1.4 MM estimator

The model is now fitted using the MM estimator, which is a soft trimming methodology. It is based on a starting robust estimator with a high breakdown point (in this case an S estimator), which provides a robust scale estimate used by the MM estimator to recover efficiency

This model shows that:

  • residuals and fitted values seem better than the OLS fit
  • it is able to identify the most oulying units (considering all the dimensions)
  • leverage points (Lyon and Toulouse) have a lower weight compared to OLS estimates

7.2.1.5 Least trimmed squares

A very similar result is obtained using the LTS estimator (i.e., an hard trimming approach), with trimming proportion equal to 10% of the units.

Using the MM or LTS estimator, as expected, increases the adjusted R squared of around 10%.

7.2.1.6 Best subset selection with MM weights

The results obtained using a best subset selection procedure are now combined with the weights obtained using the MM estimator (pretty similar to the ones obtaiend using the LTS weights)

This approach results in a significant improvement of the cost function, but the best model is less sparse and it keeps approximately 20 variables.

7.2.1.7 Elastic net

To face the problem of strong collinearity, which has not been considere yet, an Elastic-net is now fitted using 10-folds cross validation to tune lambda and considering a range of 11 equispaced alpha values. The best model is considered the one with the lowest MSPE.

This estimator proides a fairly sparse model, but it is non robust, it is biased, and it doesn’t easy allow to perform inferential reasoning.

7.2.1.8 OLS on elastic net solution

To partially solve the latter two problems, an OLS fit could be applied only on the variables previously identified by ENET.

This results in a sparser model and the diagnostic plots shows a better result compared to the raw OLS estimates, but it is still not robust.

7.2.1.9 Elastic net with MM weights

At this point, it seems reasonable to use the weights obtained by the MM estimator in an ENET model, trying to recover some robustness and keeping a sparse model

Contrarily to what was expected, the model is not so sparse. On the other hand, the mean CV error decreases substiantially (around 0.4) compared to the pure ENET model (around 1.4), and this gain would persist even allowing for a stronger shrinkage of the lambda values and hence reducing the number of coefficients. It is also interesting to note the strong decrease in CV error as some proportion of Lasso is introduced.

Such behavior should be studied further. A motivation could be the fact that ENET is based on CV, and some oulying units with weights different from 0 can affect this result. On the other hand, even providing to ENET only units with MM-weights greater than 0.9 (i.e., almost surely non outlying according to MM based on all variables), mean CV error decrease substantially, but the problem persists.

7.2.1.10 OLS on Elastic net solution with MM weights

The same is true applying an OLS estimate on the non oulying units (i.e., with MM-weights > 0.05) and the variables identified bu ENET(MM).

Such a model is sparser. It still contains some leverage points, like Toulose and Lyon, but the diagnostic plots don’t show any particularly strange behavior.

At the same two important aspects have to be taken in account:

  • the MM weights used were obtained on the base of the full model, hence they could not be really representative
  • some units are not being modeled in this framework

7.2.1.11 Elastic net with MM weights to model outliers

To face the latter problem an approach could be based on the idea of modeling apart the most outlying units. For instance, the following fit is based on ENET where the provided weights correspond to 1 - MMweights.

This model results in:

  • in a very sparse model which seems to fit reasonably well all the points
  • provides coefficients which are sensibly different than the ones for the previously considered models (e.g., unemployment coefficients changes its sign) supporting the assumption that there is more than one data generating process
  • the mean CV error increases substantially

A possible drawback of this approach, again, reside on the fact that the MM weights are based on the full model.

7.2.2 Robust approaches incorporating shrinkage

7.2.2.1 Sparse-LTS

The estimator sparse-LTS, which is a robustification of Lasso using LTS, is now applied. The parameters used perform a trimming 10% of the units, and 10 replication of 10-folds CV

This model results in:

  • a very sparse and robust model
  • the unemployment rate is the most important variable
  • clearly shows 2 different sub-popilations
  • Paris is not a clear outlier

The main drawback resides on the fact that it is not taking into account collinearity.

7.2.2.2 Elastic net with sparse-LTS weights

In order to try to solve this problem, ENET with weights provided by sparseLTS could be applied

In this case, instead of the actual minimum obtained by ENET which corresponded to a Lasso fit, the best labda kept corresponds to alpha=0.7 (at the cost of a slightly higher CV error). The highest coefficient for this model results the unemployment rate.

To perform a further validation of these results, OLS is applied on the model identified (discarding the units identified by sparse-LTS)

They show that:

  • the model seems correctly specified and all regressors are significant
  • Paris is not considered as on outlier (its leverage is strongly decreased)
  • the unemployment rate has a strong effect on the response

7.2.2.3 Penalized ENET based on the S estimator

Penalized ENET based on the S estimator (PENSE) is now applied. The PENSE estimate minimizes the robust M-scale of the residuals penalized by the L1 and L2 norm of the regression coefficients (elastic net penalty). The level of penalization is chosen to minimize the k-fold cross-validated prediction error (using a robust measure).

This models show that:

The main drawback it’s the computing time required.

7.2.2.4 Elastic net with LTS cost function

Robust elastic net, based on LTS, is now fitted to the model trimming 10% of the units. It requires the removal of the dummy variable for urban towns, in order to avoid numerical problems.

This model shows that:

  • the algorithm (even if not parallelized for Windows users) is much faster than PENSE

7.2.2.5

7.2.3 Comparison of the estimates obtained

Print table of results

This function is used to plot a summary of the results obtained in HTML format

LS0tDQp0aXRsZTogIlRTTCBQcm9qZWN0Ig0KYXV0aG9yOiAiTHVjYSBJbnNvbGlhLCBKaXN1IEtpbSBhbmQgR2V2b3JnIFllZ2hpa3lhbiIgICAgDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC8lbS8lWScpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzMnIA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBrZWVwX3RleDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiB0YW5nbyAgICANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzMnIA0KIyAgIHBkZl9kb2N1bWVudDogIyBBREQgUERGIQ0KIyAgICAgdG9jOiB5ZXMNCiMgICAgIHRvY19kZXB0aDogJzMnDQojICAgICBrZWVwX3RleDogdHJ1ZQ0KIyAgICAgbGF0ZXhfZW5naW5lOiBwZGZsYXRleA0KIyBlZGl0b3Jfb3B0aW9uczoNCiMgICAjIHRvYzogeWVzDQojICAgIyB0b2NfZGVwdGg6IDMNCiMgICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KDQojIEdlbmVyYWwgaW5mb3JtYXRpb24gDQoNCldlIGFuYWx5emUgYSBkYXRhc2V0IHB1Ymxpc2hlZCBvbiBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2V0aWVubmVscS9mcmVuY2gtZW1wbG95bWVudC1ieS10b3duKS4NCkl0IHJlZmVycyB0byBmcmVuY2ggZW1wbG95bWVudCwgc2FsYXJpZXMsIHBvcHVsYXRpb24gcGVyIHRvd24uIA0KVGhlIGFpbSBpcyB0byBldmFsdWF0ZSBlcXVhbGl0eS9pbmVxdWFsaXRpZXMgaW4gRnJhbmNlLCBhbmQgZ2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbiBvZiBidXNpbmVzcyBhY2NvcmRpbmcgdG8gdGhlaXIgc2l6ZS4NCg0KU3VjaCBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgdGhlIElOU0VFLg0KSW5mb3JtYXRpb24gcmVnYXJkaW5nIHRoZSBudW1iZXIgb2YgZmlybXMgaW4gZXZlcnkgZnJlbmNoIHRvd24sIGNhdGVnb3JpemVkIGJ5IHNpemUNCmNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly93d3cuaW5zZWUuZnIvZnIvbWV0YWRvbm5lZXMvZGVmaW5pdGlvbi9jMTEzNSkuIFRoaXMgZGF0YXNldCBjb250YWlucyBhYm91dCAzNTAwMCB1bml0cy9wZXIgdG93bi4NCg0KSW5mb3JtYXRpb24gYWJvdXQgc2FsYXJpZXMgYXJvdW5kIGZyZW5jaCB0b3duIHBlciBqb2IgY2F0ZWdvcmllcywgYWdlIGFuZCBzZXggKGV4cHJlc3NlZCBpbiBhdmVyYWdlIG5ldCBhbW91bnQgcGVyIGhvdXIgaW4gZXVybykgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy5pbnNlZS5mci9mci9zdGF0aXN0aXF1ZXMvMjUyMjUxNSkuIFRoaXMgZGF0YXNldCBjb250YWlucyBhYm91dCA1MDAwIHVuaXRzL3BlciB0b3duLiANCg0KRGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gaW4gRnJhbmNlIHBlciB0b3duLCBhZ2UsIHNleCBhbmQgbGl2aW5nIG1vZGUNCmNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly93d3cuaW5zZWUuZnIvZnIvc3RhdGlzdGlxdWVzLzI4NjM2MDcpLiBUaGlzIGRhdGFzZXQgY29udGFpbnMgYWJvdXQgOCBtaWxsaW9uIHVuaXRzL3BlciB0b3duLiBBZGRpdGlvbmFsIGluZm8gYWJvdXQgUG9wdWxhdGlvbiBEYXRhIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly93d3cuaW5zZWUuZnIvZnIvc3RhdGlzdGlxdWVzLzI4NjM2MDcjZGljdGlvbm5haXJlKS4gDQoNClRoZXNlIGRhdGFzZXRzIGhhdmUgYmVlbiBwcmUtcHJvY2Vzc2VkIGFuZCBwdXQgdG9nZXRoZXIuIFRoZSBmaW5hbCBkYXRhc2V0IGNvbnRhaW5zIDU4IHZhcmlhYmxlcyBhbmQgNTAyMiBvYnNlcnZhdGlvbnMuIA0KDQoNCiMjIEFpbSBvZiB0aGUgc3R1ZHkgDQoNClRoaXMgcHJvamVjdCBhaW1zIHRvIGV4cGxvcmUgc3RydWN0dXJlIG9mIEZyZW5jaCBsYWJvdXIgbWFya2V0LiANCkluIHBhcnRpY3VsYXIsIHdlIGFyZSBpbnRlcmVzdGVkIGluOg0KDQoqIGV2YWx1YXRpbmcgcG9zc2libGUgaW5lcXVhbGl0aWVzOiBwZXIgdG93bnMvcmVnaW9uLCBzZXgsIGFnZSwgam9iIGNhdGVnb3JpZXMgZXRjLjsNCiogZGlzY292ZXIgZ2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbiBvZiBidXNpbmVzcyBhY2NvcmRpbmcgdG8gdGhlaXIgc2l6ZQ0KKiBwcmVkaWN0aW5nIHRoZSAuLi4gdXNpbmcgYSByZWdyZXNzaW9uIG1vZGVsOw0KKiByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIC4uLiBwZXJmb3JtaW5nIGEgUENBOw0KKiBleHBsb3JlIGRpZmZlcmVudCBhbGdvcml0aG1zIHRvIGNsdXN0ZXIgbWFsZS9mZW1hbGVzIHVzaW5nIC4uLg0KDQoNCiMjIFBsYW4gZm9yIHRoZSBzdHVkeSANCg0KMS4gVW5zdXBlcnZpc2VkIGxlYXJuaW5nOiANCiogUENBDQoqIENsdXN0ZXJpbmcgbWV0aG9kcyAoSy1tZWFucy9IaWVyYXJjaGljYWwpDQoyLiBTdXBlcnZpc2VkIGxlYXJuaW5nOg0KKiBHTE0NCiogTGluZWFyL1F1YWRyYXRpYyBEaXNjcmltaW5hbnQgQW5hbHlzaXMNCiogS05ODQoqIENyb3NzLXZhbGlkYXRpb24NCiogQm9vdHN0cmFwDQoqIFN1YnNldCBzZWxlY3Rpb24gDQoqIFNocmlua2FnZSBtZXRob2RzDQoqIERpbWVuc2lvbiByZWR1Y3Rpb24gbWV0aG9kcw0KMy4gRGVzY3JpcHRpb24gb2YgcG9wdWxhdGlvbiBkZW1vZ3JhcGhpY3MgaW4gRnJhbmNlDQo0LiBTdHJ1Y3R1cmUgb2YgdGhlIGZyZW5jaCBsYWJvdXIgbWFya2V0DQo1LiAuLi4NCjYuIEZ1dHVyZSB3b3Jrcw0KDQoNCiMjIExvYWRpbmcgdG9vbHMNCg0KTG9hZGluZyBhbGwgbGlicmFyaWVzIG5lZWRlZCB0aHJvdWdob3V0IHRoZSBub3RlYm9vaw0KYGBge3IgbG9hZGluZyBsaWJyYXJpZXMsIGluY2x1ZGU9RkFMU0UsIGNhY2hlPUZBTFNFfQ0KDQojIGRhdGEgbWFuaXB1bGF0aW9uDQppZighcmVxdWlyZShwc3ljaCkpe2luc3RhbGwucGFja2FnZXMoInBzeWNoIik7IGxpYnJhcnkocHN5Y2gpfSANCmlmKCFyZXF1aXJlKHBseXIpKXtpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpOyBsaWJyYXJ5KHBseXIpfQ0KaWYoIXJlcXVpcmUoZHBseXIpKXtpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpOyBsaWJyYXJ5KGRwbHlyKX0NCiMgcGxvdHRpbmcNCmlmKCFyZXF1aXJlKFJDb2xvckJyZXdlcikpe2luc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpOyBsaWJyYXJ5KFJDb2xvckJyZXdlcil9ICMgY29sb3JzDQppZighcmVxdWlyZSh6b28pKXtpbnN0YWxsLnBhY2thZ2VzKCJ6b28iKTsgbGlicmFyeSh6b28pfSAjIGNvbG9ycw0KaWYoIXJlcXVpcmUoZ2dwbG90Mikpe2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKTsgbGlicmFyeShnZ3Bsb3QyKX0NCmlmKCFyZXF1aXJlKGdnZm9ydGlmeSkpe2luc3RhbGwucGFja2FnZXMoImdnZm9ydGlmeSIpOyBsaWJyYXJ5KGdnZm9ydGlmeSl9DQppZighcmVxdWlyZShnZ21hcCkpe2luc3RhbGwucGFja2FnZXMoImdnbWFwIik7IGxpYnJhcnkoZ2dtYXApfQ0KaWYoIXJlcXVpcmUoZ2diaXBsb3QpKXsNCiAgaWYoIXJlcXVpcmUoZGV2dG9vbHMpKXsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpfQ0KICBpbnN0YWxsX2dpdGh1YigidnF2L2dnYmlwbG90IikNCiAgbGlicmFyeShnZ2JpcGxvdCl9DQppZighcmVxdWlyZShjb3JycGxvdCkpe2luc3RhbGwucGFja2FnZXMoImNvcnJwbG90Iik7IGxpYnJhcnkoY29ycnBsb3QpfQ0KaWYoIXJlcXVpcmUoem9vKSl7aW5zdGFsbC5wYWNrYWdlcygiem9vIik7IGxpYnJhcnkoem9vKX0NCmlmKCFyZXF1aXJlKHBsb3RseSkpe2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpOyBsaWJyYXJ5KHBsb3RseSl9ICMgaW50ZXJhY3RpdmUgcGxvdHRpbmcNCmlmKCFyZXF1aXJlKGxhdHRpY2UpKXtpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIik7IGxpYnJhcnkobGF0dGljZSl9DQppZighcmVxdWlyZShNVkEpKXtpbnN0YWxsLnBhY2thZ2VzKCJNVkEiKTsgbGlicmFyeShNVkEpfQ0KaWYoIXJlcXVpcmUoYXBlKSl7aW5zdGFsbC5wYWNrYWdlcygiYXBlIik7IGxpYnJhcnkoYXBlKX0NCmlmKCFyZXF1aXJlKEtlcm5TbW9vdGgpKXtpbnN0YWxsLnBhY2thZ2VzKCJLZXJuU21vb3RoIik7IGxpYnJhcnkoS2VyblNtb290aCl9DQppZighcmVxdWlyZShyZ2wpKXtpbnN0YWxsLnBhY2thZ2VzKCJyZ2wiKTsgbGlicmFyeShyZ2wpfQ0KaWYoIXJlcXVpcmUoY2FyKSl7aW5zdGFsbC5wYWNrYWdlcygiY2FyIik7IGxpYnJhcnkoY2FyKX0NCiMgQ2x1c3RlcmluZw0KaWYoIXJlcXVpcmUobWNsdXN0KSl7aW5zdGFsbC5wYWNrYWdlcygibWNsdXN0Iik7IGxpYnJhcnkobWNsdXN0KX0NCmlmKCFyZXF1aXJlKGRic2Nhbikpe2luc3RhbGwucGFja2FnZXMoImRic2NhbiIpOyBsaWJyYXJ5KGRic2Nhbil9DQojIHBsb3QgZGVzaWduIG1hdHJpeA0KaWYoIXJlcXVpcmUocmFmYWxpYikpe2luc3RhbGwucGFja2FnZXMoInJhZmFsaWIiKTsgbGlicmFyeShyYWZhbGliKX0NCiMgY3Jvc3MgdmFsaWRhdGlvbg0KaWYoIXJlcXVpcmUoYm9vdCkpe2luc3RhbGwucGFja2FnZXMoImJvb3QiKTsgbGlicmFyeShib290KX0NCiMgZWxhc3RpYyBuZXQNCmlmKCFyZXF1aXJlKGdsbW5ldCkpe2luc3RhbGwucGFja2FnZXMoImdsbW5ldCIpOyBsaWJyYXJ5KGdsbW5ldCl9DQojIGdyb3VwIExhc3NvDQppZighcmVxdWlyZShnZ2xhc3NvKSl7aW5zdGFsbC5wYWNrYWdlcygiZ2dsYXNzbyIpOyBsaWJyYXJ5KGdnbGFzc28pfQ0KIyByb2J1c3QgZml0DQppZighcmVxdWlyZShyb2J1c3RiYXNlKSl7aW5zdGFsbC5wYWNrYWdlcygicm9idXN0YmFzZSIpOyBsaWJyYXJ5KHJvYnVzdGJhc2UpfQ0KIyByb2J1c3QgTGFzc28gDQppZighcmVxdWlyZShyb2J1c3RIRCkpe2luc3RhbGwucGFja2FnZXMoInJvYnVzdEhEIik7IGxpYnJhcnkocm9idXN0SEQpfQ0KaWYoIXJlcXVpcmUoZW5ldExUUykpe2luc3RhbGwucGFja2FnZXMoImVuZXRMVFMiKTsgbGlicmFyeShlbmV0TFRTKX0NCiMgYm94LWNveCB0cmFuZm9ybWF0aW9uIChpbiBBTk9WQSkgYW5kIHN0YW5kYXJkaXplZCByZXNpZHVhbHMNCmlmKCFyZXF1aXJlKE1BU1MpKXtpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIik7IGxpYnJhcnkoTUFTUyl9DQojIGJlc3Qgc3Vic2V0IHNlbGVjdGlvbg0KaWYoIXJlcXVpcmUobGVhcHMpKXtpbnN0YWxsLnBhY2thZ2VzKCJsZWFwcyIpOyBsaWJyYXJ5KGxlYXBzKX0NCiMgcGxvdCBmb3IgUENBDQojIGlmKCFyZXF1aXJlKGZhY3RvZXh0cmEpKXtpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIik7IGxpYnJhcnkoZmFjdG9leHRyYSl9DQoNCmBgYA0KDQpJbXBvcnQgdGhlIGRhdGFzZXRzDQpgYGB7ciBsb2FkaW5nIGRhdGFzZXRzLCB3YXJuaW5nPUZBTFNFfQ0KDQpzZXR3ZCgiLi9kYXRhIikNCmZpcm1zICAgICAgIDwtIHJlYWQuY3N2KCJiYXNlX2V0YWJsaXNzZW1lbnRfcGFyX3RyYW5jaGVfZWZmZWN0aWYuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiKQ0KZ2VvICAgICAgICAgPC0gcmVhZC5jc3YoIm5hbWVfZ2VvZ3JhcGhpY19pbmZvcm1hdGlvbi5jc3YiLCBlbmNvZGluZyA9ICJVVEYtOCIpDQpzYWxhcnkgICAgICA8LSByZWFkLmNzdigibmV0X3NhbGFyeV9wZXJfdG93bl9jYXRlZ29yaWVzLmNzdiIsIGVuY29kaW5nID0gIlVURi04IikNCnBvcHVsYXRpb24gIDwtIHJlYWQuY3N2KCJwb3B1bGF0aW9uLmNzdiIsIGVuY29kaW5nID0gIlVURi04IikNCmVkdWMgICAgICAgIDwtIHJlYWQuY3N2KCJsZXZlbF9lZHVjYXRpb24uY3N2Iiwgc2VwID0iOyIsIGVuY29kaW5nID0gIlVURi04IikNCmNhdGVnX3NvY2lvIDwtIHJlYWQuY3N2KCJDYXRlZ29yaWVfc29jaW9wcm9mZXNzaW9ubmVsbGUuY3N2Iiwgc2VwID0iOyIsIGVuY29kaW5nID0gIlVURi04IikNCnN0YXR1c193b3JrIDwtIHJlYWQuY3N2KCJFbXBsb2lzX2xpZXVfdHJhdmFpbC5jc3YiLCBzZXAgPSI7IiwgZW5jb2RpbmcgPSAiVVRGLTgiKQ0KaW5lcSAgICAgICAgPC0gcmVhZC5jc3YoIkNvbXBhcmF0ZXVyX3RlcnJpdG9pcmVzLmNzdiIsIHNlcCA9IjsiLCBlbmNvZGluZyA9ICJVVEYtOCIpDQpjb21tdW5lICAgICA8LSByZWFkLmNzdigiaW5zZWVfY29tbXVuZS5jc3YiLCBlbmNvZGluZyA9ICJVVEYtOCIpDQoNCmBgYA0KDQoNCiMgUHJlLXByb2Nlc3NpbmcNCg0KIyMgRmlybXMgZGF0YQ0KIA0KQXNzaWduIG1lYW5pbmdmdWwgbmFtZXMgYW5kIGNoZWNrIHRoZSBtb2RpZmllZCBkYXRhOg0KYGBge3IgcHJlIHByb2Nlc3NpbmcgZmlybXN9DQoNCm5hbWVzKGZpcm1zKSANCm5hbWVzKGZpcm1zKVsyOm5jb2woZmlybXMpXSA8LQ0KICBjKCJ0b3duIiwgDQogICAgInJlZ051bSIsDQogICAgImRlcHROdW0iLA0KICAgICJ0b3RhbCIsDQogICAgIm51bGwiLA0KICAgICJmaXJtc0VtcGxfMV81IiwNCiAgICAiZmlybXNFbXBsXzZfOSIsDQogICAgImZpcm1zRW1wbF8xMF8xOSIsDQogICAgImZpcm1zRW1wbF8yMF80OSIsDQogICAgImZpcm1zRW1wbF81MF85OSIsDQogICAgImZpcm1zRW1wbF8xMDBfMTk5IiwNCiAgICAiZmlybXNFbXBsXzIwMF80OTkiLA0KICAgICJmaXJtc0VtcGxfNTAwcGx1cyIpDQoNCiMgcHJlbGltaW5hcnkgY2hlY2tzDQpuYW1lcyhmaXJtcykNCmhlYWQoZmlybXMpDQpzdHIoZmlybXMpDQpzdW1tYXJ5KGZpcm1zKQ0KDQojIENoZWNrIGZvciBkdXBsaWNhdGVkIGRhdGE6IHRoZXJlIGlzIG5vDQpzdW0oZHVwbGljYXRlZC5kYXRhLmZyYW1lKGZpcm1zKSkNCg0KYGBgDQoNCkNhdGVnb3JpemUgZmlybXMnIHNpemUgYWNjb3JkaW5nIHRvIA0KW0VVIHN0YW5kYXJkXShodHRwOi8vZWMuZXVyb3BhLmV1L2V1cm9zdGF0L3N0YXRpc3RpY3MtZXhwbGFpbmVkL2luZGV4LnBocC9HbG9zc2FyeTpFbnRlcnByaXNlX3NpemUpLCANCmJ1dCBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtIGZvciBtZWRpdW0gYW5kIGxhcmdlIGZpcm1zIA0KKGkuZS4sIG1lZGl1bSBmaXJtcyBoYXZlIDwyMDAgaW5zdGVhZCBvZiA8MjUwIGVtcGxveWVlcyk6DQpgYGB7ciBtb2RpZnkgZmlybXN9DQoNCiMgbWVyZ2UgdmFyaWFibGVzDQpmaXJtcyRtaWNybyAgIDwtIGZpcm1zJGZpcm1zRW1wbF8xXzUgKyBmaXJtcyRmaXJtc0VtcGxfNl85DQpmaXJtcyRzbWFsbCAgIDwtIGZpcm1zJGZpcm1zRW1wbF8xMF8xOSArIGZpcm1zJGZpcm1zRW1wbF8yMF80OQ0KZmlybXMkbWVkaXVtICA8LSBmaXJtcyRmaXJtc0VtcGxfNTBfOTkgKyBmaXJtcyRmaXJtc0VtcGxfMTAwXzE5OQ0KZmlybXMkbGFyZ2UgICA8LSBmaXJtcyRmaXJtc0VtcGxfMjAwXzQ5OSArIGZpcm1zJGZpcm1zRW1wbF81MDBwbHVzDQoNCiMgRHJvcCB1bm5lY2Vzc2FyeSAoYXQgdGhlIG1vbWVudCkgY29sdW1ucyANCmZpcm1zIDwtIHN1YnNldChmaXJtcywgc2VsZWN0ID0gYyhDT0RHRU8sIHRvd24sIHRvdGFsLCBtaWNybywgc21hbGwsIG1lZGl1bSwgbGFyZ2UsIG51bGwpKQ0KDQojIGNoZWNrDQpzdW1tYXJ5KGZpcm1zKQ0KDQojIHRoZXJlIGlzIGFuIG9icyB3aXRoIG1vcmUgdGhhbiAzMTZLIG51bGwgZGF0YTogd2UgY2hlY2sgaWYgaXQgaXMgcGxhdXNpYmxlDQojIGdldCB0aGUgaGlnaGVzdCAyMCBudWxsIHZhbHVlcw0Kc3RyX2Zpcm1zIDwtIHNvcnQoZmlybXMkbnVsbCwgZGVjcmVhc2luZyA9IFQpWzE6MjBdDQojIGdldCB0aGVpciBpbmRleGVzDQpzdHJfZmlybXNfaW5kIDwtIG1hdGNoKHN0cl9maXJtcywgZmlybXMkbnVsbCkNCiMgZ2V0IHRoZSBjb3JyZXNwb25kaW5nIGNpdHkNCmZpcm1zJHRvd25bc3RyX2Zpcm1zX2luZF0NCiMgdGhleSBhcmUgdGhlIGxhcmdlc3QgY2l0aWVzLCBoZW5jZSBpdCBzZWVtcyByZWFzb25hYmxlLi4NCg0KYGBgDQoNCiMjIEdlb2dyYXBoaWNhbCBkYXRhIA0KDQpBc3NpZ24gbmFtZXMgYW5kIHJlbW92ZSBzb21lIHZhcmlhYmxlczoNCmBgYHtyIHByZSBwcm9jZXNzaW5nIGdlb30NCg0KbmFtZXMoZ2VvKQ0KbmFtZXMoZ2VvKVtjKDI6MTEsIDE0KV0gPQ0KICBjKCJjb2RlX3JlZ2lvbiIsDQogICAgInJlZ2lvbiIsIA0KICAgICJyZWdpb25fY2FwaXRhbCIsDQogICAgIm51bWJlcl9kZXBhcnQiLA0KICAgICJkZXBhcnRtZW50IiwgDQogICAgInByZWZlY3R1cmUiLA0KICAgICJjaXJjb25zIiwNCiAgICAidG93bl9uYW1lIiwgDQogICAgInBvc3RhbF9jb2RlIiwgDQogICAgIkNPREdFTyIsDQogICAgImVsb2lnbmVtZW50IikNCg0KIyBkcm9wIHVubmVjZXNzYXJ5IGNvbHVtbnMgKGNvZGUvbnVtIGFuZCBuYW1lIHJlcHJlc2VudHMgc2FtZSB0aGluZykgDQpnZW8gPC0gc3Vic2V0KGdlbywgc2VsZWN0ID0gLWMoRVVfY2lyY28sIGNvZGVfcmVnaW9uLCBudW1iZXJfZGVwYXJ0LCBwcmVmZWN0dXJlLCBjaXJjb25zLCBlbG9pZ25lbWVudCwgdG93bl9uYW1lKSkNCg0KIyBwcmVsaW1pbmFyeSBjaGVja3MNCm5hbWVzKGdlbykNCmhlYWQoZ2VvKQ0Kc3RyKGdlbykNCnN1bW1hcnkoZ2VvKQ0KDQpgYGANCg0KQ29ycmVjdCB0eXBvcyBmb3IgbG9uZ2l0dWRlIGRhdGEgYW5kIGtlZXAganVzdCB0aGUgdW5pcXVlIENPREdFTyB0byBhdm9pZCB0b3ducyB3aXRoIG11bHRpcGxlIHBvc3RhbCBjb2RlczoNCmBgYHtyIGZpeCBnZW99DQoNCiMgc3BvdCAiLCIgaW5zdGVhZCBvZiAiLiIgaW4gbG9uZ2l0dWRlDQpuZXdMb25nICAgICAgIDwtIGFzLmNoYXJhY3RlcihnZW8kbG9uZ2l0dWRlKSAgICAjIGNvcHkgdGhlIHZlY3Rvcg0Kc3VtKGdyZXAoIiwiLCBuZXdMb25nKSkgICAgICAgICAgICAgICAgICAgICAgICAgIyB0b3RhbCBjb21tYXMNCmluZF9sb25nX2VyciAgPC0gZ3JlcCgiLCIsIG5ld0xvbmcpICAgICAgICAgICAgICMgaW5kZXhpbmcgdGhlbQ0KbmV3TG9uZyAgICAgICA8LSBnc3ViKCIsIiwgIi4iLCBuZXdMb25nKSAgICAgICAgIyBzdWJzdGl0dXRpbmcgdGhlbSB3aXRoIGRvdHMNCmluZE5BX0xvbmcgICAgPC0gaXMubmEoYXMubnVtZXJpYygobmV3TG9uZykpKSAgICMgc3BvdCBOQQ0KIyBnZW8kbG9uZ2l0dWRlW2luZE5BX0xvbmddICAgICAgICAgICAgICAgICAgICAgICAjIHZlcmlmeSB0aGF0IHRoZXkgd2VyZSBhY3R1YWxseSBtaXNzaW5nDQpnZW8kbG9uZ2l0dWRlIDwtIGFzLm51bWVyaWMobmV3TG9uZykgICAgICAgICAgICAjIG92ZXJ3cml0ZSB0aGUgbG9uZ2l0dWRlIHZhcmlhYmxlIHdpdGggdGhlIG5ldyBvbmUNCg0KIyBDaGVjayBmb3IgZHVwbGljYXRlZCBkYXRhIChlLmcuLCBjaXRpZXMgd2l0aCBkaWZmZXJlbnQgcG9zdGFsIGNvZGVzLCB0aGF0IHdlIGRyb3BwZWQpOg0KICAjIGUuZy4sIHRvIHZlcmlmeSBpdCwgIHRyeSBvbiB0aGUgaW5pdGlhbCBkYXRhc2V0Og0KICAjIHN1bShnZW8kbm9tX2NvbW11bmUgPT0gIlBhcmlzIikNCiAgIyBpbmRfZHVwbGljIDwtIGdlbyRub21fY29tbXVuZSA9PSAiUGFyaXMiDQogICMgZ2VvW2luZF9kdXBsaWMsXQ0Kc3VtKGR1cGxpY2F0ZWQuZGF0YS5mcmFtZShnZW8pKSANCiMgcmV0YWluZyB1bmlxdWUgcG9zdGFsIGNpdGllcw0KZ2VvIDwtIGdlb1shZHVwbGljYXRlZChnZW8kQ09ER0VPKSxdDQoNCiMgY2hlY2sgYWdhaW4NCnN1bW1hcnkoZ2VvKQ0KDQpgYGANCg0KQXNzaWduIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdmFsdWVzIGZvciBtaXNzaW5nIGRhdGEgKGFsbW9zdCAzMDAwKS4NClRoZSBjb2RlIHVzZWQgdG8gcmV0cmlldmUgdGhlbSB1c2luZyBHb29nbGUgQVBJIGhhcyBiZWVuIGNvbW1lbnRlZCBhbmQgaXRzIHJlc3VsdCBpcyBsb2FkZWQuDQpgYGB7ciBhc3NpZ24gTkEgaW4gZ2VvLCB3YXJuaW5nPUZBTFNFfQ0KDQojIGluZGV4IG9mIE5BcyBhbmQgdGhlaXIgdG90YWwNCmluZE5BX2Nvb3JkID0gaXMubmEoZ2VvJGxhdGl0dWRlKSB8IGlzLm5hKGdlbyRsb25naXR1ZGUpDQpzdW0oaW5kTkFfY29vcmQpDQoNCiMgY29kZSB1c2VkIHRvIHJldHJpZXZlIHRoZSBOQSB1c2luZyBHb29nbGUgQVBJLCB3aGljaCBoYXZlIGJlZW4gc2F2ZWQgaW4gYSBjc3YgZmlsZQ0KIyANCiMgIyBpbml0aWFsaXplIHZhcmlhYmxlcw0KIyBjaXR5X3NlYXJjaCA9IDANCiMgcmVzID0gYXMuZGF0YS5mcmFtZShtYXRyaXgoYygwLCAwLCAwKSwgMSwgMykpDQojIG5hbWVzKHJlcykgPSBjKCJsb24iLCAibGF0IiwgImFkZHJlc3MiKQ0KIyANCiMgIyByZXRyaWV2ZSBsYXQgYW5kIGxvbmcgKEdvb2dsZSBBUEkgPSAyNTAwIHJlcXVlc3QgcGVyIGRheSkNCiMgIyBteV9pdGVyID0gZmxvb3Ioc3VtKGluZE5BX2Nvb3JkKS8zKQ0KIyBmb3IgKGkgaW4gMTpzdW0oaW5kTkFfY29vcmQpKXsNCiMgDQojICAgIyBjaXR5IHNlYXJjaGVkDQojICAgY2l0eV9zZWFyY2hbaV0gPSBwYXN0ZShjKGFzLmNoYXJhY3RlcihOQV9jb29yZCR0b3duX25hbWVbaV0pLCBhcy5jaGFyYWN0ZXIoTkFfY29vcmQkcG9zdGFsX2NvZGVbaV0pLCBhcy5jaGFyYWN0ZXIoTkFfY29vcmQkZGVwYXJ0bWVudFtpXSksICJGcmFuY2UiKSwgc2VwPSIgIiwgY29sbGFwc2UgPSAiLCAiKQ0KIyAgIA0KIyAgICMgc29sdXRpb24NCiMgICByZXNbaSxdID0gZ2VvY29kZShjaXR5X3NlYXJjaFtpXSwgb3V0cHV0ID0gImxhdGxvbmEiLCBzb3VyY2UgPSBjKCJnb29nbGUiLCAiZHNrIiksIG1lc3NhZ2luZyA9IEZBTFNFKQ0KIyANCiMgICAjIHJldHJpZXZlIHN0aWxsIG1pc3NpbmcgZGF0YSwgYmVjYXVzZSBvZiBleGlzdGluZyBwcm9ibGVtcyB3aXRoIEFQSSAodXAgdG8gMTUgdHJpYWxzKQ0KIyAgIGogPSAwDQojICAgd2hpbGUgKGFueShpcy5uYShyZXNbaSxdKSkgJiBqIDwgMjUpew0KIyAgICAgcmVzW2ksXSA9IGdlb2NvZGUoY2l0eV9zZWFyY2hbaV0sIG91dHB1dCA9ICJsYXRsb25hIiwgc291cmNlID0gYygiZ29vZ2xlIiwgImRzayIpLCBtZXNzYWdpbmcgPSBGQUxTRSkNCiMgICAgIGogPSBqICsgMQ0KIyAgIH0NCiMgfQ0KDQojICMgY2hlY2sgdGhlIHNvbHV0aW9uDQojIHNvbCA9IGNiaW5kKHNlYXJjaGVkID0gY2l0eV9zZWFyY2gsIHJlcykNCg0KIyAjIHNhdmUgaXQgYXMgYSBjc3YgZmlsZSB0byBzYXZlIHRpbWUNCiMgd3JpdGUuY3N2KHJldHJpZXZlZF9nZW9fTkFbLDI6M10sICJnZW9fTkFfRmluYWwuY3N2IiwgcXVvdGUgPSBGQUxTRSwgcm93Lm5hbWVzPUZBTFNFLCBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KICAgIA0KDQojIHJlYWQgdGhlIGNyZWF0ZWQgY3N2DQpzZXR3ZCgiLi9kYXRhIikNCnJldHJpZXZlZF9nZW9fTkEgPSByZWFkLmNzdigiZ2VvX05BX0ZpbmFsLmNzdiIsIGhlYWRlciA9IFQsIGVuY29kaW5nID0gIlVURi04IikNCiMgZ2V0IG9ubHkgbG9uZyBhbmQgbGF0IGFuZCBhc3NpZ24gdG8gb3JpZ2luYWwgTkEgDQpnZW8kbGF0aXR1ZGVbaW5kTkFfY29vcmRdID0gcmV0cmlldmVkX2dlb19OQVssMl0NCmdlbyRsb25naXR1ZGVbaW5kTkFfY29vcmRdID0gcmV0cmlldmVkX2dlb19OQVssMV0NCg0KIyB0aGVyZSBhcmUgMzcgc3RpbGwgbWlzc2luZyB1bml0cywgd2hpY2ggYXJlIHRvd25zIGxvY2F0ZWQgaW4gb2xkIGNvbG9uaWVzIGZhciBmcm9tIEV1cm9wZQ0KaW5kTkFfY29vcmQgPSBpcy5uYShnZW8kbGF0aXR1ZGUpIHwgaXMubmEoZ2VvJGxvbmdpdHVkZSkNCnN1bShpbmROQV9jb29yZCkNCiMgZXhjbHVkZSB0aG9zZSB0b3ducw0KZ2VvID0gZ2VvWyFpbmROQV9jb29yZCxdDQoNCnN1bW1hcnkoZ2VvKQ0KDQpgYGANCg0KUmVtb3ZlIERPTS1UT00gdG93bnMgKGkuZS4gb2xkIGNvbG9uaWVzIGZhciBmcm9tIEV1cm9wZSkNCmBgYHtyfQ0KDQojIGRlbGV0ZSBub24tRXVyb3BlYW4gY291bnRyaWVzDQppbmRfbm9uRXVyID0gZ2VvJGxhdGl0dWRlIDwgMzAgfCBnZW8kbGF0aXR1ZGUgPiA3MCB8Z2VvJGxvbmdpdHVkZSA8IC0yMCB8IGdlbyRsb25naXR1ZGUgPiAyMA0Kc3VtKGluZF9ub25FdXIpDQpnZW8gPSBnZW9bIWluZF9ub25FdXIsXQ0KDQpgYGANCg0KDQojIyBTYWxhcnkgZGF0YSANCg0KQXNzaWduIG1lYW5pbmdmdWwgbmFtZXMgYW5kIGNoZWNrIHRoZSBkYXRhOg0KYGBge3IgcHJlIHByb2Nlc3Npbmcgc2FsYXJ5fQ0KDQpuYW1lcyhzYWxhcnkpDQpuYW1lcyhzYWxhcnkpWzI6bmNvbChzYWxhcnkpXSA8LQ0KICBjKCJ0b3duIiwNCiAgICAic2FsX2dlbmVyYWwiLCAgICANCiAgICAic2FsX2V4ZWN1dGl2ZSIsDQogICAgInNhbF9taWRNYW5hZ2VyIiwNCiAgICAic2FsX2VtcGxveWVlIiwNCiAgICAic2FsX3dvcmtlciIsDQogICAgInNhbF9GZW1hbGVzIiwNCiAgICAic2FsX0ZfZXhlY3V0aXZlIiwNCiAgICAic2FsX0ZfbWlkTWFuYWdlciIsDQogICAgInNhbF9GX2VtcGxveWVlIiwNCiAgICAic2FsX0Zfd29ya2VyIiwNCiAgICAic2FsX01hbGVzIiwNCiAgICAic2FsX01fZXhlY3V0aXZlIiwNCiAgICAic2FsX01fbWlkTWFuYWdlciIsDQogICAgInNhbF9NX2VtcGxveWVlIiwNCiAgICAic2FsX01fd29ya2VyIiwNCiAgICAic2FsXzE4XzI1IiwNCiAgICAic2FsXzI2XzUwIiwNCiAgICAic2FsXzUxcGx1cyIsDQogICAgInNhbF9GXzE4XzI1IiwNCiAgICAic2FsX0ZfMjZfNTAiLA0KICAgICJzYWxfRl81MXBsdXMiLA0KICAgICJzYWxfTV8xOF8yNSIsDQogICAgInNhbF9NXzI2XzUwIiwNCiAgICAic2FsX01fNTFwbHVzIikNCg0KIyBwcmVsaW1pbmFyeSBjaGVja3MNCm5hbWVzKHNhbGFyeSkNCmhlYWQoc2FsYXJ5KQ0Kc3RyKHNhbGFyeSkNCnN1bW1hcnkoc2FsYXJ5KQ0KDQojIENoZWNrIGZvciBkdXBsaWNhdGVkIGRhdGE6IHRoZXJlIGFyZSBubw0Kc3VtKGR1cGxpY2F0ZWQuZGF0YS5mcmFtZShzYWxhcnkpKQ0KDQojIGRyb3AgdW5uZWNlc3NhcnkgdmFyaWFibGUNCnNhbGFyeSA8LXN1YnNldChzYWxhcnksIHNlbGVjdCA9IC1jKHRvd24pKQ0KDQpgYGANCg0KIyMgUG9wdWxhdGlvbiBkYXRhIA0KDQpSZW5hbWUgdGhlIHZhcmlhYmxlcyBmb3IgcG9wdWxhdGlvbiBhbmQgZXhjbHVkZSB0aGUgdW5uZWNlc3Nhcnkgb25lczoNCmBgYHtyIHByZSBwcm9jZXNzaW5nIHBvcHVsYXRpb259DQoNCiNuYW1lcyhwb3B1bGF0aW9uKQ0KbmFtZXMocG9wdWxhdGlvbilbNTo3XSA8LQ0KICBjKCJhZ2VDYXRlZzUiLA0KICAgICJzZXgiLA0KICAgICJwZW9wbGVDYXRlZ051bSIpDQoNCiMgZHJvcCB1bm5lY2Vzc2FyeSBjb2x1bW5zIChOSVZHRU8gaXMgdGhlIHNhbWUgZm9yIGFsbCkNCnBvcHVsYXRpb24gPC0gc3Vic2V0KHBvcHVsYXRpb24sIHNlbGVjdCA9IC1jKE5JVkdFTywgTElCR0VPKSkNCg0KIyBSZWZhY3RvciBzZXggYW5kIE1PQ08NCnBvcHVsYXRpb24kTU9DTyA8LSBmYWN0b3IocG9wdWxhdGlvbiRNT0NPLCBsZXZlbHMgPSBjKDExLDEyLDIxLDIyLDIzLDMxLDMyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiY2hpbGRyZW5fbGl2aW5nX3dpdGhfdHdvX3BhcmVudHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hpbGRyZW5fbGl2aW5nX3dpdGhfb25lX3BhcmVudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFkdWx0c19saXZpbmdfaW5fY291cGxlX3dpdGhvdXRfY2hpbGQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZHVsdHNfbGl2aW5nX2luX2NvdXBsZV93aXRoX2NoaWxkcmVuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWR1bHRzX2xpdmluZ19hbG9uZV93aXRoX2NoaWxkcmVuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVyc29uc19ub3RfZnJvbV9mYW1pbHlfbGl2aW5nX2luX3RoZV9ob21lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVyc29uc19saXZpbmdfYWxvbmUiKSkNCnBvcHVsYXRpb24kc2V4IDwtIGZhY3Rvcihwb3B1bGF0aW9uJHNleCwgbGV2ZWxzID0gYygxLDIpLCBsYWJlbHMgPSBjKCJNYWxlIiwgIkZlbWFsZSIpKQ0KDQojIGNoZWNrIGFnYWluDQpzdW1tYXJ5KHBvcHVsYXRpb24pDQoNCmBgYA0KDQpVbmRlcnN0YW5kIHRoZSBkaXN0cmlidXRpb24gb2YgcG9wdWxhdGlvbiBhY2NvcmRpbmcgdG8gZGlmZmVyZW50IGFnZSBjYXRlZ29yaWVzIHVzaW5nIHBvcHVsYXRpb24gcHlyYW1pZGUNCihwZXJmb3JtZWQgbm93IGJlY2F1c2UgbGF0ZXIgdGhlIGRhdGFzZXQgd2lsbCBiZSBtb2RpZmllZCk6DQpgYGB7ciBQb3B1bGF0aW9uIHB5cmFtaWRlfQ0KDQojIG5lZWQgdGhlIGluaXRpYWwgc2hhcGUgb2YgZGF0YSB0byBjb25zdHJ1Y3QgdGhlIHB5cmFtaWRlIA0KIyBQb3B1bGF0aW9uIHB5cmFtaWRlDQpwb3B1bGF0aW9uX2RhdGEyIDwtIGRkcGx5KHBvcHVsYXRpb24sIC4oc2V4LCBhZ2VDYXRlZzUpLCBmdW5jdGlvbihwb3B1bGF0aW9uKSB7DQogIGRhdGEuZnJhbWUodG90YWxfcG9wdWxhdGlvbiA9IHN1bShwb3B1bGF0aW9uJHBlb3BsZUNhdGVnTnVtKSkNCiAgfSkNCnBvcF9weXJhbWlkIDwtIGdncGxvdChkYXRhID0gcG9wdWxhdGlvbl9kYXRhMiwNCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBhZ2VDYXRlZzUsIGZpbGwgPSBzZXgsDQogICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHRlc3QgPSBzZXggPT0gIk1hbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAtdG90YWxfcG9wdWxhdGlvbiwgbm8gPSB0b3RhbF9wb3B1bGF0aW9uKSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGFicywgbGltaXRzID0gbWF4KHBvcHVsYXRpb25fZGF0YTIkdG90YWxfcG9wdWxhdGlvbikgKiBjKC0xLDEpKSArIA0KICBnZ3RpdGxlKCJQeXJhbWlkIG9mIFBvcHVsYXRpb24iKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIA0KICBsYWJzKHg9ICJBZ2UiKSsgbGFicyh5ID0gIlBvcHVsYXRpb24iKSArIGNvb3JkX2ZsaXAoKSAjICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikNCnBvcF9weXJhbWlkDQoNCmBgYA0KDQpSZS1vcmdhbml6ZSBwb3B1bGF0aW9uIGRhdGFzZXQgYW5kIHNldCBDT0RHRU8gYXMgdW5pdHMgYW5kIGNyZWF0ZSBuZXcgdmFyaWFibGVzIHVzaW5nIHRoZSBhdmFpbGFibGUgdmFyaWFibGVzLg0KYGBge3IgUmUtb3JnYW5pemUgcG9wdWxhdGlvbiBkYXRhc2V0IGJ5IENPREdFT30NCg0KIyBSZS1vcmdhbml6ZSB0aGUgZGF0YSBieSBjcmVhdGluZyBuZXcgdmFyaWFibGVzOiAidG90YWwgcG9wdWxhdGlvbiIsICJtYWxlIiwgImZlbWFsZSIsICJjaGlsZCIsICJlbGRlcmx5IiBhbmQgIndvcmtmb3JjZSIuDQpwb3B1bGF0aW9uIDwtIGRkcGx5KHBvcHVsYXRpb24sIC4oQ09ER0VPKSwgZnVuY3Rpb24ocG9wdWxhdGlvbikgew0KICBkYXRhLmZyYW1lKHRvdGFsX3BvcHVsYXRpb24gPSBzdW0ocG9wdWxhdGlvbiRwZW9wbGVDYXRlZ051bSksDQogICAgICAgICAgICAgbWFsZSA9IHN1bShwb3B1bGF0aW9uW3BvcHVsYXRpb24kc2V4ID09ICJNYWxlIixdJHBlb3BsZUNhdGVnTnVtKSwNCiAgICAgICAgICAgICBmZW1hbGUgPSBzdW0ocG9wdWxhdGlvbltwb3B1bGF0aW9uJHNleCA9PSAiRmVtYWxlIixdJHBlb3BsZUNhdGVnTnVtKSwNCiAgICAgICAgICAgICBjaGlsZCA9IHN1bShwb3B1bGF0aW9uW3BvcHVsYXRpb24kYWdlQ2F0ZWc1ICVpbiUgc2VxKDAsIDEwLCBieT01KSxdJHBlb3BsZUNhdGVnTnVtKSwNCiAgICAgICAgICAgICBlbGRlcmx5ID0gc3VtKHBvcHVsYXRpb25bcG9wdWxhdGlvbiRhZ2VDYXRlZzUgJWluJSBzZXEoNjUsIDgwLCBieT01KSxdJHBlb3BsZUNhdGVnTnVtKSwNCiAgICAgICAgICAgICB3b3JrZm9yY2UgPSBzdW0ocG9wdWxhdGlvbltwb3B1bGF0aW9uJGFnZUNhdGVnNSAlaW4lIHNlcSgxNSwgNjAsIGJ5PTUpLF0kcGVvcGxlQ2F0ZWdOdW0pIA0KICApfSkNCg0KIyBDYWxjdWxhdGUgcmF0aW9zIHVzaW5nIHRoZSBleGlzdGluZyB2YXJpYWJsZXMNCnBvcHVsYXRpb24kZGVwZW5kZW50IDwtIHBvcHVsYXRpb24kY2hpbGQgKyBwb3B1bGF0aW9uJGVsZGVybHkNCnBvcHVsYXRpb24kc2V4X3JhdGlvIDwtIGlmZWxzZShwb3B1bGF0aW9uJGZlbWFsZT09MCwgMCwgcG9wdWxhdGlvbiRtYWxlIC8gcG9wdWxhdGlvbiRmZW1hbGUpDQpwb3B1bGF0aW9uJGRlcGVuZGVuY3lfcmF0aW8gPC0gaWZlbHNlKHBvcHVsYXRpb24kd29ya2ZvcmNlPT0wLCAwLCBwb3B1bGF0aW9uJGRlcGVuZGVudCAvIHBvcHVsYXRpb24kd29ya2ZvcmNlKQ0KcG9wdWxhdGlvbiRhZ2VkX2RlcGVuZGVuY3lfcmF0aW8gPC0gaWZlbHNlKHBvcHVsYXRpb24kd29ya2ZvcmNlPT0wLCAwLCBwb3B1bGF0aW9uJGVsZGVybHkgLyBwb3B1bGF0aW9uJHdvcmtmb3JjZSkNCnBvcHVsYXRpb24kY2hpbGRfZGVwZW5kZW5jeV9yYXRpbyA8LSBpZmVsc2UocG9wdWxhdGlvbiR3b3JrZm9yY2U9PTAsIDAsIHBvcHVsYXRpb24kY2hpbGQgLyBwb3B1bGF0aW9uJHdvcmtmb3JjZSkNCg0KIyB0b3RhbCBwb3B1bGF0aW9uPTY1bWxuLCB3aGljaCBpcyByZWFzb25hYmxlDQpzdW0ocG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uKQ0KDQojIGNyZWF0ZSBkdW1teSB2YXJpYWJsZSBmb3IgdXJiYW4gY2l0eQ0KIyB0YWtpbmcgdGhlIHRvcCAyMCUgaW4gdGVybXMgb2YgcG9wdWxhdGlvbg0KcG9wdWxhdGlvbiR1cmJhbkR1bW15ID0gcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uID4gcXVhbnRpbGUocG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uLCAwLjkwKQ0KDQpgYGANCg0KIyMgRWR1Y2F0aW9uIGRhdGENCg0KQXNzaWduIG1lYW5pbmdmdWwgbmFtZXMgYW5kIGJ1aWxkIHNvbWUgdXNlZnVsIGluZGljYXRvcnM6DQpgYGB7cn0NCg0KbmFtZXMoZWR1YylbMzpuY29sKGVkdWMpXSA8LQ0KICBjKCJBZ2UxNV9Ob0RpcF9NIiwiQWdlMTVfTm9EaXBfRiIsICJBZ2UxNV9TZWNfTSIsICJBZ2UxNV9TZWNfRiIsICJBZ2UxNV9IaV9NIiwiQWdlMTVfSGlfRiIsICJBZ2UxNV9Vbml2X00iLA0KICAgICJBZ2UxNV9Vbml2X0YiLCAiQWdlMjBfTm9EaXBfTSIsICJBZ2UyMF9Ob0RpcF9GIiwgIkFnZTIwX1NlY19NIiwiQWdlMjBfU2VjX0YiLCAiQWdlMjBfSGlfTSIsICJBZ2UyMF9IaV9GIiwNCiAgICAiQWdlMjBfVW5pdl9NIiwgIkFnZTIwX1VuaXZfRiIsIkFnZTI1X05vRGlwX00iLCAiQWdlMjVfTm9EaXBfRiIsIkFnZTI1X1NlY19NIiwiQWdlMjVfU2VjX0YiLCAiQWdlMjVfSGlfTSIsDQogICAgIkFnZTI1X0hpX0YiLCJBZ2UyNV9Vbml2X00iLCJBZ2UyNV9Vbml2X0YiLCAiQWdlMzBfTm9EaXBfTSIsICJBZ2UzMF9Ob0RpcF9GIiwgIkFnZTMwX1NlY19NIiwgIkFnZTMwX1NlY19GIiwNCiAgICAiQWdlMzBfSGlfTSIsICJBZ2UzMF9IaV9GIiwiQWdlMzBfVW5pdl9NIiwiQWdlMzBfVW5pdl9GIiwgIkFnZTM1X05vRGlwX00iLCAiQWdlMzVfTm9EaXBfRiIsICJBZ2UzNV9TZWNfTSIsDQogICAgIkFnZTM1X1NlY19GIiwgIkFnZTM1X0hpX00iLCAiQWdlMzVfSGlfRiIsICJBZ2UzNV9Vbml2X00iLCJBZ2UzNV9Vbml2X0YiLCAiQWdlNDBfTm9EaXBfTSIsICJBZ2U0MF9Ob0RpcF9GIiwNCiAgICAiQWdlNDBfU2VjX00iLCJBZ2U0MF9TZWNfRiIsICJBZ2U0MF9IaV9NIiwgIkFnZTQwX0hpX0YiLCAiQWdlNDBfVW5pdl9NIiwiQWdlNDBfVW5pdl9GIiwgIkFnZTQ1X05vRGlwX00iLA0KICAgICJBZ2U0NV9Ob0RpcF9GIiwiQWdlNDVfU2VjX00iLCJBZ2U0NV9TZWNfRiIsICJBZ2U0NV9IaV9NIiwgIkFnZTQ1X0hpX0YiLCAiQWdlNDVfVW5pdl9NIiwiQWdlNDVfVW5pdl9GIiwNCiAgICAiQWdlNTBfTm9EaXBfTSIsICJBZ2U1MF9Ob0RpcF9GIiwiQWdlNTBfU2VjX00iLCJBZ2U1MF9TZWNfRiIsICJBZ2U1MF9IaV9NIiwgIkFnZTUwX0hpX0YiLCAiQWdlNTBfVW5pdl9NIiwNCiAgICAiQWdlNTBfVW5pdl9GIiwgIkFnZTU1X05vRGlwX00iLCAiQWdlNTVfTm9EaXBfRiIsIkFnZTU1X1NlY19NIiwgIkFnZTU1X1NlY19GIiwgIkFnZTU1X0hpX00iLCAiQWdlNTVfSGlfRiIsDQogICAgIkFnZTU1X1VuaXZfTSIsICJBZ2U1NV9Vbml2X0YiLCAiQWdlNjBfTm9EaXBfTSIsICJBZ2U2MF9Ob0RpcF9GIiwiQWdlNjBfU2VjX00iLCJBZ2U2MF9TZWNfRiIsICJBZ2U2MF9IaV9NIiwNCiAgICAiQWdlNjBfSGlfRiIsICJBZ2U2MF9Vbml2X00iLCAiQWdlNjBfVW5pdl9GIiwgIkFnZTY1X05vRGlwX00iLCAiQWdlNjVfTm9EaXBfRiIsIkFnZTY1X1NlY19NIiwiQWdlNjVfU2VjX0YiLA0KICAgICJBZ2U2NV9IaV9NIiwgIkFnZTY1X0hpX0YiLCAiQWdlNjVfVW5pdl9NIiwgIkFnZTY1X1VuaXZfRiIpDQoNCiMgQWdncmVnYXRlIHZhcmlhYmxlcyBieSBncm91cGluZyBhbGwgdGhlIGVkdWNhdGlvbiBsZXZlbCBjYXRlZ29yaWVzIGJ5IGdlbmRlcg0KIyB0b3RhbCBudW1iZXIgb2YgZmVtYWxlcyB3aXRoIG5vIGRpcGxvbWENCmVkdWMkZmVtYWxlX05vRGlwIDwtIHJvd1N1bXMoY2JpbmQoZWR1YyRBZ2UxNV9Ob0RpcF9GLCBlZHVjJEFnZTIwX05vRGlwX0YsIGVkdWMkQWdlMjVfTm9EaXBfRiwgZWR1YyRBZ2UzMF9Ob0RpcF9GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZHVjJEFnZTM1X05vRGlwX0YsZWR1YyRBZ2U0MF9Ob0RpcF9GLGVkdWMkQWdlNDVfTm9EaXBfRiwgZWR1YyRBZ2U1MF9Ob0RpcF9GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZHVjJEFnZTU1X05vRGlwX0YsIGVkdWMkQWdlNjBfTm9EaXBfRiwgZWR1YyRBZ2U2NV9Ob0RpcF9GKSkNCg0KIyB0b3RhbCBudW1iZXIgb2YgbWFsZXMgd2l0aCBubyBkaXBsb21hDQplZHVjJG1hbGVfTm9EaXAgPC0gcm93U3VtcyhjYmluZChlZHVjJEFnZTE1X05vRGlwX00sIGVkdWMkQWdlMjBfTm9EaXBfTSwgZWR1YyRBZ2UyNV9Ob0RpcF9NLCBlZHVjJEFnZTMwX05vRGlwX00sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZHVjJEFnZTM1X05vRGlwX00sZWR1YyRBZ2U0MF9Ob0RpcF9NLGVkdWMkQWdlNDVfTm9EaXBfTSwgZWR1YyRBZ2U1MF9Ob0RpcF9NLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2U1NV9Ob0RpcF9NLCBlZHVjJEFnZTYwX05vRGlwX00sIGVkdWMkQWdlNjVfTm9EaXBfTSkpDQoNCiMgdG90YWwgbnVtYmVyIG9mIGZlbWFsZXMgd2l0aCBzZWNvbmRhcnkgZWR1Y2F0aW9uIGxldmVsDQplZHVjJGZlbWFsZV9TZWMgPC0gcm93U3VtcyhjYmluZChlZHVjJEFnZTE1X1NlY19GLCBlZHVjJEFnZTIwX1NlY19GLCBlZHVjJEFnZTI1X1NlY19GLCBlZHVjJEFnZTMwX1NlY19GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2UzNV9TZWNfRixlZHVjJEFnZTQwX1NlY19GLGVkdWMkQWdlNDVfU2VjX0YsIGVkdWMkQWdlNTBfU2VjX0YsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2U1NV9TZWNfRiwgZWR1YyRBZ2U2MF9TZWNfRiwgZWR1YyRBZ2U2NV9TZWNfRikpDQoNCiMgdG90YWwgbnVtYmVyIG9mIG1hbGVzIHdpdGggc2Vjb25kYXJ5IGVkdWNhdGlvbiBsZXZlbA0KZWR1YyRtYWxlX1NlYyA8LSByb3dTdW1zKGNiaW5kKGVkdWMkQWdlMTVfU2VjX00sIGVkdWMkQWdlMjBfU2VjX00sIGVkdWMkQWdlMjVfU2VjX00sIGVkdWMkQWdlMzBfU2VjX00sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2UzNV9TZWNfTSxlZHVjJEFnZTQwX1NlY19NLGVkdWMkQWdlNDVfU2VjX00sIGVkdWMkQWdlNTBfU2VjX00sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkdWMkQWdlNTVfU2VjX00sIGVkdWMkQWdlNjBfU2VjX00sIGVkdWMkQWdlNjVfU2VjX00pKQ0KDQojIHRvdGFsIG51bWJlciBvZiBmZW1hbGVzIHdpdGggaGlnaC1zY2hvb2wgZWR1Y2F0aW9uIGxldmVsDQplZHVjJGZlbWFsZV9IaSA8LSByb3dTdW1zKGNiaW5kKGVkdWMkQWdlMTVfSGlfRiwgZWR1YyRBZ2UyMF9IaV9GLCBlZHVjJEFnZTI1X0hpX0YsIGVkdWMkQWdlMzBfSGlfRiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2UzNV9IaV9GLGVkdWMkQWdlNDBfSGlfRixlZHVjJEFnZTQ1X0hpX0YsIGVkdWMkQWdlNTBfSGlfRiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkdWMkQWdlNTVfSGlfRiwgZWR1YyRBZ2U2MF9IaV9GLCBlZHVjJEFnZTY1X0hpX0YpKQ0KDQojIHRvdGFsIG51bWJlciBvZiBtYWxlcyB3aXRoIGhpZ2gtc2Nob29sIGVkdWNhdGlvbiBsZXZlbA0KZWR1YyRtYWxlX0hpIDwtIHJvd1N1bXMoY2JpbmQoZWR1YyRBZ2UxNV9IaV9NLCBlZHVjJEFnZTIwX0hpX00sIGVkdWMkQWdlMjVfSGlfTSwgZWR1YyRBZ2UzMF9IaV9NLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2UzNV9IaV9NLGVkdWMkQWdlNDBfSGlfTSxlZHVjJEFnZTQ1X0hpX00sIGVkdWMkQWdlNTBfSGlfTSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZHVjJEFnZTU1X0hpX00sIGVkdWMkQWdlNjBfSGlfTSwgZWR1YyRBZ2U2NV9IaV9NKSkNCg0KIyB0b3RhbCBudW1iZXIgb2YgZmVtYWxlcyB3aXRoIHVuaXZlcnNpdHkgZGVncmVlDQplZHVjJGZlbWFsZV9Vbml2IDwtIHJvd1N1bXMoY2JpbmQoZWR1YyRBZ2UxNV9Vbml2X0YsIGVkdWMkQWdlMjBfVW5pdl9GLCBlZHVjJEFnZTI1X1VuaXZfRiwgZWR1YyRBZ2UzMF9Vbml2X0YsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWR1YyRBZ2UzNV9Vbml2X0YsZWR1YyRBZ2U0MF9Vbml2X0YsZWR1YyRBZ2U0NV9Vbml2X0YsIGVkdWMkQWdlNTBfVW5pdl9GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkdWMkQWdlNTVfVW5pdl9GLCBlZHVjJEFnZTYwX1VuaXZfRiwgZWR1YyRBZ2U2NV9Vbml2X0YpKQ0KDQojIHRvdGFsIG51bWJlciBvZiBtYWxlcyB3aXRoIHVuaXZlcnNpdHkgZGVncmVlDQplZHVjJG1hbGVfVW5pdiA8LSByb3dTdW1zKGNiaW5kKGVkdWMkQWdlMTVfVW5pdl9NLCBlZHVjJEFnZTIwX1VuaXZfTSwgZWR1YyRBZ2UyNV9Vbml2X00sIGVkdWMkQWdlMzBfVW5pdl9NLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZHVjJEFnZTM1X1VuaXZfTSxlZHVjJEFnZTQwX1VuaXZfTSxlZHVjJEFnZTQ1X1VuaXZfTSwgZWR1YyRBZ2U1MF9Vbml2X00sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkdWMkQWdlNTVfVW5pdl9NLCBlZHVjJEFnZTYwX1VuaXZfTSwgZWR1YyRBZ2U2NV9Vbml2X00pKQ0KDQojIGRyb3AgYWxsIHRoZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMgDQplZHVjIDwtIHN1YnNldChlZHVjLCBzZWxlY3Q9IGMoQ09ER0VPLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlbWFsZV9Ob0RpcCwgbWFsZV9Ob0RpcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVtYWxlX1NlYywgbWFsZV9TZWMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlbWFsZV9IaSwgbWFsZV9IaSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVtYWxlX1VuaXYsIG1hbGVfVW5pdikpDQoNCiMgdG90YWwgbnVtYmVyIG9mIHBvcHVsYXRpb24gd2l0aG91dCBhIGRpcGxvbWENCmVkdWMkbm9kaXAgPC1yb3dTdW1zKGNiaW5kKGVkdWMkZmVtYWxlX05vRGlwLCBlZHVjJG1hbGVfTm9EaXApKQ0KDQojIHRvdGFsIG51bWJlciBvZiBwb3B1bGF0aW9uIHdpdGggc2Vjb25kYXJ5IGxldmVsIG9mIGVkdWNhdGlvbg0KZWR1YyRzZWMgPC1yb3dTdW1zKGNiaW5kKGVkdWMkZmVtYWxlX1NlYywgZWR1YyRtYWxlX05vRGlwKSkNCg0KIyB0b3RhbCBudW1iZXIgb2YgcG9wdWxhdGlvbiB3aXRoIGhpZ2hzY2hvb2wgZGlwbG9tYQ0KZWR1YyRoaWdoIDwtcm93U3VtcyhjYmluZChlZHVjJGZlbWFsZV9IaSwgZWR1YyRtYWxlX0hpKSkNCg0KIyB0b3RhbCBudW1iZXIgb2YgcG9vcHVsYXRpb24gd2l0aCB1bml2ZXJzaXR5IGRpcGxvbWENCmVkdWMkdW5pdiA8LSByb3dTdW1zKGNiaW5kKGVkdWMkZmVtYWxlX1VuaXYsIGVkdWMkbWFsZV9Vbml2KSkNCg0KYGBgDQoNCg0KIyMgRGVtb2dyYXBoaWMvc29jaWFsIHByb2ZpbGVzIGRhdGENCg0KUmVuYW1lIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgDQpgYGB7ciBwcmUtcHJvY2Vzc2luZyBkZW1vZ3JhcGhpYy9zb2NpYWwgcHJvZmlsZXMgZGF0YX0NCg0KIyByZW5hbWUgdmFyaWFibGVzDQpuYW1lcyhjYXRlZ19zb2NpbylbMzpuY29sKGNhdGVnX3NvY2lvKV0gPC0NCiAgYygiTV9pbW1pX2FncmkiLA0KICAgICJGX2ltbWlfYWdyaSIsDQogICAgIk1fTm9JbW1pX2FncmkiLA0KICAgICJGX05vSW1taV9hZ3JpIiwNCiAgICAiTV9pbW1pX2NvbW0iLA0KICAgICJGX2ltbWlfY29tbSIsDQogICAgIk1fTm9JbW1pX2NvbW0iLA0KICAgICJGX05vSW1taV9jb21tIiwNCiAgICAiTV9pbW1pX2V4ZWMiLA0KICAgICJGX2ltbWlfZXhlYyIsDQogICAgIk1fTm9JbW1pX2V4ZWMiLA0KICAgICJGX05vSW1taV9leGVjIiwNCiAgICAiTV9pbW1pX21pZG1hbiIsDQogICAgIkZfaW1taV9taWRtYW4iLA0KICAgICJNX05vSW1taV9taWRtYW4iLA0KICAgICJGX05vSW1taV9taWRtYW4iLA0KICAgICJNX2ltbWlfZW1wIiwNCiAgICAiRl9pbW1pX2VtcCIsDQogICAgIk1fTm9JbW1pX2VtcCIsDQogICAgIkZfTm9JbW1pX2VtcCIsDQogICAgIk1faW1taV93b3JrZXIiLA0KICAgICJGX2ltbWlfd29ya2VyIiwNCiAgICAiTV9Ob0ltbWlfd29ya2VyIiwNCiAgICAiRl9Ob0ltbWlfd29ya2VyIiwNCiAgICAiTV9pbW1pX3JldGlyZWQiLA0KICAgICJGX2ltbWlfcmV0aXJlZCIsDQogICAgIk1fTm9JbW1pX3JldGlyZWQiLA0KICAgICJGX05vSW1taV9yZXRpcmVkIiwNCiAgICAiTV9pbW1pX25vQWN0IiwNCiAgICAiRl9pbW1pX25vQWN0IiwNCiAgICAiTV9Ob0ltbWlfbm9BY3QiLA0KICAgICJGX05vSW1taV9ub0FjdCIpDQoNCiMgZHJvcCBMSUJHRU8NCmNhdGVnX3NvY2lvIDwtIHN1YnNldChjYXRlZ19zb2Npbywgc2VsZWN0PS1jKExJQkdFTykpDQoNCmBgYA0KDQpDcmVhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBpbW1pZ3JhbnRzIGFuZCBuYXRpdmVzIHBlciB0b3duLCBhbHNvIHNlcGFyZXRlZCBieSBnZW5kZXIuDQpgYGB7ciBDYXRlZ19zb2Npby1yZW5hbWUgdmFyaWFibGVzIH0NCg0KIyB0b3RhbCBudW1iZXIgb2YgbWFsZSBpbW1pZ3JhbnRzIHBlciB0b3duIA0KY2F0ZWdfc29jaW8kbWFsZV9pbW1pZyA8LXJvd1N1bXMoY2JpbmQoY2F0ZWdfc29jaW8kTV9pbW1pX2FncmksIGNhdGVnX3NvY2lvJE1faW1taV9jb21tLCBjYXRlZ19zb2NpbyRNX2ltbWlfZW1wLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ19zb2NpbyRNX2ltbWlfZXhlYywgY2F0ZWdfc29jaW8kTV9pbW1pX21pZG1hbixjYXRlZ19zb2NpbyRNX2ltbWlfbm9BY3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdfc29jaW8kTV9pbW1pX3JldGlyZWQsIGNhdGVnX3NvY2lvJE1faW1taV93b3JrZXIpKQ0KIA0KIyB0b3RhbCBudW1iZXIgb2YgZmVtYWxlIGltbWlncmFudHMgcGVyIHRvd24NCmNhdGVnX3NvY2lvJGZlbWFsZV9pbW1pZyA8LSByb3dTdW1zKGNiaW5kKGNhdGVnX3NvY2lvJEZfaW1taV9hZ3JpLGNhdGVnX3NvY2lvJEZfaW1taV9jb21tLCBjYXRlZ19zb2NpbyRGX2ltbWlfZW1wLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnX3NvY2lvJEZfaW1taV9leGVjLCBjYXRlZ19zb2NpbyRGX2ltbWlfbWlkbWFuLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnX3NvY2lvJEZfaW1taV9ub0FjdCwgY2F0ZWdfc29jaW8kRl9pbW1pX3JldGlyZWQsIGNhdGVnX3NvY2lvJEZfaW1taV93b3JrZXIpKQ0KDQojIHRvdGFsIG51bWJlciBpbW1pZ3JhbnRzIHBlciB0b3duDQpjYXRlZ19zb2NpbyR0b3RhbF9pbW1pZyA8LSBjYXRlZ19zb2NpbyRtYWxlX2ltbWlnICsgY2F0ZWdfc29jaW8kZmVtYWxlX2ltbWlnDQoNCiMgd29ya2luZyBtYWxlIGltbWlncmFudHMgcGVyIHRvd24gDQpjYXRlZ19zb2NpbyRtYWxlX3dvcmtpbmdfaW1taWcgPC0gY2F0ZWdfc29jaW8kbWFsZV9pbW1pZyAtIGNhdGVnX3NvY2lvJE1faW1taV9yZXRpcmVkIA0KIA0KIyB3b3JraW5nIGZlbWFsZSBpbW1pZ3JhbnRzIHBlciB0b3duICANCmNhdGVnX3NvY2lvJGZlbWFsZV93b3JraW5nX2ltbWlnIDwtIGNhdGVnX3NvY2lvJGZlbWFsZV9pbW1pZyAtIGNhdGVnX3NvY2lvJEZfaW1taV9yZXRpcmVkIA0KIA0KIyB0b3RhbCBudW1iZXIgb2YgbWFsZSBuYXRpdmVzIHBlciB0b3duIA0KY2F0ZWdfc29jaW8kbWFsZV9uYXRpdmUgPC0gcm93U3VtcyhjYmluZChjYXRlZ19zb2NpbyRNX05vSW1taV9hZ3JpLCBjYXRlZ19zb2NpbyRNX05vSW1taV9jb21tLCBjYXRlZ19zb2NpbyRNX05vSW1taV9leGVjLCBjYXRlZ19zb2NpbyRNX05vSW1taV9lbXAsIGNhdGVnX3NvY2lvJE1fTm9JbW1pX21pZG1hbiwgY2F0ZWdfc29jaW8kTV9Ob0ltbWlfbm9BY3QsIGNhdGVnX3NvY2lvJE1fTm9JbW1pX3JldGlyZWQsIGNhdGVnX3NvY2lvJE1fTm9JbW1pX3dvcmtlcikpDQoNCiMgdG90YWwgbnVtYmVyIG9mIGZlbWFsZSBuYXRpdmVzIHBlciB0b3duIA0KY2F0ZWdfc29jaW8kZmVtYWxlX25hdGl2ZSA8LSByb3dTdW1zKGNiaW5kKGNhdGVnX3NvY2lvJEZfTm9JbW1pX2FncmksIGNhdGVnX3NvY2lvJEZfTm9JbW1pX2NvbW0sIGNhdGVnX3NvY2lvJEZfTm9JbW1pX2V4ZWMsIGNhdGVnX3NvY2lvJEZfTm9JbW1pX2VtcCwgY2F0ZWdfc29jaW8kRl9Ob0ltbWlfbWlkbWFuLCBjYXRlZ19zb2NpbyRGX05vSW1taV9ub0FjdCwgY2F0ZWdfc29jaW8kRl9Ob0ltbWlfcmV0aXJlZCwgY2F0ZWdfc29jaW8kRl9Ob0ltbWlfd29ya2VyKSkNCg0KIyB0b3RhbCBudW1iZXIgb2YgbmF0aXZlcyBwZXIgdG93biANCmNhdGVnX3NvY2lvJHRvdGFsX25hdGl2ZSA8LSBjYXRlZ19zb2NpbyRtYWxlX25hdGl2ZSArIGNhdGVnX3NvY2lvJGZlbWFsZV9uYXRpdmUNCg0KIyBkcm9wIHVubmVjZXNzYXJ5IHZhcmlhYmxlcyANCmNhdGVnX3NvY2lvIDwtIHN1YnNldChjYXRlZ19zb2Npbywgc2VsZWN0ID0gYyhDT0RHRU8sIG1hbGVfaW1taWcsIGZlbWFsZV9pbW1pZywgdG90YWxfaW1taWcsIG1hbGVfd29ya2luZ19pbW1pZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlbWFsZV93b3JraW5nX2ltbWlnLCBtYWxlX25hdGl2ZSwgZmVtYWxlX25hdGl2ZSwgdG90YWxfbmF0aXZlKSkgDQoNCmBgYA0KDQoNCiMjIFdvcmsgc3RhdHVzIGRhdGENCg0KUmVuYW1lIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMNCmBgYHtyIHByZS1wcm9jZXNzaW5nIHdvcmsgc3RhdHVzIGRhdGF9DQoNCiMgcmVuYW1lIHZhcmlhYmxlcw0KbmFtZXMoc3RhdHVzX3dvcmspWzM6bmNvbChzdGF0dXNfd29yayldIDwtDQogIGMoIkxlc3MyMF9NX3dhZ2Vhcm5lcl9GdWxsIiwgIkxlc3MyMF9NX3dhZ2Vhcm5lcl9IYWxmIiwgIkxlc3MyMF9NX0luZHAxX0Z1bGwiLCAiTGVzczIwX01fSW5kcDFfSGFsZiIsDQogICAgIkxlc3MyMF9NX2VtcGxfRnVsbCIsICJMZXNzMjBfTV9lbXBsX0hhbGYiLCAiTGVzczIwX01fdHJhbnNfRnVsbCIsICJMZXNzMjBfTV90cmFuc19IYWxmIiwNCiAgICAiTGVzczIwX0Zfd2FnZWFybmVyX0Z1bGwiLCAiTGVzczIwX0Zfd2FnZWFybmVyX0hhbGYiLCAiTGVzczIwX0ZfSW5kcDFfRnVsbCIsICJMZXNzMjBfRl9JbmRwMV9IYWxmIiwNCiAgICAiTGVzczIwX0ZfZW1wbF9GdWxsIiwgIkxlc3MyMF9GX2VtcGxfSGFsZiIsICJMZXNzMjBfRl90cmFuc19GdWxsIiwgIkxlc3MyMF9GX3RyYW5zX0hhbGYiLA0KICAgICJ0NF9NX3dhZ2Vhcm5lcl9GdWxsIiwgInQ0X01fd2FnZWFybmVyX0hhbGYiLCAidDRfTV9JbmRwMV9GdWxsIiwgInQ0X01fSW5kcDFfSGFsZiIsICJ0NF9NX2VtcGxfRnVsbCIsDQogICAgInQ0X01fZW1wbF9IYWxmIiwgInQ0X01fdHJhbnNfRnVsbCIsICJ0NF9NX3RyYW5zX0hhbGYiLCAidDRfRl93YWdlYXJuZXJfRnVsbCIsICJ0NF9GX3dhZ2Vhcm5lcl9IYWxmIiwNCiAgICAidDRfRl9JbmRwMV9GdWxsIiwgInQ0X0ZfSW5kcDFfSGFsZiIsICJ0NF9GX2VtcGxfRnVsbCIsICJ0NF9GX2VtcGxfSGFsZiIsICJ0NF9GX3RyYW5zX0Z1bGwiLCAidDRfRl90cmFuc19IYWxmIiwNCiAgICAidDlfTV93YWdlYXJuZXJfRnVsbCIsICJ0OV9NX3dhZ2Vhcm5lcl9IYWxmIiwgInQ5X01fSW5kcDFfRnVsbCIsICJ0OV9NX0luZHAxX0hhbGYiLCAidDlfTV9lbXBsX0Z1bGwiLA0KICAgICJ0OV9NX2VtcGxfSGFsZiIsICJ0OV9NX3RyYW5zX0Z1bGwiLCAidDlfTV90cmFuc19IYWxmIiwgInQ5X0Zfd2FnZWFybmVyX0Z1bGwiLCAidDlfRl93YWdlYXJuZXJfSGFsZiIsDQogICAgInQ5X0ZfSW5kcDFfRnVsbCIsICJ0OV9GX0luZHAxX0hhbGYiLCAidDlfRl9lbXBsX0Z1bGwiLCAidDlfRl9lbXBsX0hhbGYiLCAidDlfRl90cmFuc19GdWxsIiwgInQ5X0ZfdHJhbnNfSGFsZiIsDQogICAgIlQ0X01fd2FnZWFybmVyX0Z1bGwiLCAiVDRfTV93YWdlYXJuZXJfSGFsZiIsICJUNF9NX0luZHAxX0Z1bGwiLCAiVDRfTV9JbmRwMV9IYWxmIiwgIlQ0X01fZW1wbF9GdWxsIiwNCiAgICAiVDRfTV9lbXBsX0hhbGYiLCAiVDRfTV90cmFuc19GdWxsIiwgIlQ0X01fdHJhbnNfSGFsZiIsICJUNF9GX3dhZ2Vhcm5lcl9GdWxsIiwgIlQ0X0Zfd2FnZWFybmVyX0hhbGYiLA0KICAgICJUNF9GX0luZHAxX0Z1bGwiLCAiVDRfRl9JbmRwMV9IYWxmIiwgIlQ0X0ZfZW1wbF9GdWxsIiwgIlQ0X0ZfZW1wbF9IYWxmIiwgIlQ0X0ZfdHJhbnNfRnVsbCIsICJUNF9GX3RyYW5zX0hhbGYiLA0KICAgICJUOV9NX3dhZ2Vhcm5lcl9GdWxsIiwgIlQ5X01fd2FnZWFybmVyX0hhbGYiLCAiVDlfTV9JbmRwMV9GdWxsIiwgIlQ5X01fSW5kcDFfSGFsZiIsICJUOV9NX2VtcGxfRnVsbCIsDQogICAgIlQ5X01fZW1wbF9IYWxmIiwgIlQ5X01fdHJhbnNfRnVsbCIsICJUOV9NX3RyYW5zX0hhbGYiLCAiVDlfRl93YWdlYXJuZXJfRnVsbCIsICJUOV9GX3dhZ2Vhcm5lcl9IYWxmIiwNCiAgICAiVDlfRl9JbmRwMV9GdWxsIiwgIlQ5X0ZfSW5kcDFfSGFsZiIsICJUOV9GX2VtcGxfRnVsbCIsICJUOV9GX2VtcGxfSGFsZiIsICJUOV9GX3RyYW5zX0Z1bGwiLCAiVDlfRl90cmFuc19IYWxmIiwNCiAgICAiRkZfTV93YWdlYXJuZXJfRnVsbCIsICJGRl9NX3dhZ2Vhcm5lcl9IYWxmIiwgIkZGX01fSW5kcDFfRnVsbCIsICJGRl9NX0luZHAxX0hhbGYiLCAiRkZfTV9lbXBsX0Z1bGwiLA0KICAgICJGRl9NX2VtcGxfSGFsZiIsICJGRl9NX3RyYW5zX0Z1bGwiLCAiRkZfTV90cmFuc19IYWxmIiwgIkZGX0Zfd2FnZWFybmVyX0Z1bGwiLCAiRkZfRl93YWdlYXJuZXJfSGFsZiIsDQogICAgIkZGX0ZfSW5kcDFfRnVsbCIsICJGRl9GX0luZHAxX0hhbGYiLCAiRkZfRl9lbXBsX0Z1bGwiLCAiRkZfRl9lbXBsX0hhbGYiLCAiRkZfRl90cmFuc19GdWxsIiwgIkZGX0ZfdHJhbnNfSGFsZiIsDQogICAgIkY5X01fd2FnZWFybmVyX0Z1bGwiLCAiRjlfTV93YWdlYXJuZXJfSGFsZiIsICJGOV9NX0luZHAxX0Z1bGwiLCAiRjlfTV9JbmRwMV9IYWxmIiwgIkY5X01fZW1wbF9GdWxsIiwNCiAgICAiRjlfTV9lbXBsX0hhbGYiLCAiRjlfTV90cmFuc19GdWxsIiwgIkY5X01fdHJhbnNfSGFsZiIsICJGOV9GX3dhZ2Vhcm5lcl9GdWxsIiwgIkY5X0Zfd2FnZWFybmVyX0hhbGYiLA0KICAgICJGOV9GX0luZHAxX0Z1bGwiLCAiRjlfRl9JbmRwMV9IYWxmIiwgIkY5X0ZfZW1wbF9GdWxsIiwgIkY5X0ZfZW1wbF9IYWxmIiwgIkY5X0ZfdHJhbnNfRnVsbCIsICJGOV9GX3RyYW5zX0hhbGYiLA0KICAgICJmNF9NX3dhZ2Vhcm5lcl9GdWxsIiwgImY0X01fd2FnZWFybmVyX0hhbGYiLCAiZjRfTV9JbmRwMV9GdWxsIiwgImY0X01fSW5kcDFfSGFsZiIsICJmNF9NX2VtcGxfRnVsbCIsDQogICAgImY0X01fZW1wbF9IYWxmIiwgImY0X01fdHJhbnNfRnVsbCIsICJmNF9NX3RyYW5zX0hhbGYiLCAiZjRfRl93YWdlYXJuZXJfRnVsbCIsICJmNF9GX3dhZ2Vhcm5lcl9IYWxmIiwNCiAgICAiZjRfRl9JbmRwMV9GdWxsIiwgImY0X0ZfSW5kcDFfSGFsZiIsICJmNF9GX2VtcGxfRnVsbCIsICJmNF9GX2VtcGxfSGFsZiIsICJmNF9GX3RyYW5zX0Z1bGwiLCAiZjRfRl90cmFuc19IYWxmIiwNCiAgICAiZjlfTV93YWdlYXJuZXJfRnVsbCIsICJmOV9NX3dhZ2Vhcm5lcl9IYWxmIiwgImY5X01fSW5kcDFfRnVsbCIsICJmOV9NX0luZHAxX0hhbGYiLCAiZjlfTV9lbXBsX0Z1bGwiLA0KICAgICJmOV9NX2VtcGxfSGFsZiIsICJmOV9NX3RyYW5zX0Z1bGwiLCAiZjlfTV90cmFuc19IYWxmIiwgImY5X0Zfd2FnZWFybmVyX0Z1bGwiLCAiZjlfRl93YWdlYXJuZXJfSGFsZiIsDQogICAgImY5X0ZfSW5kcDFfRnVsbCIsICJmOV9GX0luZHAxX0hhbGYiLCAiZjlfRl9lbXBsX0Z1bGwiLCAiZjlfRl9lbXBsX0hhbGYiLCAiZjlfRl90cmFuc19GdWxsIiwgImY5X0ZfdHJhbnNfSGFsZiIsDQogICAgInM0X01fd2FnZWFybmVyX0Z1bGwiLCAiczRfTV93YWdlYXJuZXJfSGFsZiIsICJzNF9NX0luZHAxX0Z1bGwiLCAiczRfTV9JbmRwMV9IYWxmIiwgInM0X01fZW1wbF9GdWxsIiwNCiAgICAiczRfTV9lbXBsX0hhbGYiLCAiczRfTV90cmFuc19GdWxsIiwgInM0X01fdHJhbnNfSGFsZiIsICJzNF9GX3dhZ2Vhcm5lcl9GdWxsIiwgInM0X0Zfd2FnZWFybmVyX0hhbGYiLA0KICAgICJzNF9GX0luZHAxX0Z1bGwiLCAiczRfRl9JbmRwMV9IYWxmIiwgInM0X0ZfZW1wbF9GdWxsIiwgInM0X0ZfZW1wbF9IYWxmIiwgInM0X0ZfdHJhbnNfRnVsbCIsICJzNF9GX3RyYW5zX0hhbGYiLA0KICAgICJzOV9NX3dhZ2Vhcm5lcl9GdWxsIiwgInM5X01fd2FnZWFybmVyX0hhbGYiLCAiczlfTV9JbmRwMV9GdWxsIiwgInM5X01fSW5kcDFfSGFsZiIsICJzOV9NX2VtcGxfRnVsbCIsDQogICAgInM5X01fZW1wbF9IYWxmIiwgInM5X01fdHJhbnNfRnVsbCIsICJzOV9NX3RyYW5zX0hhbGYiLCAiczlfRl93YWdlYXJuZXJfRnVsbCIsICJzOV9GX3dhZ2Vhcm5lcl9IYWxmIiwNCiAgICAiczlfRl9JbmRwMV9GdWxsIiwgInM5X0ZfSW5kcDFfSGFsZiIsICJzOV9GX2VtcGxfRnVsbCIsICJzOV9GX2VtcGxfSGFsZiIsICJzOV9GX3RyYW5zX0Z1bGwiLCAiczlfRl90cmFuc19IYWxmIikNCg0KYGBgDQoNCkNyZWF0ZSBhZ2dyZWdhdGUgc3RhdGlzdGljcyBhbmQgZHJvcCB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMNCmBgYHtyIFN0YXR1cyBkcm9wIGFuZCBhZ2dyZWdhdGlvbn0NCg0KIyBTaW1wbGlmeSB0aGUgdmFyaWFibGVzIGJ5IHN1bW1pbmcgdXAgYnkgY2F0ZWdvcmllcw0KIyB0b3RhbCBudW1iZXIgb2Ygd2FnZSBlYXJuZXJzIGZvciBtYWxlcyBhbmQgZmVtYWxlcy4NCm5hbS5NID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KbmFtLkYgPSByZXAoMCwgbmNvbChzdGF0dXNfd29yaykpDQpmb3IgKGkgaW4gMTpuY29sKHN0YXR1c193b3JrKSl7DQogIHNvbCA9IGdyZXBsKCd3YWdlYXJuZXInLCBuYW1lcyhzdGF0dXNfd29yaylbaV0pDQogIGlmIChzb2w9PTEpew0KICAgIGlmIChncmVwbCgnTScsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkpew0KICAgICAgbmFtLk1baV0gPSBpICANCiAgICB9ZWxzZXtuYW0uRltpXT1pfQ0KICB9ZWxzZXsNCiAgICBuYW0uRltpXT0wDQogICAgbmFtLk1baV09MH0NCn0NCiMgbmFtZXMoc3RhdHVzX3dvcmtbbmFtLk1dKQ0KIyBuYW1lcyhzdGF0dXNfd29ya1tuYW0uRl0pDQpzdGF0dXNfd29yayR3YWdlYXJuZXJfTT1yb3dTdW1zKHN0YXR1c193b3JrWyxuYW0uTV0pDQpzdGF0dXNfd29yayR3YWdlYXJuZXJfRj1yb3dTdW1zKHN0YXR1c193b3JrWyxuYW0uRl0pIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgc2FtZSBidXQgZm9yIGluZGVwZW5kZW50IHdvcmtlcnMgYnkgZ2VuZGVyDQpuYW0uTSA9IHJlcCgwLCBuY29sKHN0YXR1c193b3JrKSkNCm5hbS5GID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KZm9yIChpIGluIDE6bmNvbChzdGF0dXNfd29yaykpew0KICBzb2wgPSBncmVwbCgnSW5kcDEnLCBuYW1lcyhzdGF0dXNfd29yaylbaV0pDQogIGlmIChzb2w9PTEpew0KICAgIGlmIChncmVwbCgnTScsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkpew0KICAgICAgbmFtLk1baV0gPSBpICANCiAgICB9ZWxzZXtuYW0uRltpXT1pfQ0KICB9ZWxzZXsNCiAgICBuYW0uRltpXT0wDQogICAgbmFtLk1baV09MH0NCn0NCiMgbmFtZXMoc3RhdHVzX3dvcmtbbmFtLk1dKQ0KIyBuYW1lcyhzdGF0dXNfd29ya1tuYW0uRl0pDQpzdGF0dXNfd29yayRpbmRlcGVuZGVudF9NPXJvd1N1bXMoc3RhdHVzX3dvcmtbLG5hbS5NXSkgDQpzdGF0dXNfd29yayRpbmRlcGVuZGVudF9GPXJvd1N1bXMoc3RhdHVzX3dvcmtbLG5hbS5GXSkNCg0KIyBzYW1lIGJ1dCBmb3IgZ292ZXJubWVudCB0cmFuc2ZlciByZWNlaXZlcnMNCm5hbS5NID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KbmFtLkYgPSByZXAoMCwgbmNvbChzdGF0dXNfd29yaykpDQpmb3IgKGkgaW4gMTpuY29sKHN0YXR1c193b3JrKSl7DQogIHNvbCA9IGdyZXBsKCd0cmFucycsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkNCiAgaWYgKHNvbD09MSl7DQogICAgaWYgKGdyZXBsKCdNJywgbmFtZXMoc3RhdHVzX3dvcmspW2ldKSl7DQogICAgICBuYW0uTVtpXSA9IGkgIA0KICAgIH1lbHNle25hbS5GW2ldPWl9DQogIH1lbHNlew0KICAgIG5hbS5GW2ldPTANCiAgICBuYW0uTVtpXT0wfQ0KfQ0KIyBuYW1lcyhzdGF0dXNfd29ya1tuYW0uTV0pDQojIG5hbWVzKHN0YXR1c193b3JrW25hbS5GXSkNCnN0YXR1c193b3JrJHRyYW5zZmVyX009cm93U3VtcyhzdGF0dXNfd29ya1ssbmFtLk1dKSAgDQpzdGF0dXNfd29yayR0cmFuc2Zlcl9GPXJvd1N1bXMoc3RhdHVzX3dvcmtbLG5hbS5GXSkNCg0KIyBzYW1lIGJ1dCBmb3IgZW1wbG95ZXJzDQpuYW0uTSA9IHJlcCgwLCBuY29sKHN0YXR1c193b3JrKSkNCm5hbS5GID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KZm9yIChpIGluIDE6bmNvbChzdGF0dXNfd29yaykpew0KICBzb2wgPSBncmVwbCgnZW1wbCcsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkNCiAgaWYgKHNvbD09MSl7DQogICAgaWYgKGdyZXBsKCdNJywgbmFtZXMoc3RhdHVzX3dvcmspW2ldKSl7DQogICAgICBuYW0uTVtpXSA9IGkgIA0KICAgIH1lbHNle25hbS5GW2ldPWl9DQogIH1lbHNlew0KICAgIG5hbS5GW2ldPTANCiAgICBuYW0uTVtpXT0wfQ0KfQ0KIyBuYW1lcyhzdGF0dXNfd29ya1tuYW0uTV0pDQojIG5hbWVzKHN0YXR1c193b3JrW25hbS5GXSkNCnN0YXR1c193b3JrJGVtcGxveWVyX009cm93U3VtcyhzdGF0dXNfd29ya1ssbmFtLk1dKQ0Kc3RhdHVzX3dvcmskZW1wbG95ZXJfRj1yb3dTdW1zKHN0YXR1c193b3JrWyxuYW0uRl0pDQogDQojIHNhbWUgYnV0IGZvciBmdWxsLXRpbWUgY29udHJhY3RzDQpuYW0uTSA9IHJlcCgwLCBuY29sKHN0YXR1c193b3JrKSkNCm5hbS5GID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KZm9yIChpIGluIDE6bmNvbChzdGF0dXNfd29yaykpew0KICBzb2wgPSBncmVwbCgnRnVsbCcsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkNCiAgaWYgKHNvbD09MSl7DQogICAgaWYgKGdyZXBsKCdNJywgbmFtZXMoc3RhdHVzX3dvcmspW2ldKSl7DQogICAgICBuYW0uTVtpXSA9IGkgIA0KICAgIH1lbHNle25hbS5GW2ldPWl9DQogIH1lbHNlew0KICAgIG5hbS5GW2ldPTANCiAgICBuYW0uTVtpXT0wfQ0KfQ0KIyBuYW1lcyhzdGF0dXNfd29ya1tuYW0uTV0pDQojIG5hbWVzKHN0YXR1c193b3JrW25hbS5GXSkNCnN0YXR1c193b3JrJGZ1bGxfTT1yb3dTdW1zKHN0YXR1c193b3JrWyxuYW0uTV0pDQpzdGF0dXNfd29yayRmdWxsX0Y9cm93U3VtcyhzdGF0dXNfd29ya1ssbmFtLkZdKQ0KDQojIHNhbWUgYnV0IGZvciBoYWxmLXRpbWUgY29udHJhY3RzDQpuYW0uTSA9IHJlcCgwLCBuY29sKHN0YXR1c193b3JrKSkNCm5hbS5GID0gcmVwKDAsIG5jb2woc3RhdHVzX3dvcmspKQ0KZm9yIChpIGluIDE6bmNvbChzdGF0dXNfd29yaykpew0KICBzb2wgPSBncmVwbCgnSGFsZicsIG5hbWVzKHN0YXR1c193b3JrKVtpXSkNCiAgaWYgKHNvbD09MSl7DQogICAgaWYgKGdyZXBsKCdNJywgbmFtZXMoc3RhdHVzX3dvcmspW2ldKSl7DQogICAgICBuYW0uTVtpXSA9IGkgIA0KICAgIH1lbHNle25hbS5GW2ldPWl9DQogIH1lbHNlew0KICAgIG5hbS5GW2ldPTANCiAgICBuYW0uTVtpXT0wfQ0KfQ0KI25hbWVzKHN0YXR1c193b3JrW25hbS5NXSkNCiNuYW1lcyhzdGF0dXNfd29ya1tuYW0uRl0pDQpzdGF0dXNfd29yayRoYWxmX009cm93U3VtcyhzdGF0dXNfd29ya1ssbmFtLk1dKQ0Kc3RhdHVzX3dvcmskaGFsZl9GPXJvd1N1bXMoc3RhdHVzX3dvcmtbLG5hbS5GXSkNCg0KIyB0b3RhbCBvZiBlYWNoIHZhcmlhYmxlcw0Kc3RhdHVzX3dvcmskZnVsbCA8LSBzdGF0dXNfd29yayRmdWxsX00gKyBzdGF0dXNfd29yayRmdWxsX0YNCnN0YXR1c193b3JrJGhhbGYgPC0gc3RhdHVzX3dvcmskaGFsZl9NICsgc3RhdHVzX3dvcmskaGFsZl9GDQpzdGF0dXNfd29yayR3YWdlYXJuZXIgPC0gc3RhdHVzX3dvcmskd2FnZWFybmVyX00gKyBzdGF0dXNfd29yayR3YWdlYXJuZXJfRg0Kc3RhdHVzX3dvcmskaW5kZXBlbmRlbnQgPC0gc3RhdHVzX3dvcmskaW5kZXBlbmRlbnRfTSArIHN0YXR1c193b3JrJGluZGVwZW5kZW50X0YNCnN0YXR1c193b3JrJHRyYW5zZmVyIDwtIHN0YXR1c193b3JrJHRyYW5zZmVyX0YgKyBzdGF0dXNfd29yayR0cmFuc2Zlcl9NIA0Kc3RhdHVzX3dvcmskZW1wbG95ZXIgPC0gc3RhdHVzX3dvcmskZW1wbG95ZXJfRiArIHN0YXR1c193b3JrJGVtcGxveWVyX00gDQoNCiMgZHJvcCB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMNCnN0YXR1c193b3JrIDwtIHN1YnNldChzdGF0dXNfd29yaywgc2VsZWN0ID0gYyhDT0RHRU8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2FnZWFybmVyX00sIHdhZ2Vhcm5lcl9GLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRlcGVuZGVudF9NLCBpbmRlcGVuZGVudF9GLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zlcl9NLCB0cmFuc2Zlcl9GLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbXBsb3llcl9NLCBlbXBsb3llcl9GLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsX00sIGZ1bGxfRiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYWxmX00sIGhhbGZfRiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLCBoYWxmLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdhZ2Vhcm5lciwgaW5kZXBlbmRlbnQsIHRyYW5zZmVyLCBlbXBsb3llcikpDQojIEZ1bGwgdmFyaWFibGUgbWVhbnMgaW5kaXZpZHVhbHMgd2l0aCBmdWxsIHRpbWUgY29udHJhY3RzLiBIYWxmIG1lYW5zIGluZGl2aWR1YWxzIHdpdGggcGFydCB0aW1lIGNvbnRyYWN0LiANCiMgInRyYW5zZmVyIiBpcyBpbmRpdmlkdWFscyByZWNlaXZpbmcgdHJhbnNmZXIgcGF5bWVudHMuIA0KIyAiaW5kZXBlbmRlbnQiIGlzIGxpa2UgYXV0by1lbnRyZXByZW5ldXJzLiANCg0KYGBgDQoNCg0KIyMgSW5lcXVhbGl0eSBkYXRhDQoNCkRyb3AgdW5uZWNlc3NhcnkgdmFyaWFibGVzIGFuZCByZW5hbWUgdGhlIG90aGVyIG9uZXMNCmBgYHtyIHByZS1wcm9jZXNzaW5nIGluZXF1YWxpdHkgZGF0YX0NCg0KbmFtZXMoaW5lcSkNCg0KIyBkcm9wIHVubmVjZXNzYXJ5IHZhcmlhYmxlcw0KaW5lcSA8LSBzdWJzZXQoaW5lcSwgc2VsZWN0ID0gLWMoTElCR0VPLCBQMDlfUE9QLCBOQUlTRDE2LCBERUNFU0QxNiwgUDE0X1JQX1BST1AsIFAwOV9FTVBMVCxFVFRPVDE1LCBFVEFaMTUsIEVUQkUxNSwgRVRGWjE1LCBFVEdVMTUsIEVUR1oxNSwgRVRPUTE1LCBFVFRFRjExNSwgRVRURUZQMTAxNSkpDQoNCiMgcmVuYW1lIHZhcmlhYmxlcyANCm5hbWVzKGluZXEpWzQ6bmNvbChpbmVxKV0gPC0NCiAgYygicG9wXzIwMTQiLA0KICAgICJTdXBlcmZpY2llIiwNCiAgICAiYmlydGgwOV8xNCIsDQogICAgImRlYXRoMDlfMTQiLA0KICAgICJob3VzZWhvbGRzMTQiLA0KICAgICJob3VzaW5nMTQiLA0KICAgICJwcmluY19yZXNpZDE0IiwNCiAgICAic2VjX3Jlc2lkMTQiLA0KICAgICJ2YWNfcmVzaWQxNCIsDQogICAgInRheF9ob3VzZTE0IiwNCiAgICAic2hhcmVkX3RheF9ob3VzZTE0IiwNCiAgICAibWVkaWFuX2xpdmluZzE0IiwNCiAgICAibGV2X2luZXExNCIsDQogICAgImVtcGwiLA0KICAgICJlbXBfc2FsIiwNCiAgICAicG9wMTVfNjQiLA0KICAgICJ1bmVtcDE1XzY0IiwNCiAgICAiYWN0MTVfNjQiKQ0KDQojIGRyb3AgbW9yZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMgDQppbmVxIDwtIHN1YnNldChpbmVxLCBzZWxlY3QgPSAtYyhwcmluY19yZXNpZDE0LCBzZWNfcmVzaWQxNCwgdmFjX3Jlc2lkMTQsIHBvcF8yMDE0LCBob3VzZWhvbGRzMTQsIGJpcnRoMDlfMTQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aDA5XzE0LCB0YXhfaG91c2UxNCwgc2hhcmVkX3RheF9ob3VzZTE0LCBsZXZfaW5lcTE0KSkgDQogDQojIHVuZW1wbG95bWVudCByYXRlIA0KaW5lcSR1bmVtcF9yYXRlIDwtIGluZXEkdW5lbXAxNV82NC9pbmVxJHBvcDE1XzY0IA0KDQojIGltcHV0ZSBOQSB1c2luZyBtZWRpYW4gKG9ubHkgMykNCmluZXEkbWVkaWFuX2xpdmluZzE0W2lzLm5hKGluZXEkbWVkaWFuX2xpdmluZzE0KV0gPSBtZWRpYW4oaW5lcSRtZWRpYW5fbGl2aW5nMTQsIG5hLnJtID0gVCkNCg0KYGBgDQoNCg0KIyMgQ29tbXVuZSBkYXRhDQoNCkRyb3AgdW5uZWNlc3NhcnkgdmFyaWFibGVzIGFuZCByZW5hbWUgdGhlIG90aGVyIG9uZXMNCmBgYHtyIHByZS1wcm9jZXNzaW5nIGNvbW11bmUgZGF0YX0NCm5hbWVzKGNvbW11bmUpDQoNCiNEcm9wIHVubmVjZXNzYXJ5IHZhcmlhYmxlcw0KY29tbXVuZSA8LXN1YnNldChjb21tdW5lLCBzZWxlY3Q9LWMobGliZ2VvLCBzY29yZWZpc2NhbCwgZGVwLCByZWcsIGV2b2x1dGlvbnBvcCwgbmJwcm9wcml0YWlyZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYmxvZ2VtZW50c2Vjb25kYWlyZWV0b2NjYXNpb25uZSwgc2NvcmV1cmJhbml0LCBzY29yZXZhcmdpb24sIHNjb3JlcGliLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYnJzaWRlbmNlc3ByaW5jaXBhbGVzLCBpbmRpY2VmaXNjYWxwYXJ0aWVsLCBuYm9jY3VwYW50c3JzaWRlbmNlcHJpbmNpcGFsZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXBtb3llbm5lc2FsYWlyZXNob3JhaXJlcywgZGVwbW95ZW5uZXNhbGFpcmVzY2FkcmVob3JhaXJlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXBtb3llbm5lc2FsYWlyZXNwcm9maW50ZXJtZGlhaSwgZGVwbW95ZW5uZXNhbGFpcmVzZW1wbG95aG9yYWlyZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVwbW95ZW5uZXNhbGFpcmVzb3V2cmlob3JhaXJlcywgdmFsZXVyYWpvdXRlcmdpb25hbGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmJpbmR1c3RyaWVzZGVzYmllbnNpbnRlcm1kaWFpcmUsIG5iZGVjb21tZXJjZSwgbmJkZXNlcnZpY2VzYXV4cGFydGljdWxpZXJzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5iaW5zdGl0dXRpb25kZWVkdWNhdGlvbnNhbnRhY3RpLCBzY29yZXZhcmdpb24sIHNjb3JlcGliLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1veWVubmVyZXZlbnVzZmlzY2F1eGRwYXJ0ZW1lbnRhLCBtb3llbm5lcmV2ZW51c2Zpc2NhdXhyZ2lvbmF1eCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdtb3llbm5lc2FsYWlyZXNob3JhaXJlcywgcmVnbW95ZW5uZXNhbGFpcmVzY2FkcmVob3JhaXJlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdtb3llbm5lc2FsYWlyZXNwcm9maW50ZXJtZGlhaSwgcmVnbW95ZW5uZXNhbGFpcmVzZW1wbG95aG9yYWlyZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnbW95ZW5uZXNhbGFpcmVzb3V2cmlob3JhaXJlcywgdGF1eHR1ZGlhbnRzLCBkeW5hbWlxdWVkbW9ncmFwaGlxdWVpbnNlZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXBhY2l0ZmlzYywgY2FwYWNpdGZpc2NhbGUsIG1veWVubmVyZXZudXNmaXNjYXV4LCBuYmxvZ2VtZW50LCBmaWRsaXQpKSANCg0KDQojIHJlbmFtZSB2YXJpYWJsZXMgIA0KbmFtZXMoY29tbXVuZSkgPC1jKCJDT0RHRU8iLCAib3JpZW50X2Vjb24iLCAiZGVtb19pbmRleCIsICJwb3B1bGF0aW9uIiwgIm5iX2hvdXNlaG9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAibmJfZmVtYWxlIiwgIm5iX21hbGUiLCAibmJfbWlub3IiLCAibmJfbWFqb3IiLCAibmJfc3R1ZGVudHMiLCAgDQogICAgICAgICAgICAgICAgICAgICJuYl9maXJtc19zZXJ2aWNlIiwgIm5iX2Zpcm1zX2NvbW1lcmNlIiwgIm5iX2Zpcm1zX2NvbnN0cnVjdGlvbiIsIA0KICAgICAgICAgICAgICAgICAgICAibmJfZmlybXNfaW5kIiwgIm5iX2NyZWF0ZWRfZmlybXMiLCAibmJfY3JlYXRlZF9pbmQiLCANCiAgICAgICAgICAgICAgICAgICAgIm5iX2NyZWF0ZWRfY29uc3RydWN0aW9uIiwgIm5iX2NyZWF0ZWRfY29tbWVyY2UiLA0KICAgICAgICAgICAgICAgICAgICAibmJfY3JlYXRlZF9zZXJ2aWNlcyIsICJ1cmJhblJ1cmFsIiwgIm5iX2FjdGl2ZSIsICJuYl9hY3RpdmVfZW1wbG95ZWVzIiwNCiAgICAgICAgICAgICAgICAgICAgIm5iX2FjdGl2ZV9ub25fZW1wbG95ZWVzIiwgImR5bWFuaWNfZGVtb19wcm9mIiwgInByb2ZpdFJhdGUiLCANCiAgICAgICAgICAgICAgICAgICAgInJlZ2lvbmFsX0dEUCIsICJkZW1vX2Vudmlyb25tZW50IiwgImV2b2xfcG9wX3Njb3JlIiwgDQogICAgICAgICAgICAgICAgICAgICJldm9sX2VudHJlcHJlbmV1cmlhbF9zY29yZSIsICJkZW1vX2VudmlyMiIpIA0KDQojIGRyb3AgbW9yZSB2YXJpYWJsZXMgDQpjb21tdW5lIDwtc3Vic2V0KGNvbW11bmUsIHNlbGVjdD0tYyhkZW1vX2luZGV4LCBwb3B1bGF0aW9uLCBwcm9maXRSYXRlLCBkZW1vX2VudmlyMiwgZGVtb19lbnZpcm9ubWVudCkpIA0KDQpgYGANCg0KDQojIFByb2R1Y2UgY29uc2lzdGVudCBkYXRhc2V0cw0KDQpUaGUgQ09ER0VPIHZhcmlhYmxlcyBoYXZlIHRvIGJlIG1lcmdlZC4NCkhvd2V2ZXIsIGZvciBkaWZmZXJlbnQgcmVhc29ucyBhbHJlYWR5IGlkZW50aWZpZWQgYnkgb3RoZXIga2FnZ2xlIHVzZXJzIHRoZXkgbmVlZCBzb21lIHByZS1wcm9jZXNzaW5nLg0KVG8gZG8gc28sIHNvbWUgYWxyZWFkeSBrbm93biBtaXN0YWtlcyBhcmUgY29ycmVjdGVkOg0KDQpgYGB7ciBmaXggYW5kIG1hdGNoIENPREdFT30NCg0KZmlybXMkQ09ER0VPICAgICAgIDwtIHN1YigiQSIsICIwIiwgZmlybXMkQ09ER0VPKQ0KZmlybXMkQ09ER0VPICAgICAgIDwtIHN1YigiQiIsICIwIiwgZmlybXMkQ09ER0VPKQ0Kc2FsYXJ5JENPREdFTyAgICAgIDwtIHN1YigiQSIsICIwIiwgc2FsYXJ5JENPREdFTykNCnNhbGFyeSRDT0RHRU8gICAgICA8LSBzdWIoIkIiLCAiMCIsIHNhbGFyeSRDT0RHRU8pDQpwb3B1bGF0aW9uJENPREdFTyAgPC0gc3ViKCJBIiwgIjAiLCBwb3B1bGF0aW9uJENPREdFTykNCnBvcHVsYXRpb24kQ09ER0VPICA8LSBzdWIoIkIiLCAiMCIsIHBvcHVsYXRpb24kQ09ER0VPKQ0KZ2VvJENPREdFTyAgICAgICAgIDwtIHN1YigiQSIsICIwIiwgZ2VvJENPREdFTykNCmdlbyRDT0RHRU8gICAgICAgICA8LSBzdWIoIkIiLCAiMCIsIGdlbyRDT0RHRU8pDQplZHVjJENPREdFTyAgICAgICAgPC0gc3ViKCJBIiwgIjAiLCBlZHVjJENPREdFTykNCmVkdWMkQ09ER0VPICAgICAgICA8LSBzdWIoIkIiLCAiMCIsIGVkdWMkQ09ER0VPKQ0KY2F0ZWdfc29jaW8kQ09ER0VPIDwtIHN1YigiQSIsICIwIiwgY2F0ZWdfc29jaW8kQ09ER0VPKQ0KY2F0ZWdfc29jaW8kQ09ER0VPIDwtIHN1YigiQiIsICIwIiwgY2F0ZWdfc29jaW8kQ09ER0VPKQ0KaW5lcSRDT0RHRU8gICAgICAgIDwtIHN1YigiQSIsICIwIiwgaW5lcSRDT0RHRU8pDQppbmVxJENPREdFTyAgICAgICAgPC0gc3ViKCJCIiwgIjAiLCBpbmVxJENPREdFTykNCnN0YXR1c193b3JrJENPREdFTyA8LSBzdWIoIkEiLCAiMCIsIHN0YXR1c193b3JrJENPREdFTykNCnN0YXR1c193b3JrJENPREdFTyA8LSBzdWIoIkIiLCAiMCIsIHN0YXR1c193b3JrJENPREdFTykNCmNvbW11bmUkQ09ER0VPICAgICA8LSBzdWIoIkEiLCAiMCIsIGNvbW11bmUkQ09ER0VPKQ0KY29tbXVuZSRDT0RHRU8gICAgIDwtIHN1YigiQiIsICIwIiwgY29tbXVuZSRDT0RHRU8pDQoNCiMgVGhlbiBhbGwgQ09ER0VPIGFyZSB0cmFzZm9ybWVkIHRvIGludGVnZXJzIGFuZCBmb3VyIG5ldyBkYXRhc2V0cyBhcmUgY3JlYXRlZCByZXRhaW5pbmcgb25seSB0aGUgY29tbW9uIENPREdFTzoNCiMgdXNlIG9ubHkgaW50ZWdlciB2YWx1ZXMNCmdlbyRDT0RHRU8gICAgICAgICA8LSBhcy5pbnRlZ2VyKGdlbyRDT0RHRU8pICANCnBvcHVsYXRpb24kQ09ER0VPICA8LSBhcy5pbnRlZ2VyKHBvcHVsYXRpb24kQ09ER0VPKQ0KZmlybXMkQ09ER0VPICAgICAgIDwtIGFzLmludGVnZXIoZmlybXMkQ09ER0VPKQ0Kc2FsYXJ5JENPREdFTyAgICAgIDwtIGFzLmludGVnZXIoc2FsYXJ5JENPREdFTykNCnN0YXR1c193b3JrJENPREdFTyA8LSBhcy5pbnRlZ2VyKHN0YXR1c193b3JrJENPREdFTykgIA0KaW5lcSRDT0RHRU8gICAgICAgIDwtIGFzLmludGVnZXIoaW5lcSRDT0RHRU8pDQplZHVjJENPREdFTyAgICAgICAgPC0gYXMuaW50ZWdlcihlZHVjJENPREdFTykNCmNhdGVnX3NvY2lvJENPREdFTyA8LSBhcy5pbnRlZ2VyKGNhdGVnX3NvY2lvJENPREdFTykNCmNvbW11bmUkQ09ER0VPICAgICA8LSBhcy5pbnRlZ2VyKGNvbW11bmUkQ09ER0VPKQ0KDQojIHN0b3JlIGRhdGFzZXRzJyBuYW1lcyB0byBsb29wIG9uIHRoZW0NCmRhdGFzZXQgPSBjKCJwb3B1bGF0aW9uIiwgInNhbGFyeSIsICJmaXJtcyIsICJnZW8iLCAiaW5lcSIsICJjb21tdW5lIiwgImVkdWMiLCAiY2F0ZWdfc29jaW8iLCAic3RhdHVzX3dvcmsiKQ0KDQojIG9idGFpbiBzb21tb24gSURzIGZvciBhbGwgZGF0YXNldHMNCmZvciAoaSBpbiBkYXRhc2V0KXsNCiAgIyBnZXQgaS10aCBuYW1lIGFuZCBjcmVhdGUgYSBuZXcgdmFyaWFibGUgY29uY2F0ZW5ldGluZyAiTkVXIiBhdCB0aGUgZW5kDQogIG5hbSA8LSBwYXN0ZShpLCAiTkVXIiwgc2VwID0gIiIpDQogICMgaW5pdGlhbGl6ZSBjb3VudGVyIHRvIGlkZW50aWZ5IHRoZSBudW1iZXIgb2YgaXRlcmF0aW9uIGluIGoNCiAgaXRlciA9IDENCiAgZm9yIChqIGluIGRhdGFzZXQpew0KICAgIGlmIChqICE9IGkpew0KICAgICAgIyBmb3IgZWFjaCBkYXRhc2V0IGRpZmZlcmVudCBmcm9tIHRoZSBpLXRoDQogICAgICBpZiAoaXRlciA9PSAxKXsNCiAgICAgICAgIyAxc3QgaXRlcmF0aW9uOiB1c2UgdGhlIG9yaWdpbmFsIGRhdGFzZXQgKGUuZy4sIGdlbykNCiAgICAgICAgYXNzaWduKG5hbSwgc2VtaV9qb2luKGdldChpKSwgZ2V0KGopLCBieSA9ICJDT0RHRU8iKSkNCiAgICAgIH0gZWxzZXsNCiAgICAgICAgIyBzdWNjZXNzaXZlIGl0ZXJhdGlvbjogdXNlIHRoZSBuZXcgZGF0YXNldCAoZS5nLiwgZ2VvTkVXKQ0KICAgICAgICBhc3NpZ24obmFtLCBzZW1pX2pvaW4oZ2V0KG5hbSksIGdldChqKSwgYnkgPSAiQ09ER0VPIikpDQogICAgICB9DQogICAgICBpdGVyID0gaXRlciArIDENCiAgICB9DQogIH0NCn0NCg0KIyBjaGVjayBob3cgbWFueSBvYnNlcnZhdGlvbiBoYXZlIGJlZW4gZGVsZXRlZA0KZm9yIChpIGluIGRhdGFzZXQpew0KICBkZWxfcm93cyA9IG5yb3coZ2V0KGkpKSAtIG5yb3coZ2V0KHBhc3RlKGksICJORVciLCBzZXAgPSAiIikpKQ0KICBkZWxfcHJvcCA9IGRlbF9yb3dzIC8gbnJvdyhnZXQoaSkpDQogIGRlbF9vYnMgPSBwYXN0ZSgiRm9yIiwgaSwgZGVsX3Jvd3MsICJ1bml0cyBoYXZlIGJlZW4gZGVsZXRlZC4iLA0KICAgICAgICAgICAgICAgICAgIlRoZXkgd2VyZSB0aGUiLCByb3VuZChkZWxfcHJvcCoxMDAsIGRpZ2l0cz0yKSwgIiUgb2YgdGhlIHRvdGFsLiIsIHNlcCA9ICIgIikNCiAgcHJpbnQocGFzdGUoZGVsX29icykpDQp9DQoNCnByaW50KHBhc3RlKCJUaGUgbmV3IGRhdGFzZXQgaGFzIiwgbnJvdyhzYWxhcnlORVcpLCAidW5pdHMgYW5kIiwgDQogICAgICBuY29sKHNhbGFyeU5FVykrbmNvbChwb3B1bGF0aW9uTkVXKStuY29sKGZpcm1zTkVXKStuY29sKGdlb05FVykrDQogICAgICAgIG5jb2woc3RhdHVzX3dvcmtORVcpK25jb2woaW5lcU5FVykrbmNvbChlZHVjTkVXKStuY29sKGNhdGVnX3NvY2lvTkVXKStuY29sKGNvbW11bmVORVcpLCAiZmVhdHVyZXMuIikpDQoNCmBgYA0KDQpPdmVyd3JpdGUgbmV3IGRhdGVzZXRzIGludG8gdGhlIG9sZCBvbmVzIGFuZCByZW1vdmUgdGhlIGxhdHRlciBvbmVzIGZyb20gbWVtb3J5DQpgYGB7ciB0YWtlIHVuaXF1ZSBkYXRhc2V0c30NCg0KZmlybXMgICAgICAgPC0gZmlybXNORVcNCmdlbyAgICAgICAgIDwtIGdlb05FVw0Kc2FsYXJ5ICAgICAgPC0gc2FsYXJ5TkVXDQpwb3B1bGF0aW9uICA8LSBwb3B1bGF0aW9uTkVXDQplZHVjICAgICAgICA8LSBlZHVjTkVXDQpjYXRlZ19zb2NpbyA8LSBjYXRlZ19zb2Npb05FVw0Kc3RhdHVzX3dvcmsgPC0gc3RhdHVzX3dvcmtORVcNCmluZXEgICAgICAgIDwtIGluZXFORVcNCmNvbW11bmUgICAgIDwtIGNvbW11bmVORVcNCg0Kcm0obGlzdD1jKCJmaXJtc05FVyIsICJnZW9ORVciLCAic2FsYXJ5TkVXIiwgInBvcHVsYXRpb25ORVciLCAiZWR1Y05FVyIsICJjYXRlZ19zb2Npb05FVyIsICJzdGF0dXNfd29ya05FVyIsICJpbmVxTkVXIiwgImNvbW11bmVORVciKSkNCg0KYGBgDQoNCg0KIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIA0KDQojIyBGaXJtcyBkYXRhDQoNCkNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG51bGwgZmlybXMgKGkuZS4sIHVua25vd24gc2l6ZXMpIGFuZCBhbmFseXplDQpmaXJtcycgZGlzdHJpYnV0aW9uIHBlciB0b3duOg0KYGBge3IgZmlybXMgZGlzdHJpYnV0aW9ufQ0KDQojIGNoZWNrIHRoZSByYXRpbyBvZiBudWxsIGZpcm1zIGZvciBlYWNoIHRvd24NCnN1bW1hcnkoZmlybXMkbnVsbC9maXJtcyR0b3RhbCkNCiMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaXMgbWlzc2luZywgc2hvdWxkIHdlIHJlbW92ZSB0aGVzZSBkYXRhPw0KZ2dwbG90KGRhdGE9ZmlybXMsIGFlcyhmaXJtcyRudWxsL2Zpcm1zJHRvdGFsKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA1MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsNCiAgbGFicyh4PSJOdWxsIGZpcm1zL3RvdGFsIGZpcm1zIiwgeT0iQ291bnQiKSArDQogIGdndGl0bGUoIlJhdGlvIGJldHdlZW4gdGhlIG51bWJlciBvZiBudWxsIGZpcm1zIGFuZCB0b3RhbCBmaXJtcyBwZXIgdG93biIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KDQojIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIGZpcm1zIHdpdGggdW5rbm93biBzaXplIA0KZ2dwbG90KGRhdGE9ZmlybXMsIGFlcyhsb2coZmlybXMkbnVsbCkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9Ik51bWJlciBvZiBmaXJtcyBvZiB1bmtub3duIHNpemUgKGxvZyBzY2FsZSkiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJOdW1iZXIgb2YgZmlybXMgb2YgdW5rbm93biBzaXplIHBlciB0b3duIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBkaXN0cmlidXRpb24gZm9yIHRoZSB0b3RhbCBudW1iZXIgb2YgZmlybXMgDQpnZ3Bsb3QoZGF0YT1maXJtcywgYWVzKGxvZyhmaXJtcyR0b3RhbCkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9Ik51bWJlciBvZiBmaXJtcyAobG9nIHNjYWxlKSIsIHk9IkRlbnNpdHkiKSArDQogIGdndGl0bGUoIlRvdGFsIG51bWJlciBvZiBmaXJtcyBwZXIgdG93biIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KDQojIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIG51bWJlciBvZiBtaWNybyBzaXplIGZpcm1zIA0KZ2dwbG90KGRhdGE9ZmlybXMsIGFlcyhsb2coZmlybXMkbWljcm8pKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA1MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsNCiAgbGFicyh4PSJOdW1iZXIgb2YgZmlybXMgb2YgbWljcm8gc2l6ZSAobG9nIHNjYWxlKSIsIHk9IkRlbnNpdHkiKSArDQogIGdndGl0bGUoIk51bWJlciBvZiBmaXJtcyBvZiBtaWNybyBzaXplIHBlciB0b3duIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBkaXN0cmlidXRpb24gZm9yIHRoZSBudW1iZXIgb2Ygc21hbGwgc2l6ZSBmaXJtcyANCmdncGxvdChkYXRhPWZpcm1zLCBhZXMobG9nKGZpcm1zJHNtYWxsKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTnVtYmVyIG9mIGZpcm1zIG9mIHNtYWxsIHNpemUgKGxvZyBzY2FsZSkiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJOdW1iZXIgb2YgZmlybXMgb2Ygc21hbGwgc2l6ZSBwZXIgdG93biIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgZGlzdHJpYnV0aW9uIGZvciB0aGUgbnVtYmVyIG9mIG1lZGl1bSBzaXplIGZpcm1zIA0KZ2dwbG90KGRhdGE9ZmlybXMsIGFlcyhsb2coZmlybXMkbWVkaXVtKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTnVtYmVyIG9mIGZpcm1zIG9mIG1lZGl1bSBzaXplIChsb2cgc2NhbGUpIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiTnVtYmVyIG9mIGZpcm1zIG9mIG1lZGl1bSBzaXplIHBlciB0b3duIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBkaXN0cmlidXRpb24gZm9yIHRoZSBudW1iZXIgb2YgbGFyZ2Ugc2l6ZSBmaXJtcyANCmdncGxvdChkYXRhPWZpcm1zLCBhZXMobG9nKGZpcm1zJGxhcmdlKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTnVtYmVyIG9mIGZpcm1zIG9mIGxhcmdlIHNpemUgKGxvZyBzY2FsZSkiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJOdW1iZXIgb2YgZmlybXMgb2YgbGFyZ2Ugc2l6ZSBwZXIgdG93biIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQpDaGVjayBjb3JyZWxhdGlvbiBhbW9uZyB0aGUgdmFyaWFibGVzOg0KYGBge3IgZmlybXMgY29ycmVsYXRpb259DQpjb3JycGxvdChjb3IoZmlybXNbLDM6OF0pKQ0KYGBgDQoNCg0KIyMjIFdoYXQgaGF2ZSBsZWFybmVkDQoNClNvbHZlZCBwcm9ibGVtczogDQoNCiogVXNlIHRoZSBFVSBmaXJtcycgY2F0ZWdvcml6YXRpb24uDQoNClNvbWUgcHJvYmxlbWF0aWMgYXNwZWN0czoNCg0KKiBTb21lIHRvd25zIGhhdmUgMTAwJSBvZiBudWxsIGZpcm1zLCBzaG91bGQgd2UgcmVtb3ZlIHRoZXNlIGRhdGE/DQoqIFRoZXNlIHZhcmlhYmxlcyBhcmUgc3Ryb25nbHkgY29ycmVsYXRlZCwgc2hvdWxkIHdlIHVzZSBqdXN0IHRoZSB0b3RhbCBhbW91bnQgaW4gdGhlIGZvbGxvd2luZyBwYXJ0cz8NCg0KV2UgY291bGQgdXNlIHRoZXNlIGRhdGEgZm9yIHRoZSBmb2xsb3dpbmcgdGFza3M6DQoNCiogcHJlZGljdCB0aGUgc2FsYXJpZXMgdXNpbmcgc3VjaCBpbmZvcm1hdGlvbiBhcyBwcm94eSBmb3IgdGhlIGNvbXBldGl0aW9uIGluIHRoZSBqb2IgbWFya2V0Ow0KKiBwcmVkaWN0IHRoZSB0b3RhbCBudW1iZXIgb2YgZmlybXMsIHVzaW5nIHNhbGFyeSBkYXRhOw0KKiBnZW8tc3BhdGlhbCBwbG90IGZvciBmaXJtcycgc2l6ZSANCg0KDQoNCiMjIEdlb2dyYXBoaWMgZGF0YQ0KDQpQbG90IGF2YWlsYWJsZSB0b3ducyBvbiBhIG1hcDoNCmBgYHtyIGdlbyBtYXB9DQoNCiMgY2VudGVyIG9mIEZyYW5jZSwgb2J0YWluZWQgdXNpbmc6DQojIGZyYV9jZW50ZXIgPSBhcy5udW1lcmljKGdlb2NvZGUoIkZyYW5jZSIpKQ0KZnJhX2NlbnRlciA9IGMoMi4yMTM3NDksIDQ2LjIyNzYzOCkNCiMgcGxvdCBhbGwgRXVyb3BlYW4gdG93bnMgYXZhaWxhYmxlDQpnZW9fcG9zID0gYXMuZGF0YS5mcmFtZShjYmluZChsb24gPSBnZW8kbG9uZ2l0dWRlLCBsYXQgPSBnZW8kbGF0aXR1ZGUpKQ0KZ2VvX3BvcyA9IGdlb19wb3NbY29tcGxldGUuY2FzZXMoZ2VvX3BvcyksXQ0KZ2dtYXAoZ2V0X2dvb2dsZW1hcChjZW50ZXI9ZnJhX2NlbnRlciwgc2NhbGU9Miwgem9vbT01KSwgZXh0ZW50PSJub3JtYWwiKSArDQogIGdlb21fcG9pbnQoYWVzKHg9bG9uLCB5PWxhdCksIGRhdGE9Z2VvX3BvcywgY29sPSJvcmFuZ2UiLCBhbHBoYT0wLjIsIHNpemU9MC4wMSkgDQoNCiMgVHJ5IHRvIGNsbCBnb29nbGUgQVBJIHVudGlsIHRoZXJlIGlzIG5vIGVycm9yDQojIA0KIyBGcmFNYXAgPSBOQQ0KIyB3aGlsZSAoYW55KGlzLm5hKEZyYU1hcCkpKXsNCiMgICBGcmFNYXAgPSB0cnlDYXRjaCh7DQojICAgICAgIGdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyPWZyYV9jZW50ZXIsIHNjYWxlPTIsIHpvb209NSksIGV4dGVudD0ibm9ybWFsIikNCiMgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiMgICAgIG1lc3NhZ2UoImNhbm5vdCBvcGVuIFVSTCIpICANCiMgICAgIEZyYU1hcCA9IE5BDQojICAgfSkNCiMgfQ0KDQpgYGANCg0KIyMjIFdoYXQgd2UgaGF2ZSBsZWFybmVkIA0KDQpTb2x2ZWQgcHJvYmxlbXM6IA0KDQoqIE1pc3NpbmcgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZTsNCiogRHVwbGljYXRpb25zIGR1ZSB0byBtdWx0aXBsZSBwb3N0YWwgY29kZXMgaW4gdGhlIHNhbWUgdG93bjsNCiogRXhjbHVkZSBub24tRXVyb3BlYW4gdG93bnM7DQoNClRoZXNlIGluZm9ybWF0aW9uIGFyZSB1c2VmdWwgdG8gcGxvdCBhbnkgZnV0dXJlIGRhdGFzZXQvYW5hbHlzaXMuDQpJbiBhZGRpdGlvbiwgdGhleSBjb3VsZCBiZSB1c2VmdWwgdG8gY29tcGFyZSBFdXJvcGVhbiB0b3ducyB2cy4gb2xkIGNvbG9uaWVzLg0KDQoNCiMjIFNhbGFyeSBkYXRhIA0KDQpVbml2YXJpYXRlIGFuYWx5c2lzIGNvbXBhcmluZyBzYWxhcmllcyBmb3IgYm90aCBnZW5kZXJzIGFtb25nIHZhcmlvdXMgam9iIGNhdGVnb3JpZXM6DQoNCmBgYHtyIGJveHBsb3RzIHdvcmtzfQ0KDQojICBudW1iZXIgb2YgdW5pdHMNCm5fc2V4IDwtIGxlbmd0aChzYWxhcnkkc2FsX0ZlbWFsZXMpDQojIHZlY3RvciByZXByZXNlbnRpbmcgbWFsZXMgYW5kIGZlbWFsZXMNCkxhYmVsIDwtIGMocmVwKCJNIiwgbl9zZXgqNSksIHJlcCgiRiIsIG5fc2V4KjUpKQ0KIyB2ZWN0b3IgcmVwcmVzZW50aW5nIHRoZSB2YXJpYWJsZSBjb25zaWRlcmVkDQpWYXJpYWJsZSA8LSBjKHJlcCgiR2VuZXJhbCIsIG5fc2V4KSwgDQogICAgICAgICAgICAgcmVwKCJFeGVjdXRpdmUiLCBuX3NleCksDQogICAgICAgICAgICAgcmVwKCJNaWRNYW5hZ2VyIiwgbl9zZXgpLA0KICAgICAgICAgICAgIHJlcCgiRW1wbG95ZWUiLCBuX3NleCksDQogICAgICAgICAgICAgcmVwKCJXb3JrZXIiLG5fc2V4KSwNCiAgICAgICAgICAgICByZXAoIkdlbmVyYWwiLCBuX3NleCksIA0KICAgICAgICAgICAgIHJlcCgiRXhlY3V0aXZlIiwgbl9zZXgpLA0KICAgICAgICAgICAgIHJlcCgiTWlkTWFuYWdlciIsIG5fc2V4KSwNCiAgICAgICAgICAgICByZXAoIkVtcGxveWVlIiwgbl9zZXgpLA0KICAgICAgICAgICAgIHJlcCgiV29ya2VyIixuX3NleCkpDQojIG1lcmdlIHRoZXNlIGRhdGENCnNhbF9zZXggPSBjYmluZC5kYXRhLmZyYW1lKExhYmVsID0gTGFiZWwsIA0KICAgICAgICAgICAgIHZhbHVlID0gYyhzYWxhcnkkc2FsX01hbGVzLCBzYWxhcnkkc2FsX01fZXhlY3V0aXZlLCBzYWxhcnkkc2FsX01fbWlkTWFuYWdlciwgc2FsYXJ5JHNhbF9NX2VtcGxveWVlLCBzYWxhcnkkc2FsX01fd29ya2VyLA0KICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnkkc2FsX0ZlbWFsZXMsIHNhbGFyeSRzYWxfRl9leGVjdXRpdmUsIHNhbGFyeSRzYWxfRl9taWRNYW5hZ2VyLCBzYWxhcnkkc2FsX0ZfZW1wbG95ZWUsIHNhbGFyeSRzYWxfRl93b3JrZXIpLA0KICAgICAgICAgICAgIFZhcmlhYmxlID0gVmFyaWFibGUpDQojIHBsb3R0aW5nIHBoYXNlDQpnZ3Bsb3QoZGF0YSA9IHNhbF9zZXgsIGFlcyh4PUxhYmVsLCB5PXZhbHVlKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBMYWJlbCkpICsNCiAgIyBub3QgY29sb3IgcG9pbnRzIHJlcGxhY2luZyBjb2xvdXIgPSBncm91cCBpbnN0ZWFkIG9mIGNvbG91cj1MYWJlbA0KICBnZW9tX3BvaW50KGFlcyh5PXZhbHVlLCBjb2xvdXI9TGFiZWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuNzUpKSArDQogIGZhY2V0X3dyYXAoIH4gVmFyaWFibGUsIHNjYWxlcz0iZnJlZSIpICsNCiAgeGxhYigiU2V4IikgKyB5bGFiKCJNZWFuIG5ldCBzYWxhcnkgcGVyIGhvdXIiKSArIGdndGl0bGUoIkdlbmRlciBjb21wYXJpc29uIGZvciBkaWZmZXJlbnQgam9iIHBvc2l0aW9ucyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgICAgICBzdGF0X2JveHBsb3QoZ2VvbSA9ICJlcnJvcmJhciIsIHdpZHRoID0gMC41KQ0KICAjICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJMZWdlbmQiKSkNCg0KIyB0aGUgc2FtZSBidXQgZXhjbHVkaW5nIG91dGxpZXJzDQpnZ3Bsb3QoZGF0YSA9IHNhbF9zZXgsIGFlcyh4PUxhYmVsLCB5PXZhbHVlKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gcXVhbnRpbGUoc2FsX3NleCR2YWx1ZSwgYygwLCAwLjkpKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBMYWJlbCkpICsNCiAgZ2VvbV9wb2ludChhZXMoeT12YWx1ZSwgY29sb3VyPUxhYmVsKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjc1KSkgKw0KICBmYWNldF93cmFwKCB+IFZhcmlhYmxlLCBzY2FsZXM9ImZyZWUiKSArDQogIHhsYWIoIlNleCIpICsgeWxhYigiTWVhbiBuZXQgc2FsYXJ5IHBlciBob3VyIikgKyANCiAgZ2d0aXRsZSgiR2VuZGVyIGNvbXBhcmlzb24gZm9yIGRpZmZlcmVudCBqb2IgcG9zaXRpb25zIGV4Y2x1ZGluZyB0aGUgbGFzdCBkZWNpbGUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIHN0YXRfYm94cGxvdChnZW9tID0gImVycm9yYmFyIiwgd2lkdGggPSAwLjUpDQoNCmBgYA0KDQoNClVuaXZhcmlhdGUgYW5hbHlzaXMgY29tcGFyaW5nIHNhbGFyaWVzIGZvciBib3RoIGdlbmRlcnMgYW1vbmcgdmFyaW91cyBhZ2VzOg0KDQpgYGB7ciBib3hwbG90cyBhZ2VzfQ0KDQojIHZlY3RvciByZXByZXNlbnRpbmcgbWFsZXMgYW5kIGZlbWFsZXMNCkxhYmVsIDwtIGMocmVwKCJNIiwgbl9zZXgqMyksIHJlcCgiRiIsIG5fc2V4KjMpKQ0KIyB2ZWN0b3IgcmVwcmVzZW50aW5nIHRoZSB2YXJpYWJsZSBjb25zaWRlcmVkDQpWYXJpYWJsZSA8LSBjKHJlcCgiMTgtMjUiLCBuX3NleCksIA0KICAgICAgICAgICAgICByZXAoIjI2LTUwIiwgbl9zZXgpLA0KICAgICAgICAgICAgICByZXAoIjUxKyIsIG5fc2V4KSwNCiAgICAgICAgICAgICAgcmVwKCIxOC0yNSIsIG5fc2V4KSwgDQogICAgICAgICAgICAgIHJlcCgiMjYtNTAiLCBuX3NleCksDQogICAgICAgICAgICAgIHJlcCgiNTErIiwgbl9zZXgpKQ0KIyBtZXJnZSB0aGVzZSBkYXRhDQpzYWxfc2V4IDwtIGNiaW5kLmRhdGEuZnJhbWUoTGFiZWwgPSBMYWJlbCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IGMoc2FsYXJ5JHNhbF9NXzE4XzI1LCBzYWxhcnkkc2FsX01fMjZfNTAsIHNhbGFyeSRzYWxfTV81MXBsdXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGFyeSRzYWxfRl8xOF8yNSwgc2FsYXJ5JHNhbF9GXzI2XzUwLCBzYWxhcnkkc2FsX0ZfNTFwbHVzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhcmlhYmxlID0gVmFyaWFibGUpDQojIHBsb3R0aW5nIHBoYXNlDQpnZ3Bsb3QoZGF0YSA9IHNhbF9zZXgsIGFlcyh4PUxhYmVsLCB5PXZhbHVlKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBMYWJlbCkpICsNCiAgZ2VvbV9wb2ludChhZXMoeT12YWx1ZSwgY29sb3VyPUxhYmVsKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjc1KSkgKw0KICBmYWNldF93cmFwKCB+IFZhcmlhYmxlLCBzY2FsZXM9ImZyZWUiKSArDQogIHhsYWIoIlNleCIpICsgeWxhYigiTWVhbiBuZXQgc2FsYXJ5IHBlciBob3VyIikgKyBnZ3RpdGxlKCJHZW5kZXIgY29tcGFyaXNvbiBmb3IgZGlmZmVyZW50IGFnZXMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIHlsaW0oYyg1LCAxMDApKSArDQogIHN0YXRfYm94cGxvdChnZW9tID0gImVycm9yYmFyIiwgd2lkdGggPSAwLjUpDQoNCmBgYA0KDQoNClRoZSBpbmNvbWUgaW5lcXVhbGl0eSBiZXR3ZWVuIGdlbmRlcnMsIGFnZSBncm91cHMgYW5kIHdvcmtpbmcgcG9zaXRpb25zIGlzIGNsZWFyLg0KSW4gdGhlIGZvbGxvd2luZyBhbmFseXNlcyB0aGUgZm9jdXMgaXMgb24gdGhlIHNhbGFyeSByYXRpbyBiZXR3ZWVuIHdvbWVuIGFuZCBtZW4gYW1vbmcgZGlmZmVyZW50DQpqb2IgcG9zaXRpb25zOg0KYGBge3IgcmF0aW8gRiB2cyBNIGFjY3Jvc3Mgam9ic30NCg0KIyBHZW5kZXIgc2FsYXJ5IHJhdGlvIGFuZCBnZW5lcmFsIGxldmVsIG9mIGluY29tZQ0KDQojIE92ZXJhbGwgbWVhbiBzYWxhcnk6IFRoZSBoaWdoZXIgdGhlIG5ldCBtZWFuIGluY29tZSwgdGhlIG1vcmUgc2tld2VkIHRoZSByYXRpbyBvZiBzYWxhcnkgYmV0d2VlbiBmZW1hbGUgYW5kIG1hbGUgaXMuIE9ubHkgMiB0b3ducyBoYXZlIGEgcmF0aW8+MQ0KIyBjcmVhdGUgb3ZlcmFsbCBGIHZzIE0gcmF0aW8NCnNhbGFyeSRzYWxhcnlfcmF0aW9fRnZzTSA8LSBzYWxhcnkkc2FsX0ZlbWFsZXMgLyBzYWxhcnkkc2FsX01hbGVzDQojIGhpc3RvZ3JhbQ0KZ2dwbG90KGRhdGE9c2FsYXJ5LCBhZXMoc2FsYXJ5JHNhbGFyeV9yYXRpb19GdnNNKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsgDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKyANCiAgbGFicyh4PSJPdmVyYWxsIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiLCB5PSJEZW5zaXR5IikgKyANCiAgbGFicyh0aXRsZSA9ICJPdmVyYWxsIHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KIyBzY2F0dGVyIHBsb3QgdnMgb3ZlcmFsbCBtZWFuIHNhbGFyeQ0KZ2dwbG90KHNhbGFyeSwgYWVzKHg9IHNhbF9nZW5lcmFsLCB5PXNhbGFyeV9yYXRpb19GdnNNKSkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgY29sb3VyID0gIiMwMDkxZmYiKSsgDQogIGxhYnMoeD0iT3ZlcmFsbCBzYWxhcnkiLCB5PSJPdmVyYWxsIHNhbGFyeSByYXRpbyhmZW1hbGVzL21hbGVzKSIpICsgDQogIGxhYnModGl0bGUgPSAiT3ZlcmFsbCBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyB2cy4gb3ZlcmFsbCBzYWxhcnkiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSANCg0KDQojIEV4ZWN1dGl2ZXMgbWVhbiBzYWxhcnk6IGEgYml0IGJldHRlciB0aGUgc2l0dWF0aW9uIGZvciBmZW1hbGVzIGluIHRoaXMgY2FzZSBhbmQgbGVzcyBza2V3ZWQNCiMgY3JlYXRlIEV4ZWN1dGl2ZXMgRiB2cyBNIHJhdGlvDQpzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fRXhlYyA8LSBzYWxhcnkkc2FsX0ZfZXhlY3V0aXZlIC8gc2FsYXJ5JHNhbF9NX2V4ZWN1dGl2ZQ0KIyBoaXN0b2dyYW0NCmdncGxvdChkYXRhPXNhbGFyeSwgYWVzKHNhbGFyeSRzYWxhcnlfcmF0aW9fRnZzTV9FeGVjKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsgDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKyANCiAgbGFicyh4PSJFeGVjdXRpdmVzIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiLCB5PSJEZW5zaXR5IikgKyANCiAgbGFicyh0aXRsZSA9ICJFeGVjdXRpdmVzIHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KIyBzY2F0dGVyIHBsb3QgdnMgZXhlY3V0aXZlcyBtZWFuIHNhbGFyeQ0KZ2dwbG90KHNhbGFyeSwgYWVzKHg9IHNhbF9nZW5lcmFsLCB5PSBzYWxhcnlfcmF0aW9fRnZzTV9FeGVjKSkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgY29sb3VyID0gIiMwMDkxZmYiKSsgDQogIGxhYnMoeD0iT3ZlcmFsbCBzYWxhcnkiLCB5PSJFeGVjdXRpdmVzIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiKSArIA0KICBsYWJzKHRpdGxlID0gIkV4ZWN1dGl2ZXMgc2FsYXJ5IHJhdGlvIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMgXG4gdnMuIG92ZXJhbGwgc2FsYXJ5IikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCg0KDQojIE1pZGRsZSBtYW5hZ2VycyBtZWFuIHNhbGFyeTogLi4uLg0KIyBjcmVhdGUgTWlkZGxlIG1hbmFnZXJzIEYgdnMgTSByYXRpbw0Kc2FsYXJ5JHNhbGFyeV9yYXRpb19GdnNNX21pZE1hbmFnIDwtIHNhbGFyeSRzYWxfRl9taWRNYW5hZ2VyIC8gc2FsYXJ5JHNhbF9NX21pZE1hbmFnZXINCiMgaGlzdG9ncmFtDQpnZ3Bsb3QoZGF0YT1zYWxhcnksIGFlcyhzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fRXhlYykpICsgDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArIA0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsgDQogIGxhYnMoeD0iTWlkZGxlIG1hbmFnZXJzIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiLCB5PSJEZW5zaXR5IikgKyANCiAgbGFicyh0aXRsZSA9ICJNaWRkbGUgbWFuYWdlcnMgc2FsYXJ5IHJhdGlvIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgDQojIHNjYXR0ZXIgcGxvdCB2cyBleGVjdXRpdmVzIG1lYW4gc2FsYXJ5DQpnZ3Bsb3Qoc2FsYXJ5LCBhZXMoeD0gc2FsX2dlbmVyYWwsIHk9IHNhbGFyeV9yYXRpb19GdnNNX21pZE1hbmFnKSkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgY29sb3VyID0gIiMwMDkxZmYiKSsgDQogIGxhYnMoeD0iT3ZlcmFsbCBzYWxhcnkiLCB5PSJNaWRkbGUgbWFuYWdlcnMgc2FsYXJ5IHJhdGlvIChmZW1hbGVzL21hbGVzKSIpICsgDQogIGxhYnModGl0bGUgPSAiTWlkZGxlIG1hbmFnZXJzIHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIFxuIHZzLiBvdmVyYWxsIHNhbGFyeSIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpDQoNCg0KIyBXb3JrZXJzIG1lYW4gc2FsYXJ5OiAuLi4NCiMgY3JlYXRlIHdvcmtlcnMgRiB2cyBNIHJhdGlvDQpzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fd29ya2VyIDwtIHNhbGFyeSRzYWxfRl93b3JrZXIgLyBzYWxhcnkkc2FsX01fd29ya2VyDQojIGhpc3RvZ3JhbQ0KZ2dwbG90KGRhdGE9c2FsYXJ5LCBhZXMoc2FsYXJ5JHNhbGFyeV9yYXRpb19GdnNNX3dvcmtlcikpICsgDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArIA0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsgDQogIGxhYnMoeD0iV29ya2VycyBzYWxhcnkgcmF0aW8gKGZlbWFsZXMvbWFsZXMpIiwgeT0iRGVuc2l0eSIpICsgDQogIGxhYnModGl0bGUgPSAiV29ya2VycyBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSANCiMgc2NhdHRlciBwbG90IHZzIHdvcmtlcnMgbWVhbiBzYWxhcnkNCmdncGxvdChzYWxhcnksIGFlcyh4PSBzYWxfZ2VuZXJhbCwgeT0gc2FsYXJ5X3JhdGlvX0Z2c01fd29ya2VyKSkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgY29sb3VyID0gIiMwMDkxZmYiKSsgDQogIGxhYnMoeD0iT3ZlcmFsbCBzYWxhcnkiLCB5PSJXb3JrZXJzIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiKSArIA0KICBsYWJzKHRpdGxlID0gIldvcmtlcnMgc2FsYXJ5IHJhdGlvIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMgXG4gdnMuIG92ZXJhbGwgc2FsYXJ5IikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCg0KDQojIEVtcGxveWVlIG1lYW4gc2FsYXJ5OiAuLi4NCiMgY3JlYXRlIEVtcGxveWVlIEYgdnMgTSByYXRpbw0Kc2FsYXJ5JHNhbGFyeV9yYXRpb19GdnNNX2VtcGxveWVlIDwtIHNhbGFyeSRzYWxfRl9lbXBsb3llZSAvIHNhbGFyeSRzYWxfTV9lbXBsb3llZQ0KIyBoaXN0b2dyYW0NCmdncGxvdChkYXRhPXNhbGFyeSwgYWVzKHNhbGFyeSRzYWxhcnlfcmF0aW9fRnZzTV9lbXBsb3llZSkpICsgDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArIA0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsgDQogIGxhYnMoeD0iRW1wbG95ZWUgc2FsYXJ5IHJhdGlvIChmZW1hbGVzL21hbGVzKSIsIHk9IkRlbnNpdHkiKSArIA0KICBsYWJzKHRpdGxlID0gIkVtcGxveWVlIHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KIyBzY2F0dGVyIHBsb3QgdnMgRW1wbG95ZWUgbWVhbiBzYWxhcnkNCmdncGxvdChzYWxhcnksIGFlcyh4PSBzYWxfZ2VuZXJhbCwgeT0gc2FsYXJ5X3JhdGlvX0Z2c01fZW1wbG95ZWUpKSArICANCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBjb2xvdXIgPSAiIzAwOTFmZiIpKyANCiAgbGFicyh4PSJPdmVyYWxsIHNhbGFyeSIsIHk9IkVtcGxveWVlIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiKSArIA0KICBsYWJzKHRpdGxlID0gIkVtcGxveWVlIHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIFxuIHZzLiBvdmVyYWxsIHNhbGFyeSIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpDQoNCmBgYA0KDQoNCk5vdyB0aGUgZm9jdXMgaXMgb24gdGhlIHNhbGFyeSByYXRpbyBiZXR3ZWVuIHdvbWVuIGFuZCBtZW4gYW1vbmcgZGlmZmVyZW50IGFnZSBncm91cHM6DQpgYGB7ciByYXRpbyBGIHZzIE0gYWNjcm9zcyBhZ2VzfQ0KDQojIDE4LTI1IG1lYW4gc2FsYXJ5OiBhcmUgcXVpdGUgZXF1YWwgYXBhcnQgZnJvbSBzb21lIG91dGxpZXJzIGFuZCBhIHF1YWRyYXRpYyB0cmVuZA0KIyBjcmVhdGUgMTgtMjUgRiB2cyBNIHJhdGlvDQpzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fMThfMjUgPC0gc2FsYXJ5JHNhbF9GXzE4XzI1IC8gc2FsYXJ5JHNhbF9NXzE4XzI1DQojIGhpc3RvZ3JhbQ0KZ2dwbG90KGRhdGE9c2FsYXJ5LCBhZXMoc2FsYXJ5JHNhbGFyeV9yYXRpb19GdnNNXzE4XzI1KSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsgDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKyANCiAgbGFicyh4PSIxOC0yNSBzYWxhcnkgcmF0aW8gKGZlbWFsZXMvbWFsZXMpIiwgeT0iRGVuc2l0eSIpICsgDQogIGxhYnModGl0bGUgPSAiMTgtMjUgc2FsYXJ5IHJhdGlvIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgDQojIHNjYXR0ZXIgcGxvdCB2cyAxOC0yNSBtZWFuIHNhbGFyeQ0KZ2dwbG90KHNhbGFyeSwgYWVzKHg9IHNhbF9nZW5lcmFsLCB5PSBzYWxhcnlfcmF0aW9fRnZzTV8xOF8yNSkpICsgIA0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGNvbG91ciA9ICIjMDA5MWZmIikrIA0KICBsYWJzKHg9Ik92ZXJhbGwgc2FsYXJ5IiwgeT0iMTgtMjUgc2FsYXJ5IHJhdGlvIChmZW1hbGVzL21hbGVzKSIpICsgDQogIGxhYnModGl0bGUgPSAiMTgtMjUgc2FsYXJ5IHJhdGlvIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMgXG4gdnMuIG92ZXJhbGwgc2FsYXJ5IikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCiMgc2NhdHRlciBwbG90IHZzIDE4LTI1IG1lYW4gc2FsYXJ5IGZvciB0aGVtDQpnZ3Bsb3Qoc2FsYXJ5LCBhZXMoeD0gc2FsXzE4XzI1LCB5PSBzYWxhcnlfcmF0aW9fRnZzTV8xOF8yNSkpICsgIA0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGNvbG91ciA9ICIjMDA5MWZmIikrIA0KICBsYWJzKHg9IjE4LTI1IHNhbGFyeSIsIHk9IjE4LTI1IHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiKSArIA0KICBsYWJzKHRpdGxlID0gIjE4LTI1IHNhbGFyeSByYXRpbyBiZXR3ZWVuIGZlbWFsZXMgYW5kIG1hbGVzIFxuIHZzLiBvdmVyYWxsIDE4LTI1IHNhbGFyeSIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpDQoNCg0KIyAyNi01MCBtZWFuIHNhbGFyeTogLi4uDQojIGNyZWF0ZSAyNi01MCBGIHZzIE0gcmF0aW8NCnNhbGFyeSRzYWxhcnlfcmF0aW9fRnZzTV8yNl81MCA8LSBzYWxhcnkkc2FsX0ZfMjZfNTAgLyBzYWxhcnkkc2FsX01fMjZfNTANCiMgaGlzdG9ncmFtDQpnZ3Bsb3QoZGF0YT1zYWxhcnksIGFlcyhzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fMjZfNTApKSArIA0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA1MCkgKyANCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArIA0KICBsYWJzKHg9IjI2LTUwIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiLCB5PSJEZW5zaXR5IikgKyANCiAgbGFicyh0aXRsZSA9ICIyNi01MCBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSANCiMgc2NhdHRlciBwbG90IHZzIDI2LTUwIG1lYW4gc2FsYXJ5DQpnZ3Bsb3Qoc2FsYXJ5LCBhZXMoeD0gc2FsX2dlbmVyYWwsIHk9IHNhbGFyeV9yYXRpb19GdnNNXzI2XzUwKSkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgY29sb3VyID0gIiMwMDkxZmYiKSsgDQogIGxhYnMoeD0iT3ZlcmFsbCBzYWxhcnkiLCB5PSIyNi01MCBzYWxhcnkgcmF0aW8gKGZlbWFsZXMvbWFsZXMpIikgKyANCiAgbGFicyh0aXRsZSA9ICIyNi01MCBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyBcbiB2cy4gb3ZlcmFsbCBzYWxhcnkiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KDQoNCiMgNTErIG1lYW4gc2FsYXJ5OiAuLi4NCiMgY3JlYXRlIDUxKyBGIHZzIE0gcmF0aW8NCnNhbGFyeSRzYWxhcnlfcmF0aW9fRnZzTV81MXBsdXMgPC0gc2FsYXJ5JHNhbF9GXzUxcGx1cyAvIHNhbGFyeSRzYWxfTV81MXBsdXMNCiMgaGlzdG9ncmFtDQpnZ3Bsb3QoZGF0YT1zYWxhcnksIGFlcyhzYWxhcnkkc2FsYXJ5X3JhdGlvX0Z2c01fNTFwbHVzKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsgDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKyANCiAgbGFicyh4PSI1MSsgc2FsYXJ5IHJhdGlvIChmZW1hbGVzL21hbGVzKSIsIHk9IkRlbnNpdHkiKSArIA0KICBsYWJzKHRpdGxlID0gIjUxKyBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSANCiMgc2NhdHRlciBwbG90IHZzIDI2LTUwIG1lYW4gc2FsYXJ5DQpnZ3Bsb3Qoc2FsYXJ5LCBhZXMoeD0gc2FsX2dlbmVyYWwsIHk9IHNhbGFyeV9yYXRpb19GdnNNXzUxcGx1cykpICsgIA0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGNvbG91ciA9ICIjMDA5MWZmIikrIA0KICBsYWJzKHg9Ik92ZXJhbGwgc2FsYXJ5IiwgeT0iNTErIHNhbGFyeSByYXRpbyAoZmVtYWxlcy9tYWxlcykiKSArIA0KICBsYWJzKHRpdGxlID0gIjUxKyBzYWxhcnkgcmF0aW8gYmV0d2VlbiBmZW1hbGVzIGFuZCBtYWxlcyBcbiB2cy4gb3ZlcmFsbCBzYWxhcnkiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KDQpgYGANCg0KDQpIaWdobGlnaHQgYml2YXJpYXRlIHJlbGF0aW9uczoNCmBgYHtyIGJpdmFyaWF0ZSBwbG90c30NCg0KIyBjb3JyZWxhdGlvbiBtYXRyaXgNCmNvcnJwbG90KGNvcihzYWxhcnlbLCAzOm5jb2woc2FsYXJ5KV0pLCBtZXRob2QgPSAiY2lyY2xlIiwgdGl0bGUgPSAiQ29ycmVsYXRpb24gbWF0cml4IGZvciBzYWxhcnkgZGF0YSIsIA0KICAgICAgICAgZGlhZyA9IFQsIHRsLmNleD0wLjUsIHR5cGU9Imxvd2VyIiwgDQogICAgICAgICB0bC5jb2wgPSAiYmxhY2siLCBtYXI9YygwLDAsMS41LDApKSANCg0KIyBtb3N0IGdlbmVyYWwgcGFpcnMNCnBhaXJzKHNhbGFyeVtjKDM6OCwgMTMsIDE4OjIwKV0sIGdhcD0wLCBtYWluID0gIlNjYXR0ZXIgbWF0cml4IG9mIHRoZSBtYWluIHZhcmlhYmxlcyBpbiBzYWxhcnkgZGF0YSIsIGNleCA9IDAuNikNCiMgcGFpcnMgaGlnaGxpZ2h0aW5nIGdlbmRlcnMnIGRpZmZlcmVuY2VzDQpwYWlycyhzYWxhcnlbYyg5OjEyLCAxNDoxNyldLCBnYXA9MCwgbWFpbiA9ICJTY2F0dGVyIG1hdHJpeCBvZiBqb2IgY2F0ZWdvcmllcyBmb3IgYm90aCBnZW5kZXJzIiwgY2V4ID0gMC42KQ0KDQpgYGANCg0KIyMjIFdoYXQgd2UgaGF2ZSBsZWFybmVkDQoNCi4uLi4NCg0KDQoNCiMjIFBvcHVsYXRpb24gZGF0YSANCg0KUGxvdCBwb3B1bGF0aW9uIGRhdGEgYWNyb3NzIGRpZmZlcmVudCB0b3duczoNCmBgYHtyIHBsb3R0aW5nIHNvbWUgcG9wdWxhdGlvbiBkYXRhIGluIGxvZzEwfQ0KDQojIEhpc3RvZ3JhbSBvZiB0b3RhbCBwb3B1bGF0aW9uIHBlciB0b3duIGluIGxvZw0KZ2dwbG90KGRhdGE9cG9wdWxhdGlvbiwgYWVzKGlmZWxzZSh0b3RhbF9wb3B1bGF0aW9uIT0wLCBsb2cxMCh0b3RhbF9wb3B1bGF0aW9uKSwgMCkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9IkxvZzEwIHRvdGFsIHBvcHVsYXRpb24iLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJIaXN0b2dyYW0gb2YgdG90YWwgcG9wdWxhdGlvbiBwZXIgdG93biBpbiBsb2cxMCBzY2FsZSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgSGlzdG9ncmFtIG9mIGRlcGVuZGVuY3kgcmF0aW8gcGVyIHRvd24NCmdncGxvdChkYXRhPXBvcHVsYXRpb24sIGFlcyhpZmVsc2UoZGVwZW5kZW5jeV9yYXRpbyE9MCwgbG9nMTAoZGVwZW5kZW5jeV9yYXRpbyksIDApKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA1MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsNCiAgbGFicyh4PSJMb2cxMCBkZXBlbmRlbmN5IHJhdGlvIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiSGlzdG9ncmFtIG9mIGRlcGVuZGVuY3kgcmF0aW8gcGVyIHRvd24gaW4gbG9nMTAgc2NhbGUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQojIEhpc3RvZ3JhbSBvZiBzZXggcmF0aW8gcGVyIHRvd24NCmdncGxvdChkYXRhPXBvcHVsYXRpb24sIGFlcyhpZmVsc2Uoc2V4X3JhdGlvIT0wLCBsb2cxMChzZXhfcmF0aW8pLCAwKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTG9nMTAgc2V4IHJhdGlvIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiSGlzdG9ncmFtIG9mIHNleCByYXRpbyBwZXIgdG93biBpbiBsb2cxMCBzY2FsZSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQpVbmRlcnN0YW5kIHdoaWNoIHRvd25zIGhhdmUgdGhlIGhpZ2hlc3QgY29uY2VudHJhdGlvbiBvZiBwb3B1bGF0aW9uIGFuZCByYXRpb3MuDQpgYGB7ciBnZW8gcGxvdCBmb3IgZWFjaCB0b3dufQ0KDQojIE1lcmdlIGdlb2dyYXBoeSBhbmQgcG9wdWxhdGlvbiBkYXRhDQojIGdlb19wb3B1bGF0aW9uIDwtIG1lcmdlKGdlbywgcG9wdWxhdGlvbiwgYnk9IkNPREdFTyIpDQpnZW9fcG9wdWxhdGlvbiA8LSBjYmluZC5kYXRhLmZyYW1lKGdlb1t1bmlxdWUoZ2VvJENPREdFTykgJWluJSB1bmlxdWUocG9wdWxhdGlvbiRDT0RHRU8pLF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25bdW5pcXVlKHBvcHVsYXRpb24kQ09ER0VPKSAlaW4lIHVuaXF1ZShnZW8kQ09ER0VPKSxdKQ0KDQojIEZyYW5jZSBtYXANCkZyYU1hcCA9IGdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyPWZyYV9jZW50ZXIsIHNjYWxlPTIsIHpvb209NiksIGV4dGVudD0ibm9ybWFsIikNCg0KIyBQbG90ICJEaXN0cmlidXRpb24gb2YgdG90YWwgcG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIiANCnNjIDwtIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9cGFsZXR0ZShyYWluYm93KDMpKSwgbGltaXRzPWMobG9nMTAobWluKGdlb19wb3B1bGF0aW9uJHRvdGFsX3BvcHVsYXRpb24pKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzEwKG1heChnZW9fcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uKSkpKSANCnBvcHVsYXRpb25fZGlzdHJpYnV0aW9uIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvX3BvcHVsYXRpb24kbG9uZ2l0dWRlLCB5PWdlb19wb3B1bGF0aW9uJGxhdGl0dWRlLA0KICAgICAgICAgICAgICAgICBjb2xvdXI9bG9nMTAoZ2VvX3BvcHVsYXRpb24kdG90YWxfcG9wdWxhdGlvbikpLCANCiAgICAgICAgICAgICBkYXRhPWdlb19wb3B1bGF0aW9uLCBhbHBoYT0wLjIsIHNpemU9MC4wMSkgKw0KICAgICAgICAgICAgIHNjICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgUG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIikNCnBvcHVsYXRpb25fZGlzdHJpYnV0aW9uDQoNCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIGFnZWQgZGVwZW5kZW5jeSByYXRpbyBmb3IgZWFjaCB0b3duIg0Kc2MgPC0gc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID1wYWxldHRlKHJhaW5ib3coNCkpLCBsaW1pdHM9YyhtaW4oZ2VvX3BvcHVsYXRpb24kYWdlZF9kZXBlbmRlbmN5X3JhdGlvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChnZW9fcG9wdWxhdGlvbiRhZ2VkX2RlcGVuZGVuY3lfcmF0aW8pKSkNCmFnZWRfcmF0aW9fZGlzdHJpYnV0aW9uIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvX3BvcHVsYXRpb24kbG9uZ2l0dWRlLCB5PWdlb19wb3B1bGF0aW9uJGxhdGl0dWRlLA0KICAgICAgICAgICAgICAgICBjb2xvdXI9Z2VvX3BvcHVsYXRpb24kYWdlZF9kZXBlbmRlbmN5X3JhdGlvKSwNCiAgICAgICAgICAgICBkYXRhPWdlb19wb3B1bGF0aW9uLCBhbHBoYT0xLCBzaXplPTEpICsNCiAgICAgICAgICAgICBzYyArIGxhYnMoY29sb3I9JycpICsgZ2d0aXRsZSgiQWdlZCBkZXBlbmRlbmN5IHJhdGlvIHBlciB0b3duIikNCmFnZWRfcmF0aW9fZGlzdHJpYnV0aW9uDQoNCiMgd2hpY2ggY2l0eSBoYXMgdGhlIGhpZ2hlc3QvbG93ZXN0IGFnZWQgZGVwZW5kZW5jeSByYXRpbyANCmdlb19wb3B1bGF0aW9uW3doaWNoLm1pbihnZW9fcG9wdWxhdGlvbiRhZ2VkX2RlcGVuZGVuY3lfcmF0aW8pLGMoJ0NPREdFTycpXSAgDQpnZW9fcG9wdWxhdGlvblt3aGljaC5tYXgoZ2VvX3BvcHVsYXRpb24kYWdlZF9kZXBlbmRlbmN5X3JhdGlvKSxjKCdDT0RHRU8nKV0gIA0KIA0KIyBnZXQgbGlzdCBvZiB0b3AgMTAgaGlnaGVzdCBhZ2VkIGRlcGVuZGVuY3kgcmF0aW8gDQpSYW5rID0gc29ydChwb3B1bGF0aW9uJGFnZWRfZGVwZW5kZW5jeV9yYXRpbywgVFJVRSkgDQpSYW5rMTAgPSBzb3J0KFJhbmtbMToxMF0sIFRSVUUpIA0Kc29sID0gcmVwKDAsIDEwKSANCmZvciAoaSBpbiAxOjEwKXsgDQogICAgc29sW2ldID0gcGFzdGUoZ2VvX3BvcHVsYXRpb24kQ09ER0VPW2dlb19wb3B1bGF0aW9uJGFnZWRfZGVwZW5kZW5jeV9yYXRpbyAlaW4lIFJhbmsxMFtpXV0gKQ0KfSANCg0KIyByZW1vdmUgdGhlIGRhdGFzZXQgY3JlYXRlZCB0byBzYXZlIG1lbW9yeQ0Kcm0obGlzdCA9IGMoImdlb19wb3B1bGF0aW9uIiwgIlJhbmsiLCAiUmFuazEwIikpIA0KDQpgYGANCg0KDQojIyMgV2hhdCB3ZSBoYXZlIGxlYXJuZWQgDQoNCldlIGNhbiBvYnNlcnZlIHRoYXQgaW4gdXJiYW4gY2l0aWVzLCB0aGUgYWdlZCBkZXBlbmRjeSByYXRpbyBpcyBsb3dlciBjb21wYXJlZCB0byBub24tdXJiYW4gY2l0aWVzLiANClRoZSBjaXR5IHdpdGggdGhlIGxvd2VzdCBhZ2VkIGRlcGVuZGVuY3kgcmF0aW8gaXMgTGEgRmVydD8tc291cy1Kb3VhcnJlIGFuZCB0aGUgaGlnaGVzdCBpcyBMJ0FpZ3VpbGxvbi1zdXItTWVyLiANCg0KIyMgRWR1Y2F0aW9uIGRhdGENCg0KYGBge3IgRGVzY3JpcHRpdmUgc3RhdGlzdGljcy1FZHVjYXRpb259IA0KIA0KIyBEaWZmZXJlbmNlIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlJ3MgZWR1Y2F0aW9uIGxldmVsICh3aXRoIFVuaXZlcnNpdHkgZGVncmVlcykgDQpzdW1tYXJ5KGVkdWMkbWFsZV9Vbml2KSANCnN1bW1hcnkoZWR1YyRmZW1hbGVfVW5pdikgDQpzdW1tYXJ5KGVkdWMkbWFsZV9Ob0RpcCkgDQpzdW1tYXJ5KGVkdWMkZmVtYWxlX05vRGlwKSANCiANCiMgTWVyZ2UgZ2VvIGFuZCBlZHVjIGRhdGEgdG8gaW5jbHVkZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGluIHRoZSBkYXRhLiANCmdlb19lZHVjIDwtIGNiaW5kLmRhdGEuZnJhbWUoZ2VvW3VuaXF1ZShnZW8kQ09ER0VPKSAlaW4lIHVuaXF1ZShlZHVjJENPREdFTyksXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkdWNbdW5pcXVlKGVkdWMkQ09ER0VPKSAlaW4lIHVuaXF1ZShnZW8kQ09ER0VPKSxdKSANCiANCiMgY2hlY2sgdGhlIHJhdGlvIG9mIGluZGl2aWR1YWxzIHdpdGggaGlnaCBhbmQgbG93IGVkdWNhdGlvbiBsZXZlbCBmb3IgZWFjaCB0b3duLiANCnN1bW1hcnkoZWR1YyR1bml2L2VkdWMkbm9kaXApIA0KIA0KIyBIaXN0b2dyYW0gb2YgdGhlIHJhdGlvIG9mIGluZGl2aWR1YWxzIHdpdGggaGlnaCBhbmQgbG93IGVkdWNhdGlvbiBsZXZlbCBmb3IgZWFjaCB0b3duIA0KZ2dwbG90KGRhdGE9ZWR1YywgYWVzKGxvZyhlZHVjJHVuaXYvZWR1YyRub2RpcCkpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGE9MC4zLCBiaW5zPSA1MCkrIA0KICAgICAgICAgICAgICAgICAgIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKyANCiAgICAgICAgICAgICAgICAgICBsYWJzKHg9ImxvZy4gVW5pdmVyc2l0eSBkZWdyZWUvTm8gZGlwbG9tYSIsIHk9IkRlbnNpdHkiKSArIA0KICAgICAgICAgICAgICAgICAgIGdndGl0bGUoIlJhdGlvIGJldHdlZW4gdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyB3aXRoIG5vIGRpcGxvbWEgYW5kICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGluZGl2aWR1YWxzIHdpdGggVW5pdmVyc2l0eSBkZWdyZWUgcGVyIHRvd24iKSArIA0KICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChoanVzdD0gMC41KSkgDQogDQojIEFkanVzdCBzY2FsZXMgYmVmb3JlIHBsb3R0aW5nIA0Kc2MgPC0gc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gYygiIzU2ZGRjNSIsICIjRjg3NjZEIiwgIiMwMEJBMzgiKSwgbGltaXRzPWMobWluKGVkdWMkdW5pdi9lZHVjJG5vZGlwKSwgbWF4KGVkdWMkdW5pdi9lZHVjJG5vZGlwKSkpIA0KIA0KIyBNYXAgcmF0aW8gIA0KZWR1Y19yYXRpb19Ob0RpcFVuaXYgPC0gDQogIEZyYU1hcCArIA0KICBnZW9tX3BvaW50KGFlcyh4PWdlb19lZHVjJGxvbmdpdHVkZSwgeT1nZW9fZWR1YyRsYXRpdHVkZSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1lZHVjJHVuaXYvZWR1YyRub2RpcCksIA0KICAgICAgICAgICAgIGRhdGE9Z2VvX2VkdWMsIGFscGhhPTEsIHNpemU9MSkgKyANCiAgICAgICAgICAgICBzYyArIGxhYnMoY29sb3I9J1JhdGlvJykgKyBnZ3RpdGxlKCJFZHVjYXRpb24gbGV2ZWwgcmF0aW8gZm9yIGVhY2ggdG93biIpIA0KZWR1Y19yYXRpb19Ob0RpcFVuaXYgDQogDQojIFN0YXRpc3RpY3Mgb2Ygc2hhcGUgZm9yIHRoZSBkYXRhIA0KIyBsaWJyYXJ5KHBzeWNoKSANCmRlc2NyaWJlKGVkdWMkdW5pdi9lZHVjJG5vZGlwKSANCiANCiMgcmVtb3ZlIHRoZSBkYXRhc2V0IGNyZWF0ZWQgdG8gc2F2ZSBtZW1vcnkgDQpybShsaXN0PWMoImdlb19lZHVjIiwgImVkdWNfcmF0aW9fTm9EaXBVbml2IikpIA0KIA0KYGBgIA0KIA0KIyMjIFdoYXQgd2UgaGF2ZSBsZWFybmVkIA0KIA0KSW4gYXZlcmFnZSA5NjAgbWFsZXMgaGF2ZSB1bml2ZXJzaXR5IGRlZ3JlZSBhbmQgMTEwMCBmZW1hbGVzIGhhdmUgdW5pdmVyc2l0eSBkZWdyZWUuICANCkluIGF2ZXJhZ2UgOTA0IG1hbGVzIGhhdmUgbm8gZGlwbG9tYSBhbmQgMTI0MiBmZW1hbGVzIGhhdmUgbm8gZGlwbG9tYS4gDQpIaWdoZXN0IHJhdGlvIGluIFBhcmlzPTc4MjUxPVl2ZWxpbmVzIA0KIA0KDQojIyBEZW1vZ3JhcGhpYy9zb2NpYWwgcHJvZmlsZXMgZGF0YQ0KDQpgYGB7ciBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIERlbW9ncmFwaGljIHByb2ZpbGVzfQ0KZ2xpbXBzZShjYXRlZ19zb2NpbykNCg0KIyBtZXJnZSBnZW8gYW5kIGNhdGVnX3NvY2lvDQpnZW9fY2F0ZWdfc29jaW8gPC0gY2JpbmQuZGF0YS5mcmFtZShnZW9bdW5pcXVlKGdlbyRDT0RHRU8pICVpbiUgdW5pcXVlKGNhdGVnX3NvY2lvJENPREdFTyksXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdfc29jaW9bdW5pcXVlKGNhdGVnX3NvY2lvJENPREdFTykgJWluJSB1bmlxdWUoZ2VvJENPREdFTyksXSkNCg0KIyBmaW5kIHdoaWNoIGNpdGllcyBoYXZlIHRoZSBtb3N0IGFuZCBsZWFzdCBudW1iZXIgb2YgaW1taWdyYW50cw0KY2F0ZWdfc29jaW9bd2hpY2gubWF4KGNhdGVnX3NvY2lvJHRvdGFsX2ltbWlnKSxjKCdDT0RHRU8nKV0gDQpjYXRlZ19zb2Npb1t3aGljaC5taW4oY2F0ZWdfc29jaW8kdG90YWxfaW1taWcpLGMoJ0NPREdFTycpXSANCg0KIyBnZXQgbGlzdCBvZiB0b3AgMTAgY2l0aWVzIHdpdGggdGhlIG1vc3QgbnVtYmVyIG9mIGltbWlncmFudHMNClJhbmsgPSBzb3J0KGNhdGVnX3NvY2lvJHRvdGFsX2ltbWlnLCBUUlVFKQ0KUmFuazEwID0gc29ydChSYW5rWzE6MTBdLCBUUlVFKQ0Kc29sID0gcmVwKDAsIDEwKQ0KZm9yIChpIGluIDE6MTApew0KICAgIHNvbFtpXSA9IHBhc3RlKGdlb19jYXRlZ19zb2NpbyRyZWdpb25bY2F0ZWdfc29jaW8kdG90YWxfaW1taWcgJWluJSBSYW5rMTBbaV1dICkNCn0NCg0KIyBBZGp1c3Qgc2NhbGVzIGJlZm9yZSBwbG90dGluZyANCnNjIDwtIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoIiNGODc2NkQiLCAgIiMwMEJBMzgiKSwgbGltaXRzPWMobWluKGdlb19jYXRlZ19zb2NpbyR0b3RhbF9pbW1pZy9wb3B1bGF0aW9uJHRvdGFsX3BvcHVsYXRpb24pLCBtYXgoZ2VvX2NhdGVnX3NvY2lvJHRvdGFsX2ltbWlnL3BvcHVsYXRpb24kdG90YWxfcG9wdWxhdGlvbikpKSANCiANCiMgTWFwIHJhdGlvICANCmRpc3RyaWJ1dGlvbl9pbW1pZyA8LSANCiAgRnJhTWFwICsgDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvX2NhdGVnX3NvY2lvJGxvbmdpdHVkZSwgeT1nZW9fY2F0ZWdfc29jaW8kbGF0aXR1ZGUsIA0KICAgICAgICAgICAgICAgICBjb2xvdXI9Z2VvX2NhdGVnX3NvY2lvJHRvdGFsX2ltbWlnL3BvcHVsYXRpb24kdG90YWxfcG9wdWxhdGlvbiksIA0KICAgICAgICAgICAgIGRhdGE9Z2VvX2NhdGVnX3NvY2lvLCBhbHBoYT0xLCBzaXplPTEpICsgDQogICAgICAgICAgICAgc2MgKyBsYWJzKGNvbG9yPScnKSArIGdndGl0bGUoIkltbWlncmFudHMgZm9yIGVhY2ggdG93biIpIA0KZGlzdHJpYnV0aW9uX2ltbWlnDQoNCnJtKGdlb19jYXRlZ19zb2NpbykNCmBgYA0KDQojIyMgV2hhdCB3ZSBoYXZlIGxlYXJuZWQNCg0KQ2l0eSB3aXRoIHRoZSBsZWFzdCBudW1iZXIgb2YgaW1taWdyYW50cyBpcyBSZW5uZXMgYW5kIHRoZSBtb3N0IGlzIFBhcmlzLg0KPSLDjmxlLWRlLUZyYW5jZSIgICAgICAgICAgICAgICJQcm92ZW5jZS1BbHBlcy1Dw7R0ZSBkJ0F6dXIiICJSaMO0bmUtQWxwZXMiICAgICAgICAgICAgICAgICJNaWRpLVB5csOpbsOpZXMiICAgICAgICAgICAgIA0KIlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIgIkFsc2FjZSIgICAgICAgICAgICAgICAgICAgICAiTGFuZ3VlZG9jLVJvdXNzaWxsb24iICAgICAgICLDjmxlLWRlLUZyYW5jZSIgICAgICAgICAgICAgDQoiw45sZS1kZS1GcmFuY2UiICAgICAgICAgICAgICAiw45sZS1kZS1GcmFuY2UiICAgICAgICAgICAgIA0KDQoNCiMjIFdvcmsgc3RhdHVzIGRhdGENCg0KYGBge3IgZGVzY3JpcHRpdmUgc3RhdHMgc3RhdHVzX3dvcmt9DQoNCnN1bW1hcnkoc3RhdHVzX3dvcmspDQpuYW1lcyhzdGF0dXNfd29yaykgDQoNCiMgbWVyZ2UgZ2VvIGFuZCBzdGF0dXNfd29yaw0KIyBnZW9fc3RhdHVzX3dvcmsgPC0gY2JpbmQuZGF0YS5mcmFtZShnZW9bdW5pcXVlKGdlbyRDT0RHRU8pICVpbiUgdW5pcXVlKHN0YXR1c193b3JrJENPREdFTyksXSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfd29ya1t1bmlxdWUoc3RhdHVzX3dvcmskQ09ER0VPKSAlaW4lIHVuaXF1ZShnZW8kQ09ER0VPKSxdKQ0KZ2VvX3N0YXR1c193b3JrIDwtIGNiaW5kLmRhdGEuZnJhbWUoZ2VvW3VuaXF1ZShnZW8kQ09ER0VPKSAlaW4lIHVuaXF1ZShzdGF0dXNfd29yayRDT0RHRU8pLF0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX3dvcmtbdW5pcXVlKHN0YXR1c193b3JrJENPREdFTykgJWluJSB1bmlxdWUoZ2VvJENPREdFTyksXSkgDQoNCg0KIyB3aGljaCB0b3duIGhhcyB0aGUgbW9zdC9sZWFzdCBwZW9wbGUgcmVjZWl2aW5nIGdvdmVybm1lbnQgdHJhbnNmZXJzDQpzdGF0dXNfd29ya1t3aGljaC5tYXgoc3RhdHVzX3dvcmskdHJhbnNmZXIpLGMoJ0NPREdFTycpXSANCnN0YXR1c193b3JrW3doaWNoLm1pbihzdGF0dXNfd29yayR0cmFuc2ZlciksYygnQ09ER0VPJyldIA0KDQojIGdldCBsaXN0IG9mIHRvcCAxMCBjaXRpZXMgd2l0aCB0aGUgcGVvcGxlIHJlY2VpdmluZyBnb3Zlcm5tZW50IHRyYW5zZmVycw0KUmFuayA9IHNvcnQoc3RhdHVzX3dvcmskdHJhbnNmZXIsIFRSVUUpDQpSYW5rMTAgPSBzb3J0KFJhbmtbMToxMF0sIFRSVUUpDQpzb2wgPSByZXAoMCwgMTApDQpmb3IgKGkgaW4gMToxMCl7DQogICAgc29sW2ldID0gcGFzdGUoZ2VvX3N0YXR1c193b3JrJENPREdFT1tzdGF0dXNfd29yayR0cmFuc2ZlciAlaW4lIFJhbmsxMFtpXV0pDQp9DQoNCiMgd2hpY2ggdG93biBoYXMgdGhlIG1vc3QvbGVhc3QgcGVvcGxlIHdpdGggcGFydCB0aW1lIGNvbnRyYWN0Lg0Kc3RhdHVzX3dvcmtbd2hpY2gubWF4KHN0YXR1c193b3JrJGhhbGYpLGMoJ0NPREdFTycpXSANCnN0YXR1c193b3JrW3doaWNoLm1pbihzdGF0dXNfd29yayRoYWxmKSxjKCdDT0RHRU8nKV0gDQoNCiMgZ2V0IGxpc3Qgb2YgdG9wIDEwIGNpdGllcyB3aXRoIHRoZSBtb3N0IHBhcnQtdGltZSBjb250cmFjdC4NClJhbmsgPSBzb3J0KHN0YXR1c193b3JrJGhhbGYsIFRSVUUpDQpSYW5rMTAgPSBzb3J0KFJhbmtbMToxMF0sIFRSVUUpDQpzb2wgPSByZXAoMCwgMTApDQpmb3IgKGkgaW4gMToxMCl7DQogICAgc29sW2ldID0gcGFzdGUoc3RhdHVzX3dvcmskQ09ER0VPW3N0YXR1c193b3JrJGhhbGYgJWluJSBSYW5rMTBbaV1dICkgDQp9DQoNCg0KIyBIaXN0b2dyYW0gKEZlbWFsZXMgd2l0aCBmdWxsIHRpbWUgY29udHJhY3QpDQpkZXNjcmliZShzdGF0dXNfd29yayRmdWxsX0YpICAgICMgU2tld25lc3MNCg0KZ2dwbG90KGRhdGE9c3RhdHVzX3dvcmssIGFlcyhpZmVsc2UoZnVsbF9GIT0wLCBsb2coZnVsbF9GKSwgMCkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDUwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9IiIsIHk9IkRlbnNpdHkiKSArDQogIGdndGl0bGUoIkZlbWFsZSB3b3JrZXJzIHdpdGggZnVsbCB0aW1lIGNvbnRyYWN0IikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgSGlzdG9ncmFtIChtYWxlcyB3aXRoIGZ1bGwgdGltZSBjb250cmFjdCkgDQpkZXNjcmliZShzdGF0dXNfd29yayRmdWxsX00pICAgICMgU2tld25lc3MgDQoNCmdncGxvdChkYXRhPXN0YXR1c193b3JrLCBhZXMoaWZlbHNlKGZ1bGxfTSE9MCwgbG9nKGZ1bGxfTSksIDApKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA1MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsNCiAgbGFicyh4PSIiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJNYWxlIHdvcmtlcnMgd2l0aCBmdWxsIHRpbWUgY29udHJhY3QiKSArIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyBIaXN0b2dyYW06IENvbnRyYWN0IHR5cGUgcmF0aW8NCmRlc2NyaWJlKHN0YXR1c193b3JrJGhhbGYvc3RhdHVzX3dvcmskZnVsbCkgICAgIyBTa2V3bmVzcw0KDQpnZ3Bsb3QoZGF0YT1zdGF0dXNfd29yaywgYWVzKHN0YXR1c193b3JrJGhhbGYvc3RhdHVzX3dvcmskZnVsbCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNTApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iQ29udHJhY3QgdHlwZSByYXRpbyAodGVtcG9yYXJ5L2Z1bGwgdGltZSkiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJDb250cmFjdCB0eXBlIHJhdGlvIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KYGBgDQoNCmBgYHtyIEJveCBwbG90fQ0KIyAgbnVtYmVyIG9mIHVuaXRzDQpuX3NleCA8LSBsZW5ndGgoc3RhdHVzX3dvcmskd2FnZWFybmVyX0YpDQoNCiMgdmVjdG9yIHJlcHJlc2VudGluZyBtYWxlcyBhbmQgZmVtYWxlcw0KTGFiZWwgPC0gYyhyZXAoIk0iLCBuX3NleCo2KSwgcmVwKCJGIiwgbl9zZXgqNikpDQoNCiMgdmVjdG9yIHJlcHJlc2VudGluZyB0aGUgdmFyaWFibGUgY29uc2lkZXJlZA0KVmFyaWFibGUgPC0gYyhyZXAoIldhZ2Vhcm5lciIsIG5fc2V4KSwgDQogICAgICAgICAgICAgcmVwKCJJbmRlcGVuZGVudCIsIG5fc2V4KSwNCiAgICAgICAgICAgICByZXAoIlRyYW5zZmVyIiwgbl9zZXgpLA0KICAgICAgICAgICAgIHJlcCgiRW1wbG95ZXIiLCBuX3NleCksDQogICAgICAgICAgICAgcmVwKCJGdWxsIixuX3NleCksDQogICAgICAgICAgICAgcmVwKCJIYWxmIiwgbl9zZXgpKQ0KICAgICAgICAgICAgIA0KIyBtZXJnZSB0aGVzZSBkYXRhDQpzdGF0dXNfc2V4ID0gY2JpbmQuZGF0YS5mcmFtZShMYWJlbCA9IExhYmVsLCANCiAgICAgICAgICAgICB2YWx1ZSA9IGMoc3RhdHVzX3dvcmskd2FnZWFybmVyX00sIHN0YXR1c193b3JrJHdhZ2Vhcm5lcl9GLCBzdGF0dXNfd29yayRpbmRlcGVuZGVudF9NLA0KICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfd29yayRpbmRlcGVuZGVudF9GLCBzdGF0dXNfd29yayR0cmFuc2Zlcl9NLCBzdGF0dXNfd29yayR0cmFuc2Zlcl9GLA0KICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNfd29yayRlbXBsb3llcl9NLCBzdGF0dXNfd29yayRlbXBsb3llcl9GLCBzdGF0dXNfd29yayRmdWxsX00sIHN0YXR1c193b3JrJGZ1bGxfRiwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX3dvcmskaGFsZl9NLCBzdGF0dXNfd29yayRoYWxmX0YpLA0KICAgICAgICAgICAgIFZhcmlhYmxlID0gVmFyaWFibGUpDQoNCiMgcGxvdHRpbmcgcGhhc2UNCmdncGxvdChkYXRhID0gc3RhdHVzX3NleCwgYWVzKHg9TGFiZWwsIHk9dmFsdWUpKSArDQogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IExhYmVsKSkgKw0KICBnZW9tX3BvaW50KGFlcyh5PXZhbHVlLCBjb2xvdXI9TGFiZWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuNzUpKSArDQogIGZhY2V0X3dyYXAoIH4gVmFyaWFibGUsIHNjYWxlcz0iZnJlZSIpICsNCiAgeGxhYigiU2V4IikgKyB5bGFiKCIjIG9mIGluZGl2aWR1YWxzIikgKyBnZ3RpdGxlKCJHZW5kZXIgY29tcGFyaXNvbiIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgc3RhdF9ib3hwbG90KGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuNSkNCg0KDQojIHRoZSBzYW1lIGJ1dCBleGNsdWRpbmcgb3V0bGllcnMNCmdncGxvdChkYXRhID0gc3RhdHVzX3NleCwgYWVzKHg9TGFiZWwsIHk9dmFsdWUpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBxdWFudGlsZShzdGF0dXNfc2V4JHZhbHVlLCBjKDAsIDAuOSkpKSArDQogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IExhYmVsKSkgKw0KICBnZW9tX3BvaW50KGFlcyh5PXZhbHVlLCBjb2xvdXI9TGFiZWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuNzUpKSArDQogIGZhY2V0X3dyYXAoIH4gVmFyaWFibGUsIHNjYWxlcz0iZnJlZSIpICsNCiAgeGxhYigiU2V4IikgKyB5bGFiKCIjIG9mIGluZGl2aWR1YWxzIikgKyANCiAgZ2d0aXRsZSgiR2VuZGVyIGNvbXBhcmlzb24gZXhjbHVkaW5nIHRoZSBsYXN0IGRlY2lsZSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgc3RhdF9ib3hwbG90KGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuNSkNCmBgYA0KDQojIyMgV2hhdCB3ZSBoYXZlIGxlYXJuZWQNCg0KUGFyaXMgaXMgdGhlIGNpdHkgd2hlcmUgdGhleSBoYXZlIHRoZSBtb3N0IGluZGl2aWR1YWxzIHJlY2VpdmluZyBnb3Zlcm5tZW50IHRyYW5zZmVycy4NCkx5b24gaXMgdGhlIGNpdHkgd2hlcmUgdGhleSBoYXZlIHRoZSBsZWFzdCBpbmRpdmlkdWFscyByZWNlaXZpbmcgZ292ZXJubWVudCB0cmFuc2ZlcnMuDQo9IsOObGUtZGUtRnJhbmNlIiAgICAgICAgICAgICAgIlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIgIlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIgIlJow7RuZS1BbHBlcyIgICAgICAgICAgICAgICANCiJNaWRpLVB5csOpbsOpZXMiICAgICAgICAgICAgICAiQXF1aXRhaW5lIiAgICAgICAgICAgICAgICAgICJMYW5ndWVkb2MtUm91c3NpbGxvbiIgICAgICAgIlBheXMgZGUgbGEgTG9pcmUiICAgICAgICAgIA0KIlJow7RuZS1BbHBlcyIgICAgICAgICAgICAgICAgIkFsc2FjZSIgICAgICAgICAgICAgICAgICAgIA0KDQpQYXJpcyBoYXMgbW9zdCBpbmRpdmlkdWFscyB3aXRoIGhhbGYgdGltZSBjb250cmFjdC4gDQpSb3VlbiBoYXMgdGhlIGxlYXN0LiANCj0gIsOObGUtZGUtRnJhbmNlIiAgICAgICAgICAgICAgIlJow7RuZS1BbHBlcyIgICAgICAgICAgICAgICAgIlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIgIk1pZGktUHlyw6luw6llcyIgICAgICAgICAgICAgDQoiUGF5cyBkZSBsYSBMb2lyZSIgICAgICAgICAgICJOb3JkLVBhcy1kZS1DYWxhaXMiICAgICAgICAgIkFsc2FjZSIgICAgICAgICAgICAgICAgICAgICAiTGFuZ3VlZG9jLVJvdXNzaWxsb24iICAgICAgDQoiQXF1aXRhaW5lIiAgICAgICAgICAgICAgICAgICJCcmV0YWduZSIgICAgDQoNCkluIGF2ZXJhZ2UsIG1vcmUgZmVtYWxlcyByZWNlaXZlIHRyYW5zZmVycyB0aGFuIG1hbGVzLiANCkluIGF2ZXJhZ2UsIG1vcmUgZmVtYWxlcyBoYXZlIHBhcnQtdGltZSBjb250cmFjdCB0aGFuIG1hbGVzLiAoSW4gYXZlcmFnZSwgNTgxIGZlbWFsZXMgaGF2ZSBwYXJ0LXRpbWUgY29udHJhY3QgKGluIGVhY2ggdG93bikgYW5kIDE3OCBtYWxlcyBoYXZlIHBhcnQtdGltZSBjb250cmFjdC4gKE1vcmUgbWFsZXMgaGF2ZSBmdWxsLXRpbWUgY29udHJhY3QuKQ0KDQpNb3JlIGZlbWFsZXMgd29yayBhcyB3YWdlLWVhcm5lcnMgdGhhbiBtYWxlcy4NCj1BbGwgdGhlc2UgZmFjdHMgY2FuIGJlIHJlbGF0ZWQgdG8gdnVsbmVyYWJpbGl0eSBvZiBmZW1hbGVzIGluIHRoZSBsYWJvdXIgbWFya2V0LiBNYW55IHN0dWRpZXMgc2hvdyB0aGF0IGZlbWFsZXMgYXJlIG1vc3RseSB3b3JraW5nIGFzIHBhcnQtdGltZSB3b3JrZXJzIGFuZCBsb3dlciBqb2IgY2F0ZWdvcmllcy4gT24gdG9wIG9mIHRoaXMsIG1vcmUgZmVtYWxlcyByZWNlaXZlIHRyYW5zZmVycy4NCldlIGNhbiBhbHNvIG9ic2VydmUgdGhpcyBpbiBvdXIgZGF0YSB0aGF0IG1vcmUgbWFsZXMgYXJlIHJlZ2lzdGVyZWQgYXMgaW5kZXBlbmRlbnQgd29ya2VycyBvciBlbXBsb3llcnMuIA0KDQojIyBJbmVxdWFsaXR5IGRhdGENCg0KYGBge3IgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBJbmVxdWFsaXR5IGRhdGF9DQpnbGltcHNlKGluZXEpDQoNCiMgbWVyZ2UgZ2VvIGFuZCBpbmVxDQpnZW9faW5lcSA8LSBjYmluZC5kYXRhLmZyYW1lKGdlb1t1bmlxdWUoZ2VvJENPREdFTykgJWluJSB1bmlxdWUoaW5lcSRDT0RHRU8pLF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluZXFbdW5pcXVlKGluZXEkQ09ER0VPKSAlaW4lIHVuaXF1ZShnZW8kQ09ER0VPKSxdKQ0KDQojIHdoaWNoIGNpdHkgaGFzIHRoZSBoaWdoZXN0L2xvd2VzdCB1bmVtcGxveW1lbnQgcmF0ZQ0KaW5lcVt3aGljaC5tYXgoaW5lcSR1bmVtcF9yYXRlKSxjKCdDT0RHRU8nKV0gDQppbmVxW3doaWNoLm1pbihpbmVxJHVuZW1wX3JhdGUpLGMoJ0NPREdFTycpXSANCg0KIyBnZXQgbGlzdCBvZiB0b3AgMTAgY2l0aWVzIHdpdGggdGhlIGhpZ2hlc3QgdW5lbXBsb3ltZW50IHJhdGUuDQpSYW5rID0gc29ydChpbmVxJHVuZW1wX3JhdGUsIFRSVUUpDQpSYW5rMTAgPSBzb3J0KFJhbmtbMToxMF0sIFRSVUUpDQpzb2wgPSByZXAoMCwgMTApDQpmb3IgKGkgaW4gMToxMCl7DQogICAgc29sW2ldID0gcGFzdGUoaW5lcSRDT0RHRU9baW5lcSR1bmVtcF9yYXRlICVpbiUgUmFuazEwW2ldXSApIA0KfQ0KDQojIHdoaWNoIGlzIHRoZSBzbWFsbGVzdC9iaWdnZXN0IGNpdHkgKGFyZWEpDQppbmVxW3doaWNoLm1heChpbmVxJFN1cGVyZmljaWUpLGMoJ0NPREdFTycpXSANCmluZXFbd2hpY2gubWluKGluZXEkU3VwZXJmaWNpZSksYygnQ09ER0VPJyldIA0KDQojIHdoaWNoIGNpdHkgaGFzIHRoZSBoaWdoZXN0L2xvd2VzdCBtZWRpYW4gbGl2aW5nIGxldmVsDQppbmVxW3doaWNoLm1heChpbmVxJG1lZGlhbl9saXZpbmcxNCksYygnQ09ER0VPJyldIA0KaW5lcVt3aGljaC5taW4oaW5lcSRtZWRpYW5fbGl2aW5nMTQpLGMoJ0NPREdFTycpXSANCg0KIyBBZGp1c3Qgc2NhbGVzIGJlZm9yZSBwbG90dGluZw0Kc2MgPC0gc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gYygiIzU2ZGRjNSIsICIjZmYzZGI3IiksIGxpbWl0cz1jKG1pbihpbmVxJHVuZW1wX3JhdGUpLCBtYXgoaW5lcSR1bmVtcF9yYXRlKSkpDQoNCiMgTWFwIHJhdGlvIA0KaW5lcV91bmVtcGxfZGlzdHJpYnV0aW9uIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvX2luZXEkbG9uZ2l0dWRlLCB5PWdlb19pbmVxJGxhdGl0dWRlLA0KICAgICAgICAgICAgICAgICBjb2xvdXI9Z2VvX2luZXEkdW5lbXBfcmF0ZSksDQogICAgICAgICAgICAgZGF0YT1nZW9faW5lcSwgYWxwaGE9MSwgc2l6ZT0xKSArDQogICAgICAgICAgICAgc2MgKyBsYWJzKGNvbG9yPSdVbmVtcGxveW1lbnQgcmF0ZScpICsgZ2d0aXRsZSgiVW5lbXBsb3ltZW50IHJhdGUgZm9yIGVhY2ggdG93biIpDQppbmVxX3VuZW1wbF9kaXN0cmlidXRpb24NCg0KIyByZW1vdmUgdGhlIGRhdGFzZXQgY3JlYXRlZCB0byBzYXZlIG1lbW9yeQ0Kcm0obGlzdD1jKCJnZW9faW5lcSIsICJpbmVxX3VuZW1wbF9kaXN0cmlidXRpb24iKSkNCg0KYGBgDQoNCiMjIyBXaGF0IHdlIGhhdmUgbGVhcm5lZA0KDQpCb3JkZWF1eCBoYXMgdGhlIGhpZ2hlc3QgdW5lbXBsb3ltZW50IHJhdGUuDQpOYW50ZXMgaGFzIHRoZSBsb3dlc3QgdW5lbXBsb3ltZW50IHJhdGUuIA0KPSJBcXVpdGFpbmUiICAgICAgICAgICAgIkxhbmd1ZWRvYy1Sb3Vzc2lsbG9uIiAiTm9yZC1QYXMtZGUtQ2FsYWlzIiAgICJIYXV0ZS1Ob3JtYW5kaWUiICAgICAgIkd1YWRlbG91cGUiICAgICAgICAgIA0KIkNvcnNlIiAgICAgICAgICAgICAgICAiTm9yZC1QYXMtZGUtQ2FsYWlzIiAgICJDb3JzZSIgICAgICAgICAgICAgICAgIlJow7RuZS1BbHBlcyIgICAgICAgICAgIk5vcmQtUGFzLWRlLUNhbGFpcyIgDQoNCiMjIENvbW11bmUgZGF0YQ0KDQpgYGB7ciBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIENvbW11bmUgZGF0YX0NCmdsaW1wc2UoY29tbXVuZSkNCg0KIyBjaGFuZ2UgdGhlIGZhY3RvciB2YXJpYWJsZXMgdG8gbnVtZXJpYw0KIyBhYWEgPSBhcy5udW1lcmljKGNvbW11bmUkdXJiYW5SdXJhbCkNCg0KIyBNZXJnZSBnZW8gYW5kIGNvbW11bmUgZGF0YSB0byBpbmNsdWRlIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUgaW4gdGhlIGRhdGEuDQpnZW9fY29tbXVuZSA8LSBjYmluZC5kYXRhLmZyYW1lKGdlb1t1bmlxdWUoZ2VvJENPREdFTykgJWluJSB1bmlxdWUoY29tbXVuZSRDT0RHRU8pLF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW11bmVbdW5pcXVlKGNvbW11bmUkQ09ER0VPKSAlaW4lIHVuaXF1ZShnZW8kQ09ER0VPKSxdKQ0KDQoNCiMgQWRqdXN0IHNjYWxlcyBiZWZvcmUgcGxvdHRpbmcNCiNzYyA8LSBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKGNvbG91cnMgPSBwYWxldHRlKHJhaW5ib3coMykpLCBsaW1pdHM9YyhtaW4oYWFhKSwgbWF4KGFhYSkpKQ0KDQojIE1hcCByYXRpbyANCiAjIHVyYmFucnVyYWw8LQ0KICMgIEZyYU1hcCArDQogIyAgZ2VvbV9wb2ludChhZXMoeD1nZW9fY29tbXVuZSRsb25naXR1ZGUsIHk9Z2VvX2NvbW11bmUkbGF0aXR1ZGUsDQogIyAgICAgICAgICAgICAgICAgY29sb3VyPWFhYSksDQogIyAgICAgICAgICAgICBkYXRhPWdlb19jb21tdW5lLCBhbHBoYT0xLCBzaXplPTAuNSkgKw0KICMgICAgICAgICAgICAgc2MgKyBsYWJzKGNvbG9yPScnKSArIGdndGl0bGUoIlVyYmFuIGFuZCBydXJhbCBjaXRpZXMiKQ0KIyB1cmJhbnJ1cmFsDQoNCiMgYm94cGxvdA0KIyAgbnVtYmVyIG9mIHVuaXRzDQpuX2Zpcm1zIDwtIGxlbmd0aChjb21tdW5lJG5iX2Zpcm1zX2NvbW1lcmNlKQ0KDQojIHZlY3RvciANCkxhYmVsIDwtIGMocmVwKCJGaXJtcyIsIG5fZmlybXMqNCkpDQoNCiMgdmVjdG9yIHJlcHJlc2VudGluZyB0aGUgdmFyaWFibGUgY29uc2lkZXJlZA0KVmFyaWFibGUgPC0gYyhyZXAoIlNlcnZpY2UiLCBuX2Zpcm1zKSwgDQogICAgICAgICAgICAgcmVwKCJJbmR1c3RyeSIsIG5fZmlybXMpLA0KICAgICAgICAgICAgIHJlcCgiQ29tbWVyY2UiLCBuX2Zpcm1zKSwNCiAgICAgICAgICAgICByZXAoIkNvbnN0cnVjdGlvbiIsIG5fZmlybXMpKQ0KICAgICAgICAgIA0KICAgICAgICAgICAgIA0KIyBtZXJnZSB0aGVzZSBkYXRhDQpmaXJtc19zZWN0b3IgPSBjYmluZC5kYXRhLmZyYW1lKExhYmVsID0gTGFiZWwsIA0KICAgICAgICAgICAgIHZhbHVlID0gYyhjb21tdW5lJG5iX2Zpcm1zX2NvbW1lcmNlLCBjb21tdW5lJG5iX2Zpcm1zX3NlcnZpY2UsIGNvbW11bmUkbmJfZmlybXNfaW5kLCBjb21tdW5lJG5iX2Zpcm1zX2NvbnN0cnVjdGlvbiksDQogICAgICAgICAgICAgVmFyaWFibGUgPSBWYXJpYWJsZSkNCg0KIyBwbG90dGluZyBwaGFzZQ0KZ2dwbG90KGRhdGEgPSBmaXJtc19zZWN0b3IsIGFlcyh4PUxhYmVsLCB5PXZhbHVlKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBMYWJlbCkpICsNCiAgZ2VvbV9wb2ludChhZXMoeT12YWx1ZSwgY29sb3VyPUxhYmVsKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjc1KSkgKw0KICBmYWNldF93cmFwKCB+IFZhcmlhYmxlLCBzY2FsZXM9ImZyZWUiKSArDQogIHhsYWIoIkZpcm1zIikgKyB5bGFiKCIjIG9mIGZpcm1zIikgKyBnZ3RpdGxlKCJTZWN0b3IgY29tcGFyaXNvbiIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgc3RhdF9ib3hwbG90KGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuNSkNCg0KDQojIHRoZSBzYW1lIGJ1dCBleGNsdWRpbmcgb3V0bGllcnMNCmdncGxvdChkYXRhID0gZmlybXNfc2VjdG9yLCBhZXMoeD1MYWJlbCwgeT12YWx1ZSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IHF1YW50aWxlKGZpcm1zX3NlY3RvciR2YWx1ZSwgYygwLCAwLjkpKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBMYWJlbCkpICsNCiAgZ2VvbV9wb2ludChhZXMoeT12YWx1ZSwgY29sb3VyPUxhYmVsKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjc1KSkgKw0KICBmYWNldF93cmFwKCB+IFZhcmlhYmxlLCBzY2FsZXM9ImZyZWUiKSArDQogIHhsYWIoIkZpcm1zIikgKyB5bGFiKCIjIG9mIGZpcm1zIikgKyANCiAgZ2d0aXRsZSgiU2VjdG9yIGNvbXBhcmlzb24gZXhjbHVkaW5nIHRoZSBsYXN0IGRlY2lsZSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgc3RhdF9ib3hwbG90KGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuNSkNCg0KDQpybShsaXN0ID0gYygiZ2VvX2NvbW11bmUiLCAiUmFuayIsICJSYW5rMTAiLCAiYWFhIikpDQpgYGANCg0KDQojIENyZWF0ZSBhbiB1bmlxdWUgZGF0YXNldA0KDQpDcmVhdGUgYW4gdW5pcXVlIGRhdGFzZXQ6DQpgYGB7ciBNZXJnZSB0aGUgZGF0YSBhbmQgY3JlYXRlIGNzdiBmaWxlfQ0KDQojIG1lcmdpbmcNCm5ld0RhdCA9IG1lcmdlKGZpcm1zLCBwb3B1bGF0aW9uLCBieT0iQ09ER0VPIikNCm5ld0RhdCA9IG1lcmdlKG5ld0RhdCwgc2FsYXJ5LCBieT0iQ09ER0VPIikNCm5ld0RhdCA9IG1lcmdlKG5ld0RhdCwgZ2VvLCBieT0iQ09ER0VPIikNCm5ld0RhdCA9IG1lcmdlKG5ld0RhdCwgc3RhdHVzX3dvcmssIGJ5PSJDT0RHRU8iKQ0KbmV3RGF0ID0gbWVyZ2UobmV3RGF0LCBpbmVxLCBieT0iQ09ER0VPIikNCm5ld0RhdCA9IG1lcmdlKG5ld0RhdCwgZWR1YywgYnk9IkNPREdFTyIpDQpuZXdEYXQgPSBtZXJnZShuZXdEYXQsIGNhdGVnX3NvY2lvLCBieT0iQ09ER0VPIikNCm5ld0RhdCA9IG1lcmdlKG5ld0RhdCwgY29tbXVuZSwgYnk9IkNPREdFTyIpDQoNCiMgY2hlY2sNCm5hbWVzKG5ld0RhdCkNCmhlYWQobmV3RGF0KQ0KDQpgYGANCg0KU2F2ZS9sb2FkIGZpbmFsIGRhdGFzZXQgKGp1c3QgdG8gYXZvaWQgcnVubmluZyBhbGwgdGhlIGNvZGUpIA0KYGBge3IgU2F2ZS9sb2FkIGZpbmFsIGRhdGFzZXQsIHdhcm5pbmc9RkFMU0V9IA0KDQojIyBVTkNPTU1FTlQgVEhFU0UgTElORSBBTkQgQ09NTUVOVCBUSEUgTkVYVCBPTkVTIFRPIFNBVkUgVEhFIERBVEFTRVQNCiMjIFRIRU4gTU9WRSBUSElTIEZJTEUgSU4gVEhFIGRhdGEgRk9MREVSDQojICNyZW1vdmUgdW51c2VkIGZhY3RvciBsZXZlbHMgb24gbmV3RGF0DQojIGZvciAoaSBpbiAxOm5jb2wobmV3RGF0KSl7DQojICAgaWYgKGlzLmZhY3RvcihuZXdEYXRbLGldKSl7DQojICAgICBuZXdEYXRbLGldID0gZHJvcGxldmVscy5mYWN0b3IobmV3RGF0WyxpXSkNCiMgICB9DQojIH0NCiMgIyBzYXZlIHRoZSBOZXdEYXQuY3N2IGZpbGUgY3JlYXRlZCAod2hpY2ggaGFzIHRvIGJlIG1vdmVkIGluIHRoZSBkYXRhIGZvbGRlcikNCiMgd3JpdGUudGFibGUobmV3RGF0LCAibmV3RGF0LmNzdiIsIHF1b3RlID0gVFJVRSwgcm93Lm5hbWVzPUZBTFNFLCBjb2wubmFtZXMgPSBUUlVFLA0KIyAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04Iiwgc2VwID0gIiwiKQ0KIA0KIyMgTE9BRElORyBEQVRBDQojIGxvYWQgdGhlIGZpbmFsIGRhdGFzZXQNCnNldHdkKCIuL2RhdGEiKQ0KbmV3RGF0IDwtIHJlYWQuY3N2KCJuZXdEYXQuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiLCBoZWFkZXIgPSBUUlVFKQ0KDQojIEpVU1QgQSBDSEVDSywgRE8gTk9UIE5FRUQgVE8gUlVODQojIGNoZWNrIChjaGFuZ2luZyBuZXdEYXQgbG9hZGVkIHdpdGggbmV3RGF0MiksIHRoZSBvbmx5IGRpZmZlcmVuY2VzIGFyZSBpbiB0aGUgb3JkZXIgb2YgMTBlLTE1DQojIGlkZW50aWNhbChuZXdEYXQsIG5ld0RhdDIpDQogDQpgYGANCg0KU3BhdGlhbCBwbG90IG9mIHRoZSBkYXRhIHRvIHNwb3QgcG9zc2libGUgcGF0dGVybnMuIA0KQSBzdWItc2FtcGxlIHdpbGwgYmUgcmV0YWluZWQgd2hlbiBuZWVkZWQgaW4gb3JkZXIgdG8gcmVkdWNlIA0KZ2VvLXNwYXRpYWwgY29ycmVsYXRpb25zLiANCmBgYHtyIG5ld0RhdCBzcGF0aWFsIHBsb3R9IA0KDQojIGNlbnRlciBvZiBGcmFuY2UsIG9idGFpbmVkIHVzaW5nOiANCiMgZnJhX2NlbnRlciA9IGFzLm51bWVyaWMoZ2VvY29kZSgiRnJhbmNlIikpIA0KZnJhX2NlbnRlciA9IGMoMi4yMTM3NDksIDQ2LjIyNzYzOCkgDQogDQpnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTUpLCBleHRlbnQ9Im5vcm1hbCIpICsgDQogIGdlb21fcG9pbnQoYWVzKHg9bG9uZ2l0dWRlLCB5PWxhdGl0dWRlKSwgZGF0YT1uZXdEYXQsIGNvbD0ib3JhbmdlIiwgYWxwaGE9MSwgc2l6ZT0wLjA1KSAgDQogDQpgYGAgDQogDQoNCiMgVW5zdXBlcnZpc2VkIExlYXJuaW5nDQoNCiMjIE11bHRpLURpbWVuc2lvbmFsIFNjYWxpbmcgKE1EUykNCg0KIyMjIFByZWxpbWluYXJ5IG11bHRpdmFyaWF0ZSBkYXRhIHZpc3VhbGlzYXRpb24gdG8gYmV0dGVyIGNvbmR1Y3QgdGhlIHVuc3VwZXJ2aXNlZCBsZWFybmluZyAgDQpgYGB7cn0NCg0KcmVnaW9uIDwtIGdlbyRyZWdpb24NCg0KIyBhZGQgcmVnaW9uIHZhcmlhYmxlIHRvIHRoZSBzYWxhcnkgZGF0YXNldA0Kc2FsYXJ5X3dpdGhfZGVwIDwtIGNiaW5kKHJlZ2lvbiwgc2FsYXJ5KQ0KDQojIGV4dHJhY3QgY29udGludW91cyB2YXJpYWJsZXMNCnNhbGFyeV92YXJpYWJsZXMgPC0gc2FsYXJ5X3dpdGhfZGVwWywgMzozNF0NCg0KI3NjYXR0ZXJwbG90IHRvIHZpc3VhbGx5IHNlZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzDQpwYWlycyhzYWxhcnlfdmFyaWFibGVzWzE6OF0sIA0KICAgICAgcGFuZWwgPSBmdW5jdGlvbiAoeCwgeSwgLi4uKSB7DQogICAgICAgICAgcG9pbnRzKHgsIHksIC4uLikNCiAgICAgICAgICBhYmxpbmUobG0oeSB+IHgpLCBjb2wgPSAicmVkIikNCiAgICAgIH0sIHBjaCA9ICIuIiwgY2V4ID0gMC41KQ0KDQojc2NhdHRlcnBsb3QgYmV0d2VlbiB2YXJpYWJsZXMgb2YgbWFqb3IgaW50ZXJlc3QNCnBhaXJzKHNhbGFyeV92YXJpYWJsZXNbLCBjKCJzYWxhcnlfcmF0aW9fRnZzTSIsICJzYWxfZ2VuZXJhbCIsICJzYWxfRmVtYWxlcyIsInNhbF9NYWxlcyIpXSwgDQogICAgICBwYW5lbCA9IGZ1bmN0aW9uICh4LCB5LCAuLi4pIHsNCiAgICAgICAgICBwb2ludHMoeCwgeSwgLi4uKQ0KICAgICAgICAgIGFibGluZShsbSh5IH4geCksIGNvbCA9ICJyZWQiKQ0KICAgICAgfSwgcGNoID0gIi4iLCBjZXggPSAwLjUpDQoNCg0KI3NjYWxlIHRoZSBkYXRhDQpzYWxhcnlfdmFyaWFibGVzX3NjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKHNjYWxlKHNhbGFyeV92YXJpYWJsZXMpKQ0KI2RpdmlkZSBzYWxfZ2VuZXJhbCBpbnRvIDQgcGFydHMNCnNhbGFyeV9sZXZlbHMgPC0gd2l0aChzYWxhcnlfdmFyaWFibGVzX3NjYWxlZCwgDQogICAgICAgICAgICAgICAgICBlcXVhbC5jb3VudChzYWxfZ2VuZXJhbCw0KSkNCg0KI3Bsb3Qgc2FsX3dvcmtlciBhZ2FpbnN0IHNhbF9taWRNYW5hZ2VyIGdpdmVuIDMgcGFydGl0aW9ucyBvZiBzYWxfZ2VuZXJhbA0KcGxvdCh4eXBsb3Qoc2FsX3dvcmtlciB+IHNhbF9taWRNYW5hZ2VyfCBjdXQoc2FsX2dlbmVyYWwsIDMpLCANCiAgICAgICAgICAgIGRhdGEgPSBzYWxhcnlfdmFyaWFibGVzLCANCiAgICAgICAgICAgIGxheW91dCA9IGMoMywgMSksIA0KICAgICAgICAgICAgeGxhYiA9ICJXb3JrZXIgU2FsYXJ5IiwgDQogICAgICAgICAgICB5bGFiID0gIk1pZG1hbmFnZXIgc2FsYXJ5IikpDQoNCiNtdWx0aWRpbWVzbmlvbmFsIHBsb3Qgb2YgbWlkTWFuYWdlciBzYWxhcnkgYWdhaW5zdCB3b3JrZXIgc2FsYXJ5LCBnaXZlbiA0IHBhcnRpdGlvbnMgb2YgRi9NIHNhbGFyeSByYXRpbywgYW5kIGdyYXlzY2FsZSBpbnRlbnNpdGllcyBvZiBnZW5lcmFsIHNhbGFyeQ0KTV9GX3NhbGFyeV9yYXRpbyAgPC0gd2l0aChzYWxhcnlfdmFyaWFibGVzLCBlcXVhbC5jb3VudChzYWxhcnlfcmF0aW9fRnZzTSwgNCkpDQpnZW5lcmFsX3NhbGFyeS5vcmQgPC0gd2l0aChzYWxhcnlfdmFyaWFibGVzLCByZXYob3JkZXIoc2FsX2dlbmVyYWwpKSkNCnNhbGFyeV92YXJpYWJsZXMub3JkZXJlZCA8LSBzYWxhcnlfdmFyaWFibGVzW2dlbmVyYWxfc2FsYXJ5Lm9yZCxdDQpzYWxfZ2VuZXJhbC5icmVha3MgPC0gd2l0aChzYWxhcnlfdmFyaWFibGVzLm9yZGVyZWQsIA0KICAgICAgICAgICAgICAgICAgICAgZG8uYnJlYWtzKHJhbmdlKHNhbF9nZW5lcmFsKSw1MCkpDQpzYWxhcnlfdmFyaWFibGVzLm9yZGVyZWQkY29sb3I8LWxldmVsLmNvbG9ycyhzYWxhcnlfdmFyaWFibGVzLm9yZGVyZWQkc2FsX2dlbmVyYWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF0PXNhbF9nZW5lcmFsLmJyZWFrcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sLnJlZ2lvbnM9Z3JleS5jb2xvcnMpDQpwbG90KHh5cGxvdChzYWxfd29ya2VyIH4gc2FsX21pZE1hbmFnZXIgfCBNX0Zfc2FsYXJ5X3JhdGlvLCANCiAgICAgICAgICAgIGRhdGEgPSBzYWxhcnlfdmFyaWFibGVzLm9yZGVyZWQsDQogICAgICAgICAgICBhc3BlY3QgPSAiaXNvIiwgDQogICAgICAgICAgICBncm91cHMgPSBjb2xvciwgDQogICAgICAgICAgICBjZXggPSAxLCBjb2wgPSAiYmxhY2siLA0KICAgICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgZ3JvdXBzLCAuLi4sIHN1YnNjcmlwdHMpIHsNCiAgICAgICAgICAgZmlsbCA8LSBncm91cHNbc3Vic2NyaXB0c10NCiAgICAgICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSkNCiAgICAgICAgICAgcGFuZWwueHlwbG90KHgsIHksIHBjaCA9IDIxLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmaWxsLCAuLi4pDQogICAgICAgfSwNCiAgICAgICBsZWdlbmQgPQ0KICAgICAgIGxpc3QocmlnaHQgPQ0KICAgICAgICAgICAgbGlzdChmdW4gPSBkcmF3LmNvbG9ya2V5LA0KICAgICAgICAgICAgICAgICBhcmdzPWxpc3Qoa2V5PWxpc3QoY29sPWdyYXkuY29sb3JzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXQgPSBzYWxfZ2VuZXJhbC5icmVha3MpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJhdyA9IEZBTFNFKSkpLA0KICAgICAgICAgICAgeGxhYiA9ICJXb3JrZXIgU2FsYXJ5IiwgDQogICAgICAgICAgICB5bGFiID0gIk1pZG1hbmFnZXIgc2FsYXJ5IikpDQoNCg0KYGBgDQoNCiMjIyMgUHJlbGltaW5hcnkgbXVsdGl2YXJpYXRlIGZpcm1zIGRhdGEgdmlzdWFsaXNhdGlvbiB0byBiZXR0ZXIgY29uZHVjdCB0aGUgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIA0KYGBge3J9DQojIGFkZCByZWdpb24gdmFyaWFibGUgdG8gdGhlIHNhbGFyeSBkYXRhc2V0DQpmaXJtc193aXRoX2RlcCA8LSBjYmluZChyZWdpb24sIGZpcm1zKQ0KDQojIGV4dHJhY3QgY29udGludW91cyB2YXJpYWJsZXMNCmZpcm1zX3ZhcmlhYmxlcyA8LSBmaXJtc193aXRoX2RlcFssIDQ6OF0NCg0KI3NjYXR0ZXJwbG90IHRvIHZpc3VhbGx5IHNlZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzDQpwYWlycyhmaXJtc192YXJpYWJsZXNbLCAyOjVdLCANCiAgICAgIHBhbmVsID0gZnVuY3Rpb24gKHgsIHksIC4uLikgew0KICAgICAgICAgIHBvaW50cyh4LCB5LCAuLi4pDQogICAgICAgICAgYWJsaW5lKGxtKHkgfiB4KSwgY29sID0gInJlZCIpDQogICAgICB9LCBwY2ggPSAiLiIsIGNleCA9IDAuNSkNCg0KDQojc2NhbGUgdGhlIGRhdGENCmZpcm1zX3ZhcmlhYmxlc19zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShzY2FsZShmaXJtc192YXJpYWJsZXMpKQ0KI2RpdmlkZSBmaXJtcyB0b3RhbCBpbnRvIDQgcGFydHMNCmZpcm1zX2xldmVscyA8LSB3aXRoKGZpcm1zX3ZhcmlhYmxlc19zY2FsZWQsIA0KICAgICAgICAgICAgICAgICAgZXF1YWwuY291bnQodG90YWwsNCkpDQoNCg0KI3Bsb3Qgc2FsX3dvcmtlciBhZ2FpbnN0IHNhbF9taWRNYW5hZ2VyIGdpdmVuIDMgcGFydGl0aW9ucyBvZiBzYWxfZ2VuZXJhbA0KcGxvdCh4eXBsb3QobWVkaXVtIH4gbGFyZ2V8IGN1dCh0b3RhbCwgMyksIA0KICAgICAgICAgICAgZGF0YSA9IGZpcm1zX3ZhcmlhYmxlcywgDQogICAgICAgICAgICBsYXlvdXQgPSBjKDMsIDEpLCANCiAgICAgICAgICAgIHhsYWIgPSAibWVkaXVtIGZpcm1zIiwgDQogICAgICAgICAgICB5bGFiID0gImxhcmdlIGZpcm1zIikpDQoNCg0KYGBgDQoNCg0KIyMjIE11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgKE1EUykgDQojIyMjIE11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgKE1EUykgb24gc2FsYXJ5IGRhdGENCg0KYGBge3J9DQojTXVsdGktZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSBvbiBzYWxhcnkgZGF0YQ0KIyBwcm9kdWNlcyBhIGxpc3Qgd2l0aCBjb3ZhcmlhbmNlIG1hdHJpeA0KIyBmb3IgZWFjaCByZWdpb24gYXMgY29tcG9uZW50Og0Kc2FsYXJ5X3ZhciA8LSB0YXBwbHkoMTpucm93KHNhbGFyeV93aXRoX2RlcCksIHNhbGFyeV93aXRoX2RlcCRyZWdpb24sIA0KICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oaSkgdmFyKHNhbGFyeV93aXRoX2RlcFtpLDM6N10pKQ0KDQojcmVtb3ZlIHJlZ2lvbnMgd2l0aCBudWxsIHZhbHVlcw0Kc2FsYXJ5X3ZhciA8LSBzYWxhcnlfdmFyWy1jKDExLCAxMiwgMTUsIDE5LCAyMCwgMjgpXQ0KDQoNCiNjcmVhdGUgZGF0YWZyYW1lIHdpdGggdGhlIGNvdW50cyBvZiB0b3ducyBwZXIgcmVnaW9uDQpyZWdpb25zX2NvdW50IDwtIGRhdGEuZnJhbWUodGFibGUoc2FsYXJ5X3dpdGhfZGVwJHJlZ2lvbikpDQpyZWdpb25zX2NvdW50X3N1YjwtcmVnaW9uc19jb3VudFshKHJlZ2lvbnNfY291bnQkRnJlcT09MCB8IHJlZ2lvbnNfY291bnQkRnJlcT09MSksXQ0KI2NoZWNrIHRvdGFsIHN1bQ0KI3Jvdy5uYW1lcyhyZWdpb25zX2NvdW50X3N1YikgPC0gMTpucm93KHJlZ2lvbnNfY291bnRfc3ViKQ0KI3N1bShyZWdpb25zX2NvdW50X3N1YiRGcmVxKQ0KDQoNCg0KIyBpbml0aWFsaXplcyBjb21tb24gY292YXJpYW5jZSBtYXRyaXggdmFyOg0KUyA8LSByZWdpb25zX2NvdW50X3N1YiRGcmVxWzFdICogYXMubWF0cml4KHNhbGFyeV92YXJbWzFdXSkNCiMgY3JlYXRlcyBjb21tb24gY292YXJpYW5jZSBtYXRyaXggUw0KcyA8LSAwDQpmb3IgKHYgaW4gYygxOjIyKSkgUyA8LSBTICsgcmVnaW9uc19jb3VudF9zdWIkRnJlcVt2XSAqIGFzLm1hdHJpeChzYWxhcnlfdmFyW1t2XV0pIA0KUyA8LSBTIC8gNTAwMw0KDQojIGZpbmRzIGNlbnRlciBvZiBlYWNoIHZhcmlhYmxlIChzYWxfZ2VuZXJhbCwgZXRjKQ0KIyBmb3IgZWFjaCByZWdpb24NCnNhbGFyeV9jZW4gPC0gdGFwcGx5KDE6bnJvdyhzYWxhcnlfd2l0aF9kZXApLCBzYWxhcnlfd2l0aF9kZXAkcmVnaW9uLCANCiAgICBmdW5jdGlvbihpKSBhcHBseShzYWxhcnlfd2l0aF9kZXBbaSxjKDM6NyldLCAyLCBtZWFuKSkNCnNhbGFyeV9jZW4gPC0gc2FsYXJ5X2NlblstYygxMSwgMTIsIDE1LCAxOSwgMjAsIDI4KV0NCg0KDQojIGNyZWF0ZSBhIG1hdHJpeCBvdXQgb2YgZWFjaCBjb21wb25lbnRzLCBlYWNoDQojIG1lYW4gbWVhc3VyZW1lbnQgZm9yIGFsbCB2YXJpYWJsZXMgYnkgcmVnaW9uDQpzYWxhcnlfY2VuIDwtIG1hdHJpeCh1bmxpc3Qoc2FsYXJ5X2NlbiksIA0KICAgIG5yb3cgPSBsZW5ndGgoc2FsYXJ5X2NlbiksIGJ5cm93ID0gVFJVRSkNCiNzYWxhcnlfY2VuDQoNCiMgY29tcHV0ZSB0aGUgbWFoYWxhbm9iaXMgZGlzdGFuY2VzOg0Kc2FsYXJ5X21haCA8LSBhcHBseShzYWxhcnlfY2VuLCAxLCANCiAgICBmdW5jdGlvbihjZW4pIG1haGFsYW5vYmlzKHNhbGFyeV9jZW4sIGNlbiwgUykpDQoNCg0KIyBydW4gaXQgb24gdHdvIGRpbWVuc2lvbnMNCnNhbGFyeV9tZHMgPC0gY21kc2NhbGUoc2FsYXJ5X21haCkNCg0KIyBkcmF3IGEgc2NhdHRlcnBsb3Qgb2YgdHdvLWRpbWVuc2lvbmFsIHNvbHV0aW9uDQojIGZyb20gY2xhc3NpY2FsIE1EUyBhcHBsaWVkIHRvIE1haGFsYW5vYmlzDQojIGRpc3RhbmNlczoNCmxpbSA8LSByYW5nZShzYWxhcnlfbWRzKSAqIDEuMg0KcGxvdChzYWxhcnlfbWRzLCB4bGFiID0gIkNvb3JkaW5hdGUgMSIsIA0KICAgICB5bGFiID0gIkNvb3JkaW5hdGUgMiIsDQogICAgIHhsaW0gPSBsaW0sIHlsaW0gPSBsaW0sIHR5cGUgPSAibiIpDQp0ZXh0KHNhbGFyeV9tZHMsIGxhYmVscyA9IGxldmVscyhzYWxhcnlfd2l0aF9kZXAkcmVnaW9uKSwgDQogICAgIGNleCA9IDAuNSkNCg0KYGBgDQpPbmUgY2FuIHNlZSB0aGF0IExhbmd1ZWRvYyBSb3Vzc2lsb24sIHRoZSB3b3JzdCByZWdpb24gaW4gdGVybXMgb2YgR0RQIHBlciBjYXBpdGEsIGlzIGZ1cnRoZXIgYXdheSBmcm9tIHRoZSBjZW50ZXIuDQoNCiMjIyMgTXVsdGktZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSBvbiBmaXJtcyBkYXRhDQoNCmBgYHtyfQ0KDQpmaXJtc193aXRoX2RlcCA8LSBmaXJtc193aXRoX2RlcFssIC05XQ0KZmlybXNfd2l0aF9kZXBfbm9ybWFsaXplZCA8LSBmaXJtc193aXRoX2RlcFssIDQ6OF0vcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uDQpmaXJtc193aXRoX2RlcF9ub3JtYWxpemVkIDwtIGNiaW5kKGZpcm1zX3dpdGhfZGVwJHJlZ2lvbiwgZmlybXNfd2l0aF9kZXBfbm9ybWFsaXplZCkNCm5hbWVzKGZpcm1zX3dpdGhfZGVwX25vcm1hbGl6ZWQpWzFdIDwtICJyZWdpb24iDQojIHByb2R1Y2VzIGEgbGlzdCB3aXRoIGNvdmFyaWFuY2UgbWF0cml4DQojIGZvciBlYWNoIHJlZ2lvbiBhcyBjb21wb25lbnQ6DQpmaXJtc192YXIgPC0gdGFwcGx5KDE6bnJvdyhmaXJtc193aXRoX2RlcF9ub3JtYWxpemVkKSwgZmlybXNfd2l0aF9kZXBfbm9ybWFsaXplZCRyZWdpb24sIA0KICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oaSkgdmFyKGZpcm1zX3dpdGhfZGVwX25vcm1hbGl6ZWRbaSwyOjZdKSkNCg0KI3JlbW92ZSByZWdpb25zIHdpdGggbnVsbCB2YWx1ZXMNCmZpcm1zX3ZhciA8LSBmaXJtc192YXJbLWMoMTEsIDEyLCAxNSwgMTksIDIwLCAyOCldDQoNCg0KI2NyZWF0ZSBkYXRhZnJhbWUgd2l0aCB0aGUgY291bnRzIG9mIHRvd25zIHBlciByZWdpb24NCnJlZ2lvbnNfY291bnQgPC0gZGF0YS5mcmFtZSh0YWJsZShmaXJtc193aXRoX2RlcF9ub3JtYWxpemVkJHJlZ2lvbikpDQpyZWdpb25zX2NvdW50X3N1YjwtcmVnaW9uc19jb3VudFshKHJlZ2lvbnNfY291bnQkRnJlcT09MCB8IHJlZ2lvbnNfY291bnQkRnJlcT09MSksXQ0KI2NoZWNrIHRvdGFsIHN1bQ0KIyByb3cubmFtZXMocmVnaW9uc19jb3VudF9zdWIpIDwtIDE6bnJvdyhyZWdpb25zX2NvdW50X3N1YikNCiMgc3VtKHJlZ2lvbnNfY291bnRfc3ViJEZyZXEpDQoNCg0KDQojIGluaXRpYWxpemVzIGNvbW1vbiBjb3ZhcmlhbmNlIG1hdHJpeCB2YXI6DQpTIDwtIHJlZ2lvbnNfY291bnRfc3ViJEZyZXFbMV0gKiBhcy5tYXRyaXgoZmlybXNfdmFyW1sxXV0pDQoNCiMgY3JlYXRlcyBjb21tb24gY292YXJpYW5jZSBtYXRyaXggUw0KcyA8LSAwDQpmb3IgKHYgaW4gYygxOjIyKSkgUyA8LSBTICsgKHJlZ2lvbnNfY291bnRfc3ViJEZyZXFbdl0gLSAxKSAqIGFzLm1hdHJpeChmaXJtc192YXJbW3ZdXSkgDQpTIDwtIFMgLyA1MDAzDQoNCiMgZmluZHMgY2VudGVyIG9mIGVhY2ggdmFyaWFibGUgKG1pY3JvLCBldGMpDQojIGZvciBlYWNoIHJlZ2lvbg0KZmlybXNfY2VuIDwtIHRhcHBseSgxOm5yb3coZmlybXNfd2l0aF9kZXBfbm9ybWFsaXplZCksIGZpcm1zX3dpdGhfZGVwX25vcm1hbGl6ZWQkcmVnaW9uLCANCiAgICBmdW5jdGlvbihpKSBhcHBseShmaXJtc193aXRoX2RlcF9ub3JtYWxpemVkW2ksLTFdLCAyLCBtZWFuKSkNCmZpcm1zX2NlbiA8LSBmaXJtc19jZW5bLWMoMTEsIDEyLCAxNSwgMTksIDIwLCAyOCldDQoNCg0KIyBjcmVhdGUgYSBtYXRyaXggb3V0IG9mIGVhY2ggY29tcG9uZW50cywgZWFjaA0KIyBtZWFuIG1lYXN1cmVtZW50IGZvciBhbGwgdmFyaWFibGVzIGJ5IHJlZ2lvbg0KZmlybXNfY2VuIDwtIG1hdHJpeCh1bmxpc3QoZmlybXNfY2VuKSwgDQogICAgbnJvdyA9IGxlbmd0aChmaXJtc19jZW4pLCBieXJvdyA9IFRSVUUpDQoNCiMgY29tcHV0ZSB0aGUgbWFoYWxhbm9iaXMgZGlzdGFuY2VzOg0KZmlybXNfbWFoIDwtIGFwcGx5KGZpcm1zX2NlbiwgMSwgDQogICAgZnVuY3Rpb24oY2VuKSBtYWhhbGFub2JpcyhmaXJtc19jZW4sIGNlbiwgUykpDQoNCg0KDQojIHJ1biBpdCBvbiB0d28gZGltZW5zaW9ucw0KZmlybXNfbWRzIDwtIGNtZHNjYWxlKGZpcm1zX21haCkNCg0KIyBkcmF3IGEgc2NhdHRlcnBsb3Qgb2YgdHdvLWRpbWVuc2lvbmFsIHNvbHV0aW9uDQojIGZyb20gY2xhc3NpY2FsIE1EUyBhcHBsaWVkIHRvIE1haGFsYW5vYmlzDQojIGRpc3RhbmNlczoNCmxpbSA8LSByYW5nZShmaXJtc19tZHMpICogMS4yDQpwbG90KGZpcm1zX21kcywgeGxhYiA9ICJDb29yZGluYXRlIDEiLCANCiAgICAgeWxhYiA9ICJDb29yZGluYXRlIDIiLA0KICAgICB4bGltID0gbGltLCB5bGltID0gbGltLCB0eXBlID0gIm4iKQ0KdGV4dChmaXJtc19tZHMsIGxhYmVscyA9IGxldmVscyhmaXJtc193aXRoX2RlcF9ub3JtYWxpemVkJHJlZ2lvbiksIA0KICAgICBjZXggPSAwLjUpDQoNCmBgYA0KU2luY2UgdGhhIGRhdGEgaXMgbm9ybWFsaXplZCBhY2NvcmRpbmcgdG8gcG9wdWxhdGlvbiBzaXplLCBIYXV0ZSBOb3JtYW5kaWUgYW5kIE1pZGkgUHlyZW5lZXMgYXJlIGZ1cnRoZXIgYXdheS4NCg0KIyMjIyBNdWx0aS1EaW1lbnNpb25hbCBTY2FsaW5nIGZvciBtZXJnZWQgZGF0YXNldA0KDQpgYGB7cn0NCm51bXMgPC0gdW5saXN0KGxhcHBseShjb21tdW5lLCBpcy5udW1lcmljKSkgDQpjb21tdW5lX3NlbGVjdGVkIDwtIGNvbW11bmVbLCBudW1zXQ0KDQpjb21tdW5lX3NlbGVjdGVkIDwtIGNvbW11bmVfc2VsZWN0ZWRbLCAyOjIwXQ0KDQpjb21tdW5lX3NlbGVjdGVkX25vcm1hbGl6ZWQgPC0gY29tbXVuZV9zZWxlY3RlZFtdL3BvcHVsYXRpb24kdG90YWxfcG9wdWxhdGlvbg0KDQp1bmVtcF9wZXJjZW50IDwtIGFzLmRhdGEuZnJhbWUoKGluZXEkdW5lbXAxNV82NC9pbmVxJHBvcDE1XzY0KSoxMDApDQpuYW1lcyh1bmVtcF9wZXJjZW50KVsxXSA8LSAidW5lbXBfcGVyY2VudCINCg0KZmlybXNfdmFyaWFibGVzX25vcm1hbGl6ZWQgPC0gZmlybXNfdmFyaWFibGVzW10vcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uDQoNCnNlbGVjdGVkX3ZhcmlhYmxlcyA8LSBjYmluZChmaXJtc192YXJpYWJsZXNfbm9ybWFsaXplZCwgc2FsYXJ5X3ZhcmlhYmxlcywgY29tbXVuZV9zZWxlY3RlZF9ub3JtYWxpemVkLCB1bmVtcF9wZXJjZW50KQ0KDQpzZWxlY3RlZF92YXJpYWJsZXNfd2l0aF9kZXAgPC0gY2JpbmQoZ2VvJHJlZ2lvbiwgc2VsZWN0ZWRfdmFyaWFibGVzKQ0KbmFtZXMoc2VsZWN0ZWRfdmFyaWFibGVzX3dpdGhfZGVwKVsxXSA8LSAicmVnaW9uIg0KDQpwY2Ffc2VsZWN0ZWRfdmFyaWFibGVzIDwtIHByY29tcChzZWxlY3RlZF92YXJpYWJsZXMpDQpzZWxlY3RlZF9wY2Ffc2NvcmVzPC0gYXMuZGF0YS5mcmFtZShwY2Ffc2VsZWN0ZWRfdmFyaWFibGVzJHhbLDE6M10pDQpzZWxlY3RlZF9wY2Ffd2l0aF9kZXAgPC0gY2JpbmQoZ2VvJHJlZ2lvbiwgc2VsZWN0ZWRfcGNhX3Njb3JlcykNCm5hbWVzKHNlbGVjdGVkX3BjYV93aXRoX2RlcClbMV0gPC0gInJlZ2lvbiINCiMgcHJvZHVjZXMgYSBsaXN0IHdpdGggY292YXJpYW5jZSBtYXRyaXgNCiMgZm9yIGVhY2ggcmVnaW9uIGFzIGNvbXBvbmVudDoNCnNlbGVjdGVkX1BDQV92YXIgPC0gdGFwcGx5KDE6bnJvdyhzZWxlY3RlZF9wY2Ffd2l0aF9kZXApLCBzZWxlY3RlZF9wY2Ffd2l0aF9kZXAkcmVnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oaSkgdmFyKHNlbGVjdGVkX3BjYV93aXRoX2RlcFtpLDI6NF0pKQ0KDQoNCiNyZW1vdmUgcmVnaW9ucyB3aXRoIG51bGwgdmFsdWVzDQpzZWxlY3RlZF9QQ0FfdmFyIDwtIHNlbGVjdGVkX1BDQV92YXJbLWMoMTEsIDEyLCAxNSwgMTksIDIwLCAyOCldDQoNCg0KI2NyZWF0ZSBkYXRhZnJhbWUgd2l0aCB0aGUgY291bnRzIG9mIHRvd25zIHBlciByZWdpb24NCnJlZ2lvbnNfY291bnQgPC0gZGF0YS5mcmFtZSh0YWJsZShzZWxlY3RlZF9wY2Ffd2l0aF9kZXAkcmVnaW9uKSkNCnJlZ2lvbnNfY291bnRfc3ViPC1yZWdpb25zX2NvdW50WyEocmVnaW9uc19jb3VudCRGcmVxPT0wIHwgcmVnaW9uc19jb3VudCRGcmVxPT0xKSxdDQojY2hlY2sgdG90YWwgc3VtDQojIHJvdy5uYW1lcyhyZWdpb25zX2NvdW50X3N1YikgPC0gMTpucm93KHJlZ2lvbnNfY291bnRfc3ViKQ0KIyBzdW0ocmVnaW9uc19jb3VudF9zdWIkRnJlcSkNCg0KDQoNCiMgaW5pdGlhbGl6ZXMgY29tbW9uIGNvdmFyaWFuY2UgbWF0cml4IHZhcjoNClMgPC0gcmVnaW9uc19jb3VudF9zdWIkRnJlcVsxXSAqIGFzLm1hdHJpeChzZWxlY3RlZF9QQ0FfdmFyW1sxXV0pDQoNCiMgY3JlYXRlcyBjb21tb24gY292YXJpYW5jZSBtYXRyaXggUw0KcyA8LSAwDQpmb3IgKHYgaW4gYygxOjIyKSkgUyA8LSBTICsgKHJlZ2lvbnNfY291bnRfc3ViJEZyZXFbdl0gLSAxKSAqIGFzLm1hdHJpeChzZWxlY3RlZF9QQ0FfdmFyW1t2XV0pDQpTIDwtIFMgLyA1MDAzDQoNCiMgZmluZHMgY2VudGVyIG9mIGVhY2ggdmFyaWFibGUgKG1pY3JvLCBldGMpDQojIGZvciBlYWNoIHJlZ2lvbg0Kc2VsZWN0ZWRfY2VuIDwtIHRhcHBseSgxOm5yb3coc2VsZWN0ZWRfcGNhX3dpdGhfZGVwKSwgc2VsZWN0ZWRfcGNhX3dpdGhfZGVwJHJlZ2lvbiwNCiAgICBmdW5jdGlvbihpKSBhcHBseShzZWxlY3RlZF9wY2Ffd2l0aF9kZXBbaSwtMV0sIDIsIG1lYW4pKQ0Kc2VsZWN0ZWRfY2VuIDwtIHNlbGVjdGVkX2NlblstYygxMSwgMTIsIDE1LCAxOSwgMjAsIDI4KV0NCg0KIyBjcmVhdGUgYSBtYXRyaXggb3V0IG9mIGVhY2ggY29tcG9uZW50cywgZWFjaA0KIyBtZWFuIG1lYXN1cmVtZW50IGZvciBhbGwgdmFyaWFibGVzIGJ5IHJlZ2lvbg0Kc2VsZWN0ZWRfY2VuIDwtIG1hdHJpeCh1bmxpc3Qoc2VsZWN0ZWRfY2VuKSwNCiAgICBucm93ID0gbGVuZ3RoKHNlbGVjdGVkX2NlbiksIGJ5cm93ID0gVFJVRSkNCg0KIyBjb21wdXRlIHRoZSBtYWhhbGFub2JpcyBkaXN0YW5jZXM6DQpzZWxlY3RlZF9tYWggPC0gYXBwbHkoc2VsZWN0ZWRfY2VuLCAxLA0KICAgIGZ1bmN0aW9uKGNlbikgbWFoYWxhbm9iaXMoc2VsZWN0ZWRfY2VuLCBjZW4sIFMpKQ0KDQoNCiMgcnVuIGl0IG9uIHR3byBkaW1lbnNpb25zDQpzZWxlY3RlZF9tZHMgPC0gY21kc2NhbGUoc2VsZWN0ZWRfbWFoKQ0KDQojIGRyYXcgYSBzY2F0dGVycGxvdCBvZiB0d28tZGltZW5zaW9uYWwgc29sdXRpb24NCiMgZnJvbSBjbGFzc2ljYWwgTURTIGFwcGxpZWQgdG8gTWFoYWxhbm9iaXMNCiMgZGlzdGFuY2VzOg0KbGltIDwtIHJhbmdlKHNlbGVjdGVkX21kcykgKiAxLjINCnBsb3Qoc2VsZWN0ZWRfbWRzLCB4bGFiID0gIkNvb3JkaW5hdGUgMSIsDQogICAgIHlsYWIgPSAiQ29vcmRpbmF0ZSAyIiwNCiAgICAgeGxpbSA9IGxpbSwgeWxpbSA9IGxpbSwgdHlwZSA9ICJuIikNCnRleHQoc2VsZWN0ZWRfbWRzLCBsYWJlbHMgPSBsZXZlbHMoc2VsZWN0ZWRfcGNhX3dpdGhfZGVwJHJlZ2lvbiksDQogICAgIGNleCA9IDAuNSkNCg0KYGBgDQpPbiB0aGUgbWVyZ2VkIGRhdGFzZXQgb25lIGNhbiBSaG9uZS1BbHBlcyBhbmQgQ290ZSBkJ0F6dXIsIHRoZSB0d28gYmlnZ2VzdCBlY29ub21pZXMgYWZ0ZXIgSWxlIGRlIEZyYW5jZS4NCg0KIyMgUENBDQoNCiMjIyMgUENBIG9uIHNhbGFyeSBkYXRhIGFuZCBkZWx0YSBNLUYNCg0KYGBge3J9DQoNCnBjYV9zYWxhcnkgPC0gcHJjb21wKHNhbGFyeV92YXJpYWJsZXMpDQphdXRvcGxvdChwY2Ffc2FsYXJ5LCBkYXRhID0gc2FsYXJ5X3dpdGhfZGVwLCBjb2xvdXIgPSAncmVnaW9uJywNCiAgICAgICAgIGxvYWRpbmdzID0gVFJVRSwgbG9hZGluZ3MuY29sb3VyID0gJ2JsdWUnLA0KICAgICAgICAgbG9hZGluZ3MubGFiZWwgPSBUUlVFLCBsb2FkaW5ncy5sYWJlbC5zaXplID0gMykNCnBsb3QocGNhX3NhbGFyeSwgdHlwZSA9ICJsIikNCg0KI2NyZWF0ZSBkYXRhc2V0IHdpdGggTS1GIGRpZmZlcmVuY2VzDQpzYWxhcnlfdmFyX2RlbHRhIDwtIGRhdGEuZnJhbWUoIk0tRiIgPSBzYWxhcnlfdmFyaWFibGVzJHNhbF9NYWxlcyAtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnlfdmFyaWFibGVzJHNhbF9GZW1hbGVzLA0KICAgICAgICAgICAgICAgICAgICAgIkV4ZWN1dGl2ZSBNLUYiID0gc2FsYXJ5X3ZhcmlhYmxlcyRzYWxfTV9leGVjdXRpdmUgLQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnlfdmFyaWFibGVzJHNhbF9GX2V4ZWN1dGl2ZSwNCiAgICAgICAgICAgICAgICAgICAgIm1pZE1hbmFnZXIgTS1GIiA9IHNhbGFyeV92YXJpYWJsZXMkc2FsX01fbWlkTWFuYWdlciAtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGFyeV92YXJpYWJsZXMkc2FsX0ZfbWlkTWFuYWdlciwNCiAgICAgICAgICAgICAgICAgICAgICAiZW1wbG95ZWUgTS1GIiA9IHNhbGFyeV92YXJpYWJsZXMkc2FsX01fZW1wbG95ZWUgLQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnlfdmFyaWFibGVzJHNhbF9GX2VtcGxveWVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgIndvcmtlciBNLUYiID0gc2FsYXJ5X3ZhcmlhYmxlcyRzYWxfTV93b3JrZXIgLQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnlfdmFyaWFibGVzJHNhbF9GX3dvcmtlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMTgtMjUgTS1GIiA9IHNhbGFyeV92YXJpYWJsZXMkc2FsX01fMThfMjUgLQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxhcnlfdmFyaWFibGVzJHNhbF9GXzE4XzI1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICIyNi01MCBNLUYiID0gc2FsYXJ5X3ZhcmlhYmxlcyRzYWxfTV8yNl81MCAtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGFyeV92YXJpYWJsZXMkc2FsX0ZfMjZfNTAsDQogICAgICAgICAgICAgICAgICAgICAgICI1MSBwbHVzIE0tRiIgPSBzYWxhcnlfdmFyaWFibGVzJHNhbF9NXzUxcGx1cyAtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbGFyeV92YXJpYWJsZXMkc2FsX0ZfNTFwbHVzKQ0KcGNhX3NhbGFyeV9kZWx0YSA8LSBwcmNvbXAoc2FsYXJ5X3Zhcl9kZWx0YSkNCmF1dG9wbG90KHBjYV9zYWxhcnlfZGVsdGEsIGRhdGEgPSBzYWxhcnlfd2l0aF9kZXAsIGNvbG91ciA9ICdyZWdpb24nLA0KICAgICAgICAgbG9hZGluZ3MgPSBUUlVFLCBsb2FkaW5ncy5jb2xvdXIgPSAnYmx1ZScsDQogICAgICAgICBsb2FkaW5ncy5sYWJlbCA9IFRSVUUsIGxvYWRpbmdzLmxhYmVsLnNpemUgPSAzKQ0KcGxvdChwY2Ffc2FsYXJ5X2RlbHRhLCB0eXBlID0gImwiKQ0KDQpgYGANClRoZXJlIGFyZSB0aHJlZSBtYWluIGRpcmVjdGlvbnMgb2YgZWlnZW52ZWN0b3JzDQpjb250cmlidXRpbmcgdG8gcHJpbmNpcGFsIGNvbXBvbmVudHMnIHNjb3JlczogdmFyaWFibGVzIGRlc2NyaWJpbmcgc2FsYXJpZXMgb2Ygb3JkaW5hcnkgc3RhZmYNCmFjcm9zcyBkaWZmZXJlbnQgYWdlIGNhdGVnb3JpZXMsIHZhcmlhYmxlcyBkZXNjcmliaW5nIG1pZC1tYW5hZ2VyIGFuZCBtYW5hZ2VyIHNhbGFyaWVzLCBhbmQNCnNhbGFyaWVzIG9mIGV4ZWN1dGl2ZXMuDQoNClBDQSBvbiBkZWx0YSBNLUYgcmV2ZWFsZWQgdGhyZWUgbWFpbiBkaXJlY3Rpb25zDQpvZiBlaWdlbnZlY3RvcnMgY29udHJpYnV0aW5nIHRvIHNjb3JlczogbWFsZS1mZW1hbGUgc2FsYXJ5IGRpZmZlcmVuY2VzIG9mIG1pZC1tYW5hZ2VycywNCmV4ZWN1dGl2ZXMsIGFuZCA1MSBhbmQgb2xkZXIgYWdlZCBwZW9wbGUuDQoNCg0KIyMjIyBQQ0Egb24gZmlybXMgZGF0YQ0KDQpgYGB7cn0NCmZpcm1zX3ZhcmlhYmxlc19ub3JtYWxpemVkIDwtIGZpcm1zX3ZhcmlhYmxlc1ssIC0xXS9wb3B1bGF0aW9uJHRvdGFsX3BvcHVsYXRpb24NCg0KcGNhX2Zpcm1zIDwtIHByY29tcChmaXJtc192YXJpYWJsZXNfbm9ybWFsaXplZCkNCmF1dG9wbG90KHBjYV9maXJtcywgZGF0YSA9IGZpcm1zX3dpdGhfZGVwLCBjb2xvdXIgPSAncmVnaW9uJywNCiAgICAgICAgIGxvYWRpbmdzID0gVFJVRSwgbG9hZGluZ3MuY29sb3VyID0gJ2JsdWUnLA0KICAgICAgICAgbG9hZGluZ3MubGFiZWwgPSBUUlVFLCBsb2FkaW5ncy5sYWJlbC5zaXplID0gMykNCnBsb3QocGNhX2Zpcm1zLCB0eXBlID0gImwiKQ0KDQpgYGANClNpbmNlIHRoZXJlIGlzIGEgc3Ryb25nDQptdWx0aWNvbGxpbmVhcml0eSBhbW9uZyB0aGUgdmFyaWFibGVzIG9mIHRoZSBmaXJtcyBkYXRhLCB0aGUgY29uZHVjdGVkIFBDQSByZXN1bHRzDQppbiB0aGUgdmFyaWFibGUgd2l0aCBjb25zaWRlcmFibHkgaGlnaGVyIHZhbHVlcywgaS5lLiB2ZXJ5IHNtYWxsIGZpcm1zLCB0byBmb3JtIGEgbWFpbg0KZWlnZW52ZWN0b3IsIGxlYXZpbmcgZmlybXMgb2Ygb3RoZXIgc2l6ZXMgdG8gZm9ybSBhIHNlcGFyYXRlIGdyb3VwIG9mIGVpZ2VudmVjdG9ycy4NCg0KDQojIyMjIFBDQSBvbiBjb21tdW5lIGRhdGFzZXQNCg0KYGBge3J9DQpjb21tdW5lX3RyaW1tZWQgPC0gY29tbXVuZVssYygibmJfZmlybXNfc2VydmljZSIsIm5iX2Zpcm1zX2NvbW1lcmNlIiwibmJfZmlybXNfY29uc3RydWN0aW9uIildDQpjb21tdW5lX3RyaW1tZWQkYWN0aXZlX2VtcGxfcHJvcCA8LSBjb21tdW5lJG5iX2FjdGl2ZV9lbXBsb3llZXMvcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uDQpjb21tdW5lX3RyaW1tZWQkbmJfZmlybXNfc2VydmljZSA8LSBjb21tdW5lJG5iX2Zpcm1zX3NlcnZpY2UvcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uDQpjb21tdW5lX3RyaW1tZWQkbmJfZmlybXNfY29tbWVyY2UgPC0gY29tbXVuZSRuYl9maXJtc19jb21tZXJjZS9wb3B1bGF0aW9uJHRvdGFsX3BvcHVsYXRpb24NCmNvbW11bmVfdHJpbW1lZCRuYl9maXJtc19jb25zdHJ1Y3Rpb24gPC0gY29tbXVuZSRuYl9maXJtc19jb25zdHJ1Y3Rpb24vcG9wdWxhdGlvbiR0b3RhbF9wb3B1bGF0aW9uDQojY29tbXVuZV90cmltbWVkIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGUoY29tbXVuZV90cmltbWVkKSkNCg0KbmFtZXMod2hpY2goc2FwcGx5KGNvbW11bmVfdHJpbW1lZCwgYW55TkEpKSkNCnN0cihjb21tdW5lX3RyaW1tZWQpDQoNCg0KY29tbXVuZV9wY2EgPC0gcHJjb21wKGNvbW11bmVfdHJpbW1lZCkNCmNvbW11bmVfcGNhDQpjb21tdW5lX3BjYV9zY29yZXM8LSBhcy5kYXRhLmZyYW1lKGNvbW11bmVfcGNhJHhbLDE6M10pDQoNCmF1dG9wbG90KGNvbW11bmVfcGNhLCBkYXRhID0gZ2VvLCBjb2xvdXIgPSAncmVnaW9uJywNCiAgICAgICAgIGxvYWRpbmdzID0gVFJVRSwgbG9hZGluZ3MuY29sb3VyID0gJ2JsdWUnLA0KICAgICAgICAgbG9hZGluZ3MubGFiZWwgPSBUUlVFLCBsb2FkaW5ncy5sYWJlbC5zaXplID0gMykNCnBsb3QoY29tbXVuZV9wY2EsIHR5cGUgPSAibCIpDQoNCiMgcGxvdDNkKGNvbW11bmVfcGNhWywxOjJdLCBjb2w9Y2x1c3RlciRjbHVzdGVyKzEpDQpzY2F0dGVyM2QoeCA9IGNvbW11bmVfcGNhX3Njb3JlcyRQQzEsIHkgPSBjb21tdW5lX3BjYV9zY29yZXMkUEMyLCB6ID0gY29tbXVuZV9wY2Ffc2NvcmVzJFBDMywgZ3JvdXBzID0gYXMuZmFjdG9yKGttZWFucyhjb21tdW5lX3BjYV9zY29yZXMsIA0KICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDYpJGNsdXN0ZXIpLA0KICAgICAgICAgIGdyaWQgPSBGQUxTRSwgc3VyZmFjZSA9IEZBTFNFKQ0KDQpgYGANClBDQSBjb25kdWN0ZWQNCm9uIHRoZSBjb21tdW5lIGRhdGFzZXQgcmV2ZWFsZWQgdHdvIG1haW4gZGlyZWN0aW9ucyBvZiBlaWdlbnZlY3RvcnM6DQpwcm9wb3J0aW9uIG9mIGFjdGl2ZSBlbXBsb3llcnMgaW4gZWFjaCB0b3duLCBhbmQgaW5kdXN0cnkgcmVsYXRlZCB2YXJpYWJsZXMuDQoNCiMjIyMgUENBIG9uIHNlbGVjdGVkIHZhcmlhYmxlcw0KDQpgYGB7cn0NCm51bXMgPC0gdW5saXN0KGxhcHBseShjb21tdW5lLCBpcy5udW1lcmljKSkgDQpjb21tdW5lX3NlbGVjdGVkIDwtIGNvbW11bmVbLCBudW1zXQ0KDQpjb21tdW5lX3NlbGVjdGVkIDwtIGNvbW11bmVfc2VsZWN0ZWRbLCAyOjIwXQ0KDQpjb21tdW5lX3NlbGVjdGVkX25vcm1hbGl6ZWQgPC0gY29tbXVuZV9zZWxlY3RlZFtdL3BvcHVsYXRpb24kdG90YWxfcG9wdWxhdGlvbg0KDQp1bmVtcF9wZXJjZW50IDwtIGFzLmRhdGEuZnJhbWUoKGluZXEkdW5lbXAxNV82NC9pbmVxJHBvcDE1XzY0KSoxMDApDQpuYW1lcyh1bmVtcF9wZXJjZW50KVsxXSA8LSAidW5lbXBfcGVyY2VudCINCg0Kc2VsZWN0ZWRfdmFyaWFibGVzIDwtIGNiaW5kKGZpcm1zX3ZhcmlhYmxlc19ub3JtYWxpemVkLCBzYWxhcnlfdmFyaWFibGVzLCBjb21tdW5lX3NlbGVjdGVkX25vcm1hbGl6ZWQsIHVuZW1wX3BlcmNlbnQpDQoNCnBjYV9zZWxlY3RlZF92YXJpYWJsZXMgPC0gcHJjb21wKHNlbGVjdGVkX3ZhcmlhYmxlcykNCmF1dG9wbG90KHBjYV9zZWxlY3RlZF92YXJpYWJsZXMsIGRhdGEgPSBnZW8sIGNvbG91ciA9ICdyZWdpb24nLA0KICAgICAgICAgbG9hZGluZ3MgPSBUUlVFLCBsb2FkaW5ncy5jb2xvdXIgPSAnYmx1ZScsDQogICAgICAgICBsb2FkaW5ncy5sYWJlbCA9IFRSVUUsIGxvYWRpbmdzLmxhYmVsLnNpemUgPSAzKQ0KcGxvdChwY2Ffc2VsZWN0ZWRfdmFyaWFibGVzLCB0eXBlID0gImwiKQ0KDQpgYGANCnJlZ2lvbmFsIEdEUCBpcyBhIHN0cmlraW5nIGltcG9ydGFudA0KZmFjdG9yLCBjb21wcmlzaW5nIGEgZGlzdGluY3RseSBvcmllbnRlZCBlaWdlbnZlY3Rvci4gQW5vdGhlciBkaXN0aW5jdCBlaWdlbnZlY3RvciwgdGhvdWdoIGxlc3MNCnN1YnN0YW50aWFsLCBpcyB0aGUgdW5lbXBsb3ltZW50IHJhdGUuDQoNCg0KIyMgQ2x1c3RlciBBbmFseXNpcw0KDQojIyMjIENsdXN0ZXIgQW5hbHlzaXMgb24gc2FsYXJ5DQpgYGB7cn0NCg0KI2NyZWF0ZSBkYXRhZnJhbWUgd2l0aCBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgc2NvcmVzIGFzIHZhcmlhYmxlcw0Kc2FsYXJ5X3BjYV9zY29yZXM8LSBhcy5kYXRhLmZyYW1lKHBjYV9zYWxhcnkkeFssMToyXSkNCnNhbGFyeV9wY2Ffc2NvcmVzIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGUoc2FsYXJ5X3BjYV9zY29yZXMpKQ0Kc2FwcGx5KHNhbGFyeV9wY2Ffc2NvcmVzLCB2YXIpDQoNCiANCiMgSy1tZWFucyBjbHVzdGVyaW5nDQpuIDwtIG5yb3coc2FsYXJ5X3BjYV9zY29yZXMpDQp3c3MgPC0gcmVwKDAsIDIyKQ0Kd3NzWzFdIDwtIChuIC0gMSkgKiBzdW0oc2FwcGx5KHNhbGFyeV9wY2Ffc2NvcmVzLCB2YXIpKQ0KZm9yIChpIGluIDI6MjIpDQogICAgd3NzW2ldIDwtIHN1bShrbWVhbnMoc2FsYXJ5X3BjYV9zY29yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IGkpJHdpdGhpbnNzKQ0KI3dlIGNhbiBzZWUgZnJvbSB0aGUgcGxvdCB0aGF0IG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXIgaXMgNQ0KcGxvdCgxOjIyLCB3c3MsIHR5cGUgPSAiYiIsIA0KICAgICB4bGFiID0gIk51bWJlciBvZiBncm91cHMiLA0KICAgICB5bGFiID0gIldpdGhpbiBncm91cHMgc3VtIG9mIHNxdWFyZXMiKQ0KDQojcGxvdHRpbmcgaW4gUEMgc3BhY2Ugc2hvd3MgdXMgdGhhdCB0aGVyZSBhcmUgaW5kZWVkIGRpc3Rpbmd1aXNoYWJsZSA1IGdyb3VwcyANCnBsb3Qoc2FsYXJ5X3BjYV9zY29yZXMsIA0KICAgICBwY2ggPSBrbWVhbnMoc2FsYXJ5X3BjYV9zY29yZXMsIA0KICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDUpJGNsdXN0ZXIpDQoNCiNub3cgd2UgaGF2ZSB0byBzZWUgd2hldGhlciB0aGVzZSBncm91cHMgY29ycmVzcG9uZCB0byBnZW9ncmFwaGljYWwgcmVnaW9ucy4uLg0KDQpgYGANCiMjIyMgY2x1c3RlcmluZyBzYWxhcnkgZGVsdGEgTS1GDQpgYGB7cn0NCnNhbGFyeV9kZWx0YV9wY2Ffc2NvcmVzIDwtIGFzLmRhdGEuZnJhbWUocGNhX3NhbGFyeV9kZWx0YSR4WywxOjNdKQ0KDQpuIDwtIG5yb3coc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXMpDQp3c3MgPC0gcmVwKDAsIDE1KQ0Kd3NzWzFdIDwtIChuIC0gMSkgKiBzdW0oc2FwcGx5KHNhbGFyeV9kZWx0YV9wY2Ffc2NvcmVzLCB2YXIpKQ0KZm9yIChpIGluIDI6MTUpDQogICAgd3NzW2ldIDwtIHN1bShrbWVhbnMoc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IGkpJHdpdGhpbnNzKQ0KI3dlIGNhbiBzZWUgZnJvbSB0aGUgcGxvdCB0aGF0IG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXIgaXMgNQ0KcGxvdCgxOjE1LCB3c3MsIHR5cGUgPSAiYiIsIA0KICAgICB4bGFiID0gIk51bWJlciBvZiBncm91cHMiLA0KICAgICB5bGFiID0gIldpdGhpbiBncm91cHMgc3VtIG9mIHNxdWFyZXMiKQ0KDQoNCiNwbG90cw0Kc2NhdHRlcjNkKHggPSBzYWxhcnlfZGVsdGFfcGNhX3Njb3JlcyRQQzEsIHkgPSBzYWxhcnlfZGVsdGFfcGNhX3Njb3JlcyRQQzIsIHogPSBzYWxhcnlfZGVsdGFfcGNhX3Njb3JlcyRQQzMsIGdyb3VwcyA9IGFzLmZhY3RvcihrbWVhbnMoc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXMsIA0KICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDYpJGNsdXN0ZXIpLA0KICAgICAgICAgIGdyaWQgPSBGQUxTRSwgc3VyZmFjZSA9IEZBTFNFKQ0KDQpjbHVzdGVycyA8LSBrbWVhbnMoc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXMsIA0KICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDYpDQoNCkZyYU1hcCA9IGdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyPWZyYV9jZW50ZXIsIHNjYWxlPTIsIHpvb209NiksIGV4dGVudD0ibm9ybWFsIikNCiANCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIHRvdGFsIHBvcHVsYXRpb24gZm9yIGVhY2ggdG93biIgDQpzYWxhcnlfZ2VvIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvJGxvbmdpdHVkZSwgeT1nZW8kbGF0aXR1ZGUsIGNvbG91cj0gZmFjdG9yKGNsdXN0ZXJzJGNsdXN0ZXIpKSwNCiAgICAgICAgICAgICBkYXRhPWdlbywgYWxwaGE9MC41LCBzaXplPTAuNSkgKyBsYWJzKGNvbG9yPScnKSArIGdndGl0bGUoIkNsdXN0ZXJzIG9mIHRvd25zIGJhc2VkIG9uIG1hbGUtZmVtYWxlIHNhbGFyeSBkaWZmZXJlbmNlIikNCnNhbGFyeV9nZW8NCg0KDQpzYWxhcnlfZGVsdGFfY2x1c3RlciA8LSBjYmluZChzYWxhcnlfdmFyX2RlbHRhLCBjbHVzdGVycyRjbHVzdGVyKQ0KbmFtZXMoc2FsYXJ5X2RlbHRhX2NsdXN0ZXIpWzldIDwtICJjbHVzdGVyIg0Kc2FsX2RlbHRhX2J5X2NsdXN0ZXIgPC0gYWdncmVnYXRlKHNhbGFyeV9kZWx0YV9jbHVzdGVyJE0uRiwgYnk9bGlzdChzYWxhcnlfZGVsdGFfY2x1c3RlciRjbHVzdGVyKSwgRlVOPW1lYW4pWzJdDQpuYW1lcyhzYWxfZGVsdGFfYnlfY2x1c3RlcilbMV0gPC0gIlNhbGFyeV9kaWZmZXJlbmNlIg0KDQpGcmFNYXAgPSBnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTYpLCBleHRlbnQ9Im5vcm1hbCIpDQogDQojIFBsb3QgIkRpc3RyaWJ1dGlvbiBvZiB0b3RhbCBwb3B1bGF0aW9uIGZvciBlYWNoIHRvd24iIA0Kc2FsYXJ5X2dlbyA8LQ0KICBGcmFNYXAgKw0KICBnZW9tX3BvaW50KGFlcyh4PWdlbyRsb25naXR1ZGUsIHk9Z2VvJGxhdGl0dWRlLCBjb2xvdXI9IHNhbGFyeV92YXJpYWJsZXMkc2FsYXJ5X3JhdGlvX0Z2c00pLA0KICAgICAgICAgICAgIGRhdGE9Z2VvLCBhbHBoYT0wLjUsIHNpemU9MC41KSArIGxhYnMoY29sb3I9JycpICsgZ2d0aXRsZSgiRmVtYWxlLU1hbGUgc2FsYXJ5IHJhdGlvIikNCnNhbGFyeV9nZW8NCg0KYGBgDQoNCiMjIyMgRmluZGluZyB0b3ducyB3aXRoIGxhcmdlIGRpZmZlcmVuY2UgaW4gbWFsZS1mZW1hbGUgc2FsYXJpZXMNCmBgYHtyfQ0KI2RldGVjdGluZyBleHRyZW1lIGNpdGllcyBpbiBQQ0ENCnF1YW50aWxlczwtdGFwcGx5KHNhbGFyeV9kZWx0YV9wY2Ffc2NvcmVzJFBDMSxmaXJtcyR0b3duLHF1YW50aWxlKQ0KbWlucSA8LSBzYXBwbHkoZmlybXMkdG93biwgZnVuY3Rpb24oeCkgcXVhbnRpbGVzW1t4XV1bIjI1JSJdKQ0KbWF4cSA8LSBzYXBwbHkoZmlybXMkdG93biwgZnVuY3Rpb24oeCkgcXVhbnRpbGVzW1t4XV1bIjc1JSJdKQ0KDQoNCnRvd25zIDwtIGFzLmRhdGEuZnJhbWUoZmlybXMkdG93bikNCmsgPC0gc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXNbd2hpY2goc2FsYXJ5X2RlbHRhX3BjYV9zY29yZXMkUEMxPG1pbnEgfCBzYWxhcnlfZGVsdGFfcGNhX3Njb3JlcyRQQzE+bWF4cSksIF0NCg0KI2ZpbmQgdG93bnMgd2l0aCBsYXJnZWRpZmZlcmVuY2UgDQpleHRyZW1lX3Rvd25fbGlzdCA8LSB0b3duc1t3aGljaChzYWxhcnlfZGVsdGFfcGNhX3Njb3JlcyRQQzE8bWlucSB8IHNhbGFyeV9kZWx0YV9wY2Ffc2NvcmVzJFBDMT5tYXhxKSwgXQ0KDQpleHRyZW1lX3Rvd25zIDwtIGFzLmRhdGEuZnJhbWUoZXh0cmVtZV90b3duX2xpc3QpDQoNCg0KI2RldGVjdGluZyBleHRyZW1lIGNpdGllcyBpbiBzYWxhcnkgZGVsdGENCnF1YW50aWxlczwtdGFwcGx5KHNhbGFyeV92YXJfZGVsdGEkTS5GLGZpcm1zJHRvd24scXVhbnRpbGUpDQptaW5xIDwtIHNhcHBseShmaXJtcyR0b3duLCBmdW5jdGlvbih4KSBxdWFudGlsZXNbW3hdXVsiMjUlIl0pDQptYXhxIDwtIHNhcHBseShmaXJtcyR0b3duLCBmdW5jdGlvbih4KSBxdWFudGlsZXNbW3hdXVsiNzUlIl0pDQoNCnRvd24gPC0gdG93bnNbd2hpY2goc2FsYXJ5X3Zhcl9kZWx0YSRNLkY8bWlucSB8IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsYXJ5X3Zhcl9kZWx0YSRNLkY+bWF4cSksIF0NCg0KZXh0cmVtZV90b3duc19tX2YgPC0gYXMuZGF0YS5mcmFtZSh0b3duKQ0KDQpnZW8gPC0gYXMuZGF0YS5mcmFtZShjYmluZChmaXJtcyR0b3duLCBnZW8pKQ0KY29sbmFtZXMoZ2VvKVtjb2xuYW1lcyhnZW8pPT0iZmlybXMkdG93biJdIDwtICJ0b3duIg0KZ2VvX2V4dHJlbWUgPC0gc3Vic2V0KGdlbywgdG93biAlaW4lIGV4dHJlbWVfdG93bnNfbV9mJHRvd24pDQpnZW9fZXh0cmVtZSA8LSBnZW9fZXh0cmVtZVssIGMoInRvd24iLCAibGF0aXR1ZGUiLCAibG9uZ2l0dWRlIildDQoNCkZyYU1hcCA9IGdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyPWZyYV9jZW50ZXIsIHNjYWxlPTIsIHpvb209NiksIGV4dGVudD0ibm9ybWFsIikNCiANCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIHRvdGFsIHBvcHVsYXRpb24gZm9yIGVhY2ggdG93biIgDQpzYWxhcnlfZ2VvIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvX2V4dHJlbWUkbG9uZ2l0dWRlLCB5PWdlb19leHRyZW1lJGxhdGl0dWRlKSwNCiAgICAgICAgICAgICBkYXRhPWdlb19leHRyZW1lLCBjb2w9InJlZCIsIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJUb3ducyB3aXRoIGxhcmdlIGRpZmZlcmVuY2UgaW4gbWFsZS1mZW1hbGUgc2FsYXJpZXMiKQ0Kc2FsYXJ5X2dlbw0KDQojQXMgeW91IGNhbiBzZWUsIHRoZSB0b3ducyB3aXRoIGxhcmdlIHNhbHJ5IGRpZmZlcmVuY2VzIGFyZSBjb25jZXRyYXRlZCBhcm91bmQgbGFyZ2UgY2l0aWVzIHN1Y2ggYXMgUGFyaXMgYW5kIEx5b24uDQoNCmBgYA0KIyMjIyBjbHVzdGVyaW5nIHRvd25zIGJhc2VkIG9uIHVuZW1wbG95ZWQgJQ0KYGBge3J9DQp1bmVtcF9wZXJjZW50IDwtIGFzLmRhdGEuZnJhbWUoKGluZXEkdW5lbXAxNV82NC9pbmVxJHBvcDE1XzY0KSoxMDApDQpuYW1lcyh1bmVtcF9wZXJjZW50KVsxXSA8LSAidW5lbXBfcGVyY2VudCINCg0KbiA8LSBucm93KHVuZW1wX3BlcmNlbnQpDQp3c3MgPC0gcmVwKDAsIDE1KQ0Kd3NzWzFdIDwtIChuIC0gMSkgKiBzdW0oc2FwcGx5KHVuZW1wX3BlcmNlbnQsIHZhcikpDQpmb3IgKGkgaW4gMjoxNSkNCiAgICB3c3NbaV0gPC0gc3VtKGttZWFucyh1bmVtcF9wZXJjZW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSBpKSR3aXRoaW5zcykNCiN3ZSBjYW4gc2VlIGZyb20gdGhlIHBsb3QgdGhhdCBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVyIGlzIDYNCnBsb3QoMToxNSwgd3NzLCB0eXBlID0gImIiLCANCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgZ3JvdXBzIiwNCiAgICAgeWxhYiA9ICJXaXRoaW4gZ3JvdXBzIHN1bSBvZiBzcXVhcmVzIikNCg0KY2x1c3RlcnMgPC0ga21lYW5zKHVuZW1wX3BlcmNlbnQsIDYpDQoNCnVuZW1wX3BlcmNlbnQgPC0gY2JpbmQodW5lbXBfcGVyY2VudCwgY2x1c3RlcnMkY2x1c3RlcikNCm5hbWVzKHVuZW1wX3BlcmNlbnQpWzJdIDwtICJjbHVzdGVyIg0KdW5lbXBfYnlfY2x1c3RlciA8LSBhZ2dyZWdhdGUodW5lbXBfcGVyY2VudCR1bmVtcF9wZXJjZW50LCBieT1saXN0KHVuZW1wX3BlcmNlbnQkY2x1c3RlciksIEZVTj1tZWFuKVsyXQ0KbmFtZXModW5lbXBfYnlfY2x1c3RlcilbMV0gPC0gInVuZW1wX3JhdGUoJSkiIA0KDQpGcmFNYXAgPSBnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTYpLCBleHRlbnQ9Im5vcm1hbCIpDQogDQojIFBsb3QgIkRpc3RyaWJ1dGlvbiBvZiB0b3RhbCBwb3B1bGF0aW9uIGZvciBlYWNoIHRvd24iIA0Kc2FsYXJ5X2dlbyA8LQ0KICBGcmFNYXAgKw0KICBnZW9tX3BvaW50KGFlcyh4PWdlbyRsb25naXR1ZGUsIHk9Z2VvJGxhdGl0dWRlLCBjb2xvdXI9IGZhY3RvcihjbHVzdGVycyRjbHVzdGVyKSwgc2l6ZSA9IHVuZW1wX3BlcmNlbnQkdW5lbXBfcGVyY2VudCksIGRhdGE9Z2VvLCBhbHBoYT0wLjUsIHNpemU9MC41KSArIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiVW5lbXBsb3ltZW50IHJhdGUiLCB2YWx1ZXMgPSB1bmVtcF9ieV9jbHVzdGVyJGB1bmVtcF9yYXRlKCUpYCkgKyBsYWJzKGNvbG9yPScnKSArIGdndGl0bGUoIkNsdXN0ZXJzIG9mIHRvd25zIGJhc2VkIG9uIHVuZW1wbG95bWVudCByYXRlIikNCnNhbGFyeV9nZW8NCg0KDQoNCmBgYA0KDQojIyMjIENsdXN0ZXJpbmcgYmFzZWQgb24gTG9jYXRpb25hbCBHaW5pIENvZWZmaWNpZW50DQpgYGB7cn0NCmluZHVzdHJ5IDwtIGNvbW11bmVbLCBjKCJuYl9maXJtc19zZXJ2aWNlIiwgIm5iX2Zpcm1zX2NvbW1lcmNlIiwgIm5iX2Zpcm1zX2NvbnN0cnVjdGlvbiIpXQ0KDQpscV9zZXJ2aWNlIDwtIChpbmR1c3RyeSRuYl9maXJtc19zZXJ2aWNlLyhpbmR1c3RyeSRuYl9maXJtc19zZXJ2aWNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbW1lcmNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbnN0cnVjdGlvbikpLyhzdW0oaW5kdXN0cnkkbmJfZmlybXNfc2VydmljZSkvc3VtKGluZHVzdHJ5KSkNCmxxX3NlcnZpY2UgPC0gYXMuZGF0YS5mcmFtZSgobHFfc2VydmljZSkpDQoNCg0KbHFfY29tbWVyY2UgPC0gKGluZHVzdHJ5JG5iX2Zpcm1zX2NvbW1lcmNlLyhpbmR1c3RyeSRuYl9maXJtc19zZXJ2aWNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbW1lcmNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbnN0cnVjdGlvbikpLyhzdW0oaW5kdXN0cnkkbmJfZmlybXNfY29tbWVyY2UpL3N1bShpbmR1c3RyeSkpDQpscV9jb21tZXJjZSA8LSBhcy5kYXRhLmZyYW1lKChscV9jb21tZXJjZSkpDQoNCmxxX2NvbnN0cnVjdGlvbiA8LSAoaW5kdXN0cnkkbmJfZmlybXNfY29uc3RydWN0aW9uLyhpbmR1c3RyeSRuYl9maXJtc19zZXJ2aWNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbW1lcmNlK2luZHVzdHJ5JG5iX2Zpcm1zX2NvbnN0cnVjdGlvbikpLyhzdW0oaW5kdXN0cnkkbmJfZmlybXNfY29uc3RydWN0aW9uKS9zdW0oaW5kdXN0cnkpKQ0KbHFfY29uc3RydWN0aW9uIDwtIGFzLmRhdGEuZnJhbWUoKGxxX2NvbnN0cnVjdGlvbikpDQoNCmluZHVzdHJ5X2xxIDwtIGNiaW5kKGluZHVzdHJ5LCBscV9zZXJ2aWNlLCBscV9jb21tZXJjZSwgbHFfY29uc3RydWN0aW9uKQ0KI2NyZWF0ZSBkYXRhZnJhbWUgd2l0aCB0aGUgY291bnRzIG9mIHRvd25zIHBlciByZWdpb24NCnJlZ2lvbnNfY291bnQgPC0gZGF0YS5mcmFtZSh0YWJsZShzYWxhcnlfd2l0aF9kZXAkcmVnaW9uKSkNCnJlZ2lvbnNfY291bnRfc3ViPC1yZWdpb25zX2NvdW50WyEocmVnaW9uc19jb3VudCRGcmVxPT0wIHwgcmVnaW9uc19jb3VudCRGcmVxPT0xKSxdDQojY2hlY2sgdG90YWwgc3VtDQpyb3cubmFtZXMocmVnaW9uc19jb3VudF9zdWIpIDwtIDE6bnJvdyhyZWdpb25zX2NvdW50X3N1YikNCnN1bShyZWdpb25zX2NvdW50X3N1YiRGcmVxKQ0KDQppbmR1c3RyeV9scSA8LSBjYmluZChmaXJtcyR0b3duLCBpbmR1c3RyeV9scSkNCm5hbWVzKGluZHVzdHJ5X2xxKVsxXSA8LSAidG93biIgDQpuYW1lcyhpbmR1c3RyeV9scSlbNV0gPC0gImxxX3NlcnZpY2UiDQpuYW1lcyhpbmR1c3RyeV9scSlbNl0gPC0gImxxX2NvbW1lcmNlIg0KbmFtZXMoaW5kdXN0cnlfbHEpWzddIDwtICJscV9jb25zdHJ1Y3Rpb24iDQoNCmRtIDwtIGRpc3QoaW5kdXN0cnlfbHFbLCA1OjddKQ0KI3JvdW5kIHRvIHR3byBkZWNpbWFscw0KZG0gPC0gcm91bmQoZG0sIDIpDQogDQojIEstbWVhbnMgY2x1c3RlcmluZw0KbiA8LSBucm93KGluZHVzdHJ5X2xxKQ0Kd3NzIDwtIHJlcCgwLCAyMikNCndzc1sxXSA8LSAobiAtIDEpICogc3VtKHNhcHBseShpbmR1c3RyeV9scVssIDU6N10sIHZhcikpDQpmb3IgKGkgaW4gMjoyMikNCiAgICB3c3NbaV0gPC0gc3VtKGttZWFucyhpbmR1c3RyeV9scVssIDU6N10sDQogICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IGkpJHdpdGhpbnNzKQ0KI3dlIGNhbiBzZWUgZnJvbSB0aGUgcGxvdCB0aGF0IG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXIgaXMgNQ0KcGxvdCgxOjIyLCB3c3MsIHR5cGUgPSAiYiIsIA0KICAgICB4bGFiID0gIk51bWJlciBvZiBncm91cHMiLA0KICAgICB5bGFiID0gIldpdGhpbiBncm91cHMgc3VtIG9mIHNxdWFyZXMiKQ0KDQpjbHVzdGVycyA8LSBrbWVhbnMoaW5kdXN0cnlfbHFbLCA1OjddLCA1KQ0KDQojTWFwcGluZyB0aGUgdG93bnMgYmFzZWQgb24gdGhlIGxvY2F0aW9uYWwgR2luaSBjb2VmZmljaWVudA0KRnJhTWFwID0gZ2dtYXAoZ2V0X2dvb2dsZW1hcChjZW50ZXI9ZnJhX2NlbnRlciwgc2NhbGU9Miwgem9vbT02KSwgZXh0ZW50PSJub3JtYWwiKQ0KIA0KIyBQbG90ICJEaXN0cmlidXRpb24gb2YgdG90YWwgcG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIiANCnNhbGFyeV9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBmYWN0b3IoY2x1c3RlcnMkY2x1c3RlcikpLA0KICAgICAgICAgICAgIGRhdGE9Z2VvLCBhbHBoYT0wLjUsIHNpemU9MC41KSArIGxhYnMoY29sb3I9JycpICsgZ2d0aXRsZSgiQ2x1c3RlcnMgb2YgdG93bnMgYmFzZWQgb24gbG9jYXRpb25hbCBnaW5pIGNvZWZmaWNpZW50IikNCnNhbGFyeV9nZW8NCg0KDQpgYGANCg0KIyMjIyBhcHBpbmcgdGhlIHRvd25zIGJhc2VkIG9uIHRoZSBpbnRlbnNpdHkgb2YgbG9jYXRpb25hbCBHaW5pIGNvZWZmaWNpZW50DQpgYGB7cn0NCiNNYXBwaW5nIHRoZSB0b3ducyBiYXNlZCBvbiB0aGUgaW50ZW5zaXR5IG9mIGxvY2F0aW9uYWwgR2luaSBjb2VmZmljaWVudCBvZiB0aGUgc2VydmljZSBpbmR1c3RyeQ0KRnJhTWFwID0gZ2dtYXAoZ2V0X2dvb2dsZW1hcChjZW50ZXI9ZnJhX2NlbnRlciwgc2NhbGU9Miwgem9vbT02KSwgZXh0ZW50PSJub3JtYWwiKQ0KIA0KIyBQbG90ICJEaXN0cmlidXRpb24gb2YgdG90YWwgcG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIiANCnNhbGFyeV9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBpbmR1c3RyeV9scSRscV9zZXJ2aWNlKSwNCiAgICAgICAgICAgICBkYXRhPWdlbywgYWxwaGE9MC41LCBzaXplPTAuNSkgKyBsYWJzKGNvbG9yPScnKSArIGdndGl0bGUoIlRvd25zIGJhc2VkIG9uIHNlcnZpY2UgbG9jYXRpb25hbCBnaW5pIGNvZWZmaWNpZW50IikNCnNhbGFyeV9nZW8NCg0KI01hcHBpbmcgdGhlIHRvd25zIGJhc2VkIG9uIHRoZSBpbnRlbnNpdHkgb2YgbG9jYXRpb25hbCBHaW5pIGNvZWZmaWNpZW50IG9mIHRoZSBjb21tZXJjZSBpbmR1c3RyeQ0KRnJhTWFwID0gZ2dtYXAoZ2V0X2dvb2dsZW1hcChjZW50ZXI9ZnJhX2NlbnRlciwgc2NhbGU9Miwgem9vbT02KSwgZXh0ZW50PSJub3JtYWwiKQ0KIA0KIyBQbG90ICJEaXN0cmlidXRpb24gb2YgdG90YWwgcG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIiANCnNhbGFyeV9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBpbmR1c3RyeV9scSRscV9jb21tZXJjZSksDQogICAgICAgICAgICAgZGF0YT1nZW8sIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJUb3ducyBiYXNlZCBvbiBjb21tZXJjZSBsb2NhdGlvbmFsIGdpbmkgY29lZmZpY2llbnQiKQ0Kc2FsYXJ5X2dlbw0KDQojTWFwcGluZyB0aGUgdG93bnMgYmFzZWQgb24gdGhlIGludGVuc2l0eSBvZiBsb2NhdGlvbmFsIEdpbmkgY29lZmZpY2llbnQgb2YgdGhlIGNvbW1lcmNlIGluZHVzdHJ5DQpGcmFNYXAgPSBnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTYpLCBleHRlbnQ9Im5vcm1hbCIpDQogDQojIFBsb3QgIkRpc3RyaWJ1dGlvbiBvZiB0b3RhbCBwb3B1bGF0aW9uIGZvciBlYWNoIHRvd24iIA0Kc2FsYXJ5X2dlbyA8LQ0KICBGcmFNYXAgKw0KICBnZW9tX3BvaW50KGFlcyh4PWdlbyRsb25naXR1ZGUsIHk9Z2VvJGxhdGl0dWRlLCBjb2xvdXI9IGluZHVzdHJ5X2xxJGxxX2NvbnN0cnVjdGlvbiksDQogICAgICAgICAgICAgZGF0YT1nZW8sIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJUb3ducyBiYXNlZCBvbiBjb25zdHJ1Y3Rpb24gbG9jYXRpb25hbCBnaW5pIGNvZWZmaWNpZW50IikNCnNhbGFyeV9nZW8NCg0KYGBgDQoNCg0KIyMjIyBTYWxhcnkgY2x1c3RlcnMgDQpgYGB7cn0NCiNjcmVhdGUgZGlzdGFuY2UgbWF0cml4IGZvciBoY2x1c3QNCmRtIDwtIGRpc3Qoc2FsYXJ5X3ZhcmlhYmxlcykNCiNyb3VuZCB0byB0d28gZGVjaW1hbHMNCmRtIDwtIHJvdW5kKGRtLCAyKQ0KIA0KIyBLLW1lYW5zIGNsdXN0ZXJpbmcNCm4gPC0gbnJvdyhzYWxhcnlfdmFyaWFibGVzKQ0Kd3NzIDwtIHJlcCgwLCAyMikNCndzc1sxXSA8LSAobiAtIDEpICogc3VtKHNhcHBseShzYWxhcnlfdmFyaWFibGVzLCB2YXIpKQ0KZm9yIChpIGluIDI6MjIpDQogICAgd3NzW2ldIDwtIHN1bShrbWVhbnMoc2FsYXJ5X3ZhcmlhYmxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBjZW50ZXJzID0gaSkkd2l0aGluc3MpDQojd2UgY2FuIHNlZSBmcm9tIHRoZSBwbG90IHRoYXQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlciBpcyA1DQpwbG90KDE6MjIsIHdzcywgdHlwZSA9ICJiIiwgDQogICAgIHhsYWIgPSAiTnVtYmVyIG9mIGdyb3VwcyIsDQogICAgIHlsYWIgPSAiV2l0aGluIGdyb3VwcyBzdW0gb2Ygc3F1YXJlcyIpDQoNCiNub3cgd2UgaGF2ZSB0byBzZWUgd2hldGhlciB0aGVzZSBncm91cHMgY29ycmVzcG9uZCB0byBnZW9ncmFwaGljYWwgcmVnaW9ucy4uLg0KYGBgDQoNCg0KIyMjIyBNYXBwaW5nIHRoZSBzYWxhcnkgY2x1c3RlcnMNCmBgYHtyfQ0KIA0KI25vdyB3ZSBoYXZlIHRvIHNlZSB3aGV0aGVyIHRoZXNlIGdyb3VwcyBjb3JyZXNwb25kIHRvIGdlb2dyYXBoaWNhbCByZWdpb25zLi4uDQpjbHVzdGVycyA8LSBrbWVhbnMoc2FsYXJ5X3ZhcmlhYmxlcywgNikNCnNhbGFyeV93aXRoX2RlcCRjbHVzdGVyIDwtIGNsdXN0ZXJzJGNsdXN0ZXINCmdlbyRjbHVzdGVyIDwtIGNsdXN0ZXJzJGNsdXN0ZXINCiANCkZyYU1hcCA9IGdnbWFwKGdldF9nb29nbGVtYXAoY2VudGVyPWZyYV9jZW50ZXIsIHNjYWxlPTIsIHpvb209NiksIGV4dGVudD0ibm9ybWFsIikNCiANCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIHRvdGFsIHBvcHVsYXRpb24gZm9yIGVhY2ggdG93biIgDQpzYWxhcnlfZ2VvIDwtDQogIEZyYU1hcCArDQogIGdlb21fcG9pbnQoYWVzKHg9Z2VvJGxvbmdpdHVkZSwgeT1nZW8kbGF0aXR1ZGUsIGNvbG91cj0gZmFjdG9yKGdlbyRjbHVzdGVyKSksDQogICAgICAgICAgICAgZGF0YT1nZW8sIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJDbHVzdGVycyBvZiB0b3ducyBiYXNlZCBvbiBzYWxhcnkiKQ0Kc2FsYXJ5X2dlbw0KDQpzYWxhcnlfY2wgPC0gY2JpbmQoc2FsYXJ5X3ZhcmlhYmxlcywgY2x1c3RlcnMkY2x1c3RlcikNCm5hbWVzKHNhbGFyeV9jbClbMzNdIDwtICJjbHVzdGVyIg0Kc2FsYXJ5X2NsdXN0ZXIgPC0gYWdncmVnYXRlKHNhbGFyeV9jbCRzYWxfZ2VuZXJhbCwgYnk9bGlzdChzYWxhcnlfY2wkY2x1c3RlciksIEZVTj1tZWFuKVsyXQ0KbmFtZXMoc2FsYXJ5X2NsdXN0ZXIpWzFdIDwtICJzYWxhcnkiIA0KDQogDQpgYGANCg0KDQojIyMjIENsdXN0ZXJpbmcgb24gc2VsZWN0ZWQgdmFyaWFibGVzDQpgYGB7cn0NCnNlbGVjdGVkX3BjYV9zY29yZXM8LSBhcy5kYXRhLmZyYW1lKHBjYV9zZWxlY3RlZF92YXJpYWJsZXMkeFssMTozXSkNCg0KIyBLLW1lYW5zIGNsdXN0ZXJpbmcNCm4gPC0gbnJvdyhzZWxlY3RlZF92YXJpYWJsZXMpDQp3c3MgPC0gcmVwKDAsIDIyKQ0Kd3NzWzFdIDwtIChuIC0gMSkgKiBzdW0oc2FwcGx5KHNlbGVjdGVkX3ZhcmlhYmxlcywgdmFyKSkNCmZvciAoaSBpbiAyOjIyKQ0KICAgIHdzc1tpXSA8LSBzdW0oa21lYW5zKHNlbGVjdGVkX3ZhcmlhYmxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBjZW50ZXJzID0gaSkkd2l0aGluc3MpDQojd2UgY2FuIHNlZSBmcm9tIHRoZSBwbG90IHRoYXQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlciBpcyA3DQpwbG90KDE6MjIsIHdzcywgdHlwZSA9ICJiIiwgDQogICAgIHhsYWIgPSAiTnVtYmVyIG9mIGdyb3VwcyIsDQogICAgIHlsYWIgPSAiV2l0aGluIGdyb3VwcyBzdW0gb2Ygc3F1YXJlcyIpDQoNCmNsdXN0ZXJzIDwtIGttZWFucyhzZWxlY3RlZF92YXJpYWJsZXMsIGNlbnRlcnMgPSA3KQ0Kc2NhdHRlcjNkKHggPSBzZWxlY3RlZF9wY2Ffc2NvcmVzJFBDMSwgeSA9IHNlbGVjdGVkX3BjYV9zY29yZXMkUEMyLCB6ID0gc2VsZWN0ZWRfcGNhX3Njb3JlcyRQQzMsIGdyb3VwcyA9IGFzLmZhY3RvcihjbHVzdGVycyRjbHVzdGVyKSwNCiAgICAgICAgICBncmlkID0gRkFMU0UsIHN1cmZhY2UgPSBGQUxTRSkNCg0KRnJhTWFwID0gZ2dtYXAoZ2V0X2dvb2dsZW1hcChjZW50ZXI9ZnJhX2NlbnRlciwgc2NhbGU9Miwgem9vbT02KSwgZXh0ZW50PSJub3JtYWwiKQ0KIA0KIyBQbG90ICJEaXN0cmlidXRpb24gb2YgdG90YWwgcG9wdWxhdGlvbiBmb3IgZWFjaCB0b3duIiANCnNhbGFyeV9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBmYWN0b3IoY2x1c3RlcnMkY2x1c3RlcikpLA0KICAgICAgICAgICAgIGRhdGE9Z2VvLCBhbHBoYT0wLjUsIHNpemU9MC41KSArIGxhYnMoY29sb3I9JycpICsgZ2d0aXRsZSgiQ2x1c3RlcnMgb2YgdG93bnMgYmFzZWQgb24gc2VsZWN0ZWQgdmFyaWFibGVzIikNCnNhbGFyeV9nZW8NCg0KDQpgYGANCg0KIyMjIyBDbHVzdGVyaW5nIGluIGNvbW11bmUNCmBgYHtyfQ0KI0sgbWVhbnMNCg0KbiA8LSBucm93KGNvbW11bmVfdHJpbW1lZCkNCndzcyA8LSByZXAoMCwgMTUpDQp3c3NbMV0gPC0gKG4gLSAxKSAqIHN1bShzYXBwbHkoY29tbXVuZV90cmltbWVkLCB2YXIpKQ0KZm9yIChpIGluIDI6MTUpDQogICAgd3NzW2ldIDwtIHN1bShrbWVhbnMoY29tbXVuZV90cmltbWVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSBpKSR3aXRoaW5zcykNCiN3ZSBjYW4gc2VlIGZyb20gdGhlIHBsb3QgdGhhdCBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVyIGlzIDExDQpwbG90KDE6MTUsIHdzcywgdHlwZSA9ICJiIiwNCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgZ3JvdXBzIiwNCiAgICAgeWxhYiA9ICJXaXRoaW4gZ3JvdXBzIHN1bSBvZiBzcXVhcmVzIikNCg0KYGBgDQoNCg0KIyMjIyBNYXBwaW5nIHRoZSBuZXcgY2x1c3RlcnMNCmBgYHtyfQ0KbiA8LSBucm93KGNvbW11bmVfdHJpbW1lZCkNCndzcyA8LSByZXAoMCwgMjIpDQp3c3NbMV0gPC0gKG4gLSAxKSAqIHN1bShzYXBwbHkoY29tbXVuZV90cmltbWVkJGFjdGl2ZV9lbXBsX3Byb3AsIHZhcikpDQpmb3IgKGkgaW4gMjoyMikNCiAgICB3c3NbaV0gPC0gc3VtKGttZWFucyhjb21tdW5lX3RyaW1tZWQkYWN0aXZlX2VtcGxfcHJvcCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjZW50ZXJzID0gaSkkd2l0aGluc3MpDQojd2UgY2FuIHNlZSBmcm9tIHRoZSBwbG90IHRoYXQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlciBpcyA3DQpwbG90KDE6MjIsIHdzcywgdHlwZSA9ICJiIiwgDQogICAgIHhsYWIgPSAiTnVtYmVyIG9mIGdyb3VwcyIsDQogICAgIHlsYWIgPSAiV2l0aGluIGdyb3VwcyBzdW0gb2Ygc3F1YXJlcyIpDQoNCiNub3cgd2UgaGF2ZSB0byBzZWUgd2hldGhlciB0aGVzZSBncm91cHMgY29ycmVzcG9uZCB0byBnZW9ncmFwaGljYWwgcmVnaW9ucy4uLg0KDQpGcmFNYXAgPSBnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTYpLCBleHRlbnQ9Im5vcm1hbCIpDQoNCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIHRvdGFsIHBvcHVsYXRpb24gZm9yIGVhY2ggdG93biINCm5ld0RhdF9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBmYWN0b3Ioa21lYW5zKGNvbW11bmVfdHJpbW1lZCRhY3RpdmVfZW1wbF9wcm9wLCANCiAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSA2KSRjbHVzdGVyKSksDQogICAgICAgICAgICAgZGF0YT1nZW8sIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJDbHVzdGVycyBvZiB0b3ducyBiYXNlZCBvbiBhY3RpdmUgZW1wbG95ZWVzIHByb3BvcnRpb24iKQ0KbmV3RGF0X2dlbw0KDQpGcmFNYXAgPSBnZ21hcChnZXRfZ29vZ2xlbWFwKGNlbnRlcj1mcmFfY2VudGVyLCBzY2FsZT0yLCB6b29tPTYpLCBleHRlbnQ9Im5vcm1hbCIpDQoNCiMgUGxvdCAiRGlzdHJpYnV0aW9uIG9mIHRvdGFsIHBvcHVsYXRpb24gZm9yIGVhY2ggdG93biINCm5ld0RhdF9nZW8gPC0NCiAgRnJhTWFwICsNCiAgZ2VvbV9wb2ludChhZXMoeD1nZW8kbG9uZ2l0dWRlLCB5PWdlbyRsYXRpdHVkZSwgY29sb3VyPSBjb21tdW5lX3RyaW1tZWQkYWN0aXZlX2VtcGxfcHJvcCksDQogICAgICAgICAgICAgZGF0YT1nZW8sIGFscGhhPTAuNSwgc2l6ZT0wLjUpICsgbGFicyhjb2xvcj0nJykgKyBnZ3RpdGxlKCJDbHVzdGVycyBvZiB0b3ducyBiYXNlZCBvbiBhY3RpdmUgZW1wbG95ZWVzIikNCm5ld0RhdF9nZW8NCmBgYA0KDQoNCg0KDQoNCiMgU3VwZXJ2aXNlZCBMZWFybmluZw0KDQojIyBBTk9WQSBmb3Igc2FsYXJ5DQoNCkZpcnN0IHRoZSBkaXN0cmlidXRpb24gb2Ygc2FsYXJ5IGZvciBib3RoIGdlbmRlcnMgaXMgY29uc2lkZXJlZCwgaW4gb3JpZ2luYWwgYW5kIGxvZy1zY2FsZQ0KYGBge3IgaGlzdCBzYWxhcnkgZm9yIGdlbmRlcn0NCg0KIyBvcmlnaW5hbCBkYXRhDQojIG1hbGUNCmdncGxvdChkYXRhPWRhdGEuZnJhbWUobmV3RGF0JHNhbF9NYWxlcyksIGFlcyhuZXdEYXQkc2FsX01hbGVzKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSA0MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sPSJibGFjayIpICsNCiAgbGFicyh4PSJNYWxlIHNhbGFyaWVzIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHNhbGFyeSBmb3IgbWFsZXMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KIyBmZW1hbGVzDQpnZ3Bsb3QoZGF0YT1kYXRhLmZyYW1lKG5ld0RhdCRzYWxfRmVtYWxlcyksIGFlcyhuZXdEYXQkc2FsX0ZlbWFsZXMpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDQwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9IkZlbWFsZSBzYWxhcmllcyIsIHk9IkRlbnNpdHkiKSArDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBzYWxhcnkgZm9yIGZlbWFsZXMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQojIGxvZyBkYXRhDQojIG1hbGUNCmdncGxvdChkYXRhPWRhdGEuZnJhbWUobG9nMTAobmV3RGF0JHNhbF9NYWxlcykpLCBhZXMobG9nMTAobmV3RGF0JHNhbF9NYWxlcykpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0uLmRlbnNpdHkuLiksIGNvbD0iYmxhY2siLCBmaWxsPSJibHVlIiwgYWxwaGEgPSAuMywgYmlucyA9IDQwKSArDQogIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KICBsYWJzKHg9IkxvZzEwIG1hbGUgc2FsYXJpZXMiLCB5PSJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygc2FsYXJ5IGZvciBtYWxlcyBpbiBsb2cgc2NhbGUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KIyBmZW1hbGVzDQpnZ3Bsb3QoZGF0YT1kYXRhLmZyYW1lKGxvZzEwKG5ld0RhdCRzYWxfRmVtYWxlcykpLCBhZXMobG9nMTAobmV3RGF0JHNhbF9GZW1hbGVzKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNDApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTG9nMTAgZmVtYWxlIHNhbGFyaWVzIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHNhbGFyeSBmb3IgZmVtYWxlcyBpbiBsb2cgc2NhbGUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQojIGhpc3QoYyhsb2cxMChuZXdEYXQkc2FsX01hbGVzKSwgbG9nMTAobmV3RGF0JHNhbF9GZW1hbGVzKSksIDMwKQ0KDQpgYGANCg0KSXQgaXMgY2xlYXIgdGhhdCB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24sIGNhbm5vdCBob2xkLg0KDQpBdCB0aGUgc2FtZSB0aW1lLCBpdCBpcyBzaG93bg0KaG93IHRvIGNoZWNrIGlmIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBtZWFuIGluIHNhbGFyeSANCmJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMgdXNpbmcgYSB0LXRlc3QgDQpgYGB7ciBzYWxhcnkgZGlmZmVyZW5jZSB0IHRlc3R9DQoNCnQudGVzdChsb2cxMChuZXdEYXQkc2FsX01hbGVzKSwgbG9nMTAobmV3RGF0JHNhbF9GZW1hbGVzKSwgYWx0ZXJuYXRpdmUgPSBjKCJ0d28uc2lkZWQiKSkNCg0KYGBgDQoNClRoaXMgc29sdXRpb24gY2FuIGFsc28gYmUgb3RiYWluZWQgZml0dGluZyBhIGxpbmVhciBtb2RlbCB3aXRoIGEgZHVtbXkgdmFyaWFibGUgY2F0Y2hpbmcgdGhlIHNleCBlZmZlY3QsDQp3aGljaCBjb3JyZXNwb25kcyB0byBhIG9uZS13YXkgQU5PVkEgbW9kZWwNCmBgYHtyIHNhbGFyeSBkaWZmZXJlbmNlIHQgdGVzdCBhcyBhbiBBTk9WQX0NCg0KIyBjcmVhdGUgdGhlIHJlcG9uc2UgYXR0YWNoaW5nIHRoZSBzYWxhcnkgZm9yIGVhY2ggc2V4DQpzYWxfeSA9IGMobG9nMTAobmV3RGF0JHNhbF9NYWxlcyksIGxvZzEwKG5ld0RhdCRzYWxfRmVtYWxlcykpDQojIGNyZWF0ZSB0aGUgZHVtbXkgdmFyaWFibGUgY29udHJvbGxpbmcgZm9yIHRoZSBjb3JyZXNwb25kaW5nIHNleA0KZHVtbXkgPSBjKHJlcCgxLCBsZW5ndGgobmV3RGF0JHNhbF9NYWxlcykpLCByZXAoMCwgbGVuZ3RoKG5ld0RhdCRzYWxfRmVtYWxlcykpKQ0KDQojIHRoZSB0IHZhbHVlIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIG9uZSBvYnRhaW5lZCBpbiB0aGUgcHJldmlvdXMgdC10ZXN0DQpzYWxfQU5PVkEgPSBsbShzYWxfeSB+IGR1bW15KQ0Kc3VtbWFyeShzYWxfQU5PVkEpDQoNCiMgcmVzaWR1YWxzIHBsb3QNCnBsb3Qoc2FsX0FOT1ZBJHJlc2lkdWFscykNCmFibGluZSAoMCwgMCwgY29sID0gInJlZCIpDQoNCmBgYA0KDQpUbyBpbXByb3ZlIHN1Y2ggYW5hbHlzaXMgc29tZSBtb3JlIGFzcGVjdHMgYXJlIG5vdyBjb25zaWRlcmVkLg0KSW4gcGFydGljdWFsYXIsIHRoZSBtb2RlbCBjb25zaWRlcmVkIGluIGFuIEFOT1ZBLCB3aGVyZSB0aGUNCnByZWRpY3RvcnMgdGFrZSBpbiBhY2NvdW50IGVhY2ggZ2VuZGVyIGZvciBkaWZmZXJlbnQgYWdlIGFuZCBqb2IgbGV2ZWxzLA0KcGx1cyB0aGVpciBpbnRlcmFjdGlvbiB0ZXJtcy4NCg0KRmlyc3Qgb2YgYWxsIHRoZSBkYXRhc2V0IGlzIGNyZWF0ZWQgYW5kIGEgc3Vic2FtcGxlIG9mIGl0IGlzIGNvbnNpZGVyZWQsIA0KaW4gb3JkZXIgdG8gYXZvaWQgc3Ryb25nIGNvcnJlbGF0aW9uIGFtb25nIHVuaXRzDQpgYGB7ciBzYWxhcnkgY29uc3RydWN0IGZ1bGwgQU5PVkEgbW9kZWx9DQoNCnNldC5zZWVkKDMwMCkNCg0KIyBjcmVhdGUgcmVzcG9uc2UgdmFyaWFibGUNCnNhbF95ID0gYyhuZXdEYXQkc2FsX01fMThfMjUsIG5ld0RhdCRzYWxfTV8yNl81MCwgbmV3RGF0JHNhbF9NXzUxcGx1cywNCiAgICAgICAgICBuZXdEYXQkc2FsX01fZXhlY3V0aXZlLCBuZXdEYXQkc2FsX01fbWlkTWFuYWdlciwgbmV3RGF0JHNhbF9NX2VtcGxveWVlLCBuZXdEYXQkc2FsX01fd29ya2VyLA0KICAgICAgICAgIG5ld0RhdCRzYWxfRl8xOF8yNSwgbmV3RGF0JHNhbF9GXzI2XzUwLCBuZXdEYXQkc2FsX0ZfNTFwbHVzLA0KICAgICAgICAgIG5ld0RhdCRzYWxfRl9leGVjdXRpdmUsIG5ld0RhdCRzYWxfRl9taWRNYW5hZ2VyLCBuZXdEYXQkc2FsX0ZfZW1wbG95ZWUsIG5ld0RhdCRzYWxfRl93b3JrZXIpDQoNCm5fc2FsX3kgPSBsZW5ndGgoc2FsX3kpICAgICAgICAgICAgICMgbGVuZ3RoIHJlc3BvbnNlIHZhcmlhYmxlDQpuX2NhdCA9IGxlbmd0aChuZXdEYXQkc2FsX01fMThfMjUpICAjIGxlbmd0aCBvZiBlYWNoIGNhdGVnb3J5IChpLmUuLCBvcmlnaW5hbCB2ZWN0b3JzKQ0KDQojIGNyZWF0ZSBzZXggZHVtbXkgdmFyaWFibGUsIDEgZm9yIG1hbGVzIGFuZCAwIGZvciBmZW1hbGVzDQpzYWxfc2V4ID0gcmVwKDAsIG5fc2FsX3kpICAgIyBmdWxsIHJlZ3Jlc3NvcnMNCnNhbF9zZXhbMTpuX3NhbF95LzJdID0gMSAgICAjIGFzc2lnbiBtYWxlcw0KDQojIGNyZWF0ZSBhZ2UgZHVtbXkgdmFyaWFibGVzLCAxOC0yNSB5ZWFycyBvbGQgaXMgdGhlIGJhc2UgY2FzZQ0Kc2FsX2FnZSA9IGNiaW5kKHJlcCgwLCBuX3NhbF95KSwgcmVwKDAsIG5fc2FsX3kpKSAjIGZ1bGwgcmVncmVzc29ycw0KIyAyNi01MCB5Lm8uDQpzYWxfYWdlWyhuX2NhdCsxKToobl9jYXQqMiksIDFdID0gMSAgICAgIyBtYWxlcw0Kc2FsX2FnZVsobl9jYXQqOCsxKToobl9jYXQqOSksIDFdID0gMSAgICMgZmVtYWxlcw0KIyA1MSsgeS5vLg0Kc2FsX2FnZVsobl9jYXQqMisxKToobl9jYXQqMyksIDJdID0gMSAgICMgbWFsZXMNCnNhbF9hZ2VbKG5fY2F0KjkrMSk6KG5fY2F0KjEwKSwgMl0gPSAxICAjIGZlbWFsZXMNCg0KIyBjcmVhdGUgam9iIHR5cGUgZHVtbXkgdmFyaWFibGVzLCB3b3JrZXIgaXMgdGhlIGJhc2UgY2FzZQ0Kc2FsX2pvYiA9IGNiaW5kKHJlcCgwLCBuX3NhbF95KSwgcmVwKDAsIG5fc2FsX3kpLCByZXAoMCwgbl9zYWxfeSkpICMgZnVsbCByZWdyZXNzb3JzDQojIGV4ZWN1dGl2ZXMNCnNhbF9qb2JbKG5fY2F0KjMrMSk6KG5fY2F0KjQpLCAxXSA9IDEgICAgICMgbWFsZXMNCnNhbF9qb2JbKG5fY2F0KjEwKzEpOihuX2NhdCoxMSksIDFdID0gMSAgICMgZmVtYWxlcw0KIyBtaWRkbGUgbWFuYWdlcnMNCnNhbF9qb2JbKG5fY2F0KjQrMSk6KG5fY2F0KjUpLCAyXSA9IDEgICAgICMgbWFsZXMNCnNhbF9qb2JbKG5fY2F0KjExKzEpOihuX2NhdCoxMiksIDJdID0gMSAgICMgZmVtYWxlcw0KIyBlbXBsb3llZQ0Kc2FsX2pvYlsobl9jYXQqNSsxKToobl9jYXQqNiksIDNdID0gICAxICAgIyBtYWxlcw0Kc2FsX2pvYlsobl9jYXQqMTIrMSk6KG5fY2F0KjEzKSwgM10gPSAxICAgIyBmZW1hbGVzDQoNCiMgZmluYWwgZGF0YSBzZXQNCmRhdGFfQU5PVkEgPSBjYmluZC5kYXRhLmZyYW1lKHJlc3BvbnNlID0gc2FsX3ksIHNleCA9IHNhbF9zZXgsIGFnZSA9IHNhbF9hZ2UsIGpvYiA9IHNhbF9qb2IpDQojIG5hbWVzKGRhdGFfQU5PVkEpDQojIHNob3cgcmVncmVzc29ycycgc2hhcGUNCmltYWdlbWF0KGRhdGFfQU5PVkFbLC0xXSwgeGF4dCA9ICJuIiwgbWFpbiA9ICJGYWN0b3JzIGZvciBBTk9WQSIpDQpheGlzKDEsIGF0PTE6NiwgbGFiZWxzPWMoIlNleCIsICJBZ2UgMjYtNTAiLCAiQWdlIDUxKyIsICJFeGVjdXQuIiwgIk1pZC5NYW4uIiwgIkVtcGwuIikpDQpib3goKQ0KIyBzdWIgc2FtcGxlIHRvIGF2b2lkIGNvcnJlbGF0aW9uLCAodW5iYWxhbmNlZCBkYXRhc2V0KQ0KIyBzZXQuc2VlZCgyMCkNCnN1YnMgPSBzYW1wbGUoMTpuX3NhbF95LCBzaXplID0gcm91bmQobl9zYWxfeSowLjEpKQ0KZGF0YV9BTk9WQSA9IGRhdGFfQU5PVkFbc3VicyxdDQoNCiMgc2hvdyByYW5kb21pemVkIGRhdGENCmltYWdlbWF0KGRhdGFfQU5PVkFbLC0xXSwgeGF4dCA9ICJuIiwgbWFpbiA9ICJGYWN0b3JzIGZvciBBTk9WQSBhZnRlciBzdWItc2FtcGxpbmciKQ0KYXhpcygxLCBhdD0xOjYsIGxhYmVscz1jKCJTZXgiLCAiQWdlIDI2LTUwIiwgIkFnZSA1MSsiLCAiRXhlY3V0LiIsICJNaWQuTWFuLiIsICJFbXBsLiIpKQ0KYm94KCkNCg0KYGBgDQoNClJ1biB0aGUgQU5PVkEgbW9kZWwsIHRyYW5mb3JtaW5nIHRoZSByZXNwb25zZSB2YXJpYWJsZSB1c2luZyBCb3gtQ294IHRyYW5zZm9ybWF0aW9uDQpgYGB7ciBzYWxhcnkgQU5PVkEgbW9kZWx9DQoNCiMgcGxvdCB0aGUgdHJhbnNmb3JtZWQgcmVzcG9uc2UgdmFyaWFibGUgKHN1Z2dlc3RlZCBieSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uKQ0Kc2FsX3kgPSBkYXRhX0FOT1ZBWywxXV4tMQ0KZ2dwbG90KGRhdGE9ZGF0YS5mcmFtZShkYXRhX0FOT1ZBWywxXSksIGFlcyhzYWxfeSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gNjApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0ic2FsYXJ5Xi0xIiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiVHJhbnNmb3JtYXRpb24gb2Ygc2FsYXJ5IGZvdW5kIHVzaW5nIEJveC1Db3ggdHJhbnNmb3JtYXRpb24iKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQojIHRha2UgdGhlIGlkZW50aWZpZWQgc3Vic2FtcGxlIGZvciBkdW1teSB2YXJpYWJsZXMNCnNleCA9IHNhbF9zZXhbc3Vic10NCmFnZSA9IHNhbF9hZ2Vbc3VicyxdDQpqb2IgPSBzYWxfam9iW3N1YnMsXQ0KIyBBTk9WQSBtb2RlbA0Kc2FsX0FOT1ZBID0gbG0oc2FsX3kgfiBzZXggKyBhZ2UgKyBqb2IgKyBzZXg6YWdlICsgc2V4OmpvYikNCnN1bW1hcnkoc2FsX0FOT1ZBKQ0KYW5vdmEoc2FsX0FOT1ZBKQ0KIyBhb3Yoc2FsX3kgfiBzZXggKyBhZ2UgKyBqb2IgKyBzZXg6YWdlICsgc2V4OmpvYikNCnBsb3Qoc2FsX0FOT1ZBLCAxKQ0KDQojIGJveC1jb3ggdHJhbnNmb3JtYXRpb24gc3VnZ2VzdGVkIHRvIHVzZSB5Xi0xDQpib3hjb3goc2FsX0FOT1ZBKQ0KdGl0bGUoIkJveC1Db3ggdHJhbnNmb3JtYXRpb24gb2YgdGhlIHJlc3BvbnNlIikNCg0KIyBjb21wYXJpc29uIGEgZnVsbCBtb2RlbCB3aXRoIGEgcmVkdWNlZCBvbmUgZXhjbHVkaW5nIG9uZSBpbnRlcmFjdGlvbiB0ZXJtDQpzYWxfQU5PVkEyID0gbG0oc2FsX3kgfiBzZXggKyBhZ2UgKyBqb2IgKyBzZXg6YWdlKQ0KYW5vdmEoc2FsX0FOT1ZBLCBzYWxfQU5PVkEyKQ0KDQpgYGANCg0KDQpUbyBjaGVjayBpZiB0aGUgbW9kZWwgY2FuIGJlIHNwYXJzaWZpZWQsIGNvbnNpZGVyaW5nIGdyb3VwcyBvZiB2YXJpYWJsZXMNCmFzIGEgd2hvbGUsIHRoZSBncm91cC1MYXNzbyBpcyBhcHBsaWVkLg0KSXQgc2hvd3MgdGhhdCB0aGUgaW50ZXJhY3Rpb24gdGVybSBzZXg6am9iIGlzIG5vdCBzbyBpbXBvcnRhbnQuDQpgYGB7ciBzYWxhcnkgZ3JvdXAtTGFzc299DQoNCiMgZGVmaW5lIHRoZSBjb25zaWRlcmVkIG1vZGVsLCB3aGljaCBpbmNsdWRlcyBpbnRlcmFjdGlvbiB0ZXJtcw0KZm9ybSA8LSBtb2RlbC5tYXRyaXgoc2FsX3kgfiAoc2V4ICsgYWdlICsgam9iKV4yKQ0KIyByZW1vdmUgaW50ZXJhY3Rpb24gYWdlOmpvYg0KIyBjb2xuYW1lcyhmb3JtKQ0KZm9ybSA9IGZvcm1bLDI6MTJdDQoNCiMgZGVmaW5lIGdyb3VwcycgbGFiZWxzDQpncnAgPSBjKDEsMiwyLDMsMywzLDQsNCw1LDUsNSkNCg0KI0dyb3VwLUxhc3NvIHdpdGggMTAtZm9sZHMgQ3Jvc3MgVmFsaWRhdGlvbiBhbmQgTEFEIGNvc3QgZnVuY3Rpb24NCmZpdC5jdj1jdi5nZ2xhc3NvKHg9Zm9ybSwgeT1zYWxfeSwgZ3JvdXA9Z3JwLCBuZm9sZHM9MTAsIHByZWQubG9zcyA9ICJMMiIpDQoNCiMgcGxvdCBDViBmb3IgTGFtYmRhDQpwbG90KGZpdC5jdikgDQptdGV4dChleHByZXNzaW9uKCIxMC1mb2xkcyBDViBmb3IgZ3JvdXAtTGFzc28gaW4gQU5PVkEiKSwgb3V0ZXI9VFJVRSwgIGNleD0xLCBsaW5lPS0yLjIpDQoNCiMgZXh0cmFjdCBjb2VmZmljaWVudHMNCiMgY29lZi5tYXQgPSBmaXQuY3YkZ2dsYXNzby5maXQkYmV0YQ0KIyBwbG90IHNlcGFyYXRlbHkgZWFjaCBjb2VmZmljaWVudA0KY29scyA9IGJyZXdlci5wYWwoNSxuYW1lPSJTZXQxIikNCnBsb3QoZml0LmN2JGdnbGFzc28uZml0LCBjb2wgPSBjb2xzLCBsd2QgPSAzLCBtYWluID0gIkNvZWZmaWNpZW50cyB2cy5MYW1iZGEgdmFsdWVzIikNCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBwYXN0ZSgiR3JvdXAiLCAxOjUpLCBjb2w9Y29sc1sxOjVdLCBjZXggPSAwLjY1LCBwY2ggPSAxLCBsd2Q9NCwgYnR5ID0ibiIpDQoNCiMgcGxvdCBjb2VmZmljaWVudHMnIG5vcm1zIChub3Qgd29ya2luZyksIGRvaW5nIGl0IG1hbnVhbGx5DQojIGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9nZ2xhc3NvL3ZlcnNpb25zLzEuMy90b3BpY3MvcGxvdC5nZ2xhc3NvDQojIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL2FyY2hpdmUvcC9nZ2xhc3NvL2lzc3Vlcw0KICAjIHBsb3QoZml0LmN2JGdnbGFzc28uZml0LCBncm91cD1UUlVFLCBjb2wgPSAgY29scywgbHdkID0gMikNCiAgIyBsZWdlbmQoJ2JvdHRvbXJpZ2h0JywgbGVnZW5kID0gcGFzdGUoIkdyb3VwIiwgMTo1KSwgY29sPWNvbHNbMTo1XSwgY2V4ID0gMC42NSwgcGNoID0gMSwgbHdkPTQsIGJ0eSA9Im4iKQ0KIyBpbml0aWFsaXplIG5vcm1zDQpjb2VmZk5vcm0gPSBtYXRyaXgoMCwgbnJvdyA9ICBtYXgoZml0LmN2JGdnbGFzc28uZml0JGdyb3VwKSwgbGVuZ3RoKGZpdC5jdiRnZ2xhc3NvLmZpdCRsYW1iZGEpKQ0KIyBjb21wdXRlIG5vcm1zIGdyb3VwLXdpc2UNCmZvciAoaSBpbiAxOm1heChmaXQuY3YkZ2dsYXNzby5maXQkZ3JvdXApKXsgDQogIGluZCA9IGZpdC5jdiRnZ2xhc3NvLmZpdCRncm91cCA9PSBpDQogIGNvZWZmID0gZml0LmN2JGdnbGFzc28uZml0JGJldGFbaW5kLF0NCiAgaWYgKHN1bShpbmQpPjEpew0KICAgIGNvZWZmTm9ybVtpLF0gPSBhcHBseShjb2VmZiwgMiwgZnVuY3Rpb24oeCl7c3FydChzdW0oeF4yKSl9KQ0KICB9IGVsc2UgY29lZmZOb3JtW2ksXSA9IGNvZWZmDQp9IA0KIyBjb2VmZmljaWVudHMnIG5vcm0gcGxvdA0KcGxvdChsb2coZml0LmN2JGdnbGFzc28uZml0JGxhbWJkYSksIGNvZWZmTm9ybVsxLF0sIG1haW49IkNvZWZmaWNpZW50cycgbm9ybSB2cyBMYW1iZGEiLA0KICAgICB5bGFiPSJDb2VmZmljaWVudHMnIG5vcm0iLHhsYWI9IkxuIExhbWJkYSB2YWx1ZXMiLA0KICAgICBjb2w9Y29sc1sxXSwNCiAgICAgIyB4bGltPWMoLTEsMTAwKSwNCiAgICAgeWxpbT1jKG1pbihjb2VmZk5vcm0pLCBtYXgoY29lZmZOb3JtKSksDQogICAgIHR5cGU9ImwiLGx3ZD00KQ0KZm9yIChqIGluIDI6bnJvdyhjb2VmZk5vcm0pKXsNCiAgbGluZXMobG9nKGZpdC5jdiRnZ2xhc3NvLmZpdCRsYW1iZGEpLCBjb2VmZk5vcm1baixdLCB0eXBlPSJsIixsd2Q9NCwgY29sPWNvbHNbal0pDQp9DQphYmxpbmUoMCwwLCBsd2Q9MykNCiMgcGxvdCBsZWdlbmQgb25jZQ0KIyBncmlkKCkNCnBhcihuZXc9VCkNCmxlZ2VuZCgndG9wcmlnaHQnLCBsZWdlbmQgPSBwYXN0ZSgiZ3JvdXAgIiwgMTo1KSwgbHR5PTEsIGNvbD1jb2xzWzE6NV0sIGNleCA9IDAuNixsd2Q9MikNCg0KI1BpY2sgdGhlIGJlc3QgTGFtYmRhDQpsbWJkYT1maXQuY3YkbGFtYmRhLjFzZQ0KKGNvZWZzPWNvZWYuZ2dsYXNzbyhvYmplY3Q9Zml0LmN2JGdnbGFzc28uZml0LHM9bG1iZGEpKQ0KI0F0IGJlc3QgbGFtYmRhIGdldCBjb2VmZmljaWVudHMgYW5kIGZpdHRlZCB2YWx1ZXMNCnBsdD1zYWxfeS1wcmVkaWN0LmdnbGFzc28ob2JqZWN0PWZpdC5jdiRnZ2xhc3NvLmZpdCxuZXd4PWZvcm0scz1sbWJkYSx0eXBlPSdsaW5rJykNCnBsb3QocGx0LCB5bGFiPSJyZXNpZHVhbHMiLCB4bGFiPSJpbmRleCIsIG1haW49IlBsb3Qgb2YgcmVzaWR1YWxzIikNCmFibGluZSgwLCAwLCBjb2w9ICJyZWQiKQ0KDQpgYGANCg0KRnJvbSB0aGlzIHN0dWR5IGl0IGlzIHBvc3NpYmxlIHRvIGNvbmNsdWRlIHRoYXQgZWFjaCBmYWN0b3IgY29uc2lkZXJlZCANCmlzIHJlbGV2YW50IHRvIGRldGVybWluZSB0aGUgc2FsYXJ5IGxldmVsLiANCg0KVGhlIGxlYXN0IGltcG9ydGFudCBmYWN0b3IgcmVzdWx0cyB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBzZXggYW5kIGpvYiwNCndoaWNoIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHVzaW5nIGdyb3VwLUxhc3NvLg0KVGhpcyBtZWFucyB0aGF0IHRoZXJlIGlzIG5vIGdlbmRlciBkaXNjcmltaW5hdGlvbg0KYmV0d2VlbiB2YXJpb3VzIGpvYnMsIGV2ZW4gaWYgZ2VuZGVyIGRpc2NyaW1pbmF0aW9uIGV4aXN0cyANCmluIGdlbmVyYWwgYW5kIGZvciBkaWZmZXJlbnQgYWdlIGdyb3Vwcy4NCg0KSW4gdGhlIG5leHQgc2VjdGlvbiB0aGlzIHRvcGljIHdpbGwgYmUgaW52ZXN0aWdhdGVkIGZ1cnRoZXIuDQoNCg0KIyMgRGlmZmVyZW5jZSBpbiBzYWxhcnk6IG1lbiB2cy4gZmVtYWxlcw0KDQpUaGUgZm9jdXMgb2YgdGhpcyBzZWN0aW9uIGlzIG9uIHRoZSBvbiB0aGUgZGlmZmVyZW5jZSBvbiBzYWxhcnkgDQpiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzIGluIHZhcmlvdXMgdG93bnMuDQpTb21lIGNsYXNzaWNhbCBhcHByb2FjaGVzIHdpbGwgYmUgY29tcGFyZWQgd2l0aCByb2J1c3Qgb25lcywNCnRvIGRpYWdub3NlIHRoZSBwb3NzaWJsZSBuZWdhdGl2ZSBlZmZlY3Qgb2YgdGhlIHByZXNlbmNlIG9mIG5vaXNlIG9yIG91dGxpZXJzLg0KSXQgaXMgbW90aXZhdGVkIGJ5IHRoZSBhc3N1bXB0aW9ucyB0aGF0IG5vdCBhbGwgdGhlIHRvd25zIGNvbWUgZnJvbSANCnRoZSBzYW1lIGRhdGEgZ2VuZXJldGluZyBwcm9jZXNzLg0KDQpgYGB7ciBmdW5jdGlvbnMgbGluZWFyIG1vZGVsfQ0KDQojIFRoZXNlIGZ1bmN0aW9ucyBhcmUgdXNlZCB0byBjcmVhdGUgdGhlIHBsb3RzIHVzZWQgaW4gdGhlIG5leHQgY2h1bmtzDQoNCiMgUExPVFMgRk9SIGludGVyYWN0aXZlIHJlZ3Jlc3Npb24gZGlhZ25vc3RpYyBwbG90cw0KUmVncmVzc2lvblBsb3RzIDwtIGZ1bmN0aW9uKGZpdCwgdGV4dGxhYmVscywgcm9idXN0PUYsIG91dCA9IE5VTEwpew0KICANCiAgIyBudW1iZXIgb2YgdW5pdHMNCiAgbiA9IGxlbmd0aChmaXQkZml0dGVkLnZhbHVlcykNCiAgDQogICMgb3JpZ2luYWwgcmVzcG9uc2UNCiAgeSA9IGZpdCR5DQogIA0KICAjIEV4dHJhY3QgZml0dGVkIHZhbHVlcyBmcm9tIGxtKCkgb2JqZWN0DQogIEZpdHRlZC5WYWx1ZXMgPC0gIGZpdCRmaXR0ZWQudmFsdWVzDQogIA0KICAjIEV4dHJhY3QgcmVzaWR1YWxzIGZyb20gbG0oKSBvYmplY3QNCiAgUmVzaWR1YWxzIDwtICBmaXQkcmVzaWR1YWxzDQogIA0KICBpZiAocm9idXN0ID09IEYpew0KICAgICMgRXh0cmFjdCBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIGZyb20gbG0oKSBvYmplY3QNCiAgICBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzIDwtIE1BU1M6OnN0ZHJlcyhmaXQpICANCiAgICANCiAgICAjIENhbGN1bGF0ZSBMZXZlcmFnZQ0KICAgIExldmVyYWdlIDwtIGxtLmluZmx1ZW5jZShmaXQpJGhhdA0KICAgIA0KICAgICMgdGV4dCBmb3IgdGhlIGxhYmVscyBvZiB0aGUgcGxvdHMNCiAgICB0dCA9ICJPTFMiDQogIH0gZWxzZXsNCiAgICAjIHN0YW5kYXJkaXplZCByZXNpZHVhbHMgYmFzZWQgb24gYSByb2J1c3Qgc2NhbGUgZXN0aW1hdGUNCiAgICBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzIDwtIGZpdCRyZXNpZHVhbHMvZml0JHNjYWxlDQogICAgDQogICAgIyBnZXQgdGhlIHJvYnVzdCBkaXN0YW5jZXMgYmFzZWQgb24gbWFoYWxhbm9iaXMgZGlzdGFuY2UgZnJvbSB0aGUgcm9idXN0IG91dHB1dCANCiAgICAjIChpdCBoYXMgdG8gYmUgc3BlY2lmaWVkIGluIHRoZSBjYWxsIHRvIGxtcm9iIHVzaW5nOiBjb250cm9sID0gbG1yb2IuY29udHJvbChjb21wdXRlLnJkID0gVCkpDQogICAgTGV2ZXJhZ2UgPC0gZml0JE1EDQogICAgDQogICAgIyB0ZXh0IGZvciB0aGUgbGFiZWxzIG9mIHRoZSBwbG90cw0KICAgIHR0ID0gIlJvYnVzdCINCiAgfQ0KICANCiAgIyBFeHRyYWN0IGZpdHRlZCB2YWx1ZXMgZm9yIGxtKCkgb2JqZWN0DQogIFRoZW9yZXRpY2FsLlF1YW50aWxlcyA8LSBxcW5vcm0oUmVzaWR1YWxzLCBwbG90Lml0ID0gRikkeA0KICANCiAgIyBDcmVhdGUgZGF0YSBmcmFtZSANCiAgIyBXaWxsIGJlIHVzZWQgYXMgaW5wdXQgdG8gcGxvdGx5DQogIHJlZ01hdCA8LSBkYXRhLmZyYW1lKEZpdHRlZC5WYWx1ZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICBSZXNpZHVhbHMsIA0KICAgICAgICAgICAgICAgICAgICAgICBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzLCANCiAgICAgICAgICAgICAgICAgICAgICAgVGhlb3JldGljYWwuUXVhbnRpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICBMZXZlcmFnZSwgDQogICAgICAgICAgICAgICAgICAgICAgIHRleHRsYWJlbHMpDQogIA0KICAjIFBsb3QgdXNpbmcgUGxvdGx5DQogIA0KICAjIHRleHQgaW5mbw0KICB0IDwtIGxpc3QoDQogICAgZmFtaWx5ID0gInNhbnMgc2VyaWYiLA0KICAgIHNpemUgPSAxNCwNCiAgICBjb2xvciA9IHRvUkdCKCJncmV5NTAiKSkNCiAgDQogIA0KICAjIFBsb3QgMTogRml0dGVkIHZzIFJlc2lkdWFscw0KICAjIEZvciBzY2F0dGVyIHBsb3Qgc21vb3RoZXINCiAgTE9FU1MgPC0gbG9lc3Muc21vb3RoKEZpdHRlZC5WYWx1ZXMsIFJlc2lkdWFscykNCiAgDQogICMgcGx0MSA8LSByZWdNYXQgJT4lIA0KICAjICAgcGxvdF9seSh4ID0gRml0dGVkLlZhbHVlcywgeSA9IFJlc2lkdWFscywgDQogICMgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsIGhvdmVyaW5mbyA9ICJ0ZXh0IiwNCiAgIyAgICAgICAgICAgY29sb3IgPSBvdXQsIG5hbWUgPSAiRGF0YSIsIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDEwLCBvcGFjaXR5ID0gMC41KSwNCiAgIyAgICAgICAgICAgdGV4dCA9IHBhc3RlKCc8L2JyPiBVbml0OiAnLCAxOm4sDQogICMgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4gVG93bjogJywgYXMuY2hhcmFjdGVyKHRleHRsYWJlbHMpLA0KICAjICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IHg6ICcsIEZpdHRlZC5WYWx1ZXMsDQogICMgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4geTogJywgUmVzaWR1YWxzKSkgJT4lIA0KICAjICAgDQogICMgICBhZGRfdHJhY2UoeCA9IExPRVNTJHgsIHkgPSBMT0VTUyR5LCB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJTbW9vdGgiLA0KICAjICAgICAgICAgICAgIGluaGVyaXQgPSBGLCBsaW5lID0gbGlzdCh3aWR0aCA9IDIpKSAlPiUgDQogICMgDQogICMgICBsYXlvdXQodGl0bGUgPSBwYXN0ZSh0dCwgIiBSZXNpZHVhbHMgdnMgRml0dGVkIFZhbHVlcyIpLCBwbG90X2JnY29sb3IgPSAiI2U2ZTZlNiIsDQogICMgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkZpdHRlZCBWYWx1ZXMiKSwNCiAgIyAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVzaWR1YWxzIikpDQogIA0KICAjIFBsb3QgMjogRml0dGVkIHZzIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMNCiAgIyBGb3Igc2NhdHRlciBwbG90IHNtb290aGVyDQogIExPRVNTIDwtIGxvZXNzLnNtb290aChGaXR0ZWQuVmFsdWVzLCBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzKQ0KICANCiAgcGx0MiA8LSByZWdNYXQgJT4lIA0KICAgIHBsb3RfbHkoeCA9IEZpdHRlZC5WYWx1ZXMsIHkgPSBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzLCANCiAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsIGhvdmVyaW5mbyA9ICJ0ZXh0IiwNCiAgICAgICAgICAgIGNvbG9yID0gb3V0LCBuYW1lID0gIkRhdGEiLCBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCwgb3BhY2l0eSA9IDAuNSksDQogICAgICAgICAgICB0ZXh0ID0gcGFzdGUoJzwvYnI+IFVuaXQ6ICcsIDE6biwNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4gVG93bjogJywgYXMuY2hhcmFjdGVyKHRleHRsYWJlbHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICc8L2JyPiB4OiAnLCBGaXR0ZWQuVmFsdWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICc8L2JyPiB5OiAnLCBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzKSkgJT4lIA0KICAgIA0KICAgIGFkZF90cmFjZSh4ID0gTE9FU1MkeCwgeSA9IExPRVNTJHksIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibGluZXMiLCBuYW1lID0gIlNtb290aCIsDQogICAgICAgICAgICAgIGluaGVyaXQgPSBGLCBsaW5lID0gbGlzdCh3aWR0aCA9IDIpKSAlPiUgDQogICAgDQogICAgbGF5b3V0KHRpdGxlID0gcGFzdGUodHQsICIgU3RhbmRhcmRpemVkIHJlc2lkdWFscyB2cyBGaXR0ZWQgVmFsdWVzIiksIHBsb3RfYmdjb2xvciA9ICIjZTZlNmU2IiwNCiAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkZpdHRlZCBWYWx1ZXMiKSwNCiAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlN0YW5kYXJkaXplZCByZXNpZHVhbHMiKSkgIA0KDQogICMgUGxvdCAzOiBSZXNpZHVhbHMgaW5kZXgNCiAgcGx0MyA8LSByZWdNYXQgJT4lIA0KICAgIHBsb3RfbHkoeCA9IDE6biwgeSA9IFN0YW5kYXJkaXplZC5SZXNpZHVhbHMsIA0KICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwgaG92ZXJpbmZvID0gInRleHQiLA0KICAgICAgICAgICAgY29sb3IgPSBvdXQsIG5hbWUgPSAiRGF0YSIsIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDEwLCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgdGV4dCA9IHBhc3RlKCc8L2JyPiBVbml0OiAnLCAxOm4sDQogICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IFRvd246ICcsIGFzLmNoYXJhY3Rlcih0ZXh0bGFiZWxzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4geDogJywgMTpuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICc8L2JyPiB5OiAnLCBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzKSkgJT4lIA0KICAgIA0KICAgIGFkZF9zZWdtZW50cyh4ID0gLTUsIHhlbmQgPSBuKzUsIHkgPSAwLCB5ZW5kID0gMCwgbW9kZSA9ICJsaW5lIiwgaW5oZXJpdCA9IEYsIA0KICAgICAgICAgICAgICAgICBuYW1lID0gIk51bGwgbGluZSIsIGxpbmUgPSBsaXN0KHdpZHRoID0gMikpICU+JQ0KICAgIA0KICAgIGxheW91dCh0aXRsZSA9IHBhc3RlKHR0LCAiIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMnIGluZGV4IiksIHBsb3RfYmdjb2xvciA9ICIjZTZlNmU2IiwgDQogICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkluZGV4IiksDQogICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlN0YW5kYXJkaXplZC5SZXNpZHVhbHMiKSkNCg0KICAjIFBsb3QgNDogUVEgUG90DQogIHBsdDQgPC0gcmVnTWF0ICU+JSANCiAgICBwbG90X2x5KHggPSBUaGVvcmV0aWNhbC5RdWFudGlsZXMsIHkgPSBTdGFuZGFyZGl6ZWQuUmVzaWR1YWxzLCANCiAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsIGhvdmVyaW5mbyA9ICJ0ZXh0IiwgDQogICAgICAgICAgICBjb2xvciA9IG91dCwgbmFtZSA9ICJEYXRhIiwgbWFya2VyID0gbGlzdChzaXplID0gMTAsIG9wYWNpdHkgPSAwLjUpLA0KICAgICAgICAgICB0ZXh0ID0gcGFzdGUoJzwvYnI+IFVuaXQ6ICcsIDE6biwNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4gVG93bjogJywgYXMuY2hhcmFjdGVyKHRleHRsYWJlbHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICc8L2JyPiB4OiAnLCBUaGVvcmV0aWNhbC5RdWFudGlsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IHk6ICcsIFN0YW5kYXJkaXplZC5SZXNpZHVhbHMpKSAlPiUgDQogICAgDQogICAgYWRkX3RyYWNlKHggPSBUaGVvcmV0aWNhbC5RdWFudGlsZXMsIHkgPSBUaGVvcmV0aWNhbC5RdWFudGlsZXMsIHR5cGUgPSAic2NhdHRlciIsIA0KICAgICAgICAgICAgICBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJUaGVvcmV0aWNhbCIsIGxpbmUgPSBsaXN0KHdpZHRoID0gMiksIA0KICAgICAgICAgICAgICBpbmhlcml0ID0gRikgJT4lDQogICAgDQogICAgbGF5b3V0KHRpdGxlID0gcGFzdGUodHQsICIgUS1RIFBsb3QiKSwgcGxvdF9iZ2NvbG9yID0gIiNlNmU2ZTYiLA0KICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJOb3JtYWwgdGhlb3JldGljYWwgcXVhbnRpbGVzIiksDQogICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIkRhdGEgcXVhbnRpbGVzIikpDQogIA0KICAjIFBsb3Q1OiBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UNCiAgIyBGb3Igc2NhdHRlciBwbG90IHNtb290aGVyDQogIExPRVNTIDwtIGxvZXNzLnNtb290aChMZXZlcmFnZSwgUmVzaWR1YWxzKQ0KICANCiAgcGx0NSA8LSByZWdNYXQgJT4lIA0KICAgIHBsb3RfbHkoeCA9IExldmVyYWdlLCB5ID0gUmVzaWR1YWxzLCANCiAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsIGhvdmVyaW5mbyA9ICJ0ZXh0IiwgDQogICAgICAgICAgICBjb2xvciA9IG91dCwgbmFtZSA9ICJEYXRhIiwgbWFya2VyID0gbGlzdChzaXplID0gMTAsIG9wYWNpdHkgPSAwLjUpLCANCiAgICAgICAgICAgdGV4dCA9IHBhc3RlKCc8L2JyPiBVbml0OiAnLCAxOm4sDQogICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IFRvd246ICcsIGFzLmNoYXJhY3Rlcih0ZXh0bGFiZWxzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4geDogJywgTGV2ZXJhZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IHk6ICcsIFJlc2lkdWFscykpICU+JSANCiAgICANCiAgICBhZGRfdHJhY2UoeCA9IExPRVNTJHgsIHkgPSBMT0VTUyR5LCB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJTbW9vdGgiLA0KICAgICAgICAgICAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDIpLCBpbmhlcml0ID0gRikgJT4lDQogICAgDQogICAgbGF5b3V0KHRpdGxlID0gcGFzdGUodHQsICIgTGV2ZXJhZ2UgdnMgUmVzaWR1YWxzIiksIHBsb3RfYmdjb2xvciA9ICIjZTZlNmU2IiwgDQogICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkxldmVyYWdlIHZhbHVlcyIpLA0KICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJSZXNpZHVhbHMiKSkgIA0KDQogICMgUGxvdCA2OiBGaXR0ZWQgdnMgcmVzcG9uc2UNCiAgIyBGb3Igc2NhdHRlciBwbG90IHNtb290aGVyDQogIExPRVNTIDwtIGxvZXNzLnNtb290aChGaXR0ZWQuVmFsdWVzLCB5KQ0KICANCiAgcGx0NiA8LSByZWdNYXQgJT4lIA0KICAgIHBsb3RfbHkoeCA9IEZpdHRlZC5WYWx1ZXMsIHkgPXksIA0KICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwgaG92ZXJpbmZvID0gInRleHQiLA0KICAgICAgICAgICAgY29sb3IgPSBvdXQsIG5hbWUgPSAiRGF0YSIsIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDEwLCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgdGV4dCA9IHBhc3RlKCc8L2JyPiBVbml0OiAnLCAxOm4sDQogICAgICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IFRvd246ICcsIGFzLmNoYXJhY3Rlcih0ZXh0bGFiZWxzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4geDogJywgRml0dGVkLlZhbHVlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAnPC9icj4geTogJywgeSkpICU+JSANCiAgICANCiAgICBhZGRfdHJhY2UoeCA9IExPRVNTJHgsIHkgPSBMT0VTUyR5LCB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJTbW9vdGgiLA0KICAgICAgICAgICAgICBpbmhlcml0ID0gRiwgbGluZSA9IGxpc3Qod2lkdGggPSAyKSkgJT4lIA0KICANCiAgICBsYXlvdXQodGl0bGUgPSBwYXN0ZSh0dCwgIiBGaXR0ZWQgVmFsdWVzIHZzIHJlc3BvbnNlIiksIHBsb3RfYmdjb2xvciA9ICIjZTZlNmU2IiwNCiAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiRml0dGVkIFZhbHVlcyIpLA0KICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJSZXNwb25zZSB2YWx1ZXMiKSkNCg0KICAjICMgUGxvdCA3OiBNSVNTSU5HOiBzcXJ0KGFicyhSZXNpZHVhbHMpKQ0KICAjICMgRm9yIHNjYXR0ZXIgcGxvdCBzbW9vdGhlcg0KICAjIExPRVNTMiA8LSBsb2Vzcy5zbW9vdGgoRml0dGVkLlZhbHVlcywgUm9vdC5SZXNpZHVhbHMpDQogICMgDQogICMgcGx0MyA8LSByZWdNYXQgJT4lIA0KICAjICAgcGxvdF9seSh4ID0gRml0dGVkLlZhbHVlcywgeSA9IFJvb3QuUmVzaWR1YWxzLCANCiAgIyAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwgaG92ZXJpbmZvID0gInRleHQiLCANCiAgIyAgICAgICAgICAgbmFtZSA9ICJEYXRhIiwgbWFya2VyID0gbGlzdChzaXplID0gMTAsIG9wYWNpdHkgPSAwLjUpLA0KICAjICAgICAgICAgICB0ZXh0ID0gcGFzdGUoJ3Rvd246ICcsIGFzLmNoYXJhY3Rlcih0ZXh0bGFiZWxzKSkpICU+JQ0KICAjICAgDQogICMgICBhZGRfdHJhY2UoeCA9IExPRVNTMiR4LCB5ID0gTE9FU1MyJHksIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibGluZXMiLCBuYW1lID0gIlNtb290aCIsDQogICMgICAgICAgICAgICAgbGluZSA9IGxpc3Qod2lkdGggPSAyKSwgaW5oZXJpdCA9IEYpICU+JSANCiAgIyAgIA0KICAjICAgbGF5b3V0KHRpdGxlID0gIlNjYWxlIExvY2F0aW9uIiwgcGxvdF9iZ2NvbG9yID0gIiNlNmU2ZTYiKQ0KICBwbHQgPSBsaXN0KHBsdDIsIHBsdDMsIHBsdDQsIHBsdDUsIHBsdDYpICMgcGx0MSwgDQogIHJldHVybihwbHQpDQoNCiAgIyBub3Qgd29ya2luZy4uLnRvIGFkZCBpbiBhbGwgcGxvdHMNCiAgIyB0byBhdm9pZCBlcnJvciB3aXRoIGNvbG9yUGFsZXR0ZTwzDQogICMgc3VwcHJlc3NXYXJuaW5ncyh3YXJuaW5nKCJSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwiKSkNCn0NCg0KIw0KIyBQTE9UUyBGT1IgQkVTVCBTVUJTRVQgUkVHUkVTU0lPTg0KIw0KcGxvdC5yZWdzdWJzZXRzMiA8LSAgDQogIGZ1bmN0aW9uICh4LCBsYWJlbHMgPSBvYmokeG5hbWVzLCBtYWluID0gTlVMTCwgc2NhbGUgPSBjKCJiaWMiLCAgDQogICAgICJDcCIsICJhZGpyMiIsICJyMiIpLCBjb2wgPSBncmF5KHNlcSgwLCAwLjksIGxlbmd0aCA9IDEwKSksIC4uLikgIA0KICB7IA0KICAgIG9iaiA8LSB4IA0KICAgIGxzdW0gPC0gc3VtbWFyeShvYmopIA0KICAgIHBhcihtYXIgPSBjKDcsIDUsIDYsIDMpICsgMC4xKSANCiAgICBubW9kZWxzIDwtIGxlbmd0aChsc3VtJHJzcSkgDQogICAgbnAgPC0gb2JqJG5wIA0KICAgIHByb3BzY2FsZSA8LSBGQUxTRSANCiAgICBzc2NhbGUgPC0gcG1hdGNoKHNjYWxlWzFdLCBjKCJiaWMiLCAiQ3AiLCAiYWRqcjIiLCAicjIiKSwgIA0KICAgICAgICAgICAgICAgICAgICAgbm9tYXRjaCA9IDApIA0KICAgIGlmIChzc2NhbGUgPT0gMCkgIA0KICAgICAgc3RvcChwYXN0ZSgiVW5yZWNvZ25pc2VkIHNjYWxlPSIsIHNjYWxlKSkgDQogICAgaWYgKHByb3BzY2FsZSkgIA0KICAgICAgc3RvcChwYXN0ZSgiUHJvcG9ydGlvbmFsIHNjYWxpbmcgb25seSBmb3IgcHJvYmFiaWxpdGllcyIpKSANCiAgICB5c2NhbGUgPC0gc3dpdGNoKHNzY2FsZSwgbHN1bSRiaWMsIGxzdW0kY3AsIGxzdW0kYWRqcjIsIGxzdW0kcnNxKSANCiAgICB1cCA8LSBzd2l0Y2goc3NjYWxlLCAtMSwgLTEsIDEsIDEpIA0KICAgIGluZGV4IDwtIG9yZGVyKHlzY2FsZSAqIHVwKSANCiAgICBjb2xvcnNjYWxlIDwtIHN3aXRjaChzc2NhbGUsIHlzY2FsZSwgeXNjYWxlLCAtbG9nKHBtYXgoeXNjYWxlLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDFlLTA0KSksIC1sb2cocG1heCh5c2NhbGUsIDFlLTA0KSkpIA0KICAgIGltYWdlKHogPSB0KGlmZWxzZShsc3VtJHdoaWNoW2luZGV4LCBdLCBjb2xvcnNjYWxlW2luZGV4XSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBOQSArIG1heChjb2xvcnNjYWxlKSAqIDEuNSkpLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCAgDQogICAgICAgICAgeCA9ICgxOm5wKSwgeSA9IDE6bm1vZGVscywgeGxhYiA9ICIiLCB5bGFiID0gc2NhbGVbMV0sICANCiAgICAgICAgICBjb2wgPSBjb2wpIA0KICAgIGxhc3BhciA8LSBwYXIoImxhcyIpIA0KICAgIG9uLmV4aXQocGFyKGxhcyA9IGxhc3BhcikpIA0KICAgIHBhcihsYXMgPSAyKSANCiAgICBheGlzKDEsIGF0ID0gMTpucCwgbGFiZWxzID0gbGFiZWxzLCAuLi4pICMgSSBtb2RpZmllZCB0aGlzIGxpbmUgDQogICAgYXhpcygyLCBhdCA9IDE6bm1vZGVscywgbGFiZWxzID0gc2lnbmlmKHlzY2FsZVtpbmRleF0sIDIpLCAuLi4pIA0KICAgICMgYXhpcygyLGNleC5heGlzPTAuMDEpDQogICAgaWYgKCFpcy5udWxsKG1haW4pKSAgDQogICAgICB0aXRsZShtYWluID0gbWFpbikgDQogICAgYm94KCkgDQogICAgaW52aXNpYmxlKE5VTEwpDQogIH0gDQoNCg0KYGBgDQoNCg0KVGhlIGNvbnNpZGVyZWQgbW9kZWwgaXMgYnVpbHQgdXNpbmcgYWxsIHRoZSB2YXJpYWJsZXMgdGhhdCBjb3VsZCBleHBsYWluIHRoZSBnYXAgaW4gc2FsYXJ5LiANClNvbWUgb2YgdGhlbSByZWZlcnMgdG8gdGhlIGRpZmZlcmVuY2Ugb2Ygc29tZSBxdWFudGl0eSBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzIGZvciBlYWNoIHRvd24gDQooZS5nLiwgZGlwbG9tYSwgdW5pdmVyaXN0eSwgaW5kZXBlbmRlbnQgd29ya2VycywgZXRjLikuDQpUaGUgZmluYWwgbW9kZWwgY29uc2lzdCBvZiA0MyB2YXJpYWJsZXMgYW5kIDUwMjUgdW5pdHMuDQoNCkFkZGl0aW9uYWxseSwgc29tZSB2YXJpYWJsZXMgd2hpY2ggd291bGQgaGF2ZSBoYWQgYSBodWdlIHBvc2l0aXZlIGltcGFjdCBvbiB0aGUgcHJlZGljdGlvbg0Kb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGhhdmUgbm9uIGJlZW4gY29uc2lkZXJlZCwgYmVjYXVzZSBvZiB0aGVpciBzdHJvbmcgcmVsYXRpb24NCihlLmcuLCBnYXAgaW4gc2FsYXJ5IGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMgZm9yIHZhcmlvdXMgYWdlcyBvciBvY2N1cGF0aW9ucykNCmBgYHtyIGxpbmVhciBtb2RlbCBzcGVjaWZpY2F0aW9uIGZvciBkZWx0YSBNLUYgc2FsYXJ5LCBmaWcua2VlcD0nYWxsJ30NCg0KIyByZXNwb25zZSB2YXJpYWJsZQ0KIyB5ID0gbG9nMTAobmV3RGF0JHNhbF9NYWxlcyAtIG5ld0RhdCRzYWxfRmVtYWxlcykNCnkgPSBuZXdEYXQkc2FsX01hbGVzIC0gbmV3RGF0JHNhbF9GZW1hbGVzDQoNCiMgb3JpZ2luYWwgcmVzcG9uc2UgdmFyaWFibGUgaGlzdG9ncmFtDQpnZ3Bsb3QoZGF0YT1hcy5kYXRhLmZyYW1lKHkpLCBhZXMoeSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYSA9IC4zLCBiaW5zID0gMzUpICsNCiAgZ2VvbV9kZW5zaXR5KGNvbD0iYmxhY2siKSArDQogIGxhYnMoeD0iTWFsZS1GZW1hbGUgc2FsYXJ5IiwgeT0iRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiT3JpZ2luYWwgcmVzcG9uc2UgdmFyaWFibGUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQp4ID0gY2JpbmQuZGF0YS5mcmFtZShwb3AgPSBuZXdEYXQkdG90YWxfcG9wdWxhdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICBmaXJtcyA9IG5ld0RhdCR0b3RhbCwNCiAgICAgICAgICAgICAgICAgICAgIHJhdGlvX3BvcF9maXJtcyA9IG5ld0RhdCR0b3RhbC9uZXdEYXQkdG90YWxfcG9wdWxhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICMgZGVsdGFfZXhlYyA9IG5ld0RhdCRzYWxfTV9leGVjdXRpdmUgLSBuZXdEYXQkc2FsX0ZfZXhlY3V0aXZlLA0KICAgICAgICAgICAgICAgICAgICAgIyBkZWx0YV9taWRNYW4gPSBuZXdEYXQkc2FsX01fbWlkTWFuYWdlciAtIG5ld0RhdCRzYWxfRl9taWRNYW5hZ2VyLA0KICAgICAgICAgICAgICAgICAgICAgIyBkZWx0YV9lbXBsID0gbmV3RGF0JHNhbF9NX2VtcGxveWVlIC0gbmV3RGF0JHNhbF9GX2VtcGxveWVlLA0KICAgICAgICAgICAgICAgICAgICAgIyBkZWx0YV93b3JrZXIgPSBuZXdEYXQkc2FsX01fd29ya2VyIC0gbmV3RGF0JHNhbF9GX3dvcmtlciwNCiAgICAgICAgICAgICAgICAgICAgIHdvcmtmb3JjZSA9IG5ld0RhdCR3b3JrZm9yY2UsDQogICAgICAgICAgICAgICAgICAgICBkZXBlbmRlbmN5X3JhdGlvID0gbmV3RGF0JGRlcGVuZGVuY3lfcmF0aW8sDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV93YWdlYXJuZXIgPSBuZXdEYXQkd2FnZWFybmVyX00gLSBuZXdEYXQkd2FnZWFybmVyX0YsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9pbmRlcCA9IG5ld0RhdCRpbmRlcGVuZGVudF9NIC0gbmV3RGF0JGluZGVwZW5kZW50X0YsDQogICAgICAgICAgICAgICAgICAgICAjIGRlbHRhX3RyYW5zZmVyID0gbmV3RGF0JHRyYW5zZmVyX00gLSBuZXdEYXQkdHJhbnNmZXJfRiwgIyAyMDAwIHplcm9zDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9lbXBsb3llciA9IG5ld0RhdCRlbXBsb3llcl9NIC0gbmV3RGF0JGVtcGxveWVyX0YsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9mdWxsID0gbmV3RGF0JGZ1bGxfTSAtIG5ld0RhdCRmdWxsX0YsDQogICAgICAgICAgICAgICAgICAgICAjIGRlbHRhX2hhbGYgPSBuZXdEYXQkaGFsZl9NIC0gbmV3RGF0JGhhbGZfRiwgIyBPTFMgZ2l2ZXMgTkENCiAgICAgICAgICAgICAgICAgICAgIHN1cGVyZmljaWUgPSBuZXdEYXQkU3VwZXJmaWNpZSwNCiAgICAgICAgICAgICAgICAgICAgIGhvdXNpbmcxNCA9IG5ld0RhdCRob3VzaW5nMTQsDQogICAgICAgICAgICAgICAgICAgICBtZWRpYW5fbGl2aW5nMTQgPSBuZXdEYXQkbWVkaWFuX2xpdmluZzE0LA0KICAgICAgICAgICAgICAgICAgICAgZW1wbCA9IG5ld0RhdCRlbXBsLA0KICAgICAgICAgICAgICAgICAgICAgZW1wX3NhbCA9IG5ld0RhdCRlbXBfc2FsLA0KICAgICAgICAgICAgICAgICAgICAgcG9wMTVfNjQgPSBuZXdEYXQkcG9wMTVfNjQsDQogICAgICAgICAgICAgICAgICAgICB1bmVtcDE1XzY0ID0gbmV3RGF0JHVuZW1wMTVfNjQsDQogICAgICAgICAgICAgICAgICAgICBhY3QxNV82NCA9IG5ld0RhdCRhY3QxNV82NCwNCiAgICAgICAgICAgICAgICAgICAgIHVuZW1wX3JhdGUgPSBuZXdEYXQkdW5lbXBfcmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgIGRlbHRhX05vRGlwID0gbmV3RGF0JG1hbGVfTm9EaXAgLSBuZXdEYXQkZmVtYWxlX05vRGlwLA0KICAgICAgICAgICAgICAgICAgICAgZGVsdGFfc2VjID0gbmV3RGF0JG1hbGVfU2VjIC0gbmV3RGF0JGZlbWFsZV9TZWMsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9oaSA9IG5ld0RhdCRtYWxlX0hpIC0gbmV3RGF0JGZlbWFsZV9IaSwNCiAgICAgICAgICAgICAgICAgICAgIGRlbHRhX3VuaXYgPSBuZXdEYXQkbWFsZV9Vbml2IC0gbmV3RGF0JGZlbWFsZV9Vbml2LA0KICAgICAgICAgICAgICAgICAgICAgbm9kaXAgPSBuZXdEYXQkbm9kaXAsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9pbW1pZyA9IG5ld0RhdCRtYWxlX2ltbWlnIC0gbmV3RGF0JGZlbWFsZV9pbW1pZywNCiAgICAgICAgICAgICAgICAgICAgIGRlbHRhX3dvcmtpbmdJbW1pZyA9IG5ld0RhdCRtYWxlX3dvcmtpbmdfaW1taWcgLSBuZXdEYXQkZmVtYWxlX3dvcmtpbmdfaW1taWcsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9uYXRpdmUgPSBuZXdEYXQkbWFsZV9uYXRpdmUgLSBuZXdEYXQkZmVtYWxlX25hdGl2ZSwNCiAgICAgICAgICAgICAgICAgICAgICMgZXZvbF9lbnRyZXByID0gbmV3RGF0JGV2b2xfZW50cmVwcmVuZXVyaWFsX3Njb3JlLA0KICAgICAgICAgICAgICAgICAgICAgIyBldm9sX3BvcCA9IG5ld0RhdCRldm9sX3BvcF9zY29yZSwNCiAgICAgICAgICAgICAgICAgICAgIHJlZ2lvbmFsX0dEUCA9IG5ld0RhdCRyZWdpb25hbF9HRFAsDQogICAgICAgICAgICAgICAgICAgICAjIGR5bWFuaWNfZGVtb19wcm9mID0gbmV3RGF0JGR5bWFuaWNfZGVtb19wcm9mLA0KICAgICAgICAgICAgICAgICAgICAgbmJfYWN0aXZlX2VtcGxveWVlcyA9IG5ld0RhdCRuYl9hY3RpdmVfZW1wbG95ZWVzLA0KICAgICAgICAgICAgICAgICAgICAgbmJfYWN0aXZlX25vbl9lbXBsb3llZXMgPSBuZXdEYXQkbmJfYWN0aXZlX25vbl9lbXBsb3llZXMsDQogICAgICAgICAgICAgICAgICAgICAjIG5iX2FjdGl2ZSA9IG5ld0RhdCRuYl9hY3RpdmUsICAgIyBPTFMgZ2l2ZXMgTkENCiAgICAgICAgICAgICAgICAgICAgIG5iX2NyZWF0ZWRfc2VydmljZXMgPSBuZXdEYXQkbmJfY3JlYXRlZF9zZXJ2aWNlcywNCiAgICAgICAgICAgICAgICAgICAgIG5iX2NyZWF0ZWRfY29tbWVyY2UgPSBuZXdEYXQkbmJfY3JlYXRlZF9jb21tZXJjZSwNCiAgICAgICAgICAgICAgICAgICAgIG5iX2NyZWF0ZWRfY29uc3RydWN0aW9uID0gbmV3RGF0JG5iX2NyZWF0ZWRfY29uc3RydWN0aW9uLA0KICAgICAgICAgICAgICAgICAgICAgbmJfY3JlYXRlZF9pbmQgPSBuZXdEYXQkbmJfY3JlYXRlZF9pbmQsDQogICAgICAgICAgICAgICAgICAgICAjIG5iX2NyZWF0ZWRfZmlybXMgPSBuZXdEYXQkbmJfY3JlYXRlZF9maXJtcywgIyBPTFMgZ2l2ZXMgIA0KICAgICAgICAgICAgICAgICAgICAgbmJfZmlybXNfc2VydmljZSA9IG5ld0RhdCRuYl9maXJtc19zZXJ2aWNlLA0KICAgICAgICAgICAgICAgICAgICAgbmJfZmlybXNfY29tbWVyY2UgPSBuZXdEYXQkbmJfZmlybXNfY29tbWVyY2UsDQogICAgICAgICAgICAgICAgICAgICBuYl9maXJtc19jb25zdHJ1Y3Rpb24gPSBuZXdEYXQkbmJfZmlybXNfY29uc3RydWN0aW9uLA0KICAgICAgICAgICAgICAgICAgICAgbmJfZmlybXNfaW5kID0gbmV3RGF0JG5iX2Zpcm1zX2luZCwNCiAgICAgICAgICAgICAgICAgICAgIG5iX21ham9yID0gbmV3RGF0JG5iX21ham9yLA0KICAgICAgICAgICAgICAgICAgICAgbmJfbWlub3IgPSBuZXdEYXQkbmJfbWlub3IsDQogICAgICAgICAgICAgICAgICAgICBkZWx0YV9udW1fc2V4ID0gbmV3RGF0JG5iX21hbGUgLSBuZXdEYXQkbmJfZmVtYWxlLA0KICAgICAgICAgICAgICAgICAgICAgbmJfaG91c2Vob2xkID0gbmV3RGF0JG5iX2hvdXNlaG9sZCkNCiAgICAgICAgICAgICAgICAgICAgICMgdXJiYW5UUlVFID0gbmV3RGF0JHVyYmFuRHVtbXkpDQoNCiMgYWRkIGR1bW15IGZvciB1cmJhbiAhISENCg0KIyBtZXJnZSB2YXJpYWJsZXMNCm1vZCA9IGNiaW5kLmRhdGEuZnJhbWUoeSwgeCkNCiMgZGltKG1vZCkNCg0KIyBTcGVjaWZ5IHRoZSBtb2RlbCB3aXRoIGFkZGl0aW9uYWwgcG9seW5vbWlhbHMgYXMgcHJlZGljdG9ycw0KIyAjIG1lcmdlIHZhcmlhYmxlcw0KIyBtb2R5eCA9IGNiaW5kLmRhdGEuZnJhbWUoeSwgeCkNCiMgIyBtZXJnZSB2YXJpYWJsZXMgZXhjbHVkaW5nIG91bGllcnMNCiMgIyBtb2QgPSBtb2RbIW91dGwsXQ0KIyANCiMgIyBhZGQgdHJhbnNmb3JtYXRpb25zIG9uIHRoZSBwcmVkaWN0b3JzDQojICAgbW9kID0gY2JpbmQuZGF0YS5mcmFtZShtb2QsIG1vZFssMjpuY29sKG1vZCldXjIsIG1vZFssMjpuY29sKG1vZCldXjMpDQojICAgbmFtZXNkYXRhQlMgPSBuYW1lcyhtb2QpDQojICAgbGxsID0gKChkaW0obW9kKVsyXS0xKS8zKQ0KIyAgICMgZm9yIChpIGluIDE6bGxsKzEpew0KIyAgICMgICBzdHIgPSBuYW1lc2RhdGFCU1tpXQ0KIyAgICMgICBuYW1lc2RhdGFCU1tpXSA9IHJlZ21hdGNoZXMoc3RyLCByZWdleHByKCJfIiwgc3RyKSwgaW52ZXJ0ID0gVFJVRSlbWzFdXVstMV0NCiMgICAjIH0NCiMgICBuYW1lc2RhdGFCU1sobGxsKzIpOihsbGwqMisxKV0gPSBwYXN0ZShuYW1lc2RhdGFCU1sxOmxsbCsxXSwgcmVwKCJeMiIsIGxsbCksIHNlcD0iIikNCiMgICBuYW1lc2RhdGFCU1sobGxsKjIrMik6KGxsbCozKzEpXSA9IHBhc3RlKG5hbWVzZGF0YUJTWzE6bGxsKzFdLCByZXAoIl4zIiwgbGxsKSwgc2VwPSIiKQ0KIyAgICMgbmFtZXNkYXRhQlNbKGxsbCozKzIpOihsbGwqNCsxKV0gPSBwYXN0ZShuYW1lc2RhdGFCU1sxOmxsbCsxXSwgcmVwKCJsb2ciLCBsbGwpKQ0KIyAgIG5hbWVzKG1vZCkgPSBuYW1lc2RhdGFCUw0KDQojIHNjYXR0ZXIgcGxvdCBtYXRyaXgNCiMgcGFpcnMobW9kKQ0KIyBjb3JyZWxhdGlvbnMNCmNvcnJwbG90KGNvcihtb2QpLCBtZXRob2QgPSAiY29sb3IiLCAjdGl0bGUgPSAiQ29ycmVsYXRpb24gbWF0cml4IGZvciBYIHZhcmlhYmxlcyIsDQogICAgICAgICBkaWFnID0gVCwgdGwuY2V4PTAuNSwgdHlwZT0ibG93ZXIiLCAjY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCJncmVlbiIsIm5hdnlibHVlIikpKDEwMCkpDQogICAgICAgICB0bC5jb2wgPSAiYmxhY2siKSAgIyAsIG1hcj1jKDAsMCwxLjUsMCkNCg0KYGBgDQoNClRoZXNlIHBsb3RzIHNob3dzIHRoYXQ6IA0KDQoqIHRoZSByZXNwb25zZSBpcyBsb25nIHRhaWxlZCwgd2hpY2ggYWxzbyBtb3RpdmF0ZXMgdGhlIHJvYnVzdCBmcmFtZXdvcmsNCiogdGhlcmUgaXMgc3Ryb25nIGV2aWRlbmNlIG9mIGNvbGxpbmVhcml0eQ0KDQpUaGUgZmluYWwgbW9kZWwgaXMgYnVpbHQgY29uc2lkZXJpbmcgYSByYW5kb20gc2FtcGxlIG9mIG9ic2VydmF0aW9ucyAoNzAlIG9mIHRoZW0pDQppbiBvcmRlciB0byByZWR1Y2Ugc3BhdGlhbCBjb3JyZWxhdGlvbiBhbW9uZyB1bml0cw0KYGBge3Igc3ViLXNhbXBsaW5nfQ0KDQpzZXQuc2VlZCgxKQ0Kc3VicyA9IHNhbXBsZSgxOmxlbmd0aCh5KSwgc2l6ZSA9IHJvdW5kKGxlbmd0aCh5KSowLjcpKQ0KeSA9IHlbc3Vic10NCnggPSB4W3N1YnMsXQ0KbW9kID0gbW9kW3N1YnMsXQ0KdG93bnMgPSBuZXdEYXQkdG93bltzdWJzXQ0KcHJpbnQoIlRoZSBmaW5hbCBkYXRhc2V0IGhhcyBhIGRpbWVuc2lvbiBvZjogIikNCmRpbShtb2QpDQoNCiMgZHVtbXkgdmFyaWFibGUgZm9yIFBhcmlzIGFuZCBDaGFtYm91cmN5LCB1c2VkIGxhdGVyIGZvciBwbG90cyAoZm91bmQgZXgtcG9zdCkNCmRjaXR5ID0gcmVwKDEsIGxlbmd0aCh5KSkNCmRjaXR5W3Rvd25zPT0iUGFyaXMiXSA9IDINCmRjaXR5W3Rvd25zPT0iQ2hhbWJvdXJjeSJdID0gMw0KZGNpdHkgPSBhcy5mYWN0b3IoZGNpdHkpDQoNCiMgIyByZWR1Y2VkIHJlc3BvbnNlIHZhcmlhYmxlIGhpc3RvZ3JhbQ0KIyBnZ3Bsb3QoZGF0YT1hcy5kYXRhLmZyYW1lKHkpLCBhZXMoeSkpICsNCiMgICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9Li5kZW5zaXR5Li4pLCBjb2w9ImJsYWNrIiwgZmlsbD0iYmx1ZSIsIGFscGhhID0gLjMsIGJpbnMgPSAzNSkgKw0KIyAgIGdlb21fZGVuc2l0eShjb2w9ImJsYWNrIikgKw0KIyAgIGxhYnMoeD0iTWFsZS1GZW1hbGUgc2FsYXJ5IiwgeT0iRGVuc2l0eSIpICsNCiMgICBnZ3RpdGxlKCJPcmlnaW5hbCByZXNwb25zZSB2YXJpYWJsZSIpICsNCiMgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCiMgDQojICMgY29ycmVsYXRpb25zDQojIGNvcnJwbG90KGNvcihtb2QpLCBtZXRob2QgPSAiY29sb3IiLCAjIHRpdGxlID0gIkNvcnJlbGF0aW9uIG1hdHJpeCBmb3IgWCB2YXJpYWJsZXMiLA0KIyAgICAgICAgICBkaWFnID0gVCwgdGwuY2V4PTAuNSwgdHlwZT0ibG93ZXIiLCAjY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCJncmVlbiIsIm5hdnlibHVlIikpKDEwMCkpDQojICAgICAgICAgIHRsLmNvbCA9ICJibGFjayIpICAjICwgbWFyPWMoMCwwLDEuNSwwKQ0KDQoNCg0KYGBgDQoNCkluIHRoZSBmb2xsb3dpbmcgYW5hbHlzZXMgdG8gdG93bnMgd2lsbCBiZSBvZnRlbiBoaWdobGlnaHRlZCBhbmQgDQp0aGV5IGhhdmUgYmVlbiBmb3VuZCBleC1wb3N0Lg0KVGhleSBhcmUgY29uc2lkZXJlZCBhcyAyIHR5cGljYWwgb3V0bGllcnMgYnV0IGZvciBkaWZmZXJlbnQgcmVhc29ucy4NCk9uZSBvZiB0aGVtIGlzIFBhcmlzLCB3aGljaCBpcyBhbiBvdXRsaWVyIGZvciBvYnZpb3VzIHJlYXNvbnMuIA0KVGhlIG90aGVyICh1bmV4cGVjdGVkKSBvbmUgaXMgW0NoYW1ib3VyY3ldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NoYW1ib3VyY3kpLCANCndoaWNoIGhhcyBiZWVuIHNwb3R0ZWQgYnkgdGhlIGZvbGxvd2luZyBhbmFseXNpcyBhbmQgaXQgaXMgYSB2ZXJ5IHNtYWxsIHRvd24uDQoNCkFsbCB0aGUgY29lZmZpY2llbnRzJyBlc3RpbWF0ZXMgb2J0YWluZWQgaW4gdGhlIGZvbGxvd2luZyB3aWxsIGJlIHNhdmVkDQppbiBhIGRhdGFmcmFtZSB0byBhbGxvdyBhbiBlYXN5IGV4LXBvc3QgY29tcGFyaXNvbi4NCkZvciBmaXR0aW5nIG1ldGhvZHMgdGhhdCBwcm92aWRlIHNpZ25pZmljYW5jZSBsZXZlbHMsIG9ubHkgdmFyaWFibGVzDQpzaWduaWZpY2FudCBhdCAwLjAxJSB3aWxsIGJlIGtlcHQuDQoNCiMjIyBOYWl2ZSBhcHByb2FjaGVzIA0KDQojIyMjIE9yZGluYXJ5IGxlYXN0IHNxdWFyZXMNCg0KQSBjbGFzc2ljYWwgbGluZWFyIG1vZGVsIGJhc2VkIG9uIE9MUyBpcyBmaXJzdCBmaXR0ZWQNCmBgYHtyIE9MU30NCg0KIyBPTFMNCm9scyA9IGxtKHkgfiAuLCBkYXRhID0gbW9kLCB5PVQpDQpzdW1tYXJ5KG9scykNCg0KIyBWSUYNCiMgdmlmKG9scykNCg0KIyBkaWFnbm9zdGljIHBsb3RzDQojIGxheW91dChtYXRyaXgoMTo2LCBuciA9IDIpLCBoZWlnaHQgPSBjKDIsIDEpKQ0KUmVncmVzc2lvblBsb3RzKG9scywgdG93bnMsIHJvYnVzdCA9IEYsIG91dD1kY2l0eSkNCg0KIyBleHRyYWN0IHZhcmlhYmxlcyBzaWduaWZpY2FudCBhdCAwLjAxJSBhbmQgc2F2ZSB0aGVtIGluIGEgZGF0YSBmcmFtZQ0KY29lZmYuZGYgPSBkYXRhLmZyYW1lKGFzLm1hdHJpeChzdW1tYXJ5KG9scylbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDEpLCANCiAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBjKCIoSW50ZXJjZXB0KSIsIG5hbWVzKHgpKSkNCm5hbWVzKGNvZWZmLmRmKSA9IGMoIk9MUyIpDQpjb2VmZi5kZltjb2VmZi5kZj09VFJVRV0gPSBzcHJpbnRmKCIlLjJlIiwNCiAgICAgICAgICAgYXMubWF0cml4KG9scyRjb2VmZmljaWVudHNbc3VtbWFyeShvbHMpW1siY29lZmZpY2llbnRzIl1dWywiUHIoPnx0fCkiXTwwLjAxXSksIDQpDQpjb2VmZi5kZltjb2VmZi5kZj09RkFMU0VdID0gIi0tIg0KDQpgYGANCg0KSXQgc2hvd3MgdGhhdDoNCg0KKiB0aGVyZSBpcyBldG9yZXNrYWRhc3RpY2l0eQ0KKiBQYXJpcyBoYXMgYSBsZXZhcmFnZSBlcXVhbCB0byAxDQoNCkFsc28sIGhpZ2ggY29sbGluZWFyaXR5IGFuZCByb2J1c3RuZXNzIGFyZSBub3QgdGFrZW4gaW50byBhY2NvdW50Lg0KQXQgdGhlIHNhbWUgdGltZSwgcnVubmluZyBkaWZmZXJlbnQgdGltZXMgT0xTIGRpZG4ndCBwcm9kdWNlIGFueSBjb2VmZmljaWVudHMnIGluc3RhYmlsaXR5LA0KZXZlbiBpZiBWSUYgdmFsdWVzIGFyZSB2ZXJ5IGhpZ2guDQoNCiMjIyMgQmVzdCBzdWJzZXQgc2VsZWN0aW9uDQoNCk1vZGVsIHNlbGVjdGlvbiBieSBleGhhdXN0aXZlIHNlYXJjaCAoc3VmZmVyaW5nIHRoZSBzYW1lIGRyYXdiYWNrcyBhcyBPTFMpIGlzIG5vdyBhcHBsaWVkLA0Kd2hpY2ggaXMgYWxzbyBhIHZlcnkgaGVhdnkgYXBwcm9hY2ggaW4gdGVybXMgb2YgY29tcHV0aW5nIHRpbWUuDQpgYGB7ciBCU1MgZXhoYXVzdGl2ZX0NCg0Kc2V0LnNlZWQoMSkNCg0KIyBiZXN0IHN1YnNldCBzZWxlY3Rpb24NCmJlc3Quc3ViID0gcmVnc3Vic2V0cyh5IH4gLiwgZGF0YSA9IG1vZCwgbnZtYXggPSBuY29sKG1vZCksIHJlYWxseS5iaWc9VFJVRSwgbWV0aG9kPSJleGhhdXN0aXZlIikgIyBleGhhdXN0aXZlICMgc2VxcmVwDQpiZXN0LnN1Yi5zdW1tYXJ5ID0gc3VtbWFyeShiZXN0LnN1YikNCiMgbWFudWFsIHBsb3R0aW5nDQpwYXIobWZyb3cgPWMoMiwyKSkNCiMgcnNxDQpwbG90KGJlc3Quc3ViLnN1bW1hcnkkcnNxICwgeGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9IlJzcSIsIHR5cGU9ImwiKQ0KaW5kX1JzcSA9IHdoaWNoLm1heChiZXN0LnN1Yi5zdW1tYXJ5JHJzcSkNCnBvaW50cyhpbmRfUnNxLCBiZXN0LnN1Yi5zdW1tYXJ5JGFkanIyW2luZF9Sc3FdLCBjb2wgPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KIyBhZGpSc3ENCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRhZGpyMiAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9IkFkanVzdGVkIFJTcSIsIHR5cGU9ImwiKQ0KaW5kX2FkalJzcSA9IHdoaWNoLm1heChiZXN0LnN1Yi5zdW1tYXJ5JGFkanIyKQ0KcG9pbnRzKGluZF9hZGpSc3EsIGJlc3Quc3ViLnN1bW1hcnkkYWRqcjJbaW5kX2FkalJzcV0sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQojIENwDQpwbG90KGJlc3Quc3ViLnN1bW1hcnkkY3AgLHhsYWI9Ik51bWJlciBvZiBWYXJpYWJsZXMiLCB5bGFiPSJDcCIsIHR5cGU9ImwiKQ0KaW5kX0NwID0gd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkY3ApDQpwb2ludHMoaW5kX0NwLCBiZXN0LnN1Yi5zdW1tYXJ5JGNwW2luZF9hZGpSc3FdLCBjb2wgPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KIyBiaWMNCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRiaWMgLHhsYWI9Ik51bWJlciBvZiBWYXJpYWJsZXMiLCB5bGFiPSJiaWMiLCB0eXBlPSJsIikNCmluZF9iaWMgPSB3aGljaC5taW4oYmVzdC5zdWIuc3VtbWFyeSRiaWMpDQpwb2ludHMoaW5kX2JpYywgYmVzdC5zdWIuc3VtbWFyeSRiaWNbaW5kX2JpY10sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQptdGV4dCgiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIGZvciBkZWx0YSBnZW5kZXIgc2FsYXJ5Iiwgb3V0ZXI9VFJVRSwgIGNleD0xLjIsIGxpbmU9LTEuNSkNCg0KIyBidWlsdC1pbiBwbG90cw0KcGFyKG1mcm93PWMoMSwxKSkNCnBsb3QucmVnc3Vic2V0czIoYmVzdC5zdWIsIHNjYWxlID0gInIyIiwgY2V4LmF4aXMgPSAwLjYsIG1haW4gPSAiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIHVzaW5nIFIgc3F1YXJlZCIpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJhZGpyMiIsIGNleC5heGlzID0gMC42LCBtYWluID0gIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiB1c2luZyBhZGp1c3RlZCBSIHNxdWFyZWQiKQ0KcGxvdC5yZWdzdWJzZXRzMihiZXN0LnN1Yiwgc2NhbGUgPSAiQ3AiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgTWFsbG93cycgQ3AiKQ0KcGxvdC5yZWdzdWJzZXRzMihiZXN0LnN1Yiwgc2NhbGUgPSAiYmljIiwgY2V4LmF4aXMgPSAwLjYsIG1haW4gPSAiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIHVzaW5nIEJJQyIpDQoNCiMgbXRleHQoIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiBmb3Igc2FsYXJ5IDE4LTI1IHVzaW5nIEJJQyIsIG91dGVyPVRSVUUsICBjZXg9MS40LCBsaW5lPS0zLjUpDQoNCiMgcmV0cmlldmUgdGhlIG1vZGVsIHdpdGggbWluIEJJQw0KY29lZmZpY2llbnRzKGJlc3Quc3ViLCB3aGljaC5taW4oYmVzdC5zdWIuc3VtbWFyeSRiaWMpKQ0Kbm5uID0gbmFtZXMoY29lZmZpY2llbnRzKGJlc3Quc3ViLCB3aGljaC5taW4oYmVzdC5zdWIuc3VtbWFyeSRiaWMpKSkNCm5ublsobm5uID09ICJ1cmJhblRSVUVUUlVFIildID0gInVyYmFuVFJVRSINCiAgIyBubm4gPC0gZ3N1Yih4ID0gbm5uLA0KICAjICAgICAgICAgICAgIHBhdHRlcm4gPSAiYCIsDQogICMgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiIikNCg0KIyBhcHBseSBPTFMgdG8gb2J0YWluIGRpYWdub3N0aWMgcGxvdHMgKGFwcHJveGltYXRlbHkgdGhlIHNhbWUgY29lZmZpY2llbnRzKQ0KbW9kX2JpYyA9IG1vZFssIGNvbG5hbWVzKG1vZCkgJWluJSBubm5dIA0KbW9kX2JpYyA9IGNiaW5kLmRhdGEuZnJhbWUoeSwgbW9kX2JpYykNCm9sc19iaWMgPSBsbSh5IH4gLiwgZGF0YSA9IG1vZF9iaWMsIHk9VCkNCnN1bW1hcnkob2xzX2JpYykNClJlZ3Jlc3Npb25QbG90cyhvbHNfYmljLCB0ZXh0bGFiZWxzID0gdG93bnMsIG91dCA9IGRjaXR5KQ0KDQojIGV4dHJhY3QgdmFyaWFibGVzIHNpZ25pZmljYW50IGF0IDAuMDElIGFuZCBzYXZlIHRoZW0gaW4gYSBkYXRhIGZyYW1lDQppQlNTID0gcm93Lm5hbWVzKGNvZWZmLmRmKSAlaW4lIG5ubg0KY29lZmYuZGYkQlNTID0gYXMuY2hhcmFjdGVyKHJlcCgiLS0iLCBucm93KGNvZWZmLmRmKSkpDQpjb2VmZi5kZiRCU1NbaUJTU10gPSBzcHJpbnRmKCIlLjJlIiwgYXMubWF0cml4KGNvZWZmaWNpZW50cyhiZXN0LnN1Yiwgd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkYmljKSkpKQ0KDQpgYGANCg0KDQojIyMjIFN0ZXB3aXNlIHNlbGVjdGlvbg0KDQpNb2RlbCBzZWxlY3Rpb24gYnkgc2VxdWVudGlhbCByZXBsYWNlbWVudCAoc3VmZmVyaW5nIHRoZSBzYW1lIGRyYXdiYWNrcyBhcyBPTFMpIGlzIG5vdyBhcHBsaWVkDQphbmQgaXQgaXMgdmVyeSBmYXN0IGNvbXBhcmVkIHRvIHRoZSBleGhhdXN0aXZlIHNlYXJjaA0KYGBge3IgQlNTIHNlcXJlcH0NCg0Kc2V0LnNlZWQoMSkNCg0KIyBiZXN0IHN1YnNldCBzZWxlY3Rpb24NCmJlc3Quc3ViID0gcmVnc3Vic2V0cyh5IH4gLiwgZGF0YSA9IG1vZCwgbnZtYXggPSBuY29sKG1vZCksIHJlYWxseS5iaWc9VFJVRSwgbWV0aG9kPSJzZXFyZXAiKSAjIGV4aGF1c3RpdmUgIyBzZXFyZXANCmJlc3Quc3ViLnN1bW1hcnkgPSBzdW1tYXJ5KGJlc3Quc3ViKQ0KIyBtYW51YWwgcGxvdHRpbmcNCnBhcihtZnJvdyA9YygyLDIpKQ0KIyByc3ENCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRyc3EgLCB4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iUnNxIiwgdHlwZT0ibCIpDQppbmRfUnNxID0gd2hpY2gubWF4KGJlc3Quc3ViLnN1bW1hcnkkcnNxKQ0KcG9pbnRzKGluZF9Sc3EsIGJlc3Quc3ViLnN1bW1hcnkkYWRqcjJbaW5kX1JzcV0sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQojIGFkalJzcQ0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JGFkanIyICx4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iQWRqdXN0ZWQgUlNxIiwgdHlwZT0ibCIpDQppbmRfYWRqUnNxID0gd2hpY2gubWF4KGJlc3Quc3ViLnN1bW1hcnkkYWRqcjIpDQpwb2ludHMoaW5kX2FkalJzcSwgYmVzdC5zdWIuc3VtbWFyeSRhZGpyMltpbmRfYWRqUnNxXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCiMgQ3ANCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRjcCAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9IkNwIiwgdHlwZT0ibCIpDQppbmRfQ3AgPSB3aGljaC5taW4oYmVzdC5zdWIuc3VtbWFyeSRjcCkNCnBvaW50cyhpbmRfQ3AsIGJlc3Quc3ViLnN1bW1hcnkkY3BbaW5kX2FkalJzcV0sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQojIGJpYw0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JGJpYyAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9ImJpYyIsIHR5cGU9ImwiKQ0KaW5kX2JpYyA9IHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykNCnBvaW50cyhpbmRfYmljLCBiZXN0LnN1Yi5zdW1tYXJ5JGJpY1tpbmRfYmljXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCm10ZXh0KCJCZXN0IHN1YnNldCBzZWxlY3Rpb24gZm9yIGRlbHRhIGdlbmRlciBzYWxhcnkiLCBvdXRlcj1UUlVFLCAgY2V4PTEuMiwgbGluZT0tMS41KQ0KDQojIGJ1aWx0LWluIHBsb3RzDQpwYXIobWZyb3c9YygxLDEpKQ0KcGxvdC5yZWdzdWJzZXRzMihiZXN0LnN1Yiwgc2NhbGUgPSAicjIiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgUiBzcXVhcmVkIikNCnBsb3QucmVnc3Vic2V0czIoYmVzdC5zdWIsIHNjYWxlID0gImFkanIyIiwgY2V4LmF4aXMgPSAwLjYsIG1haW4gPSAiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIHVzaW5nIGFkanVzdGVkIFIgc3F1YXJlZCIpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJDcCIsIGNleC5heGlzID0gMC42LCBtYWluID0gIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiB1c2luZyBNYWxsb3dzJyBDcCIpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJiaWMiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgQklDIikNCg0KIyBtdGV4dCgiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIGZvciBzYWxhcnkgMTgtMjUgdXNpbmcgQklDIiwgb3V0ZXI9VFJVRSwgIGNleD0xLjQsIGxpbmU9LTMuNSkNCg0KIyByZXRyaWV2ZSB0aGUgbW9kZWwgd2l0aCBtaW4gQklDDQpjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpDQpubm4gPSBuYW1lcyhjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpKQ0Kbm5uWyhubm4gPT0gInVyYmFuVFJVRVRSVUUiKV0gPSAidXJiYW5UUlVFIg0KICAjIG5ubiA8LSBnc3ViKHggPSBubm4sDQogICMgICAgICAgICAgICAgcGF0dGVybiA9ICJgIiwNCiAgIyAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIiKQ0KDQojIGFwcGx5IE9MUyB0byBvYnRhaW4gZGlhZ25vc3RpYyBwbG90cyAoYXBwcm94aW1hdGVseSB0aGUgc2FtZSBjb2VmZmljaWVudHMpDQptb2RfYmljID0gbW9kWywgY29sbmFtZXMobW9kKSAlaW4lIG5ubl0gDQptb2RfYmljID0gY2JpbmQuZGF0YS5mcmFtZSh5LCBtb2RfYmljKQ0Kb2xzX2JpYyA9IGxtKHkgfiAuLCBkYXRhID0gbW9kX2JpYywgeT1UKQ0Kc3VtbWFyeShvbHNfYmljKQ0KUmVncmVzc2lvblBsb3RzKG9sc19iaWMsIHRleHRsYWJlbHMgPSB0b3ducywgb3V0ID0gZGNpdHkpDQoNCiMgZXh0cmFjdCB2YXJpYWJsZXMgc2lnbmlmaWNhbnQgYXQgMC4wMSUgYW5kIHNhdmUgdGhlbSBpbiBhIGRhdGEgZnJhbWUNCmlCU1MgPSByb3cubmFtZXMoY29lZmYuZGYpICVpbiUgbm5uDQpjb2VmZi5kZiQiQlNTLVNSIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KY29lZmYuZGYkIkJTUy1TUiJbaUJTU10gPSBzcHJpbnRmKCIlLjJlIiwgYXMubWF0cml4KGNvZWZmaWNpZW50cyhiZXN0LnN1Yiwgd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkYmljKSkpKQ0KDQpgYGANCg0KVGhpcyBhcHByb2FjaCByZXN1bHRzIGluIGEgc3BhcnNlciBtb2RlbCBjb21wYXJlZCB0byB0aGUgZXhoYXVzdGl2ZSBzZWFyY2gsDQphbmQgdGhlIGNvc3QgKGUuZy4sIGluIHRlcm1zIG9mIEJJQykgaXMgaGlnaGVyLg0KDQoNCiMjIyMgTU0gZXN0aW1hdG9yDQoNClRoZSBtb2RlbCBpcyBub3cgZml0dGVkIHVzaW5nIHRoZSBNTSBlc3RpbWF0b3IsDQp3aGljaCBpcyBhIHNvZnQgdHJpbW1pbmcgbWV0aG9kb2xvZ3kuDQpJdCBpcyBiYXNlZCBvbiBhIHN0YXJ0aW5nIHJvYnVzdCBlc3RpbWF0b3Igd2l0aCBhIGhpZ2gNCmJyZWFrZG93biBwb2ludCAoaW4gdGhpcyBjYXNlIGFuIFMgZXN0aW1hdG9yKSwNCndoaWNoIHByb3ZpZGVzIGEgcm9idXN0IHNjYWxlIGVzdGltYXRlIHVzZWQgYnkNCnRoZSBNTSBlc3RpbWF0b3IgdG8gcmVjb3ZlciBlZmZpY2llbmN5DQpgYGB7ciBNTX0NCg0Kc2V0LnNlZWQoMTEpICMgdG8gZW5zdXJlIGNvbnZlcmdlbmNlDQoNCiMgcm9idXN0IGZpdCB1c2luZyBNTSBlc3RpbWF0b3IgDQptbSA9IGxtcm9iKHl+LiwgZGF0YSA9IG1vZCwgeT1ULCBjb250cm9sID0gbG1yb2IuY29udHJvbChjb21wdXRlLnJkID0gVCkpICMgc2V0dGluZyA9ICJLUzIwMTQiLA0Kc3VtbWFyeShtbSkNCg0KIyBvdXRsaWVycyBpZGVudGlmaWVkIGFuZCBpbXBvcnRhbnQgY2l0aWVzDQpvdXRsTU0gPSBhcy5jaGFyYWN0ZXIoZGNpdHkpDQpvdXRsTU1bbW0kcndlaWdodHMgPCAyLjhlLTA1XSA9ICIwIg0Kb3V0bE1NW2RjaXR5IT0xXSA9IGFzLmNoYXJhY3RlcihkY2l0eVtkY2l0eSE9MV0pDQpvdXRsTU0gPSBhcy5mYWN0b3Iob3V0bE1NKQ0KDQojIGRpYWdub3N0aWMgcGxvdHMNCiAgIyBsYXlvdXQobWF0cml4KDE6NiwgbnIgPSAyKSwgaGVpZ2h0ID0gYygyLCAxKSkNCiAgIyBwbG90KG1tLCBjZXggPSAwLjYsIGlkLm4gPSAxMCwgbGFiZWxzLmlkID0gbmV3RGF0JHRvd25fbmFtZSwgc3ViLmNhcHRpb249Ik1NIikNClJlZ3Jlc3Npb25QbG90cyhtbSwgdG93bnMsIHJvYnVzdCA9IFQsb3V0PW91dGxNTSkNCg0KIyBleHRyYWN0IHZhcmlhYmxlcyBzaWduaWZpY2FudCBhdCAwLjAxJSBhbmQgc2F2ZSB0aGVtIGluIGEgZGF0YSBmcmFtZQ0KY29lZmYuZGYkTU0gPSBhcy5tYXRyaXgoc3VtbWFyeShtbSlbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDEpDQpjb2VmZi5kZiRNTVtjb2VmZi5kZiRNTT09VFJVRV0gPSBzcHJpbnRmKCIlLjJlIiwNCiAgICAgICAgICAgICAgIGFzLm1hdHJpeChtbSRjb2VmZmljaWVudHNbc3VtbWFyeShtbSlbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDFdKSwgNCkgDQpjb2VmZi5kZiRNTVtjb2VmZi5kZiRNTT09RkFMU0VdID0gIi0tIg0KDQpgYGANClRoaXMgbW9kZWwgc2hvd3MgdGhhdDoNCg0KKiByZXNpZHVhbHMgYW5kIGZpdHRlZCB2YWx1ZXMgc2VlbSBiZXR0ZXIgdGhhbiB0aGUgT0xTIGZpdA0KKiBpdCBpcyBhYmxlIHRvIGlkZW50aWZ5IHRoZSBtb3N0IG91bHlpbmcgdW5pdHMgKGNvbnNpZGVyaW5nIGFsbCB0aGUgZGltZW5zaW9ucykNCiogbGV2ZXJhZ2UgcG9pbnRzIChMeW9uIGFuZCBUb3Vsb3VzZSkgaGF2ZSBhIGxvd2VyIHdlaWdodCBjb21wYXJlZCB0byBPTFMgZXN0aW1hdGVzDQoNCg0KIyMjIyBMZWFzdCB0cmltbWVkIHNxdWFyZXMNCg0KQSB2ZXJ5IHNpbWlsYXIgcmVzdWx0IGlzIG9idGFpbmVkIHVzaW5nIHRoZSBMVFMgZXN0aW1hdG9yIA0KKGkuZS4sIGFuIGhhcmQgdHJpbW1pbmcgYXBwcm9hY2gpLCB3aXRoIHRyaW1taW5nIHByb3BvcnRpb24gZXF1YWwgDQp0byAxMCUgb2YgdGhlIHVuaXRzLg0KYGBge3IgTFRTfQ0KDQpzZXQuc2VlZCgxKQ0KDQpsdHNfc29sID0gbHRzUmVnKHl+LiwgZGF0YSA9IG1vZCwgIG1ldGhvZCA9ICJsdHMiLCBhbHBoYSA9IDAuOSwgbWNkID0gRikNCnN1bW1hcnkobHRzX3NvbCkNCiMgYXNzaWduIHZhcmlhYmxlcyB1c2VkIGZvciBwbG90dGluZw0KbHRzX3NvbCR5ID0geQ0KbHRzX3NvbCRNRCA9IG1tJE1EICMgdXNlIE1NIG1haGFsYW5vYmlzIGRpc3RhbmNlIChmb3IgY29tcHV0YXRpb25hbCBwcm9ibGVtcyBvZiBzaW5ndWxhcml0eSkNCiAgIyBsdHNfc29sJE1EID0gY292TWNkKG1vZFssYygxOjEyLCAxNDpuY29sKG1vZCkpXSkkbWFoDQoNCiMgb3V0bGllcnMgaWRlbnRpZmllZCBhbmQgaW1wb3J0YW50IGNpdGllcw0Kb3V0bExUUyA9IGFzLmNoYXJhY3RlcihkY2l0eSkNCm91dGxMVFNbbHRzX3NvbCRsdHMud3QgPCAyZS0wNV0gPSAiMCINCm91dGxMVFNbZGNpdHkhPTFdID0gYXMuY2hhcmFjdGVyKGRjaXR5W2RjaXR5IT0xXSkNCm91dGxMVFMgPSBhcy5mYWN0b3Iob3V0bExUUykNCg0KIyBwbG90KGx0c19zb2wpDQpSZWdyZXNzaW9uUGxvdHMobHRzX3NvbCwgdG93bnMsIHJvYnVzdCA9IFQsIG91dCA9IG91dGxMVFMpDQoNCiMgZXh0cmFjdCB2YXJpYWJsZXMgc2lnbmlmaWNhbnQgYXQgMC4wMSUgYW5kIHNhdmUgdGhlbSBpbiBhIGRhdGEgZnJhbWUNCmNvZWZmLmRmJExUUyA9IHN1bW1hcnkobHRzX3NvbClbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDENCmNvZWZmLmRmJExUU1tjb2VmZi5kZiRMVFM9PVRSVUVdID0NCiAgc3ByaW50ZigiJS4yZSIsIGFzLm1hdHJpeChsdHNfc29sJGNvZWZmaWNpZW50c1tzdW1tYXJ5KGx0c19zb2wpW1siY29lZmZpY2llbnRzIl1dWywiUHIoPnx0fCkiXTwwLjAxXSksIDQpDQpjb2VmZi5kZiRMVFNbY29lZmYuZGYkTFRTPT1GQUxTRV0gPSAiLS0iDQoNCmBgYA0KDQpVc2luZyB0aGUgTU0gb3IgTFRTIGVzdGltYXRvciwgYXMgZXhwZWN0ZWQsIGluY3JlYXNlcyB0aGUgYWRqdXN0ZWQgUiBzcXVhcmVkIG9mIGFyb3VuZCAxMCUuDQoNCg0KIyMjIyBCZXN0IHN1YnNldCBzZWxlY3Rpb24gd2l0aCBNTSB3ZWlnaHRzDQoNClRoZSByZXN1bHRzIG9idGFpbmVkIHVzaW5nIGEgYmVzdCBzdWJzZXQgc2VsZWN0aW9uIHByb2NlZHVyZSBhcmUgbm93IGNvbWJpbmVkIHdpdGggdGhlDQp3ZWlnaHRzIG9idGFpbmVkIHVzaW5nIHRoZSBNTSBlc3RpbWF0b3IgKHByZXR0eSBzaW1pbGFyIHRvIHRoZSBvbmVzIG9idGFpZW5kIHVzaW5nIHRoZSBMVFMgd2VpZ2h0cykNCmBgYHtyIEJTUyhNTSkgZXhoYXVzdGl2ZX0NCg0Kc2V0LnNlZWQoMSkNCg0KIyBiZXN0IHN1YnNldCBzZWxlY3Rpb24NCmJlc3Quc3ViID0gcmVnc3Vic2V0cyh5IH4gLiwgZGF0YSA9IG1vZCwgbnZtYXggPSBuY29sKG1vZCksIHJlYWxseS5iaWc9VFJVRSwgbWV0aG9kPSJleGhhdXN0aXZlIiwgd2VpZ2h0cyA9IGx0c19zb2wkbHRzLnd0KSAjIGV4aGF1c3RpdmUgIyBzZXFyZXANCmJlc3Quc3ViLnN1bW1hcnkgPSBzdW1tYXJ5KGJlc3Quc3ViKQ0KIyBtYW51YWwgcGxvdHRpbmcNCnBhcihtZnJvdyA9YygyLDIpKQ0KIyByc3ENCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRyc3EgLCB4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iUnNxIiwgdHlwZT0ibCIpDQppbmRfUnNxID0gd2hpY2gubWF4KGJlc3Quc3ViLnN1bW1hcnkkcnNxKQ0KcG9pbnRzKGluZF9Sc3EsIGJlc3Quc3ViLnN1bW1hcnkkYWRqcjJbaW5kX1JzcV0sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQojIGFkalJzcQ0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JGFkanIyICx4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iQWRqdXN0ZWQgUlNxIiwgdHlwZT0ibCIpDQppbmRfYWRqUnNxID0gd2hpY2gubWF4KGJlc3Quc3ViLnN1bW1hcnkkYWRqcjIpDQpwb2ludHMoaW5kX2FkalJzcSwgYmVzdC5zdWIuc3VtbWFyeSRhZGpyMltpbmRfYWRqUnNxXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCiMgQ3ANCnBsb3QoYmVzdC5zdWIuc3VtbWFyeSRjcCAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9IkNwIiwgdHlwZT0ibCIpDQppbmRfQ3AgPSB3aGljaC5taW4oYmVzdC5zdWIuc3VtbWFyeSRjcCkNCnBvaW50cyhpbmRfQ3AsIGJlc3Quc3ViLnN1bW1hcnkkY3BbaW5kX2FkalJzcV0sIGNvbCA9InJlZCIsIGNleD0yLCBwY2g9MjApDQojIGJpYw0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JGJpYyAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWI9ImJpYyIsIHR5cGU9ImwiKQ0KaW5kX2JpYyA9IHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykNCnBvaW50cyhpbmRfYmljLCBiZXN0LnN1Yi5zdW1tYXJ5JGJpY1tpbmRfYmljXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCm10ZXh0KCJCZXN0IHN1YnNldCBzZWxlY3Rpb24gZm9yIGRlbHRhIGdlbmRlciBzYWxhcnkiLCBvdXRlcj1UUlVFLCAgY2V4PTEuMiwgbGluZT0tMS41KQ0KDQojIGJ1aWx0LWluIHBsb3RzDQpwYXIobWZyb3c9YygxLDEpKQ0KcGxvdC5yZWdzdWJzZXRzMihiZXN0LnN1Yiwgc2NhbGUgPSAicjIiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgUiBzcXVhcmVkIikNCnBsb3QucmVnc3Vic2V0czIoYmVzdC5zdWIsIHNjYWxlID0gImFkanIyIiwgY2V4LmF4aXMgPSAwLjYsIG1haW4gPSAiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIHVzaW5nIGFkanVzdGVkIFIgc3F1YXJlZCIpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJDcCIsIGNleC5heGlzID0gMC42LCBtYWluID0gIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiB1c2luZyBNYWxsb3dzJyBDcCIpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJiaWMiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgQklDIikNCg0KIyBtdGV4dCgiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIGZvciBzYWxhcnkgMTgtMjUgdXNpbmcgQklDIiwgb3V0ZXI9VFJVRSwgIGNleD0xLjQsIGxpbmU9LTMuNSkNCg0KIyByZXRyaWV2ZSB0aGUgbW9kZWwgd2l0aCBtaW4gQklDDQpjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpDQpubm4gPSBuYW1lcyhjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpKQ0Kbm5uWyhubm4gPT0gInVyYmFuVFJVRVRSVUUiKV0gPSAidXJiYW5UUlVFIg0KICAjIG5ubiA8LSBnc3ViKHggPSBubm4sDQogICMgICAgICAgICAgICAgcGF0dGVybiA9ICJgIiwNCiAgIyAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIiKQ0KDQojIGFwcGx5IE9MUyB0byBvYnRhaW4gZGlhZ25vc3RpYyBwbG90cyAoYXBwcm94aW1hdGVseSB0aGUgc2FtZSBjb2VmZmljaWVudHMpDQogICMgbW9kX2JpYyA9IG1vZFssIGNvbG5hbWVzKG1vZCkgJWluJSBubm5dIA0KICAjIG1vZF9iaWMgPSBjYmluZC5kYXRhLmZyYW1lKHksIG1vZF9iaWMpDQogICMgb2xzX2JpYyA9IGxtKHkgfiAuLCBkYXRhID0gbW9kX2JpYywgeT1UKQ0KICAjIHN1bW1hcnkob2xzX2JpYykNCiAgIyBSZWdyZXNzaW9uUGxvdHMob2xzX2JpYywgdGV4dGxhYmVscyA9IHRvd25zLCBvdXQgPSBkY2l0eSkNCg0KIyBleHRyYWN0IHZhcmlhYmxlcyBzaWduaWZpY2FudCBhdCAwLjAxJSBhbmQgc2F2ZSB0aGVtIGluIGEgZGF0YSBmcmFtZQ0KaUJTUyA9IHJvdy5uYW1lcyhjb2VmZi5kZikgJWluJSBubm4NCmNvZWZmLmRmJCJCU1MoTU0pIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KY29lZmYuZGYkIkJTUyhNTSkiW2lCU1NdID0gc3ByaW50ZigiJS4yZSIsIGFzLm1hdHJpeChjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpKSkNCg0KYGBgDQoNClRoaXMgYXBwcm9hY2ggcmVzdWx0cyBpbiBhIHNpZ25pZmljYW50IGltcHJvdmVtZW50IG9mIHRoZSBjb3N0IGZ1bmN0aW9uLA0KYnV0IHRoZSBiZXN0IG1vZGVsIGlzIGxlc3Mgc3BhcnNlIGFuZCBpdCBrZWVwcyBhcHByb3hpbWF0ZWx5IDIwIHZhcmlhYmxlcy4NCg0KDQojIyMjIEVsYXN0aWMgbmV0DQoNClRvIGZhY2UgdGhlIHByb2JsZW0gb2Ygc3Ryb25nIGNvbGxpbmVhcml0eSwgd2hpY2ggaGFzIG5vdCBiZWVuIGNvbnNpZGVyZSB5ZXQsDQphbiBFbGFzdGljLW5ldCBpcyBub3cgZml0dGVkIHVzaW5nIDEwLWZvbGRzIGNyb3NzIHZhbGlkYXRpb24gDQp0byB0dW5lIGxhbWJkYSBhbmQgY29uc2lkZXJpbmcgYSByYW5nZSBvZiAxMSBlcXVpc3BhY2VkIGFscGhhIHZhbHVlcy4NClRoZSBiZXN0IG1vZGVsIGlzIGNvbnNpZGVyZWQgdGhlIG9uZSB3aXRoIHRoZSBsb3dlc3QgTVNQRS4NCmBgYHtyIEVORVR9DQoNCnNldC5zZWVkKDIpDQoNCiMgRWxhc3RpYyBuZXQNCnggPSBhcy5tYXRyaXgobW9kWywgMjpuY29sKG1vZCldKQ0KeSA9IG1vZFssIDFdDQpwYXIobWZyb3cgPWMoMyw0KSkNCnBhcihvbWE9YygwLDAsMiwwKSkNCmFscCA9IHNlcSgwLCAxLCAwLjEpICMgc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAyMCkNCg0KIyBpbml0aWFsaXplIGxpc3QgdG8gY29udGFpbiB0aGUgdmFyaWFibGVzIHNlbGVjdGVkLCANCiMgbnVtYmVyIG9mIG5vbiB6ZXJvIGNvZWZmaWNpZW50cywNCiMgY29zdCBmdW5jdGlvbiBhbmQgdGhlIGNvdW50ZXINCm56Y29lZiA9IG1hdHJpeChOQSwgbGVuZ3RoKGFscCksIDIpDQp2YXJfc2VsZWMgPSBsaXN0KCkNCmNvc3RDViA9IHJlcChOQSwgbGVuZ3RoKGFscCkpDQppID0gMQ0KDQpmb3IgKGogaW4gYWxwKXsNCiAgIyBzZXQuc2VlZCAoMykNCiAgY3Yub3V0ID0gY3YuZ2xtbmV0KHgsIHksIGFscGhhID0gaiwgbmZvbGRzPTEwKSAjIHN0YW5kYXJkaXplLnJlc3BvbnNlID0gVCAsIHBhcmFsbGVsID0gVCANCiAgcGxvdChjdi5vdXQpDQogIHRpdGxlKHBhc3RlKCJhbHBoYSA9ICIsIGopLCBsaW5lID0gMC4zKQ0KICAjIHNhdmUgc2VsZWN0ZWQgdmFyaWFibGVzDQogICMga2VlcCBsb3dlciBudW1iZXIgb2YgdmFyaWFibGVzIHNlbGVjdGVkIGJ1IGxhbWJkYSBvciBsYW1iZGEuMXNlDQogIGlmIChzdW0oY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLm1pbiIpICE9IDApID4gc3VtKGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKSAhPSAwKSl7ICMgbG93ZXN0IGlzIG1pbiBMYW1iZGENCiAgICB0bXBfY29lZmZzIDwtIGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKSANCiAgfSBlbHNlIHsNCiAgICB0bXBfY29lZmZzIDwtIGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS5taW4iKSAjIGN2Lm91dCRsYW1iZGEuMXNlIA0KICB9DQogIHZhcl9zZWxlY1tbaV1dID0gZGF0YS5mcmFtZShuYW1lID0gdG1wX2NvZWZmc0BEaW1uYW1lc1tbMV1dW3RtcF9jb2VmZnNAaSArIDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50ID0gdG1wX2NvZWZmc0B4KQ0KICAjIHNhdmUgbnVtYmVyIG9mIG5vbiB6ZXJvIHZhcmlhYmxlcw0KICBuemNvZWZbaSwxXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEubWluXQ0KICBuemNvZWZbaSwyXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEuMXNlXQ0KICAjIHNhdmUgdGhlIHZhbHVlIG9mIHRoZSBjb3N0IGZ1bmN0aW9uDQogIGNvc3RDVltpXSA9IG1pbihjdi5vdXQkY3ZtKQ0KICAjIGluY3JlYXNlIGl0ZXJhdG9yDQogIGkgPSBpICsgMQ0KfQ0KbXRleHQoZXhwcmVzc2lvbigiQmVzdCBsYW1iZGEgZm9yIGRlbHRhIGdlbmRlciBzYWxhcnkgdXNpbmcgZWxhc3RpYy1uZXQgYW5kIDEwLWZvbGRzIENWIiksIG91dGVyPVRSVUUsICBjZXg9MSwgbGluZT0wLjIpDQoNCiMgYmVzdCBtb2RlbCAobWluIG1lYW4gQ3YgZXJyb3IpDQpiZXN0Q1YgPSB3aGljaC5taW4oY29zdENWKQ0KIyAjIHNob3cgdGhlIGNvZWZmaWNpZW50cyBvYnRhaW5lZCBmb3IgdGhlIGJlc3QgbW9kZWwgKGFkZGVkIGluIHRoZSBmaW5hbCB0YWJsZSkNCiMgcHJpbnQodmFyX3NlbGVjW1tiZXN0Q1ZdXSkNCg0KcGFyKG1mcm93ID1jKDEsMSkpDQojIHBsb3RtZWFuIEN2IGVycm9yDQpwbG90KGFscCwgY29zdENWLCBtYWluPSJNZWFuIDEwLWZvbGRzIENWIGVycm9yIGZvciBlYWNoIGFscGhhIGxldmVsIiwNCiAgICAgeGxhYiA9ICJhbHBoYSBwcm9wb3J0aW9uIiwgeWxhYiA9ICJtZWFuIENWIGVycm9yIiwgdHlwZSA9ICJiIiwgY29sID0gImJsdWUiKQ0KYXhpcyhzaWRlID0gMSwgYXQgPSBjKGFscCkpDQoNCiMgcGxvdCBudW1iZXIgb2Ygbm9uIHplcm8gY29lZmZpY2llbnRzDQpwbG90KGFscCwgbnpjb2VmWywxXSwgbWFpbj0iTnVtYmVyIG9mIG5vbi16ZXJvIGNvZWZmaWNpZW50cyBmb3IgZWFjaCBhbHBoYSBsZXZlbCIsDQogICAgIHhsYWIgPSAiYWxwaGEgcHJvcG9ydGlvbiIsIHlsYWIgPSAiTm9uLXplcm8gY29lZmZpY2llbnRzIiwgdHlwZSA9ICJiIiwgY29sID0gImJsdWUiLA0KICAgICB5bGltID0gYyhtaW4obnpjb2VmKSwgbWF4KG56Y29lZikpKQ0KbGluZXMoYWxwLCBuemNvZWZbLDJdLCBjb2wgPSAicmVkIiwgdHlwZSA9ICJiIikNCmxlZ2VuZCgndG9wcmlnaHQnLCBjKCJsYW1iZGEgbWluIiwgImxhbWJkYSAxU0UiKSwgbHR5ID0gMSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgY2V4ID0gMC43LGx3ZD0yKSANCmF4aXMoc2lkZSA9IDEsIGF0ID0gYyhhbHApKQ0KDQojIGV4dHJhY3Qgc2lnbmlmaWNhbnQgdmFyaWFibGVzIGFuZCBzYXZlIGluIGEgZGF0YWZyYW1lDQpjb2VmZi5kZiRFTkVUID0gYXMuY2hhcmFjdGVyKHJlcCgiLS0iLCBucm93KGNvZWZmLmRmKSkpDQppRU5FVCA9IHJvdy5uYW1lcyhjb2VmZi5kZikgJWluJSB2YXJfc2VsZWNbW2Jlc3RDVl1dJG5hbWUNCmNvZWZmLmRmJEVORVRbaUVORVRdID0gc3ByaW50ZigiJS4yZSIsIGFzLm1hdHJpeCh2YXJfc2VsZWNbW2Jlc3RDVl1dJGNvZWZmaWNpZW50KSkgDQoNCiMgIyBkaWFnbm9zdGljIHBsb3RzDQojICMgY3JlYXRlIHF1YW50aXRpZXMgb2YgaW50ZXJlc3QNCiMgY3Yub3V0JGZpdHRlZC52YWx1ZXMgPSANCiMgICBhcy5tYXRyaXgoY2JpbmQuZGF0YS5mcmFtZSgiKEludGVyY2VwdCkiID0gcmVwKDEsIG5yb3coeCkpLCB4KSlbLGldICUqJSB2YXJfc2VsZWNbW2Jlc3RDVl1dJGNvZWZmaWNpZW50DQojIGN2Lm91dCRyZXNpZHVhbHMgPSB5IC0gY3Yub3V0JGZpdHRlZC52YWx1ZXMNCiMgY3Yub3V0JHJlc2lkdWFscyR5ID0geQ0KIyBjdi5vdXQkIA0KIyANCiMgIyBvdXRsaWVycyBpZGVudGlmaWVkIGFuZCBpbXBvcnRhbnQgY2l0aWVzDQojIG91dGwgPSBkY2l0eQ0KIyAjIG91dGxbbHRzX3NvbCRsdHMud3QgPCAyZS0wNV0gPSAiMCINCiMgIyBvdXRsW2RjaXR5IT0xXSA9IGFzLmNoYXJhY3RlcihkY2l0eVtkY2l0eSE9MV0pDQojICMgb3V0bCA9IGFzLmZhY3RvcihvdXRsKQ0KIyANCiMgIyBwbG90KGx0c19zb2wpDQojIFJlZ3Jlc3Npb25QbG90cyhjdi5vdXQsIHRvd25zLCByb2J1c3QgPSBGLCBvdXQgPSBvdXRsKQ0KDQpgYGANCg0KVGhpcyBlc3RpbWF0b3IgcHJvaWRlcyBhIGZhaXJseSBzcGFyc2UgbW9kZWwsDQpidXQgaXQgaXMgbm9uIHJvYnVzdCwgaXQgaXMgYmlhc2VkLCBhbmQgaXQgZG9lc24ndCANCmVhc3kgYWxsb3cgdG8gcGVyZm9ybSBpbmZlcmVudGlhbCByZWFzb25pbmcuDQoNCg0KIyMjIyBPTFMgb24gZWxhc3RpYyBuZXQgc29sdXRpb24NCg0KVG8gcGFydGlhbGx5IHNvbHZlIHRoZSBsYXR0ZXIgdHdvIHByb2JsZW1zLA0KYW4gT0xTIGZpdCBjb3VsZCBiZSBhcHBsaWVkIG9ubHkgb24gdGhlIHZhcmlhYmxlcyANCnByZXZpb3VzbHkgaWRlbnRpZmllZCBieSBFTkVULg0KYGBge3IgRU5FVCtPTFN9DQoNCiMgT0xTDQppbmRDb2VmID0gbmFtZXMobW9kKSAlaW4lIHZhcl9zZWxlY1tbYmVzdENWXV0kbmFtZQ0KbW9kRU5FVE9MUyA9IGNiaW5kLmRhdGEuZnJhbWUoeSwgbW9kWyxpbmRDb2VmXSkNCkVORVRfb2xzID0gbG0oeSB+IC4sIHk9VCwgZGF0YSA9IG1vZEVORVRPTFMpDQpzdW1tYXJ5KEVORVRfb2xzKQ0KDQojIGRpYWdub3N0aWMgcGxvdHMNCiMgbGF5b3V0KG1hdHJpeCgxOjYsIG5yID0gMiksIGhlaWdodCA9IGMoMiwgMSkpDQpSZWdyZXNzaW9uUGxvdHMoRU5FVF9vbHMsIHRvd25zLCByb2J1c3QgPSBGLCBvdXQ9ZGNpdHkpDQoNCiMgZXh0cmFjdCB2YXJpYWJsZXMgc2lnbmlmaWNhbnQgYXQgMC4wMSUgYW5kIHNhdmUgdGhlbSBpbiBhIGRhdGEgZnJhbWUNCmNvZWZmLmRmJCJFTkVUK09MUyIgPSBhcy5jaGFyYWN0ZXIocmVwKCItLSIsIG5yb3coY29lZmYuZGYpKSkNCmNvZWZmLmRmJCJFTkVUK09MUyJbaUVORVRdID0gYXMubWF0cml4KHN1bW1hcnkoRU5FVF9vbHMpW1siY29lZmZpY2llbnRzIl1dWywiUHIoPnx0fCkiXTwwLjAxKQ0KY29lZmYuZGZbY29lZmYuZGY9PVRSVUVdID0gc3ByaW50ZigiJS4yZSIsDQogICAgICAgICAgIGFzLm1hdHJpeChFTkVUX29scyRjb2VmZmljaWVudHNbc3VtbWFyeShFTkVUX29scylbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDFdKSwgNCkNCmNvZWZmLmRmW2NvZWZmLmRmPT1GQUxTRV0gPSAiLS0iDQoNCmBgYA0KDQpUaGlzIHJlc3VsdHMgaW4gYSBzcGFyc2VyIG1vZGVsIGFuZCB0aGUgZGlhZ25vc3RpYyBwbG90cw0Kc2hvd3MgYSBiZXR0ZXIgcmVzdWx0IGNvbXBhcmVkIHRvIHRoZSByYXcgT0xTIGVzdGltYXRlcywNCmJ1dCBpdCBpcyBzdGlsbCBub3Qgcm9idXN0Lg0KDQoNCiMjIyMgRWxhc3RpYyBuZXQgd2l0aCBNTSB3ZWlnaHRzDQoNCkF0IHRoaXMgcG9pbnQsIGl0IHNlZW1zIHJlYXNvbmFibGUgdG8gdXNlIHRoZSB3ZWlnaHRzDQpvYnRhaW5lZCBieSB0aGUgTU0gZXN0aW1hdG9yIGluIGFuIEVORVQgbW9kZWwsDQp0cnlpbmcgdG8gcmVjb3ZlciBzb21lIHJvYnVzdG5lc3MgYW5kIGtlZXBpbmcgYSBzcGFyc2UgbW9kZWwNCmBgYHtyIEVORVQoTU0pfQ0KDQpzZXQuc2VlZCgxKQ0KDQojIGluaXRpYWxpemUgbGlzdCB0byBjb250YWluIHRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQsIA0KIyBudW1iZXIgb2Ygbm9uIHplcm8gY29lZmZpY2llbnRzLA0KIyBjb3N0IGZ1bmN0aW9uIGFuZCB0aGUgY291bnRlcg0Kbnpjb2VmID0gbWF0cml4KE5BLCBsZW5ndGgoYWxwKSwgMikNCnZhcl9zZWxlYyA9IGxpc3QoKQ0KY29zdENWID0gcmVwKE5BLCBsZW5ndGgoYWxwKSkNCmkgPSAxDQoNCnBhcihtZnJvdyA9YygzLDQpKQ0KcGFyKG9tYT1jKDAsMCwyLDApKQ0KDQojIHh4ID0geFttbSRyd2VpZ2h0cz4wLjksXQ0KIyB5eSA9IHlbbW0kcndlaWdodHM+MC45XQ0KDQoNCmZvciAoaiBpbiBhbHApew0KICAjIHNldC5zZWVkICgzKQ0KICBjdi5vdXQgPSBjdi5nbG1uZXQoeCwgeSwgYWxwaGEgPSBqLCBuZm9sZHM9MTAsIHdlaWdodHMgPSBtbSRyd2VpZ2h0cykgIyBsdHNfc29sJGx0cy53dA0KICBwbG90KGN2Lm91dCkNCiAgdGl0bGUocGFzdGUoImFscGhhID0gIiwgaiksIGxpbmUgPSAwLjMpDQogICMgc2F2ZSBzZWxlY3RlZCB2YXJpYWJsZXMNCiAgIyBrZWVwIGxvd2VyIG51bWJlciBvZiB2YXJpYWJsZXMgc2VsZWN0ZWQgYnUgbGFtYmRhIG9yIGxhbWJkYS4xc2UNCiAgaWYgKHN1bShjb2VmKGN2Lm91dCwgcyA9ICJsYW1iZGEubWluIikgIT0gMCkgPiBzdW0oY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLjFzZSIpICE9IDApKXsgIyBsb3dlc3QgaXMgbWluIExhbWJkYQ0KICAgIHRtcF9jb2VmZnMgPC0gY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLjFzZSIpIA0KICB9IGVsc2Ugew0KICAgIHRtcF9jb2VmZnMgPC0gY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLm1pbiIpICMgY3Yub3V0JGxhbWJkYS4xc2UgDQogIH0NCiAgdmFyX3NlbGVjW1tpXV0gPSBkYXRhLmZyYW1lKG5hbWUgPSB0bXBfY29lZmZzQERpbW5hbWVzW1sxXV1bdG1wX2NvZWZmc0BpICsgMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZmZpY2llbnQgPSB0bXBfY29lZmZzQHgpDQogICMgc2F2ZSBudW1iZXIgb2Ygbm9uIHplcm8gdmFyaWFibGVzDQogIG56Y29lZltpLDFdID0gY3Yub3V0JG56ZXJvW2N2Lm91dCRsYW1iZGEgPT0gY3Yub3V0JGxhbWJkYS5taW5dDQogIG56Y29lZltpLDJdID0gY3Yub3V0JG56ZXJvW2N2Lm91dCRsYW1iZGEgPT0gY3Yub3V0JGxhbWJkYS4xc2VdDQogICMgc2F2ZSB0aGUgdmFsdWUgb2YgdGhlIGNvc3QgZnVuY3Rpb24NCiAgY29zdENWW2ldID0gbWluKGN2Lm91dCRjdm0pDQogICMgaW5jcmVhc2UgaXRlcmF0b3INCiAgaSA9IGkgKyAxDQp9DQptdGV4dChleHByZXNzaW9uKCJCZXN0IGxhbWJkYSBmb3IgZGVsdGEgZ2VuZGVyIHNhbGFyeSB1c2luZyBlbGFzdGljLW5ldCBhbmQgMTAtZm9sZHMgQ1Ygd2l0aCBNTSB3ZWlnaHRzIiksIA0KICAgICAgb3V0ZXI9VFJVRSwgIGNleD0wLjgsIGxpbmU9MC4yKQ0KDQojIGJlc3QgbW9kZWwgKG1pbiBtZWFuIEN2IGVycm9yKQ0KYmVzdENWID0gd2hpY2gubWluKGNvc3RDVikNCiMgIyBzaG93IHRoZSBjb2VmZmljaWVudHMgb2J0YWluZWQgZm9yIHRoZSBiZXN0IG1vZGVsIChhZGRlZCBpbiB0aGUgZmluYWwgdGFibGUpDQojIHByaW50KHZhcl9zZWxlY1tbYmVzdENWXV0pDQoNCnBhcihtZnJvdyA9YygxLDEpKQ0KIyBwbG90bWVhbiBDdiBlcnJvcg0KcGxvdChhbHAsIGNvc3RDViwgbWFpbj0iTWVhbiAxMC1mb2xkcyBDViBlcnJvciBmb3IgZWFjaCBhbHBoYSBsZXZlbCIsDQogICAgIHhsYWIgPSAiYWxwaGEgcHJvcG9ydGlvbiIsIHlsYWIgPSAibWVhbiBDViBlcnJvciIsIHR5cGUgPSAiYiIsIGNvbCA9ICJibHVlIikNCmF4aXMoc2lkZSA9IDEsIGF0ID0gYyhhbHApKQ0KDQojIHBsb3QgbnVtYmVyIG9mIG5vbiB6ZXJvIGNvZWZmaWNpZW50cw0KcGxvdChhbHAsIG56Y29lZlssMV0sIG1haW49Ik51bWJlciBvZiBub24temVybyBjb2VmZmljaWVudHMgZm9yIGVhY2ggYWxwaGEgbGV2ZWwiLA0KICAgICB4bGFiID0gImFscGhhIHByb3BvcnRpb24iLCB5bGFiID0gIk5vbi16ZXJvIGNvZWZmaWNpZW50cyIsIHR5cGUgPSAiYiIsIGNvbCA9ICJibHVlIiwNCiAgICAgeWxpbSA9IGMobWluKG56Y29lZiksIG1heChuemNvZWYpKSkNCmxpbmVzKGFscCwgbnpjb2VmWywyXSwgY29sID0gInJlZCIsIHR5cGUgPSAiYiIpDQpsZWdlbmQoJ3RvcHJpZ2h0JywgYygibGFtYmRhIG1pbiIsICJsYW1iZGEgMVNFIiksIGx0eSA9IDEsIGNvbCA9IGMoImJsdWUiLCAicmVkIiksIGNleCA9IDAuNyxsd2Q9MikgDQpheGlzKHNpZGUgPSAxLCBhdCA9IGMoYWxwKSkNCg0KIyBleHRyYWN0IHNpZ25pZmljYW50IHZhcmlhYmxlcyBhbmQgc2F2ZSBpbiBhIGRhdGFmcmFtZQ0KY29lZmYuZGYkIkVORVQoTU0pIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KaUVORVRNTSA9IHJvdy5uYW1lcyhjb2VmZi5kZikgJWluJSB2YXJfc2VsZWNbW2Jlc3RDVl1dJG5hbWUNCmNvZWZmLmRmJCJFTkVUKE1NKSJbaUVORVRNTV0gPSBzcHJpbnRmKCIlLjJlIiwgYXMubWF0cml4KHZhcl9zZWxlY1tbYmVzdENWXV0kY29lZmZpY2llbnQpKSANCg0KYGBgDQoNCkNvbnRyYXJpbHkgdG8gd2hhdCB3YXMgZXhwZWN0ZWQsIHRoZSBtb2RlbCBpcyBub3Qgc28gc3BhcnNlLg0KT24gdGhlIG90aGVyIGhhbmQsIHRoZSBtZWFuIENWIGVycm9yIGRlY3JlYXNlcyBzdWJzdGlhbnRpYWxseSAoYXJvdW5kIDAuNCkNCmNvbXBhcmVkIHRvIHRoZSBwdXJlIEVORVQgbW9kZWwgKGFyb3VuZCAxLjQpLCBhbmQgdGhpcyBnYWluIHdvdWxkDQpwZXJzaXN0IGV2ZW4gYWxsb3dpbmcgZm9yIGEgc3Ryb25nZXIgc2hyaW5rYWdlIG9mIHRoZSBsYW1iZGEgdmFsdWVzDQphbmQgaGVuY2UgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBjb2VmZmljaWVudHMuDQpJdCBpcyBhbHNvIGludGVyZXN0aW5nIHRvIG5vdGUgdGhlIHN0cm9uZyBkZWNyZWFzZSBpbiBDViBlcnJvcg0KYXMgc29tZSBwcm9wb3J0aW9uIG9mIExhc3NvIGlzIGludHJvZHVjZWQuDQoNClN1Y2ggYmVoYXZpb3Igc2hvdWxkIGJlIHN0dWRpZWQgZnVydGhlci4NCkEgbW90aXZhdGlvbiBjb3VsZCBiZSB0aGUgZmFjdCB0aGF0IEVORVQgaXMgYmFzZWQgb24gQ1YsIGFuZCBzb21lIG91bHlpbmcgdW5pdHMgd2l0aCB3ZWlnaHRzDQpkaWZmZXJlbnQgZnJvbSAwIGNhbiBhZmZlY3QgdGhpcyByZXN1bHQuDQpPbiB0aGUgb3RoZXIgaGFuZCwgZXZlbiBwcm92aWRpbmcgdG8gRU5FVCBvbmx5IHVuaXRzIHdpdGggTU0td2VpZ2h0cw0KZ3JlYXRlciB0aGFuIDAuOSAoaS5lLiwgYWxtb3N0IHN1cmVseSBub24gb3V0bHlpbmcgYWNjb3JkaW5nIHRvIE1NIGJhc2VkIG9uIGFsbCB2YXJpYWJsZXMpLA0KbWVhbiBDViBlcnJvciBkZWNyZWFzZSBzdWJzdGFudGlhbGx5LCBidXQgdGhlIHByb2JsZW0gcGVyc2lzdHMuDQoNCg0KIyMjIyBPTFMgb24gRWxhc3RpYyBuZXQgc29sdXRpb24gd2l0aCBNTSB3ZWlnaHRzDQoNClRoZSBzYW1lIGlzIHRydWUgYXBwbHlpbmcgYW4gT0xTIGVzdGltYXRlIG9uIHRoZSBub24gb3VseWluZyB1bml0cw0KKGkuZS4sIHdpdGggTU0td2VpZ2h0cyA+IDAuMDUpIGFuZCB0aGUgdmFyaWFibGVzIGlkZW50aWZpZWQgYnUgRU5FVChNTSkuDQpgYGB7ciBFTkVUKE1NKStPTFN9DQoNCiMgVG8gdHJ5IHRvIHNvbHZlIHRoaXMgcHJvYmxlbSwgYW5vdGhlciBzdGVwIG9mDQojIE9MUyBlc3RpbWF0ZSBpcyBjb21wdXRlZCBvbiB0aGUgdmFyaWFibGVzIHNlbGVjdGVkIGJ5DQojIEUtTmV0IHdpdGggTU0gd2VpZ2h0cywgZXhjbHVkaW5nIGZyb210IHRoZSBmaXQgdW5pdHMNCiMgd2l0aCBNTS13ZWlnaHRzIDw9IDAuMDUNCg0KIyBBREQgT0xTDQppbmRDb2VmID0gbmFtZXMobW9kKSAlaW4lIHZhcl9zZWxlY1tbYmVzdENWXV0kbmFtZQ0KbW9kRU5FVE1NT0xTID0gY2JpbmQuZGF0YS5mcmFtZSh5LCBtb2RbLGluZENvZWZdKQ0KbW9kRU5FVE1NT0xTID0gbW9kRU5FVE1NT0xTW21tJHJ3ZWlnaHRzPjAuMDUsXQ0KRU5FVE1NX29scyA9IGxtKHkgfiAuLCB5PVQsIGRhdGEgPSBtb2RFTkVUTU1PTFMpDQpzdW1tYXJ5KEVORVRNTV9vbHMpDQoNCiMgZGlhZ25vc3RpYyBwbG90cw0KIyBsYXlvdXQobWF0cml4KDE6NiwgbnIgPSAyKSwgaGVpZ2h0ID0gYygyLCAxKSkNClJlZ3Jlc3Npb25QbG90cyhFTkVUTU1fb2xzLCB0b3duc1ttbSRyd2VpZ2h0cz4wLjA1XSwgcm9idXN0ID0gRiwgb3V0PWRjaXR5W21tJHJ3ZWlnaHRzPjAuMDVdKQ0KDQojIGV4dHJhY3QgdmFyaWFibGVzIHNpZ25pZmljYW50IGF0IDAuMDElIGFuZCBzYXZlIHRoZW0gaW4gYSBkYXRhIGZyYW1lDQpjb2VmZi5kZiQiRU5FVChNTSkrT0xTIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KY29lZmYuZGYkIkVORVQoTU0pK09MUyJbaUVORVRNTV0gPSBhcy5tYXRyaXgoc3VtbWFyeShFTkVUTU1fb2xzKVtbImNvZWZmaWNpZW50cyJdXVssIlByKD58dHwpIl08MC4wMSkNCmNvZWZmLmRmJCJFTkVUKE1NKStPTFMiW2NvZWZmLmRmJCJFTkVUKE1NKStPTFMiPT1UUlVFXSA9IHNwcmludGYoIiUuMmUiLA0KICAgICAgICAgICBhcy5tYXRyaXgoRU5FVE1NX29scyRjb2VmZmljaWVudHNbc3VtbWFyeShFTkVUTU1fb2xzKVtbImNvZWZmaWNpZW50cyJdXVssIlByKD58dHwpIl08MC4wMV0pLCA0KQ0KY29lZmYuZGYkIkVORVQoTU0pK09MUyJbY29lZmYuZGYkIkVORVQoTU0pK09MUyI9PUZBTFNFXSA9ICItLSINCg0KYGBgDQoNCg0KU3VjaCBhIG1vZGVsIGlzIHNwYXJzZXIuIEl0IHN0aWxsIGNvbnRhaW5zIHNvbWUgbGV2ZXJhZ2UgcG9pbnRzLA0KbGlrZSBUb3Vsb3NlIGFuZCBMeW9uLCBidXQgdGhlIGRpYWdub3N0aWMgcGxvdHMgZG9uJ3Qgc2hvdyBhbnkNCnBhcnRpY3VsYXJseSBzdHJhbmdlIGJlaGF2aW9yLg0KDQpBdCB0aGUgc2FtZSB0d28gaW1wb3J0YW50IGFzcGVjdHMgaGF2ZSB0byBiZSB0YWtlbiBpbiBhY2NvdW50Og0KDQoqIHRoZSBNTSB3ZWlnaHRzIHVzZWQgd2VyZSBvYnRhaW5lZCBvbiB0aGUgYmFzZSBvZiB0aGUgZnVsbCBtb2RlbCwgDQogIGhlbmNlIHRoZXkgY291bGQgbm90IGJlIHJlYWxseSByZXByZXNlbnRhdGl2ZQ0KKiBzb21lIHVuaXRzIGFyZSBub3QgYmVpbmcgbW9kZWxlZCBpbiB0aGlzIGZyYW1ld29yaw0KDQoNCiMjIyMgRWxhc3RpYyBuZXQgd2l0aCBNTSB3ZWlnaHRzIHRvIG1vZGVsIG91dGxpZXJzDQoNClRvIGZhY2UgdGhlIGxhdHRlciBwcm9ibGVtIGFuIGFwcHJvYWNoIGNvdWxkIGJlIGJhc2VkIG9uIHRoZSBpZGVhIG9mIG1vZGVsaW5nDQphcGFydCB0aGUgbW9zdCBvdXRseWluZyB1bml0cy4NCkZvciBpbnN0YW5jZSwgdGhlIGZvbGxvd2luZyBmaXQgaXMgYmFzZWQgb24gRU5FVCB3aGVyZSB0aGUgcHJvdmlkZWQgd2VpZ2h0cw0KY29ycmVzcG9uZCB0byAxIC0gTU13ZWlnaHRzLg0KYGBge3IgRU5FVChNTW91dCl9DQoNCnNldC5zZWVkKDEpDQoNCiMgaW5pdGlhbGl6ZSBsaXN0IHRvIGNvbnRhaW4gdGhlIHZhcmlhYmxlcyBzZWxlY3RlZCwgDQojIG51bWJlciBvZiBub24gemVybyBjb2VmZmljaWVudHMsDQojIGNvc3QgZnVuY3Rpb24gYW5kIHRoZSBjb3VudGVyDQpuemNvZWYgPSBtYXRyaXgoTkEsIGxlbmd0aChhbHApLCAyKQ0KdmFyX3NlbGVjID0gbGlzdCgpDQpjb3N0Q1YgPSByZXAoTkEsIGxlbmd0aChhbHApKQ0KaSA9IDENCg0KcGFyKG1mcm93ID1jKDMsNCkpDQpwYXIob21hPWMoMCwwLDIsMCkpDQoNCmZvciAoaiBpbiBhbHApew0KICAjIHNldC5zZWVkICgzKQ0KICBjdi5vdXQgPSBjdi5nbG1uZXQoeCwgeSwgYWxwaGEgPSBqLCBuZm9sZHM9MTAsIHdlaWdodHMgPSAxLW1tJHJ3ZWlnaHRzKSAjIHN0YW5kYXJkaXplLnJlc3BvbnNlID0gVCAsIHBhcmFsbGVsID0gVCANCiAgcGxvdChjdi5vdXQpDQogIHRpdGxlKHBhc3RlKCJhbHBoYSA9ICIsIGopLCBsaW5lID0gMC4zKQ0KICAjIHNhdmUgc2VsZWN0ZWQgdmFyaWFibGVzDQogICMga2VlcCBsb3dlciBudW1iZXIgb2YgdmFyaWFibGVzIHNlbGVjdGVkIGJ1IGxhbWJkYSBvciBsYW1iZGEuMXNlDQogIGlmIChzdW0oY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLm1pbiIpICE9IDApID4gc3VtKGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKSAhPSAwKSl7ICMgbG93ZXN0IGlzIG1pbiBMYW1iZGENCiAgICB0bXBfY29lZmZzIDwtIGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKSANCiAgfSBlbHNlIHsNCiAgICB0bXBfY29lZmZzIDwtIGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS5taW4iKSAjIGN2Lm91dCRsYW1iZGEuMXNlIA0KICB9DQogIHZhcl9zZWxlY1tbaV1dID0gZGF0YS5mcmFtZShuYW1lID0gdG1wX2NvZWZmc0BEaW1uYW1lc1tbMV1dW3RtcF9jb2VmZnNAaSArIDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50ID0gdG1wX2NvZWZmc0B4KQ0KICAjIHNhdmUgbnVtYmVyIG9mIG5vbiB6ZXJvIHZhcmlhYmxlcw0KICBuemNvZWZbaSwxXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEubWluXQ0KICBuemNvZWZbaSwyXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEuMXNlXQ0KICAjIHNhdmUgdGhlIHZhbHVlIG9mIHRoZSBjb3N0IGZ1bmN0aW9uDQogIGNvc3RDVltpXSA9IG1pbihjdi5vdXQkY3ZtKQ0KICAjIGluY3JlYXNlIGl0ZXJhdG9yDQogIGkgPSBpICsgMQ0KfQ0KbXRleHQoZXhwcmVzc2lvbigiQmVzdCBsYW1iZGEgZm9yIGRlbHRhIGdlbmRlciBzYWxhcnkgdXNpbmcgZWxhc3RpYy1uZXQgYW5kIDEwLWZvbGRzIENWIHdpdGggTU0gd2VpZ2h0cyIpLCANCiAgICAgIG91dGVyPVRSVUUsICBjZXg9MC44LCBsaW5lPTAuMikNCg0KIyBiZXN0IG1vZGVsIChtaW4gbWVhbiBDdiBlcnJvcikNCmJlc3RDViA9IHdoaWNoLm1pbihjb3N0Q1YpDQojICMgc2hvdyB0aGUgY29lZmZpY2llbnRzIG9idGFpbmVkIGZvciB0aGUgYmVzdCBtb2RlbCAoYWRkZWQgaW4gdGhlIGZpbmFsIHRhYmxlKQ0KIyBwcmludCh2YXJfc2VsZWNbW2Jlc3RDVl1dKQ0KDQpwYXIobWZyb3cgPWMoMSwxKSkNCiMgcGxvdG1lYW4gQ3YgZXJyb3INCnBsb3QoYWxwLCBjb3N0Q1YsIG1haW49Ik1lYW4gMTAtZm9sZHMgQ1YgZXJyb3IgZm9yIGVhY2ggYWxwaGEgbGV2ZWwiLA0KICAgICB4bGFiID0gImFscGhhIHByb3BvcnRpb24iLCB5bGFiID0gIm1lYW4gQ1YgZXJyb3IiLCB0eXBlID0gImIiLCBjb2wgPSAiYmx1ZSIpDQpheGlzKHNpZGUgPSAxLCBhdCA9IGMoYWxwKSkNCg0KIyBwbG90IG51bWJlciBvZiBub24gemVybyBjb2VmZmljaWVudHMNCnBsb3QoYWxwLCBuemNvZWZbLDFdLCBtYWluPSJOdW1iZXIgb2Ygbm9uLXplcm8gY29lZmZpY2llbnRzIGZvciBlYWNoIGFscGhhIGxldmVsIiwNCiAgICAgeGxhYiA9ICJhbHBoYSBwcm9wb3J0aW9uIiwgeWxhYiA9ICJOb24temVybyBjb2VmZmljaWVudHMiLCB0eXBlID0gImIiLCBjb2wgPSAiYmx1ZSIsDQogICAgIHlsaW0gPSBjKG1pbihuemNvZWYpLCBtYXgobnpjb2VmKSkpDQpsaW5lcyhhbHAsIG56Y29lZlssMl0sIGNvbCA9ICJyZWQiLCB0eXBlID0gImIiKQ0KbGVnZW5kKCd0b3ByaWdodCcsIGMoImxhbWJkYSBtaW4iLCAibGFtYmRhIDFTRSIpLCBsdHkgPSAxLCBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCBjZXggPSAwLjcsbHdkPTIpIA0KYXhpcyhzaWRlID0gMSwgYXQgPSBjKGFscCkpDQoNCiMgZXh0cmFjdCBzaWduaWZpY2FudCB2YXJpYWJsZXMgYW5kIHNhdmUgaW4gYSBkYXRhZnJhbWUNCmNvZWZmLmRmJCJFTkVUKE1Nb3V0KSIgPSBhcy5jaGFyYWN0ZXIocmVwKCItLSIsIG5yb3coY29lZmYuZGYpKSkNCmlFTkVUTU0gPSByb3cubmFtZXMoY29lZmYuZGYpICVpbiUgdmFyX3NlbGVjW1tiZXN0Q1ZdXSRuYW1lDQpjb2VmZi5kZiQiRU5FVChNTW91dCkiW2lFTkVUTU1dID0gc3ByaW50ZigiJS4yZSIsIGFzLm1hdHJpeCh2YXJfc2VsZWNbW2Jlc3RDVl1dJGNvZWZmaWNpZW50KSkgDQoNCiMgQUREIE9MUyAob25seSB0byBvYnRhaW4gZGlhZ25vc3RpYyBwbG90cykNCnRyaW0gPSAwLjE1ICMgTU0gd2VpZ2h0cyBjdXRvZmYgdG8gY29uc2lkZXIgb3V0bGllcnMNCmluZENvZWYgPSBuYW1lcyhtb2QpICVpbiUgdmFyX3NlbGVjW1tiZXN0Q1ZdXSRuYW1lDQptb2RFTkVUTU1PTFNvdXQgPSBjYmluZC5kYXRhLmZyYW1lKHkgPSB5W21tJHJ3ZWlnaHRzPHRyaW1dLCBtb2RbbW0kcndlaWdodHM8dHJpbSxpbmRDb2VmXSkgDQptb2RFTkVUTU1PTFNvdXQgPSBsbSh5IH4gLiwgeT1ULCBkYXRhID0gbW9kRU5FVE1NT0xTb3V0KQ0Kc3VtbWFyeShtb2RFTkVUTU1PTFNvdXQpDQoNCiMgZGlhZ25vc3RpYyBwbG90cw0KIyBsYXlvdXQobWF0cml4KDE6NiwgbnIgPSAyKSwgaGVpZ2h0ID0gYygyLCAxKSkNCm91dGxPcHBvc2l0ZSA9IGFzLmNoYXJhY3RlcihvdXRsTU0pIA0Kb3V0bE9wcG9zaXRlW291dGxPcHBvc2l0ZSA9PSAwXSA9IDEwMA0Kb3V0bE9wcG9zaXRlW291dGxPcHBvc2l0ZSA9PSAxXSA9IDANCm91dGxPcHBvc2l0ZVtvdXRsT3Bwb3NpdGUgPT0gMTAwXSA9IDENCm91dGxPcHBvc2l0ZSA9IGFzLmZhY3RvcihvdXRsT3Bwb3NpdGUpDQpSZWdyZXNzaW9uUGxvdHMobW9kRU5FVE1NT0xTb3V0LCB0b3duc1ttbSRyd2VpZ2h0czx0cmltXSwgcm9idXN0ID0gRiwgb3V0PWRjaXR5W21tJHJ3ZWlnaHRzPHRyaW1dKQ0KDQojIGV4dHJhY3QgdmFyaWFibGVzIHNpZ25pZmljYW50IGF0IDAuMDElIGFuZCBzYXZlIHRoZW0gaW4gYSBkYXRhIGZyYW1lDQojIGNvZWZmLmRmJCJFTkVUKE1Nb3V0KSIgPSBhcy5jaGFyYWN0ZXIocmVwKCItLSIsIG5yb3coY29lZmYuZGYpKSkNCiMgY29lZmYuZGYkIkVORVQoTU1vdXQpIltpRU5FVE1NXSA9IGFzLm1hdHJpeChzdW1tYXJ5KG1vZEVORVRNTU9MU291dClbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDEpDQojIGNvZWZmLmRmW2NvZWZmLmRmPT1UUlVFXSA9IHNwcmludGYoIiUuMmUiLA0KIyAgICAgICAgICAgIGFzLm1hdHJpeChtb2RFTkVUTU1PTFNvdXQkY29lZmZpY2llbnRzW3N1bW1hcnkobW9kRU5FVE1NT0xTb3V0KVtbImNvZWZmaWNpZW50cyJdXVssIlByKD58dHwpIl08MC4wMV0pLCA0KQ0KIyBjb2VmZi5kZltjb2VmZi5kZj09RkFMU0VdID0gIi0tIiANCg0KYGBgDQoNClRoaXMgbW9kZWwgcmVzdWx0cyBpbjoNCg0KKiBpbiBhIHZlcnkgc3BhcnNlIG1vZGVsIHdoaWNoIHNlZW1zIHRvIGZpdCByZWFzb25hYmx5IHdlbGwgYWxsIHRoZSBwb2ludHMNCiogcHJvdmlkZXMgY29lZmZpY2llbnRzIHdoaWNoIGFyZSBzZW5zaWJseSBkaWZmZXJlbnQgdGhhbiB0aGUgb25lcyBmb3IgdGhlIHByZXZpb3VzbHkgY29uc2lkZXJlZCBtb2RlbHMNCihlLmcuLCB1bmVtcGxveW1lbnQgY29lZmZpY2llbnRzIGNoYW5nZXMgaXRzIHNpZ24pIHN1cHBvcnRpbmcgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGVyZSBpcyBtb3JlIHRoYW4gb25lIGRhdGEgZ2VuZXJhdGluZyBwcm9jZXNzDQoqIHRoZSBtZWFuIENWIGVycm9yIGluY3JlYXNlcyBzdWJzdGFudGlhbGx5IA0KDQpBIHBvc3NpYmxlIGRyYXdiYWNrIG9mIHRoaXMgYXBwcm9hY2gsIGFnYWluLCByZXNpZGUgb24gdGhlIGZhY3QgdGhhdCB0aGUgTU0gd2VpZ2h0cyBhcmUgYmFzZWQgb24gdGhlIGZ1bGwgbW9kZWwuDQoNCg0KIyMjIFJvYnVzdCBhcHByb2FjaGVzIGluY29ycG9yYXRpbmcgc2hyaW5rYWdlDQoNCiMjIyMgU3BhcnNlLUxUUw0KDQpUaGUgZXN0aW1hdG9yIHNwYXJzZS1MVFMsIHdoaWNoIGlzIGEgcm9idXN0aWZpY2F0aW9uIG9mIExhc3NvIHVzaW5nIExUUywgaXMgbm93IGFwcGxpZWQuDQpUaGUgcGFyYW1ldGVycyB1c2VkIHBlcmZvcm0gYSB0cmltbWluZyAxMCUgb2YgdGhlIHVuaXRzLCBhbmQgMTAgcmVwbGljYXRpb24gb2YgMTAtZm9sZHMgQ1YNCmBgYHtyIHNwYXJzZUxUUywgd2FybmluZz1GQUxTRX0NCg0Kc2V0LnNlZWQoMSkNCg0KIyBpbnN0YWxsLnBhY2thZ2VzKCJzbm93IikNCiMgbGlicmFyeSgic25vdyIpDQoNCiMgZml0IHJvYnVzdCBMYXNzbw0KY2wgPSBtYWtlQ2x1c3RlcihkZXRlY3RDb3JlcygpKQ0Kc29sX3NwYXJzZUxUUyA9IHNwYXJzZUxUUyh4LCB5LCBtb2RlID0gImxhbWJkYSIsIGFscGhhID0gMC45LCBjbCA9IGNsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWw9VFJVRSwgY3JpdCA9ICJQRSIsIHNwbGl0cyA9IGZvbGRDb250cm9sKDEwLCAxMCkpICMgY3JpdCA9ICJCSUMiDQojIHNvbF9zcGFyc2VMVFNfQ1YgPSBwZXJyeShzb2xfc3BhcnNlTFRTLCBzcGxpdHMgPSBmb2xkQ29udHJvbCgxMCwgMTApLCBjb3N0ID0gcnRtc3BlLCBuY29yZXMgPSBjbCkNCnN0b3BDbHVzdGVyKGNsKQ0KDQojIGNoZWNrIHRoZSBzb2x1dGlvbg0KcHJpbnQoc29sX3NwYXJzZUxUUykgDQoNCiMgZml0IGFuZCBldmFsdWF0ZSBzcGFyc2UgTFRTIG1vZGVsDQogICMgcGxvdChzb2xfc3BhcnNlTFRTX0NWLCAiZGVuc2l0eSIpIA0KDQojIGFzc2lnbiBsZXZlcmFnZXMsIGFzc2lnbmVkIG9ubHkgdG8gb2J0YWluIGRpYWdub3N0aWMgcGxvdHMNCnNvbF9zcGFyc2VMVFMkTUQgPSByZXAoMSwgbGVuZ3RoKHkpKSAjIGNvdk1jZCh4KSRtYWggIyBjb21wdXRlIHN0YW5kYXJkIGxldmVyYWdlcw0KIyBvdXRsaWVycyBpZGVudGlmaWVkIGFuZCBpbXBvcnRhbnQgY2l0aWVzDQpvdXRsU0xUUyA9IGFzLmNoYXJhY3Rlcihzb2xfc3BhcnNlTFRTJHd0KQ0Kb3V0bFNMVFNbZGNpdHkhPTFdID0gYXMuY2hhcmFjdGVyKGRjaXR5W2RjaXR5IT0xXSkNCm91dGxTTFRTID0gYXMuZmFjdG9yKG91dGxTTFRTKQ0KIyBkaWFnbm9zdGljIHBsb3RzDQpSZWdyZXNzaW9uUGxvdHMoc29sX3NwYXJzZUxUUywgdGV4dGxhYmVscz10b3ducywgcm9idXN0ID0gVFJVRSwgb3V0ID0gb3V0bFNMVFMpW1sxXV0NClJlZ3Jlc3Npb25QbG90cyhzb2xfc3BhcnNlTFRTLCB0ZXh0bGFiZWxzPXRvd25zLCByb2J1c3QgPSBUUlVFLCBvdXQgPSBvdXRsU0xUUylbWzJdXQ0KUmVncmVzc2lvblBsb3RzKHNvbF9zcGFyc2VMVFMsIHRleHRsYWJlbHM9dG93bnMsIHJvYnVzdCA9IFRSVUUsIG91dCA9IG91dGxTTFRTKVtbM11dDQpkaWFnbm9zdGljUGxvdChzb2xfc3BhcnNlTFRTLCBpZC5uID0gMTAsIGFzayA9IEZBTFNFLCBsYWJlbD10b3ducywgd2hpY2ggPSAicmRpYWciKQ0KDQojIGV4dHJhY3QgdmFyaWFibGVzIHNpZ25pZmljYW50IGFuZCBzYXZlIHRoZW0gaW4gYSBkYXRhIGZyYW1lDQppU0xUUyA9IGNvZWZmaWNpZW50cyhzb2xfc3BhcnNlTFRTKSE9MA0KY29lZmYuZGYkU0xUUyA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KY29lZmYuZGYkU0xUU1tpU0xUU10gPSBzcHJpbnRmKCIlLjJlIiwgYXMubWF0cml4KGNvZWZmaWNpZW50cyhzb2xfc3BhcnNlTFRTKVtpU0xUU10pKQ0KDQpgYGANCg0KVGhpcyBtb2RlbCByZXN1bHRzIGluOiANCg0KKiBhIHZlcnkgc3BhcnNlIGFuZCByb2J1c3QgbW9kZWwNCiogdGhlIHVuZW1wbG95bWVudCByYXRlIGlzIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZQ0KKiBjbGVhcmx5IHNob3dzIDIgZGlmZmVyZW50IHN1Yi1wb3BpbGF0aW9ucw0KKiBQYXJpcyBpcyBub3QgYSBjbGVhciBvdXRsaWVyDQoNClRoZSBtYWluIGRyYXdiYWNrIHJlc2lkZXMgb24gdGhlIGZhY3QgdGhhdCBpdCBpcyBub3QgdGFraW5nIGludG8gYWNjb3VudCBjb2xsaW5lYXJpdHkuDQoNCg0KIyMjIyBFbGFzdGljIG5ldCB3aXRoIHNwYXJzZS1MVFMgd2VpZ2h0cw0KDQpJbiBvcmRlciB0byB0cnkgdG8gc29sdmUgdGhpcyBwcm9ibGVtLCBFTkVUIHdpdGggd2VpZ2h0cyBwcm92aWRlZCBieSBzcGFyc2VMVFMgY291bGQgYmUgYXBwbGllZA0KYGBge3IgRU5FVChTTFRTKX0NCg0Kc2V0LnNlZWQoMSkNCg0KIyBpbml0aWFsaXplIGxpc3QgdG8gY29udGFpbiB0aGUgdmFyaWFibGVzIHNlbGVjdGVkLA0KIyBudW1iZXIgb2Ygbm9uIHplcm8gY29lZmZpY2llbnRzLA0KIyBjb3N0IGZ1bmN0aW9uIGFuZCB0aGUgY291bnRlcg0Kbnpjb2VmID0gbWF0cml4KE5BLCBsZW5ndGgoYWxwKSwgMikNCnZhcl9zZWxlYyA9IGxpc3QoKQ0KY29zdENWID0gcmVwKE5BLCBsZW5ndGgoYWxwKSkNCmkgPSAxDQoNCnBhcihtZnJvdyA9YygzLDQpKQ0KcGFyKG9tYT1jKDAsMCwyLDApKQ0KDQpub25PdXRTTFRTID0gYXMubG9naWNhbChzb2xfc3BhcnNlTFRTJHd0KQ0KDQpmb3IgKGogaW4gYWxwKXsNCiAgIyBzZXQuc2VlZCAoMykNCiAgY3Yub3V0ID0gY3YuZ2xtbmV0KHgsIHksIGFscGhhID0gaiwgbmZvbGRzPTEwLCB3ZWlnaHRzID0gbm9uT3V0U0xUUykNCiAgcGxvdChjdi5vdXQpDQogIHRpdGxlKHBhc3RlKCJhbHBoYSA9ICIsIGopLCBsaW5lID0gMC4zKQ0KICAjIHNhdmUgc2VsZWN0ZWQgdmFyaWFibGVzDQogICMga2VlcCBsb3dlciBudW1iZXIgb2YgdmFyaWFibGVzIHNlbGVjdGVkIGJ5IGxhbWJkYSBvciBsYW1iZGEuMXNlDQogIGlmIChzdW0oY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLm1pbiIpICE9IDApID4gc3VtKGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKSAhPSAwKSl7ICMgbG93ZXN0IGlzIG1pbiBMYW1iZGENCiAgICB0bXBfY29lZmZzIDwtIGNvZWYoY3Yub3V0LCBzID0gImxhbWJkYS4xc2UiKQ0KICB9IGVsc2Ugew0KICAgIHRtcF9jb2VmZnMgPC0gY29lZihjdi5vdXQsIHMgPSAibGFtYmRhLm1pbiIpICMgY3Yub3V0JGxhbWJkYS4xc2UNCiAgfQ0KICB2YXJfc2VsZWNbW2ldXSA9IGRhdGEuZnJhbWUobmFtZSA9IHRtcF9jb2VmZnNARGltbmFtZXNbWzFdXVt0bXBfY29lZmZzQGkgKyAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50ID0gdG1wX2NvZWZmc0B4KQ0KICAjIHNhdmUgbnVtYmVyIG9mIG5vbiB6ZXJvIHZhcmlhYmxlcw0KICBuemNvZWZbaSwxXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEubWluXQ0KICBuemNvZWZbaSwyXSA9IGN2Lm91dCRuemVyb1tjdi5vdXQkbGFtYmRhID09IGN2Lm91dCRsYW1iZGEuMXNlXQ0KICAjIHNhdmUgdGhlIHZhbHVlIG9mIHRoZSBjb3N0IGZ1bmN0aW9uDQogIGNvc3RDVltpXSA9IG1pbihjdi5vdXQkY3ZtKQ0KICAjIGluY3JlYXNlIGl0ZXJhdG9yDQogIGkgPSBpICsgMQ0KfQ0KbXRleHQoZXhwcmVzc2lvbigiQmVzdCBsYW1iZGEgZm9yIGRlbHRhIGdlbmRlciBzYWxhcnkgdXNpbmcgZWxhc3RpYy1uZXQgYW5kIDEwLWZvbGRzIENWIHdpdGggc3BhcnNlLUxUUyB3ZWlnaHRzIiksDQogICAgICBvdXRlcj1UUlVFLCAgY2V4PTAuOCwgbGluZT0wLjIpDQoNCiMgYmVzdCBtb2RlbCAobWluIG1lYW4gQ3YgZXJyb3IpDQpiZXN0Q1YgPSB3aGljaC5taW4oY29zdENWKQ0KIyBtYW51YWxseSBzZWxlY3QgdGhlIDh0aCBhcyB0aGUgYmVzdCBtb2RlbA0KYmVzdENWID0gOA0KIyAjIHNob3cgdGhlIGNvZWZmaWNpZW50cyBvYnRhaW5lZCBmb3IgdGhlIGJlc3QgbW9kZWwgKGFkZGVkIGluIHRoZSBmaW5hbCB0YWJsZSkNCiMgcHJpbnQodmFyX3NlbGVjW1tiZXN0Q1ZdXSkNCg0KcGFyKG1mcm93ID1jKDEsMSkpDQojIHBsb3RtZWFuIEN2IGVycm9yDQpwbG90KGFscCwgY29zdENWLCBtYWluPSJNZWFuIDEwLWZvbGRzIENWIGVycm9yIGZvciBlYWNoIGFscGhhIGxldmVsIiwNCiAgICAgeGxhYiA9ICJhbHBoYSBwcm9wb3J0aW9uIiwgeWxhYiA9ICJtZWFuIENWIGVycm9yIiwgdHlwZSA9ICJiIiwgY29sID0gImJsdWUiKQ0KYXhpcyhzaWRlID0gMSwgYXQgPSBjKGFscCkpDQoNCiMgcGxvdCBudW1iZXIgb2Ygbm9uIHplcm8gY29lZmZpY2llbnRzDQpwbG90KGFscCwgbnpjb2VmWywxXSwgbWFpbj0iTnVtYmVyIG9mIG5vbi16ZXJvIGNvZWZmaWNpZW50cyBmb3IgZWFjaCBhbHBoYSBsZXZlbCIsDQogICAgIHhsYWIgPSAiYWxwaGEgcHJvcG9ydGlvbiIsIHlsYWIgPSAiTm9uLXplcm8gY29lZmZpY2llbnRzIiwgdHlwZSA9ICJiIiwgY29sID0gImJsdWUiLA0KICAgICB5bGltID0gYyhtaW4obnpjb2VmKSwgbWF4KG56Y29lZikpKQ0KbGluZXMoYWxwLCBuemNvZWZbLDJdLCBjb2wgPSAicmVkIiwgdHlwZSA9ICJiIikNCmxlZ2VuZCgndG9wcmlnaHQnLCBjKCJsYW1iZGEgbWluIiwgImxhbWJkYSAxU0UiKSwgbHR5ID0gMSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgY2V4ID0gMC43LGx3ZD0yKQ0KYXhpcyhzaWRlID0gMSwgYXQgPSBjKGFscCkpDQoNCiMgZXh0cmFjdCBzaWduaWZpY2FudCB2YXJpYWJsZXMgYW5kIHNhdmUgaW4gYSBkYXRhZnJhbWUNCmNvZWZmLmRmJCJFTkVUKFNMVFMpIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KaUVORVRTTFRTID0gcm93Lm5hbWVzKGNvZWZmLmRmKSAlaW4lIHZhcl9zZWxlY1tbYmVzdENWXV0kbmFtZQ0KY29lZmYuZGYkIkVORVQoU0xUUykiW2lFTkVUU0xUU10gPSBzcHJpbnRmKCIlLjJlIiwgYXMubWF0cml4KHZhcl9zZWxlY1tbYmVzdENWXV0kY29lZmZpY2llbnQpKQ0KDQpgYGANCg0KSW4gdGhpcyBjYXNlLCBpbnN0ZWFkIG9mIHRoZSBhY3R1YWwgbWluaW11bSBvYnRhaW5lZCBieSBFTkVUIHdoaWNoDQpjb3JyZXNwb25kZWQgdG8gYSBMYXNzbyBmaXQsIHRoZSBiZXN0IGxhYmRhIGtlcHQgY29ycmVzcG9uZHMgdG8gYWxwaGE9MC43DQooYXQgdGhlIGNvc3Qgb2YgYSBzbGlnaHRseSBoaWdoZXIgQ1YgZXJyb3IpLg0KVGhlIGhpZ2hlc3QgY29lZmZpY2llbnQgZm9yIHRoaXMgbW9kZWwgcmVzdWx0cyB0aGUgdW5lbXBsb3ltZW50IHJhdGUuDQoNClRvIHBlcmZvcm0gYSBmdXJ0aGVyIHZhbGlkYXRpb24gb2YgdGhlc2UgcmVzdWx0cywgT0xTIGlzIGFwcGxpZWQgb24gdGhlIG1vZGVsIGlkZW50aWZpZWQgDQooZGlzY2FyZGluZyB0aGUgdW5pdHMgaWRlbnRpZmllZCBieSBzcGFyc2UtTFRTKQ0KYGBge3IgRU5FVChTTFRTKStPTFN9DQoNCiMgQUREIE9MUw0KaW5kQ29lZiA9IG5hbWVzKG1vZCkgJWluJSB2YXJfc2VsZWNbW2Jlc3RDVl1dJG5hbWUNCm1vZEVORVRzbHRzT0xTID0gY2JpbmQuZGF0YS5mcmFtZSh5ID0geVtub25PdXRTTFRTXSwgbW9kW25vbk91dFNMVFMsaW5kQ29lZl0pIA0KRU5FVF9TTFRTX29scyA9IGxtKHkgfiAuLCB5PVQsIGRhdGEgPSBtb2RFTkVUc2x0c09MUykNCnN1bW1hcnkoRU5FVF9TTFRTX29scykNCg0KIyBkaWFnbm9zdGljIHBsb3RzDQojIGxheW91dChtYXRyaXgoMTo2LCBuciA9IDIpLCBoZWlnaHQgPSBjKDIsIDEpKQ0KUmVncmVzc2lvblBsb3RzKEVORVRfU0xUU19vbHMsIHRvd25zW25vbk91dFNMVFNdLCByb2J1c3QgPSBGLCBvdXQ9ZGNpdHlbbm9uT3V0U0xUU10pDQoNCiMgIyBleHRyYWN0IHZhcmlhYmxlcyBzaWduaWZpY2FudCBhdCAwLjAxJSBhbmQgc2F2ZSB0aGVtIGluIGEgZGF0YSBmcmFtZQ0KIyBjb2VmZi5kZiQiRU5FVChTTFRTKStPTFMiID0gYXMuY2hhcmFjdGVyKHJlcCgiLS0iLCBucm93KGNvZWZmLmRmKSkpDQojIGNvZWZmLmRmJCJFTkVUKFNMVFMpK09MUyJbaUVORVRTTFRTXSA9IGFzLm1hdHJpeChzdW1tYXJ5KEVORVRfU0xUU19vbHMpW1siY29lZmZpY2llbnRzIl1dWywiUHIoPnx0fCkiXTwwLjAxKQ0KIyBjb2VmZi5kZiQiRU5FVChTTFRTKStPTFMiW2NvZWZmLmRmJCJFTkVUK1NMVFMrT0xTIj09VFJVRV0gPSBzcHJpbnRmKCIlLjJlIiwNCiMgICAgICAgICAgICBhcy5tYXRyaXgoRU5FVF9TTFRTX29scyRjb2VmZmljaWVudHNbc3VtbWFyeShFTkVUX1NMVFNfb2xzKQ0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbWyJjb2VmZmljaWVudHMiXV1bLCJQcig+fHR8KSJdPDAuMDFdKSwgNCkNCiMgY29lZmYuZGYkIkVORVQoU0xUUykrT0xTIltjb2VmZi5kZiQiRU5FVCtTTFRTK09MUyI9PUZBTFNFXSA9ICItLSINCg0KYGBgDQoNClRoZXkgc2hvdyB0aGF0Og0KDQoqIHRoZSBtb2RlbCBzZWVtcyBjb3JyZWN0bHkgc3BlY2lmaWVkIGFuZCBhbGwgcmVncmVzc29ycyBhcmUgc2lnbmlmaWNhbnQNCiogUGFyaXMgaXMgbm90IGNvbnNpZGVyZWQgYXMgb24gb3V0bGllciAoaXRzIGxldmVyYWdlIGlzIHN0cm9uZ2x5IGRlY3JlYXNlZCkNCiogdGhlIHVuZW1wbG95bWVudCByYXRlIGhhcyBhIHN0cm9uZyBlZmZlY3Qgb24gdGhlIHJlc3BvbnNlDQoNCg0KIyMjIyBQZW5hbGl6ZWQgRU5FVCBiYXNlZCBvbiB0aGUgUyBlc3RpbWF0b3INCg0KUGVuYWxpemVkIEVORVQgYmFzZWQgb24gdGhlIFMgZXN0aW1hdG9yIChQRU5TRSkgaXMgbm93IGFwcGxpZWQuDQpUaGUgUEVOU0UgZXN0aW1hdGUgbWluaW1pemVzIHRoZSByb2J1c3QgTS1zY2FsZSBvZiB0aGUgcmVzaWR1YWxzIHBlbmFsaXplZCBieSB0aGUgTDEgYW5kIEwyDQpub3JtIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAoZWxhc3RpYyBuZXQgcGVuYWx0eSkuIFRoZSBsZXZlbCBvZiBwZW5hbGl6YXRpb24gaXMgY2hvc2VuIHRvDQptaW5pbWl6ZSB0aGUgay1mb2xkIGNyb3NzLXZhbGlkYXRlZCBwcmVkaWN0aW9uIGVycm9yICh1c2luZyBhIHJvYnVzdCBtZWFzdXJlKS4NCmBgYHtyIEVORVRTfQ0KDQojIGluc3RhbGwucGFja2FnZXMoInBlbnNlIikNCmxpYnJhcnkoInBlbnNlIikNCg0KYWxwID0gc2VxKDAsIDEsbGVuZ3RoLm91dCA9IDUpDQppID0gMQ0Kc29sX0VORVRTID0gbGlzdCgpDQoNCmZvciAoaiBpbiBhbHApew0KICBjbCA9IG1ha2VDbHVzdGVyKGRldGVjdENvcmVzKCkpDQogIHNvbF9FTkVUU1tbaV1dID0gcGVuc2UoeFssMTpuY29sKHgpLTFdLCB5LCBhbHBoYSA9IGosIG5sYW1iZGEgPSAxMCwgY3ZfayA9IDIsIGNsID0gY2wpDQogIHN0b3BDbHVzdGVyKGNsKQ0KICBpID0gaSArIDENCn0NCg0KIyBwZW5zZW0oeCwgYWxwaGEsIHNjYWxlLCBubGFtYmRhID0gNTAsIGxhbWJkYSwNCiMgbGFtYmRhX21pbl9yYXRpbywgc3RhbmRhcmRpemUsIGN2X2sgPSA1LCBjdl9vYmplY3RpdmUsDQojIG5jb3JlcyA9IGdldE9wdGlvbigibWMuY29yZXMiLCAxTCksIGNsID0gTlVMTCwNCiMgbW1fb3B0aW9ucyA9IG1zdGVwX29wdGlvbnMoKSwgZW5fb3B0aW9ucywgeF90cmFpbiwgeV90cmFpbiwgLi4uKQ0KDQoNCmBgYA0KDQpUaGlzIG1vZGVscyBzaG93IHRoYXQ6DQoqDQoqDQoNClRoZSBtYWluIGRyYXdiYWNrIGl0J3MgdGhlIGNvbXB1dGluZyB0aW1lIHJlcXVpcmVkLg0KDQoNCiMjIyMgRWxhc3RpYyBuZXQgd2l0aCBMVFMgY29zdCBmdW5jdGlvbg0KDQpSb2J1c3QgZWxhc3RpYyBuZXQsIGJhc2VkIG9uIExUUywgaXMgbm93IGZpdHRlZCB0byB0aGUgbW9kZWwgdHJpbW1pbmcgMTAlIG9mIHRoZSB1bml0cy4gDQpJdCByZXF1aXJlcyB0aGUgcmVtb3ZhbCBvZiB0aGUgZHVtbXkgdmFyaWFibGUgZm9yIHVyYmFuIHRvd25zLCANCmluIG9yZGVyIHRvIGF2b2lkIG51bWVyaWNhbCBwcm9ibGVtcy4NCmBgYHtyIGVuZXRMVFN9DQoNCiMgLCBlY2hvPUZBTFNFLCBjYWNoZT1GQUxTRQ0KDQojIHNsb3dlciB2ZXJzaW9uDQpzb2xfZW5ldExUUyA9IGVuZXRMVFMoeFssMTpuY29sKHgpLTFdLCB5LCBmYW1pbHk9ImdhdXNzaWFuIiwgYWxwaGFzPXNlcSgwLCAxLCAwLjIpLCBoc2l6ZT0wLjksDQogICAgICAgICAgICAgICAgICAgICAgbmZvbGQ9MTAsIHBhcmE9RkFMU0UsIHJlcGwgPSAxMCwgZGVsPTAuMDUpICMgcGFyYWxsZWwgZG9lcyBub3Qgd29yayBvbiB3aW5kb3dzDQojIGZhc3RlciB2ZXJzaW9uDQojIHNvbF9lbmV0TFRTID0gZW5ldExUUyh4WywxOm5jb2woeCktMV0sIHksIGZhbWlseT0iZ2F1c3NpYW4iLCBhbHBoYXM9c2VxKDAsIDEsIDAuMiksIGhzaXplPTAuOSwgDQojICAgICAgICAgICAgICAgICAgICAgICBuZm9sZD0xMCwgcGFyYT1UUlVFLCByZXBsID0gNSwgZGVsPTAuMDUpICMgcGFyYWxsZWwgZG9lcyBub3Qgd29yayBvbiB3aW5kb3dzDQoNCnByaW50KHNvbF9lbmV0TFRTKQ0KDQojIHNhdmUgb3JpZ2luYWwgcmVzcG9uc2UgKHVzZWQgZm9yIGRpYWdub3N0aWMgcGxvdHMpDQpzb2xfZW5ldExUUyR5ID0geQ0KIyBjb25zdHJ1Y3QgbWFoYWxhbm9iaXMgZGlzdGFuY2UgKHVzZWQgZm9yIGRpYWdub3N0aWMgcGxvdHMpDQpzb2xfZW5ldExUUyRNRCA9IGhhdHZhbHVlcyhvbHMpICMgY292TWNkKHgpJG1haCAjIGNvbXB1dGUgc3RhbmRhcmQgbGV2ZXJhZ2VzDQojIGNvbnN0cnVjdCByb2J1c3Qgc2NhbGUgKHVzZWQgZm9yIGRpYWdub3N0aWMgcGxvdHMpDQpzb2xfZW5ldExUUyRzY2FsZSA9IHNkKHNvbF9lbmV0TFRTJHJlc2lkdWFsc1thcy5sb2dpY2FsKHNvbF9lbmV0TFRTJHd0KV0pDQojIGludGVyYWN0aXZlIHBsb3RzDQpSZWdyZXNzaW9uUGxvdHMoc29sX2VuZXRMVFMsIHRleHRsYWJlbHM9dG93bnMsIHJvYnVzdCA9IFRSVUUsIG91dCA9IGFzLmZhY3Rvcihzb2xfZW5ldExUUyR3dCkpW1sxXV0NClJlZ3Jlc3Npb25QbG90cyhzb2xfZW5ldExUUywgdGV4dGxhYmVscz10b3ducywgcm9idXN0ID0gVFJVRSwgb3V0ID0gYXMuZmFjdG9yKHNvbF9lbmV0TFRTJHd0KSlbWzJdXQ0KUmVncmVzc2lvblBsb3RzKHNvbF9lbmV0TFRTLCB0ZXh0bGFiZWxzPXRvd25zLCByb2J1c3QgPSBUUlVFLCBvdXQgPSBhcy5mYWN0b3Ioc29sX2VuZXRMVFMkd3QpKVtbM11dDQoNCiMgYnVpbHQtaW4gcGxvdHMNCnBsb3RDb2VmLmVuZXRMVFMoc29sX2VuZXRMVFMsIGNleCA9IDUpDQpwbG90RGlhZ25vc3RpYy5lbmV0TFRTKHNvbF9lbmV0TFRTLCBjZXggPSAzKQ0KcGxvdFJlc2lkLmVuZXRMVFMoc29sX2VuZXRMVFMsIGNleCA9IDMpDQojIHBsb3Qoc29sX2VuZXRMVFMsIG1ldGhvZD1jKCJjb2VmZmljaWVudHMiLCJyZXNpZCIsImRpYWdub3N0aWMiKSwgdmVycz1jKCJyZXdlaWdodGVkIiwicmF3IiksLi4uKQ0KDQojIHNhdmUgb3V0bGllcnMgaW5kZXggDQpvdXRsRU5FVExUUyA9IGFzLmxvZ2ljYWwoc29sX2VuZXRMVFMkd3QpDQoNCiMgc2F2ZSBjb2VmZmljaWVudHMgaW4gYSBkYXRhZnJhbWUNCmlFTkVUTFRTID0gYyhUUlVFLCBzb2xfZW5ldExUUyRjb2VmZmljaWVudHMgIT0gMCwgRkFMU0UpICMgbWFudWFsbHkgaW5jbHVkZSBpbnRlcmNlcHQgYW5kIGR1bW15IGZvciBVcmJhbg0KY29lZmYuZGYkIkVORVRMVFMiID0gYXMuY2hhcmFjdGVyKHJlcCgiLS0iLCBucm93KGNvZWZmLmRmKSkpDQpzb2xfZW5ldExUUyRjb2VmZmljaWVudHNXaXRoSW50ID0gYygiKEludGVyY2VwdCkiID0gc29sX2VuZXRMVFMkYTAsIHNvbF9lbmV0TFRTJGNvZWZmaWNpZW50cykNCmNvZWZmLmRmJCJFTkVUTFRTIltpRU5FVExUU10gPSBzcHJpbnRmKCIlLjJlIixzb2xfZW5ldExUUyRjb2VmZmljaWVudHNXaXRoSW50W2lFTkVUTFRTXSwgNCkNCg0KIyMgcGVyZm9ybSBDViBmb3IgdXNlci12YWx1ZXMNCiMgDQojIENhbGN1bGF0ZSBsYW1iZGEgcGF0aCAoZmlyc3QgZ2V0IGxhbWJkYV9tYXgpOg0KIyBsYW1iZGFfbWF4IDwtIDEwICMgbWF4KGFicyhjb2xTdW1zKHN4KnN5KSkpL24NCiMgZXBzaWxvbiA8LSAuMDAwMQ0KIyBLIDwtIDEwDQojIGxhbWJkYXBhdGggPC0gcm91bmQoZXhwKHNlcShsb2cobGFtYmRhX21heCksIGxvZyhsYW1iZGFfbWF4KmVwc2lsb24pLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IEspKSwgZGlnaXRzID0gMTApDQojIHNvbF9jdiA9IGN2LmVuZXRMVFMoaW5kZXg9TlVMTCx4LHksZmFtaWx5PSJnYXVzc2lhbiIscm91bmQobGVuZ3RoKHkpKjAuOTApLA0KIyAgICAgICAgICAgIHNlcSgwLCAxLCAwLjIpLGxhbWJkYXMgPSBsYW1iZGFwYXRoICxuY29yZXM9MSxyZXBsPTUsbmZvbGQ9MTAscGxvdD1UUlVFKQ0KIyBzb2xfY3YNCg0KIyBPTFMNCm1vZEVORVRsdHNPTFMgPSBkYXRhLmZyYW1lKG1vZFtvdXRsRU5FVExUUyxpRU5FVExUU10pDQpFTkVUbHRzX29scyA9IGxtKHkgfiAuLCB5PVQsIGRhdGEgPSBtb2RFTkVUbHRzT0xTLCBzdWJzZXQgPSBzb2xfZW5ldExUUyR3dCkNCnN1bW1hcnkoRU5FVGx0c19vbHMpDQoNCiMgZGlhZ25vc3RpYyBwbG90cw0KIyBsYXlvdXQobWF0cml4KDE6NiwgbnIgPSAyKSwgaGVpZ2h0ID0gYygyLCAxKSkNClJlZ3Jlc3Npb25QbG90cyhFTkVUbHRzX29scywgdG93bnNbb3V0bEVORVRMVFNdLCByb2J1c3QgPSBGLCBvdXQ9b3V0bEVORVRMVFNbb3V0bEVORVRMVFM9PTFdKQ0KDQojIGV4dHJhY3QgdmFyaWFibGVzIHNpZ25pZmljYW50IGF0IDAuMDElIGFuZCBzYXZlIHRoZW0gaW4gYSBkYXRhIGZyYW1lDQogICMgY29lZmYuZGYkIkVORVRMVFMrT0xTIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KICAjIGNvZWZmLmRmJCJFTkVUTFRTK09MUyJbaUVORVRMVFNdID0gYXMubWF0cml4KHN1bW1hcnkoRU5FVGx0c19vbHMpW1siY29lZmZpY2llbnRzIl1dWywiUHIoPnx0fCkiXTwwLjAxKQ0KICAjIGNvZWZmLmRmW2NvZWZmLmRmPT1UUlVFXSA9IHNwcmludGYoIiUuMmUiLA0KICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubWF0cml4KEVORVRsdHNfb2xzJGNvZWZmaWNpZW50c1tzdW1tYXJ5KEVORVRsdHNfb2xzKVtbImNvZWZmaWNpZW50cyJdXVssIlByKD58dHwpIl08MC4wMV0pLCA0KQ0KICAjIGNvZWZmLmRmW2NvZWZmLmRmPT1GQUxTRV0gPSAiLS0iDQoNCmBgYA0KDQpUaGlzIG1vZGVsIHNob3dzIHRoYXQ6DQoNCiogdGhlIGFsZ29yaXRobSAoZXZlbiBpZiBub3QgcGFyYWxsZWxpemVkIGZvciBXaW5kb3dzIHVzZXJzKSBpcyBtdWNoIGZhc3RlciB0aGFuIFBFTlNFDQoqIC4uLg0KDQoNCiMjIyMgDQoNCmBgYHtyIEJTUyB3ZWlnaHRlZH0NCg0KIyBiZXN0IHN1YnNldCBzZWxlY3Rpb24NCmJlc3Quc3ViID0gcmVnc3Vic2V0cyh5IH4gLiwgZGF0YSA9IG1vZEVORVRsdHNPTFMsIG52bWF4ID0gbmNvbChtb2QpLCBtZXRob2Q9ImV4aGF1c3RpdmUiKSAjIGV4aGF1c3RpdmUgIyBzZXFyZXAgIyByZWFsbHkuYmlnPVRSVUUsIA0KYmVzdC5zdWIuc3VtbWFyeSA9IHN1bW1hcnkoYmVzdC5zdWIpDQojIG1hbnVhbCBwbG90dGluZw0KcGFyKG1mcm93ID1jKDIsMikpDQojIHJzcQ0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JHJzcSAsIHhsYWI9Ik51bWJlciBvZiBWYXJpYWJsZXMiLCB5bGFiPSJSc3EiLCB0eXBlPSJsIikNCmluZF9Sc3EgPSB3aGljaC5tYXgoYmVzdC5zdWIuc3VtbWFyeSRyc3EpDQpwb2ludHMoaW5kX1JzcSwgYmVzdC5zdWIuc3VtbWFyeSRhZGpyMltpbmRfUnNxXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCiMgYWRqUnNxDQpwbG90KGJlc3Quc3ViLnN1bW1hcnkkYWRqcjIgLHhsYWI9Ik51bWJlciBvZiBWYXJpYWJsZXMiLCB5bGFiPSJBZGp1c3RlZCBSU3EiLCB0eXBlPSJsIikNCmluZF9hZGpSc3EgPSB3aGljaC5tYXgoYmVzdC5zdWIuc3VtbWFyeSRhZGpyMikNCnBvaW50cyhpbmRfYWRqUnNxLCBiZXN0LnN1Yi5zdW1tYXJ5JGFkanIyW2luZF9hZGpSc3FdLCBjb2wgPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KIyBDcA0KcGxvdChiZXN0LnN1Yi5zdW1tYXJ5JGNwICx4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iQ3AiLCB0eXBlPSJsIikNCmluZF9DcCA9IHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGNwKQ0KcG9pbnRzKGluZF9DcCwgYmVzdC5zdWIuc3VtbWFyeSRjcFtpbmRfYWRqUnNxXSwgY29sID0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCiMgYmljDQpwbG90KGJlc3Quc3ViLnN1bW1hcnkkYmljICx4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYj0iYmljIiwgdHlwZT0ibCIpDQppbmRfYmljID0gd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkYmljKQ0KcG9pbnRzKGluZF9iaWMsIGJlc3Quc3ViLnN1bW1hcnkkYmljW2luZF9iaWNdLCBjb2wgPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KbXRleHQoIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiBmb3IgZGVsdGEgZ2VuZGVyIHNhbGFyeSIsIG91dGVyPVRSVUUsICBjZXg9MS4yLCBsaW5lPS0xLjUpDQoNCiMgYnVpbHQtaW4gcGxvdHMNCnBhcihtZnJvdz1jKDEsMSkpDQpwbG90LnJlZ3N1YnNldHMyKGJlc3Quc3ViLCBzY2FsZSA9ICJyMiIsIGNleC5heGlzID0gMC42LCBtYWluID0gIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiB1c2luZyBSIHNxdWFyZWQiKQ0KcGxvdC5yZWdzdWJzZXRzMihiZXN0LnN1Yiwgc2NhbGUgPSAiYWRqcjIiLCBjZXguYXhpcyA9IDAuNiwgbWFpbiA9ICJCZXN0IHN1YnNldCBzZWxlY3Rpb24gdXNpbmcgYWRqdXN0ZWQgUiBzcXVhcmVkIikNCnBsb3QucmVnc3Vic2V0czIoYmVzdC5zdWIsIHNjYWxlID0gIkNwIiwgY2V4LmF4aXMgPSAwLjYsIG1haW4gPSAiQmVzdCBzdWJzZXQgc2VsZWN0aW9uIHVzaW5nIE1hbGxvd3MnIENwIikNCnBsb3QucmVnc3Vic2V0czIoYmVzdC5zdWIsIHNjYWxlID0gImJpYyIsIGNleC5heGlzID0gMC42LCBtYWluID0gIkJlc3Qgc3Vic2V0IHNlbGVjdGlvbiB1c2luZyBCSUMiKQ0KDQojIG10ZXh0KCJCZXN0IHN1YnNldCBzZWxlY3Rpb24gZm9yIHNhbGFyeSAxOC0yNSB1c2luZyBCSUMiLCBvdXRlcj1UUlVFLCAgY2V4PTEuNCwgbGluZT0tMy41KQ0KDQojIHJldHJpZXZlIHRoZSBtb2RlbCB3aXRoIG1pbiBCSUMNCmNvZWZmaWNpZW50cyhiZXN0LnN1Yiwgd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkYmljKSkNCm5ubiA9IG5hbWVzKGNvZWZmaWNpZW50cyhiZXN0LnN1Yiwgd2hpY2gubWluKGJlc3Quc3ViLnN1bW1hcnkkYmljKSkpDQoNCiMgYXBwbHkgT0xTDQptb2RfYmljID0gbW9kW21tJHJ3ZWlnaHRzPnRyaW0sIGNvbG5hbWVzKG1vZCkgJWluJSBubm5dIA0KbW9kX2JpYyA9IGNiaW5kLmRhdGEuZnJhbWUoeSA9IHlbbW0kcndlaWdodHM+dHJpbV0sIG1vZF9iaWMpDQpvbHNfYmljID0gbG0oeSB+IC4sIGRhdGEgPSBtb2RfYmljLCB5PVQpDQpzdW1tYXJ5KG9sc19iaWMpDQpSZWdyZXNzaW9uUGxvdHMob2xzX2JpYywgdGV4dGxhYmVscyA9IHRvd25zW21tJHJ3ZWlnaHRzPnRyaW1dLCBvdXQgPSBkY2l0eVttbSRyd2VpZ2h0cz50cmltXSkNCg0KIyBleHRyYWN0IHZhcmlhYmxlcyBzaWduaWZpY2FudCBhdCAwLjAxJSBhbmQgc2F2ZSB0aGVtIGluIGEgZGF0YSBmcmFtZQ0KaUJTUyA9IHJvdy5uYW1lcyhjb2VmZi5kZikgJWluJSBubm4NCmNvZWZmLmRmJCJCU1MoU0VORVQpIiA9IGFzLmNoYXJhY3RlcihyZXAoIi0tIiwgbnJvdyhjb2VmZi5kZikpKQ0KY29lZmYuZGYkIkJTUyhTRU5FVCkiW2lCU1NdID0gc3ByaW50ZigiJS4yZSIsIGFzLm1hdHJpeChjb2VmZmljaWVudHMoYmVzdC5zdWIsIHdoaWNoLm1pbihiZXN0LnN1Yi5zdW1tYXJ5JGJpYykpKSkNCg0KYGBgDQoNCg0KIyMjIENvbXBhcmlzb24gb2YgdGhlIGVzdGltYXRlcyBvYnRhaW5lZA0KDQpQcmludCB0YWJsZSBvZiByZXN1bHRzDQpgYGB7cn0NCiMgIHByaW50IHJlc3VsdHMgaW4gcGRmLCByZXN1bHRzPSdhc2lzJywgcmVuZGVyPW5vcm1hbF9wcmludA0KDQoNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KcGRmKCJyZXN1bHRzIHNwYXJzZSBtb2RlbHMucGRmIiwgaGVpZ2h0PTExLCB3aWR0aD0xMikNCmdyaWQudGFibGUoY29lZmYuZGZbMToyMyxdKQ0KZ3JpZC5uZXdwYWdlKCkNCmdyaWQudGFibGUoY29lZmYuZGZbMjQ6bnJvdyhjb2VmZi5kZiksXSkNCmRldi5vZmYoKQ0KDQpgYGANCg0KVGhpcyBmdW5jdGlvbiBpcyB1c2VkIHRvIHBsb3QgYSBzdW1tYXJ5IG9mIHRoZSByZXN1bHRzIG9idGFpbmVkIGluIEhUTUwgZm9ybWF0DQpgYGB7ciBpbnRlcmFjdGl2ZSByZXN1bHRzIGZvciBMTSwgZWNobyA9IEZBTFNFfQ0KDQojIyBQUk9DRURVUkUgRk9SIFdJTkRPV1MNCiMjIEluc3RhbGwgNjRiaXQgamF2YSBhbmQgc2V0IGl0cyBQYXRoIGluIGVudmlyb25tZW50YWwgdmFyaWFibGVzLCB0aGVuDQojIGluc3RhbGwucGFja2FnZXMoJ3JKYXZhJywgLmxpYlBhdGhzKClbMV0sICdodHRwOi8vd3d3LnJmb3JnZS5uZXQvJykNCiMgb3B0aW9ucyhqYXZhLmhvbWU9IkM6L1Byb2dyYW0gRmlsZXMvSmF2YS9qcmUxLjguMF8xNzEiKSANCiMgU3lzLnNldGVudihKQVZBX0hPTUU9J0M6L1Byb2dyYW0gRmlsZXMvSmF2YS9qcmUxLjguMF8xNzEnKSAjIGZvciA2NC1iaXQgdmVyc2lvbiANCiMjIEluc3RhbGwgbW9yZSBwYWNrYWdlcw0KIyBpbnN0YWxsLnBhY2thZ2VzKCJSZXBvcnRlUnMiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJzaGlueSIpDQojIGluc3RhbGwucGFja2FnZXMoInJzY29ubmVjdCIpDQoNCiMgV0FSTklORzogaW4gYSBub3RlYm9vayB0aGUgaW50ZXJhY3Rpb24gaXMgZGlzcGF5ZWQganVzdCBhdCB0aGUgZW5kDQojIEJFVFRFUiBUTyBSVU4gSVQgSU4gQSBOT1JNQUwgU0NSSVBUDQoNCiMgTG9hZCBwYWNrYWdlcw0KbGlicmFyeSgickphdmEiKQ0KbGlicmFyeSgic2hpbnkiKQ0KbGlicmFyeSgiUmVwb3J0ZVJzIikNCmxpYnJhcnkoInJzY29ubmVjdCIpDQoNCiMgc3RvcmUgYSBkYXRhZnJhbWUgd2l0aCBYIGluIHBsYWNlIG9mIHNpZ25pZmljYW50IGNvZWZmaWNpZW50cw0KbXljb2VmZi5kZiA9IGNvZWZmLmRmDQpteWNvZWZmLmRmW215Y29lZmYuZGYhPSItLSJdID0gIlgiDQoNCiMgdWkNCnVpIDwtIGZsdWlkUGFnZSgNCiAgdGFncyRoMSgiTGluZWFyIG1vZGVscycgcmVzdWx0cyBmb3IgZ2VuZGVyJ3MgZ2FwIGluIHNhbGFyeSIpLA0KICB0YWJsZU91dHB1dChvdXRwdXRJZCA9ICJ0YWJsZSIpLA0KICBicigpLA0KICB2ZXJiYXRpbVRleHRPdXRwdXQob3V0cHV0SWQgPSAib3V0IikNCikNCg0KIyBzZXJ2ZXINCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIG91dHB1dCR0YWJsZSA8LSByZW5kZXJGbGV4VGFibGUoew0KICAgICMgQ3JlYXRlIGNoZWNrYm94ZXMgICANCiAgICBteWNvZWZmLmRmJFZhcmlhYmxlIDwtIHBhc3RlMCgnPGxhYmVsPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImNhcicsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcV9hbG9uZyhyb3duYW1lcyhjb2VmZi5kZikpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIj4gPHNwYW4+Jywgcm93bmFtZXMoY29lZmYuZGYpLCAnPC9zcGFuPjwvbGFiZWw+JykNCiAgICBteWNvZWZmLmRmIDwtIG15Y29lZmYuZGZbYygiVmFyaWFibGUiLCBuYW1lcyhjb2VmZi5kZikpXSAjIFB1dCBjb2wgJ05hbWUnIGluIHRoZSBmaXJzdCBwbGFjZQ0KICAgICMgbXljb2VmZi5kZiA9IGNvZWZmLmRmWywxOm5jb2woY29lZmYuZGYpLTFdDQogICAgZnQgPC0gdmFuaWxsYS50YWJsZShteWNvZWZmLmRmKSAjIGNvbnZlcnQgdG8gRmxleFRhYmxlIG9iamVjdA0KICAgIGZ0WywgIlZhcmlhYmxlIiwgdG8gPSAiaGVhZGVyIl0gPC0gcGFyTGVmdCgpICMgbGVmdCBhbGlnbiBjaGVja2JveGVzDQogICAgZnRbLCAiVmFyaWFibGUiXSA8LSBwYXJMZWZ0KCkgIyBsZWZ0IGFsaWduIGhlYWRlcg0KICAgIHJldHVybihmdCkNCiAgfSkNCiAgIyB0aGUgaW5wdXRzIGNyZWF0ZWQgYXJlIGluIGlucHV0JGNhcjEsIGlucHV0JGNhcjIsIC4uLg0KICBvdXRwdXQkb3V0IDwtIHJlbmRlclByaW50KHsNCiAgICAjIHJlc3VsdHMNCiAgICByZXMgPC0gdW5saXN0KGxhcHBseSgxOm5yb3cobXljb2VmZi5kZiksIGZ1bmN0aW9uKGkpIGlucHV0W1twYXN0ZTAoImNhciIsIGkpXV0pKQ0KICAgICMgcHJpbnQocmVzKQ0KICAgIGlmIChhbnkocmVzKSkgew0KICAgICAgbXljb2VmZi5kZltteWNvZWZmLmRmIT0iLS0iXSA9ICJYIg0KICAgICAgcmVzMiA9IGFzLmRhdGEuZnJhbWUoY29lZmYuZGZbcmVzLF0pICMgcm93bmFtZXMobXljb2VmZi5kZilbcmVzXSkNCiAgICAgIHByaW50KHJlczIpDQogICAgfQ0KICB9KQ0KfQ0KDQojIGxhdW5jaCBhcHANCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCg0KYGBgDQo=