BIBLIOTECAS

library(magrittr)
library(combinat)

CONFIGURAÇÕES INICIAIS

‘A’ é a matriz de restrições, de tamanho m x n (m = nº de restrições e n = número de variáveis, na forma padrão). ‘b’ é o vetor right hand side, conjunto de valores numéricos do lado direito da igualdade. ‘f_obj’ é o vetor de coeficientes da função objetivo, chamado de c na teoria. Neste exemplo, o sistema tem duas incógnitas, descontadas as incógnitas de folga, e três restrições (questão 4a).

# nomes das variáveis
var_names <- c("x1","x2","xf1","xf2","xf3")

# montagem de A
A <- rbind(c(1,0,1,0,0),
           c(0,1,0,1,0),
           c(4,-2,0,0,1))
colnames(A) <- var_names

# montagem de b
b <- matrix(c(6,8,10),ncol=1)

# montagem de f_obj
f_obj <- c(-4,-7,0,0,0)

# m: nº de restrições
# n: nº de variáveis
m <- nrow(A)
n <- ncol(A)

cat("Matriz A: \n");print(A);cat("\n")
Matriz A: 
     x1 x2 xf1 xf2 xf3
[1,]  1  0   1   0   0
[2,]  0  1   0   1   0
[3,]  4 -2   0   0   1
cat("Vetor b: \n");print(b);cat("\n")
Vetor b: 
     [,1]
[1,]    6
[2,]    8
[3,]   10
cat("Vetor c: \n");print(f_obj);cat("\n")
Vetor c: 
[1] -4 -7  0  0  0

DEFINIÇÃO DA MATRIZ ‘B’ E MULTIPLICADOR SIMPLEX

A matriz ‘B’ representa uma matriz LI de um sistema do tipo B.x = b tal que x é uma solução factível (i.e., um vetor de elementos não-negativos). Portanto, a escolha de ‘B’ deve satisfazer: a) det(B) != 0 e b) (B^-1).b não tem elementos negativos. O código a seguir testa as permutações do conjunto {1,2,3,…,n} das colunas de ‘A’ até encontrar ‘B’ adequada:

# todas as combinações possíveis de m colunas de A 
comb = combn(1:n,m)
# busca de uma matriz básica factível
  # processo dura, no máximo, até o nº de permutações
  for (i in 1:ncol(comb)){
    # estimativa inicial da matriz básica
    B <- A[,comb[,i]]
    
    # condição 1: det(B) != 0
    if (det(B) != 0){
      xB = inv(B) %*% b
      
      # condição 2: não-negatividade da solução básica inicial
      if (sum(xB<0)==0) {
        i_sel <- i
        # encerramento da busca
        break
      }
    }
  }
cat("Combinações: \n");print(comb);cat("\n")
Combinações: 
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    1    1    1    1    1    2    2    2     3
[2,]    2    2    2    3    3    4    3    3    4     4
[3,]    3    4    5    4    5    5    4    5    5     5
cat("Nº de iterações para achar 'B': ");cat(i_sel);cat("\n\n")
Nº de iterações para achar 'B': 2
cat("Matriz básica inicial: \n")
Matriz básica inicial: 
print(B);cat("\n")
     x1 x2 xf2
[1,]  1  0   0
[2,]  0  1   1
[3,]  4 -2   0
cat("Solução básica inicial: \n")
Solução básica inicial: 
print(xB)
    [,1]
x1     6
x2     7
xf2    1

Como foi achada na segunda iteração, a matriz ‘B’ corresponde ao índices da segunda coluna da matriz de permutações (colunas 1, 2 e 4 de A). De posse da matriz básica, é hora de calcular o multiplicador simplex, simplesmente multiplicando os coeficientes da função objetivo correspondentes às variáveis básicas (aquelas de índice 1, 2 e 4, correspondentes aos índices das colunas que construíram ‘B’. No nosso exemplo, corresponde às variáveis x1, x2 e xf2):

# coeficientes de custo relativo das variáveis básicas  
  cB = f_obj[comb[,i_sel]]
  
  # multiplicador simplex
  lambdaT = matrix(cB,nrow = 1) %*% inv(B)
  
  # exibição do multiplicador da iteração
  cat("Multiplicador simplex:\n");print(lambdaT);cat("\n")
Multiplicador simplex:
     [,1] [,2] [,3]
[1,]  -18    0  3.5

CUSTOS RELATIVOS

