Thống kê mô tả

Đọc dữ liệu

#xuất hiện câu lệnh, không xuất hiện kết quả 
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.3.3
## Warning: package 'ggplot2' was built under R version 4.3.3
## Warning: package 'tidyr' was built under R version 4.3.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(DT)
## Warning: package 'DT' was built under R version 4.3.3
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(psych)
## Warning: package 'psych' was built under R version 4.3.3
## 
## Attaching package: 'psych'
## 
## The following objects are masked from 'package:scales':
## 
##     alpha, rescale
## 
## The following objects are masked from 'package:ggplot2':
## 
##     %+%, alpha
library(knitr)
## Warning: package 'knitr' was built under R version 4.3.3
library(kableExtra)
## Warning: package 'kableExtra' was built under R version 4.3.3
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
d <- read.csv('D:/HK2_PTDLDT_TMT/Supermarket Transactions.csv',header = T)
cat<- c("MaritalStatus", "Homeowner", "City", "StateorProvince", "Country", "ProductFamily", "ProductDepartment", "ProductCategory")
d_cat<-d[, cat] #lấy tất cả các hàng và các biến trong cat
datatable(d_cat,options = list(scrollX = TRUE))
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html

2.1 Biến Gender

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender được kết quả như sau

Gender_1<-table(d_cat$Gender)
Gender_1
## < table of extent 0 >
Gender_2<-table(d$Gender)/sum(nrow(d))*100 #tần suất
Gender_2
## 
##        F        M 
## 50.99936 49.00064
pie(Gender_2,
labels = paste(names(Gender_2), round(Gender_2, 2), "%"),
main = "Tỷ lệ giới tính")

Nhận xét

  • Biểu đồ thể hiện số lượng khách hàng theo giới tính gồm hai nhóm: F (nữ) và M (nam).

  • Số lượng khách hàng nữ (F) nhỉnh hơn so với khách hàng nam (M), tuy nhiên sự chênh lệch không lớn cho thấy siêu thị thu hút khách hàng từ cả hai nhóm giới tính.Điều này cho thấy dữ liệu có tính đại diện giới tương đối tốt, tránh được thiên lệch giới tính.

2.2 Biến MaritalStatus

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến MaritalStatus được kết quả như sau:

MaritalStatusr_1<-table(d_cat$MaritalStatus)
MaritalStatusr_1
## 
##    M    S 
## 6866 7193
MaritalStatus_2<-table(d_cat$MaritalStatus)/sum(nrow(d_cat))*100 
MaritalStatus_2
## 
##        M        S 
## 48.83704 51.16296

Trực quan hóa dữ liệu

c1 <- as.data.frame(table(d_cat$MaritalStatus))
colnames(c1) <- c("MaritalStatus", "Count")
ggplot(c1, aes(x = MaritalStatus, y = Count, fill = MaritalStatus)) +
geom_bar(stat = "identity") +
labs(title = "Biểu đồ biểu thị tình trạng hôn nhân khách hàng", x = "Tinh trạng hôn nhân", y = "Số lượng") +
theme_minimal()

Nhận xét

Kết quả cho thấy biến MaritalStatus có trạng thái hôn nhân độc thân (S) chiếm tỷ lệ cao hơn một chút với 51.16% tổng thể, tương ứng 7193 người. Trong khi đó, nhóm đã kết hôn (M) chiếm 48.84%, tương ứng 6866 người. Như vậy, dân số trong mẫu được phân bố tương đối đều giữa hai nhóm độc thân và đã kết hôn, với số người độc thân nhỉnh hơn một chút. Điều này giúp cân bằng trong các phân tích liên quan đến trạng thái hôn nhân.

2.3 Biến Homeowner

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Homeowner được kết quả như sau:

Homeowner_1<-table(d_cat$Homeowner)
Homeowner_1
## 
##    N    Y 
## 5615 8444
Homeowner_2<-table(d$Homeowner)/sum(nrow(d))*100#tần suất
Homeowner_2
## 
##        N        Y 
## 39.93883 60.06117

Trực quan hóa dữ liệu

pie(Homeowner_2,
labels = paste(names(Homeowner_2), round(Homeowner_2, 2), "%"),
main = "Tỷ lệ có hoặc không sở hữu nhà")

Nhận xét

Dựa vào kết quả thống kê, trong tổng số quan sát, có 8,444 người là chủ sở hữu nhà (Homeowner = Y) chiếm khoảng 60.06% tổng thể, trong khi 5,615 người không phải là chủ sở hữu nhà chiếm khoảng 39.94%. Điều này cho thấy đa số khách hàng trong dữ liệu là những người sở hữu nhà, điều này có thể ảnh hưởng tích cực đến hành vi tiêu dùng hoặc khả năng chi trả của họ trong các phân tích tiếp theo.

2.4 Biến AnnualIncome

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến AnnualIncome được kết quả như sau:

AnnualIncome_1<-table(d_cat$AnnualIncome)
AnnualIncome_1
## < table of extent 0 >
AnnualIncome_2<-table(d$AnnualIncome)/sum(nrow(d))*100#tần suất
AnnualIncome_2
## 
##   $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

Trực quan hóa dữ liệu

c4_1 <- d |>
  group_by(AnnualIncome) |>
  summarise(freq = n()) |>
  mutate(per = freq / sum(freq),
         label = paste0(freq, " (", round(per * 100, 1), "%)"))

ggplot(c4_1 , aes(x = "", y = per, fill = AnnualIncome)) +
  geom_col(width = 1, color = "white") +
  coord_polar("y") +
  geom_text(aes(label = paste0(round(per*100, 2), "%")),
            position = position_stack(vjust = 0.5),
            size = 2.5) +
  scale_fill_brewer(palette = "Pastel2") +  # Bạn có thể chọn Set1, Set2...
  labs(title = "Tỷ lệ thu nhập hằng năm của khách hàng", fill = "Thu nhập") +
  theme_void()

Nhận xét

Nhóm thu nhập $30K - $50K chiếm hơn 1/3 tổng số quan sát với 32.73%, là nhóm đông nhất trong dữ liệu. Khi kết hợp với nhóm $10K - $30K, tổng tỷ lệ lên đến khoảng 55%, cho thấy đa số khách hàng thuộc nhóm có thu nhập trung bình hoặc thấp, phản ánh mức thu nhập phổ biến trong mẫu. Ngược lại, nhóm thu nhập cao trên $90K, bao gồm các khoảng $90K - $110K, $110K - $130K, $130K - $150K và $150K+, chỉ chiếm khoảng 16.28% tổng số, thể hiện nhóm thu nhập cao tương đối nhỏ. Kết quả này cho thấy phần lớn các đơn mua hàng được thực hiện bởi khách hàng có mức thu nhập trung bình hoặc thấp.

2.5 Biến City

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến City được kết quả như sau:

City_1<-table(d$City)
City_1
## 
##      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
City_2<-table(d$City)/sum(nrow(d))*100#tần suất
City_2
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##     2.7242336     1.0171420     5.7685468     5.9321431     3.2150224 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##     0.5334661     6.0103848     6.5865282     4.6518245     1.3798990 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##     3.3003770     6.2308841     9.8584537     4.4170994     6.1597553 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##     0.9246746     6.5580767     6.2237712     8.9408920     4.5024539 
##      Victoria   Walla Walla        Yakima 
##     1.2518671     1.1380610     2.6744434

