library(skimr)
library(dplyr)
library(tidyr)
library(tibble)
library(ggplot2)
library(VIM)
library(readr)
library(rpart.plot)

LOAD DATASET

Set some variable as factor

postop_data$SEX<-as.factor(postop_data$SEX)
postop_data$EYE<-as.factor(postop_data$EYE)
postop_data$IOL_MODEL_NEW<-as.factor(postop_data$IOL_MODEL_NEW)
#postop_data$IOL_SIZE<-as.factor(postop_data$IOL_SIZE)
postop_data$IOL_POSITION<-as.factor(postop_data$IOL_POSITION)
postop_data$POSITION<-as.factor(postop_data$POSITION)

CLEANING DATA

Remove registers with innacurated information

postop_data<-postop_data %>% filter(!(grepl("Bastias",NAME) & EYE == "OD")) 
postop_data<-postop_data %>% filter(!(grepl("Bonadeo",NAME))) 

Set PO_IOL_ANT_RAD_MM outlier from register 32 equals to register 31 (OD = OS)

postop_data[32,]$PO_IOL_ANT_RAD_MM=postop_data[31,]$PO_IOL_ANT_RAD_MM
postop_data %>% filter(grepl("Tomas",NAME)) %>% select(-NAME)

Remove IOL with very few instances

#postop_data$IOL_MODEL_NEW %>% unique()
postop_data %>% group_by(IOL_MODEL_NEW) %>% summarise(n=n()) %>% arrange(desc(n))
`summarise()` ungrouping output (override with `.groups` argument)
postop_data<- postop_data %>% filter( IOL_MODEL_NEW %in% c("VICM5","VTICM5","VTICMO","VICMO"))

EXPLORATORY DATA ANALYSIS (EDA)

Basic distribution information

Categorical variables

skim(postop_data %>% select(SEX,AGE, IOL_MODEL_NEW, IOL_SIZE,IOL_POSITION,POSITION)) %>% yank("factor") %>% knitr::kable()
skim_variable n_missing complete_rate ordered n_unique top_counts
SEX 0 1.0000 FALSE 2 F: 62, M: 18, NS: 0
IOL_MODEL_NEW 0 1.0000 FALSE 4 VIC: 49, VTI: 14, VTI: 10, VIC: 7
IOL_POSITION 0 1.0000 FALSE 3 H: 58, V: 18, O: 4
POSITION 25 0.6875 FALSE 12 CP-: 27, CA-: 5, CP-: 5, CA-: 3

Numerical variables

skim(postop_data %>% select(SEX,AGE, IOL_MODEL_NEW,
                            IOL_SIZE,
                            IOL_POSITION,
                            IOL_EQ,
                            IOL_POWER_SPH,
                            T_RETROPOSITION_MM,
                            N_RETROPOSITION_MM
                            )) %>% yank("numeric") %>% knitr::kable()
skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
AGE 21 0.7375 31.2203390 5.1396625 22.000 28.0000 31.000 34.0000 44.000 ▃▅▇▂▁
IOL_SIZE 0 1.0000 13.2537500 0.2024807 12.600 13.2000 13.200 13.2000 13.700 ▁▁▇▁▁
IOL_EQ 1 0.9875 -9.5253165 3.5587548 -17.500 -11.5000 -9.000 -7.5000 0.000 ▃▃▇▃▁
IOL_POWER_SPH 0 1.0000 -9.9500000 3.4664760 -17.500 -11.7500 -9.000 -7.5000 -3.000 ▃▂▇▆▂
T_RETROPOSITION_MM 25 0.6875 0.4342909 0.2133933 0.000 0.3115 0.445 0.5295 0.982 ▃▇▇▅▁
N_RETROPOSITION_MM 25 0.6875 0.4217455 0.1797101 0.053 0.3075 0.432 0.5310 0.824 ▂▅▇▃▂

PO MEASURES:

Discriminated by gender:

NOTE: PO_IOL_ANT_RAD_MM column was removed for better visualization since it still shows some outliers (>1000)

