packages <- c('readxl','dplyr','ggplot2')
missing_packages <- setdiff(packages, rownames(installed.packages()))
if (length(missing_packages)) install.packages(missing_packages)
library(readxl)
library(dplyr)
library(ggplot2)
index <- read_excel('C:/Users/noahs/Desktop/Ch1/SJV/Index.xlsx')
# Keep tract IDs as 11-character FIPS codes
# because Excel may store these as numbers
index <- read_excel('C:/Users/noahs/Desktop/Ch1/SJV/Index.xlsx')
index$tract <- sprintf('%011.0f', as.numeric(index$tract))
themes <- list(
LU = c('ImpairedWaterBodies','SmATS','avg_impervious','tree_canopy'),
HV = c('asthma','Low_Birth_Weight','Cardiovascular'),
HE = c('ozone','PM2_5','Diesel_PM','Pesticides','traffic','LST_Max','LST_Mean'),
SV = c('Linguistic_Isol','Poverty','HousBurd','children','elderly',
'pop_density','Total_outdoor','perc_no_vehicle_access','energy_burden')
)
# Combine all indicators into one list
vars <- unlist(themes, use.names = FALSE)
# Check for missing indicator columns
missing_vars <- setdiff(vars, names(index))
if (length(missing_vars) > 0) {
stop(paste("Missing columns:",
paste(missing_vars, collapse = ", ")))
}
# Convert indicators to numeric values
for (v in vars) {
index[[v]] <- as.numeric(index[[v]])
}
# Check for missing values after conversion
colSums(is.na(index[vars]))
ImpairedWaterBodies SmATS avg_impervious tree_canopy asthma Low_Birth_Weight Cardiovascular ozone PM2_5
0 0 0 0 0 1 0 0 0
Diesel_PM Pesticides traffic LST_Max LST_Mean Linguistic_Isol Poverty HousBurd children
0 0 1 0 1 1 1 1 1
elderly pop_density Total_outdoor perc_no_vehicle_access energy_burden
0 0 0 7 0
# Check missing values for all indicators
missing_data <- colSums(is.na(index[vars]))
print(missing_data)
ImpairedWaterBodies SmATS avg_impervious tree_canopy asthma Low_Birth_Weight Cardiovascular ozone PM2_5
0 0 0 0 0 1 0 0 0
Diesel_PM Pesticides traffic LST_Max LST_Mean Linguistic_Isol Poverty HousBurd children
0 0 1 0 1 1 1 1 1
elderly pop_density Total_outdoor perc_no_vehicle_access energy_burden
0 0 0 7 0
# Identify census tracts with missing values
incomplete_tracts <- index[
!complete.cases(index[vars]),
c("tract", vars)
]
print(incomplete_tracts)
# A tibble: 9 × 24
tract ImpairedWaterBodies SmATS avg_impervious tree_canopy asthma Low_Birth_Weight Cardiovascular ozone PM2_5 Diesel_PM Pesticides traffic LST_Max LST_Mean Linguistic_Isol Poverty HousBurd children elderly
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 06077… 6 0.9 47.5 1.43 69.7 8.3 21.5 0.0441 8.65 0.0181 7.55 NA 45.6 42.6 19.5 19.9 14.8 12.4 7.32
2 06077… 9 63.8 1.30 2.10 52.8 6.44 15.1 0.0441 7.67 0.0389 147. 171. 56.1 NA 1.64 16.3 8.75 10.3 19.1
3 06077… 4 153. 30.6 1.98 47.2 6.24 20.5 0.0454 8.47 0.0978 112. 791. 58.0 43.1 10.0 24.9 21.6 17.3 5.69
4 06077… 0 70.0 40.7 3.99 44.8 6 20.1 0.0459 8.66 0.0880 0.140 266. 50.3 43.1 5.31 22.1 16 8.14 11.9
5 06077… 0 120. 54.3 5.41 40.4 5.11 20.1 0.0463 8.64 0.226 2.33 553. 49.7 45.6 5.95 36.0 20.1 11.8 13.8
6 06077… 0 15 60.2 2.45 40.5 7.86 17.7 0.0440 5.54 0.0175 0 125. 49.9 45.9 6.30 12.8 17.4 12.9 13.5
7 06077… 0 15.3 35.5 2.36 49.8 7.02 18.4 0.0443 6.25 0.0492 0.463 235. 52.3 47.2 1.88 24.9 10 7.25 11.2
8 06077… 3 0.2 48.1 4.99 28.1 4.95 14.4 0.0436 8.07 0.0238 32.6 394. 46.7 41.8 2.00 8.12 3.40 9.96 5.96
9 06077… 3 0.2 29.0 0.502 41.5 NA 15.6 0.0453 7.50 0.0597 62.6 18.3 46.9 42.7 NA NA NA NA 13.1
# ℹ 4 more variables: pop_density <dbl>, Total_outdoor <dbl>, perc_no_vehicle_access <dbl>, energy_burden <dbl>
# Min-max normalization
normalize <- function(x) {
min_x <- min(x, na.rm = TRUE)
max_x <- max(x, na.rm = TRUE)
if (max_x == min_x) {
return(rep(NA_real_, length(x)))
}
(x - min_x) / (max_x - min_x)
}
# Create normalized dataset
normalized <- index
# Normalize all 23 indicators
for (v in vars) {
normalized[[v]] <- normalize(index[[v]])
}
# Reverse tree canopy
# Lower canopy = higher vulnerability
normalized$tree_canopy <- 1 - normalized$tree_canopy
# Check normalized values
summary(normalized[vars])
ImpairedWaterBodies SmATS avg_impervious tree_canopy asthma Low_Birth_Weight Cardiovascular ozone PM2_5 Diesel_PM Pesticides
Min. :0.0000 Min. :0.00000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000 Min. :0.0000000
1st Qu.:0.0000 1st Qu.:0.04781 1st Qu.:0.4158 1st Qu.:0.4808 1st Qu.:0.1479 1st Qu.:0.4322 1st Qu.:0.3413 1st Qu.:0.3553 1st Qu.:0.5368 1st Qu.:0.02400 1st Qu.:0.0000000
Median :0.1579 Median :0.12426 Median :0.6373 Median :0.6952 Median :0.2619 Median :0.5491 Median :0.5998 Median :0.4824 Median :0.6564 Median :0.04408 Median :0.0005198
Mean :0.2093 Mean :0.19339 Mean :0.5402 Mean :0.6454 Mean :0.2924 Mean :0.5577 Mean :0.5549 Mean :0.4631 Mean :0.6571 Mean :0.09086 Mean :0.0518297
3rd Qu.:0.3553 3rd Qu.:0.24496 3rd Qu.:0.6973 3rd Qu.:0.8310 3rd Qu.:0.4135 3rd Qu.:0.6847 3rd Qu.:0.7853 3rd Qu.:0.5299 3rd Qu.:0.8104 3rd Qu.:0.08704 3rd Qu.:0.0265091
Max. :1.0000 Max. :1.00000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000 Max. :1.0000000
NA's :1
traffic LST_Max LST_Mean Linguistic_Isol Poverty HousBurd children elderly pop_density Total_outdoor perc_no_vehicle_access
Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000
1st Qu.:0.1365 1st Qu.:0.1667 1st Qu.:0.5493 1st Qu.:0.07489 1st Qu.:0.2059 1st Qu.:0.1891 1st Qu.:0.3056 1st Qu.:0.1362 1st Qu.:0.1705 1st Qu.:0.2337 1st Qu.:0.04503
Median :0.2492 Median :0.2236 Median :0.6896 Median :0.17736 Median :0.3425 Median :0.2892 Median :0.4308 Median :0.2674 Median :0.3327 Median :0.3240 Median :0.08628
Mean :0.2957 Mean :0.2632 Mean :0.6324 Mean :0.21637 Mean :0.3907 Mean :0.3134 Mean :0.4345 Mean :0.2844 Mean :0.3248 Mean :0.3413 Mean :0.13101
3rd Qu.:0.3995 3rd Qu.:0.3308 3rd Qu.:0.7899 3rd Qu.:0.27681 3rd Qu.:0.5332 3rd Qu.:0.4066 3rd Qu.:0.5646 3rd Qu.:0.3818 3rd Qu.:0.4570 3rd Qu.:0.4169 3rd Qu.:0.17308
Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000
NA's :1 NA's :1 NA's :1 NA's :1 NA's :1 NA's :1 NA's :7
energy_burden
Min. :0.0000
1st Qu.:0.3333
Median :0.5000
Mean :0.4751
3rd Qu.:0.5000
Max. :1.0000
# Land Use & Environmental Features
normalized$LU <- rowMeans(
normalized[themes$LU],
na.rm = FALSE
)
# Health Vulnerability
normalized$HV <- rowMeans(
normalized[themes$HV],
na.rm = FALSE
)
# Heat & Environmental Exposure
normalized$HE <- rowMeans(
normalized[themes$HE],
na.rm = FALSE
)
# Socioeconomic Vulnerability
normalized$SV <- rowMeans(
normalized[themes$SV],
na.rm = FALSE
)
# Calculate Opportunity Index
normalized$Opportunity_Index <- rowMeans(
normalized[c("LU", "HV", "HE", "SV")],
na.rm = FALSE
)
# View results
summary(normalized$Opportunity_Index)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.2100 0.3193 0.3752 0.3866 0.4451 0.6396 9
# Create lists for PCA results
pca_results <- list()
pca_loadings <- list()
pca_variance <- list()
# Run PCA for each theme
for (theme in names(themes)) {
# Select indicators
data <- index[themes[[theme]]]
# Remove rows with missing values
data <- na.omit(data)
# Standardize and run PCA
pca <- prcomp(
data,
center = TRUE,
scale. = TRUE
)
# Save PCA results
pca_results[[theme]] <- pca
# Extract loadings
pca_loadings[[theme]] <- pca$rotation
# Calculate explained variance
variance <- pca$sdev^2 / sum(pca$sdev^2)
pca_variance[[theme]] <- variance
# Print results
cat("\n========================\n")
cat("PCA Theme:", theme, "\n")
cat("========================\n")
print(summary(pca))
print(pca$rotation)
}
========================
PCA Theme: LU
========================
Importance of components:
PC1 PC2 PC3 PC4
Standard deviation 1.2292 1.0492 0.9534 0.6922
Proportion of Variance 0.3777 0.2752 0.2272 0.1198
Cumulative Proportion 0.3777 0.6530 0.8802 1.0000
PC1 PC2 PC3 PC4
ImpairedWaterBodies 0.5886010 -0.4650469 -0.2592655 -0.6083270
SmATS -0.3402914 -0.6963567 -0.4820635 0.4085387
avg_impervious -0.6422848 0.2187652 -0.3901179 -0.6224307
tree_canopy 0.3538656 0.5009596 -0.7404081 0.2749808
========================
PCA Theme: HV
========================
Importance of components:
PC1 PC2 PC3
Standard deviation 1.2562 0.9345 0.7406
Proportion of Variance 0.5261 0.2911 0.1828
Cumulative Proportion 0.5261 0.8172 1.0000
PC1 PC2 PC3
asthma -0.6578798 0.1342572 -0.7410595
Low_Birth_Weight -0.5987070 0.5037045 0.6227614
Cardiovascular -0.4568852 -0.8533796 0.2509963
========================
PCA Theme: HE
========================
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6 PC7
Standard deviation 1.4550 1.1505 1.0360 0.9797 0.82166 0.71578 0.58194
Proportion of Variance 0.3024 0.1891 0.1533 0.1371 0.09645 0.07319 0.04838
Cumulative Proportion 0.3024 0.4915 0.6449 0.7820 0.87843 0.95162 1.00000
PC1 PC2 PC3 PC4 PC5 PC6 PC7
ozone -0.2787390 0.52456281 -0.2993920 -0.16178113 -0.7056835 -0.11950813 0.13805178
PM2_5 0.4281421 0.39919658 0.2774168 -0.18939281 0.1852795 -0.71189920 0.05811841
Diesel_PM 0.2792124 0.62588852 -0.1996177 -0.05958408 0.3832975 0.56760427 0.13347248
Pesticides -0.5318277 0.31420723 0.2086590 -0.11608873 0.2476442 -0.02470387 -0.70673901
traffic 0.2782188 0.16462861 0.6688910 0.37736956 -0.4395314 0.29204597 -0.16489431
LST_Max -0.2774607 0.21540812 -0.1175404 0.84488840 0.2090034 -0.24488033 0.21287085
LST_Mean 0.4730841 -0.03885984 -0.5373897 0.25433480 -0.1502244 -0.10428429 -0.62270771
========================
PCA Theme: SV
========================
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8 PC9
Standard deviation 2.0083 1.1023 1.0244 0.88733 0.75612 0.66756 0.62318 0.55096 0.45377
Proportion of Variance 0.4481 0.1350 0.1166 0.08748 0.06352 0.04951 0.04315 0.03373 0.02288
Cumulative Proportion 0.4481 0.5831 0.6997 0.78720 0.85073 0.90024 0.94339 0.97712 1.00000
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8 PC9
Linguistic_Isol -0.4149005 0.107479556 -0.20230411 -0.03343097 0.15527883 -0.02821378 -0.3380019 -0.77460524 -0.18733657
Poverty -0.4417129 0.135626913 0.04462317 0.11720996 -0.20729694 0.07704066 -0.1015468 0.03481641 0.84281339
HousBurd -0.3670935 0.186538771 0.28056627 -0.13192789 -0.17447356 -0.74035172 0.3636362 0.02485198 -0.15136913
children -0.2218364 -0.527728099 0.13493684 0.69505130 -0.08484864 0.10828159 0.3359050 -0.15841301 -0.11889655
elderly 0.2676716 0.592532037 0.20587422 0.13815726 -0.39947177 0.33375232 0.3451782 -0.34973825 -0.05790471
pop_density -0.2001110 -0.292895328 0.64238418 -0.53287431 0.07853711 0.38867041 0.0880527 -0.11054547 -0.01868365
Total_outdoor -0.3039878 -0.005944897 -0.59412342 -0.30374637 0.07354248 0.26962389 0.6153776 0.07152041 -0.02003120
perc_no_vehicle_access -0.2862734 0.462671200 0.22007226 0.29841112 0.65062428 0.18668151 0.0242996 0.29501148 -0.14393663
energy_burden -0.4033915 0.076012865 -0.06083070 0.01701178 -0.54846142 0.25183488 -0.3524175 0.38259397 -0.43897672
# Create explained variance table
variance_table <- data.frame()
for (theme in names(pca_variance)) {
variance <- pca_variance[[theme]]
temp <- data.frame(
Theme = theme,
Component = paste0("PC", seq_along(variance)),
Explained_Variance = variance * 100,
Cumulative_Variance = cumsum(variance) * 100
)
variance_table <- rbind(
variance_table,
temp
)
}
print(variance_table)
Theme Component Explained_Variance Cumulative_Variance
1 LU PC1 37.775198 37.77520
2 LU PC2 27.521826 65.29702
3 LU PC3 22.723343 88.02037
4 LU PC4 11.979633 100.00000
5 HV PC1 52.604857 52.60486
6 HV PC2 29.111320 81.71618
7 HV PC3 18.283823 100.00000
8 HE PC1 30.243669 30.24367
9 HE PC2 18.908299 49.15197
10 HE PC3 15.333320 64.48529
11 HE PC4 13.712935 78.19822
12 HE PC5 9.644661 87.84288
13 HE PC6 7.319116 95.16200
14 HE PC7 4.838000 100.00000
15 SV PC1 44.812051 44.81205
16 SV PC2 13.499525 58.31158
17 SV PC3 11.660407 69.97198
18 SV PC4 8.748383 78.72037
19 SV PC5 6.352444 85.07281
20 SV PC6 4.951458 90.02427
21 SV PC7 4.315062 94.33933
22 SV PC8 3.372812 97.71214
23 SV PC9 2.287858 100.00000
# Extract PC1 loadings
loading_table <- data.frame()
for (theme in names(pca_results)) {
pca <- pca_results[[theme]]
temp <- data.frame(
Theme = theme,
Indicator = rownames(pca$rotation),
PC1_Loading = pca$rotation[, 1]
)
loading_table <- rbind(
loading_table,
temp
)
}
print(loading_table)
Theme Indicator PC1_Loading
ImpairedWaterBodies LU ImpairedWaterBodies 0.5886010
SmATS LU SmATS -0.3402914
avg_impervious LU avg_impervious -0.6422848
tree_canopy LU tree_canopy 0.3538656
asthma HV asthma -0.6578798
Low_Birth_Weight HV Low_Birth_Weight -0.5987070
Cardiovascular HV Cardiovascular -0.4568852
ozone HE ozone -0.2787390
PM2_5 HE PM2_5 0.4281421
Diesel_PM HE Diesel_PM 0.2792124
Pesticides HE Pesticides -0.5318277
traffic HE traffic 0.2782188
LST_Max HE LST_Max -0.2774607
LST_Mean HE LST_Mean 0.4730841
Linguistic_Isol SV Linguistic_Isol -0.4149005
Poverty SV Poverty -0.4417129
HousBurd SV HousBurd -0.3670935
children SV children -0.2218364
elderly SV elderly 0.2676716
pop_density SV pop_density -0.2001110
Total_outdoor SV Total_outdoor -0.3039878
perc_no_vehicle_access SV perc_no_vehicle_access -0.2862734
energy_burden SV energy_burden -0.4033915
# Correlation matrices for each theme
for (theme in names(themes)) {
cat("\nCorrelation Matrix:", theme, "\n")
correlation <- cor(
index[themes[[theme]]],
use = "pairwise.complete.obs"
)
print(round(correlation, 3))
}
Correlation Matrix: LU
ImpairedWaterBodies SmATS avg_impervious tree_canopy
ImpairedWaterBodies 1.000 0.048 -0.410 0.153
SmATS 0.048 1.000 0.212 -0.188
avg_impervious -0.410 0.212 1.000 -0.042
tree_canopy 0.153 -0.188 -0.042 1.000
Correlation Matrix: HV
asthma Low_Birth_Weight Cardiovascular
asthma 1.000 0.428 0.275
Low_Birth_Weight 0.428 1.000 0.142
Cardiovascular 0.275 0.142 1.000
Correlation Matrix: HE
ozone PM2_5 Diesel_PM Pesticides traffic LST_Max LST_Mean
ozone 1.000 -0.078 0.131 0.333 -0.140 0.143 -0.124
PM2_5 -0.078 1.000 0.380 -0.205 0.308 -0.215 0.209
Diesel_PM 0.131 0.380 1.000 -0.065 0.102 -0.018 0.252
Pesticides 0.333 -0.205 -0.065 1.000 -0.173 0.263 -0.571
traffic -0.140 0.308 0.102 -0.173 1.000 -0.018 0.040
LST_Max 0.143 -0.215 -0.018 0.263 -0.018 1.000 -0.064
LST_Mean -0.124 0.209 0.252 -0.571 0.040 -0.064 1.000
Correlation Matrix: SV
Linguistic_Isol Poverty HousBurd children elderly pop_density Total_outdoor perc_no_vehicle_access energy_burden
Linguistic_Isol 1.000 0.697 0.531 0.256 -0.400 0.182 0.545 0.473 0.614
Poverty 0.697 1.000 0.638 0.359 -0.299 0.268 0.467 0.531 0.748
HousBurd 0.531 0.638 1.000 0.210 -0.224 0.334 0.302 0.446 0.519
children 0.256 0.359 0.210 1.000 -0.398 0.186 0.109 0.124 0.301
elderly -0.400 -0.299 -0.224 -0.398 1.000 -0.268 -0.359 -0.043 -0.261
pop_density 0.182 0.268 0.334 0.186 -0.268 1.000 0.042 0.143 0.228
Total_outdoor 0.545 0.467 0.302 0.109 -0.359 0.042 1.000 0.202 0.486
perc_no_vehicle_access 0.473 0.531 0.446 0.124 -0.043 0.143 0.202 1.000 0.359
energy_burden 0.614 0.748 0.519 0.301 -0.261 0.228 0.486 0.359 1.000
# Create output folder
outdir <- dirname(
"C:/Users/noahs/Desktop/Ch1/SJV/Index.xlsx"
)
# Export Opportunity Index
write.csv(
normalized,
file.path(outdir, "Opportunity_Index_Results.csv"),
row.names = FALSE
)
# Export explained variance
write.csv(
variance_table,
file.path(outdir, "PCA_Explained_Variance.csv"),
row.names = FALSE
)
# Export PC1 loadings
write.csv(
loading_table,
file.path(outdir, "PCA_Loadings.csv"),
row.names = FALSE
)
# Export missing data report
write.csv(
data.frame(
Indicator = names(missing_data),
Missing_Count = as.integer(missing_data)
),
file.path(outdir, "Missing_Data_Report.csv"),
row.names = FALSE
)
hist(
normalized$Opportunity_Index,
breaks = 20,
main = "Central Valley Opportunity Index",
xlab = "Opportunity Index",
ylab = "Number of Census Tracts"
)

