0.1 Hồi qui tuyến tính

Trong trường hợp chúng ta muốn tìm ra một mô hình tuyến tính giữa biến số y và các biến số mô tả x(i) khác. Làm thế nào để chọn lựa được một mô hình tối ưu, mô tả được chính xác nhất sự biến thiên của y thông qua các biến số x(i).

Trước hết là chọn lựa ra được những biến số x(i) thật sự có liên quan với biến số y. Đó là trả lời câu hỏi: số lượng biến số và những biến số x(i) nào?

Tính ổn định của mô hình (model stability) là điều mà những nhà nghiên cứu quan tâm. Cụ thể, một mô hình tốt với dataset cụ thể, nhưng khi data thay đổi, dù nhỏ, đã làm cho mô hình thay đổi nhiều, tức là residual sum of squared tăng lên nhiều thì mô hình đó không ổn định. Người ta dùng thủ thuật boostrap để chọn lựa ra những mô hình ổn định nhất. Khi thử nghiệm nhiều mô hình, biến số nào có xác suất xuất hiện trong mô hình cao hơn sẽ được chọn lựa.

Có rất nhiều phương pháp được vận dụng để chọn lựa mô hình hồi qui, ở đây tôi muốn giới thiệu với các bạn chọn lựa mô hình với package mplot: Biểu đồ hóa việc chọn lựa mô hình để xem xét và quyết định chọn mô hình tối ưu.

0.2 Chuẩn bị dữ liệu

library(mplot)
data("diabetes")
head(diabetes)
##   age sex  bmi map  tc   ldl hdl tch    ltg glu   y
## 1  59   2 32.1 101 157  93.2  38   4 4.8598  87 151
## 2  48   1 21.6  87 183 103.2  70   3 3.8918  69  75
## 3  72   2 30.5  93 156  93.6  41   4 4.6728  85 141
## 4  24   1 25.3  84 198 131.4  40   5 4.8903  89 206
## 5  50   1 23.0 101 192 125.4  52   4 4.2905  80 135
## 6  23   1 22.6  89 139  64.8  61   2 4.1897  68  97

Bộ dữ liệu này gồm 10 biến số mô tả như age, sex, bmi, map và các số đo huyết tương như tc, ldl, hdl, tch, ltg, glu cùng biến số kết cục, y, đo lường sự tiến triển của bệnh tiểu đường. Chúng ta sẽ tìm mô hình hồi qui giữa y và các biến số còn lại.

0.3 Đi tìm mô hình: biến số nào có tương quan với y ?

Chúng ta khảo sát mối tương quan giữa y và các biến số còn lại.

library(corrplot)
M<-cor(diabetes)
# function for creating correlation matrix
cor.mtest <- function(mat, ...) {
  mat <- as.matrix(mat)
  n <- ncol(mat)
  p.mat<- matrix(NA, n, n)
  diag(p.mat) <- 0
  for (i in 1:(n - 1)) {
    for (j in (i + 1):n) {
      tmp <- cor.test(mat[, i], mat[, j], ...)
      p.mat[i, j] <- p.mat[j, i] <- tmp$p.value
    }
  }
  colnames(p.mat) <- rownames(p.mat) <- colnames(mat)
  p.mat
}
# matrix of the p-value of the correlation
p.mat <- cor.mtest(mtcars)
# Creating correlation plot
col <- colorRampPalette(c("#BB4444", "#EE9988", "#FFFFFF", "#77AADD", "#4477AA"))
corrplot(M, method="color", col=col(200),  
         type="upper", order="hclust", 
         addCoef.col = "black", # Add coefficient of correlation
         tl.col="black", tl.srt=45, #Text label color and rotation
         # Combine with significance
         p.mat = p.mat, sig.level = 0.01, insig = "blank", 
         # hide correlation coefficient on the principal diagonal
         diag=FALSE 
)

Trước hết, nhìn vào biến số y ở trung tâm của biểu đồ này. Chúng ta thấy y có tương quan cao hơn với các biến số bmi (0.59), ltg (0.57), map (0.44), tch (0.43), hdl (-0.39), glu(0.38)… Tuy rằng, những mối tương quan này không mạnh lắm.

Nếu một mô hình được chọn lựa thì các biến số trên, nhiều khả năng sẽ được đưa vào, sau khi đã loại trừ các mối tương quan lẫn nhau giữa chúng.

0.4 Package mplot

Các hàm của mplot:

vis(): tạo ra một object gồm các biến số được xem xét (variable inclusion)

af(): tạo ra một hàng rào giới hạn ngưỡng ý nghĩa của các biến số (adaptive fence)

0.4.1 Dùng Boostraped probability để so sánh các biến số

Chúng ta vẽ biểu đồ với Penalty (sum of squared coefficients) để kiểm soát các hệ số của các biến số được đưa vào trong mô hình và Boostraped probability là xác suất các biến số được chọn lựa trong những mô hình đang được máy tính đánh giá.

