Licença

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

License: CC BY-SA 4.0

Citação

Sugestão de citação: FIGUEIREDO, Adriano Marcos Rodrigues. Econometria: dica muito útil de visualizar resultados com pacote performance. Campo Grande-MS,Brasil: RStudio/Rpubs, 2021. Disponível em http://rpubs.com/amrofi/dica_performance.

1 Introdução

Usarei o pacote mpg para exemplo do pacote performance de Lüdecke, Ben-Shachar, et al. (2021). Instruções interessantes estão em <https://youtu.be/EPIxQ5i5oxs>, e <https://easystats.github.io/performance/>.

2 Dados e pacote

library(tidyverse)
library(performance)
mpg

3 Modelo com data.frame

Seja um modelo usual, linear de \(hwy=f(disp,class,cyl)\) para os dados do dataset mpg. Posso acessar o \(R^2\) com uso da funçãor2(), ou a performance geral do modelo pela função model_performance(). Esta última fornece mais indicadores do que apenas o \(R^2\) colocado antes.

model_lm <- lm(hwy ~ displ + class + cyl, data = mpg)

model_lm

Call:
lm(formula = hwy ~ displ + class + cyl, data = mpg)

Coefficients:
    (Intercept)            displ     classcompact     classmidsize  
        39.0048          -0.5779          -3.2478          -2.9486  
   classminivan      classpickup  classsubcompact         classsuv  
        -6.9398         -10.2181          -2.6336          -9.0290  
            cyl  
        -1.3306  
r2(model_lm)
# R2 for Linear Regression
       R2: 0.809
  adj. R2: 0.803
model_performance(model_lm)

A função check_model(model_lm) permitirá olhar graficamente:

check_model(model_lm)  # requer o pacote see e ggrepel e qqplotr

Posso comparar com um segundo modelo:

model2 <- glm(am ~ wt + cyl, data = mtcars, family = binomial)
r2(model2)
# R2 for Logistic Regression
  Tjur's R2: 0.705
model_performance(model2)

Podemos comparar os modelos, fazendo (neste caso, fiz um novo modelo lm para comparar modelos de mesma classe):

model_lm2 <- lm(hwy ~ displ + class, data = mpg)
2
[1] 2
model_lm2

Call:
lm(formula = hwy ~ displ + class, data = mpg)

Coefficients:
    (Intercept)            displ     classcompact     classmidsize  
         38.953           -2.298           -5.312           -4.947  
   classminivan      classpickup  classsubcompact         classsuv  
         -8.799          -11.923           -4.699          -10.585  
compare_performance(model_lm, model_lm2)

Podemos também fazer essa comparação fazendo a combinação de funções e o plot. Em todos os casos (indicadores de performance), o ajuste foi melhor para o model_lm.

compare_performance(model_lm, model_lm2, rank = T)
plot(compare_performance(model_lm, model_lm2))

Vou comparar fazendo um modelo de fixed effects, e com datasets iguais. Observe que ele avisará caso os modelos venham de datasets diferentes.

model_lmer <- lme4::lmer(hwy ~ displ + class + cyl + (1 | cty), data = mpg)

model_lmer
Linear mixed model fit by REML ['lmerMod']
Formula: hwy ~ displ + class + cyl + (1 | cty)
   Data: mpg
REML criterion at convergence: 846.322
Random effects:
 Groups   Name        Std.Dev.
 cty      (Intercept) 7.656   
 Residual             1.157   
Number of obs: 234, groups:  cty, 21
Fixed Effects:
    (Intercept)            displ     classcompact     classmidsize  
        31.7544           0.1468          -1.5995          -1.0515  
   classminivan      classpickup  classsubcompact         classsuv  
        -3.3315          -5.7680          -2.0567          -5.1225  
            cyl  
        -0.3089  
r2(model_lmer)
# R2 for Mixed Models

  Conditional R2: 0.979
     Marginal R2: 0.068
model_performance(model_lmer)
plot(compare_performance(model_lm, model_lmer))

Portanto, neste caso, o modelo lmer é melhorar para previsões, conforme valores menores de AIC (868<1130) e BIC (906<1164) para o modelo lmer. Também olhando o R2 ajustado, para o model_lm = 0.803 enquanto o R2 (cond) = 0.979.

compare_performance(model_lm, model_lmer)
check_model(model_lmer)

4 Modelo com tidy

library(tidymodels)

# * Linear Regression ----
model_lm_tidy <- linear_reg() %>%
    set_engine("lm") %>%
    fit(hwy ~ displ + class + cyl, data = mpg)

check_model(model_lm_tidy)

model_lm2_tidy <- linear_reg() %>%
    set_engine("lm") %>%
    fit(hwy ~ displ + class, data = mpg)
compare_performance(model_lm_tidy, model_lm2_tidy, rank = T)
plot(compare_performance(model_lm_tidy, model_lm2_tidy))

check_model(model_lm2_tidy)

5 Testes individuais

É possível realizar alguns testes de pressupostos de modo individualizado, mas ele não mostra os resultados de modo evidente, mas apenas indica se tem problemas. Vejamos.

check_autocorrelation(model_lm)
Warning: Autocorrelated residuals detected (p < .001).
check_collinearity(model_lm)  # VIF
check_heteroscedasticity(model_lm)  # BP test
Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).
plot(check_heteroscedasticity(model_lm))
Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).

