library(lubridate)
library(tidyverse)
library(pROC)
library(plotROC)
library(caret)
library(doMC)
registerDoMC(cores=8)

Dataset generation

The same datasets used in Paula’s thesis

winter and summer data was merged in one file

data %>% group_by(eng_season) %>% summarise(total=n())

FNL lags

Creating FNL lags for 6, 12, 18 and 24 hs before the target_day

lags<- data %>% select(-eng_season, -target_timestamp, -target_year, -target_day, -target_hour,-eng_cyclic_day, -eng_nino_year, -eng_nino_month, -eng_zonda,-target_month, -precip_3hr_sum) %>% imap_dfc(~set_names(map(1:4, lag, x = .x), 
                        paste(.y, 'lag', c("6","12","18","24"), sep = '_')))
trainset<-lags %>% bind_cols(data %>% select(eng_season, target_timestamp, target_year, target_day, eng_cyclic_day, eng_nino_year, eng_nino_month, eng_zonda,target_month), .) %>% slice(7:nrow(data)) 
# considering only timestamp at 00 hour
trainset<-trainset[seq(1,nrow(trainset),4),]
# Removing the hours from timestamp
trainset<-trainset %>% mutate(target_timestamp=as.integer(target_timestamp/100))
head(trainset)

The cmorph daily file contains the 4 closest geo-points to Mendoza Aerodrome station. Taken from From ./2.selected/cmorph_mza_daily/cmorph_paste_mza_dly

cmorph_daily<-read_delim("data/cmorph_mza_dly",delim =" ")
Parsed with column specification:
cols(
  `1998010100` = col_integer(),
  `0.496000` = col_double(),
  `0.340000` = col_double(),
  `0.168000` = col_double(),
  `0.157000` = col_double()
)
names(cmorph_daily)<-c("target_timestamp","cmorph1","cmorph2","cmorph3","cmorph4")
head(cmorph_daily)

Merging with cmorph_daily

Merging Cmoprh daily with previous trainset

trainset_merged <- inner_join(trainset,cmorph_daily %>% mutate(target_timestamp=as.integer(target_timestamp/100)),by="target_timestamp")

Comparison with wmo

Georefence of the 4 CMORPH selected point and the MZA Aerodrome meteo station.

The two closest point to MWO are cmorph1 and cmorph3

library(ggmap)
library(sp)
mza_map <- get_map(location = "mendoza", zoom = 10,maptype = "roadmap",color = "bw")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=mendoza&zoom=10&size=640x640&scale=2&maptype=roadmap&language=en-EN&sensor=false
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=mendoza&sensor=false
mza_cmorph_geo_data=read.csv("/home/harpo/Dropbox/ongoing-work/meteo/scripts/R/cmorph_mza_data_geo.csv",header=T,sep=",")
selected_mza_cmorph_geo_data<-mza_cmorph_geo_data %>% 
                filter(cuadrante=="5_5" | cuadrante=="5_6" | cuadrante=="6_6" | cuadrante=="6_5" )
selected_mza_cmorph_geo_data<-cbind(nombre=c("cmorph1","cmorph2","cmorph3","cmorph4"),selected_mza_cmorph_geo_data)
gg<-ggmap(mza_map)+ 
   geom_point(aes(x=lon - 360 ,y=lat),data=selected_mza_cmorph_geo_data,color='red')+
   geom_text(aes(x=lon - 360.03 ,y=lat+0.02,label=cuadrante),size=3,data=selected_mza_cmorph_geo_data,color='red')+
   geom_text(aes(x=lon - 360.03 ,y=lat+0.04,label=nombre),size=3,data=selected_mza_cmorph_geo_data,color='red')+
   geom_point(y=-32.8331,x=-68.7715,size=3,color='blue')
  
gg

A comparison between MWO precipitation days and cmorph3

Creating precip class with threshold=0.0

The point corresponding to CMORPH3 5_6 was chosen as the target precipitation volume

trainset_labeled <- trainset_merged %>% 
  mutate(precip_reg=cmorph3,precip=ifelse(precip_reg>threshold,'yes','no'),eng_season_num=ifelse(eng_season=="summer",1,0)) %>%
  select(-cmorph1, -cmorph2, -cmorph3, -cmorph4, -eng_season)

Day of rainfall distribution

trainset_labeled %>% group_by(eng_season_num,precip) %>% summarise(total=n()) %>% ggplot()+
  geom_col(aes(x=as.factor(eng_season_num),y=total,fill=precip))+
  theme_bw()

Removing selected lags and some features

trainset_labeled<-trainset_labeled %>% 
  select( -contains("lag_24") ) %>% 
  select( -contains("lag_18") ) %>%
  #select( -contains("lag_12") ) %>%
  select(-target_year, -target_timestamp ,-eng_zonda) 
#%>% 
  #filter(eng_season_num==1) %>% 
  #select(-eng_season_num)
#names(trainset_labeled)

Removing Highly correlated features

length(names(trainset_labeled_selected))
[1] 114

Listing the final set of features

as.data.frame(names(trainset_labeled_selected))

Creating Train and Test sets

set.seed(12121)
trainset_labeled_selected$precip<-as.factor(trainset_labeled_selected$precip)
train_index <- createDataPartition(trainset_labeled_selected$precip , p=0.80, list=FALSE)
data_train <- trainset_labeled_selected[ train_index,]
data_test <-  trainset_labeled_selected[-train_index,]
data_train <- data_train %>% select(-precip_reg)
#data_reg <- data_reg %>% slice(1:nrow(data_reg)) # check here for lag
# add precipitation numerical value to test
#data_test$precip_reg <- trainset_merged[-train_index,]$cmorph1
train_formula<-formula(precip ~ .) 
nrow(data_train)
[1] 2171

Model evaluation

Model fine tuning on Training set

