Loading & Viewing Data

store_data=Hackathon_Working_Data
store_valid=Hackathon_Validation_Data
head(store_data,10)
NA
NA

Checking null values:

colSums(is.na(store_data))
    MONTH STORECODE       DAY   BILL_ID  BILL_AMT       QTY     VALUE 
        0         0         0         0         0         0         0 
    PRICE       GRP      SGRP     SSGRP       CMP      MBRD       BRD 
        0         0         0         0         0         0         0 

It shows that there are no missing value in the data so we can mobve over to the descriptive analysis of the data.

Descriptive analysis:

summary(store_data)
    MONTH            STORECODE              DAY       
 Length:26985       Length:26985       Min.   : 1.00  
 Class :character   Class :character   1st Qu.: 7.00  
 Mode  :character   Mode  :character   Median :14.00  
                                       Mean   :15.17  
                                       3rd Qu.:23.00  
                                       Max.   :31.00  
   BILL_ID             BILL_AMT           QTY           
 Length:26985       Min.   :   0.0   Min.   :    0.500  
 Class :character   1st Qu.:  40.0   1st Qu.:    1.000  
 Mode  :character   Median : 111.0   Median :    1.000  
                    Mean   : 278.8   Mean   :    4.105  
                    3rd Qu.: 280.0   3rd Qu.:    2.000  
                    Max.   :7292.0   Max.   :12000.000  
     VALUE             PRICE             GRP           
 Min.   :   0.00   Min.   :   0.00   Length:26985      
 1st Qu.:  10.00   1st Qu.:  10.00   Class :character  
 Median :  30.00   Median :  22.00   Mode  :character  
 Mean   :  67.81   Mean   :  52.81                     
 3rd Qu.:  80.00   3rd Qu.:  64.00                     
 Max.   :3150.00   Max.   :3150.00                     
     SGRP              SSGRP               CMP           
 Length:26985       Length:26985       Length:26985      
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
     MBRD               BRD           
 Length:26985       Length:26985      
 Class :character   Class :character  
 Mode  :character   Mode  :character  
                                      
                                      
                                      

The above shown is the descriptive information about each feature (i.e central tendency of numeric features and count of categorical variables).

Structure:

str(store_data)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    26985 obs. of  14 variables:
 $ MONTH    : chr  "M1" "M1" "M1" "M1" ...
 $ STORECODE: chr  "N1" "N1" "N1" "N1" ...
 $ DAY      : num  4 4 4 4 4 4 4 4 4 4 ...
 $ BILL_ID  : chr  "T375" "T379" "T381" "T382" ...
 $ BILL_AMT : num  225 95 10 108 19 109 109 25 25 25 ...
 $ QTY      : num  1 1 1 1 1 1 1 1 1 1 ...
 $ VALUE    : num  225 95 10 108 19 10 99 5 10 10 ...
 $ PRICE    : num  225 95 10 108 19 10 99 5 10 10 ...
 $ GRP      : chr  "BUTTER MARGR  (4/94)" "CONFECTIONERY - ECLAIRS" "CHOCOLATE" "PACKAGED TEA" ...
 $ SGRP     : chr  "BUTTER" "CONFECTIONERY - ECLAIRS" "CHOCOLATE PANNED" "MAIN PACKS" ...
 $ SSGRP    : chr  "SALTED" "CONFECTIONERY - ECLAIRS" "CHOCOLATE PANNED" "MAIN PACKS" ...
 $ CMP      : chr  "G C M M F" "PARLE PRODS" "MONDELEZ INTERNATIONAL" "GUJ TEA PROCESSORS" ...
 $ MBRD     : chr  "AMUL" "MELODY" "CADBURY SHOTS" "WAGH BAKRI" ...
 $ BRD      : chr  "AMUL" "MELODY CHOCOLATY" "CADBURY SHOTS" "WAGH BAKRI INSTANT" ...
 - attr(*, "spec")=List of 3
  ..$ cols   :List of 14
  .. ..$ MONTH    : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ STORECODE: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ DAY      : list()
  .. .. ..- attr(*, "class")= chr  "collector_double" "collector"
  .. ..$ BILL_ID  : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ BILL_AMT : list()
  .. .. ..- attr(*, "class")= chr  "collector_double" "collector"
  .. ..$ QTY      : list()
  .. .. ..- attr(*, "class")= chr  "collector_double" "collector"
  .. ..$ VALUE    : list()
  .. .. ..- attr(*, "class")= chr  "collector_double" "collector"
  .. ..$ PRICE    : list()
  .. .. ..- attr(*, "class")= chr  "collector_double" "collector"
  .. ..$ GRP      : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ SGRP     : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ SSGRP    : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ CMP      : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ MBRD     : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ BRD      : list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  ..$ default: list()
  .. ..- attr(*, "class")= chr  "collector_guess" "collector"
  ..$ skip   : num 1
  ..- attr(*, "class")= chr "col_spec"

