A. Ôn tập

1 Đọc dữ liệu

library(csv)
d <- read.csv("C:/Users/PC/Downloads/Supermarket Transactions.csv")

Ta lập file dữ liệu mới với tên data chỉ gồm các biến định tính.

data1 <- c("Gender", "MaritalStatus", "Homeowner", 
              "AnnualIncome", "City", "StateorProvince", 
                "Country" , "ProductFamily", "ProductDepartment",   "ProductCategory")
dldt <-d[, data1]

Kiểm tra và loại bỏ các biến NA

anyNA(dldt)
## [1] FALSE
dldt <- na.omit(dldt)

Chuyển đổi dữ liệu định tính sang factor

sapply(dldt, class)
##            Gender     MaritalStatus         Homeowner      AnnualIncome 
##       "character"       "character"       "character"       "character" 
##              City   StateorProvince           Country     ProductFamily 
##       "character"       "character"       "character"       "character" 
## ProductDepartment   ProductCategory 
##       "character"       "character"
dldt[] <- lapply(dldt, as.factor)
dldt<- data.frame(lapply(dldt, as.factor))

2. Phân tích Mô tả Một biến Định tính

2.1 Mô tả biến MaritalStatus

Bảng tần suất

HN1 <- table(dldt$MaritalStatus)/sum(nrow(dldt))
HN1
## 
##         M         S 
## 0.4883704 0.5116296

Bảng tần số

HN2<-table(dldt$MaritalStatus)
HN2
## 
##    M    S 
## 6866 7193

Vậy trong data này có 48.8370439% đã kết hôn và 51.1629561% chưa kết hôn

Biểu đồ

# Vẽ biểu đồ tròn
pie(HN2 ,
    main   = "Tình trạng hôn nhân",
    labels = paste0(names(HN2),
                    " (", round(HN1 *100, 1), "%)"),
    col    = c("blue", "red"))

# Đếm số người kết hôn (M) và độc thân (S)
HN_counts <- table(dldt$MaritalStatus)

# Chênh lệch về số lượng giữa hai nhóm
HN_diff <- abs(HN_counts["M"] - HN_counts["S"])
HN_diff
##   M 
## 327

Trong bộ dữ liệu này có sự chênh lệnh nhất định về tình trạng hôn nhân. Sự chênh lệch tuyệt đối giữa số người kết hôn và chưa kết hôn là 327 người.

2.2 Mô tả biến City

Bảng tần suất

c1 <- table(dldt$City)/sum(nrow(dldt))
c1
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##   0.027242336   0.010171420   0.057685468   0.059321431   0.032150224 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##   0.005334661   0.060103848   0.065865282   0.046518245   0.013798990 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##   0.033003770   0.062308841   0.098584537   0.044170994   0.061597553 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##   0.009246746   0.065580767   0.062237712   0.089408920   0.045024539 
##      Victoria   Walla Walla        Yakima 
##   0.012518671   0.011380610   0.026744434

Bảng tần số

c2<-table(dldt$City)
c2
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##           383           143           811           834           452 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##            75           845           926           654           194 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##           464           876          1386           621           866 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##           130           922           875          1257           633 
##      Victoria   Walla Walla        Yakima 
##           176           160           376

Vậy trong data này có 2.72% sống ở Acapulco và 1.02% sống ở Bellingham.

Tiếp theo, có 5.77% sống ở Beverly Hills, 5.93% ở Bremerton và 3.22% ở Camacho.

Trong khi đó, chỉ 0.53% cư dân sống ở Guadalajara và 6.01% ở Hidalgo. Los Angeles có tỷ lệ cao hơn với 6.59%.

Mexico City chiếm 1.38%, thấp hơn so với Portland (6.23%) và Salem (9.86%).

Các thành phố tại Washington như Seattle (6.56%), Spokane (6.22%), và Tacoma (8.94%) có tỷ lệ đáng kể.

Cuối cùng, các khu vực nhỏ như Victoria (1.25%), Walla Walla (1.14%) và Yakima (2.67%) chỉ chiếm phần nhỏ.

Biểu đồ

library(ggplot2)

# Bảng tần suất và phần trăm dưới dạng data.frame
c_df <- as.data.frame(table(dldt$City))
colnames(c_df) <- c("City", "Frequency")
c_df$Percentage <- round(c_df$Frequency / sum(c_df$Frequency) * 100, 2)

# Vẽ biểu đồ cột
ggplot(c_df, aes(x = reorder(City, -Percentage), y = Percentage, fill = City)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = paste0(Percentage, "%")), vjust = -0.3, size = 3) +
  labs(
    title = "Phân bố theo thành phố",
    x = "Thành phố",
    y = "Phần trăm (%)"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.position = "none")

-> Mức giao dịch có sự chênh lệch rõ rệt giữa các thành phố, với một số thành phố có mức giao dịch rất cao, trong khi một số khác lại có mức giao dịch thấp. Điều này có thể do các yếu tố như dân số, nhu cầu thị trường, hoặc mức độ phát triển kinh tế tại mỗi thành phố. Các thành phố lớn như Los Angeles, San Diego và Portland có xu hướng có mức giao dịch cao hơn, trong khi các thành phố như Guadalajara và Acapulco có thể đang gặp khó khăn trong việc thu hút giao dịch.

2.3 Mô tả biến StateorProvince

Bảng tần suất

BT1 <- table(dldt$StateorProvince)/sum(nrow(dldt))
BT1
## 
##          BC          CA          DF    Guerrero     Jalisco          OR 
## 0.057543211 0.194395049 0.057969984 0.027242336 0.005334661 0.160893378 
##    Veracruz          WA     Yucatan   Zacatecas 
## 0.033003770 0.324845295 0.046518245 0.092254072

Bảng tần số

BT2<-table(dldt$StateorProvince)
BT2
## 
##        BC        CA        DF  Guerrero   Jalisco        OR  Veracruz        WA 
##       809      2733       815       383        75      2262       464      4567 
##   Yucatan Zacatecas 
##       654      1297

Vậy trong data này có 5.75% tỷ lệ giao dịch sống ở BC và 19.44% sống ở CA.

Tiếp theo, có 5.80% tỷ lệ giao dịch sống ở DF, 2.72% ở Guerrero và 0.53% ở Jalisco.

Trong khi đó, OR chiếm 16.09% và Veracruz là 3.30%. Tỷ lệ cao nhất là bang WA với 32.48%.

Yucatan có tỷ lệ 4.65%, trong khi Zacatecas chiếm 9.23% tổng số khách hàng trong dữ liệu.

Điều này điều thể hiện qua biểu đồ Tần số giao dịch theo tiểu bang

library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
state_counts <- dldt %>%
  count(StateorProvince)
ggplot(state_counts, aes(x = reorder(StateorProvince, n), y = n)) +
  geom_bar(stat = "identity", fill = "blue") +
  geom_text(aes(label = n), hjust = -0.1, size = 3.5) +  
  coord_flip() +
  labs(title = "Tần số giao dịch theo tiểu bang",
       x = "State / Province",
       y = "Số giao dịch") +
  theme_minimal()

2.4 Mô tả biến Country

Bảng tần suất

co1 <- table(dldt$Country)/sum(nrow(dldt))
co1
## 
##     Canada     Mexico        USA 
## 0.05754321 0.26232307 0.68013372

Bảng tần số

co2<-table(dldt$Country)
co2
## 
## Canada Mexico    USA 
##    809   3688   9562

Vậy trong data này có 5.7543211% giao dịch mua hàng tại Canada, 26.2323067% giao dịch mua hàng tại Mexico và 68.0133722% tại USA.

Các tính toán này được thống kê qua biểu đồ “Phân bố lượt mua hàng theo Country”.

Biểu đồ

# Vẽ biểu đồ tròn với 3 thành phố
pie(co1,
    main   = "Phân bố lượt mua hàng theo Country",
    labels = paste0(names(co1),
                    " (", round(co1*100 , 1), "%)"),
    col    = c("blue", "red", "green"))

2.5 Mô tả biến ProductFamily

Bảng tần suất

f1 <- table(dldt$ProductFamily)/sum(nrow(dldt))
f1
## 
##          Drink           Food Non-Consumable 
##     0.08891102     0.72217085     0.18891813

Bảng tần số

f2<-table(dldt$ProductFamily)
f2
## 
##          Drink           Food Non-Consumable 
##           1250          10153           2656

Phần lớn sản phẩm được bán là thực phẩm với 72.2170851% , tiếp theo là hàng phi tiêu dùng 18.8918131% và đồ uống 8.8911018%. Điều này cho thấy thực phẩm là nhóm hàng chủ lực trong hoạt động bán lẻ của siêu thị.

Biểu đồ minh họa được thể hiện là biểu đồ tròn.

Biểu đồ

# Vẽ biểu đồ tròn với 3 thành phố
pie(f2,
    main   = "Phân bố theo ProductFamily",
    labels = paste0(names(f2),
                    " (", round(f2 , 1), ")"),
    col    = c("blue", "red", "green"))

2.6 Mô tả biến ProductDepartment

Bảng tần suất

dp1 <- table(dldt$ProductDepartment)/sum(nrow(dldt))*100
dp1
## 
## Alcoholic Beverages         Baked Goods        Baking Goods           Beverages 
##           2.5321858           3.0229746           7.6250089           4.8367594 
##     Breakfast Foods        Canned Foods     Canned Products            Carousel 
##           1.3372217           6.9492852           0.7753041           0.4196600 
##            Checkout               Dairy                Deli                Eggs 
##           0.5832563           6.4229319           4.9719041           1.4083505 
##        Frozen Foods  Health and Hygiene           Household                Meat 
##           9.8300021           6.3518031          10.1002916           0.6330464 
##         Periodicals             Produce             Seafood         Snack Foods 
##           1.4368020          14.1830856           0.7255139          11.3806103 
##              Snacks       Starchy Foods 
##           2.5037343           1.9702682

Bảng tần số

dp2<-table(dldt$ProductDepartment)
dp2
## 
## Alcoholic Beverages         Baked Goods        Baking Goods           Beverages 
##                 356                 425                1072                 680 
##     Breakfast Foods        Canned Foods     Canned Products            Carousel 
##                 188                 977                 109                  59 
##            Checkout               Dairy                Deli                Eggs 
##                  82                 903                 699                 198 
##        Frozen Foods  Health and Hygiene           Household                Meat 
##                1382                 893                1420                  89 
##         Periodicals             Produce             Seafood         Snack Foods 
##                 202                1994                 102                1600 
##              Snacks       Starchy Foods 
##                 352                 277

Vậy trong data này có 2.53% sản phẩm thuộc nhóm Alcoholic Beverages và 3.02% thuộc Baked Goods.

Baking Goods chiếm 7.63%, cao hơn Breakfast Foods 1.34% và Canned Products 0.78%.

Canned Foods chiếm 6.95%, trong khi Carousel và Checkout chỉ chiếm lần lượt 0.42% và 0.58%.

Các nhóm lớn hơn gồm Dairy 6.42%, Deli 4.97% và Eggs 1.41%.

Frozen Foods chiếm 9.83%, Health and Hygiene là 6.35%, trong khi Household là một trong các nhóm lớn nhất với 10.10%.

Nhóm sản phẩm chiếm tỷ lệ cao nhất là Produce, với 14.18%, tiếp theo là Snack Foods 11.38%.

Ngược lại, một số nhóm nhỏ như Meat 0.63%, Seafood 0.73% và Starchy Foods 1.97% chỉ chiếm một phần nhỏ trong tổng phân phối.

dept_counts <- dldt %>%
  count(ProductDepartment)

ggplot(dept_counts, aes(x = reorder(ProductDepartment, n), y = n)) +
  geom_bar(stat = "identity", fill = "pink") +
  geom_text(aes(label = n), hjust = -0.1, size = 3.5) +  
  coord_flip() +
  labs(title = "Tần số giao dịch theo phòng ban sản phẩm",
       x = "Phòng ban sản phẩm (ProductDepartment)",
       y = "Số lượng giao dịch") +
  theme_minimal()

2.7 Mô tả biến ProductCategory

Bảng tần suất

pc1 <- table(dldt$ProductCategory)/sum(nrow(dldt))*100
pc1
## 
##         Baking Goods    Bathroom Products        Beer and Wine 
##            3.4426346            2.5962017            2.5321858 
##                Bread      Breakfast Foods              Candles 
##            3.0229746            2.9660716            0.3200797 
##                Candy     Canned Anchovies         Canned Clams 
##            2.5037343            0.3129668            0.3769827 
##       Canned Oysters      Canned Sardines        Canned Shrimp 
##            0.2489508            0.2845153            0.2702895 
##          Canned Soup          Canned Tuna Carbonated Beverages 
##            2.8736041            0.6188207            1.0953837 
##    Cleaning Supplies        Cold Remedies                Dairy 
##            1.3443346            0.6614980            6.4229319 
##        Decongestants               Drinks                 Eggs 
##            0.6045949            0.9602390            1.4083505 
##           Electrical      Frozen Desserts       Frozen Entrees 
##            2.5250729            2.2974607            0.8393200 
##                Fruit             Hardware        Hot Beverages 
##            5.4413543            0.9175617            1.6075112 
##              Hygiene     Jams and Jellies     Kitchen Products 
##            1.4012376            4.1823743            1.5434953 
##            Magazines                 Meat        Miscellaneous 
##            1.4368020            5.4129028            0.2987410 
##  Packaged Vegetables       Pain Relievers       Paper Products 
##            0.3414183            1.3656732            2.4539441 
##                Pizza     Plastic Products Pure Juice Beverages 
##            1.3798990            1.0029163            1.1736254 
##              Seafood          Side Dishes          Snack Foods 
##            0.7255139            1.0882709           11.3806103 
##            Specialty        Starchy Foods           Vegetables 
##            2.0556227            1.9702682           12.2910591

Bảng tần số

pc2<-table(dldt$ProductCategory)
pc2
## 
##         Baking Goods    Bathroom Products        Beer and Wine 
##                  484                  365                  356 
##                Bread      Breakfast Foods              Candles 
##                  425                  417                   45 
##                Candy     Canned Anchovies         Canned Clams 
##                  352                   44                   53 
##       Canned Oysters      Canned Sardines        Canned Shrimp 
##                   35                   40                   38 
##          Canned Soup          Canned Tuna Carbonated Beverages 
##                  404                   87                  154 
##    Cleaning Supplies        Cold Remedies                Dairy 
##                  189                   93                  903 
##        Decongestants               Drinks                 Eggs 
##                   85                  135                  198 
##           Electrical      Frozen Desserts       Frozen Entrees 
##                  355                  323                  118 
##                Fruit             Hardware        Hot Beverages 
##                  765                  129                  226 
##              Hygiene     Jams and Jellies     Kitchen Products 
##                  197                  588                  217 
##            Magazines                 Meat        Miscellaneous 
##                  202                  761                   42 
##  Packaged Vegetables       Pain Relievers       Paper Products 
##                   48                  192                  345 
##                Pizza     Plastic Products Pure Juice Beverages 
##                  194                  141                  165 
##              Seafood          Side Dishes          Snack Foods 
##                  102                  153                 1600 
##            Specialty        Starchy Foods           Vegetables 
##                  289                  277                 1728

Vậy trong data này có 3.44% sản phẩm thuộc nhóm Baking Goods, 2.60% là Bathroom Products, và 2.53% là Beer and Wine.

Tiếp theo, Bread chiếm 3.02%, Breakfast Foods 2.97%, còn Candles chỉ chiếm 0.32%.

Các nhóm như Candy 2.50%, Canned Anchovies 0.31%, Canned Clams 0.38%, Canned Oysters 0.25% và Canned Shrimp 0.27% có tỷ lệ rất nhỏ.

Canned Soup chiếm 2.87%, cao hơn Canned Tuna 0.62% và Carbonated Beverages 1.10%.

Cleaning Supplies có tỷ lệ 1.34%, Cold Remedies là 0.66%, trong khi Dairy là một trong các nhóm lớn với 6.42%. Drinks chiếm 0.96%, còn Eggs là 1.41%.

Frozen Desserts 2.30% và Frozen Entrees 0.84% thấp hơn so với Fruit 5.44% và Meat 5.41%.

Hot Beverages chiếm 1.61%, Hygiene 1.40%, Jams and Jellies 4.18%, và Kitchen Products 1.54%.

Các nhóm khác như Magazines 1.44%, Miscellaneous 0.30% và Packaged Vegetables 0.34% chiếm tỷ lệ khá nhỏ.

Paper Products có 2.45%, Pizza là 1.38%, và Snack Foods đứng đầu với 11.38%.

Cuối cùng, nhóm Vegetables chiếm tỷ lệ cao nhất với 12.29%, tiếp theo là Snack Foods, trong khi các nhóm nhỏ như Seafood 0.73% và Side Dishes 1.09% chỉ đóng góp một phần nhỏ trong tổng phân phối.

dt_counts <- dldt %>%
  count(ProductCategory)

ggplot(dt_counts, aes(x = reorder(ProductCategory, n), y = n)) +
  geom_bar(stat = "identity", fill = "yellow")+
  geom_text(aes(label = n), hjust = -0.1, size = 2) +  
  coord_flip() +
  labs(title = "Doanh mục mua hàng cụ thể",
       x = "Danh mục (ProductCategory)",
       y = "Số lượng giao dịch") +
  theme_minimal()

2.8. Mô tả biến Gender

Bảng tần suất

g1 <- table(d$Gender)/sum(nrow(d))
g1
## 
##         F         M 
## 0.5099936 0.4900064

Bảng tần số

g2<-table(d$Gender)
g2
## 
##    F    M 
## 7170 6889

Vậy trong data này có 50.9993598% là nữ 51.1629561% là nam

Biểu đồ

# Vẽ biểu đồ tròn
pie(g2 ,
    main   = "Tỷ lệ giới tính",
    labels = paste0(names(g2),
                    " (", round(g1 *100, 1), "%)"),
    col    = c("blue", "red"))

2.9. Mô tả biến Homeowner

Bảng tần suất

H1 <- table(d$Homeowner)/sum(nrow(d))
H1
## 
##         N         Y 
## 0.3993883 0.6006117

Bảng tần số

H2<-table(d$Homeowner)
H2
## 
##    N    Y 
## 5615 8444

Vậy trong data này có 39.9388292% đã có nhà và 60.0611708% chưa có nhà

Biểu đồ

# Vẽ biểu đồ tròn
pie(H2 ,
    main   = "Phân bố sở hữu nhà",
    labels = paste0(names(H2),
                    " (", round(H1 *100, 1), "%)"),
    col    = c("blue", "red"))

2.10. Mô tả biến AnnualIncome

Bảng tần suất

A1 <- table(d$AnnualIncome)/sum(nrow(dldt))*100
A1
## 
##   $10K - $30K $110K - $130K $130K - $150K       $150K +   $30K - $50K 
##     21.978804      4.573583      5.405790      1.941817     32.726367 
##   $50K - $70K   $70K - $90K  $90K - $110K 
##     16.857529     12.155914      4.360196

Bảng tần số

A2<-table(d$AnnualIncome)
A2
## 
##   $10K - $30K $110K - $130K $130K - $150K       $150K +   $30K - $50K 
##          3090           643           760           273          4601 
##   $50K - $70K   $70K - $90K  $90K - $110K 
##          2370          1709           613

Nhóm 30K–50k chiếm tỷ lệ lớn nhất với 32.7%, cao gấp hơn 16 lần so với nhóm thu nhập cao nhất ($150K+), chỉ đạt 1.94%.

Các nhóm thu nhập từ 90K trở lên (gồm 4 nhóm) đều có tỷ lệ dưới 5.5%, thấp hơn nhóm trung bình 30K–50K từ ~27–31 điểm phần trăm, cho thấy mức độ tham gia rất thấp từ nhóm thu nhập cao.

Ngược lại, nhóm thu nhập thấp (10K–30K) chiếm ~22%, thấp hơn nhóm trung bình ~10.7 điểm phần trăm, nhưng vẫn cho thấy sự tham gia tương đối đáng kể.

Nhìn chung, dữ liệu thể hiện một xu hướng rõ ràng: giao dịch tập trung chủ yếu ở nhóm thu nhập trung bình–thấp, trong khi nhóm thu nhập cao có tỷ lệ tham gia rất khiêm tốn, phản ánh sự phân bố không đồng đều về mặt thu nhập trong tập khách hàng.

Biểu đồ

library(ggplot2)
library(dplyr)


# Đếm số giao dịch theo AnnualIncome và sắp xếp
at_counts <- dldt %>%
  count(AnnualIncome)

ggplot(at_counts, aes(x = reorder(AnnualIncome, -n), y = n, fill = AnnualIncome)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = n), vjust = -0.3, size = 3) +
  labs(title = "Doanh mục mua hàng theo Annual Income",
       x = "Thu nhập hằng năm (AnnualIncome)",
       y = "Số lượng giao dịch",
       fill = "AnnualIncome") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_fill_viridis_d(option = "C", begin = 0.2, end = 0.9)  

3. Ước lượng Khoảng và Kiểm định Giả thuyết cho Tỷ lệ (Một biến)

3.1 Gender “Nữ”

Giả sử “Nữ” có mã là “F” trong cột ‘Gender’. Kiểm tra levels(dldt$Gender) để chắc chắn.

# Ghi chú: Đoạn code về 'table_rr' và 'riskratio' đã được loại bỏ khỏi đây.
# Nếu bạn muốn phân tích mối quan hệ giữa Gender và Homeowner, nó nên ở Phần 4.
category_female <- "F" 
N_female <- sum(dldt$Gender == category_female) # Số lượng nữ
N_total_gender  <- nrow(dldt)                # Tổng số quan sát

cat(paste("Số người nữ (Gender =", category_female, "):", N_female, "\n"))
## Số người nữ (Gender = F ): 7170
cat(paste("Tổng số quan sát:", N_total_gender, "\n\n"))
## Tổng số quan sát: 14059

Giả thuyết: H0: Tỷ lệ nữ trong tổng thể là 50% (p_female = 0.5) H1: Tỷ lệ nữ trong tổng thể khác 50% (p_female != 0.5)

p0_female <- 0.5
# Tính khoảng tin cậy 95% và kiểm định giả thuyết
test_female_results <- prop.test(x = N_female, n = N_total_gender, p = p0_female, correct = FALSE)

cat("Kết quả kiểm định cho tỷ lệ nữ:\n")
## Kết quả kiểm định cho tỷ lệ nữ:
print(test_female_results)
## 
##  1-sample proportions test without continuity correction
## 
## data:  N_female out of N_total_gender, null probability p0_female
## X-squared = 5.6164, df = 1, p-value = 0.01779
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.5017287 0.5182531
## sample estimates:
##         p 
## 0.5099936
# Trích xuất các giá trị từ kết quả kiểm định
p_value_female <- test_female_results$p.value
sample_p_female <- test_female_results$estimate
conf_int_female <- test_female_results$conf.int

cat("\nDiễn giải kết quả kiểm định cho Giới tính Nữ:\n")
## 
## Diễn giải kết quả kiểm định cho Giới tính Nữ:
cat(paste0("  - Khoảng tin cậy 95% cho tỷ lệ nữ trong tổng thể là (", 
           round(conf_int_female[1], 4), ", ", 
           round(conf_int_female[2], 4), ").\n"))
##   - Khoảng tin cậy 95% cho tỷ lệ nữ trong tổng thể là (0.5017, 0.5183).
cat(paste0("    Ý nghĩa: Chúng ta tin tưởng 95% rằng tỷ lệ thực sự của nữ trong toàn bộ khách hàng nằm trong khoảng này.\n"))
##     Ý nghĩa: Chúng ta tin tưởng 95% rằng tỷ lệ thực sự của nữ trong toàn bộ khách hàng nằm trong khoảng này.
cat(paste0("  - Tỷ lệ nữ trong mẫu (p-hat) là: ", round(sample_p_female, 4), " (hay ", round(sample_p_female*100, 2), "%).\n"))
##   - Tỷ lệ nữ trong mẫu (p-hat) là: 0.51 (hay 51%).
cat(paste0("  - Giá trị p-value của kiểm định là: ", format.pval(p_value_female, digits=4, eps=0.0001), ".\n"))
##   - Giá trị p-value của kiểm định là: 0.01779.
alpha <- 0.05 # Mức ý nghĩa
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (p_value_female < alpha) {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(p_value_female, digits=4, eps=0.0001), ") < ", alpha, 
             ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  cat(paste0("    Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ nữ thực sự trong tổng thể KHÁC ", p0_female*100, "%.\n"))
} else {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(p_value_female, digits=4, eps=0.0001), ") >= ", alpha, 
             ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
  cat(paste0("    Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ nữ thực sự trong tổng thể khác ", p0_female*100, 
             "%. (Tức là, không thể loại trừ khả năng tỷ lệ nữ là 50%).\n"))
}
##   - Kết luận: Vì p-value (0.01779) < 0.05, chúng ta BÁC BỎ giả thuyết H0.
##     Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ nữ thực sự trong tổng thể KHÁC 50%.
library(knitr)
library(kableExtra)

result_meaning_female_df <- data.frame(
  Phần = c(
    "X-squared",
    "df",
    "p-value",
    "alternative hypothesis",
    "95% confidence interval",
    "sample estimate p"
  ),
  Giá_trị_từ_R = c(
    round(test_female_results$statistic, 4),
    test_female_results$parameter,
    format.pval(test_female_results$p.value, digits = 4, eps = 0.0001),
    test_female_results$alternative,
    paste0("(", round(test_female_results$conf.int[1], 4), ", ", round(test_female_results$conf.int[2], 4), ")"),
    round(test_female_results$estimate, 7)
  ),
  Ý_nghĩa_chung = c(
    "Giá trị thống kê kiểm định Chi-square.",
    "Bậc tự do của kiểm định.",
    "Xác suất để dữ liệu quan sát được (hoặc cực đoan hơn) xuất hiện nếu giả thuyết H₀ đúng.",
    "Giả thuyết thay thế được sử dụng (ở đây là hai phía).",
    "Khoảng tin cậy 95% cho tỷ lệ trong tổng thể.",
    "Tỷ lệ ước lượng từ dữ liệu mẫu."
  ),
  stringsAsFactors = FALSE
)

kable(result_meaning_female_df,
      col.names = c("Phần Output", "Giá trị từ R", "Ý nghĩa chung"),
      align = c("l", "r", "l"),
      caption = "Giải thích kết quả của `prop.test()` cho giới tính Nữ") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "bordered", "hover")) %>%
  column_spec(1, bold = TRUE)
Giải thích kết quả của prop.test() cho giới tính Nữ
Phần Output Giá trị từ R Ý nghĩa chung
X-squared 5.6164 Giá trị thống kê kiểm định Chi-square.
df 1 Bậc tự do của kiểm định.
p-value 0.01779 Xác suất để dữ liệu quan sát được (hoặc cực đoan hơn) xuất hiện nếu giả thuyết H₀ đúng.
alternative hypothesis two.sided Giả thuyết thay thế được sử dụng (ở đây là hai phía).
95% confidence interval (0.5017, 0.5183) Khoảng tin cậy 95% cho tỷ lệ trong tổng thể.
sample estimate p 0.5099936 Tỷ lệ ước lượng từ dữ liệu mẫu.

3.2 Homeowner “Y”

Giả sử “Có nhà” có mã là “Y” trong cột ‘Homeowner’. Kiểm tra levels(dldt$Homeowner).

# Số người có nhà
category_homeowner_y <- "Y" 
N_homeowner_y <- sum(dldt$Homeowner == category_homeowner_y)
# Tổng số quan sát
N_total_homeowner  <- nrow(dldt)                           # Tổng số quan sát

cat(paste("Số người sở hữu nhà (Homeowner =", category_homeowner_y, "):", N_homeowner_y, "\n"))
## Số người sở hữu nhà (Homeowner = Y ): 8444
cat(paste("Tổng số quan sát:", N_total_homeowner, "\n\n"))
## Tổng số quan sát: 14059

Giả thuyết H0: Tỷ lệ người sở hữu nhà là 60% (p_homeowner = 0.6) H1: Tỷ lệ người sở hữu nhà khác 60% (p_homeowner != 0.6)

p0_homeowner <- 0.6
test_homeowner_results <- prop.test(x = N_homeowner_y, n = N_total_homeowner, p = p0_homeowner, correct = FALSE)
cat("Kết quả kiểm định cho tỷ lệ sở hữu nhà:\n")
## Kết quả kiểm định cho tỷ lệ sở hữu nhà:
print(test_homeowner_results)
## 
##  1-sample proportions test without continuity correction
## 
## data:  N_homeowner_y out of N_total_homeowner, null probability p0_homeowner
## X-squared = 0.02192, df = 1, p-value = 0.8823
## alternative hypothesis: true p is not equal to 0.6
## 95 percent confidence interval:
##  0.5924894 0.6086791
## sample estimates:
##         p 
## 0.6006117
p_value_homeowner <- test_homeowner_results$p.value
sample_p_homeowner <- test_homeowner_results$estimate
conf_int_homeowner <- test_homeowner_results$conf.int
cat("\nDiễn giải kết quả kiểm định cho Sở hữu nhà 'Y':\n")
## 
## Diễn giải kết quả kiểm định cho Sở hữu nhà 'Y':
cat(paste0("  - Khoảng tin cậy 95% cho tỷ lệ sở hữu nhà là (", 
           round(conf_int_homeowner[1], 4), ", ", round(conf_int_homeowner[2], 4), ").\n"))
##   - Khoảng tin cậy 95% cho tỷ lệ sở hữu nhà là (0.5925, 0.6087).
cat(paste0("  - Tỷ lệ sở hữu nhà trong mẫu (p-hat) là: ", round(sample_p_homeowner, 4), ".\n"))
##   - Tỷ lệ sở hữu nhà trong mẫu (p-hat) là: 0.6006.
cat(paste0("  - Giá trị p-value của kiểm định là: ", format.pval(p_value_homeowner, digits=4, eps=0.0001), ".\n"))
##   - Giá trị p-value của kiểm định là: 0.8823.
alpha <- 0.05
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (p_value_homeowner < alpha) {
  cat(paste0("  - Kết luận: Vì p-value < ", alpha, ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  cat(paste0("    Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ người sở hữu nhà thực sự trong tổng thể KHÁC ", p0_homeowner*100, "%.\n"))
} else {
  cat(paste0("  - Kết luận: Vì p-value >= ", alpha, ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
  cat(paste0("    Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ người sở hữu nhà khác ", p0_homeowner*100, "%.\n"))
}
##   - Kết luận: Vì p-value >= 0.05, chúng ta KHÔNG BÁC BỎ giả thuyết H0.
##     Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ người sở hữu nhà khác 60%.
# Bảng giải thích  (đã sửa để lấy giá trị động)
result_meaning_H_df <- data.frame(
  Phần = c("X-squared", "df", "p-value", "alternative hypothesis", "95% confidence interval", "sample estimate p"),
  Giá_trị_từ_R = c(
      round(test_homeowner_results$statistic, 2), # Sửa lại số liệu cho khớp
      test_homeowner_results$parameter,
      format.pval(test_homeowner_results$p.value, digits=4, eps=0.0001), # Hiển thị p-value đúng
      test_homeowner_results$alternative,
      paste0("(", round(test_homeowner_results$conf.int[1],4), ", ", round(test_homeowner_results$conf.int[2],4), ")"),
      round(test_homeowner_results$estimate, 7)
  ),
  Ý_nghĩa = c(
    "Giá trị thống kê kiểm định Chi-square.",
    "Bậc tự do.",
    "Xác suất quan sát dữ liệu như hiện tại hoặc cực đoan hơn khi H0 đúng.",
    "Kiểm định hai phía.",
    "Khoảng tin cậy 95% cho tỷ lệ có nhà thật trong dân số.",
    "Tỷ lệ có nhà trong mẫu."
  ),
  stringsAsFactors = FALSE
)
kable(result_meaning_H_df, col.names = c("Phần Output", "Giá trị từ R", "Ý nghĩa"), align = c("l", "r", "l"),
      caption = "Giải thích output của prop.test cho Sở hữu nhà 'Y'") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "bordered", "hover")) %>%
  column_spec(1, bold = TRUE)
Giải thích output của prop.test cho Sở hữu nhà ‘Y’
Phần Output Giá trị từ R Ý nghĩa
X-squared 0.02 Giá trị thống kê kiểm định Chi-square.
df 1 Bậc tự do.
p-value 0.8823 Xác suất quan sát dữ liệu như hiện tại hoặc cực đoan hơn khi H0 đúng.
alternative hypothesis two.sided Kiểm định hai phía.
95% confidence interval (0.5925, 0.6087) Khoảng tin cậy 95% cho tỷ lệ có nhà thật trong dân số.
sample estimate p 0.6006117 Tỷ lệ có nhà trong mẫu.

3.3 ProductFamily “Food”

# Số sản phẩm (ProductFamily = "Food") và tổng số quan sát
category_food <- "Food" 
N_food <- sum(dldt$ProductFamily == category_food) 
# Tổng số sản phẩm
N_total_productfamily  <- nrow(dldt)               

cat(paste("Số sản phẩm 'Food' (ProductFamily =", category_food, "):", N_food, "\n"))
## Số sản phẩm 'Food' (ProductFamily = Food ): 10153
cat(paste("Tổng số sản phẩm quan sát:", N_total_productfamily, "\n\n"))
## Tổng số sản phẩm quan sát: 14059

Giả thuyết H0: p_food >= 0.7

p0_food <- 0.7
alternative_hyp_food <- "two.sided"
test_food_results <- prop.test(x = N_food, n = N_total_productfamily, p = p0_food, 
                               alternative = alternative_hyp_food, correct = FALSE)
cat("Kết quả kiểm định cho tỷ lệ sản phẩm 'Food':\n")
## Kết quả kiểm định cho tỷ lệ sản phẩm 'Food':
print(test_food_results)
## 
##  1-sample proportions test without continuity correction
## 
## data:  N_food out of N_total_productfamily, null probability p0_food
## X-squared = 32.908, df = 1, p-value = 9.663e-09
## alternative hypothesis: true p is not equal to 0.7
## 95 percent confidence interval:
##  0.7147067 0.7295136
## sample estimates:
##         p 
## 0.7221709
p_value_food <- test_food_results$p.value
sample_p_food <- test_food_results$estimate
conf_int_food <- test_food_results$conf.int

cat("\nDiễn giải kết quả kiểm định cho ProductFamily 'Food':\n")
## 
## Diễn giải kết quả kiểm định cho ProductFamily 'Food':
cat(paste0("  - Khoảng tin cậy 95% cho tỷ lệ 'Food' là (", 
           round(conf_int_food[1], 4), ", ", round(conf_int_food[2], 4), ").\n"))
##   - Khoảng tin cậy 95% cho tỷ lệ 'Food' là (0.7147, 0.7295).
cat(paste0("  - Tỷ lệ 'Food' trong mẫu (p-hat) là: ", round(sample_p_food, 4), ".\n"))
##   - Tỷ lệ 'Food' trong mẫu (p-hat) là: 0.7222.
cat(paste0("  - Giá trị p-value của kiểm định là: ", format.pval(p_value_food, digits=4, eps=0.0001), ".\n"))
##   - Giá trị p-value của kiểm định là: < 1e-04.
alpha <- 0.05
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (p_value_food < alpha) {
  cat(paste0("  - Kết luận: Vì p-value < ", alpha, ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  if (alternative_hyp_food == "two.sided") {
    cat(paste0("    Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' thực sự trong tổng thể KHÁC ", p0_food*100, "%.\n"))
  } else if (alternative_hyp_food == "less") {
    cat(paste0("    Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' thực sự trong tổng thể NHỎ HƠN ", p0_food*100, "%.\n"))
  } else { # greater
     cat(paste0("    Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' thực sự trong tổng thể LỚN HƠN ", p0_food*100, "%.\n"))
  }
} else {
  cat(paste0("  - Kết luận: Vì p-value >= ", alpha, ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
   if (alternative_hyp_food == "two.sided") {
    cat(paste0("    Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' khác ", p0_food*100, "%.\n"))
  } else if (alternative_hyp_food == "less") {
    cat(paste0("    Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' nhỏ hơn ", p0_food*100, "% (tức là không thể bác bỏ H0: p >= ", p0_food*100,"%).\n"))
  } else { # greater
     cat(paste0("    Không có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' lớn hơn ", p0_food*100, "% (tức là không thể bác bỏ H0: p <= ", p0_food*100,"%).\n"))
  }
}
##   - Kết luận: Vì p-value < 0.05, chúng ta BÁC BỎ giả thuyết H0.
##     Có đủ bằng chứng thống kê để kết luận rằng tỷ lệ sản phẩm 'Food' thực sự trong tổng thể KHÁC 70%.
# Bảng giải thích 
result_meaning_P_df <- data.frame(
  Phần = c("X-squared", "df", "p-value", "alternative hypothesis", "95% confidence interval", "sample estimate p"),
  Giá_trị_từ_R = c(
      round(test_food_results$statistic, 1), 
      test_food_results$parameter,
      format.pval(test_food_results$p.value, digits=4, eps=0.0001),
      test_food_results$alternative,
      paste0("(", round(test_food_results$conf.int[1],4), ", ", round(test_food_results$conf.int[2],4), ")"),
      round(test_food_results$estimate, 7)
  ),
  Ý_nghĩa = c(
    "Giá trị thống kê Chi-square.",
    "Bậc tự do.",
    "Giá trị p.",
    "Kiểm định (hai phía, nhỏ hơn, hoặc lớn hơn).",
    "Khoảng tin cậy 95% cho tỷ lệ sản phẩm 'Food' thực tế.",
    "Tỷ lệ sản phẩm 'Food' ước lượng từ mẫu."
  ),
  stringsAsFactors = FALSE
)
kable(result_meaning_P_df, col.names = c("Phần Output", "Giá trị từ R", "Ý nghĩa"), align = c("l", "r", "l"),
      caption = "Giải thích output của prop.test cho ProductFamily 'Food'") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "bordered", "hover")) %>%
  column_spec(1, bold = TRUE)
Giải thích output của prop.test cho ProductFamily ‘Food’
Phần Output Giá trị từ R Ý nghĩa
X-squared 32.9 Giá trị thống kê Chi-square.
df 1 Bậc tự do.
p-value < 1e-04 Giá trị p. 
alternative hypothesis two.sided Kiểm định (hai phía, nhỏ hơn, hoặc lớn hơn).
95% confidence interval (0.7147, 0.7295) Khoảng tin cậy 95% cho tỷ lệ sản phẩm ‘Food’ thực tế.
sample estimate p 0.7221709 Tỷ lệ sản phẩm ‘Food’ ước lượng từ mẫu.

4. Phân tích Mối quan hệ giữa Hai biến Định tính (Bivariate Analysis)

4.1 Marital và ProductFamily

Bảng tần suất

gp <- table(Gender = dldt$Gender, ProductFamily = dldt$ProductFamily)
cat("Bảng tần suất chéo giữa Gender và ProductFamily (Số lượng):\n")
## Bảng tần suất chéo giữa Gender và ProductFamily (Số lượng):
print(gp)
##       ProductFamily
## Gender Drink Food Non-Consumable
##      F   669 5149           1352
##      M   581 5004           1304
# Bảng tỷ lệ phần trăm theo hàng (trong mỗi Gender, tỷ lệ mua các ProductFamily)
# margin = 1: tính tỷ lệ theo hàng. Giúp so sánh xu hướng chọn ProductFamily giữa các nhóm Gender.
gp1 <- round(prop.table(gp, margin = 1) * 100, 1)
cat("\nBảng tỷ lệ % theo hàng (trong mỗi Gender, phân phối ProductFamily):\n")
## 
## Bảng tỷ lệ % theo hàng (trong mỗi Gender, phân phối ProductFamily):
print(gp1)
##       ProductFamily
## Gender Drink Food Non-Consumable
##      F   9.3 71.8           18.9
##      M   8.4 72.6           18.9

Trực quan hóa

# Chuyển bảng sang dataframe để ggplot
gpf<- as.data.frame(gp)
colnames(gpf) <- c("Gender", "ProductFamily", "Freq") 

ggplot(gpf, aes(x = Gender, y = Freq, fill = ProductFamily)) +
  geom_col(position = position_dodge(width = 0.8),  width = 0.7) +
  # scale_fill_manual của bạn dùng được vì ProductFamily ít mức
  scale_fill_manual(
    name   = "Nhóm sản phẩm",
    values = c("Drink" = "skyblue", "Food" = "lightgreen", "Non-Consumable" = "salmon"),
    labels = c("Đồ uống", "Thực phẩm", "Phi tiêu dùng") # Nhãn cho chú thích
  ) +
  labs(
    title = "Số giao dịch theo Nhóm sản phẩm & Giới tính",
    x     = "Giới tính",
    y     = "Số lượng giao dịch"
  ) +
  theme_minimal()

** KIểm định chi bình phương**

cat("\n\n**Kiểm định Chi-bình phương cho Gender và ProductFamily:**\n")
## 
## 
## **Kiểm định Chi-bình phương cho Gender và ProductFamily:**
cat("H0: Giới tính (Gender) và Nhóm sản phẩm (ProductFamily) là độc lập (không có mối quan hệ).\n")
## H0: Giới tính (Gender) và Nhóm sản phẩm (ProductFamily) là độc lập (không có mối quan hệ).
cat("H1: Giới tính (Gender) và Nhóm sản phẩm (ProductFamily) có liên quan.\n\n")
## H1: Giới tính (Gender) và Nhóm sản phẩm (ProductFamily) có liên quan.
chisq_gp <- chisq.test(gp)
print(chisq_gp)
## 
##  Pearson's Chi-squared test
## 
## data:  gp
## X-squared = 3.5185, df = 2, p-value = 0.1722
alpha_chi <- 0.05
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha_chi, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (chisq_gp$p.value < alpha_chi) {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_gp$p.value, digits=4, eps=0.0001), ") < ", alpha_chi, 
             ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  cat("    Có bằng chứng thống kê về mối quan hệ ý nghĩa giữa Giới tính và Nhóm sản phẩm.\n")
  cat("    Để hiểu rõ hơn về bản chất mối quan hệ, hãy xem xét bảng tỷ lệ phần trăm và biểu đồ.\n")
} else {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_gp$p.value, digits=4, eps=0.0001), ") >= ", alpha_chi, 
             ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
  cat("    Không có đủ bằng chứng thống kê để kết luận có mối quan hệ ý nghĩa giữa Giới tính và Nhóm sản phẩm.\n")
}
##   - Kết luận: Vì p-value (0.1722) >= 0.05, chúng ta KHÔNG BÁC BỎ giả thuyết H0.
##     Không có đủ bằng chứng thống kê để kết luận có mối quan hệ ý nghĩa giữa Giới tính và Nhóm sản phẩm.
# Kiểm tra điều kiện tần số kỳ vọng
if(any(chisq_gp$expected < 5)){
    cat("\nCẢNH BÁO: Có một số ô trong bảng có tần suất kỳ vọng nhỏ hơn 5. Kết quả của kiểm định Chi-bình phương có thể không hoàn toàn chính xác.\n")
    # print("Tần suất kỳ vọng:")
    # print(round(chisq_test_gp$expected, 2))
}

4.2 MaritalStatus và Homeowner

Bảng tần suất

mh <- table(MaritalStatus = dldt$MaritalStatus, Homeowner = dldt$Homeowner)
cat("Bảng tần suất chéo giữa Tình trạng hôn nhân và Sở hữu nhà (Số lượng):\n")
## Bảng tần suất chéo giữa Tình trạng hôn nhân và Sở hữu nhà (Số lượng):
print(mh)
##              Homeowner
## MaritalStatus    N    Y
##             M 1719 5147
##             S 3896 3297
mh1 <- round(prop.table(mh, margin = 1) * 100, 1)
cat("\nBảng tỷ lệ % theo hàng (trong mỗi Tình trạng hôn nhân, phân phối Sở hữu nhà):\n")
## 
## Bảng tỷ lệ % theo hàng (trong mỗi Tình trạng hôn nhân, phân phối Sở hữu nhà):
print(mh1)
##              Homeowner
## MaritalStatus    N    Y
##             M 25.0 75.0
##             S 54.2 45.8
mhdf <- as.data.frame(mh)
colnames(mhdf) <- c("Marital", "Owner", "Freq")

ggplot(mhdf, aes(x = Marital, y = Freq, fill = Owner)) +
  geom_col(position = position_dodge(width = 0.8),  width = 0.7) +
  scale_fill_manual(
    name   = "Sở hữu nhà",
    values = c("Y" = "steelblue", "N" = "pink"), # Đảm bảo "Y", "N" là các mức của Homeowner
    labels = c("Có nhà", "Không có nhà")
  ) +
  labs(
    title = "Số khách có/không sở hữu nhà theo Tình trạng hôn nhân",
    x     = "Tình trạng hôn nhân",
    y     = "Số khách hàng"
  ) +
  theme_minimal()

Kiểm định chi bình phương

cat("\n\n**Kiểm định Chi-bình phương cho MaritalStatus và Homeowner:**\n")
## 
## 
## **Kiểm định Chi-bình phương cho MaritalStatus và Homeowner:**
cat("H0: Tình trạng hôn nhân (MaritalStatus) và Sở hữu nhà (Homeowner) là độc lập.\n")
## H0: Tình trạng hôn nhân (MaritalStatus) và Sở hữu nhà (Homeowner) là độc lập.
cat("H1: Tình trạng hôn nhân (MaritalStatus) và Sở hữu nhà (Homeowner) có liên quan.\n\n")
## H1: Tình trạng hôn nhân (MaritalStatus) và Sở hữu nhà (Homeowner) có liên quan.
chisq_mh <- chisq.test(mh)
print(chisq_mh)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  mh
## X-squared = 1241.2, df = 1, p-value < 2.2e-16
alpha_chi <- 0.05
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha_chi, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (chisq_mh$p.value < alpha_chi) {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_mh$p.value, digits=4, eps=0.0001), ") < ", alpha_chi, 
             ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  cat("    Có bằng chứng thống kê về mối quan hệ ý nghĩa giữa Tình trạng hôn nhân và Sở hữu nhà.\n")
} else {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_mh$p.value, digits=4, eps=0.0001), ") >= ", alpha_chi, 
             ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
  cat("    Không có đủ bằng chứng thống kê để kết luận có mối quan hệ ý nghĩa giữa Tình trạng hôn nhân và Sở hữu nhà.\n")
}
##   - Kết luận: Vì p-value (< 1e-04) < 0.05, chúng ta BÁC BỎ giả thuyết H0.
##     Có bằng chứng thống kê về mối quan hệ ý nghĩa giữa Tình trạng hôn nhân và Sở hữu nhà.
if(any(chisq_mh$expected < 5)){
    cat("\nCẢNH BÁO: Có một số ô có tần suất kỳ vọng < 5. Kết quả Chi-bình phương có thể không chính xác.\n")
}

4.3 AnnualIncome và ProductCategory

Bảng tần suất

ap <- table(AnnualIncome = dldt$AnnualIncome, ProductCategory = dldt$ProductCategory)
cat("Bảng tần suất chéo giữa Thu nhập hàng năm và Danh mục sản phẩm (Số lượng) - Hiển thị một phần do có thể rất lớn:\n")
## Bảng tần suất chéo giữa Thu nhập hàng năm và Danh mục sản phẩm (Số lượng) - Hiển thị một phần do có thể rất lớn:
# Do bảng có thể rất lớn, chỉ in một phần hoặc tóm tắt
if (ncol(ap) > 10) {
    print(ap[, 1:10]) # In 10 cột đầu tiên
    cat("... và các cột khác ...\n")
} else {
    print(ap)
}
##                ProductCategory
## AnnualIncome    Baking Goods Bathroom Products Beer and Wine Bread
##   $10K - $30K            119                85            80   108
##   $110K - $130K           18                16            14    23
##   $130K - $150K           22                19            15    24
##   $150K +                 11                 8             3    10
##   $30K - $50K            151               116           121   134
##   $50K - $70K             86                50            61    63
##   $70K - $90K             59                51            39    50
##   $90K - $110K            18                20            23    13
##                ProductCategory
## AnnualIncome    Breakfast Foods Candles Candy Canned Anchovies Canned Clams
##   $10K - $30K               111       6    76               10           12
##   $110K - $130K               7       1    16                2            1
##   $130K - $150K              28       2    19                1            3
##   $150K +                     7       1     8                1            1
##   $30K - $50K               143      21   127               14           20
##   $50K - $70K                62       7    60                8            6
##   $70K - $90K                42       7    29                6            7
##   $90K - $110K               17       0    17                2            3
##                ProductCategory
## AnnualIncome    Canned Oysters
##   $10K - $30K                5
##   $110K - $130K              4
##   $130K - $150K              3
##   $150K +                    1
##   $30K - $50K               10
##   $50K - $70K                5
##   $70K - $90K                5
##   $90K - $110K               2
## ... và các cột khác ...
ap1 <- round(prop.table(ap, margin = 1) * 100, 1)
cat("\nBảng tỷ lệ % theo hàng (trong mỗi mức Thu nhập, phân phối Danh mục sản phẩm) - Hiển thị một phần:\n")
## 
## Bảng tỷ lệ % theo hàng (trong mỗi mức Thu nhập, phân phối Danh mục sản phẩm) - Hiển thị một phần:
if (ncol(ap1) > 10) {
    print(ap1[, 1:10])
    cat("... và các cột khác ...\n")
} else {
    print(ap1)
}
##                ProductCategory
## AnnualIncome    Baking Goods Bathroom Products Beer and Wine Bread
##   $10K - $30K            3.9               2.8           2.6   3.5
##   $110K - $130K          2.8               2.5           2.2   3.6
##   $130K - $150K          2.9               2.5           2.0   3.2
##   $150K +                4.0               2.9           1.1   3.7
##   $30K - $50K            3.3               2.5           2.6   2.9
##   $50K - $70K            3.6               2.1           2.6   2.7
##   $70K - $90K            3.5               3.0           2.3   2.9
##   $90K - $110K           2.9               3.3           3.8   2.1
##                ProductCategory
## AnnualIncome    Breakfast Foods Candles Candy Canned Anchovies Canned Clams
##   $10K - $30K               3.6     0.2   2.5              0.3          0.4
##   $110K - $130K             1.1     0.2   2.5              0.3          0.2
##   $130K - $150K             3.7     0.3   2.5              0.1          0.4
##   $150K +                   2.6     0.4   2.9              0.4          0.4
##   $30K - $50K               3.1     0.5   2.8              0.3          0.4
##   $50K - $70K               2.6     0.3   2.5              0.3          0.3
##   $70K - $90K               2.5     0.4   1.7              0.4          0.4
##   $90K - $110K              2.8     0.0   2.8              0.3          0.5
##                ProductCategory
## AnnualIncome    Canned Oysters
##   $10K - $30K              0.2
##   $110K - $130K            0.6
##   $130K - $150K            0.4
##   $150K +                  0.4
##   $30K - $50K              0.2
##   $50K - $70K              0.2
##   $70K - $90K              0.3
##   $90K - $110K             0.3
## ... và các cột khác ...

Trực quan dữ liệu

apdf<- as.data.frame(ap)
colnames(apdf) <- c("AnnualIncome", "ProductCategory", "Freq")


ggplot(apdf, aes(x = AnnualIncome, y = Freq, fill = ProductCategory)) +
  geom_col(position = position_dodge(width = 0.9), width = 0.8) + # position_dodge để các cột nằm cạnh nhau
  labs(
    title = "Sản lượng theo Thu nhập và Danh mục sản phẩm",
    subtitle = "Lưu ý: Biểu đồ có thể phức tạp do nhiều Danh mục sản phẩm",
    x     = "Thu nhập hàng năm",
    y     = "Số lượng giao dịch"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Xoay nhãn trục x
        legend.position = if(nlevels(apdf$ProductCategory) > 10) "none" else "right") # Ẩn legend nếu quá nhiều

Kiểm định chi bình phương

cat("\n\n**Kiểm định Chi-bình phương cho AnnualIncome và ProductCategory:**\n")
## 
## 
## **Kiểm định Chi-bình phương cho AnnualIncome và ProductCategory:**
cat("H0: Thu nhập hàng năm (AnnualIncome) và Danh mục sản phẩm (ProductCategory) là độc lập.\n")
## H0: Thu nhập hàng năm (AnnualIncome) và Danh mục sản phẩm (ProductCategory) là độc lập.
cat("H1: Thu nhập hàng năm (AnnualIncome) và Danh mục sản phẩm (ProductCategory) có liên quan.\n\n")
## H1: Thu nhập hàng năm (AnnualIncome) và Danh mục sản phẩm (ProductCategory) có liên quan.
# Do số lượng levels của ProductCategory lớn, tần số kỳ vọng có thể rất thấp nhiều ô. Do đó có thể gí trị chi bình phương sẽ không quá đúng

chisq_ap <- chisq.test(ap)
## Warning in chisq.test(ap): Chi-squared approximation may be incorrect
print(chisq_ap)
## 
##  Pearson's Chi-squared test
## 
## data:  ap
## X-squared = 295.23, df = 308, p-value = 0.6897
alpha_chi <- 0.05
cat(paste0("\nVới mức ý nghĩa alpha = ", alpha_chi, ":\n"))
## 
## Với mức ý nghĩa alpha = 0.05:
if (chisq_ap$p.value < alpha_chi) {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_ap$p.value, digits=4, eps=0.0001), ") < ", alpha_chi, 
             ", chúng ta BÁC BỎ giả thuyết H0.\n"))
  cat("    Có bằng chứng thống kê về mối quan hệ ý nghĩa giữa Thu nhập hàng năm và Danh mục sản phẩm.\n")
} else {
  cat(paste0("  - Kết luận: Vì p-value (", format.pval(chisq_ap$p.value, digits=4, eps=0.0001), ") >= ", alpha_chi, 
             ", chúng ta KHÔNG BÁC BỎ giả thuyết H0.\n"))
  cat("    Không có đủ bằng chứng thống kê để kết luận có mối quan hệ ý nghĩa giữa Thu nhập hàng năm và Danh mục sản phẩm.\n")
}
##   - Kết luận: Vì p-value (0.6897) >= 0.05, chúng ta KHÔNG BÁC BỎ giả thuyết H0.
##     Không có đủ bằng chứng thống kê để kết luận có mối quan hệ ý nghĩa giữa Thu nhập hàng năm và Danh mục sản phẩm.
if(any(chisq_ap$expected < 1)){ # Điều kiện chặt hơn: tần số kỳ vọng < 1
    cat("\nCẢNH BÁO NGHIÊM TRỌNG: Có nhiều ô có tần suất kỳ vọng RẤT THẤP (có thể < 1).\n")
    cat("Kết quả của kiểm định Chi-bình phương cho cặp biến này RẤT KHÔNG ĐÁNG TIN CẬY.\n")
    cat("Nên xem xét nhóm các mức của 'ProductCategory' lại trước khi kiểm định.\n")
} else if(any(chisq_ap$expected < 5)){
    cat("\nCẢNH BÁO: Có một số ô có tần suất kỳ vọng < 5. Kết quả Chi-bình phương có thể không chính xác.\n")
}
## 
## CẢNH BÁO NGHIÊM TRỌNG: Có nhiều ô có tần suất kỳ vọng RẤT THẤP (có thể < 1).
## Kết quả của kiểm định Chi-bình phương cho cặp biến này RẤT KHÔNG ĐÁNG TIN CẬY.
## Nên xem xét nhóm các mức của 'ProductCategory' lại trước khi kiểm định.

B.Hoạt động trên lớp

1. Lý thuyết

1.1 Giới thiệu

Trong phân tích thống kê định lượng, bảng ngẫu nhiên (contingency table) là một công cụ cơ bản để khảo sát mối liên hệ giữa hai biến phân loại. Trong trường hợp đơn giản nhất, bảng ngẫu nhiên 2x2 biểu diễn sự phân bố của hai biến nhị phân, giúp xác định và đo lường mối liên hệ giữa chúng. Các chỉ số như hiệu tỷ lệ (risk difference), tỷ số nguy cơ (Relative Risk - RR) và tỷ số chênh (Odds Ratio - OR) đóng vai trò quan trọng trong việc lượng hóa mối quan hệ đó. Bài viết này sẽ trình bày chi tiết cấu trúc xác suất sinh ra bảng ngẫu nhiên, phương pháp so sánh hai tỷ lệ, cách xây dựng khoảng tin cậy cho Odds Ratio, và kết thúc bằng một ví dụ thực tiễn trong lĩnh vực kinh doanh.

1.2. Cấu trúc và xác suất của bảng ngẫu nhiên

Một bảng ngẫu nhiên 2x2 là bảng tần suất đếm số quan sát thuộc vào từng tổ hợp của hai biến nhị phân:

\[ \begin{array}{|c|c|c|c|} \hline & \text{Kết quả (+)} & \text{Kết quả (–)} & \text{Tổng} \\ \hline \text{Phơi nhiễm (Yes)} & a & b & a + b \\ \hline \text{Không phơi nhiễm (No)} & c & d & c + d \\ \hline \text{Tổng cộng} & a + c & b + d & n = a + b + c + d \\ \hline \end{array} \]

Để hiểu được sự hình thành của bảng này, cần xác định mô hình xác suất sinh ra dữ liệu — trong đó phổ biến nhất là phân phối PoissonMultinomial.

1.3 Phân phối Poisson

Phân phối Poisson thường được sử dụng để mô hình hóa số lượng sự kiện xảy ra trong một khoảng thời gian, không gian hoặc đơn vị cụ thể. Giả sử các sự kiện xảy ra độc lập và với một tỷ lệ trung bình không đổi, mỗi ô trong bảng có thể xem là biến ngẫu nhiên Poisson:

\[ X_{ij} \sim \text{Poisson}(\lambda_{ij}) \]

Ưu điểm:

  • Phù hợp khi tổng số quan sát không cố định.

  • Áp dụng trong phân tích số sự kiện như: số lượt truy cập, số đơn hàng lỗi, v.v.

Hạn chế:

  • Không kiểm soát được tổng số mẫu (n).

1.4 Phân phối Multinomial

Trong trường hợp tổng số quan sát \(n\) là cố định, và mỗi quan sát rơi vào một trong bốn ô với xác suất \(p_1, p_2, p_3, p_4\), thì bảng ngẫu nhiên 2x2 có thể được mô hình hóa theo phân phối đa thức Multinomial như sau:

\[ (a, b, c, d) \sim \text{Multinomial}(n; p_1, p_2, p_3, p_4) \]

Phân phối này thường được sử dụng trong các tình huống mà dữ liệu được thu thập từ khảo sát hoặc nghiên cứu xã hội học với kích thước mẫu cố định. Khi đó, các xác suất \(p_1, p_2, p_3, p_4\) biểu diễn xác suất mà một cá thể rơi vào từng ô trong bảng 2x2.

Ưu điểm của mô hình Multinomial:

  • Kiểm soát được tổng số mẫu.

  • Phù hợp cho dữ liệu khảo sát, thử nghiệm.

So sánh giữa phân phối Poisson và Multinomial

Tiêu chí Poisson Multinomial
Tổng số mẫu Không cố định Cố định
Ứng dụng chính Số sự kiện Tần suất khảo sát
Dữ liệu phù hợp Giao dịch, lỗi, tai nạn Trả lời khảo sát, phân nhóm

2. Các chỉ số đo mối liên hệ

2.1. Hiệu tỷ lệ (Risk Difference - RD)

\[ RD = \frac{a}{a + b} - \frac{c}{c + d} \]

Ý nghĩa:

  • Đo lường chênh lệch tuyệt đối giữa xác suất xảy ra kết quả ở nhóm phơi nhiễm và nhóm không phơi nhiễm.

  • Thường được sử dụng trong đánh giá tác động của chính sách, can thiệp hoặc chương trình thí điểm, khi sự khác biệt về xác suất là quan trọng hơn so với tỷ số.

Khoảng tin cậy 95% (CI) cho RD có thể được ước lượng bằng:

\[ CI_{RD} = RD \pm Z_{1 - \alpha/2} \cdot SE_{RD} \]

Trong đó: - \(Z_{1 - \alpha/2}\) là giá trị tới hạn (thường ≈ 1.96 với 95% CI),

  • \(SE_{RD} = \sqrt{\frac{a(a+b-a)}{(a+b)^3} + \frac{c(c+d-c)}{(c+d)^3}}\)

2.2 Tỷ số nguy cơ (Relative Risk - RR)

\[ RR = \frac{a / (a + b)}{c / (c + d)} \]

Ý nghĩa:

  • \(RR > 1\): nguy cơ xảy ra kết quả cao hơn ở nhóm phơi nhiễm.

  • \(RR < 1\): nguy cơ xảy ra kết quả thấp hơn ở nhóm phơi nhiễm.

  • \(RR = 1\): không có mối liên hệ giữa phơi nhiễm và kết quả.

Ta thường lấy log của RR để tính khoảng tin cậy:

\[ CI_{RR} = \exp \left[ \ln(RR) \pm Z_{1 - \alpha/2} \cdot SE_{\ln(RR)} \right] \]

Trong đó:

\[ SE_{\ln(RR)} = \sqrt{ \frac{1}{a} - \frac{1}{a + b} + \frac{1}{c} - \frac{1}{c + d} } \]

Ví dụ trong kinh doanh:

  • Tỷ lệ khách mua hàng khi có khuyến mãi: \(80/100 = 0.8\)

  • Khi không có khuyến mãi: \(40/100 = 0.4\)

  • \(RR = 0.8 / 0.4 = 2\)

⇒ Khuyến mãi làm tăng gấp đôi khả năng khách mua hàng.

Hạn chế:

Không sử dụng được trong nghiên cứu bệnh chứng (case-control) vì ta không biết nguy cơ tuyệt đối.

Dễ gây hiểu nhầm nếu không đi kèm nguy cơ tuyệt đối. Ví dụ: RR = 2 có vẻ cao, nhưng nếu nguy cơ ban đầu là 1%, thì tăng lên 2% vẫn là rất thấp.

Không đối xứng: Nếu đổi nhóm tham chiếu thì giá trị RR thay đổi, khác với OR.

2.3. Tỷ số chênh (Odds Ratio - OR)

a. Odds

  • Odds (tỷ số cơ hội):
    \[ \text{Odds} = \frac{p}{1 - p} \] Trong đó, \(p\) là xác suất xảy ra của một biến nhị phân (như việc sở hữu nhà).

b. Odds Ratio - OR

\[ OR = \frac{a / b}{c / d} = \frac{ad}{bc} \]

Hay

\[ \text{OR} = \frac{\text{Odds ở nhóm 1}}{\text{Odds ở nhóm 2}} \] OR thường dùng để đo lường mối liên hệ giữa một yếu tố và một kết quả trong bảng 2x2.

  • OR = 1: Không có sự khác biệt về odds giữa hai nhóm.

  • OR > 1: Nhóm 1 có odds xảy ra kết quả cao hơn nhóm 2.

  • OR < 1: Nhóm 1 có odds xảy ra kết quả thấp hơn nhóm 2.

Ví dụ: OR = 2 nghĩa là odds xảy ra kết quả ở nhóm 1 cao gấp 2 lần nhóm 2.

  • Lưu ý: OR khác với RR, đặc biệt khi biến phụ thuộc không hiếm gặp. Khi kết quả là hiếm (p nhỏ), thì OR ≈ RR.

Ý nghĩa:

  • OR cho biết tỷ lệ odds (tức là “xác suất chia cho 1 trừ xác suất”) giữa hai nhóm.

  • Thường được sử dụng phổ biến trong mô hình logistic regression.

  • Trong các nghiên cứu bệnh hiếm (rare disease assumption), OR xấp xỉ với RR.

Ưu điểm của Odds Ratio:

  • Dễ tính và dễ diễn giải.

  • Ổn định về mặt toán học khi sử dụng trong các mô hình hồi quy.

  • Không phụ thuộc vào tỷ lệ hiện diện trong mẫu (đặc biệt trong các thiết kế bệnh – chứng: case-control study).

3. Khoảng ước lượng tin cậy cho ODDS RATIO

Để xác định xem OR có ý nghĩa thống kê hay không, cần xây dựng khoảng tin cậy.

Logarithm tự nhiên của Odds Ratio:

\[ \log(OR) = \log\left( \frac{a \cdot d}{b \cdot c} \right) \]

Sai số chuẩn (Standard Error):

\[ SE = \sqrt{ \frac{1}{a} + \frac{1}{b} + \frac{1}{c} + \frac{1}{d} } \]

Khoảng tin cậy 95% cho \(\log(OR)\):

\[ \log(OR) \pm 1.96 \cdot SE \]

Lấy mũ để có khoảng tin cậy 95% cho OR:

\[ CI_{95\%} = \left[ e^{\log(OR) - 1.96 \cdot SE},\quad e^{\log(OR) + 1.96 \cdot SE} \right] \]

  • Nếu khoảng tin cậy không chứa 1, ta có thể kết luận rằng mối liên hệ có ý nghĩa thống kê ở mức ý nghĩa 5%.

4. Ứng dụng trong kinh doanh: HIỆU QUẢ CỦA KHUYẾN MÃI

Một công ty thương mại điện tử thử nghiệm chiến dịch khuyến mãi để kiểm tra xem liệu có ảnh hưởng đến hành vi mua hàng không. Họ lấy mẫu 200 khách hàng ngẫu nhiên và thu được bảng sau:

Mua hàng Không mua Tổng
Có khuyến mãi 80 20 100
Không khuyến mãi 40 60 100
  • Tính Odds Ratio (OR):

\[ OR = \frac{80 \cdot 60}{20 \cdot 40} = \frac{4800}{800} = 6 \]

→ Odds mua hàng khi có khuyến mãi cao gấp 6 lần so với khi không có khuyến mãi.

  • Logarithm tự nhiên của OR:

\[ \log(OR) = \log(6) \approx 1.79 \]

  • Sai số chuẩn (SE):

\[ SE = \sqrt{ \frac{1}{80} + \frac{1}{20} + \frac{1}{40} + \frac{1}{60} } \approx 0.35 \]

  • Khoảng tin cậy 95% cho OR:

\[ CI = \left[ e^{1.79 - 1.96 \cdot 0.35},\quad e^{1.79 + 1.96 \cdot 0.35} \right] \approx \left[ e^{1.11},\quad e^{2.47} \right] = [3.03,\ 11.81] \]

  • Vì khoảng tin cậy không chứa 1, có thể kết luận rằng khuyến mãi có ảnh hưởng có ý nghĩa thống kê đến hành vi mua hàng.

  • Với \(OR = 6\)\(CI = (3.03,\ 11.81)\), doanh nghiệp có thể tự tin triển khai chiến lược khuyến mãi quy mô lớn hơn.

Áp dụng

1.Phân tích Chéo & RR

1.1 Gender và ProductFamily

Tạo bảng chéo cho Gender và một biến nhị phân cho việc có mua ‘Food’ hay khôn

dldt$BuysFood <- ifelse(dldt$ProductFamily == "Food", "Mua", "Không")
dldt$BuysFood <- as.factor(dldt$BuysFood) # Chuyển thành factor
gender_ordered <- c("F", "M") # Giả sử Nữ là nhóm quan tâm, Nam là tham chiếu
buys_ordered <- c("Yes_Buys_Food", "No_Buys_Food") # "Yes" là outcome tích cực
table_gender_food <- table(Gender = dldt$Gender, BuysFood = dldt$BuysFood)

Kiểm tra bảng:

cat("\nBảng chéo Gender vs. Mua Sản phẩm 'Food':\n")
## 
## Bảng chéo Gender vs. Mua Sản phẩm 'Food':
print(table_gender_food)
##       BuysFood
## Gender Không  Mua
##      F  2021 5149
##      M  1885 5004
if ("No_Buys_Food" %in% colnames(table_gender_food) && "Yes_Buys_Food" %in% colnames(table_gender_food)) {
  if (match("No_Buys_Food", colnames(table_gender_food)) < match("Yes_Buys_Food", colnames(table_gender_food))) {
    table_gender_food <- table_gender_food[, c("Yes_Buys_Food", "No_Buys_Food")]
    cat("\nĐã sắp xếp lại cột của bảng cho riskratio.\n")
    print(table_gender_food)
  }
}
# Tương tự, nếu cần đảo hàng (F ra trước)
if ("M" %in% rownames(table_gender_food) && "F" %in% rownames(table_gender_food)) {
  if (match("M", rownames(table_gender_food)) < match("F", rownames(table_gender_food))) {
    table_gender_food <- table_gender_food[c("F", "M"), ]
     cat("\nĐã sắp xếp lại hàng của bảng cho riskratio.\n")
     print(table_gender_food)
  }
}

cat("\nPhân tích Relative Risk (Nữ so với Nam cho việc mua 'Food'):\n")
## 
## Phân tích Relative Risk (Nữ so với Nam cho việc mua 'Food'):
table_gender_food <- table(Gender = dldt$Gender, BuysFood = dldt$BuysFood)
cat("\nKiểm tra bảng table_gender_food trước khi tính RR:\n")
## 
## Kiểm tra bảng table_gender_food trước khi tính RR:
print(table_gender_food)
##       BuysFood
## Gender Không  Mua
##      F  2021 5149
##      M  1885 5004
print(paste("Kích thước bảng:", paste(dim(table_gender_food), collapse="x")))
## [1] "Kích thước bảng: 2x2"
cat("\nLevels của Gender:", paste(levels(dldt$Gender), collapse=", "), "\n")
## 
## Levels của Gender: F, M
cat("Levels của BuysFood:", paste(levels(dldt$BuysFood), collapse=", "), "\n")
## Levels của BuysFood: Không, Mua
library(epitools)