postop_data%>% select_if(is.numeric) %>% 
  select(starts_with("PO"),-PO_IOL_ANT_RAD_MM) %>% 
  tibble::add_column(SEX=postop_data$SEX) %>% 
  reshape2::melt() %>%
  ggplot()+
    facet_wrap(~SEX)+
    geom_boxplot(aes(x=variable,y=value,fill=variable))+
    #ggdark::dark_theme_gray()+
    theme_bw()+
    theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1))+
  guides(fill=FALSE)
Using SEX as id variables

NA

Discriminated by gender and eyes:


postop_data%>% select_if(is.numeric) %>% 
  select(starts_with("PO"),-PO_IOL_ANT_RAD_MM) %>% 
  tibble::add_column(EYE=postop_data$EYE) %>%
  tibble::add_column(SEX=postop_data$SEX) %>%
  reshape2::melt() %>%
  ggplot()+
    facet_grid(~EYE+SEX)+
    geom_boxplot(aes(x=variable,y=value,fill=variable))+
    theme_bw()+
    #ggdark::dark_theme_gray()+
    theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1))+
  guides(fill=FALSE)
Using EYE, SEX as id variables

postop_data%>% select_if(is.numeric) %>% 
  select(starts_with("PO"),-PO_IOL_ANT_RAD_MM) %>% 
  tibble::add_column(MODEL=postop_data$IOL_MODEL_NEW) %>%
  tibble::add_column(SIZE=postop_data$IOL_SIZE) %>%
  tidyr::pivot_longer(cols = c(-MODEL,-SIZE)) %>%
  ggplot()+
    facet_wrap(~SIZE,ncol = 4)+
    geom_boxplot(aes(x=name,y=value,fill=name))+
    theme_bw()+
    #ggdark::dark_theme_gray()+
    theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1))+
  guides(fill=FALSE)

REMOVE MISSING DATA

THe plot below shows in the Y axis the combination of variables with missing values (red) and X axis, the name of the variables. The histogram in the Y-axis show the frequency of each combination and the histogram on top of the plot shows the frecuency of missing values for each variable.



res<-aggr(postop_data, combined = TRUE, 
          numbers = TRUE, 
          sortCombs = TRUE, 
          sortVars = TRUE, 
          labels=names(postop_data),
          cex.axis=.4, 
          varheight = FALSE,
          cex.numbers=0.4,
          cex.lab=0.4,
          prop = FALSE)

 Variables sorted by number of missings: 

#res$combinations

#res$combinations
#plot(res, numbers = TRUE, prop = FALSE)
#res

Remove columns not providing enough information.

Some of the column were removed based on the number of missing values and some other just because they not provide valuable information (this must be confirmed)

library(lubridate)
postop_data <- postop_data %>% select(-CR,
                                      -AGE,
                                      -QX,
                                      -QX_DATE,
                                      -CONTROL_DATE,
                                      -IOL_MODEL_OLD,
                                      -IOL_EQ_CHECK,
                                      -IOL_EQ_CALC,
                                      -IOL_CYL,-IOL_AXIS,
                                      -PO_C_VAULT_1D_MM)


postop_data <- postop_data %>% mutate(AGE=floor(interval(dmy(DOB),today())/years(1))) %>% select(-DOB)

Remove missing values in POSITION

postop_data<-postop_data %>% filter(!is.na(POSITION))

Impute missing values

A basic KNN imputation is done for performing some other analisys such as PCA.


postop_data_imputed <- kNN(postop_data)
postop_data<-postop_data_imputed %>% select(-ends_with("_imp"))
postop_data %>% select(-NAME)
NA

CORRELATION ANALYSIS

Considered variables for PCA

library(ggplot2)
library(ggfortify)
library(cluster)
#postop_data_pca %>% ncol
#postop_data %>% nrow