The above information signifies mainly about the class type of each variables and as we can see some of variables consists of data in string format we need to convert it into factor in order to use it for analysis purpose.

Converting charcter type data feature into factor:

chrc=colnames(store_data[sapply(store_data,class)=="character"])
store_data[,chrc]=lapply(store_data[,chrc],as.factor)

Data Visualization:

Sales vs Month

library(ggplot2)
st=store_data[(store_data$MONTH=="M1"),]
st=as.data.frame.matrix(st)
MAX=colSums(st$VALUE)
library(dplyr)
ggplot(store_data,aes(MONTH,VALUE))+geom_bar(stat = "identity",fill="skyblue")+geom_label(data = store_data %>% filter(MONTH=="M2"),label=MAX,label.padding = unit(0.55, "lines"), 
    label.size = 0.35,col="black",fill="#69b3a2")+theme_bw()

NA
NA
NA

Above graph shows that highest sale was on Month M2.

Sales vs Day

It is very clear from the above graph that 1 week of the month is the time of peak sale.

Data spliting

In order to build a model to infer sales value we have to build a model for which data is needed to train the model and also to check the performance of the model we need a test data to measure the accuracy of predoiction of the model, to meet that purpose we split the data using catools library.

library(caTools)
set.seed(11)
splitz=sample.split(store_data$VALUE,SplitRatio = 0.75)
tr_store=subset(store_data,splitz=="TRUE")
ts_store=subset(store_data,splitz=="FALSE")

Model Building:

Xgboost:

#Data preparing for xgboost algorithm
library(dplyr)
tr_store1=select(tr_store,-c("VALUE"))
ts_store1=select(ts_store,-c("VALUE"))
label_tr=tr_store$VALUE
lable_ts=ts_store$VALUE
#Converting Data into matrix
tr_store1=data.matrix(tr_store1)
ts_store1=data.matrix(ts_store1)
#Loading xgboost library
library(xgboost)

Attaching package: 㤼㸱xgboost㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    slice
#Converting train and test dat into xgb.dmatrix
dtrain=xgb.DMatrix(data = tr_store1,label=label_tr)
dtest=xgb.DMatrix(data = ts_store1,label=lable_ts)
#Setting parameters for model
params = list(booster = "gbtree", objective = "reg:linear", eta=0.3, gamma=0, max_depth=6, min_child_weight=1, subsample=1, colsample_bytree=1)
#calculating best value of nrounds using xgb.cv
xgbcv = xgb.cv( params = params, data = dtrain, nrounds =100, nfold = 5, showsd = T, stratified = T, print_every_n = 10, early_stop_round = 20, maximize = F)
[1] train-rmse:102.859344+1.644619  test-rmse:104.301829+8.793971 
[11]    train-rmse:8.178496+0.352855    test-rmse:22.362740+6.380684 
[21]    train-rmse:3.609056+0.331405    test-rmse:20.664499+5.838938 
[31]    train-rmse:2.813690+0.259298    test-rmse:20.449806+5.833832 
[41]    train-rmse:2.280538+0.237111    test-rmse:20.394627+5.847138 
[51]    train-rmse:1.903526+0.190269    test-rmse:20.355018+5.850336 
[61]    train-rmse:1.603461+0.174214    test-rmse:20.362537+5.829320 
[71]    train-rmse:1.366090+0.129508    test-rmse:20.353934+5.815968 
[81]    train-rmse:1.198732+0.116086    test-rmse:20.353267+5.841785 
[91]    train-rmse:1.021717+0.103120    test-rmse:20.350785+5.854212 
[100]   train-rmse:0.912785+0.082845    test-rmse:20.349273+5.855793 

As we can see that the min rmse is at the 100 value so we will take nrounds as 100 for model building.

#model building
 xgb1 = xgb.train (params = params, data = dtrain, nrounds = 100, watchlist = list(val=dtest,train=dtrain), print_every_n = 10, early_stop_round = 10, maximize = F , eval_metric = "rmse")
