Este documento tem como objetivo contribuir para o entendimento de como resolver problemas de otimização não-condicionada no R. Para tanto, vamos aprender os seguintes pontos:
- Como usar a abordagem da derivadas parciais para solucionar problemas com apenas uma variável de escolha
- Encontrar as condições de primeira e segunda ordem;
- Obter os valores máximos e mínimos da função objetivo;
- Gerar gráficos com os resultados.
- Como usar a abordagem de diferencial total para solucionar problemas com \(N\) variáveis
- Encontrar a solução do problema usando a função
optim
- Testar se esta função encontra resultados semelhantes aos obtidos por meio dos determinantes dos menores principais líderes da matriz hessiana avaliada nos pontos críticos.
ABORDAGEM DAS DERIVADAS PARCIAIS
Como estudamos em sala de aula, uma alternativa para encontrar os valores críticos (candidatos à máximo e/ou mínimo) de uma função é fazer uso das derivadas parciais. Neste sentido, entendemos que igualar a derivada parcial de primeira ordem nos permite encontrar os valores críticos (chamamos tal procedimento de condição necessária de primeira ordem, CPO). Por outro lado, avaliar o sinal da derivada parcial de segunda ordem nos pontos críticos nos fornece a conclusão se o valor crítico é ponto de máximo, mínimo e/ou inflexão.
No decorrer desta seção, vamos entender como usar o pacote rootSolve
para resolver problemas de otimização não-condicionada com 1 variável de escolha por meio da abordagem das derivadas parciais. Para tanto, vamos usar como exemplo a função \(y = x^{3} + 3x^{2} - 6x -8\). Observe o formato da função para o domínio \(-5<x<4\) no gráfico abaixo.

