Portfolio Theory Again

Portfolio Theory đã được mô tả và giải thích trực quan trong trong phần 1 tập trung vào hai vấn đề chính:

  1. Những tính toán ban đầu liên quan đến lí thuyết này bằng ngôn ngữ ma trận (lợi tức và rủi ro).
  2. Tìm danh mục đầu tư mà rủi ro là bé nhất (Global Minimum Variance - GMV Portfolio) dựa trên phương pháp mô phỏng.

Trước hết chúng ta lặp lại việc tìm GMV Portfolio từ 10000 danh mục như đã biết từ phần 1:

# Clear workspace: 
rm(list = ls())

# Some stocks selected (Microsoft, Starbucks and Apple): 

my_symbols <- c("MSFT", "SBUX", "GE")

# Number of stocks: 

n <- length(my_symbols)

# Collect data: 

library(tidyquant)
price_data <- tq_get(my_symbols,
                     from = "2018-01-01",
                     to = "2020-12-31",
                     get = "stock.prices")

# Calculate log return: 

library(tidyverse)

price_data %>%
  group_by(symbol) %>% 
  mutate(lag1 = lag(adjusted, n = 1L)) %>% 
  filter(!is.na(lag1)) %>% 
  mutate(daily_return = log(adjusted / lag1)) %>% 
  ungroup() %>% 
  select(symbol, date, daily_return) -> daily_returns

# Calculate risk and return for assets:
  
daily_returns %>% 
  group_by(symbol) %>% 
  summarise(risk = sd(daily_return), return = mean(daily_return)) -> df_risk_return

# Convert to wide form: 

daily_returns %>% 
  pivot_wider(names_from = symbol, values_from = daily_return) %>% 
  select(-date) -> returns_wide

# Asset mean return: 

mu <- colMeans(returns_wide) 

# Covariance matrix: 

sigma_matrix <- cov(returns_wide)

# Function calculates risk-return for a random-weight portfolio: 
library(rportfolios)

portfolio_risk_return <- function(j) {
  
  # Create random weights: 
  set.seed(j)
  
  X <- random.bounded(n = n, x.t = 1, x.l = rep(-1, n), x.u = rep(1, n))

  # Portfolio return: 
  mu_port_rand = crossprod(X, mu) %>% as.numeric()

  # Portfolio risk: 
  sig_port_rand <- t(X) %*% sigma_matrix %*% X %>% as.numeric() %>% sqrt()
  
  # Create a data frame of results: 
  df_poft <- tibble(symbol = my_symbols, 
                    weight = X, 
                    return = mu_port_rand, 
                    risk = sig_port_rand,
                    port_index = j)
  
  return(df_poft)

}

# Use the function and combine results in form of data frame: 

lapply(1:10000, portfolio_risk_return) -> all_return_port

do.call("bind_rows", all_return_port) -> df_return_10000

df_return_10000 %>% 
  filter(!duplicated(port_index)) -> df_return_mini_10000

# Minimum risk portfolio (GMV Portfolio) from simulation: 
min_risk <- df_return_mini_10000 %>% slice(which.min(risk)) 

GMV Portfolio có rủi ro - lợi tức và trọng số của các tài sản được trình bày ở Table 1:

Table 1: Minimum Risk Portfolio by Simulation
symbol weight return risk
MSFT 0.4500578 0.0009458 0.017933
SBUX 0.4588066 0.0009458 0.017933
GE 0.0911357 0.0009458 0.017933

Danh mục có rủi ro thấp nhất này đạt được khi các trọng số của MSFT, SBUX và GE lần lượt là 45%, 46% và 9%. Tuy nhiên đây là kết quả rút ra dựa trên mô phỏng. Nói chính xác hơn, đây là danh mục đầu tư có risk nhỏ nhất trong số 10000 danh mục có trọng số ngẫu nhiên.

GMV Portfolio

Portfolio Theory chỉ ra rằng các trọng số các tài sản của GMV Portfolio được tính toán dựa trên việc giải hệ ma trận dưới đây:

\[\begin{equation} \tag{1} z_m = A_mb \end{equation}\]

Với \(z_m\), \(A_m\)\(b\) là các ma trận dưới đây:

