
Benford law
Nếu chúng ta gặp câu hỏi “Tần suất xuất hiện các chữ số từ 1 đến 9 tại vị trí chữ số đầu tiên của bộ dữ liệu về thu nhập cá nhân của 9 triệu người là bao nhiêu?” thì chúng ta dễ bị lôi kéo bởi ý tưởng rằng tần suất đó tuân theo Uniform Distribution và do vậy tần suất xuất hiện của mỗi chữ số sẽ là tương tự nhau và xấp xỉ 1/9 = 11.11% như chúng ta có thể thấy:
# Clear workspace:
rm(list = ls())
# Create our theme:
library(tidyverse)
library(extrafont)
my_font <- "Roboto Condensed"
my_theme <- function(...) {
theme(panel.grid.major.x = element_blank()) +
theme(panel.grid.minor.y = element_blank()) +
theme(axis.ticks.x = element_blank()) +
theme(text = element_text(family = my_font, size = 16)) +
theme(plot.caption = element_text(size = 10)) +
theme(plot.title = element_text(vjust = 4)) +
theme(plot.margin = unit(rep(0.8, 4), "cm"))
}
random_df <- tibble(digit = sample(1:9, size = 9000000, replace = TRUE))
random_df %>%
group_by(digit) %>%
count() %>%
ungroup() %>%
mutate(digit = as.factor(digit)) %>%
ggplot(aes(digit, n / sum(n))) +
geom_col(width = 0.65, fill = "steelblue") +
scale_y_continuous(breaks = seq(0, 0.12, 0.025), expand = c(0, 0), limits = c(0, 0.12), label = scales::percent) +
my_theme() +
labs(x = NULL, y = NULL, title = "Figure 1: Frequency of occurrences",
caption = "Source: Data based on Random Generator")

Giả định rằng các chữ số từ 1 đến 9 xuất hiện theo Uniform Distribution là một giả định hợp lí vì rõ ràng là: không có lí do gì mà một chữ số cụ thể nào đó, như số 1 chẳng hạn, lại có cơ hội xuất hiện nhiều hơn số 9 hay một số bất kì nào khác. Nếu khác đi, có vẻ thượng đế sẽ ưu ái với số 1.
Tuy nhiên năm 1938 một nhà vật lí kiêm kĩ sư điện tại GE phủ nhận giả thuyết này và cho rằng các chứ số từ 1 đến 9 (kí hiệu là d) sẽ có xác suất xuất hiện \(P(d)\) được thể hiện theo công thức sau:
\[P(d) = log_{10}(1 + \frac{1}{d})\]
Công thức này gọi là Benford law thực ra đã được đề cập trước đó vào năm 1881 bởi Simon Newcomb và do vậy còn có tên gọi khác là Newcomb–Benford law. Chúng ta minh họa định luật này bằng công cụ hình ảnh:
# Data Frame based on Benford's law (from https://en.wikipedia.org/wiki/Benford%27s_law):
df_benford <- tibble(digit = 1:9, prob = sapply(1:9, function(d) {log10(1 + 1 / d)}))
df_benford %>%
mutate(digit = as.character(digit)) %>%
mutate(text = round(100*prob, 1)) %>%
mutate(text = paste0(text, "%")) -> df_plot
# Graph:
df_plot %>%
ggplot(aes(digit, prob)) +
geom_col(width = 0.65, fill = "steelblue") +
geom_line(aes(as.numeric(digit), prob), color = "orange", size = 1) +
geom_point(aes(as.numeric(digit), prob), color = "firebrick", size = 2) +
geom_text(aes(label = text), vjust = 1.3, color = "white", family = my_font, size = 4.5) +
scale_y_continuous(breaks = seq(0, 0.305, 0.05), expand = c(0, 0), limits = c(0, 0.31), label = scales::percent) +
my_theme() +
labs(x = NULL, y = NULL, title = "Figure 2: The Law of Anomalous Numbers",
caption = "Source: Data based on Benford's law")

Theo định luật này thì rõ ràng số 1 có tần suất xuất hiện lớn nhất và các số càng lớn thì tần suất xuất hiện càng ít đi. Chi tiết hơn về định luật này cũng như các ứng dụng của nó bạn đọc có thể tìm hiểu thêm ở đây. Chúng ta sẽ kiểm tra xem liệu các chữ số từ 1 đến 9 fit ở mức độ như thế nào với định luật Benford với một số bộ dữ liệu cụ thể.
Trước hết chúng ta viết một hàm nhận input là chuỗi các số từ 1 đến 9 - là chữ số ở vị trí thứ nhất từ một chuỗi số bất kì và hiển thị tần suất xuất hiện của chúng đồng thời so sánh tần suất thực tế với tần suất được dự báo bởi công thức Benford:
compare_with_benford <- function(x) {
tibble(digit = x) %>%
na.omit() %>%
group_by(digit) %>%
count() %>%
ungroup() %>%
mutate(digit = as.factor(digit)) %>%
ggplot(aes(digit, n / sum(n))) +
geom_col(width = 0.65, fill = "steelblue") +
geom_line(data = df_benford, aes(as.numeric(digit), prob), color = "orange", size = 1) +
geom_point(data = df_benford, aes(as.numeric(digit), prob), color = "firebrick", size = 2) +
my_theme() +
theme(axis.title = element_blank())
}
Với chuỗi poptotal từ bộ số liệu midwest đi kèm với thư viện ggplot2:

Rõ ràng phân bố của các chữ số chưa fit lắm với định luật Benford. Điều này có thể nằm ở nguyên nhân là mới chỉ có 437 quan sát - một số lượng chưa đủ nhiều. Nếu quy mô dữ liệu tăng lên thì mức độ fit với định luật Benford có thể cao hơn:

Vậy là khi số quan sát tăng lên 2180 thì tần suất xuất hiện của các số từ 1 đến 9 tiệm cận hơn với các tần suất dự báo bởi quy luật Benford. Chúng ta lại xét một bộ dữ liệu khác là txhousing trong đó volume là tổng giá trị doanh thu các thương vụ giao dịch bất động sản ở Texas:

Các bằng chứng thực nghiệm ở trên về tần suất suất xuất hiện các chữ số từ 1 đến 9 trong thế giới thực có vẻ xác nhận định luật Benford. Và bởi vậy định luật này có nhiều ứng dụng. Một trong những ứng dụng đó là xác định các báo cáo tài chính gian lận của công ti Enron. Bạn đọc có thể tham khảo bài viết của The Wall Street Journal về áp dụng Benford law với tình huống của Enron ở đây.
Benford law as a feature engineering technique
Định luật Benford có thể được sử dụng như là một kĩ thuật feature engineering cho các mô hình Machine Learning. Dưới đây chúng ta sẽ so sánh chất lượng dự báo/phân loại của mô hình phân loại Logistic Classifier cho hai tình huống:
- Tạo ra các features mới bằng cách sử dụng Benford law
- Sử dụng chỉ các features nguyên bản.
Bộ dữ liệu sử dụng là creditcard.csv từ cuộc thi Credit Card Fraud Detection và có thể download tại đây. Ở đây chúng ta sẽ tạo ra các features mới từ Amount - một feature sẵn có của bộ dữ liệu. Trước hết ta so sánh tần suất xuât hiện các chữ số từ 1 đến 9:
# Load data:
data <- read_csv("creditcard.csv")
# Data manipulation:
data %>%
mutate(Class = case_when(Class == 1 ~ "Fraud", TRUE ~ "NonFraud")) %>%
mutate(Amount_text = as.character(Amount)) %>%
mutate(digit_0 = case_when(!str_detect(Amount_text, "\\.") ~ "Yes", TRUE ~ "No")) %>%
mutate(digit = str_sub(Amount_text, start = 1, end = 1)) -> data
# Calculate frequencies and join datasets:
data %>%
filter(digit != "0") %>%
group_by(digit) %>%
count() %>%
ungroup() %>%
mutate(actual_rate = n / sum(n)) %>%
full_join(df_plot %>% select(digit, prob)) %>%
mutate(diff_freq = actual_rate - prob) -> digit_from_creditdata
# Compare actual Frequency and Benford's law:
x <- data %>%
filter(digit != "0") %>%
pull(digit)
compare_with_benford(x) +
scale_y_continuous(breaks = seq(0, 0.42, 0.05), expand = c(0, 0), limits = c(0, 0.42), label = scales::percent) +
labs(x = NULL, y = NULL, title = "Figure 6: Actual frequency compared with Benford's law",
caption = "Source: Credit Card Fraud Detection, https://www.kaggle.com")