- CONDIÇÃO DE PRIMEIRA ORDEM (CPO)
Um dos passos iniciais em qualquer problema de otimização é encontrar condições necessárias e suficientes para o problema em questão. Ou seja, encontrar as raízes responsáveis por atender a condição \(f'(x=x_{0}) = 0\), também conhecida como condição de primeira ordem (CPO).
No código abaixo, mostramos como fazer uso do R para encontrar os valores críticos, ou seja, valor de \(x_{0}\) que torna a primeira derivada igual a zero.
######
### PACOTES NECESSÁRIOS
######
# Instalar pacotes (executar apenas uma vez)
# install.packages("rootSolve")
# Carregar os pacotes no ambiente
suppressMessages(require(rootSolve))
######
### FUNÇÃO OBJETIVO
######
# A função que analisaremos
y = function(x) (x^3 + 3*x^2 - 6*x - 8)
######
### CONDIÇÃO DE PRIMEIRA ORDEM
######
# função vazia que receberá o resultado da primeira derivada
derivada1 = function(x){}
# resultado da derivação usando as funções body e D. body permite acessar uma função
# seja para gravar o resultado ou para pegar a função. D aplica a derivação
body(derivada1) = D(body(y), 'x')
# Encontrar as raizes que tornam a primeira derivada igual a zero para o intervalo -5<x<4
roots = rootSolve::uniroot.all(derivada1, c(-5, 4))
Como resultado encontramos que os valores críticos são -2.73 e 0.73. Agora, precisamos avaliar se eles são pontos de mínimo, máximo ou sela.
- CONDIÇÃO DE SEGUNDA ORDEM (CSO)
Assim, precisamos verificar a condição de segunda ordem que verifica se a segunda derivada avaliada nas raízes da condição necessária (\(f"(x=x_{0})\)) é positiva, negativa ou igual a zero. Abaixo, exemplo de como encontrar as condições de primeira e segunda ordem
# Função que armazenará o resultado da derivada de segunda ordem
derivada2 = function(x){}
# Obtendo a derivada segunda
body(derivada2) = D(body(derivada1), "x")
# Encontrar o valor da derivada segunda para os valores críticos. Como
# estudamos, valores críticos que geram f"(x)<0 são pontos de máximo e
# valores críticos que geram f"(x)>0 são ponto de mínimo. Assim, pelos
# resultados esperamos que a primeira raiz seja um máximo e a segunda um
# mínimo.
derivada2(roots)
[1] -10.39229 10.39228
# Avaliar a função nas raízes da condição necessária (f'(x)=0).
# Aqui, usamos y() porque definimos a função como y. Observe que
# confirmamos que a primeira raíz gera um máximo e a segunda um mínimo
y(roots)
[1] 10.3923 -10.3923
- GRÁFICO COM OS VALORES DE MÁXIMO E/OU MÍNIMO RELATIVOS
Uma vez que testamos as condições de primeira e segunda ordem e temos os valores críticos da condição necessária, \(f'(x=x_{0}) = 0\), avaliados na condição suficiente, \(f"(x=x_{0})\), podemos definir o máximo e/ou mínimo. O código abaixo mostra como criar um gráfico que nos mostra tais avlores.
# Usamos a função curve que desenha a função. Seus parâmetros são:
# - expr: a função a ser plotada
# - from: valor inferior do intervalo
# - to: valor superior do intervalo
# - type: tipo de gráfico (l: em linha)
# - ylab: nome do eixo vertical
# - xlab: nome do eixo horizontal
curve(expr = y, from = -5, to = 4, type = "l", ylab = "y=f(x)", xlab = "")
# Após isso, adicionamos as raízes para a função no intervalo. Usamos
# a função points que tem os seguintes parâmetros:
# - x: a variável do eixo verticial
# - y: a variável do eixo horizontal
# Observe que a função colocará um ponto nas coordenadas (x,y)
points(y(roots) ~ roots)

ABORDAGEM DE DIFERENCIAL TOTAL
Como estudamos em sala de aula, resolver problemas de otimização não-condicionada com \(N\) variáveis de escolha por meio da abordagem de derivadas parciais pode ser trabalhoso. Assim, nesta seção mostramos como usar a abordagem de diferencial total para resolver problemas de otimização não-condicionada com mais de uma variável de escolha no R. Para tanto, vamos aprender os seguintes pontos:
- EXEMPLO 1: ENCONTRAR MÁXIMO
Suponha que temos a função \(f(x,y,z) = -5x^2+10x+xz-2y^2+4y+2yz-4z^2\) e queremos encontrar os valores de \(x\), \(y\) e \(z\) que maximizam \(f(x,y,z)\). O códigoa abaixo, mostra como formular tal problema no R e como fazer uso do função optim
para encontrar a solução.
######
### FUNÇÃO OBJETIVO
######
f = function(x) -5*x[1]^2+10*x[1]+x[1]*x[3]-2*x[2]^2+4*x[2]+2*x[2]*x[3]-4*x[3]^2
#####
### CPO E CSO AO MESMO TEMPO
#####
# Aqui, usamos a função optim que permite resolver diversos problemas
# de otimização. Seus parâmetros são:
# - par: valores iniciais para as variáveis de escolha
# - fn: a função objetivo que será minimizada (ou maximizada)
# - gr: uma função com a derivada parcial (gradiente) da função objetivo
# - method: o método a ser usado pelo algoritmo (use o help da função para entender melhor)
# - lower: limite inferior para as variáveis de escolha (restringir o domínio)
# - upper: limite superior para as variáveis de escolha (restringir o domínio)
# - hessian: valor lógico (retornar (TRUE) ou não retornar (FALSE) a matriz hessiana)
# - control = define se o problema é de máximo (fnscale=-1) ou mínimo (defaul)
# Otimizacao (repare o uso de control = list(fnscale=1) dado que queremos o máximo)
otimizacao1 = optim(par = c(0,0,0), fn = f, method = "L-BFGS-B", control = list(fnscale =-1), lower = c(0,0,0), hessian = TRUE)
# Valores críticos (CPO)
valores_criticos = otimizacao1$par
# Matriz Hessiana (CSO)
h1 = det(matrix(otimizacao1$hessian[1,1])) # primeiro menor principal líder
h2 = det(matrix(otimizacao1$hessian[c(1,2),c(1,2)], ncol = 2, byrow = TRUE)) # segundo menor principal líder
h3 = det(matrix(otimizacao1$hessian, ncol = 3, byrow = TRUE)) # terceiro menor principal líder
#####
### REGRA PARA AVALIAÇÃO DOS VALORES CRÍTICOS
#####
# Como estudamos em sala de aula, a regra que deve ser usada para definir se o valor
# crítico gera máximo ou mínimo é:
# - Máximo: H1<0, H2>0, H3<0, ..., (-1)^nHn>0
# - Mínimo: H1>0, H2>0, ..., Hn>0
Como resultado temos que o valor de máximo é 7.652 e que os valores de \(x\), \(y\) e \(z\) que geram este valor são 1.04, 1.22 e 0.43, respectivamente. Além disso, se fizemos o cálculo dos determinantes dos menores principais líderes da matriz hessiana resultante, obtemos que o primeiro menor princial tem valor igual a -10 (negativo) enquanto o segundo tem o valor 40 (positivo) e o terceiro tem o valor -276 (negativo) . Isso confirma que os resultados obtidos são para um máximo, conforme estudamos em sala de aula.
- EXEMPLO 2: ENCONTRAR MÍNIMO
Suponha agora que temos a função \(z(x_{1},x_{2},x_{3}) = 2*(x_{1})^2 + x_{1}*x_{2} + 4*(x_{2})^2 + x_{1}*x_{3} + (x_{3})^2 + 2\) e queremos encontrar os valores de \(x_{1}\), \(x_{2}\) e \(x_{3}\) que minimizam \(z(x_{1},x_{2},x_{3})\). Este exemplo foi resolvido em sala de aula e usaremos o R para cofirmar a resposta encontrada.
f = function(x){
x1 = x[1]
x2 = x[2]
x3 = x[3]
2*(x1)^2 + x1*x2 + 4*(x2)^2 + x1*x3 + (x3)^2 + 2
}
optim(c(-1,-1,-1), f, method = "BFGS", hessian = TRUE)
Como resultado temos que os valores de \(x_{1}\), \(x_{2}\) e \(x_{3}\) que minimizam \(z(x_{1},x_{2},x_{3})\) são bem próximos de \(0\), conforme encontramos em sala de aula. Ainda, o valor mínimo de \(z(x_{1},x_{2},x_{3})\) é \(2\) e a matriz hessiana é bem próxima da encontrada em sala de aula.
REFERÊNCIAS
Chiang, Alpha C., and Kevin Wainwright. 2006. Matemática Para Economistas. Elsevier.
Soetaert, Karline, and Peter MJ Herman. 2008. A Practical Guide to Ecological Modelling Using R as a Simulation Platform. Springer Science & Business Media.
LS0tCnRpdGxlOiA8Y2VudGVyPiA8aDI+IDxiPiBPdGltaXphw6fDo28gTsOjby1Db25kaWNpb25hZGEgPC9iPiA8L2gyPiA8L2NlbnRlcj4gCmF1dGhvcjogPGNlbnRlcj4gSHVkc29uIENoYXZlcyBDb3N0YSAtIElCTUVDL01HIDwvY2VudGVyPgpncmFwaGljczogeWVzCmxpbmtjb2xvcjogYmx1ZQpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIGZpZ19jYXB0aW9uOiB5ZXMKcmVmZXJlbmNlczoKLSBpZDogc29ldGFlcnQyMDA4cHJhY3RpY2FsCiAgdGl0bGU6IEEgcHJhY3RpY2FsIGd1aWRlIHRvIGVjb2xvZ2ljYWwgbW9kZWxsaW5nIHVzaW5nIFIgYXMgYSBzaW11bGF0aW9uIHBsYXRmb3JtCiAgYXV0aG9yOgogIC0gZmFtaWx5OiBTb2V0YWVydAogICAgZ2l2ZW46IEthcmxpbmUKICAtIGZhbWlseTogSGVybWFuCiAgICBnaXZlbjogUGV0ZXIgTUoKICBwdWJsaXNoZXI6IFNwcmluZ2VyIFNjaWVuY2UgXCYgQnVzaW5lc3MgTWVkaWEKICB0eXBlOiBib29rCiAgaXNzdWVkOgogICAgeWVhcjogMjAwOAotIGlkOiBhbHBoYTIwMDRtYXRlbWF0aWNhCiAgdGl0bGU6IE1hdGVtw6F0aWNhIHBhcmEgZWNvbm9taXN0YXMKICBhdXRob3I6IAogIC0gZmFtaWx5OiBDaGlhbmcKICAgIGdpdmVuOiBBbHBoYSBDLgogIC0gZmFtaWx5OiBXYWlud3JpZ2h0CiAgICBnaXZlbjogS2V2aW4KICBwdWJsaXNoZXI6IEVsc2V2aWVyCiAgdHlwZTogYm9vawogIGlzc3VlZDoKICAgIHllYXI6IDIwMDYKbm9jaXRlOiB8IAogIEBzb2V0YWVydDIwMDhwcmFjdGljYWwsIEBhbHBoYTIwMDRtYXRlbWF0aWNhCi0tLQoKRXN0ZSBkb2N1bWVudG8gdGVtIGNvbW8gb2JqZXRpdm8gY29udHJpYnVpciBwYXJhIG8gZW50ZW5kaW1lbnRvIGRlIGNvbW8gcmVzb2x2ZXIgcHJvYmxlbWFzIGRlIG90aW1pemHDp8OjbyBuw6NvLWNvbmRpY2lvbmFkYSBubyBbUl0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pLiBQYXJhIHRhbnRvLCB2YW1vcyBhcHJlbmRlciBvcyBzZWd1aW50ZXMgcG9udG9zOgoKKiBDb21vIHVzYXIgYSBhYm9yZGFnZW0gZGEgKipkZXJpdmFkYXMgcGFyY2lhaXMqKiBwYXJhIHNvbHVjaW9uYXIgcHJvYmxlbWFzIGNvbSBhcGVuYXMgdW1hIHZhcmnDoXZlbCBkZSBlc2NvbGhhCiAgICAqIEVuY29udHJhciBhcyBjb25kacOnw7VlcyBkZSBwcmltZWlyYSBlIHNlZ3VuZGEgb3JkZW07CiAgICAqIE9idGVyIG9zIHZhbG9yZXMgbcOheGltb3MgZSBtw61uaW1vcyBkYSBmdW7Dp8OjbyBvYmpldGl2bzsKICAgICogR2VyYXIgZ3LDoWZpY29zIGNvbSBvcyByZXN1bHRhZG9zLgoqIENvbW8gdXNhciBhIGFib3JkYWdlbSBkZSAqKmRpZmVyZW5jaWFsIHRvdGFsKiogcGFyYSBzb2x1Y2lvbmFyIHByb2JsZW1hcyBjb20gJE4kIHZhcmnDoXZlaXMKICAgICogRW5jb250cmFyIGEgc29sdcOnw6NvIGRvIHByb2JsZW1hIHVzYW5kbyBhIGZ1bsOnw6NvIGBvcHRpbWAKICAgICogVGVzdGFyIHNlIGVzdGEgZnVuw6fDo28gZW5jb250cmEgcmVzdWx0YWRvcyBzZW1lbGhhbnRlcyBhb3Mgb2J0aWRvcyBwb3IgbWVpbyBkb3MgZGV0ZXJtaW5hbnRlcyBkb3MgbWVub3JlcyBwcmluY2lwYWlzIGzDrWRlcmVzIGRhIG1hdHJpeiBoZXNzaWFuYSBhdmFsaWFkYSBub3MgcG9udG9zIGNyw610aWNvcy4KCgojIyMjIyAqKkFCT1JEQUdFTSBEQVMgREVSSVZBREFTIFBBUkNJQUlTKioKCkNvbW8gZXN0dWRhbW9zIGVtIHNhbGEgZGUgYXVsYSwgdW1hIGFsdGVybmF0aXZhIHBhcmEgZW5jb250cmFyIG9zIHZhbG9yZXMgY3LDrXRpY29zIChjYW5kaWRhdG9zIMOgIG3DoXhpbW8gZS9vdSBtw61uaW1vKSBkZSB1bWEgZnVuw6fDo28gw6kgZmF6ZXIgdXNvIGRhcyBkZXJpdmFkYXMgcGFyY2lhaXMuIE5lc3RlIHNlbnRpZG8sIGVudGVuZGVtb3MgcXVlIGlndWFsYXIgYSBkZXJpdmFkYSBwYXJjaWFsIGRlIHByaW1laXJhIG9yZGVtIG5vcyBwZXJtaXRlIGVuY29udHJhciBvcyB2YWxvcmVzIGNyw610aWNvcyAoY2hhbWFtb3MgdGFsIHByb2NlZGltZW50byBkZSBjb25kacOnw6NvIG5lY2Vzc8OhcmlhIGRlIHByaW1laXJhIG9yZGVtLCBDUE8pLiBQb3Igb3V0cm8gbGFkbywgYXZhbGlhciBvIHNpbmFsIGRhIGRlcml2YWRhIHBhcmNpYWwgZGUgc2VndW5kYSBvcmRlbSBub3MgcG9udG9zIGNyw610aWNvcyBub3MgZm9ybmVjZSBhIGNvbmNsdXPDo28gc2UgbyB2YWxvciBjcsOtdGljbyDDqSBwb250byBkZSBtw6F4aW1vLCBtw61uaW1vIGUvb3UgaW5mbGV4w6NvLiAKCk5vIGRlY29ycmVyIGRlc3RhIHNlw6fDo28sIHZhbW9zIGVudGVuZGVyIGNvbW8gdXNhciBvIHBhY290ZSBgcm9vdFNvbHZlYCBwYXJhIHJlc29sdmVyIHByb2JsZW1hcyBkZSBvdGltaXphw6fDo28gbsOjby1jb25kaWNpb25hZGEgY29tIDEgdmFyacOhdmVsIGRlIGVzY29saGEgcG9yIG1laW8gZGEgYWJvcmRhZ2VtIGRhcyBkZXJpdmFkYXMgcGFyY2lhaXMuIFBhcmEgdGFudG8sIHZhbW9zIHVzYXIgY29tbyBleGVtcGxvIGEgZnVuw6fDo28gJHkgPSB4XnszfSArIDN4XnsyfSAtIDZ4IC04JC4gT2JzZXJ2ZSBvIGZvcm1hdG8gZGEgZnVuw6fDo28gcGFyYSBvIGRvbcOtbmlvICQtNTx4PDQkIG5vIGdyw6FmaWNvIGFiYWl4by4gCgpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcycsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTl9CnkgPSBmdW5jdGlvbih4KSAoeF4zICsgMyAqIHheMiAtIDYqeCAtIDgpCmN1cnZlKHksIC01LCA0LCB5bGFiID0gInk9Zih4KSIpCmBgYAoKKiAqKkNPTkRJw4fDg08gREUgUFJJTUVJUkEgT1JERU0gKENQTykqKgoKVW0gZG9zIHBhc3NvcyBpbmljaWFpcyBlbSBxdWFscXVlciBwcm9ibGVtYSBkZSBvdGltaXphw6fDo28gw6kgZW5jb250cmFyIGNvbmRpw6fDtWVzIG5lY2Vzc8OhcmlhcyBlIHN1ZmljaWVudGVzIHBhcmEgbyBwcm9ibGVtYSBlbSBxdWVzdMOjby4gT3Ugc2VqYSwgZW5jb250cmFyIGFzIHJhw616ZXMgcmVzcG9uc8OhdmVpcyBwb3IgYXRlbmRlciBhIGNvbmRpw6fDo28gJGYnKHg9eF97MH0pID0gMCQsIHRhbWLDqW0gY29uaGVjaWRhIGNvbW8gY29uZGnDp8OjbyBkZSBwcmltZWlyYSBvcmRlbSAoQ1BPKS4gCgpObyBjw7NkaWdvIGFiYWl4bywgbW9zdHJhbW9zIGNvbW8gZmF6ZXIgdXNvIGRvIFIgcGFyYSBlbmNvbnRyYXIgb3MgdmFsb3JlcyBjcsOtdGljb3MsIG91IHNlamEsIHZhbG9yIGRlICR4X3swfSQgcXVlIHRvcm5hIGEgcHJpbWVpcmEgZGVyaXZhZGEgaWd1YWwgYSB6ZXJvLgoKYGBge3J9CiMjIyMjIwojIyMgUEFDT1RFUyBORUNFU1PDgVJJT1MKIyMjIyMjCgojIEluc3RhbGFyIHBhY290ZXMgKGV4ZWN1dGFyIGFwZW5hcyB1bWEgdmV6KQojIGluc3RhbGwucGFja2FnZXMoInJvb3RTb2x2ZSIpCgojIENhcnJlZ2FyIG9zIHBhY290ZXMgbm8gYW1iaWVudGUKc3VwcHJlc3NNZXNzYWdlcyhyZXF1aXJlKHJvb3RTb2x2ZSkpICAgICAgICAgICAgICAgCgoKIyMjIyMjCiMjIyBGVU7Dh8ODTyBPQkpFVElWTyAKIyMjIyMjCgojIEEgZnVuw6fDo28gcXVlIGFuYWxpc2FyZW1vcwp5ID0gZnVuY3Rpb24oeCkgKHheMyArIDMqeF4yIC0gNip4IC0gOCkKCiMjIyMjIwojIyMgQ09OREnDh8ODTyBERSBQUklNRUlSQSBPUkRFTSAKIyMjIyMjCgojIGZ1bsOnw6NvIHZhemlhIHF1ZSByZWNlYmVyw6EgbyByZXN1bHRhZG8gZGEgcHJpbWVpcmEgZGVyaXZhZGEgCmRlcml2YWRhMSA9IGZ1bmN0aW9uKHgpe30KCiMgcmVzdWx0YWRvIGRhIGRlcml2YcOnw6NvIHVzYW5kbyBhcyBmdW7Dp8O1ZXMgYm9keSBlIEQuIGJvZHkgcGVybWl0ZSBhY2Vzc2FyIHVtYSBmdW7Dp8OjbwojIHNlamEgcGFyYSBncmF2YXIgbyByZXN1bHRhZG8gb3UgcGFyYSBwZWdhciBhIGZ1bsOnw6NvLiBEIGFwbGljYSBhIGRlcml2YcOnw6NvCmJvZHkoZGVyaXZhZGExKSA9IEQoYm9keSh5KSwgJ3gnKQoKIyBFbmNvbnRyYXIgYXMgcmFpemVzIHF1ZSB0b3JuYW0gYSBwcmltZWlyYSBkZXJpdmFkYSBpZ3VhbCBhIHplcm8gcGFyYSBvIGludGVydmFsbyAtNTx4PDQKcm9vdHMgPSByb290U29sdmU6OnVuaXJvb3QuYWxsKGRlcml2YWRhMSwgYygtNSwgNCkpCmBgYAoKQ29tbyByZXN1bHRhZG8gZW5jb250cmFtb3MgcXVlIG9zIHZhbG9yZXMgY3LDrXRpY29zIHPDo28gYHIgcm91bmQocm9vdHNbMV0sMilgIGUgYHIgcm91bmQocm9vdHNbMl0sMilgLiBBZ29yYSwgcHJlY2lzYW1vcyBhdmFsaWFyIHNlIGVsZXMgc8OjbyBwb250b3MgZGUgbcOtbmltbywgbcOheGltbyBvdSBzZWxhLiAKCiogKipDT05EScOHw4NPIERFIFNFR1VOREEgT1JERU0gKENTTykqKgoKQXNzaW0sIHByZWNpc2Ftb3MgdmVyaWZpY2FyIGEgY29uZGnDp8OjbyBkZSBzZWd1bmRhIG9yZGVtIHF1ZSB2ZXJpZmljYSBzZSBhIHNlZ3VuZGEgZGVyaXZhZGEgYXZhbGlhZGEgbmFzIHJhw616ZXMgZGEgY29uZGnDp8OjbyBuZWNlc3PDoXJpYSAoJGYiKHg9eF97MH0pJCkgw6kgcG9zaXRpdmEsIG5lZ2F0aXZhIG91IGlndWFsIGEgemVyby4gQWJhaXhvLCBleGVtcGxvIGRlIGNvbW8gZW5jb250cmFyIGFzIGNvbmRpw6fDtWVzIGRlIHByaW1laXJhIGUgc2VndW5kYSBvcmRlbQoKYGBge3J9CgojIEZ1bsOnw6NvIHF1ZSBhcm1hemVuYXLDoSBvIHJlc3VsdGFkbyBkYSBkZXJpdmFkYSBkZSBzZWd1bmRhIG9yZGVtCmRlcml2YWRhMiA9IGZ1bmN0aW9uKHgpe30KCiMgT2J0ZW5kbyBhIGRlcml2YWRhIHNlZ3VuZGEKYm9keShkZXJpdmFkYTIpID0gRChib2R5KGRlcml2YWRhMSksICJ4IikKCiMgRW5jb250cmFyIG8gdmFsb3IgZGEgZGVyaXZhZGEgc2VndW5kYSBwYXJhIG9zIHZhbG9yZXMgY3LDrXRpY29zLiBDb21vCiMgZXN0dWRhbW9zLCB2YWxvcmVzIGNyw610aWNvcyBxdWUgZ2VyYW0gZiIoeCk8MCBzw6NvIHBvbnRvcyBkZSBtw6F4aW1vIGUKIyB2YWxvcmVzIGNyw610aWNvcyBxdWUgZ2VyYW0gZiIoeCk+MCBzw6NvIHBvbnRvIGRlIG3DrW5pbW8uIEFzc2ltLCBwZWxvcwojIHJlc3VsdGFkb3MgZXNwZXJhbW9zIHF1ZSBhIHByaW1laXJhIHJhaXogc2VqYSB1bSBtw6F4aW1vIGUgYSBzZWd1bmRhIHVtCiMgbcOtbmltby4KZGVyaXZhZGEyKHJvb3RzKQoKIyBBdmFsaWFyIGEgZnVuw6fDo28gbmFzIHJhw616ZXMgZGEgY29uZGnDp8OjbyBuZWNlc3PDoXJpYSAoZicoeCk9MCkuCiMgQXF1aSwgdXNhbW9zIHkoKSBwb3JxdWUgZGVmaW5pbW9zIGEgZnVuw6fDo28gY29tbyB5LiBPYnNlcnZlIHF1ZSAKIyBjb25maXJtYW1vcyBxdWUgYSBwcmltZWlyYSByYcOteiBnZXJhIHVtIG3DoXhpbW8gZSBhIHNlZ3VuZGEgdW0gbcOtbmltbwp5KHJvb3RzKQpgYGAKCiogKipHUsOBRklDTyBDT00gT1MgVkFMT1JFUyBERSBNw4FYSU1PIEUvT1UgTcONTklNTyBSRUxBVElWT1MqKgoKVW1hIHZleiBxdWUgdGVzdGFtb3MgYXMgY29uZGnDp8O1ZXMgZGUgcHJpbWVpcmEgZSBzZWd1bmRhIG9yZGVtIGUgdGVtb3Mgb3MgdmFsb3JlcyBjcsOtdGljb3MgZGEgY29uZGnDp8OjbyBuZWNlc3PDoXJpYSwgJGYnKHg9eF97MH0pID0gMCQsIGF2YWxpYWRvcyBuYSBjb25kacOnw6NvIHN1ZmljaWVudGUsICRmIih4PXhfezB9KSQsIHBvZGVtb3MgZGVmaW5pciBvIG3DoXhpbW8gZS9vdSBtw61uaW1vLiBPIGPDs2RpZ28gYWJhaXhvIG1vc3RyYSBjb21vIGNyaWFyIHVtIGdyw6FmaWNvIHF1ZSBub3MgbW9zdHJhIHRhaXMgYXZsb3Jlcy4KCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OSwgZmlnLmFsaWduPSdjZW50ZXInfQojIFVzYW1vcyBhIGZ1bsOnw6NvIGN1cnZlIHF1ZSBkZXNlbmhhIGEgZnVuw6fDo28uIFNldXMgcGFyw6JtZXRyb3Mgc8OjbzoKIyAtIGV4cHI6IGEgZnVuw6fDo28gYSBzZXIgcGxvdGFkYQojIC0gZnJvbTogdmFsb3IgaW5mZXJpb3IgZG8gaW50ZXJ2YWxvCiMgLSB0bzogdmFsb3Igc3VwZXJpb3IgZG8gaW50ZXJ2YWxvCiMgLSB0eXBlOiB0aXBvIGRlIGdyw6FmaWNvIChsOiBlbSBsaW5oYSkKIyAtIHlsYWI6IG5vbWUgZG8gZWl4byB2ZXJ0aWNhbAojIC0geGxhYjogbm9tZSBkbyBlaXhvIGhvcml6b250YWwKY3VydmUoZXhwciA9IHksIGZyb20gPSAtNSwgdG8gPSA0LCB0eXBlID0gImwiLCB5bGFiID0gInk9Zih4KSIsIHhsYWIgPSAiIikKCiMgQXDDs3MgaXNzbywgYWRpY2lvbmFtb3MgYXMgcmHDrXplcyBwYXJhIGEgZnVuw6fDo28gbm8gaW50ZXJ2YWxvLiBVc2Ftb3MKIyBhIGZ1bsOnw6NvIHBvaW50cyBxdWUgdGVtIG9zIHNlZ3VpbnRlcyBwYXLDom1ldHJvczoKIyAtIHg6IGEgdmFyacOhdmVsIGRvIGVpeG8gdmVydGljaWFsCiMgLSB5OiBhIHZhcmnDoXZlbCBkbyBlaXhvIGhvcml6b250YWwKIyBPYnNlcnZlIHF1ZSBhIGZ1bsOnw6NvIGNvbG9jYXLDoSB1bSBwb250byBuYXMgY29vcmRlbmFkYXMgKHgseSkKcG9pbnRzKHkocm9vdHMpIH4gcm9vdHMpCgpgYGAKCiMjIyMjICoqQUJPUkRBR0VNIERFIERJRkVSRU5DSUFMIFRPVEFMKioKCkNvbW8gZXN0dWRhbW9zIGVtIHNhbGEgZGUgYXVsYSwgcmVzb2x2ZXIgcHJvYmxlbWFzIGRlIG90aW1pemHDp8OjbyBuw6NvLWNvbmRpY2lvbmFkYSBjb20gJE4kIHZhcmnDoXZlaXMgZGUgZXNjb2xoYSBwb3IgbWVpbyBkYSBhYm9yZGFnZW0gZGUgZGVyaXZhZGFzIHBhcmNpYWlzIHBvZGUgc2VyIHRyYWJhbGhvc28uIEFzc2ltLCBuZXN0YSBzZcOnw6NvIG1vc3RyYW1vcyBjb21vIHVzYXIgYSBhYm9yZGFnZW0gZGUgZGlmZXJlbmNpYWwgdG90YWwgcGFyYSByZXNvbHZlciBwcm9ibGVtYXMgZGUgb3RpbWl6YcOnw6NvIG7Do28tY29uZGljaW9uYWRhIGNvbSBtYWlzIGRlIHVtYSB2YXJpw6F2ZWwgZGUgZXNjb2xoYSBubyBbUl0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pLiBQYXJhIHRhbnRvLCB2YW1vcyBhcHJlbmRlciBvcyBzZWd1aW50ZXMgcG9udG9zOgoKKiAqKkVYRU1QTE8gMTogRU5DT05UUkFSIE3DgVhJTU8qKgoKU3Vwb25oYSBxdWUgdGVtb3MgYSBmdW7Dp8OjbyAkZih4LHkseikgPSAtNXheMisxMHgreHotMnleMis0eSsyeXotNHpeMiQgZSBxdWVyZW1vcyBlbmNvbnRyYXIgb3MgdmFsb3JlcyBkZSAkeCQsICR5JCBlICR6JCBxdWUgbWF4aW1pemFtICRmKHgseSx6KSQuIE8gY8OzZGlnb2EgYWJhaXhvLCBtb3N0cmEgY29tbyBmb3JtdWxhciB0YWwgcHJvYmxlbWEgbm8gUiBlIGNvbW8gZmF6ZXIgdXNvIGRvIGZ1bsOnw6NvIGBvcHRpbWAgcGFyYSBlbmNvbnRyYXIgYSBzb2x1w6fDo28uCgoKYGBge3J9CiMjIyMjIwojIyMgRlVOw4fDg08gT0JKRVRJVk8gCiMjIyMjIwoKZiA9IGZ1bmN0aW9uKHgpIC01KnhbMV1eMisxMCp4WzFdK3hbMV0qeFszXS0yKnhbMl1eMis0KnhbMl0rMip4WzJdKnhbM10tNCp4WzNdXjIKCiMjIyMjCiMjIyBDUE8gRSBDU08gQU8gTUVTTU8gVEVNUE8KIyMjIyMKCiMgQXF1aSwgdXNhbW9zIGEgZnVuw6fDo28gb3B0aW0gcXVlIHBlcm1pdGUgcmVzb2x2ZXIgZGl2ZXJzb3MgcHJvYmxlbWFzIAojIGRlIG90aW1pemHDp8Ojby4gU2V1cyBwYXLDom1ldHJvcyBzw6NvOgojIC0gcGFyOiB2YWxvcmVzIGluaWNpYWlzIHBhcmEgYXMgdmFyacOhdmVpcyBkZSBlc2NvbGhhCiMgLSBmbjogYSBmdW7Dp8OjbyBvYmpldGl2byBxdWUgc2Vyw6EgbWluaW1pemFkYSAob3UgbWF4aW1pemFkYSkKIyAtIGdyOiB1bWEgZnVuw6fDo28gY29tIGEgZGVyaXZhZGEgcGFyY2lhbCAoZ3JhZGllbnRlKSBkYSBmdW7Dp8OjbyBvYmpldGl2bwojIC0gbWV0aG9kOiBvIG3DqXRvZG8gYSBzZXIgdXNhZG8gcGVsbyBhbGdvcml0bW8gKHVzZSBvIGhlbHAgZGEgZnVuw6fDo28gcGFyYSBlbnRlbmRlciBtZWxob3IpCiMgLSBsb3dlcjogbGltaXRlIGluZmVyaW9yIHBhcmEgYXMgdmFyacOhdmVpcyBkZSBlc2NvbGhhIChyZXN0cmluZ2lyIG8gZG9tw61uaW8pCiMgLSB1cHBlcjogbGltaXRlIHN1cGVyaW9yIHBhcmEgYXMgdmFyacOhdmVpcyBkZSBlc2NvbGhhIChyZXN0cmluZ2lyIG8gZG9tw61uaW8pCiMgLSBoZXNzaWFuOiB2YWxvciBsw7NnaWNvIChyZXRvcm5hciAoVFJVRSkgb3UgbsOjbyByZXRvcm5hciAoRkFMU0UpIGEgbWF0cml6IGhlc3NpYW5hKQojIC0gY29udHJvbCA9IGRlZmluZSBzZSBvIHByb2JsZW1hIMOpIGRlIG3DoXhpbW8gKGZuc2NhbGU9LTEpIG91IG3DrW5pbW8gKGRlZmF1bCkKCiMgT3RpbWl6YWNhbyAocmVwYXJlIG8gdXNvIGRlIGNvbnRyb2wgPSBsaXN0KGZuc2NhbGU9MSkgZGFkbyBxdWUgcXVlcmVtb3MgbyBtw6F4aW1vKQpvdGltaXphY2FvMSA9IG9wdGltKHBhciA9IGMoMCwwLDApLCBmbiA9IGYsIG1ldGhvZCA9ICJMLUJGR1MtQiIsIGNvbnRyb2wgPSBsaXN0KGZuc2NhbGUgPS0xKSwgbG93ZXIgPSBjKDAsMCwwKSwgaGVzc2lhbiA9IFRSVUUpCgojIFZhbG9yZXMgY3LDrXRpY29zIChDUE8pCnZhbG9yZXNfY3JpdGljb3MgPSBvdGltaXphY2FvMSRwYXIKCiMgTWF0cml6IEhlc3NpYW5hIChDU08pCmgxID0gZGV0KG1hdHJpeChvdGltaXphY2FvMSRoZXNzaWFuWzEsMV0pKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHByaW1laXJvIG1lbm9yIHByaW5jaXBhbCBsw61kZXIKaDIgPSBkZXQobWF0cml4KG90aW1pemFjYW8xJGhlc3NpYW5bYygxLDIpLGMoMSwyKV0sIG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKSAgICMgc2VndW5kbyBtZW5vciBwcmluY2lwYWwgbMOtZGVyCmgzID0gZGV0KG1hdHJpeChvdGltaXphY2FvMSRoZXNzaWFuLCBuY29sID0gMywgYnlyb3cgPSBUUlVFKSkgICAgICAgICAgICAgICAgICAjIHRlcmNlaXJvIG1lbm9yIHByaW5jaXBhbCBsw61kZXIKCiMjIyMjCiMjIyBSRUdSQSBQQVJBIEFWQUxJQcOHw4NPIERPUyBWQUxPUkVTIENSw41USUNPUwojIyMjIwoKIyBDb21vIGVzdHVkYW1vcyBlbSBzYWxhIGRlIGF1bGEsIGEgcmVncmEgcXVlIGRldmUgc2VyIHVzYWRhIHBhcmEgZGVmaW5pciBzZSBvIHZhbG9yCiMgY3LDrXRpY28gZ2VyYSBtw6F4aW1vIG91IG3DrW5pbW8gw6k6CiMgLSBNw6F4aW1vOiBIMTwwLCBIMj4wLCBIMzwwLCAuLi4sICgtMSlebkhuPjAKIyAtIE3DrW5pbW86IEgxPjAsIEgyPjAsIC4uLiwgSG4+MApgYGAKCkNvbW8gcmVzdWx0YWRvIHRlbW9zIHF1ZSBvIHZhbG9yIGRlIG3DoXhpbW8gw6kgYHIgcm91bmQob3RpbWl6YWNhbzEkdmFsdWUsMylgIGUgcXVlIG9zIHZhbG9yZXMgZGUgJHgkLCAkeSQgZSAkeiQgcXVlIGdlcmFtIGVzdGUgdmFsb3Igc8OjbyBgciByb3VuZCh2YWxvcmVzX2NyaXRpY29zWzFdLDIpYCwgYHIgcm91bmQodmFsb3Jlc19jcml0aWNvc1syXSwyKWAgZSBgciByb3VuZCh2YWxvcmVzX2NyaXRpY29zWzNdLDIpYCwgcmVzcGVjdGl2YW1lbnRlLiBBbMOpbSBkaXNzbywgc2UgZml6ZW1vcyBvIGPDoWxjdWxvIGRvcyBkZXRlcm1pbmFudGVzIGRvcyBtZW5vcmVzIHByaW5jaXBhaXMgbMOtZGVyZXMgZGEgbWF0cml6IGhlc3NpYW5hIHJlc3VsdGFudGUsIG9idGVtb3MgcXVlIG8gcHJpbWVpcm8gbWVub3IgcHJpbmNpYWwgdGVtIHZhbG9yIGlndWFsIGEgYHIgaDFgICgqKm5lZ2F0aXZvKiopIGVucXVhbnRvIG8gc2VndW5kbyB0ZW0gbyB2YWxvciBgciBoMmAgKCoqcG9zaXRpdm8qKikgZSBvIHRlcmNlaXJvIHRlbSBvIHZhbG9yIGByIGgzYCAoKipuZWdhdGl2byoqKSAuIElzc28gY29uZmlybWEgcXVlIG9zIHJlc3VsdGFkb3Mgb2J0aWRvcyBzw6NvIHBhcmEgdW0gKiptw6F4aW1vKiosIGNvbmZvcm1lIGVzdHVkYW1vcyBlbSBzYWxhIGRlIGF1bGEuCgoqICoqRVhFTVBMTyAyOiBFTkNPTlRSQVIgTcONTklNTyoqCgpTdXBvbmhhIGFnb3JhIHF1ZSB0ZW1vcyBhIGZ1bsOnw6NvICR6KHhfezF9LHhfezJ9LHhfezN9KSA9IDIqKHhfezF9KV4yICsgeF97MX0qeF97Mn0gKyA0Kih4X3syfSleMiArIHhfezF9KnhfezN9ICsgKHhfezN9KV4yICsgMiQgZSBxdWVyZW1vcyBlbmNvbnRyYXIgb3MgdmFsb3JlcyBkZSAkeF97MX0kLCAkeF97Mn0kIGUgJHhfezN9JCBxdWUgbWluaW1pemFtICR6KHhfezF9LHhfezJ9LHhfezN9KSQuIEVzdGUgZXhlbXBsbyBmb2kgcmVzb2x2aWRvIGVtIHNhbGEgZGUgYXVsYSBlIHVzYXJlbW9zIG8gW1JdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvKSBwYXJhIGNvZmlybWFyIGEgcmVzcG9zdGEgZW5jb250cmFkYS4gCgpgYGB7cn0KZiA9IGZ1bmN0aW9uKHgpewogIHgxID0geFsxXQogIHgyID0geFsyXQogIHgzID0geFszXQogIDIqKHgxKV4yICsgeDEqeDIgKyA0Kih4MileMiArIHgxKngzICsgKHgzKV4yICsgMgp9CgpvcHRpbShjKC0xLC0xLC0xKSwgZiwgbWV0aG9kID0gIkJGR1MiLCBoZXNzaWFuID0gVFJVRSkKYGBgCgpDb21vIHJlc3VsdGFkbyB0ZW1vcyBxdWUgb3MgdmFsb3JlcyBkZSAkeF97MX0kLCAkeF97Mn0kIGUgJHhfezN9JCBxdWUgbWluaW1pemFtICR6KHhfezF9LHhfezJ9LHhfezN9KSQgc8OjbyBiZW0gcHLDs3hpbW9zIGRlICQwJCwgY29uZm9ybWUgZW5jb250cmFtb3MgZW0gc2FsYSBkZSBhdWxhLiBBaW5kYSwgbyB2YWxvciBtw61uaW1vIGRlICR6KHhfezF9LHhfezJ9LHhfezN9KSQgw6kgJDIkIGUgYSBtYXRyaXogaGVzc2lhbmEgw6kgYmVtIHByw7N4aW1hIGRhIGVuY29udHJhZGEgZW0gc2FsYSBkZSBhdWxhLgoKCiMjIyMgKipSRUZFUsOKTkNJQVMqKg==