timestamp client query query_type answer_ip domain_l1 domain_l2
Min. :2017-02-20 23:12:13 Length:10000000 Length:10000000 Length:10000000 Length:10000000 Length:10000000 Length:10000000
1st Qu.:2017-03-07 19:37:18 Class :character Class :character Class :character Class :character Class :character Class :character
Median :2017-03-08 22:39:36 Mode :character Mode :character Mode :character Mode :character Mode :character Mode :character
Mean :2017-03-09 07:33:45
3rd Qu.:2017-03-11 12:45:21
Max. :2017-03-15 07:16:55
ttl answer dga.probability dga.class
Min. :0.000e+00 Length:10000000 Min. :0 Min. :0
1st Qu.:0.000e+00 Class :character 1st Qu.:0 1st Qu.:0
Median :3.000e+01 Mode :character Median :0 Median :0
Mean :4.872e+03 Mean :0 Mean :0
3rd Qu.:3.000e+02 3rd Qu.:0 3rd Qu.:0
Max. :2.147e+09 Max. :1 Max. :1
NA's :388724 NA's :388724
Feature Generation
Feature file available at https://www.dropbox.com/s/vl9y5p32e2umxu1/wbone-client-profile-1h-last8days.csv?dl=1
summary(clients_profiles)
client tot_requests tot_detected ratio_detected tot_nx ratio_nx tot_mx
Length:15870 Min. : 1.0 Min. : 0.000 Min. :0.0000 Min. : 0.0 Min. :0.00000 Min. : 0.0000
Class :character 1st Qu.: 2.0 1st Qu.: 0.000 1st Qu.:0.0000 1st Qu.: 0.0 1st Qu.:0.00000 1st Qu.: 0.0000
Mode :character Median : 6.0 Median : 0.000 Median :0.0000 Median : 0.0 Median :0.00000 Median : 0.0000
Mean : 516.7 Mean : 3.822 Mean :0.0082 Mean : 149.8 Mean :0.10165 Mean : 0.6547
3rd Qu.: 43.0 3rd Qu.: 0.000 3rd Qu.:0.0000 3rd Qu.: 3.0 3rd Qu.:0.07692 3rd Qu.: 0.0000
Max. :61787.0 Max. :609.000 Max. :1.0000 Max. :54037.0 Max. :1.00000 Max. :170.0000
ratio_mx tot_fail ratio_fail tot_reverse ratio_reverse max_sameip tot_sameip
Min. :0.000000 Min. : 0.0 Min. :0.00000 Min. : 0.00 Min. :0.00000 Min. : 0.000 Min. : 0.00
1st Qu.:0.000000 1st Qu.: 0.0 1st Qu.:0.00000 1st Qu.: 0.00 1st Qu.:0.00000 1st Qu.: 0.000 1st Qu.: 0.00
Median :0.000000 Median : 0.0 Median :0.00000 Median : 0.00 Median :0.00000 Median : 0.000 Median : 0.00
Mean :0.002088 Mean : 105.5 Mean :0.03087 Mean : 15.35 Mean :0.02532 Mean : 4.256 Mean : 55.59
3rd Qu.:0.000000 3rd Qu.: 0.0 3rd Qu.:0.00000 3rd Qu.: 0.00 3rd Qu.:0.00000 3rd Qu.: 2.000 3rd Qu.: 2.00
Max. :1.000000 Max. :12510.0 Max. :1.00000 Max. :2177.00 Max. :1.00000 Max. :1610.000 Max. :5692.00
avg_sameip sd_sameip tot_samedomain max_samedomain avg_samedomain sd_samedomain ratio_tot_samedomain
Min. : 0.0000 Min. : 0.0000 Min. : 0.00 Min. : 0.000 Min. :0.000 Min. :0.0000 Min. :0.0000
1st Qu.: 0.0000 1st Qu.: 0.0000 1st Qu.: 0.00 1st Qu.: 0.000 1st Qu.:0.000 1st Qu.:0.0000 1st Qu.:0.0000
Median : 0.0000 Median : 0.0000 Median : 0.00 Median : 0.000 Median :0.000 Median :0.0000 Median :0.0000
Mean : 0.7989 Mean : 0.4673 Mean : 68.87 Mean : 2.177 Mean :1.158 Mean :0.2819 Mean :0.1419
3rd Qu.: 2.0000 3rd Qu.: 0.0000 3rd Qu.: 7.00 3rd Qu.: 3.000 3rd Qu.:2.444 3rd Qu.:0.4754 3rd Qu.:0.2208
Max. :41.7500 Max. :70.1429 Max. :5310.00 Max. :84.000 Max. :9.000 Max. :5.6569 Max. :1.0000
ratio_max_samedomain ratio_avg_samedomain ratio_sd_samedomain ratio_tot_sameip ratio_max_sameip ratio_avg_sameip ratio_sd_sameip
Min. :0.00000 Min. :0.00000 Min. :0.0000000 Min. :0.00000 Min. :0.000000 Min. :0.0000000 Min. :0.000000
1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.0000000 1st Qu.:0.00000 1st Qu.:0.000000 1st Qu.:0.0000000 1st Qu.:0.000000
Median :0.00000 Median :0.00000 Median :0.0000000 Median :0.00000 Median :0.000000 Median :0.0000000 Median :0.000000
Mean :0.08588 Mean :0.08116 Mean :0.0037496 Mean :0.03802 Mean :0.020587 Mean :0.0181529 Mean :0.001514
3rd Qu.:0.05039 3rd Qu.:0.03571 3rd Qu.:0.0001406 3rd Qu.:0.02563 3rd Qu.:0.005566 3rd Qu.:0.0006352 3rd Qu.:0.000000
Max. :1.00000 Max. :1.00000 Max. :0.3535534 Max. :1.00000 Max. :1.000000 Max. :1.0000000 Max. :0.489536
profilenum currenttm
Min. : 0.0 Min. :2017-03-07 07:16:51
1st Qu.: 59.0 1st Qu.:2017-03-08 20:16:51
Median :113.0 Median :2017-03-10 14:16:51
Mean :105.8 Mean :2017-03-10 21:25:51
3rd Qu.:155.0 3rd Qu.:2017-03-12 20:16:51
Max. :192.0 Max. :2017-03-15 07:16:51
Selecting features
client_profile_selected_features<-clients_profiles %>% mutate(ratio_requests=tot_requests/max(tot_requests)) %>%
#select(client,tot_requests, tot_detected,tot_nx,tot_mx,tot_fail,tot_reverse,tot_samedomain,tot_sameip)
select(client,tot_requests,ratio_requests,ratio_detected,ratio_nx,ratio_mx,ratio_fail,ratio_reverse,
ratio_tot_samedomain,ratio_tot_sameip,
ratio_avg_samedomain,ratio_avg_sameip,
ratio_sd_samedomain,ratio_sd_sameip)
client_profile_ratio<-unique(client_profile_ratio)
#select(client,tot_requests,tot_detected,max_sameip,avg_sameip,sd_sameip,max_samedomain,avg_samedomain,max_samedomain,ratio_nx)
client_profile_selected_features
Matrix Correlation heatmap
library(d3heatmap)
corr_client_profile_selected_features<-cor(client_profile_selected_features[,c(-1,-2)],use="complete.obs")
d3heatmap(corr_client_profile_selected_features,symm=T,colors = "Reds",Rowv=T,
width = 600,height = 400,
yaxis_font_size = "8pt",
xaxis_font_size = "8pt")
Per Feature Scatter plot
pairs(client_profile_selected_features[,c(-1,-2)],cex.labels = 1.6)