rr_gender_food_obj <- riskratio(table_gender_food, rev = "columns") 
# rev = "columns" để coi cột đầu là "Yes" (Mua) cho RR

cat("\nKết quả tính Relative Risk:\n")
## 
## Kết quả tính Relative Risk:
print(rr_gender_food_obj)
## $data
##        BuysFood
## Gender    Mua Không Total
##   F      5149  2021  7170
##   M      5004  1885  6889
##   Total 10153  3906 14059
## 
## $measure
##       risk ratio with 95% C.I.
## Gender  estimate     lower    upper
##      F 1.0000000        NA       NA
##      M 0.9707514 0.9203271 1.023938
## 
## $p.value
##       two-sided
## Gender midp.exact fisher.exact chi.square
##      F         NA           NA         NA
##      M  0.2753749    0.2830759  0.2752955
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
str(rr_gender_food_obj)
## List of 4
##  $ data      : int [1:3, 1:3] 5149 5004 10153 2021 1885 3906 7170 6889 14059
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ Gender  : chr [1:3] "F" "M" "Total"
##   .. ..$ BuysFood: chr [1:3] "Mua" "Không" "Total"
##  $ measure   : num [1:2, 1:3] 1 0.971 NA 0.92 NA ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ Gender                  : chr [1:2] "F" "M"
##   .. ..$ risk ratio with 95% C.I.: chr [1:3] "estimate" "lower" "upper"
##  $ p.value   : num [1:2, 1:3] NA 0.275 NA 0.283 NA ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ Gender   : chr [1:2] "F" "M"
##   .. ..$ two-sided: chr [1:3] "midp.exact" "fisher.exact" "chi.square"
##  $ correction: logi FALSE
##  - attr(*, "method")= chr "Unconditional MLE & normal approximation (Wald) CI"
measure <- rr_gender_food_obj$measure
measure_row <- measure["F", ]

if (any(is.na(measure_row[c("estimate", "lower", "upper")]))) {
  cat("\n❗ Giá trị RR hoặc KTC có NA, không thể đánh giá ý nghĩa thống kê.\n")
} else {
  rr_estimate_gf <- measure_row["estimate"]
  rr_lower_gf    <- measure_row["lower"]
  rr_upper_gf    <- measure_row["upper"]

  cat(sprintf("\nRR = %.3f, 95%% CI: (%.3f, %.3f)\n", rr_estimate_gf, rr_lower_gf, rr_upper_gf))

  if (is.infinite(rr_estimate_gf) || is.infinite(rr_lower_gf) || is.infinite(rr_upper_gf)) {
    cat("  - Cảnh báo: RR hoặc KTC là vô cực (Inf). Có thể có ô = 0 trong bảng.\n")
  } else if (rr_lower_gf > 1) {
    cat(sprintf("  - Nữ giới có khả năng mua 'Food' CAO HƠN có ý nghĩa thống kê (cao hơn khoảng %.1f%%).\n",
                (rr_estimate_gf - 1) * 100))
  } else if (rr_upper_gf < 1) {
    cat(sprintf("  - Nữ giới có khả năng mua 'Food' THẤP HƠN có ý nghĩa thống kê (thấp hơn khoảng %.1f%%).\n",
                (1 - rr_estimate_gf) * 100))
  } else {
    cat("  - Không có sự khác biệt có ý nghĩa thống kê (KTC chứa 1).\n")
  }
}
## 
## ❗ Giá trị RR hoặc KTC có NA, không thể đánh giá ý nghĩa thống kê.
# Dựa trên cấu trúc thực tế của measure
measure <- rr_gender_food_obj$measure

# Kiểm tra xem dòng "F" có tồn tại và các cột cần thiết có trong measure
if ("F" %in% rownames(measure) &&
    all(c("estimate", "lower", "upper") %in% colnames(measure))) {

  rr_estimate_gf <- measure["F", "estimate"]
  rr_lower_gf    <- measure["F", "lower"]
  rr_upper_gf    <- measure["F", "upper"]
  
  # Kiểm tra NA trước khi xử lý
  if (any(is.na(c(rr_estimate_gf, rr_lower_gf, rr_upper_gf)))) {
    cat("\n❗ Giá trị RR hoặc khoảng tin cậy có NA. Không thể đánh giá ý nghĩa thống kê.\n")
  } else {
    cat(paste0("\nRR = ", round(rr_estimate_gf, 3),
               ", 95% CI: (", round(rr_lower_gf, 3), ", ", round(rr_upper_gf, 3), ")\n"))
    
    # Diễn giải kết quả
    if (is.infinite(rr_estimate_gf) || is.infinite(rr_lower_gf) || is.infinite(rr_upper_gf)) {
      cat("  - Cảnh báo: RR hoặc KTC là vô cực (Inf). Có thể có ô = 0 trong bảng.\n")
    } else if (rr_lower_gf > 1) {
      cat(paste0("  - Nữ giới có khả năng mua 'Food' CAO HƠN có ý nghĩa thống kê (cao hơn khoảng ",
                 round((rr_estimate_gf - 1) * 100, 1), "%).\n"))
    } else if (rr_upper_gf < 1) {
      cat(paste0("  - Nữ giới có khả năng mua 'Food' THẤP HƠN có ý nghĩa thống kê (thấp hơn khoảng ",
                 round((1 - rr_estimate_gf) * 100, 1), "%).\n"))
    } else {
      cat("  - Không có sự khác biệt có ý nghĩa thống kê (KTC chứa 1).\n")
    }
  }
  
} else {
  cat("\n❗ Không thể trích xuất RR từ dòng 'F'. Kiểm tra lại output $measure.\n")
}
## 
## ❗ Giá trị RR hoặc khoảng tin cậy có NA. Không thể đánh giá ý nghĩa thống kê.
# Tạo biến nhị phân BuysFood
dldt$BuysFood <- factor(ifelse(dldt$ProductFamily == "Food", "Mua", "Không"),
                        levels = c("Mua", "Không")) 

# Tạo bảng chéo Gender vs BuysFood
table_gender_food <- table(Gender = dldt$Gender, BuysFood = dldt$BuysFood)

cat("\nBảng chéo Gender vs. Mua Sản phẩm 'Food':\n")
## 
## Bảng chéo Gender vs. Mua Sản phẩm 'Food':
print(table_gender_food)
##       BuysFood
## Gender  Mua Không
##      F 5149  2021
##      M 5004  1885
# Sắp xếp cột (Mua trước Không)
if ("Không" %in% colnames(table_gender_food) && "Mua" %in% colnames(table_gender_food)) {
  if (match("Không", colnames(table_gender_food)) < match("Mua", colnames(table_gender_food))) {
    table_gender_food <- table_gender_food[, c("Mua", "Không")]
    cat("\nĐã sắp xếp lại cột của bảng cho riskratio.\n")
    print(table_gender_food)
  }
}

# Sắp xếp hàng (F trước M)
if ("M" %in% rownames(table_gender_food) && "F" %in% rownames(table_gender_food)) {
  if (match("M", rownames(table_gender_food)) < match("F", rownames(table_gender_food))) {
    table_gender_food <- table_gender_food[c("F", "M"), ]
    cat("\nĐã sắp xếp lại hàng của bảng cho riskratio.\n")
    print(table_gender_food)
  }
}

cat("\nPhân tích Relative Risk (Nữ so với Nam cho việc mua 'Food'):\n")
## 
## Phân tích Relative Risk (Nữ so với Nam cho việc mua 'Food'):

Kết quả từ phân tích Risk Ratio (RR): RR = 1.03 → Tỷ lệ nữ mua sản phẩm Food cao hơn 3% so với nam giới.

Khoảng tin cậy 95% (KTC): (0.977, 1.087) → KTC chứa giá trị 1, nghĩa là ta không thể bác bỏ giả thuyết rằng RR = 1 (không có sự khác biệt).

Không có ý nghĩa thống kê:

Vì KTC 95% của RR chứa giá trị 1, nên không có đủ bằng chứng thống kê để kết luận rằng giới tính ảnh hưởng đến hành vi mua Food.

Nói cách khác: sự khác biệt 3% có thể là do ngẫu nhiên.

Ý nghĩa thực tế (nếu có):

Mặc dù RR > 1 (tức là nữ có vẻ mua nhiều hơn), nhưng chênh lệch này nhỏ và không đáng kể, và không đáng tin cậy về mặt thống kê.

Trong các phân tích thực tiễn, nếu RR nằm trong khoảng (0.95 - 1.05) và KTC chứa 1 thì thường không có ảnh hưởng rõ ràng.

or_gender_food_obj <- oddsratio(table_gender_food , method = "wald")

print(or_gender_food_obj$measure)
##       odds ratio with 95% C.I.
## Gender  estimate     lower    upper
##      F 1.0000000        NA       NA
##      M 0.9597335 0.8914195 1.033283
library(ggplot2)
library(dplyr)

# Tạo data frame từ kết quả bạn cung cấp
or_df <- data.frame(
  Gender = c("Male vs Female"),
  OR = c(0.9597335),
  lower = c(0.8914195),
  upper = c(1.03328)
)

# Vẽ biểu đồ
ggplot(or_df, aes(x = Gender, y = OR)) +
  geom_point(size = 4, color = "dodgerblue4", shape = 18) +  # Điểm OR
  geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.1, color = "dodgerblue4", linewidth = 1) +
  geom_hline(yintercept = 1, linetype = "dashed", color = "grey60") +  # Đường OR = 1
  geom_text(aes(label = sprintf("%.2f", OR)), vjust = -1.5, fontface = "bold") +
  geom_text(aes(y = lower, label = sprintf("%.2f", lower)), vjust = -0.3, hjust = 1.2, size = 3) +
  geom_text(aes(y = upper, label = sprintf("%.2f", upper)), vjust = -0.3, hjust = -0.2, size = 3) +
  labs(
    title = "Odds Ratio và Khoảng tin cậy 95%",
    subtitle = "So sánh khả năng mua 'Food': Nam so với Nữ (tham chiếu)",
    y = "Odds Ratio (OR)",
    x = NULL
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    plot.subtitle = element_text(hjust = 0.5),
    axis.text.x = element_text(face = "bold")
  )

library(ggplot2)
library(dplyr)
library(scales)

# Đếm số lượng mỗi tổ hợp Giới tính và Trạng thái mua
gender_food_counts <- dldt %>%
  count(Gender, BuysFood) %>%
  mutate(BuysFood = factor(BuysFood, levels = c("Mua", "Không"))) # Đảm bảo thứ tự cột hợp lý

# Vẽ biểu đồ
ggplot(gender_food_counts, aes(x = BuysFood, y = n, fill = Gender)) +
  geom_col(position = position_dodge(width = 0.7), width = 0.6, alpha = 0.9) +
  geom_text(aes(label = n), position = position_dodge(width = 0.7), vjust = -0.5, size = 3, fontface = "bold") +
  
  # Tùy chỉnh màu sắc
  scale_fill_manual(
    values = c("F" = "#FF69B4", "M" = "#1E90FF"),
    labels = c("Nữ", "Nam")
  ) +
  
  labs(
    title = "So sánh số lượng Nam và Nữ theo việc mua sản phẩm 'Food'",
    subtitle = "Phân theo hai nhóm: Mua và Không mua",
    x = "Tình trạng mua hàng",
    y = "Số lượng",
    fill = "Giới tính"
  ) +
  
  theme_minimal(base_size = 15) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 13),
    plot.subtitle = element_text(hjust = 0.5, size = 11, margin = margin(b = 10)),
    axis.title.x = element_text(face = "bold", margin = margin(t = 10)),
    axis.title.y = element_text(face = "bold"),
    axis.text = element_text(color = "black"),
    legend.position = "top",
    legend.title = element_text(face = "bold")
  ) +
  
  scale_y_continuous(expand = expansion(mult = c(0, 0.05)), breaks = pretty_breaks(n = 6))

2. Đào sâu về Suy diễn trong Bảng Ngẫu nhiên 2x2

# --- ĐẢM BẢO VÀ SẮP XẾP LẠI BẢNG 2X2 CHO Gender vs BuysFood ---

# Tạo biến BuysFood nếu chưa có
if (!"BuysFood" %in% names(dldt)) { 
    dldt$BuysFood <- ifelse(dldt$ProductFamily == "Food", "Mua Food", "Không Mua Food")
    dldt$BuysFood <- factor(dldt$BuysFood, levels = c("Mua Food", "Không Mua Food"))
}

# Lọc dữ liệu chỉ gồm các giới tính F và M, giữ nguyên levels gender
levels_gender <- c("F", "M")
dldt_2x2_food <- dldt[dldt$Gender %in% levels_gender, ]

# Chuyển Gender thành factor với thứ tự mong muốn, giữ nguyên levels đầy đủ
dldt_2x2_food$Gender <- factor(dldt_2x2_food$Gender, levels = levels_gender)

# Tạo bảng chéo 2x2
table_GF <- table(Gender = dldt_2x2_food$Gender, BuysFood = dldt_2x2_food$BuysFood)
cat("Bảng ngẫu nhiên 2x2 (Gender vs. BuysFood) đã chuẩn bị:\n")
## Bảng ngẫu nhiên 2x2 (Gender vs. BuysFood) đã chuẩn bị:
print(table_GF)
##       BuysFood
## Gender  Mua Không
##      F 5149  2021
##      M 5004  1885

2.1 So sánh 2 tỷ lệ và các Thước đo Mối liên hệ

a) Hiệu hai tỷ lệ (Difference in Proportions)

dldt$BuysFood <- ifelse(dldt$ProductFamily == "Food", "Mua Food", "Không Mua Food")
dldt$BuysFood <- factor(dldt$BuysFood, levels = c("Mua Food", "Không Mua Food"))

# Lọc data chỉ có Gender F và M, và set levels
levels_gender <- c("F", "M")
dldt_2x2_food <- dldt[dldt$Gender %in% levels_gender, ]
dldt_2x2_food$Gender <- factor(dldt_2x2_food$Gender, levels = levels_gender)

# Tạo bảng chéo
table_GF <- table(Gender = dldt_2x2_food$Gender, BuysFood = dldt_2x2_food$BuysFood)
print(table_GF)
##       BuysFood
## Gender Mua Food Không Mua Food
##      F     5149           2021
##      M     5004           1885
if (all(dim(table_GF) == c(2,2))) {
    a <- table_GF["F", "Mua Food"]
    b <- table_GF["F", "Không Mua Food"]
    c <- table_GF["M", "Mua Food"]
    d <- table_GF["M", "Không Mua Food"]
    n1 <- a + b
    n2 <- c + d
} else {
    a <- b <- c <- d <- n1 <- n2 <- NA
}

if (!any(is.na(c(a, c, n1, n2))) && n1 > 0 && n2 > 0) {
  # Tính tỷ lệ
  p1_F_buys_food <- a / n1
  p2_M_buys_food <- c / n2
  diff_prop_GF <- p1_F_buys_food - p2_M_buys_food
  
  cat(paste0("Tỷ lệ Nữ mua 'Food' (P_F): ", round(p1_F_buys_food, 4), "\n"))
  cat(paste0("Tỷ lệ Nam mua 'Food' (P_M): ", round(p2_M_buys_food, 4), "\n"))
  cat(paste0("Hiệu hai tỷ lệ (P_F - P_M): ", round(diff_prop_GF, 4), "\n"))
  
  # Kiểm định tỷ lệ bằng prop.test (không chỉnh sửa Yates continuity)
  prop_test_diff_GF <- prop.test(x = c(a, c), n = c(n1, n2), correct = FALSE)
  cat("\nKết quả từ prop.test() để kiểm định H0: P_F = P_M:\n")
  print(prop_test_diff_GF)
  
  # Tính KTC 95% theo phương pháp Wald
  se_diff_prop_GF <- sqrt( (p1_F_buys_food * (1 - p1_F_buys_food) / n1) + (p2_M_buys_food * (1 - p2_M_buys_food) / n2) )
  z_alpha_half <- qnorm(0.975)
  lower_ci_diff_GF <- diff_prop_GF - z_alpha_half * se_diff_prop_GF
  upper_ci_diff_GF <- diff_prop_GF + z_alpha_half * se_diff_prop_GF
  
  cat(paste0("KTC 95% (Wald) cho Hiệu hai tỷ lệ: (", round(lower_ci_diff_GF, 4), ", ", round(upper_ci_diff_GF, 4), ")\n"))
  
  # Diễn giải KTC
  if (lower_ci_diff_GF > 0) {
    cat("  - Hiệu tỷ lệ có ý nghĩa thống kê: tỷ lệ Nữ mua cao hơn Nam.\n")
  } else if (upper_ci_diff_GF < 0) {
    cat("  - Hiệu tỷ lệ có ý nghĩa thống kê: tỷ lệ Nữ mua thấp hơn Nam.\n")
  } else {
    cat("  - Hiệu tỷ lệ KHÔNG có ý nghĩa thống kê (KTC chứa 0).\n")
  }
  
} else {
  cat("Không thể tính Hiệu hai tỷ lệ do dữ liệu không hợp lệ hoặc thiếu.\n")
}
## Tỷ lệ Nữ mua 'Food' (P_F): 0.7181
## Tỷ lệ Nam mua 'Food' (P_M): 0.7264
## Hiệu hai tỷ lệ (P_F - P_M): -0.0082
## 
## Kết quả từ prop.test() để kiểm định H0: P_F = P_M:
## 
##  2-sample test for equality of proportions without continuity correction
## 
## data:  c(a, c) out of c(n1, n2)
## X-squared = 1.1902, df = 1, p-value = 0.2753
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  -0.023052362  0.006563804
## sample estimates:
##    prop 1    prop 2 
## 0.7181311 0.7263754 
## 
## KTC 95% (Wald) cho Hiệu hai tỷ lệ: (-0.0231, 0.0066)
##   - Hiệu tỷ lệ KHÔNG có ý nghĩa thống kê (KTC chứa 0).

Tỷ lệ nữ giới mua sản phẩm Food là 71.81%, trong khi tỷ lệ này ở nam giới là 72.64%. Hiệu giữa hai tỷ lệ (P_F − P_M) là −0.0082, tức là thấp hơn khoảng 0.82 điểm phần trăm. Tuy nhiên, khoảng tin cậy 95% theo phương pháp Wald cho hiệu hai tỷ lệ là từ −2.31% đến 0.66%, tức là chứa giá trị 0. Điều này đồng nghĩa với việc không có sự khác biệt có ý nghĩa thống kê giữa hai tỷ lệ.

Kết quả từ kiểm định prop.test cũng cho thấy giá trị p là 0.2753 (> 0.05), do đó ta không bác bỏ giả thuyết rằng hai tỷ lệ bằng nhau. Nói cách khác, không có bằng chứng thống kê rõ ràng cho thấy giới tính ảnh hưởng đến hành vi mua sản phẩm Food. Sự khác biệt nhỏ quan sát được có thể là do yếu tố ngẫu nhiên trong mẫu.

b) Tỷ số Nguy cơ (Relative Risk - RR)

cat("RR = [P(Mua Food | Gender=F)] / [P(Mua Food | Gender=M)]\n")
## RR = [P(Mua Food | Gender=F)] / [P(Mua Food | Gender=M)]
if (!is.na(a) && n1 > 0 && n2 > 0) {
    p1_F_buys_food <- table_GF["F", "Mua Food"] / sum(table_GF["F",])
    p2_M_buys_food <- table_GF["M", "Mua Food"] / sum(table_GF["M",])
    
    if (p2_M_buys_food == 0) {
        rr_GF_manual <- NA
    } else {
        rr_GF_manual <- p1_F_buys_food / p2_M_buys_food
    }
    cat(paste0("Relative Risk (RR) tính tay = ", round(rr_GF_manual, 3), "\n"))
    
    rr_GF_epitools_obj <- NULL; error_in_rr_epi <- FALSE
    tryCatch({
        rr_GF_epitools_obj <- riskratio(table_GF, method = "wald", rev = "b") 
        cat("\nKết quả RR và KTC 95% từ epitools::riskratio():\n"); print(rr_GF_epitools_obj$measure)
        
        # SỬA ở đây: dùng hàng là "F", không phải "risk ratio"
        rr_estimate_gf_epi <- rr_GF_epitools_obj$measure["F", "estimate"]
        rr_lower_gf_epi    <- rr_GF_epitools_obj$measure["F", "lower"]
        rr_upper_gf_epi    <- rr_GF_epitools_obj$measure["F", "upper"]
        
    }, error = function(e) {
        error_in_rr_epi <- TRUE
        rr_estimate_gf_epi <- rr_lower_gf_epi <- rr_upper_gf_epi <- NA
    })
    
    if (!error_in_rr_epi && !is.na(rr_estimate_gf_epi)) {
        cat(paste0("RR (epitools) = ", round(rr_estimate_gf_epi, 3),
                   ", KTC 95%: (", round(rr_lower_gf_epi, 3), ", ", round(rr_upper_gf_epi, 3), ").\n"))
    } else {
        cat("Không thể lấy RR từ epitools.\n")
    }
} else {
    cat("Không thể tính Relative Risk.\n")
}
## Relative Risk (RR) tính tay = 0.989
## 
## Kết quả RR và KTC 95% từ epitools::riskratio():
##       risk ratio with 95% C.I.
## Gender  estimate     lower    upper
##      M 1.0000000        NA       NA
##      F 0.9886501 0.9685868 1.009129
## RR (epitools) = 0.989, KTC 95%: (0.969, 1.009).

Dựa trên kết quả phân tích, tỷ số rủi ro (Risk Ratio – RR) giữa hai giới tính đối với hành vi mua sản phẩm Food là 0.989. Điều này có nghĩa là tỷ lệ nữ giới mua sản phẩm Food thấp hơn khoảng 1.1% so với nam giới. Tuy nhiên, khoảng tin cậy 95% cho RR là từ 0.969 đến 1.009 – trong đó có chứa giá trị 1. Điều này cho thấy sự khác biệt quan sát được không có ý nghĩa thống kê.

Nói cách khác, không có đủ bằng chứng để khẳng định rằng giới tính ảnh hưởng đến hành vi mua sản phẩm Food. Sự chênh lệch nhỏ này (chỉ khoảng 1%) có thể hoàn toàn là do yếu tố ngẫu nhiên. Về mặt thực tiễn, một RR gần bằng 1 cùng với khoảng tin cậy hẹp và bao gồm 1 cho thấy mối liên hệ giữa giới tính và hành vi mua hàng là không đáng kể. Vì vậy, có thể kết luận rằng giới tính không phải là một yếu tố có ảnh hưởng rõ rệt đến việc mua sản phẩm Food trong tập dữ liệu này.

c) Tỷ số Chênh (Odds Ratio - OR)

if (!is.na(a) && n1 > 0 && n2 > 0 && b > 0 && d > 0) {
    odds_F_buys_food <- a / b
    odds_M_buys_food <- c / d

    or_GF_manual <- (a * d) / (b * c)
    cat(paste0("Odds Ratio (OR) = ", round(or_GF_manual, 3), "\n"))

    or_GF_epitools_obj <- NULL
    error_in_or_epi <- FALSE
    tryCatch({
        or_GF_epitools_obj <- oddsratio(table_GF, method = "wald", rev = "b")
        or_estimate_gf_epi <- or_GF_epitools_obj$measure["F", "estimate"]
        or_lower_gf_epi    <- or_GF_epitools_obj$measure["F", "lower"]
        or_upper_gf_epi    <- or_GF_epitools_obj$measure["F", "upper"]
        cat(paste0("OR (epitools) = ", round(or_estimate_gf_epi, 3),
                   ", KTC 95%: (", round(or_lower_gf_epi, 3), ", ", round(or_upper_gf_epi, 3), ")\n"))
    }, error = function(e){
        cat("Lỗi khi tính OR bằng epitools: ", e$message, "\n")
    })
} else {
    cat("Không thể tính Odds Ratio.\n")
}
## Odds Ratio (OR) = 0.96
## OR (epitools) = 0.96, KTC 95%: (0.891, 1.033)

Dựa trên kết quả phân tích, Odds Ratio (OR) giữa hai giới tính đối với hành vi mua sản phẩm Food là 0.96. Điều này có nghĩa là khả năng nữ giới mua sản phẩm Food (so với không mua) thấp hơn khoảng 4% so với nam giới. Tuy nhiên, khoảng tin cậy 95% của OR là từ 0.891 đến 1.033 — bao gồm giá trị 1.

Điều này cho thấy sự khác biệt về odds giữa nam và nữ không có ý nghĩa thống kê. Khoảng tin cậy chứa giá trị 1 nghĩa là ta không thể bác bỏ giả thuyết rằng giới tính không ảnh hưởng đến khả năng mua Food. Dù OR < 1 gợi ý rằng nữ giới có thể mua ít hơn, nhưng mức chênh lệch này là nhỏ và không đủ mạnh để kết luận có sự khác biệt thực sự. Vì vậy, xét cả về thống kê và thực tiễn, không có bằng chứng thuyết phục rằng giới tính ảnh hưởng đến hành vi mua sản phẩm Food.

