http://cran.r-project.org/web/views/Optimization.html

https://www.rdocumentation.org/packages/ROI/versions/0.2-1

The R Optimization Infrastructure (ROI)

The R Optimization Infrastructure (ROI) package promotes the development and use of interoperable (open source) optimization problem solvers for R.

ROI handle LP up to MILP and MIQCP problems using the following supported solvers :

  1. lpSolve

  2. quadprog

  3. Rcplex

  4. Rglpk (default)

  5. Rsymphony

Solvers

CPLEX is a commercial solver. Students can get academic license.

GLPK is free. R has the high level interface Rglpk.

http://winglpk.sourceforge.net/

https://cran.r-project.org/web/packages/Rglpk/index.html

Linear Progarmming

#install.packages("ROI")
#install.packages(c( "PortfolioAnalytics","ECOSolveR","Rsymphony","Rglpk","ROI.plugin.symphony","ROI.plugin.glpk","fPortfolio","ROI.plugin.quadprog","ROI.plugin.nloptr","ROI.plugin.ipop","ROI.plugin.ecos"))

library("ROI")
ROI: R Optimization Infrastructure
Registered solver plugins: nlminb, ecos, glpk, ipop, nloptr, quadprog, symphony.
Default solver: auto.
#library("ROI.plugin.glpk")
#library("ROI.plugin.symphony")
#library(ROI.plugin.ipop)

#library(ROI.plugin.nloptr)
#library(ROI.plugin.quadprog)
#library(ROI.plugin.ecos)

ROI_registered_solvers()
               nlminb                  ecos                  glpk 
  "ROI.plugin.nlminb"     "ROI.plugin.ecos"     "ROI.plugin.glpk" 
                 ipop                nloptr              quadprog 
    "ROI.plugin.ipop"   "ROI.plugin.nloptr" "ROI.plugin.quadprog" 
             symphony 
"ROI.plugin.symphony" 
ROI.plugin.nlminb

ROI.plugin.ecos

ROI.plugin.glpk

ROI.plugin.ipop

ROI.plugin.nloptr

ROI.plugin.quadprog

ROI.plugin.symphony
## Simple linear program.
## maximize:   2 x_1 + 4 x_2 + 3 x_3
## subject to: 3 x_1 + 4 x_2 + 2 x_3 <= 60
##             2 x_1 +   x_2 +   x_3 <= 40
##               x_1 + 3 x_2 + 2 x_3 <= 80
##               x_1, x_2, x_3 are non-negative real numbers

LP <- OP( c(2, 4, 3),
          L_constraint(L = matrix(c(3, 2, 1, 4, 1, 3, 2, 2, 2), nrow = 3),
                       dir = c("<=", "<=", "<="),
                       rhs = c(60, 40, 80)),
          max = TRUE )
LP
ROI Optimization Problem:

Maximize a linear objective function with
- 3 objective variables,

subject to
- 3 constraints of type linear.

Solve

sol <- ROI_solve(LP)# , solver = "glpk")
sol
Optimal solution found.
The objective value is: 7.666667e+01

Solutions

solution(sol, type = c("primal"))
[1]  0.000000  6.666667 16.666667
solution(sol, type = c("dual"))
[1] -1.833333  0.000000  0.000000

Extract solutions

sol$solution
[1]  0.000000  6.666667 16.666667
sol$objval
[1] 76.66667
sol$status
$code
[1] 0

$msg
  solver glpk
    code 5
  symbol GLP_OPT
 message Solution is optimal.
roi_code 0
sol$message
$optimum
[1] 76.66667

$solution
[1]  0.000000  6.666667 16.666667

$status
[1] 5

$solution_dual
[1] -1.833333  0.000000  0.000000

$auxiliary
$auxiliary$primal
[1] 60.00000 40.00000 53.33333

$auxiliary$dual
[1] 0.8333333 0.6666667 0.0000000

Quadratic Progarmming

It will require putting problem in matrix form.

## Simple quadratic program.
## minimize: - 5 x_2 + 1/2 (x_1^2 + x_2^2 + x_3^2)
## subject to: -4 x_1 - 3 x_2       >= -8
##              2 x_1 +   x_2       >=  2
##                    - 2 x_2 + x_3 >=  0

QP <- OP( Q_objective (Q = diag(1, 3), L = c(0, -5, 0)),
          L_constraint(L = matrix(c(-4,-3,0,2,1,0,0,-2,1),
                                  ncol = 3, byrow = TRUE),
                       dir = rep(">=", 3),
                       rhs = c(-8,2,0)) )
QP
ROI Optimization Problem:

Minimize a quadratic objective function with
- 3 objective variables,

subject to
- 3 constraints of type linear.

Solve

sol <- ROI_solve(QP, solver = "quadprog")
sol
Optimal solution found.
The objective value is: -2.380952e+00

Extract solutions

sol$solution
[1] 0.4761905 1.0476190 2.0952381
sol$objval
[1] -2.380952
sol$status
$code
[1] 0

$msg
  solver quadprog
    code 0
  symbol OPTIMAL
 message Solution is optimal
roi_code 0
sol$message
NULL

Solutions

solution(sol, type = c("primal"))
[1] 0.4761905 1.0476190 2.0952381
solution(sol, type = c("dual"))
[1] NA

Portfolio optimization - minimum variance

## Portfolio optimization - minimum variance
## -----------------------------------------
## get monthly returns of 30 US stocks
data( US30 )
r <- na.omit( US30 )
## objective function to minimize
obj <- Q_objective( 2*cov(r) )
## full investment constraint
full_invest <- L_constraint( rep(1, ncol(US30)), "==", 1 )
## create optimization problem / long-only

(op <- OP( objective = obj, constraints = full_invest ))
ROI Optimization Problem:

Minimize a quadratic objective function with
- 30 objective variables,

subject to
- 1 constraints of type linear.

Solution

## solve the problem - only works if a QP solver is registered
##  
res <- ROI_solve( op )
res
Optimal solution found.
The objective value is: 9.326635e-04
sol <- solution( res )
names( sol ) <- colnames( US30 )
round( sol[ which(sol > 1/10^6) ], 3 )
  IBM   MCD   MRK    PG     T    VZ   WMT   XOM 
0.165 0.301 0.005 0.138 0.031 0.091 0.169 0.101 

LP with boundary

suppressMessages(library(ROI))
lp_obj <- L_objective(c(1, 2))
 lp_con <- L_constraint(c(1, 1), dir="==", rhs=2)
 lp_bound <- V_bound(ui=1:2, ub=c(3, 3))
 #ui an integer vector specifying the indices of non-standard (i.e., values != Inf) upper bounds.
 # ub up bound
 lp <- OP(objective=lp_obj, constraints=lp_con, bounds=lp_bound, maximum=FALSE)
 bounds(lp)