lm.d <- lm(y ~ ., data = diabetes)
vis.d <- vis(lm.d, B = 200, seed = 2018)
plot(vis.d, interactive = FALSE, which = "vip")

Đây là kết quả sau B = 200 lần boostraping data. Biểu đồ này lấy đường RV (redundent variable) làm ngưỡng ý nghĩa. Hai biến số glu và age không được xem là có ý nghĩa khi được đưa vào mô hình vì chúng nắm dưới đường RV.

bmi là biến số xuất hiện nhiều nhất trong các mô hình với xác suất 100%. Kế tiếp là các biến số ltg, map, sex…

0.4.2 Dùng -2*Log-LikeliHood (residual sum of squared: -2LL) để so sánh các biến số

plot(vis.d, interactive = FALSE, which = "lvk")

Một cách khái quát khi số lượng biến số được đưa vào mô hình nhiều hơn thì -2LL giảm đi, nói cách khác các mô hình chính xác hơn. Chương trình tự động đưa biến số age vào để so sánh.

Tuy nhiên, biểu đồ cho thấy không có sự khác biệt đáng kể về -2LL trong những mô hình có biến số age và không age. Khi chọn từ 2-3 biến số đẩ đưa vào mô hình thì age càng không thể xuất hiện.

Tương tự, chúng ta có thể so sánh vai trò của 2 biến số bmi và hdl trong các mô hình qua biểu đồ sau.

plot1 <- plot(vis.d, interactive = FALSE, highlight = "bmi", which = "lvk")
plot2 <- plot(vis.d, interactive = FALSE, highlight = "hdl", which = "lvk")
library(gridExtra)
grid.arrange(plot1, plot2, ncol = 2)

Vai trò của hdl không khác mấy với biến số age.

Ngược lại, bmi có vai trò quan trọng trong các mô hình. Biểu đồ bên trái, khi có bmi trong mô hình (màu đỏ) thì -2LL giảm đáng kể so với những mô hình không có bmi (màu xanh).

0.4.3 Kết hợp -2LL và Boostraped probability để so sánh các mô hình trên biểu đồ

plot1.1 <- plot(vis.d, interactive = FALSE, which = "boot", max.circle = 10, highlight = "bmi") 
plot1.2 <- plot(vis.d, interactive = FALSE, which = "boot", max.circle = 10, highlight = "hdl") 
grid.arrange(plot1.1, plot1.2, ncol = 2)

Kết quả được thể hiện tương tự trong sự kết hợp hai thông số trên.

Những mô hình có sự hiện diện của bmi cho -2LL thấp hơn đáng kể trong sự so sanh. Vòng tròn to cho biết mô hình đó có xác suất chọn lựa cao (gần bằng 1). Những mô hình có từ 7 biến số trở lên không cải thiện hơn -2LL. Mô hình có 5 - 6 biến số đã đạt được -2LL thấp tương đối, với xác suất chọn lựa cao (vòng tròn) khá lớn.

0.4.4 Xem bảng xác suất chọn mô hình và LogLikelihood

print(vis.d)
##                                     name prob logLikelihood
##                                      y~1 1.00      -2547.17
##                                    y~bmi 0.66      -2454.02
##                                    y~ltg 0.34      -2461.86
##                                y~bmi+ltg 0.99      -2411.20
##                            y~bmi+map+ltg 0.66      -2402.61
##                         y~bmi+map+tc+ltg 0.40      -2397.48
##                        y~bmi+map+hdl+ltg 0.34      -2397.71
##                    y~sex+bmi+map+hdl+ltg 0.69      -2390.13
##                 y~sex+bmi+map+tc+ldl+ltg 0.40      -2387.30
##  y~sex+bmi+map+tc+ldl+hdl+tch+ltg+glu+RV 0.30      -2385.39

Xác suất chọn lựa càng cao càng tốt đồng thời loglikelihood càng thấp càng tốt. Vậy mô hình:

y ~ sex + bmi + map + hdl + ltg

Với P = 69% và LogLikelihood = - 2390.13 có thể là tối ưu để chọn lựa.

0.4.5 Sử dụng hàm af

Trong những mô hình được so sánh, mô hình nào có xác suất chọn lựa cao nhất với số lượng biến số ít nhất sẽ là tối ưu. Vì vậy hãy tìm đỉnh xác suất đầu tiên để phát hiện mô hình tốt nhất.

af.d <- af(lm.d, B = 200, n.c = 100, c.max = 100, seed = 2018)
plot(af.d, interactive = FALSE, best.only = TRUE, legend.position = "right")

Đỉnh xác suất đầu tiên đạt được với những mô hình gồm 5 biến số kể trên trong biểu đồ adaptive fence. Chúng ta có thêm bằng chứng để chọn lựa mô hình này.