LS0tDQp0aXRsZTogIlNKViBDUkMgUENBIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQojbG9hZCBhbmQgdXBkYXRlIHBhY2thZ2VzIGlmIG5lZWRlZA0KcGFja2FnZXMgPC0gYygncmVhZHhsJywnZHBseXInLCdnZ3Bsb3QyJykNCm1pc3NpbmdfcGFja2FnZXMgPC0gc2V0ZGlmZihwYWNrYWdlcywgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKQ0KaWYgKGxlbmd0aChtaXNzaW5nX3BhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhtaXNzaW5nX3BhY2thZ2VzKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQoNCmBgYA0KYGBge3J9DQojIEtlZXAgdHJhY3QgSURzIGFzIDExLWNoYXJhY3RlciBGSVBTIGNvZGVzDQojIGJlY2F1c2UgRXhjZWwgbWF5IHN0b3JlIHRoZXNlIGFzIG51bWJlcnMNCmluZGV4IDwtICByZWFkX2V4Y2VsKCdDOi9Vc2Vycy9ub2Focy9EZXNrdG9wL0NoMS9TSlYvSW5kZXgueGxzeCcpDQppbmRleCR0cmFjdCA8LSBzcHJpbnRmKCclMDExLjBmJywgYXMubnVtZXJpYyhpbmRleCR0cmFjdCkpDQoNCnRoZW1lcyA8LSBsaXN0KA0KICBMVSA9IGMoJ0ltcGFpcmVkV2F0ZXJCb2RpZXMnLCdTbUFUUycsJ2F2Z19pbXBlcnZpb3VzJywndHJlZV9jYW5vcHknKSwNCiAgSFYgPSBjKCdhc3RobWEnLCdMb3dfQmlydGhfV2VpZ2h0JywnQ2FyZGlvdmFzY3VsYXInKSwNCiAgSEUgPSBjKCdvem9uZScsJ1BNMl81JywnRGllc2VsX1BNJywnUGVzdGljaWRlcycsJ3RyYWZmaWMnLCdMU1RfTWF4JywnTFNUX01lYW4nKSwNCiAgU1YgPSBjKCdMaW5ndWlzdGljX0lzb2wnLCdQb3ZlcnR5JywnSG91c0J1cmQnLCdjaGlsZHJlbicsJ2VsZGVybHknLA0KICAgICAgICAgJ3BvcF9kZW5zaXR5JywnVG90YWxfb3V0ZG9vcicsJ3BlcmNfbm9fdmVoaWNsZV9hY2Nlc3MnLCdlbmVyZ3lfYnVyZGVuJykNCikNCiMgQ29tYmluZSBhbGwgaW5kaWNhdG9ycyBpbnRvIG9uZSBsaXN0DQp2YXJzIDwtIHVubGlzdCh0aGVtZXMsIHVzZS5uYW1lcyA9IEZBTFNFKQ0KDQojIENoZWNrIGZvciBtaXNzaW5nIGluZGljYXRvciBjb2x1bW5zDQptaXNzaW5nX3ZhcnMgPC0gc2V0ZGlmZih2YXJzLCBuYW1lcyhpbmRleCkpDQoNCmlmIChsZW5ndGgobWlzc2luZ192YXJzKSA+IDApIHsNCiAgc3RvcChwYXN0ZSgiTWlzc2luZyBjb2x1bW5zOiIsDQogICAgICAgICAgICAgcGFzdGUobWlzc2luZ192YXJzLCBjb2xsYXBzZSA9ICIsICIpKSkNCn0NCg0KIyBDb252ZXJ0IGluZGljYXRvcnMgdG8gbnVtZXJpYyB2YWx1ZXMNCmZvciAodiBpbiB2YXJzKSB7DQogIGluZGV4W1t2XV0gPC0gYXMubnVtZXJpYyhpbmRleFtbdl1dKQ0KfQ0KDQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBhZnRlciBjb252ZXJzaW9uDQpjb2xTdW1zKGlzLm5hKGluZGV4W3ZhcnNdKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgZm9yIGFsbCBpbmRpY2F0b3JzDQptaXNzaW5nX2RhdGEgPC0gY29sU3Vtcyhpcy5uYShpbmRleFt2YXJzXSkpDQoNCnByaW50KG1pc3NpbmdfZGF0YSkNCg0KIyBJZGVudGlmeSBjZW5zdXMgdHJhY3RzIHdpdGggbWlzc2luZyB2YWx1ZXMNCmluY29tcGxldGVfdHJhY3RzIDwtIGluZGV4Ww0KICAhY29tcGxldGUuY2FzZXMoaW5kZXhbdmFyc10pLA0KICBjKCJ0cmFjdCIsIHZhcnMpDQpdDQoNCnByaW50KGluY29tcGxldGVfdHJhY3RzKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNaW4tbWF4IG5vcm1hbGl6YXRpb24NCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7DQogIG1pbl94IDwtIG1pbih4LCBuYS5ybSA9IFRSVUUpDQogIG1heF94IDwtIG1heCh4LCBuYS5ybSA9IFRSVUUpDQoNCiAgaWYgKG1heF94ID09IG1pbl94KSB7DQogICAgcmV0dXJuKHJlcChOQV9yZWFsXywgbGVuZ3RoKHgpKSkNCiAgfQ0KDQogICh4IC0gbWluX3gpIC8gKG1heF94IC0gbWluX3gpDQp9DQoNCiMgQ3JlYXRlIG5vcm1hbGl6ZWQgZGF0YXNldA0Kbm9ybWFsaXplZCA8LSBpbmRleA0KDQojIE5vcm1hbGl6ZSBhbGwgMjMgaW5kaWNhdG9ycw0KZm9yICh2IGluIHZhcnMpIHsNCiAgbm9ybWFsaXplZFtbdl1dIDwtIG5vcm1hbGl6ZShpbmRleFtbdl1dKQ0KfQ0KDQojIFJldmVyc2UgdHJlZSBjYW5vcHkNCiMgTG93ZXIgY2Fub3B5ID0gaGlnaGVyIHZ1bG5lcmFiaWxpdHkNCm5vcm1hbGl6ZWQkdHJlZV9jYW5vcHkgPC0gMSAtIG5vcm1hbGl6ZWQkdHJlZV9jYW5vcHkNCg0KIyBDaGVjayBub3JtYWxpemVkIHZhbHVlcw0Kc3VtbWFyeShub3JtYWxpemVkW3ZhcnNdKQ0KYGBgDQpgYGB7cn0NCiMgTGFuZCBVc2UgJiBFbnZpcm9ubWVudGFsIEZlYXR1cmVzDQpub3JtYWxpemVkJExVIDwtIHJvd01lYW5zKA0KICBub3JtYWxpemVkW3RoZW1lcyRMVV0sDQogIG5hLnJtID0gRkFMU0UNCikNCg0KIyBIZWFsdGggVnVsbmVyYWJpbGl0eQ0Kbm9ybWFsaXplZCRIViA8LSByb3dNZWFucygNCiAgbm9ybWFsaXplZFt0aGVtZXMkSFZdLA0KICBuYS5ybSA9IEZBTFNFDQopDQoNCiMgSGVhdCAmIEVudmlyb25tZW50YWwgRXhwb3N1cmUNCm5vcm1hbGl6ZWQkSEUgPC0gcm93TWVhbnMoDQogIG5vcm1hbGl6ZWRbdGhlbWVzJEhFXSwNCiAgbmEucm0gPSBGQUxTRQ0KKQ0KDQojIFNvY2lvZWNvbm9taWMgVnVsbmVyYWJpbGl0eQ0Kbm9ybWFsaXplZCRTViA8LSByb3dNZWFucygNCiAgbm9ybWFsaXplZFt0aGVtZXMkU1ZdLA0KICBuYS5ybSA9IEZBTFNFDQopDQoNCiMgQ2FsY3VsYXRlIE9wcG9ydHVuaXR5IEluZGV4DQpub3JtYWxpemVkJE9wcG9ydHVuaXR5X0luZGV4IDwtIHJvd01lYW5zKA0KICBub3JtYWxpemVkW2MoIkxVIiwgIkhWIiwgIkhFIiwgIlNWIildLA0KICBuYS5ybSA9IEZBTFNFDQopDQoNCiMgVmlldyByZXN1bHRzDQpzdW1tYXJ5KG5vcm1hbGl6ZWQkT3Bwb3J0dW5pdHlfSW5kZXgpDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBsaXN0cyBmb3IgUENBIHJlc3VsdHMNCnBjYV9yZXN1bHRzIDwtIGxpc3QoKQ0KcGNhX2xvYWRpbmdzIDwtIGxpc3QoKQ0KcGNhX3ZhcmlhbmNlIDwtIGxpc3QoKQ0KDQojIFJ1biBQQ0EgZm9yIGVhY2ggdGhlbWUNCmZvciAodGhlbWUgaW4gbmFtZXModGhlbWVzKSkgew0KDQogICMgU2VsZWN0IGluZGljYXRvcnMNCiAgZGF0YSA8LSBpbmRleFt0aGVtZXNbW3RoZW1lXV1dDQoNCiAgIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzDQogIGRhdGEgPC0gbmEub21pdChkYXRhKQ0KDQogICMgU3RhbmRhcmRpemUgYW5kIHJ1biBQQ0ENCiAgcGNhIDwtIHByY29tcCgNCiAgICBkYXRhLA0KICAgIGNlbnRlciA9IFRSVUUsDQogICAgc2NhbGUuID0gVFJVRQ0KICApDQoNCiAgIyBTYXZlIFBDQSByZXN1bHRzDQogIHBjYV9yZXN1bHRzW1t0aGVtZV1dIDwtIHBjYQ0KDQogICMgRXh0cmFjdCBsb2FkaW5ncw0KICBwY2FfbG9hZGluZ3NbW3RoZW1lXV0gPC0gcGNhJHJvdGF0aW9uDQoNCiAgIyBDYWxjdWxhdGUgZXhwbGFpbmVkIHZhcmlhbmNlDQogIHZhcmlhbmNlIDwtIHBjYSRzZGV2XjIgLyBzdW0ocGNhJHNkZXZeMikNCg0KICBwY2FfdmFyaWFuY2VbW3RoZW1lXV0gPC0gdmFyaWFuY2UNCg0KICAjIFByaW50IHJlc3VsdHMNCiAgY2F0KCJcbj09PT09PT09PT09PT09PT09PT09PT09PVxuIikNCiAgY2F0KCJQQ0EgVGhlbWU6IiwgdGhlbWUsICJcbiIpDQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09XG4iKQ0KDQogIHByaW50KHN1bW1hcnkocGNhKSkNCiAgcHJpbnQocGNhJHJvdGF0aW9uKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZXhwbGFpbmVkIHZhcmlhbmNlIHRhYmxlDQp2YXJpYW5jZV90YWJsZSA8LSBkYXRhLmZyYW1lKCkNCg0KZm9yICh0aGVtZSBpbiBuYW1lcyhwY2FfdmFyaWFuY2UpKSB7DQoNCiAgdmFyaWFuY2UgPC0gcGNhX3ZhcmlhbmNlW1t0aGVtZV1dDQoNCiAgdGVtcCA8LSBkYXRhLmZyYW1lKA0KICAgIFRoZW1lID0gdGhlbWUsDQogICAgQ29tcG9uZW50ID0gcGFzdGUwKCJQQyIsIHNlcV9hbG9uZyh2YXJpYW5jZSkpLA0KICAgIEV4cGxhaW5lZF9WYXJpYW5jZSA9IHZhcmlhbmNlICogMTAwLA0KICAgIEN1bXVsYXRpdmVfVmFyaWFuY2UgPSBjdW1zdW0odmFyaWFuY2UpICogMTAwDQogICkNCg0KICB2YXJpYW5jZV90YWJsZSA8LSByYmluZCgNCiAgICB2YXJpYW5jZV90YWJsZSwNCiAgICB0ZW1wDQogICkNCn0NCg0KcHJpbnQodmFyaWFuY2VfdGFibGUpDQpgYGANCg0KYGBge3J9DQojIEV4dHJhY3QgUEMxIGxvYWRpbmdzDQpsb2FkaW5nX3RhYmxlIDwtIGRhdGEuZnJhbWUoKQ0KDQpmb3IgKHRoZW1lIGluIG5hbWVzKHBjYV9yZXN1bHRzKSkgew0KDQogIHBjYSA8LSBwY2FfcmVzdWx0c1tbdGhlbWVdXQ0KDQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBUaGVtZSA9IHRoZW1lLA0KICAgIEluZGljYXRvciA9IHJvd25hbWVzKHBjYSRyb3RhdGlvbiksDQogICAgUEMxX0xvYWRpbmcgPSBwY2Ekcm90YXRpb25bLCAxXQ0KICApDQoNCiAgbG9hZGluZ190YWJsZSA8LSByYmluZCgNCiAgICBsb2FkaW5nX3RhYmxlLA0KICAgIHRlbXANCiAgKQ0KfQ0KDQpwcmludChsb2FkaW5nX3RhYmxlKQ0KYGBgDQoNCg0KYGBge3J9DQojIENvcnJlbGF0aW9uIG1hdHJpY2VzIGZvciBlYWNoIHRoZW1lDQpmb3IgKHRoZW1lIGluIG5hbWVzKHRoZW1lcykpIHsNCg0KICBjYXQoIlxuQ29ycmVsYXRpb24gTWF0cml4OiIsIHRoZW1lLCAiXG4iKQ0KDQogIGNvcnJlbGF0aW9uIDwtIGNvcigNCiAgICBpbmRleFt0aGVtZXNbW3RoZW1lXV1dLA0KICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiDQogICkNCg0KICBwcmludChyb3VuZChjb3JyZWxhdGlvbiwgMykpDQp9DQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBvdXRwdXQgZm9sZGVyDQpvdXRkaXIgPC0gZGlybmFtZSgNCiAgIkM6L1VzZXJzL25vYWhzL0Rlc2t0b3AvQ2gxL1NKVi9JbmRleC54bHN4Ig0KKQ0KDQojIEV4cG9ydCBPcHBvcnR1bml0eSBJbmRleA0Kd3JpdGUuY3N2KA0KICBub3JtYWxpemVkLA0KICBmaWxlLnBhdGgob3V0ZGlyLCAiT3Bwb3J0dW5pdHlfSW5kZXhfUmVzdWx0cy5jc3YiKSwNCiAgcm93Lm5hbWVzID0gRkFMU0UNCikNCg0KIyBFeHBvcnQgZXhwbGFpbmVkIHZhcmlhbmNlDQp3cml0ZS5jc3YoDQogIHZhcmlhbmNlX3RhYmxlLA0KICBmaWxlLnBhdGgob3V0ZGlyLCAiUENBX0V4cGxhaW5lZF9WYXJpYW5jZS5jc3YiKSwNCiAgcm93Lm5hbWVzID0gRkFMU0UNCikNCg0KIyBFeHBvcnQgUEMxIGxvYWRpbmdzDQp3cml0ZS5jc3YoDQogIGxvYWRpbmdfdGFibGUsDQogIGZpbGUucGF0aChvdXRkaXIsICJQQ0FfTG9hZGluZ3MuY3N2IiksDQogIHJvdy5uYW1lcyA9IEZBTFNFDQopDQoNCiMgRXhwb3J0IG1pc3NpbmcgZGF0YSByZXBvcnQNCndyaXRlLmNzdigNCiAgZGF0YS5mcmFtZSgNCiAgICBJbmRpY2F0b3IgPSBuYW1lcyhtaXNzaW5nX2RhdGEpLA0KICAgIE1pc3NpbmdfQ291bnQgPSBhcy5pbnRlZ2VyKG1pc3NpbmdfZGF0YSkNCiAgKSwNCiAgZmlsZS5wYXRoKG91dGRpciwgIk1pc3NpbmdfRGF0YV9SZXBvcnQuY3N2IiksDQogIHJvdy5uYW1lcyA9IEZBTFNFDQopDQpgYGANCg0KDQpgYGB7cn0NCmhpc3QoDQogIG5vcm1hbGl6ZWQkT3Bwb3J0dW5pdHlfSW5kZXgsDQogIGJyZWFrcyA9IDIwLA0KICBtYWluID0gIkNlbnRyYWwgVmFsbGV5IE9wcG9ydHVuaXR5IEluZGV4IiwNCiAgeGxhYiA9ICJPcHBvcnR1bml0eSBJbmRleCIsDQogIHlsYWIgPSAiTnVtYmVyIG9mIENlbnN1cyBUcmFjdHMiDQopDQpgYGANCg0K