Probabilidades del Poker

Para este laboratorio vamos generar una simulacion para medir dentro de un deck normal de 52 cartas, en una reparticion de 5 cartas por juego (que es la mano que se reparte en el poker), que probabilidad tienen cada tipo de combinacion de cartas de ser repartida. Para ello nos basamos en las combinaciones esenciales del poker, estas pueden ser revisadas aqui. Las que veremos son:

  1. Par: esta combinacion se logra cuando se juntan dos cartas del mismo numero y diferente manjar.
  2. Doble Par: esta combinacion se logra cuando en las 5 cartas se logra obtener dos pares de cartas con el mismo numero.
  3. Tercia: esta combinacion se logra cuando se juntan 3 cartas del mismo numero de las 5 cartas de la mano.
  4. Escalera: es cuando se juntan 5 de las cartas y estas van en escalera, es decir del 6 al diez sin importar su manjar.
  5. Color: es una combinacion de 5 cartas del mismo manjar, aunque estas no vayan en escalera
  6. Full house: es la combinacion de 3 cartas del mismo numero, mas un par de cartas del mismo numero.
  7. Poker: cuando se juntan 4 cartas del mismo numero.
  8. Escalera de color: cuando se juntan 5 cartas consecutivas del mismo manjar.
  9. Escalera real: consiste en 5 cartas consecutivas del mismo manjar pero la escalera se da con las cartas mas altas es decir del 10 hasta el As.

Para ello primero vamos a generar un dataframe que contenga todas las posibles cartas.

cartas=matrix(c(rep( c(2:10,"J","Q","K","A"),4),rep(c("TREBOLES","DIAMANTES","CORAZONES","ESPADAS"),rep(13,4))), ncol=2,dimnames=list(NULL,c("numero","manjar")))
cartas
      numero manjar     
 [1,] "2"    "TREBOLES" 
 [2,] "3"    "TREBOLES" 
 [3,] "4"    "TREBOLES" 
 [4,] "5"    "TREBOLES" 
 [5,] "6"    "TREBOLES" 
 [6,] "7"    "TREBOLES" 
 [7,] "8"    "TREBOLES" 
 [8,] "9"    "TREBOLES" 
 [9,] "10"   "TREBOLES" 
[10,] "J"    "TREBOLES" 
[11,] "Q"    "TREBOLES" 
[12,] "K"    "TREBOLES" 
[13,] "A"    "TREBOLES" 
[14,] "2"    "DIAMANTES"
[15,] "3"    "DIAMANTES"
[16,] "4"    "DIAMANTES"
[17,] "5"    "DIAMANTES"
[18,] "6"    "DIAMANTES"
[19,] "7"    "DIAMANTES"
[20,] "8"    "DIAMANTES"
[21,] "9"    "DIAMANTES"
[22,] "10"   "DIAMANTES"
[23,] "J"    "DIAMANTES"
[24,] "Q"    "DIAMANTES"
[25,] "K"    "DIAMANTES"
[26,] "A"    "DIAMANTES"
[27,] "2"    "CORAZONES"
[28,] "3"    "CORAZONES"
[29,] "4"    "CORAZONES"
[30,] "5"    "CORAZONES"
[31,] "6"    "CORAZONES"
[32,] "7"    "CORAZONES"
[33,] "8"    "CORAZONES"
[34,] "9"    "CORAZONES"
[35,] "10"   "CORAZONES"
[36,] "J"    "CORAZONES"
[37,] "Q"    "CORAZONES"
[38,] "K"    "CORAZONES"
[39,] "A"    "CORAZONES"
[40,] "2"    "ESPADAS"  
[41,] "3"    "ESPADAS"  
[42,] "4"    "ESPADAS"  
[43,] "5"    "ESPADAS"  
[44,] "6"    "ESPADAS"  
[45,] "7"    "ESPADAS"  
[46,] "8"    "ESPADAS"  
[47,] "9"    "ESPADAS"  
[48,] "10"   "ESPADAS"  
[49,] "J"    "ESPADAS"  
[50,] "Q"    "ESPADAS"  
[51,] "K"    "ESPADAS"  
[52,] "A"    "ESPADAS"  

Ahora para realizar nuestras pruebas usamos una funcion que nos genere aleatoriamente una combinacion de 5 cartas

repartir = function()
{
    return( cartas[sample(1:52,5,replace=F),])
}

repartir()
     numero manjar     
[1,] "A"    "DIAMANTES"
[2,] "4"    "CORAZONES"
[3,] "7"    "DIAMANTES"
[4,] "K"    "ESPADAS"  
[5,] "8"    "TREBOLES" 

Ahora vamos a genera run arreglo de los numeros de cartas ordenados de mayor a menor, esto nos permitira determinar los dos tipos de escalera, el que va de As hasta K y el que va de 2 hasta As, esto ya que el As puede ser 1 o 14 dependiendo de la combinacion.

orden_alto = c(2:10,"J","Q","K","A")
orden_bajo = c("A",2:10,"J","Q","K")
orden_alto
 [1] "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "J"  "Q"  "K"  "A" 
orden_bajo
 [1] "A"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "J"  "Q"  "K" 

Ahora vamos a rankear nuestra mano de cartas usando el orden alto o el orden bajo, esto para determinar la posicion dentro del orden para cada carta y asi poder determinar si es una escalera de cualquier tipo.

rank_alto = sort( sapply(mano[,"numero"],function(carta) which(carta == orden_alto)) )
rank_alto
 3  3  6  K  K 
 2  2  5 12 12 

Ahora haremos lo mismo para el orden bajo.

rank_bajo = sort( sapply(mano[,"numero"],function(carta) which(carta == orden_bajo)) )
rank_bajo
 3  3  6  K  K 
 3  3  6 13 13 

