#Ranking usando Markov
library(rvest)
library(tidyverse)
library(stringr)
library(parallel)
Función para hacer el scan de wikipedia
dataframlinks <- function(url){
print(url)
read_html(url) %>%
html_nodes("a") %>%
html_attr('href') %>%
data_frame() %>%
rename(links='.') %>%
filter(str_detect(links,"/wiki/")) %>%
filter(!str_detect(links,"#")) %>%
filter(!str_detect(links,"image")) %>%
filter(!str_detect(tolower(links),"file"))%>%
filter(!str_detect(links,"https:")) %>%
filter(!str_detect(links,"//")) %>%
filter(!str_detect(links,":"))%>% return()
}
Función para contruir la matriz de relaciones
buildmatrix <- function(vlist, vmaxrows){
mat<-matrix(0, nrow = maxrows,ncol = maxrows)
for(i in 1:vmaxrows){
textid=vlist[[i]]$link
for(j in 1:vmaxrows){
mat[i,j]=0
content=vlist[[j]]$content
total=nrow(content%>%filter(str_detect(links,textid)))
if(total>0 && i!=j){
mat[i,j]=1
}
}
print(paste("i:",i,'/',vmaxrows,Sys.time()))
}
return(mat)
}
Función para contruir la matriz base de markov Esta matriz contiene el artefacto para conectar closters
buildmarkovmatrix <- function(mat, vmaxrows){
m_scluster=matrix(data = rep(.2, vmaxrows*vmaxrows), nrow = vmaxrows, ncol = vmaxrows)
matoper=mat*.85+m_scluster*.15
for(i in 1:vmaxrows) {
# i-th element of `u1` squared into `i`-th position of `usq`
matoper[i,] <- matoper[i,] / sum(matoper[i,])
print(c(matoper[i,],i,sum(matoper[i,])))
}
return(matoper)
}
Funcion de multiplicacion de matrices en paralelo
matprod.par <- function(cl, A, B){
if (ncol(A) != nrow(B)) stop("Matrices do not conforme")
idx <- splitIndices(nrow(A), length(cl))
Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
## ans <- clusterApply(cl, Alist, function(aa, B) aa %*% B, B)
## Same as above, but faster:
ans <- clusterApply(cl, Alist, get("%*%"), B)
do.call(rbind, ans)
}
Función de multiplicacion de matrices en paralelo
matprod.par <- function(cl, A, B){
if (ncol(A) != nrow(B)) stop("Matrices do not conforme")
idx <- splitIndices(nrow(A), length(cl))
Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
## ans <- clusterApply(cl, Alist, function(aa, B) aa %*% B, B)
## Same as above, but faster:
ans <- clusterApply(cl, Alist, get("%*%"), B)
do.call(rbind, ans)
}
Función Para asignar el Ranking
assignScore <- function(mat,dataset ,vmaxrows){
for(i in 1:vmaxrows){
dataset$ranking[i]= mat[i,i]
}
return(dataset)
}
Funcion para listar el ranking de las artistas
mostrar_artistas_ranking <- function(textsearch,dataset){
filteredtable<-dataset %>%
filter(str_detect(links, regex(textsearch, ignore_case = T)))
filteredtable<-filteredtable[order(filteredtable$ranking),c(1,2)]
return(filteredtable)
}
Test de funcion para obtener el data frame de urls
glimpse( test_links)
Observations: 1,954
Variables: 1
$ links <chr> "/wiki/List_of_American_television_actresses", "/wiki/Beverly_Aadland", "/wiki/Mariann_Aalda", "/wiki/Caroline_Aaron", "/wiki/Diahnne_...
Proyecto: paso 1. leer el root del directorio de actresses
Listof_links<-dataframlinks("https://en.wikipedia.org/wiki/List_of_American_film_actresses")
glimpse( Listof_links)
paso 2. Contruir la lista de objetos con su respectivo URK para cada una de las actrices
paso 3. Contruir el arreglo de referencias de cada una de las actrices si el bucle da error de comunicacion reintentar nuevamente desde el for hasta completar la lista Nota: el campo prapared$processed se volvera verdadero una vez el URL request se haya realizado
paso 4. Se procede a contruir la matriz de markov paso 4.1. se crea la matriz de relaciones paso 4.2. se construye la matriz de markov con el la inclusion de saltos por cluster paso 4.2 se procesa en paralelo la matriz de markov
mat<-c()
mat<-buildmatrix(veclist,maxrows)
markov<-buildmarkovmatrix(mat,maxrows)
(nc <- detectCores())
cl <- makeCluster(rep("localhost", nc))
matpown=matprod.par(cl,markov,markov)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
matpown=matprod.par(cl,matpown,matpown)
stopCluster(cl)
Matriz de relaciones
glimpse(mat , ... = )
num [1:1954, 1:1954] 0 0 0 0 0 0 0 0 0 1 ...
Matriz de markov
glimpse(markov )
num [1:1954, 1:1954] 0.000471 0.00049 0.000497 0.000401 0.000497 ...
Matriz con potencia a n=12
glimpse(matpown )
num [1:1954, 1:1954] 0.00298 0.00298 0.00298 0.00298 0.00298 ...
Asignacion de los score basados en el producto de matrices scoredlist<-assignScore(matpown,prapared,maxrows)
Mostrar el ranking de las artistas basados en texto
- jen
respuesta<-mostrar_artistas_ranking(“jen”,scoredlist)
respuesta<-mostrar_artistas_ranking("jen",scoredlist)
respuesta
- boy respuesta<-mostrar_artistas_ranking(“boy”,scoredlist)
respuesta<-mostrar_artistas_ranking("boy",scoredlist)
respuesta
- Bell respuesta<-mostrar_artistas_ranking(“Bell”,scoredlist)
respuesta<-mostrar_artistas_ranking("Bell",scoredlist)
respuesta
LS0tDQp0aXRsZTogIlJhbmtpbmcgaGFjaWVuZG8gdXNvIGRlIENhZGVuYXMgZGUgbWFya292IHByb3llY3RvIGVzdGFkaXN0aWNhIDIsIEJ5cm9uIEhlcm5hbmRleiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojUmFua2luZyB1c2FuZG8gTWFya292DQoNCg0KYGBge3J9DQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHBhcmFsbGVsKQ0KDQpgYGANCg0KRnVuY2nDs24gcGFyYSBoYWNlciBlbCBzY2FuIGRlIHdpa2lwZWRpYQ0KYGBge3J9DQpkYXRhZnJhbWxpbmtzIDwtIGZ1bmN0aW9uKHVybCl7DQogIHByaW50KHVybCkgDQogICAgcmVhZF9odG1sKHVybCkgJT4lIA0KICAgIGh0bWxfbm9kZXMoImEiKSAlPiUgDQogICAgaHRtbF9hdHRyKCdocmVmJykgJT4lIA0KICAgIGRhdGFfZnJhbWUoKSAlPiUNCiAgICByZW5hbWUobGlua3M9Jy4nKSAlPiUgDQogICAgZmlsdGVyKHN0cl9kZXRlY3QobGlua3MsIi93aWtpLyIpKSAlPiUNCiAgICBmaWx0ZXIoIXN0cl9kZXRlY3QobGlua3MsIiMiKSkgJT4lDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmtzLCJpbWFnZSIpKSAlPiUgDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KHRvbG93ZXIobGlua3MpLCJmaWxlIikpJT4lDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmtzLCJodHRwczoiKSkgJT4lIA0KICAgIGZpbHRlcighc3RyX2RldGVjdChsaW5rcywiLy8iKSkgJT4lIA0KICAgIGZpbHRlcighc3RyX2RldGVjdChsaW5rcywiOiIpKSU+JSByZXR1cm4oKQ0KICANCiAgDQogICANCn0NCmBgYA0KRnVuY2nDs24gcGFyYSBjb250cnVpciBsYSBtYXRyaXogZGUgcmVsYWNpb25lcyANCmBgYHtyfQ0KYnVpbGRtYXRyaXggPC0gZnVuY3Rpb24odmxpc3QsIHZtYXhyb3dzKXsNCiAgbWF0PC1tYXRyaXgoMCwgbnJvdyA9IG1heHJvd3MsbmNvbCA9IG1heHJvd3MpDQogIA0KICBmb3IoaSBpbiAxOnZtYXhyb3dzKXsNCiAgICB0ZXh0aWQ9dmxpc3RbW2ldXSRsaW5rDQogICAgDQogICAgZm9yKGogaW4gMTp2bWF4cm93cyl7DQogICAgICANCiAgICAgIG1hdFtpLGpdPTANCiAgICAgIGNvbnRlbnQ9dmxpc3RbW2pdXSRjb250ZW50DQogICAgICB0b3RhbD1ucm93KGNvbnRlbnQlPiVmaWx0ZXIoc3RyX2RldGVjdChsaW5rcyx0ZXh0aWQpKSkNCiAgICAgIGlmKHRvdGFsPjAgJiYgaSE9ail7DQogICAgICAgIG1hdFtpLGpdPTEgDQoNCiAgICAgIH0NCiAgICB9DQogICAgcHJpbnQocGFzdGUoImk6IixpLCcvJyx2bWF4cm93cyxTeXMudGltZSgpKSkNCg0KDQogICAgDQogICAgDQogIH0gIA0KICANCg0KICByZXR1cm4obWF0KQ0KfQ0KYGBgDQpGdW5jacOzbiBwYXJhIGNvbnRydWlyIGxhIG1hdHJpeiBiYXNlIGRlIG1hcmtvdg0KRXN0YSBtYXRyaXogY29udGllbmUgZWwgYXJ0ZWZhY3RvIHBhcmEgY29uZWN0YXIgY2xvc3RlcnMNCmBgYHtyfQ0KYnVpbGRtYXJrb3ZtYXRyaXggPC0gZnVuY3Rpb24obWF0LCB2bWF4cm93cyl7DQoNCiAgDQogIG1fc2NsdXN0ZXI9bWF0cml4KGRhdGEgPSByZXAoLjIsIHZtYXhyb3dzKnZtYXhyb3dzKSwgbnJvdyA9IHZtYXhyb3dzLCBuY29sID0gdm1heHJvd3MpDQogIG1hdG9wZXI9bWF0Ki44NSttX3NjbHVzdGVyKi4xNQ0KICBmb3IoaSBpbiAxOnZtYXhyb3dzKSB7DQogICAgIyBpLXRoIGVsZW1lbnQgb2YgYHUxYCBzcXVhcmVkIGludG8gYGlgLXRoIHBvc2l0aW9uIG9mIGB1c3FgDQogICAgbWF0b3BlcltpLF0gPC0gbWF0b3BlcltpLF0gLyBzdW0obWF0b3BlcltpLF0pDQogICAgcHJpbnQoYyhtYXRvcGVyW2ksXSxpLHN1bShtYXRvcGVyW2ksXSkpKQ0KICB9DQogIA0KICANCiAgcmV0dXJuKG1hdG9wZXIpDQp9DQpgYGANCkZ1bmNpb24gZGUgbXVsdGlwbGljYWNpb24gZGUgbWF0cmljZXMgZW4gcGFyYWxlbG8NCmBgYHtyfQ0KbWF0cHJvZC5wYXIgPC0gZnVuY3Rpb24oY2wsIEEsIEIpew0KICBpZiAobmNvbChBKSAhPSBucm93KEIpKSBzdG9wKCJNYXRyaWNlcyBkbyBub3QgY29uZm9ybWUiKQ0KICBpZHggPC0gc3BsaXRJbmRpY2VzKG5yb3coQSksIGxlbmd0aChjbCkpDQogIEFsaXN0IDwtIGxhcHBseShpZHgsIGZ1bmN0aW9uKGlpKSBBW2lpLCxkcm9wPUZBTFNFXSkNCiAgIyMgYW5zIDwtIGNsdXN0ZXJBcHBseShjbCwgQWxpc3QsIGZ1bmN0aW9uKGFhLCBCKSBhYSAlKiUgQiwgQikNCiAgIyMgU2FtZSBhcyBhYm92ZSwgYnV0IGZhc3RlcjoNCiAgYW5zIDwtIGNsdXN0ZXJBcHBseShjbCwgQWxpc3QsIGdldCgiJSolIiksIEIpDQogIGRvLmNhbGwocmJpbmQsIGFucykNCn0NCmBgYA0KDQpGdW5jacOzbiBkZSBtdWx0aXBsaWNhY2lvbiBkZSBtYXRyaWNlcyBlbiBwYXJhbGVsbw0KYGBge3J9DQptYXRwcm9kLnBhciA8LSBmdW5jdGlvbihjbCwgQSwgQil7DQogIGlmIChuY29sKEEpICE9IG5yb3coQikpIHN0b3AoIk1hdHJpY2VzIGRvIG5vdCBjb25mb3JtZSIpDQogIGlkeCA8LSBzcGxpdEluZGljZXMobnJvdyhBKSwgbGVuZ3RoKGNsKSkNCiAgQWxpc3QgPC0gbGFwcGx5KGlkeCwgZnVuY3Rpb24oaWkpIEFbaWksLGRyb3A9RkFMU0VdKQ0KICAjIyBhbnMgPC0gY2x1c3RlckFwcGx5KGNsLCBBbGlzdCwgZnVuY3Rpb24oYWEsIEIpIGFhICUqJSBCLCBCKQ0KICAjIyBTYW1lIGFzIGFib3ZlLCBidXQgZmFzdGVyOg0KICBhbnMgPC0gY2x1c3RlckFwcGx5KGNsLCBBbGlzdCwgZ2V0KCIlKiUiKSwgQikNCiAgZG8uY2FsbChyYmluZCwgYW5zKQ0KfQ0KYGBgDQpGdW5jacOzbiBQYXJhIGFzaWduYXIgZWwgUmFua2luZw0KYGBge3J9DQphc3NpZ25TY29yZSA8LSBmdW5jdGlvbihtYXQsZGF0YXNldCAsdm1heHJvd3Mpew0KICANCiAgDQogIGZvcihpIGluIDE6dm1heHJvd3Mpew0KICAgIGRhdGFzZXQkcmFua2luZ1tpXT0gIG1hdFtpLGldDQogIA0KDQogIH0NCg0KICByZXR1cm4oZGF0YXNldCkNCn0NCmBgYA0KRnVuY2lvbiBwYXJhIGxpc3RhciBlbCByYW5raW5nIGRlIGxhcyBhcnRpc3RhcyANCmBgYHtyfQ0KbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nIDwtIGZ1bmN0aW9uKHRleHRzZWFyY2gsZGF0YXNldCl7DQogIA0KICBmaWx0ZXJlZHRhYmxlPC1kYXRhc2V0ICU+JSANCiAgICBmaWx0ZXIoc3RyX2RldGVjdChsaW5rcywgcmVnZXgodGV4dHNlYXJjaCwgaWdub3JlX2Nhc2UgPSBUKSkpDQogIA0KICBmaWx0ZXJlZHRhYmxlPC1maWx0ZXJlZHRhYmxlW29yZGVyKC1maWx0ZXJlZHRhYmxlJHJhbmtpbmcpLGMoMSwyKV0NCiAgICANCg0KICANCiAgDQogIHJldHVybihmaWx0ZXJlZHRhYmxlKQ0KfQ0KYGBgDQpUZXN0IGRlIGZ1bmNpb24gcGFyYSBvYnRlbmVyIGVsIGRhdGEgZnJhbWUgZGUgdXJscw0KYGBge3J9DQojbGVjdHVyYSBkZWwgcm9vdA0KdGVzdF9saW5rczwtZGF0YWZyYW1saW5rcygiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlzdF9vZl9BbWVyaWNhbl9maWxtX2FjdHJlc3NlcyIpDQpnbGltcHNlKCB0ZXN0X2xpbmtzKQ0KI2xlY3R1cmEgZGVsIHJvb3QNCmBgYCAgDQoNCg0KDQoNCg0KDQoNCg0KDQpQcm95ZWN0bzoNCnBhc28gMS4gbGVlciBlbCByb290IGRlbCBkaXJlY3RvcmlvIGRlIGFjdHJlc3Nlcw0KYGBge3J9DQpMaXN0b2ZfbGlua3M8LWRhdGFmcmFtbGlua3MoImh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfQW1lcmljYW5fZmlsbV9hY3RyZXNzZXMiKQ0KDQpnbGltcHNlKCBMaXN0b2ZfbGlua3MpDQpgYGANCnBhc28gMi4gQ29udHJ1aXIgbGEgbGlzdGEgZGUgb2JqZXRvcyBjb24gc3UgcmVzcGVjdGl2byBVUksgcGFyYSBjYWRhIHVuYSBkZSBsYXMgYWN0cmljZXMNCmBgYHtyfQ0KcHJhcGFyZWQ8LSBMaXN0b2ZfbGlua3MlPiVtdXRhdGUocmFua2luZz0wLCBpbmRleD0gMSwgcHJvY2Vzc2VkPUZBTFNFLHVybD1wYXN0ZSgiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnIixMaXN0b2ZfbGlua3MkbGlua3MsIHNlcCA9ICAiIiAgKSAgKQ0KDQpgYGANCnBhc28gMy4gQ29udHJ1aXIgZWwgYXJyZWdsbyBkZSByZWZlcmVuY2lhcyBkZSBjYWRhIHVuYSBkZSBsYXMgYWN0cmljZXMgDQpzaSBlbCBidWNsZSBkYSBlcnJvciBkZSBjb211bmljYWNpb24gcmVpbnRlbnRhciBudWV2YW1lbnRlIGRlc2RlIGVsIGZvciBoYXN0YSBjb21wbGV0YXIgbGEgbGlzdGENCk5vdGE6IGVsIGNhbXBvIHByYXBhcmVkJHByb2Nlc3NlZCBzZSB2b2x2ZXJhIHZlcmRhZGVybyB1bmEgdmV6IGVsIFVSTCByZXF1ZXN0IHNlIGhheWEgcmVhbGl6YWRvDQpgYGB7cn0NCnZlY2xpc3Q9bGlzdCgpDQptYXhyb3dzPW5yb3cocHJhcGFyZWQpDQojbWF4cm93cz0xNQ0KZm9yKGkgaW4gMTptYXhyb3dzKSB7DQogICMgaS10aCBlbGVtZW50IG9mIGB1MWAgc3F1YXJlZCBpbnRvIGBpYC10aCBwb3NpdGlvbiBvZiBgdXNxYA0KICBwcmFwYXJlZFtpLCdpbmRleCddPWkNCiAgaWYoIXByYXBhcmVkJHByb2Nlc3NlZFtpXSApew0KICAgIA0KICAgIHByaW50KHBhc3RlKGksIi8iLG5yb3cocHJhcGFyZWQpKSkgDQogICAgbHN0bGlua3M8LWRhdGFmcmFtbGlua3MocHJhcGFyZWQkdXJsW2ldKQ0KICAgIHZlY3JlZj1saXN0KCJpZHgiPSBpLCJsaW5rIj0gcHJhcGFyZWQkbGlua3NbaV0sImNvbnRlbnQiPWxzdGxpbmtzKQ0KICAgIHByYXBhcmVkW2ksJ3Byb2Nlc3NlZCddPVRSVUUNCiAgICBwcmludCh2ZWNyZWYpDQogICAgdmVjbGlzdFtbaV1dIDwtIHZlY3JlZg0KICAgIA0KICAgIHByaW50KHBhc3RlKCIuLi4iLHZlY2xpc3RbW2ldXSRsaW5rKSkNCiAgfQ0KfQ0KDQpwcmFwYXJlZA0KYGBgDQpwYXNvIDQuIFNlIHByb2NlZGUgYSBjb250cnVpciBsYSBtYXRyaXogZGUgbWFya292DQogIHBhc28gNC4xLiBzZSBjcmVhIGxhIG1hdHJpeiBkZSByZWxhY2lvbmVzDQogIHBhc28gNC4yLiBzZSBjb25zdHJ1eWUgbGEgbWF0cml6IGRlIG1hcmtvdiBjb24gZWwgbGEgaW5jbHVzaW9uIGRlIHNhbHRvcyBwb3IgY2x1c3RlciANCiAgcGFzbyA0LjIgc2UgcHJvY2VzYSBlbiBwYXJhbGVsbyBsYSBtYXRyaXogZGUgbWFya292DQoNCmBgYHtyfQ0KbWF0PC1jKCkNCm1hdDwtYnVpbGRtYXRyaXgodmVjbGlzdCxtYXhyb3dzKQ0KbWFya292PC1idWlsZG1hcmtvdm1hdHJpeChtYXQsbWF4cm93cykNCg0KKG5jIDwtIGRldGVjdENvcmVzKCkpDQpjbCA8LSBtYWtlQ2x1c3RlcihyZXAoImxvY2FsaG9zdCIsIG5jKSkNCg0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXJrb3YsbWFya292KQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0Kc3RvcENsdXN0ZXIoY2wpDQpgYGANCg0KTWF0cml6IGRlIHJlbGFjaW9uZXMNCmBgYHtyfQ0KZ2xpbXBzZShtYXQgKQ0KYGBgDQpNYXRyaXogZGUgbWFya292DQpgYGB7cn0NCmdsaW1wc2UobWFya292ICkNCmBgYA0KDQpNYXRyaXogY29uIHBvdGVuY2lhIGEgbj0xMg0KYGBge3J9DQpnbGltcHNlKG1hdHBvd24gKQ0KYGBgDQoNCkFzaWduYWNpb24gZGUgbG9zIHNjb3JlIGJhc2Fkb3MgZW4gZWwgcHJvZHVjdG8gZGUgbWF0cmljZXMNCnNjb3JlZGxpc3Q8LWFzc2lnblNjb3JlKG1hdHBvd24scHJhcGFyZWQsbWF4cm93cykNCmBgYHtyfQ0Kc2NvcmVkbGlzdDwtYXNzaWduU2NvcmUobWF0cG93bixwcmFwYXJlZCxtYXhyb3dzKQ0Kc2NvcmVkbGlzdA0KYGBgDQpNb3N0cmFyIGVsIHJhbmtpbmcgZGUgbGFzIGFydGlzdGFzIGJhc2Fkb3MgZW4gdGV4dG8NCg0KMS4gamVuDQoNCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJqZW4iLHNjb3JlZGxpc3QpDQpgYGB7cn0NCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJqZW4iLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA0KDQoyLiBib3kNCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJib3kiLHNjb3JlZGxpc3QpDQpgYGB7cn0NCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJib3kiLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA0KDQoyLiBCZWxsDQpyZXNwdWVzdGE8LW1vc3RyYXJfYXJ0aXN0YXNfcmFua2luZygiQmVsbCIsc2NvcmVkbGlzdCkNCmBgYHtyfQ0KcmVzcHVlc3RhPC1tb3N0cmFyX2FydGlzdGFzX3JhbmtpbmcoIkJlbGwiLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA==