#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

  1. jen

respuesta<-mostrar_artistas_ranking(“jen”,scoredlist)

respuesta<-mostrar_artistas_ranking("jen",scoredlist)
respuesta
  1. boy respuesta<-mostrar_artistas_ranking(“boy”,scoredlist)
respuesta<-mostrar_artistas_ranking("boy",scoredlist)
respuesta
  1. Bell respuesta<-mostrar_artistas_ranking(“Bell”,scoredlist)
respuesta<-mostrar_artistas_ranking("Bell",scoredlist)
respuesta
LS0tDQp0aXRsZTogIlJhbmtpbmcgaGFjaWVuZG8gdXNvIGRlIENhZGVuYXMgZGUgbWFya292IHByb3llY3RvIGVzdGFkaXN0aWNhIDIsIEJ5cm9uIEhlcm5hbmRleiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojUmFua2luZyB1c2FuZG8gTWFya292DQoNCg0KYGBge3J9DQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHBhcmFsbGVsKQ0KDQpgYGANCg0KRnVuY2nDs24gcGFyYSBoYWNlciBlbCBzY2FuIGRlIHdpa2lwZWRpYQ0KYGBge3J9DQpkYXRhZnJhbWxpbmtzIDwtIGZ1bmN0aW9uKHVybCl7DQogIHByaW50KHVybCkgDQogICAgcmVhZF9odG1sKHVybCkgJT4lIA0KICAgIGh0bWxfbm9kZXMoImEiKSAlPiUgDQogICAgaHRtbF9hdHRyKCdocmVmJykgJT4lIA0KICAgIGRhdGFfZnJhbWUoKSAlPiUNCiAgICByZW5hbWUobGlua3M9Jy4nKSAlPiUgDQogICAgZmlsdGVyKHN0cl9kZXRlY3QobGlua3MsIi93aWtpLyIpKSAlPiUNCiAgICBmaWx0ZXIoIXN0cl9kZXRlY3QobGlua3MsIiMiKSkgJT4lDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmtzLCJpbWFnZSIpKSAlPiUgDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KHRvbG93ZXIobGlua3MpLCJmaWxlIikpJT4lDQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmtzLCJodHRwczoiKSkgJT4lIA0KICAgIGZpbHRlcighc3RyX2RldGVjdChsaW5rcywiLy8iKSkgJT4lIA0KICAgIGZpbHRlcighc3RyX2RldGVjdChsaW5rcywiOiIpKSU+JSByZXR1cm4oKQ0KICANCiAgDQogICANCn0NCmBgYA0KRnVuY2nDs24gcGFyYSBjb250cnVpciBsYSBtYXRyaXogZGUgcmVsYWNpb25lcyANCmBgYHtyfQ0KYnVpbGRtYXRyaXggPC0gZnVuY3Rpb24odmxpc3QsIHZtYXhyb3dzKXsNCiAgbWF0PC1tYXRyaXgoMCwgbnJvdyA9IG1heHJvd3MsbmNvbCA9IG1heHJvd3MpDQogIA0KICBmb3IoaSBpbiAxOnZtYXhyb3dzKXsNCiAgICB0ZXh0aWQ9dmxpc3RbW2ldXSRsaW5rDQogICAgDQogICAgZm9yKGogaW4gMTp2bWF4cm93cyl7DQogICAgICANCiAgICAgIG1hdFtpLGpdPTANCiAgICAgIGNvbnRlbnQ9dmxpc3RbW2pdXSRjb250ZW50DQogICAgICB0b3RhbD1ucm93KGNvbnRlbnQlPiVmaWx0ZXIoc3RyX2RldGVjdChsaW5rcyx0ZXh0aWQpKSkNCiAgICAgIGlmKHRvdGFsPjAgJiYgaSE9ail7DQogICAgICAgIG1hdFtpLGpdPTEgDQoNCiAgICAgIH0NCiAgICB9DQogICAgcHJpbnQocGFzdGUoImk6IixpLCcvJyx2bWF4cm93cyxTeXMudGltZSgpKSkNCg0KDQogICAgDQogICAgDQogIH0gIA0KICANCg0KICByZXR1cm4obWF0KQ0KfQ0KYGBgDQpGdW5jacOzbiBwYXJhIGNvbnRydWlyIGxhIG1hdHJpeiBiYXNlIGRlIG1hcmtvdg0KRXN0YSBtYXRyaXogY29udGllbmUgZWwgYXJ0ZWZhY3RvIHBhcmEgY29uZWN0YXIgY2xvc3RlcnMNCmBgYHtyfQ0KYnVpbGRtYXJrb3ZtYXRyaXggPC0gZnVuY3Rpb24obWF0LCB2bWF4cm93cyl7DQoNCiAgDQogIG1fc2NsdXN0ZXI9bWF0cml4KGRhdGEgPSByZXAoLjIsIHZtYXhyb3dzKnZtYXhyb3dzKSwgbnJvdyA9IHZtYXhyb3dzLCBuY29sID0gdm1heHJvd3MpDQogIG1hdG9wZXI9bWF0Ki44NSttX3NjbHVzdGVyKi4xNQ0KICBmb3IoaSBpbiAxOnZtYXhyb3dzKSB7DQogICAgIyBpLXRoIGVsZW1lbnQgb2YgYHUxYCBzcXVhcmVkIGludG8gYGlgLXRoIHBvc2l0aW9uIG9mIGB1c3FgDQogICAgbWF0b3BlcltpLF0gPC0gbWF0b3BlcltpLF0gLyBzdW0obWF0b3BlcltpLF0pDQogICAgcHJpbnQoYyhtYXRvcGVyW2ksXSxpLHN1bShtYXRvcGVyW2ksXSkpKQ0KICB9DQogIA0KICANCiAgcmV0dXJuKG1hdG9wZXIpDQp9DQpgYGANCkZ1bmNpb24gZGUgbXVsdGlwbGljYWNpb24gZGUgbWF0cmljZXMgZW4gcGFyYWxlbG8NCmBgYHtyfQ0KbWF0cHJvZC5wYXIgPC0gZnVuY3Rpb24oY2wsIEEsIEIpew0KICBpZiAobmNvbChBKSAhPSBucm93KEIpKSBzdG9wKCJNYXRyaWNlcyBkbyBub3QgY29uZm9ybWUiKQ0KICBpZHggPC0gc3BsaXRJbmRpY2VzKG5yb3coQSksIGxlbmd0aChjbCkpDQogIEFsaXN0IDwtIGxhcHBseShpZHgsIGZ1bmN0aW9uKGlpKSBBW2lpLCxkcm9wPUZBTFNFXSkNCiAgIyMgYW5zIDwtIGNsdXN0ZXJBcHBseShjbCwgQWxpc3QsIGZ1bmN0aW9uKGFhLCBCKSBhYSAlKiUgQiwgQikNCiAgIyMgU2FtZSBhcyBhYm92ZSwgYnV0IGZhc3RlcjoNCiAgYW5zIDwtIGNsdXN0ZXJBcHBseShjbCwgQWxpc3QsIGdldCgiJSolIiksIEIpDQogIGRvLmNhbGwocmJpbmQsIGFucykNCn0NCmBgYA0KDQpGdW5jacOzbiBkZSBtdWx0aXBsaWNhY2lvbiBkZSBtYXRyaWNlcyBlbiBwYXJhbGVsbw0KYGBge3J9DQptYXRwcm9kLnBhciA8LSBmdW5jdGlvbihjbCwgQSwgQil7DQogIGlmIChuY29sKEEpICE9IG5yb3coQikpIHN0b3AoIk1hdHJpY2VzIGRvIG5vdCBjb25mb3JtZSIpDQogIGlkeCA8LSBzcGxpdEluZGljZXMobnJvdyhBKSwgbGVuZ3RoKGNsKSkNCiAgQWxpc3QgPC0gbGFwcGx5KGlkeCwgZnVuY3Rpb24oaWkpIEFbaWksLGRyb3A9RkFMU0VdKQ0KICAjIyBhbnMgPC0gY2x1c3RlckFwcGx5KGNsLCBBbGlzdCwgZnVuY3Rpb24oYWEsIEIpIGFhICUqJSBCLCBCKQ0KICAjIyBTYW1lIGFzIGFib3ZlLCBidXQgZmFzdGVyOg0KICBhbnMgPC0gY2x1c3RlckFwcGx5KGNsLCBBbGlzdCwgZ2V0KCIlKiUiKSwgQikNCiAgZG8uY2FsbChyYmluZCwgYW5zKQ0KfQ0KYGBgDQpGdW5jacOzbiBQYXJhIGFzaWduYXIgZWwgUmFua2luZw0KYGBge3J9DQphc3NpZ25TY29yZSA8LSBmdW5jdGlvbihtYXQsZGF0YXNldCAsdm1heHJvd3Mpew0KICANCiAgDQogIGZvcihpIGluIDE6dm1heHJvd3Mpew0KICAgIGRhdGFzZXQkcmFua2luZ1tpXT0gIG1hdFtpLGldDQogIA0KDQogIH0NCg0KICByZXR1cm4oZGF0YXNldCkNCn0NCmBgYA0KRnVuY2lvbiBwYXJhIGxpc3RhciBlbCByYW5raW5nIGRlIGxhcyBhcnRpc3RhcyANCmBgYHtyfQ0KbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nIDwtIGZ1bmN0aW9uKHRleHRzZWFyY2gsZGF0YXNldCl7DQogIA0KICBmaWx0ZXJlZHRhYmxlPC1kYXRhc2V0ICU+JSANCiAgICBmaWx0ZXIoc3RyX2RldGVjdChsaW5rcywgcmVnZXgodGV4dHNlYXJjaCwgaWdub3JlX2Nhc2UgPSBUKSkpDQogIA0KICBmaWx0ZXJlZHRhYmxlPC1maWx0ZXJlZHRhYmxlW29yZGVyKC1maWx0ZXJlZHRhYmxlJHJhbmtpbmcpLGMoMSwyKV0NCiAgICANCg0KICANCiAgDQogIHJldHVybihmaWx0ZXJlZHRhYmxlKQ0KfQ0KYGBgDQpUZXN0IGRlIGZ1bmNpb24gcGFyYSBvYnRlbmVyIGVsIGRhdGEgZnJhbWUgZGUgdXJscw0KYGBge3J9DQojbGVjdHVyYSBkZWwgcm9vdA0KdGVzdF9saW5rczwtZGF0YWZyYW1saW5rcygiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlzdF9vZl9BbWVyaWNhbl9maWxtX2FjdHJlc3NlcyIpDQpnbGltcHNlKCB0ZXN0X2xpbmtzKQ0KI2xlY3R1cmEgZGVsIHJvb3QNCmBgYCAgDQoNCg0KDQoNCg0KDQoNCg0KDQpQcm95ZWN0bzoNCnBhc28gMS4gbGVlciBlbCByb290IGRlbCBkaXJlY3RvcmlvIGRlIGFjdHJlc3Nlcw0KYGBge3J9DQpMaXN0b2ZfbGlua3M8LWRhdGFmcmFtbGlua3MoImh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfQW1lcmljYW5fZmlsbV9hY3RyZXNzZXMiKQ0KDQpnbGltcHNlKCBMaXN0b2ZfbGlua3MpDQpgYGANCnBhc28gMi4gQ29udHJ1aXIgbGEgbGlzdGEgZGUgb2JqZXRvcyBjb24gc3UgcmVzcGVjdGl2byBVUksgcGFyYSBjYWRhIHVuYSBkZSBsYXMgYWN0cmljZXMNCmBgYHtyfQ0KcHJhcGFyZWQ8LSBMaXN0b2ZfbGlua3MlPiVtdXRhdGUocmFua2luZz0wLCBpbmRleD0gMSwgcHJvY2Vzc2VkPUZBTFNFLHVybD1wYXN0ZSgiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnIixMaXN0b2ZfbGlua3MkbGlua3MsIHNlcCA9ICAiIiAgKSAgKQ0KDQpgYGANCnBhc28gMy4gQ29udHJ1aXIgZWwgYXJyZWdsbyBkZSByZWZlcmVuY2lhcyBkZSBjYWRhIHVuYSBkZSBsYXMgYWN0cmljZXMgDQpzaSBlbCBidWNsZSBkYSBlcnJvciBkZSBjb211bmljYWNpb24gcmVpbnRlbnRhciBudWV2YW1lbnRlIGRlc2RlIGVsIGZvciBoYXN0YSBjb21wbGV0YXIgbGEgbGlzdGENCk5vdGE6IGVsIGNhbXBvIHByYXBhcmVkJHByb2Nlc3NlZCBzZSB2b2x2ZXJhIHZlcmRhZGVybyB1bmEgdmV6IGVsIFVSTCByZXF1ZXN0IHNlIGhheWEgcmVhbGl6YWRvDQpgYGB7cn0NCnZlY2xpc3Q9bGlzdCgpDQptYXhyb3dzPW5yb3cocHJhcGFyZWQpDQojbWF4cm93cz0xNQ0KZm9yKGkgaW4gMTptYXhyb3dzKSB7DQogICMgaS10aCBlbGVtZW50IG9mIGB1MWAgc3F1YXJlZCBpbnRvIGBpYC10aCBwb3NpdGlvbiBvZiBgdXNxYA0KICBwcmFwYXJlZFtpLCdpbmRleCddPWkNCiAgaWYoIXByYXBhcmVkJHByb2Nlc3NlZFtpXSApew0KICAgIA0KICAgIHByaW50KHBhc3RlKGksIi8iLG5yb3cocHJhcGFyZWQpKSkgDQogICAgbHN0bGlua3M8LWRhdGFmcmFtbGlua3MocHJhcGFyZWQkdXJsW2ldKQ0KICAgIHZlY3JlZj1saXN0KCJpZHgiPSBpLCJsaW5rIj0gcHJhcGFyZWQkbGlua3NbaV0sImNvbnRlbnQiPWxzdGxpbmtzKQ0KICAgIHByYXBhcmVkW2ksJ3Byb2Nlc3NlZCddPVRSVUUNCiAgICBwcmludCh2ZWNyZWYpDQogICAgdmVjbGlzdFtbaV1dIDwtIHZlY3JlZg0KICAgIA0KICAgIHByaW50KHBhc3RlKCIuLi4iLHZlY2xpc3RbW2ldXSRsaW5rKSkNCiAgfQ0KfQ0KDQpwcmFwYXJlZA0KYGBgDQpwYXNvIDQuIFNlIHByb2NlZGUgYSBjb250cnVpciBsYSBtYXRyaXogZGUgbWFya292DQogIHBhc28gNC4xLiBzZSBjcmVhIGxhIG1hdHJpeiBkZSByZWxhY2lvbmVzDQogIHBhc28gNC4yLiBzZSBjb25zdHJ1eWUgbGEgbWF0cml6IGRlIG1hcmtvdiBjb24gZWwgbGEgaW5jbHVzaW9uIGRlIHNhbHRvcyBwb3IgY2x1c3RlciANCiAgcGFzbyA0LjIgc2UgcHJvY2VzYSBlbiBwYXJhbGVsbyBsYSBtYXRyaXogZGUgbWFya292DQoNCmBgYHtyfQ0KbWF0PC1jKCkNCm1hdDwtYnVpbGRtYXRyaXgodmVjbGlzdCxtYXhyb3dzKQ0KbWFya292PC1idWlsZG1hcmtvdm1hdHJpeChtYXQsbWF4cm93cykNCg0KKG5jIDwtIGRldGVjdENvcmVzKCkpDQpjbCA8LSBtYWtlQ2x1c3RlcihyZXAoImxvY2FsaG9zdCIsIG5jKSkNCg0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXJrb3YsbWFya292KQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0KbWF0cG93bj1tYXRwcm9kLnBhcihjbCxtYXRwb3duLG1hdHBvd24pDQptYXRwb3duPW1hdHByb2QucGFyKGNsLG1hdHBvd24sbWF0cG93bikNCm1hdHBvd249bWF0cHJvZC5wYXIoY2wsbWF0cG93bixtYXRwb3duKQ0Kc3RvcENsdXN0ZXIoY2wpDQpgYGANCg0KTWF0cml6IGRlIHJlbGFjaW9uZXMNCmBgYHtyfQ0KZ2xpbXBzZShtYXQgKQ0KYGBgDQpNYXRyaXogZGUgbWFya292DQpgYGB7cn0NCmdsaW1wc2UobWFya292ICkNCmBgYA0KDQpNYXRyaXogY29uIHBvdGVuY2lhIGEgbj0xMg0KYGBge3J9DQpnbGltcHNlKG1hdHBvd24gKQ0KYGBgDQoNCkFzaWduYWNpb24gZGUgbG9zIHNjb3JlIGJhc2Fkb3MgZW4gZWwgcHJvZHVjdG8gZGUgbWF0cmljZXMNCnNjb3JlZGxpc3Q8LWFzc2lnblNjb3JlKG1hdHBvd24scHJhcGFyZWQsbWF4cm93cykNCmBgYHtyfQ0Kc2NvcmVkbGlzdDwtYXNzaWduU2NvcmUobWF0cG93bixwcmFwYXJlZCxtYXhyb3dzKQ0Kc2NvcmVkbGlzdA0KYGBgDQpNb3N0cmFyIGVsIHJhbmtpbmcgZGUgbGFzIGFydGlzdGFzIGJhc2Fkb3MgZW4gdGV4dG8NCg0KMS4gamVuDQoNCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJqZW4iLHNjb3JlZGxpc3QpDQpgYGB7cn0NCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJqZW4iLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA0KDQoyLiBib3kNCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJib3kiLHNjb3JlZGxpc3QpDQpgYGB7cn0NCnJlc3B1ZXN0YTwtbW9zdHJhcl9hcnRpc3Rhc19yYW5raW5nKCJib3kiLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA0KDQoyLiBCZWxsDQpyZXNwdWVzdGE8LW1vc3RyYXJfYXJ0aXN0YXNfcmFua2luZygiQmVsbCIsc2NvcmVkbGlzdCkNCmBgYHtyfQ0KcmVzcHVlc3RhPC1tb3N0cmFyX2FydGlzdGFzX3JhbmtpbmcoIkJlbGwiLHNjb3JlZGxpc3QpDQpyZXNwdWVzdGENCmBgYA==