Trực quan hóa dữ liệu

c5 <- as.data.frame(City_1)
colnames(c5) <- c("City", "Count")
ggplot(c5, aes(x = City, y = Count, fill = City)) +
  geom_col(fill = "lightgreen", color = "black") +
  labs(x = "Thành phố", y = "Tần số", title = "Biểu đồ tần số khách hàng ở các thành phố") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét

Dựa trên dữ liệu, địa điểm Salem có tần số cao nhất với 1386 với 9.86% tổng thể, tiếp theo là Tacoma và Los Angeles cũng có chỉ số rất lớn, lần lượt nằm trong nhóm cao nhì. Ngược lại, các thành phố như Mexico City, Guadalajara và San Francisco có số lượng khách thấp nhất, cho thấy sự khác biệt rõ rệt về mức độ hoặc chỉ số giữa các khu vực. Nhìn chung, có sự phân bố không đồng đều của các các thành phố.

2.6 Biến StateorProvince

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến StateorProvince được kết quả như sau:

StateorProvince_1<- table(d$StateorProvince)
StateorProvince_1
## 
##        BC        CA        DF  Guerrero   Jalisco        OR  Veracruz        WA 
##       809      2733       815       383        75      2262       464      4567 
##   Yucatan Zacatecas 
##       654      1297
StateorProvince_2<-table(d$StateorProvince)/sum(nrow(d))*100 #tần suất
StateorProvince_2
## 
##         BC         CA         DF   Guerrero    Jalisco         OR   Veracruz 
##  5.7543211 19.4395049  5.7969984  2.7242336  0.5334661 16.0893378  3.3003770 
##         WA    Yucatan  Zacatecas 
## 32.4845295  4.6518245  9.2254072

Trực quan hóa dữ liệu

c6 <- as.data.frame(StateorProvince_1)
colnames(c6) <- c("StateorProvince", "Count")
ggplot(c6, aes(x = StateorProvince, y = Count, fill = City)) +
  geom_col(fill = "skyblue", color = "black") +
  labs(x = "Bang", y = "Tần số", title = "Biểu đồ tần số khách hàng ở các bang") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

c6_1 <- d |>
  group_by(StateorProvince) |>
  summarise(freq = n()) |>
  mutate(per = freq / sum(freq),
         label = paste0(freq, " (", round(per * 100, 1), "%)"))

ggplot(c6_1 , aes(x = "", y = per, fill = StateorProvince)) +
  geom_col(width = 1, color = "white") +
  coord_polar("y") +
  geom_text(aes(label = paste0(round(per*100, 2), "%")),
            position = position_stack(vjust = 0.5),
            size = 2.5) +
  scale_fill_brewer(palette = "Pastel1") +  # Bạn có thể chọn Set1, Set2...
  labs(title = "Tỷ lệ mua hàng ở các ban", fill = "Bang") +
  theme_void()

Nhận xét

Dữ liệu này cho thấy sự phân bố địa lý rất rõ ràng, với ba bang WA (Washington), CA (California) và OR (Oregon) thống trị tuyệt đối – lần lượt chiếm 32.48%, 19.44% và 16.09%. Ba bang này, đều nằm ở khu vực Tây Bắc Thái Bình Dương của Hoa Kỳ, cộng lại chiếm tới 68.01% tổng dữ liệu.

Trong khi đó, các bang của Mexico như DF, Guerrero, Jalisco, Veracruz, Yucatan và Zacatecas chỉ chiếm tỷ trọng nhỏ hơn, tổng cộng chỉ chiếm khoảng 26.23% dữ liệu.

Như vậy, dữ liệu cho thấy sự thiên lệch mạnh mẽ về mặt địa lý, chủ yếu tập trung vào ba bang phía Tây Bắc Hoa Kỳ, trong khi các tỉnh của Mexico chỉ góp phần khiêm tốn.

2.7 Biến Country

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Country được kết quả như sau:

Country_1<- table(d$Country)
Country_1
## 
## Canada Mexico    USA 
##    809   3688   9562
Country_2<-table(d$Country)/sum(nrow(d))*100
Country_2
## 
##    Canada    Mexico       USA 
##  5.754321 26.232307 68.013372

Trực quan hóa dữ liệu

c7 <- as.data.frame(Country_1)
colnames(c7) <- c("Country", "Count")
ggplot(c7, aes(x = Country, y = Count, fill = Country)) +
  geom_col(fill = "#07575B", color = "black") +
  labs(x = "Quốc gia", y = "Tần số", title = "Biểu đồ tần số khách hàng ở các quốc gia") +
  theme(axis.text.x = element_text(angle = 0, hjust = 1))

c7_1 <- d |>
  group_by(Country) |>
  summarise(freq = n()) |>
  mutate(per = freq / sum(freq),
         label = paste0(freq, " (", round(per * 100, 1), "%)"))

ggplot(c7_1 , aes(x = "", y = per, fill = Country)) +
  geom_col(width = 1, color = "white") +
  coord_polar("y") +
  geom_text(aes(label = paste0(round(per*100, 2), "%")),
            position = position_stack(vjust = 0.5),
            size = 2.5) +
  scale_fill_brewer(palette = "Spectral") +  # Bạn có thể chọn Set1, Set2...
  labs(title = "Tỷ lệ ở các quốc gia", fill = "Quốc gia") +
  theme_void()

Nhận xét

Kết quả tần số cho biến Country cho thấy rằng phần lớn khách hàng (68%) đến từ Mỹ (USA), trong khi Canada và Mexico chiếm tỷ lệ lần lượt là khoảng 5.75% và 26.23%. Điều này cho thấy sự phân bổ không đồng đều của dữ liệu giữa ba quốc gia, với Mỹ là thị trường trọng tâm.

2.8 Biến ProductFamily

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến ProductFamily được kết quả như sau:

ProductFamily_1<- table(d$ProductFamily)
ProductFamily_1
## 
##          Drink           Food Non-Consumable 
##           1250          10153           2656
ProductFamily_2<-table(d$ProductFamily)/sum(nrow(d))*100
ProductFamily_2
## 
##          Drink           Food Non-Consumable 
##       8.891102      72.217085      18.891813

Trực quan hóa dữ liệu

c8 <- as.data.frame(ProductFamily_1)
colnames(c8) <- c("ProductFamily", "Count")
ggplot(c8, aes(x = ProductFamily, y = Count, fill = ProductFamily)) +
  geom_col(fill = "lightpink", color = "black") +
  labs(x = "Sản phẩm gia đình", y = "Tần số", title = "Biểu đồ biểu thị nhóm sản phẩm khách hàng mua") +
  theme(axis.text.x = element_text(angle = 0, hjust = 1))

Nhận xét

Kết quả tần số cho thấy nhóm thực phẩm (Food) chiếm áp đảo với hơn 72% tổng số mặt hàng, trong khi đồ uống (Drink) chỉ chiếm gần 9% và các mặt hàng phi thực phẩm (Non-Consumable) chiếm khoảng 19%. Sự chênh lệch này phản ánh rõ tầm quan trọng và nhu cầu cao của thực phẩm trong danh sách mua sắm hàng ngày, so với các loại đồ uống và sản phẩm phi thực phẩm khác.