check_normality(model_lm)  # shapiro test
Warning: Non-normality of residuals detected (p < .001).
plot(check_normality(model_lm))
Warning: Non-normality of residuals detected (p < .001).

check_outliers(model_lm)
OK: No outliers detected.
plot(check_outliers(model_lm))

check_singularity(model_lm)
[1] FALSE

Vejamos como obter os resultados dos testes. O valor resultante é o p-valor do teste realizado. Normalmente os artigos científicos exigirão também que se relate o valor da estatística de teste.

x <- check_heteroscedasticity(model_lm)
Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).
print(x)
[1] 1.749816e-09
attr(,"object_name")
[1] "model_lm"
attr(,"class")
[1] "check_heteroscedasticity"     "see_check_heteroscedasticity"
[3] "numeric"                     

6 Índice geral de performance

É possível também gerar um indice geral de performance, com modelos de classes diferentes. Vejamos. Sejam quatro modelos distintos: linear, binomial, mixed, poisson.

m1 <- lm(mpg ~ wt + cyl, data = mtcars)  #linear
model_performance(m1)
m2 <- glm(vs ~ wt + mpg, data = mtcars, family = "binomial")  #binominal
model_performance(m2)
library(lme4)
m3 <- lme4::lmer(Reaction ~ Days + (1 + Days | Subject), data = sleepstudy)  #mixed
model_performance(m3)
counts <- c(18, 17, 15, 20, 10, 20, 25, 13, 12)
outcome <- gl(3, 1, 9)
treatment <- gl(3, 3)
m4 <- glm(counts ~ outcome + treatment, family = poisson())  #poisson
model_performance(m4)

Posso olhar cada um dos resultados anteriores, ou fazer um índice geral de performance como no chunk a seguir. Veja o aviso de que os datasets de origem são diferentes. Atentar para a instrução do pacote acerca deste indicador: calculation is based on normalizing all indices (i.e. rescaling them to a range from 0 to 1), and taking the mean value of all indices for each model.

compare_performance(m1, m2, m3, m4, rank = TRUE)

A coluna do Performance-Score é a última desta comparação (o leitor precisará passar as colunas para o lado para visualizá-la).

Como ao gerar o html de output ele está formatando toda a tabela de saída do compare_performance, sugiro ao leitor que olhe a saída do print da comparação.

print(compare_performance(m1, m2, m3, m4, rank = TRUE))
# Comparison of Model Performance Indices

Name |   Model |      AIC |      BIC |   RMSE |  Sigma | Performance-Score
--------------------------------------------------------------------------
m2   |     glm |   31.298 |   35.695 |  0.359 |  0.934 |           100.00%
m4   |     glm |   56.761 |   57.747 |  3.043 |  1.132 |            96.21%
m1   |      lm |  156.010 |  161.873 |  2.444 |  2.568 |            92.46%
m3   | lmerMod | 1755.628 | 1774.786 | 23.438 | 25.592 |             0.00%

A interpretação é a seguinte: os indicadores utilizados para comparação, considerando que são modelos distintos, serão neste caso: AIC, BIC, RMSE e Sigma.

O modelo m2 teve menores valores de cada um deles comparativamente aos demais, em todos os casos (Performance-Score =100%). O modelo m4 foi o segundo melhor conform eo Performance-Score = 96.21%.

O plot de teia de aranha pode auxiliar na visualização entre os componentes do indice.

plot(compare_performance(m1, m2, m3, m4, rank = TRUE))

Se comparássemos apenas m2 e m4, outros indicadores entrariam no computo do indicador geral.

print(compare_performance(m2, m4, rank = TRUE))
# Comparison of Model Performance Indices

Name | Model |    AIC |    BIC |  RMSE | Sigma | Score_log | Score_spherical | Performance-Score
------------------------------------------------------------------------------------------------
m2   |   glm | 31.298 | 35.695 | 0.359 | 0.934 |   -14.903 |           0.095 |            66.67%
m4   |   glm | 56.761 | 57.747 | 3.043 | 1.132 |    -2.598 |           0.324 |            33.33%
plot(compare_performance(m2, m4, rank = TRUE))

Referências

Referências de pacotes utilizados - lista todos os pacotes que foram abertos em algum procedimento (mesmo que dentro de rotinas de outros pacotes). Ou seja, chamamos apenas o tidyverse, performance e tidymodels, mas estes usam muitos outros!

Dancho, Matt (2021). easystats: Quickly investigate model performance. <https://www.business-science.io/r/2021/07/13/easystats-performance-check-model.html?utm_source=Business+Science+-+Combined+List&utm_campaign=54dc4a32b4-R_TIPS_041_EASYSTATS&utm_medium=email&utm_term=0_a4e5b7c52f-54dc4a32b4-308012619&mc_cid=54dc4a32b4&mc_eid=b83cd76e52>. Accessed on July, 15th, 2021.