\[\begin{equation*} A_m = \begin{pmatrix} 2\Sigma & 1\\ 1' & 0 \end{pmatrix} \end{equation*}\]

\[\begin{equation*} z_m = \begin{pmatrix} m \\ \lambda \end{pmatrix} \end{equation*}\]

\[\begin{equation*} b = \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{equation*}\]

Còn \(m\) là ma trận trọng số các tài sản:

\[\begin{equation*} m = \begin{pmatrix} x_A & x_B & x_C \end{pmatrix} \end{equation*}\]

Lưu ý rằng tổng các trọng số luôn thoản mãn \(x_A + x_B + x_C = 1\). Dưới đây là R codes tìm tỉ trọng các tài sản cho GMV Portfolio bằng cách giải ma trận được mô tả trong phương trình 1:

##       MSFT       SBUX         GE 
## 0.43822562 0.46718882 0.09458556

Kết quả này chỉ ra rằng các trọng số của các tài sản cho GMV Portfolio có chút khác biệt nhỏ so với cách tìm bằng phương pháp mô phỏng. Điều này không có gì lạ vì Portfolio Theory đưa ra công thức chính xác để tìm trọng số cho GMV Portfolio còn phương pháp mô phỏng chỉ thì lại đi theo một hướng khác: chỉ ra ma trận có risk bé nhất trong số 10000 danh mục đầu tư. Dễ thấy rằng hai kết quả này sẽ tiệm cận nhau nếu chúng ta tìm kiếm danh mục có risk nhỏ nhất trong số 1.000.000 danh mục đầu tư ngẫu nhiên.

Để tiện lợi cho việc so sánh, chúng ta viết hàm tính rủi ro - lợi tức của một danh mục đầu tư bất kì khi biết thêm tỉ trọng của các tài sản:

Rồi sử dụng hàm này:

Table 2: GMV Portfolio by Portfolio Theory
symbol weight return risk
MSFT 0.4382256 0.0009357 0.0179318
SBUX 0.4671888 0.0009357 0.0179318
GE 0.0945856 0.0009357 0.0179318

So sánh Table 1 và Table 2 chúng ta có thể thấy rằng sự sai khác về risk chỉ là 0.0067%.

Efficient Portfolio

Một Efficient Portfolio là danh mục đầu tư mà với một lợi tức kì vọng cố định trước nhưng lại có rủi ro thấp nhất. Giả sử đặt lợi tức mục tiêu của danh mục là bằng với lợi tức của mã MSFT. Danh mục có mức lợi tức mục tiêu này nhưng rủi ro thấp hơn rủi ro của MSFT sẽ có các trọng số được tính như sau:

##       MSFT       SBUX         GE 
##  0.6385378  0.4650062 -0.1035440

Với trọng số tìm ra ở trên chúng ta tính toán cặp risk-return tương ứng và so sánh với MSFT là điểm màu da cam (kí hiệu EFF1) trong Figure 2 dưới đây:

Trong Figure 2 thì điểm màu đỏ chính là GMV Portfolio - danh mục có rủi ro thấp nhất với các trọng số được tính toán theo Portfolio Theory. Để hỗ trợ cho tính toán và so sánh chúng ta nên viết hàm tính toán tỉ trọng cho danh mục đầu tư hiệu quả (Efficient Portfolio) khi định trước lợi tức mục tiêu:

Với một mục tiêu khác về lợi tức - chẳng hạn r = 0.25% thì tỉ trọng các tài sản mà rủi ro thấp nhất là:

Table 3: Efficient Portfolio which Target Return = 0.25%
symbol weight return risk
MSFT 1.2691154 0.0025 0.0305428
SBUX 0.4581352 0.0025 0.0305428
GE -0.7272506 0.0025 0.0305428

Chúng ta khảo sát cơ cấu của danh mục đầu tư với một loạt ngưỡng lợi tức định trước nằm trong khoảng từ 0 đến 0.3%:

Khi lợi tức yêu cầu của danh mục đầu tư tăng, thì từ Figure 3 có thể thấy:

  1. Tỉ trọng tài sản phân bổ cho MSFT tăng.
  2. Tỉ trọng tài sản phân bổ cho GE tăng nhưng đây là tài sản “vay mượn”.
  3. Tại bất kì ngưỡng nào của lực tức thì tỉ trọng của SBUX có vẻ như không thay đổi nhiều. Điều này ngụ ý rằng: lợi tức của danh mục đầu tư là “trơ”, gần như không nhạy trước sự thay đổi về tỉ trọng của mã cổ phiếu này.

Efficient Frontier

Giả sử có hai danh mục đầu tư hiệu quả dưới đây:

  1. Danh mục đầu tư có rủi ro là thấp nhất nhưng lợi tức là cao nhất có thể được. Đây chính là GMV Portfolio.
  2. Danh mục đầu tư có lợi tức định trước là 0.25% nhưng rủi ro là thấp nhất (kí hiệu là MaxReturn Portfolio).

Xét danh mục đầu tư được tạo thành từ sự kết hợp của hai danh mục đầu tư trên trong đó tỉ trọng đầu tư vào hai danh mục này là \(\alpha\)\(\beta\), kí hiệu là Combined Portfolio - CP. Lúc này có hai khả năng xẩy ra:

  1. Nếu danh mục này có lợi tức cao hơn lợi tức của GMV Portfolio thì danh mục đầu tư này sẽ thuộc đường danh mục hiệu quả (Efficient Frontier).
  2. Ngược lại, nếu danh mục đầu tư này có lợi tức thấp hơn lợi tức của GMV Portfolio thì danh mục đầu tư này thuộc đường danh mục đầu tư phi hiệu quả (Inefficient Frontier).

Chúng ta xét danh mục đầu tư được hình thành từ hai danh mục đầu tư trên với các trọng số lần lượt là \(\alpha = 0.4\)\(\beta = 1 - \alpha\) rồi tính toán cặp risk-return cho danh mục này:

Table 4: Risk and Return for Combined Portfolio
symbol weight return risk
MSFT 0.9367595 0.0018743 0.0232728
SBUX 0.4617566 0.0018743 0.0232728
GE -0.3985162 0.0018743 0.0232728

Table 4 cho thấy đây là danh mục thuộc Efficient Frontier vì lợi tức của danh mục này là 0.19% cao hơn lợi tức của GMV Portfolio chỉ có lợi tức là 0.09%. Dễ kiểm tra thấy rằng rủi ro của danh mục đầu tư này 0.0233 - một con số thấp hơn 0.0305 (là rủi ro của danh mục đầu tư hiệu quả có lợi tức mục tiêu 0.25$) nhưng thấp hơn 0.0179 (là rủi ro của GMV Portfolio).

Chúng ta có thể khảo sát và vẽ Efficient Frontier bằng cách tính toán cặp risk-return cho 100 ngưỡng khác nhau của lợi tức từ 0% đến 0.25% như dưới đây:

low_return <- 0

up_return <- df_efficient_025$return[1]

target_returns_100 <- seq(low_return, up_return, length.out = 100) %>% unique()

lapply(target_returns_100, find_efficient_port) -> list_eff_port_100

do.call("bind_rows", list_eff_port_100) -> df_eff_port_100

df_eff_port_100 %>% 
  filter(!duplicated(return)) -> df_eff_frontier

# GMV portfolio: 
df_eff_frontier %>% slice(which.min(risk)) -> gmv_port 

# Maximum return portfolio: 
df_eff_frontier %>% slice(which.max(risk)) -> max_return_port 

# Portfolios belong to Efficient Frontier: 
df_eff_frontier %>% filter(return > gmv_port$return) -> df_eff_frontier_line

# Portfolio belong to Inefficient Frontier: 
df_eff_frontier %>% filter(return < gmv_port$return) -> df_ineff_frontier

# Graph of Efficient Frontier: 

df_return_mini_10000 %>% 
  ggplot(aes(risk, return)) + 
  geom_point(alpha = 0.05, color = "blue") + 
  geom_point(data = df_eff_frontier_line, color = "blue") + 
  geom_point(data = max_return_port, color = "purple", shape = 18, size = 3) + 
  geom_text_repel(data = max_return_port, aes(label = "Min25"), direction = "y") + 
  geom_point(data = df_ineff_frontier, color = "red") + 
  geom_point(data = gmv_port, color = "green", shape = 18, size = 3) + 
  geom_text_repel(data = gmv_port, aes(label = "GMV"), direction = "x") + 
  geom_point(data = df_risk_return, color = "orange", size = 3) + 
  geom_text_repel(data = df_risk_return, aes(label = symbol)) + 
  annotate("text", label = "Efficient Frontier", x = 0.022, y = 0.002) + 
  annotate("text", label = "Inefficient Frontier", x = 0.021, y = 0) + 
  scale_y_continuous(labels = percent) + 
  scale_x_continuous(labels = percent) + 
  labs(x = expression("Risk" ~ (sigma)), 
       y = expression("Return" ~ (mu)), 
       title = "Figure 3: Efficient Frontier") 

Ở Figure 3 thì Efficient Frontier là đường cong tạo ra bằng cách nối các điểm màu xanh. Inefficient Frontier là đường cong tạo ra bằng cách nối các điểm màu đỏ.

Tangency Portfolio

Sharpe Ratio là một thước đo đánh giá hiệu quả của danh mục đầu tư và được tính theo công thức:

\[S=\frac{\mu_p - r_{f}}{\sigma_p}\] Trong đó \(\sigma_p\)\(\mu_p\) lần lượt là rủi ro và lợi tức kì vọng của danh mục còn \(r_f\) là lợi tức của tài sản phi rủi ro. Ở đây lợi tức của tài sản phi rủi ro được chọn làm tham chiếu và thường là trái phiếu chính phủ (hoặc T-bills).

Tangency Portfolio là danh mục đầu tư mà có Sharpe Ratio là cao nhất. Dưới đây là R codes tìm tỉ trọng cho các tài sản cho Tangency Portfolio (kí hiệu là TAN) nếu biết trước lãi suất (daily return) của tài sản phi rủi ro (như trái phiếu chính phủ) là 0.01%:

##       MSFT       SBUX         GE 
##  1.2563678  0.4582741 -0.7146419

Với tỉ trọng các tài sản tìm được thì cặp risk-return của Tangency Portfolio được tính ở Table 5:

Table 4: Risk and Return for Tangency Portfolio
symbol weight return risk
MSFT 1.2563678 0.002476 0.0302365
SBUX 0.4582741 0.002476 0.0302365
GE -0.7146419 0.002476 0.0302365

Và Sharpe Ratio của danh mục này sẽ là:

## [1] 0.0785804

Mutual Fund Separation Theorem

Xét một danh mục đầu tư được hình thành từ việc phân bổ tỉ trọng là \(x_t\) cho Tangency Portfolio ở trên và phần còn lại là \(1 - x_t\) cho tài sản phi rủi ro T-bills. Rủi ro và lợi tức của danh mục đầu tư này, lần lượt kí hiệu là \(\sigma^{e}_p\)\(\mu^{e}_p\) sẽ là:

\[\begin{equation} \tag{2} \sigma^{e}_p = x_t\sigma_p \end{equation}\]

\[\begin{equation} \tag{3} \mu^{e}_p = r_f + x_t(\mu_p - r_f) \end{equation}\]

Giả sử một nhà đầu tư thuộc nhóm ngại rủi ro (a highly risk averse investor) đầu tư vào Tangency Portfolio và T-bills với một mục tiêu khiêm tốn về lợi tức và \(\sigma^{e}_p = 0.02\). Danh mục đầu thư thỏa mãn yêu cầu này của nhà đầu tư sẽ có tỉ trọng cho T-bills theo công thức 2 là:

## [1] 0.6614513

Và tỉ trọng phân bổ cho T-bills là:

## [1] 0.3385487

Và lợi tức của danh mục đầu tư theo công thức 3 sẽ là:

## [1] 0.001671608

Cụ thể hơn, tỉ trọng đầu tư vào ba tài sản rủi ro sẽ là:

##       MSFT       SBUX         GE 
##  0.8310261  0.3031260 -0.4727008

Dễ suy ra rằng tổng tỉ trọng của các tài sản rủi ro sẽ bằng 0.6614513:

## [1] 0.6614513

Đặt tên cho danh mục đầu tư này là TAN1. Các thông tin về TAN1:

Table 5: Risk and Return for TAN1 Portfolio
symbol weight return risk
MSFT 0.8310261 0.0016716 0.02
SBUX 0.3031260 0.0016716 0.02
GE -0.4727008 0.0016716 0.02
T-bills 0.3385487 0.0016716 0.02

Trên hệ trục tọa độ risk-return TAN1 sẽ là điểm nằm trên đường thẳng nối hai điểm TAN và T-bills và đường thẳng này được gọi là Tangency Line. Dưới đây là R codes cho Tangency Line:

Một nhà đầu tư có ngưỡng chịu rủi ro cao (a risk tolerant investor) rất có thể ưa thích một danh mục đầu tư có lợi tức cao hơn với cái giá phải trả là rủi ro cao hơn. Những danh mục đầu tư kiểu này sẽ: (1) vẫn nằm trên Tangency Line, và (2) nằm về phía trái của TAN1.

LS0tDQp0aXRsZTogJ1F1YW50aXRhdGl2ZSBGaW5hbmNlOiBQb3J0Zm9saW8gVGhlb3J5IChQYXJ0IDIpJw0KYXV0aG9yOiAnQXV0aG9yOiBOZ3V5ZW4gQ2hpIER1bmcnDQpzdWJ0aXRsZTogIlIgRmluYW5jZSBTZXJpZXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAjIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgICMgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogImZsYXRseSINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTApDQoNCmBgYA0KDQohW10oQzovVXNlcnMvQURNSU4vRG9jdW1lbnRzL2ludjIuanBnKQ0KDQojIFBvcnRmb2xpbyBUaGVvcnkgQWdhaW4NCg0KW1BvcnRmb2xpbyBUaGVvcnldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01vZGVybl9wb3J0Zm9saW9fdGhlb3J5KSDEkcOjIMSRxrDhu6NjIG3DtCB04bqjIHbDoCBnaeG6o2kgdGjDrWNoIHRy4buxYyBxdWFuIHRyb25nIHRyb25nIFtwaOG6p24gMV0oaHR0cHM6Ly9ycHVicy5jb20vY2hpZHVuZ2t0LzcxNjU0NSkgdOG6rXAgdHJ1bmcgdsOgbyBoYWkgduG6pW4gxJHhu4EgY2jDrW5oOiANCg0KMS4gTmjhu69uZyB0w61uaCB0b8OhbiBiYW4gxJHhuqd1IGxpw6puIHF1YW4gxJHhur9uIGzDrSB0aHV54bq/dCBuw6B5IGLhurFuZyBuZ8O0biBuZ+G7ryBtYSB0cuG6rW4gKGzhu6NpIHThu6ljIHbDoCBy4bunaSBybykuDQoyLiBUw6xtIGRhbmggbeG7pWMgxJHhuqd1IHTGsCBtw6AgcuG7p2kgcm8gbMOgIGLDqSBuaOG6pXQgKEdsb2JhbCBNaW5pbXVtIFZhcmlhbmNlIC0gR01WIFBvcnRmb2xpbykgZOG7sWEgdHLDqm4gcGjGsMahbmcgcGjDoXAgbcO0IHBo4buPbmcuIA0KDQpUcsaw4bubYyBo4bq/dCBjaMO6bmcgdGEgbOG6t3AgbOG6oWkgdmnhu4djIHTDrG0gR01WIFBvcnRmb2xpbyB04burIDEwMDAwIGRhbmggbeG7pWMgbmjGsCDEkcOjIGJp4bq/dCB04burIHBo4bqnbiAxOiANCg0KYGBge3J9DQojIENsZWFyIHdvcmtzcGFjZTogDQpybShsaXN0ID0gbHMoKSkNCg0KIyBTb21lIHN0b2NrcyBzZWxlY3RlZCAoTWljcm9zb2Z0LCBTdGFyYnVja3MgYW5kIEFwcGxlKTogDQoNCm15X3N5bWJvbHMgPC0gYygiTVNGVCIsICJTQlVYIiwgIkdFIikNCg0KIyBOdW1iZXIgb2Ygc3RvY2tzOiANCg0KbiA8LSBsZW5ndGgobXlfc3ltYm9scykNCg0KIyBDb2xsZWN0IGRhdGE6IA0KDQpsaWJyYXJ5KHRpZHlxdWFudCkNCnByaWNlX2RhdGEgPC0gdHFfZ2V0KG15X3N5bWJvbHMsDQogICAgICAgICAgICAgICAgICAgICBmcm9tID0gIjIwMTgtMDEtMDEiLA0KICAgICAgICAgICAgICAgICAgICAgdG8gPSAiMjAyMC0xMi0zMSIsDQogICAgICAgICAgICAgICAgICAgICBnZXQgPSAic3RvY2sucHJpY2VzIikNCg0KIyBDYWxjdWxhdGUgbG9nIHJldHVybjogDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpwcmljZV9kYXRhICU+JQ0KICBncm91cF9ieShzeW1ib2wpICU+JSANCiAgbXV0YXRlKGxhZzEgPSBsYWcoYWRqdXN0ZWQsIG4gPSAxTCkpICU+JSANCiAgZmlsdGVyKCFpcy5uYShsYWcxKSkgJT4lIA0KICBtdXRhdGUoZGFpbHlfcmV0dXJuID0gbG9nKGFkanVzdGVkIC8gbGFnMSkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgc2VsZWN0KHN5bWJvbCwgZGF0ZSwgZGFpbHlfcmV0dXJuKSAtPiBkYWlseV9yZXR1cm5zDQoNCiMgQ2FsY3VsYXRlIHJpc2sgYW5kIHJldHVybiBmb3IgYXNzZXRzOg0KICANCmRhaWx5X3JldHVybnMgJT4lIA0KICBncm91cF9ieShzeW1ib2wpICU+JSANCiAgc3VtbWFyaXNlKHJpc2sgPSBzZChkYWlseV9yZXR1cm4pLCByZXR1cm4gPSBtZWFuKGRhaWx5X3JldHVybikpIC0+IGRmX3Jpc2tfcmV0dXJuDQoNCiMgQ29udmVydCB0byB3aWRlIGZvcm06IA0KDQpkYWlseV9yZXR1cm5zICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN5bWJvbCwgdmFsdWVzX2Zyb20gPSBkYWlseV9yZXR1cm4pICU+JSANCiAgc2VsZWN0KC1kYXRlKSAtPiByZXR1cm5zX3dpZGUNCg0KIyBBc3NldCBtZWFuIHJldHVybjogDQoNCm11IDwtIGNvbE1lYW5zKHJldHVybnNfd2lkZSkgDQoNCiMgQ292YXJpYW5jZSBtYXRyaXg6IA0KDQpzaWdtYV9tYXRyaXggPC0gY292KHJldHVybnNfd2lkZSkNCg0KIyBGdW5jdGlvbiBjYWxjdWxhdGVzIHJpc2stcmV0dXJuIGZvciBhIHJhbmRvbS13ZWlnaHQgcG9ydGZvbGlvOiANCmxpYnJhcnkocnBvcnRmb2xpb3MpDQoNCnBvcnRmb2xpb19yaXNrX3JldHVybiA8LSBmdW5jdGlvbihqKSB7DQogIA0KICAjIENyZWF0ZSByYW5kb20gd2VpZ2h0czogDQogIHNldC5zZWVkKGopDQogIA0KICBYIDwtIHJhbmRvbS5ib3VuZGVkKG4gPSBuLCB4LnQgPSAxLCB4LmwgPSByZXAoLTEsIG4pLCB4LnUgPSByZXAoMSwgbikpDQoNCiAgIyBQb3J0Zm9saW8gcmV0dXJuOiANCiAgbXVfcG9ydF9yYW5kID0gY3Jvc3Nwcm9kKFgsIG11KSAlPiUgYXMubnVtZXJpYygpDQoNCiAgIyBQb3J0Zm9saW8gcmlzazogDQogIHNpZ19wb3J0X3JhbmQgPC0gdChYKSAlKiUgc2lnbWFfbWF0cml4ICUqJSBYICU+JSBhcy5udW1lcmljKCkgJT4lIHNxcnQoKQ0KICANCiAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIG9mIHJlc3VsdHM6IA0KICBkZl9wb2Z0IDwtIHRpYmJsZShzeW1ib2wgPSBteV9zeW1ib2xzLCANCiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gWCwgDQogICAgICAgICAgICAgICAgICAgIHJldHVybiA9IG11X3BvcnRfcmFuZCwgDQogICAgICAgICAgICAgICAgICAgIHJpc2sgPSBzaWdfcG9ydF9yYW5kLA0KICAgICAgICAgICAgICAgICAgICBwb3J0X2luZGV4ID0gaikNCiAgDQogIHJldHVybihkZl9wb2Z0KQ0KDQp9DQoNCiMgVXNlIHRoZSBmdW5jdGlvbiBhbmQgY29tYmluZSByZXN1bHRzIGluIGZvcm0gb2YgZGF0YSBmcmFtZTogDQoNCmxhcHBseSgxOjEwMDAwLCBwb3J0Zm9saW9fcmlza19yZXR1cm4pIC0+IGFsbF9yZXR1cm5fcG9ydA0KDQpkby5jYWxsKCJiaW5kX3Jvd3MiLCBhbGxfcmV0dXJuX3BvcnQpIC0+IGRmX3JldHVybl8xMDAwMA0KDQpkZl9yZXR1cm5fMTAwMDAgJT4lIA0KICBmaWx0ZXIoIWR1cGxpY2F0ZWQocG9ydF9pbmRleCkpIC0+IGRmX3JldHVybl9taW5pXzEwMDAwDQoNCiMgTWluaW11bSByaXNrIHBvcnRmb2xpbyAoR01WIFBvcnRmb2xpbykgZnJvbSBzaW11bGF0aW9uOiANCm1pbl9yaXNrIDwtIGRmX3JldHVybl9taW5pXzEwMDAwICU+JSBzbGljZSh3aGljaC5taW4ocmlzaykpIA0KDQpgYGANCg0KR01WIFBvcnRmb2xpbyBjw7MgcuG7p2kgcm8gLSBs4bujaSB04bupYyB2w6AgdHLhu41uZyBz4buRIGPhu6dhIGPDoWMgdMOgaSBz4bqjbiDEkcaw4bujYyB0csOsbmggYsOgeSDhu58gVGFibGUgMTogDQoNCmBgYHtyfQ0KbGlicmFyeShrYWJsZUV4dHJhKSAjIEZvciBwcmVzZW50aW5nIHRhYmxlLiANCg0KZGZfcmV0dXJuXzEwMDAwICU+JSANCiAgZmlsdGVyKHBvcnRfaW5kZXggPT0gbWluX3Jpc2skcG9ydF9pbmRleCkgJT4lIA0KICBzZWxlY3QoLXBvcnRfaW5kZXgpICU+JSANCiAga2JsKGNhcHRpb24gPSAiVGFibGUgMTogTWluaW11bSBSaXNrIFBvcnRmb2xpbyBieSBTaW11bGF0aW9uIiwgZXNjYXBlID0gVFJVRSkgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sX2ZvbnQgPSAiQ2FtYnJpYSIpDQpgYGANCg0KRGFuaCBt4bulYyBjw7MgcuG7p2kgcm8gdGjhuqVwIG5o4bqldCBuw6B5IMSR4bqhdCDEkcaw4bujYyBraGkgY8OhYyB0cuG7jW5nIHPhu5EgY+G7p2EgTVNGVCwgU0JVWCB2w6AgR0UgbOG6p24gbMaw4bujdCBsw6AgNDUlLCA0NiUgdsOgIDklLiBUdXkgbmhpw6puIMSRw6J5IGzDoCBr4bq/dCBxdeG6oyByw7p0IHJhIGThu7FhIHRyw6puIG3DtCBwaOG7j25nLiBOw7NpIGNow61uaCB4w6FjIGjGoW4sIMSRw6J5IGzDoCBkYW5oIG3hu6VjIMSR4bqndSB0xrAgY8OzIHJpc2sgbmjhu48gbmjhuqV0IHRyb25nIHPhu5EgMTAwMDAgZGFuaCBt4bulYyBjw7MgdHLhu41uZyBz4buRIG5n4bqrdSBuaGnDqm4uIA0KDQoNCiMgR01WIFBvcnRmb2xpbw0KDQpQb3J0Zm9saW8gVGhlb3J5IGNo4buJIHJhIHLhurFuZyBjw6FjIHRy4buNbmcgc+G7kSBjw6FjIHTDoGkgc+G6o24gY+G7p2EgR01WIFBvcnRmb2xpbyDEkcaw4bujYyB0w61uaCB0b8OhbiBk4buxYSB0csOqbiB2aeG7h2MgZ2nhuqNpIGjhu4cgbWEgdHLhuq1uIGTGsOG7m2kgxJHDonk6IA0KDQokJFxiZWdpbntlcXVhdGlvbn0NClx0YWd7MX0NCnpfbSA9IEFfbWINClxlbmR7ZXF1YXRpb259JCQgIA0KDQpW4bubaSAkel9tJCwgJEFfbSQgdsOgICRiJCBsw6AgY8OhYyBtYSB0cuG6rW4gZMaw4bubaSDEkcOieTogDQoNCg0KJCRcYmVnaW57ZXF1YXRpb24qfQ0KQV9tID0gDQpcYmVnaW57cG1hdHJpeH0NCjJcU2lnbWEgJiAxXFwNCjEnICYgMCAgDQpcZW5ke3BtYXRyaXh9DQpcZW5ke2VxdWF0aW9uKn0kJCANCg0KJCRcYmVnaW57ZXF1YXRpb24qfQ0Kel9tID0gDQpcYmVnaW57cG1hdHJpeH0NCm0gXFwNClxsYW1iZGEgIA0KXGVuZHtwbWF0cml4fQ0KXGVuZHtlcXVhdGlvbip9JCQNCg0KJCRcYmVnaW57ZXF1YXRpb24qfQ0KYiA9IA0KXGJlZ2lue3BtYXRyaXh9DQowIFxcDQoxICANClxlbmR7cG1hdHJpeH0NClxlbmR7ZXF1YXRpb24qfSQkDQoNCkPDsm4gJG0kIGzDoCBtYSB0cuG6rW4gdHLhu41uZyBz4buRIGPDoWMgdMOgaSBz4bqjbjogDQoNCiQkXGJlZ2lue2VxdWF0aW9uKn0NCm0gPSANClxiZWdpbntwbWF0cml4fQ0KeF9BICYgeF9CICYgeF9DIA0KXGVuZHtwbWF0cml4fQ0KXGVuZHtlcXVhdGlvbip9JCQNCg0KTMawdSDDvSBy4bqxbmcgdOG7lW5nIGPDoWMgdHLhu41uZyBz4buRIGx1w7RuIHRob+G6o24gbcOjbiAkeF9BICsgeF9CICsgeF9DID0gMSQuIETGsOG7m2kgxJHDonkgbMOgIFIgY29kZXMgdMOsbSB04buJIHRy4buNbmcgY8OhYyB0w6BpIHPhuqNuIGNobyBHTVYgUG9ydGZvbGlvIGLhurFuZyBjw6FjaCBnaeG6o2kgbWEgdHLhuq1uIMSRxrDhu6NjIG3DtCB04bqjIHRyb25nIHBoxrDGoW5nIHRyw6xuaCAxOiANCg0KDQpgYGB7cn0NCg0KIyBDYWxjdWxhdGUgd2VpZ2h0cyBmb3IgR01WIFBvcnRmb2xpbzogDQoNCnRvcF9tYXQgPC0gY2JpbmQoMipzaWdtYV9tYXRyaXgsIHJlcCgxLCBuKSkNCg0KYm90X3ZlYyA8LSBjKHJlcCgxLCBuKSwgMCkNCg0KQW1fbWF0IDwtIHJiaW5kKHRvcF9tYXQsIGJvdF92ZWMpDQoNCmJfdmVjIDwtIGMocmVwKDAsIG4pLCAxKQ0KDQp6bV9tYXQgPC0gc29sdmUoQW1fbWF0KSAlKiUgYl92ZWMNCg0KWF9taW5pbXVtX3ZhciA8LSB6bV9tYXRbMTozLCAxXQ0KDQojIFByaW50IHJlc3VsdHM6IA0KDQpwcmludChYX21pbmltdW1fdmFyKQ0KDQpgYGANCg0KS+G6v3QgcXXhuqMgbsOgeSBjaOG7iSByYSBy4bqxbmcgY8OhYyB0cuG7jW5nIHPhu5EgY+G7p2EgY8OhYyB0w6BpIHPhuqNuIGNobyBHTVYgUG9ydGZvbGlvIGPDsyBjaMO6dCBraMOhYyBiaeG7h3Qgbmjhu48gc28gduG7m2kgY8OhY2ggdMOsbSBi4bqxbmcgcGjGsMahbmcgcGjDoXAgbcO0IHBo4buPbmcuIMSQaeG7gXUgbsOgeSBraMO0bmcgY8OzIGfDrCBs4bqhIHbDrCBQb3J0Zm9saW8gVGhlb3J5IMSRxrBhIHJhIGPDtG5nIHRo4bupYyBjaMOtbmggeMOhYyDEkeG7gyB0w6xtIHRy4buNbmcgc+G7kSBjaG8gR01WIFBvcnRmb2xpbyBjw7JuIHBoxrDGoW5nIHBow6FwIG3DtCBwaOG7j25nIGNo4buJIHRow6wgbOG6oWkgxJFpIHRoZW8gbeG7mXQgaMaw4bubbmcga2jDoWM6IGNo4buJIHJhIG1hIHRy4bqtbiBjw7MgcmlzayBiw6kgbmjhuqV0IHRyb25nIHPhu5EgMTAwMDAgZGFuaCBt4bulYyDEkeG6p3UgdMawLiBE4buFIHRo4bqleSBy4bqxbmcgaGFpIGvhur90IHF14bqjIG7DoHkgc+G6vSB0aeG7h20gY+G6rW4gbmhhdSBu4bq/dSBjaMO6bmcgdGEgdMOsbSBraeG6v20gZGFuaCBt4bulYyBjw7MgcmlzayBuaOG7jyBuaOG6pXQgdHJvbmcgc+G7kSAxLjAwMC4wMDAgZGFuaCBt4bulYyDEkeG6p3UgdMawIG5n4bqrdSBuaGnDqm4uIA0KDQrEkOG7gyB0aeG7h24gbOG7o2kgY2hvIHZp4buHYyBzbyBzw6FuaCwgY2jDum5nIHRhIHZp4bq/dCBow6BtIHTDrW5oIHLhu6dpIHJvIC0gbOG7o2kgdOG7qWMgY+G7p2EgbeG7mXQgZGFuaCBt4bulYyDEkeG6p3UgdMawIGLhuqV0IGvDrCBraGkgYmnhur90IHRow6ptIHThu4kgdHLhu41uZyBj4bunYSBjw6FjIHTDoGkgc+G6o246IA0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gY2FsY3VsYXRlIHJpc2stcmV0dXJuIHdpdGggZ2l2ZSB3ZWlnaHRzOg0KDQpyaXNrX3JldHVybl9wb3J0Zm9saW8gPC0gZnVuY3Rpb24od2VpZ2h0cykgew0KICANCiAgIyBQb3J0Zm9saW8gcmV0dXJuOiANCiAgbXVfcG9ydCA8LSBjcm9zc3Byb2Qod2VpZ2h0cywgbXUpICU+JSBhcy5udW1lcmljKCkNCg0KICAjIFBvcnRmb2xpbyByaXNrOiANCg0KICBzaWcyX3BvcnQgPC0gdCh3ZWlnaHRzKSAlKiUgc2lnbWFfbWF0cml4ICUqJSB3ZWlnaHRzDQoNCiAgc2lnX3BvcnQgPC0gc2lnMl9wb3J0ICU+JSBhcy5udW1lcmljKCkgJT4lIHNxcnQoKQ0KDQogICMgUHJpbnQgcmVzdWx0czogDQogIA0KICB0aWJibGUoc3ltYm9sID0gbXlfc3ltYm9scywgd2VpZ2h0ID0gd2VpZ2h0cywgcmV0dXJuID0gbXVfcG9ydCwgcmlzayA9IHNpZ19wb3J0KSAtPiBkZl9yZXN1bHRzDQogIA0KICByZXR1cm4oZGZfcmVzdWx0cykNCg0KfQ0KDQpgYGANCg0KUuG7k2kgc+G7rSBk4bulbmcgaMOgbSBuw6B5OiANCg0KYGBge3J9DQpyaXNrX3JldHVybl9wb3J0Zm9saW8od2VpZ2h0cyA9IFhfbWluaW11bV92YXIpIC0+IG1pbl92YXINCg0KbWluX3ZhciAlPiUgDQogIGtibChjYXB0aW9uID0gIlRhYmxlIDI6IEdNViBQb3J0Zm9saW8gYnkgUG9ydGZvbGlvIFRoZW9yeSIsIGVzY2FwZSA9IFRSVUUpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KICANCmBgYA0KDQpTbyBzw6FuaCBUYWJsZSAxIHbDoCBUYWJsZSAyIGNow7puZyB0YSBjw7MgdGjhu4MgdGjhuqV5IHLhurFuZyBz4buxIHNhaSBraMOhYyB24buBIHJpc2sgY2jhu4kgbMOgIDAuMDA2NyUuIA0KDQojIEVmZmljaWVudCBQb3J0Zm9saW8NCg0KTeG7mXQgRWZmaWNpZW50IFBvcnRmb2xpbyBsw6AgZGFuaCBt4bulYyDEkeG6p3UgdMawIG3DoCB24bubaSBt4buZdCBs4bujaSB04bupYyBrw6wgduG7jW5nIGPhu5EgxJHhu4tuaCB0csaw4bubYyBuaMawbmcgbOG6oWkgY8OzIHLhu6dpIHJvIHRo4bqlcCBuaOG6pXQuIEdp4bqjIHPhu60gxJHhurd0IGzhu6NpIHThu6ljIG3hu6VjIHRpw6p1IGPhu6dhIGRhbmggbeG7pWMgbMOgIGLhurFuZyB24bubaSBs4bujaSB04bupYyBj4bunYSBtw6MgTVNGVC4gRGFuaCBt4bulYyBjw7MgbeG7qWMgbOG7o2kgdOG7qWMgbeG7pWMgdGnDqnUgbsOgeSBuaMawbmcgcuG7p2kgcm8gdGjhuqVwIGjGoW4gcuG7p2kgcm8gY+G7p2EgTVNGVCBz4bq9IGPDsyBjw6FjIHRy4buNbmcgc+G7kSDEkcaw4bujYyB0w61uaCBuaMawIHNhdTogDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICBFZmZpY2llbnQgcG9ydGZvbGlvIHdpdGggdGhlIHNhbWUgZXhwZWN0ZWQgcmV0dXJuIGFzIE1pY3Jvc29mdA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZXR1cm5fdGFyZ2V0IDwtIG11W215X3N5bWJvbHNbMV1dDQoNCnRvcF9tYXQgPC0gY2JpbmQoMipzaWdtYV9tYXRyaXgsIG11LCByZXAoMSwgbikpDQoNCm1pZF92ZWMgPC0gYyhtdSwgcmVwKDAsIG4gLSAxKSkNCg0KYm90X3ZlYyA8LSBjKHJlcCgxLCBuKSwgcmVwKDAsIG4gLSAxKSkNCg0KQV9tYXQgPSByYmluZCh0b3BfbWF0LCBtaWRfdmVjLCBib3RfdmVjKQ0KDQpibXNmdF92ZWMgPC0gYyhyZXAoMCwgbiksIHJldHVybl90YXJnZXQsIDEpDQoNCnpfbWF0IDwtIHNvbHZlKEFfbWF0KSAlKiUgYm1zZnRfdmVjDQoNClhfZWZmaWNpZW50X2FzX21zZnQgPC0gel9tYXRbMTozLCAxXQ0KDQojIFByaW50IHJlc3VsdHM6IA0KcHJpbnQoWF9lZmZpY2llbnRfYXNfbXNmdCkNCg0KYGBgDQoNClbhu5tpIHRy4buNbmcgc+G7kSB0w6xtIHJhIOG7nyB0csOqbiBjaMO6bmcgdGEgdMOtbmggdG/DoW4gY+G6t3Agcmlzay1yZXR1cm4gdMawxqFuZyDhu6luZyB2w6Agc28gc8OhbmggduG7m2kgTVNGVCBsw6AgxJFp4buDbSBtw6B1IGRhIGNhbSAoa8OtIGhp4buHdSBFRkYxKSB0cm9uZyBGaWd1cmUgMiBkxrDhu5tpIMSRw6J5OiANCg0KYGBge3J9DQoNCnJpc2tfcmV0dXJuX3BvcnRmb2xpbyh3ZWlnaHRzID0gWF9lZmZpY2llbnRfYXNfbXNmdCkgLT4gZGZfYXNfbXNmdA0KDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KHNjYWxlcykNCg0KZGZfcmV0dXJuX21pbmlfMTAwMDAgJT4lIA0KICBnZ3Bsb3QoYWVzKHJpc2ssIHJldHVybikpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjA1LCBjb2xvciA9ICJibHVlIikgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gbWluX3ZhciAlPiUgc2xpY2UoMSksIGNvbG9yID0gInJlZCIsIHNpemUgPSAzLCBzaGFwZSA9IDE3KSArIA0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IG1pbl92YXIgJT4lIHNsaWNlKDEpLCBhZXMobGFiZWwgPSAiR01WIikpICsgDQogIGdlb21fcG9pbnQoZGF0YSA9IGRmX3Jpc2tfcmV0dXJuLCBjb2xvciA9ICJncmVlbiIsIHNpemUgPSAyKSArIA0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGRmX3Jpc2tfcmV0dXJuLCBhZXMobGFiZWwgPSBzeW1ib2wpKSArICANCiAgZ2VvbV9wb2ludChkYXRhID0gZGZfYXNfbXNmdCwgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDIpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZGZfYXNfbXNmdCAlPiUgc2xpY2UoMSksIGFlcyhsYWJlbCA9ICJFRkYxIikpICsgDQogIGxhYnMoeCA9IGV4cHJlc3Npb24oIlJpc2siIH4gKHNpZ21hKSksIA0KICAgICAgIHkgPSBleHByZXNzaW9uKCJSZXR1cm4iIH4gKG11KSksIA0KICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxOiBFZmZpY2llbnQgUG9ydGZvbGlvIHdpdGggdGhlIHNhbWUgcmV0dXJuIGFzIE1pY3Jvc29mdCIpIA0KICANCmBgYA0KDQpUcm9uZyBGaWd1cmUgMiB0aMOsIMSRaeG7g20gbcOgdSDEkeG7jyBjaMOtbmggbMOgIEdNViBQb3J0Zm9saW8gLSBkYW5oIG3hu6VjIGPDsyBy4bunaSBybyB0aOG6pXAgbmjhuqV0IHbhu5tpIGPDoWMgdHLhu41uZyBz4buRIMSRxrDhu6NjIHTDrW5oIHRvw6FuIHRoZW8gUG9ydGZvbGlvIFRoZW9yeS4gxJDhu4MgaOG7lyB0cuG7oyBjaG8gdMOtbmggdG/DoW4gdsOgIHNvIHPDoW5oIGNow7puZyB0YSBuw6puIHZp4bq/dCBow6BtIHTDrW5oIHRvw6FuIHThu4kgdHLhu41uZyBjaG8gZGFuaCBt4bulYyDEkeG6p3UgdMawIGhp4buHdSBxdeG6oyAoRWZmaWNpZW50IFBvcnRmb2xpbykga2hpIMSR4buLbmggdHLGsOG7m2MgbOG7o2kgdOG7qWMgbeG7pWMgdGnDqnU6IA0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gZmluZCB3ZWlnaHRzIGZvciBhIGVmZmljaWVudCBwb3J0Zm9saW8gd2l0aCBnaXZlbiByZXR1cm46IA0KDQpmaW5kX2VmZmljaWVudF9wb3J0IDwtIGZ1bmN0aW9uKGdpdmVuX3JldHVybikgew0KICANCiAgdG9wX21hdCA8LSBjYmluZCgyKnNpZ21hX21hdHJpeCwgbXUsIHJlcCgxLCBuKSkNCiAgDQogIG1pZF92ZWMgPC0gYyhtdSwgcmVwKDAsIG4gLSAxKSkNCg0KICBib3RfdmVjIDwtIGMocmVwKDEsIG4pLCByZXAoMCwgbiAtIDEpKQ0KDQogIEFfbWF0ID0gcmJpbmQodG9wX21hdCwgbWlkX3ZlYywgYm90X3ZlYykNCg0KICBteV92ZWMgPC0gYyhyZXAoMCwgbiksIGdpdmVuX3JldHVybiwgMSkNCg0KICB6X21hdCA8LSBzb2x2ZShBX21hdCkgJSolIG15X3ZlYw0KDQogIFhfZWZmaWNpZW50IDwtIHpfbWF0WzE6MywgMV0NCiAgDQogIFhfZWZmaWNpZW50ICU+JSANCiAgICByaXNrX3JldHVybl9wb3J0Zm9saW8oKSAlPiUgDQogICAgcmV0dXJuKCkNCn0NCmBgYA0KDQoNClbhu5tpIG3hu5l0IG3hu6VjIHRpw6p1IGtow6FjIHbhu4EgbOG7o2kgdOG7qWMgLSBjaOG6s25nIGjhuqFuICpyID0gMC4yNSUqIHRow6wgdOG7iSB0cuG7jW5nIGPDoWMgdMOgaSBz4bqjbiBtw6AgcuG7p2kgcm8gdGjhuqVwIG5o4bqldCBsw6A6IA0KDQpgYGB7cn0NCmZpbmRfZWZmaWNpZW50X3BvcnQoZ2l2ZW5fcmV0dXJuID0gMC4wMDI1KSAtPiBkZl9lZmZpY2llbnRfMDI1DQoNCg0KZGZfZWZmaWNpZW50XzAyNSAlPiUgDQogIGtibChjYXB0aW9uID0gIlRhYmxlIDM6IEVmZmljaWVudCBQb3J0Zm9saW8gd2hpY2ggVGFyZ2V0IFJldHVybiA9IDAuMjUlIiwgZXNjYXBlID0gVFJVRSkgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sX2ZvbnQgPSAiQ2FtYnJpYSIpDQpgYGANCg0KQ2jDum5nIHRhIGto4bqjbyBzw6F0IGPGoSBj4bqldSBj4bunYSBkYW5oIG3hu6VjIMSR4bqndSB0xrAgduG7m2kgbeG7mXQgbG/huqF0IG5nxrDhu6FuZyBs4bujaSB04bupYyDEkeG7i25oIHRyxrDhu5tjIG7hurFtIHRyb25nIGtob+G6o25nIHThu6sgMCDEkeG6v24gMC4zJTogDQoNCmBgYHtyfQ0KDQojIEdlbmVyYXRlIHNlcXVlbmNlIG9mIHJldHVybnM6IA0KdGFyZ2V0X3JldHVybnMgPC0gc2VxKDAsIDAuMDAyNSwgbGVuZ3RoLm91dCA9IDIwKSAlPiUgdW5pcXVlKCkNCmxhcHBseSh0YXJnZXRfcmV0dXJucywgZmluZF9lZmZpY2llbnRfcG9ydCkgLT4gbGlzdF9lZmZfcG9ydA0KZG8uY2FsbCgiYmluZF9yb3dzIiwgbGlzdF9lZmZfcG9ydCkgLT4gZGZfZWZmX3BvcnQNCg0KcGFzdGUwKHJvdW5kKHRhcmdldF9yZXR1cm5zKjEwMCwgMiksICIlIikgLT4geF9sYWJlbHMNCm15X2NvbG9ycyA8LSBjKCcjZTQxYTFjJywnIzM3N2ViOCcsJyM0ZGFmNGEnKQ0KDQpkZl9lZmZfcG9ydCAlPiUgDQogIG11dGF0ZShyZXR1cm4gPSBmYWN0b3IocmV0dXJuKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHJldHVybiwgd2VpZ2h0LCBmaWxsID0gc3ltYm9sKSkgKyANCiAgZ2VvbV9jb2woKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IHhfbGFiZWxzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtMSwgMiksIGJyZWFrcyA9IHNlcSgtMSwgMiwgMC4yNSksIGxhYmVscyA9IHBlcmNlbnQpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKyANCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwgMC44NSkpICsgDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpICsgDQogIGxhYnMoeCA9IGV4cHJlc3Npb24oIlJldHVybiIgfiAoc2lnbWEpKSwgDQogICAgICAgeSA9ICJXZWlnaHQiLCANCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMjogQXNzZXQgV2VpZ2h0IG9mIEVmZmljaWVudCBQb3J0Zm9saW9zIikgDQogIA0KYGBgDQoNCktoaSBs4bujaSB04bupYyB5w6p1IGPhuqd1IGPhu6dhIGRhbmggbeG7pWMgxJHhuqd1IHTGsCB0xINuZywgdGjDrCB04burIEZpZ3VyZSAzIGPDsyB0aOG7gyB0aOG6pXk6IA0KDQoxLiBU4buJIHRy4buNbmcgdMOgaSBz4bqjbiBwaMOibiBi4buVIGNobyBNU0ZUIHTEg25nLiANCjIuIFThu4kgdHLhu41uZyB0w6BpIHPhuqNuIHBow6JuIGLhu5UgY2hvIEdFIHTEg25nIG5oxrBuZyDEkcOieSBsw6AgdMOgaSBz4bqjbiAidmF5IG3GsOG7o24iLiANCjMuIFThuqFpIGLhuqV0IGvDrCBuZ8aw4buhbmcgbsOgbyBj4bunYSBs4buxYyB04bupYyB0aMOsIHThu4kgdHLhu41uZyBj4bunYSBTQlVYIGPDsyB24bq7IG5oxrAga2jDtG5nIHRoYXkgxJHhu5VpIG5oaeG7gXUuIMSQaeG7gXUgbsOgeSBuZ+G7pSDDvSBy4bqxbmc6IGzhu6NpIHThu6ljIGPhu6dhIGRhbmggbeG7pWMgxJHhuqd1IHTGsCBsw6AgInRyxqEiLCBn4bqnbiBuaMawIGtow7RuZyBuaOG6oXkgdHLGsOG7m2Mgc+G7sSB0aGF5IMSR4buVaSB24buBIHThu4kgdHLhu41uZyBj4bunYSBtw6MgY+G7lSBwaGnhur91IG7DoHkuIA0KDQoNCiMgRWZmaWNpZW50IEZyb250aWVyDQoNCkdp4bqjIHPhu60gY8OzIGhhaSBkYW5oIG3hu6VjIMSR4bqndSB0xrAgaGnhu4d1IHF14bqjIGTGsOG7m2kgxJHDonk6IA0KDQoxLiBEYW5oIG3hu6VjIMSR4bqndSB0xrAgY8OzIHLhu6dpIHJvIGzDoCB0aOG6pXAgbmjhuqV0IG5oxrBuZyBs4bujaSB04bupYyBsw6AgY2FvIG5o4bqldCBjw7MgdGjhu4MgxJHGsOG7o2MuIMSQw6J5IGNow61uaCBsw6AgR01WIFBvcnRmb2xpby4gDQoyLiBEYW5oIG3hu6VjIMSR4bqndSB0xrAgY8OzIGzhu6NpIHThu6ljIMSR4buLbmggdHLGsOG7m2MgbMOgIDAuMjUlIG5oxrBuZyBy4bunaSBybyBsw6AgdGjhuqVwIG5o4bqldCAoa8OtIGhp4buHdSBsw6AgTWF4UmV0dXJuIFBvcnRmb2xpbykuIA0KDQpYw6l0IGRhbmggbeG7pWMgxJHhuqd1IHTGsCDEkcaw4bujYyB04bqhbyB0aMOgbmggdOG7qyBz4buxIGvhur90IGjhu6NwIGPhu6dhIGhhaSBkYW5oIG3hu6VjIMSR4bqndSB0xrAgdHLDqm4gdHJvbmcgxJHDsyB04buJIHRy4buNbmcgxJHhuqd1IHTGsCB2w6BvIGhhaSBkYW5oIG3hu6VjIG7DoHkgbMOgICRcYWxwaGEkIHbDoCAkXGJldGEkLCBrw60gaGnhu4d1IGzDoCBDb21iaW5lZCBQb3J0Zm9saW8gLSBDUC4gTMO6YyBuw6B5IGPDsyBoYWkga2jhuqMgbsSDbmcgeOG6qXkgcmE6IA0KDQoxLiBO4bq/dSBkYW5oIG3hu6VjIG7DoHkgY8OzIGzhu6NpIHThu6ljIGNhbyBoxqFuIGzhu6NpIHThu6ljIGPhu6dhIEdNViBQb3J0Zm9saW8gdGjDrCBkYW5oIG3hu6VjIMSR4bqndSB0xrAgbsOgeSBz4bq9IHRodeG7mWMgxJHGsOG7nW5nIGRhbmggbeG7pWMgaGnhu4d1IHF14bqjIChFZmZpY2llbnQgRnJvbnRpZXIpLiAgDQoyLiBOZ8aw4bujYyBs4bqhaSwgbuG6v3UgZGFuaCBt4bulYyDEkeG6p3UgdMawIG7DoHkgY8OzIGzhu6NpIHThu6ljIHRo4bqlcCBoxqFuIGzhu6NpIHThu6ljIGPhu6dhIEdNViBQb3J0Zm9saW8gdGjDrCBkYW5oIG3hu6VjIMSR4bqndSB0xrAgbsOgeSB0aHXhu5ljIMSRxrDhu51uZyBkYW5oIG3hu6VjIMSR4bqndSB0xrAgcGhpIGhp4buHdSBxdeG6oyAoSW5lZmZpY2llbnQgRnJvbnRpZXIpLiANCg0KQ2jDum5nIHRhIHjDqXQgZGFuaCBt4bulYyDEkeG6p3UgdMawIMSRxrDhu6NjIGjDrG5oIHRow6BuaCB04burIGhhaSBkYW5oIG3hu6VjIMSR4bqndSB0xrAgdHLDqm4gduG7m2kgY8OhYyB0cuG7jW5nIHPhu5EgbOG6p24gbMaw4bujdCBsw6AgJFxhbHBoYSA9IDAuNCQgdsOgICRcYmV0YSA9IDEgLSBcYWxwaGEkIHLhu5NpIHTDrW5oIHRvw6FuIGPhurdwIHJpc2stcmV0dXJuIGNobyBkYW5oIG3hu6VjIG7DoHk6DQoNCg0KYGBge3J9DQojIEFzc2V0IHdlaWdodHMgZm9yIGNvbWJpbmVkIHBvcnRmb2xpbzogDQoNCmFscGhhIDwtIDAuNA0KYmV0YSA8LSAxIC0gYWxwaGENClhfY29tYmluZWQgPC0gYWxwaGEqbWluX3ZhciR3ZWlnaHQgKyBiZXRhKmRmX2VmZmljaWVudF8wMjUkd2VpZ2h0DQoNCiMgUmlzayBhbmQgUmV0dXJuOiANCg0Kcmlza19yZXR1cm5fcG9ydGZvbGlvKHdlaWdodHMgPSBYX2NvbWJpbmVkKSAtPiBkZl9jb21iaW5lZF9wb3J0DQoNCmRmX2NvbWJpbmVkX3BvcnQgJT4lIA0KICBrYmwoY2FwdGlvbiA9ICJUYWJsZSA0OiBSaXNrIGFuZCBSZXR1cm4gZm9yIENvbWJpbmVkIFBvcnRmb2xpbyIsIGVzY2FwZSA9IFRSVUUpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KYGBgDQoNClRhYmxlIDQgY2hvIHRo4bqleSDEkcOieSBsw6AgZGFuaCBt4bulYyB0aHXhu5ljIEVmZmljaWVudCBGcm9udGllciB2w6wgbOG7o2kgdOG7qWMgY+G7p2EgZGFuaCBt4bulYyBuw6B5IGzDoCAwLjE5JSBjYW8gaMahbiBs4bujaSB04bupYyBj4bunYSBHTVYgUG9ydGZvbGlvIGNo4buJIGPDsyBs4bujaSB04bupYyBsw6AgMC4wOSUuIEThu4Uga2nhu4NtIHRyYSB0aOG6pXkgcuG6sW5nIHLhu6dpIHJvIGPhu6dhIGRhbmggbeG7pWMgxJHhuqd1IHTGsCBuw6B5IDAuMDIzMyAtIG3hu5l0IGNvbiBz4buRIHRo4bqlcCBoxqFuIDAuMDMwNSAobMOgIHLhu6dpIHJvIGPhu6dhIGRhbmggbeG7pWMgxJHhuqd1IHTGsCBoaeG7h3UgcXXhuqMgY8OzIGzhu6NpIHThu6ljIG3hu6VjIHRpw6p1IDAuMjUkKSBuaMawbmcgdGjhuqVwIGjGoW4gMC4wMTc5IChsw6AgcuG7p2kgcm8gY+G7p2EgR01WIFBvcnRmb2xpbykuIA0KDQpDaMO6bmcgdGEgY8OzIHRo4buDIGto4bqjbyBzw6F0IHbDoCB24bq9IEVmZmljaWVudCBGcm9udGllciBi4bqxbmcgY8OhY2ggdMOtbmggdG/DoW4gY+G6t3Agcmlzay1yZXR1cm4gY2hvIDEwMCBuZ8aw4buhbmcga2jDoWMgbmhhdSBj4bunYSBs4bujaSB04bupYyB04burIDAlIMSR4bq/biAwLjI1JSBuaMawIGTGsOG7m2kgxJHDonk6ICANCg0KYGBge3J9DQpsb3dfcmV0dXJuIDwtIDANCg0KdXBfcmV0dXJuIDwtIGRmX2VmZmljaWVudF8wMjUkcmV0dXJuWzFdDQoNCnRhcmdldF9yZXR1cm5zXzEwMCA8LSBzZXEobG93X3JldHVybiwgdXBfcmV0dXJuLCBsZW5ndGgub3V0ID0gMTAwKSAlPiUgdW5pcXVlKCkNCg0KbGFwcGx5KHRhcmdldF9yZXR1cm5zXzEwMCwgZmluZF9lZmZpY2llbnRfcG9ydCkgLT4gbGlzdF9lZmZfcG9ydF8xMDANCg0KZG8uY2FsbCgiYmluZF9yb3dzIiwgbGlzdF9lZmZfcG9ydF8xMDApIC0+IGRmX2VmZl9wb3J0XzEwMA0KDQpkZl9lZmZfcG9ydF8xMDAgJT4lIA0KICBmaWx0ZXIoIWR1cGxpY2F0ZWQocmV0dXJuKSkgLT4gZGZfZWZmX2Zyb250aWVyDQoNCiMgR01WIHBvcnRmb2xpbzogDQpkZl9lZmZfZnJvbnRpZXIgJT4lIHNsaWNlKHdoaWNoLm1pbihyaXNrKSkgLT4gZ212X3BvcnQgDQoNCiMgTWF4aW11bSByZXR1cm4gcG9ydGZvbGlvOiANCmRmX2VmZl9mcm9udGllciAlPiUgc2xpY2Uod2hpY2gubWF4KHJpc2spKSAtPiBtYXhfcmV0dXJuX3BvcnQgDQoNCiMgUG9ydGZvbGlvcyBiZWxvbmcgdG8gRWZmaWNpZW50IEZyb250aWVyOiANCmRmX2VmZl9mcm9udGllciAlPiUgZmlsdGVyKHJldHVybiA+IGdtdl9wb3J0JHJldHVybikgLT4gZGZfZWZmX2Zyb250aWVyX2xpbmUNCg0KIyBQb3J0Zm9saW8gYmVsb25nIHRvIEluZWZmaWNpZW50IEZyb250aWVyOiANCmRmX2VmZl9mcm9udGllciAlPiUgZmlsdGVyKHJldHVybiA8IGdtdl9wb3J0JHJldHVybikgLT4gZGZfaW5lZmZfZnJvbnRpZXINCg0KIyBHcmFwaCBvZiBFZmZpY2llbnQgRnJvbnRpZXI6IA0KDQpkZl9yZXR1cm5fbWluaV8xMDAwMCAlPiUgDQogIGdncGxvdChhZXMocmlzaywgcmV0dXJuKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMDUsIGNvbG9yID0gImJsdWUiKSArIA0KICBnZW9tX3BvaW50KGRhdGEgPSBkZl9lZmZfZnJvbnRpZXJfbGluZSwgY29sb3IgPSAiYmx1ZSIpICsgDQogIGdlb21fcG9pbnQoZGF0YSA9IG1heF9yZXR1cm5fcG9ydCwgY29sb3IgPSAicHVycGxlIiwgc2hhcGUgPSAxOCwgc2l6ZSA9IDMpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gbWF4X3JldHVybl9wb3J0LCBhZXMobGFiZWwgPSAiTWluMjUiKSwgZGlyZWN0aW9uID0gInkiKSArIA0KICBnZW9tX3BvaW50KGRhdGEgPSBkZl9pbmVmZl9mcm9udGllciwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gZ212X3BvcnQsIGNvbG9yID0gImdyZWVuIiwgc2hhcGUgPSAxOCwgc2l6ZSA9IDMpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZ212X3BvcnQsIGFlcyhsYWJlbCA9ICJHTVYiKSwgZGlyZWN0aW9uID0gIngiKSArIA0KICBnZW9tX3BvaW50KGRhdGEgPSBkZl9yaXNrX3JldHVybiwgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDMpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZGZfcmlza19yZXR1cm4sIGFlcyhsYWJlbCA9IHN5bWJvbCkpICsgDQogIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSAiRWZmaWNpZW50IEZyb250aWVyIiwgeCA9IDAuMDIyLCB5ID0gMC4wMDIpICsgDQogIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSAiSW5lZmZpY2llbnQgRnJvbnRpZXIiLCB4ID0gMC4wMjEsIHkgPSAwKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQpICsgDQogIGxhYnMoeCA9IGV4cHJlc3Npb24oIlJpc2siIH4gKHNpZ21hKSksIA0KICAgICAgIHkgPSBleHByZXNzaW9uKCJSZXR1cm4iIH4gKG11KSksIA0KICAgICAgIHRpdGxlID0gIkZpZ3VyZSAzOiBFZmZpY2llbnQgRnJvbnRpZXIiKSANCg0KDQpgYGANCg0K4bueIEZpZ3VyZSAzIHRow6wgRWZmaWNpZW50IEZyb250aWVyIGzDoCDEkcaw4budbmcgY29uZyB04bqhbyByYSBi4bqxbmcgY8OhY2ggbuG7kWkgY8OhYyDEkWnhu4NtIG3DoHUgeGFuaC4gSW5lZmZpY2llbnQgRnJvbnRpZXIgbMOgIMSRxrDhu51uZyBjb25nIHThuqFvIHJhIGLhurFuZyBjw6FjaCBu4buRaSBjw6FjIMSRaeG7g20gbcOgdSDEkeG7jy4gDQoNCiMgVGFuZ2VuY3kgUG9ydGZvbGlvDQoNCltTaGFycGUgUmF0aW9dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NoYXJwZV9yYXRpbykgbMOgIG3hu5l0IHRoxrDhu5tjIMSRbyDEkcOhbmggZ2nDoSBoaeG7h3UgcXXhuqMgY+G7p2EgZGFuaCBt4bulYyDEkeG6p3UgdMawIHbDoCDEkcaw4bujYyB0w61uaCB0aGVvIGPDtG5nIHRo4bupYzogDQoNCiQkUz1cZnJhY3tcbXVfcCAtIHJfe2Z9fXtcc2lnbWFfcH0kJA0KVHJvbmcgxJHDsyAkXHNpZ21hX3AkIHbDoCAkXG11X3AkIGzhuqduIGzGsOG7o3QgbMOgIHLhu6dpIHJvIHbDoCBs4bujaSB04bupYyBrw6wgduG7jW5nIGPhu6dhIGRhbmggbeG7pWMgY8OybiAkcl9mJCBsw6AgbOG7o2kgdOG7qWMgY+G7p2EgdMOgaSBz4bqjbiBwaGkgcuG7p2kgcm8uIOG7niDEkcOieSBs4bujaSB04bupYyBj4bunYSB0w6BpIHPhuqNuIHBoaSBy4bunaSBybyDEkcaw4bujYyBjaOG7jW4gbMOgbSB0aGFtIGNoaeG6v3UgdsOgIHRoxrDhu51uZyBsw6AgdHLDoWkgcGhp4bq/dSBjaMOtbmggcGjhu6cgKGhv4bq3YyBULWJpbGxzKS4gDQoNClRhbmdlbmN5IFBvcnRmb2xpbyBsw6AgZGFuaCBt4bulYyDEkeG6p3UgdMawIG3DoCBjw7MgU2hhcnBlIFJhdGlvIGzDoCBjYW8gbmjhuqV0LiBExrDhu5tpIMSRw6J5IGzDoCBSIGNvZGVzIHTDrG0gdOG7iSB0cuG7jW5nIGNobyBjw6FjIHTDoGkgc+G6o24gY2hvIFRhbmdlbmN5IFBvcnRmb2xpbyAoa8OtIGhp4buHdSBsw6AgVEFOKSBu4bq/dSBiaeG6v3QgdHLGsOG7m2MgbMOjaSBzdeG6pXQgKGRhaWx5IHJldHVybikgY+G7p2EgdMOgaSBz4bqjbiBwaGkgcuG7p2kgcm8gKG5oxrAgdHLDoWkgcGhp4bq/dSBjaMOtbmggcGjhu6cpIGzDoCAwLjAxJTogDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgd2VpZ2h0cyBmb3IgVGFuZ2VuY3kgUG9ydGZvbGlvOiANCg0KcmYgPC0gMC4wMDAxDQoNCnNpZ21hX2ludl9tYXQgPC0gc29sdmUoc2lnbWFfbWF0cml4KQ0KDQpvbmVfdmVjIDwtIHJlcCgxLCBuKQ0KDQptdV9taW51c19yZiA8LSBtdSAtIHJmKm9uZV92ZWMNCg0KdG9wX21hdCA8LSBzaWdtYV9pbnZfbWF0ICUqJSBtdV9taW51c19yZg0KDQpib3RfdmFsIDwtIGFzLm51bWVyaWModChvbmVfdmVjKSAlKiUgdG9wX21hdCkNCg0KWF90YW5nZW5jeSA8LSB0b3BfbWF0WywgMV0gLyBib3RfdmFsDQoNCiMgUHJpbnQgcmVzdWx0czogDQoNCnByaW50KFhfdGFuZ2VuY3kpDQoNCmBgYA0KDQpW4bubaSB04buJIHRy4buNbmcgY8OhYyB0w6BpIHPhuqNuIHTDrG0gxJHGsOG7o2MgdGjDrCBj4bq3cCByaXNrLXJldHVybiBj4bunYSBUYW5nZW5jeSBQb3J0Zm9saW8gxJHGsOG7o2MgdMOtbmgg4bufIFRhYmxlIDU6IA0KDQpgYGB7cn0NCnJpc2tfcmV0dXJuX3BvcnRmb2xpbyh3ZWlnaHRzID0gWF90YW5nZW5jeSkgLT4gZGZfdGFuZ2VuY3kNCg0KZGZfdGFuZ2VuY3kgJT4lIA0KICBrYmwoY2FwdGlvbiA9ICJUYWJsZSA0OiBSaXNrIGFuZCBSZXR1cm4gZm9yIFRhbmdlbmN5IFBvcnRmb2xpbyIsIGVzY2FwZSA9IFRSVUUpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KDQpgYGANCg0KVsOgIFNoYXJwZSBSYXRpbyBj4bunYSBkYW5oIG3hu6VjIG7DoHkgc+G6vSBsw6A6IA0KDQpgYGB7cn0NCmRmX3RhbmdlbmN5ICU+JSANCiAgc2xpY2UoMSkgJT4lIA0KICBtdXRhdGUocHJlbWl1bSA9IHJldHVybiAtIHJmKSAlPiUgDQogIG11dGF0ZShzaGFycGVSYXRpbyA9IHByZW1pdW0gLyByaXNrKSAlPiUgDQogIHB1bGwoc2hhcnBlUmF0aW8pDQpgYGANCg0KDQojIE11dHVhbCBGdW5kIFNlcGFyYXRpb24gVGhlb3JlbQ0KDQpYw6l0IG3hu5l0IGRhbmggbeG7pWMgxJHhuqd1IHTGsCDEkcaw4bujYyBow6xuaCB0aMOgbmggdOG7qyB2aeG7h2MgcGjDom4gYuG7lSB04buJIHRy4buNbmcgbMOgICR4X3QkIGNobyBUYW5nZW5jeSBQb3J0Zm9saW8g4bufIHRyw6puIHbDoCBwaOG6p24gY8OybiBs4bqhaSBsw6AgJDEgLSB4X3QkIGNobyB0w6BpIHPhuqNuIHBoaSBy4bunaSBybyBULWJpbGxzLiBS4bunaSBybyB2w6AgbOG7o2kgdOG7qWMgY+G7p2EgZGFuaCBt4bulYyDEkeG6p3UgdMawIG7DoHksIGzhuqduIGzGsOG7o3Qga8OtIGhp4buHdSBsw6AgJFxzaWdtYV57ZX1fcCQgdsOgICRcbXVee2V9X3AkIHPhur0gbMOgOiANCg0KJCRcYmVnaW57ZXF1YXRpb259IFx0YWd7Mn0gXHNpZ21hXntlfV9wID0geF90XHNpZ21hX3AgXGVuZHtlcXVhdGlvbn0kJA0KDQokJFxiZWdpbntlcXVhdGlvbn0gXHRhZ3szfSBcbXVee2V9X3AgPSByX2YgKyB4X3QoXG11X3AgLSByX2YpIFxlbmR7ZXF1YXRpb259JCQNCg0KR2nhuqMgc+G7rSBt4buZdCBuaMOgIMSR4bqndSB0xrAgdGh14buZYyBuaMOzbSBuZ+G6oWkgcuG7p2kgcm8gKGEgaGlnaGx5IHJpc2sgYXZlcnNlIGludmVzdG9yKSDEkeG6p3UgdMawIHbDoG8gVGFuZ2VuY3kgUG9ydGZvbGlvIHbDoCBULWJpbGxzIHbhu5tpIG3hu5l0IG3hu6VjIHRpw6p1IGtoacOqbSB04buRbiB24buBIGzhu6NpIHThu6ljIHbDoCAkXHNpZ21hXntlfV9wID0gMC4wMiQuIERhbmggbeG7pWMgxJHhuqd1IHRoxrAgdGjhu49hIG3Do24gecOqdSBj4bqndSBuw6B5IGPhu6dhIG5ow6AgxJHhuqd1IHTGsCBz4bq9IGPDsyB04buJIHRy4buNbmcgY2hvIFQtYmlsbHMgdGhlbyBjw7RuZyB0aOG7qWMgMiBsw6A6IA0KDQpgYGB7cn0NCnNpZ21hX3RhcmdldCA8LSAwLjAyDQoNClhfZm9yX1RhbmdlbmN5IDwtIHNpZ21hX3RhcmdldCAvICBkZl90YW5nZW5jeSRyaXNrWzFdDQoNCnByaW50KFhfZm9yX1RhbmdlbmN5KQ0KYGBgDQoNClbDoCB04buJIHRy4buNbmcgcGjDom4gYuG7lSBjaG8gVC1iaWxscyBsw6A6IA0KDQpgYGB7cn0NClhfZm9yX1RiaWxscyA8LSAxIC0gWF9mb3JfVGFuZ2VuY3kNCnByaW50KFhfZm9yX1RiaWxscykNCmBgYA0KDQpWw6AgbOG7o2kgdOG7qWMgY+G7p2EgZGFuaCBt4bulYyDEkeG6p3UgdMawIHRoZW8gY8O0bmcgdGjhu6ljIDMgc+G6vSBsw6A6IA0KDQpgYGB7cn0NCm11X1RhbmdlbmN5X1RiaWxsIDwtIHJmICsgWF9mb3JfVGFuZ2VuY3kqKGRmX3RhbmdlbmN5JHJldHVyblsxXSAtIHJmKQ0KDQpwcmludChtdV9UYW5nZW5jeV9UYmlsbCkNCmBgYA0KDQpD4bulIHRo4buDIGjGoW4sIHThu4kgdHLhu41uZyDEkeG6p3UgdMawIHbDoG8gYmEgdMOgaSBz4bqjbiBy4bunaSBybyBz4bq9IGzDoDogDQoNCmBgYHtyfQ0KWF90YW5nZW5jeSpYX2Zvcl9UYW5nZW5jeQ0KYGBgDQoNCkThu4Ugc3V5IHJhIHLhurFuZyB04buVbmcgdOG7iSB0cuG7jW5nIGPhu6dhIGPDoWMgdMOgaSBz4bqjbiBy4bunaSBybyBz4bq9IGLhurFuZyAwLjY2MTQ1MTM6IA0KDQpgYGB7cn0NCnN1bShYX3RhbmdlbmN5KlhfZm9yX1RhbmdlbmN5KQ0KYGBgDQoNCsSQ4bq3dCB0w6puIGNobyBkYW5oIG3hu6VjIMSR4bqndSB0xrAgbsOgeSBsw6AgVEFOMS4gQ8OhYyB0aMO0bmcgdGluIHbhu4EgVEFOMTogDQoNCmBgYHtyfQ0KZGZfdGFuZ2VuY3kxIDwtIHRpYmJsZShzeW1ib2wgPSBjKG15X3N5bWJvbHMsICJULWJpbGxzIiksDQogICAgICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IGMoWF90YW5nZW5jeSpYX2Zvcl9UYW5nZW5jeSwgWF9mb3JfVGJpbGxzKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHJldHVybiA9IG11X1RhbmdlbmN5X1RiaWxsLCANCiAgICAgICAgICAgICAgICAgICAgICAgcmlzayA9IHNpZ21hX3RhcmdldCkNCg0KZGZfdGFuZ2VuY3kxICU+JSANCiAga2JsKGNhcHRpb24gPSAiVGFibGUgNTogUmlzayBhbmQgUmV0dXJuIGZvciBUQU4xIFBvcnRmb2xpbyIsIGVzY2FwZSA9IFRSVUUpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KDQpgYGANCg0KVHLDqm4gaOG7hyB0cuG7pWMgdOG7jWEgxJHhu5kgcmlzay1yZXR1cm4gVEFOMSBz4bq9IGzDoCDEkWnhu4NtIG7hurFtIHRyw6puIMSRxrDhu51uZyB0aOG6s25nIG7hu5FpIGhhaSDEkWnhu4NtIFRBTiB2w6AgVC1iaWxscyB2w6AgxJHGsOG7nW5nIHRo4bqzbmcgbsOgeSDEkcaw4bujYyBn4buNaSBsw6AgVGFuZ2VuY3kgTGluZS4gRMaw4bubaSDEkcOieSBsw6AgUiBjb2RlcyBjaG8gVGFuZ2VuY3kgTGluZTogDQoNCmBgYHtyfQ0KDQojIFByZXBhcmUgZGF0YTogDQoNCmRmX2ZyZWVfcmlzayA8LSB0aWJibGUoc3ltYm9sID0gIlQtYmlsbHMiLCByaXNrID0gMCwgcmV0dXJuID0gcmYpDQoNCmRmX3RhbmdlbmN5ICU+JSANCiAgc2xpY2UoMSkgJT4lIA0KICBtdXRhdGUoc3ltYm9sID0gIlRBTiIpICU+JSANCiAgYmluZF9yb3dzKGRmX3RhbmdlbmN5MSAlPiUgc2xpY2UoMSkgJT4lIG11dGF0ZShzeW1ib2wgPSAiVEFOMSIpKSAlPiUgDQogIGJpbmRfcm93cyhkZl9mcmVlX3Jpc2sgJT4lIG11dGF0ZShzeW1ib2wgPSAiVC1iaWxscyIpKSAtPiBkZl90YW5nZW5jeV9saW5lDQoNCiMgUGxvdCBUYW5nZW5jeSBMaW5lOiANCg0KZGZfZWZmX2Zyb250aWVyX2xpbmUgJT4lIA0KICBnZ3Bsb3QoYWVzKHJpc2ssIHJldHVybikpICsgDQogIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIpICsgDQogIGdlb21fcG9pbnQoZGF0YSA9IGRmX2luZWZmX2Zyb250aWVyLCBjb2xvciA9ICJyZWQiKSArICANCiAgZ2VvbV9wb2ludChkYXRhID0gZ212X3BvcnQsIGNvbG9yID0gImdyZWVuIiwgc2hhcGUgPSAxOCwgc2l6ZSA9IDMpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZ212X3BvcnQsIGFlcyhsYWJlbCA9ICJHTVYiKSwgZGlyZWN0aW9uID0gIngiKSArIA0KICBnZW9tX3BvaW50KGRhdGEgPSBkZl9yaXNrX3JldHVybiwgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDMpICsgDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZGZfcmlza19yZXR1cm4sIGFlcyhsYWJlbCA9IHN5bWJvbCkpICsgDQogIGdlb21fcG9pbnQoZGF0YSA9IGRmX3RhbmdlbmN5X2xpbmUsIGNvbG9yID0gInB1cnBsZSIsIHNpemUgPSAzLCBzaGFwZSA9IDE4KSArIA0KICBnZW9tX2xpbmUoZGF0YSA9IGRmX3RhbmdlbmN5X2xpbmUsIGNvbG9yID0gInB1cnBsZSIsIHNpemUgPSAxKSArIA0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGRmX3RhbmdlbmN5X2xpbmUsIGFlcyhsYWJlbCA9IHN5bWJvbCksIGRpcmVjdGlvbiA9ICJ5IikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsID0gIlRhbmdlbmN5IExpbmUiLCB4ID0gMC4wMSwgeSA9IDAuMDAxMSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50KSArIA0KICBsYWJzKHggPSBleHByZXNzaW9uKCJSaXNrIiB+IChzaWdtYSkpLCANCiAgICAgICB5ID0gZXhwcmVzc2lvbigiUmV0dXJuIiB+IChtdSkpLCANCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgNDogVGFuZ2VuY3kgTGluZSIpICANCg0KYGBgDQoNCk3hu5l0IG5ow6AgxJHhuqd1IHTGsCBjw7MgbmfGsOG7oW5nIGNo4buLdSBy4bunaSBybyBjYW8gKGEgcmlzayB0b2xlcmFudCBpbnZlc3RvcikgcuG6pXQgY8OzIHRo4buDIMawYSB0aMOtY2ggbeG7mXQgZGFuaCBt4bulYyDEkeG6p3UgdMawIGPDsyBs4bujaSB04bupYyBjYW8gaMahbiB24bubaSBjw6FpIGdpw6EgcGjhuqNpIHRy4bqjIGzDoCBy4bunaSBybyBjYW8gaMahbi4gTmjhu69uZyBkYW5oIG3hu6VjIMSR4bqndSB0xrAga2nhu4N1IG7DoHkgc+G6vTogKDEpIHbhuqtuIG7hurFtIHRyw6puIFRhbmdlbmN5IExpbmUsIHbDoCAoMikgbuG6sW0gduG7gSBwaMOtYSB0csOhaSBj4bunYSBUQU4xLiANCg0KIyBSZWZlcmVuY2VzDQoNCjEuIFtJbnZlc3RtZW50cyAxMHRoIEVkaXRpb24gYnkgWnZpIEJvZGllLCBBbGV4IEthbmUgKEF1dGhvciksIEFsYW4gSi4gTWFyY3VzXShodHRwczovL3d3dy5hbWF6b24uY29tL0ludmVzdG1lbnRzLTEwdGgtWnZpLUJvZGllL2RwLzAwNzc4NjE2NzEpLiANCjIuIFtNb2Rlcm4gUG9ydGZvbGlvIFRoZW9yeSBhbmQgSW52ZXN0bWVudCBBbmFseXNpcywgOXRoIEVkaXRpb24gYnkgRWx0b24gZXQgYWwuXShodHRwczovL3d3dy53aWxleS5jb20vZW4tdXMvTW9kZXJuK1BvcnRmb2xpbytUaGVvcnkrYW5kK0ludmVzdG1lbnQrQW5hbHlzaXMlMkMrOXRoK0VkaXRpb24tcC05NzgxMTE4NDY5OTQxKS4gDQozLiBbQmFzaWNzIG9mIE1hdHJpeCBBbGdlYnJhIGZvciBTdGF0aXN0aWNzIHdpdGggUiBieSBOaWNrIEZpZWxsZXJdKGh0dHBzOi8vd3d3LmFtYXpvbi5jb20vQmFzaWNzLU1hdHJpeC1BbGdlYnJhLVN0YXRpc3RpY3MtQ2hhcG1hbi1lYm9vay9kcC9CMDExNlI0NlRRL3JlZj1zcl8xXzE/ZGNoaWxkPTEma2V5d29yZHM9bWF0cml4K2NhbGN1bGF0aW9uK2luK3ImcWlkPTE2MTE0OTE5Njkmcz1ib29rcyZzcj0xLTEpLiANCjQuIFtQb3J0Zm9saW8gVGhlb3J5IHdpdGggTWF0cml4IEFsZ2VicmEgYnkgRXJpYyBaaXZvdF0oaHR0cHM6Ly9mYWN1bHR5Lndhc2hpbmd0b24uZWR1L2V6aXZvdC9lY29uNDI0L3BvcnRmb2xpb1RoZW9yeU1hdHJpeC5wZGYpLiANCg0KDQoNCg0K