2.9 Biến ProductDepartment

Thống kê tần suất

Ta thực hiện thống kê tần số và tần suất cho biến ProductDepartment được kết quả như sau:

ProductDepartment_1<- table(d$ProductDepartment)
ProductDepartment_1
## 
## 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
ProductDepartment_2<-table(d$ProductDepartment)/sum(nrow(d))*100
ProductDepartment_2
## 
## 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

Trực quan hóa dữ liệu

c9 <- as.data.frame(ProductDepartment_1)
colnames(c9) <- c("ProductDepartment", "Count")
ggplot(c9, aes(x = ProductDepartment, y = Count, fill = ProductDepartment)) +
  geom_col(fill = "lightpink", color = "black") +
  labs(x = "Sản phẩm", y = "Tần số", title = "Bộ phận sản phẩm khách hàng mua") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét

Dữ liệu cho thấy sự phân bổ rõ rệt trong thói quen mua sắm: các mặt hàng tươi sống và đồ gia dụng chiếm phần lớn, với Produce (14.18%) và Household (10.1%) cộng lại gần 25% tổng số mặt hàng. Điều này phản ánh sự ưu tiên tiêu dùng các sản phẩm thiết yếu hàng ngày.

Ngược lại, sự thiếu cân đối thể hiện ở các nhóm như Meat và Seafood, khi chỉ chiếm chưa tới 1% tổng số mặt hàng, cho thấy nhu cầu của các mặt hàng này ít hơn đáng kể so với các nhóm khác. Điều này có thể xuất phát từ thói quen ăn uống hoặc đặc điểm nguồn cung cấp của cửa hàng.

2.10 Biến ProductCategory

Thống tần suất

Ta thực hiện thống kê tần số và tần suất cho biến ProductCategory được kết quả như sau:

ProductCategory_1<- table(d$ProductCategory)
ProductCategory_1
## 
##         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
ProductCategory_2<-table(d$ProductCategory)/sum(nrow(d))*100
ProductCategory_2
## 
##         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

Trực quan hóa dữ liệu

c10 <- as.data.frame(ProductCategory_1)
colnames(c10) <- c("ProductCategory", "Count")

ggplot(c10, aes(x = ProductCategory, y = Count, fill = ProductCategory)) +
  geom_col(fill = "lightblue", color = "black") +
  labs(x = "Sản phẩm", y = "Tần số", title = "Danh mục sản phẩm khách hàng mua") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét

Dữ liệu tần số của các danh mục sản phẩm cho thấy rằng các mặt hàng thiết yếu và phổ biến nhất như rau củ (1.728), đồ ăn vặt (1.600), sữa (903), trái cây (765) và thịt (761) có tần suất cao nhất. Điều này phản ánh nhu cầu sử dụng hằng ngày của các gia đình. Ngược lại, các sản phẩm như cá cơm đóng hộp (44), hàu đóng hộp (35), tôm đóng hộp (38) và nến (45) lại có tần suất thấp nhất, cho thấy đây là những mặt hàng ít phổ biến hoặc chỉ được sử dụng trong những trường hợp nhất định. Tóm lại, sự phân bố tần số này phản ánh rõ ràng xu hướng tiêu dùng và mức độ phổ biến của từng loại sản phẩm.

