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.
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.
Dados e pacote
library(tidyverse)
library(performance)
mpg
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)

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"
Í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.
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.
———. 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.
———. 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