postop_data_pca<-postop_data %>% select(starts_with("PO_")) %>% select_if(is.numeric) %>% 
  add_column(T_RETROPOSITION_MM=postop_data$T_RETROPOSITION_MM) %>% 
  add_column(N_RETROPOSITION_MM=postop_data$N_RETROPOSITION_MM) %>% 
  add_column(C_VAULT_MM=postop_data$C_VAULT_MM) %>%
  add_column(N_VAULT_MM=postop_data$N_VAULT_MM) %>%
  add_column(T_VAULT_MM=postop_data$T_VAULT_MM) %>% 
  add_column(AGE=postop_data$AGE) %>%
  add_column(IOL_POWER=postop_data$IOL_POWER) #%>%
  #add_column(EYE=postop_data$EYE) 
  
  
names(postop_data_pca) %>% as_tibble()

PCA

pca<-prcomp(postop_data_pca,center=TRUE,scale.=TRUE)
postop_data_pca_res<-data.frame(pca$x,position=postop_data$POSITION,
                            gender=postop_data$SEX,
                            istoric=as.factor(postop_data$IS_TORIC_LENS))

plot<-autoplot(pca,data=postop_data,colour='POSITION',loadings = TRUE,
         loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 2,shape=1,size=3)

#plot+
#  theme_bw()


#ggplot(postop_data_pca_res) +
#  geom_point(aes(x=PC1,y=PC2,color=position),shape =1, size=3 )+
#  theme_bw()




#plotly::plot_ly(postop_data_pca_res , type="scatter3d", 
#                x = ~PC2, y = ~PC1, z = ~PC3, color = ~position,
#                colors = c('blue', 'orange',"red","green"), 
#                opacity=0.5, marker = list(size = 3),text = ~age~gender) 
library(FactoMineR)
#postop_data_pca<-postop_data %>% select(starts_with("PO_")) %>% select_if(is.numeric) %>% add_column(postop_data$SEX)
res_pca = PCA(postop_data_pca, scale.unit=TRUE, ncp=6, 
              graph=T,
              quali.sup=12
              )

PCA Feature Selection

res_pca$var$contrib %>% as.data.frame() %>% add_rownames()%>% reshape2::melt() %>% group_by(variable) %>%
  slice(which.max(value))
Using rowname as id variables
# %>% summarise(max=max(value))

Correlation matrix

Using Spearman correlation

library(d3heatmap)
postop_data_cor_matrix<-cor(postop_data_pca,method="spearman")
#heatmap(postop_data_cor_matrix)
d3heatmap(postop_data_cor_matrix,colors = "Blues",cexRow = 0.8, cexCol = 0.8)

Pairs matrix

pairs(postop_data_pca)