registerDoMC(cores=8)
ctrl_fast <- trainControl(method="repeatedcv", 
                     repeats=1,
                     number=5, 
                     summaryFunction=twoClassSummary,
                     verboseIter=F,
                     #preProcOptions = list(pcaComp = 10),
                     classProbs=TRUE,
                     allowParallel = T)  
nnGrid <-  expand.grid(decay = c(0), size=c(10,20,50,100,200,300)) 
svmGrid <-  expand.grid(sigma = c(0.001,0.00001), C=c(0.1,0.001,0.5)) 
kerasGrid <- expand.grid(
  size=c(20,50,2000,5000),
  lambda=c(0),
  batch_size=c(1024),
  decay=c(0),
  activation=c('tanh','sigmoid','relu'),
  lr=c(0.001), rho=c(0.9)
)
ctrl_fast$sampling<-"smote"
rfFit <- caret::train(train_formula,
               data = data_train,
               metric="ROC",
               #preProcess=c("pca"),
               
               #method = "mlpWeightDecay",
               #method = "svmRadial",
               #method ="mlpKerasDecay",
               method = "rf",
               #tuneGrid=kerasGrid,
               tuneLength=11,
               verbose=0,
               #epochs=100,
               trControl = ctrl_fast)

Resulting models

rfFit
Random Forest 

2171 samples
 112 predictors
   2 classes: 'no', 'yes' 

No pre-processing
Resampling: Cross-Validated (5 fold, repeated 1 times) 
Summary of sample sizes: 1736, 1737, 1737, 1737, 1737 
Addtional sampling using SMOTE

Resampling results across tuning parameters:

  mtry  ROC        Sens       Spec     
    2   0.7545842  0.8185177  0.4931619
   13   0.7554712  0.8382199  0.4592636
   24   0.7542969  0.8318298  0.4693746
   35   0.7485711  0.8381943  0.4111631
   46   0.7396687  0.8366071  0.4177674
   57   0.7474437  0.8440525  0.4626534
   68   0.7443988  0.8259773  0.4386908
   79   0.7361201  0.8323631  0.4143776
   90   0.7456732  0.8270355  0.4522501
  101   0.7455349  0.8392865  0.4044418
  112   0.7402596  0.8243660  0.4586791

ROC was used to select the optimal model using the largest value.
The final value used for the model was mtry = 13.
ggplot(rfFit)+theme_bw()

Most relevant features for RF

THe list of the most relevant features used by the RandomForest Classifier

varImp(rfFit, scale = F)
rf variable importance

  only 20 most important variables shown (out of 112)

                                    Overall
eng_season_num                        28.70
fnl_cin_3__1_t0_lag_12                27.89
fnl_cape_3__1_t0_lag_12               22.89
fnl_cape_3__3_t0_lag_12               21.44
target_month                          18.02
fnl_cin_3__1_t0_lag_6                 17.93
fnl_sealevelpress_3__3_t0_lag_6       16.26
fnl_cin_3__2_t0_lag_12                15.86
fnl_cape_3__1_t0_lag_6                14.88
fnl_sealevelpress_1__3_t0_lag_6       14.35
fnl_cin_3__3_t0_lag_12                14.27
fnl_cape_3__2_t0_lag_12               13.39
fnl_vwind_850_1__3_t0_lag_12          12.44
fnl_vwind_850_1__2_t0_lag_12          12.21
fnl_absvorticity_500_1__3_t0_lag_12   12.06
fnl_cape_3__2_t0_lag_6                11.97
fnl_vwind_850_1__1_t0_lag_12          11.77
fnl_vwind_850_3__3_t0_lag_12          11.70
fnl_vwind_850_2__3_t0_lag_12          11.43
fnl_uwind_850_1__1_t0_lag_6           11.26

Evaluation on test set

Confusion Matrix

data_test_results<-cbind(data_test,predicted_precip_probs=predict(rfFit,data_test,type='prob'))
data_test_results<-cbind(data_test_results,predicted_precip=ifelse(data_test_results$predicted_precip_probs.yes >=0.4,'yes','no'))
cm<-caret::confusionMatrix(as.factor(data_test_results$predicted_precip),data_test_results$precip,positive="yes")
cm
Confusion Matrix and Statistics

          Reference
Prediction  no yes
       no  339  20
       yes 130  52
                                          
               Accuracy : 0.7227          
                 95% CI : (0.6829, 0.7601)
    No Information Rate : 0.8669          
    P-Value [Acc > NIR] : 1               
                                          
                  Kappa : 0.2703          
 Mcnemar's Test P-Value : <2e-16          
                                          
            Sensitivity : 0.72222         
            Specificity : 0.72281         
         Pos Pred Value : 0.28571         
         Neg Pred Value : 0.94429         
             Prevalence : 0.13309         
         Detection Rate : 0.09612         
   Detection Prevalence : 0.33641         
      Balanced Accuracy : 0.72252         
                                          
       'Positive' Class : yes             
                                          

ROC Curve

#plot(roc(data_test$profile,predsrprofilerobsamp$Malicious))
ggplot(cbind(predict(rfFit,data_test,type='prob'),class=data_test$precip), 
       aes(m = yes, d = factor(class, labels=c("no","yes"),levels = c("no", "yes")))) + 
    geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') + 
    geom_abline(intercept = 0, slope = 1,colour='red')+
  theme_bw()

Analysis of the results

Distribution of TP and FN (what kind of rainfall are we detecting)

