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:

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.

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.

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

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:

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.

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==