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=