ROI Variable Bounds:

0 lower and 2 upper non-standard variable bounds.
lp
ROI Optimization Problem:

Minimize a linear objective function with
- 2 objective variables,

subject to
- 1 constraints of type linear.

Solutions

 x <- ROI_solve(lp)
 x$objval
[1] 2
 x$solution
[1] 2 0

Change boundary

# change boundary 
 bounds(lp) <- V_bound(ui=1:2, ub=c(1, 1))

Solutions

 y <- ROI_solve(lp)
 y$objval
[1] 3
 y$solution
[1] 1 1

Other optimization package in R

We also can solve optimization problem using other packages.

#install.packages(c("lpSolve", "lpSolveAPI"))
#install.packages(c("quadprog","Rglpk", "nlme"))
suppressMessages(library(lpSolve))

library(lpSolveAPI)
suppressMessages(library(quadprog))
#suppressMessages(library(nlme))

suppressMessages(library(Rglpk))
args(lp)
NULL
args(solve.QP)
function (Dmat, dvec, Amat, bvec, meq = 0, factorized = FALSE) 
NULL
args(Rglpk_solve_LP)
function (obj, mat, dir, rhs, bounds = NULL, types = NULL, max = FALSE, 
    control = list(), ...) 
NULL
#args(nlminb)
#args(optim)
#args(Rcplex)

Linear Programming by lpsolve

Here is a list of some key features of lp_solve:

  • Mixed Integer Linear Programming (MILP) solver

  • Basically no limit on model size

  • It is free and with sources

  • Supports Integer variables, Semi-continuous variables and Special Ordered Sets

Example by lpSolveAPI

straightforward

Set up model
library(lpSolveAPI)
#create an empty model
lprec <- make.lp(nrow =0, ncol= 4)
# nrow  : # a nonnegative integer value specifying the number of constaints in the linear program.
# ncol  : # a nonnegative integer value specifying the number of decision variables in the linear program.
Setup objective function
# object
set.objfn(lprec, c(1, 3, 6.24, 0.1))
Add constraints
#constraint
add.constraint(lprec, c(0, 78.26, 0, 2.9), ">=", 92.3)
add.constraint(lprec, c(0.24, 0, 11.31, 0), "<=", 14.8)
add.constraint(lprec, c(12.68, 0, 0.08, 0.9), ">=", 4)
Set bounds
set.bounds(lprec, lower = c(28.6, 18), columns = c(1, 4))
set.bounds(lprec, upper = 48.98, columns = 4)
Setup names
RowNames <- c("THISROW", "THATROW", "LASTROW")
ColNames <- c("COLONE", "COLTWO", "COLTHREE", "COLFOUR")
dimnames(lprec) <- list(RowNames, ColNames)
lprec
Model name: 
            COLONE    COLTWO  COLTHREE   COLFOUR          
Minimize         1         3      6.24       0.1          
THISROW          0     78.26         0       2.9  >=  92.3
THATROW       0.24         0     11.31         0  <=  14.8
LASTROW      12.68         0      0.08       0.9  >=     4
Kind           Std       Std       Std       Std          
Type          Real      Real      Real      Real          
Upper          Inf       Inf       Inf     48.98          
Lower         28.6         0         0        18          
Solve the model
solve(lprec)
[1] 0
  # 
  # 
Extract solutions
get.objective(lprec)
[1] 31.78276
  # [1] 31.78276
  # 
get.variables(lprec)
[1] 28.60000  0.00000  0.00000 31.82759
  # [1] 28.60000  0.00000  0.00000 31.82759
  # 
get.constraints(lprec)
[1]  92.3000   6.8640 391.2928
  # [1]  92.3000   6.8640 391.2928

Example by lpSolve

provide a lot of information including sensitivities analysis.

Setup objective function
library(lpSolve)
#
# Set up problem: maximize
#   x1 + 9 x2 +   x3 subject to
#   x1 + 2 x2 + 3 x3  <= 9
# 3 x1 + 2 x2 + 2 x3 <= 15
#
f.obj <- c(1, 9, 1)
Setup constraints
f.con <- matrix (c(1, 2, 3, 3, 2, 2), nrow=2, byrow=TRUE)
f.dir <- c("<=", "<=")
f.rhs <- c(9, 15)
Solve the model
#
lp ("max", f.obj, f.con, f.dir, f.rhs)
Success: the objective function is 40.5 
##  Success: the objective function is 40.5
Extract solutions
lp ("max", f.obj, f.con, f.dir, f.rhs)$solution
[1] 0.0 4.5 0.0
Get sensitivities
# Get sensitivities
#
lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.from
[1] -1e+30  2e+00 -1e+30
##  [1] -1e+30  2e+00 -1e+30
lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.to  
[1] 4.50e+00 1.00e+30 1.35e+01
##  [1] 4.50e+00 1.00e+30 1.35e+01
#
# Right now the dual values for the constraints and the variables are
# combined, constraints coming first. So in this example...
#
lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals     
[1]   4.5   0.0  -3.5   0.0 -12.5
##  [1]   4.5   0.0  -3.5   0.0 -10.5
#
# ...the duals of the constraints are 4.5 and 0, and of the variables,
# -3.5, 0.0, -10.5. Here are the lower and upper limits on these:
#
lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.from
[1] -1.776357e-15 -1.000000e+30 -1.000000e+30 -1.000000e+30 -6.000000e+00
##  [1]  0e+00 -1e+30 -1e+30 -1e+30 -6e+00
lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.to  
[1] 1.5e+01 1.0e+30 3.0e+00 1.0e+30 3.0e+00
##  [1] 1.5e+01 1.0e+30 3.0e+00 1.0e+30 3.0e+00
#

Intergr Programming

# Run again, this time requiring that all three variables be integer
#
lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3)
Success: the objective function is 37 
##  Success: the objective function is 37
lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3)$solution
[1] 1 4 0
##  [1] 1 4 0
#
# You can get sensitivities in the integer case, but they're harder to
# interpret.
#
lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3, compute.sens=TRUE)$duals
[1]  1  0  0  7 -2
##  [1] 1 0 0 7 0
#

Quadratic Programming by quadprog

solving quadratic programming problems of the form min(-d^T b + 1/2 b^T D b) with the constraints A^T b >= b_0.

D is diagnal matix

library(quadprog)