ggplot(client_profile_ratio,aes(ratio_nx,ratio_detected))+
geom_point(aes(alpha=tot_requests),color='skyblue')+
geom_smooth(method = 'lm',color='orange')+
theme_bw()
Kmeans clustering
kmeans_model<-kmeans(client_profile_selected_features[,c(-1,-2)],centers=5,nstart=40)
client_profile_ratio<-cbind(client_profile_selected_features,cluster=as.factor(kmeans_model$cluster))
client_profile_ratio %>% group_by(cluster) %>% summarise(n=n())
save(kmeans_model,file="../models/kmeans_model_1h_last8days.Rdata")
Clustering Results
According to clustering results
- Cluster n. 1 groups clients with high NX replies as well as some reverse queries among others. (possible DGA)
- Cluster n. 2 groups those clients not showing DGA or other malwaver behavior (Possible NORMAL??)
- Cluster n. 3 groups those clients with the highest samedomains values (possible Fastflux?)
- Cluster n. 4 groups those clients with a value increment for sameip and samedomains (Possible DGA/Fastflux? Not really)
- Cluster n. 5 groups those clients with high values in FAIL queries (Possible what??)
some IPS to check
213.191.105.210 shows some possible random generated domains
213.191.105.242 Shows a lot of NX records. Some queries contain the kk. TLD (??)
T-NSE 2D representation with clusters
Limitations of PCA
PCA is a linear algorithm. It will not be able to interpret complex polynomial relationship between features. On the other hand, t-SNE is based on probability distributions with random walk on neighborhood graphs to find the structure within the data.
tsne_model<-Rtsne(unique(client_profile_ratio[,c(-2,-ncol(client_profile_ratio))]),pca=FALSE, pca_center =TRUE, pca_scale=TRUE,check_duplicates=F,
perplexity = 400, max_iter = 1000)
To remember: It seems that if we generate 1h profiles the the 2D representation tends to be more difficult to analyze. The are many points very similar corresponding to the same client profile at different time periods. Using a higher perplexity value seems to solve that issue.
Each point represents a 1 hour profile. The size of a given point/profile corresponds to the request ratios for that profile. Finally, color presents differents clusters. Remember you can select/deselect the cluster by clicking the legend.
client_profile_unique<-unique(client_profile_ratio[,c(-2)])
tsne_client_profile<-data.frame(tsne_model$Y,cluster=client_profile_unique$cluster,client=client_profile_unique$client,requests=
client_profile_unique$ratio_requests)
sampleid<-sample(nrow(tsne_client_profile),nrow(tsne_client_profile))
g<-ggplot(tsne_client_profile[sampleid,],aes(x=X1,y=X2))+
geom_point(aes(color=as.factor(cluster),size=requests,text=paste("ipddr",client)))+
#geom_point(aes(shape=asignacion),size=3)+
ylab("X1")+xlab("X2")+
theme_classic()+
#scale_shape_manual(values=c(8,6))+
guides(color=FALSE,alpha=FALSE)
ggplotly(g)
Decision Tree Analysis
library(rpart.plot)
library(rpart)
library(rpart.utils)
formula <- as.formula(cluster~.)
tree=rpart(formula,data=client_profile_ratio[,c(-1,-2,-3,-4)],control = rpart.control(minsplit=500, cp=0.005,xval=10),model=T,x=T,y=T)
rpart.plot(tree,
extra=4,
box.palette="GnBu",
branch.lty=5, shadow.col=0, nn=TRUE, cex =0.9,under=T
)
printcp(tree)
Clients in cluster 1
tsne_client_profile %>% filter(cluster==1) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0 ) %>% select(client)
Clients in cluster 2
tsne_client_profile %>% filter(cluster==2) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0) %>% select(client)
Clients in cluster 3
tsne_client_profile %>% filter(cluster==3) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0) %>% select(client)
Clients in cluster 4
tsne_client_profile %>% filter(cluster==4) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0) %>% select(client)
Clients in cluster 5
tsne_client_profile %>% filter(cluster==5) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0) %>% select(client)
Clients in cluster 6
tsne_client_profile %>% filter(cluster==6) %>% group_by(client) %>% summarise(profiles=n()) %>% arrange(desc(profiles)) %>% filter(profiles>0) %>% select(client)
LS0tCnRpdGxlOiAiV0IgY2xpZW50IDEgaG91ciBwcm9maWxlIGdlbmVyYXRpb24gKGxhc3QgOCBkYXlzKSAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogeWVzCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxNHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDlweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogOXB4Cn0KPC9zdHlsZT4KCgoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MTIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShkb01DKQpyZWdpc3RlckRvTUMoY29yZXM9MykKZG9tYWluczwtcmVhZF9jc3YoZmlsZT0iL2hvbWUvaGFycG8vRHJvcGJveC9vbmdvaW5nLXdvcmsvZ2l0LXJlcG9zL2RnYS13Yi9kYXRhc2V0cy9kYXRhYmFzZS5jc3YiLHByb2dyZXNzID0gVCkKc3VtbWFyeShkb21haW5zKQoKICAjZG9tYWlucyAlPiUgZmlsdGVyKGlzLm5hKGRnYS5jbGFzcykpIApgYGAKCgoKCiMjIEZlYXR1cmUgR2VuZXJhdGlvbgoKRmVhdHVyZSBmaWxlIGF2YWlsYWJsZSBhdCAgaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy92bDl5NXAzMmUydW14dTEvd2JvbmUtY2xpZW50LXByb2ZpbGUtMWgtbGFzdDhkYXlzLmNzdj9kbD0xCgoKYGBge3IgZmVhdHVyZSBnZW5lcmF0aW9uLCBlY2hvPVRSVUV9Cm1heF9udW1fcHJvZmlsZXM9MTkyCndpbmRvd3NpemU8LTYwKjEgIyAyNEhzIHdpbmRvdwppbml0aWFsX3RtPC1kb21haW5zJHRpbWVzdGFtcFtucm93KGRvbWFpbnMpLTFdCmNsaWVudHNfcHJvZmlsZXM8LWRhdGEuZnJhbWUoKQoKY2xpZW50c19wcm9maWxlczwtZm9yZWFjaCAocHJvZmlsZW51bSA9IDA6bWF4X251bV9wcm9maWxlcywuY29tYmluZT0ncmJpbmQnLCAubXVsdGljb21iaW5lPUZBTFNFICkgJWRvcGFyJSB7CiAgY3VycmVudHRtPC1pbml0aWFsX3RtIC0gKG1pbnV0ZXMod2luZG93c2l6ZSkqcHJvZmlsZW51bSkKICBkb21haW5zX3dpbmRvdzwtZG9tYWlucyAlPiUgZmlsdGVyKHRpbWVzdGFtcCA+IGN1cnJlbnR0bSAtbWludXRlcyh3aW5kb3dzaXplKSAmIHRpbWVzdGFtcCA8IGN1cnJlbnR0bSkKIAogICAjIFRvdGFsIGFtb3VudCBvZiBkb21hbnMgcmVxdWVzdGVkIAogIG5fcmVxdWVzdHMgPC1kb21haW5zX3dpbmRvdyAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgCiAgc3VtbWFyaXNlKHRvdF9yZXF1ZXN0cz1uKCkpICMlPiUgYXJyYW5nZShkZXNjKHRvdF9yZXF1ZXN0cykpCgogICMgVG90YWwgYW1vdW50IG9mIGRvbWFpbnMgZGV0ZWN0ZWQgYnkgdGhlIE5OIGFuZCByYXRpby4KICBuX2RldGVjdGVkPC1kb21haW5zX3dpbmRvdyAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgCiAgc3VtbWFyaXNlKHRvdF9kZXRlY3RlZD1zdW0oaWZlbHNlKGRnYS5jbGFzcz09MSwxLDApLG5hLnJtPVQpKQogIG5fZGV0ZWN0ZWQkcmF0aW9fZGV0ZWN0ZWQ8LW5fZGV0ZWN0ZWQkdG90X2RldGVjdGVkL25fcmVxdWVzdHMkdG90X3JlcXVlc3RzCgoKICAjIFRvdGFsIGFtb3VudCBvZiBOWERPTUFJTiBhbmQgcmF0aW8KICBuX254PC1kb21haW5zX3dpbmRvdyAgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIAogIHN1bW1hcmlzZSh0b3Rfbng9c3VtKGdyZXBsKCJOWERPTUFJTiIsYW5zd2VyKSkpCiAgbl9ueCRyYXRpb19ueDwtbl9ueCR0b3Rfbngvbl9yZXF1ZXN0cyR0b3RfcmVxdWVzdHMKCiAgIyBUb3RhbCBhbW91bnQgb2YgU0VSVkZBSUwgYW5kIHJhdGlvCiAgbl9mYWlsPC1kb21haW5zX3dpbmRvdyAgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIAogIHN1bW1hcmlzZSh0b3RfZmFpbD1zdW0oZ3JlcGwoIlNFUlZGQUlMIixhbnN3ZXIpKSkKICBuX2ZhaWwkcmF0aW9fZmFpbDwtbl9mYWlsJHRvdF9mYWlsL25fcmVxdWVzdHMkdG90X3JlcXVlc3RzCgogICMgVG90YWwgYW1vdW50IG9mIE1YIGFuZCByYXRpbwogIG5fbXg8LWRvbWFpbnNfd2luZG93ICAlPiUgIGdyb3VwX2J5KGNsaWVudCkgICU+JSAKICBzdW1tYXJpc2UodG90X214PXN1bShxdWVyeV90eXBlPT0iTVgiKSkgCiAgbl9teCRyYXRpb19teDwtbl9teCR0b3RfbXgvbl9yZXF1ZXN0cyR0b3RfcmVxdWVzdHMKCiAgCiAgbl9yZXZlcnNlIDwtIGRvbWFpbnNfd2luZG93ICU+JSBncm91cF9ieShjbGllbnQpICU+JQogIHN1bW1hcmlzZSh0b3RfcmV2ZXJzZT1zdW0oZ3JlcGwoImluLWFkZHIuYXJwYS4iLHF1ZXJ5KSkpIAogIG5fcmV2ZXJzZSRyYXRpb19yZXZlcnNlPC1uX3JldmVyc2UkdG90X3JldmVyc2Uvbl9yZXF1ZXN0cyR0b3RfcmVxdWVzdHMKCiAgICAKICAjIGRvbWFpbnMgc2hhcmluZyB0aGUgc2FtZSBJUCAKICBhbGxfc2FtZWlwPC1kb21haW5zX3dpbmRvdyAlPiUgIAogIGZpbHRlcihxdWVyeV90eXBlPT0iQSIgJiBhbnN3ZXJfaXAhPSJOWERPTUFJTiIgJiBhbnN3ZXJfaXAhPSJTRVJWRkFJTCIpICU+JSAgCiAgZ3JvdXBfYnkoY2xpZW50LGFuc3dlcl9pcCkgJT4lIAogIHN1bW1hcmlzZSh0b3RfZGlzdHF1ZXJ5PW5fZGlzdGluY3QocXVlcnkpKSAKCiAgI1RoZSBtYXggKGludCkgYW1vdW50IG9mIHJlcXVlc3RzIHdoZXJlIG9uZSBJUCByZXNvbHZlZCB0byBtb3JlIHRoYW4gb25lIGRvbWFpbi4KICBtYXhfc2FtZWlwPC1hbGxfc2FtZWlwICU+JSBmaWx0ZXIodG90X2Rpc3RxdWVyeSA+MSkgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShtYXhfc2FtZWlwPW1heCh0b3RfZGlzdHF1ZXJ5KSkKCiAgICNUaGUgYXZnICBvZiByZXF1ZXN0cyB3aGVyZSBvbmUgSVAgcmVzb2x2ZWQgdG8gbW9yZSB0aGFuIG9uZSBkb21haW4uCiAgYXZnX3NhbWVpcDwtYWxsX3NhbWVpcCAlPiUgZmlsdGVyKHRvdF9kaXN0cXVlcnkgPjEpICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2UoYXZnX3NhbWVpcD1tZWFuKHRvdF9kaXN0cXVlcnkpKQoKICAjVGhlIGF2ZyAgb2YgcmVxdWVzdHMgd2hlcmUgb25lIElQIHJlc29sdmVkIHRvIG1vcmUgdGhhbiBvbmUgZG9tYWluLgogIHNkX3NhbWVpcDwtYWxsX3NhbWVpcCAlPiUgZmlsdGVyKHRvdF9kaXN0cXVlcnkgPjEpICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2Uoc2Rfc2FtZWlwPXNkKHRvdF9kaXN0cXVlcnkpKQoKICAjVGhlIGFtb3VudCBvZiBncm91cHMgb2Ygc2FtZSBJUHMgdGhhdCByZXNvbHZlZCB0byBtb3JlIHRoYW4gb25lIGRvbWFpbi4KICBuX3NhbWVpcDwtYWxsX3NhbWVpcCAlPiUgZmlsdGVyKHRvdF9kaXN0cXVlcnkgPjEpICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2UodG90X3NhbWVpcD1zdW0odG90X2Rpc3RxdWVyeSkpCiAgCiAgCiAgIyBUaGUgYW1vdW50IG9mIGRvbWFpbnMgdGhhdCBlYWNoIG9mIHRoZW0gaGFkIG1vcmUgdGhhbiAxIElQLgogICMgKEZhc3QgZmx1eCkKICBhbGxfc2FtZWRvbWFpbjwtZG9tYWluc193aW5kb3cgJT4lICAKICBmaWx0ZXIocXVlcnlfdHlwZT09IkEiICYgYW5zd2VyX2lwIT0iTlhET01BSU4iICYgYW5zd2VyX2lwIT0iU0VSVkZBSUwiKSAlPiUgIAogIGdyb3VwX2J5KGNsaWVudCxxdWVyeSkgJT4lIAogIHN1bW1hcmlzZSh0b3RfZGlzdGRvbWFpbj1uX2Rpc3RpbmN0KGFuc3dlcl9pcCkpIAogIAogICNUaGUgbWF4IChpbnQpIGFtb3VudCBvZiByZXF1ZXN0cyB3aGVyZSBvbmUgZG9tYWluIHJlc29sdmVkIHRvIG1vcmUgdGhhbiBvbmUgSVAKICBtYXhfc2FtZWRvbWFpbjwtYWxsX3NhbWVkb21haW4gJT4lIGZpbHRlcih0b3RfZGlzdGRvbWFpbiA+MSkgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShtYXhfc2FtZWRvbWFpbj1tYXgodG90X2Rpc3Rkb21haW4pKQoKICAjVGhlIGF2ZyAgb2YgcmVxdWVzdHMgd2hlcmUgb25lIGRvbWFpbiByZXNvbHZlZCB0byBtb3JlIHRoYW4gb25lIElQCiAgYXZnX3NhbWVkb21haW48LWFsbF9zYW1lZG9tYWluICU+JSBmaWx0ZXIodG90X2Rpc3Rkb21haW4gPjEpICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2UoYXZnX3NhbWVkb21haW49bWVhbih0b3RfZGlzdGRvbWFpbikpCgogICNUaGUgYXZnICBvZiByZXF1ZXN0cyB3aGVyZSBvbmUgZG9tYWluIHJlc29sdmVkIHRvIG1vcmUgdGhhbiBvbmUgSVAKICBzZF9zYW1lZG9tYWluPC1hbGxfc2FtZWRvbWFpbiAlPiUgZmlsdGVyKHRvdF9kaXN0ZG9tYWluID4xKSAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgc3VtbWFyaXNlKHNkX3NhbWVkb21haW49c2QodG90X2Rpc3Rkb21haW4pKQoKICAjVGhlIGFtb3VudCBvZiBncm91cHMgb2Ygc2FtZSBkb21haW4gdGhhdCByZXNvbHZlZCB0byBtb3JlIHRoYW4gb25lIElQCiAgbl9zYW1lZG9tYWluPC1hbGxfc2FtZWRvbWFpbiAlPiUgZmlsdGVyKHRvdF9kaXN0ZG9tYWluID4xKSAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgc3VtbWFyaXNlKHRvdF9zYW1lZG9tYWluPXN1bSh0b3RfZGlzdGRvbWFpbikpCiAgCiAgCiAgIyBQcm9maWxlIERhdGFiYXNlIENyZWF0aW9uIC0tLS0tLQogIAogIGNsaWVudF9wcm9maWxlPC1pbm5lcl9qb2luKG5fcmVxdWVzdHMsbl9kZXRlY3RlZCxieT0iY2xpZW50IikgJT4lIAogIGlubmVyX2pvaW4obl9ueCxieT0iY2xpZW50IikgJT4lCiAgaW5uZXJfam9pbihuX214LGJ5PSJjbGllbnQiKSAlPiUKICBpbm5lcl9qb2luKG5fZmFpbCxieT0iY2xpZW50IikgJT4lCiAgaW5uZXJfam9pbihuX3JldmVyc2UsYnk9ImNsaWVudCIpICU+JQogIGZ1bGxfam9pbihtYXhfc2FtZWlwLGJ5PSJjbGllbnQiKSAlPiUKICBmdWxsX2pvaW4obl9zYW1laXAsYnk9ImNsaWVudCIpICU+JQogIGZ1bGxfam9pbihhdmdfc2FtZWlwLGJ5PSJjbGllbnQiKSAlPiUKICBmdWxsX2pvaW4oc2Rfc2FtZWlwLGJ5PSJjbGllbnQiKSAgJT4lCiAgZnVsbF9qb2luKG5fc2FtZWRvbWFpbixieT0iY2xpZW50IikgJT4lCiAgZnVsbF9qb2luKG1heF9zYW1lZG9tYWluLGJ5PSJjbGllbnQiKSAlPiUKICBmdWxsX2pvaW4oYXZnX3NhbWVkb21haW4sYnk9ImNsaWVudCIpICU+JQogIGZ1bGxfam9pbihzZF9zYW1lZG9tYWluLGJ5PSJjbGllbnQiKSAKICAgIAogIAogICMgcmVwbGFjZSBOQXMgd2l0aCB6ZXJvcwogIGNsaWVudF9wcm9maWxlPC1jbGllbnRfcHJvZmlsZSAlPiUgbXV0YXRlKG1heF9zYW1laXA9aWZlbHNlKGlzLm5hKG1heF9zYW1laXApLDAsbWF4X3NhbWVpcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90X3NhbWVpcD1pZmVsc2UoaXMubmEodG90X3NhbWVpcCksMCx0b3Rfc2FtZWlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfc2FtZWlwPWlmZWxzZShpcy5uYShhdmdfc2FtZWlwKSwwLGF2Z19zYW1laXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkX3NhbWVpcD1pZmVsc2UoaXMubmEoc2Rfc2FtZWlwKSwwLHNkX3NhbWVpcCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICBjbGllbnRfcHJvZmlsZTwtY2xpZW50X3Byb2ZpbGUgJT4lIG11dGF0ZShtYXhfc2FtZWRvbWFpbj1pZmVsc2UoaXMubmEobWF4X3NhbWVkb21haW4pLDAsbWF4X3NhbWVkb21haW4pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdF9zYW1lZG9tYWluPWlmZWxzZShpcy5uYSh0b3Rfc2FtZWRvbWFpbiksMCx0b3Rfc2FtZWRvbWFpbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnX3NhbWVkb21haW49aWZlbHNlKGlzLm5hKGF2Z19zYW1lZG9tYWluKSwwLGF2Z19zYW1lZG9tYWluKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZF9zYW1lZG9tYWluPWlmZWxzZShpcy5uYShzZF9zYW1lZG9tYWluKSwwLHNkX3NhbWVkb21haW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogIAogIGNsaWVudF9wcm9maWxlPC1jbGllbnRfcHJvZmlsZSAlPiUgbXV0YXRlKHJhdGlvX3RvdF9zYW1lZG9tYWluPXRvdF9zYW1lZG9tYWluL3RvdF9yZXF1ZXN0cykgCiAgY2xpZW50X3Byb2ZpbGU8LWNsaWVudF9wcm9maWxlICU+JSBtdXRhdGUocmF0aW9fbWF4X3NhbWVkb21haW49bWF4X3NhbWVkb21haW4vdG90X3JlcXVlc3RzKSAKICBjbGllbnRfcHJvZmlsZTwtY2xpZW50X3Byb2ZpbGUgJT4lIG11dGF0ZShyYXRpb19hdmdfc2FtZWRvbWFpbj1hdmdfc2FtZWRvbWFpbi90b3RfcmVxdWVzdHMpIAogIGNsaWVudF9wcm9maWxlPC1jbGllbnRfcHJvZmlsZSAlPiUgbXV0YXRlKHJhdGlvX3NkX3NhbWVkb21haW49c2Rfc2FtZWRvbWFpbi90b3RfcmVxdWVzdHMpIAogIAogIGNsaWVudF9wcm9maWxlPC1jbGllbnRfcHJvZmlsZSAlPiUgbXV0YXRlKHJhdGlvX3RvdF9zYW1laXA9dG90X3NhbWVpcC90b3RfcmVxdWVzdHMpIAogICBjbGllbnRfcHJvZmlsZTwtY2xpZW50X3Byb2ZpbGUgJT4lIG11dGF0ZShyYXRpb19tYXhfc2FtZWlwPW1heF9zYW1laXAvdG90X3JlcXVlc3RzKSAKICAgY2xpZW50X3Byb2ZpbGU8LWNsaWVudF9wcm9maWxlICU+JSBtdXRhdGUocmF0aW9fYXZnX3NhbWVpcD1hdmdfc2FtZWlwL3RvdF9yZXF1ZXN0cykgCiAgIGNsaWVudF9wcm9maWxlPC1jbGllbnRfcHJvZmlsZSAlPiUgbXV0YXRlKHJhdGlvX3NkX3NhbWVpcD1zZF9zYW1laXAvdG90X3JlcXVlc3RzKSAKIAogIAogIAogIAogICBjbGllbnRfcHJvZmlsZSRwcm9maWxlbnVtPC1wcm9maWxlbnVtCiAgY2xpZW50X3Byb2ZpbGUkY3VycmVudHRtPC1jdXJyZW50dG0KICAjY2xpZW50c19wcm9maWxlczwtcmJpbmQoY2xpZW50c19wcm9maWxlcyxjbGllbnRfcHJvZmlsZSkKICAjY3VycmVudHRtPC1jdXJyZW50dG0gLSBtaW51dGVzKHdpbmRvd3NpemUpCiAgCiAgcmV0dXJuKGNsaWVudF9wcm9maWxlKQp9CnN1bW1hcnkoY2xpZW50c19wcm9maWxlcykKYGBgCgoKYGBge3Igc2F2ZS1jc3YsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnJlYWRyOjp3cml0ZV9jc3YoY2xpZW50c19wcm9maWxlcywiLi4vZGF0YS93Ym9uZS1jbGllbnQtcHJvZmlsZS0xaC1sYXN0OGRheXMuY3N2IikKY2xpZW50c19wcm9maWxlcwpgYGAKCmBgYHtyIHJlYWQtY3N2LCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpjbGllbnRzX3Byb2ZpbGVzPC1yZWFkcjo6cmVhZF9jc3YoIi4uL2RhdGEvd2JvbmUtY2xpZW50LXByb2ZpbGUtMjRocy1sYXN0M3dlZWtzLmNzdiIpCmNsaWVudHNfcHJvZmlsZXMgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShub2Zwcm9maWxlcz1uX2Rpc3RpbmN0KHByb2ZpbGVudW0pKQpjbGllbnRzX3Byb2ZpbGVzICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2UodG90X3JlcXVlc3RzPXN1bSh0b3RfcmVxdWVzdHMpKQpjbGllbnRzX3Byb2ZpbGVzICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2Uobj1uKCkpCmRvbWFpbnMgJT4lIGZpbHRlcihjbGllbnQ9PSIxLjIwMi4yMzYuMTM2IikKY2xpZW50c19wcm9maWxlcyAlPiUgZmlsdGVyKGNsaWVudD09IjEuMjAyLjIzNi4xMzYiKQpgYGAKCgpgYGB7ciBwbG90LXRtLXByb2ZpbGVzLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKZ2dwbG90KGNsaWVudHNfcHJvZmlsZXMpKwogIGdlb21fbGluZShhZXMoeD1jdXJyZW50dG0seT1yYXRpb19kZXRlY3RlZCksY29sb3I9J3JlZCcpKwogIGdlb21fbGluZShhZXMoeD1jdXJyZW50dG0seT1yYXRpb19ueCksY29sb3I9J2JsdWUnKSsKICBnZW9tX2xpbmUoYWVzKHg9Y3VycmVudHRtLHk9cmF0aW9fbXgpLGNvbG9yPSdvcmFuZ2UnKSsKICBnZW9tX2xpbmUoYWVzKHg9Y3VycmVudHRtLHk9cmF0aW9fZmFpbCksY29sb3I9J3NreWJsdWUnKSsKICBnZW9tX2xpbmUoYWVzKHg9Y3VycmVudHRtLHk9cmF0aW9fdG90X3NhbWVpcCksY29sb3I9J2dyZWVuJykrCiAgeGxhYigiRGF0ZXRpbWUiKSt5bGFiKCJ2YWx1ZSIpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkrCiAgZmFjZXRfd3JhcCh+Y2xpZW50LHNjYWxlcyA9ICJmcmVlX3kiKSsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLnRleHQueD0gZWxlbWVudF9ibGFuaygpLHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTUpCiAgICAgICAgKQpgYGAKCgojIyBTZWxlY3RpbmcgZmVhdHVyZXMKCmBgYHtyIHNlbGVjdGluZyBmZWF0dXJlc30KY2xpZW50X3Byb2ZpbGVfc2VsZWN0ZWRfZmVhdHVyZXM8LWNsaWVudHNfcHJvZmlsZXMgICAlPiUgbXV0YXRlKHJhdGlvX3JlcXVlc3RzPXRvdF9yZXF1ZXN0cy9tYXgodG90X3JlcXVlc3RzKSkgJT4lCiAgI3NlbGVjdChjbGllbnQsdG90X3JlcXVlc3RzLCB0b3RfZGV0ZWN0ZWQsdG90X254LHRvdF9teCx0b3RfZmFpbCx0b3RfcmV2ZXJzZSx0b3Rfc2FtZWRvbWFpbix0b3Rfc2FtZWlwKQogc2VsZWN0KGNsaWVudCx0b3RfcmVxdWVzdHMscmF0aW9fcmVxdWVzdHMscmF0aW9fZGV0ZWN0ZWQscmF0aW9fbngscmF0aW9fbXgscmF0aW9fZmFpbCxyYXRpb19yZXZlcnNlLAogICAgICAgIHJhdGlvX3RvdF9zYW1lZG9tYWluLHJhdGlvX3RvdF9zYW1laXAsCiAgICAgICAgcmF0aW9fYXZnX3NhbWVkb21haW4scmF0aW9fYXZnX3NhbWVpcCwKICAgICAgICByYXRpb19zZF9zYW1lZG9tYWluLHJhdGlvX3NkX3NhbWVpcCkKY2xpZW50X3Byb2ZpbGVfcmF0aW88LXVuaXF1ZShjbGllbnRfcHJvZmlsZV9yYXRpbykKICAjc2VsZWN0KGNsaWVudCx0b3RfcmVxdWVzdHMsdG90X2RldGVjdGVkLG1heF9zYW1laXAsYXZnX3NhbWVpcCxzZF9zYW1laXAsbWF4X3NhbWVkb21haW4sYXZnX3NhbWVkb21haW4sbWF4X3NhbWVkb21haW4scmF0aW9fbngpCmNsaWVudF9wcm9maWxlX3NlbGVjdGVkX2ZlYXR1cmVzCmBgYAoKIyMgTWF0cml4IENvcnJlbGF0aW9uIGhlYXRtYXAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KbGlicmFyeShkM2hlYXRtYXApCmNvcnJfY2xpZW50X3Byb2ZpbGVfc2VsZWN0ZWRfZmVhdHVyZXM8LWNvcihjbGllbnRfcHJvZmlsZV9zZWxlY3RlZF9mZWF0dXJlc1ssYygtMSwtMildLHVzZT0iY29tcGxldGUub2JzIikKZDNoZWF0bWFwKGNvcnJfY2xpZW50X3Byb2ZpbGVfc2VsZWN0ZWRfZmVhdHVyZXMsc3ltbT1ULGNvbG9ycyA9ICJSZWRzIixSb3d2PVQsCiAgICAgICAgICB3aWR0aCA9IDYwMCxoZWlnaHQgPSA0MDAsIAogICAgICAgICAgeWF4aXNfZm9udF9zaXplID0gIjhwdCIsCiAgICAgICAgICB4YXhpc19mb250X3NpemUgPSAiOHB0IikKYGBgCiMjIFBlciBGZWF0dXJlIFNjYXR0ZXIgcGxvdAoKYGBge3IsIGZpZy5oZWlnaHQ9MTJ9CnBhaXJzKGNsaWVudF9wcm9maWxlX3NlbGVjdGVkX2ZlYXR1cmVzWyxjKC0xLC0yKV0sY2V4LmxhYmVscyA9IDEuNikKYGBgCgpgYGB7cn0KZ2dwbG90KGNsaWVudF9wcm9maWxlX3JhdGlvLGFlcyhyYXRpb19ueCxyYXRpb19kZXRlY3RlZCkpKwogIGdlb21fcG9pbnQoYWVzKGFscGhhPXRvdF9yZXF1ZXN0cyksY29sb3I9J3NreWJsdWUnKSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLGNvbG9yPSdvcmFuZ2UnKSsKICB0aGVtZV9idygpCmBgYApgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyMgQ2xpZW50IGZlYXR1cmVzIGhlYXRtYXAKIyMgKG9ubHkgY29uc2lkZXJpbmcgcmF0aW9zKQojY2xpZW50X3Byb2ZpbGVfcmF0aW88LWNsaWVudF9wcm9maWxlX3JhdGlvICU+JSBtdXRhdGUoaWQ9YXMubnVtZXJpYyhhcy5mYWN0b3IoY2xpZW50KSkpCgpkM2hlYXRtYXAodChjbGllbnRfcHJvZmlsZV9yYXRpb1ssLTFdKSxzeW1tPUYsY29sb3JzID0gIlJlZHMiLFJvd3Y9RiwKICAgICAgICAgICNsYWJDb2wgPSB0KGNsaWVudF9wcm9maWxlX3JhdGlvWywxXSksCiAgICAgICAgICB3aWR0aCA9IDEwNDAsCiAgICAgICAgICBoZWlnaHQgPSAyNTAsIAogICAgICAgICAgeWF4aXNfZm9udF9zaXplID0gIjhwdCIsCiAgICAgICAgICB4YXhpc19mb250X3NpemUgPSAiMHB0IixhbmltX2R1cmF0aW9uID0gMCkKYGBgCgojIyBLbWVhbnMgY2x1c3RlcmluZwoKYGBge3J9CgprbWVhbnNfbW9kZWw8LWttZWFucyhjbGllbnRfcHJvZmlsZV9zZWxlY3RlZF9mZWF0dXJlc1ssYygtMSwtMildLGNlbnRlcnM9NSxuc3RhcnQ9NDApCmNsaWVudF9wcm9maWxlX3JhdGlvPC1jYmluZChjbGllbnRfcHJvZmlsZV9zZWxlY3RlZF9mZWF0dXJlcyxjbHVzdGVyPWFzLmZhY3RvcihrbWVhbnNfbW9kZWwkY2x1c3RlcikpCmNsaWVudF9wcm9maWxlX3JhdGlvICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgc3VtbWFyaXNlKG49bigpKQpzYXZlKGttZWFuc19tb2RlbCxmaWxlPSIuLi9tb2RlbHMva21lYW5zX21vZGVsXzFoX2xhc3Q4ZGF5cy5SZGF0YSIpCmBgYAoKCiMjIFNvbWUgYmFzaWMgcGVyIGNsdXN0ZXIgaW5mb3JtYXRpb24KCmBgYHtyfQpjbHVzdGVyX3N1bW1hcnk8LXVuaXF1ZShjbGllbnRfcHJvZmlsZV9yYXRpb1ssYygtMildKSAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHN1bW1hcmlzZShuPW4oKSxueD1tZWFuKHJhdGlvX254KSxteD1tZWFuKHJhdGlvX214KSxzYW1laXA9bWVhbihyYXRpb190b3Rfc2FtZWlwKSxzYW1lZG9tYWluPW1lYW4ocmF0aW9fdG90X3NhbWVkb21haW4pLHJldmVyc2U9bWVhbihyYXRpb19yZXZlcnNlKSxkZXRlY3RlZD1tZWFuKHJhdGlvX2RldGVjdGVkKSxyZXF1ZXN0cz1tZWFuKHJhdGlvX3JlcXVlc3RzKSxmYWlsPW1lYW4ocmF0aW9fZmFpbCksIGlwZGlzdD1uX2Rpc3RpbmN0KGNsaWVudCkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCmNsdXN0ZXJfc3VtbWFyeQoKZDNoZWF0bWFwKGNsdXN0ZXJfc3VtbWFyeVssYygtMSwtMiwtMTEpXSxjb2xvcnMgPSAiUmVkcyIsUm93dj1ULAogICAgICAgICAgd2lkdGggPSA3MDAsaGVpZ2h0ID0gNDAwLCAKICAgICAgICAgIHlheGlzX2ZvbnRfc2l6ZSA9ICI4cHQiLAogICAgICAgICAgeGF4aXNfZm9udF9zaXplID0gIjhwdCIpCmBgYAoKIyMgQ2x1c3RlcmluZyBSZXN1bHRzIApBY2NvcmRpbmcgdG8gY2x1c3RlcmluZyByZXN1bHRzCgoxLiAqKkNsdXN0ZXIgbi4gMSoqIGdyb3VwcyBjbGllbnRzIHdpdGggaGlnaCBOWCByZXBsaWVzIGFzIHdlbGwgYXMgc29tZSByZXZlcnNlIHF1ZXJpZXMgYW1vbmcgb3RoZXJzLiAqKihwb3NzaWJsZSBER0EpKioKMi4gKipDbHVzdGVyIG4uIDIqKiBncm91cHMgdGhvc2UgY2xpZW50cyBub3Qgc2hvd2luZyBER0Egb3Igb3RoZXIgbWFsd2F2ZXIgYmVoYXZpb3IgKiooUG9zc2libGUgTk9STUFMPz8pKioKMy4gKipDbHVzdGVyIG4uIDMqKiBncm91cHMgdGhvc2UgY2xpZW50cyB3aXRoIHRoZSBoaWdoZXN0ICpzYW1lZG9tYWlucyogdmFsdWVzICoqKHBvc3NpYmxlIEZhc3RmbHV4PykqKgo0LiAqKkNsdXN0ZXIgbi4gNCoqIGdyb3VwcyB0aG9zZSBjbGllbnRzIHdpdGggYSB2YWx1ZSBpbmNyZW1lbnQgIGZvciAqc2FtZWlwKiBhbmQgKnNhbWVkb21haW5zKiAqKihQb3NzaWJsZSBER0EvRmFzdGZsdXg/IE5vdCByZWFsbHkpKioKNS4gKipDbHVzdGVyIG4uIDUqKiBncm91cHMgdGhvc2UgY2xpZW50cyB3aXRoIGhpZ2ggdmFsdWVzIGluIEZBSUwgcXVlcmllcyAqKihQb3NzaWJsZSB3aGF0Pz8pKioKCipzb21lIElQUyB0byBjaGVjayoKCioyMTMuMTkxLjEwNS4yMTAqIHNob3dzIHNvbWUgcG9zc2libGUgcmFuZG9tIGdlbmVyYXRlZCBkb21haW5zCgoqMjEzLjE5MS4xMDUuMjQyKiBTaG93cyBhIGxvdCBvZiBOWCByZWNvcmRzLiBTb21lIHF1ZXJpZXMgY29udGFpbiB0aGUga2suIFRMRCAoPz8pCgojIyBULU5TRSAyRCByZXByZXNlbnRhdGlvbiB3aXRoIGNsdXN0ZXJzIAoKKipMaW1pdGF0aW9ucyBvZiBQQ0EqKgoKUENBIGlzIGEgbGluZWFyIGFsZ29yaXRobS4gSXQgd2lsbCBub3QgYmUgYWJsZSB0byBpbnRlcnByZXQgY29tcGxleCBwb2x5bm9taWFsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZlYXR1cmVzLiBPbiB0aGUgb3RoZXIgaGFuZCwgdC1TTkUgaXMgYmFzZWQgb24gcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9ucyB3aXRoIHJhbmRvbSB3YWxrIG9uIG5laWdoYm9yaG9vZCBncmFwaHMgdG8gZmluZCB0aGUgc3RydWN0dXJlIHdpdGhpbiB0aGUgZGF0YS4KCgoKCgpgYGB7cn0KdHNuZV9tb2RlbDwtUnRzbmUodW5pcXVlKGNsaWVudF9wcm9maWxlX3JhdGlvWyxjKC0yLC1uY29sKGNsaWVudF9wcm9maWxlX3JhdGlvKSldKSxwY2E9RkFMU0UsIHBjYV9jZW50ZXIgPVRSVUUsIHBjYV9zY2FsZT1UUlVFLGNoZWNrX2R1cGxpY2F0ZXM9RiwKICAgICAgICAgICAgICAgICAgcGVycGxleGl0eSA9IDQwMCwgbWF4X2l0ZXIgPSAxMDAwKQpgYGAKCgpUbyByZW1lbWJlcjogSXQgc2VlbXMgdGhhdCBpZiB3ZSBnZW5lcmF0ZSAxaCBwcm9maWxlcyB0aGUgdGhlIDJEIHJlcHJlc2VudGF0aW9uIHRlbmRzIHRvIGJlIG1vcmUgZGlmZmljdWx0IHRvIGFuYWx5emUuIFRoZSBhcmUgbWFueSBwb2ludHMgdmVyeSBzaW1pbGFyIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHNhbWUgY2xpZW50IHByb2ZpbGUgYXQgZGlmZmVyZW50IHRpbWUgcGVyaW9kcy4gVXNpbmcgYSBoaWdoZXIgcGVycGxleGl0eSB2YWx1ZSBzZWVtcyB0byBzb2x2ZSB0aGF0IGlzc3VlLiAKCkVhY2ggcG9pbnQgcmVwcmVzZW50cyBhIDEgaG91ciBwcm9maWxlLiBUaGUgKipzaXplKiogb2YgYSBnaXZlbiBwb2ludC9wcm9maWxlIGNvcnJlc3BvbmRzIHRvIHRoZSByZXF1ZXN0IHJhdGlvcyBmb3IgdGhhdCBwcm9maWxlLiBGaW5hbGx5LCAgKipjb2xvcioqIHByZXNlbnRzIGRpZmZlcmVudHMgY2x1c3RlcnMuClJlbWVtYmVyIHlvdSBjYW4gc2VsZWN0L2Rlc2VsZWN0IHRoZSBjbHVzdGVyIGJ5IGNsaWNraW5nIHRoZSBsZWdlbmQuIAoKYGBge3IgcGxvdCB0bnNlIGNsdXN0ZXJpbmcsIHdhcm5pbmc9RkFMU0V9CmNsaWVudF9wcm9maWxlX3VuaXF1ZTwtdW5pcXVlKGNsaWVudF9wcm9maWxlX3JhdGlvWyxjKC0yKV0pCnRzbmVfY2xpZW50X3Byb2ZpbGU8LWRhdGEuZnJhbWUodHNuZV9tb2RlbCRZLGNsdXN0ZXI9Y2xpZW50X3Byb2ZpbGVfdW5pcXVlJGNsdXN0ZXIsY2xpZW50PWNsaWVudF9wcm9maWxlX3VuaXF1ZSRjbGllbnQscmVxdWVzdHM9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGllbnRfcHJvZmlsZV91bmlxdWUkcmF0aW9fcmVxdWVzdHMpCgpzYW1wbGVpZDwtc2FtcGxlKG5yb3codHNuZV9jbGllbnRfcHJvZmlsZSksbnJvdyh0c25lX2NsaWVudF9wcm9maWxlKSkKCmc8LWdncGxvdCh0c25lX2NsaWVudF9wcm9maWxlW3NhbXBsZWlkLF0sYWVzKHg9WDEseT1YMikpKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFzLmZhY3RvcihjbHVzdGVyKSxzaXplPXJlcXVlc3RzLHRleHQ9cGFzdGUoImlwZGRyIixjbGllbnQpKSkrCiAgI2dlb21fcG9pbnQoYWVzKHNoYXBlPWFzaWduYWNpb24pLHNpemU9MykrCiAgeWxhYigiWDEiKSt4bGFiKCJYMiIpKwogIHRoZW1lX2NsYXNzaWMoKSsKI3NjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Yyg4LDYpKSsKICAgZ3VpZGVzKGNvbG9yPUZBTFNFLGFscGhhPUZBTFNFKQpnZ3Bsb3RseShnKQoKYGBgCgojIyBEZWNpc2lvbiBUcmVlIEFuYWx5c2lzCgpgYGB7ciBkdHJlZSBhbmFsaXN5cywgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC51dGlscykKCmZvcm11bGEgPC0gYXMuZm9ybXVsYShjbHVzdGVyfi4pCnRyZWU9cnBhcnQoZm9ybXVsYSxkYXRhPWNsaWVudF9wcm9maWxlX3JhdGlvWyxjKC0xLC0yLC0zLC00KV0sY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQ9NTAwLCBjcD0wLjAwNSx4dmFsPTEwKSxtb2RlbD1ULHg9VCx5PVQpCnJwYXJ0LnBsb3QodHJlZSwKICAgICAgICAgICBleHRyYT00LCAKICAgICAgICAgICBib3gucGFsZXR0ZT0iR25CdSIsCiAgICAgICAgICAgYnJhbmNoLmx0eT01LCBzaGFkb3cuY29sPTAsIG5uPVRSVUUsIGNleCA9MC45LHVuZGVyPVQKICAgICAgICApCnByaW50Y3AodHJlZSkKYGBgCgoKIyMgQ2xpZW50cyBpbiBjbHVzdGVyIDEKCmBgYHtyfQp0c25lX2NsaWVudF9wcm9maWxlICU+JSBmaWx0ZXIoY2x1c3Rlcj09MSkgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShwcm9maWxlcz1uKCkpICU+JSBhcnJhbmdlKGRlc2MocHJvZmlsZXMpKSAlPiUgZmlsdGVyKHByb2ZpbGVzPjAgKSAgJT4lIHNlbGVjdChjbGllbnQpCmBgYAoKIyMgQ2xpZW50cyBpbiBjbHVzdGVyIDIKCmBgYHtyfQp0c25lX2NsaWVudF9wcm9maWxlICU+JSBmaWx0ZXIoY2x1c3Rlcj09MikgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShwcm9maWxlcz1uKCkpICU+JSBhcnJhbmdlKGRlc2MocHJvZmlsZXMpKSAlPiUgZmlsdGVyKHByb2ZpbGVzPjApICAlPiUgc2VsZWN0KGNsaWVudCkKYGBgCgojIyBDbGllbnRzIGluIGNsdXN0ZXIgMwoKYGBge3J9CnRzbmVfY2xpZW50X3Byb2ZpbGUgJT4lIGZpbHRlcihjbHVzdGVyPT0zKSAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgc3VtbWFyaXNlKHByb2ZpbGVzPW4oKSkgJT4lIGFycmFuZ2UoZGVzYyhwcm9maWxlcykpICU+JSBmaWx0ZXIocHJvZmlsZXM+MCkgICU+JSBzZWxlY3QoY2xpZW50KQpgYGAKIyMgQ2xpZW50cyBpbiBjbHVzdGVyIDQKCmBgYHtyfQp0c25lX2NsaWVudF9wcm9maWxlICU+JSBmaWx0ZXIoY2x1c3Rlcj09NCkgJT4lIGdyb3VwX2J5KGNsaWVudCkgJT4lIHN1bW1hcmlzZShwcm9maWxlcz1uKCkpICU+JSBhcnJhbmdlKGRlc2MocHJvZmlsZXMpKSAlPiUgZmlsdGVyKHByb2ZpbGVzPjApICAlPiUgc2VsZWN0KGNsaWVudCkKYGBgCgojIyBDbGllbnRzIGluIGNsdXN0ZXIgNQoKYGBge3J9CnRzbmVfY2xpZW50X3Byb2ZpbGUgJT4lIGZpbHRlcihjbHVzdGVyPT01KSAlPiUgZ3JvdXBfYnkoY2xpZW50KSAlPiUgc3VtbWFyaXNlKHByb2ZpbGVzPW4oKSkgJT4lIGFycmFuZ2UoZGVzYyhwcm9maWxlcykpICU+JSBmaWx0ZXIocHJvZmlsZXM+MCkgICU+JSBzZWxlY3QoY2xpZW50KQpgYGAKCiMjIENsaWVudHMgaW4gY2x1c3RlciA2CgpgYGB7cn0KdHNuZV9jbGllbnRfcHJvZmlsZSAlPiUgZmlsdGVyKGNsdXN0ZXI9PTYpICU+JSBncm91cF9ieShjbGllbnQpICU+JSBzdW1tYXJpc2UocHJvZmlsZXM9bigpKSAlPiUgYXJyYW5nZShkZXNjKHByb2ZpbGVzKSkgJT4lIGZpbHRlcihwcm9maWxlcz4wKSAlPiUgc2VsZWN0KGNsaWVudCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyMgUENBIDJEIHJlcHJlc2VudGF0aW9uIAoKbGlicmFyeShwbG90bHkpCnBjYTwtcHJjb21wKGNsaWVudF9wcm9maWxlX3JhdGlvWyxjKC0xLC0yLC0zLC1uY29sKGNsaWVudF9wcm9maWxlX3JhdGlvKSldLCBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFKSAKCnBjYV9jbGllbnRfcHJvZmlsZTwtZGF0YS5mcmFtZShwY2EkeCxjbGllbnQ9Y2xpZW50X3Byb2ZpbGVfcmF0aW8kY2xpZW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGV0ZWN0ZWQ9aWZlbHNlKGNsaWVudF9wcm9maWxlX3JhdGlvJHJhdGlvX2RldGVjdGVkPjAsImRldGVjdGVkIiwibm90ZGV0ZWN0ZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGlvX3JlcXVlc3RlZD1jbGllbnRfcHJvZmlsZV9yYXRpbyRyYXRpb19yZXF1ZXN0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3RlZD1jbGllbnRfcHJvZmlsZV9yYXRpbyR0b3RfcmVxdWVzdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyPWNsaWVudF9wcm9maWxlX3JhdGlvJGNsdXN0ZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCgpnPC1nZ3Bsb3QocGNhX2NsaWVudF9wcm9maWxlLGFlcyh4PVBDMyx5PVBDMikpKwogIGdlb21faml0dGVyKGFlcyhjb2xvcj1hcy5mYWN0b3IoY2x1c3RlciksdGV4dD1wYXN0ZShjbGllbnQsIjxCUj4iLHJlcXVlc3RlZCkpKSsKICAjZ2VvbV9wb2ludChhZXMoc2hhcGU9YXNpZ25hY2lvbiksc2l6ZT0zKSsKICB5bGFiKCJQQzEiKSt4bGFiKCJQQzIiKSsKICB0aGVtZV9jbGFzc2ljKCkrCiNzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoOCw2KSkrCiAgIGd1aWRlcyhjb2xvcj1GQUxTRSxhbHBoYT1GQUxTRSkKZwoKZ2dwbG90bHkoZykKYGBgCgoK