Allaire, JJ, Yihui Xie, Jonathan McPherson, Javier Luraschi, Kevin Ushey, Aron Atkins, Hadley Wickham, Joe Cheng, Winston Chang, and Richard Iannone. 2021. Rmarkdown: Dynamic Documents for r. https://CRAN.R-project.org/package=rmarkdown.
Barnier, Julien. 2021. Rmdformats: HTML Output Formats and Templates for Rmarkdown Documents. https://github.com/juba/rmdformats.
Bates, Douglas, and Martin Maechler. 2021. Matrix: Sparse and Dense Matrix Classes and Methods. http://Matrix.R-forge.R-project.org/.
Bates, Douglas, Martin Maechler, Ben Bolker, and Steven Walker. 2021. Lme4: Linear Mixed-Effects Models Using Eigen and S4. https://github.com/lme4/lme4/.
Bates, Douglas, Martin Mächler, Ben Bolker, and Steve Walker. 2015. “Fitting Linear Mixed-Effects Models Using lme4.” Journal of Statistical Software 67 (1): 1–48. https://doi.org/10.18637/jss.v067.i01.
Bray, Andrew, Chester Ismay, Evgeni Chasnovski, Ben Baumer, and Mine Cetinkaya-Rundel. 2021. Infer: Tidy Statistical Inference. https://CRAN.R-project.org/package=infer.
Henry, Lionel, and Hadley Wickham. 2020. Purrr: Functional Programming Tools. https://CRAN.R-project.org/package=purrr.
Kuhn, Max. 2020a. Dials: Tools for Creating Tuning Parameter Values. https://CRAN.R-project.org/package=dials.
———. 2020b. Modeldata: Data Sets Used Useful for Modeling Packages. https://CRAN.R-project.org/package=modeldata.
———. 2021a. Tune: Tidy Tuning Tools. https://CRAN.R-project.org/package=tune.
———. 2021b. Workflowsets: Create a Collection of Tidymodels Workflows. https://CRAN.R-project.org/package=workflowsets.
Kuhn, Max, and Davis Vaughan. 2021a. Parsnip: A Common API to Modeling and Analysis Functions. https://CRAN.R-project.org/package=parsnip.
———. 2021b. Yardstick: Tidy Characterizations of Model Performance. https://CRAN.R-project.org/package=yardstick.
Kuhn, Max, and Hadley Wickham. 2020. Tidymodels: A Collection of Packages for Modeling and Machine Learning Using Tidyverse Principles. https://www.tidymodels.org.
———. 2021a. Recipes: Preprocessing Tools to Create Design Matrices. https://CRAN.R-project.org/package=recipes.
———. 2021b. Tidymodels: Easily Install and Load the Tidymodels Packages. https://CRAN.R-project.org/package=tidymodels.
Lüdecke, Daniel, Mattan S. Ben-Shachar, Indrajeet Patil, Philip Waggoner, and Dominique Makowski. 2021. performance: An R Package for Assessment, Comparison and Testing of Statistical Models.” Journal of Open Source Software 6 (60): 3139. https://doi.org/10.21105/joss.03139.
Lüdecke, Daniel, Dominique Makowski, Mattan S. Ben-Shachar, Indrajeet Patil, Philip Waggoner, and Brenton M. Wiernik. 2021. Performance: Assessment of Regression Models Performance. https://easystats.github.io/performance/.
Müller, Kirill, and Hadley Wickham. 2021. Tibble: Simple Data Frames. https://CRAN.R-project.org/package=tibble.
R Core Team. 2021. R: A Language and Environment for Statistical Computing. Vienna, Austria: R Foundation for Statistical Computing. https://www.R-project.org/.
Robinson, David, Alex Hayes, and Simon Couch. 2021. Broom: Convert Statistical Objects into Tidy Tibbles. https://CRAN.R-project.org/package=broom.
Silge, Julia, Fanny Chow, Max Kuhn, and Hadley Wickham. 2021. Rsample: General Resampling Infrastructure. https://CRAN.R-project.org/package=rsample.
Vaughan, Davis. 2021. Workflows: Modeling Workflows. https://CRAN.R-project.org/package=workflows.
Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.
———. 2021a. Forcats: Tools for Working with Categorical Variables (Factors). https://CRAN.R-project.org/package=forcats.
———. 2021b. Stringr: Simple, Consistent Wrappers for Common String Operations.
———. 2021c. Tidyr: Tidy Messy Data. https://CRAN.R-project.org/package=tidyr.
———. 2021d. Tidyverse: Easily Install and Load the Tidyverse. https://CRAN.R-project.org/package=tidyverse.
Wickham, Hadley, Mara Averick, Jennifer Bryan, Winston Chang, Lucy D’Agostino McGowan, Romain François, Garrett Grolemund, et al. 2019. “Welcome to the tidyverse.” Journal of Open Source Software 4 (43): 1686. https://doi.org/10.21105/joss.01686.
Wickham, Hadley, Winston Chang, Lionel Henry, Thomas Lin Pedersen, Kohske Takahashi, Claus Wilke, Kara Woo, Hiroaki Yutani, and Dewey Dunnington. 2021. Ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics. https://CRAN.R-project.org/package=ggplot2.
Wickham, Hadley, Romain François, Lionel Henry, and Kirill Müller. 2021. Dplyr: A Grammar of Data Manipulation. https://CRAN.R-project.org/package=dplyr.
Wickham, Hadley, and Jim Hester. 2020. Readr: Read Rectangular Text Data. https://CRAN.R-project.org/package=readr.
Wickham, Hadley, and Dana Seidel. 2020. Scales: Scale Functions for Visualization. https://CRAN.R-project.org/package=scales.
Xie, Yihui. 2014. “Knitr: A Comprehensive Tool for Reproducible Research in R.” In Implementing Reproducible Computational Research, edited by Victoria Stodden, Friedrich Leisch, and Roger D. Peng. Chapman; Hall/CRC. http://www.crcpress.com/product/isbn/9781466561595.
———. 2015. Dynamic Documents with R and Knitr. 2nd ed. Boca Raton, Florida: Chapman; Hall/CRC. https://yihui.org/knitr/.
———. 2016. Bookdown: Authoring Books and Technical Documents with R Markdown. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/bookdown.
———. 2021a. Bookdown: Authoring Books and Technical Documents with r Markdown. https://CRAN.R-project.org/package=bookdown.
———. 2021b. Knitr: A General-Purpose Package for Dynamic Report Generation in r. https://yihui.org/knitr/.
Xie, Yihui, J. J. Allaire, and Garrett Grolemund. 2018. R Markdown: The Definitive Guide. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/rmarkdown.
Xie, Yihui, Christophe Dervieux, and Emily Riederer. 2020. R Markdown Cookbook. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/rmarkdown-cookbook.
LS0tDQp0aXRsZTogIkVjb25vbWV0cmlhOiBkaWNhIG11aXRvIMO6dGlsIGRlIHZpc3VhbGl6YXIgcmVzdWx0YWRvcyBjb20gcGFjb3RlIHBlcmZvcm1hbmNlIg0KYXV0aG9yOiAiQWRyaWFubyBNYXJjb3MgUm9kcmlndWVzIEZpZ3VlaXJlZG8sICplLW1haWw6IGFkcmlhbm8uZmlndWVpcmVkb0B1Zm1zLmJyKiINCmFic3RyYWN0OiANCiAgVGhlIGV4YW1wbGUgaXMgYSBzaW1wbGlmaWVkIGV4ZXJjaXNlIHVzaW5nIGEgbGluZWFyIG11bHRpcGxlIHJlZ3Jlc3Npb24gd2l0aCB0aGUgcGFja2FnZSBwZXJmb3JtYW5jZS4gDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYCINCmJpYmxpb2dyYXBoeTogcGFja2FnZXMuYmliDQpub2NpdGU6ICdAKicNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0aGVtZTogZGVmYXVsdA0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IG5vDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgZmlnX2NhcHRpb246IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCmBgYHtyIGtuaXRyX2luaXQsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocm1hcmtkb3duKQ0KbGlicmFyeShybWRmb3JtYXRzKQ0KDQojIyBHbG9iYWwgb3B0aW9ucw0Kb3B0aW9ucyhtYXgucHJpbnQ9IjEwMCIpDQpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsDQoJICAgICAgICAgICAgIGNhY2hlPVRSVUUsDQogICAgICAgICAgICAgICBwcm9tcHQ9RkFMU0UsDQogICAgICAgICAgICAgICB0aWR5PVRSVUUsDQogICAgICAgICAgICAgICBjb21tZW50PU5BLA0KICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwNCiAgICAgICAgICAgICAgIHdhcm5pbmc9RkFMU0UsDQogICAgICAgICAgICAgICBvdXQud2lkdGg9NzUwLCANCiAgICAgICAgICAgICAgIGZpZy5oZWlnaHQ9OCwgDQogICAgICAgICAgICAgICBmaWcud2lkdGg9OCkNCm9wdHNfa25pdCRzZXQod2lkdGg9MTAwKQ0KYGBgDQoNCiMgTGljZW7Dp2EgeyNMaWNlbsOnYSAudW5udW1iZXJlZH0NCg0KVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLVNoYXJlQWxpa2UgNC4wIEludGVybmF0aW9uYWwgTGljZW5zZS4gVG8gdmlldyBhIGNvcHkgb2YgdGhpcyBsaWNlbnNlLCB2aXNpdCA8aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLz4gb3Igc2VuZCBhIGxldHRlciB0byBDcmVhdGl2ZSBDb21tb25zLCBQTyBCb3ggMTg2NiwgTW91bnRhaW4gVmlldywgQ0EgOTQwNDIsIFVTQS4NCg0KIVtMaWNlbnNlOiBDQyBCWS1TQSA0LjBdKGh0dHBzOi8vbWlycm9ycy5jcmVhdGl2ZWNvbW1vbnMub3JnL3ByZXNza2l0L2J1dHRvbnMvODh4MzEvcG5nL2J5LXNhLnBuZyl7d2lkdGg9IjI1JSJ9DQoNCiMgQ2l0YcOnw6NvIHsjQ2l0YcOnw6NvIC51bm51bWJlcmVkfQ0KDQpTdWdlc3TDo28gZGUgY2l0YcOnw6NvOiBGSUdVRUlSRURPLCBBZHJpYW5vIE1hcmNvcyBSb2RyaWd1ZXMuIEVjb25vbWV0cmlhOiBkaWNhIG11aXRvIMO6dGlsIGRlIHZpc3VhbGl6YXIgcmVzdWx0YWRvcyBjb20gcGFjb3RlIGBwZXJmb3JtYW5jZWAuIENhbXBvIEdyYW5kZS1NUyxCcmFzaWw6IFJTdHVkaW8vUnB1YnMsIDIwMjEuIERpc3BvbsOtdmVsIGVtIDxodHRwOi8vcnB1YnMuY29tL2Ftcm9maS9kaWNhX3BlcmZvcm1hbmNlPi4NCg0KIyBJbnRyb2R1w6fDo28NCg0KVXNhcmVpIG8gcGFjb3RlIG1wZyBwYXJhIGV4ZW1wbG8gZG8gcGFjb3RlIGBwZXJmb3JtYW5jZWAgZGUgQHBlcmZvcm1hbmNlMjAyMS4gSW5zdHJ1w6fDtWVzIGludGVyZXNzYW50ZXMgZXN0w6NvIGVtIFw8PGh0dHBzOi8veW91dHUuYmUvRVBJeFE1aTVveHM+XD4sIGUgXDw8aHR0cHM6Ly9lYXN5c3RhdHMuZ2l0aHViLmlvL3BlcmZvcm1hbmNlLz5cPi4NCg0KIyBEYWRvcyBlIHBhY290ZQ0KDQpgYGB7ciBkYWRvc30NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwZXJmb3JtYW5jZSkNCm1wZw0KYGBgDQoNCiMgTW9kZWxvIGNvbSBkYXRhLmZyYW1lDQoNClNlamEgdW0gbW9kZWxvIHVzdWFsLCBsaW5lYXIgZGUgJGh3eT1mKGRpc3AsY2xhc3MsY3lsKSQgcGFyYSBvcyBkYWRvcyBkbyBkYXRhc2V0IGBtcGdgLiBQb3NzbyBhY2Vzc2FyIG8gJFJeMiQgY29tIHVzbyBkYSBmdW7Dp8Ojb2ByMigpYCwgb3UgYSBwZXJmb3JtYW5jZSBnZXJhbCBkbyBtb2RlbG8gcGVsYSBmdW7Dp8OjbyBgbW9kZWxfcGVyZm9ybWFuY2UoKWAuIEVzdGEgw7psdGltYSBmb3JuZWNlIG1haXMgaW5kaWNhZG9yZXMgZG8gcXVlIGFwZW5hcyBvICRSXjIkIGNvbG9jYWRvIGFudGVzLg0KDQpgYGB7ciBtb2RlbG8xfQ0KbW9kZWxfbG0gPC0gbG0oaHd5IH4gZGlzcGwgKyBjbGFzcytjeWwsIGRhdGEgPSBtcGcpDQoNCm1vZGVsX2xtDQpyMihtb2RlbF9sbSkNCm1vZGVsX3BlcmZvcm1hbmNlKG1vZGVsX2xtKQ0KDQpgYGANCg0KQSBmdW7Dp8OjbyBjaGVja19tb2RlbChtb2RlbF9sbSkgcGVybWl0aXLDoSBvbGhhciBncmFmaWNhbWVudGU6DQoNCmBgYHtyIGNoZWNrX21vZGVsbzF9DQpjaGVja19tb2RlbChtb2RlbF9sbSkgICMgcmVxdWVyIG8gcGFjb3RlIHNlZSBlIGdncmVwZWwgZSBxcXBsb3RyDQpgYGANCg0KUG9zc28gY29tcGFyYXIgY29tIHVtIHNlZ3VuZG8gbW9kZWxvOg0KDQpgYGB7ciBtb2RlbG9fZ2xtfQ0KbW9kZWwyIDwtIGdsbShhbSB+IHd0ICsgY3lsLCBkYXRhID0gbXRjYXJzLCBmYW1pbHkgPSBiaW5vbWlhbCkNCnIyKG1vZGVsMikNCm1vZGVsX3BlcmZvcm1hbmNlKG1vZGVsMikNCmBgYA0KDQpQb2RlbW9zIGNvbXBhcmFyIG9zIG1vZGVsb3MsIGZhemVuZG8gKG5lc3RlIGNhc28sIGZpeiB1bSBub3ZvIG1vZGVsbyBsbSBwYXJhIGNvbXBhcmFyIG1vZGVsb3MgZGUgbWVzbWEgY2xhc3NlKToNCg0KYGBge3J9DQptb2RlbF9sbTIgPC0gbG0oaHd5IH4gZGlzcGwgKyBjbGFzcywgZGF0YSA9IG1wZykNCjINCm1vZGVsX2xtMg0KY29tcGFyZV9wZXJmb3JtYW5jZShtb2RlbF9sbSxtb2RlbF9sbTIpDQpgYGANCg0KUG9kZW1vcyB0YW1iw6ltIGZhemVyIGVzc2EgY29tcGFyYcOnw6NvIGZhemVuZG8gYSBjb21iaW5hw6fDo28gZGUgZnVuw6fDtWVzIGUgbyBwbG90LiBFbSB0b2RvcyBvcyBjYXNvcyAoaW5kaWNhZG9yZXMgZGUgcGVyZm9ybWFuY2UpLCBvIGFqdXN0ZSBmb2kgbWVsaG9yIHBhcmEgbyBgbW9kZWxfbG1gLg0KDQpgYGB7cn0NCmNvbXBhcmVfcGVyZm9ybWFuY2UobW9kZWxfbG0sbW9kZWxfbG0yLHJhbmsgPSBUKQ0KcGxvdChjb21wYXJlX3BlcmZvcm1hbmNlKG1vZGVsX2xtLG1vZGVsX2xtMikpDQpgYGANCg0KVm91IGNvbXBhcmFyIGZhemVuZG8gdW0gbW9kZWxvIGRlIGZpeGVkIGVmZmVjdHMsIGUgY29tIGRhdGFzZXRzIGlndWFpcy4gT2JzZXJ2ZSBxdWUgZWxlIGF2aXNhcsOhIGNhc28gb3MgbW9kZWxvcyB2ZW5oYW0gZGUgZGF0YXNldHMgZGlmZXJlbnRlcy4NCg0KYGBge3J9DQptb2RlbF9sbWVyIDwtIGxtZTQ6OmxtZXIoaHd5IH4gZGlzcGwgKyBjbGFzcytjeWwrICgxfGN0eSksIGRhdGEgPSBtcGcpDQoNCm1vZGVsX2xtZXINCnIyKG1vZGVsX2xtZXIpDQptb2RlbF9wZXJmb3JtYW5jZShtb2RlbF9sbWVyKQ0KcGxvdChjb21wYXJlX3BlcmZvcm1hbmNlKG1vZGVsX2xtLG1vZGVsX2xtZXIpKQ0KDQpgYGANCg0KUG9ydGFudG8sIG5lc3RlIGNhc28sIG8gbW9kZWxvIGxtZXIgw6kgbWVsaG9yYXIgcGFyYSBwcmV2aXPDtWVzLCBjb25mb3JtZSB2YWxvcmVzIG1lbm9yZXMgZGUgQUlDICg4NjhcPDExMzApIGUgQklDICg5MDZcPDExNjQpIHBhcmEgbyBtb2RlbG8gbG1lci4gVGFtYsOpbSBvbGhhbmRvIG8gUjIgYWp1c3RhZG8sIHBhcmEgbyBtb2RlbF9sbSA9IDAuODAzIGVucXVhbnRvIG8gUjIgKGNvbmQpID0gMC45NzkuDQoNCmBgYHtyfQ0KY29tcGFyZV9wZXJmb3JtYW5jZShtb2RlbF9sbSxtb2RlbF9sbWVyKQ0KY2hlY2tfbW9kZWwobW9kZWxfbG1lcikNCmBgYA0KDQojIE1vZGVsbyBjb20gdGlkeQ0KDQpgYGB7ciB0aWR5bW9kZWx9DQpsaWJyYXJ5KHRpZHltb2RlbHMpDQoNCiMgKiBMaW5lYXIgUmVncmVzc2lvbiAtLS0tDQptb2RlbF9sbV90aWR5IDwtIGxpbmVhcl9yZWcoKSAlPiUNCiAgICBzZXRfZW5naW5lKCJsbSIpICU+JQ0KICAgIGZpdChod3kgfiBkaXNwbCArIGNsYXNzICsgY3lsLCBkYXRhID0gbXBnKQ0KDQpjaGVja19tb2RlbChtb2RlbF9sbV90aWR5KQ0KbW9kZWxfbG0yX3RpZHkgPC0gbGluZWFyX3JlZygpICU+JQ0KICAgIHNldF9lbmdpbmUoImxtIikgJT4lDQogICAgZml0KGh3eSB+IGRpc3BsICsgY2xhc3MsIGRhdGEgPSBtcGcpDQpjb21wYXJlX3BlcmZvcm1hbmNlKG1vZGVsX2xtX3RpZHksbW9kZWxfbG0yX3RpZHkscmFuayA9IFQpDQpwbG90KGNvbXBhcmVfcGVyZm9ybWFuY2UobW9kZWxfbG1fdGlkeSxtb2RlbF9sbTJfdGlkeSkpDQpjaGVja19tb2RlbChtb2RlbF9sbTJfdGlkeSkNCmBgYA0KDQojIFRlc3RlcyBpbmRpdmlkdWFpcw0KDQrDiSBwb3Nzw612ZWwgcmVhbGl6YXIgYWxndW5zIHRlc3RlcyBkZSBwcmVzc3Vwb3N0b3MgZGUgbW9kbyBpbmRpdmlkdWFsaXphZG8sIG1hcyBlbGUgbsOjbyBtb3N0cmEgb3MgcmVzdWx0YWRvcyBkZSBtb2RvIGV2aWRlbnRlLCBtYXMgYXBlbmFzIGluZGljYSBzZSB0ZW0gcHJvYmxlbWFzLiBWZWphbW9zLg0KDQpgYGB7ciB0ZXN0ZXN9DQpjaGVja19hdXRvY29ycmVsYXRpb24obW9kZWxfbG0pDQpjaGVja19jb2xsaW5lYXJpdHkobW9kZWxfbG0pICAjIFZJRg0KY2hlY2tfaGV0ZXJvc2NlZGFzdGljaXR5KG1vZGVsX2xtKSAgIyBCUCB0ZXN0DQpwbG90KGNoZWNrX2hldGVyb3NjZWRhc3RpY2l0eShtb2RlbF9sbSkpDQpjaGVja19ub3JtYWxpdHkobW9kZWxfbG0pICMgc2hhcGlybyB0ZXN0DQpwbG90KGNoZWNrX25vcm1hbGl0eShtb2RlbF9sbSkpDQpjaGVja19vdXRsaWVycyhtb2RlbF9sbSkNCnBsb3QoY2hlY2tfb3V0bGllcnMobW9kZWxfbG0pKQ0KY2hlY2tfc2luZ3VsYXJpdHkobW9kZWxfbG0pDQoNCmBgYA0KDQpWZWphbW9zIGNvbW8gb2J0ZXIgb3MgcmVzdWx0YWRvcyBkb3MgdGVzdGVzLiBPIHZhbG9yIHJlc3VsdGFudGUgw6kgbyBwLXZhbG9yIGRvIHRlc3RlIHJlYWxpemFkby4gTm9ybWFsbWVudGUgb3MgYXJ0aWdvcyBjaWVudMOtZmljb3MgZXhpZ2lyw6NvIHRhbWLDqW0gcXVlIHNlIHJlbGF0ZSBvIHZhbG9yIGRhIGVzdGF0w61zdGljYSBkZSB0ZXN0ZS4NCg0KYGBge3J9DQp4PC1jaGVja19oZXRlcm9zY2VkYXN0aWNpdHkobW9kZWxfbG0pDQpwcmludCh4KQ0KYGBgDQoNCiMgw41uZGljZSBnZXJhbCBkZSBwZXJmb3JtYW5jZQ0KDQrDiSBwb3Nzw612ZWwgdGFtYsOpbSBnZXJhciB1bSBpbmRpY2UgZ2VyYWwgZGUgcGVyZm9ybWFuY2UsIGNvbSBtb2RlbG9zIGRlIGNsYXNzZXMgZGlmZXJlbnRlcy4gVmVqYW1vcy4gU2VqYW0gcXVhdHJvIG1vZGVsb3MgZGlzdGludG9zOiBsaW5lYXIsIGJpbm9taWFsLCBtaXhlZCwgcG9pc3Nvbi4NCg0KYGBge3IgbW9kZWxvc30NCm0xIDwtIGxtKG1wZyB+IHd0ICsgY3lsLCBkYXRhID0gbXRjYXJzKSAjbGluZWFyDQptb2RlbF9wZXJmb3JtYW5jZShtMSkNCm0yIDwtIGdsbSh2cyB+IHd0ICsgbXBnLCBkYXRhID0gbXRjYXJzLCBmYW1pbHkgPSAiYmlub21pYWwiKSAjYmlub21pbmFsDQptb2RlbF9wZXJmb3JtYW5jZShtMikNCmxpYnJhcnkobG1lNCkNCm0zIDwtIGxtZTQ6OmxtZXIoUmVhY3Rpb24gfiBEYXlzICsgKDEgKyBEYXlzIHwgU3ViamVjdCksIGRhdGEgPSBzbGVlcHN0dWR5KSAjbWl4ZWQNCm1vZGVsX3BlcmZvcm1hbmNlKG0zKQ0KY291bnRzIDwtIGMoMTgsIDE3LCAxNSwgMjAsIDEwLCAyMCwgMjUsIDEzLCAxMikNCm91dGNvbWUgPC0gZ2woMywgMSwgOSkNCnRyZWF0bWVudCA8LSBnbCgzLCAzKQ0KbTQgPC0gZ2xtKGNvdW50cyB+IG91dGNvbWUgKyB0cmVhdG1lbnQsIGZhbWlseSA9IHBvaXNzb24oKSkgICNwb2lzc29uDQptb2RlbF9wZXJmb3JtYW5jZShtNCkNCmBgYA0KDQpQb3NzbyBvbGhhciBjYWRhIHVtIGRvcyByZXN1bHRhZG9zIGFudGVyaW9yZXMsIG91IGZhemVyIHVtIMOtbmRpY2UgZ2VyYWwgZGUgcGVyZm9ybWFuY2UgY29tbyBubyBjaHVuayBhIHNlZ3Vpci4gVmVqYSBvIGF2aXNvIGRlIHF1ZSBvcyBkYXRhc2V0cyBkZSBvcmlnZW0gc8OjbyBkaWZlcmVudGVzLiBBdGVudGFyIHBhcmEgYSBpbnN0cnXDp8OjbyBkbyBwYWNvdGUgYWNlcmNhIGRlc3RlIGluZGljYWRvcjogKioqY2FsY3VsYXRpb24gaXMgYmFzZWQgb24gbm9ybWFsaXppbmcgYWxsIGluZGljZXMgKGkuZS4gcmVzY2FsaW5nIHRoZW0gdG8gYSByYW5nZSBmcm9tIDAgdG8gMSksIGFuZCB0YWtpbmcgdGhlIG1lYW4gdmFsdWUgb2YgYWxsIGluZGljZXMgZm9yIGVhY2ggbW9kZWwuKioqDQoNCmBgYHtyfQ0KY29tcGFyZV9wZXJmb3JtYW5jZShtMSwgbTIsIG0zLCBtNCwgcmFuayA9IFRSVUUpDQpgYGANCg0KQSBjb2x1bmEgZG8gUGVyZm9ybWFuY2UtU2NvcmUgw6kgYSDDumx0aW1hIGRlc3RhIGNvbXBhcmHDp8OjbyAobyBsZWl0b3IgcHJlY2lzYXLDoSBwYXNzYXIgYXMgY29sdW5hcyBwYXJhIG8gbGFkbyBwYXJhIHZpc3VhbGl6w6EtbGEpLg0KDQpDb21vIGFvIGdlcmFyIG8gaHRtbCBkZSBvdXRwdXQgZWxlIGVzdMOhIGZvcm1hdGFuZG8gdG9kYSBhIHRhYmVsYSBkZSBzYcOtZGEgZG8gYGNvbXBhcmVfcGVyZm9ybWFuY2VgLCBzdWdpcm8gYW8gbGVpdG9yIHF1ZSBvbGhlIGEgc2HDrWRhIGRvIHByaW50IGRhIGNvbXBhcmHDp8Ojby4NCg0KYGBge3J9DQpwcmludChjb21wYXJlX3BlcmZvcm1hbmNlKG0xLCBtMiwgbTMsIG00LCByYW5rID0gVFJVRSkpDQpgYGANCg0KQSBpbnRlcnByZXRhw6fDo28gw6kgYSBzZWd1aW50ZTogb3MgaW5kaWNhZG9yZXMgdXRpbGl6YWRvcyBwYXJhIGNvbXBhcmHDp8OjbywgY29uc2lkZXJhbmRvIHF1ZSBzw6NvIG1vZGVsb3MgZGlzdGludG9zLCBzZXLDo28gbmVzdGUgY2FzbzogQUlDLCBCSUMsIFJNU0UgZSBTaWdtYS4NCg0KTyBtb2RlbG8gbTIgdGV2ZSBtZW5vcmVzIHZhbG9yZXMgZGUgY2FkYSB1bSBkZWxlcyBjb21wYXJhdGl2YW1lbnRlIGFvcyBkZW1haXMsIGVtIHRvZG9zIG9zIGNhc29zIChQZXJmb3JtYW5jZS1TY29yZSA9MTAwJSkuIE8gbW9kZWxvIG00IGZvaSBvIHNlZ3VuZG8gbWVsaG9yIGNvbmZvcm0gZW8gUGVyZm9ybWFuY2UtU2NvcmUgPSA5Ni4yMSUuDQoNCk8gcGxvdCBkZSB0ZWlhIGRlIGFyYW5oYSBwb2RlIGF1eGlsaWFyIG5hIHZpc3VhbGl6YcOnw6NvIGVudHJlIG9zIGNvbXBvbmVudGVzIGRvIGluZGljZS4NCg0KYGBge3J9DQpwbG90KGNvbXBhcmVfcGVyZm9ybWFuY2UobTEsIG0yLCBtMywgbTQsIHJhbmsgPSBUUlVFKSkgDQpgYGANCg0KU2UgY29tcGFyw6Fzc2Vtb3MgYXBlbmFzIG0yIGUgbTQsIG91dHJvcyBpbmRpY2Fkb3JlcyBlbnRyYXJpYW0gbm8gY29tcHV0byBkbyBpbmRpY2Fkb3IgZ2VyYWwuDQoNCmBgYHtyfQ0KcHJpbnQoY29tcGFyZV9wZXJmb3JtYW5jZShtMiwgbTQsIHJhbmsgPSBUUlVFKSkNCnBsb3QoY29tcGFyZV9wZXJmb3JtYW5jZShtMiwgbTQsIHJhbmsgPSBUUlVFKSkNCmBgYA0KDQojIFJlZmVyw6puY2lhcyB7I1JlZmVyw6puY2lhcyAudW5udW1iZXJlZH0NCg0KKipSZWZlcsOqbmNpYXMgZGUgcGFjb3RlcyB1dGlsaXphZG9zIC0qKiBsaXN0YSB0b2RvcyBvcyBwYWNvdGVzIHF1ZSBmb3JhbSBhYmVydG9zIGVtIGFsZ3VtIHByb2NlZGltZW50byAobWVzbW8gcXVlIGRlbnRybyBkZSByb3RpbmFzIGRlIG91dHJvcyBwYWNvdGVzKS4gT3Ugc2VqYSwgY2hhbWFtb3MgYXBlbmFzIG8gYHRpZHl2ZXJzZWAsIGBwZXJmb3JtYW5jZWAgZSBgdGlkeW1vZGVsc2AsIG1hcyBlc3RlcyB1c2FtIG11aXRvcyBvdXRyb3MhDQoNCkRhbmNobywgTWF0dCAoMjAyMSkuICoqZWFzeXN0YXRzOiBRdWlja2x5IGludmVzdGlnYXRlIG1vZGVsIHBlcmZvcm1hbmNlLioqIFw8PGh0dHBzOi8vd3d3LmJ1c2luZXNzLXNjaWVuY2UuaW8vci8yMDIxLzA3LzEzL2Vhc3lzdGF0cy1wZXJmb3JtYW5jZS1jaGVjay1tb2RlbC5odG1sP3V0bV9zb3VyY2U9QnVzaW5lc3MrU2NpZW5jZSstK0NvbWJpbmVkK0xpc3QmdXRtX2NhbXBhaWduPTU0ZGM0YTMyYjQtUl9USVBTXzA0MV9FQVNZU1RBVFMmdXRtX21lZGl1bT1lbWFpbCZ1dG1fdGVybT0wX2E0ZTViN2M1MmYtNTRkYzRhMzJiNC0zMDgwMTI2MTkmbWNfY2lkPTU0ZGM0YTMyYjQmbWNfZWlkPWI4M2NkNzZlNTI+XD4uIEFjY2Vzc2VkIG9uIEp1bHksIDE1dGgsIDIwMjEuDQoNCmBgYHtyLGluY2x1ZGU9RkFMU0V9DQprbml0cjo6d3JpdGVfYmliKGMoLnBhY2thZ2VzKCksICJib29rZG93biIpLCAicGFja2FnZXMuYmliIiwgd2lkdGggPSA2MCkNCmBgYA0K