## Assume we want to minimize: -(0 5 0) %*% b + 1/2 b^T D b
## under the constraints:      A^T b >= b0
##          1 0 0
##      D = 0 1 0
##          0 0 1
## with b0 = (-8,2,0)^T
## and      (-4  2  0) 
##      A = (-3  1 -2)
##          ( 0  0  1)
## we can use solve.QP as follows:
##
Dmat       <- matrix(0,3,3)
diag(Dmat) <- 1
dvec       <- c(0,5,0)
Amat       <- matrix(c(-4,-3,0,2,1,0,0,-2,1),3,3)
bvec       <- c(-8,2,0)
solve.QP(Dmat,dvec,Amat,bvec=bvec)
$solution
[1] 0.4761905 1.0476190 2.0952381

$value
[1] -2.380952

$unconstrained.solution
[1] 0 5 0

$iterations
[1] 3 0

$Lagrangian
[1] 0.0000000 0.2380952 2.0952381

$iact
[1] 3 2

Football MIP by glpk

trying to pick the best possible fantasy football team given different constraints. goal is to pick the players that maximize the sum of their projected points.

The constraints are:

  1. The team must include:

    • 1 QB

    • 2 RBs

    • 2 WRs

    • 1 TE

  2. A player’s risk must not exceed 6

  3. The sum of the players’ costs must not exceed 300.

#http://stackoverflow.com/questions/15147398/optimize-value-with-linear-or-non-linear-constraints-in-r



# We are going to solve:
# maximize f'x subject to A*x <dir> b
# where:
#   x is the variable to solve for: a vector of 0 or 1:
#     1 when the player is selected, 0 otherwise,
#   f is your objective vector,
#   A is a matrix, b a vector, and <dir> a vector of "<=", "==", or ">=",
#   defining your linear constraints.


name <- c("Aaron Rodgers","Tom Brady","Arian Foster","Ray Rice","LeSean McCoy","Calvin Johnson","Larry Fitzgerald","Wes Welker","Rob Gronkowski","Jimmy Graham")


# position
pos <- c("QB","QB","RB","RB","RB","WR","WR","WR","TE","TE")

# points
pts <- c(167, 136, 195, 174, 144, 135, 89, 81, 114, 111) 

risk <- c(2.9, 3.4, 0.7, 1.1, 3.5, 5.0, 6.7, 4.7, 3.7, 8.8) 

cost <- c(60, 47, 63, 62, 40, 60, 50, 35, 40, 40) 

#mydata <- data.frame(name, pos, pts, risk, cost) 

# number of variables
num.players <- length(name)



# objective:
f <- pts



# the variable are booleans
var.types <- rep("B", num.players)



# the constraints
A <- rbind(as.numeric(pos == "QB"), # num QB
           as.numeric(pos == "RB"), # num RB
           as.numeric(pos == "WR"), # num WR
           as.numeric(pos == "TE"), # num TE
           diag(risk),              # player's risk, a set of multiple constraints
           cost)                    # total cost

# diretion
dir <- c("==",
         "==",
         "==",
         "==",
         rep("<=", num.players),
         "<=")

# rhs b vector
b <- c(1,
       2,
       2,
       1,
       rep(6, num.players),
       300)



# solve
library(Rglpk)
sol <- Rglpk_solve_LP(obj = f, mat = A, dir = dir, rhs = b,
                      types = var.types, max = TRUE)
sol
$optimum
[1] 836

$solution
 [1] 1 0 1 0 1 1 0 1 1 0

$status
[1] 0

$solution_dual
[1] NA

$auxiliary
$auxiliary$primal
 [1]   1.0   2.0   2.0   1.0   2.9   0.0   0.7   0.0   3.5   5.0   0.0
[12]   4.7   3.7   0.0 298.0

$auxiliary$dual
[1] NA
# $optimum
# [1] 836                      ### <- the optimal total points



# $solution
#  [1] 1 0 1 0 1 1 0 1 1 0     ### <- a `1` for the selected players

# $status
# [1] 0                        ### <- an optimal solution has been found

# your dream team
name[sol$solution == 1]
[1] "Aaron Rodgers"  "Arian Foster"   "LeSean McCoy"   "Calvin Johnson"
[5] "Wes Welker"     "Rob Gronkowski"
Aaron Rodgers

Arian Foster

LeSean McCoy

Calvin Johnson

Wes Welker

Rob Gronkowski
# [1] "Aaron Rodgers"  "Arian Foster"   "LeSean McCoy"
# [4] "Calvin Johnson" "Wes Welker"     "Rob Gronkowski