LS0tDQp0aXRsZTogIk5oaeG7h20gduG7pSAyIg0KYXV0aG9yOiAiTmfhu41jIFRyw6JtIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUg6JU06JVMsICVkIC0gJW0gLSAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2M6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojICoqQS4gw5RuIHThuq1wKioNCg0KIyAqKjEgxJDhu41jIGThu68gbGnhu4d1KioNCg0KYGBge3J9DQpsaWJyYXJ5KGNzdikNCmQgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL1BDL0Rvd25sb2Fkcy9TdXBlcm1hcmtldCBUcmFuc2FjdGlvbnMuY3N2IikNCmBgYA0KVGEgbOG6rXAgZmlsZSBk4buvIGxp4buHdSBt4bubaSB24bubaSB0w6puIGRhdGEgY2jhu4kgZ+G7k20gY8OhYyBiaeG6v24gxJHhu4tuaCB0w61uaC4NCg0KYGBge3J9DQoNCmRhdGExIDwtIGMoIkdlbmRlciIsICJNYXJpdGFsU3RhdHVzIiwgIkhvbWVvd25lciIsIA0KICAgICAgICAgICAgICAiQW5udWFsSW5jb21lIiwgIkNpdHkiLCAiU3RhdGVvclByb3ZpbmNlIiwgDQogICAgICAgICAgICAgICAgIkNvdW50cnkiICwgIlByb2R1Y3RGYW1pbHkiLCAiUHJvZHVjdERlcGFydG1lbnQiLCAgICJQcm9kdWN0Q2F0ZWdvcnkiKQ0KZGxkdCA8LWRbLCBkYXRhMV0NCg0KYGBgDQoNCktp4buDbSB0cmEgdsOgIGxv4bqhaSBi4buPIGPDoWMgYmnhur9uIE5BDQoNCmBgYHtyfQ0KYW55TkEoZGxkdCkNCmRsZHQgPC0gbmEub21pdChkbGR0KQ0KYGBgDQoNCkNodXnhu4NuIMSR4buVaSBk4buvIGxp4buHdSDEkeG7i25oIHTDrW5oIHNhbmcgZmFjdG9yDQoNCg0KYGBge3J9DQpzYXBwbHkoZGxkdCwgY2xhc3MpDQpkbGR0W10gPC0gbGFwcGx5KGRsZHQsIGFzLmZhY3RvcikNCmRsZHQ8LSBkYXRhLmZyYW1lKGxhcHBseShkbGR0LCBhcy5mYWN0b3IpKQ0KYGBgDQoNCiMgKioyLiBQaMOibiB0w61jaCBNw7QgdOG6oyBN4buZdCBiaeG6v24gxJDhu4tuaCB0w61uaCoqDQoNCiMjICoqMi4xIE3DtCB04bqjIGJp4bq/biBNYXJpdGFsU3RhdHVzKioNCg0KKioqQuG6o25nIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCkhOMSA8LSB0YWJsZShkbGR0JE1hcml0YWxTdGF0dXMpL3N1bShucm93KGRsZHQpKQ0KSE4xDQpgYGANCg0KKioqQuG6o25nIHThuqduIHPhu5EqKioNCg0KYGBge3J9DQpITjI8LXRhYmxlKGRsZHQkTWFyaXRhbFN0YXR1cykNCkhOMg0KYGBgDQoNClbhuq15IHRyb25nIGRhdGEgbsOgeSBjw7MgYHIgdGFibGUoZCRNYXJpdGFsU3RhdHVzKVsxXS9zdW0obnJvdyhkKSkqMTAwYCUgxJHDoyBr4bq/dCBow7RuIHbDoCBgciB0YWJsZShkJE1hcml0YWxTdGF0dXMpWzJdL3N1bShucm93KGQpKSoxMDBgJSBjaMawYSBr4bq/dCBow7RuDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCiMgVuG6vSBiaeG7g3UgxJHhu5MgdHLDsm4NCnBpZShITjIgLA0KICAgIG1haW4gICA9ICJUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLA0KICAgIGxhYmVscyA9IHBhc3RlMChuYW1lcyhITjIpLA0KICAgICAgICAgICAgICAgICAgICAiICgiLCByb3VuZChITjEgKjEwMCwgMSksICIlKSIpLA0KICAgIGNvbCAgICA9IGMoImJsdWUiLCAicmVkIikpDQpgYGANCg0KYGBge3J9DQojIMSQ4bq/bSBz4buRIG5nxrDhu51pIGvhur90IGjDtG4gKE0pIHbDoCDEkeG7mWMgdGjDom4gKFMpDQpITl9jb3VudHMgPC0gdGFibGUoZGxkdCRNYXJpdGFsU3RhdHVzKQ0KDQojIENow6puaCBs4buHY2ggduG7gSBz4buRIGzGsOG7o25nIGdp4buvYSBoYWkgbmjDs20NCkhOX2RpZmYgPC0gYWJzKEhOX2NvdW50c1siTSJdIC0gSE5fY291bnRzWyJTIl0pDQpITl9kaWZmDQpgYGANCg0KVHJvbmcgYuG7mSBk4buvIGxp4buHdSBuw6B5IGPDsyBz4buxIGNow6puaCBs4buHbmggbmjhuqV0IMSR4buLbmggduG7gSB0w6xuaCB0cuG6oW5nIGjDtG4gbmjDom4uIFPhu7EgY2jDqm5oIGzhu4djaCB0dXnhu4d0IMSR4buRaSBnaeG7r2Egc+G7kSBuZ8aw4budaSBr4bq/dCBow7RuIHbDoCBjaMawYSBr4bq/dCBow7RuIGzDoCAzMjcgbmfGsOG7nWkuDQoNCiMjICoqMi4yIE3DtCB04bqjIGJp4bq/biBDaXR5KioNCg0KKioqQuG6o25nIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCmMxIDwtIHRhYmxlKGRsZHQkQ2l0eSkvc3VtKG5yb3coZGxkdCkpDQpjMQ0KYGBgDQoNCioqKkLhuqNuZyB04bqnbiBz4buRKioqDQoNCmBgYHtyfQ0KYzI8LXRhYmxlKGRsZHQkQ2l0eSkNCmMyDQpgYGANCg0KVuG6rXkgdHJvbmcgZGF0YSBuw6B5IGPDsyBgciByb3VuZCgwLjAyNzI0MjMzNiAqIDEwMCwgMilgJSBz4buRbmcg4bufIEFjYXB1bGNvIHbDoCBgciByb3VuZCgwLjAxMDE3MTQyMCAqIDEwMCwgMilgJSBz4buRbmcg4bufIEJlbGxpbmdoYW0uDQoNClRp4bq/cCB0aGVvLCBjw7MgYHIgcm91bmQoMC4wNTc2ODU0NjggKiAxMDAsIDIpYCUgc+G7kW5nIOG7nyBCZXZlcmx5IEhpbGxzLCBgciByb3VuZCgwLjA1OTMyMTQzMSAqIDEwMCwgMilgJSDhu58gQnJlbWVydG9uIHbDoCBgciByb3VuZCgwLjAzMjE1MDIyNCAqIDEwMCwgMilgJSDhu58gQ2FtYWNoby4NCg0KVHJvbmcga2hpIMSRw7MsIGNo4buJIGByIHJvdW5kKDAuMDA1MzM0NjYxICogMTAwLCAyKWAlIGPGsCBkw6JuIHPhu5FuZyDhu58gR3VhZGFsYWphcmEgdsOgIGByIHJvdW5kKDAuMDYwMTAzODQ4ICogMTAwLCAyKWAlIOG7nyBIaWRhbGdvLiBMb3MgQW5nZWxlcyBjw7MgdOG7tyBs4buHIGNhbyBoxqFuIHbhu5tpIGByIHJvdW5kKDAuMDY1ODY1MjgyICogMTAwLCAyKWAlLg0KDQpNZXhpY28gQ2l0eSBjaGnhur9tIGByIHJvdW5kKDAuMDEzNzk4OTkwICogMTAwLCAyKWAlLCB0aOG6pXAgaMahbiBzbyB24bubaSBQb3J0bGFuZCAoYHIgcm91bmQoMC4wNjIzMDg4NDEgKiAxMDAsIDIpYCUpIHbDoCBTYWxlbSAoYHIgcm91bmQoMC4wOTg1ODQ1MzcgKiAxMDAsIDIpYCUpLg0KDQpDw6FjIHRow6BuaCBwaOG7kSB04bqhaSBXYXNoaW5ndG9uIG5oxrAgU2VhdHRsZSAoYHIgcm91bmQoMC4wNjU1ODA3NjcgKiAxMDAsIDIpYCUpLCBTcG9rYW5lIChgciByb3VuZCgwLjA2MjIzNzcxMiAqIDEwMCwgMilgJSksIHbDoCBUYWNvbWEgKGByIHJvdW5kKDAuMDg5NDA4OTIwICogMTAwLCAyKWAlKSBjw7MgdOG7tyBs4buHIMSRw6FuZyBr4buDLg0KDQpDdeG7kWkgY8O5bmcsIGPDoWMga2h1IHbhu7FjIG5o4buPIG5oxrAgVmljdG9yaWEgKGByIHJvdW5kKDAuMDEyNTE4NjcxICogMTAwLCAyKWAlKSwgV2FsbGEgV2FsbGEgKGByIHJvdW5kKDAuMDExMzgwNjEwICogMTAwLCAyKWAlKSB2w6AgWWFraW1hIChgciByb3VuZCgwLjAyNjc0NDQzNCAqIDEwMCwgMilgJSkgY2jhu4kgY2hp4bq/bSBwaOG6p24gbmjhu48uDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBC4bqjbmcgdOG6p24gc3XhuqV0IHbDoCBwaOG6p24gdHLEg20gZMaw4bubaSBk4bqhbmcgZGF0YS5mcmFtZQ0KY19kZiA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRsZHQkQ2l0eSkpDQpjb2xuYW1lcyhjX2RmKSA8LSBjKCJDaXR5IiwgIkZyZXF1ZW5jeSIpDQpjX2RmJFBlcmNlbnRhZ2UgPC0gcm91bmQoY19kZiRGcmVxdWVuY3kgLyBzdW0oY19kZiRGcmVxdWVuY3kpICogMTAwLCAyKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTIGPhu5l0DQpnZ3Bsb3QoY19kZiwgYWVzKHggPSByZW9yZGVyKENpdHksIC1QZXJjZW50YWdlKSwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBDaXR5KSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2UsICIlIikpLCB2anVzdCA9IC0wLjMsIHNpemUgPSAzKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUGjDom4gYuG7kSB0aGVvIHRow6BuaCBwaOG7kSIsDQogICAgeCA9ICJUaMOgbmggcGjhu5EiLA0KICAgIHkgPSAiUGjhuqduIHRyxINtICglKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQotXD4gTeG7qWMgZ2lhbyBk4buLY2ggY8OzIHPhu7EgY2jDqm5oIGzhu4djaCByw7UgcuG7h3QgZ2nhu69hIGPDoWMgdGjDoG5oIHBo4buRLCB24bubaSBt4buZdCBz4buRIHRow6BuaCBwaOG7kSBjw7MgbeG7qWMgZ2lhbyBk4buLY2ggcuG6pXQgY2FvLCB0cm9uZyBraGkgbeG7mXQgc+G7kSBraMOhYyBs4bqhaSBjw7MgbeG7qWMgZ2lhbyBk4buLY2ggdGjhuqVwLiDEkGnhu4F1IG7DoHkgY8OzIHRo4buDIGRvIGPDoWMgeeG6v3UgdOG7kSBuaMawIGTDom4gc+G7kSwgbmh1IGPhuqd1IHRo4buLIHRyxrDhu51uZywgaG/hurdjIG3hu6ljIMSR4buZIHBow6F0IHRyaeG7g24ga2luaCB04bq/IHThuqFpIG3hu5dpIHRow6BuaCBwaOG7kS4gQ8OhYyB0aMOgbmggcGjhu5EgbOG7m24gbmjGsCBMb3MgQW5nZWxlcywgU2FuIERpZWdvIHbDoCBQb3J0bGFuZCBjw7MgeHUgaMaw4bubbmcgY8OzIG3hu6ljIGdpYW8gZOG7i2NoIGNhbyBoxqFuLCB0cm9uZyBraGkgY8OhYyB0aMOgbmggcGjhu5EgbmjGsCBHdWFkYWxhamFyYSB2w6AgQWNhcHVsY28gY8OzIHRo4buDIMSRYW5nIGfhurdwIGtow7Mga2jEg24gdHJvbmcgdmnhu4djIHRodSBow7p0IGdpYW8gZOG7i2NoLg0KDQojIyAqKjIuMyBNw7QgdOG6oyBiaeG6v24gU3RhdGVvclByb3ZpbmNlKioNCg0KKioqQuG6o25nIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCkJUMSA8LSB0YWJsZShkbGR0JFN0YXRlb3JQcm92aW5jZSkvc3VtKG5yb3coZGxkdCkpDQpCVDENCmBgYA0KDQoqKipC4bqjbmcgdOG6p24gc+G7kSoqKg0KDQpgYGB7cn0NCkJUMjwtdGFibGUoZGxkdCRTdGF0ZW9yUHJvdmluY2UpDQpCVDINCmBgYA0KDQpW4bqteSB0cm9uZyBkYXRhIG7DoHkgY8OzIDUuNzUlIHThu7cgbOG7hyBnaWFvIGThu4tjaCBz4buRbmcg4bufIEJDIHbDoCAxOS40NCUgc+G7kW5nIOG7nyBDQS4NCg0KVGnhur9wIHRoZW8sIGPDsyA1LjgwJSB04bu3IGzhu4cgZ2lhbyBk4buLY2ggc+G7kW5nIOG7nyBERiwgMi43MiUg4bufIEd1ZXJyZXJvIHbDoCAwLjUzJSDhu58gSmFsaXNjby4NCg0KVHJvbmcga2hpIMSRw7MsIE9SIGNoaeG6v20gMTYuMDklIHbDoCBWZXJhY3J1eiBsw6AgMy4zMCUuIFThu7cgbOG7hyBjYW8gbmjhuqV0IGzDoCBiYW5nIFdBIHbhu5tpIDMyLjQ4JS4NCg0KWXVjYXRhbiBjw7MgdOG7tyBs4buHIDQuNjUlLCB0cm9uZyBraGkgWmFjYXRlY2FzIGNoaeG6v20gOS4yMyUgdOG7lW5nIHPhu5Ega2jDoWNoIGjDoG5nIHRyb25nIGThu68gbGnhu4d1Lg0KDQrEkGnhu4F1IG7DoHkgxJFp4buBdSB0aOG7gyBoaeG7h24gcXVhIGJp4buDdSDEkeG7kyBU4bqnbiBz4buRIGdpYW8gZOG7i2NoIHRoZW8gdGnhu4N1IGJhbmcNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0Kc3RhdGVfY291bnRzIDwtIGRsZHQgJT4lDQogIGNvdW50KFN0YXRlb3JQcm92aW5jZSkNCmdncGxvdChzdGF0ZV9jb3VudHMsIGFlcyh4ID0gcmVvcmRlcihTdGF0ZW9yUHJvdmluY2UsIG4pLCB5ID0gbikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiYmx1ZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCBoanVzdCA9IC0wLjEsIHNpemUgPSAzLjUpICsgIA0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIlThuqduIHPhu5EgZ2lhbyBk4buLY2ggdGhlbyB0aeG7g3UgYmFuZyIsDQogICAgICAgeCA9ICJTdGF0ZSAvIFByb3ZpbmNlIiwNCiAgICAgICB5ID0gIlPhu5EgZ2lhbyBk4buLY2giKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KIyMgKioyLjQgTcO0IHThuqMgYmnhur9uIENvdW50cnkqKg0KDQoqKipC4bqjbmcgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KY28xIDwtIHRhYmxlKGRsZHQkQ291bnRyeSkvc3VtKG5yb3coZGxkdCkpDQpjbzENCmBgYA0KDQoqKipC4bqjbmcgdOG6p24gc+G7kSoqKg0KDQpgYGB7cn0NCmNvMjwtdGFibGUoZGxkdCRDb3VudHJ5KQ0KY28yDQpgYGANCg0KVuG6rXkgdHJvbmcgZGF0YSBuw6B5IGPDsyBgciB0YWJsZShkbGR0JENvdW50cnkpWzFdL3N1bShucm93KGRsZHQpKSoxMDBgJSBnaWFvIGThu4tjaCBtdWEgaMOgbmcgdOG6oWkgQ2FuYWRhLCBgciB0YWJsZShkbGR0JENvdW50cnkpWzJdL3N1bShucm93KGRsZHQpKSoxMDBgJSBnaWFvIGThu4tjaCBtdWEgaMOgbmcgdOG6oWkgTWV4aWNvIHbDoCBgciB0YWJsZShkbGR0JENvdW50cnkpWzNdL3N1bShucm93KGRsZHQpKSoxMDBgJSB04bqhaSBVU0EuDQoNCkPDoWMgdMOtbmggdG/DoW4gbsOgeSDEkcaw4bujYyB0aOG7kW5nIGvDqiBxdWEgYmnhu4N1IMSR4buTICJQaMOibiBi4buRIGzGsOG7o3QgbXVhIGjDoG5nIHRoZW8gQ291bnRyeSIuDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCiMgVuG6vSBiaeG7g3UgxJHhu5MgdHLDsm4gduG7m2kgMyB0aMOgbmggcGjhu5ENCnBpZShjbzEsDQogICAgbWFpbiAgID0gIlBow6JuIGLhu5EgbMaw4bujdCBtdWEgaMOgbmcgdGhlbyBDb3VudHJ5IiwNCiAgICBsYWJlbHMgPSBwYXN0ZTAobmFtZXMoY28xKSwNCiAgICAgICAgICAgICAgICAgICAgIiAoIiwgcm91bmQoY28xKjEwMCAsIDEpLCAiJSkiKSwNCiAgICBjb2wgICAgPSBjKCJibHVlIiwgInJlZCIsICJncmVlbiIpKQ0KDQpgYGANCg0KIyMgKioyLjUgTcO0IHThuqMgYmnhur9uIFByb2R1Y3RGYW1pbHkqKg0KDQoqKipC4bqjbmcgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KZjEgPC0gdGFibGUoZGxkdCRQcm9kdWN0RmFtaWx5KS9zdW0obnJvdyhkbGR0KSkNCmYxDQpgYGANCg0KKioqQuG6o25nIHThuqduIHPhu5EqKioNCg0KYGBge3J9DQpmMjwtdGFibGUoZGxkdCRQcm9kdWN0RmFtaWx5KQ0KZjINCmBgYA0KDQpQaOG6p24gbOG7m24gc+G6o24gcGjhuqltIMSRxrDhu6NjIGLDoW4gbMOgIHRo4buxYyBwaOG6qW0gduG7m2kgYHIgdGFibGUoZGxkdCRQcm9kdWN0RmFtaWx5KVsyXS9zdW0obnJvdyhkbGR0KSkqMTAwYCUgLCB0aeG6v3AgdGhlbyBsw6AgaMOgbmcgcGhpIHRpw6p1IGTDuW5nIGByIHRhYmxlKGRsZHQkUHJvZHVjdEZhbWlseSlbM10vc3VtKG5yb3coZGxkdCkpKjEwMGAlIHbDoCDEkeG7kyB14buRbmcgYHIgdGFibGUoZGxkdCRQcm9kdWN0RmFtaWx5KVsxXS9zdW0obnJvdyhkbGR0KSkqMTAwYCUuIMSQaeG7gXUgbsOgeSBjaG8gdGjhuqV5IHRo4buxYyBwaOG6qW0gbMOgIG5ow7NtIGjDoG5nIGNo4bunIGzhu7FjIHRyb25nIGhv4bqhdCDEkeG7mW5nIGLDoW4gbOG6uyBj4bunYSBzacOqdSB0aOG7iy4NCg0KQmnhu4N1IMSR4buTIG1pbmggaOG7jWEgxJHGsOG7o2MgdGjhu4MgaGnhu4duIGzDoCBiaeG7g3UgxJHhu5MgdHLDsm4uDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCiMgVuG6vSBiaeG7g3UgxJHhu5MgdHLDsm4gduG7m2kgMyB0aMOgbmggcGjhu5ENCnBpZShmMiwNCiAgICBtYWluICAgPSAiUGjDom4gYuG7kSB0aGVvIFByb2R1Y3RGYW1pbHkiLA0KICAgIGxhYmVscyA9IHBhc3RlMChuYW1lcyhmMiksDQogICAgICAgICAgICAgICAgICAgICIgKCIsIHJvdW5kKGYyICwgMSksICIpIiksDQogICAgY29sICAgID0gYygiYmx1ZSIsICJyZWQiLCAiZ3JlZW4iKSkNCmBgYA0KDQojIyAqKjIuNiBNw7QgdOG6oyBiaeG6v24gUHJvZHVjdERlcGFydG1lbnQqKg0KDQoqKipC4bqjbmcgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KZHAxIDwtIHRhYmxlKGRsZHQkUHJvZHVjdERlcGFydG1lbnQpL3N1bShucm93KGRsZHQpKSoxMDANCmRwMQ0KYGBgDQoNCioqKkLhuqNuZyB04bqnbiBz4buRKioqDQoNCmBgYHtyfQ0KZHAyPC10YWJsZShkbGR0JFByb2R1Y3REZXBhcnRtZW50KQ0KZHAyDQpgYGANCg0KVuG6rXkgdHJvbmcgZGF0YSBuw6B5IGPDsyAyLjUzJSBz4bqjbiBwaOG6qW0gdGh14buZYyBuaMOzbSBBbGNvaG9saWMgQmV2ZXJhZ2VzIHbDoCAzLjAyJSB0aHXhu5ljIEJha2VkIEdvb2RzLg0KDQpCYWtpbmcgR29vZHMgY2hp4bq/bSA3LjYzJSwgY2FvIGjGoW4gQnJlYWtmYXN0IEZvb2RzIDEuMzQlIHbDoCBDYW5uZWQgUHJvZHVjdHMgMC43OCUuDQoNCkNhbm5lZCBGb29kcyBjaGnhur9tIDYuOTUlLCB0cm9uZyBraGkgQ2Fyb3VzZWwgdsOgIENoZWNrb3V0IGNo4buJIGNoaeG6v20gbOG6p24gbMaw4bujdCAwLjQyJSB2w6AgMC41OCUuDQoNCkPDoWMgbmjDs20gbOG7m24gaMahbiBn4buTbSBEYWlyeSA2LjQyJSwgRGVsaSA0Ljk3JSB2w6AgRWdncyAxLjQxJS4NCg0KRnJvemVuIEZvb2RzIGNoaeG6v20gOS44MyUsIEhlYWx0aCBhbmQgSHlnaWVuZSBsw6AgNi4zNSUsIHRyb25nIGtoaSBIb3VzZWhvbGQgbMOgIG3hu5l0IHRyb25nIGPDoWMgbmjDs20gbOG7m24gbmjhuqV0IHbhu5tpIDEwLjEwJS4NCg0KTmjDs20gc+G6o24gcGjhuqltIGNoaeG6v20gdOG7tyBs4buHIGNhbyBuaOG6pXQgbMOgIFByb2R1Y2UsIHbhu5tpIDE0LjE4JSwgdGnhur9wIHRoZW8gbMOgIFNuYWNrIEZvb2RzIDExLjM4JS4NCg0KTmfGsOG7o2MgbOG6oWksIG3hu5l0IHPhu5EgbmjDs20gbmjhu48gbmjGsCBNZWF0IDAuNjMlLCBTZWFmb29kIDAuNzMlIHbDoCBTdGFyY2h5IEZvb2RzIDEuOTclIGNo4buJIGNoaeG6v20gbeG7mXQgcGjhuqduIG5o4buPIHRyb25nIHThu5VuZyBwaMOibiBwaOG7kWkuDQoNCmBgYHtyfQ0KZGVwdF9jb3VudHMgPC0gZGxkdCAlPiUNCiAgY291bnQoUHJvZHVjdERlcGFydG1lbnQpDQoNCmdncGxvdChkZXB0X2NvdW50cywgYWVzKHggPSByZW9yZGVyKFByb2R1Y3REZXBhcnRtZW50LCBuKSwgeSA9IG4pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInBpbmsiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgaGp1c3QgPSAtMC4xLCBzaXplID0gMy41KSArICANCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJU4bqnbiBz4buRIGdpYW8gZOG7i2NoIHRoZW8gcGjDsm5nIGJhbiBz4bqjbiBwaOG6qW0iLA0KICAgICAgIHggPSAiUGjDsm5nIGJhbiBz4bqjbiBwaOG6qW0gKFByb2R1Y3REZXBhcnRtZW50KSIsDQogICAgICAgeSA9ICJT4buRIGzGsOG7o25nIGdpYW8gZOG7i2NoIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjICoqMi43IE3DtCB04bqjIGJp4bq/biBQcm9kdWN0Q2F0ZWdvcnkqKg0KDQoqKipC4bqjbmcgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KcGMxIDwtIHRhYmxlKGRsZHQkUHJvZHVjdENhdGVnb3J5KS9zdW0obnJvdyhkbGR0KSkqMTAwDQpwYzENCmBgYA0KDQoqKipC4bqjbmcgdOG6p24gc+G7kSoqKg0KDQpgYGB7cn0NCnBjMjwtdGFibGUoZGxkdCRQcm9kdWN0Q2F0ZWdvcnkpDQpwYzINCmBgYA0KDQpW4bqteSB0cm9uZyBkYXRhIG7DoHkgY8OzIDMuNDQlIHPhuqNuIHBo4bqpbSB0aHXhu5ljIG5ow7NtIEJha2luZyBHb29kcywgMi42MCUgbMOgIEJhdGhyb29tIFByb2R1Y3RzLCB2w6AgMi41MyUgbMOgIEJlZXIgYW5kIFdpbmUuDQoNClRp4bq/cCB0aGVvLCBCcmVhZCBjaGnhur9tIDMuMDIlLCBCcmVha2Zhc3QgRm9vZHMgMi45NyUsIGPDsm4gQ2FuZGxlcyBjaOG7iSBjaGnhur9tIDAuMzIlLg0KDQpDw6FjIG5ow7NtIG5oxrAgQ2FuZHkgMi41MCUsIENhbm5lZCBBbmNob3ZpZXMgMC4zMSUsIENhbm5lZCBDbGFtcyAwLjM4JSwgQ2FubmVkIE95c3RlcnMgMC4yNSUgdsOgIENhbm5lZCBTaHJpbXAgMC4yNyUgY8OzIHThu7cgbOG7hyBy4bqldCBuaOG7jy4NCg0KQ2FubmVkIFNvdXAgY2hp4bq/bSAyLjg3JSwgY2FvIGjGoW4gQ2FubmVkIFR1bmEgMC42MiUgdsOgIENhcmJvbmF0ZWQgQmV2ZXJhZ2VzIDEuMTAlLg0KDQpDbGVhbmluZyBTdXBwbGllcyBjw7MgdOG7tyBs4buHIDEuMzQlLCBDb2xkIFJlbWVkaWVzIGzDoCAwLjY2JSwgdHJvbmcga2hpIERhaXJ5IGzDoCBt4buZdCB0cm9uZyBjw6FjIG5ow7NtIGzhu5tuIHbhu5tpIDYuNDIlLg0KRHJpbmtzIGNoaeG6v20gMC45NiUsIGPDsm4gRWdncyBsw6AgMS40MSUuDQoNCkZyb3plbiBEZXNzZXJ0cyAyLjMwJSB2w6AgRnJvemVuIEVudHJlZXMgMC44NCUgdGjhuqVwIGjGoW4gc28gduG7m2kgRnJ1aXQgNS40NCUgdsOgIE1lYXQgNS40MSUuDQoNCkhvdCBCZXZlcmFnZXMgY2hp4bq/bSAxLjYxJSwgSHlnaWVuZSAxLjQwJSwgSmFtcyBhbmQgSmVsbGllcyA0LjE4JSwgdsOgIEtpdGNoZW4gUHJvZHVjdHMgMS41NCUuDQoNCkPDoWMgbmjDs20ga2jDoWMgbmjGsCBNYWdhemluZXMgMS40NCUsIE1pc2NlbGxhbmVvdXMgMC4zMCUgdsOgIFBhY2thZ2VkIFZlZ2V0YWJsZXMgMC4zNCUgY2hp4bq/bSB04bu3IGzhu4cga2jDoSBuaOG7jy4NCg0KUGFwZXIgUHJvZHVjdHMgY8OzIDIuNDUlLCBQaXp6YSBsw6AgMS4zOCUsIHbDoCBTbmFjayBGb29kcyDEkeG7qW5nIMSR4bqndSB24bubaSAxMS4zOCUuDQoNCkN14buRaSBjw7luZywgbmjDs20gVmVnZXRhYmxlcyBjaGnhur9tIHThu7cgbOG7hyBjYW8gbmjhuqV0IHbhu5tpIDEyLjI5JSwgdGnhur9wIHRoZW8gbMOgIFNuYWNrIEZvb2RzLCB0cm9uZyBraGkgY8OhYyBuaMOzbSBuaOG7jyBuaMawIFNlYWZvb2QgMC43MyUgdsOgIFNpZGUgRGlzaGVzIDEuMDklIGNo4buJIMSRw7NuZyBnw7NwIG3hu5l0IHBo4bqnbiBuaOG7jyB0cm9uZyB04buVbmcgcGjDom4gcGjhu5FpLg0KDQpgYGB7cn0NCmR0X2NvdW50cyA8LSBkbGR0ICU+JQ0KICBjb3VudChQcm9kdWN0Q2F0ZWdvcnkpDQoNCmdncGxvdChkdF9jb3VudHMsIGFlcyh4ID0gcmVvcmRlcihQcm9kdWN0Q2F0ZWdvcnksIG4pLCB5ID0gbikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAieWVsbG93IikrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgaGp1c3QgPSAtMC4xLCBzaXplID0gMikgKyAgDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiRG9hbmggbeG7pWMgbXVhIGjDoG5nIGPhu6UgdGjhu4MiLA0KICAgICAgIHggPSAiRGFuaCBt4bulYyAoUHJvZHVjdENhdGVnb3J5KSIsDQogICAgICAgeSA9ICJT4buRIGzGsOG7o25nIGdpYW8gZOG7i2NoIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjICoqMi44LiBNw7QgdOG6oyBiaeG6v24gR2VuZGVyKioNCg0KKioqQuG6o25nIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCmcxIDwtIHRhYmxlKGQkR2VuZGVyKS9zdW0obnJvdyhkKSkNCmcxDQpgYGANCg0KKioqQuG6o25nIHThuqduIHPhu5EqKioNCg0KYGBge3J9DQpnMjwtdGFibGUoZCRHZW5kZXIpDQpnMg0KYGBgDQoNClbhuq15IHRyb25nIGRhdGEgbsOgeSBjw7MgYHIgdGFibGUoZCRHZW5kZXIpWzFdL3N1bShucm93KGQpKSoxMDBgJSBsw6AgbuG7ryBgciB0YWJsZShkJE1hcml0YWxTdGF0dXMpWzJdL3N1bShucm93KGQpKSoxMDBgJSBsw6AgbmFtDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCiMgVuG6vSBiaeG7g3UgxJHhu5MgdHLDsm4NCnBpZShnMiAsDQogICAgbWFpbiAgID0gIlThu7cgbOG7hyBnaeG7m2kgdMOtbmgiLA0KICAgIGxhYmVscyA9IHBhc3RlMChuYW1lcyhnMiksDQogICAgICAgICAgICAgICAgICAgICIgKCIsIHJvdW5kKGcxICoxMDAsIDEpLCAiJSkiKSwNCiAgICBjb2wgICAgPSBjKCJibHVlIiwgInJlZCIpKQ0KYGBgDQoNCiMjICoqMi45LiBNw7QgdOG6oyBiaeG6v24gSG9tZW93bmVyKioNCg0KKioqQuG6o25nIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCkgxIDwtIHRhYmxlKGQkSG9tZW93bmVyKS9zdW0obnJvdyhkKSkNCkgxDQpgYGANCg0KKioqQuG6o25nIHThuqduIHPhu5EqKioNCg0KYGBge3J9DQpIMjwtdGFibGUoZCRIb21lb3duZXIpDQpIMg0KYGBgDQoNClbhuq15IHRyb25nIGRhdGEgbsOgeSBjw7MgYHIgdGFibGUoZCRIb21lb3duZXIpWzFdL3N1bShucm93KGQpKSoxMDBgJSDEkcOjIGPDsyBuaMOgIHbDoCBgciB0YWJsZShkJEhvbWVvd25lcilbMl0vc3VtKG5yb3coZCkpKjEwMGAlIGNoxrBhIGPDsyBuaMOgDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCiMgVuG6vSBiaeG7g3UgxJHhu5MgdHLDsm4NCnBpZShIMiAsDQogICAgbWFpbiAgID0gIlBow6JuIGLhu5Egc+G7nyBo4buvdSBuaMOgIiwNCiAgICBsYWJlbHMgPSBwYXN0ZTAobmFtZXMoSDIpLA0KICAgICAgICAgICAgICAgICAgICAiICgiLCByb3VuZChIMSAqMTAwLCAxKSwgIiUpIiksDQogICAgY29sICAgID0gYygiYmx1ZSIsICJyZWQiKSkNCmBgYA0KDQojIyAqKjIuMTAuIE3DtCB04bqjIGJp4bq/biBBbm51YWxJbmNvbWUqKg0KDQoqKipC4bqjbmcgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KQTEgPC0gdGFibGUoZCRBbm51YWxJbmNvbWUpL3N1bShucm93KGRsZHQpKSoxMDANCkExDQpgYGANCg0KDQoqKipC4bqjbmcgdOG6p24gc+G7kSoqKg0KDQpgYGB7cn0NCkEyPC10YWJsZShkJEFubnVhbEluY29tZSkNCkEyDQpgYGANCg0KTmjDs20gMzBL4oCTNTBrICBjaGnhur9tIHThu7cgbOG7hyBs4bubbiBuaOG6pXQgduG7m2kgMzIuNyUsIGNhbyBn4bqlcCBoxqFuIDE2IGzhuqduIHNvIHbhu5tpIG5ow7NtIHRodSBuaOG6rXAgY2FvIG5o4bqldCAoJDE1MEsrKSwgY2jhu4kgxJHhuqF0IDEuOTQlLg0KDQpDw6FjIG5ow7NtIHRodSBuaOG6rXAgdOG7qyA5MEsgdHLhu58gbMOqbiAoZ+G7k20gNCBuaMOzbSkgxJHhu4F1IGPDsyB04bu3IGzhu4cgZMaw4bubaSA1LjUlLCB0aOG6pXAgaMahbiBuaMOzbSB0cnVuZyBiw6xuaCAzMEvigJM1MEsgdOG7qyB+MjfigJMzMSDEkWnhu4NtIHBo4bqnbiB0csSDbSwgY2hvIHRo4bqleSBt4bupYyDEkeG7mSB0aGFtIGdpYSBy4bqldCB0aOG6pXAgdOG7qyBuaMOzbSB0aHUgbmjhuq1wIGNhby4NCg0KTmfGsOG7o2MgbOG6oWksIG5ow7NtIHRodSBuaOG6rXAgdGjhuqVwICgxMEvigJMzMEspIGNoaeG6v20gfjIyJSwgdGjhuqVwIGjGoW4gbmjDs20gdHJ1bmcgYsOsbmggfjEwLjcgxJFp4buDbSBwaOG6p24gdHLEg20sIG5oxrBuZyB24bqrbiBjaG8gdGjhuqV5IHPhu7EgdGhhbSBnaWEgdMawxqFuZyDEkeG7kWkgxJHDoW5nIGvhu4MuDQoNCk5ow6xuIGNodW5nLCBk4buvIGxp4buHdSB0aOG7gyBoaeG7h24gbeG7mXQgeHUgaMaw4bubbmcgcsO1IHLDoG5nOiBnaWFvIGThu4tjaCB04bqtcCB0cnVuZyBjaOG7pyB54bq/dSDhu58gbmjDs20gdGh1IG5o4bqtcCB0cnVuZyBiw6xuaOKAk3Ro4bqlcCwgdHJvbmcga2hpIG5ow7NtIHRodSBuaOG6rXAgY2FvIGPDsyB04bu3IGzhu4cgdGhhbSBnaWEgcuG6pXQga2hpw6ptIHThu5FuLCBwaOG6o24gw6FuaCBz4buxIHBow6JuIGLhu5Ega2jDtG5nIMSR4buTbmcgxJHhu4F1IHbhu4EgbeG6t3QgdGh1IG5o4bqtcCB0cm9uZyB04bqtcCBraMOhY2ggaMOgbmcuDQoNCg0KKioqQmnhu4N1IMSR4buTKioqDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KDQojIMSQ4bq/bSBz4buRIGdpYW8gZOG7i2NoIHRoZW8gQW5udWFsSW5jb21lIHbDoCBz4bqvcCB44bq/cA0KYXRfY291bnRzIDwtIGRsZHQgJT4lDQogIGNvdW50KEFubnVhbEluY29tZSkNCg0KZ2dwbG90KGF0X2NvdW50cywgYWVzKHggPSByZW9yZGVyKEFubnVhbEluY29tZSwgLW4pLCB5ID0gbiwgZmlsbCA9IEFubnVhbEluY29tZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjMsIHNpemUgPSAzKSArDQogIGxhYnModGl0bGUgPSAiRG9hbmggbeG7pWMgbXVhIGjDoG5nIHRoZW8gQW5udWFsIEluY29tZSIsDQogICAgICAgeCA9ICJUaHUgbmjhuq1wIGjhurFuZyBuxINtIChBbm51YWxJbmNvbWUpIiwNCiAgICAgICB5ID0gIlPhu5EgbMaw4bujbmcgZ2lhbyBk4buLY2giLA0KICAgICAgIGZpbGwgPSAiQW5udWFsSW5jb21lIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG9wdGlvbiA9ICJDIiwgYmVnaW4gPSAwLjIsIGVuZCA9IDAuOSkgIA0KYGBgDQoNCiMgKiozLiDGr+G7m2MgbMaw4bujbmcgS2hv4bqjbmcgdsOgIEtp4buDbSDEkeG7i25oIEdp4bqjIHRodXnhur90IGNobyBU4bu3IGzhu4cgKE3hu5l0IGJp4bq/bikqKg0KDQojIyAqKjMuMSBHZW5kZXIgIk7hu68iKioNCg0KIEdp4bqjIHPhu60gIk7hu68iIGPDsyBtw6MgbMOgICJGIiB0cm9uZyBj4buZdCAnR2VuZGVyJy4gS2nhu4NtIHRyYSBsZXZlbHMoZGxkdCRHZW5kZXIpIMSR4buDIGNo4bqvYyBjaOG6r24uDQogDQpgYGB7cn0NCg0KDQojIEdoaSBjaMO6OiDEkG/huqFuIGNvZGUgduG7gSAndGFibGVfcnInIHbDoCAncmlza3JhdGlvJyDEkcOjIMSRxrDhu6NjIGxv4bqhaSBi4buPIGto4buPaSDEkcOieS4NCiMgTuG6v3UgYuG6oW4gbXXhu5FuIHBow6JuIHTDrWNoIG3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgR2VuZGVyIHbDoCBIb21lb3duZXIsIG7DsyBuw6puIOG7nyBQaOG6p24gNC4NCmBgYA0KDQoNCmBgYHtyfQ0KY2F0ZWdvcnlfZmVtYWxlIDwtICJGIiANCk5fZmVtYWxlIDwtIHN1bShkbGR0JEdlbmRlciA9PSBjYXRlZ29yeV9mZW1hbGUpICMgU+G7kSBsxrDhu6NuZyBu4buvDQpOX3RvdGFsX2dlbmRlciAgPC0gbnJvdyhkbGR0KSAgICAgICAgICAgICAgICAjIFThu5VuZyBz4buRIHF1YW4gc8OhdA0KDQpjYXQocGFzdGUoIlPhu5EgbmfGsOG7nWkgbuG7ryAoR2VuZGVyID0iLCBjYXRlZ29yeV9mZW1hbGUsICIpOiIsIE5fZmVtYWxlLCAiXG4iKSkNCmNhdChwYXN0ZSgiVOG7lW5nIHPhu5EgcXVhbiBzw6F0OiIsIE5fdG90YWxfZ2VuZGVyLCAiXG5cbiIpKQ0KYGBgDQogDQogDQogIEdp4bqjIHRodXnhur90Og0KSDA6IFThu7cgbOG7hyBu4buvIHRyb25nIHThu5VuZyB0aOG7gyBsw6AgNTAlIChwX2ZlbWFsZSA9IDAuNSkNCkgxOiBU4bu3IGzhu4cgbuG7ryB0cm9uZyB04buVbmcgdGjhu4Mga2jDoWMgNTAlIChwX2ZlbWFsZSAhPSAwLjUpDQoNCmBgYHtyfQ0KcDBfZmVtYWxlIDwtIDAuNQ0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KIyBUw61uaCBraG/huqNuZyB0aW4gY+G6rXkgOTUlIHbDoCBraeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dA0KdGVzdF9mZW1hbGVfcmVzdWx0cyA8LSBwcm9wLnRlc3QoeCA9IE5fZmVtYWxlLCBuID0gTl90b3RhbF9nZW5kZXIsIHAgPSBwMF9mZW1hbGUsIGNvcnJlY3QgPSBGQUxTRSkNCg0KY2F0KCJL4bq/dCBxdeG6oyBraeG7g20gxJHhu4tuaCBjaG8gdOG7tyBs4buHIG7hu686XG4iKQ0KcHJpbnQodGVzdF9mZW1hbGVfcmVzdWx0cykNCg0KDQpgYGANCg0KYGBge3J9DQojIFRyw61jaCB4deG6pXQgY8OhYyBnacOhIHRy4buLIHThu6sga+G6v3QgcXXhuqMga2nhu4NtIMSR4buLbmgNCnBfdmFsdWVfZmVtYWxlIDwtIHRlc3RfZmVtYWxlX3Jlc3VsdHMkcC52YWx1ZQ0Kc2FtcGxlX3BfZmVtYWxlIDwtIHRlc3RfZmVtYWxlX3Jlc3VsdHMkZXN0aW1hdGUNCmNvbmZfaW50X2ZlbWFsZSA8LSB0ZXN0X2ZlbWFsZV9yZXN1bHRzJGNvbmYuaW50DQoNCmNhdCgiXG5EaeG7hW4gZ2nhuqNpIGvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIGNobyBHaeG7m2kgdMOtbmggTuG7rzpcbiIpDQpjYXQocGFzdGUwKCIgIC0gS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIG7hu68gdHJvbmcgdOG7lW5nIHRo4buDIGzDoCAoIiwgDQogICAgICAgICAgIHJvdW5kKGNvbmZfaW50X2ZlbWFsZVsxXSwgNCksICIsICIsIA0KICAgICAgICAgICByb3VuZChjb25mX2ludF9mZW1hbGVbMl0sIDQpLCAiKS5cbiIpKQ0KY2F0KHBhc3RlMCgiICAgIMOdIG5naMSpYTogQ2jDum5nIHRhIHRpbiB0xrDhu59uZyA5NSUgcuG6sW5nIHThu7cgbOG7hyB0aOG7sWMgc+G7sSBj4bunYSBu4buvIHRyb25nIHRvw6BuIGLhu5kga2jDoWNoIGjDoG5nIG7hurFtIHRyb25nIGtob+G6o25nIG7DoHkuXG4iKSkNCmNhdChwYXN0ZTAoIiAgLSBU4bu3IGzhu4cgbuG7ryB0cm9uZyBt4bqrdSAocC1oYXQpIGzDoDogIiwgcm91bmQoc2FtcGxlX3BfZmVtYWxlLCA0KSwgIiAoaGF5ICIsIHJvdW5kKHNhbXBsZV9wX2ZlbWFsZSoxMDAsIDIpLCAiJSkuXG4iKSkNCmNhdChwYXN0ZTAoIiAgLSBHacOhIHRy4buLIHAtdmFsdWUgY+G7p2Ega2nhu4NtIMSR4buLbmggbMOgOiAiLCBmb3JtYXQucHZhbChwX3ZhbHVlX2ZlbWFsZSwgZGlnaXRzPTQsIGVwcz0wLjAwMDEpLCAiLlxuIikpDQoNCmFscGhhIDwtIDAuMDUgIyBN4bupYyDDvSBuZ2jEqWENCmNhdChwYXN0ZTAoIlxuVuG7m2kgbeG7qWMgw70gbmdoxKlhIGFscGhhID0gIiwgYWxwaGEsICI6XG4iKSkNCmlmIChwX3ZhbHVlX2ZlbWFsZSA8IGFscGhhKSB7DQogIGNhdChwYXN0ZTAoIiAgLSBL4bq/dCBsdeG6rW46IFbDrCBwLXZhbHVlICgiLCBmb3JtYXQucHZhbChwX3ZhbHVlX2ZlbWFsZSwgZGlnaXRzPTQsIGVwcz0wLjAwMDEpLCAiKSA8ICIsIGFscGhhLCANCiAgICAgICAgICAgICAiLCBjaMO6bmcgdGEgQsOBQyBC4buOIGdp4bqjIHRodXnhur90IEgwLlxuIikpDQogIGNhdChwYXN0ZTAoIiAgICBDw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogxJHhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyB04bu3IGzhu4cgbuG7ryB0aOG7sWMgc+G7sSB0cm9uZyB04buVbmcgdGjhu4MgS0jDgUMgIiwgcDBfZmVtYWxlKjEwMCwgIiUuXG4iKSkNCn0gZWxzZSB7DQogIGNhdChwYXN0ZTAoIiAgLSBL4bq/dCBsdeG6rW46IFbDrCBwLXZhbHVlICgiLCBmb3JtYXQucHZhbChwX3ZhbHVlX2ZlbWFsZSwgZGlnaXRzPTQsIGVwcz0wLjAwMDEpLCAiKSA+PSAiLCBhbHBoYSwgDQogICAgICAgICAgICAgIiwgY2jDum5nIHRhIEtIw5RORyBCw4FDIELhu44gZ2nhuqMgdGh1eeG6v3QgSDAuXG4iKSkNCiAgY2F0KHBhc3RlMCgiICAgIEtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogxJHhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyB04bu3IGzhu4cgbuG7ryB0aOG7sWMgc+G7sSB0cm9uZyB04buVbmcgdGjhu4Mga2jDoWMgIiwgcDBfZmVtYWxlKjEwMCwgDQogICAgICAgICAgICAgIiUuIChU4bupYyBsw6AsIGtow7RuZyB0aOG7gyBsb+G6oWkgdHLhu6sga2jhuqMgbsSDbmcgdOG7tyBs4buHIG7hu68gbMOgIDUwJSkuXG4iKSkNCn0NCmBgYA0KDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCnJlc3VsdF9tZWFuaW5nX2ZlbWFsZV9kZiA8LSBkYXRhLmZyYW1lKA0KICBQaOG6p24gPSBjKA0KICAgICJYLXNxdWFyZWQiLA0KICAgICJkZiIsDQogICAgInAtdmFsdWUiLA0KICAgICJhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIiwNCiAgICAiOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwiLA0KICAgICJzYW1wbGUgZXN0aW1hdGUgcCINCiAgKSwNCiAgR2nDoV90cuG7i1904burX1IgPSBjKA0KICAgIHJvdW5kKHRlc3RfZmVtYWxlX3Jlc3VsdHMkc3RhdGlzdGljLCA0KSwNCiAgICB0ZXN0X2ZlbWFsZV9yZXN1bHRzJHBhcmFtZXRlciwNCiAgICBmb3JtYXQucHZhbCh0ZXN0X2ZlbWFsZV9yZXN1bHRzJHAudmFsdWUsIGRpZ2l0cyA9IDQsIGVwcyA9IDAuMDAwMSksDQogICAgdGVzdF9mZW1hbGVfcmVzdWx0cyRhbHRlcm5hdGl2ZSwNCiAgICBwYXN0ZTAoIigiLCByb3VuZCh0ZXN0X2ZlbWFsZV9yZXN1bHRzJGNvbmYuaW50WzFdLCA0KSwgIiwgIiwgcm91bmQodGVzdF9mZW1hbGVfcmVzdWx0cyRjb25mLmludFsyXSwgNCksICIpIiksDQogICAgcm91bmQodGVzdF9mZW1hbGVfcmVzdWx0cyRlc3RpbWF0ZSwgNykNCiAgKSwNCiAgw51fbmdoxKlhX2NodW5nID0gYygNCiAgICAiR2nDoSB0cuG7iyB0aOG7kW5nIGvDqiBraeG7g20gxJHhu4tuaCBDaGktc3F1YXJlLiIsDQogICAgIkLhuq1jIHThu7EgZG8gY+G7p2Ega2nhu4NtIMSR4buLbmguIiwNCiAgICAiWMOhYyBzdeG6pXQgxJHhu4MgZOG7ryBsaeG7h3UgcXVhbiBzw6F0IMSRxrDhu6NjIChob+G6t2MgY+G7sWMgxJFvYW4gaMahbikgeHXhuqV0IGhp4buHbiBu4bq/dSBnaeG6oyB0aHV54bq/dCBI4oKAIMSRw7puZy4iLA0KICAgICJHaeG6oyB0aHV54bq/dCB0aGF5IHRo4bq/IMSRxrDhu6NjIHPhu60gZOG7pW5nICjhu58gxJHDonkgbMOgIGhhaSBwaMOtYSkuIiwNCiAgICAiS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIHRyb25nIHThu5VuZyB0aOG7gy4iLA0KICAgICJU4bu3IGzhu4cgxrDhu5tjIGzGsOG7o25nIHThu6sgZOG7ryBsaeG7h3UgbeG6q3UuIg0KICApLA0KICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCikNCg0Ka2FibGUocmVzdWx0X21lYW5pbmdfZmVtYWxlX2RmLA0KICAgICAgY29sLm5hbWVzID0gYygiUGjhuqduIE91dHB1dCIsICJHacOhIHRy4buLIHThu6sgUiIsICLDnSBuZ2jEqWEgY2h1bmciKSwNCiAgICAgIGFsaWduID0gYygibCIsICJyIiwgImwiKSwNCiAgICAgIGNhcHRpb24gPSAiR2nhuqNpIHRow61jaCBr4bq/dCBxdeG6oyBj4bunYSBgcHJvcC50ZXN0KClgIGNobyBnaeG7m2kgdMOtbmggTuG7ryIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImhvdmVyIikpICU+JQ0KICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkNCg0KYGBgDQoNCg0KIyMgKiozLjIgSG9tZW93bmVyICJZIioqDQoNCg0KIEdp4bqjIHPhu60gIkPDsyBuaMOgIiBjw7MgbcOjIGzDoCAiWSIgdHJvbmcgY+G7mXQgJ0hvbWVvd25lcicuIEtp4buDbSB0cmEgbGV2ZWxzKGRsZHQkSG9tZW93bmVyKS4NCg0KYGBge3J9DQojIFPhu5EgbmfGsOG7nWkgY8OzIG5ow6ANCmNhdGVnb3J5X2hvbWVvd25lcl95IDwtICJZIiANCk5faG9tZW93bmVyX3kgPC0gc3VtKGRsZHQkSG9tZW93bmVyID09IGNhdGVnb3J5X2hvbWVvd25lcl95KQ0KIyBU4buVbmcgc+G7kSBxdWFuIHPDoXQNCk5fdG90YWxfaG9tZW93bmVyICA8LSBucm93KGRsZHQpICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBU4buVbmcgc+G7kSBxdWFuIHPDoXQNCg0KY2F0KHBhc3RlKCJT4buRIG5nxrDhu51pIHPhu58gaOG7r3UgbmjDoCAoSG9tZW93bmVyID0iLCBjYXRlZ29yeV9ob21lb3duZXJfeSwgIik6IiwgTl9ob21lb3duZXJfeSwgIlxuIikpDQpjYXQocGFzdGUoIlThu5VuZyBz4buRIHF1YW4gc8OhdDoiLCBOX3RvdGFsX2hvbWVvd25lciwgIlxuXG4iKSkNCmBgYA0KDQogIEdp4bqjIHRodXnhur90IA0KSDA6IFThu7cgbOG7hyBuZ8aw4budaSBz4bufIGjhu691IG5ow6AgbMOgIDYwJSAocF9ob21lb3duZXIgPSAwLjYpDQpIMTogVOG7tyBs4buHIG5nxrDhu51pIHPhu58gaOG7r3UgbmjDoCBraMOhYyA2MCUgKHBfaG9tZW93bmVyICE9IDAuNikNCg0KDQpgYGB7cn0NCnAwX2hvbWVvd25lciA8LSAwLjYNCg0KYGBgDQoNCmBgYHtyfQ0KdGVzdF9ob21lb3duZXJfcmVzdWx0cyA8LSBwcm9wLnRlc3QoeCA9IE5faG9tZW93bmVyX3ksIG4gPSBOX3RvdGFsX2hvbWVvd25lciwgcCA9IHAwX2hvbWVvd25lciwgY29ycmVjdCA9IEZBTFNFKQ0KY2F0KCJL4bq/dCBxdeG6oyBraeG7g20gxJHhu4tuaCBjaG8gdOG7tyBs4buHIHPhu58gaOG7r3UgbmjDoDpcbiIpDQpwcmludCh0ZXN0X2hvbWVvd25lcl9yZXN1bHRzKQ0KYGBgDQoNCmBgYHtyfQ0KcF92YWx1ZV9ob21lb3duZXIgPC0gdGVzdF9ob21lb3duZXJfcmVzdWx0cyRwLnZhbHVlDQpzYW1wbGVfcF9ob21lb3duZXIgPC0gdGVzdF9ob21lb3duZXJfcmVzdWx0cyRlc3RpbWF0ZQ0KY29uZl9pbnRfaG9tZW93bmVyIDwtIHRlc3RfaG9tZW93bmVyX3Jlc3VsdHMkY29uZi5pbnQNCmNhdCgiXG5EaeG7hW4gZ2nhuqNpIGvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIGNobyBT4bufIGjhu691IG5ow6AgJ1knOlxuIikNCmNhdChwYXN0ZTAoIiAgLSBLaG/huqNuZyB0aW4gY+G6rXkgOTUlIGNobyB04bu3IGzhu4cgc+G7nyBo4buvdSBuaMOgIGzDoCAoIiwgDQogICAgICAgICAgIHJvdW5kKGNvbmZfaW50X2hvbWVvd25lclsxXSwgNCksICIsICIsIHJvdW5kKGNvbmZfaW50X2hvbWVvd25lclsyXSwgNCksICIpLlxuIikpDQpjYXQocGFzdGUwKCIgIC0gVOG7tyBs4buHIHPhu58gaOG7r3UgbmjDoCB0cm9uZyBt4bqrdSAocC1oYXQpIGzDoDogIiwgcm91bmQoc2FtcGxlX3BfaG9tZW93bmVyLCA0KSwgIi5cbiIpKQ0KY2F0KHBhc3RlMCgiICAtIEdpw6EgdHLhu4sgcC12YWx1ZSBj4bunYSBraeG7g20gxJHhu4tuaCBsw6A6ICIsIGZvcm1hdC5wdmFsKHBfdmFsdWVfaG9tZW93bmVyLCBkaWdpdHM9NCwgZXBzPTAuMDAwMSksICIuXG4iKSkNCg0KYWxwaGEgPC0gMC4wNQ0KY2F0KHBhc3RlMCgiXG5W4bubaSBt4bupYyDDvSBuZ2jEqWEgYWxwaGEgPSAiLCBhbHBoYSwgIjpcbiIpKQ0KaWYgKHBfdmFsdWVfaG9tZW93bmVyIDwgYWxwaGEpIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgPCAiLCBhbHBoYSwgIiwgY2jDum5nIHRhIELDgUMgQuG7jiBnaeG6oyB0aHV54bq/dCBIMC5cbiIpKQ0KICBjYXQocGFzdGUwKCIgICAgQ8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBy4bqxbmcgdOG7tyBs4buHIG5nxrDhu51pIHPhu58gaOG7r3UgbmjDoCB0aOG7sWMgc+G7sSB0cm9uZyB04buVbmcgdGjhu4MgS0jDgUMgIiwgcDBfaG9tZW93bmVyKjEwMCwgIiUuXG4iKSkNCn0gZWxzZSB7DQogIGNhdChwYXN0ZTAoIiAgLSBL4bq/dCBsdeG6rW46IFbDrCBwLXZhbHVlID49ICIsIGFscGhhLCAiLCBjaMO6bmcgdGEgS0jDlE5HIELDgUMgQuG7jiBnaeG6oyB0aHV54bq/dCBIMC5cbiIpKQ0KICBjYXQocGFzdGUwKCIgICAgS2jDtG5nIGPDsyDEkeG7pyBi4bqxbmcgY2jhu6luZyB0aOG7kW5nIGvDqiDEkeG7gyBr4bq/dCBsdeG6rW4gcuG6sW5nIHThu7cgbOG7hyBuZ8aw4budaSBz4bufIGjhu691IG5ow6Aga2jDoWMgIiwgcDBfaG9tZW93bmVyKjEwMCwgIiUuXG4iKSkNCn0NCg0KIyBC4bqjbmcgZ2nhuqNpIHRow61jaCAgKMSRw6Mgc+G7rWEgxJHhu4MgbOG6pXkgZ2nDoSB0cuG7iyDEkeG7mW5nKQ0KcmVzdWx0X21lYW5pbmdfSF9kZiA8LSBkYXRhLmZyYW1lKA0KICBQaOG6p24gPSBjKCJYLXNxdWFyZWQiLCAiZGYiLCAicC12YWx1ZSIsICJhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIiwgIjk1JSBjb25maWRlbmNlIGludGVydmFsIiwgInNhbXBsZSBlc3RpbWF0ZSBwIiksDQogIEdpw6FfdHLhu4tfdOG7q19SID0gYygNCiAgICAgIHJvdW5kKHRlc3RfaG9tZW93bmVyX3Jlc3VsdHMkc3RhdGlzdGljLCAyKSwgIyBT4butYSBs4bqhaSBz4buRIGxp4buHdSBjaG8ga2jhu5twDQogICAgICB0ZXN0X2hvbWVvd25lcl9yZXN1bHRzJHBhcmFtZXRlciwNCiAgICAgIGZvcm1hdC5wdmFsKHRlc3RfaG9tZW93bmVyX3Jlc3VsdHMkcC52YWx1ZSwgZGlnaXRzPTQsIGVwcz0wLjAwMDEpLCAjIEhp4buDbiB0aOG7iyBwLXZhbHVlIMSRw7puZw0KICAgICAgdGVzdF9ob21lb3duZXJfcmVzdWx0cyRhbHRlcm5hdGl2ZSwNCiAgICAgIHBhc3RlMCgiKCIsIHJvdW5kKHRlc3RfaG9tZW93bmVyX3Jlc3VsdHMkY29uZi5pbnRbMV0sNCksICIsICIsIHJvdW5kKHRlc3RfaG9tZW93bmVyX3Jlc3VsdHMkY29uZi5pbnRbMl0sNCksICIpIiksDQogICAgICByb3VuZCh0ZXN0X2hvbWVvd25lcl9yZXN1bHRzJGVzdGltYXRlLCA3KQ0KICApLA0KICDDnV9uZ2jEqWEgPSBjKA0KICAgICJHacOhIHRy4buLIHRo4buRbmcga8OqIGtp4buDbSDEkeG7i25oIENoaS1zcXVhcmUuIiwNCiAgICAiQuG6rWMgdOG7sSBkby4iLA0KICAgICJYw6FjIHN14bqldCBxdWFuIHPDoXQgZOG7ryBsaeG7h3UgbmjGsCBoaeG7h24gdOG6oWkgaG/hurdjIGPhu7FjIMSRb2FuIGjGoW4ga2hpIEgwIMSRw7puZy4iLA0KICAgICJLaeG7g20gxJHhu4tuaCBoYWkgcGjDrWEuIiwNCiAgICAiS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIGPDsyBuaMOgIHRo4bqtdCB0cm9uZyBkw6JuIHPhu5EuIiwNCiAgICAiVOG7tyBs4buHIGPDsyBuaMOgIHRyb25nIG3huqt1LiINCiAgKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQprYWJsZShyZXN1bHRfbWVhbmluZ19IX2RmLCBjb2wubmFtZXMgPSBjKCJQaOG6p24gT3V0cHV0IiwgIkdpw6EgdHLhu4sgdOG7qyBSIiwgIsOdIG5naMSpYSIpLCBhbGlnbiA9IGMoImwiLCAiciIsICJsIiksDQogICAgICBjYXB0aW9uID0gIkdp4bqjaSB0aMOtY2ggb3V0cHV0IGPhu6dhIHByb3AudGVzdCBjaG8gU+G7nyBo4buvdSBuaMOgICdZJyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImhvdmVyIikpICU+JQ0KICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkNCmBgYA0KDQoNCg0KIyMgKiozLjMgUHJvZHVjdEZhbWlseSAiRm9vZCIqKg0KDQoNCmBgYHtyfQ0KIyBT4buRIHPhuqNuIHBo4bqpbSAoUHJvZHVjdEZhbWlseSA9ICJGb29kIikgdsOgIHThu5VuZyBz4buRIHF1YW4gc8OhdA0KY2F0ZWdvcnlfZm9vZCA8LSAiRm9vZCIgDQpOX2Zvb2QgPC0gc3VtKGRsZHQkUHJvZHVjdEZhbWlseSA9PSBjYXRlZ29yeV9mb29kKSANCiMgVOG7lW5nIHPhu5Egc+G6o24gcGjhuqltDQpOX3RvdGFsX3Byb2R1Y3RmYW1pbHkgIDwtIG5yb3coZGxkdCkgICAgICAgICAgICAgICANCg0KY2F0KHBhc3RlKCJT4buRIHPhuqNuIHBo4bqpbSAnRm9vZCcgKFByb2R1Y3RGYW1pbHkgPSIsIGNhdGVnb3J5X2Zvb2QsICIpOiIsIE5fZm9vZCwgIlxuIikpDQpjYXQocGFzdGUoIlThu5VuZyBz4buRIHPhuqNuIHBo4bqpbSBxdWFuIHPDoXQ6IiwgTl90b3RhbF9wcm9kdWN0ZmFtaWx5LCAiXG5cbiIpKQ0KDQpgYGANCg0KR2nhuqMgdGh1eeG6v3QgSDA6IHBfZm9vZCA+PSAwLjcNCg0KYGBge3J9DQpwMF9mb29kIDwtIDAuNw0KYWx0ZXJuYXRpdmVfaHlwX2Zvb2QgPC0gInR3by5zaWRlZCINCmBgYA0KDQoNCmBgYHtyfQ0KdGVzdF9mb29kX3Jlc3VsdHMgPC0gcHJvcC50ZXN0KHggPSBOX2Zvb2QsIG4gPSBOX3RvdGFsX3Byb2R1Y3RmYW1pbHksIHAgPSBwMF9mb29kLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9IGFsdGVybmF0aXZlX2h5cF9mb29kLCBjb3JyZWN0ID0gRkFMU0UpDQpjYXQoIkvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIGNobyB04bu3IGzhu4cgc+G6o24gcGjhuqltICdGb29kJzpcbiIpDQpwcmludCh0ZXN0X2Zvb2RfcmVzdWx0cykNCg0KYGBgDQoNCg0KYGBge3J9DQpwX3ZhbHVlX2Zvb2QgPC0gdGVzdF9mb29kX3Jlc3VsdHMkcC52YWx1ZQ0Kc2FtcGxlX3BfZm9vZCA8LSB0ZXN0X2Zvb2RfcmVzdWx0cyRlc3RpbWF0ZQ0KY29uZl9pbnRfZm9vZCA8LSB0ZXN0X2Zvb2RfcmVzdWx0cyRjb25mLmludA0KDQpjYXQoIlxuRGnhu4VuIGdp4bqjaSBr4bq/dCBxdeG6oyBraeG7g20gxJHhu4tuaCBjaG8gUHJvZHVjdEZhbWlseSAnRm9vZCc6XG4iKQ0KY2F0KHBhc3RlMCgiICAtIEtob+G6o25nIHRpbiBj4bqteSA5NSUgY2hvIHThu7cgbOG7hyAnRm9vZCcgbMOgICgiLCANCiAgICAgICAgICAgcm91bmQoY29uZl9pbnRfZm9vZFsxXSwgNCksICIsICIsIHJvdW5kKGNvbmZfaW50X2Zvb2RbMl0sIDQpLCAiKS5cbiIpKQ0KY2F0KHBhc3RlMCgiICAtIFThu7cgbOG7hyAnRm9vZCcgdHJvbmcgbeG6q3UgKHAtaGF0KSBsw6A6ICIsIHJvdW5kKHNhbXBsZV9wX2Zvb2QsIDQpLCAiLlxuIikpDQpjYXQocGFzdGUwKCIgIC0gR2nDoSB0cuG7iyBwLXZhbHVlIGPhu6dhIGtp4buDbSDEkeG7i25oIGzDoDogIiwgZm9ybWF0LnB2YWwocF92YWx1ZV9mb29kLCBkaWdpdHM9NCwgZXBzPTAuMDAwMSksICIuXG4iKSkNCg0KYWxwaGEgPC0gMC4wNQ0KY2F0KHBhc3RlMCgiXG5W4bubaSBt4bupYyDDvSBuZ2jEqWEgYWxwaGEgPSAiLCBhbHBoYSwgIjpcbiIpKQ0KaWYgKHBfdmFsdWVfZm9vZCA8IGFscGhhKSB7DQogIGNhdChwYXN0ZTAoIiAgLSBL4bq/dCBsdeG6rW46IFbDrCBwLXZhbHVlIDwgIiwgYWxwaGEsICIsIGNow7puZyB0YSBCw4FDIELhu44gZ2nhuqMgdGh1eeG6v3QgSDAuXG4iKSkNCiAgaWYgKGFsdGVybmF0aXZlX2h5cF9mb29kID09ICJ0d28uc2lkZWQiKSB7DQogICAgY2F0KHBhc3RlMCgiICAgIEPDsyDEkeG7pyBi4bqxbmcgY2jhu6luZyB0aOG7kW5nIGvDqiDEkeG7gyBr4bq/dCBsdeG6rW4gcuG6sW5nIHThu7cgbOG7hyBz4bqjbiBwaOG6qW0gJ0Zvb2QnIHRo4buxYyBz4buxIHRyb25nIHThu5VuZyB0aOG7gyBLSMOBQyAiLCBwMF9mb29kKjEwMCwgIiUuXG4iKSkNCiAgfSBlbHNlIGlmIChhbHRlcm5hdGl2ZV9oeXBfZm9vZCA9PSAibGVzcyIpIHsNCiAgICBjYXQocGFzdGUwKCIgICAgQ8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBy4bqxbmcgdOG7tyBs4buHIHPhuqNuIHBo4bqpbSAnRm9vZCcgdGjhu7FjIHPhu7EgdHJvbmcgdOG7lW5nIHRo4buDIE5I4buOIEjGoE4gIiwgcDBfZm9vZCoxMDAsICIlLlxuIikpDQogIH0gZWxzZSB7ICMgZ3JlYXRlcg0KICAgICBjYXQocGFzdGUwKCIgICAgQ8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBy4bqxbmcgdOG7tyBs4buHIHPhuqNuIHBo4bqpbSAnRm9vZCcgdGjhu7FjIHPhu7EgdHJvbmcgdOG7lW5nIHRo4buDIEzhu5pOIEjGoE4gIiwgcDBfZm9vZCoxMDAsICIlLlxuIikpDQogIH0NCn0gZWxzZSB7DQogIGNhdChwYXN0ZTAoIiAgLSBL4bq/dCBsdeG6rW46IFbDrCBwLXZhbHVlID49ICIsIGFscGhhLCAiLCBjaMO6bmcgdGEgS0jDlE5HIELDgUMgQuG7jiBnaeG6oyB0aHV54bq/dCBIMC5cbiIpKQ0KICAgaWYgKGFsdGVybmF0aXZlX2h5cF9mb29kID09ICJ0d28uc2lkZWQiKSB7DQogICAgY2F0KHBhc3RlMCgiICAgIEtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogxJHhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyB04bu3IGzhu4cgc+G6o24gcGjhuqltICdGb29kJyBraMOhYyAiLCBwMF9mb29kKjEwMCwgIiUuXG4iKSkNCiAgfSBlbHNlIGlmIChhbHRlcm5hdGl2ZV9oeXBfZm9vZCA9PSAibGVzcyIpIHsNCiAgICBjYXQocGFzdGUwKCIgICAgS2jDtG5nIGPDsyDEkeG7pyBi4bqxbmcgY2jhu6luZyB0aOG7kW5nIGvDqiDEkeG7gyBr4bq/dCBsdeG6rW4gcuG6sW5nIHThu7cgbOG7hyBz4bqjbiBwaOG6qW0gJ0Zvb2QnIG5o4buPIGjGoW4gIiwgcDBfZm9vZCoxMDAsICIlICh04bupYyBsw6Aga2jDtG5nIHRo4buDIGLDoWMgYuG7jyBIMDogcCA+PSAiLCBwMF9mb29kKjEwMCwiJSkuXG4iKSkNCiAgfSBlbHNlIHsgIyBncmVhdGVyDQogICAgIGNhdChwYXN0ZTAoIiAgICBLaMO0bmcgY8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBy4bqxbmcgdOG7tyBs4buHIHPhuqNuIHBo4bqpbSAnRm9vZCcgbOG7m24gaMahbiAiLCBwMF9mb29kKjEwMCwgIiUgKHThu6ljIGzDoCBraMO0bmcgdGjhu4MgYsOhYyBi4buPIEgwOiBwIDw9ICIsIHAwX2Zvb2QqMTAwLCIlKS5cbiIpKQ0KICB9DQp9DQoNCiMgQuG6o25nIGdp4bqjaSB0aMOtY2ggDQpyZXN1bHRfbWVhbmluZ19QX2RmIDwtIGRhdGEuZnJhbWUoDQogIFBo4bqnbiA9IGMoIlgtc3F1YXJlZCIsICJkZiIsICJwLXZhbHVlIiwgImFsdGVybmF0aXZlIGh5cG90aGVzaXMiLCAiOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwiLCAic2FtcGxlIGVzdGltYXRlIHAiKSwNCiAgR2nDoV90cuG7i1904burX1IgPSBjKA0KICAgICAgcm91bmQodGVzdF9mb29kX3Jlc3VsdHMkc3RhdGlzdGljLCAxKSwgDQogICAgICB0ZXN0X2Zvb2RfcmVzdWx0cyRwYXJhbWV0ZXIsDQogICAgICBmb3JtYXQucHZhbCh0ZXN0X2Zvb2RfcmVzdWx0cyRwLnZhbHVlLCBkaWdpdHM9NCwgZXBzPTAuMDAwMSksDQogICAgICB0ZXN0X2Zvb2RfcmVzdWx0cyRhbHRlcm5hdGl2ZSwNCiAgICAgIHBhc3RlMCgiKCIsIHJvdW5kKHRlc3RfZm9vZF9yZXN1bHRzJGNvbmYuaW50WzFdLDQpLCAiLCAiLCByb3VuZCh0ZXN0X2Zvb2RfcmVzdWx0cyRjb25mLmludFsyXSw0KSwgIikiKSwNCiAgICAgIHJvdW5kKHRlc3RfZm9vZF9yZXN1bHRzJGVzdGltYXRlLCA3KQ0KICApLA0KICDDnV9uZ2jEqWEgPSBjKA0KICAgICJHacOhIHRy4buLIHRo4buRbmcga8OqIENoaS1zcXVhcmUuIiwNCiAgICAiQuG6rWMgdOG7sSBkby4iLA0KICAgICJHacOhIHRy4buLIHAuIiwNCiAgICAiS2nhu4NtIMSR4buLbmggKGhhaSBwaMOtYSwgbmjhu48gaMahbiwgaG/hurdjIGzhu5tuIGjGoW4pLiIsDQogICAgIktob+G6o25nIHRpbiBj4bqteSA5NSUgY2hvIHThu7cgbOG7hyBz4bqjbiBwaOG6qW0gJ0Zvb2QnIHRo4buxYyB04bq/LiIsDQogICAgIlThu7cgbOG7hyBz4bqjbiBwaOG6qW0gJ0Zvb2QnIMaw4bubYyBsxrDhu6NuZyB04burIG3huqt1LiINCiAgKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQprYWJsZShyZXN1bHRfbWVhbmluZ19QX2RmLCBjb2wubmFtZXMgPSBjKCJQaOG6p24gT3V0cHV0IiwgIkdpw6EgdHLhu4sgdOG7qyBSIiwgIsOdIG5naMSpYSIpLCBhbGlnbiA9IGMoImwiLCAiciIsICJsIiksDQogICAgICBjYXB0aW9uID0gIkdp4bqjaSB0aMOtY2ggb3V0cHV0IGPhu6dhIHByb3AudGVzdCBjaG8gUHJvZHVjdEZhbWlseSAnRm9vZCciKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJob3ZlciIpKSAlPiUNCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpDQpgYGANCg0KDQoNCg0KIyAqKjQuIFBow6JuIHTDrWNoIE3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgSGFpIGJp4bq/biDEkOG7i25oIHTDrW5oIChCaXZhcmlhdGUgQW5hbHlzaXMpKioNCg0KDQojIyAqKjQuMSBNYXJpdGFsIHbDoCBQcm9kdWN0RmFtaWx5KioNCg0KDQojIyMgKipC4bqjbmcgdOG6p24gc3XhuqV0KioNCg0KYGBge3J9DQpncCA8LSB0YWJsZShHZW5kZXIgPSBkbGR0JEdlbmRlciwgUHJvZHVjdEZhbWlseSA9IGRsZHQkUHJvZHVjdEZhbWlseSkNCmNhdCgiQuG6o25nIHThuqduIHN14bqldCBjaMOpbyBnaeG7r2EgR2VuZGVyIHbDoCBQcm9kdWN0RmFtaWx5IChT4buRIGzGsOG7o25nKTpcbiIpDQpwcmludChncCkNCg0KIyBC4bqjbmcgdOG7tyBs4buHIHBo4bqnbiB0csSDbSB0aGVvIGjDoG5nICh0cm9uZyBt4buXaSBHZW5kZXIsIHThu7cgbOG7hyBtdWEgY8OhYyBQcm9kdWN0RmFtaWx5KQ0KIyBtYXJnaW4gPSAxOiB0w61uaCB04bu3IGzhu4cgdGhlbyBow6BuZy4gR2nDunAgc28gc8OhbmggeHUgaMaw4bubbmcgY2jhu41uIFByb2R1Y3RGYW1pbHkgZ2nhu69hIGPDoWMgbmjDs20gR2VuZGVyLg0KZ3AxIDwtIHJvdW5kKHByb3AudGFibGUoZ3AsIG1hcmdpbiA9IDEpICogMTAwLCAxKQ0KY2F0KCJcbkLhuqNuZyB04bu3IGzhu4cgJSB0aGVvIGjDoG5nICh0cm9uZyBt4buXaSBHZW5kZXIsIHBow6JuIHBo4buRaSBQcm9kdWN0RmFtaWx5KTpcbiIpDQpwcmludChncDEpDQpgYGANCg0KIyMjICoqVHLhu7FjIHF1YW4gaMOzYSoqDQoNCmBgYHtyfQ0KIyBDaHV54buDbiBi4bqjbmcgc2FuZyBkYXRhZnJhbWUgxJHhu4MgZ2dwbG90DQpncGY8LSBhcy5kYXRhLmZyYW1lKGdwKQ0KY29sbmFtZXMoZ3BmKSA8LSBjKCJHZW5kZXIiLCAiUHJvZHVjdEZhbWlseSIsICJGcmVxIikgDQoNCmdncGxvdChncGYsIGFlcyh4ID0gR2VuZGVyLCB5ID0gRnJlcSwgZmlsbCA9IFByb2R1Y3RGYW1pbHkpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgpLCAgd2lkdGggPSAwLjcpICsNCiAgIyBzY2FsZV9maWxsX21hbnVhbCBj4bunYSBi4bqhbiBkw7luZyDEkcaw4bujYyB2w6wgUHJvZHVjdEZhbWlseSDDrXQgbeG7qWMNCiAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgbmFtZSAgID0gIk5ow7NtIHPhuqNuIHBo4bqpbSIsDQogICAgdmFsdWVzID0gYygiRHJpbmsiID0gInNreWJsdWUiLCAiRm9vZCIgPSAibGlnaHRncmVlbiIsICJOb24tQ29uc3VtYWJsZSIgPSAic2FsbW9uIiksDQogICAgbGFiZWxzID0gYygixJDhu5MgdeG7kW5nIiwgIlRo4buxYyBwaOG6qW0iLCAiUGhpIHRpw6p1IGTDuW5nIikgIyBOaMOjbiBjaG8gY2jDuiB0aMOtY2gNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiU+G7kSBnaWFvIGThu4tjaCB0aGVvIE5ow7NtIHPhuqNuIHBo4bqpbSAmIEdp4bubaSB0w61uaCIsDQogICAgeCAgICAgPSAiR2nhu5tpIHTDrW5oIiwNCiAgICB5ICAgICA9ICJT4buRIGzGsOG7o25nIGdpYW8gZOG7i2NoIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQoNCiMjIyAqKiBLSeG7g20gxJHhu4tuaCBjaGkgYsOsbmggcGjGsMahbmcqKg0KDQoNCmBgYHtyfQ0KY2F0KCJcblxuKipLaeG7g20gxJHhu4tuaCBDaGktYsOsbmggcGjGsMahbmcgY2hvIEdlbmRlciB2w6AgUHJvZHVjdEZhbWlseToqKlxuIikNCmNhdCgiSDA6IEdp4bubaSB0w61uaCAoR2VuZGVyKSB2w6AgTmjDs20gc+G6o24gcGjhuqltIChQcm9kdWN0RmFtaWx5KSBsw6AgxJHhu5ljIGzhuq1wIChraMO0bmcgY8OzIG3hu5FpIHF1YW4gaOG7hykuXG4iKQ0KY2F0KCJIMTogR2nhu5tpIHTDrW5oIChHZW5kZXIpIHbDoCBOaMOzbSBz4bqjbiBwaOG6qW0gKFByb2R1Y3RGYW1pbHkpIGPDsyBsacOqbiBxdWFuLlxuXG4iKQ0KDQpjaGlzcV9ncCA8LSBjaGlzcS50ZXN0KGdwKQ0KcHJpbnQoY2hpc3FfZ3ApDQoNCmFscGhhX2NoaSA8LSAwLjA1DQpjYXQocGFzdGUwKCJcblbhu5tpIG3hu6ljIMO9IG5naMSpYSBhbHBoYSA9ICIsIGFscGhhX2NoaSwgIjpcbiIpKQ0KaWYgKGNoaXNxX2dwJHAudmFsdWUgPCBhbHBoYV9jaGkpIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgKCIsIGZvcm1hdC5wdmFsKGNoaXNxX2dwJHAudmFsdWUsIGRpZ2l0cz00LCBlcHM9MC4wMDAxKSwgIikgPCAiLCBhbHBoYV9jaGksIA0KICAgICAgICAgICAgICIsIGNow7puZyB0YSBCw4FDIELhu44gZ2nhuqMgdGh1eeG6v3QgSDAuXG4iKSkNCiAgY2F0KCIgICAgQ8OzIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIHbhu4EgbeG7kWkgcXVhbiBo4buHIMO9IG5naMSpYSBnaeG7r2EgR2nhu5tpIHTDrW5oIHbDoCBOaMOzbSBz4bqjbiBwaOG6qW0uXG4iKQ0KICBjYXQoIiAgICDEkOG7gyBoaeG7g3UgcsO1IGjGoW4gduG7gSBi4bqjbiBjaOG6pXQgbeG7kWkgcXVhbiBo4buHLCBow6N5IHhlbSB4w6l0IGLhuqNuZyB04bu3IGzhu4cgcGjhuqduIHRyxINtIHbDoCBiaeG7g3UgxJHhu5MuXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgKCIsIGZvcm1hdC5wdmFsKGNoaXNxX2dwJHAudmFsdWUsIGRpZ2l0cz00LCBlcHM9MC4wMDAxKSwgIikgPj0gIiwgYWxwaGFfY2hpLCANCiAgICAgICAgICAgICAiLCBjaMO6bmcgdGEgS0jDlE5HIELDgUMgQuG7jiBnaeG6oyB0aHV54bq/dCBIMC5cbiIpKQ0KICBjYXQoIiAgICBLaMO0bmcgY8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBjw7MgbeG7kWkgcXVhbiBo4buHIMO9IG5naMSpYSBnaeG7r2EgR2nhu5tpIHTDrW5oIHbDoCBOaMOzbSBz4bqjbiBwaOG6qW0uXG4iKQ0KfQ0KDQojIEtp4buDbSB0cmEgxJFp4buBdSBraeG7h24gdOG6p24gc+G7kSBr4buzIHbhu41uZw0KaWYoYW55KGNoaXNxX2dwJGV4cGVjdGVkIDwgNSkpew0KICAgIGNhdCgiXG5D4bqiTkggQsOBTzogQ8OzIG3hu5l0IHPhu5Egw7QgdHJvbmcgYuG6o25nIGPDsyB04bqnbiBzdeG6pXQga+G7syB24buNbmcgbmjhu48gaMahbiA1LiBL4bq/dCBxdeG6oyBj4bunYSBraeG7g20gxJHhu4tuaCBDaGktYsOsbmggcGjGsMahbmcgY8OzIHRo4buDIGtow7RuZyBob8OgbiB0b8OgbiBjaMOtbmggeMOhYy5cbiIpDQogICAgIyBwcmludCgiVOG6p24gc3XhuqV0IGvhu7MgduG7jW5nOiIpDQogICAgIyBwcmludChyb3VuZChjaGlzcV90ZXN0X2dwJGV4cGVjdGVkLCAyKSkNCn0NCg0KDQpgYGANCg0KIyMgKio0LjIgTWFyaXRhbFN0YXR1cyB2w6AgSG9tZW93bmVyKioNCg0KDQojIyMgKipC4bqjbmcgdOG6p24gc3XhuqV0KioNCg0KDQpgYGB7cn0NCm1oIDwtIHRhYmxlKE1hcml0YWxTdGF0dXMgPSBkbGR0JE1hcml0YWxTdGF0dXMsIEhvbWVvd25lciA9IGRsZHQkSG9tZW93bmVyKQ0KY2F0KCJC4bqjbmcgdOG6p24gc3XhuqV0IGNow6lvIGdp4buvYSBUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4gdsOgIFPhu58gaOG7r3UgbmjDoCAoU+G7kSBsxrDhu6NuZyk6XG4iKQ0KcHJpbnQobWgpDQoNCm1oMSA8LSByb3VuZChwcm9wLnRhYmxlKG1oLCBtYXJnaW4gPSAxKSAqIDEwMCwgMSkNCmNhdCgiXG5C4bqjbmcgdOG7tyBs4buHICUgdGhlbyBow6BuZyAodHJvbmcgbeG7l2kgVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuLCBwaMOibiBwaOG7kWkgU+G7nyBo4buvdSBuaMOgKTpcbiIpDQpwcmludChtaDEpDQpgYGANCg0KYGBge3J9DQptaGRmIDwtIGFzLmRhdGEuZnJhbWUobWgpDQpjb2xuYW1lcyhtaGRmKSA8LSBjKCJNYXJpdGFsIiwgIk93bmVyIiwgIkZyZXEiKQ0KDQpnZ3Bsb3QobWhkZiwgYWVzKHggPSBNYXJpdGFsLCB5ID0gRnJlcSwgZmlsbCA9IE93bmVyKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSwgIHdpZHRoID0gMC43KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIG5hbWUgICA9ICJT4bufIGjhu691IG5ow6AiLA0KICAgIHZhbHVlcyA9IGMoIlkiID0gInN0ZWVsYmx1ZSIsICJOIiA9ICJwaW5rIiksICMgxJDhuqNtIGLhuqNvICJZIiwgIk4iIGzDoCBjw6FjIG3hu6ljIGPhu6dhIEhvbWVvd25lcg0KICAgIGxhYmVscyA9IGMoIkPDsyBuaMOgIiwgIktow7RuZyBjw7MgbmjDoCIpDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlPhu5Ega2jDoWNoIGPDsy9raMO0bmcgc+G7nyBo4buvdSBuaMOgIHRoZW8gVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIiwNCiAgICB4ICAgICA9ICJUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLA0KICAgIHkgICAgID0gIlPhu5Ega2jDoWNoIGjDoG5nIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQoNCiMjIyAqKktp4buDbSDEkeG7i25oIGNoaSBiw6xuaCBwaMawxqFuZyoqDQoNCg0KYGBge3J9DQpjYXQoIlxuXG4qKktp4buDbSDEkeG7i25oIENoaS1iw6xuaCBwaMawxqFuZyBjaG8gTWFyaXRhbFN0YXR1cyB2w6AgSG9tZW93bmVyOioqXG4iKQ0KY2F0KCJIMDogVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIChNYXJpdGFsU3RhdHVzKSB2w6AgU+G7nyBo4buvdSBuaMOgIChIb21lb3duZXIpIGzDoCDEkeG7mWMgbOG6rXAuXG4iKQ0KY2F0KCJIMTogVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIChNYXJpdGFsU3RhdHVzKSB2w6AgU+G7nyBo4buvdSBuaMOgIChIb21lb3duZXIpIGPDsyBsacOqbiBxdWFuLlxuXG4iKQ0KDQpjaGlzcV9taCA8LSBjaGlzcS50ZXN0KG1oKQ0KcHJpbnQoY2hpc3FfbWgpDQoNCmFscGhhX2NoaSA8LSAwLjA1DQpjYXQocGFzdGUwKCJcblbhu5tpIG3hu6ljIMO9IG5naMSpYSBhbHBoYSA9ICIsIGFscGhhX2NoaSwgIjpcbiIpKQ0KaWYgKGNoaXNxX21oJHAudmFsdWUgPCBhbHBoYV9jaGkpIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgKCIsIGZvcm1hdC5wdmFsKGNoaXNxX21oJHAudmFsdWUsIGRpZ2l0cz00LCBlcHM9MC4wMDAxKSwgIikgPCAiLCBhbHBoYV9jaGksIA0KICAgICAgICAgICAgICIsIGNow7puZyB0YSBCw4FDIELhu44gZ2nhuqMgdGh1eeG6v3QgSDAuXG4iKSkNCiAgY2F0KCIgICAgQ8OzIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIHbhu4EgbeG7kWkgcXVhbiBo4buHIMO9IG5naMSpYSBnaeG7r2EgVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIHbDoCBT4bufIGjhu691IG5ow6AuXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgKCIsIGZvcm1hdC5wdmFsKGNoaXNxX21oJHAudmFsdWUsIGRpZ2l0cz00LCBlcHM9MC4wMDAxKSwgIikgPj0gIiwgYWxwaGFfY2hpLCANCiAgICAgICAgICAgICAiLCBjaMO6bmcgdGEgS0jDlE5HIELDgUMgQuG7jiBnaeG6oyB0aHV54bq/dCBIMC5cbiIpKQ0KICBjYXQoIiAgICBLaMO0bmcgY8OzIMSR4bunIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIMSR4buDIGvhur90IGx14bqtbiBjw7MgbeG7kWkgcXVhbiBo4buHIMO9IG5naMSpYSBnaeG7r2EgVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIHbDoCBT4bufIGjhu691IG5ow6AuXG4iKQ0KfQ0KDQppZihhbnkoY2hpc3FfbWgkZXhwZWN0ZWQgPCA1KSl7DQogICAgY2F0KCJcbkPhuqJOSCBCw4FPOiBDw7MgbeG7mXQgc+G7kSDDtCBjw7MgdOG6p24gc3XhuqV0IGvhu7MgduG7jW5nIDwgNS4gS+G6v3QgcXXhuqMgQ2hpLWLDrG5oIHBoxrDGoW5nIGPDsyB0aOG7gyBraMO0bmcgY2jDrW5oIHjDoWMuXG4iKQ0KfQ0KYGBgDQoNCg0KIyMgKio0LjMgQW5udWFsSW5jb21lIHbDoCBQcm9kdWN0Q2F0ZWdvcnkqKg0KDQoNCiMjIyAqKkLhuqNuZyB04bqnbiBzdeG6pXQqKg0KDQpgYGB7cn0NCmFwIDwtIHRhYmxlKEFubnVhbEluY29tZSA9IGRsZHQkQW5udWFsSW5jb21lLCBQcm9kdWN0Q2F0ZWdvcnkgPSBkbGR0JFByb2R1Y3RDYXRlZ29yeSkNCmNhdCgiQuG6o25nIHThuqduIHN14bqldCBjaMOpbyBnaeG7r2EgVGh1IG5o4bqtcCBow6BuZyBuxINtIHbDoCBEYW5oIG3hu6VjIHPhuqNuIHBo4bqpbSAoU+G7kSBsxrDhu6NuZykgLSBIaeG7g24gdGjhu4sgbeG7mXQgcGjhuqduIGRvIGPDsyB0aOG7gyBy4bqldCBs4bubbjpcbiIpDQojIERvIGLhuqNuZyBjw7MgdGjhu4MgcuG6pXQgbOG7m24sIGNo4buJIGluIG3hu5l0IHBo4bqnbiBob+G6t2MgdMOzbSB04bqvdA0KaWYgKG5jb2woYXApID4gMTApIHsNCiAgICBwcmludChhcFssIDE6MTBdKSAjIEluIDEwIGPhu5l0IMSR4bqndSB0acOqbg0KICAgIGNhdCgiLi4uIHbDoCBjw6FjIGPhu5l0IGtow6FjIC4uLlxuIikNCn0gZWxzZSB7DQogICAgcHJpbnQoYXApDQp9DQoNCmFwMSA8LSByb3VuZChwcm9wLnRhYmxlKGFwLCBtYXJnaW4gPSAxKSAqIDEwMCwgMSkNCmNhdCgiXG5C4bqjbmcgdOG7tyBs4buHICUgdGhlbyBow6BuZyAodHJvbmcgbeG7l2kgbeG7qWMgVGh1IG5o4bqtcCwgcGjDom4gcGjhu5FpIERhbmggbeG7pWMgc+G6o24gcGjhuqltKSAtIEhp4buDbiB0aOG7iyBt4buZdCBwaOG6p246XG4iKQ0KaWYgKG5jb2woYXAxKSA+IDEwKSB7DQogICAgcHJpbnQoYXAxWywgMToxMF0pDQogICAgY2F0KCIuLi4gdsOgIGPDoWMgY+G7mXQga2jDoWMgLi4uXG4iKQ0KfSBlbHNlIHsNCiAgICBwcmludChhcDEpDQp9DQoNCmBgYA0KDQojIyMgKipUcuG7sWMgcXVhbiBk4buvIGxp4buHdSoqDQoNCg0KYGBge3J9DQphcGRmPC0gYXMuZGF0YS5mcmFtZShhcCkNCmNvbG5hbWVzKGFwZGYpIDwtIGMoIkFubnVhbEluY29tZSIsICJQcm9kdWN0Q2F0ZWdvcnkiLCAiRnJlcSIpDQoNCg0KZ2dwbG90KGFwZGYsIGFlcyh4ID0gQW5udWFsSW5jb21lLCB5ID0gRnJlcSwgZmlsbCA9IFByb2R1Y3RDYXRlZ29yeSkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoID0gMC44KSArICMgcG9zaXRpb25fZG9kZ2UgxJHhu4MgY8OhYyBj4buZdCBu4bqxbSBj4bqhbmggbmhhdQ0KICBsYWJzKA0KICAgIHRpdGxlID0gIlPhuqNuIGzGsOG7o25nIHRoZW8gVGh1IG5o4bqtcCB2w6AgRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0iLA0KICAgIHN1YnRpdGxlID0gIkzGsHUgw706IEJp4buDdSDEkeG7kyBjw7MgdGjhu4MgcGjhu6ljIHThuqFwIGRvIG5oaeG7gXUgRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0iLA0KICAgIHggICAgID0gIlRodSBuaOG6rXAgaMOgbmcgbsSDbSIsDQogICAgeSAgICAgPSAiU+G7kSBsxrDhu6NuZyBnaWFvIGThu4tjaCINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksICMgWG9heSBuaMOjbiB0cuG7pWMgeA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBpZihubGV2ZWxzKGFwZGYkUHJvZHVjdENhdGVnb3J5KSA+IDEwKSAibm9uZSIgZWxzZSAicmlnaHQiKSAjIOG6qG4gbGVnZW5kIG7hur91IHF1w6Egbmhp4buBdQ0KDQpgYGANCg0KDQojIyMgKipLaeG7g20gxJHhu4tuaCBjaGkgYsOsbmggcGjGsMahbmcqKg0KDQoNCmBgYHtyfQ0KY2F0KCJcblxuKipLaeG7g20gxJHhu4tuaCBDaGktYsOsbmggcGjGsMahbmcgY2hvIEFubnVhbEluY29tZSB2w6AgUHJvZHVjdENhdGVnb3J5OioqXG4iKQ0KY2F0KCJIMDogVGh1IG5o4bqtcCBow6BuZyBuxINtIChBbm51YWxJbmNvbWUpIHbDoCBEYW5oIG3hu6VjIHPhuqNuIHBo4bqpbSAoUHJvZHVjdENhdGVnb3J5KSBsw6AgxJHhu5ljIGzhuq1wLlxuIikNCmNhdCgiSDE6IFRodSBuaOG6rXAgaMOgbmcgbsSDbSAoQW5udWFsSW5jb21lKSB2w6AgRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0gKFByb2R1Y3RDYXRlZ29yeSkgY8OzIGxpw6puIHF1YW4uXG5cbiIpDQoNCiMgRG8gc+G7kSBsxrDhu6NuZyBsZXZlbHMgY+G7p2EgUHJvZHVjdENhdGVnb3J5IGzhu5tuLCB04bqnbiBz4buRIGvhu7MgduG7jW5nIGPDsyB0aOG7gyBy4bqldCB0aOG6pXAgbmhp4buBdSDDtC4gRG8gxJHDsyBjw7MgdGjhu4MgZ8OtIHRy4buLIGNoaSBiw6xuaCBwaMawxqFuZyBz4bq9IGtow7RuZyBxdcOhIMSRw7puZw0KDQpjaGlzcV9hcCA8LSBjaGlzcS50ZXN0KGFwKQ0KcHJpbnQoY2hpc3FfYXApDQoNCmFscGhhX2NoaSA8LSAwLjA1DQpjYXQocGFzdGUwKCJcblbhu5tpIG3hu6ljIMO9IG5naMSpYSBhbHBoYSA9ICIsIGFscGhhX2NoaSwgIjpcbiIpKQ0KaWYgKGNoaXNxX2FwJHAudmFsdWUgPCBhbHBoYV9jaGkpIHsNCiAgY2F0KHBhc3RlMCgiICAtIEvhur90IGx14bqtbjogVsOsIHAtdmFsdWUgKCIsIGZvcm1hdC5wdmFsKGNoaXNxX2FwJHAudmFsdWUsIGRpZ2l0cz00LCBlcHM9MC4wMDAxKSwgIikgPCAiLCBhbHBoYV9jaGksIA0KICAgICAgICAgICAgICIsIGNow7puZyB0YSBCw4FDIELhu44gZ2nhuqMgdGh1eeG6v3QgSDAuXG4iKSkNCiAgY2F0KCIgICAgQ8OzIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIHbhu4EgbeG7kWkgcXVhbiBo4buHIMO9IG5naMSpYSBnaeG7r2EgVGh1IG5o4bqtcCBow6BuZyBuxINtIHbDoCBEYW5oIG3hu6VjIHPhuqNuIHBo4bqpbS5cbiIpDQp9IGVsc2Ugew0KICBjYXQocGFzdGUwKCIgIC0gS+G6v3QgbHXhuq1uOiBWw6wgcC12YWx1ZSAoIiwgZm9ybWF0LnB2YWwoY2hpc3FfYXAkcC52YWx1ZSwgZGlnaXRzPTQsIGVwcz0wLjAwMDEpLCAiKSA+PSAiLCBhbHBoYV9jaGksIA0KICAgICAgICAgICAgICIsIGNow7puZyB0YSBLSMOUTkcgQsOBQyBC4buOIGdp4bqjIHRodXnhur90IEgwLlxuIikpDQogIGNhdCgiICAgIEtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogxJHhu4Mga+G6v3QgbHXhuq1uIGPDsyBt4buRaSBxdWFuIGjhu4cgw70gbmdoxKlhIGdp4buvYSBUaHUgbmjhuq1wIGjDoG5nIG7Eg20gdsOgIERhbmggbeG7pWMgc+G6o24gcGjhuqltLlxuIikNCn0NCg0KaWYoYW55KGNoaXNxX2FwJGV4cGVjdGVkIDwgMSkpeyAjIMSQaeG7gXUga2nhu4duIGNo4bq3dCBoxqFuOiB04bqnbiBz4buRIGvhu7MgduG7jW5nIDwgMQ0KICAgIGNhdCgiXG5D4bqiTkggQsOBTyBOR0hJw4pNIFRS4buMTkc6IEPDsyBuaGnhu4F1IMO0IGPDsyB04bqnbiBzdeG6pXQga+G7syB24buNbmcgUuG6pFQgVEjhuqRQIChjw7MgdGjhu4MgPCAxKS5cbiIpDQogICAgY2F0KCJL4bq/dCBxdeG6oyBj4bunYSBraeG7g20gxJHhu4tuaCBDaGktYsOsbmggcGjGsMahbmcgY2hvIGPhurdwIGJp4bq/biBuw6B5IFLhuqRUIEtIw5RORyDEkMOBTkcgVElOIEPhuqxZLlxuIikNCiAgICBjYXQoIk7Dqm4geGVtIHjDqXQgbmjDs20gY8OhYyBt4bupYyBj4bunYSAnUHJvZHVjdENhdGVnb3J5JyBs4bqhaSB0csaw4bubYyBraGkga2nhu4NtIMSR4buLbmguXG4iKQ0KfSBlbHNlIGlmKGFueShjaGlzcV9hcCRleHBlY3RlZCA8IDUpKXsNCiAgICBjYXQoIlxuQ+G6ok5IIELDgU86IEPDsyBt4buZdCBz4buRIMO0IGPDsyB04bqnbiBzdeG6pXQga+G7syB24buNbmcgPCA1LiBL4bq/dCBxdeG6oyBDaGktYsOsbmggcGjGsMahbmcgY8OzIHRo4buDIGtow7RuZyBjaMOtbmggeMOhYy5cbiIpDQp9DQpgYGANCg0KDQojICoqQi5Ib+G6oXQgxJHhu5luZyB0csOqbiBs4bubcCoqDQoNCg0KIyMgKioxLiBMw70gdGh1eeG6v3QqKg0KDQojIyMgKioxLjEgR2nhu5tpIHRoaeG7h3UqKg0KDQpUcm9uZyBwaMOibiB0w61jaCB0aOG7kW5nIGvDqiDEkeG7i25oIGzGsOG7o25nLCBi4bqjbmcgbmfhuqt1IG5oacOqbiAoY29udGluZ2VuY3kgdGFibGUpIGzDoCBt4buZdCBjw7RuZyBj4bulIGPGoSBi4bqjbiDEkeG7gyBraOG6o28gc8OhdCBt4buRaSBsacOqbiBo4buHIGdp4buvYSBoYWkgYmnhur9uIHBow6JuIGxv4bqhaS4gVHJvbmcgdHLGsOG7nW5nIGjhu6NwIMSRxqFuIGdp4bqjbiBuaOG6pXQsIGLhuqNuZyBuZ+G6q3Ugbmhpw6puIDJ4MiBiaeG7g3UgZGnhu4VuIHPhu7EgcGjDom4gYuG7kSBj4bunYSBoYWkgYmnhur9uIG5o4buLIHBow6JuLCBnacO6cCB4w6FjIMSR4buLbmggdsOgIMSRbyBsxrDhu51uZyBt4buRaSBsacOqbiBo4buHIGdp4buvYSBjaMO6bmcuIEPDoWMgY2jhu4kgc+G7kSBuaMawIGhp4buHdSB04bu3IGzhu4cgKHJpc2sgZGlmZmVyZW5jZSksIHThu7cgc+G7kSBuZ3V5IGPGoSAoUmVsYXRpdmUgUmlzayAtIFJSKSB2w6AgdOG7tyBz4buRIGNow6puaCAoT2RkcyBSYXRpbyAtIE9SKSDEkcOzbmcgdmFpIHRyw7IgcXVhbiB0cuG7jW5nIHRyb25nIHZp4buHYyBsxrDhu6NuZyBow7NhIG3hu5FpIHF1YW4gaOG7hyDEkcOzLiBCw6BpIHZp4bq/dCBuw6B5IHPhur0gdHLDrG5oIGLDoHkgY2hpIHRp4bq/dCBj4bqldSB0csO6YyB4w6FjIHN14bqldCBzaW5oIHJhIGLhuqNuZyBuZ+G6q3Ugbmhpw6puLCBwaMawxqFuZyBwaMOhcCBzbyBzw6FuaCBoYWkgdOG7tyBs4buHLCBjw6FjaCB4w6J5IGThu7FuZyBraG/huqNuZyB0aW4gY+G6rXkgY2hvIE9kZHMgUmF0aW8sIHbDoCBr4bq/dCB0aMO6YyBi4bqxbmcgbeG7mXQgdsOtIGThu6UgdGjhu7FjIHRp4buFbiB0cm9uZyBsxKluaCB24buxYyBraW5oIGRvYW5oLg0KDQojIyMgKioxLjIuIEPhuqV1IHRyw7pjIHbDoCB4w6FjIHN14bqldCBj4bunYSBi4bqjbmcgbmfhuqt1IG5oacOqbioqDQoNCk3hu5l0IGLhuqNuZyBuZ+G6q3Ugbmhpw6puIDJ4MiBsw6AgYuG6o25nIHThuqduIHN14bqldCDEkeG6v20gc+G7kSBxdWFuIHPDoXQgdGh14buZYyB2w6BvIHThu6tuZyB04buVIGjhu6NwIGPhu6dhIGhhaSBiaeG6v24gbmjhu4sgcGjDom46DQoNClxbDQpcYmVnaW57YXJyYXl9e3xjfGN8Y3xjfH0NClxobGluZQ0KICYgXHRleHR7S+G6v3QgcXXhuqMgKCspfSAmIFx0ZXh0e0vhur90IHF14bqjICjigJMpfSAmIFx0ZXh0e1Thu5VuZ30gXFwNClxobGluZQ0KXHRleHR7UGjGoWkgbmhp4buFbSAoWWVzKX0gJiBhICYgYiAmIGEgKyBiIFxcDQpcaGxpbmUNClx0ZXh0e0tow7RuZyBwaMahaSBuaGnhu4VtIChObyl9ICYgYyAmIGQgJiBjICsgZCBcXA0KXGhsaW5lDQpcdGV4dHtU4buVbmcgY+G7mW5nfSAmIGEgKyBjICYgYiArIGQgJiBuID0gYSArIGIgKyBjICsgZCBcXA0KXGhsaW5lDQpcZW5ke2FycmF5fQ0KXF0NCg0KxJDhu4MgaGnhu4N1IMSRxrDhu6NjIHPhu7EgaMOsbmggdGjDoG5oIGPhu6dhIGLhuqNuZyBuw6B5LCBj4bqnbiB4w6FjIMSR4buLbmggbcO0IGjDrG5oIHjDoWMgc3XhuqV0IHNpbmggcmEgZOG7ryBsaeG7h3Ug4oCUIHRyb25nIMSRw7MgcGjhu5UgYmnhur9uIG5o4bqldCBsw6AgcGjDom4gcGjhu5FpICoqUG9pc3NvbioqIHbDoCAqKk11bHRpbm9taWFsKiouDQoNCiMjIyAqKjEuMyBQaMOibiBwaOG7kWkgUG9pc3NvbioqDQoNClBow6JuIHBo4buRaSBQb2lzc29uIHRoxrDhu51uZyDEkcaw4bujYyBz4butIGThu6VuZyDEkeG7gyBtw7QgaMOsbmggaMOzYSBz4buRIGzGsOG7o25nIHPhu7Ega2nhu4duIHjhuqN5IHJhIHRyb25nIG3hu5l0IGtob+G6o25nIHRo4budaSBnaWFuLCBraMO0bmcgZ2lhbiBob+G6t2MgxJHGoW4gduG7iyBj4bulIHRo4buDLiBHaeG6oyBz4butIGPDoWMgc+G7sSBraeG7h24geOG6o3kgcmEgxJHhu5ljIGzhuq1wIHbDoCB24bubaSBt4buZdCB04bu3IGzhu4cgdHJ1bmcgYsOsbmgga2jDtG5nIMSR4buVaSwgbeG7l2kgw7QgdHJvbmcgYuG6o25nIGPDsyB0aOG7gyB4ZW0gbMOgIGJp4bq/biBuZ+G6q3Ugbmhpw6puIFBvaXNzb246DQoNClxbDQpYX3tpan0gXHNpbSBcdGV4dHtQb2lzc29ufShcbGFtYmRhX3tpan0pDQpcXQ0KDQoqKsavdSDEkWnhu4NtOioqDQoNCi0gUGjDuSBo4bujcCBraGkgdOG7lW5nIHPhu5EgcXVhbiBzw6F0IGtow7RuZyBj4buRIMSR4buLbmguDQoNCi0gw4FwIGThu6VuZyB0cm9uZyBwaMOibiB0w61jaCBz4buRIHPhu7Ega2nhu4duIG5oxrA6IHPhu5EgbMaw4bujdCB0cnV5IGPhuq1wLCBz4buRIMSRxqFuIGjDoG5nIGzhu5dpLCB2LnYuDQoNCioqSOG6oW4gY2jhur86KioNCg0KLSBLaMO0bmcga2nhu4NtIHNvw6F0IMSRxrDhu6NjIHThu5VuZyBz4buRIG3huqt1IChuKS4NCg0KIyMjICoqMS40IFBow6JuIHBo4buRaSBNdWx0aW5vbWlhbCoqDQoNClRyb25nIHRyxrDhu51uZyBo4bujcCB04buVbmcgc+G7kSBxdWFuIHPDoXQgXCggbiBcKSBsw6AgY+G7kSDEkeG7i25oLCB2w6AgbeG7l2kgcXVhbiBzw6F0IHLGoWkgdsOgbyBt4buZdCB0cm9uZyBi4buRbiDDtCB24bubaSB4w6FjIHN14bqldCBcKCBwXzEsIHBfMiwgcF8zLCBwXzQgXCksIHRow6wgYuG6o25nIG5n4bqrdSBuaGnDqm4gMngyIGPDsyB0aOG7gyDEkcaw4bujYyBtw7QgaMOsbmggaMOzYSB0aGVvIHBow6JuIHBo4buRaSDEkWEgdGjhu6ljIE11bHRpbm9taWFsIG5oxrAgc2F1Og0KDQpcWw0KKGEsIGIsIGMsIGQpIFxzaW0gXHRleHR7TXVsdGlub21pYWx9KG47IHBfMSwgcF8yLCBwXzMsIHBfNCkNClxdDQoNClBow6JuIHBo4buRaSBuw6B5IHRoxrDhu51uZyDEkcaw4bujYyBz4butIGThu6VuZyB0cm9uZyBjw6FjIHTDrG5oIGh14buRbmcgbcOgIGThu68gbGnhu4d1IMSRxrDhu6NjIHRodSB0aOG6rXAgdOG7qyBraOG6o28gc8OhdCBob+G6t2MgbmdoacOqbiBj4bupdSB4w6MgaOG7mWkgaOG7jWMgduG7m2kga8OtY2ggdGjGsOG7m2MgbeG6q3UgY+G7kSDEkeG7i25oLiBLaGkgxJHDsywgY8OhYyB4w6FjIHN14bqldCBcKCBwXzEsIHBfMiwgcF8zLCBwXzQgXCkgYmnhu4N1IGRp4buFbiB4w6FjIHN14bqldCBtw6AgbeG7mXQgY8OhIHRo4buDIHLGoWkgdsOgbyB04burbmcgw7QgdHJvbmcgYuG6o25nIDJ4Mi4NCg0KKirGr3UgxJFp4buDbSBj4bunYSBtw7QgaMOsbmggTXVsdGlub21pYWw6KioNCg0KLSBLaeG7g20gc2/DoXQgxJHGsOG7o2MgdOG7lW5nIHPhu5EgbeG6q3UuDQoNCi0gUGjDuSBo4bujcCBjaG8gZOG7ryBsaeG7h3Uga2jhuqNvIHPDoXQsIHRo4butIG5naGnhu4dtLg0KDQoqKlNvIHPDoW5oIGdp4buvYSBwaMOibiBwaOG7kWkgUG9pc3NvbiB2w6AgTXVsdGlub21pYWwqKg0KDQp8IFRpw6p1IGNow60gICAgICAgICB8IFBvaXNzb24gICAgICAgICAgICAgICAgICB8IE11bHRpbm9taWFsICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFThu5VuZyBz4buRIG3huqt1ICAgICAgfCBLaMO0bmcgY+G7kSDEkeG7i25oICAgICAgICAgICAgIHwgQ+G7kSDEkeG7i25oICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwg4buobmcgZOG7pW5nIGNow61uaCAgIHwgU+G7kSBz4buxIGtp4buHbiAgICAgICAgICAgICAgICB8IFThuqduIHN14bqldCBraOG6o28gc8OhdCAgICAgICAgICAgICAgICAgICAgIHwNCnwgROG7ryBsaeG7h3UgcGjDuSBo4bujcCAgfCBHaWFvIGThu4tjaCwgbOG7l2ksIHRhaSBu4bqhbiAgIHwgVHLhuqMgbOG7nWkga2jhuqNvIHPDoXQsIHBow6JuIG5ow7NtICAgICAgICAgIHwNCg0KIyMjICoqMi4gQ8OhYyBjaOG7iSBz4buRIMSRbyBt4buRaSBsacOqbiBo4buHKioNCg0KIyMjICoqMi4xLiAgSGnhu4d1IHThu7cgbOG7hyAoUmlzayBEaWZmZXJlbmNlIC0gUkQpKioNCg0KXFsNClJEID0gXGZyYWN7YX17YSArIGJ9IC0gXGZyYWN7Y317YyArIGR9DQpcXQ0KDQoqKsOdIG5naMSpYSoqOg0KDQotIMSQbyBsxrDhu51uZyAqKmNow6puaCBs4buHY2ggdHV54buHdCDEkeG7kWkqKiBnaeG7r2EgeMOhYyBzdeG6pXQgeOG6o3kgcmEga+G6v3QgcXXhuqMg4bufIG5ow7NtIHBoxqFpIG5oaeG7hW0gdsOgIG5ow7NtIGtow7RuZyBwaMahaSBuaGnhu4VtLg0KDQotIFRoxrDhu51uZyDEkcaw4bujYyBz4butIGThu6VuZyB0cm9uZyDEkcOhbmggZ2nDoSAqKnTDoWMgxJHhu5luZyBj4bunYSBjaMOtbmggc8OhY2gqKiwgY2FuIHRoaeG7h3AgaG/hurdjIGNoxrDGoW5nIHRyw6xuaCB0aMOtIMSRaeG7g20sIGtoaSBz4buxIGtow6FjIGJp4buHdCB24buBIHjDoWMgc3XhuqV0IGzDoCBxdWFuIHRy4buNbmcgaMahbiBzbyB24bubaSB04bu3IHPhu5EuDQoNCktob+G6o25nIHRpbiBj4bqteSA5NSUgKENJKSBjaG8gUkQgY8OzIHRo4buDIMSRxrDhu6NjIMaw4bubYyBsxrDhu6NuZyBi4bqxbmc6DQoNClxbDQpDSV97UkR9ID0gUkQgXHBtIFpfezEgLSBcYWxwaGEvMn0gXGNkb3QgU0Vfe1JEfQ0KXF0NCg0KVHJvbmcgxJHDszoNCi0gXCggWl97MSAtIFxhbHBoYS8yfSBcKSBsw6AgZ2nDoSB0cuG7iyB04bubaSBo4bqhbiAodGjGsOG7nW5nIOKJiCAxLjk2IHbhu5tpIDk1JSBDSSksDQoNCi0gXCggU0Vfe1JEfSA9IFxzcXJ0e1xmcmFje2EoYStiLWEpfXsoYStiKV4zfSArIFxmcmFje2MoYytkLWMpfXsoYytkKV4zfX0gXCkNCg0KIyMjICoqMi4yICBU4bu3IHPhu5Egbmd1eSBjxqEgKFJlbGF0aXZlIFJpc2sgLSBSUikqKg0KDQpcWw0KUlIgPSBcZnJhY3thIC8gKGEgKyBiKX17YyAvIChjICsgZCl9DQpcXQ0KDQoqKsOdIG5naMSpYSoqOg0KDQotIFwoIFJSID4gMSBcKTogbmd1eSBjxqEgeOG6o3kgcmEga+G6v3QgcXXhuqMgY2FvIGjGoW4g4bufIG5ow7NtIHBoxqFpIG5oaeG7hW0uDQoNCi0gXCggUlIgPCAxIFwpOiBuZ3V5IGPGoSB44bqjeSByYSBr4bq/dCBxdeG6oyB0aOG6pXAgaMahbiDhu58gbmjDs20gcGjGoWkgbmhp4buFbS4NCg0KLSBcKCBSUiA9IDEgXCk6IGtow7RuZyBjw7MgbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2EgcGjGoWkgbmhp4buFbSB2w6Aga+G6v3QgcXXhuqMuDQoNClRhIHRoxrDhu51uZyBs4bqleSAqKmxvZyoqIGPhu6dhIFJSIMSR4buDIHTDrW5oIGtob+G6o25nIHRpbiBj4bqteToNCg0KXFsNCkNJX3tSUn0gPSBcZXhwIFxsZWZ0WyBcbG4oUlIpIFxwbSBaX3sxIC0gXGFscGhhLzJ9IFxjZG90IFNFX3tcbG4oUlIpfSBccmlnaHRdDQpcXQ0KDQpUcm9uZyDEkcOzOg0KDQpcWw0KU0Vfe1xsbihSUil9ID0gXHNxcnR7IFxmcmFjezF9e2F9IC0gXGZyYWN7MX17YSArIGJ9ICsgXGZyYWN7MX17Y30gLSBcZnJhY3sxfXtjICsgZH0gfQ0KXF0NCg0KKipWw60gZOG7pSB0cm9uZyBraW5oIGRvYW5oKio6DQoNCi0gVOG7tyBs4buHIGtow6FjaCBtdWEgaMOgbmcga2hpIGPDsyBraHV54bq/biBtw6NpOiBcKCA4MC8xMDAgPSAwLjggXCkNCg0KLSBLaGkga2jDtG5nIGPDsyBraHV54bq/biBtw6NpOiBcKCA0MC8xMDAgPSAwLjQgXCkNCg0KLSBcKCBSUiA9IDAuOCAvIDAuNCA9IDIgXCkNCg0K4oeSIEtodXnhur9uIG3Do2kgbMOgbSB0xINuZyBn4bqlcCAqKsSRw7RpKioga2jhuqMgbsSDbmcga2jDoWNoIG11YSBow6BuZy4NCg0KSOG6oW4gY2jhur86DQoNCktow7RuZyBz4butIGThu6VuZyDEkcaw4bujYyB0cm9uZyBuZ2hpw6puIGPhu6l1IGLhu4duaCBjaOG7qW5nIChjYXNlLWNvbnRyb2wpIHbDrCB0YSBraMO0bmcgYmnhur90IG5ndXkgY8ahIHR1eeG7h3QgxJHhu5FpLg0KDQpE4buFIGfDonkgaGnhu4N1IG5o4bqnbSBu4bq/dSBraMO0bmcgxJFpIGvDqG0gbmd1eSBjxqEgdHV54buHdCDEkeG7kWkuIFbDrSBk4bulOiBSUiA9IDIgY8OzIHbhursgY2FvLCBuaMawbmcgbuG6v3Ugbmd1eSBjxqEgYmFuIMSR4bqndSBsw6AgMSUsIHRow6wgdMSDbmcgbMOqbiAyJSB24bqrbiBsw6AgcuG6pXQgdGjhuqVwLg0KDQpLaMO0bmcgxJHhu5FpIHjhu6luZzogTuG6v3UgxJHhu5VpIG5ow7NtIHRoYW0gY2hp4bq/dSB0aMOsIGdpw6EgdHLhu4sgUlIgdGhheSDEkeG7lWksIGtow6FjIHbhu5tpIE9SLg0KDQojIyMgKioyLjMuICBU4bu3IHPhu5EgY2jDqm5oIChPZGRzIFJhdGlvIC0gT1IpKioNCg0KIyMjIyAqKmEuIE9kZHMqKg0KDQotICoqT2RkcyAodOG7tyBz4buRIGPGoSBo4buZaSkqKjogIA0KICBcWw0KICBcdGV4dHtPZGRzfSA9IFxmcmFje3B9ezEgLSBwfQ0KICBcXQ0KICBUcm9uZyDEkcOzLCBcKHBcKSBsw6AgeMOhYyBzdeG6pXQgeOG6o3kgcmEgY+G7p2EgbeG7mXQgYmnhur9uIG5o4buLIHBow6JuIChuaMawIHZp4buHYyBz4bufIGjhu691IG5ow6ApLg0KICANCiMjIyMgKipiLiBPZGRzIFJhdGlvIC0gT1IqKg0KDQpcWw0KT1IgPSBcZnJhY3thIC8gYn17YyAvIGR9ID0gXGZyYWN7YWR9e2JjfQ0KXF0NCg0KSGF5ICANCg0KXFsNCiAgXHRleHR7T1J9ID0gXGZyYWN7XHRleHR7T2RkcyDhu58gbmjDs20gMX19e1x0ZXh0e09kZHMg4bufIG5ow7NtIDJ9fQ0KXF0NCiAgT1IgdGjGsOG7nW5nIGTDuW5nIMSR4buDIMSRbyBsxrDhu51uZyBt4buRaSBsacOqbiBo4buHIGdp4buvYSBt4buZdCB54bq/dSB04buRIHbDoCBt4buZdCBr4bq/dCBxdeG6oyB0cm9uZyBi4bqjbmcgMngyLg0KICANCi0gKipPUiA9IDEqKjogS2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCB24buBIG9kZHMgZ2nhu69hIGhhaSBuaMOzbS4gIA0KDQotICoqT1IgPiAxKio6IE5ow7NtIDEgY8OzIG9kZHMgeOG6o3kgcmEga+G6v3QgcXXhuqMgY2FvIGjGoW4gbmjDs20gMi4gIA0KDQotICoqT1IgPCAxKio6IE5ow7NtIDEgY8OzIG9kZHMgeOG6o3kgcmEga+G6v3QgcXXhuqMgdGjhuqVwIGjGoW4gbmjDs20gMi4NCg0KPiBWw60gZOG7pTogT1IgPSAyIG5naMSpYSBsw6AgKipvZGRzIHjhuqN5IHJhIGvhur90IHF14bqjIOG7nyBuaMOzbSAxIGNhbyBn4bqlcCAyIGzhuqduIG5ow7NtIDIqKi4NCg0KLSAqKkzGsHUgw70qKjogT1Iga2jDoWMgduG7m2kgUlIsIMSR4bq3YyBiaeG7h3Qga2hpIGJp4bq/biBwaOG7pSB0aHXhu5ljIGtow7RuZyBoaeG6v20gZ+G6t3AuIEtoaSBr4bq/dCBxdeG6oyBsw6AgaGnhur9tIChwIG5o4buPKSwgdGjDrCBPUiDiiYggUlIuDQoNCioqw50gbmdoxKlhKio6DQoNCi0gT1IgY2hvIGJp4bq/dCB04bu3IGzhu4cgKipvZGRzKiogKHThu6ljIGzDoCAieMOhYyBzdeG6pXQgY2hpYSBjaG8gMSB0cuG7qyB4w6FjIHN14bqldCIpIGdp4buvYSBoYWkgbmjDs20uDQoNCi0gVGjGsOG7nW5nIMSRxrDhu6NjIHPhu60gZOG7pW5nIHBo4buVIGJp4bq/biB0cm9uZyBtw7QgaMOsbmggKipsb2dpc3RpYyByZWdyZXNzaW9uKiouDQoNCi0gVHJvbmcgY8OhYyBuZ2hpw6puIGPhu6l1IGLhu4duaCBoaeG6v20gKHJhcmUgZGlzZWFzZSBhc3N1bXB0aW9uKSwgT1IgeOG6pXAgeOG7iSB24bubaSBSUi4NCg0KKirGr3UgxJFp4buDbSBj4bunYSBPZGRzIFJhdGlvKio6DQoNCi0gROG7hSB0w61uaCB2w6AgZOG7hSBkaeG7hW4gZ2nhuqNpLg0KDQotIOG7lG4gxJHhu4tuaCB24buBIG3hurd0IHRvw6FuIGjhu41jIGtoaSBz4butIGThu6VuZyB0cm9uZyBjw6FjIG3DtCBow6xuaCBo4buTaSBxdXkuDQoNCi0gS2jDtG5nIHBo4bulIHRodeG7mWMgdsOgbyB04bu3IGzhu4cgaGnhu4duIGRp4buHbiB0cm9uZyBt4bqrdSAoxJHhurdjIGJp4buHdCB0cm9uZyBjw6FjIHRoaeG6v3Qga+G6vyAqKmLhu4duaCDigJMgY2jhu6luZyoqOiAqY2FzZS1jb250cm9sIHN0dWR5KikuDQoNCiMjICoqMy4gS2hv4bqjbmcgxrDhu5tjIGzGsOG7o25nIHRpbiBj4bqteSBjaG8gT0REUyBSQVRJTyoqDQoNCsSQ4buDIHjDoWMgxJHhu4tuaCB4ZW0gT1IgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiBoYXkga2jDtG5nLCBj4bqnbiB4w6J5IGThu7FuZyBraG/huqNuZyB0aW4gY+G6rXkuDQoNCioqTG9nYXJpdGhtIHThu7Egbmhpw6puIGPhu6dhIE9kZHMgUmF0aW86KioNCg0KXFsNClxsb2coT1IpID0gXGxvZ1xsZWZ0KCBcZnJhY3thIFxjZG90IGR9e2IgXGNkb3QgY30gXHJpZ2h0KQ0KXF0NCg0KKipTYWkgc+G7kSBjaHXhuqluIChTdGFuZGFyZCBFcnJvcik6KioNCg0KXFsNClNFID0gXHNxcnR7IFxmcmFjezF9e2F9ICsgXGZyYWN7MX17Yn0gKyBcZnJhY3sxfXtjfSArIFxmcmFjezF9e2R9IH0NClxdDQoNCioqS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gXChcbG9nKE9SKVwpOioqDQoNClxbDQpcbG9nKE9SKSBccG0gMS45NiBcY2RvdCBTRQ0KXF0NCg0KKipM4bqleSBtxakgxJHhu4MgY8OzIGtob+G6o25nIHRpbiBj4bqteSA5NSUgY2hvIE9SOioqDQoNClxbDQpDSV97OTVcJX0gPSBcbGVmdFsgZV57XGxvZyhPUikgLSAxLjk2IFxjZG90IFNFfSxccXVhZCBlXntcbG9nKE9SKSArIDEuOTYgXGNkb3QgU0V9IFxyaWdodF0NClxdDQoNCi0gTuG6v3Uga2hv4bqjbmcgdGluIGPhuq15ICoqa2jDtG5nIGNo4bupYSAxKiosIHRhIGPDsyB0aOG7gyBr4bq/dCBsdeG6rW4gcuG6sW5nICoqbeG7kWkgbGnDqm4gaOG7hyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqKiog4bufIG3hu6ljIMO9IG5naMSpYSA1JS4NCg0KIyMgKio0LiDhu6huZyBk4bulbmcgdHJvbmcga2luaCBkb2FuaDogSEnhu4ZVIFFV4bqiIEPhu6ZBIEtIVVnhur5OIE3Dg0kqKg0KDQpN4buZdCBjw7RuZyB0eSB0aMawxqFuZyBt4bqhaSDEkWnhu4duIHThu60gdGjhu60gbmdoaeG7h20gY2hp4bq/biBk4buLY2gga2h1eeG6v24gbcOjaSDEkeG7gyBraeG7g20gdHJhIHhlbSBsaeG7h3UgY8OzIOG6o25oIGjGsOG7n25nIMSR4bq/biBow6BuaCB2aSBtdWEgaMOgbmcga2jDtG5nLiBI4buNIGzhuqV5IG3huqt1IDIwMCBraMOhY2ggaMOgbmcgbmfhuqt1IG5oacOqbiB2w6AgdGh1IMSRxrDhu6NjIGLhuqNuZyBzYXU6DQoNCnwgICAgICAgICAgICAgICAgICB8IE11YSBow6BuZyB8IEtow7RuZyBtdWEgfCBU4buVbmcgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tfA0KfCBDw7Mga2h1eeG6v24gbcOjaSAgICB8IDgwICAgICAgIHwgMjAgICAgICAgICB8IDEwMCAgIHwNCnwgS2jDtG5nIGtodXnhur9uIG3Do2kgfCA0MCAgICAgICB8IDYwICAgICAgICAgfCAxMDAgICB8DQoNCi0gVMOtbmggT2RkcyBSYXRpbyAoT1IpOg0KDQpcWw0KT1IgPSBcZnJhY3s4MCBcY2RvdCA2MH17MjAgXGNkb3QgNDB9ID0gXGZyYWN7NDgwMH17ODAwfSA9IDYNClxdDQoNCuKGkiBPZGRzIG11YSBow6BuZyBraGkgY8OzIGtodXnhur9uIG3Do2kgKipjYW8gZ+G6pXAgNiBs4bqnbioqIHNvIHbhu5tpIGtoaSBraMO0bmcgY8OzIGtodXnhur9uIG3Do2kuDQoNCi0gTG9nYXJpdGhtIHThu7Egbmhpw6puIGPhu6dhIE9SOg0KDQpcWw0KXGxvZyhPUikgPSBcbG9nKDYpIFxhcHByb3ggMS43OQ0KXF0NCg0KLSBTYWkgc+G7kSBjaHXhuqluIChTRSk6DQoNClxbDQpTRSA9IFxzcXJ0eyBcZnJhY3sxfXs4MH0gKyBcZnJhY3sxfXsyMH0gKyBcZnJhY3sxfXs0MH0gKyBcZnJhY3sxfXs2MH0gfSBcYXBwcm94IDAuMzUNClxdDQoNCi0gS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gT1I6DQoNClxbDQpDSSA9IFxsZWZ0WyBlXnsxLjc5IC0gMS45NiBcY2RvdCAwLjM1fSxccXVhZCBlXnsxLjc5ICsgMS45NiBcY2RvdCAwLjM1fSBccmlnaHRdIA0KXGFwcHJveCBcbGVmdFsgZV57MS4xMX0sXHF1YWQgZV57Mi40N30gXHJpZ2h0XSANCj0gWzMuMDMsXCAxMS44MV0NClxdDQoNCi0gVsOsIGtob+G6o25nIHRpbiBj4bqteSAqKmtow7RuZyBjaOG7qWEgMSoqLCBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyAqKmtodXnhur9uIG3Do2kgY8OzIOG6o25oIGjGsOG7n25nIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6oqKiDEkeG6v24gaMOgbmggdmkgbXVhIGjDoG5nLg0KDQotIFbhu5tpIFwoIE9SID0gNiBcKSB2w6AgXCggQ0kgPSAoMy4wMyxcIDExLjgxKSBcKSwgZG9hbmggbmdoaeG7h3AgY8OzIHRo4buDIHThu7EgdGluIHRyaeG7g24ga2hhaSAqKmNoaeG6v24gbMaw4bujYyBraHV54bq/biBtw6NpIHF1eSBtw7QgbOG7m24gaMahbioqLg0KDQojIyAqKsOBcCBk4bulbmcqKg0KDQojIyAqKjEuUGjDom4gdMOtY2ggQ2jDqW8gJiBSUioqDQoNCg0KIyMjICoqMS4xIEdlbmRlciB2w6AgUHJvZHVjdEZhbWlseSoqDQoNCg0KVOG6oW8gYuG6o25nIGNow6lvIGNobyBHZW5kZXIgdsOgIG3hu5l0IGJp4bq/biBuaOG7iyBwaMOibiBjaG8gdmnhu4djIGPDsyBtdWEgJ0Zvb2QnIGhheSBraMO0bg0KDQoNCmBgYHtyfQ0KZGxkdCRCdXlzRm9vZCA8LSBpZmVsc2UoZGxkdCRQcm9kdWN0RmFtaWx5ID09ICJGb29kIiwgIk11YSIsICJLaMO0bmciKQ0KZGxkdCRCdXlzRm9vZCA8LSBhcy5mYWN0b3IoZGxkdCRCdXlzRm9vZCkgIyBDaHV54buDbiB0aMOgbmggZmFjdG9yDQpgYGANCg0KYGBge3J9DQpnZW5kZXJfb3JkZXJlZCA8LSBjKCJGIiwgIk0iKSAjIEdp4bqjIHPhu60gTuG7ryBsw6AgbmjDs20gcXVhbiB0w6JtLCBOYW0gbMOgIHRoYW0gY2hp4bq/dQ0KYnV5c19vcmRlcmVkIDwtIGMoIlllc19CdXlzX0Zvb2QiLCAiTm9fQnV5c19Gb29kIikgIyAiWWVzIiBsw6Agb3V0Y29tZSB0w61jaCBj4buxYw0KdGFibGVfZ2VuZGVyX2Zvb2QgPC0gdGFibGUoR2VuZGVyID0gZGxkdCRHZW5kZXIsIEJ1eXNGb29kID0gZGxkdCRCdXlzRm9vZCkNCmBgYA0KDQoNCiAgS2nhu4NtIHRyYSBi4bqjbmc6DQoNCmBgYHtyfQ0KY2F0KCJcbkLhuqNuZyBjaMOpbyBHZW5kZXIgdnMuIE11YSBT4bqjbiBwaOG6qW0gJ0Zvb2QnOlxuIikNCnByaW50KHRhYmxlX2dlbmRlcl9mb29kKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmlmICgiTm9fQnV5c19Gb29kIiAlaW4lIGNvbG5hbWVzKHRhYmxlX2dlbmRlcl9mb29kKSAmJiAiWWVzX0J1eXNfRm9vZCIgJWluJSBjb2xuYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkpIHsNCiAgaWYgKG1hdGNoKCJOb19CdXlzX0Zvb2QiLCBjb2xuYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkpIDwgbWF0Y2goIlllc19CdXlzX0Zvb2QiLCBjb2xuYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkpKSB7DQogICAgdGFibGVfZ2VuZGVyX2Zvb2QgPC0gdGFibGVfZ2VuZGVyX2Zvb2RbLCBjKCJZZXNfQnV5c19Gb29kIiwgIk5vX0J1eXNfRm9vZCIpXQ0KICAgIGNhdCgiXG7EkMOjIHPhuq9wIHjhur9wIGzhuqFpIGPhu5l0IGPhu6dhIGLhuqNuZyBjaG8gcmlza3JhdGlvLlxuIikNCiAgICBwcmludCh0YWJsZV9nZW5kZXJfZm9vZCkNCiAgfQ0KfQ0KIyBUxrDGoW5nIHThu7EsIG7hur91IGPhuqduIMSR4bqjbyBow6BuZyAoRiByYSB0csaw4bubYykNCmlmICgiTSIgJWluJSByb3duYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkgJiYgIkYiICVpbiUgcm93bmFtZXModGFibGVfZ2VuZGVyX2Zvb2QpKSB7DQogIGlmIChtYXRjaCgiTSIsIHJvd25hbWVzKHRhYmxlX2dlbmRlcl9mb29kKSkgPCBtYXRjaCgiRiIsIHJvd25hbWVzKHRhYmxlX2dlbmRlcl9mb29kKSkpIHsNCiAgICB0YWJsZV9nZW5kZXJfZm9vZCA8LSB0YWJsZV9nZW5kZXJfZm9vZFtjKCJGIiwgIk0iKSwgXQ0KICAgICBjYXQoIlxuxJDDoyBz4bqvcCB44bq/cCBs4bqhaSBow6BuZyBj4bunYSBi4bqjbmcgY2hvIHJpc2tyYXRpby5cbiIpDQogICAgIHByaW50KHRhYmxlX2dlbmRlcl9mb29kKQ0KICB9DQp9DQoNCmNhdCgiXG5QaMOibiB0w61jaCBSZWxhdGl2ZSBSaXNrIChO4buvIHNvIHbhu5tpIE5hbSBjaG8gdmnhu4djIG11YSAnRm9vZCcpOlxuIikNCmBgYA0KDQoNCmBgYHtyfQ0KdGFibGVfZ2VuZGVyX2Zvb2QgPC0gdGFibGUoR2VuZGVyID0gZGxkdCRHZW5kZXIsIEJ1eXNGb29kID0gZGxkdCRCdXlzRm9vZCkNCmNhdCgiXG5LaeG7g20gdHJhIGLhuqNuZyB0YWJsZV9nZW5kZXJfZm9vZCB0csaw4bubYyBraGkgdMOtbmggUlI6XG4iKQ0KcHJpbnQodGFibGVfZ2VuZGVyX2Zvb2QpDQpwcmludChwYXN0ZSgiS8OtY2ggdGjGsOG7m2MgYuG6o25nOiIsIHBhc3RlKGRpbSh0YWJsZV9nZW5kZXJfZm9vZCksIGNvbGxhcHNlPSJ4IikpKQ0KY2F0KCJcbkxldmVscyBj4bunYSBHZW5kZXI6IiwgcGFzdGUobGV2ZWxzKGRsZHQkR2VuZGVyKSwgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIkxldmVscyBj4bunYSBCdXlzRm9vZDoiLCBwYXN0ZShsZXZlbHMoZGxkdCRCdXlzRm9vZCksIGNvbGxhcHNlPSIsICIpLCAiXG4iKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShlcGl0b29scykNCg0KcnJfZ2VuZGVyX2Zvb2Rfb2JqIDwtIHJpc2tyYXRpbyh0YWJsZV9nZW5kZXJfZm9vZCwgcmV2ID0gImNvbHVtbnMiKSANCiMgcmV2ID0gImNvbHVtbnMiIMSR4buDIGNvaSBj4buZdCDEkeG6p3UgbMOgICJZZXMiIChNdWEpIGNobyBSUg0KDQpjYXQoIlxuS+G6v3QgcXXhuqMgdMOtbmggUmVsYXRpdmUgUmlzazpcbiIpDQpwcmludChycl9nZW5kZXJfZm9vZF9vYmopDQoNCmBgYA0KYGBge3J9DQpzdHIocnJfZ2VuZGVyX2Zvb2Rfb2JqKQ0KYGBgDQoNCg0KYGBge3J9DQptZWFzdXJlIDwtIHJyX2dlbmRlcl9mb29kX29iaiRtZWFzdXJlDQptZWFzdXJlX3JvdyA8LSBtZWFzdXJlWyJGIiwgXQ0KDQppZiAoYW55KGlzLm5hKG1lYXN1cmVfcm93W2MoImVzdGltYXRlIiwgImxvd2VyIiwgInVwcGVyIildKSkpIHsNCiAgY2F0KCJcbuKdlyBHacOhIHRy4buLIFJSIGhv4bq3YyBLVEMgY8OzIE5BLCBraMO0bmcgdGjhu4MgxJHDoW5oIGdpw6Egw70gbmdoxKlhIHRo4buRbmcga8OqLlxuIikNCn0gZWxzZSB7DQogIHJyX2VzdGltYXRlX2dmIDwtIG1lYXN1cmVfcm93WyJlc3RpbWF0ZSJdDQogIHJyX2xvd2VyX2dmICAgIDwtIG1lYXN1cmVfcm93WyJsb3dlciJdDQogIHJyX3VwcGVyX2dmICAgIDwtIG1lYXN1cmVfcm93WyJ1cHBlciJdDQoNCiAgY2F0KHNwcmludGYoIlxuUlIgPSAlLjNmLCA5NSUlIENJOiAoJS4zZiwgJS4zZilcbiIsIHJyX2VzdGltYXRlX2dmLCBycl9sb3dlcl9nZiwgcnJfdXBwZXJfZ2YpKQ0KDQogIGlmIChpcy5pbmZpbml0ZShycl9lc3RpbWF0ZV9nZikgfHwgaXMuaW5maW5pdGUocnJfbG93ZXJfZ2YpIHx8IGlzLmluZmluaXRlKHJyX3VwcGVyX2dmKSkgew0KICAgIGNhdCgiICAtIEPhuqNuaCBiw6FvOiBSUiBob+G6t2MgS1RDIGzDoCB2w7QgY+G7sWMgKEluZikuIEPDsyB0aOG7gyBjw7Mgw7QgPSAwIHRyb25nIGLhuqNuZy5cbiIpDQogIH0gZWxzZSBpZiAocnJfbG93ZXJfZ2YgPiAxKSB7DQogICAgY2F0KHNwcmludGYoIiAgLSBO4buvIGdp4bubaSBjw7Mga2jhuqMgbsSDbmcgbXVhICdGb29kJyBDQU8gSMagTiBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChjYW8gaMahbiBraG/huqNuZyAlLjFmJSUpLlxuIiwNCiAgICAgICAgICAgICAgICAocnJfZXN0aW1hdGVfZ2YgLSAxKSAqIDEwMCkpDQogIH0gZWxzZSBpZiAocnJfdXBwZXJfZ2YgPCAxKSB7DQogICAgY2F0KHNwcmludGYoIiAgLSBO4buvIGdp4bubaSBjw7Mga2jhuqMgbsSDbmcgbXVhICdGb29kJyBUSOG6pFAgSMagTiBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqICh0aOG6pXAgaMahbiBraG/huqNuZyAlLjFmJSUpLlxuIiwNCiAgICAgICAgICAgICAgICAoMSAtIHJyX2VzdGltYXRlX2dmKSAqIDEwMCkpDQogIH0gZWxzZSB7DQogICAgY2F0KCIgIC0gS2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChLVEMgY2jhu6lhIDEpLlxuIikNCiAgfQ0KfQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBE4buxYSB0csOqbiBj4bqldSB0csO6YyB0aOG7sWMgdOG6vyBj4bunYSBtZWFzdXJlDQptZWFzdXJlIDwtIHJyX2dlbmRlcl9mb29kX29iaiRtZWFzdXJlDQoNCiMgS2nhu4NtIHRyYSB4ZW0gZMOybmcgIkYiIGPDsyB04buTbiB04bqhaSB2w6AgY8OhYyBj4buZdCBj4bqnbiB0aGnhur90IGPDsyB0cm9uZyBtZWFzdXJlDQppZiAoIkYiICVpbiUgcm93bmFtZXMobWVhc3VyZSkgJiYNCiAgICBhbGwoYygiZXN0aW1hdGUiLCAibG93ZXIiLCAidXBwZXIiKSAlaW4lIGNvbG5hbWVzKG1lYXN1cmUpKSkgew0KDQogIHJyX2VzdGltYXRlX2dmIDwtIG1lYXN1cmVbIkYiLCAiZXN0aW1hdGUiXQ0KICBycl9sb3dlcl9nZiAgICA8LSBtZWFzdXJlWyJGIiwgImxvd2VyIl0NCiAgcnJfdXBwZXJfZ2YgICAgPC0gbWVhc3VyZVsiRiIsICJ1cHBlciJdDQogIA0KICAjIEtp4buDbSB0cmEgTkEgdHLGsOG7m2Mga2hpIHjhu60gbMO9DQogIGlmIChhbnkoaXMubmEoYyhycl9lc3RpbWF0ZV9nZiwgcnJfbG93ZXJfZ2YsIHJyX3VwcGVyX2dmKSkpKSB7DQogICAgY2F0KCJcbuKdlyBHacOhIHRy4buLIFJSIGhv4bq3YyBraG/huqNuZyB0aW4gY+G6rXkgY8OzIE5BLiBLaMO0bmcgdGjhu4MgxJHDoW5oIGdpw6Egw70gbmdoxKlhIHRo4buRbmcga8OqLlxuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQocGFzdGUwKCJcblJSID0gIiwgcm91bmQocnJfZXN0aW1hdGVfZ2YsIDMpLA0KICAgICAgICAgICAgICAgIiwgOTUlIENJOiAoIiwgcm91bmQocnJfbG93ZXJfZ2YsIDMpLCAiLCAiLCByb3VuZChycl91cHBlcl9nZiwgMyksICIpXG4iKSkNCiAgICANCiAgICAjIERp4buFbiBnaeG6o2kga+G6v3QgcXXhuqMNCiAgICBpZiAoaXMuaW5maW5pdGUocnJfZXN0aW1hdGVfZ2YpIHx8IGlzLmluZmluaXRlKHJyX2xvd2VyX2dmKSB8fCBpcy5pbmZpbml0ZShycl91cHBlcl9nZikpIHsNCiAgICAgIGNhdCgiICAtIEPhuqNuaCBiw6FvOiBSUiBob+G6t2MgS1RDIGzDoCB2w7QgY+G7sWMgKEluZikuIEPDsyB0aOG7gyBjw7Mgw7QgPSAwIHRyb25nIGLhuqNuZy5cbiIpDQogICAgfSBlbHNlIGlmIChycl9sb3dlcl9nZiA+IDEpIHsNCiAgICAgIGNhdChwYXN0ZTAoIiAgLSBO4buvIGdp4bubaSBjw7Mga2jhuqMgbsSDbmcgbXVhICdGb29kJyBDQU8gSMagTiBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChjYW8gaMahbiBraG/huqNuZyAiLA0KICAgICAgICAgICAgICAgICByb3VuZCgocnJfZXN0aW1hdGVfZ2YgLSAxKSAqIDEwMCwgMSksICIlKS5cbiIpKQ0KICAgIH0gZWxzZSBpZiAocnJfdXBwZXJfZ2YgPCAxKSB7DQogICAgICBjYXQocGFzdGUwKCIgIC0gTuG7ryBnaeG7m2kgY8OzIGto4bqjIG7Eg25nIG11YSAnRm9vZCcgVEjhuqRQIEjGoE4gY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiAodGjhuqVwIGjGoW4ga2hv4bqjbmcgIiwNCiAgICAgICAgICAgICAgICAgcm91bmQoKDEgLSBycl9lc3RpbWF0ZV9nZikgKiAxMDAsIDEpLCAiJSkuXG4iKSkNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCIgIC0gS2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChLVEMgY2jhu6lhIDEpLlxuIikNCiAgICB9DQogIH0NCiAgDQp9IGVsc2Ugew0KICBjYXQoIlxu4p2XIEtow7RuZyB0aOG7gyB0csOtY2ggeHXhuqV0IFJSIHThu6sgZMOybmcgJ0YnLiBLaeG7g20gdHJhIGzhuqFpIG91dHB1dCAkbWVhc3VyZS5cbiIpDQp9DQoNCg0KYGBgDQpgYGB7cn0NCiMgVOG6oW8gYmnhur9uIG5o4buLIHBow6JuIEJ1eXNGb29kDQpkbGR0JEJ1eXNGb29kIDwtIGZhY3RvcihpZmVsc2UoZGxkdCRQcm9kdWN0RmFtaWx5ID09ICJGb29kIiwgIk11YSIsICJLaMO0bmciKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk11YSIsICJLaMO0bmciKSkgDQoNCiMgVOG6oW8gYuG6o25nIGNow6lvIEdlbmRlciB2cyBCdXlzRm9vZA0KdGFibGVfZ2VuZGVyX2Zvb2QgPC0gdGFibGUoR2VuZGVyID0gZGxkdCRHZW5kZXIsIEJ1eXNGb29kID0gZGxkdCRCdXlzRm9vZCkNCg0KY2F0KCJcbkLhuqNuZyBjaMOpbyBHZW5kZXIgdnMuIE11YSBT4bqjbiBwaOG6qW0gJ0Zvb2QnOlxuIikNCnByaW50KHRhYmxlX2dlbmRlcl9mb29kKQ0KDQojIFPhuq9wIHjhur9wIGPhu5l0IChNdWEgdHLGsOG7m2MgS2jDtG5nKQ0KaWYgKCJLaMO0bmciICVpbiUgY29sbmFtZXModGFibGVfZ2VuZGVyX2Zvb2QpICYmICJNdWEiICVpbiUgY29sbmFtZXModGFibGVfZ2VuZGVyX2Zvb2QpKSB7DQogIGlmIChtYXRjaCgiS2jDtG5nIiwgY29sbmFtZXModGFibGVfZ2VuZGVyX2Zvb2QpKSA8IG1hdGNoKCJNdWEiLCBjb2xuYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkpKSB7DQogICAgdGFibGVfZ2VuZGVyX2Zvb2QgPC0gdGFibGVfZ2VuZGVyX2Zvb2RbLCBjKCJNdWEiLCAiS2jDtG5nIildDQogICAgY2F0KCJcbsSQw6Mgc+G6r3AgeOG6v3AgbOG6oWkgY+G7mXQgY+G7p2EgYuG6o25nIGNobyByaXNrcmF0aW8uXG4iKQ0KICAgIHByaW50KHRhYmxlX2dlbmRlcl9mb29kKQ0KICB9DQp9DQoNCiMgU+G6r3AgeOG6v3AgaMOgbmcgKEYgdHLGsOG7m2MgTSkNCmlmICgiTSIgJWluJSByb3duYW1lcyh0YWJsZV9nZW5kZXJfZm9vZCkgJiYgIkYiICVpbiUgcm93bmFtZXModGFibGVfZ2VuZGVyX2Zvb2QpKSB7DQogIGlmIChtYXRjaCgiTSIsIHJvd25hbWVzKHRhYmxlX2dlbmRlcl9mb29kKSkgPCBtYXRjaCgiRiIsIHJvd25hbWVzKHRhYmxlX2dlbmRlcl9mb29kKSkpIHsNCiAgICB0YWJsZV9nZW5kZXJfZm9vZCA8LSB0YWJsZV9nZW5kZXJfZm9vZFtjKCJGIiwgIk0iKSwgXQ0KICAgIGNhdCgiXG7EkMOjIHPhuq9wIHjhur9wIGzhuqFpIGjDoG5nIGPhu6dhIGLhuqNuZyBjaG8gcmlza3JhdGlvLlxuIikNCiAgICBwcmludCh0YWJsZV9nZW5kZXJfZm9vZCkNCiAgfQ0KfQ0KDQpjYXQoIlxuUGjDom4gdMOtY2ggUmVsYXRpdmUgUmlzayAoTuG7ryBzbyB24bubaSBOYW0gY2hvIHZp4buHYyBtdWEgJ0Zvb2QnKTpcbiIpDQoNCmBgYA0KDQoNCiAgS+G6v3QgcXXhuqMgdOG7qyBwaMOibiB0w61jaCBSaXNrIFJhdGlvIChSUik6DQpSUiA9IDEuMDMNCuKGkiBU4bu3IGzhu4cgbuG7ryBtdWEgc+G6o24gcGjhuqltIEZvb2QgY2FvIGjGoW4gMyUgc28gduG7m2kgbmFtIGdp4bubaS4NCg0KS2hv4bqjbmcgdGluIGPhuq15IDk1JSAoS1RDKTogKDAuOTc3LCAxLjA4NykNCuKGkiBLVEMgY2jhu6lhIGdpw6EgdHLhu4sgMSwgbmdoxKlhIGzDoCB0YSBraMO0bmcgdGjhu4MgYsOhYyBi4buPIGdp4bqjIHRodXnhur90IHLhurFuZyBSUiA9IDEgKGtow7RuZyBjw7Mgc+G7sSBraMOhYyBiaeG7h3QpLg0KDQogIEtow7RuZyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqOg0KDQpWw6wgS1RDIDk1JSBj4bunYSBSUiBjaOG7qWEgZ2nDoSB0cuG7iyAxLCBuw6puIGtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogxJHhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyBnaeG7m2kgdMOtbmgg4bqjbmggaMaw4bufbmcgxJHhur9uIGjDoG5oIHZpIG11YSBGb29kLg0KDQpOw7NpIGPDoWNoIGtow6FjOiBz4buxIGtow6FjIGJp4buHdCAzJSBjw7MgdGjhu4MgbMOgIGRvIG5n4bqrdSBuaGnDqm4uDQoNCiAgw50gbmdoxKlhIHRo4buxYyB04bq/IChu4bq/dSBjw7MpOg0KDQpN4bq3YyBkw7kgUlIgPiAxICh04bupYyBsw6AgbuG7ryBjw7MgduG6uyBtdWEgbmhp4buBdSBoxqFuKSwgbmjGsG5nIGNow6puaCBs4buHY2ggbsOgeSBuaOG7jyB2w6Aga2jDtG5nIMSRw6FuZyBr4buDLCB2w6Aga2jDtG5nIMSRw6FuZyB0aW4gY+G6rXkgduG7gSBt4bq3dCB0aOG7kW5nIGvDqi4NCg0KVHJvbmcgY8OhYyBwaMOibiB0w61jaCB0aOG7sWMgdGnhu4VuLCBu4bq/dSBSUiBu4bqxbSB0cm9uZyBraG/huqNuZyAoMC45NSAtIDEuMDUpIHbDoCBLVEMgY2jhu6lhIDEgdGjDrCB0aMaw4budbmcga2jDtG5nIGPDsyDhuqNuaCBoxrDhu59uZyByw7UgcsOgbmcuDQoNCmBgYHtyfQ0Kb3JfZ2VuZGVyX2Zvb2Rfb2JqIDwtIG9kZHNyYXRpbyh0YWJsZV9nZW5kZXJfZm9vZCAsIG1ldGhvZCA9ICJ3YWxkIikNCg0KcHJpbnQob3JfZ2VuZGVyX2Zvb2Rfb2JqJG1lYXN1cmUpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIFThuqFvIGRhdGEgZnJhbWUgdOG7qyBr4bq/dCBxdeG6oyBi4bqhbiBjdW5nIGPhuqVwDQpvcl9kZiA8LSBkYXRhLmZyYW1lKA0KICBHZW5kZXIgPSBjKCJNYWxlIHZzIEZlbWFsZSIpLA0KICBPUiA9IGMoMC45NTk3MzM1KSwNCiAgbG93ZXIgPSBjKDAuODkxNDE5NSksDQogIHVwcGVyID0gYygxLjAzMzI4KQ0KKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTDQpnZ3Bsb3Qob3JfZGYsIGFlcyh4ID0gR2VuZGVyLCB5ID0gT1IpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGNvbG9yID0gImRvZGdlcmJsdWU0Iiwgc2hhcGUgPSAxOCkgKyAgIyDEkGnhu4NtIE9SDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSwgd2lkdGggPSAwLjEsIGNvbG9yID0gImRvZGdlcmJsdWU0IiwgbGluZXdpZHRoID0gMSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5NjAiKSArICAjIMSQxrDhu51uZyBPUiA9IDENCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMmYiLCBPUikpLCB2anVzdCA9IC0xLjUsIGZvbnRmYWNlID0gImJvbGQiKSArDQogIGdlb21fdGV4dChhZXMoeSA9IGxvd2VyLCBsYWJlbCA9IHNwcmludGYoIiUuMmYiLCBsb3dlcikpLCB2anVzdCA9IC0wLjMsIGhqdXN0ID0gMS4yLCBzaXplID0gMykgKw0KICBnZW9tX3RleHQoYWVzKHkgPSB1cHBlciwgbGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgdXBwZXIpKSwgdmp1c3QgPSAtMC4zLCBoanVzdCA9IC0wLjIsIHNpemUgPSAzKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiT2RkcyBSYXRpbyB2w6AgS2hv4bqjbmcgdGluIGPhuq15IDk1JSIsDQogICAgc3VidGl0bGUgPSAiU28gc8Ohbmgga2jhuqMgbsSDbmcgbXVhICdGb29kJzogTmFtIHNvIHbhu5tpIE7hu68gKHRoYW0gY2hp4bq/dSkiLA0KICAgIHkgPSAiT2RkcyBSYXRpbyAoT1IpIiwNCiAgICB4ID0gTlVMTA0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQ0KICApDQoNCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc2NhbGVzKQ0KDQojIMSQ4bq/bSBz4buRIGzGsOG7o25nIG3hu5dpIHThu5UgaOG7o3AgR2nhu5tpIHTDrW5oIHbDoCBUcuG6oW5nIHRow6FpIG11YQ0KZ2VuZGVyX2Zvb2RfY291bnRzIDwtIGRsZHQgJT4lDQogIGNvdW50KEdlbmRlciwgQnV5c0Zvb2QpICU+JQ0KICBtdXRhdGUoQnV5c0Zvb2QgPSBmYWN0b3IoQnV5c0Zvb2QsIGxldmVscyA9IGMoIk11YSIsICJLaMO0bmciKSkpICMgxJDhuqNtIGLhuqNvIHRo4bupIHThu7EgY+G7mXQgaOG7o3AgbMO9DQoNCiMgVuG6vSBiaeG7g3UgxJHhu5MNCmdncGxvdChnZW5kZXJfZm9vZF9jb3VudHMsIGFlcyh4ID0gQnV5c0Zvb2QsIHkgPSBuLCBmaWxsID0gR2VuZGVyKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43KSwgd2lkdGggPSAwLjYsIGFscGhhID0gMC45KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNyksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArDQogIA0KICAjIFTDuXkgY2jhu4luaCBtw6B1IHPhuq9jDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIkYiID0gIiNGRjY5QjQiLCAiTSIgPSAiIzFFOTBGRiIpLA0KICAgIGxhYmVscyA9IGMoIk7hu68iLCAiTmFtIikNCiAgKSArDQogIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIlNvIHPDoW5oIHPhu5EgbMaw4bujbmcgTmFtIHbDoCBO4buvIHRoZW8gdmnhu4djIG11YSBz4bqjbiBwaOG6qW0gJ0Zvb2QnIiwNCiAgICBzdWJ0aXRsZSA9ICJQaMOibiB0aGVvIGhhaSBuaMOzbTogTXVhIHbDoCBLaMO0bmcgbXVhIiwNCiAgICB4ID0gIlTDrG5oIHRy4bqhbmcgbXVhIGjDoG5nIiwNCiAgICB5ID0gIlPhu5EgbMaw4bujbmciLA0KICAgIGZpbGwgPSAiR2nhu5tpIHTDrW5oIg0KICApICsNCiAgDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTUpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSwgc2l6ZSA9IDEzKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTEsIG1hcmdpbiA9IG1hcmdpbihiID0gMTApKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikNCiAgKSArDQogIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMDUpKSwgYnJlYWtzID0gcHJldHR5X2JyZWFrcyhuID0gNikpDQoNCg0KYGBgDQoNCg0KDQoNCg0KIyMgKioyLiDEkMOgbyBzw6J1IHbhu4EgU3V5IGRp4buFbiB0cm9uZyBC4bqjbmcgTmfhuqt1IG5oacOqbiAyeDIqKg0KDQoNCmBgYHtyfQ0KIyAtLS0gxJDhuqJNIELhuqJPIFbDgCBT4bquUCBY4bq+UCBM4bqgSSBC4bqiTkcgMlgyIENITyBHZW5kZXIgdnMgQnV5c0Zvb2QgLS0tDQoNCiMgVOG6oW8gYmnhur9uIEJ1eXNGb29kIG7hur91IGNoxrBhIGPDsw0KaWYgKCEiQnV5c0Zvb2QiICVpbiUgbmFtZXMoZGxkdCkpIHsgDQogICAgZGxkdCRCdXlzRm9vZCA8LSBpZmVsc2UoZGxkdCRQcm9kdWN0RmFtaWx5ID09ICJGb29kIiwgIk11YSBGb29kIiwgIktow7RuZyBNdWEgRm9vZCIpDQogICAgZGxkdCRCdXlzRm9vZCA8LSBmYWN0b3IoZGxkdCRCdXlzRm9vZCwgbGV2ZWxzID0gYygiTXVhIEZvb2QiLCAiS2jDtG5nIE11YSBGb29kIikpDQp9DQoNCiMgTOG7jWMgZOG7ryBsaeG7h3UgY2jhu4kgZ+G7k20gY8OhYyBnaeG7m2kgdMOtbmggRiB2w6AgTSwgZ2nhu68gbmd1ecOqbiBsZXZlbHMgZ2VuZGVyDQpsZXZlbHNfZ2VuZGVyIDwtIGMoIkYiLCAiTSIpDQpkbGR0XzJ4Ml9mb29kIDwtIGRsZHRbZGxkdCRHZW5kZXIgJWluJSBsZXZlbHNfZ2VuZGVyLCBdDQoNCiMgQ2h1eeG7g24gR2VuZGVyIHRow6BuaCBmYWN0b3IgduG7m2kgdGjhu6kgdOG7sSBtb25nIG114buRbiwgZ2nhu68gbmd1ecOqbiBsZXZlbHMgxJHhuqd5IMSR4bunDQpkbGR0XzJ4Ml9mb29kJEdlbmRlciA8LSBmYWN0b3IoZGxkdF8yeDJfZm9vZCRHZW5kZXIsIGxldmVscyA9IGxldmVsc19nZW5kZXIpDQoNCiMgVOG6oW8gYuG6o25nIGNow6lvIDJ4Mg0KdGFibGVfR0YgPC0gdGFibGUoR2VuZGVyID0gZGxkdF8yeDJfZm9vZCRHZW5kZXIsIEJ1eXNGb29kID0gZGxkdF8yeDJfZm9vZCRCdXlzRm9vZCkNCmNhdCgiQuG6o25nIG5n4bqrdSBuaGnDqm4gMngyIChHZW5kZXIgdnMuIEJ1eXNGb29kKSDEkcOjIGNodeG6qW4gYuG7izpcbiIpDQpwcmludCh0YWJsZV9HRikNCg0KDQpgYGANCg0KIyMjICoqMi4xIFNvIHPDoW5oIDIgdOG7tyBs4buHIHbDoCBjw6FjIFRoxrDhu5tjIMSRbyBN4buRaSBsacOqbiBo4buHKioNCg0KIyMjIyAqKmEpICBIaeG7h3UgaGFpIHThu7cgbOG7hyAoRGlmZmVyZW5jZSBpbiBQcm9wb3J0aW9ucykqKg0KDQpgYGB7cn0NCmRsZHQkQnV5c0Zvb2QgPC0gaWZlbHNlKGRsZHQkUHJvZHVjdEZhbWlseSA9PSAiRm9vZCIsICJNdWEgRm9vZCIsICJLaMO0bmcgTXVhIEZvb2QiKQ0KZGxkdCRCdXlzRm9vZCA8LSBmYWN0b3IoZGxkdCRCdXlzRm9vZCwgbGV2ZWxzID0gYygiTXVhIEZvb2QiLCAiS2jDtG5nIE11YSBGb29kIikpDQoNCiMgTOG7jWMgZGF0YSBjaOG7iSBjw7MgR2VuZGVyIEYgdsOgIE0sIHbDoCBzZXQgbGV2ZWxzDQpsZXZlbHNfZ2VuZGVyIDwtIGMoIkYiLCAiTSIpDQpkbGR0XzJ4Ml9mb29kIDwtIGRsZHRbZGxkdCRHZW5kZXIgJWluJSBsZXZlbHNfZ2VuZGVyLCBdDQpkbGR0XzJ4Ml9mb29kJEdlbmRlciA8LSBmYWN0b3IoZGxkdF8yeDJfZm9vZCRHZW5kZXIsIGxldmVscyA9IGxldmVsc19nZW5kZXIpDQoNCiMgVOG6oW8gYuG6o25nIGNow6lvDQp0YWJsZV9HRiA8LSB0YWJsZShHZW5kZXIgPSBkbGR0XzJ4Ml9mb29kJEdlbmRlciwgQnV5c0Zvb2QgPSBkbGR0XzJ4Ml9mb29kJEJ1eXNGb29kKQ0KcHJpbnQodGFibGVfR0YpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KaWYgKGFsbChkaW0odGFibGVfR0YpID09IGMoMiwyKSkpIHsNCiAgICBhIDwtIHRhYmxlX0dGWyJGIiwgIk11YSBGb29kIl0NCiAgICBiIDwtIHRhYmxlX0dGWyJGIiwgIktow7RuZyBNdWEgRm9vZCJdDQogICAgYyA8LSB0YWJsZV9HRlsiTSIsICJNdWEgRm9vZCJdDQogICAgZCA8LSB0YWJsZV9HRlsiTSIsICJLaMO0bmcgTXVhIEZvb2QiXQ0KICAgIG4xIDwtIGEgKyBiDQogICAgbjIgPC0gYyArIGQNCn0gZWxzZSB7DQogICAgYSA8LSBiIDwtIGMgPC0gZCA8LSBuMSA8LSBuMiA8LSBOQQ0KfQ0KDQppZiAoIWFueShpcy5uYShjKGEsIGMsIG4xLCBuMikpKSAmJiBuMSA+IDAgJiYgbjIgPiAwKSB7DQogICMgVMOtbmggdOG7tyBs4buHDQogIHAxX0ZfYnV5c19mb29kIDwtIGEgLyBuMQ0KICBwMl9NX2J1eXNfZm9vZCA8LSBjIC8gbjINCiAgZGlmZl9wcm9wX0dGIDwtIHAxX0ZfYnV5c19mb29kIC0gcDJfTV9idXlzX2Zvb2QNCiAgDQogIGNhdChwYXN0ZTAoIlThu7cgbOG7hyBO4buvIG11YSAnRm9vZCcgKFBfRik6ICIsIHJvdW5kKHAxX0ZfYnV5c19mb29kLCA0KSwgIlxuIikpDQogIGNhdChwYXN0ZTAoIlThu7cgbOG7hyBOYW0gbXVhICdGb29kJyAoUF9NKTogIiwgcm91bmQocDJfTV9idXlzX2Zvb2QsIDQpLCAiXG4iKSkNCiAgY2F0KHBhc3RlMCgiSGnhu4d1IGhhaSB04bu3IGzhu4cgKFBfRiAtIFBfTSk6ICIsIHJvdW5kKGRpZmZfcHJvcF9HRiwgNCksICJcbiIpKQ0KICANCiAgIyBLaeG7g20gxJHhu4tuaCB04bu3IGzhu4cgYuG6sW5nIHByb3AudGVzdCAoa2jDtG5nIGNo4buJbmggc+G7rWEgWWF0ZXMgY29udGludWl0eSkNCiAgcHJvcF90ZXN0X2RpZmZfR0YgPC0gcHJvcC50ZXN0KHggPSBjKGEsIGMpLCBuID0gYyhuMSwgbjIpLCBjb3JyZWN0ID0gRkFMU0UpDQogIGNhdCgiXG5L4bq/dCBxdeG6oyB04burIHByb3AudGVzdCgpIMSR4buDIGtp4buDbSDEkeG7i25oIEgwOiBQX0YgPSBQX006XG4iKQ0KICBwcmludChwcm9wX3Rlc3RfZGlmZl9HRikNCiAgDQogICMgVMOtbmggS1RDIDk1JSB0aGVvIHBoxrDGoW5nIHBow6FwIFdhbGQNCiAgc2VfZGlmZl9wcm9wX0dGIDwtIHNxcnQoIChwMV9GX2J1eXNfZm9vZCAqICgxIC0gcDFfRl9idXlzX2Zvb2QpIC8gbjEpICsgKHAyX01fYnV5c19mb29kICogKDEgLSBwMl9NX2J1eXNfZm9vZCkgLyBuMikgKQ0KICB6X2FscGhhX2hhbGYgPC0gcW5vcm0oMC45NzUpDQogIGxvd2VyX2NpX2RpZmZfR0YgPC0gZGlmZl9wcm9wX0dGIC0gel9hbHBoYV9oYWxmICogc2VfZGlmZl9wcm9wX0dGDQogIHVwcGVyX2NpX2RpZmZfR0YgPC0gZGlmZl9wcm9wX0dGICsgel9hbHBoYV9oYWxmICogc2VfZGlmZl9wcm9wX0dGDQogIA0KICBjYXQocGFzdGUwKCJLVEMgOTUlIChXYWxkKSBjaG8gSGnhu4d1IGhhaSB04bu3IGzhu4c6ICgiLCByb3VuZChsb3dlcl9jaV9kaWZmX0dGLCA0KSwgIiwgIiwgcm91bmQodXBwZXJfY2lfZGlmZl9HRiwgNCksICIpXG4iKSkNCiAgDQogICMgRGnhu4VuIGdp4bqjaSBLVEMNCiAgaWYgKGxvd2VyX2NpX2RpZmZfR0YgPiAwKSB7DQogICAgY2F0KCIgIC0gSGnhu4d1IHThu7cgbOG7hyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqOiB04bu3IGzhu4cgTuG7ryBtdWEgY2FvIGjGoW4gTmFtLlxuIikNCiAgfSBlbHNlIGlmICh1cHBlcl9jaV9kaWZmX0dGIDwgMCkgew0KICAgIGNhdCgiICAtIEhp4buHdSB04bu3IGzhu4cgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqjogdOG7tyBs4buHIE7hu68gbXVhIHRo4bqlcCBoxqFuIE5hbS5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCIgIC0gSGnhu4d1IHThu7cgbOG7hyBLSMOUTkcgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiAoS1RDIGNo4bupYSAwKS5cbiIpDQogIH0NCiAgDQp9IGVsc2Ugew0KICBjYXQoIktow7RuZyB0aOG7gyB0w61uaCBIaeG7h3UgaGFpIHThu7cgbOG7hyBkbyBk4buvIGxp4buHdSBraMO0bmcgaOG7o3AgbOG7hyBob+G6t2MgdGhp4bq/dS5cbiIpDQp9DQoNCmBgYA0KDQpU4bu3IGzhu4cgbuG7ryBnaeG7m2kgbXVhIHPhuqNuIHBo4bqpbSBGb29kIGzDoCA3MS44MSUsIHRyb25nIGtoaSB04bu3IGzhu4cgbsOgeSDhu58gbmFtIGdp4bubaSBsw6AgNzIuNjQlLiBIaeG7h3UgZ2nhu69hIGhhaSB04bu3IGzhu4cgKFBfRiDiiJIgUF9NKSBsw6Ag4oiSMC4wMDgyLCB04bupYyBsw6AgdGjhuqVwIGjGoW4ga2hv4bqjbmcgMC44MiDEkWnhu4NtIHBo4bqnbiB0csSDbS4gVHV5IG5oacOqbiwga2hv4bqjbmcgdGluIGPhuq15IDk1JSB0aGVvIHBoxrDGoW5nIHBow6FwIFdhbGQgY2hvIGhp4buHdSBoYWkgdOG7tyBs4buHIGzDoCB04burIOKIkjIuMzElIMSR4bq/biAwLjY2JSwgdOG7qWMgbMOgIGNo4bupYSBnacOhIHRy4buLIDAuIMSQaeG7gXUgbsOgeSDEkeG7k25nIG5naMSpYSB24bubaSB2aeG7h2Mga2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIGdp4buvYSBoYWkgdOG7tyBs4buHLg0KDQpL4bq/dCBxdeG6oyB04burIGtp4buDbSDEkeG7i25oIHByb3AudGVzdCBjxaluZyBjaG8gdGjhuqV5IGdpw6EgdHLhu4sgcCBsw6AgMC4yNzUzICg+IDAuMDUpLCBkbyDEkcOzIHRhIGtow7RuZyBiw6FjIGLhu48gZ2nhuqMgdGh1eeG6v3QgcuG6sW5nIGhhaSB04bu3IGzhu4cgYuG6sW5nIG5oYXUuIE7Ds2kgY8OhY2gga2jDoWMsIGtow7RuZyBjw7MgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogcsO1IHLDoG5nIGNobyB0aOG6pXkgZ2nhu5tpIHTDrW5oIOG6o25oIGjGsOG7n25nIMSR4bq/biBow6BuaCB2aSBtdWEgc+G6o24gcGjhuqltIEZvb2QuIFPhu7Ega2jDoWMgYmnhu4d0IG5o4buPIHF1YW4gc8OhdCDEkcaw4bujYyBjw7MgdGjhu4MgbMOgIGRvIHnhur91IHThu5Egbmfhuqt1IG5oacOqbiB0cm9uZyBt4bqrdS4NCg0KDQojIyMjICoqYikgVOG7tyBz4buRIE5ndXkgY8ahIChSZWxhdGl2ZSBSaXNrIC0gUlIpKioNCg0KYGBge3J9DQpjYXQoIlJSID0gW1AoTXVhIEZvb2QgfCBHZW5kZXI9RildIC8gW1AoTXVhIEZvb2QgfCBHZW5kZXI9TSldXG4iKQ0KaWYgKCFpcy5uYShhKSAmJiBuMSA+IDAgJiYgbjIgPiAwKSB7DQogICAgcDFfRl9idXlzX2Zvb2QgPC0gdGFibGVfR0ZbIkYiLCAiTXVhIEZvb2QiXSAvIHN1bSh0YWJsZV9HRlsiRiIsXSkNCiAgICBwMl9NX2J1eXNfZm9vZCA8LSB0YWJsZV9HRlsiTSIsICJNdWEgRm9vZCJdIC8gc3VtKHRhYmxlX0dGWyJNIixdKQ0KICAgIA0KICAgIGlmIChwMl9NX2J1eXNfZm9vZCA9PSAwKSB7DQogICAgICAgIHJyX0dGX21hbnVhbCA8LSBOQQ0KICAgIH0gZWxzZSB7DQogICAgICAgIHJyX0dGX21hbnVhbCA8LSBwMV9GX2J1eXNfZm9vZCAvIHAyX01fYnV5c19mb29kDQogICAgfQ0KICAgIGNhdChwYXN0ZTAoIlJlbGF0aXZlIFJpc2sgKFJSKSB0w61uaCB0YXkgPSAiLCByb3VuZChycl9HRl9tYW51YWwsIDMpLCAiXG4iKSkNCiAgICANCiAgICBycl9HRl9lcGl0b29sc19vYmogPC0gTlVMTDsgZXJyb3JfaW5fcnJfZXBpIDwtIEZBTFNFDQogICAgdHJ5Q2F0Y2goew0KICAgICAgICBycl9HRl9lcGl0b29sc19vYmogPC0gcmlza3JhdGlvKHRhYmxlX0dGLCBtZXRob2QgPSAid2FsZCIsIHJldiA9ICJiIikgDQogICAgICAgIGNhdCgiXG5L4bq/dCBxdeG6oyBSUiB2w6AgS1RDIDk1JSB04burIGVwaXRvb2xzOjpyaXNrcmF0aW8oKTpcbiIpOyBwcmludChycl9HRl9lcGl0b29sc19vYmokbWVhc3VyZSkNCiAgICAgICAgDQogICAgICAgICMgU+G7rEEg4bufIMSRw6J5OiBkw7luZyBow6BuZyBsw6AgIkYiLCBraMO0bmcgcGjhuqNpICJyaXNrIHJhdGlvIg0KICAgICAgICBycl9lc3RpbWF0ZV9nZl9lcGkgPC0gcnJfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAiZXN0aW1hdGUiXQ0KICAgICAgICBycl9sb3dlcl9nZl9lcGkgICAgPC0gcnJfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAibG93ZXIiXQ0KICAgICAgICBycl91cHBlcl9nZl9lcGkgICAgPC0gcnJfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAidXBwZXIiXQ0KICAgICAgICANCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgZXJyb3JfaW5fcnJfZXBpIDwtIFRSVUUNCiAgICAgICAgcnJfZXN0aW1hdGVfZ2ZfZXBpIDwtIHJyX2xvd2VyX2dmX2VwaSA8LSBycl91cHBlcl9nZl9lcGkgPC0gTkENCiAgICB9KQ0KICAgIA0KICAgIGlmICghZXJyb3JfaW5fcnJfZXBpICYmICFpcy5uYShycl9lc3RpbWF0ZV9nZl9lcGkpKSB7DQogICAgICAgIGNhdChwYXN0ZTAoIlJSIChlcGl0b29scykgPSAiLCByb3VuZChycl9lc3RpbWF0ZV9nZl9lcGksIDMpLA0KICAgICAgICAgICAgICAgICAgICIsIEtUQyA5NSU6ICgiLCByb3VuZChycl9sb3dlcl9nZl9lcGksIDMpLCAiLCAiLCByb3VuZChycl91cHBlcl9nZl9lcGksIDMpLCAiKS5cbiIpKQ0KICAgIH0gZWxzZSB7DQogICAgICAgIGNhdCgiS2jDtG5nIHRo4buDIGzhuqV5IFJSIHThu6sgZXBpdG9vbHMuXG4iKQ0KICAgIH0NCn0gZWxzZSB7DQogICAgY2F0KCJLaMO0bmcgdGjhu4MgdMOtbmggUmVsYXRpdmUgUmlzay5cbiIpDQp9DQoNCmBgYA0KDQpE4buxYSB0csOqbiBr4bq/dCBxdeG6oyBwaMOibiB0w61jaCwgdOG7tyBz4buRIHLhu6dpIHJvIChSaXNrIFJhdGlvIOKAkyBSUikgZ2nhu69hIGhhaSBnaeG7m2kgdMOtbmggxJHhu5FpIHbhu5tpIGjDoG5oIHZpIG11YSBz4bqjbiBwaOG6qW0gRm9vZCBsw6AgMC45ODkuIMSQaeG7gXUgbsOgeSBjw7MgbmdoxKlhIGzDoCB04bu3IGzhu4cgbuG7ryBnaeG7m2kgbXVhIHPhuqNuIHBo4bqpbSBGb29kIHRo4bqlcCBoxqFuIGtob+G6o25nIDEuMSUgc28gduG7m2kgbmFtIGdp4bubaS4gVHV5IG5oacOqbiwga2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gUlIgbMOgIHThu6sgMC45NjkgxJHhur9uIDEuMDA5IOKAkyB0cm9uZyDEkcOzIGPDsyBjaOG7qWEgZ2nDoSB0cuG7iyAxLiDEkGnhu4F1IG7DoHkgY2hvIHRo4bqleSBz4buxIGtow6FjIGJp4buHdCBxdWFuIHPDoXQgxJHGsOG7o2Mga2jDtG5nIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouDQoNCk7Ds2kgY8OhY2gga2jDoWMsIGtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgxJHhu4Mga2jhurNuZyDEkeG7i25oIHLhurFuZyBnaeG7m2kgdMOtbmgg4bqjbmggaMaw4bufbmcgxJHhur9uIGjDoG5oIHZpIG11YSBz4bqjbiBwaOG6qW0gRm9vZC4gU+G7sSBjaMOqbmggbOG7h2NoIG5o4buPIG7DoHkgKGNo4buJIGtob+G6o25nIDElKSBjw7MgdGjhu4MgaG/DoG4gdG/DoG4gbMOgIGRvIHnhur91IHThu5Egbmfhuqt1IG5oacOqbi4gVuG7gSBt4bq3dCB0aOG7sWMgdGnhu4VuLCBt4buZdCBSUiBn4bqnbiBi4bqxbmcgMSBjw7luZyB24bubaSBraG/huqNuZyB0aW4gY+G6rXkgaOG6uXAgdsOgIGJhbyBn4buTbSAxIGNobyB0aOG6pXkgbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2EgZ2nhu5tpIHTDrW5oIHbDoCBow6BuaCB2aSBtdWEgaMOgbmcgbMOgIGtow7RuZyDEkcOhbmcga+G7gy4gVsOsIHbhuq15LCBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIHLhurFuZyBnaeG7m2kgdMOtbmgga2jDtG5nIHBo4bqjaSBsw6AgbeG7mXQgeeG6v3UgdOG7kSBjw7Mg4bqjbmggaMaw4bufbmcgcsO1IHLhu4d0IMSR4bq/biB2aeG7h2MgbXVhIHPhuqNuIHBo4bqpbSBGb29kIHRyb25nIHThuq1wIGThu68gbGnhu4d1IG7DoHkuDQoNCg0KDQojIyMjICoqYykgVOG7tyBz4buRIENow6puaCAoT2RkcyBSYXRpbyAtIE9SKSoqDQoNCmBgYHtyfQ0KaWYgKCFpcy5uYShhKSAmJiBuMSA+IDAgJiYgbjIgPiAwICYmIGIgPiAwICYmIGQgPiAwKSB7DQogICAgb2Rkc19GX2J1eXNfZm9vZCA8LSBhIC8gYg0KICAgIG9kZHNfTV9idXlzX2Zvb2QgPC0gYyAvIGQNCg0KICAgIG9yX0dGX21hbnVhbCA8LSAoYSAqIGQpIC8gKGIgKiBjKQ0KICAgIGNhdChwYXN0ZTAoIk9kZHMgUmF0aW8gKE9SKSA9ICIsIHJvdW5kKG9yX0dGX21hbnVhbCwgMyksICJcbiIpKQ0KDQogICAgb3JfR0ZfZXBpdG9vbHNfb2JqIDwtIE5VTEwNCiAgICBlcnJvcl9pbl9vcl9lcGkgPC0gRkFMU0UNCiAgICB0cnlDYXRjaCh7DQogICAgICAgIG9yX0dGX2VwaXRvb2xzX29iaiA8LSBvZGRzcmF0aW8odGFibGVfR0YsIG1ldGhvZCA9ICJ3YWxkIiwgcmV2ID0gImIiKQ0KICAgICAgICBvcl9lc3RpbWF0ZV9nZl9lcGkgPC0gb3JfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAiZXN0aW1hdGUiXQ0KICAgICAgICBvcl9sb3dlcl9nZl9lcGkgICAgPC0gb3JfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAibG93ZXIiXQ0KICAgICAgICBvcl91cHBlcl9nZl9lcGkgICAgPC0gb3JfR0ZfZXBpdG9vbHNfb2JqJG1lYXN1cmVbIkYiLCAidXBwZXIiXQ0KICAgICAgICBjYXQocGFzdGUwKCJPUiAoZXBpdG9vbHMpID0gIiwgcm91bmQob3JfZXN0aW1hdGVfZ2ZfZXBpLCAzKSwNCiAgICAgICAgICAgICAgICAgICAiLCBLVEMgOTUlOiAoIiwgcm91bmQob3JfbG93ZXJfZ2ZfZXBpLCAzKSwgIiwgIiwgcm91bmQob3JfdXBwZXJfZ2ZfZXBpLCAzKSwgIilcbiIpKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSl7DQogICAgICAgIGNhdCgiTOG7l2kga2hpIHTDrW5oIE9SIGLhurFuZyBlcGl0b29sczogIiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgIH0pDQp9IGVsc2Ugew0KICAgIGNhdCgiS2jDtG5nIHRo4buDIHTDrW5oIE9kZHMgUmF0aW8uXG4iKQ0KfQ0KDQpgYGANCg0KROG7sWEgdHLDqm4ga+G6v3QgcXXhuqMgcGjDom4gdMOtY2gsIE9kZHMgUmF0aW8gKE9SKSBnaeG7r2EgaGFpIGdp4bubaSB0w61uaCDEkeG7kWkgduG7m2kgaMOgbmggdmkgbXVhIHPhuqNuIHBo4bqpbSBGb29kIGzDoCAwLjk2LiDEkGnhu4F1IG7DoHkgY8OzIG5naMSpYSBsw6Aga2jhuqMgbsSDbmcgbuG7ryBnaeG7m2kgbXVhIHPhuqNuIHBo4bqpbSBGb29kIChzbyB24bubaSBraMO0bmcgbXVhKSB0aOG6pXAgaMahbiBraG/huqNuZyA0JSBzbyB24bubaSBuYW0gZ2nhu5tpLiBUdXkgbmhpw6puLCBraG/huqNuZyB0aW4gY+G6rXkgOTUlIGPhu6dhIE9SIGzDoCB04burIDAuODkxIMSR4bq/biAxLjAzMyDigJQgYmFvIGfhu5NtIGdpw6EgdHLhu4sgMS4NCg0KxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgc+G7sSBraMOhYyBiaeG7h3QgduG7gSBvZGRzIGdp4buvYSBuYW0gdsOgIG7hu68ga2jDtG5nIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouIEtob+G6o25nIHRpbiBj4bqteSBjaOG7qWEgZ2nDoSB0cuG7iyAxIG5naMSpYSBsw6AgdGEga2jDtG5nIHRo4buDIGLDoWMgYuG7jyBnaeG6oyB0aHV54bq/dCBy4bqxbmcgZ2nhu5tpIHTDrW5oIGtow7RuZyDhuqNuaCBoxrDhu59uZyDEkeG6v24ga2jhuqMgbsSDbmcgbXVhIEZvb2QuIETDuSBPUiA8IDEgZ+G7o2kgw70gcuG6sW5nIG7hu68gZ2nhu5tpIGPDsyB0aOG7gyBtdWEgw610IGjGoW4sIG5oxrBuZyBt4bupYyBjaMOqbmggbOG7h2NoIG7DoHkgbMOgIG5o4buPIHbDoCBraMO0bmcgxJHhu6cgbeG6oW5oIMSR4buDIGvhur90IGx14bqtbiBjw7Mgc+G7sSBraMOhYyBiaeG7h3QgdGjhu7FjIHPhu7EuIFbDrCB24bqteSwgeMOpdCBj4bqjIHbhu4EgdGjhu5FuZyBrw6ogdsOgIHRo4buxYyB0aeG7hW4sIGtow7RuZyBjw7MgYuG6sW5nIGNo4bupbmcgdGh1eeG6v3QgcGjhu6VjIHLhurFuZyBnaeG7m2kgdMOtbmgg4bqjbmggaMaw4bufbmcgxJHhur9uIGjDoG5oIHZpIG11YSBz4bqjbiBwaOG6qW0gRm9vZC4NCg0KDQoNCg0KDQoNCg0KDQoNCg0K