Nos interessam os custos associados às variáveis nulas (como ‘xB’ foi encontrado considerando que as variáveis remanescentes - 3 e 5, xf1 e xf2 no nosso exemplo - são nulas, tudo relativo a elas recebe o adjetivo “nulo”). Esse custo é simplesmente os coeficientes da função objetivo associados às variáveis não-básicas (nulas), de índices 3 e 5, menos lambdaT.N, onde N é formado pelas colunas 3 e 5 de A. lambdaT é o multiplicador simplex do passo anterior:

# índice das variáveis nulas
  sel = 1:ncol(A)
  sel = sel[-comb[,i_sel]]
  
  # coeficientes de custo relativo das variáveis nulas  
  cN <- f_obj[sel] - lambdaT %*% A[,sel]
  
  # exibição dos coeficientes de custo relativo das variáveis nulas  
  cat("Índices das variáveis nulas:\n");print(sel);cat("\n")
Índices das variáveis nulas:
[1] 3 5
  cat("Custos relativos cN:\n");print(cN);cat("\n")
Custos relativos cN:
     xf1  xf3
[1,]  18 -3.5

CONDIÇÕES DE PARADA E OTIMIZAÇÃO DA SOLUÇÃO INICIAL

O algoritmo encerra quando todos os custos da variáveis nulas são maiores que 0 (para o caso de minimização). No nosso exemplo, isto não se configura, e já sabemos que haverá outra iteração. Caso tivéssemos cN >= 0 em todos os elementos, ‘xB’ já seria ótima.

A otimização começa pela determinação da variável de mínimo custo, a de índice 5 (xf3) no nosso exemplo. a direção simplex é o produto da inversa de B pela coluna 5, equivalente à variável de menor custo:

# índice do mínimo custo relativo de cN
  k = which(cN == min(cN,na.rm = T))
# direção simplex
    y = inv(B) %*% matrix(A[,sel[k]],ncol=1)
    cat("Direção simplex y:\n");print(y);cat("\n")
Direção simplex y:
    [,1]
x1   0.0
x2  -0.5
xf2  0.5

Se não houvesse elemento de ‘y’ estritamente positivo (>0), o problema seria ilimitado; no nosso exemplo, temos apenas o terceiro elemento de y satisfazendo essa condição. Com todos os elementos de ‘y’ maiores que zero, construímos o vetor ‘eps’ composto pelos quocientes entre ‘xB’ e ‘y’ (novamente, somente os elementos de ‘xB’ que correspondem aos elementos de ‘y’ maiores que 0):

# seleção de y>0
      sel_y <- y>0
      # passo
      eps = xB[sel_y]/y[sel_y]
cat("Passo mínimo: ");cat(eps)
Passo mínimo: 2

Agora selecionamos o menor passo mínimo, trivial no nosso exemplo, posto que só há um elemento. Esse elemento corresponde à variável básica dessa iteração que será substituída na próxima:

# índice do passo mínimo
      l = which(eps == min(eps))
      
      # seleção da variável substituída
      sel2 <- (((1:n)[-sel])[sel_y])[l]
cat("Variável substituída: ");cat(sel2);cat(", ");cat(var_names[sel2])
Variável substituída: 4, xf2

Para que xf2 possa sair, alguma terá que entrar, e será aquela com o menor custo mínimo dentre as variáveis nulas:

# coluna que vai sair da base
      L = A[,sel2]
      nL <- var_names[sel2]
# coluna que vai entrar da base
      K = A[,sel[k]]
      nK <- var_names[sel[k]]
      cat("Variável de substituição: ");cat(sel[k]);cat(", ");cat(nK)
Variável de substituição: 5, xf3

Agora, resta apenas proceder à substituição e recomeçar o processo, até que não haja mais custos de variáveis nulas negativos:

# substituição das colunas
      A[,sel[k]] = L
      A[,sel2] = K
      colnames(A)[sel[k]] <- nL
      colnames(A)[sel2] <- nK
      
      cat("Nova matriz A:\n");print(A);cat("\n---------------------\n\n")
Nova matriz A:
     x1 x2 xf1 xf3 xf2
[1,]  1  0   1   0   0
[2,]  0  1   0   0   1
[3,]  4 -2   0   1   0

---------------------