# total cost
sum(cost * sol$solution)
[1] 298
LS0tDQp0aXRsZTogIkVDT040NTcgUiBsYWIgMiBPcHRpbWl6YXRpb24gaW4gUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCmh0dHA6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3ZpZXdzL09wdGltaXphdGlvbi5odG1sDQoNCmh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ST0kvdmVyc2lvbnMvMC4yLTENCg0KDQojIyBUaGUgUiBPcHRpbWl6YXRpb24gSW5mcmFzdHJ1Y3R1cmUgKFJPSSkNCg0KVGhlIFIgT3B0aW1pemF0aW9uIEluZnJhc3RydWN0dXJlIChST0kpIHBhY2thZ2UgcHJvbW90ZXMgdGhlIGRldmVsb3BtZW50IGFuZCB1c2Ugb2YgaW50ZXJvcGVyYWJsZSAob3BlbiBzb3VyY2UpIG9wdGltaXphdGlvbiBwcm9ibGVtIHNvbHZlcnMgZm9yIFIuDQoNCg0KUk9JIGhhbmRsZSBMUCB1cCB0byBNSUxQIGFuZCBNSVFDUCBwcm9ibGVtcyB1c2luZyB0aGUgZm9sbG93aW5nIHN1cHBvcnRlZCBzb2x2ZXJzIDoNCg0KMS4gbHBTb2x2ZQ0KDQoyLiBxdWFkcHJvZw0KDQozLiBSY3BsZXgNCg0KNC4gUmdscGsgKGRlZmF1bHQpDQoNCjUuIFJzeW1waG9ueQ0KDQoNCg0KDQoNCg0KIyMgU29sdmVycw0KDQpDUExFWCBpcyBhIGNvbW1lcmNpYWwgc29sdmVyLiBTdHVkZW50cyBjYW4gZ2V0IGFjYWRlbWljIGxpY2Vuc2UuIA0KDQpHTFBLIGlzIGZyZWUuIFIgaGFzIHRoZSBoaWdoIGxldmVsIGludGVyZmFjZSBSZ2xway4NCg0KaHR0cDovL3dpbmdscGsuc291cmNlZm9yZ2UubmV0Lw0KDQpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUmdscGsvaW5kZXguaHRtbA0KDQoNCg0KDQoNCg0KDQojIyBMaW5lYXIgUHJvZ2FybW1pbmcNCg0KDQoNCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJST0kiKQ0KI2luc3RhbGwucGFja2FnZXMoYyggIlBvcnRmb2xpb0FuYWx5dGljcyIsIkVDT1NvbHZlUiIsIlJzeW1waG9ueSIsIlJnbHBrIiwiUk9JLnBsdWdpbi5zeW1waG9ueSIsIlJPSS5wbHVnaW4uZ2xwayIsImZQb3J0Zm9saW8iLCJST0kucGx1Z2luLnF1YWRwcm9nIiwiUk9JLnBsdWdpbi5ubG9wdHIiLCJST0kucGx1Z2luLmlwb3AiLCJST0kucGx1Z2luLmVjb3MiKSkNCg0KbGlicmFyeSgiUk9JIikNCg0KI2xpYnJhcnkoIlJPSS5wbHVnaW4uZ2xwayIpDQojbGlicmFyeSgiUk9JLnBsdWdpbi5zeW1waG9ueSIpDQojbGlicmFyeShST0kucGx1Z2luLmlwb3ApDQoNCiNsaWJyYXJ5KFJPSS5wbHVnaW4ubmxvcHRyKQ0KI2xpYnJhcnkoUk9JLnBsdWdpbi5xdWFkcHJvZykNCiNsaWJyYXJ5KFJPSS5wbHVnaW4uZWNvcykNCg0KUk9JX3JlZ2lzdGVyZWRfc29sdmVycygpDQpgYGANCg0KDQoNCg0KDQoNCmBgYHtyfQ0KIyMgU2ltcGxlIGxpbmVhciBwcm9ncmFtLg0KIyMgbWF4aW1pemU6ICAgMiB4XzEgKyA0IHhfMiArIDMgeF8zDQojIyBzdWJqZWN0IHRvOiAzIHhfMSArIDQgeF8yICsgMiB4XzMgPD0gNjANCiMjICAgICAgICAgICAgIDIgeF8xICsgICB4XzIgKyAgIHhfMyA8PSA0MA0KIyMgICAgICAgICAgICAgICB4XzEgKyAzIHhfMiArIDIgeF8zIDw9IDgwDQojIyAgICAgICAgICAgICAgIHhfMSwgeF8yLCB4XzMgYXJlIG5vbi1uZWdhdGl2ZSByZWFsIG51bWJlcnMNCg0KTFAgPC0gT1AoIGMoMiwgNCwgMyksDQogICAgICAgICAgTF9jb25zdHJhaW50KEwgPSBtYXRyaXgoYygzLCAyLCAxLCA0LCAxLCAzLCAyLCAyLCAyKSwgbnJvdyA9IDMpLA0KICAgICAgICAgICAgICAgICAgICAgICBkaXIgPSBjKCI8PSIsICI8PSIsICI8PSIpLA0KICAgICAgICAgICAgICAgICAgICAgICByaHMgPSBjKDYwLCA0MCwgODApKSwNCiAgICAgICAgICBtYXggPSBUUlVFICkNCkxQDQpgYGANCg0KIyMjIyBTb2x2ZQ0KDQpgYGB7cn0NCnNvbCA8LSBST0lfc29sdmUoTFApIyAsIHNvbHZlciA9ICJnbHBrIikNCnNvbA0KYGBgDQoNCiMjIyMgU29sdXRpb25zDQoNCmBgYHtyfQ0Kc29sdXRpb24oc29sLCB0eXBlID0gYygicHJpbWFsIikpDQpzb2x1dGlvbihzb2wsIHR5cGUgPSBjKCJkdWFsIikpDQpgYGANCg0KDQojIyMjIEV4dHJhY3Qgc29sdXRpb25zDQpgYGB7cn0NCnNvbCRzb2x1dGlvbg0Kc29sJG9ianZhbA0Kc29sJHN0YXR1cw0Kc29sJG1lc3NhZ2UNCmBgYA0KDQojIyBRdWFkcmF0aWMgUHJvZ2FybW1pbmcNCg0KDQpJdCB3aWxsIHJlcXVpcmUgcHV0dGluZyBwcm9ibGVtIGluIG1hdHJpeCBmb3JtLg0KDQpgYGB7cn0NCiMjIFNpbXBsZSBxdWFkcmF0aWMgcHJvZ3JhbS4NCiMjIG1pbmltaXplOiAtIDUgeF8yICsgMS8yICh4XzFeMiArIHhfMl4yICsgeF8zXjIpDQojIyBzdWJqZWN0IHRvOiAtNCB4XzEgLSAzIHhfMiAgICAgICA+PSAtOA0KIyMgICAgICAgICAgICAgIDIgeF8xICsgICB4XzIgICAgICAgPj0gIDINCiMjICAgICAgICAgICAgICAgICAgICAtIDIgeF8yICsgeF8zID49ICAwDQoNClFQIDwtIE9QKCBRX29iamVjdGl2ZSAoUSA9IGRpYWcoMSwgMyksIEwgPSBjKDAsIC01LCAwKSksDQogICAgICAgICAgTF9jb25zdHJhaW50KEwgPSBtYXRyaXgoYygtNCwtMywwLDIsMSwwLDAsLTIsMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgIGRpciA9IHJlcCgiPj0iLCAzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgcmhzID0gYygtOCwyLDApKSApDQpRUA0KYGBgDQoNCiMjIyMgU29sdmUNCg0KYGBge3J9DQpzb2wgPC0gUk9JX3NvbHZlKFFQLCBzb2x2ZXIgPSAicXVhZHByb2ciKQ0Kc29sDQpgYGANCg0KDQojIyMjIEV4dHJhY3Qgc29sdXRpb25zDQpgYGB7cn0NCnNvbCRzb2x1dGlvbg0Kc29sJG9ianZhbA0Kc29sJHN0YXR1cw0Kc29sJG1lc3NhZ2UNCmBgYA0KDQoNCiMjIyMgU29sdXRpb25zDQoNCmBgYHtyfQ0Kc29sdXRpb24oc29sLCB0eXBlID0gYygicHJpbWFsIikpDQpzb2x1dGlvbihzb2wsIHR5cGUgPSBjKCJkdWFsIikpDQpgYGANCg0KDQoNCiMjIFBvcnRmb2xpbyBvcHRpbWl6YXRpb24gLSBtaW5pbXVtIHZhcmlhbmNlDQoNCmBgYHtyfQ0KIyMgUG9ydGZvbGlvIG9wdGltaXphdGlvbiAtIG1pbmltdW0gdmFyaWFuY2UNCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBnZXQgbW9udGhseSByZXR1cm5zIG9mIDMwIFVTIHN0b2Nrcw0KZGF0YSggVVMzMCApDQpyIDwtIG5hLm9taXQoIFVTMzAgKQ0KIyMgb2JqZWN0aXZlIGZ1bmN0aW9uIHRvIG1pbmltaXplDQpvYmogPC0gUV9vYmplY3RpdmUoIDIqY292KHIpICkNCiMjIGZ1bGwgaW52ZXN0bWVudCBjb25zdHJhaW50DQpmdWxsX2ludmVzdCA8LSBMX2NvbnN0cmFpbnQoIHJlcCgxLCBuY29sKFVTMzApKSwgIj09IiwgMSApDQojIyBjcmVhdGUgb3B0aW1pemF0aW9uIHByb2JsZW0gLyBsb25nLW9ubHkNCg0KKG9wIDwtIE9QKCBvYmplY3RpdmUgPSBvYmosIGNvbnN0cmFpbnRzID0gZnVsbF9pbnZlc3QgKSkNCmBgYA0KDQojIyMjIFNvbHV0aW9uDQoNCg0KYGBge3J9DQoNCiMjIHNvbHZlIHRoZSBwcm9ibGVtIC0gb25seSB3b3JrcyBpZiBhIFFQIHNvbHZlciBpcyByZWdpc3RlcmVkDQojIyAgDQpyZXMgPC0gUk9JX3NvbHZlKCBvcCApDQpyZXMNCnNvbCA8LSBzb2x1dGlvbiggcmVzICkNCm5hbWVzKCBzb2wgKSA8LSBjb2xuYW1lcyggVVMzMCApDQpyb3VuZCggc29sWyB3aGljaChzb2wgPiAxLzEwXjYpIF0sIDMgKQ0KYGBgDQoNCg0KDQoNCiMjIyBMUCB3aXRoIGJvdW5kYXJ5DQoNCg0KYGBge3J9DQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoUk9JKSkNCmxwX29iaiA8LSBMX29iamVjdGl2ZShjKDEsIDIpKQ0KIGxwX2NvbiA8LSBMX2NvbnN0cmFpbnQoYygxLCAxKSwgZGlyPSI9PSIsIHJocz0yKQ0KIGxwX2JvdW5kIDwtIFZfYm91bmQodWk9MToyLCB1Yj1jKDMsIDMpKQ0KICN1aSBhbiBpbnRlZ2VyIHZlY3RvciBzcGVjaWZ5aW5nIHRoZSBpbmRpY2VzIG9mIG5vbi1zdGFuZGFyZCAoaS5lLiwgdmFsdWVzICE9IEluZikgdXBwZXIgYm91bmRzLg0KICMgdWIgdXAgYm91bmQNCiBscCA8LSBPUChvYmplY3RpdmU9bHBfb2JqLCBjb25zdHJhaW50cz1scF9jb24sIGJvdW5kcz1scF9ib3VuZCwgbWF4aW11bT1GQUxTRSkNCiBib3VuZHMobHApDQpscA0KYGBgDQoNCiANCiMjIyMgU29sdXRpb25zIA0KIA0KYGBge3J9DQogeCA8LSBST0lfc29sdmUobHApDQogeCRvYmp2YWwNCiB4JHNvbHV0aW9uDQpgYGANCg0KIyMjIyAgQ2hhbmdlIGJvdW5kYXJ5IA0KDQpgYGB7cn0NCiMgY2hhbmdlIGJvdW5kYXJ5IA0KIGJvdW5kcyhscCkgPC0gVl9ib3VuZCh1aT0xOjIsIHViPWMoMSwgMSkpDQoNCmBgYA0KDQoNCiMjIyMgU29sdXRpb25zIA0KDQpgYGB7cn0NCiB5IDwtIFJPSV9zb2x2ZShscCkNCiB5JG9ianZhbA0KIHkkc29sdXRpb24NCmBgYA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIE90aGVyIG9wdGltaXphdGlvbiBwYWNrYWdlIGluIFINCg0KDQpXZSBhbHNvIGNhbiBzb2x2ZSBvcHRpbWl6YXRpb24gcHJvYmxlbSB1c2luZyBvdGhlciBwYWNrYWdlcy4NCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcyhjKCJscFNvbHZlIiwgImxwU29sdmVBUEkiKSkNCiNpbnN0YWxsLnBhY2thZ2VzKGMoInF1YWRwcm9nIiwiUmdscGsiLCAibmxtZSIpKQ0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGxwU29sdmUpKQ0KDQpsaWJyYXJ5KGxwU29sdmVBUEkpDQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocXVhZHByb2cpKQ0KI3N1cHByZXNzTWVzc2FnZXMobGlicmFyeShubG1lKSkNCg0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFJnbHBrKSkNCmBgYA0KDQpgYGB7cn0NCmFyZ3MobHApDQphcmdzKHNvbHZlLlFQKQ0KYXJncyhSZ2xwa19zb2x2ZV9MUCkNCiNhcmdzKG5sbWluYikNCiNhcmdzKG9wdGltKQ0KI2FyZ3MoUmNwbGV4KQ0KYGBgDQoNCg0KIyMjICBMaW5lYXIgUHJvZ3JhbW1pbmcgYnkgbHBzb2x2ZQ0KDQpIZXJlIGlzIGEgbGlzdCBvZiBzb21lIGtleSBmZWF0dXJlcyBvZiBscF9zb2x2ZToNCg0KLSBNaXhlZCBJbnRlZ2VyIExpbmVhciBQcm9ncmFtbWluZyAoTUlMUCkgc29sdmVyDQoNCi0gQmFzaWNhbGx5IG5vIGxpbWl0IG9uIG1vZGVsIHNpemUNCg0KLSBJdCBpcyBmcmVlIGFuZCB3aXRoIHNvdXJjZXMNCg0KLSBTdXBwb3J0cyBJbnRlZ2VyIHZhcmlhYmxlcywgU2VtaS1jb250aW51b3VzIHZhcmlhYmxlcyBhbmQgU3BlY2lhbCBPcmRlcmVkIFNldHMNCg0KDQojIyMjIEV4YW1wbGUgYnkgbHBTb2x2ZUFQSQ0KDQpzdHJhaWdodGZvcndhcmQNCg0KIyMjIyMgU2V0IHVwIG1vZGVsDQoNCmBgYHtyfQ0KbGlicmFyeShscFNvbHZlQVBJKQ0KI2NyZWF0ZSBhbiBlbXB0eSBtb2RlbA0KbHByZWMgPC0gbWFrZS5scChucm93ID0wLCBuY29sPSA0KQ0KIyBucm93CTogIyBhIG5vbm5lZ2F0aXZlIGludGVnZXIgdmFsdWUgc3BlY2lmeWluZyB0aGUgbnVtYmVyIG9mIGNvbnN0YWludHMgaW4gdGhlIGxpbmVhciBwcm9ncmFtLg0KIyBuY29sCTogIyBhIG5vbm5lZ2F0aXZlIGludGVnZXIgdmFsdWUgc3BlY2lmeWluZyB0aGUgbnVtYmVyIG9mIGRlY2lzaW9uIHZhcmlhYmxlcyBpbiB0aGUgbGluZWFyIHByb2dyYW0uDQoNCg0KYGBgDQoNCiMjIyMjIFNldHVwIG9iamVjdGl2ZSBmdW5jdGlvbg0KDQpgYGB7cn0NCiMgb2JqZWN0DQpzZXQub2JqZm4obHByZWMsIGMoMSwgMywgNi4yNCwgMC4xKSkNCmBgYA0KDQojIyMjIyBBZGQgY29uc3RyYWludHMNCg0KYGBge3J9DQoNCiNjb25zdHJhaW50DQphZGQuY29uc3RyYWludChscHJlYywgYygwLCA3OC4yNiwgMCwgMi45KSwgIj49IiwgOTIuMykNCmFkZC5jb25zdHJhaW50KGxwcmVjLCBjKDAuMjQsIDAsIDExLjMxLCAwKSwgIjw9IiwgMTQuOCkNCmFkZC5jb25zdHJhaW50KGxwcmVjLCBjKDEyLjY4LCAwLCAwLjA4LCAwLjkpLCAiPj0iLCA0KQ0KDQpgYGANCg0KDQojIyMjIyBTZXQgYm91bmRzDQoNCmBgYHtyfQ0Kc2V0LmJvdW5kcyhscHJlYywgbG93ZXIgPSBjKDI4LjYsIDE4KSwgY29sdW1ucyA9IGMoMSwgNCkpDQpzZXQuYm91bmRzKGxwcmVjLCB1cHBlciA9IDQ4Ljk4LCBjb2x1bW5zID0gNCkNCg0KYGBgDQoNCg0KIyMjIyMgU2V0dXAgbmFtZXMgDQoNCg0KYGBge3J9DQpSb3dOYW1lcyA8LSBjKCJUSElTUk9XIiwgIlRIQVRST1ciLCAiTEFTVFJPVyIpDQpDb2xOYW1lcyA8LSBjKCJDT0xPTkUiLCAiQ09MVFdPIiwgIkNPTFRIUkVFIiwgIkNPTEZPVVIiKQ0KZGltbmFtZXMobHByZWMpIDwtIGxpc3QoUm93TmFtZXMsIENvbE5hbWVzKQ0KbHByZWMNCmBgYA0KDQoNCg0KDQojIyMjIyBTb2x2ZSB0aGUgbW9kZWwNCg0KYGBge3J9DQpzb2x2ZShscHJlYykNCiAgIyANCiAgIyANCg0KYGBgDQoNCg0KDQojIyMjIyBFeHRyYWN0IHNvbHV0aW9ucw0KDQoNCmBgYHtyfQ0KZ2V0Lm9iamVjdGl2ZShscHJlYykNCiAgIyBbMV0gMzEuNzgyNzYNCiAgIyANCmdldC52YXJpYWJsZXMobHByZWMpDQogICMgWzFdIDI4LjYwMDAwICAwLjAwMDAwICAwLjAwMDAwIDMxLjgyNzU5DQogICMgDQpnZXQuY29uc3RyYWludHMobHByZWMpDQogICMgWzFdICA5Mi4zMDAwICAgNi44NjQwIDM5MS4yOTI4DQpgYGANCg0KDQoNCiMjIyMgRXhhbXBsZSBieSBscFNvbHZlDQoNCnByb3ZpZGUgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaW5jbHVkaW5nIHNlbnNpdGl2aXRpZXMgYW5hbHlzaXMuIA0KDQoNCg0KDQojIyMjIyBTZXR1cCBvYmplY3RpdmUgZnVuY3Rpb24NCg0KYGBge3J9DQpsaWJyYXJ5KGxwU29sdmUpDQojDQojIFNldCB1cCBwcm9ibGVtOiBtYXhpbWl6ZQ0KIyAgIHgxICsgOSB4MiArICAgeDMgc3ViamVjdCB0bw0KIyAgIHgxICsgMiB4MiArIDMgeDMgIDw9IDkNCiMgMyB4MSArIDIgeDIgKyAyIHgzIDw9IDE1DQojDQpmLm9iaiA8LSBjKDEsIDksIDEpDQpgYGANCg0KIyMjIyMgU2V0dXAgY29uc3RyYWludHMNCg0KYGBge3J9DQpmLmNvbiA8LSBtYXRyaXggKGMoMSwgMiwgMywgMywgMiwgMiksIG5yb3c9MiwgYnlyb3c9VFJVRSkNCmYuZGlyIDwtIGMoIjw9IiwgIjw9IikNCmYucmhzIDwtIGMoOSwgMTUpDQoNCmBgYA0KDQojIyMjIyBTb2x2ZSB0aGUgbW9kZWwNCg0KYGBge3J9DQojDQpscCAoIm1heCIsIGYub2JqLCBmLmNvbiwgZi5kaXIsIGYucmhzKQ0KIyMgIFN1Y2Nlc3M6IHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gaXMgNDAuNQ0KDQpgYGANCg0KDQojIyMjIyBFeHRyYWN0IHNvbHV0aW9ucw0KDQpgYGB7cn0NCg0KbHAgKCJtYXgiLCBmLm9iaiwgZi5jb24sIGYuZGlyLCBmLnJocykkc29sdXRpb24NCmBgYA0KDQojIyMjIyMgIEdldCBzZW5zaXRpdml0aWVzDQoNCmBgYHtyfQ0KDQoNCiMgR2V0IHNlbnNpdGl2aXRpZXMNCiMNCmxwICgibWF4IiwgZi5vYmosIGYuY29uLCBmLmRpciwgZi5yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKSRzZW5zLmNvZWYuZnJvbQ0KIyMgIFsxXSAtMWUrMzAgIDJlKzAwIC0xZSszMA0KbHAgKCJtYXgiLCBmLm9iaiwgZi5jb24sIGYuZGlyLCBmLnJocywgY29tcHV0ZS5zZW5zPVRSVUUpJHNlbnMuY29lZi50byAgDQojIyAgWzFdIDQuNTBlKzAwIDEuMDBlKzMwIDEuMzVlKzAxDQojDQojIFJpZ2h0IG5vdyB0aGUgZHVhbCB2YWx1ZXMgZm9yIHRoZSBjb25zdHJhaW50cyBhbmQgdGhlIHZhcmlhYmxlcyBhcmUNCiMgY29tYmluZWQsIGNvbnN0cmFpbnRzIGNvbWluZyBmaXJzdC4gU28gaW4gdGhpcyBleGFtcGxlLi4uDQojDQpscCAoIm1heCIsIGYub2JqLCBmLmNvbiwgZi5kaXIsIGYucmhzLCBjb21wdXRlLnNlbnM9VFJVRSkkZHVhbHMgICAgIA0KIyMgIFsxXSAgIDQuNSAgIDAuMCAgLTMuNSAgIDAuMCAtMTAuNQ0KIw0KIyAuLi50aGUgZHVhbHMgb2YgdGhlIGNvbnN0cmFpbnRzIGFyZSA0LjUgYW5kIDAsIGFuZCBvZiB0aGUgdmFyaWFibGVzLA0KIyAtMy41LCAwLjAsIC0xMC41LiBIZXJlIGFyZSB0aGUgbG93ZXIgYW5kIHVwcGVyIGxpbWl0cyBvbiB0aGVzZToNCiMNCmxwICgibWF4IiwgZi5vYmosIGYuY29uLCBmLmRpciwgZi5yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKSRkdWFscy5mcm9tDQojIyAgWzFdICAwZSswMCAtMWUrMzAgLTFlKzMwIC0xZSszMCAtNmUrMDANCmxwICgibWF4IiwgZi5vYmosIGYuY29uLCBmLmRpciwgZi5yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKSRkdWFscy50byAgDQojIyAgWzFdIDEuNWUrMDEgMS4wZSszMCAzLjBlKzAwIDEuMGUrMzAgMy4wZSswMA0KIw0KDQoNCmBgYA0KDQojIyMjIEludGVyZ3IgUHJvZ3JhbW1pbmcNCg0KYGBge3J9DQojIFJ1biBhZ2FpbiwgdGhpcyB0aW1lIHJlcXVpcmluZyB0aGF0IGFsbCB0aHJlZSB2YXJpYWJsZXMgYmUgaW50ZWdlcg0KIw0KbHAgKCJtYXgiLCBmLm9iaiwgZi5jb24sIGYuZGlyLCBmLnJocywgaW50LnZlYz0xOjMpDQojIyAgU3VjY2VzczogdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBpcyAzNw0KbHAgKCJtYXgiLCBmLm9iaiwgZi5jb24sIGYuZGlyLCBmLnJocywgaW50LnZlYz0xOjMpJHNvbHV0aW9uDQojIyAgWzFdIDEgNCAwDQojDQojIFlvdSBjYW4gZ2V0IHNlbnNpdGl2aXRpZXMgaW4gdGhlIGludGVnZXIgY2FzZSwgYnV0IHRoZXkncmUgaGFyZGVyIHRvDQojIGludGVycHJldC4NCiMNCmxwICgibWF4IiwgZi5vYmosIGYuY29uLCBmLmRpciwgZi5yaHMsIGludC52ZWM9MTozLCBjb21wdXRlLnNlbnM9VFJVRSkkZHVhbHMNCiMjICBbMV0gMSAwIDAgNyAwDQojDQpgYGANCg0KDQoNCg0KIyMjIFF1YWRyYXRpYyBQcm9ncmFtbWluZyBieSBxdWFkcHJvZw0KDQoNCnNvbHZpbmcgcXVhZHJhdGljIHByb2dyYW1taW5nIHByb2JsZW1zIG9mIHRoZSBmb3JtIG1pbigtZF5UIGIgKyAxLzIgYl5UIEQgYikgd2l0aCB0aGUgY29uc3RyYWludHMgQV5UIGIgPj0gYl8wLg0KDQpEIGlzIGRpYWduYWwgbWF0aXggDQoNCmBgYHtyfQ0KbGlicmFyeShxdWFkcHJvZykNCg0KIyMgQXNzdW1lIHdlIHdhbnQgdG8gbWluaW1pemU6IC0oMCA1IDApICUqJSBiICsgMS8yIGJeVCBEIGINCiMjIHVuZGVyIHRoZSBjb25zdHJhaW50czogICAgICBBXlQgYiA+PSBiMA0KIyMgICAgICAgICAgMSAwIDANCiMjICAgICAgRCA9IDAgMSAwDQojIyAgICAgICAgICAwIDAgMQ0KIyMgd2l0aCBiMCA9ICgtOCwyLDApXlQNCiMjIGFuZCAgICAgICgtNCAgMiAgMCkgDQojIyAgICAgIEEgPSAoLTMgIDEgLTIpDQojIyAgICAgICAgICAoIDAgIDAgIDEpDQojIyB3ZSBjYW4gdXNlIHNvbHZlLlFQIGFzIGZvbGxvd3M6DQojIw0KRG1hdCAgICAgICA8LSBtYXRyaXgoMCwzLDMpDQpkaWFnKERtYXQpIDwtIDENCmR2ZWMgICAgICAgPC0gYygwLDUsMCkNCkFtYXQgICAgICAgPC0gbWF0cml4KGMoLTQsLTMsMCwyLDEsMCwwLC0yLDEpLDMsMykNCmJ2ZWMgICAgICAgPC0gYygtOCwyLDApDQpzb2x2ZS5RUChEbWF0LGR2ZWMsQW1hdCxidmVjPWJ2ZWMpDQoNCmBgYA0KDQoNCg0KDQoNCiMjIyBGb290YmFsbCBNSVAgYnkgZ2xwaw0KDQoNCg0KdHJ5aW5nIHRvIHBpY2sgdGhlIGJlc3QgcG9zc2libGUgZmFudGFzeSBmb290YmFsbCB0ZWFtIGdpdmVuIGRpZmZlcmVudCBjb25zdHJhaW50cy4gIGdvYWwgaXMgdG8gcGljayB0aGUgcGxheWVycyB0aGF0IG1heGltaXplIHRoZSBzdW0gb2YgdGhlaXIgcHJvamVjdGVkIHBvaW50cy4NCg0KVGhlIGNvbnN0cmFpbnRzIGFyZToNCg0KMSkgVGhlIHRlYW0gbXVzdCBpbmNsdWRlOg0KDQogICAgLSAxIFFCDQogIA0KICAgIC0gMiBSQnMNCg0KICAgIC0gMiBXUnMNCg0KICAgIC0gMSBURQ0KDQoyKSBBIHBsYXllcidzIHJpc2sgbXVzdCBub3QgZXhjZWVkIDYNCg0KMykgVGhlIHN1bSBvZiB0aGUgcGxheWVycycgY29zdHMgbXVzdCBub3QgZXhjZWVkIDMwMC4gDQoNCg0KYGBge3J9DQojaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNTE0NzM5OC9vcHRpbWl6ZS12YWx1ZS13aXRoLWxpbmVhci1vci1ub24tbGluZWFyLWNvbnN0cmFpbnRzLWluLXINCg0KDQoNCiMgV2UgYXJlIGdvaW5nIHRvIHNvbHZlOg0KIyBtYXhpbWl6ZSBmJ3ggc3ViamVjdCB0byBBKnggPGRpcj4gYg0KIyB3aGVyZToNCiMgICB4IGlzIHRoZSB2YXJpYWJsZSB0byBzb2x2ZSBmb3I6IGEgdmVjdG9yIG9mIDAgb3IgMToNCiMgICAgIDEgd2hlbiB0aGUgcGxheWVyIGlzIHNlbGVjdGVkLCAwIG90aGVyd2lzZSwNCiMgICBmIGlzIHlvdXIgb2JqZWN0aXZlIHZlY3RvciwNCiMgICBBIGlzIGEgbWF0cml4LCBiIGEgdmVjdG9yLCBhbmQgPGRpcj4gYSB2ZWN0b3Igb2YgIjw9IiwgIj09Iiwgb3IgIj49IiwNCiMgICBkZWZpbmluZyB5b3VyIGxpbmVhciBjb25zdHJhaW50cy4NCg0KDQpuYW1lIDwtIGMoIkFhcm9uIFJvZGdlcnMiLCJUb20gQnJhZHkiLCJBcmlhbiBGb3N0ZXIiLCJSYXkgUmljZSIsIkxlU2VhbiBNY0NveSIsIkNhbHZpbiBKb2huc29uIiwiTGFycnkgRml0emdlcmFsZCIsIldlcyBXZWxrZXIiLCJSb2IgR3Jvbmtvd3NraSIsIkppbW15IEdyYWhhbSIpDQoNCg0KIyBwb3NpdGlvbg0KcG9zIDwtIGMoIlFCIiwiUUIiLCJSQiIsIlJCIiwiUkIiLCJXUiIsIldSIiwiV1IiLCJURSIsIlRFIikNCg0KIyBwb2ludHMNCnB0cyA8LSBjKDE2NywgMTM2LCAxOTUsIDE3NCwgMTQ0LCAxMzUsIDg5LCA4MSwgMTE0LCAxMTEpIA0KDQpyaXNrIDwtIGMoMi45LCAzLjQsIDAuNywgMS4xLCAzLjUsIDUuMCwgNi43LCA0LjcsIDMuNywgOC44KSANCg0KY29zdCA8LSBjKDYwLCA0NywgNjMsIDYyLCA0MCwgNjAsIDUwLCAzNSwgNDAsIDQwKSANCg0KI215ZGF0YSA8LSBkYXRhLmZyYW1lKG5hbWUsIHBvcywgcHRzLCByaXNrLCBjb3N0KSANCg0KIyBudW1iZXIgb2YgdmFyaWFibGVzDQpudW0ucGxheWVycyA8LSBsZW5ndGgobmFtZSkNCg0KDQoNCiMgb2JqZWN0aXZlOg0KZiA8LSBwdHMNCg0KDQoNCiMgdGhlIHZhcmlhYmxlIGFyZSBib29sZWFucw0KdmFyLnR5cGVzIDwtIHJlcCgiQiIsIG51bS5wbGF5ZXJzKQ0KDQoNCg0KIyB0aGUgY29uc3RyYWludHMNCkEgPC0gcmJpbmQoYXMubnVtZXJpYyhwb3MgPT0gIlFCIiksICMgbnVtIFFCDQogICAgICAgICAgIGFzLm51bWVyaWMocG9zID09ICJSQiIpLCAjIG51bSBSQg0KICAgICAgICAgICBhcy5udW1lcmljKHBvcyA9PSAiV1IiKSwgIyBudW0gV1INCiAgICAgICAgICAgYXMubnVtZXJpYyhwb3MgPT0gIlRFIiksICMgbnVtIFRFDQogICAgICAgICAgIGRpYWcocmlzayksICAgICAgICAgICAgICAjIHBsYXllcidzIHJpc2ssIGEgc2V0IG9mIG11bHRpcGxlIGNvbnN0cmFpbnRzDQogICAgICAgICAgIGNvc3QpICAgICAgICAgICAgICAgICAgICAjIHRvdGFsIGNvc3QNCg0KIyBkaXJldGlvbg0KZGlyIDwtIGMoIj09IiwNCiAgICAgICAgICI9PSIsDQogICAgICAgICAiPT0iLA0KICAgICAgICAgIj09IiwNCiAgICAgICAgIHJlcCgiPD0iLCBudW0ucGxheWVycyksDQogICAgICAgICAiPD0iKQ0KDQojIHJocyBiIHZlY3Rvcg0KYiA8LSBjKDEsDQogICAgICAgMiwNCiAgICAgICAyLA0KICAgICAgIDEsDQogICAgICAgcmVwKDYsIG51bS5wbGF5ZXJzKSwNCiAgICAgICAzMDApDQoNCg0KDQojIHNvbHZlDQpsaWJyYXJ5KFJnbHBrKQ0Kc29sIDwtIFJnbHBrX3NvbHZlX0xQKG9iaiA9IGYsIG1hdCA9IEEsIGRpciA9IGRpciwgcmhzID0gYiwNCiAgICAgICAgICAgICAgICAgICAgICB0eXBlcyA9IHZhci50eXBlcywgbWF4ID0gVFJVRSkNCnNvbA0KIyAkb3B0aW11bQ0KIyBbMV0gODM2ICAgICAgICAgICAgICAgICAgICAgICMjIyA8LSB0aGUgb3B0aW1hbCB0b3RhbCBwb2ludHMNCg0KDQoNCiMgJHNvbHV0aW9uDQojICBbMV0gMSAwIDEgMCAxIDEgMCAxIDEgMCAgICAgIyMjIDwtIGEgYDFgIGZvciB0aGUgc2VsZWN0ZWQgcGxheWVycw0KDQojICRzdGF0dXMNCiMgWzFdIDAgICAgICAgICAgICAgICAgICAgICAgICAjIyMgPC0gYW4gb3B0aW1hbCBzb2x1dGlvbiBoYXMgYmVlbiBmb3VuZA0KDQojIHlvdXIgZHJlYW0gdGVhbQ0KbmFtZVtzb2wkc29sdXRpb24gPT0gMV0NCiMgWzFdICJBYXJvbiBSb2RnZXJzIiAgIkFyaWFuIEZvc3RlciIgICAiTGVTZWFuIE1jQ295Ig0KIyBbNF0gIkNhbHZpbiBKb2huc29uIiAiV2VzIFdlbGtlciIgICAgICJSb2IgR3Jvbmtvd3NraQ0KDQoNCiMgdG90YWwgY29zdA0Kc3VtKGNvc3QgKiBzb2wkc29sdXRpb24pDQoNCmBgYA0KDQoNCg0KDQoNCg==