LS0tDQp0aXRsZTogIkLDoGkgdOG6rXAgdHLDqm4gbOG7m3AiDQphdXRob3I6ICJRdeG7s25oIEjGsMahbmciDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclSDolTTolUywgJWQgLSAlbSAtICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlICANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojICoqVGjhu5FuZyBrw6ogbcO0IHThuqMqKg0KDQojIyAqKsSQ4buNYyBk4buvIGxp4buHdSoqDQoNCmBgYHtyfSANCiN4deG6pXQgaGnhu4duIGPDonUgbOG7h25oLCBraMO0bmcgeHXhuqV0IGhp4buHbiBr4bq/dCBxdeG6oyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KHBzeWNoKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmQgPC0gcmVhZC5jc3YoJ0Q6L0hLMl9QVERMRFRfVE1UL1N1cGVybWFya2V0IFRyYW5zYWN0aW9ucy5jc3YnLGhlYWRlciA9IFQpDQpgYGANCmBgYHtyfQ0KY2F0PC0gYygiTWFyaXRhbFN0YXR1cyIsICJIb21lb3duZXIiLCAiQ2l0eSIsICJTdGF0ZW9yUHJvdmluY2UiLCAiQ291bnRyeSIsICJQcm9kdWN0RmFtaWx5IiwgIlByb2R1Y3REZXBhcnRtZW50IiwgIlByb2R1Y3RDYXRlZ29yeSIpDQpkX2NhdDwtZFssIGNhdF0gI2zhuqV5IHThuqV0IGPhuqMgY8OhYyBow6BuZyB2w6AgY8OhYyBiaeG6v24gdHJvbmcgY2F0DQpkYXRhdGFibGUoZF9jYXQsb3B0aW9ucyA9IGxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQ0KYGBgDQoNCg0KIyMgKioyLjEgQmnhur9uIEdlbmRlcioqDQoNCipUaOG7kW5nIGvDqiB04bqnbiBzdeG6pXQqIA0KIA0KVGEgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY2hvIGJp4bq/biBHZW5kZXIgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXUNCg0KYGBge3J9DQpHZW5kZXJfMTwtdGFibGUoZF9jYXQkR2VuZGVyKQ0KR2VuZGVyXzENCmBgYA0KYGBge3J9DQpHZW5kZXJfMjwtdGFibGUoZCRHZW5kZXIpL3N1bShucm93KGQpKSoxMDAgI3ThuqduIHN14bqldA0KR2VuZGVyXzINCmBgYA0KDQoNCg0KYGBge3J9DQpwaWUoR2VuZGVyXzIsDQpsYWJlbHMgPSBwYXN0ZShuYW1lcyhHZW5kZXJfMiksIHJvdW5kKEdlbmRlcl8yLCAyKSwgIiUiKSwNCm1haW4gPSAiVOG7tyBs4buHIGdp4bubaSB0w61uaCIpDQpgYGANCg0KKk5o4bqtbiB4w6l0Kg0KDQotIEJp4buDdSDEkeG7kyB0aOG7gyBoaeG7h24gc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgdGhlbyBnaeG7m2kgdMOtbmggZ+G7k20gaGFpIG5ow7NtOiBGIChu4buvKSB2w6AgTSAobmFtKS4NCg0KLSBT4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBu4buvIChGKSBuaOG7iW5oIGjGoW4gc28gduG7m2kga2jDoWNoIGjDoG5nIG5hbSAoTSksIHR1eSBuaGnDqm4gc+G7sSBjaMOqbmggbOG7h2NoIGtow7RuZyBs4bubbiBjaG8gdGjhuqV5IHNpw6p1IHRo4buLIHRodSBow7p0IGtow6FjaCBow6BuZyB04burIGPhuqMgaGFpIG5ow7NtIGdp4bubaSB0w61uaC7EkGnhu4F1IG7DoHkgY2hvIHRo4bqleSBk4buvIGxp4buHdSBjw7MgdMOtbmggxJHhuqFpIGRp4buHbiBnaeG7m2kgdMawxqFuZyDEkeG7kWkgdOG7kXQsIHRyw6FuaCDEkcaw4bujYyB0aGnDqm4gbOG7h2NoIGdp4bubaSB0w61uaC4NCg0KIyMgKioyLjIgQmnhur9uIE1hcml0YWxTdGF0dXMqKg0KDQoqVGjhu5FuZyBrw6ogdOG6p24gc3XhuqV0Kg0KDQpUYSB0aOG7sWMgaGnhu4duIHRo4buRbmcga8OqIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBjaG8gYmnhur9uIE1hcml0YWxTdGF0dXMgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KTWFyaXRhbFN0YXR1c3JfMTwtdGFibGUoZF9jYXQkTWFyaXRhbFN0YXR1cykNCk1hcml0YWxTdGF0dXNyXzENCmBgYA0KYGBge3J9DQpNYXJpdGFsU3RhdHVzXzI8LXRhYmxlKGRfY2F0JE1hcml0YWxTdGF0dXMpL3N1bShucm93KGRfY2F0KSkqMTAwIA0KTWFyaXRhbFN0YXR1c18yDQpgYGANCg0KKlRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UqDQoNCmBgYHtyfQ0KYzEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShkX2NhdCRNYXJpdGFsU3RhdHVzKSkNCmNvbG5hbWVzKGMxKSA8LSBjKCJNYXJpdGFsU3RhdHVzIiwgIkNvdW50IikNCmdncGxvdChjMSwgYWVzKHggPSBNYXJpdGFsU3RhdHVzLCB5ID0gQ291bnQsIGZpbGwgPSBNYXJpdGFsU3RhdHVzKSkgKw0KZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCmxhYnModGl0bGUgPSAiQmnhu4N1IMSR4buTIGJp4buDdSB0aOG7iyB0w6xuaCB0cuG6oW5nIGjDtG4gbmjDom4ga2jDoWNoIGjDoG5nIiwgeCA9ICJUaW5oIHRy4bqhbmcgaMO0biBuaMOibiIsIHkgPSAiU+G7kSBsxrDhu6NuZyIpICsNCnRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCipOaOG6rW4geMOpdCoNCg0KS+G6v3QgcXXhuqMgY2hvIHRo4bqleSBiaeG6v24gTWFyaXRhbFN0YXR1cyBjw7MgdHLhuqFuZyB0aMOhaSBow7RuIG5ow6JuIMSR4buZYyB0aMOibiAoUykgY2hp4bq/bSB04bu3IGzhu4cgY2FvIGjGoW4gbeG7mXQgY2jDunQgduG7m2kgNTEuMTYlIHThu5VuZyB0aOG7gywgdMawxqFuZyDhu6luZyA3MTkzIG5nxrDhu51pLiBUcm9uZyBraGkgxJHDsywgbmjDs20gxJHDoyBr4bq/dCBow7RuIChNKSBjaGnhur9tIDQ4Ljg0JSwgdMawxqFuZyDhu6luZyA2ODY2IG5nxrDhu51pLiBOaMawIHbhuq15LCBkw6JuIHPhu5EgdHJvbmcgbeG6q3UgxJHGsOG7o2MgcGjDom4gYuG7kSB0xrDGoW5nIMSR4buRaSDEkeG7gXUgZ2nhu69hIGhhaSBuaMOzbSDEkeG7mWMgdGjDom4gdsOgIMSRw6Mga+G6v3QgaMO0biwgduG7m2kgc+G7kSBuZ8aw4budaSDEkeG7mWMgdGjDom4gbmjhu4luaCBoxqFuIG3hu5l0IGNow7p0LiDEkGnhu4F1IG7DoHkgZ2nDunAgY8OibiBi4bqxbmcgdHJvbmcgY8OhYyBwaMOibiB0w61jaCBsacOqbiBxdWFuIMSR4bq/biB0cuG6oW5nIHRow6FpIGjDtG4gbmjDom4uDQoNCiMjICoqMi4zIEJp4bq/biBIb21lb3duZXIqKg0KDQoqVGjhu5FuZyBrw6ogdOG6p24gc3XhuqV0Kg0KDQpUYSB0aOG7sWMgaGnhu4duIHRo4buRbmcga8OqIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBjaG8gYmnhur9uIEhvbWVvd25lciAgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KSG9tZW93bmVyXzE8LXRhYmxlKGRfY2F0JEhvbWVvd25lcikNCkhvbWVvd25lcl8xDQpgYGANCg0KYGBge3J9DQpIb21lb3duZXJfMjwtdGFibGUoZCRIb21lb3duZXIpL3N1bShucm93KGQpKSoxMDAjdOG6p24gc3XhuqV0DQpIb21lb3duZXJfMg0KYGBgDQoNCipUcuG7sWMgcXVhbiBow7NhIGThu68gbGnhu4d1Kg0KDQpgYGB7cn0NCnBpZShIb21lb3duZXJfMiwNCmxhYmVscyA9IHBhc3RlKG5hbWVzKEhvbWVvd25lcl8yKSwgcm91bmQoSG9tZW93bmVyXzIsIDIpLCAiJSIpLA0KbWFpbiA9ICJU4bu3IGzhu4cgY8OzIGhv4bq3YyBraMO0bmcgc+G7nyBo4buvdSBuaMOgIikNCmBgYA0KDQoqTmjhuq1uIHjDqXQqDQoNCkThu7FhIHbDoG8ga+G6v3QgcXXhuqMgdGjhu5FuZyBrw6osIHRyb25nIHThu5VuZyBz4buRIHF1YW4gc8OhdCwgY8OzIDgsNDQ0IG5nxrDhu51pIGzDoCBjaOG7pyBz4bufIGjhu691IG5ow6AgKEhvbWVvd25lciA9IFkpIGNoaeG6v20ga2hv4bqjbmcgNjAuMDYlIHThu5VuZyB0aOG7gywgdHJvbmcga2hpIDUsNjE1IG5nxrDhu51pIGtow7RuZyBwaOG6o2kgbMOgIGNo4bunIHPhu58gaOG7r3UgbmjDoCBjaGnhur9tIGtob+G6o25nIDM5Ljk0JS4gxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgxJFhIHPhu5Ega2jDoWNoIGjDoG5nIHRyb25nIGThu68gbGnhu4d1IGzDoCBuaOG7r25nIG5nxrDhu51pIHPhu58gaOG7r3UgbmjDoCwgxJFp4buBdSBuw6B5IGPDsyB0aOG7gyDhuqNuaCBoxrDhu59uZyB0w61jaCBj4buxYyDEkeG6v24gaMOgbmggdmkgdGnDqnUgZMO5bmcgaG/hurdjIGto4bqjIG7Eg25nIGNoaSB0cuG6oyBj4bunYSBo4buNIHRyb25nIGPDoWMgcGjDom4gdMOtY2ggdGnhur9wIHRoZW8uDQoNCiMjICoqMi40IEJp4bq/biBBbm51YWxJbmNvbWUqKg0KDQoqVGjhu5FuZyBrw6ogdOG6p24gc3XhuqV0KiANCg0KVGEgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY2hvIGJp4bq/biBBbm51YWxJbmNvbWUgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KQW5udWFsSW5jb21lXzE8LXRhYmxlKGRfY2F0JEFubnVhbEluY29tZSkNCkFubnVhbEluY29tZV8xDQpgYGANCg0KDQpgYGB7cn0NCkFubnVhbEluY29tZV8yPC10YWJsZShkJEFubnVhbEluY29tZSkvc3VtKG5yb3coZCkpKjEwMCN04bqnbiBzdeG6pXQNCkFubnVhbEluY29tZV8yDQpgYGANCg0KKlRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UqDQoNCmBgYHtyfQ0KYzRfMSA8LSBkIHw+DQogIGdyb3VwX2J5KEFubnVhbEluY29tZSkgfD4NCiAgc3VtbWFyaXNlKGZyZXEgPSBuKCkpIHw+DQogIG11dGF0ZShwZXIgPSBmcmVxIC8gc3VtKGZyZXEpLA0KICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoZnJlcSwgIiAoIiwgcm91bmQocGVyICogMTAwLCAxKSwgIiUpIikpDQoNCmdncGxvdChjNF8xICwgYWVzKHggPSAiIiwgeSA9IHBlciwgZmlsbCA9IEFubnVhbEluY29tZSkpICsNCiAgZ2VvbV9jb2wod2lkdGggPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQocGVyKjEwMCwgMiksICIlIikpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksDQogICAgICAgICAgICBzaXplID0gMi41KSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsgICMgQuG6oW4gY8OzIHRo4buDIGNo4buNbiBTZXQxLCBTZXQyLi4uDQogIGxhYnModGl0bGUgPSAiVOG7tyBs4buHIHRodSBuaOG6rXAgaOG6sW5nIG7Eg20gY+G7p2Ega2jDoWNoIGjDoG5nIiwgZmlsbCA9ICJUaHUgbmjhuq1wIikgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQoqTmjhuq1uIHjDqXQqDQoNCk5ow7NtIHRodSBuaOG6rXAgJDMwSyAtICQ1MEsgY2hp4bq/bSBoxqFuIDEvMyB04buVbmcgc+G7kSBxdWFuIHPDoXQgduG7m2kgMzIuNzMlLCBsw6AgbmjDs20gxJHDtG5nIG5o4bqldCB0cm9uZyBk4buvIGxp4buHdS4gS2hpIGvhur90IGjhu6NwIHbhu5tpIG5ow7NtICQxMEsgLSAkMzBLLCB04buVbmcgdOG7tyBs4buHIGzDqm4gxJHhur9uIGtob+G6o25nIDU1JSwgY2hvIHRo4bqleSDEkWEgc+G7kSBraMOhY2ggaMOgbmcgdGh14buZYyBuaMOzbSBjw7MgdGh1IG5o4bqtcCB0cnVuZyBiw6xuaCBob+G6t2MgdGjhuqVwLCBwaOG6o24gw6FuaCBt4bupYyB0aHUgbmjhuq1wIHBo4buVIGJp4bq/biB0cm9uZyBt4bqrdS4gTmfGsOG7o2MgbOG6oWksIG5ow7NtIHRodSBuaOG6rXAgY2FvIHRyw6puICQ5MEssIGJhbyBn4buTbSBjw6FjIGtob+G6o25nICQ5MEsgLSAkMTEwSywgJDExMEsgLSAkMTMwSywgJDEzMEsgLSAkMTUwSyB2w6AgJDE1MEsrLCBjaOG7iSBjaGnhur9tIGtob+G6o25nIDE2LjI4JSB04buVbmcgc+G7kSwgdGjhu4MgaGnhu4duIG5ow7NtIHRodSBuaOG6rXAgY2FvIHTGsMahbmcgxJHhu5FpIG5o4buPLiBL4bq/dCBxdeG6oyBuw6B5IGNobyB0aOG6pXkgcGjhuqduIGzhu5tuIGPDoWMgxJHGoW4gbXVhIGjDoG5nIMSRxrDhu6NjIHRo4buxYyBoaeG7h24gYuG7n2kga2jDoWNoIGjDoG5nIGPDsyBt4bupYyB0aHUgbmjhuq1wIHRydW5nIGLDrG5oIGhv4bq3YyB0aOG6pXAuDQoNCiMjICoqMi41IEJp4bq/biBDaXR5KioNCg0KKlRo4buRbmcga8OqIHThuqduIHN14bqldCogDQoNClRhIHRo4buxYyBoaeG7h24gdGjhu5FuZyBrw6ogdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGNobyBiaeG6v24gQ2l0eSDEkcaw4bujYyBr4bq/dCBxdeG6oyBuaMawIHNhdToNCg0KYGBge3J9DQpDaXR5XzE8LXRhYmxlKGQkQ2l0eSkNCkNpdHlfMQ0KYGBgDQpgYGB7cn0NCkNpdHlfMjwtdGFibGUoZCRDaXR5KS9zdW0obnJvdyhkKSkqMTAwI3ThuqduIHN14bqldA0KQ2l0eV8yDQpgYGANCg0KKlRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UqDQoNCmBgYHtyfQ0KYzUgPC0gYXMuZGF0YS5mcmFtZShDaXR5XzEpDQpjb2xuYW1lcyhjNSkgPC0gYygiQ2l0eSIsICJDb3VudCIpDQpnZ3Bsb3QoYzUsIGFlcyh4ID0gQ2l0eSwgeSA9IENvdW50LCBmaWxsID0gQ2l0eSkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJsaWdodGdyZWVuIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJUaMOgbmggcGjhu5EiLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSBraMOhY2ggaMOgbmcg4bufIGPDoWMgdGjDoG5oIHBo4buRIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCipOaOG6rW4geMOpdCoNCg0KROG7sWEgdHLDqm4gZOG7ryBsaeG7h3UsIMSR4buLYSDEkWnhu4NtIFNhbGVtIGPDsyB04bqnbiBz4buRIGNhbyBuaOG6pXQgduG7m2kgMTM4NiB24bubaSA5Ljg2JSB04buVbmcgdGjhu4MsIHRp4bq/cCB0aGVvIGzDoCBUYWNvbWEgdsOgIExvcyBBbmdlbGVzIGPFqW5nIGPDsyBjaOG7iSBz4buRIHLhuqV0IGzhu5tuLCBs4bqnbiBsxrDhu6N0IG7hurFtIHRyb25nIG5ow7NtIGNhbyBuaMOsLiBOZ8aw4bujYyBs4bqhaSwgY8OhYyB0aMOgbmggcGjhu5EgbmjGsCBNZXhpY28gQ2l0eSwgR3VhZGFsYWphcmEgdsOgIFNhbiBGcmFuY2lzY28gY8OzIHPhu5EgbMaw4bujbmcga2jDoWNoIHRo4bqlcCBuaOG6pXQsIGNobyB0aOG6pXkgc+G7sSBraMOhYyBiaeG7h3QgcsO1IHLhu4d0IHbhu4EgbeG7qWMgxJHhu5kgaG/hurdjIGNo4buJIHPhu5EgZ2nhu69hIGPDoWMga2h1IHbhu7FjLiBOaMOsbiBjaHVuZywgY8OzIHPhu7EgIHBow6JuIGLhu5Ega2jDtG5nIMSR4buTbmcgxJHhu4F1IGPhu6dhIGPDoWMgY8OhYyB0aMOgbmggcGjhu5EuDQoNCiMjICoqMi42IEJp4bq/biBTdGF0ZW9yUHJvdmluY2UqKg0KDQoqVGjhu5FuZyBrw6ogdOG6p24gc3XhuqV0KiANCg0KVGEgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY2hvIGJp4bq/biBTdGF0ZW9yUHJvdmluY2UgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KU3RhdGVvclByb3ZpbmNlXzE8LSB0YWJsZShkJFN0YXRlb3JQcm92aW5jZSkNClN0YXRlb3JQcm92aW5jZV8xDQpgYGANCg0KYGBge3J9DQpTdGF0ZW9yUHJvdmluY2VfMjwtdGFibGUoZCRTdGF0ZW9yUHJvdmluY2UpL3N1bShucm93KGQpKSoxMDAgI3ThuqduIHN14bqldA0KU3RhdGVvclByb3ZpbmNlXzINCmBgYA0KDQoqVHLhu7FjIHF1YW4gaMOzYSBk4buvIGxp4buHdSoNCg0KYGBge3J9DQpjNiA8LSBhcy5kYXRhLmZyYW1lKFN0YXRlb3JQcm92aW5jZV8xKQ0KY29sbmFtZXMoYzYpIDwtIGMoIlN0YXRlb3JQcm92aW5jZSIsICJDb3VudCIpDQpnZ3Bsb3QoYzYsIGFlcyh4ID0gU3RhdGVvclByb3ZpbmNlLCB5ID0gQ291bnQsIGZpbGwgPSBDaXR5KSkgKw0KICBnZW9tX2NvbChmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh4ID0gIkJhbmciLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSBraMOhY2ggaMOgbmcg4bufIGPDoWMgYmFuZyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpjNl8xIDwtIGQgfD4NCiAgZ3JvdXBfYnkoU3RhdGVvclByb3ZpbmNlKSB8Pg0KICBzdW1tYXJpc2UoZnJlcSA9IG4oKSkgfD4NCiAgbXV0YXRlKHBlciA9IGZyZXEgLyBzdW0oZnJlcSksDQogICAgICAgICBsYWJlbCA9IHBhc3RlMChmcmVxLCAiICgiLCByb3VuZChwZXIgKiAxMDAsIDEpLCAiJSkiKSkNCg0KZ2dwbG90KGM2XzEgLCBhZXMoeCA9ICIiLCB5ID0gcGVyLCBmaWxsID0gU3RhdGVvclByb3ZpbmNlKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDEsIGNvbG9yID0gIndoaXRlIikgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZChwZXIqMTAwLCAyKSwgIiUiKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgIHNpemUgPSAyLjUpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwxIikgKyAgIyBC4bqhbiBjw7MgdGjhu4MgY2jhu41uIFNldDEsIFNldDIuLi4NCiAgbGFicyh0aXRsZSA9ICJU4bu3IGzhu4cgbXVhIGjDoG5nIOG7nyBjw6FjIGJhbiIsIGZpbGwgPSAiQmFuZyIpICsNCiAgdGhlbWVfdm9pZCgpDQoNCmBgYA0KDQoqTmjhuq1uIHjDqXQqDQoNCkThu68gbGnhu4d1IG7DoHkgY2hvIHRo4bqleSBz4buxIHBow6JuIGLhu5EgxJHhu4thIGzDvSBy4bqldCByw7UgcsOgbmcsIHbhu5tpIGJhIGJhbmcgV0EgKFdhc2hpbmd0b24pLCBDQSAoQ2FsaWZvcm5pYSkgdsOgIE9SIChPcmVnb24pIHRo4buRbmcgdHLhu4sgdHV54buHdCDEkeG7kWkg4oCTIGzhuqduIGzGsOG7o3QgY2hp4bq/bSAzMi40OCUsIDE5LjQ0JSB2w6AgMTYuMDklLiBCYSBiYW5nIG7DoHksIMSR4buBdSBu4bqxbSDhu58ga2h1IHbhu7FjIFTDonkgQuG6r2MgVGjDoWkgQsOsbmggRMawxqFuZyBj4bunYSBIb2EgS+G7sywgY+G7mW5nIGzhuqFpIGNoaeG6v20gdOG7m2kgNjguMDElIHThu5VuZyBk4buvIGxp4buHdS4NCg0KVHJvbmcga2hpIMSRw7MsIGPDoWMgYmFuZyBj4bunYSBNZXhpY28gbmjGsCBERiwgR3VlcnJlcm8sIEphbGlzY28sIFZlcmFjcnV6LCBZdWNhdGFuIHbDoCBaYWNhdGVjYXMgY2jhu4kgY2hp4bq/bSB04bu3IHRy4buNbmcgbmjhu48gaMahbiwgdOG7lW5nIGPhu5luZyBjaOG7iSBjaGnhur9tIGtob+G6o25nIDI2LjIzJSBk4buvIGxp4buHdS4NCg0KTmjGsCB24bqteSwgZOG7ryBsaeG7h3UgY2hvIHRo4bqleSBz4buxIHRoacOqbiBs4buHY2ggbeG6oW5oIG3hur0gduG7gSBt4bq3dCDEkeG7i2EgbMO9LCBjaOG7pyB54bq/dSB04bqtcCB0cnVuZyB2w6BvIGJhIGJhbmcgcGjDrWEgVMOieSBC4bqvYyBIb2EgS+G7sywgdHJvbmcga2hpIGPDoWMgdOG7iW5oIGPhu6dhIE1leGljbyBjaOG7iSBnw7NwIHBo4bqnbiBraGnDqm0gdOG7kW4uDQoNCiMjICoqMi43IEJp4bq/biBDb3VudHJ5KioNCg0KKlRo4buRbmcga8OqIHThuqduIHN14bqldCoNCg0KVGEgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY2hvIGJp4bq/biBDb3VudHJ5IMSRxrDhu6NjIGvhur90IHF14bqjIG5oxrAgc2F1Og0KDQpgYGB7cn0NCkNvdW50cnlfMTwtIHRhYmxlKGQkQ291bnRyeSkNCkNvdW50cnlfMQ0KYGBgDQpgYGB7cn0NCkNvdW50cnlfMjwtdGFibGUoZCRDb3VudHJ5KS9zdW0obnJvdyhkKSkqMTAwDQpDb3VudHJ5XzINCmBgYA0KDQoqVHLhu7FjIHF1YW4gaMOzYSBk4buvIGxp4buHdSoNCg0KYGBge3J9DQpjNyA8LSBhcy5kYXRhLmZyYW1lKENvdW50cnlfMSkNCmNvbG5hbWVzKGM3KSA8LSBjKCJDb3VudHJ5IiwgIkNvdW50IikNCmdncGxvdChjNywgYWVzKHggPSBDb3VudHJ5LCB5ID0gQ291bnQsIGZpbGwgPSBDb3VudHJ5KSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiMwNzU3NUIiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh4ID0gIlF14buRYyBnaWEiLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSBraMOhY2ggaMOgbmcg4bufIGPDoWMgcXXhu5FjIGdpYSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmM3XzEgPC0gZCB8Pg0KICBncm91cF9ieShDb3VudHJ5KSB8Pg0KICBzdW1tYXJpc2UoZnJlcSA9IG4oKSkgfD4NCiAgbXV0YXRlKHBlciA9IGZyZXEgLyBzdW0oZnJlcSksDQogICAgICAgICBsYWJlbCA9IHBhc3RlMChmcmVxLCAiICgiLCByb3VuZChwZXIgKiAxMDAsIDEpLCAiJSkiKSkNCg0KZ2dwbG90KGM3XzEgLCBhZXMoeCA9ICIiLCB5ID0gcGVyLCBmaWxsID0gQ291bnRyeSkpICsNCiAgZ2VvbV9jb2wod2lkdGggPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQocGVyKjEwMCwgMiksICIlIikpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksDQogICAgICAgICAgICBzaXplID0gMi41KSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKSArICAjIELhuqFuIGPDsyB0aOG7gyBjaOG7jW4gU2V0MSwgU2V0Mi4uLg0KICBsYWJzKHRpdGxlID0gIlThu7cgbOG7hyDhu58gY8OhYyBxdeG7kWMgZ2lhIiwgZmlsbCA9ICJRdeG7kWMgZ2lhIikgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQoqTmjhuq1uIHjDqXQqDQoNCkvhur90IHF14bqjIHThuqduIHPhu5EgY2hvIGJp4bq/biBDb3VudHJ5IGNobyB0aOG6pXkgcuG6sW5nIHBo4bqnbiBs4bubbiBraMOhY2ggaMOgbmcgKDY4JSkgxJHhur9uIHThu6sgTeG7uSAoVVNBKSwgdHJvbmcga2hpIENhbmFkYSB2w6AgTWV4aWNvIGNoaeG6v20gdOG7tyBs4buHIGzhuqduIGzGsOG7o3QgbMOgIGtob+G6o25nIDUuNzUlIHbDoCAyNi4yMyUuIMSQaeG7gXUgbsOgeSBjaG8gdGjhuqV5IHPhu7EgcGjDom4gYuG7lSBraMO0bmcgxJHhu5NuZyDEkeG7gXUgY+G7p2EgZOG7ryBsaeG7h3UgZ2nhu69hIGJhIHF14buRYyBnaWEsIHbhu5tpIE3hu7kgbMOgIHRo4buLIHRyxrDhu51uZyB0cuG7jW5nIHTDom0uDQoNCiMjICoqMi44IEJp4bq/biBQcm9kdWN0RmFtaWx5KioNCg0KKlRo4buRbmcga8OqIHThuqduIHN14bqldCogDQoNClRhIHRo4buxYyBoaeG7h24gdGjhu5FuZyBrw6ogdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGNobyBiaeG6v24gUHJvZHVjdEZhbWlseSDEkcaw4bujYyBr4bq/dCBxdeG6oyBuaMawIHNhdToNCg0KYGBge3J9DQpQcm9kdWN0RmFtaWx5XzE8LSB0YWJsZShkJFByb2R1Y3RGYW1pbHkpDQpQcm9kdWN0RmFtaWx5XzENCmBgYA0KDQpgYGB7cn0NClByb2R1Y3RGYW1pbHlfMjwtdGFibGUoZCRQcm9kdWN0RmFtaWx5KS9zdW0obnJvdyhkKSkqMTAwDQpQcm9kdWN0RmFtaWx5XzINCmBgYA0KDQoqVHLhu7FjIHF1YW4gaMOzYSBk4buvIGxp4buHdSoNCg0KYGBge3J9DQpjOCA8LSBhcy5kYXRhLmZyYW1lKFByb2R1Y3RGYW1pbHlfMSkNCmNvbG5hbWVzKGM4KSA8LSBjKCJQcm9kdWN0RmFtaWx5IiwgIkNvdW50IikNCmdncGxvdChjOCwgYWVzKHggPSBQcm9kdWN0RmFtaWx5LCB5ID0gQ291bnQsIGZpbGwgPSBQcm9kdWN0RmFtaWx5KSkgKw0KICBnZW9tX2NvbChmaWxsID0gImxpZ2h0cGluayIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHggPSAiU+G6o24gcGjhuqltIGdpYSDEkcOsbmgiLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgYmnhu4N1IHRo4buLIG5ow7NtIHPhuqNuIHBo4bqpbSBraMOhY2ggaMOgbmcgbXVhIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpDQpgYGANCg0KKk5o4bqtbiB4w6l0Kg0KDQpL4bq/dCBxdeG6oyB04bqnbiBz4buRIGNobyB0aOG6pXkgbmjDs20gdGjhu7FjIHBo4bqpbSAoRm9vZCkgY2hp4bq/bSDDoXAgxJHhuqNvIHbhu5tpIGjGoW4gNzIlIHThu5VuZyBz4buRIG3hurd0IGjDoG5nLCB0cm9uZyBraGkgxJHhu5MgdeG7kW5nIChEcmluaykgY2jhu4kgY2hp4bq/bSBn4bqnbiA5JSB2w6AgY8OhYyBt4bq3dCBow6BuZyBwaGkgdGjhu7FjIHBo4bqpbSAoTm9uLUNvbnN1bWFibGUpIGNoaeG6v20ga2hv4bqjbmcgMTklLiBT4buxIGNow6puaCBs4buHY2ggbsOgeSBwaOG6o24gw6FuaCByw7UgdOG6p20gcXVhbiB0cuG7jW5nIHbDoCBuaHUgY+G6p3UgY2FvIGPhu6dhIHRo4buxYyBwaOG6qW0gdHJvbmcgZGFuaCBzw6FjaCBtdWEgc+G6r20gaMOgbmcgbmfDoHksIHNvIHbhu5tpIGPDoWMgbG/huqFpIMSR4buTIHXhu5FuZyB2w6Agc+G6o24gcGjhuqltIHBoaSB0aOG7sWMgcGjhuqltIGtow6FjLg0KDQojIyAqKjIuOSBCaeG6v24gUHJvZHVjdERlcGFydG1lbnQqKg0KDQoqVGjhu5FuZyBrw6ogdOG6p24gc3XhuqV0KiANCg0KVGEgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY2hvIGJp4bq/biBQcm9kdWN0RGVwYXJ0bWVudCAgxJHGsOG7o2Mga+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KUHJvZHVjdERlcGFydG1lbnRfMTwtIHRhYmxlKGQkUHJvZHVjdERlcGFydG1lbnQpDQpQcm9kdWN0RGVwYXJ0bWVudF8xDQpgYGANCg0KYGBge3J9DQpQcm9kdWN0RGVwYXJ0bWVudF8yPC10YWJsZShkJFByb2R1Y3REZXBhcnRtZW50KS9zdW0obnJvdyhkKSkqMTAwDQpQcm9kdWN0RGVwYXJ0bWVudF8yDQpgYGANCg0KKlRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UqDQoNCmBgYHtyfQ0KYzkgPC0gYXMuZGF0YS5mcmFtZShQcm9kdWN0RGVwYXJ0bWVudF8xKQ0KY29sbmFtZXMoYzkpIDwtIGMoIlByb2R1Y3REZXBhcnRtZW50IiwgIkNvdW50IikNCmdncGxvdChjOSwgYWVzKHggPSBQcm9kdWN0RGVwYXJ0bWVudCwgeSA9IENvdW50LCBmaWxsID0gUHJvZHVjdERlcGFydG1lbnQpKSArDQogIGdlb21fY29sKGZpbGwgPSAibGlnaHRwaW5rIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJT4bqjbiBwaOG6qW0iLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJC4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0ga2jDoWNoIGjDoG5nIG11YSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KYGBgDQoNCipOaOG6rW4geMOpdCoNCg0KROG7ryBsaeG7h3UgY2hvIHRo4bqleSBz4buxIHBow6JuIGLhu5UgcsO1IHLhu4d0IHRyb25nIHRow7NpIHF1ZW4gbXVhIHPhuq9tOiBjw6FjIG3hurd0IGjDoG5nIHTGsMahaSBz4buRbmcgdsOgIMSR4buTIGdpYSBk4bulbmcgY2hp4bq/bSBwaOG6p24gbOG7m24sIHbhu5tpIFByb2R1Y2UgKDE0LjE4JSkgdsOgIEhvdXNlaG9sZCAoMTAuMSUpIGPhu5luZyBs4bqhaSBn4bqnbiAyNSUgdOG7lW5nIHPhu5EgbeG6t3QgaMOgbmcuIMSQaeG7gXUgbsOgeSBwaOG6o24gw6FuaCBz4buxIMawdSB0acOqbiB0acOqdSBkw7luZyBjw6FjIHPhuqNuIHBo4bqpbSB0aGnhur90IHnhur91IGjDoG5nIG5nw6B5Lg0KDQpOZ8aw4bujYyBs4bqhaSwgc+G7sSB0aGnhur91IGPDom4gxJHhu5FpIHRo4buDIGhp4buHbiDhu58gY8OhYyBuaMOzbSBuaMawIE1lYXQgdsOgIFNlYWZvb2QsIGtoaSBjaOG7iSBjaGnhur9tIGNoxrBhIHThu5tpIDElIHThu5VuZyBz4buRIG3hurd0IGjDoG5nLCBjaG8gdGjhuqV5IG5odSBj4bqndSBj4bunYSBjw6FjIG3hurd0IGjDoG5nIG7DoHkgw610IGjGoW4gxJHDoW5nIGvhu4Mgc28gduG7m2kgY8OhYyBuaMOzbSBraMOhYy4gxJBp4buBdSBuw6B5IGPDsyB0aOG7gyB4deG6pXQgcGjDoXQgdOG7qyB0aMOzaSBxdWVuIMSDbiB14buRbmcgaG/hurdjIMSR4bq3YyDEkWnhu4NtIG5ndeG7k24gY3VuZyBj4bqlcCBj4bunYSBj4butYSBow6BuZy4NCg0KIyMgKioyLjEwIEJp4bq/biBQcm9kdWN0Q2F0ZWdvcnkqKg0KDQoqVGjhu5FuZyB04bqnbiBzdeG6pXQqDQoNClRhIHRo4buxYyBoaeG7h24gdGjhu5FuZyBrw6ogdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGNobyBiaeG6v24gUHJvZHVjdENhdGVnb3J5ICDEkcaw4bujYyBr4bq/dCBxdeG6oyBuaMawIHNhdToNCg0KYGBge3J9DQpQcm9kdWN0Q2F0ZWdvcnlfMTwtIHRhYmxlKGQkUHJvZHVjdENhdGVnb3J5KQ0KUHJvZHVjdENhdGVnb3J5XzENCmBgYA0KYGBge3J9DQpQcm9kdWN0Q2F0ZWdvcnlfMjwtdGFibGUoZCRQcm9kdWN0Q2F0ZWdvcnkpL3N1bShucm93KGQpKSoxMDANClByb2R1Y3RDYXRlZ29yeV8yDQpgYGANCg0KKlRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UqDQoNCmBgYHtyfQ0KYzEwIDwtIGFzLmRhdGEuZnJhbWUoUHJvZHVjdENhdGVnb3J5XzEpDQpjb2xuYW1lcyhjMTApIDwtIGMoIlByb2R1Y3RDYXRlZ29yeSIsICJDb3VudCIpDQoNCmdncGxvdChjMTAsIGFlcyh4ID0gUHJvZHVjdENhdGVnb3J5LCB5ID0gQ291bnQsIGZpbGwgPSBQcm9kdWN0Q2F0ZWdvcnkpKSArDQogIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJT4bqjbiBwaOG6qW0iLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJEYW5oIG3hu6VjIHPhuqNuIHBo4bqpbSBraMOhY2ggaMOgbmcgbXVhIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCipOaOG6rW4geMOpdCoNCg0KROG7ryBsaeG7h3UgdOG6p24gc+G7kSBj4bunYSBjw6FjIGRhbmggbeG7pWMgc+G6o24gcGjhuqltIGNobyB0aOG6pXkgcuG6sW5nIGPDoWMgbeG6t3QgaMOgbmcgdGhp4bq/dCB54bq/dSB2w6AgcGjhu5UgYmnhur9uIG5o4bqldCBuaMawIHJhdSBj4bunICgxLjcyOCksIMSR4buTIMSDbiB24bq3dCAoMS42MDApLCBz4buvYSAoOTAzKSwgdHLDoWkgY8OieSAoNzY1KSB2w6AgdGjhu4t0ICg3NjEpIGPDsyB04bqnbiBzdeG6pXQgY2FvIG5o4bqldC4gxJBp4buBdSBuw6B5IHBo4bqjbiDDoW5oIG5odSBj4bqndSBz4butIGThu6VuZyBo4bqxbmcgbmfDoHkgY+G7p2EgY8OhYyBnaWEgxJHDrG5oLiBOZ8aw4bujYyBs4bqhaSwgY8OhYyBz4bqjbiBwaOG6qW0gbmjGsCBjw6EgY8ahbSDEkcOzbmcgaOG7mXAgKDQ0KSwgaMOgdSDEkcOzbmcgaOG7mXAgKDM1KSwgdMO0bSDEkcOzbmcgaOG7mXAgKDM4KSB2w6AgbuG6v24gKDQ1KSBs4bqhaSBjw7MgdOG6p24gc3XhuqV0IHRo4bqlcCBuaOG6pXQsIGNobyB0aOG6pXkgxJHDonkgbMOgIG5o4buvbmcgbeG6t3QgaMOgbmcgw610IHBo4buVIGJp4bq/biBob+G6t2MgY2jhu4kgxJHGsOG7o2Mgc+G7rSBk4bulbmcgdHJvbmcgbmjhu69uZyB0csaw4budbmcgaOG7o3AgbmjhuqV0IMSR4buLbmguIFTDs20gbOG6oWksIHPhu7EgcGjDom4gYuG7kSB04bqnbiBz4buRIG7DoHkgcGjhuqNuIMOhbmggcsO1IHLDoG5nIHh1IGjGsOG7m25nIHRpw6p1IGTDuW5nIHbDoCBt4bupYyDEkeG7mSBwaOG7lSBiaeG6v24gY+G7p2EgdOG7q25nIGxv4bqhaSBz4bqjbiBwaOG6qW0uDQo=