Từ Figure 6 rõ ràng tần suất xuất hiện của số 1 là tương đối cao (giống như tình huống về các số liệu tài chính gian lận của công ti Enron). Chúng ta thử kiểm tra sự khác biệt về tỉ lệ các giao dịch gian lận giữa nhóm có và không xuất hiện chữ số 1:
## # A tibble: 2 x 5
## digit_1 Fraud NonFraud rate diff
## <chr> <int> <int> <dbl> <dbl>
## 1 No 290 184062 0.00157 1.28
## 2 Yes 202 100253 0.00201 1.28
Rõ ràng nhóm giao dịch có xuất hiện chữ số 1 có tỉ lệ giao dịch gian lận cao hơn 28% so với nhóm không có chữ số 1. Mặc dù Benford law không cover cho chữ số 0 nhưng chúng ta vẫn nên điều tra xem giữa nhóm có chữ số 0 và phần còn lại có khác biệt đáng kể hay không về tỉ lệ các giao dịch tài chính gian lận:
## # A tibble: 2 x 5
## digit_0 Fraud NonFraud rate diff
## <chr> <int> <int> <dbl> <dbl>
## 1 No 424 267579 0.00158 2.56
## 2 Yes 68 16736 0.00405 2.56
Kết quả này cho thấy các giao dịch có chữ số 0 có tỉ lệ giao dịch gian lận cao hơn 156% so với nhóm còn lại. Chúng là các giao dịch quy mô nhỏ có giá trị giao dịch chưa đến 1 hoặc bằng 0 như chúng ta có thể thấy:
## # A tibble: 10 x 2
## Amount digit_0
## <dbl> <chr>
## 1 0.99 Yes
## 2 0.77 Yes
## 3 0.89 Yes
## 4 0 Yes
## 5 0.01 Yes
## 6 0.89 Yes
## 7 0.89 Yes
## 8 0.76 Yes
## 9 0.89 Yes
## 10 0.89 Yes
Từ các phân tích trên chúng ta mạnh dạn đưa ra một giả định rằng feature mới cho biết thông tin về giá trị của chữ số xuất hiện ở the first digit có thể là một chỉ báo tốt, hoặc chí ít là có ý nghĩa trong việc phân biệt giao dịch gian lận. Do vậy chúng ta tạo ra hai bộ số liệu như sau:
- Bộ dữ liệu nguyên bản.
- Bộ dữ liệu có các features mới dựa trên Benford law.
Dưới đây là R codes:
# Transform data:
data %>%
mutate(digit_0 = case_when(str_detect(digit, "0") ~ "Yes", TRUE ~ "No")) %>%
mutate(digit_0 = as.factor(digit_0), Class = as.factor(Class)) %>%
select(-Time, - Amount_text) %>%
full_join(digit_from_creditdata %>% select(digit, diff_freq)) %>%
mutate(diff_freq = replace_na(diff_freq, 0), digit = as.factor(digit)) -> df_modelling
# Split data:
library(caret)
set.seed(1)
id <- createDataPartition(y = df_modelling$Class, p = 0.2, list = FALSE)
df_modelling %>% slice(id) -> train_benford
df_modelling %>% slice(-id) -> test_benford
train_benford %>% select(-digit_0, -digit, -diff_freq) -> train_none
test_benford %>% select(-digit_0, -digit, -diff_freq) -> test_none
Huấn luyện Logistic Classifier với hai bộ dữ liệu đã được chuẩn bị và sử dụng các model có được thực hiện dự báo xác suất cho sự kiện “giao dịch gian lận” trên bộ dữ liệu test:
# Set conditions for training model and cross-validation:
set.seed(1)
number <- 3
repeats <- 1
control <- trainControl(method = "repeatedcv",
number = number,
repeats = repeats,
classProbs = TRUE,
summaryFunction = twoClassSummary,
allowParallel = TRUE)
# Parallel Computing with multicores from our computer:
library(parallel)
nCores <- detectCores(logical = TRUE)
nThreads <- detectCores(logical = TRUE)
cl <- makeCluster(nThreads)
# Logistic Classifier with feature engineering using Benford law:
model <- "glm"
set.seed(1)
glm1 <- train(Class ~.,
data = train_benford %>% select(-Amount),
trControl = control,
method = model,
metric = "ROC")
# Logistic Classifier with original data:
set.seed(1)
glm2 <- train(Class ~.,
data = train_none,
trControl = control,
method = model,
metric = "ROC")
# Use models for predicting probability of fraud:
predict(glm1, test_benford %>% select(-Amount), type = "prob") %>% pull(Fraud) -> pd1
predict(glm2, test_none, type = "prob") %>% pull(Fraud) -> pd2
Vì mức độ chính xác cho dự báo một giao dịch gian lận thực sự là gian lận (dương tính thật) phụ thuộc vào ngưỡng (Threshold hay Cutoff) nên chúng ta khảo sát chất lượng của hai mô hình đồng thời trên một loạt ngưỡng được lựa chọn:
# Function calculates model performances by cutoff for model 1:
byCutoff_rf1 <- function(cutoff) {
pred <- case_when(pd1 >= cutoff ~ "Fraud", TRUE ~ "NonFraud") %>% as.factor()
confusionMatrix(pred, test_benford$Class, positive = "Fraud") -> cm
cm$table %>% as.vector() -> bg
cm$overall %>% as.vector() -> acc
cm$byClass %>% as.vector() -> sen
data.frame(BB = bg[1],
BG = bg[2],
GB = bg[3],
GG = bg[4],
Accuracy = acc[1],
Kappa = acc[2],
Recall = sen[1],
Specificity = sen[2],
Cutoff = cutoff) -> model_perCutoff
return(model_perCutoff)
}
# Function calculates model performances by cutoff for model 2:
byCutoff_rf2 <- function(cutoff) {
pred <- case_when(pd2 >= cutoff ~ "Fraud", TRUE ~ "NonFraud") %>% as.factor()
confusionMatrix(pred, test_benford$Class, positive = "Fraud") -> cm
cm$table %>% as.vector() -> bg
cm$overall %>% as.vector() -> acc
cm$byClass %>% as.vector() -> sen
data.frame(BB = bg[1],
BG = bg[2],
GB = bg[3],
GG = bg[4],
Accuracy = acc[1],
Kappa = acc[2],
Recall = sen[1],
Specificity = sen[2],
Cutoff = cutoff) -> model_perCutoff
return(model_perCutoff)
}
Rồi sử dụng hai hàm trên:
So sánh chất lượng của hai mô hình phân loại tương ứng với một loạt ngưỡng được lựa chọn:
# Compare model performance by Feature Engineering Method:
my_colors <- c("firebrick", "steelblue")
df_comparision %>%
select(5:10) %>%
gather(Metric, Value, -Cutoff, -`Feature Engineering`) %>%
ggplot(aes(Cutoff, Value, color = `Feature Engineering`)) +
geom_line() +
geom_point() +
scale_color_manual(values = my_colors) +
facet_wrap(~ Metric, scales = "free") +
theme(legend.position = "top") +
my_theme() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(breaks = seq(0.05, 0.75, 0.1)) +
labs(x = NULL, y = NULL,
title = "Figure 7: Model Performance by Feature Engineering Method",
caption = "Source: Credit Card Fraud Detection (https://www.kaggle.com)")