Como vemos dependiendo del orden esto nos da una posicion diferente, ahora bien como calculamos si las cartas estan en un orden lineal, para ello si suponemos el orden mas bajo, es decir del 1-5 podemos comparar si las posiciones estan dentro de 1 a 5, esto supondria que nuestor rango va de As a 5 o de 2 a 6, no obstante si nuestra mano fuera de 10 hasta As o 9 hasta K, lo cual corresponde a las posiciones 10 hasta 14, es decir tendriamos un arreglo de la siguiente forma:

10:14
[1] 10 11 12 13 14

Si sustraemos el valor menor y agregamos uno, esto nos deja con un arreglo de 1 a 5 siempre.

test <- 10:14
test-min(test)+1
[1] 1 2 3 4 5
test <- sample(5:9, 5, replace=FALSE)
test
[1] 7 6 5 9 8
test-min(test)+1
[1] 3 2 1 5 4

Si aplicamos esta formula a rank bajo y rank alto para determinar si todas las posiciones estan entre 1 y 5 obtenemos:

rank_alto-min(rank_alto)+1 == 1:5
    3     3     6     K     K 
 TRUE FALSE FALSE FALSE FALSE 

A este resultado podemos aplicarle la funcion all la cual opera un OR a todo el arreglo, lo cual nos permite determinar si es una escalera o no, hagamos una prueba

test <- sample(5:9, 5, replace=FALSE)
test
[1] 9 6 7 8 5
sort(test-min(test)+1) == 1:5
[1] TRUE TRUE TRUE TRUE TRUE
all(sort(test-min(test)+1) == 1:5)
[1] TRUE

Bien como se veria esto para determinar si es escalera:

all(sort(rank_alto-min(rank_alto)+1) == 1:5)
[1] FALSE
all(sort(rank_bajo-min(rank_bajo)+1) == 1:5)
[1] FALSE

Como esto hemos determinado si se trata de una escalera. Ahora como podemos determinar si es si las cartas tienen un solo manjar, bueno esto es sencillo solo contamos los manjares que tiene la mano si este es igual a uno es que tenemos un solo manjar.

length(unique(mano[,"manjar"])) == 1
[1] FALSE

Con esto ya podemos probar si nuestra mano se trata de una escalera e incluso si se trata de una escalera royal. Ahora veamos la prueba de si se trata de un poker, para ello tabulamos los diferentes numeros que tenemos en nuestra mano:

table(mano[,"numero"])

3 6 K 
2 1 2 

Esto nos agrupa las cartas y su numero de ocurrencias, bueno como podemos saber si se trata de un poker, probemos