FP_days<- as.data.frame(data_test_results) %>% filter(precip == 'no' & predicted_precip == 'yes')
TP_days<- as.data.frame(data_test_results) %>% filter(precip == 'yes' & predicted_precip == 'yes')
FN_days<- as.data.frame(data_test_results) %>% filter(precip == 'yes' & predicted_precip == 'no')
TN_days<- as.data.frame(data_test_results) %>% filter(precip == 'no' & predicted_precip == 'no')
rbind(data.frame(variable=rep("FN",length(FN_days$precip_reg)),value=FN_days$precip_reg),
data.frame(variable=rep("TP",length(TP_days$precip_reg)),value=TP_days$precip_reg)
#data.frame(variable=rep("TN",length(TN_days$precip_reg)),value=TN_days$precip_reg),
#data.frame(variable=rep("FP",length(FP_days$precip_reg)),value=FP_days$precip_reg)
) %>%
  ggplot()+
  geom_boxplot(aes(x=variable,y=value,fill=variable))+
  ylab("precipitation volume")+xlab("results")+
  theme_bw()

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkocGxvdFJPQykKbGlicmFyeShjYXJldCkKbGlicmFyeShkb01DKQpyZWdpc3RlckRvTUMoY29yZXM9OCkKYGBgCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpzdGF0aW9uX3ZzX2Ntb3JwaF9temE9cmVhZC5jc3YoCiAgZmlsZT0iZGF0YS9zdGF0aW9uLXZzLWNtb3JwaC1temEtZGFpbHkuY3N2IixzZXA9JywnLGhlYWRlcj1GKQpzdGF0aW9uX3ZzX2Ntb3JwaF9temE9Y2JpbmQocmVwKCJtemEiKSxzdGF0aW9uX3ZzX2Ntb3JwaF9temEpCm5hbWVzKHN0YXRpb25fdnNfY21vcnBoX216YSk8LWMoImNpdHkiLCJ0cyIsInN0YXRpb24iLCJjbW9ycGgiKQoKbW9udGg9bW9udGgoYXMuRGF0ZShhcy5jaGFyYWN0ZXIoc3RhdGlvbl92c19jbW9ycGhfbXphJHRzKSwiJVklbSVkIikpCnllYXI9eWVhcihhcy5EYXRlKGFzLmNoYXJhY3RlcihzdGF0aW9uX3ZzX2Ntb3JwaF9temEkdHMpLCIlWSVtJWQiKSkKZGF5PWRheShhcy5EYXRlKGFzLmNoYXJhY3RlcihzdGF0aW9uX3ZzX2Ntb3JwaF9temEkdHMpLCIlWSVtJWQiKSkKc3RhdGlvbl92c19jbW9ycGg9Y2JpbmQoc3RhdGlvbl92c19jbW9ycGhfbXphLHllYXIsbW9udGgsZGF5KQoKCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQp3bW9fZGF0YTwtc3RhdGlvbl92c19jbW9ycGggJT4lIGdyb3VwX2J5KHllYXIpICU+JSBzdW1tYXJpc2UodG90YWw9bigpLHN0YXRpb249c3VtKGlmZWxzZShzdGF0aW9uPjAsMSwwKSksY21vcnBoPXN1bShpZmVsc2UoY21vcnBoPjAsMSwwKSkpCndtb19kYXRhX21lbHRlZDwtbWVsdCh3bW9fZGF0YSxpZD1jKCJ5ZWFyIikpCndtb19kYXRhX3Bsb3Q8LWdncGxvdCh3bW9fZGF0YSkrCiAjIGdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcih5ZWFyKSx5PXZhbHVlLGZpbGw9dmFyaWFibGUpLHBvc2l0aW9uPSdkb2RnZScpKwogIGdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcih5ZWFyKSx5PXRvdGFsLGZpbGw9J3JlZCcpKSsKICBnZW9tX2NvbChhZXMoeD1hcy5mYWN0b3IoeWVhcikseT1jbW9ycGgsZmlsbD0nZ3JlZW4nKSkrCiAgZ2VvbV9jb2woYWVzKHg9YXMuZmFjdG9yKHllYXIpLHk9c3RhdGlvbixmaWxsPSdibHVlJykpKwogIHNjYWxlX2ZpbGxfbWFudWFsKCJEYXlzIHdpdGggbWVhc3VyZXMiLHZhbHVlcyA9YygncmVkJz0ncGluaycsJ2JsdWUnPSdza3libHVlJywnZ3JlZW4nPSdsaWdodGdyZWVuJyksIGxhYmVscyA9IGMoJ3N0YXRpb24nLCdjbW9ycGgnLCd0b3RhbCcpKSsKICB5bGFiKCJkYXlzIikreGxhYigieWVhciIpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkrCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKd21vX2RhdGFfcGxvdAp3bW9fZGF0YQpnZ3NhdmUod21vX2RhdGFfcGxvdCxmaWxlbmFtZSA9ICJwcmVjaXBpdGF0aW9uLWRheXMucGRmIiwgd2lkdGggPSA4LGhlaWdodCA9IDQsZGV2aWNlID0gInBkZiIpCmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0Kd21vX2RhdGFfZml4ZWQ8LXN0YXRpb25fdnNfY21vcnBoICU+JSBncm91cF9ieSh5ZWFyKSAlPiUgc3VtbWFyaXNlKHRvdGFsPW4oKSxzdGF0aW9uPXN1bShpZmVsc2Uoc3RhdGlvbj4wLDEsMCkpLGNtb3JwaD1zdW0oaWZlbHNlKGNtb3JwaD4wLjEsMSwwKSkpCndtb19kYXRhX2ZpeGVkX21lbHRlZDwtbWVsdCh3bW9fZGF0YV9maXhlZCxpZD1jKCJ5ZWFyIikpCndtb19kYXRhX2ZpeGVkX3Bsb3Q8LWdncGxvdCh3bW9fZGF0YV9maXhlZCkrCiAjIGdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcih5ZWFyKSx5PXZhbHVlLGZpbGw9dmFyaWFibGUpLHBvc2l0aW9uPSdkb2RnZScpKwogICNnZW9tX2NvbChhZXMoeD1hcy5mYWN0b3IoeWVhcikseT10b3RhbCxmaWxsPSdyZWQnKSkrCiAgCmdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcih5ZWFyKSx5PWNtb3JwaCxmaWxsPSdncmVlbicpLGFscGhhPTAuNykrCiAgZ2VvbV9jb2woYWVzKHg9YXMuZmFjdG9yKHllYXIpLHk9c3RhdGlvbixmaWxsPSdibHVlJyksYWxwaGE9MSkrCiAgICBzY2FsZV9maWxsX21hbnVhbCgiIix2YWx1ZXMgPWMoJ3JlZCc9J3BpbmsnLCdibHVlJz0nc2t5Ymx1ZScsJ2dyZWVuJz0nbGlnaHRncmVlbicpLCBsYWJlbHMgPSBjKCdzdGF0aW9uJywnY21vcnBoJywndG90YWwnKSkrCiAgeWxhYigiZGF5cyIpK3hsYWIoInllYXIiKSsKICB0aGVtZV9idygpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpKwogICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCndtb19kYXRhX2ZpeGVkX3Bsb3QKZ2dzYXZlKHdtb19kYXRhX2ZpeGVkX3Bsb3QsZmlsZW5hbWUgPSAicHJlY2lwaXRhdGlvbi1jbW9ycGgtc3RhdGlvbi1maXhlZC0wMS5wZGYiLCB3aWR0aCA9IDgsaGVpZ2h0ID0gNCxkZXZpY2UgPSAicGRmIikKYGBgCgojIERhdGFzZXQgZ2VuZXJhdGlvbgojIyBUaGUgc2FtZSBkYXRhc2V0cyB1c2VkIGluIFBhdWxhJ3MgdGhlc2lzCmBgYHtyfQpyZWdpc3RlckRvTUMoY29yZXM9OCkKbXphX3dpbnRlcjwtcmVhZF9jc3YoIi9ob21lL2hhcnBvL0Ryb3Bib3gvb25nb2luZy13b3JrL2dpdC1yZXBvcy9tZXRlby1tbC9kYXRhL2pvdXJuYWwtcGF1bGEvNS5jbHMtbXphLXdpbnRlci1zMS1wNi5jc3YiKQptemFfd2ludGVyX3JlZzwtcmVhZF9jc3YoIi9ob21lL2hhcnBvL0Ryb3Bib3gvb25nb2luZy13b3JrL2dpdC1yZXBvcy9tZXRlby1tbC9kYXRhL2pvdXJuYWwtcGF1bGEvNS5yZWctbXphLXdpbnRlci1zMS1wNi5jc3YiKQptemFfc3VtbWVyPC1yZWFkX2NzdigiL2hvbWUvaGFycG8vRHJvcGJveC9vbmdvaW5nLXdvcmsvZ2l0LXJlcG9zL21ldGVvLW1sL2RhdGEvam91cm5hbC1wYXVsYS81LmNscy1temEtc3VtbWVyLXMxLXA2LmNzdiIpCm16YV9zdW1tZXJfcmVnPC1yZWFkX2NzdigiL2hvbWUvaGFycG8vRHJvcGJveC9vbmdvaW5nLXdvcmsvZ2l0LXJlcG9zL21ldGVvLW1sL2RhdGEvam91cm5hbC1wYXVsYS81LnJlZy1temEtc3VtbWVyLXMxLXA2LmNzdiIpCmRhdGE8LXJiaW5kKG16YV9zdW1tZXJfcmVnLG16YV93aW50ZXJfcmVnKQpucm93KGRhdGEpCmhlYWQoZGF0YSkKYGBgCgoqKndpbnRlcioqIGFuZCAqKnN1bW1lcioqIGRhdGEgd2FzIG1lcmdlZCBpbiBvbmUgZmlsZQoKYGBge3J9CmRhdGEgJT4lIGdyb3VwX2J5KGVuZ19zZWFzb24pICU+JSBzdW1tYXJpc2UodG90YWw9bigpKQpgYGAKCgojIyBGTkwgbGFncyAKQ3JlYXRpbmcgRk5MIGxhZ3MgZm9yIDYsIDEyLCAxOCBhbmQgMjQgaHMgYmVmb3JlIHRoZSAqKnRhcmdldF9kYXkqKgpgYGB7ciBkYXRhc2V0IGdlbmVyYXRpb259CgpsYWdzPC0gZGF0YSAlPiUgc2VsZWN0KC1lbmdfc2Vhc29uLCAtdGFyZ2V0X3RpbWVzdGFtcCwgLXRhcmdldF95ZWFyLCAtdGFyZ2V0X2RheSwgLXRhcmdldF9ob3VyLC1lbmdfY3ljbGljX2RheSwgLWVuZ19uaW5vX3llYXIsIC1lbmdfbmlub19tb250aCwgLWVuZ196b25kYSwtdGFyZ2V0X21vbnRoLCAtcHJlY2lwXzNocl9zdW0pICU+JSBpbWFwX2RmYyh+c2V0X25hbWVzKG1hcCgxOjQsIGxhZywgeCA9IC54KSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKC55LCAnbGFnJywgYygiNiIsIjEyIiwiMTgiLCIyNCIpLCBzZXAgPSAnXycpKSkKCnRyYWluc2V0PC1sYWdzICU+JSBiaW5kX2NvbHMoZGF0YSAlPiUgc2VsZWN0KGVuZ19zZWFzb24sIHRhcmdldF90aW1lc3RhbXAsIHRhcmdldF95ZWFyLCB0YXJnZXRfZGF5LCBlbmdfY3ljbGljX2RheSwgZW5nX25pbm9feWVhciwgZW5nX25pbm9fbW9udGgsIGVuZ196b25kYSx0YXJnZXRfbW9udGgpLCAuKSAlPiUgc2xpY2UoNzpucm93KGRhdGEpKSAKCiMgY29uc2lkZXJpbmcgb25seSB0aW1lc3RhbXAgYXQgMDAgaG91cgp0cmFpbnNldDwtdHJhaW5zZXRbc2VxKDEsbnJvdyh0cmFpbnNldCksNCksXQoKIyBSZW1vdmluZyB0aGUgaG91cnMgZnJvbSB0aW1lc3RhbXAKdHJhaW5zZXQ8LXRyYWluc2V0ICU+JSBtdXRhdGUodGFyZ2V0X3RpbWVzdGFtcD1hcy5pbnRlZ2VyKHRhcmdldF90aW1lc3RhbXAvMTAwKSkKaGVhZCh0cmFpbnNldCkKYGBgCgpUaGUgY21vcnBoIGRhaWx5IGZpbGUgY29udGFpbnMgdGhlIDQgY2xvc2VzdCBnZW8tcG9pbnRzIHRvIE1lbmRvemEgQWVyb2Ryb21lIHN0YXRpb24uClRha2VuIGZyb20gRnJvbSBgYGAuLzIuc2VsZWN0ZWQvY21vcnBoX216YV9kYWlseS9jbW9ycGhfcGFzdGVfbXphX2RseWBgYAoKYGBge3J9CgpjbW9ycGhfZGFpbHk8LXJlYWRfZGVsaW0oImRhdGEvY21vcnBoX216YV9kbHkiLGRlbGltID0iICIpCm5hbWVzKGNtb3JwaF9kYWlseSk8LWMoInRhcmdldF90aW1lc3RhbXAiLCJjbW9ycGgxIiwiY21vcnBoMiIsImNtb3JwaDMiLCJjbW9ycGg0IikKaGVhZChjbW9ycGhfZGFpbHkpCmBgYAoKIyMgIE1lcmdpbmcgd2l0aCBjbW9ycGhfZGFpbHkKTWVyZ2luZyBDbW9wcmggZGFpbHkgd2l0aCBwcmV2aW91cyB0cmFpbnNldCAKCmBgYHtyfQoKdHJhaW5zZXRfbWVyZ2VkIDwtIGlubmVyX2pvaW4odHJhaW5zZXQsY21vcnBoX2RhaWx5ICU+JSBtdXRhdGUodGFyZ2V0X3RpbWVzdGFtcD1hcy5pbnRlZ2VyKHRhcmdldF90aW1lc3RhbXAvMTAwKSksYnk9InRhcmdldF90aW1lc3RhbXAiKQoKYGBgCgojIyBDb21wYXJpc29uIHdpdGggd21vCgojIyMgR2VvcmVmZW5jZSBvZiB0aGUgNCBDTU9SUEggc2VsZWN0ZWQgcG9pbnQgYW5kIHRoZSBNWkEgQWVyb2Ryb21lIG1ldGVvIHN0YXRpb24uIAoKVGhlIHR3byBjbG9zZXN0IHBvaW50IHRvIE1XTyBhcmUgKmNtb3JwaDEqIGFuZCAqY21vcnBoMyoKYGBge3J9CmxpYnJhcnkoZ2dtYXApCmxpYnJhcnkoc3ApCm16YV9tYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9ICJtZW5kb3phIiwgem9vbSA9IDEwLG1hcHR5cGUgPSAicm9hZG1hcCIsY29sb3IgPSAiYnciKQptemFfY21vcnBoX2dlb19kYXRhPXJlYWQuY3N2KCIvaG9tZS9oYXJwby9Ecm9wYm94L29uZ29pbmctd29yay9tZXRlby9zY3JpcHRzL1IvY21vcnBoX216YV9kYXRhX2dlby5jc3YiLGhlYWRlcj1ULHNlcD0iLCIpCnNlbGVjdGVkX216YV9jbW9ycGhfZ2VvX2RhdGE8LW16YV9jbW9ycGhfZ2VvX2RhdGEgJT4lIAogICAgICAgICAgICAgICAgZmlsdGVyKGN1YWRyYW50ZT09IjVfNSIgfCBjdWFkcmFudGU9PSI1XzYiIHwgY3VhZHJhbnRlPT0iNl82IiB8IGN1YWRyYW50ZT09IjZfNSIgKQoKc2VsZWN0ZWRfbXphX2Ntb3JwaF9nZW9fZGF0YTwtY2JpbmQobm9tYnJlPWMoImNtb3JwaDEiLCJjbW9ycGgyIiwiY21vcnBoMyIsImNtb3JwaDQiKSxzZWxlY3RlZF9temFfY21vcnBoX2dlb19kYXRhKQoKZ2c8LWdnbWFwKG16YV9tYXApKyAKICAgZ2VvbV9wb2ludChhZXMoeD1sb24gLSAzNjAgLHk9bGF0KSxkYXRhPXNlbGVjdGVkX216YV9jbW9ycGhfZ2VvX2RhdGEsY29sb3I9J3JlZCcpKwogICBnZW9tX3RleHQoYWVzKHg9bG9uIC0gMzYwLjAzICx5PWxhdCswLjAyLGxhYmVsPWN1YWRyYW50ZSksc2l6ZT0zLGRhdGE9c2VsZWN0ZWRfbXphX2Ntb3JwaF9nZW9fZGF0YSxjb2xvcj0ncmVkJykrCiAgIGdlb21fdGV4dChhZXMoeD1sb24gLSAzNjAuMDMgLHk9bGF0KzAuMDQsbGFiZWw9bm9tYnJlKSxzaXplPTMsZGF0YT1zZWxlY3RlZF9temFfY21vcnBoX2dlb19kYXRhLGNvbG9yPSdyZWQnKSsKICAgZ2VvbV9wb2ludCh5PS0zMi44MzMxLHg9LTY4Ljc3MTUsc2l6ZT0zLGNvbG9yPSdibHVlJykKICAKCmdnCgpgYGAKCiMjIyBBIGNvbXBhcmlzb24gYmV0d2VlbiBNV08gcHJlY2lwaXRhdGlvbiBkYXlzIGFuZCBjbW9ycGgzCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQp0aHJlc2hvbGQ9MC4xCmEgPC10cmFpbnNldF9tZXJnZWQgJT4lIGlubmVyX2pvaW4oc3RhdGlvbl92c19jbW9ycGhfbXphLCBieT1jKCJ0YXJnZXRfdGltZXN0YW1wIiA9ICJ0cyIpKSAgJT4lIAogIG11dGF0ZShjbW9ycGg9aWZlbHNlKGNtb3JwaDM+dGhyZXNob2xkLCd5ZXMnLCdubycpLHN0YXRpb249aWZlbHNlKHN0YXRpb24+MSwneWVzJywnbm8nKSkKCgoKYSAlPiUgZ3JvdXBfYnkodGFyZ2V0X3llYXIpICU+JSBzdW1tYXJpc2UoY21vcnBoPXN1bShjbW9ycGg9PSd5ZXMnKSxzdGF0aW9uPXN1bShzdGF0aW9uPT0neWVzJyksdG90YWw9bigpKSAlPiUKICBnZ3Bsb3QoYWVzKGFscGhhPTAuNSkpICsKICBnZW9tX2NvbChhZXMoeD1hcy5mYWN0b3IodGFyZ2V0X3llYXIpLHk9dG90YWwpLGZpbGw9J3JlZCcpKwogIGdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcih0YXJnZXRfeWVhcikseT1jbW9ycGgpLGZpbGw9J2JsdWUnKSsKICBnZW9tX2NvbChhZXMoeD1hcy5mYWN0b3IodGFyZ2V0X3llYXIpLHk9c3RhdGlvbiksZmlsbD0nZ3JlZW4nKSsKICB0aGVtZV9idygpCgojY29uZnVzaW9uTWF0cml4KGFzLmZhY3RvcihhJGNtb3JwaDEpLGFzLmZhY3RvcihhJHN0YXRpb24pKQpgYGAKCiMjIENyZWF0aW5nIHByZWNpcCBjbGFzcyB3aXRoIGBgYHRocmVzaG9sZD0wLjBgYGAKClRoZSAgcG9pbnQgY29ycmVzcG9uZGluZyB0byBDTU9SUEgzIDVfNiB3YXMgY2hvc2VuIGFzIHRoZSB0YXJnZXQgcHJlY2lwaXRhdGlvbiB2b2x1bWUKCmBgYHtyfQoKdHJhaW5zZXRfbGFiZWxlZCA8LSB0cmFpbnNldF9tZXJnZWQgJT4lIAogIG11dGF0ZShwcmVjaXBfcmVnPWNtb3JwaDMscHJlY2lwPWlmZWxzZShwcmVjaXBfcmVnPnRocmVzaG9sZCwneWVzJywnbm8nKSxlbmdfc2Vhc29uX251bT1pZmVsc2UoZW5nX3NlYXNvbj09InN1bW1lciIsMSwwKSkgJT4lCiAgc2VsZWN0KC1jbW9ycGgxLCAtY21vcnBoMiwgLWNtb3JwaDMsIC1jbW9ycGg0LCAtZW5nX3NlYXNvbikKYGBgCgojIyMgRGF5IG9mIHJhaW5mYWxsIGRpc3RyaWJ1dGlvbgpgYGB7cn0KdHJhaW5zZXRfbGFiZWxlZCAlPiUgZ3JvdXBfYnkoZW5nX3NlYXNvbl9udW0scHJlY2lwKSAlPiUgc3VtbWFyaXNlKHRvdGFsPW4oKSkgJT4lIGdncGxvdCgpKwogIGdlb21fY29sKGFlcyh4PWFzLmZhY3Rvcihlbmdfc2Vhc29uX251bSkseT10b3RhbCxmaWxsPXByZWNpcCkpKwogIHRoZW1lX2J3KCkKYGBgCgojIyBSZW1vdmluZyBzZWxlY3RlZCBsYWdzIGFuZCBzb21lIGZlYXR1cmVzCmBgYHtyfQp0cmFpbnNldF9sYWJlbGVkPC10cmFpbnNldF9sYWJlbGVkICU+JSAKICBzZWxlY3QoIC1jb250YWlucygibGFnXzI0IikgKSAlPiUgCiAgc2VsZWN0KCAtY29udGFpbnMoImxhZ18xOCIpICkgJT4lCiAgI3NlbGVjdCggLWNvbnRhaW5zKCJsYWdfMTIiKSApICU+JQogIHNlbGVjdCgtdGFyZ2V0X3llYXIsIC10YXJnZXRfdGltZXN0YW1wICwtZW5nX3pvbmRhKSAKIyU+JSAKICAjZmlsdGVyKGVuZ19zZWFzb25fbnVtPT0xKSAlPiUgCiAgI3NlbGVjdCgtZW5nX3NlYXNvbl9udW0pCiNuYW1lcyh0cmFpbnNldF9sYWJlbGVkKQpgYGAKCiMjIFJlbW92aW5nIEhpZ2hseSBjb3JyZWxhdGVkIGZlYXR1cmVzCmBgYHtyIHJlbW92ZSBjb3JyZWxhdGVkfQp0cmFpbnNldF9jb3I8LWNvcih0cmFpbnNldF9sYWJlbGVkICU+JSBzZWxlY3QoLXByZWNpcCwtcHJlY2lwX3JlZykpCnRyYWluc2V0aWQ8LWZpbmRDb3JyZWxhdGlvbih0cmFpbnNldF9jb3IsIGN1dG9mZiA9IC45KQp0cmFpbnNldF9sYWJlbGVkX3NlbGVjdGVkPC10cmFpbnNldF9sYWJlbGVkWywtdHJhaW5zZXRpZF0jLHByZWNpcD10cmFpbnNldF9sYWJlbGVkJHByZWNpcCkKbGVuZ3RoKG5hbWVzKHRyYWluc2V0X2xhYmVsZWRfc2VsZWN0ZWQpKQoKYGBgCgojIyBMaXN0aW5nIHRoZSBmaW5hbCBzZXQgb2YgZmVhdHVyZXMKYGBge3J9CmFzLmRhdGEuZnJhbWUobmFtZXModHJhaW5zZXRfbGFiZWxlZF9zZWxlY3RlZCkpCmBgYAojIENyZWF0aW5nIFRyYWluIGFuZCBUZXN0IHNldHMKCmBgYHtyIHRyYWluaW5nIGFuZCB0ZXN0aW5nfQpzZXQuc2VlZCgxMjEyMSkKdHJhaW5zZXRfbGFiZWxlZF9zZWxlY3RlZCRwcmVjaXA8LWFzLmZhY3Rvcih0cmFpbnNldF9sYWJlbGVkX3NlbGVjdGVkJHByZWNpcCkKdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih0cmFpbnNldF9sYWJlbGVkX3NlbGVjdGVkJHByZWNpcCAsIHA9MC44MCwgbGlzdD1GQUxTRSkKZGF0YV90cmFpbiA8LSB0cmFpbnNldF9sYWJlbGVkX3NlbGVjdGVkWyB0cmFpbl9pbmRleCxdCmRhdGFfdGVzdCA8LSAgdHJhaW5zZXRfbGFiZWxlZF9zZWxlY3RlZFstdHJhaW5faW5kZXgsXQpkYXRhX3RyYWluIDwtIGRhdGFfdHJhaW4gJT4lIHNlbGVjdCgtcHJlY2lwX3JlZykKCiNkYXRhX3JlZyA8LSBkYXRhX3JlZyAlPiUgc2xpY2UoMTpucm93KGRhdGFfcmVnKSkgIyBjaGVjayBoZXJlIGZvciBsYWcKIyBhZGQgcHJlY2lwaXRhdGlvbiBudW1lcmljYWwgdmFsdWUgdG8gdGVzdAojZGF0YV90ZXN0JHByZWNpcF9yZWcgPC0gdHJhaW5zZXRfbWVyZ2VkWy10cmFpbl9pbmRleCxdJGNtb3JwaDEKdHJhaW5fZm9ybXVsYTwtZm9ybXVsYShwcmVjaXAgfiAuKSAKbnJvdyhkYXRhX3RyYWluKQpgYGAKCiMgTW9kZWwgZXZhbHVhdGlvbgojIyBNb2RlbCBmaW5lIHR1bmluZyBvbiBUcmFpbmluZyBzZXQKYGBge3IgdHJhaW5pbmd9CnJlZ2lzdGVyRG9NQyhjb3Jlcz04KQpjdHJsX2Zhc3QgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0icmVwZWF0ZWRjdiIsIAogICAgICAgICAgICAgICAgICAgICByZXBlYXRzPTEsCiAgICAgICAgICAgICAgICAgICAgIG51bWJlcj01LCAKICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeUZ1bmN0aW9uPXR3b0NsYXNzU3VtbWFyeSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZUl0ZXI9RiwKICAgICAgICAgICAgICAgICAgICAgI3ByZVByb2NPcHRpb25zID0gbGlzdChwY2FDb21wID0gMTApLAogICAgICAgICAgICAgICAgICAgICBjbGFzc1Byb2JzPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGFsbG93UGFyYWxsZWwgPSBUKSAgCgpubkdyaWQgPC0gIGV4cGFuZC5ncmlkKGRlY2F5ID0gYygwKSwgc2l6ZT1jKDEwLDIwLDUwLDEwMCwyMDAsMzAwKSkgCnN2bUdyaWQgPC0gIGV4cGFuZC5ncmlkKHNpZ21hID0gYygwLjAwMSwwLjAwMDAxKSwgQz1jKDAuMSwwLjAwMSwwLjUpKSAKCmtlcmFzR3JpZCA8LSBleHBhbmQuZ3JpZCgKICBzaXplPWMoMjAsNTAsMjAwMCw1MDAwKSwKICBsYW1iZGE9YygwKSwKICBiYXRjaF9zaXplPWMoMTAyNCksCiAgZGVjYXk9YygwKSwKICBhY3RpdmF0aW9uPWMoJ3RhbmgnLCdzaWdtb2lkJywncmVsdScpLAogIGxyPWMoMC4wMDEpLCByaG89YygwLjkpCikKCmN0cmxfZmFzdCRzYW1wbGluZzwtInNtb3RlIgpyZkZpdCA8LSBjYXJldDo6dHJhaW4odHJhaW5fZm9ybXVsYSwKICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sCiAgICAgICAgICAgICAgIG1ldHJpYz0iUk9DIiwKICAgICAgICAgICAgICAgI3ByZVByb2Nlc3M9YygicGNhIiksCiAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAjbWV0aG9kID0gIm1scFdlaWdodERlY2F5IiwKICAgICAgICAgICAgICAgI21ldGhvZCA9ICJzdm1SYWRpYWwiLAogICAgICAgICAgICAgICAjbWV0aG9kID0ibWxwS2VyYXNEZWNheSIsCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsCiAgICAgICAgICAgICAgICN0dW5lR3JpZD1rZXJhc0dyaWQsCiAgICAgICAgICAgICAgIHR1bmVMZW5ndGg9MTEsCiAgICAgICAgICAgICAgIHZlcmJvc2U9MCwKICAgICAgICAgICAgICAgI2Vwb2Nocz0xMDAsCiAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmxfZmFzdCkKYGBgCiMjIFJlc3VsdGluZyBtb2RlbHMKCmBgYHtyIGZpbmFsIG1vZGVsfQpyZkZpdApnZ3Bsb3QocmZGaXQpK3RoZW1lX2J3KCkKYGBgCgoKCiMjIE1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgZm9yIFJGClRIZSBsaXN0IG9mIHRoZSBtb3N0IHJlbGV2YW50IGZlYXR1cmVzIHVzZWQgYnkgdGhlIFJhbmRvbUZvcmVzdCBDbGFzc2lmaWVyCmBgYHtyIHZhcmlhYmxlIGltcG9ydGFuY2V9CnZhckltcChyZkZpdCwgc2NhbGUgPSBGKQpgYGAKIyBFdmFsdWF0aW9uIG9uIHRlc3Qgc2V0CgojIyBDb25mdXNpb24gTWF0cml4CmBgYHtyIHRlc3Rpbmd9CmRhdGFfdGVzdF9yZXN1bHRzPC1jYmluZChkYXRhX3Rlc3QscHJlZGljdGVkX3ByZWNpcF9wcm9icz1wcmVkaWN0KHJmRml0LGRhdGFfdGVzdCx0eXBlPSdwcm9iJykpCmRhdGFfdGVzdF9yZXN1bHRzPC1jYmluZChkYXRhX3Rlc3RfcmVzdWx0cyxwcmVkaWN0ZWRfcHJlY2lwPWlmZWxzZShkYXRhX3Rlc3RfcmVzdWx0cyRwcmVkaWN0ZWRfcHJlY2lwX3Byb2JzLnllcyA+PTAuNCwneWVzJywnbm8nKSkKCgpjbTwtY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChhcy5mYWN0b3IoZGF0YV90ZXN0X3Jlc3VsdHMkcHJlZGljdGVkX3ByZWNpcCksZGF0YV90ZXN0X3Jlc3VsdHMkcHJlY2lwLHBvc2l0aXZlPSJ5ZXMiKQpjbQpgYGAKIyMgUk9DIEN1cnZlCmBgYHtyIHJvYyBjdXJ2ZX0KI3Bsb3Qocm9jKGRhdGFfdGVzdCRwcm9maWxlLHByZWRzcnByb2ZpbGVyb2JzYW1wJE1hbGljaW91cykpCmdncGxvdChjYmluZChwcmVkaWN0KHJmRml0LGRhdGFfdGVzdCx0eXBlPSdwcm9iJyksY2xhc3M9ZGF0YV90ZXN0JHByZWNpcCksIAogICAgICAgYWVzKG0gPSB5ZXMsIGQgPSBmYWN0b3IoY2xhc3MsIGxhYmVscz1jKCJubyIsInllcyIpLGxldmVscyA9IGMoIm5vIiwgInllcyIpKSkpICsgCiAgICBnZW9tX3JvYyhoanVzdCA9IC0wLjQsIHZqdXN0ID0gMS41LGNvbG91cj0nb3JhbmdlJykgKyAKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSxjb2xvdXI9J3JlZCcpKwogIHRoZW1lX2J3KCkKCmBgYAojIEFuYWx5c2lzIG9mIHRoZSByZXN1bHRzCiMjIERpc3RyaWJ1dGlvbiBvZiBUUCBhbmQgRk4gKHdoYXQga2luZCBvZiByYWluZmFsbCBhcmUgd2UgZGV0ZWN0aW5nKQoKYGBge3J9CkZQX2RheXM8LSBhcy5kYXRhLmZyYW1lKGRhdGFfdGVzdF9yZXN1bHRzKSAlPiUgZmlsdGVyKHByZWNpcCA9PSAnbm8nICYgcHJlZGljdGVkX3ByZWNpcCA9PSAneWVzJykKVFBfZGF5czwtIGFzLmRhdGEuZnJhbWUoZGF0YV90ZXN0X3Jlc3VsdHMpICU+JSBmaWx0ZXIocHJlY2lwID09ICd5ZXMnICYgcHJlZGljdGVkX3ByZWNpcCA9PSAneWVzJykKCkZOX2RheXM8LSBhcy5kYXRhLmZyYW1lKGRhdGFfdGVzdF9yZXN1bHRzKSAlPiUgZmlsdGVyKHByZWNpcCA9PSAneWVzJyAmIHByZWRpY3RlZF9wcmVjaXAgPT0gJ25vJykKVE5fZGF5czwtIGFzLmRhdGEuZnJhbWUoZGF0YV90ZXN0X3Jlc3VsdHMpICU+JSBmaWx0ZXIocHJlY2lwID09ICdubycgJiBwcmVkaWN0ZWRfcHJlY2lwID09ICdubycpCgoKcmJpbmQoZGF0YS5mcmFtZSh2YXJpYWJsZT1yZXAoIkZOIixsZW5ndGgoRk5fZGF5cyRwcmVjaXBfcmVnKSksdmFsdWU9Rk5fZGF5cyRwcmVjaXBfcmVnKSwKZGF0YS5mcmFtZSh2YXJpYWJsZT1yZXAoIlRQIixsZW5ndGgoVFBfZGF5cyRwcmVjaXBfcmVnKSksdmFsdWU9VFBfZGF5cyRwcmVjaXBfcmVnKQojZGF0YS5mcmFtZSh2YXJpYWJsZT1yZXAoIlROIixsZW5ndGgoVE5fZGF5cyRwcmVjaXBfcmVnKSksdmFsdWU9VE5fZGF5cyRwcmVjaXBfcmVnKSwKI2RhdGEuZnJhbWUodmFyaWFibGU9cmVwKCJGUCIsbGVuZ3RoKEZQX2RheXMkcHJlY2lwX3JlZykpLHZhbHVlPUZQX2RheXMkcHJlY2lwX3JlZykKKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2JveHBsb3QoYWVzKHg9dmFyaWFibGUseT12YWx1ZSxmaWxsPXZhcmlhYmxlKSkrCiAgeWxhYigicHJlY2lwaXRhdGlvbiB2b2x1bWUiKSt4bGFiKCJyZXN1bHRzIikrCiAgdGhlbWVfYncoKQoKYGBgCgo=