Figure 7 cho thấy tại tất cả các ngưỡng chất lượng phân loại (đặc biệt là khả năng xác minh đúng giao dịch gian lận) của mô hình sử dụng features được extract từ Amount dựa trên Benford law là tốt hơn.
Conclusion
Các cơ quan chuyên về điều tra các gian lận về thuế ở châu Âu đã công khai thừa nhận việc áp dụng định luật Benford cho điều tra kĩ hơn các cases gian lận. Các công ti kiểm toán (Auditing Firms) cũng áp dụng định luật này trong các nghiệp vụ liên quan đến hoạt động kiểm toán các số liệu tài chính bị nghi ngờ là giả mạo hoặc bị bóp méo. Ở Mĩ thì IRS (cơ quan chuyên thuế) không phủ nhận nhưng cũng không thừa nhận việc sử dụng định luật này để xác minh các gian lận về thuế. Bài viết này trình bày một hướng ứng dụng mới của định luật Benford như là một kĩ thuật cho Feature Engineering/Extraction. Sử dụng Logistic Classifier cho bộ dữ liệu creditcard.csv cho thấy rằng các features được tạo ra bằng cách vận dụng định luật Benford sẽ nâng cao tỉ lệ xác minh các giao dịch tài chính gian lận. Tuy nhiên hiệu lực của kĩ thuật feature engineering này cho các mô hình học máy khác hay cách tinh vi hơn của việc vận dụng định luật Benford cho fearure engineering là chưa được đề cập trong bài viết này.
LS0tDQp0aXRsZTogIkJlbmZvcmQgbGF3IGFzIGEgZmVhdHVyZSBlbmdpbmVlcmluZyB0ZWNobmlxdWUgZm9yIE1hY2hpbmUgTGVhcm5pbmcgTW9kZWxzIg0KYXV0aG9yOiAiTmd1eWVuIENoaSBEdW5nIg0Kc3VidGl0bGU6ICJSIE1hY2hpbmUgTGVhcm5pbmcgU2VyaWVzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4NCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNikNCmBgYA0KDQoNCiFbXShDOi9Vc2Vycy9BRE1JTi9Eb2N1bWVudHMvYmVuZm9yZC5qcGcpDQoNCg0KIyBCZW5mb3JkIGxhdw0KDQpO4bq/dSBjaMO6bmcgdGEgZ+G6t3AgY8OidSBo4buPaSAiVOG6p24gc3XhuqV0IHh14bqldCBoaeG7h24gY8OhYyBjaOG7ryBz4buRIHThu6sgMSDEkeG6v24gOSB04bqhaSB24buLIHRyw60gY2jhu68gc+G7kSDEkeG6p3UgdGnDqm4gY+G7p2EgYuG7mSBk4buvIGxp4buHdSB24buBIHRodSBuaOG6rXAgY8OhIG5ow6JuIGPhu6dhIDkgdHJp4buHdSBuZ8aw4budaSBsw6AgYmFvIG5oacOqdT8iIHRow6wgY2jDum5nIHRhIGThu4UgYuG7iyBsw7RpIGvDqW8gYuG7n2kgw70gdMaw4bufbmcgcuG6sW5nIHThuqduIHN14bqldCDEkcOzIHR1w6JuIHRoZW8gVW5pZm9ybSBEaXN0cmlidXRpb24gdsOgIGRvIHbhuq15IHThuqduIHN14bqldCB4deG6pXQgaGnhu4duIGPhu6dhIG3hu5dpIGNo4buvIHPhu5Egc+G6vSBsw6AgdMawxqFuZyB04buxIG5oYXUgdsOgIHjhuqVwIHjhu4kgMS85ID0gMTEuMTElIG5oxrAgY2jDum5nIHRhIGPDsyB0aOG7gyB0aOG6pXk6IA0KDQpgYGB7cn0NCg0KIyBDbGVhciB3b3Jrc3BhY2U6IA0KDQpybShsaXN0ID0gbHMoKSkNCg0KIyBDcmVhdGUgb3VyIHRoZW1lOiANCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGV4dHJhZm9udCkNCm15X2ZvbnQgPC0gIlJvYm90byBDb25kZW5zZWQiIA0KDQpteV90aGVtZSA8LSBmdW5jdGlvbiguLi4pIHsNCiAgDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogICAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArIA0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gbXlfZm9udCwgc2l6ZSA9IDE2KSkgKyANCiAgICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKyANCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gNCkpICsgDQogICAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KHJlcCgwLjgsIDQpLCAiY20iKSkNCn0NCg0KcmFuZG9tX2RmIDwtIHRpYmJsZShkaWdpdCA9IHNhbXBsZSgxOjksIHNpemUgPSA5MDAwMDAwLCByZXBsYWNlID0gVFJVRSkpDQoNCnJhbmRvbV9kZiAlPiUgDQogIGdyb3VwX2J5KGRpZ2l0KSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUoZGlnaXQgPSBhcy5mYWN0b3IoZGlnaXQpKSAlPiUgDQogIGdncGxvdChhZXMoZGlnaXQsIG4gLyBzdW0obikpKSArIA0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUsIGZpbGwgPSAic3RlZWxibHVlIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAwLjEyLCAwLjAyNSksIGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMC4xMiksIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBteV90aGVtZSgpICsgDQogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCB0aXRsZSA9ICJGaWd1cmUgMTogRnJlcXVlbmN5IG9mIG9jY3VycmVuY2VzIiwgDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IERhdGEgYmFzZWQgb24gUmFuZG9tIEdlbmVyYXRvciIpDQpgYGANCg0KR2nhuqMgxJHhu4tuaCBy4bqxbmcgY8OhYyBjaOG7ryBz4buRIHThu6sgMSDEkeG6v24gOSB4deG6pXQgaGnhu4duIHRoZW8gVW5pZm9ybSBEaXN0cmlidXRpb24gbMOgIG3hu5l0IGdp4bqjIMSR4buLbmggaOG7o3AgbMOtIHbDrCByw7UgcsOgbmcgbMOgOiBraMO0bmcgY8OzIGzDrSBkbyBnw6wgbcOgIG3hu5l0IGNo4buvIHPhu5EgY+G7pSB0aOG7gyBuw6BvIMSRw7MsIG5oxrAgc+G7kSAxIGNo4bqzbmcgaOG6oW4sIGzhuqFpIGPDsyBjxqEgaOG7mWkgeHXhuqV0IGhp4buHbiBuaGnhu4F1IGjGoW4gc+G7kSA5IGhheSBt4buZdCBz4buRIGLhuqV0IGvDrCBuw6BvIGtow6FjLiBO4bq/dSBraMOhYyDEkWksIGPDsyB24bq7IHRoxrDhu6NuZyDEkeG6vyBz4bq9IMawdSDDoWkgduG7m2kgc+G7kSAxLiANCg0KDQpUdXkgbmhpw6puIG7Eg20gMTkzOCBt4buZdCBuaMOgIHbhuq10IGzDrSBracOqbSBrxKkgc8awIMSRaeG7h24gdOG6oWkgR0UgcGjhu6cgbmjhuq1uIGdp4bqjIHRodXnhur90IG7DoHkgdsOgIGNobyBy4bqxbmcgY8OhYyBjaOG7qSBz4buRIHThu6sgMSDEkeG6v24gOSAoa8OtIGhp4buHdSBsw6AgZCkgc+G6vSBjw7MgeMOhYyBzdeG6pXQgeHXhuqV0IGhp4buHbiAkUChkKSQgxJHGsOG7o2MgdGjhu4MgaGnhu4duIHRoZW8gY8O0bmcgdGjhu6ljIHNhdTogDQoNCiQkUChkKSA9IGxvZ197MTB9KDEgKyBcZnJhY3sxfXtkfSkkJA0KDQpDw7RuZyB0aOG7qWMgbsOgeSBn4buNaSBsw6AgQmVuZm9yZCBsYXcgdGjhu7FjIHJhIMSRw6MgxJHGsOG7o2MgxJHhu4EgY+G6rXAgdHLGsOG7m2MgxJHDsyB2w6BvIG7Eg20gMTg4MSBi4bufaSBTaW1vbiBOZXdjb21iIHbDoCBkbyB24bqteSBjw7JuIGPDsyB0w6puIGfhu41pIGtow6FjIGzDoCBOZXdjb21i4oCTQmVuZm9yZCBsYXcuIENow7puZyB0YSBtaW5oIGjhu41hIMSR4buLbmggbHXhuq10IG7DoHkgYuG6sW5nIGPDtG5nIGPhu6UgaMOsbmgg4bqjbmg6IA0KDQoNCmBgYHtyfQ0KIyBEYXRhIEZyYW1lIGJhc2VkIG9uIEJlbmZvcmQncyBsYXcgKGZyb20gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmVuZm9yZCUyN3NfbGF3KTogDQoNCmRmX2JlbmZvcmQgPC0gdGliYmxlKGRpZ2l0ID0gMTo5LCBwcm9iID0gc2FwcGx5KDE6OSwgZnVuY3Rpb24oZCkge2xvZzEwKDEgKyAxIC8gZCl9KSkNCg0KZGZfYmVuZm9yZCAlPiUgDQogIG11dGF0ZShkaWdpdCA9IGFzLmNoYXJhY3RlcihkaWdpdCkpICU+JSANCiAgbXV0YXRlKHRleHQgPSByb3VuZCgxMDAqcHJvYiwgMSkpICU+JSANCiAgbXV0YXRlKHRleHQgPSBwYXN0ZTAodGV4dCwgIiUiKSkgLT4gZGZfcGxvdA0KDQojIEdyYXBoOiANCg0KZGZfcGxvdCAlPiUgDQogIGdncGxvdChhZXMoZGlnaXQsIHByb2IpKSArIA0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUsIGZpbGwgPSAic3RlZWxibHVlIikgKyANCiAgZ2VvbV9saW5lKGFlcyhhcy5udW1lcmljKGRpZ2l0KSwgcHJvYiksIGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAxKSArIA0KICBnZW9tX3BvaW50KGFlcyhhcy5udW1lcmljKGRpZ2l0KSwgcHJvYiksIGNvbG9yID0gImZpcmVicmljayIsIHNpemUgPSAyKSArIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGV4dCksIHZqdXN0ID0gMS4zLCBjb2xvciA9ICJ3aGl0ZSIsIGZhbWlseSA9IG15X2ZvbnQsIHNpemUgPSA0LjUpICsgDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMC4zMDUsIDAuMDUpLCBleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDAuMzEpLCBsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbXlfdGhlbWUoKSArIA0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiRmlndXJlIDI6IFRoZSBMYXcgb2YgQW5vbWFsb3VzIE51bWJlcnMiLCANCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogRGF0YSBiYXNlZCBvbiBCZW5mb3JkJ3MgbGF3IikNCmBgYA0KDQpUaGVvIMSR4buLbmggbHXhuq10IG7DoHkgdGjDrCByw7UgcsOgbmcgc+G7kSAxIGPDsyB04bqnbiBzdeG6pXQgeHXhuqV0IGhp4buHbiBs4bubbiBuaOG6pXQgdsOgIGPDoWMgc+G7kSBjw6BuZyBs4bubbiB0aMOsIHThuqduIHN14bqldCB4deG6pXQgaGnhu4duIGPDoG5nIMOtdCDEkWkuIENoaSB0aeG6v3QgaMahbiB24buBIMSR4buLbmggbHXhuq10IG7DoHkgY8WpbmcgbmjGsCBjw6FjIOG7qW5nIGThu6VuZyBj4bunYSBuw7MgYuG6oW4gxJHhu41jIGPDsyB0aOG7gyB0w6xtIGhp4buDdSB0aMOqbSBb4bufIMSRw6J5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9CZW5mb3JkJ3NfbGF3KS4gQ2jDum5nIHRhIHPhur0ga2nhu4NtIHRyYSB4ZW0gbGnhu4d1IGPDoWMgY2jhu68gc+G7kSB04burIDEgxJHhur9uIDkgZml0IOG7nyBt4bupYyDEkeG7mSBuaMawIHRo4bq/IG7DoG8gduG7m2kgxJHhu4tuaCBsdeG6rXQgQmVuZm9yZCB24bubaSBt4buZdCBz4buRIGLhu5kgZOG7ryBsaeG7h3UgY+G7pSB0aOG7gy4gDQoNClRyxrDhu5tjIGjhur90IGNow7puZyB0YSB2aeG6v3QgbeG7mXQgaMOgbSBuaOG6rW4gaW5wdXQgbMOgIGNodeG7l2kgY8OhYyBz4buRIHThu6sgMSDEkeG6v24gOSAtIGzDoCBjaOG7ryBz4buRIOG7nyB24buLIHRyw60gdGjhu6kgbmjhuqV0IHThu6sgbeG7mXQgY2h14buXaSBz4buRIGLhuqV0IGvDrCB2w6AgaGnhu4NuIHRo4buLIHThuqduIHN14bqldCB4deG6pXQgaGnhu4duIGPhu6dhIGNow7puZyDEkeG7k25nIHRo4budaSBzbyBzw6FuaCB04bqnbiBzdeG6pXQgdGjhu7FjIHThur8gduG7m2kgdOG6p24gc3XhuqV0IMSRxrDhu6NjIGThu7EgYsOhbyBi4bufaSBjw7RuZyB0aOG7qWMgQmVuZm9yZDogDQoNCg0KYGBge3J9DQpjb21wYXJlX3dpdGhfYmVuZm9yZCA8LSBmdW5jdGlvbih4KSB7DQogIA0KICB0aWJibGUoZGlnaXQgPSB4KSAlPiUgDQogICAgbmEub21pdCgpICU+JSANCiAgICBncm91cF9ieShkaWdpdCkgJT4lIA0KICAgIGNvdW50KCkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgbXV0YXRlKGRpZ2l0ID0gYXMuZmFjdG9yKGRpZ2l0KSkgJT4lIA0KICAgIGdncGxvdChhZXMoZGlnaXQsIG4gLyBzdW0obikpKSArIA0KICAgIGdlb21fY29sKHdpZHRoID0gMC42NSwgZmlsbCA9ICJzdGVlbGJsdWUiKSArIA0KICAgIGdlb21fbGluZShkYXRhID0gZGZfYmVuZm9yZCwgYWVzKGFzLm51bWVyaWMoZGlnaXQpLCBwcm9iKSwgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDEpICsgDQogICAgZ2VvbV9wb2ludChkYXRhID0gZGZfYmVuZm9yZCwgYWVzKGFzLm51bWVyaWMoZGlnaXQpLCBwcm9iKSwgY29sb3IgPSAiZmlyZWJyaWNrIiwgc2l6ZSA9IDIpICsgDQogICAgbXlfdGhlbWUoKSArIA0KICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpDQoNCn0NCmBgYA0KDQpW4bubaSBjaHXhu5dpIHBvcHRvdGFsIHThu6sgYuG7mSBz4buRIGxp4buHdSAqKm1pZHdlc3QqKiDEkWkga8OobSB24bubaSB0aMawIHZp4buHbiAqKmdncGxvdDIqKjogDQoNCg0KYGBge3J9DQptaWR3ZXN0ICU+JSANCiAgcHVsbChwb3B0b3RhbCkgJT4lIA0KICBhcy5jaGFyYWN0ZXIoKSAlPiUgDQogIHN0cl9zdWIoc3RhcnQgPSAxLCBlbmQgPSAxKSAtPiB0b3RhbHBvcA0KDQpjb21wYXJlX3dpdGhfYmVuZm9yZCh4ID0gdG90YWxwb3ApICsgDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmUgMzogRnJlcXVlbmN5IG9mIG9jY3VycmVuY2VzLCBwb3B0b3RhbCAoNDM3IG9icykiLCANCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogRGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gb2YgbWlkd2VzdCBjb3VudGllcyIpDQoNCmBgYA0KDQpSw7UgcsOgbmcgcGjDom4gYuG7kSBj4bunYSBjw6FjIGNo4buvIHPhu5EgY2jGsGEgZml0IGzhuq9tIHbhu5tpIMSR4buLbmggbHXhuq10IEJlbmZvcmQuIMSQaeG7gXUgbsOgeSBjw7MgdGjhu4MgbuG6sW0g4bufIG5ndXnDqm4gbmjDom4gbMOgIG3hu5tpIGNo4buJIGPDsyA0MzcgcXVhbiBzw6F0IC0gbeG7mXQgc+G7kSBsxrDhu6NuZyBjaMawYSDEkeG7pyBuaGnhu4F1LiBO4bq/dSBxdXkgbcO0IGThu68gbGnhu4d1IHTEg25nIGzDqm4gdGjDrCBt4bupYyDEkeG7mSBmaXQgduG7m2kgxJHhu4tuaCBsdeG6rXQgQmVuZm9yZCBjw7MgdGjhu4MgY2FvIGjGoW46IA0KDQpgYGB7cn0NCm1pZHdlc3QgJT4lIA0KICBzZWxlY3QoY29udGFpbnMoInBvcCIpKSAlPiUgDQogIHNlbGVjdCgzOjcpICU+JSANCiAgZ2F0aGVyKGEsIGIpICU+JSANCiAgZmlsdGVyKGIgIT0gMCkgJT4lIA0KICBwdWxsKGIpICU+JSANCiAgYXMuY2hhcmFjdGVyKCkgJT4lIA0KICBzdHJfc3ViKHN0YXJ0ID0gMSwgZW5kID0gMSkgLT4gcG9wZGVuX2FsbF90eXBlcw0KDQpjb21wYXJlX3dpdGhfYmVuZm9yZCh4ID0gcG9wZGVuX2FsbF90eXBlcykgKyANCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyZSA0OiBGcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZXMsIHBvcHRvdGFsIGZvciBhbGwgdHlwZXMgKDIxODAgb2JzKSIsIA0KICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBEZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiBvZiBtaWR3ZXN0IGNvdW50aWVzIikNCg0KYGBgDQoNCg0KVuG6rXkgbMOgIGtoaSBz4buRIHF1YW4gc8OhdCB0xINuZyBsw6puIDIxODAgdGjDrCB04bqnbiBzdeG6pXQgeHXhuqV0IGhp4buHbiBj4bunYSBjw6FjIHPhu5EgdOG7qyAxIMSR4bq/biA5IHRp4buHbSBj4bqtbiBoxqFuIHbhu5tpIGPDoWMgdOG6p24gc3XhuqV0IGThu7EgYsOhbyBi4bufaSBxdXkgbHXhuq10IEJlbmZvcmQuIENow7puZyB0YSBs4bqhaSB4w6l0IG3hu5l0IGLhu5kgZOG7ryBsaeG7h3Uga2jDoWMgbMOgIHR4aG91c2luZyB0cm9uZyDEkcOzIHZvbHVtZSBsw6AgdOG7lW5nIGdpw6EgdHLhu4sgZG9hbmggdGh1IGPDoWMgdGjGsMahbmcgduG7pSBnaWFvIGThu4tjaCBi4bqldCDEkeG7mW5nIHPhuqNuIOG7nyBUZXhhczogDQoNCg0KYGBge3J9DQoNCnR4aG91c2luZyAlPiUgDQogIHB1bGwodm9sdW1lKSAlPiUgDQogIGFzLmNoYXJhY3RlcigpICU+JSANCiAgc3RyX3N1YihzdGFydCA9IDEsIGVuZCA9IDEpIC0+IHZvbHVtZQ0KDQpjb21wYXJlX3dpdGhfYmVuZm9yZCh4ID0gdm9sdW1lKSArIA0KICAgIGxhYnModGl0bGUgPSAiRmlndXJlIDU6IEZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlcywgdm9sdW1lICg4NjAyIG9icykiLCANCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogSW5mb3JtYXRpb24gYWJvdXQgdGhlIGhvdXNpbmcgbWFya2V0IGluIFRleGFzIHByb3ZpZGVkIGJ5IHRoZSBUQU1VIHJlYWwgZXN0YXRlIGNlbnRlci4iKQ0KDQoNCmBgYA0KDQoNCkPDoWMgYuG6sW5nIGNo4bupbmcgdGjhu7FjIG5naGnhu4dtIOG7nyB0csOqbiB24buBIHThuqduIHN14bqldCBzdeG6pXQgeHXhuqV0IGhp4buHbiBjw6FjIGNo4buvIHPhu5EgdOG7qyAxIMSR4bq/biA5IHRyb25nIHRo4bq/IGdp4bubaSB0aOG7sWMgY8OzIHbhursgeMOhYyBuaOG6rW4gxJHhu4tuaCBsdeG6rXQgQmVuZm9yZC4gVsOgIGLhu59pIHbhuq15IMSR4buLbmggbHXhuq10IG7DoHkgY8OzIG5oaeG7gXUg4bupbmcgZOG7pW5nLiBN4buZdCB0cm9uZyBuaOG7r25nIOG7qW5nIGThu6VuZyDEkcOzIGzDoCB4w6FjIMSR4buLbmggY8OhYyBiw6FvIGPDoW8gdMOgaSBjaMOtbmggZ2lhbiBs4bqtbiBj4bunYSBjw7RuZyB0aSBFbnJvbi4gQuG6oW4gxJHhu41jIGPDsyB0aOG7gyB0aGFtIGto4bqjbyBiw6BpIHZp4bq/dCBj4bunYSAqKlRoZSBXYWxsIFN0cmVldCBKb3VybmFsKiogduG7gSDDoXAgZOG7pW5nIEJlbmZvcmQgbGF3IHbhu5tpIHTDrG5oIGh14buRbmcgY+G7p2EgRW5yb24gW+G7nyDEkcOieV0oaHR0cHM6Ly93d3cud3NqLmNvbS9hcnRpY2xlcy9hY2NvdW50YW50cy1pbmNyZWFzaW5nbHktdXNlLWRhdGEtYW5hbHlzaXMtdG8tY2F0Y2gtZnJhdWQtMTQxNzgwNDg4NikuIA0KDQojIEJlbmZvcmQgbGF3IGFzIGEgZmVhdHVyZSBlbmdpbmVlcmluZyB0ZWNobmlxdWUNCg0KxJDhu4tuaCBsdeG6rXQgQmVuZm9yZCBjw7MgdGjhu4MgxJHGsOG7o2Mgc+G7rSBk4bulbmcgbmjGsCBsw6AgbeG7mXQga8SpIHRodeG6rXQgZmVhdHVyZSBlbmdpbmVlcmluZyBjaG8gY8OhYyBtw7QgaMOsbmggTWFjaGluZSBMZWFybmluZy4gRMaw4bubaSDEkcOieSBjaMO6bmcgdGEgc+G6vSBzbyBzw6FuaCBjaOG6pXQgbMaw4bujbmcgZOG7sSBiw6FvL3Bow6JuIGxv4bqhaSBj4bunYSBtw7QgaMOsbmggcGjDom4gbG/huqFpIExvZ2lzdGljIENsYXNzaWZpZXIgY2hvIGhhaSB0w6xuaCBodeG7kW5nOiANCg0KMS4gVOG6oW8gcmEgY8OhYyBmZWF0dXJlcyBt4bubaSBi4bqxbmcgY8OhY2ggc+G7rSBk4bulbmcgQmVuZm9yZCBsYXcNCjIuIFPhu60gZOG7pW5nIGNo4buJIGPDoWMgZmVhdHVyZXMgbmd1ecOqbiBi4bqjbi4gDQoNCkLhu5kgZOG7ryBsaeG7h3Ugc+G7rSBk4bulbmcgbMOgICoqY3JlZGl0Y2FyZC5jc3YqKiB04burIGN14buZYyB0aGkgKipDcmVkaXQgQ2FyZCBGcmF1ZCBEZXRlY3Rpb24qKiB2w6AgY8OzIHRo4buDIGRvd25sb2FkIFt04bqhaSDEkcOieV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9tbGctdWxiL2NyZWRpdGNhcmRmcmF1ZCkuIOG7niDEkcOieSBjaMO6bmcgdGEgc+G6vSB04bqhbyByYSBjw6FjIGZlYXR1cmVzIG3hu5tpIHThu6sgQW1vdW50IC0gbeG7mXQgZmVhdHVyZSBz4bq1biBjw7MgY+G7p2EgYuG7mSBk4buvIGxp4buHdS4gVHLGsOG7m2MgaOG6v3QgdGEgc28gc8OhbmggdOG6p24gc3XhuqV0IHh1w6J0IGhp4buHbiBjw6FjIGNo4buvIHPhu5EgdOG7qyAxIMSR4bq/biA5OiANCg0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhOiANCmRhdGEgPC0gcmVhZF9jc3YoImNyZWRpdGNhcmQuY3N2IikNCg0KIyBEYXRhIG1hbmlwdWxhdGlvbjogDQoNCmRhdGEgJT4lIA0KICBtdXRhdGUoQ2xhc3MgPSBjYXNlX3doZW4oQ2xhc3MgPT0gMSB+ICJGcmF1ZCIsIFRSVUUgfiAiTm9uRnJhdWQiKSkgJT4lIA0KICBtdXRhdGUoQW1vdW50X3RleHQgPSBhcy5jaGFyYWN0ZXIoQW1vdW50KSkgJT4lIA0KICBtdXRhdGUoZGlnaXRfMCA9IGNhc2Vfd2hlbighc3RyX2RldGVjdChBbW91bnRfdGV4dCwgIlxcLiIpIH4gIlllcyIsIFRSVUUgfiAiTm8iKSkgJT4lIA0KICBtdXRhdGUoZGlnaXQgPSBzdHJfc3ViKEFtb3VudF90ZXh0LCBzdGFydCA9IDEsIGVuZCA9IDEpKSAtPiBkYXRhDQoNCiMgQ2FsY3VsYXRlIGZyZXF1ZW5jaWVzIGFuZCBqb2luIGRhdGFzZXRzOiANCg0KZGF0YSAlPiUgDQogIGZpbHRlcihkaWdpdCAhPSAiMCIpICU+JSANCiAgZ3JvdXBfYnkoZGlnaXQpICU+JSANCiAgY291bnQoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShhY3R1YWxfcmF0ZSA9IG4gLyBzdW0obikpICU+JSANCiAgZnVsbF9qb2luKGRmX3Bsb3QgJT4lIHNlbGVjdChkaWdpdCwgcHJvYikpICU+JSANCiAgbXV0YXRlKGRpZmZfZnJlcSA9IGFjdHVhbF9yYXRlIC0gcHJvYikgLT4gZGlnaXRfZnJvbV9jcmVkaXRkYXRhDQoNCiMgQ29tcGFyZSBhY3R1YWwgRnJlcXVlbmN5IGFuZCBCZW5mb3JkJ3MgbGF3OiANCg0KeCA8LSBkYXRhICU+JSANCiAgZmlsdGVyKGRpZ2l0ICE9ICIwIikgJT4lIA0KICBwdWxsKGRpZ2l0KQ0KDQpjb21wYXJlX3dpdGhfYmVuZm9yZCh4KSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDAuNDIsIDAuMDUpLCBleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDAuNDIpLCBsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHRpdGxlID0gIkZpZ3VyZSA2OiBBY3R1YWwgZnJlcXVlbmN5IGNvbXBhcmVkIHdpdGggQmVuZm9yZCdzIGxhdyIsIA0KICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBDcmVkaXQgQ2FyZCBGcmF1ZCBEZXRlY3Rpb24sIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20iKQ0KYGBgDQoNCg0KVOG7qyBGaWd1cmUgNiByw7UgcsOgbmcgdOG6p24gc3XhuqV0IHh14bqldCBoaeG7h24gY+G7p2Egc+G7kSAxIGzDoCB0xrDGoW5nIMSR4buRaSBjYW8gKGdp4buRbmcgbmjGsCB0w6xuaCBodeG7kW5nIHbhu4EgY8OhYyBz4buRIGxp4buHdSB0w6BpIGNow61uaCBnaWFuIGzhuq1uIGPhu6dhIGPDtG5nIHRpIEVucm9uKS4gQ2jDum5nIHRhIHRo4butIGtp4buDbSB0cmEgc+G7sSBraMOhYyBiaeG7h3QgduG7gSB04buJIGzhu4cgY8OhYyBnaWFvIGThu4tjaCBnaWFuIGzhuq1uIGdp4buvYSBuaMOzbSBjw7MgdsOgIGtow7RuZyB4deG6pXQgaGnhu4duIGNo4buvIHPhu5EgMTogDQoNCmBgYHtyfQ0KZGF0YSAlPiUgDQogIG11dGF0ZShkaWdpdF8xID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoZGlnaXQsICIxIikgfiAiWWVzIiwgVFJVRSB+ICJObyIpKSAlPiUgDQogIGdyb3VwX2J5KENsYXNzLCBkaWdpdF8xKSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzcHJlYWQodmFsdWUgPSAibiIsIGtleSA9ICJDbGFzcyIpICU+JSANCiAgbXV0YXRlKHJhdGUgPSBGcmF1ZCAvIChGcmF1ZCArIE5vbkZyYXVkKSkgJT4lIA0KICBtdXRhdGUoZGlmZiA9IHJhdGVbMl0gLyByYXRlWzFdKQ0KYGBgDQoNClLDtSByw6BuZyBuaMOzbSBnaWFvIGThu4tjaCBjw7MgeHXhuqV0IGhp4buHbiBjaOG7ryBz4buRIDEgY8OzIHThu4kgbOG7hyBnaWFvIGThu4tjaCBnaWFuIGzhuq1uIGNhbyBoxqFuIDI4JSBzbyB24bubaSBuaMOzbSBraMO0bmcgY8OzIGNo4buvIHPhu5EgMS4gTeG6t2MgZMO5IEJlbmZvcmQgbGF3IGtow7RuZyBjb3ZlciBjaG8gY2jhu68gc+G7kSAwIG5oxrBuZyBjaMO6bmcgdGEgduG6q24gbsOqbiDEkWnhu4F1IHRyYSB4ZW0gZ2nhu69hIG5ow7NtIGPDsyBjaOG7ryBz4buRIDAgdsOgIHBo4bqnbiBjw7JuIGzhuqFpIGPDsyBraMOhYyBiaeG7h3QgxJHDoW5nIGvhu4MgaGF5IGtow7RuZyB24buBIHThu4kgbOG7hyBjw6FjIGdpYW8gZOG7i2NoIHTDoGkgY2jDrW5oIGdpYW4gbOG6rW46IA0KDQoNCmBgYHtyfQ0KZGF0YSAlPiUgDQogIG11dGF0ZShkaWdpdF8wID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoZGlnaXQsICIwIikgfiAiWWVzIiwgVFJVRSB+ICJObyIpKSAlPiUgDQogIGdyb3VwX2J5KENsYXNzLCBkaWdpdF8wKSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzcHJlYWQodmFsdWUgPSAibiIsIGtleSA9ICJDbGFzcyIpICU+JSANCiAgbXV0YXRlKHJhdGUgPSBGcmF1ZCAvIChGcmF1ZCArIE5vbkZyYXVkKSkgJT4lIA0KICBtdXRhdGUoZGlmZiA9IHJhdGVbMl0gLyByYXRlWzFdKQ0KDQpgYGANCg0KDQpL4bq/dCBxdeG6oyBuw6B5IGNobyB0aOG6pXkgY8OhYyBnaWFvIGThu4tjaCBjw7MgY2jhu68gc+G7kSAwIGPDsyB04buJIGzhu4cgZ2lhbyBk4buLY2ggZ2lhbiBs4bqtbiBjYW8gaMahbiAxNTYlIHNvIHbhu5tpIG5ow7NtIGPDsm4gbOG6oWkuIENow7puZyBsw6AgY8OhYyBnaWFvIGThu4tjaCBxdXkgbcO0IG5o4buPIGPDsyBnacOhIHRy4buLIGdpYW8gZOG7i2NoIGNoxrBhIMSR4bq/biAxIGhv4bq3YyBi4bqxbmcgMCBuaMawIGNow7puZyB0YSBjw7MgdGjhu4MgdGjhuqV5OiANCg0KYGBge3J9DQpkYXRhICU+JSANCiAgbXV0YXRlKGRpZ2l0XzAgPSBjYXNlX3doZW4oc3RyX2RldGVjdChkaWdpdCwgIjAiKSB+ICJZZXMiLCBUUlVFIH4gIk5vIikpICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3QoZGlnaXQsICIwIikpICU+JSANCiAgc2VsZWN0KEFtb3VudCwgZGlnaXRfMCkgJT4lIA0KICBzYW1wbGVfbigxMCkNCmBgYA0KDQpU4burIGPDoWMgcGjDom4gdMOtY2ggdHLDqm4gY2jDum5nIHRhIG3huqFuaCBk4bqhbiDEkcawYSByYSBt4buZdCBnaeG6oyDEkeG7i25oIHLhurFuZyAqZmVhdHVyZSBt4bubaSBjaG8gYmnhur90IHRow7RuZyB0aW4gduG7gSBnacOhIHRy4buLIGPhu6dhIGNo4buvIHPhu5EgeHXhuqV0IGhp4buHbiDhu58gdGhlIGZpcnN0IGRpZ2l0IGPDsyB0aOG7gyBsw6AgbeG7mXQgY2jhu4kgYsOhbyB04buRdCwgaG/hurdjIGNow60gw610IGzDoCBjw7Mgw70gbmdoxKlhIHRyb25nIHZp4buHYyBwaMOibiBiaeG7h3QgZ2lhbyBk4buLY2ggZ2lhbiBs4bqtbiouIERvIHbhuq15IGNow7puZyB0YSB04bqhbyByYSBoYWkgYuG7mSBz4buRIGxp4buHdSBuaMawIHNhdTogDQoNCjEuIELhu5kgZOG7ryBsaeG7h3Ugbmd1ecOqbiBi4bqjbi4gDQoyLiBC4buZIGThu68gbGnhu4d1IGPDsyBjw6FjIGZlYXR1cmVzIG3hu5tpIGThu7FhIHRyw6puIEJlbmZvcmQgbGF3LiANCg0KRMaw4bubaSDEkcOieSBsw6AgUiBjb2RlczogDQoNCmBgYHtyfQ0KIyBUcmFuc2Zvcm0gZGF0YTogDQoNCmRhdGEgJT4lIA0KICBtdXRhdGUoZGlnaXRfMCA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KGRpZ2l0LCAiMCIpIH4gIlllcyIsIFRSVUUgfiAiTm8iKSkgJT4lIA0KICBtdXRhdGUoZGlnaXRfMCA9IGFzLmZhY3RvcihkaWdpdF8wKSwgQ2xhc3MgPSBhcy5mYWN0b3IoQ2xhc3MpKSAlPiUNCiAgc2VsZWN0KC1UaW1lLCAtIEFtb3VudF90ZXh0KSAlPiUgDQogIGZ1bGxfam9pbihkaWdpdF9mcm9tX2NyZWRpdGRhdGEgJT4lIHNlbGVjdChkaWdpdCwgZGlmZl9mcmVxKSkgJT4lIA0KICBtdXRhdGUoZGlmZl9mcmVxID0gcmVwbGFjZV9uYShkaWZmX2ZyZXEsIDApLCBkaWdpdCA9IGFzLmZhY3RvcihkaWdpdCkpIC0+IGRmX21vZGVsbGluZw0KDQojIFNwbGl0IGRhdGE6DQoNCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxKQ0KaWQgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGZfbW9kZWxsaW5nJENsYXNzLCBwID0gMC4yLCBsaXN0ID0gRkFMU0UpDQoNCmRmX21vZGVsbGluZyAlPiUgc2xpY2UoaWQpIC0+IHRyYWluX2JlbmZvcmQNCmRmX21vZGVsbGluZyAlPiUgc2xpY2UoLWlkKSAtPiB0ZXN0X2JlbmZvcmQNCg0KdHJhaW5fYmVuZm9yZCAlPiUgc2VsZWN0KC1kaWdpdF8wLCAtZGlnaXQsIC1kaWZmX2ZyZXEpIC0+IHRyYWluX25vbmUNCnRlc3RfYmVuZm9yZCAlPiUgc2VsZWN0KC1kaWdpdF8wLCAtZGlnaXQsIC1kaWZmX2ZyZXEpIC0+IHRlc3Rfbm9uZQ0KYGBgDQoNCkh14bqlbiBsdXnhu4duIExvZ2lzdGljIENsYXNzaWZpZXIgduG7m2kgaGFpIGLhu5kgZOG7ryBsaeG7h3UgxJHDoyDEkcaw4bujYyBjaHXhuqluIGLhu4sgdsOgIHPhu60gZOG7pW5nIGPDoWMgbW9kZWwgY8OzIMSRxrDhu6NjIHRo4buxYyBoaeG7h24gZOG7sSBiw6FvIHjDoWMgc3XhuqV0IGNobyBz4buxIGtp4buHbiAiZ2lhbyBk4buLY2ggZ2lhbiBs4bqtbiIgdHLDqm4gYuG7mSBk4buvIGxp4buHdSB0ZXN0OiANCg0KYGBge3J9DQojIFNldCBjb25kaXRpb25zIGZvciB0cmFpbmluZyBtb2RlbCBhbmQgY3Jvc3MtdmFsaWRhdGlvbjogDQoNCnNldC5zZWVkKDEpDQpudW1iZXIgPC0gMw0KcmVwZWF0cyA8LSAxDQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAicmVwZWF0ZWRjdiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gbnVtYmVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdHMgPSByZXBlYXRzLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnlGdW5jdGlvbiA9IHR3b0NsYXNzU3VtbWFyeSwgDQogICAgICAgICAgICAgICAgICAgICAgICBhbGxvd1BhcmFsbGVsID0gVFJVRSkNCg0KIyBQYXJhbGxlbCBDb21wdXRpbmcgd2l0aCBtdWx0aWNvcmVzIGZyb20gb3VyIGNvbXB1dGVyOiANCg0KbGlicmFyeShwYXJhbGxlbCkNCm5Db3JlcyA8LSBkZXRlY3RDb3Jlcyhsb2dpY2FsID0gVFJVRSkNCm5UaHJlYWRzIDwtIGRldGVjdENvcmVzKGxvZ2ljYWwgPSBUUlVFKQ0KY2wgPC0gbWFrZUNsdXN0ZXIoblRocmVhZHMpDQoNCg0KIyBMb2dpc3RpYyBDbGFzc2lmaWVyIHdpdGggZmVhdHVyZSBlbmdpbmVlcmluZyB1c2luZyBCZW5mb3JkIGxhdzogDQoNCm1vZGVsIDwtICJnbG0iDQoNCnNldC5zZWVkKDEpDQpnbG0xIDwtIHRyYWluKENsYXNzIH4uLCANCiAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2JlbmZvcmQgJT4lIHNlbGVjdCgtQW1vdW50KSwgDQogICAgICAgICAgICAgIHRyQ29udHJvbCA9IGNvbnRyb2wsDQogICAgICAgICAgICAgIG1ldGhvZCA9IG1vZGVsLA0KICAgICAgICAgICAgICBtZXRyaWMgPSAiUk9DIikNCg0KIyBMb2dpc3RpYyBDbGFzc2lmaWVyIHdpdGggb3JpZ2luYWwgZGF0YTogDQoNCnNldC5zZWVkKDEpDQpnbG0yIDwtIHRyYWluKENsYXNzIH4uLCANCiAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX25vbmUsIA0KICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjb250cm9sLA0KICAgICAgICAgICAgICBtZXRob2QgPSBtb2RlbCwgDQogICAgICAgICAgICAgIG1ldHJpYyA9ICJST0MiKQ0KDQoNCiMgVXNlIG1vZGVscyBmb3IgcHJlZGljdGluZyBwcm9iYWJpbGl0eSBvZiBmcmF1ZDogDQoNCnByZWRpY3QoZ2xtMSwgdGVzdF9iZW5mb3JkICU+JSBzZWxlY3QoLUFtb3VudCksIHR5cGUgPSAicHJvYiIpICU+JSBwdWxsKEZyYXVkKSAtPiBwZDENCnByZWRpY3QoZ2xtMiwgdGVzdF9ub25lLCB0eXBlID0gInByb2IiKSAlPiUgcHVsbChGcmF1ZCkgLT4gcGQyDQpgYGANCg0KVsOsIG3hu6ljIMSR4buZIGNow61uaCB4w6FjIGNobyBk4buxIGLDoW8gbeG7mXQgZ2lhbyBk4buLY2ggZ2lhbiBs4bqtbiB0aOG7sWMgc+G7sSBsw6AgZ2lhbiBs4bqtbiAoZMawxqFuZyB0w61uaCB0aOG6rXQpIHBo4bulIHRodeG7mWMgdsOgbyBuZ8aw4buhbmcgKFRocmVzaG9sZCBoYXkgQ3V0b2ZmKSBuw6puIGNow7puZyB0YSBraOG6o28gc8OhdCBjaOG6pXQgbMaw4bujbmcgY+G7p2EgaGFpIG3DtCBow6xuaCDEkeG7k25nIHRo4budaSB0csOqbiBt4buZdCBsb+G6oXQgbmfGsOG7oW5nIMSRxrDhu6NjIGzhu7FhIGNo4buNbjogDQoNCg0KYGBge3J9DQojIEZ1bmN0aW9uIGNhbGN1bGF0ZXMgbW9kZWwgcGVyZm9ybWFuY2VzIGJ5IGN1dG9mZiBmb3IgbW9kZWwgMTogDQoNCmJ5Q3V0b2ZmX3JmMSA8LSBmdW5jdGlvbihjdXRvZmYpIHsNCiAgDQogIHByZWQgPC0gY2FzZV93aGVuKHBkMSA+PSBjdXRvZmYgfiAiRnJhdWQiLCBUUlVFIH4gIk5vbkZyYXVkIikgJT4lIGFzLmZhY3RvcigpDQogIA0KICBjb25mdXNpb25NYXRyaXgocHJlZCwgdGVzdF9iZW5mb3JkJENsYXNzLCBwb3NpdGl2ZSA9ICJGcmF1ZCIpIC0+IGNtDQogIA0KICBjbSR0YWJsZSAlPiUgYXMudmVjdG9yKCkgLT4gYmcNCiAgY20kb3ZlcmFsbCAlPiUgYXMudmVjdG9yKCkgLT4gYWNjDQogIGNtJGJ5Q2xhc3MgJT4lIGFzLnZlY3RvcigpIC0+IHNlbg0KICANCiAgZGF0YS5mcmFtZShCQiA9IGJnWzFdLCANCiAgICAgICAgICAgICBCRyA9IGJnWzJdLCANCiAgICAgICAgICAgICBHQiA9IGJnWzNdLCANCiAgICAgICAgICAgICBHRyA9IGJnWzRdLCANCiAgICAgICAgICAgICBBY2N1cmFjeSA9IGFjY1sxXSwNCiAgICAgICAgICAgICBLYXBwYSA9IGFjY1syXSwNCiAgICAgICAgICAgICBSZWNhbGwgPSBzZW5bMV0sDQogICAgICAgICAgICAgU3BlY2lmaWNpdHkgPSBzZW5bMl0sIA0KICAgICAgICAgICAgIEN1dG9mZiA9IGN1dG9mZikgLT4gbW9kZWxfcGVyQ3V0b2ZmDQogIA0KICByZXR1cm4obW9kZWxfcGVyQ3V0b2ZmKQ0KICANCn0NCg0KDQojIEZ1bmN0aW9uIGNhbGN1bGF0ZXMgbW9kZWwgcGVyZm9ybWFuY2VzIGJ5IGN1dG9mZiBmb3IgbW9kZWwgMjogDQoNCmJ5Q3V0b2ZmX3JmMiA8LSBmdW5jdGlvbihjdXRvZmYpIHsNCiAgDQogIHByZWQgPC0gY2FzZV93aGVuKHBkMiA+PSBjdXRvZmYgfiAiRnJhdWQiLCBUUlVFIH4gIk5vbkZyYXVkIikgJT4lIGFzLmZhY3RvcigpDQogIA0KICBjb25mdXNpb25NYXRyaXgocHJlZCwgdGVzdF9iZW5mb3JkJENsYXNzLCBwb3NpdGl2ZSA9ICJGcmF1ZCIpIC0+IGNtDQogIA0KICBjbSR0YWJsZSAlPiUgYXMudmVjdG9yKCkgLT4gYmcNCiAgY20kb3ZlcmFsbCAlPiUgYXMudmVjdG9yKCkgLT4gYWNjDQogIGNtJGJ5Q2xhc3MgJT4lIGFzLnZlY3RvcigpIC0+IHNlbg0KICANCiAgZGF0YS5mcmFtZShCQiA9IGJnWzFdLA0KICAgICAgICAgICAgIEJHID0gYmdbMl0sDQogICAgICAgICAgICAgR0IgPSBiZ1szXSwNCiAgICAgICAgICAgICBHRyA9IGJnWzRdLCANCiAgICAgICAgICAgICBBY2N1cmFjeSA9IGFjY1sxXSwNCiAgICAgICAgICAgICBLYXBwYSA9IGFjY1syXSwNCiAgICAgICAgICAgICBSZWNhbGwgPSBzZW5bMV0sIA0KICAgICAgICAgICAgIFNwZWNpZmljaXR5ID0gc2VuWzJdLCANCiAgICAgICAgICAgICBDdXRvZmYgPSBjdXRvZmYpIC0+IG1vZGVsX3BlckN1dG9mZg0KICANCiAgcmV0dXJuKG1vZGVsX3BlckN1dG9mZikNCiAgDQp9DQoNCmBgYA0KDQpS4buTaSBz4butIGThu6VuZyBoYWkgaMOgbSB0csOqbjogDQoNCmBgYHtyfQ0KIyBVc2UgdGhpcyBmdWN0aW9uOiANCg0KbGFwcGx5KHNlcSgwLjA1LCAwLjc1LCAwLjA1KSwgYnlDdXRvZmZfcmYxKSAtPiBsaXN0MQ0KbGFwcGx5KHNlcSgwLjA1LCAwLjc1LCAwLjA1KSwgYnlDdXRvZmZfcmYyKSAtPiBsaXN0Mg0KDQojIENvbnZlcnQgdG8gZGF0YSBmcmFtZSBhbmQgY29tYmluZSB0aGUgdHdvIGRhdGFzZXRzOiANCg0KZG8uY2FsbCgiYmluZF9yb3dzIiwgbGlzdDEpIC0+IGRmMQ0KZG8uY2FsbCgiYmluZF9yb3dzIiwgbGlzdDIpIC0+IGRmMg0KDQpiaW5kX3Jvd3MoZGYxICU+JSBtdXRhdGUoYEZlYXR1cmUgRW5naW5lZXJpbmdgID0gIkJlbmZvcmQiKSwgDQogICAgICAgICAgZGYyICU+JSBtdXRhdGUoYEZlYXR1cmUgRW5naW5lZXJpbmdgID0gIk5vbmUiKSkgLT4gZGZfY29tcGFyaXNpb24NCmBgYA0KDQoNClNvIHPDoW5oIGNo4bqldCBsxrDhu6NuZyBj4bunYSBoYWkgbcO0IGjDrG5oIHBow6JuIGxv4bqhaSB0xrDGoW5nIOG7qW5nIHbhu5tpIG3hu5l0IGxv4bqhdCBuZ8aw4buhbmcgxJHGsOG7o2MgbOG7sWEgY2jhu41uOiANCg0KDQpgYGB7cn0NCiMgQ29tcGFyZSBtb2RlbCBwZXJmb3JtYW5jZSBieSBGZWF0dXJlIEVuZ2luZWVyaW5nIE1ldGhvZDogDQoNCm15X2NvbG9ycyA8LSBjKCJmaXJlYnJpY2siLCAic3RlZWxibHVlIikNCg0KZGZfY29tcGFyaXNpb24gJT4lIA0KICBzZWxlY3QoNToxMCkgJT4lIA0KICBnYXRoZXIoTWV0cmljLCBWYWx1ZSwgLUN1dG9mZiwgLWBGZWF0dXJlIEVuZ2luZWVyaW5nYCkgJT4lIA0KICBnZ3Bsb3QoYWVzKEN1dG9mZiwgVmFsdWUsIGNvbG9yID0gYEZlYXR1cmUgRW5naW5lZXJpbmdgKSkgKyANCiAgZ2VvbV9saW5lKCkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsgDQogIGZhY2V0X3dyYXAofiBNZXRyaWMsIHNjYWxlcyA9ICJmcmVlIikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgDQogIG15X3RoZW1lKCkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLjA1LCAwLjc1LCAwLjEpKSArIA0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgDQogICAgICAgdGl0bGUgPSAiRmlndXJlIDc6IE1vZGVsIFBlcmZvcm1hbmNlIGJ5IEZlYXR1cmUgRW5naW5lZXJpbmcgTWV0aG9kIiwgDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IENyZWRpdCBDYXJkIEZyYXVkIERldGVjdGlvbiAoaHR0cHM6Ly93d3cua2FnZ2xlLmNvbSkiKQ0KDQpgYGANCg0KRmlndXJlIDcgY2hvIHRo4bqleSB04bqhaSB04bqldCBj4bqjIGPDoWMgbmfGsOG7oW5nIGNo4bqldCBsxrDhu6NuZyBwaMOibiBsb+G6oWkgKMSR4bq3YyBiaeG7h3QgbMOgIGto4bqjIG7Eg25nIHjDoWMgbWluaCDEkcO6bmcgZ2lhbyBk4buLY2ggZ2lhbiBs4bqtbikgY+G7p2EgbcO0IGjDrG5oIHPhu60gZOG7pW5nIGZlYXR1cmVzIMSRxrDhu6NjIGV4dHJhY3QgdOG7qyBBbW91bnQgZOG7sWEgdHLDqm4gQmVuZm9yZCBsYXcgbMOgIHThu5F0IGjGoW4uIA0KDQoNCiMgQ29uY2x1c2lvbg0KDQpDw6FjIGPGoSBxdWFuIGNodXnDqm4gduG7gSDEkWnhu4F1IHRyYSBjw6FjIGdpYW4gbOG6rW4gduG7gSB0aHXhur8g4bufIGNow6J1IMOCdSDEkcOjIGPDtG5nIGtoYWkgdGjhu6thIG5o4bqtbiB2aeG7h2Mgw6FwIGThu6VuZyDEkeG7i25oIGx14bqtdCBCZW5mb3JkIGNobyDEkWnhu4F1IHRyYSBrxKkgaMahbiBjw6FjIGNhc2VzIGdpYW4gbOG6rW4uIEPDoWMgY8O0bmcgdGkga2nhu4NtIHRvw6FuIChBdWRpdGluZyBGaXJtcykgY8Wpbmcgw6FwIGThu6VuZyDEkeG7i25oIGx14bqtdCBuw6B5IHRyb25nIGPDoWMgbmdoaeG7h3AgduG7pSBsacOqbiBxdWFuIMSR4bq/biBob+G6oXQgxJHhu5luZyBraeG7g20gdG/DoW4gY8OhYyBz4buRIGxp4buHdSB0w6BpIGNow61uaCBi4buLIG5naGkgbmfhu50gbMOgIGdp4bqjIG3huqFvIGhv4bq3YyBi4buLIGLDs3AgbcOpby4g4bueIE3EqSB0aMOsIElSUyAoY8ahIHF1YW4gY2h1ecOqbiB0aHXhur8pIGtow7RuZyBwaOG7pyBuaOG6rW4gbmjGsG5nIGPFqW5nIGtow7RuZyB0aOG7q2Egbmjhuq1uIHZp4buHYyBz4butIGThu6VuZyDEkeG7i25oIGx14bqtdCBuw6B5IMSR4buDIHjDoWMgbWluaCBjw6FjIGdpYW4gbOG6rW4gduG7gSB0aHXhur8uIELDoGkgdmnhur90IG7DoHkgdHLDrG5oIGLDoHkgbeG7mXQgaMaw4bubbmcg4bupbmcgZOG7pW5nIG3hu5tpIGPhu6dhIMSR4buLbmggbHXhuq10IEJlbmZvcmQgbmjGsCBsw6AgbeG7mXQga8SpIHRodeG6rXQgY2hvIEZlYXR1cmUgRW5naW5lZXJpbmcvRXh0cmFjdGlvbi4gU+G7rSBk4bulbmcgTG9naXN0aWMgQ2xhc3NpZmllciBjaG8gYuG7mSBk4buvIGxp4buHdSAqKmNyZWRpdGNhcmQuY3N2KiogY2hvIHRo4bqleSBy4bqxbmcgY8OhYyBmZWF0dXJlcyDEkcaw4bujYyB04bqhbyByYSBi4bqxbmcgY8OhY2ggduG6rW4gZOG7pW5nIMSR4buLbmggbHXhuq10IEJlbmZvcmQgc+G6vSBuw6JuZyBjYW8gdOG7iSBs4buHIHjDoWMgbWluaCBjw6FjIGdpYW8gZOG7i2NoIHTDoGkgY2jDrW5oIGdpYW4gbOG6rW4uIFR1eSBuaGnDqm4gaGnhu4d1IGzhu7FjIGPhu6dhIGvEqSB0aHXhuq10IGZlYXR1cmUgZW5naW5lZXJpbmcgbsOgeSBjaG8gY8OhYyBtw7QgaMOsbmggaOG7jWMgbcOheSBraMOhYyBoYXkgY8OhY2ggdGluaCB2aSBoxqFuIGPhu6dhIHZp4buHYyB24bqtbiBk4bulbmcgxJHhu4tuaCBsdeG6rXQgQmVuZm9yZCBjaG8gZmVhcnVyZSBlbmdpbmVlcmluZyBsw6AgY2jGsGEgxJHGsOG7o2MgxJHhu4EgY+G6rXAgdHJvbmcgYsOgaSB2aeG6v3QgbsOgeS4gDQoNCg0KIyBSZWZlcmVuY2VzDQoNCjEuIFtBbiBJbnRyb2R1Y3Rpb24gdG8gQmVuZm9yZCdzIExhd10oaHR0cHM6Ly93d3cuYW1hem9uLmNvbS9JbnRyb2R1Y3Rpb24tQmVuZm9yZHMtTGF3LUFybm8tQmVyZ2VyL2RwLzA2OTExNjMwNjUvcmVmPXNyXzFfMz9kY2hpbGQ9MSZrZXl3b3Jkcz1CZW5mb3JkJUUyJTgwJTk5cytMYXcmcWlkPTE1OTgwNjk5MTImc3I9OC0zKS4gDQoyLiBbQXBwbGljYXRpb24gb2YgU3RhdGlzdGljYWwgTWV0aG9kcyBmb3IgVGF4IEluc3BlY3Rpb24gb2YgRW50ZXJwcmlzZXM6IEEgQ2FzZSBTdHVkeSBpbiBWaWV0bmFtXShodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2NoYXB0ZXIvMTAuMTAwNy85NzgtMy0zMTktNzMxNTAtNl81MSkuIA0KMy4gW0JlbmZvcmQncyBMYXc6IFRoZW9yeSwgVGhlIEdlbmVyYWwgTGF3IE9mIFJlbGF0aXZlIFF1YW50aXRpZXMsIEFuZCBGb3JlbnNpYyBGcmF1ZCBEZXRlY3Rpb24gQXBwbGljYXRpb25zXShodHRwczovL3d3dy5hbWF6b24uY29tL0JlbmZvcmRzLUxhdy1RdWFudGl0aWVzLURldGVjdGlvbi1BcHBsaWNhdGlvbnMvZHAvOTgxNDY1MTIwNi9yZWY9c3JfMV80P2RjaGlsZD0xJmtleXdvcmRzPUJlbmZvcmQlRTIlODAlOTlzK0xhdyZxaWQ9MTU5ODA2OTkxMiZzcj04LTQpLiANCjQuIFtGb3JlbnNpYyBBbmFseXRpY3M6IE1ldGhvZHMgYW5kIFRlY2huaXF1ZXMgZm9yIEZvcmVuc2ljIEFjY291bnRpbmcgSW52ZXN0aWdhdGlvbnNdKGh0dHBzOi8vd3d3LmFtYXpvbi5jb20vRm9yZW5zaWMtQW5hbHl0aWNzLVRlY2huaXF1ZXMtQWNjb3VudGluZy1JbnZlc3RpZ2F0aW9ucy9kcC8xMTE5NTg1NzY3L3JlZj1zcl8xXzc/ZGNoaWxkPTEma2V5d29yZHM9QmVuZm9yZCVFMiU4MCU5OXMrTGF3JnFpZD0xNTk4MDY5OTEyJnNyPTgtNykuIA0KNS4gW0JlbmZvcmTigJlzIExhdzogQXBwbHlpbmcgdG8gRXhpc3RpbmcgRGF0YV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vYmVuZm9yZHMtbGF3LWFwcGx5aW5nLXRvLWV4aXN0aW5nLWRhdGEvKS4gDQoNCg0KDQoNCg0KDQoNCg0KDQo=