y ~ sex + bmi + map + hdl + ltg

Đi đến kết luận: Tiến triển của bệnh tiểu đường có thể tiên lượng thông qua các biến số như giới tính của bệnh nhân, cộng với bmi, áp suất động mạch (map), hdl và ltg.

0.4.6 Interactive plots

Sẽ rất thú vị với những biểu đồ interactive, trong đó các bạn có thể chọn lựa các thông số để biểu đồ thay đổi tức thời các kết quả với code:

mplot(lm.d, vis.d, af.d)

0.5 Kết luận

Với sự hỗ trợ của package mplot, chúng ta có thêm một công cụ để phân tích và chọn lựa mô hình linear regression.

Lưu ý mplot cũng hoạt động tốt với các mô hình glm (hồi qui logistic - classification).

0.6 Đọc thêm về mplot package

https://www.jstatsoft.org/article/view/v083i09

LS0tDQp0aXRsZTogJ21QbG90OiBW4bq9IEJp4buDdSDEkOG7kyBWaeG7h2MgQ2jhu41uIEzhu7FhIE3DtCBIw6xuaCBDaG8gSOG7k2kgUXVpIFR1eeG6v24gVMOtbmgnDQphdXRob3I6ICJOZ3V5ZW4gTmdvYyBUaGlldSINCmRhdGU6ICJNYXJjaCAyMSwgMjAxOCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIyBI4buTaSBxdWkgdHV54bq/biB0w61uaA0KDQpUcm9uZyB0csaw4budbmcgaOG7o3AgY2jDum5nIHRhIG114buRbiB0w6xtIHJhIG3hu5l0IG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIGdp4buvYSBiaeG6v24gc+G7kSB5IHbDoCBjw6FjIGJp4bq/biBz4buRIG3DtCB04bqjIHgoaSkga2jDoWMuIEzDoG0gdGjhur8gbsOgbyDEkeG7gyBjaOG7jW4gbOG7sWEgxJHGsOG7o2MgbeG7mXQgbcO0IGjDrG5oIHThu5FpIMawdSwgbcO0IHThuqMgxJHGsOG7o2MgY2jDrW5oIHjDoWMgbmjhuqV0IHPhu7EgYmnhur9uIHRoacOqbiBj4bunYSB5IHRow7RuZyBxdWEgY8OhYyBiaeG6v24gc+G7kSB4KGkpLg0KDQpUcsaw4bubYyBo4bq/dCBsw6AgY2jhu41uIGzhu7FhIHJhIMSRxrDhu6NjIG5o4buvbmcgYmnhur9uIHPhu5EgeChpKSB0aOG6rXQgc+G7sSBjw7MgbGnDqm4gcXVhbiB24bubaSBiaeG6v24gc+G7kSB5LiDEkMOzIGzDoCAgdHLhuqMgbOG7nWkgY8OidSBo4buPaTogc+G7kSBsxrDhu6NuZyBiaeG6v24gc+G7kSB2w6Agbmjhu69uZyBiaeG6v24gc+G7kSB4KGkpIG7DoG8/DQoNClTDrW5oIOG7lW4gxJHhu4tuaCBj4bunYSBtw7QgaMOsbmggKG1vZGVsIHN0YWJpbGl0eSkgbMOgIMSRaeG7gXUgbcOgIG5o4buvbmcgbmjDoCBuZ2hpw6puIGPhu6l1IHF1YW4gdMOibS4gQ+G7pSB0aOG7gywgbeG7mXQgbcO0IGjDrG5oIHThu5F0IHbhu5tpIGRhdGFzZXQgY+G7pSB0aOG7gywgbmjGsG5nIGtoaSBkYXRhIHRoYXkgxJHhu5VpLCBkw7kgbmjhu48sIMSRw6MgbMOgbSBjaG8gbcO0IGjDrG5oIHRoYXkgxJHhu5VpIG5oaeG7gXUsIHThu6ljIGzDoCByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlZCB0xINuZyBsw6puIG5oaeG7gXUgdGjDrCBtw7QgaMOsbmggxJHDsyBraMO0bmcg4buVbiDEkeG7i25oLiBOZ8aw4budaSB0YSBkw7luZyB0aOG7pyB0aHXhuq10IGJvb3N0cmFwIMSR4buDIGNo4buNbiBs4buxYSByYSBuaOG7r25nIG3DtCBow6xuaCDhu5VuIMSR4buLbmggbmjhuqV0LiBLaGkgdGjhu60gbmdoaeG7h20gbmhp4buBdSBtw7QgaMOsbmgsIGJp4bq/biBz4buRIG7DoG8gY8OzIHjDoWMgc3XhuqV0IHh14bqldCBoaeG7h24gdHJvbmcgbcO0IGjDrG5oIGNhbyBoxqFuIHPhur0gxJHGsOG7o2MgY2jhu41uIGzhu7FhLiANCg0KQ8OzIHLhuqV0IG5oaeG7gXUgcGjGsMahbmcgcGjDoXAgxJHGsOG7o2MgduG6rW4gZOG7pW5nIMSR4buDIGNo4buNbiBs4buxYSBtw7QgaMOsbmggaOG7k2kgcXVpLCDhu58gxJHDonkgdMO0aSBtdeG7kW4gZ2nhu5tpIHRoaeG7h3UgduG7m2kgY8OhYyBi4bqhbiBjaOG7jW4gbOG7sWEgbcO0IGjDrG5oIHbhu5tpIHBhY2thZ2UgbXBsb3Q6IEJp4buDdSDEkeG7kyBow7NhIHZp4buHYyBjaOG7jW4gbOG7sWEgbcO0IGjDrG5oIMSR4buDIHhlbSB4w6l0IHbDoCBxdXnhur90IMSR4buLbmggY2jhu41uIG3DtCBow6xuaCB04buRaSDGsHUuDQoNCiMjIENodeG6qW4gYuG7iyBk4buvIGxp4buHdQ0KDQoNCmBgYHtyICwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShtcGxvdCkNCmRhdGEoImRpYWJldGVzIikNCmhlYWQoZGlhYmV0ZXMpDQpgYGANCg0KQuG7mSBk4buvIGxp4buHdSBuw6B5IGfhu5NtIDEwIGJp4bq/biBz4buRIG3DtCB04bqjIG5oxrAgYWdlLCBzZXgsIGJtaSwgbWFwIHbDoCBjw6FjIHPhu5EgxJFvIGh1eeG6v3QgdMawxqFuZyBuaMawIHRjLCBsZGwsIGhkbCwgdGNoLCBsdGcsIGdsdSBjw7luZyBiaeG6v24gc+G7kSBr4bq/dCBj4bulYywgeSwgIMSRbyBsxrDhu51uZyBz4buxIHRp4bq/biB0cmnhu4NuIGPhu6dhIGLhu4duaCB0aeG7g3UgxJHGsOG7nW5nLiBDaMO6bmcgdGEgc+G6vSB0w6xtIG3DtCBow6xuaCBo4buTaSBxdWkgZ2nhu69hIHkgdsOgIGPDoWMgYmnhur9uIHPhu5EgY8OybiBs4bqhaS4NCg0KDQojIyDEkGkgdMOsbSBtw7QgaMOsbmg6IGJp4bq/biBz4buRIG7DoG8gY8OzIHTGsMahbmcgcXVhbiB24bubaSB5ID8NCg0KQ2jDum5nIHRhIGto4bqjbyBzw6F0IG3hu5FpIHTGsMahbmcgcXVhbiBnaeG7r2EgeSB2w6AgY8OhYyBiaeG6v24gc+G7kSBjw7JuIGzhuqFpLg0KDQpgYGB7ciAsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoY29ycnBsb3QpDQpNPC1jb3IoZGlhYmV0ZXMpDQojIGZ1bmN0aW9uIGZvciBjcmVhdGluZyBjb3JyZWxhdGlvbiBtYXRyaXgNCmNvci5tdGVzdCA8LSBmdW5jdGlvbihtYXQsIC4uLikgew0KICBtYXQgPC0gYXMubWF0cml4KG1hdCkNCiAgbiA8LSBuY29sKG1hdCkNCiAgcC5tYXQ8LSBtYXRyaXgoTkEsIG4sIG4pDQogIGRpYWcocC5tYXQpIDwtIDANCiAgZm9yIChpIGluIDE6KG4gLSAxKSkgew0KICAgIGZvciAoaiBpbiAoaSArIDEpOm4pIHsNCiAgICAgIHRtcCA8LSBjb3IudGVzdChtYXRbLCBpXSwgbWF0Wywgal0sIC4uLikNCiAgICAgIHAubWF0W2ksIGpdIDwtIHAubWF0W2osIGldIDwtIHRtcCRwLnZhbHVlDQogICAgfQ0KICB9DQogIGNvbG5hbWVzKHAubWF0KSA8LSByb3duYW1lcyhwLm1hdCkgPC0gY29sbmFtZXMobWF0KQ0KICBwLm1hdA0KfQ0KIyBtYXRyaXggb2YgdGhlIHAtdmFsdWUgb2YgdGhlIGNvcnJlbGF0aW9uDQpwLm1hdCA8LSBjb3IubXRlc3QobXRjYXJzKQ0KIyBDcmVhdGluZyBjb3JyZWxhdGlvbiBwbG90DQpjb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjQkI0NDQ0IiwgIiNFRTk5ODgiLCAiI0ZGRkZGRiIsICIjNzdBQUREIiwgIiM0NDc3QUEiKSkNCmNvcnJwbG90KE0sIG1ldGhvZD0iY29sb3IiLCBjb2w9Y29sKDIwMCksICANCiAgICAgICAgIHR5cGU9InVwcGVyIiwgb3JkZXI9ImhjbHVzdCIsIA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCAjIEFkZCBjb2VmZmljaWVudCBvZiBjb3JyZWxhdGlvbg0KICAgICAgICAgdGwuY29sPSJibGFjayIsIHRsLnNydD00NSwgI1RleHQgbGFiZWwgY29sb3IgYW5kIHJvdGF0aW9uDQogICAgICAgICAjIENvbWJpbmUgd2l0aCBzaWduaWZpY2FuY2UNCiAgICAgICAgIHAubWF0ID0gcC5tYXQsIHNpZy5sZXZlbCA9IDAuMDEsIGluc2lnID0gImJsYW5rIiwgDQogICAgICAgICAjIGhpZGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb24gdGhlIHByaW5jaXBhbCBkaWFnb25hbA0KICAgICAgICAgZGlhZz1GQUxTRSANCikNCmBgYA0KDQpUcsaw4bubYyBo4bq/dCwgbmjDrG4gdsOgbyBiaeG6v24gc+G7kSB5IOG7nyB0cnVuZyB0w6JtIGPhu6dhIGJp4buDdSDEkeG7kyBuw6B5LiBDaMO6bmcgdGEgdGjhuqV5IHkgY8OzIHTGsMahbmcgcXVhbiBjYW8gaMahbiB24bubaSBjw6FjIGJp4bq/biBz4buRICBibWkgKDAuNTkpLCBsdGcgKDAuNTcpLCBtYXAgKDAuNDQpLCB0Y2ggKDAuNDMpLCBoZGwgKC0wLjM5KSwgZ2x1KDAuMzgpLi4uIFR1eSBy4bqxbmcsIG5o4buvbmcgbeG7kWkgdMawxqFuZyBxdWFuIG7DoHkga2jDtG5nIG3huqFuaCBs4bqvbS4gDQoNCk7hur91IG3hu5l0IG3DtCBow6xuaCDEkcaw4bujYyBjaOG7jW4gbOG7sWEgdGjDrCBjw6FjIGJp4bq/biBz4buRIHRyw6puLCBuaGnhu4F1IGto4bqjIG7Eg25nIHPhur0gxJHGsOG7o2MgxJHGsGEgdsOgbywgc2F1IGtoaSDEkcOjIGxv4bqhaSB0cuG7qyBjw6FjIG3hu5FpIHTGsMahbmcgcXVhbiBs4bqrbiBuaGF1IGdp4buvYSBjaMO6bmcuDQoNCiMjIFBhY2thZ2UgbXBsb3QNCg0KQ8OhYyBow6BtIGPhu6dhIG1wbG90Og0KDQp2aXMoKTogdOG6oW8gcmEgbeG7mXQgb2JqZWN0IGfhu5NtIGPDoWMgYmnhur9uIHPhu5EgxJHGsOG7o2MgeGVtIHjDqXQgKHZhcmlhYmxlIGluY2x1c2lvbikNCg0KYWYoKTogdOG6oW8gcmEgbeG7mXQgaMOgbmcgcsOgbyBnaeG7m2kgaOG6oW4gbmfGsOG7oW5nIMO9IG5naMSpYSBj4bunYSBjw6FjIGJp4bq/biBz4buRIChhZGFwdGl2ZSBmZW5jZSkNCg0KIyMjIETDuW5nIEJvb3N0cmFwZWQgcHJvYmFiaWxpdHkgxJHhu4Mgc28gc8OhbmggY8OhYyBiaeG6v24gc+G7kQ0KDQpDaMO6bmcgdGEgduG6vSBiaeG7g3UgxJHhu5MgduG7m2kgUGVuYWx0eSAoc3VtIG9mIHNxdWFyZWQgY29lZmZpY2llbnRzKSDEkeG7gyBraeG7g20gc2/DoXQgIGPDoWMgaOG7hyAgc+G7kSBj4bunYSBjw6FjIGJp4bq/biBz4buRIMSRxrDhu6NjIMSRxrBhIHbDoG8gdHJvbmcgbcO0IGjDrG5oIHbDoCBCb29zdHJhcGVkIHByb2JhYmlsaXR5IGzDoCB4w6FjIHN14bqldCBjw6FjIGJp4bq/biBz4buRIMSRxrDhu6NjIGNo4buNbiBs4buxYSB0cm9uZyBuaOG7r25nIG3DtCBow6xuaCDEkWFuZyDEkcaw4bujYyBtw6F5IHTDrW5oIMSRw6FuaCBnacOhLg0KDQpgYGB7ciAsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0NCmxtLmQgPC0gbG0oeSB+IC4sIGRhdGEgPSBkaWFiZXRlcykNCnZpcy5kIDwtIHZpcyhsbS5kLCBCID0gMjAwLCBzZWVkID0gMjAxOCkNCnBsb3QodmlzLmQsIGludGVyYWN0aXZlID0gRkFMU0UsIHdoaWNoID0gInZpcCIpDQpgYGANCg0KxJDDonkgbMOgIGvhur90IHF14bqjIHNhdSBCID0gMjAwIGzhuqduIGJvb3N0cmFwaW5nIGRhdGEuIEJp4buDdSDEkeG7kyBuw6B5IGzhuqV5IMSRxrDhu51uZyBSViAocmVkdW5kZW50IHZhcmlhYmxlKSBsw6BtIG5nxrDhu6FuZyDDvSBuZ2jEqWEuIEhhaSBiaeG6v24gc+G7kSBnbHUgdsOgIGFnZSBraMO0bmcgxJHGsOG7o2MgeGVtIGzDoCBjw7Mgw70gbmdoxKlhIGtoaSDEkcaw4bujYyDEkcawYSB2w6BvIG3DtCBow6xuaCB2w6wgY2jDum5nIG7huq9tIGTGsOG7m2kgxJHGsOG7nW5nIFJWLg0KDQpibWkgbMOgIGJp4bq/biBz4buRIHh14bqldCBoaeG7h24gbmhp4buBdSBuaOG6pXQgdHJvbmcgY8OhYyBtw7QgaMOsbmggduG7m2kgeMOhYyBzdeG6pXQgMTAwJS4gS+G6vyB0aeG6v3AgbMOgIGPDoWMgYmnhur9uIHPhu5EgbHRnLCBtYXAsIHNleC4uLg0KDQojIyMgRMO5bmcgLTIqTG9nLUxpa2VsaUhvb2QgKHJlc2lkdWFsIHN1bSBvZiBzcXVhcmVkOiAtMkxMKSDEkeG7gyBzbyBzw6FuaCBjw6FjIGJp4bq/biBz4buRDQoNCmBgYHtyfQ0KcGxvdCh2aXMuZCwgaW50ZXJhY3RpdmUgPSBGQUxTRSwgd2hpY2ggPSAibHZrIikNCmBgYA0KDQpN4buZdCBjw6FjaCBraMOhaSBxdcOhdCBraGkgc+G7kSBsxrDhu6NuZyBiaeG6v24gc+G7kSDEkcaw4bujYyDEkcawYSB2w6BvIG3DtCBow6xuaCBuaGnhu4F1IGjGoW4gdGjDrCAtMkxMIGdp4bqjbSDEkWksIG7Ds2kgY8OhY2gga2jDoWMgY8OhYyBtw7QgaMOsbmggY2jDrW5oIHjDoWMgaMahbi4gQ2jGsMahbmcgdHLDrG5oIHThu7EgxJHhu5luZyDEkcawYSBiaeG6v24gc+G7kSBhZ2UgdsOgbyDEkeG7gyBzbyBzw6FuaC4NCg0KVHV5IG5oacOqbiwgYmnhu4N1IMSR4buTIGNobyB0aOG6pXkga2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCDEkcOhbmcga+G7gyB24buBIC0yTEwgdHJvbmcgbmjhu69uZyBtw7QgaMOsbmggY8OzIGJp4bq/biBz4buRIGFnZSB2w6Aga2jDtG5nIGFnZS4gS2hpIGNo4buNbiB04burIDItMyBiaeG6v24gc+G7kSDEkeG6qSDEkcawYSB2w6BvIG3DtCBow6xuaCB0aMOsIGFnZSBjw6BuZyBraMO0bmcgdGjhu4MgeHXhuqV0IGhp4buHbi4NCg0KVMawxqFuZyB04buxLCBjaMO6bmcgdGEgY8OzIHRo4buDIHNvIHPDoW5oIHZhaSB0csOyIGPhu6dhIDIgYmnhur9uIHPhu5EgYm1pIHbDoCBoZGwgdHJvbmcgY8OhYyBtw7QgaMOsbmggcXVhIGJp4buDdSDEkeG7kyBzYXUuDQoNCmBgYHtyfQ0KcGxvdDEgPC0gcGxvdCh2aXMuZCwgaW50ZXJhY3RpdmUgPSBGQUxTRSwgaGlnaGxpZ2h0ID0gImJtaSIsIHdoaWNoID0gImx2ayIpDQpwbG90MiA8LSBwbG90KHZpcy5kLCBpbnRlcmFjdGl2ZSA9IEZBTFNFLCBoaWdobGlnaHQgPSAiaGRsIiwgd2hpY2ggPSAibHZrIikNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbCA9IDIpDQpgYGANCg0KDQpWYWkgdHLDsiBj4bunYSBoZGwga2jDtG5nIGtow6FjIG3huqV5IHbhu5tpIGJp4bq/biBz4buRIGFnZS4NCg0KTmfGsOG7o2MgbOG6oWksIGJtaSBjw7MgdmFpIHRyw7IgcXVhbiB0cuG7jW5nIHRyb25nIGPDoWMgbcO0IGjDrG5oLiBCaeG7g3UgxJHhu5MgYsOqbiB0csOhaSwga2hpIGPDsyBibWkgdHJvbmcgbcO0IGjDrG5oIChtw6B1IMSR4buPKSB0aMOsIC0yTEwgZ2nhuqNtIMSRw6FuZyBr4buDIHNvIHbhu5tpIG5o4buvbmcgbcO0IGjDrG5oIGtow7RuZyBjw7MgYm1pIChtw6B1IHhhbmgpLg0KDQojIyMgS+G6v3QgaOG7o3AgLTJMTCB2w6AgQm9vc3RyYXBlZCBwcm9iYWJpbGl0eSDEkeG7gyBzbyBzw6FuaCBjw6FjIG3DtCBow6xuaCB0csOqbiBiaeG7g3UgxJHhu5MNCg0KYGBge3J9DQpwbG90MS4xIDwtIHBsb3QodmlzLmQsIGludGVyYWN0aXZlID0gRkFMU0UsIHdoaWNoID0gImJvb3QiLCBtYXguY2lyY2xlID0gMTAsIGhpZ2hsaWdodCA9ICJibWkiKSANCnBsb3QxLjIgPC0gcGxvdCh2aXMuZCwgaW50ZXJhY3RpdmUgPSBGQUxTRSwgd2hpY2ggPSAiYm9vdCIsIG1heC5jaXJjbGUgPSAxMCwgaGlnaGxpZ2h0ID0gImhkbCIpIA0KZ3JpZC5hcnJhbmdlKHBsb3QxLjEsIHBsb3QxLjIsIG5jb2wgPSAyKQ0KYGBgDQoNCkvhur90IHF14bqjIMSRxrDhu6NjIHRo4buDIGhp4buHbiB0xrDGoW5nIHThu7EgdHJvbmcgc+G7sSBr4bq/dCBo4bujcCBoYWkgdGjDtG5nIHPhu5EgdHLDqm4uDQoNCk5o4buvbmcgbcO0IGjDrG5oIGPDsyBz4buxIGhp4buHbiBkaeG7h24gY+G7p2EgYm1pIGNobyAtMkxMIHRo4bqlcCBoxqFuIMSRw6FuZyBr4buDIHRyb25nIHPhu7Egc28gc2FuaC4gVsOybmcgdHLDsm4gdG8gY2hvIGJp4bq/dCBtw7QgaMOsbmggxJHDsyBjw7MgeMOhYyBzdeG6pXQgY2jhu41uIGzhu7FhIGNhbyAoZ+G6p24gYuG6sW5nIDEpLiBOaOG7r25nIG3DtCBow6xuaCBjw7MgdOG7qyA3IGJp4bq/biBz4buRIHRy4bufIGzDqm4ga2jDtG5nIGPhuqNpIHRoaeG7h24gaMahbiAtMkxMLiBNw7QgaMOsbmggY8OzIDUgLSA2IGJp4bq/biBz4buRIMSRw6MgxJHhuqF0IMSRxrDhu6NjIC0yTEwgdGjhuqVwIHTGsMahbmcgxJHhu5FpLCB24bubaSB4w6FjIHN14bqldCBjaOG7jW4gbOG7sWEgY2FvICh2w7JuZyB0csOybikga2jDoSBs4bubbi4NCg0KIyMjIFhlbSBi4bqjbmcgeMOhYyBzdeG6pXQgY2jhu41uIG3DtCBow6xuaCB2w6AgTG9nTGlrZWxpaG9vZA0KDQpgYGB7cn0NCnByaW50KHZpcy5kKQ0KYGBgDQoNCljDoWMgc3XhuqV0IGNo4buNbiBs4buxYSBjw6BuZyBjYW8gY8OgbmcgdOG7kXQgxJHhu5NuZyB0aOG7nWkgbG9nbGlrZWxpaG9vZCBjw6BuZyB0aOG6pXAgY8OgbmcgdOG7kXQuIFbhuq15IG3DtCBow6xuaDoNCg0KeSB+IHNleCArIGJtaSArIG1hcCArIGhkbCArIGx0Zw0KDQpW4bubaSBQID0gNjklIHbDoCBMb2dMaWtlbGlob29kID0gLSAyMzkwLjEzIGPDsyB0aOG7gyBsw6AgdOG7kWkgxrB1IMSR4buDIGNo4buNbiBs4buxYS4NCg0KIyMjIFPhu60gZOG7pW5nIGjDoG0gYWYgDQoNClRyb25nIG5o4buvbmcgbcO0IGjDrG5oIMSRxrDhu6NjIHNvIHPDoW5oLCBtw7QgaMOsbmggbsOgbyBjw7MgeMOhYyBzdeG6pXQgY2jhu41uIGzhu7FhIGNhbyBuaOG6pXQgduG7m2kgc+G7kSBsxrDhu6NuZyBiaeG6v24gc+G7kSDDrXQgbmjhuqV0IHPhur0gbMOgIHThu5FpIMawdS4gVsOsIHbhuq15IGjDo3kgdMOsbSDEkeG7iW5oIHjDoWMgc3XhuqV0IMSR4bqndSB0acOqbiDEkeG7gyBwaMOhdCBoaeG7h24gbcO0IGjDrG5oIHThu5F0IG5o4bqldC4NCg0KYGBge3J9DQphZi5kIDwtIGFmKGxtLmQsIEIgPSAyMDAsIG4uYyA9IDEwMCwgYy5tYXggPSAxMDAsIHNlZWQgPSAyMDE4KQ0KcGxvdChhZi5kLCBpbnRlcmFjdGl2ZSA9IEZBTFNFLCBiZXN0Lm9ubHkgPSBUUlVFLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQ0KYGBgDQoNCsSQ4buJbmggeMOhYyBzdeG6pXQgxJHhuqd1IHRpw6puIMSR4bqhdCDEkcaw4bujYyB24bubaSBuaOG7r25nIG3DtCBow6xuaCBn4buTbSA1IGJp4bq/biBz4buRIGvhu4MgIHRyw6puIHRyb25nIGJp4buDdSDEkeG7kyBhZGFwdGl2ZSBmZW5jZS4gQ2jDum5nIHRhIGPDsyB0aMOqbSBi4bqxbmcgY2jhu6luZyDEkeG7gyBjaOG7jW4gbOG7sWEgbcO0IGjDrG5oIG7DoHkuIA0KDQp5IH4gc2V4ICsgYm1pICsgbWFwICsgaGRsICsgbHRnDQoNCsSQaSDEkeG6v24ga+G6v3QgbHXhuq1uOiBUaeG6v24gdHJp4buDbiBj4bunYSBi4buHbmggdGnhu4N1IMSRxrDhu51uZyBjw7MgdGjhu4MgdGnDqm4gbMaw4bujbmcgdGjDtG5nIHF1YSBjw6FjIGJp4bq/biBz4buRIG5oxrAgZ2nhu5tpIHTDrW5oIGPhu6dhIGLhu4duaCBuaMOibiwgY+G7mW5nIHbhu5tpIGJtaSwgw6FwIHN14bqldCDEkeG7mW5nIG3huqFjaCAobWFwKSwgaGRsIHbDoCBsdGcuDQoNCiMjIyBJbnRlcmFjdGl2ZSBwbG90cw0KDQpT4bq9IHLhuqV0IHRow7ogduG7iyB24bubaSBuaOG7r25nIGJp4buDdSDEkeG7kyBpbnRlcmFjdGl2ZSwgdHJvbmcgxJHDsyBjw6FjIGLhuqFuIGPDsyB0aOG7gyBjaOG7jW4gbOG7sWEgY8OhYyB0aMO0bmcgc+G7kSDEkeG7gyBiaeG7g3UgxJHhu5MgdGhheSDEkeG7lWkgdOG7qWMgdGjhu51pIGPDoWMga+G6v3QgcXXhuqMgduG7m2kgY29kZToNCg0KbXBsb3QobG0uZCwgdmlzLmQsIGFmLmQpDQoNCiFbXShGOi9SL1AxLmpwZykNCg0KIyMgS+G6v3QgbHXhuq1uDQoNClbhu5tpIHPhu7EgaOG7lyB0cuG7oyBj4bunYSBwYWNrYWdlIG1wbG90LCBjaMO6bmcgdGEgY8OzIHRow6ptIG3hu5l0IGPDtG5nIGPhu6UgxJHhu4MgcGjDom4gdMOtY2ggdsOgIGNo4buNbiBs4buxYSBtw7QgaMOsbmggbGluZWFyIHJlZ3Jlc3Npb24uDQoNCkzGsHUgw70gbXBsb3QgY8WpbmcgaG/huqF0IMSR4buZbmcgdOG7kXQgduG7m2kgY8OhYyBtw7QgaMOsbmggZ2xtICho4buTaSBxdWkgbG9naXN0aWMgLSBjbGFzc2lmaWNhdGlvbikuDQoNCg0KIyMgxJDhu41jIHRow6ptIHbhu4EgbXBsb3QgcGFja2FnZQ0KDQpodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2FydGljbGUvdmlldy92MDgzaTA5DQoNCg0KDQoNCg0KDQoNCg==