[1] val-rmse:82.972633  train-rmse:102.535957 
[11]    val-rmse:13.527576  train-rmse:8.792507 
[21]    val-rmse:12.989974  train-rmse:3.831362 
[31]    val-rmse:12.997304  train-rmse:3.191860 
[41]    val-rmse:12.929111  train-rmse:2.572822 
[51]    val-rmse:12.893428  train-rmse:2.121477 
[61]    val-rmse:12.828229  train-rmse:1.765125 
[71]    val-rmse:12.802021  train-rmse:1.520930 
[81]    val-rmse:12.785632  train-rmse:1.344225 
[91]    val-rmse:12.792033  train-rmse:1.140620 
[100]   val-rmse:12.771682  train-rmse:1.039576 

Now we can predict the VALUE for test data and evaluate the accuracy of model.

#predicting test data
predict_store=predict(xgb1,dtest)
#Evaluating accuracy by rmse
RMSE(lable_ts,predict_store)
[1] "rmse of predicted and orignal data is = 12.7716943226327"

Now we can also see the importance of the variable in the terms of information gain.

from the above plot we can see that price qty bill_amt gives most of the information to predict the sales value.

Now as per our project now we have to predict the value for the provided validation data.

Viewing validation data:

Here we observe that validation data consist of only 4 variables,thus we need a new model to predict the sales value using these variables.

Data Preperation

:

#xgbcv for dewtermining optimum value for nrounds
xgbcv1=xgb.cv(params = params1,data = dtrain1,nrounds = 100,nfold = 5,showsd = T,stratified = T,print_every_n = 10,early_stopping_rounds = 100,maximize = T)
[1] train-rmse:116.840094+2.721652  test-rmse:117.426261+13.024942 
Multiple eval metrics are present. Will use test_rmse for early stopping.
Will train until test_rmse hasn't improved in 100 rounds.

[11]    train-rmse:96.665439+3.043220   test-rmse:103.885532+12.234097 
[21]    train-rmse:95.211403+2.999960   test-rmse:104.309747+12.538476 
[31]    train-rmse:94.514597+3.085344   test-rmse:104.515683+12.105920 
[41]    train-rmse:94.101265+3.187785   test-rmse:104.815491+12.038717 
[51]    train-rmse:93.866852+3.177768   test-rmse:105.225247+11.935744 
[61]    train-rmse:93.700253+3.173371   test-rmse:105.485733+11.808685 
[71]    train-rmse:93.585498+3.181186   test-rmse:105.668302+11.720508 
[81]    train-rmse:93.484912+3.185968   test-rmse:105.851183+11.687715 
[91]    train-rmse:93.419533+3.192469   test-rmse:105.981120+11.658599 
[100]   train-rmse:93.370892+3.192582   test-rmse:106.093030+11.624056 

We observe that at nrounds = 11 the rmse for test and train are minimum so we will use that value for model building.

xgb1 = xgb.train (params = params1, data = dtrain1, nrounds = 11, watchlist = list(val=dtest1,train=dtrain1), print_every_n = 10, early_stop_round = 10, maximize = F , eval_metric = "rmse")
[1] val-rmse:92.612694  train-rmse:117.165169 
[11]    val-rmse:84.392136  train-rmse:97.752678 

Now we have to determine the important variable for model building and predicting the sales value.

Determining important variables:

imp1=xgb.importance(feature_names = colnames(dtrain1),model = xgb11)
xgb.plot.importance(importance_matrix = imp1,col2rgb(col = "skyblue",alpha = 0.7))

Here we can observe grp is the more significant than the other two variables for predicting sales value.

Making prediction and evaluating performance:

Before predicting the sales value for validation data we will check the performance of the model by predicting test data and evaluating through rmse.

#Making prediction 
xgb_predict1=predict(xgb1,dtest1)
#Checking accuracy
RMSE(lable_ts1,xgb_predict1)
[1] "rmse of predicted and orignal data is = 84.392133260772"

Now we also have to prepare validation data before making predictions.

head(xgb_predict11)
[1] 64.42259 31.22036 80.81692 25.64476 25.64476 25.64476

Converting prediction into dataframe for submission:

xgb_predict11=data.frame(xgb_predict11)
colnames(xgb_predict11)="VALUE"
df=data.frame(ID=store_valid$ID,VALUE=xgb_predict11)
df
write.csv(df,"C:/Users/ajesh/Desktop/prediction11.csv",row.names = FALSE)
LS0tDQp0aXRsZTogIlNhbGVzIFByZWRpY3Rpb24iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KPGgzPkxvYWRpbmcgJiBWaWV3aW5nIERhdGE8aDM+DQpgYGB7cn0NCnN0b3JlX2RhdGE9SGFja2F0aG9uX1dvcmtpbmdfRGF0YQ0Kc3RvcmVfdmFsaWQ9SGFja2F0aG9uX1ZhbGlkYXRpb25fRGF0YQ0KaGVhZChzdG9yZV9kYXRhLDEwKQ0KDQoNCmBgYA0KPGgzPkNoZWNraW5nIG51bGwgdmFsdWVzOjxoMz4NCmBgYHtyfQ0KY29sU3Vtcyhpcy5uYShzdG9yZV9kYXRhKSkNCmBgYA0KSXQgc2hvd3MgdGhhdCB0aGVyZSBhcmUgbm8gbWlzc2luZyB2YWx1ZSBpbiB0aGUgZGF0YSBzbyB3ZSBjYW4gbW9idmUgb3ZlciB0byB0aGUgZGVzY3JpcHRpdmUgYW5hbHlzaXMgb2YgdGhlIGRhdGEuDQoNCjxoMz5EZXNjcmlwdGl2ZSBhbmFseXNpczo8aDM+DQpgYGB7cn0NCnN1bW1hcnkoc3RvcmVfZGF0YSkNCmBgYA0KVGhlIGFib3ZlIHNob3duIGlzIHRoZSAgZGVzY3JpcHRpdmUgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBmZWF0dXJlIChpLmUgY2VudHJhbCB0ZW5kZW5jeSBvZiBudW1lcmljIGZlYXR1cmVzIGFuZCBjb3VudCBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMpLg0KDQo8aDM+U3RydWN0dXJlOjxoMz4NCmBgYHtyfQ0Kc3RyKHN0b3JlX2RhdGEpDQpgYGANClRoZSBhYm92ZSBpbmZvcm1hdGlvbiBzaWduaWZpZXMgbWFpbmx5IGFib3V0IHRoZSBjbGFzcyB0eXBlIG9mIGVhY2ggdmFyaWFibGVzIGFuZCBhcyB3ZSBjYW4gc2VlIHNvbWUgb2YgdmFyaWFibGVzIGNvbnNpc3RzIG9mIGRhdGEgaW4gc3RyaW5nIGZvcm1hdCB3ZSBuZWVkIHRvIGNvbnZlcnQgaXQgaW50byBmYWN0b3IgaW4gb3JkZXIgdG8gdXNlIGl0IGZvciBhbmFseXNpcyBwdXJwb3NlLg0KDQo8aDM+Q29udmVydGluZyBjaGFyY3RlciB0eXBlIGRhdGEgZmVhdHVyZSBpbnRvIGZhY3Rvcjo8aDM+DQpgYGB7cn0NCmNocmM9Y29sbmFtZXMoc3RvcmVfZGF0YVtzYXBwbHkoc3RvcmVfZGF0YSxjbGFzcyk9PSJjaGFyYWN0ZXIiXSkNCnN0b3JlX2RhdGFbLGNocmNdPWxhcHBseShzdG9yZV9kYXRhWyxjaHJjXSxhcy5mYWN0b3IpDQoNCmBgYA0KPGgzPkRhdGEgVmlzdWFsaXphdGlvbjo8aDM+DQoNClNhbGVzIHZzIE1vbnRoDQpgYGB7cixmaWcuaGVpZ2h0PTYsZmlnLndpZHRoPTh9DQpsaWJyYXJ5KGdncGxvdDIpDQpzdD1zdG9yZV9kYXRhWyhzdG9yZV9kYXRhJE1PTlRIPT0iTTEiKSxdDQpzdD1hcy5kYXRhLmZyYW1lLm1hdHJpeChzdCkNCk1BWD1jb2xTdW1zKHN0JFZBTFVFKQ0KbGlicmFyeShkcGx5cikNCmdncGxvdChzdG9yZV9kYXRhLGFlcyhNT05USCxWQUxVRSkpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGZpbGw9InNreWJsdWUiKStnZW9tX2xhYmVsKGRhdGEgPSBzdG9yZV9kYXRhICU+JSBmaWx0ZXIoTU9OVEg9PSJNMiIpLGxhYmVsPU1BWCxsYWJlbC5wYWRkaW5nID0gdW5pdCgwLjU1LCAibGluZXMiKSwgDQogICAgbGFiZWwuc2l6ZSA9IDAuMzUsY29sPSJibGFjayIsZmlsbD0iIzY5YjNhMiIpK3RoZW1lX2J3KCkNCg0KDQoNCmBgYA0KQWJvdmUgZ3JhcGggc2hvd3MgdGhhdCBoaWdoZXN0IHNhbGUgd2FzIG9uIE1vbnRoIE0yLg0KDQpTYWxlcyB2cyBEYXkNCmBgYHtyfQ0KZ2dwbG90KHN0b3JlX2RhdGEsYWVzKERBWSxWQUxVRSkpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGZpbGw9Im9yYW5nZSIsYWxwaGE9MC41KSt0aGVtZV9kYXJrKCkNCmBgYA0KSXQgaXMgdmVyeSBjbGVhciBmcm9tIHRoZSBhYm92ZSBncmFwaCB0aGF0IDEgd2VlayBvZiB0aGUgbW9udGggaXMgdGhlIHRpbWUgb2YgcGVhayBzYWxlLg0KDQo8aDM+RGF0YSBzcGxpdGluZzxoMz4NCkluIG9yZGVyIHRvIGJ1aWxkIGEgbW9kZWwgdG8gaW5mZXIgc2FsZXMgdmFsdWUgd2UgaGF2ZSB0byBidWlsZCBhIG1vZGVsIGZvciB3aGljaCBkYXRhIGlzIG5lZWRlZCB0byB0cmFpbiB0aGUgbW9kZWwgYW5kIGFsc28gdG8gY2hlY2sgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbCB3ZSBuZWVkIGEgdGVzdCBkYXRhIHRvIG1lYXN1cmUgdGhlIGFjY3VyYWN5IG9mIHByZWRvaWN0aW9uIG9mIHRoZSBtb2RlbCwgdG8gbWVldCB0aGF0IHB1cnBvc2Ugd2Ugc3BsaXQgdGhlIGRhdGEgdXNpbmcgY2F0b29scyBsaWJyYXJ5Lg0KYGBge3J9DQpsaWJyYXJ5KGNhVG9vbHMpDQpzZXQuc2VlZCgxMSkNCnNwbGl0ej1zYW1wbGUuc3BsaXQoc3RvcmVfZGF0YSRWQUxVRSxTcGxpdFJhdGlvID0gMC43NSkNCnRyX3N0b3JlPXN1YnNldChzdG9yZV9kYXRhLHNwbGl0ej09IlRSVUUiKQ0KdHNfc3RvcmU9c3Vic2V0KHN0b3JlX2RhdGEsc3BsaXR6PT0iRkFMU0UiKQ0KDQpgYGANCjxoMz5Nb2RlbCBCdWlsZGluZzo8aDM+DQoNCjxoMz5YZ2Jvb3N0OjxoMz4NCmBgYHtyfQ0KI0RhdGEgcHJlcGFyaW5nIGZvciB4Z2Jvb3N0IGFsZ29yaXRobQ0KbGlicmFyeShkcGx5cikNCnRyX3N0b3JlMT1zZWxlY3QodHJfc3RvcmUsLWMoIlZBTFVFIikpDQp0c19zdG9yZTE9c2VsZWN0KHRzX3N0b3JlLC1jKCJWQUxVRSIpKQ0KbGFiZWxfdHI9dHJfc3RvcmUkVkFMVUUNCmxhYmxlX3RzPXRzX3N0b3JlJFZBTFVFDQojQ29udmVydGluZyBEYXRhIGludG8gbWF0cml4DQp0cl9zdG9yZTE9ZGF0YS5tYXRyaXgodHJfc3RvcmUxKQ0KdHNfc3RvcmUxPWRhdGEubWF0cml4KHRzX3N0b3JlMSkNCiNMb2FkaW5nIHhnYm9vc3QgbGlicmFyeQ0KbGlicmFyeSh4Z2Jvb3N0KQ0KI0NvbnZlcnRpbmcgdHJhaW4gYW5kIHRlc3QgZGF0IGludG8geGdiLmRtYXRyaXgNCmR0cmFpbj14Z2IuRE1hdHJpeChkYXRhID0gdHJfc3RvcmUxLGxhYmVsPWxhYmVsX3RyKQ0KZHRlc3Q9eGdiLkRNYXRyaXgoZGF0YSA9IHRzX3N0b3JlMSxsYWJlbD1sYWJsZV90cykNCiNTZXR0aW5nIHBhcmFtZXRlcnMgZm9yIG1vZGVsDQpwYXJhbXMgPSBsaXN0KGJvb3N0ZXIgPSAiZ2J0cmVlIiwgb2JqZWN0aXZlID0gInJlZzpsaW5lYXIiLCBldGE9MC4zLCBnYW1tYT0wLCBtYXhfZGVwdGg9NiwgbWluX2NoaWxkX3dlaWdodD0xLCBzdWJzYW1wbGU9MSwgY29sc2FtcGxlX2J5dHJlZT0xKQ0KI2NhbGN1bGF0aW5nIGJlc3QgdmFsdWUgb2YgbnJvdW5kcyB1c2luZyB4Z2IuY3YNCnhnYmN2ID0geGdiLmN2KCBwYXJhbXMgPSBwYXJhbXMsIGRhdGEgPSBkdHJhaW4sIG5yb3VuZHMgPTEwMCwgbmZvbGQgPSA1LCBzaG93c2QgPSBULCBzdHJhdGlmaWVkID0gVCwgcHJpbnRfZXZlcnlfbiA9IDEwLCBlYXJseV9zdG9wX3JvdW5kID0gMjAsIG1heGltaXplID0gRikNCg0KDQpgYGANCkFzIHdlIGNhbiBzZWUgdGhhdCB0aGUgbWluIHJtc2UgaXMgYXQgdGhlIDEwMCB2YWx1ZSBzbyB3ZSB3aWxsIHRha2UgbnJvdW5kcyBhcyAxMDAgZm9yIG1vZGVsIGJ1aWxkaW5nLg0KDQoNCmBgYHtyfQ0KI21vZGVsIGJ1aWxkaW5nDQogeGdiMSA9IHhnYi50cmFpbiAocGFyYW1zID0gcGFyYW1zLCBkYXRhID0gZHRyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QgPSBsaXN0KHZhbD1kdGVzdCx0cmFpbj1kdHJhaW4pLCBwcmludF9ldmVyeV9uID0gMTAsIGVhcmx5X3N0b3Bfcm91bmQgPSAxMCwgbWF4aW1pemUgPSBGICwgZXZhbF9tZXRyaWMgPSAicm1zZSIpDQpgYGANCk5vdyB3ZSBjYW4gcHJlZGljdCB0aGUgVkFMVUUgZm9yIHRlc3QgZGF0YSBhbmQgZXZhbHVhdGUgdGhlIGFjY3VyYWN5IG9mIG1vZGVsLg0KYGBge3J9DQojcHJlZGljdGluZyB0ZXN0IGRhdGENCnByZWRpY3Rfc3RvcmU9cHJlZGljdCh4Z2IxLGR0ZXN0KQ0KI0V2YWx1YXRpbmcgYWNjdXJhY3kgYnkgcm1zZQ0KUk1TRShsYWJsZV90cyxwcmVkaWN0X3N0b3JlKQ0KDQpgYGANCk5vdyB3ZSBjYW4gYWxzbyBzZWUgdGhlIGltcG9ydGFuY2Ugb2YgdGhlIHZhcmlhYmxlIGluIHRoZSB0ZXJtcyBvZiBpbmZvcm1hdGlvbiBnYWluLg0KYGBge3J9DQppbXA9eGdiLmltcG9ydGFuY2UoZmVhdHVyZV9uYW1lcyA9IGNvbG5hbWVzKGR0cmFpbiksbW9kZWwgPSB4Z2IxKQ0KeGdiLnBsb3QuaW1wb3J0YW5jZShpbXBvcnRhbmNlX21hdHJpeCA9IGltcCkNCg0KYGBgDQpmcm9tIHRoZSBhYm92ZSBwbG90IHdlIGNhbiBzZWUgdGhhdCBwcmljZSBxdHkgYmlsbF9hbXQgZ2l2ZXMgbW9zdCBvZiB0aGUgaW5mb3JtYXRpb24gdG8gcHJlZGljdCB0aGUgc2FsZXMgdmFsdWUuIA0KDQpOb3cgYXMgcGVyIG91ciBwcm9qZWN0IG5vdyB3ZSBoYXZlIHRvIHByZWRpY3QgdGhlIHZhbHVlIGZvciB0aGUgcHJvdmlkZWQgdmFsaWRhdGlvbiBkYXRhLg0KPGgzPlZpZXdpbmcgdmFsaWRhdGlvbiBkYXRhOjxoMz4NCg0KYGBge3J9DQpoZWFkKHN0b3JlX3ZhbGlkLDEwKQ0KYGBgDQpIZXJlIHdlIG9ic2VydmUgdGhhdCB2YWxpZGF0aW9uIGRhdGEgY29uc2lzdCBvZiBvbmx5IDQgdmFyaWFibGVzLHRodXMgd2UgbmVlZCBhIG5ldyBtb2RlbCB0byBwcmVkaWN0IHRoZSBzYWxlcyB2YWx1ZSB1c2luZyB0aGVzZSB2YXJpYWJsZXMuDQoNCjxoMz5EYXRhIFByZXBlcmF0aW9uPGgzPjoNCmBgYHtyfQ0KI0RhdGEgcHJlcGFyaW5nDQp0cl9zdG9yZTExPXNlbGVjdCh0cl9zdG9yZSwtYygiVkFMVUUiKSkNCnRzX3N0b3JlMTE9c2VsZWN0KHRzX3N0b3JlLC1jKCJWQUxVRSIpKQ0KbGFiZWxfdHIxPXRyX3N0b3JlJFZBTFVFDQpsYWJsZV90czE9dHNfc3RvcmUkVkFMVUUNCiNTZWxlY3Rpbmcgb25seSB0aG9zZSB2YXJpYWJsZXMgZnJvbSB3b3JraW5nIGRhdGEgdGhhaHQgaXMgcHJlc2VudCBpbiB2YWxpZGF0aW9uIGRhdGENCnRyX3N0b3JlMTE9c2VsZWN0KHRyX3N0b3JlMTEsYygiU1RPUkVDT0RFIiwiTU9OVEgiLCJHUlAiKSkNCnRzX3N0b3JlMTE9c2VsZWN0KHRzX3N0b3JlMTEsYygiU1RPUkVDT0RFIiwiTU9OVEgiLCJHUlAiKSkNCiNjb252ZXJ0aW5nIGludG8gbWF0cml4DQp0cl9zdG9yZTExPWRhdGEubWF0cml4KHRyX3N0b3JlMTEpDQp0c19zdG9yZTExPWRhdGEubWF0cml4KHRzX3N0b3JlMTEpDQpsaWJyYXJ5KHhnYm9vc3QpDQojQ29udmVydGluZyB0cmFpbmlnIGRhdGEgaW50byB4Z2IuZG1hdHJpeCBmb3IgbW9kZWwgYnVpbGRpbmcNCmR0cmFpbjE9eGdiLkRNYXRyaXgodHJfc3RvcmUxMSxsYWJlbD1sYWJlbF90cjEpDQpkdGVzdDE9eGdiLkRNYXRyaXgodHNfc3RvcmUxMSxsYWJlbD1sYWJsZV90czEpDQojU2V0dGluZyBwYXJhbWV0ZXINCnBhcmFtczEgPSBsaXN0KGJvb3N0ZXIgPSAiZ2J0cmVlIiwgb2JqZWN0aXZlID0gInJlZzpsaW5lYXIiLCBldGE9MC41LCBnYW1tYT0wLCBtYXhfZGVwdGg9NiwgbWluX2NoaWxkX3dlaWdodD0xLCBzdWJzYW1wbGU9MSwgY29sc2FtcGxlX2J5dHJlZT0xKQ0KI3hnYmN2IGZvciBkZXd0ZXJtaW5pbmcgb3B0aW11bSB2YWx1ZSBmb3IgbnJvdW5kcw0KeGdiY3YxPXhnYi5jdihwYXJhbXMgPSBwYXJhbXMxLGRhdGEgPSBkdHJhaW4xLG5yb3VuZHMgPSAxMDAsbmZvbGQgPSA1LHNob3dzZCA9IFQsc3RyYXRpZmllZCA9IFQscHJpbnRfZXZlcnlfbiA9IDEwLGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDEwMCxtYXhpbWl6ZSA9IFQpDQoNCmBgYA0KV2Ugb2JzZXJ2ZSB0aGF0IGF0IG5yb3VuZHMgPSAxMSB0aGUgcm1zZSBmb3IgdGVzdCAgYW5kIHRyYWluIGFyZSBtaW5pbXVtIHNvIHdlIHdpbGwgdXNlIHRoYXQgdmFsdWUgZm9yIG1vZGVsIGJ1aWxkaW5nLiANCmBgYHtyfQ0KeGdiMSA9IHhnYi50cmFpbiAocGFyYW1zID0gcGFyYW1zMSwgZGF0YSA9IGR0cmFpbjEsIG5yb3VuZHMgPSAxMSwgd2F0Y2hsaXN0ID0gbGlzdCh2YWw9ZHRlc3QxLHRyYWluPWR0cmFpbjEpLCBwcmludF9ldmVyeV9uID0gMTAsIGVhcmx5X3N0b3Bfcm91bmQgPSAxMCwgbWF4aW1pemUgPSBGICwgZXZhbF9tZXRyaWMgPSAicm1zZSIpDQpgYGANCk5vdyB3ZSBoYXZlICB0byBkZXRlcm1pbmUgdGhlIGltcG9ydGFudCB2YXJpYWJsZSBmb3IgbW9kZWwgYnVpbGRpbmcgYW5kIHByZWRpY3RpbmcgdGhlIHNhbGVzIHZhbHVlLg0KDQo8aDM+RGV0ZXJtaW5pbmcgaW1wb3J0YW50IHZhcmlhYmxlczo8aDM+DQpgYGB7cn0NCmltcDE9eGdiLmltcG9ydGFuY2UoZmVhdHVyZV9uYW1lcyA9IGNvbG5hbWVzKGR0cmFpbjEpLG1vZGVsID0geGdiMTEpDQp4Z2IucGxvdC5pbXBvcnRhbmNlKGltcG9ydGFuY2VfbWF0cml4ID0gaW1wMSxjb2wycmdiKGNvbCA9ICJza3libHVlIixhbHBoYSA9IDAuNykpDQpgYGANCkhlcmUgd2UgY2FuIG9ic2VydmUgZ3JwIGlzIHRoZSBtb3JlIHNpZ25pZmljYW50IHRoYW4gdGhlIG90aGVyIHR3byB2YXJpYWJsZXMgZm9yIHByZWRpY3Rpbmcgc2FsZXMgdmFsdWUuDQoNCjxoMz5NYWtpbmcgcHJlZGljdGlvbiBhbmQgZXZhbHVhdGluZyBwZXJmb3JtYW5jZTo8aDM+DQoNCkJlZm9yZSBwcmVkaWN0aW5nIHRoZSBzYWxlcyB2YWx1ZSBmb3IgdmFsaWRhdGlvbiBkYXRhIHdlIHdpbGwgY2hlY2sgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbCBieSBwcmVkaWN0aW5nIHRlc3QgZGF0YSBhbmQgZXZhbHVhdGluZyB0aHJvdWdoIHJtc2UuDQoNCmBgYHtyfQ0KI01ha2luZyBwcmVkaWN0aW9uIA0KeGdiX3ByZWRpY3QxPXByZWRpY3QoeGdiMSxkdGVzdDEpDQojQ2hlY2tpbmcgYWNjdXJhY3kNClJNU0UobGFibGVfdHMxLHhnYl9wcmVkaWN0MSkNCg0KYGBgDQoNCk5vdyB3ZSBhbHNvIGhhdmUgdG8gcHJlcGFyZSB2YWxpZGF0aW9uIGRhdGEgYmVmb3JlIG1ha2luZyBwcmVkaWN0aW9ucy4NCmBgYHtyfQ0KI0NvbnZlcnRpbmcgY2hhcmFjdGVyIHR5cGUgdmFyaWFibGUgaW50byBmYWN0b3INCmNocmMxPWNvbG5hbWVzKHN0b3JlX3ZhbGlkW3NhcHBseShzdG9yZV92YWxpZCxjbGFzcyk9PSJjaGFyYWN0ZXIiXSkNCnN0b3JlX3ZhbGlkWyxjaHJjMV09bGFwcGx5KHN0b3JlX3ZhbGlkWyxjaHJjMV0sYXMuZmFjdG9yKQ0Kc3RvcmVfdmFsaWQxPXNlbGVjdChzdG9yZV92YWxpZCwtYygiSUQiKSkNCnN0b3JlX3ZhbGlkMT1kYXRhLm1hdHJpeChzdG9yZV92YWxpZDEpDQojQ29udmVydGluZyBpbnRvIHhnYi5kbWF0cml4DQpkdmFsaWQ9eGdiLkRNYXRyaXgoc3RvcmVfdmFsaWQxKQ0KI01ha2luZyBwcmVkaWN0aW9uDQp4Z2JfcHJlZGljdDExPXByZWRpY3QoeGdiMSxkdmFsaWQpDQpoZWFkKHhnYl9wcmVkaWN0MTEpDQpgYGANCjxoNT5Db252ZXJ0aW5nIHByZWRpY3Rpb24gaW50byBkYXRhZnJhbWUgZm9yIHN1Ym1pc3Npb246PGgzPg0KYGBge3J9DQp4Z2JfcHJlZGljdDExPWRhdGEuZnJhbWUoeGdiX3ByZWRpY3QxMSkNCmNvbG5hbWVzKHhnYl9wcmVkaWN0MTEpPSJWQUxVRSINCmRmPWRhdGEuZnJhbWUoSUQ9c3RvcmVfdmFsaWQkSUQsVkFMVUU9eGdiX3ByZWRpY3QxMSkNCmRmDQp3cml0ZS5jc3YoZGYsIkM6L1VzZXJzL2FqZXNoL0Rlc2t0b3AvcHJlZGljdGlvbjExLmNzdiIscm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0K