mano <- matrix(c("2", "CORAZONES","2", "ESPADAS","2", "TREBOLES","2", "DIAMANTES","3", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar")))
mano
     numero manjar     
[1,] "2"    "CORAZONES"
[2,] "2"    "ESPADAS"  
[3,] "2"    "TREBOLES" 
[4,] "2"    "DIAMANTES"
[5,] "3"    "CORAZONES"
table(mano[,"numero"])

2 3 
4 1 

Como vemos y podemos intuir si se trata de una poker, la funcion table, siempre arrojara un arreglo de 4 y 1, con lo que podemos realizar:

all(sort(table(mano[,"numero"]))==c(1,4))
[1] TRUE

Y aqui tenemos nuestra validacion de poker, ahora intenemos algo similar para un full house,

mano <- matrix(c("2", "CORAZONES","2", "ESPADAS","2", "TREBOLES","3", "DIAMANTES","3", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar")))
mano
     numero manjar     
[1,] "2"    "CORAZONES"
[2,] "2"    "ESPADAS"  
[3,] "2"    "TREBOLES" 
[4,] "3"    "DIAMANTES"
[5,] "3"    "CORAZONES"
all(sort(table(mano[,"numero"])) == c(2,3))
[1] TRUE

Si tuvieramos solamente un trio esto nos queda

mano <- matrix(c("2", "CORAZONES","2", "ESPADAS","2", "TREBOLES","4", "DIAMANTES","3", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar")))
mano
     numero manjar     
[1,] "2"    "CORAZONES"
[2,] "2"    "ESPADAS"  
[3,] "2"    "TREBOLES" 
[4,] "4"    "DIAMANTES"
[5,] "3"    "CORAZONES"
all(sort(table(mano[,"numero"])) == c(1,1,3))
[1] TRUE

Para dos pares:

mano <- matrix(c("2", "CORAZONES","2", "ESPADAS","4", "TREBOLES","3", "DIAMANTES","3", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar")))
mano
     numero manjar     
[1,] "2"    "CORAZONES"
[2,] "2"    "ESPADAS"  
[3,] "4"    "TREBOLES" 
[4,] "3"    "DIAMANTES"
[5,] "3"    "CORAZONES"
all(sort(table(mano[,"numero"])) == c(1,2,2))
[1] TRUE

Ahora que pasa si solamente tenemos un par, esto lo podemos hacer de dos maneras, la primera ya sea comparando contra el arreglo [1,1,1,2] o bien validando que la tabla tenga exactamente 4 elementos, veamos:

mano <- matrix(c("2", "CORAZONES","2", "ESPADAS","5", "TREBOLES","7", "DIAMANTES","3", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar")))
mano
     numero manjar     
[1,] "2"    "CORAZONES"
[2,] "2"    "ESPADAS"  
[3,] "5"    "TREBOLES" 
[4,] "7"    "DIAMANTES"
[5,] "3"    "CORAZONES"
all(sort(table(mano[,"numero"])) == c(1,1,1,2))
[1] TRUE
length(table(mano[,"numero"]))==4
[1] TRUE

Por estandar nos quedamos con la comparativa del arreglo. Muy bien ahora armemos una funcion que valide todas las posibilidades:

  if(es_escalera & son_mismo_manjar){
    if(c("K","A") %in% mano[,"numero"]) return("ESCALERA ROYAL")
    return("ESCALERA DE COLOR")
  }
Error: object 'es_escalera' not found

Probemos nuestra funcion:

clasificar_mano(matrix(c("10", "CORAZONES","J", "CORAZONES","Q", "CORAZONES","K", "CORAZONES","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "ESCALERA ROYAL"
clasificar_mano(matrix(c("6", "CORAZONES","7", "CORAZONES","8", "CORAZONES","9", "CORAZONES","10", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "ESCALERA DE COLOR"
clasificar_mano(matrix(c("10", "CORAZONES","J", "ESPADAS","Q", "CORAZONES","K", "TREBOLES","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "ESCALERA"
clasificar_mano(matrix(c("10", "CORAZONES","J", "CORAZONES","Q", "CORAZONES","2", "CORAZONES","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "MISMO MANJAR"
clasificar_mano(matrix(c("10", "CORAZONES","10", "ESPADAS","10", "TREBOLES","10", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "POKER"
clasificar_mano(matrix(c("10", "CORAZONES","10", "ESPADAS","10", "TREBOLES","A", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "FULL"
clasificar_mano(matrix(c("10", "CORAZONES","10", "ESPADAS","10", "TREBOLES","2", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "TERCIA"
clasificar_mano(matrix(c("10", "CORAZONES","10", "ESPADAS","A", "TREBOLES","7", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "DOBLE PAR"
clasificar_mano(matrix(c("10", "CORAZONES","10", "ESPADAS","7", "TREBOLES","8", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "PAR"
clasificar_mano(matrix(c("10", "CORAZONES","3", "ESPADAS","7", "TREBOLES","K", "ESPADAS","A", "CORAZONES"), ncol=2, byrow=TRUE, dimnames = list(NULL,c("numero", "manjar"))))
[1] "NADA"

Finalmente podemos realizar la simulacion:

juegos <- sapply(1:10, function(x) clasificar_mano(repartir()))
juegos
 [1] "PAR"       "PAR"       "PAR"       "TERCIA"    "PAR"       "NADA"      "DOBLE PAR" "PAR"       "NADA"      "PAR"      
table(juegos)
juegos
DOBLE PAR      NADA       PAR    TERCIA 
        1         2         6         1 


simulacion <- function(N=1000){

  resultados <- data.frame(mano_poker=c("ESCALERA ROYAL", "ESCALERA DE COLOR", "ESCALERA", "MISMO MANJAR", "POKER", "FULL", "TERCIA", "DOBLE PAR", "PAR", "NADA"),
                           ocurrencias= rep(0,10))
  
  
  for(i in 1:N){
    juego <- clasificar_mano(repartir())
    resultados[resultados$mano_poker == juego,]$ocurrencias <- resultados[resultados$mano_poker == juego,]$ocurrencias + 1
  }
  
  resultados$porcentaje <- resultados$ocurrencias/N
  return(resultados)
}

simulacion(5000)

Que podemos concluir de estos resultados, que de cada 5 mil juegos, el 50% no obtendremos nada, el 41% tendremos un par, el 4% obtendremos 2 pares, el 3% tercia. La escalera pareciera tener mas probabilidades, pero la escalera de color y la escalera royal parecen casi imposibles de salir.

LS0tCnRpdGxlOiAiTW9kZWxhY2lvbiB5IFNpbXVsYWNpb24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIFByb2JhYmlsaWRhZGVzIGRlbCBQb2tlcgoKUGFyYSBlc3RlIGxhYm9yYXRvcmlvIHZhbW9zIGdlbmVyYXIgdW5hIHNpbXVsYWNpb24gcGFyYSBtZWRpciBkZW50cm8gZGUgdW4gZGVjayBub3JtYWwgZGUgNTIgY2FydGFzLCBlbiB1bmEgcmVwYXJ0aWNpb24gZGUgNSBjYXJ0YXMgcG9yIGp1ZWdvIChxdWUgZXMgbGEgbWFubyBxdWUgc2UgcmVwYXJ0ZSBlbiBlbCBwb2tlciksIHF1ZSBwcm9iYWJpbGlkYWQgdGllbmVuIGNhZGEgdGlwbyBkZSBjb21iaW5hY2lvbiBkZSBjYXJ0YXMgZGUgc2VyIHJlcGFydGlkYS4gUGFyYSBlbGxvIG5vcyBiYXNhbW9zIGVuIGxhcyBjb21iaW5hY2lvbmVzIGVzZW5jaWFsZXMgZGVsIHBva2VyLCBlc3RhcyBwdWVkZW4gc2VyIHJldmlzYWRhcyBbYXF1aV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlzdF9vZl9wb2tlcl9oYW5kcykuIExhcyBxdWUgdmVyZW1vcyBzb246CgoxLiBQYXI6IGVzdGEgY29tYmluYWNpb24gc2UgbG9ncmEgY3VhbmRvIHNlIGp1bnRhbiBkb3MgY2FydGFzIGRlbCBtaXNtbyBudW1lcm8geSBkaWZlcmVudGUgbWFuamFyLgoyLiBEb2JsZSBQYXI6IGVzdGEgY29tYmluYWNpb24gc2UgbG9ncmEgY3VhbmRvIGVuIGxhcyA1IGNhcnRhcyBzZSBsb2dyYSBvYnRlbmVyIGRvcyBwYXJlcyBkZSBjYXJ0YXMgY29uIGVsIG1pc21vIG51bWVyby4KMy4gVGVyY2lhOiBlc3RhIGNvbWJpbmFjaW9uIHNlIGxvZ3JhIGN1YW5kbyBzZSBqdW50YW4gMyBjYXJ0YXMgZGVsIG1pc21vIG51bWVybyBkZSBsYXMgNSBjYXJ0YXMgZGUgbGEgbWFuby4KNC4gRXNjYWxlcmE6IGVzIGN1YW5kbyBzZSBqdW50YW4gNSBkZSBsYXMgY2FydGFzIHkgZXN0YXMgdmFuIGVuIGVzY2FsZXJhLCBlcyBkZWNpciBkZWwgNiBhbCBkaWV6IHNpbiBpbXBvcnRhciBzdSBtYW5qYXIuCjUuIENvbG9yOiBlcyB1bmEgY29tYmluYWNpb24gZGUgNSBjYXJ0YXMgZGVsIG1pc21vIG1hbmphciwgYXVucXVlIGVzdGFzIG5vIHZheWFuIGVuIGVzY2FsZXJhCjYuIEZ1bGwgaG91c2U6IGVzIGxhIGNvbWJpbmFjaW9uIGRlIDMgY2FydGFzIGRlbCBtaXNtbyBudW1lcm8sIG1hcyB1biBwYXIgZGUgY2FydGFzIGRlbCBtaXNtbyBudW1lcm8uCjcuIFBva2VyOiBjdWFuZG8gc2UganVudGFuIDQgY2FydGFzIGRlbCBtaXNtbyBudW1lcm8uCjguIEVzY2FsZXJhIGRlIGNvbG9yOiBjdWFuZG8gc2UganVudGFuIDUgY2FydGFzIGNvbnNlY3V0aXZhcyBkZWwgbWlzbW8gbWFuamFyLgo5LiBFc2NhbGVyYSByZWFsOiBjb25zaXN0ZSBlbiA1IGNhcnRhcyBjb25zZWN1dGl2YXMgZGVsIG1pc21vIG1hbmphciBwZXJvIGxhIGVzY2FsZXJhIHNlIGRhIGNvbiBsYXMgY2FydGFzIG1hcyBhbHRhcyBlcyBkZWNpciBkZWwgMTAgaGFzdGEgZWwgQXMuCgpQYXJhIGVsbG8gcHJpbWVybyB2YW1vcyBhIGdlbmVyYXIgdW4gZGF0YWZyYW1lIHF1ZSBjb250ZW5nYSB0b2RhcyBsYXMgcG9zaWJsZXMgY2FydGFzLgpgYGB7cn0KY2FydGFzPW1hdHJpeChjKHJlcCggYygyOjEwLCJKIiwiUSIsIksiLCJBIiksNCkscmVwKGMoIlRSRUJPTEVTIiwiRElBTUFOVEVTIiwiQ09SQVpPTkVTIiwiRVNQQURBUyIpLHJlcCgxMyw0KSkpLCBuY29sPTIsZGltbmFtZXM9bGlzdChOVUxMLGMoIm51bWVybyIsIm1hbmphciIpKSkKY2FydGFzCmBgYAoKQWhvcmEgcGFyYSByZWFsaXphciBudWVzdHJhcyBwcnVlYmFzIHVzYW1vcyB1bmEgZnVuY2lvbiBxdWUgbm9zIGdlbmVyZSBhbGVhdG9yaWFtZW50ZSB1bmEgY29tYmluYWNpb24gZGUgNSBjYXJ0YXMKCmBgYHtyfQpyZXBhcnRpciA9IGZ1bmN0aW9uKCkKewogICAgcmV0dXJuKCBjYXJ0YXNbc2FtcGxlKDE6NTIsNSxyZXBsYWNlPUYpLF0pCn0KCnJlcGFydGlyKCkKYGBgCgpBaG9yYSB2YW1vcyBhIGdlbmVyYSBydW4gYXJyZWdsbyBkZSBsb3MgbnVtZXJvcyBkZSBjYXJ0YXMgb3JkZW5hZG9zIGRlIG1heW9yIGEgbWVub3IsIGVzdG8gbm9zIHBlcm1pdGlyYSBkZXRlcm1pbmFyIGxvcyBkb3MgdGlwb3MgZGUgZXNjYWxlcmEsIGVsIHF1ZSB2YSBkZSBBcyBoYXN0YSBLIHkgZWwgcXVlIHZhIGRlIDIgaGFzdGEgQXMsIGVzdG8geWEgcXVlIGVsIEFzIHB1ZWRlIHNlciAxIG8gMTQgZGVwZW5kaWVuZG8gZGUgbGEgY29tYmluYWNpb24uCmBgYHtyfQpvcmRlbl9hbHRvID0gYygyOjEwLCJKIiwiUSIsIksiLCJBIikKb3JkZW5fYmFqbyA9IGMoIkEiLDI6MTAsIkoiLCJRIiwiSyIpCm9yZGVuX2FsdG8Kb3JkZW5fYmFqbwpgYGAKCkFob3JhIHZhbW9zIGEgcmFua2VhciBudWVzdHJhIG1hbm8gZGUgY2FydGFzIHVzYW5kbyBlbCBvcmRlbiBhbHRvIG8gZWwgb3JkZW4gYmFqbywgZXN0byBwYXJhIGRldGVybWluYXIgbGEgcG9zaWNpb24gZGVudHJvIGRlbCBvcmRlbiBwYXJhIGNhZGEgY2FydGEgeSBhc2kgcG9kZXIgZGV0ZXJtaW5hciBzaSBlcyB1bmEgZXNjYWxlcmEgZGUgY3VhbHF1aWVyIHRpcG8uCgpgYGB7cn0KcmFua19hbHRvID0gc29ydCggc2FwcGx5KG1hbm9bLCJudW1lcm8iXSxmdW5jdGlvbihjYXJ0YSkgd2hpY2goY2FydGEgPT0gb3JkZW5fYWx0bykpICkKcmFua19hbHRvCmBgYApBaG9yYSBoYXJlbW9zIGxvIG1pc21vIHBhcmEgZWwgb3JkZW4gYmFqby4KCmBgYHtyfQpyYW5rX2Jham8gPSBzb3J0KCBzYXBwbHkobWFub1ssIm51bWVybyJdLGZ1bmN0aW9uKGNhcnRhKSB3aGljaChjYXJ0YSA9PSBvcmRlbl9iYWpvKSkgKQpyYW5rX2Jham8KYGBgCkNvbW8gdmVtb3MgZGVwZW5kaWVuZG8gZGVsIG9yZGVuIGVzdG8gbm9zIGRhIHVuYSBwb3NpY2lvbiBkaWZlcmVudGUsIGFob3JhIGJpZW4gY29tbyBjYWxjdWxhbW9zIHNpIGxhcyBjYXJ0YXMgZXN0YW4gZW4gdW4gb3JkZW4gbGluZWFsLCBwYXJhIGVsbG8gc2kgc3Vwb25lbW9zIGVsIG9yZGVuIG1hcyBiYWpvLCBlcyBkZWNpciBkZWwgMS01IHBvZGVtb3MgY29tcGFyYXIgc2kgbGFzIHBvc2ljaW9uZXMgZXN0YW4gZGVudHJvIGRlIDEgYSA1LCBlc3RvIHN1cG9uZHJpYSBxdWUgbnVlc3RvciByYW5nbyB2YSBkZSBBcyBhIDUgbyBkZSAyIGEgNiwgbm8gb2JzdGFudGUgc2kgbnVlc3RyYSBtYW5vIGZ1ZXJhIGRlIDEwIGhhc3RhIEFzIG8gOSBoYXN0YSBLLCBsbyBjdWFsIGNvcnJlc3BvbmRlIGEgbGFzIHBvc2ljaW9uZXMgMTAgaGFzdGEgMTQsIGVzIGRlY2lyIHRlbmRyaWFtb3MgdW4gYXJyZWdsbyBkZSBsYSBzaWd1aWVudGUgZm9ybWE6CgpgYGB7cn0KMTA6MTQKYGBgClNpIHN1c3RyYWVtb3MgZWwgdmFsb3IgbWVub3IgeSBhZ3JlZ2Ftb3MgdW5vLCBlc3RvIG5vcyBkZWphIGNvbiB1biBhcnJlZ2xvIGRlIDEgYSA1IHNpZW1wcmUuCgpgYGB7cn0KdGVzdCA8LSAxMDoxNAp0ZXN0LW1pbih0ZXN0KSsxCmBgYAoKYGBge3J9CnRlc3QgPC0gc2FtcGxlKDU6OSwgNSwgcmVwbGFjZT1GQUxTRSkKdGVzdAp0ZXN0LW1pbih0ZXN0KSsxCmBgYApTaSBhcGxpY2Ftb3MgZXN0YSBmb3JtdWxhIGEgcmFuayBiYWpvIHkgcmFuayBhbHRvIHBhcmEgZGV0ZXJtaW5hciBzaSB0b2RhcyBsYXMgcG9zaWNpb25lcyBlc3RhbiBlbnRyZSAxIHkgNSBvYnRlbmVtb3M6CgpgYGB7cn0KcmFua19hbHRvLW1pbihyYW5rX2FsdG8pKzEgPT0gMTo1CmBgYAoKQSBlc3RlIHJlc3VsdGFkbyBwb2RlbW9zIGFwbGljYXJsZSBsYSBmdW5jaW9uIGBhbGxgIGxhIGN1YWwgb3BlcmEgdW4gYE9SYCBhIHRvZG8gZWwgYXJyZWdsbywgbG8gY3VhbCBub3MgcGVybWl0ZSBkZXRlcm1pbmFyIHNpIGVzIHVuYSBlc2NhbGVyYSBvIG5vLCBoYWdhbW9zIHVuYSBwcnVlYmEKCmBgYHtyfQp0ZXN0IDwtIHNhbXBsZSg1OjksIDUsIHJlcGxhY2U9RkFMU0UpCnRlc3QKc29ydCh0ZXN0LW1pbih0ZXN0KSsxKSA9PSAxOjUKYWxsKHNvcnQodGVzdC1taW4odGVzdCkrMSkgPT0gMTo1KQpgYGAKQmllbiBjb21vIHNlIHZlcmlhIGVzdG8gcGFyYSBkZXRlcm1pbmFyIHNpIGVzIGVzY2FsZXJhOgpgYGB7cn0KYWxsKHNvcnQocmFua19hbHRvLW1pbihyYW5rX2FsdG8pKzEpID09IDE6NSkKYWxsKHNvcnQocmFua19iYWpvLW1pbihyYW5rX2Jham8pKzEpID09IDE6NSkKCmBgYAoKQ29tbyBlc3RvIGhlbW9zIGRldGVybWluYWRvIHNpIHNlIHRyYXRhIGRlIHVuYSBlc2NhbGVyYS4gQWhvcmEgY29tbyBwb2RlbW9zIGRldGVybWluYXIgc2kgZXMgc2kgbGFzIGNhcnRhcyB0aWVuZW4gdW4gc29sbyBtYW5qYXIsIGJ1ZW5vIGVzdG8gZXMgc2VuY2lsbG8gc29sbyBjb250YW1vcyBsb3MgbWFuamFyZXMgcXVlIHRpZW5lIGxhIG1hbm8gc2kgZXN0ZSBlcyBpZ3VhbCBhIHVubyBlcyBxdWUgdGVuZW1vcyB1biBzb2xvIG1hbmphci4KCmBgYHtyfQpsZW5ndGgodW5pcXVlKG1hbm9bLCJtYW5qYXIiXSkpID09IDEKYGBgCgpDb24gZXN0byB5YSBwb2RlbW9zIHByb2JhciBzaSBudWVzdHJhIG1hbm8gc2UgdHJhdGEgZGUgdW5hIGVzY2FsZXJhIGUgaW5jbHVzbyBzaSBzZSB0cmF0YSBkZSB1bmEgZXNjYWxlcmEgcm95YWwuIEFob3JhIHZlYW1vcyBsYSBwcnVlYmEgZGUgc2kgc2UgdHJhdGEgZGUgdW4gcG9rZXIsIHBhcmEgZWxsbyB0YWJ1bGFtb3MgbG9zIGRpZmVyZW50ZXMgbnVtZXJvcyBxdWUgdGVuZW1vcyBlbiBudWVzdHJhIG1hbm86CgpgYGB7cn0KdGFibGUobWFub1ssIm51bWVybyJdKQpgYGAKRXN0byBub3MgYWdydXBhIGxhcyBjYXJ0YXMgeSBzdSBudW1lcm8gZGUgb2N1cnJlbmNpYXMsIGJ1ZW5vIGNvbW8gcG9kZW1vcyBzYWJlciBzaSBzZSB0cmF0YSBkZSB1biBwb2tlciwgcHJvYmVtb3MKCmBgYHtyfQptYW5vIDwtIG1hdHJpeChjKCIyIiwgIkNPUkFaT05FUyIsIjIiLCAiRVNQQURBUyIsIjIiLCAiVFJFQk9MRVMiLCIyIiwgIkRJQU1BTlRFUyIsIjMiLCAiQ09SQVpPTkVTIiksIG5jb2w9MiwgYnlyb3c9VFJVRSwgZGltbmFtZXMgPSBsaXN0KE5VTEwsYygibnVtZXJvIiwgIm1hbmphciIpKSkKbWFubwp0YWJsZShtYW5vWywibnVtZXJvIl0pCmBgYApDb21vIHZlbW9zIHkgcG9kZW1vcyBpbnR1aXIgc2kgc2UgdHJhdGEgZGUgdW5hIHBva2VyLCBsYSBmdW5jaW9uIHRhYmxlLCBzaWVtcHJlIGFycm9qYXJhIHVuIGFycmVnbG8gZGUgNCB5IDEsIGNvbiBsbyBxdWUgcG9kZW1vcyByZWFsaXphcjoKCmBgYHtyfQphbGwoc29ydCh0YWJsZShtYW5vWywibnVtZXJvIl0pKT09YygxLDQpKQpgYGAKWSBhcXVpIHRlbmVtb3MgbnVlc3RyYSB2YWxpZGFjaW9uIGRlIHBva2VyLCBhaG9yYSBpbnRlbmVtb3MgYWxnbyBzaW1pbGFyIHBhcmEgdW4gZnVsbCBob3VzZSwKCmBgYHtyfQptYW5vIDwtIG1hdHJpeChjKCIyIiwgIkNPUkFaT05FUyIsIjIiLCAiRVNQQURBUyIsIjIiLCAiVFJFQk9MRVMiLCIzIiwgIkRJQU1BTlRFUyIsIjMiLCAiQ09SQVpPTkVTIiksIG5jb2w9MiwgYnlyb3c9VFJVRSwgZGltbmFtZXMgPSBsaXN0KE5VTEwsYygibnVtZXJvIiwgIm1hbmphciIpKSkKbWFubwphbGwoc29ydCh0YWJsZShtYW5vWywibnVtZXJvIl0pKSA9PSBjKDIsMykpCmBgYApTaSB0dXZpZXJhbW9zIHNvbGFtZW50ZSB1biB0cmlvIGVzdG8gbm9zIHF1ZWRhCgpgYGB7cn0KbWFubyA8LSBtYXRyaXgoYygiMiIsICJDT1JBWk9ORVMiLCIyIiwgIkVTUEFEQVMiLCIyIiwgIlRSRUJPTEVTIiwiNCIsICJESUFNQU5URVMiLCIzIiwgIkNPUkFaT05FUyIpLCBuY29sPTIsIGJ5cm93PVRSVUUsIGRpbW5hbWVzID0gbGlzdChOVUxMLGMoIm51bWVybyIsICJtYW5qYXIiKSkpCm1hbm8KYWxsKHNvcnQodGFibGUobWFub1ssIm51bWVybyJdKSkgPT0gYygxLDEsMykpCmBgYAoKUGFyYSBkb3MgcGFyZXM6CgpgYGB7cn0KbWFubyA8LSBtYXRyaXgoYygiMiIsICJDT1JBWk9ORVMiLCIyIiwgIkVTUEFEQVMiLCI0IiwgIlRSRUJPTEVTIiwiMyIsICJESUFNQU5URVMiLCIzIiwgIkNPUkFaT05FUyIpLCBuY29sPTIsIGJ5cm93PVRSVUUsIGRpbW5hbWVzID0gbGlzdChOVUxMLGMoIm51bWVybyIsICJtYW5qYXIiKSkpCm1hbm8KYWxsKHNvcnQodGFibGUobWFub1ssIm51bWVybyJdKSkgPT0gYygxLDIsMikpCmBgYApBaG9yYSBxdWUgcGFzYSBzaSBzb2xhbWVudGUgdGVuZW1vcyB1biBwYXIsIGVzdG8gbG8gcG9kZW1vcyBoYWNlciBkZSBkb3MgbWFuZXJhcywgbGEgcHJpbWVyYSB5YSBzZWEgY29tcGFyYW5kbyBjb250cmEgZWwgYXJyZWdsbyBgWzEsMSwxLDJdYCBvIGJpZW4gdmFsaWRhbmRvIHF1ZSBsYSB0YWJsYSB0ZW5nYSBleGFjdGFtZW50ZSA0IGVsZW1lbnRvcywgdmVhbW9zOgoKYGBge3J9Cm1hbm8gPC0gbWF0cml4KGMoIjIiLCAiQ09SQVpPTkVTIiwiMiIsICJFU1BBREFTIiwiNSIsICJUUkVCT0xFUyIsIjciLCAiRElBTUFOVEVTIiwiMyIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKQptYW5vCmFsbChzb3J0KHRhYmxlKG1hbm9bLCJudW1lcm8iXSkpID09IGMoMSwxLDEsMikpCmxlbmd0aCh0YWJsZShtYW5vWywibnVtZXJvIl0pKT09NApgYGAKUG9yIGVzdGFuZGFyIG5vcyBxdWVkYW1vcyBjb24gbGEgY29tcGFyYXRpdmEgZGVsIGFycmVnbG8uIE11eSBiaWVuIGFob3JhIGFybWVtb3MgdW5hIGZ1bmNpb24gcXVlIHZhbGlkZSB0b2RhcyBsYXMgcG9zaWJpbGlkYWRlczoKCmBgYHtyfQpjbGFzaWZpY2FyX21hbm8gPC0gZnVuY3Rpb24obWFubyl7CiAgI0hhZ2Ftb3MgZWwgcmFucXVlbwogIHJhbmtfYWx0byA8LSBjKDI6MTAsICJKIiwiUSIsIksiLCJBIikKICByYW5rX2Jham8gPC0gYygiQSIsMjoxMCwiSiIsIlEiLCJLIikKICAKICAjQWhvcmEgZXN0YWJsZXNjYW1vcyBlbCBvcmRlbgogIG9yZGVuX2FsdG8gPC0gc29ydChzYXBwbHkobWFub1ssIm51bWVybyJdLCBmdW5jdGlvbihjYXJ0YSkgd2hpY2goY2FydGEgPT0gcmFua19hbHRvKSkpCiAgb3JkZW5fYmFqbyA8LSBzb3J0KHNhcHBseShtYW5vWywibnVtZXJvIl0sIGZ1bmN0aW9uKGNhcnRhKSB3aGljaChjYXJ0YSA9PSByYW5rX2FsdG8pKSkKICAKICAjQ3JlYW1vcyB1bmEgdmFyaWFibGUgcGFyYSB2YWxpZGFyIHNpIGVzIGVzY2FsZXJhCiAgZXNfZXNjYWxlcmEgPC0gYWxsKG9yZGVuX2Jham8tbWluKG9yZGVuX2Jham8pKzEgPT0gMTo1KSB8IGFsbChvcmRlbl9hbHRvLW1pbihvcmRlbl9hbHRvKSsxID09IDE6NSkKICAKICAjVmVhbW9zIHNpIHRvZGFzIHNvbiBkZWwgbWlzbW8gbWFuYWphcgogIHNvbl9taXNtb19tYW5qYXIgPC0gbGVuZ3RoKHVuaXF1ZShtYW5vWywibWFuamFyIl0pKSA9PSAxCiAgCiAgI0Fob3JhIHZlYW1vcyBzaSBzZSB0cmF0YSBkZSB1biBwb2tlcgogIHRhYmxhIDwtIHNvcnQodGFibGUobWFub1ssIm51bWVybyJdKSkKICAKICAKICBlc19wb2tlciA8LSBhbGwodGFibGFbMToyXSA9PSBjKDEsNCkpCiAgZXNfZnVsbCA8LSBhbGwodGFibGFbMToyXSA9PSBjKDIsMykpCiAgZXNfdGVyY2lhIDwtIGFsbCh0YWJsYVsxOjNdID09IGMoMSwxLDMpKQogIGVzX2RvYmxlX3BhciA8LSBhbGwodGFibGFbMTozXSA9PSBjKDEsMiwyKSkKICBlc19wYXIgPC0gYWxsKHRhYmxhWzE6NF0gPT0gYygxLDEsMSwyKSkKICAKICBpZihlc19lc2NhbGVyYSAmJiBzb25fbWlzbW9fbWFuamFyKXsKICAgIGlmKGFsbChjKCJLIiwiQSIpICVpbiUgbWFub1ssIm51bWVybyJdKSkgcmV0dXJuKCJFU0NBTEVSQSBST1lBTCIpCiAgICByZXR1cm4oIkVTQ0FMRVJBIERFIENPTE9SIikKICB9CiAgCiAgaWYoZXNfZXNjYWxlcmEpIHJldHVybigiRVNDQUxFUkEiKQogIGlmKHNvbl9taXNtb19tYW5qYXIpIHJldHVybigiTUlTTU8gTUFOSkFSIikKICBpZihlc19wb2tlcikgcmV0dXJuKCJQT0tFUiIpCiAgaWYoZXNfZnVsbCkgcmV0dXJuKCJGVUxMIikKICBpZihlc190ZXJjaWEpIHJldHVybigiVEVSQ0lBIikKICBpZihlc19kb2JsZV9wYXIpIHJldHVybigiRE9CTEUgUEFSIikKICBpZihlc19wYXIpIHJldHVybigiUEFSIikKICByZXR1cm4oIk5BREEiKQp9CmBgYAoKUHJvYmVtb3MgbnVlc3RyYSBmdW5jaW9uOgoKYGBge3J9CmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiMTAiLCAiQ09SQVpPTkVTIiwiSiIsICJDT1JBWk9ORVMiLCJRIiwgIkNPUkFaT05FUyIsIksiLCAiQ09SQVpPTkVTIiwiQSIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiNiIsICJDT1JBWk9ORVMiLCI3IiwgIkNPUkFaT05FUyIsIjgiLCAiQ09SQVpPTkVTIiwiOSIsICJDT1JBWk9ORVMiLCIxMCIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiMTAiLCAiQ09SQVpPTkVTIiwiSiIsICJFU1BBREFTIiwiUSIsICJDT1JBWk9ORVMiLCJLIiwgIlRSRUJPTEVTIiwiQSIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiMTAiLCAiQ09SQVpPTkVTIiwiSiIsICJDT1JBWk9ORVMiLCJRIiwgIkNPUkFaT05FUyIsIjIiLCAiQ09SQVpPTkVTIiwiQSIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiMTAiLCAiQ09SQVpPTkVTIiwiMTAiLCAiRVNQQURBUyIsIjEwIiwgIlRSRUJPTEVTIiwiMTAiLCAiRVNQQURBUyIsIkEiLCAiQ09SQVpPTkVTIiksIG5jb2w9MiwgYnlyb3c9VFJVRSwgZGltbmFtZXMgPSBsaXN0KE5VTEwsYygibnVtZXJvIiwgIm1hbmphciIpKSkpCgpjbGFzaWZpY2FyX21hbm8obWF0cml4KGMoIjEwIiwgIkNPUkFaT05FUyIsIjEwIiwgIkVTUEFEQVMiLCIxMCIsICJUUkVCT0xFUyIsIkEiLCAiRVNQQURBUyIsIkEiLCAiQ09SQVpPTkVTIiksIG5jb2w9MiwgYnlyb3c9VFJVRSwgZGltbmFtZXMgPSBsaXN0KE5VTEwsYygibnVtZXJvIiwgIm1hbmphciIpKSkpCgpjbGFzaWZpY2FyX21hbm8obWF0cml4KGMoIjEwIiwgIkNPUkFaT05FUyIsIjEwIiwgIkVTUEFEQVMiLCIxMCIsICJUUkVCT0xFUyIsIjIiLCAiRVNQQURBUyIsIkEiLCAiQ09SQVpPTkVTIiksIG5jb2w9MiwgYnlyb3c9VFJVRSwgZGltbmFtZXMgPSBsaXN0KE5VTEwsYygibnVtZXJvIiwgIm1hbmphciIpKSkpCgpjbGFzaWZpY2FyX21hbm8obWF0cml4KGMoIjEwIiwgIkNPUkFaT05FUyIsIjEwIiwgIkVTUEFEQVMiLCJBIiwgIlRSRUJPTEVTIiwiNyIsICJFU1BBREFTIiwiQSIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmNsYXNpZmljYXJfbWFubyhtYXRyaXgoYygiMTAiLCAiQ09SQVpPTkVTIiwiMTAiLCAiRVNQQURBUyIsIjciLCAiVFJFQk9MRVMiLCI4IiwgIkVTUEFEQVMiLCJBIiwgIkNPUkFaT05FUyIpLCBuY29sPTIsIGJ5cm93PVRSVUUsIGRpbW5hbWVzID0gbGlzdChOVUxMLGMoIm51bWVybyIsICJtYW5qYXIiKSkpKQoKY2xhc2lmaWNhcl9tYW5vKG1hdHJpeChjKCIxMCIsICJDT1JBWk9ORVMiLCIzIiwgIkVTUEFEQVMiLCI3IiwgIlRSRUJPTEVTIiwiSyIsICJFU1BBREFTIiwiQSIsICJDT1JBWk9ORVMiKSwgbmNvbD0yLCBieXJvdz1UUlVFLCBkaW1uYW1lcyA9IGxpc3QoTlVMTCxjKCJudW1lcm8iLCAibWFuamFyIikpKSkKCmBgYAoKRmluYWxtZW50ZSBwb2RlbW9zIHJlYWxpemFyIGxhIHNpbXVsYWNpb246CmBgYHtyfQpqdWVnb3MgPC0gc2FwcGx5KDE6MTAsIGZ1bmN0aW9uKHgpIGNsYXNpZmljYXJfbWFubyhyZXBhcnRpcigpKSkKanVlZ29zCgpzYXBwbHkodGFibGUoanVlZ29zKQpgYGAKCmBgYHtyfQoKCnNpbXVsYWNpb24gPC0gZnVuY3Rpb24oTj0xMDAwKXsKCiAgcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKG1hbm9fcG9rZXI9YygiRVNDQUxFUkEgUk9ZQUwiLCAiRVNDQUxFUkEgREUgQ09MT1IiLCAiRVNDQUxFUkEiLCAiTUlTTU8gTUFOSkFSIiwgIlBPS0VSIiwgIkZVTEwiLCAiVEVSQ0lBIiwgIkRPQkxFIFBBUiIsICJQQVIiLCAiTkFEQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBvY3VycmVuY2lhcz0gcmVwKDAsMTApKQogIAogIAogIGZvcihpIGluIDE6Til7CiAgICBqdWVnbyA8LSBjbGFzaWZpY2FyX21hbm8ocmVwYXJ0aXIoKSkKICAgIHJlc3VsdGFkb3NbcmVzdWx0YWRvcyRtYW5vX3Bva2VyID09IGp1ZWdvLF0kb2N1cnJlbmNpYXMgPC0gcmVzdWx0YWRvc1tyZXN1bHRhZG9zJG1hbm9fcG9rZXIgPT0ganVlZ28sXSRvY3VycmVuY2lhcyArIDEKICB9CiAgCiAgcmVzdWx0YWRvcyRwb3JjZW50YWplIDwtIHJlc3VsdGFkb3Mkb2N1cnJlbmNpYXMvTgogIHJldHVybihyZXN1bHRhZG9zKQp9CgpzaW11bGFjaW9uKDUwMDApCmBgYAoKUXVlIHBvZGVtb3MgY29uY2x1aXIgZGUgZXN0b3MgcmVzdWx0YWRvcywgcXVlIGRlIGNhZGEgNSBtaWwganVlZ29zLCBlbCA1MCUgbm8gb2J0ZW5kcmVtb3MgbmFkYSwgZWwgNDElIHRlbmRyZW1vcyB1biBwYXIsIGVsIDQlIG9idGVuZHJlbW9zIDIgcGFyZXMsIGVsIDMlIHRlcmNpYS4gTGEgZXNjYWxlcmEgcGFyZWNpZXJhIHRlbmVyIG1hcyBwcm9iYWJpbGlkYWRlcywgcGVybyBsYSBlc2NhbGVyYSBkZSBjb2xvciB5IGxhIGVzY2FsZXJhIHJveWFsIHBhcmVjZW4gY2FzaSBpbXBvc2libGVzIGRlIHNhbGlyLg==