Note que houve a inversão na prioridade de busca entre ‘xf2’ e ‘xf3’. Como o sistema implementado aqui é sequencial, as colunas 1, 2 e 3 de ‘A’ serão descartadas, como na iteração anterior, e, de novo, as colunas 1, 2 e 4 serão selecionadas, mas agora com ‘xf3’ ocupando a 4ª posição. Num cálculo manual, bastaria substituir a última coluna de ‘B’, (0 1 0)‘, por (0 0 1)’ e executar os passos. A partir daqui, volta-se à seção # DEFINIÇÃO DA MATRIZ ‘B’ E MULTIPLICADOR SIMPLEX.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQogIAojIEJJQkxJT1RFQ0FTCmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGNvbWJpbmF0KQpgYGAKCiMgQ09ORklHVVJBw4fDlUVTIElOSUNJQUlTCgonQScgw6kgYSBtYXRyaXogZGUgcmVzdHJpw6fDtWVzLCBkZSB0YW1hbmhvIG0geCBuIChtID0gbsK6IGRlIHJlc3RyacOnw7VlcyBlIG4gPSBuw7ptZXJvIGRlIHZhcmnDoXZlaXMsIG5hIGZvcm1hIHBhZHLDo28pLiAnYicgw6kgbyB2ZXRvciA8aT5yaWdodCBoYW5kIHNpZGU8L2k+LCBjb25qdW50byBkZSB2YWxvcmVzIG51bcOpcmljb3MgZG8gbGFkbyBkaXJlaXRvIGRhIGlndWFsZGFkZS4gJ2Zfb2JqJyDDqSBvIHZldG9yIGRlIGNvZWZpY2llbnRlcyBkYSBmdW7Dp8OjbyBvYmpldGl2bywgY2hhbWFkbyBkZSA8aT5jPC9pPiBuYSB0ZW9yaWEuIE5lc3RlIGV4ZW1wbG8sIG8gc2lzdGVtYSB0ZW0gZHVhcyBpbmPDs2duaXRhcywgZGVzY29udGFkYXMgYXMgaW5jw7Nnbml0YXMgZGUgZm9sZ2EsIGUgdHLDqnMgcmVzdHJpw6fDtWVzIChxdWVzdMOjbyA0YSkuIAoKYGBge3J9CiMgbm9tZXMgZGFzIHZhcmnDoXZlaXMKdmFyX25hbWVzIDwtIGMoIngxIiwieDIiLCJ4ZjEiLCJ4ZjIiLCJ4ZjMiKQoKIyBtb250YWdlbSBkZSBBCkEgPC0gcmJpbmQoYygxLDAsMSwwLDApLAogICAgICAgICAgIGMoMCwxLDAsMSwwKSwKICAgICAgICAgICBjKDQsLTIsMCwwLDEpKQpjb2xuYW1lcyhBKSA8LSB2YXJfbmFtZXMKCiMgbW9udGFnZW0gZGUgYgpiIDwtIG1hdHJpeChjKDYsOCwxMCksbmNvbD0xKQoKIyBtb250YWdlbSBkZSBmX29iagpmX29iaiA8LSBjKC00LC03LDAsMCwwKQoKIyBtOiBuwrogZGUgcmVzdHJpw6fDtWVzCiMgbjogbsK6IGRlIHZhcmnDoXZlaXMKbSA8LSBucm93KEEpCm4gPC0gbmNvbChBKQoKY2F0KCJNYXRyaXogQTogXG4iKTtwcmludChBKTtjYXQoIlxuIikKY2F0KCJWZXRvciBiOiBcbiIpO3ByaW50KGIpO2NhdCgiXG4iKQpjYXQoIlZldG9yIGM6IFxuIik7cHJpbnQoZl9vYmopO2NhdCgiXG4iKQoKYGBgCgojIERFRklOScOHw4NPIERBIE1BVFJJWiAnQicgRSBNVUxUSVBMSUNBRE9SIFNJTVBMRVgKCkEgbWF0cml6ICdCJyByZXByZXNlbnRhIHVtYSBtYXRyaXogTEkgZGUgdW0gc2lzdGVtYSBkbyB0aXBvIEIueCA9IGIgdGFsIHF1ZSB4IMOpIHVtYSBzb2x1w6fDo28gZmFjdMOtdmVsIChpLmUuLCB1bSB2ZXRvciBkZSBlbGVtZW50b3MgbsOjby1uZWdhdGl2b3MpLiBQb3J0YW50bywgYSBlc2NvbGhhIGRlICdCJyBkZXZlIHNhdGlzZmF6ZXI6IGEpICBkZXQoQikgIT0gMCBlIGIpIChCXi0xKS5iIG7Do28gdGVtIGVsZW1lbnRvcyBuZWdhdGl2b3MuIE8gY8OzZGlnbyBhIHNlZ3VpciB0ZXN0YSBhcyBwZXJtdXRhw6fDtWVzIGRvIGNvbmp1bnRvIHsxLDIsMywuLi4sbn0gZGFzIGNvbHVuYXMgZGUgJ0EnIGF0w6kgZW5jb250cmFyICdCJyBhZGVxdWFkYToKYGBge3J9CiMgdG9kYXMgYXMgY29tYmluYcOnw7VlcyBwb3Nzw612ZWlzIGRlIG0gY29sdW5hcyBkZSBBIApjb21iID0gY29tYm4oMTpuLG0pCiMgYnVzY2EgZGUgdW1hIG1hdHJpeiBiw6FzaWNhIGZhY3TDrXZlbAogICMgcHJvY2Vzc28gZHVyYSwgbm8gbcOheGltbywgYXTDqSBvIG7CuiBkZSBwZXJtdXRhw6fDtWVzCiAgZm9yIChpIGluIDE6bmNvbChjb21iKSl7CiAgICAjIGVzdGltYXRpdmEgaW5pY2lhbCBkYSBtYXRyaXogYsOhc2ljYQogICAgQiA8LSBBWyxjb21iWyxpXV0KICAgIAogICAgIyBjb25kacOnw6NvIDE6IGRldChCKSAhPSAwCiAgICBpZiAoZGV0KEIpICE9IDApewogICAgICB4QiA9IGludihCKSAlKiUgYgogICAgICAKICAgICAgIyBjb25kacOnw6NvIDI6IG7Do28tbmVnYXRpdmlkYWRlIGRhIHNvbHXDp8OjbyBiw6FzaWNhIGluaWNpYWwKICAgICAgaWYgKHN1bSh4QjwwKT09MCkgewogICAgICAgIGlfc2VsIDwtIGkKICAgICAgICAjIGVuY2VycmFtZW50byBkYSBidXNjYQogICAgICAgIGJyZWFrCiAgICAgIH0KICAgIH0KICB9CmNhdCgiQ29tYmluYcOnw7VlczogXG4iKTtwcmludChjb21iKTtjYXQoIlxuIikKY2F0KCJOwrogZGUgaXRlcmHDp8O1ZXMgcGFyYSBhY2hhciAnQic6ICIpO2NhdChpX3NlbCk7Y2F0KCJcblxuIikKY2F0KCJNYXRyaXogYsOhc2ljYSBpbmljaWFsOiBcbiIpCnByaW50KEIpO2NhdCgiXG4iKQpjYXQoIlNvbHXDp8OjbyBiw6FzaWNhIGluaWNpYWw6IFxuIikKcHJpbnQoeEIpCgpgYGAKCkNvbW8gZm9pIGFjaGFkYSBuYSBzZWd1bmRhIGl0ZXJhw6fDo28sIGEgbWF0cml6ICdCJyBjb3JyZXNwb25kZSBhbyDDrW5kaWNlcyBkYSBzZWd1bmRhIGNvbHVuYSBkYSBtYXRyaXogZGUgcGVybXV0YcOnw7VlcyAoY29sdW5hcyAxLCAyIGUgNCBkZSBBKS4gRGUgcG9zc2UgZGEgbWF0cml6IGLDoXNpY2EsIMOpIGhvcmEgZGUgY2FsY3VsYXIgbyBtdWx0aXBsaWNhZG9yIHNpbXBsZXgsIHNpbXBsZXNtZW50ZSBtdWx0aXBsaWNhbmRvIG9zIGNvZWZpY2llbnRlcyBkYSBmdW7Dp8OjbyBvYmpldGl2byBjb3JyZXNwb25kZW50ZXMgw6BzIHZhcmnDoXZlaXMgYsOhc2ljYXMgKGFxdWVsYXMgZGUgw61uZGljZSAxLCAyIGUgNCwgY29ycmVzcG9uZGVudGVzIGFvcyDDrW5kaWNlcyBkYXMgY29sdW5hcyBxdWUgY29uc3RydcOtcmFtICdCJy4gTm8gbm9zc28gZXhlbXBsbywgY29ycmVzcG9uZGUgw6BzIHZhcmnDoXZlaXMgeDEsIHgyIGUgeGYyKToKCmBgYHtyfQojIGNvZWZpY2llbnRlcyBkZSBjdXN0byByZWxhdGl2byBkYXMgdmFyacOhdmVpcyBiw6FzaWNhcyAgCiAgY0IgPSBmX29ialtjb21iWyxpX3NlbF1dCiAgCiAgIyBtdWx0aXBsaWNhZG9yIHNpbXBsZXgKICBsYW1iZGFUID0gbWF0cml4KGNCLG5yb3cgPSAxKSAlKiUgaW52KEIpCiAgCiAgIyBleGliacOnw6NvIGRvIG11bHRpcGxpY2Fkb3IgZGEgaXRlcmHDp8OjbwogIGNhdCgiTXVsdGlwbGljYWRvciBzaW1wbGV4OlxuIik7cHJpbnQobGFtYmRhVCk7Y2F0KCJcbiIpCmBgYAoKIyBDVVNUT1MgUkVMQVRJVk9TCgpOb3MgaW50ZXJlc3NhbSBvcyBjdXN0b3MgYXNzb2NpYWRvcyDDoHMgdmFyacOhdmVpcyBudWxhcyAoY29tbyAneEInIGZvaSBlbmNvbnRyYWRvIGNvbnNpZGVyYW5kbyBxdWUgYXMgdmFyacOhdmVpcyByZW1hbmVzY2VudGVzIC0gMyBlIDUsIHhmMSBlIHhmMiBubyBub3NzbyBleGVtcGxvIC0gc8OjbyBudWxhcywgdHVkbyByZWxhdGl2byBhIGVsYXMgcmVjZWJlIG8gYWRqZXRpdm8gIm51bG8iKS4gRXNzZSBjdXN0byDDqSBzaW1wbGVzbWVudGUgb3MgY29lZmljaWVudGVzIGRhIGZ1bsOnw6NvIG9iamV0aXZvIGFzc29jaWFkb3Mgw6BzIHZhcmnDoXZlaXMgbsOjby1iw6FzaWNhcyAobnVsYXMpLCBkZSDDrW5kaWNlcyAzIGUgNSwgbWVub3MgbGFtYmRhVC5OLCBvbmRlIE4gw6kgZm9ybWFkbyBwZWxhcyBjb2x1bmFzIDMgZSA1IGRlIEEuIGxhbWJkYVQgw6kgbyBtdWx0aXBsaWNhZG9yIHNpbXBsZXggZG8gcGFzc28gYW50ZXJpb3I6CgpgYGB7cn0KIyDDrW5kaWNlIGRhcyB2YXJpw6F2ZWlzIG51bGFzCiAgc2VsID0gMTpuY29sKEEpCiAgc2VsID0gc2VsWy1jb21iWyxpX3NlbF1dCiAgCiAgIyBjb2VmaWNpZW50ZXMgZGUgY3VzdG8gcmVsYXRpdm8gZGFzIHZhcmnDoXZlaXMgbnVsYXMgIAogIGNOIDwtIGZfb2JqW3NlbF0gLSBsYW1iZGFUICUqJSBBWyxzZWxdCiAgCiAgIyBleGliacOnw6NvIGRvcyBjb2VmaWNpZW50ZXMgZGUgY3VzdG8gcmVsYXRpdm8gZGFzIHZhcmnDoXZlaXMgbnVsYXMgIAogIGNhdCgiw41uZGljZXMgZGFzIHZhcmnDoXZlaXMgbnVsYXM6XG4iKTtwcmludChzZWwpO2NhdCgiXG4iKQogIGNhdCgiQ3VzdG9zIHJlbGF0aXZvcyBjTjpcbiIpO3ByaW50KGNOKTtjYXQoIlxuIikKYGBgCgojIENPTkRJw4fDlUVTIERFIFBBUkFEQSBFIE9USU1JWkHDh8ODTyBEQSBTT0xVw4fDg08gSU5JQ0lBTAoKTyBhbGdvcml0bW8gZW5jZXJyYSBxdWFuZG8gdG9kb3Mgb3MgY3VzdG9zIGRhIHZhcmnDoXZlaXMgbnVsYXMgc8OjbyBtYWlvcmVzIHF1ZSAwIChwYXJhIG8gY2FzbyBkZSBtaW5pbWl6YcOnw6NvKS4gTm8gbm9zc28gZXhlbXBsbywgaXN0byBuw6NvIHNlIGNvbmZpZ3VyYSwgZSBqw6Egc2FiZW1vcyBxdWUgaGF2ZXLDoSBvdXRyYSBpdGVyYcOnw6NvLiBDYXNvIHRpdsOpc3NlbW9zIGNOID49IDAgZW0gdG9kb3Mgb3MgZWxlbWVudG9zLCAneEInIGrDoSBzZXJpYSDDs3RpbWEuCgpBIG90aW1pemHDp8OjbyBjb21lw6dhIHBlbGEgZGV0ZXJtaW5hw6fDo28gZGEgdmFyacOhdmVsIGRlIG3DrW5pbW8gY3VzdG8sIGEgZGUgw61uZGljZSA1ICh4ZjMpIG5vIG5vc3NvIGV4ZW1wbG8uIGEgPGk+ZGlyZcOnw6NvIHNpbXBsZXg8L2k+IMOpIG8gcHJvZHV0byBkYSBpbnZlcnNhIGRlIEIgcGVsYSBjb2x1bmEgNSwgZXF1aXZhbGVudGUgw6AgdmFyacOhdmVsIGRlIG1lbm9yIGN1c3RvOgoKYGBge3J9CiMgw61uZGljZSBkbyBtw61uaW1vIGN1c3RvIHJlbGF0aXZvIGRlIGNOCiAgayA9IHdoaWNoKGNOID09IG1pbihjTixuYS5ybSA9IFQpKQojIGRpcmXDp8OjbyBzaW1wbGV4CiAgICB5ID0gaW52KEIpICUqJSBtYXRyaXgoQVssc2VsW2tdXSxuY29sPTEpCiAgICBjYXQoIkRpcmXDp8OjbyBzaW1wbGV4IHk6XG4iKTtwcmludCh5KTtjYXQoIlxuIikKYGBgCgpTZSBuw6NvIGhvdXZlc3NlIGVsZW1lbnRvIGRlICd5JyBlc3RyaXRhbWVudGUgcG9zaXRpdm8gKD4wKSwgbyBwcm9ibGVtYSBzZXJpYSBpbGltaXRhZG87IG5vIG5vc3NvIGV4ZW1wbG8sIHRlbW9zIGFwZW5hcyBvIHRlcmNlaXJvIGVsZW1lbnRvIGRlIHkgc2F0aXNmYXplbmRvIGVzc2EgY29uZGnDp8Ojby4gQ29tIHRvZG9zIG9zIGVsZW1lbnRvcyBkZSAneScgbWFpb3JlcyBxdWUgemVybywgY29uc3RydcOtbW9zIG8gdmV0b3IgJ2VwcycgY29tcG9zdG8gcGVsb3MgcXVvY2llbnRlcyBlbnRyZSAneEInIGUgJ3knIChub3ZhbWVudGUsIHNvbWVudGUgb3MgZWxlbWVudG9zIGRlICd4QicgcXVlIGNvcnJlc3BvbmRlbSBhb3MgZWxlbWVudG9zIGRlICd5JyBtYWlvcmVzIHF1ZSAwKToKCmBgYHtyfQojIHNlbGXDp8OjbyBkZSB5PjAKICAgICAgc2VsX3kgPC0geT4wCiAgICAgICMgcGFzc28KICAgICAgZXBzID0geEJbc2VsX3ldL3lbc2VsX3ldCmNhdCgiUGFzc28gbcOtbmltbzogIik7Y2F0KGVwcykKYGBgCgpBZ29yYSBzZWxlY2lvbmFtb3MgbyBtZW5vciBwYXNzbyBtw61uaW1vLCB0cml2aWFsIG5vIG5vc3NvIGV4ZW1wbG8sIHBvc3RvIHF1ZSBzw7MgaMOhIHVtIGVsZW1lbnRvLiBFc3NlIGVsZW1lbnRvIGNvcnJlc3BvbmRlIMOgIHZhcmnDoXZlbCBiw6FzaWNhIGRlc3NhIGl0ZXJhw6fDo28gcXVlIHNlcsOhIHN1YnN0aXR1w61kYSBuYSBwcsOzeGltYToKCmBgYHtyfQojIMOtbmRpY2UgZG8gcGFzc28gbcOtbmltbwogICAgICBsID0gd2hpY2goZXBzID09IG1pbihlcHMpKQogICAgICAKICAgICAgIyBzZWxlw6fDo28gZGEgdmFyacOhdmVsIHN1YnN0aXR1w61kYQogICAgICBzZWwyIDwtICgoKDE6bilbLXNlbF0pW3NlbF95XSlbbF0KY2F0KCJWYXJpw6F2ZWwgc3Vic3RpdHXDrWRhOiAiKTtjYXQoc2VsMik7Y2F0KCIsICIpO2NhdCh2YXJfbmFtZXNbc2VsMl0pCmBgYAoKUGFyYSBxdWUgeGYyIHBvc3NhIHNhaXIsIGFsZ3VtYSB0ZXLDoSBxdWUgZW50cmFyLCBlIHNlcsOhIGFxdWVsYSBjb20gbyBtZW5vciBjdXN0byBtw61uaW1vIGRlbnRyZSBhcyB2YXJpw6F2ZWlzIG51bGFzOgoKYGBge3J9CiMgY29sdW5hIHF1ZSB2YWkgc2FpciBkYSBiYXNlCiAgICAgIEwgPSBBWyxzZWwyXQogICAgICBuTCA8LSB2YXJfbmFtZXNbc2VsMl0KIyBjb2x1bmEgcXVlIHZhaSBlbnRyYXIgZGEgYmFzZQogICAgICBLID0gQVssc2VsW2tdXQogICAgICBuSyA8LSB2YXJfbmFtZXNbc2VsW2tdXQogICAgICBjYXQoIlZhcmnDoXZlbCBkZSBzdWJzdGl0dWnDp8OjbzogIik7Y2F0KHNlbFtrXSk7Y2F0KCIsICIpO2NhdChuSykKYGBgCgpBZ29yYSwgcmVzdGEgYXBlbmFzIHByb2NlZGVyIMOgIHN1YnN0aXR1acOnw6NvIGUgcmVjb21lw6dhciBvIHByb2Nlc3NvLCBhdMOpIHF1ZSBuw6NvIGhhamEgbWFpcyBjdXN0b3MgZGUgdmFyacOhdmVpcyBudWxhcyBuZWdhdGl2b3M6CgpgYGB7cn0KIyBzdWJzdGl0dWnDp8OjbyBkYXMgY29sdW5hcwogICAgICBBWyxzZWxba11dID0gTAogICAgICBBWyxzZWwyXSA9IEsKICAgICAgY29sbmFtZXMoQSlbc2VsW2tdXSA8LSBuTAogICAgICBjb2xuYW1lcyhBKVtzZWwyXSA8LSBuSwogICAgICAKICAgICAgY2F0KCJOb3ZhIG1hdHJpeiBBOlxuIik7cHJpbnQoQSk7Y2F0KCJcbi0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4iKQpgYGAKCk5vdGUgcXVlIGhvdXZlIGEgaW52ZXJzw6NvIG5hIHByaW9yaWRhZGUgZGUgYnVzY2EgZW50cmUgJ3hmMicgZSAneGYzJy4gQ29tbyBvIHNpc3RlbWEgaW1wbGVtZW50YWRvIGFxdWkgw6kgc2VxdWVuY2lhbCwgYXMgY29sdW5hcyAxLCAyIGUgMyBkZSAnQScgc2Vyw6NvIGRlc2NhcnRhZGFzLCBjb21vIG5hIGl0ZXJhw6fDo28gYW50ZXJpb3IsIGUsIGRlIG5vdm8sIGFzIGNvbHVuYXMgMSwgMiBlIDQgc2Vyw6NvIHNlbGVjaW9uYWRhcywgbWFzIGFnb3JhIGNvbSAneGYzJyBvY3VwYW5kbyBhIDTCqiBwb3Npw6fDo28uIE51bSBjw6FsY3VsbyBtYW51YWwsIGJhc3RhcmlhIHN1YnN0aXR1aXIgYSDDumx0aW1hIGNvbHVuYSBkZSAnQicsICgwIDEgMCknLCBwb3IgKDAgMCAxKScgZSBleGVjdXRhciBvcyBwYXNzb3MuIEEgcGFydGlyIGRhcXVpLCB2b2x0YS1zZSDDoCBzZcOnw6NvICMgREVGSU5Jw4fDg08gREEgTUFUUklaICdCJyBFIE1VTFRJUExJQ0FET1IgU0lNUExFWC4=