readr::write_csv(postop_data,path="postop_data_cleaned.csv")
LS0tCnRpdGxlOiAiSVogSUNMIEVEQSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgoKCmBgYHtyfQpsaWJyYXJ5KHNraW1yKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFZJTSkKbGlicmFyeShyZWFkcikKbGlicmFyeShycGFydC5wbG90KQpgYGAKCiMgTE9BRCBEQVRBU0VUCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpwb3N0b3BfZGF0YSA8LSByZWFkcjo6cmVhZF9kZWxpbSgiZGF0YXNldHMvQVJDU0NBTiBQT1NUT1AgSUNMIC0gQ0xFQU5FRC50c3YiLGRlbGltPSdcdCcpCmBgYAoKIyMjIFNldCBzb21lIHZhcmlhYmxlIGFzIGZhY3RvcgpgYGB7cn0KcG9zdG9wX2RhdGEkU0VYPC1hcy5mYWN0b3IocG9zdG9wX2RhdGEkU0VYKQpwb3N0b3BfZGF0YSRFWUU8LWFzLmZhY3Rvcihwb3N0b3BfZGF0YSRFWUUpCnBvc3RvcF9kYXRhJElPTF9NT0RFTF9ORVc8LWFzLmZhY3Rvcihwb3N0b3BfZGF0YSRJT0xfTU9ERUxfTkVXKQojcG9zdG9wX2RhdGEkSU9MX1NJWkU8LWFzLmZhY3Rvcihwb3N0b3BfZGF0YSRJT0xfU0laRSkKcG9zdG9wX2RhdGEkSU9MX1BPU0lUSU9OPC1hcy5mYWN0b3IocG9zdG9wX2RhdGEkSU9MX1BPU0lUSU9OKQpwb3N0b3BfZGF0YSRQT1NJVElPTjwtYXMuZmFjdG9yKHBvc3RvcF9kYXRhJFBPU0lUSU9OKQpgYGAKCiMgQ0xFQU5JTkcgREFUQQoKIyMjIFJlbW92ZSByZWdpc3RlcnMgd2l0aCBpbm5hY3VyYXRlZCBpbmZvcm1hdGlvbgoKYGBge3J9CnBvc3RvcF9kYXRhPC1wb3N0b3BfZGF0YSAlPiUgZmlsdGVyKCEoZ3JlcGwoIkJhc3RpYXMiLE5BTUUpICYgRVlFID09ICJPRCIpKSAKcG9zdG9wX2RhdGE8LXBvc3RvcF9kYXRhICU+JSBmaWx0ZXIoIShncmVwbCgiQm9uYWRlbyIsTkFNRSkpKSAKCmBgYAoKIyMjIFNldCAgUE9fSU9MX0FOVF9SQURfTU0gb3V0bGllciBmcm9tIHJlZ2lzdGVyIDMyIGVxdWFscyB0byByZWdpc3RlciAzMSAoT0QgPSBPUykKYGBge3J9CnBvc3RvcF9kYXRhWzMyLF0kUE9fSU9MX0FOVF9SQURfTU09cG9zdG9wX2RhdGFbMzEsXSRQT19JT0xfQU5UX1JBRF9NTQpwb3N0b3BfZGF0YSAlPiUgZmlsdGVyKGdyZXBsKCJUb21hcyIsTkFNRSkpICU+JSBzZWxlY3QoLU5BTUUpCmBgYAoKIyMjIFJlbW92ZSBJT0wgd2l0aCB2ZXJ5IGZldyBpbnN0YW5jZXMKCmBgYHtyfQojcG9zdG9wX2RhdGEkSU9MX01PREVMX05FVyAlPiUgdW5pcXVlKCkKcG9zdG9wX2RhdGEgJT4lIGdyb3VwX2J5KElPTF9NT0RFTF9ORVcpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCnBvc3RvcF9kYXRhPC0gcG9zdG9wX2RhdGEgJT4lIGZpbHRlciggSU9MX01PREVMX05FVyAlaW4lIGMoIlZJQ001IiwiVlRJQ001IiwiVlRJQ01PIiwiVklDTU8iKSkKYGBgCgoKIyBFWFBMT1JBVE9SWSBEQVRBIEFOQUxZU0lTIChFREEpCgoKCiMjIyBCYXNpYyBkaXN0cmlidXRpb24gaW5mb3JtYXRpb24KIyMjIENhdGVnb3JpY2FsIHZhcmlhYmxlcwoKYGBge3J9CnNraW0ocG9zdG9wX2RhdGEgJT4lIHNlbGVjdChTRVgsQUdFLCBJT0xfTU9ERUxfTkVXLCBJT0xfU0laRSxJT0xfUE9TSVRJT04sUE9TSVRJT04pKSAlPiUgeWFuaygiZmFjdG9yIikgJT4lIGtuaXRyOjprYWJsZSgpCmBgYAoKCiMjIyBOdW1lcmljYWwgdmFyaWFibGVzCmBgYHtyfQpza2ltKHBvc3RvcF9kYXRhICU+JSBzZWxlY3QoU0VYLEFHRSwgSU9MX01PREVMX05FVywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIElPTF9TSVpFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgSU9MX1BPU0lUSU9OLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgSU9MX0VRLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgSU9MX1BPV0VSX1NQSCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRfUkVUUk9QT1NJVElPTl9NTSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5fUkVUUk9QT1NJVElPTl9NTQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgJT4lIHlhbmsoIm51bWVyaWMiKSAlPiUga25pdHI6OmthYmxlKCkKYGBgCgojIyBQTyBNRUFTVVJFUzogCgojIyMjIERpc2NyaW1pbmF0ZWQgYnkgZ2VuZGVyOgoKTk9URTogUE9fSU9MX0FOVF9SQURfTU0gY29sdW1uIHdhcyByZW1vdmVkIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbiBzaW5jZSBpdCBzdGlsbCBzaG93cyBzb21lIG91dGxpZXJzICg+MTAwMCkKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEyfQpwb3N0b3BfZGF0YSU+JSBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lIAogIHNlbGVjdChzdGFydHNfd2l0aCgiUE8iKSwtUE9fSU9MX0FOVF9SQURfTU0pICU+JSAKICB0aWJibGU6OmFkZF9jb2x1bW4oU0VYPXBvc3RvcF9kYXRhJFNFWCkgJT4lIAogIHJlc2hhcGUyOjptZWx0KCkgJT4lCiAgZ2dwbG90KCkrCiAgICBmYWNldF93cmFwKH5TRVgpKwogICAgZ2VvbV9ib3hwbG90KGFlcyh4PXZhcmlhYmxlLHk9dmFsdWUsZmlsbD12YXJpYWJsZSkpKwogICAgI2dnZGFyazo6ZGFya190aGVtZV9ncmF5KCkrCiAgICB0aGVtZV9idygpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSsKICBndWlkZXMoZmlsbD1GQUxTRSkKICAgCmBgYAoKIyMjIyBEaXNjcmltaW5hdGVkIGJ5IGdlbmRlciBhbmQgZXllczoKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEyfQoKcG9zdG9wX2RhdGElPiUgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBzZWxlY3Qoc3RhcnRzX3dpdGgoIlBPIiksLVBPX0lPTF9BTlRfUkFEX01NKSAlPiUgCiAgdGliYmxlOjphZGRfY29sdW1uKEVZRT1wb3N0b3BfZGF0YSRFWUUpICU+JQogIHRpYmJsZTo6YWRkX2NvbHVtbihTRVg9cG9zdG9wX2RhdGEkU0VYKSAlPiUKICByZXNoYXBlMjo6bWVsdCgpICU+JQogIGdncGxvdCgpKwogICAgZmFjZXRfZ3JpZCh+RVlFK1NFWCkrCiAgICBnZW9tX2JveHBsb3QoYWVzKHg9dmFyaWFibGUseT12YWx1ZSxmaWxsPXZhcmlhYmxlKSkrCiAgICB0aGVtZV9idygpKwogICAgI2dnZGFyazo6ZGFya190aGVtZV9ncmF5KCkrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpKwogIGd1aWRlcyhmaWxsPUZBTFNFKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEyfQpwb3N0b3BfZGF0YSU+JSBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lIAogIHNlbGVjdChzdGFydHNfd2l0aCgiUE8iKSwtUE9fSU9MX0FOVF9SQURfTU0pICU+JSAKICB0aWJibGU6OmFkZF9jb2x1bW4oTU9ERUw9cG9zdG9wX2RhdGEkSU9MX01PREVMX05FVykgJT4lCiAgdGliYmxlOjphZGRfY29sdW1uKFNJWkU9cG9zdG9wX2RhdGEkSU9MX1NJWkUpICU+JQogIHRpZHlyOjpwaXZvdF9sb25nZXIoY29scyA9IGMoLU1PREVMLC1TSVpFKSkgJT4lCiAgZ2dwbG90KCkrCiAgICBmYWNldF93cmFwKH5TSVpFLG5jb2wgPSA0KSsKICAgIGdlb21fYm94cGxvdChhZXMoeD1uYW1lLHk9dmFsdWUsZmlsbD1uYW1lKSkrCiAgICB0aGVtZV9idygpKwogICAgI2dnZGFyazo6ZGFya190aGVtZV9ncmF5KCkrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpKwogIGd1aWRlcyhmaWxsPUZBTFNFKQpgYGAKCiMgUkVNT1ZFIE1JU1NJTkcgREFUQQoKVEhlIHBsb3QgYmVsb3cgc2hvd3MgaW4gdGhlIFkgYXhpcyB0aGUgY29tYmluYXRpb24gb2YgdmFyaWFibGVzIHdpdGggbWlzc2luZyB2YWx1ZXMgKHJlZCkgYW5kIFggYXhpcywgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlcy4gVGhlIGhpc3RvZ3JhbSBpbiB0aGUgWS1heGlzIHNob3cgdGhlIGZyZXF1ZW5jeSBvZiBlYWNoIGNvbWJpbmF0aW9uIGFuZCB0aGUgaGlzdG9ncmFtIG9uIHRvcCBvZiB0aGUgcGxvdCBzaG93cyB0aGUgZnJlY3VlbmN5IG9mIG1pc3NpbmcgdmFsdWVzIGZvciBlYWNoIHZhcmlhYmxlLgoKYGBge3J9CgoKcmVzPC1hZ2dyKHBvc3RvcF9kYXRhLCBjb21iaW5lZCA9IFRSVUUsIAogICAgICAgICAgbnVtYmVycyA9IFRSVUUsIAogICAgICAgICAgc29ydENvbWJzID0gVFJVRSwgCiAgICAgICAgICBzb3J0VmFycyA9IFRSVUUsIAogICAgICAgICAgbGFiZWxzPW5hbWVzKHBvc3RvcF9kYXRhKSwKICAgICAgICAgIGNleC5heGlzPS40LCAKICAgICAgICAgIHZhcmhlaWdodCA9IEZBTFNFLAogICAgICAgICAgY2V4Lm51bWJlcnM9MC40LAogICAgICAgICAgY2V4LmxhYj0wLjQsCiAgICAgICAgICBwcm9wID0gRkFMU0UpCiNyZXMkY29tYmluYXRpb25zCgojcmVzJGNvbWJpbmF0aW9ucwojcGxvdChyZXMsIG51bWJlcnMgPSBUUlVFLCBwcm9wID0gRkFMU0UpCiNyZXMKYGBgCgojIyBSZW1vdmUgY29sdW1ucyBub3QgcHJvdmlkaW5nIGVub3VnaCBpbmZvcm1hdGlvbi4KClNvbWUgb2YgdGhlIGNvbHVtbiB3ZXJlIHJlbW92ZWQgYmFzZWQgb24gdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBhbmQgc29tZSBvdGhlciBqdXN0IGJlY2F1c2UgdGhleSBub3QgcHJvdmlkZSB2YWx1YWJsZSBpbmZvcm1hdGlvbiAodGhpcyBtdXN0IGJlIGNvbmZpcm1lZCkKCmBgYHtyfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKcG9zdG9wX2RhdGEgPC0gcG9zdG9wX2RhdGEgJT4lIHNlbGVjdCgtQ1IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUFHRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUVgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVFYX0RBVEUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUNPTlRST0xfREFURSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtSU9MX01PREVMX09MRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtSU9MX0VRX0NIRUNLLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1JT0xfRVFfQ0FMQywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtSU9MX0NZTCwtSU9MX0FYSVMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVBPX0NfVkFVTFRfMURfTU0pCgoKcG9zdG9wX2RhdGEgPC0gcG9zdG9wX2RhdGEgJT4lIG11dGF0ZShBR0U9Zmxvb3IoaW50ZXJ2YWwoZG15KERPQiksdG9kYXkoKSkveWVhcnMoMSkpKSAlPiUgc2VsZWN0KC1ET0IpCmBgYAoKIyMgUmVtb3ZlIG1pc3NpbmcgdmFsdWVzIGluIFBPU0lUSU9OCgpgYGB7cn0KcG9zdG9wX2RhdGE8LXBvc3RvcF9kYXRhICU+JSBmaWx0ZXIoIWlzLm5hKFBPU0lUSU9OKSkKYGBgCgoKCiMjIEltcHV0ZSBtaXNzaW5nIHZhbHVlcwpBIGJhc2ljIEtOTiBpbXB1dGF0aW9uIGlzIGRvbmUgZm9yIHBlcmZvcm1pbmcgc29tZSBvdGhlciBhbmFsaXN5cyBzdWNoIGFzIFBDQS4KCmBgYHtyfQoKcG9zdG9wX2RhdGFfaW1wdXRlZCA8LSBrTk4ocG9zdG9wX2RhdGEpCnBvc3RvcF9kYXRhPC1wb3N0b3BfZGF0YV9pbXB1dGVkICU+JSBzZWxlY3QoLWVuZHNfd2l0aCgiX2ltcCIpKQpwb3N0b3BfZGF0YSAlPiUgc2VsZWN0KC1OQU1FKQoKYGBgCgoKIyBDT1JSRUxBVElPTiBBTkFMWVNJUwojIyBDb25zaWRlcmVkIHZhcmlhYmxlcyBmb3IgUENBCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpsaWJyYXJ5KGNsdXN0ZXIpCiNwb3N0b3BfZGF0YV9wY2EgJT4lIG5jb2wKI3Bvc3RvcF9kYXRhICU+JSBucm93Cgpwb3N0b3BfZGF0YV9wY2E8LXBvc3RvcF9kYXRhICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoIlBPXyIpKSAlPiUgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBhZGRfY29sdW1uKFRfUkVUUk9QT1NJVElPTl9NTT1wb3N0b3BfZGF0YSRUX1JFVFJPUE9TSVRJT05fTU0pICU+JSAKICBhZGRfY29sdW1uKE5fUkVUUk9QT1NJVElPTl9NTT1wb3N0b3BfZGF0YSROX1JFVFJPUE9TSVRJT05fTU0pICU+JSAKICBhZGRfY29sdW1uKENfVkFVTFRfTU09cG9zdG9wX2RhdGEkQ19WQVVMVF9NTSkgJT4lCiAgYWRkX2NvbHVtbihOX1ZBVUxUX01NPXBvc3RvcF9kYXRhJE5fVkFVTFRfTU0pICU+JQogIGFkZF9jb2x1bW4oVF9WQVVMVF9NTT1wb3N0b3BfZGF0YSRUX1ZBVUxUX01NKSAlPiUgCiAgYWRkX2NvbHVtbihBR0U9cG9zdG9wX2RhdGEkQUdFKSAlPiUKICBhZGRfY29sdW1uKElPTF9QT1dFUj1wb3N0b3BfZGF0YSRJT0xfUE9XRVIpICMlPiUKICAjYWRkX2NvbHVtbihFWUU9cG9zdG9wX2RhdGEkRVlFKSAKICAKICAKbmFtZXMocG9zdG9wX2RhdGFfcGNhKSAlPiUgYXNfdGliYmxlKCkKYGBgCgoKIyMgUENBCgpgYGB7cn0KcGNhPC1wcmNvbXAocG9zdG9wX2RhdGFfcGNhLGNlbnRlcj1UUlVFLHNjYWxlLj1UUlVFKQpwb3N0b3BfZGF0YV9wY2FfcmVzPC1kYXRhLmZyYW1lKHBjYSR4LHBvc2l0aW9uPXBvc3RvcF9kYXRhJFBPU0lUSU9OLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZGVyPXBvc3RvcF9kYXRhJFNFWCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzdG9yaWM9YXMuZmFjdG9yKHBvc3RvcF9kYXRhJElTX1RPUklDX0xFTlMpKQoKcGxvdDwtYXV0b3Bsb3QocGNhLGRhdGE9cG9zdG9wX2RhdGEsY29sb3VyPSdQT1NJVElPTicsbG9hZGluZ3MgPSBUUlVFLAogICAgICAgICBsb2FkaW5ncy5jb2xvdXIgPSAnYmx1ZScsCiAgICAgICAgIGxvYWRpbmdzLmxhYmVsID0gVFJVRSwgbG9hZGluZ3MubGFiZWwuc2l6ZSA9IDIsc2hhcGU9MSxzaXplPTMpCgojcGxvdCsKIyAgdGhlbWVfYncoKQoKCiNnZ3Bsb3QocG9zdG9wX2RhdGFfcGNhX3JlcykgKwojICBnZW9tX3BvaW50KGFlcyh4PVBDMSx5PVBDMixjb2xvcj1wb3NpdGlvbiksc2hhcGUgPTEsIHNpemU9MyApKwojICB0aGVtZV9idygpCgoKCgojcGxvdGx5OjpwbG90X2x5KHBvc3RvcF9kYXRhX3BjYV9yZXMgLCB0eXBlPSJzY2F0dGVyM2QiLCAKIyAgICAgICAgICAgICAgICB4ID0gflBDMiwgeSA9IH5QQzEsIHogPSB+UEMzLCBjb2xvciA9IH5wb3NpdGlvbiwKIyAgICAgICAgICAgICAgICBjb2xvcnMgPSBjKCdibHVlJywgJ29yYW5nZScsInJlZCIsImdyZWVuIiksIAojICAgICAgICAgICAgICAgIG9wYWNpdHk9MC41LCBtYXJrZXIgPSBsaXN0KHNpemUgPSAzKSx0ZXh0ID0gfmFnZX5nZW5kZXIpIApgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTh9CmxpYnJhcnkoRmFjdG9NaW5lUikKI3Bvc3RvcF9kYXRhX3BjYTwtcG9zdG9wX2RhdGEgJT4lIHNlbGVjdChzdGFydHNfd2l0aCgiUE9fIikpICU+JSBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lIGFkZF9jb2x1bW4ocG9zdG9wX2RhdGEkU0VYKQpyZXNfcGNhID0gUENBKHBvc3RvcF9kYXRhX3BjYSwgc2NhbGUudW5pdD1UUlVFLCBuY3A9NiwgCiAgICAgICAgICAgICAgZ3JhcGg9VCwKICAgICAgICAgICAgICBxdWFsaS5zdXA9MTIKICAgICAgICAgICAgICApCmBgYAoKCiMjIFBDQSBGZWF0dXJlIFNlbGVjdGlvbgpgYGB7cn0KcmVzX3BjYSR2YXIkY29udHJpYiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBhZGRfcm93bmFtZXMoKSU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBncm91cF9ieSh2YXJpYWJsZSkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KHZhbHVlKSkKIyAlPiUgc3VtbWFyaXNlKG1heD1tYXgodmFsdWUpKQpgYGAKCgojIyBDb3JyZWxhdGlvbiBtYXRyaXgKClVzaW5nIFNwZWFybWFuIGNvcnJlbGF0aW9uCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CmxpYnJhcnkoZDNoZWF0bWFwKQpwb3N0b3BfZGF0YV9jb3JfbWF0cml4PC1jb3IocG9zdG9wX2RhdGFfcGNhLG1ldGhvZD0ic3BlYXJtYW4iKQojaGVhdG1hcChwb3N0b3BfZGF0YV9jb3JfbWF0cml4KQpkM2hlYXRtYXAocG9zdG9wX2RhdGFfY29yX21hdHJpeCxjb2xvcnMgPSAiQmx1ZXMiLGNleFJvdyA9IDAuOCwgY2V4Q29sID0gMC44KQpgYGAKCgojIyBQYWlycyBtYXRyaXgKYGBge3IgZmlnLndpZHRoPTEyfQpwYWlycyhwb3N0b3BfZGF0YV9wY2EpCmBgYApgYGB7cn0KcmVhZHI6OndyaXRlX2Nzdihwb3N0b3BfZGF0YSxwYXRoPSJwb3N0b3BfZGF0YV9jbGVhbmVkLmNzdiIpCmBgYAoK