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 :
lpSolve
quadprog
Rcplex
Rglpk (default)
Rsymphony
more…
Install all the packages.
Uncomment the code in follow chunk and install all packages.
#install.packages("ROI")
#install.packages(c( "Rglpk","ROI.plugin.symphony","ROI.plugin.glpk","ROI.plugin.quadprog","ROI.plugin.nloptr","ROI.plugin.ipop","ROI.plugin.ecos"))
Linear Progarmming
library("ROI")
ROI: R Optimization Infrastructure
Registered solver plugins: nlminb, ecos, glpk, ipop, nloptr, quadprog, symphony.
Default solver: auto.
$$
\[\begin{align*}
\text{maximize: } 2 x_1 &+ 4 x_2 + 3 x_3\\
\text{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
\end{align*}\]
$$
LP # model named as LP
ROI Optimization Problem:
Maximize a linear objective function with
- 3 objective variables,
subject to
- 3 constraints of type linear.
Solve
sol
Optimal solution found.
The objective value is: 9.000000e+01
Solutions
solution(sol, type = c("dual")) # solution for dual problem: minimize by subject to A'y >= c
[1] -2.5 -2.0 0.0
Quadratic Progarmming
It will require putting problem in matrix form.
three decision variables: x1, x2, x3
$$
\[\begin{align*}
\text{minimize: } - 5 x_2 + 1/2 (x_1^2 + x_2^2 + x_3^2)\\
\text{subject to: } -4 x_1 - 3 x_2 & >= -8 \\
2 x_1 + x_2 & >= 2 \\
- 2 x_2 + x_3 & >= 0
\end{align*}\]
$$
QP
ROI Optimization Problem:
Minimize a quadratic objective function with
- 3 objective variables,
subject to
- 3 constraints of type linear.
Solve
sol
Optimal solution found.
The objective value is: -2.380952e+00
Solutions
solution(sol, type = c("dual"))
[1] NA
Portfolio optimization - minimum variance
To minimize the risk, we choose invest in 30 stocks and we need to decide the share for each stock. Decision variables: x1, x2,…, x30. sum(x, i) = 1. If xi is allowed to negative, it means we can borrow stock and sell it.
(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
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
three decision variables: x1, x2
$$
\[\begin{align*}
\text{minimize: } x_1 + 2* x_2\\
\text{subject to: } x_1 + x_2 & = 2 \\
x_1 & <= 3 \\
x_2 & <= 3
\end{align*}\]
$$
lp
ROI Optimization Problem:
Minimize a linear objective function with
- 2 objective variables,
subject to
- 1 constraints of type linear.
Solutions
x$solution
[1] 2 0
Change boundary
$$
\[\begin{align*}
\text{minimize: } x_1 + 2* x_2\\
\text{subject to: } x_1 + x_2 & = 2 \\
x_1 & <= 1 \\
x_2 & <= 1
\end{align*}\]
$$
Solutions
y$solution
[1] 1 1
Other optimization package in R
We also can solve optimization problem using other packages.
Install packages
Uncomment the code in follow chunk and install all packages.
install.packages(c("quadprog", "nlme","Rglpk"))
Error in install.packages : Updating loaded packages
suppressMessages(library(Rglpk))
Error in library(Rglpk) : there is no package called æ… glpk?
args() function tells us how we can use those functions such as lp, solve.QP, Rglpk_solve_LP
args(lp)
args(solve.QP)
args(Rglpk_solve_LP)
#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
If we have 4 decision variables: x1,…, x4
\[\begin{align*}
\text{minimize: } x_1 + 3 x_2 + 6.24 x_3 + 0.1 x_4\\
\text{subject to: } 78.26 x_2 + 2.9 x_4 & >= 92.3 \\
0.2 x_1 + 11.91 x_3 & <= 14.8 \\
12.68 x_1 + 0.08 x_3 + 0.9 x_4 &>= 4 \\
18 <= x_4 & <= 48.98 \\
x_1 & >= 28.6
\end{align*}\]
Set up model
Setup objective function
set.objfn(lpmodel, c(1, 3, 6.24, 0.1)) # object function, vector c
Error in set.objfn(lpmodel, c(1, 3, 6.24, 0.1)) :
object 'lpmodel' not found
Add constraints
Set bounds
Setup names
Show model setting
lpmodel
Not necessary
lpmodel
Model name:
C1 C2 C3 C4
Minimize 1 3 6.24 0.1
R1 0 78.26 0 2.9 >= 92.3
R2 0.24 0 11.31 0 <= 14.8
R3 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(lpmodel)
[1] 0
Example by lpSolve
provide a lot of information including sensitivities analysis.
\[\begin{align*}
\text{maximize: } x_1 + 9 x_2 + x_3 \\
\text{subject to: } x_1 + 2 x_2 + 3 x_3 & <= 9 \\
3 x_1 + 2 x_2 + 2 x_3 & <= 15
\end{align*}\]
Setup objective function
Setup constraints
Solve the model
lp ("max", model_obj, model_con, model_dir, model_rhs)
Success: the objective function is 40.5
Intergr Programming
Previous model with constraints that all decision variables are integer.
lp ("max", model_obj, model_con, model_dir, model_rhs, int.vec=1:3, compute.sens=TRUE)$duals
[1] 1 0 0 7 -2
Quadratic Programming by quadprog
solving quadratic programming problems of the form
min(c’ x + 1/2 x’ D x)
with the constraints A’ x >= b_0.
$$
\[\begin{align*}
\text{minimize: } - 5 x_2 + 1/2 (x_1^2 + x_2^2 + x_3^2)\\
\text{subject to: } -4 x_1 - 3 x_2 & >= -8 \\
2 x_1 + x_2 & >= 2 \\
- 2 x_2 + x_3 & >= 0
\end{align*}\]
$$
D is diagnal matix . 1/2 is convetion.
solve.QP(Dmat,cvec,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
Solve it using ROI
football <- OP( f, # objective function, vector c
L_constraint(L = A, # linear constraint: Maxtrix A
dir = dir, # direction
rhs = b), # right hand side: vector b
types = var.types,
max = TRUE)
football
ROI Optimization Problem:
Maximize a linear objective function with
- 10 objective variables,
subject to
- 15 constraints of type linear.
Some of the objective variables are of type binary or integer.
Solve
sol
Optimal solution found.
The objective value is: 8.360000e+02
Solutions
solution(sol, type = c("dual")) # solution for dual problem: minimize by subject to A'y >= c
[1] NA
LS0tDQp0aXRsZTogIkVDT040NTcgUiBsYWIgMyBPcHRpbWl6YXRpb24gaW4gUiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyMgSW5mb3JtYXRpb24gYWJvdXQgb3B0aW1pemFpb24gaW4gUg0KDQpodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi92aWV3cy9PcHRpbWl6YXRpb24uaHRtbA0KDQpodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvUk9JL3ZlcnNpb25zLzAuMi0xDQoNCg0KVGhpcyBmaWxlIGNhbiBiZSBkb3dubG9hZGVkIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9FQ09ONDU3LWZhMTYvbGFicy9yYXcvbWFzdGVyL2xhYjAzL2xhYjAzLlJtZA0KDQojIyBUaGUgUiBPcHRpbWl6YXRpb24gSW5mcmFzdHJ1Y3R1cmUgKFJPSSkNCg0KVGhlIFIgT3B0aW1pemF0aW9uIEluZnJhc3RydWN0dXJlIChST0kpIHBhY2thZ2UgcHJvbW90ZXMgdGhlIGRldmVsb3BtZW50IGFuZCB1c2Ugb2YgaW50ZXJvcGVyYWJsZSAob3BlbiBzb3VyY2UpIG9wdGltaXphdGlvbiBwcm9ibGVtIHNvbHZlcnMgZm9yIFIuDQoNCg0KUk9JIGhhbmRsZSBMUCB1cCB0byBNSUxQIGFuZCBNSVFDUCBwcm9ibGVtcyB1c2luZyB0aGUgZm9sbG93aW5nIHN1cHBvcnRlZCBzb2x2ZXJzIDoNCg0KMS4gbHBTb2x2ZQ0KDQoyLiBxdWFkcHJvZw0KDQozLiBSY3BsZXgNCg0KNC4gUmdscGsgKGRlZmF1bHQpDQoNCjUuIFJzeW1waG9ueQ0KDQptb3JlLi4uIA0KDQoNCg0KDQojIyBTb2x2ZXJzDQoNCkNQTEVYIGlzIGEgY29tbWVyY2lhbCBzb2x2ZXIuIFN0dWRlbnRzIGNhbiBnZXQgYWNhZGVtaWMgbGljZW5zZS4gDQoNCkdMUEsgaXMgZnJlZS4gUiBoYXMgdGhlIGhpZ2ggbGV2ZWwgaW50ZXJmYWNlIFJnbHBrLg0KDQpodHRwOi8vd2luZ2xway5zb3VyY2Vmb3JnZS5uZXQvDQoNCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9SZ2xway9pbmRleC5odG1sDQoNCg0KDQojIyBJbnN0YWxsIGFsbCB0aGUgcGFja2FnZXMuDQoNClVuY29tbWVudCB0aGUgY29kZSBpbiBmb2xsb3cgY2h1bmsgYW5kIGluc3RhbGwgYWxsIHBhY2thZ2VzLg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJST0kiKQ0KI2luc3RhbGwucGFja2FnZXMoYyggIlJnbHBrIiwiUk9JLnBsdWdpbi5zeW1waG9ueSIsIlJPSS5wbHVnaW4uZ2xwayIsIlJPSS5wbHVnaW4ucXVhZHByb2ciLCJST0kucGx1Z2luLm5sb3B0ciIsIlJPSS5wbHVnaW4uaXBvcCIsIlJPSS5wbHVnaW4uZWNvcyIpKQ0KYGBgDQoNCg0KDQoNCiMjIExpbmVhciBQcm9nYXJtbWluZw0KDQoNCg0KDQoNCmBgYHtyfQ0KDQoNCmxpYnJhcnkoIlJPSSIpDQoNCiNsaWJyYXJ5KCJST0kucGx1Z2luLmdscGsiKQ0KI2xpYnJhcnkoIlJPSS5wbHVnaW4uc3ltcGhvbnkiKQ0KI2xpYnJhcnkoUk9JLnBsdWdpbi5pcG9wKQ0KDQojbGlicmFyeShST0kucGx1Z2luLm5sb3B0cikNCiNsaWJyYXJ5KFJPSS5wbHVnaW4ucXVhZHByb2cpDQojbGlicmFyeShST0kucGx1Z2luLmVjb3MpDQoNClJPSV9yZWdpc3RlcmVkX3NvbHZlcnMoKQ0KYGBgDQoNCg0KDQokJFxiZWdpbnthbGlnbip9IA0KICAgIFx0ZXh0e21heGltaXplOiB9ICAgMiB4XzEgJisgNCB4XzIgKyAzIHhfM1xcDQpcdGV4dHtzdWJqZWN0IHRvOiB9ICAgICAzIHhfMSAmKyA0IHhfMiArIDIgeF8zICYgPD0gNjAgXFwNCiAgICAgICAgICAgICAgICAgICAgICAgIDIgeF8xICYrICAgeF8yICsgICB4XzMgJiA8PSA0MCBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4XzEgJisgMyB4XzIgKyAyIHhfMyAmIDw9IDgwDQoNClxlbmR7YWxpZ24qfSQkDQoNCg0KDQoNCmBgYHtyfQ0KIyMgU2ltcGxlIGxpbmVhciBwcm9ncmFtLg0KIyMgdGhyZWUgZGVjaXNpb24gdmFyaWFibGVzOiB4MSwgeDIsIHgzDQojIyBtYXRyaXggZm9ybTogICAgb2JqZWN0aXZlOiBjKnggDQojIyAgICAgICAgICAgICAgIGNvbnN0cmFpbnRzOiBBKnggPD0gYiAgDQojIyBtYXhpbWl6ZTogICAyIHhfMSArIDQgeF8yICsgMyB4XzMNCiMjIHN1YmplY3QgdG86IDMgeF8xICsgNCB4XzIgKyAyIHhfMyA8PSA2MA0KIyMgICAgICAgICAgICAgMiB4XzEgKyAgIHhfMiArICAgeF8zIDw9IDQwDQojIyAgICAgICAgICAgICAgIHhfMSArIDMgeF8yICsgMiB4XzMgPD0gODANCiMjICAgICAgICAgICAgICAgeF8xLCB4XzIsIHhfMyBhcmUgbm9uLW5lZ2F0aXZlIHJlYWwgbnVtYmVycw0KDQpMUCA8LSBPUCggYygyLCA0LCAzKSwgIyBvYmplY3RpdmUgZnVuY3Rpb24sIHZlY3RvciBjIA0KICAgICAgICAgIExfY29uc3RyYWludChMID0gbWF0cml4KGMoMywgMiwgMSwgNCwgMSwgMywgMiwgMSwgMiksIG5yb3cgPSAzKSwgIyBsaW5lYXIgY29uc3RyYWludDogTWF4dHJpeCBBDQogICAgICAgICAgICAgICAgICAgICAgIGRpciA9IGMoIjw9IiwgIjw9IiwgIjw9IiksICMgZGlyZWN0aW9uDQogICAgICAgICAgICAgICAgICAgICAgIHJocyA9IGMoNjAsIDQwLCA4MCkpLCAgICAgICMgcmlnaHQgaGFuZCBzaWRlOiB2ZWN0b3IgYiANCiAgICAgICAgICBtYXggPSBUUlVFICkgICMgZGVmYXVsdHMgaXMgbWluaW1pemU6IG1heCA9IEZBTFNFLiBjaGFuZ2UgdG8gbWF4DQpMUCAjIG1vZGVsIG5hbWVkIGFzIExQDQpgYGANCg0KIyMjIyBTb2x2ZQ0KDQpgYGB7cn0NCnNvbCA8LSBST0lfc29sdmUoTFApIyAsIHNvbHZlciA9ICJnbHBrIikgICMgU29sdmUgbW9kZWwgTFAgd2l0aCBzcGVjaWZpYyBzb2x2ZXIgZ2xwayBvciBhdXRvbWF0aWNhbGx5IGNob29zZSBieSBST0kuDQpzb2wNCmBgYA0KDQojIyMjIFNvbHV0aW9ucw0KDQpgYGB7cn0NCnNvbHV0aW9uKHNvbCwgdHlwZSA9IGMoInByaW1hbCIpKSAjIHNvbHV0aW9uIGZvciBwcmltYWwgcHJvYmxlbQ0Kc29sdXRpb24oc29sLCB0eXBlID0gYygiZHVhbCIpKSAgIyBzb2x1dGlvbiBmb3IgZHVhbCBwcm9ibGVtOiBtaW5pbWl6ZSBieSBzdWJqZWN0IHRvIEEneSA+PSBjDQpgYGANCg0KDQojIyMjIEV4dHJhY3Qgc29sdXRpb25zDQpgYGB7cn0NCnNvbCRzb2x1dGlvbg0Kc29sJG9ianZhbA0Kc29sJHN0YXR1cw0Kc29sJG1lc3NhZ2UgIyBkZXRhaWxlZCBtZXNzYWdlIGZvciBzb2x1dGlvbiANCmBgYA0KDQojIyBRdWFkcmF0aWMgUHJvZ2FybW1pbmcNCg0KDQpJdCB3aWxsIHJlcXVpcmUgcHV0dGluZyBwcm9ibGVtIGluIG1hdHJpeCBmb3JtLg0KDQp0aHJlZSBkZWNpc2lvbiB2YXJpYWJsZXM6IHgxLCB4MiwgeDMNCg0KJCRcYmVnaW57YWxpZ24qfSANCiAgICBcdGV4dHttaW5pbWl6ZTogfSAgIC0gNSB4XzIgKyAxLzIgKHhfMV4yICsgeF8yXjIgKyB4XzNeMilcXA0KXHRleHR7c3ViamVjdCB0bzogfSAgICAgLTQgeF8xIC0gMyB4XzIgICAgICAgJiA+PSAtOCBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgIDIgeF8xICsgICB4XzIgICAgICAgJiA+PSAyIFxcDQogICAgICAgICAgICAgICAgICAgICAgICAgLSAyIHhfMiArIHhfMyAmID49IDANCg0KXGVuZHthbGlnbip9JCQNCg0KDQoNCmBgYHtyfQ0KIyMgU2ltcGxlIHF1YWRyYXRpYyBwcm9ncmFtLg0KIyMgbWluaW1pemU6IC0gNSB4XzIgKyAxLzIgKHhfMV4yICsgeF8yXjIgKyB4XzNeMikNCiMjIHN1YmplY3QgdG86IC00IHhfMSAtIDMgeF8yICAgICAgID49IC04DQojIyAgICAgICAgICAgICAgMiB4XzEgKyAgIHhfMiAgICAgICA+PSAgMg0KIyMgICAgICAgICAgICAgICAgICAgIC0gMiB4XzIgKyB4XzMgPj0gIDANCg0KUVAgPC0gT1AoIFFfb2JqZWN0aXZlIChRID0gZGlhZygxLCAzKSwgTCA9IGMoMCwgLTUsIDApKSwgICAjIHF1YWRyYXRpYyBvYmplY3RpdmUNCiAgICAgICAgICAjIHF1ZHJhdGljIHBhcnQ6IHgnUXgsICAgICBsaW5lYXIgcGFydDogTHgNCiAgICAgICAgICBMX2NvbnN0cmFpbnQoTCA9IG1hdHJpeChjKC00LC0zLDAsMiwxLDAsMCwtMiwxKSwgIyAgbGluZWFyIGNvbnN0cmFpbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMywgYnlyb3cgPSBUUlVFKSwgI+OAgGJ5cm93IGlzIGVhc3kgZm9yIHJlYWRpbmcNCiAgICAgICAgICAgICAgICAgICAgICAgIyBMaW5lYXI6ICAgIEF4DQogICAgICAgICAgICAgICAgICAgICAgIGRpciA9IHJlcCgiPj0iLCAzKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHJocyA9IGMoLTgsMiwwKSkgKSAjIHJpZ2h0IGhhbmQgc2lkZSwgdmVjdG9yIGINClFQDQpgYGANCg0KIyMjIyBTb2x2ZQ0KDQpgYGB7cn0NCnNvbCA8LSBST0lfc29sdmUoUVAsIHNvbHZlciA9ICJxdWFkcHJvZyIpICMgY2hvb3NlIHF1YWRyYXRpYyBzb2x2ZXINCnNvbA0KYGBgDQoNCg0KIyMjIyBFeHRyYWN0IHNvbHV0aW9ucw0KYGBge3J9DQpzb2wkc29sdXRpb24NCnNvbCRvYmp2YWwNCnNvbCRzdGF0dXMNCnNvbCRtZXNzYWdlICNub3QgbXVjaCBpbmZvcm1hdGlvbg0KYGBgDQoNCg0KIyMjIyBTb2x1dGlvbnMNCg0KYGBge3J9DQpzb2x1dGlvbihzb2wsIHR5cGUgPSBjKCJwcmltYWwiKSkNCnNvbHV0aW9uKHNvbCwgdHlwZSA9IGMoImR1YWwiKSkgIyBxdWFkcmF0aWMgc29sdmVyIGlzIG5vdCBnb29kIGF0IGl0Lg0KYGBgDQoNCg0KDQojIyBQb3J0Zm9saW8gb3B0aW1pemF0aW9uIC0gbWluaW11bSB2YXJpYW5jZQ0KDQpUbyBtaW5pbWl6ZSB0aGUgcmlzaywgd2UgY2hvb3NlIGludmVzdCBpbiAzMCBzdG9ja3MgYW5kIHdlIG5lZWQgdG8gZGVjaWRlIHRoZSBzaGFyZSBmb3IgZWFjaCBzdG9jay4NCkRlY2lzaW9uIHZhcmlhYmxlczogeDEsIHgyLC4uLiwgeDMwLiBzdW0oeCwgaSkgPSAxLg0KSWYgeGkgaXMgYWxsb3dlZCB0byBuZWdhdGl2ZSwgaXQgbWVhbnMgd2UgY2FuIGJvcnJvdyBzdG9jayBhbmQgc2VsbCBpdC4NCg0KDQpgYGB7cn0NCiMjIFBvcnRmb2xpbyBvcHRpbWl6YXRpb24gLSBtaW5pbXVtIHZhcmlhbmNlDQojIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgZ2V0IG1vbnRobHkgcmV0dXJucyBvZiAzMCBVUyBzdG9ja3MNCg0KIyMgICANCmRhdGEoIFVTMzAgKSAgIyAxODAgbW9udGhzIGZvciAzMCBzdG9ja3MNCnIgPC0gbmEub21pdCggVVMzMCApDQojIyBvYmplY3RpdmUgZnVuY3Rpb24gdG8gbWluaW1pemUNCm9iaiA8LSBRX29iamVjdGl2ZSggMipjb3YocikgKSAjIHF1YWRyYXRpYyBvYmplY3RpdmU6IHgnTXggLiBNIGlzIHZhcmlhbmNlIGNvbnZhcmlhbmNlIG1hdHJpeDogY292KHIpIC4gIA0KIyMgZnVsbCBpbnZlc3RtZW50IGNvbnN0cmFpbnQNCmZ1bGxfaW52ZXN0IDwtIExfY29uc3RyYWludCggcmVwKDEsIG5jb2woVVMzMCkpLCAiPT0iLCAxICkgIyAgbGluZWFyIGNvbnN0cmFpbnQgZm9yIHN0b2NrIHNoYXJlcw0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICgxLDEsIC4uLiwgMSkgKiAoeDEseDIsLi4uLCB4MzApID09MQ0KIw0KIyMgY3JlYXRlIG9wdGltaXphdGlvbiBwcm9ibGVtIC8gbG9uZy1vbmx5DQojIyANCihvcCA8LSBPUCggb2JqZWN0aXZlID0gb2JqLCBjb25zdHJhaW50cyA9IGZ1bGxfaW52ZXN0ICkpIA0KYGBgDQoNCiMjIyMgU29sdXRpb24NCg0KDQpgYGB7cn0NCg0KIyMgc29sdmUgdGhlIHByb2JsZW0gLSBvbmx5IHdvcmtzIGlmIGEgUVAgc29sdmVyIGlzIHJlZ2lzdGVyZWQNCiMjICANCnJlcyA8LSBST0lfc29sdmUoIG9wICkNCnJlcw0Kc29sIDwtIHNvbHV0aW9uKCByZXMgKQ0KbmFtZXMoIHNvbCApIDwtIGNvbG5hbWVzKCBVUzMwICkNCnJvdW5kKCBzb2xbIHdoaWNoKHNvbCA+IDEvMTBeNikgXSwgMyApICMgb25seSBzaG93IGxhcmdlIHNoYXJlIHN0b2NrLiBOb3QgYWxsIDMwIHhpLg0KYGBgDQoNCg0KDQoNCiMjIyBMUCB3aXRoIGJvdW5kYXJ5DQoNCg0KDQp0aHJlZSBkZWNpc2lvbiB2YXJpYWJsZXM6IHgxLCB4Mg0KDQokJFxiZWdpbnthbGlnbip9IA0KICAgIFx0ZXh0e21pbmltaXplOiB9ICAgeF8xICsgMiogeF8yXFwNClx0ZXh0e3N1YmplY3QgdG86IH0gICAgICB4XzEgKyAgeF8yICAgICAgICYgPSAyIFxcDQogICAgICAgICAgICAgICAgICAgICAgICAgIHhfMSAgICAgICYgPD0gMyBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4XzIgICYgPD0gMw0KDQpcZW5ke2FsaWduKn0kJA0KDQoNCg0KDQoNCmBgYHtyfQ0KDQpscF9vYmogPC0gTF9vYmplY3RpdmUoYygxLCAyKSkgIyAgbGluZWFyIG9iamVjdGl2ZQ0KIGxwX2NvbiA8LSBMX2NvbnN0cmFpbnQoYygxLCAxKSwgZGlyPSI9PSIsIHJocz0yKSAgIyAgbGluZWFyIGNvbnN0cmFpbnQNCiBscF9ib3VuZCA8LSBWX2JvdW5kKHVpPTE6MiwgdWI9YygzLCAzKSkNCiAjdWkgYW4gaW50ZWdlciB2ZWN0b3Igc3BlY2lmeWluZyB0aGUgaW5kaWNlcyBvZiBub24tc3RhbmRhcmQgKGkuZS4sIHZhbHVlcyAhPSBJbmYpIHVwcGVyIGJvdW5kcy4NCiAjIHgxIGFuZCB4MiBib3RoIGhhdmUgdXAgYm91bmQNCiAjIHViIHVwIGJvdW5kDQogbHAgPC0gT1Aob2JqZWN0aXZlPWxwX29iaiwgDQogICAgICAgICAgY29uc3RyYWludHM9bHBfY29uLCANCiAgICAgICAgICBib3VuZHM9bHBfYm91bmQsIA0KICAgICAgICAgIG1heGltdW09RkFMU0UpDQogYm91bmRzKGxwKQ0KbHANCmBgYA0KDQogDQojIyMjIFNvbHV0aW9ucyANCiANCmBgYHtyfQ0KIHggPC0gUk9JX3NvbHZlKGxwKQ0KIHgkb2JqdmFsDQogeCRzb2x1dGlvbg0KYGBgDQoNCiMjIyMgIENoYW5nZSBib3VuZGFyeSANCg0KJCRcYmVnaW57YWxpZ24qfSANCiAgICBcdGV4dHttaW5pbWl6ZTogfSAgIHhfMSArIDIqIHhfMlxcDQpcdGV4dHtzdWJqZWN0IHRvOiB9ICAgICAgeF8xICsgIHhfMiAgICAgICAmID0gMiBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4XzEgICAgICAmIDw9IDEgXFwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF8yICAmIDw9IDENCg0KXGVuZHthbGlnbip9JCQNCg0KYGBge3J9DQojIGNoYW5nZSBib3VuZGFyeSANCiBib3VuZHMobHApIDwtIFZfYm91bmQodWk9MToyLCB1Yj1jKDEsIDEpKQ0KDQpgYGANCg0KDQojIyMjIFNvbHV0aW9ucyANCg0KYGBge3J9DQogeSA8LSBST0lfc29sdmUobHApDQogeSRvYmp2YWwNCiB5JHNvbHV0aW9uDQpgYGANCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBPdGhlciBvcHRpbWl6YXRpb24gcGFja2FnZSBpbiBSDQoNCg0KV2UgYWxzbyBjYW4gc29sdmUgb3B0aW1pemF0aW9uIHByb2JsZW0gdXNpbmcgb3RoZXIgcGFja2FnZXMuDQoNCiMjIyBJbnN0YWxsIHBhY2thZ2VzDQoNClVuY29tbWVudCB0aGUgY29kZSBpbiBmb2xsb3cgY2h1bmsgYW5kIGluc3RhbGwgYWxsIHBhY2thZ2VzLg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoImxwU29sdmUiLCAibHBTb2x2ZUFQSSIpKQ0KI2luc3RhbGwucGFja2FnZXMoYygicXVhZHByb2ciLCAibmxtZSIsIlJnbHBrIikpDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkobHBTb2x2ZSkpDQoNCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShscFNvbHZlQVBJKSkNCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShxdWFkcHJvZykpDQojc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KG5sbWUpKQ0KDQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoUmdscGspKQ0KYGBgDQoNCmFyZ3MoKSBmdW5jdGlvbiB0ZWxscyB1cyBob3cgd2UgY2FuIHVzZSB0aG9zZSBmdW5jdGlvbnMgc3VjaCBhcyBscCwgc29sdmUuUVAsIFJnbHBrX3NvbHZlX0xQDQoNCmBgYHtyfQ0KYXJncyhscCkNCmFyZ3Moc29sdmUuUVApDQphcmdzKFJnbHBrX3NvbHZlX0xQKQ0KI2FyZ3MobmxtaW5iKQ0KI2FyZ3Mob3B0aW0pDQojYXJncyhSY3BsZXgpDQpgYGANCg0KDQojIyMgIExpbmVhciBQcm9ncmFtbWluZyBieSBscHNvbHZlDQoNCkhlcmUgaXMgYSBsaXN0IG9mIHNvbWUga2V5IGZlYXR1cmVzIG9mIGxwX3NvbHZlOg0KDQotIE1peGVkIEludGVnZXIgTGluZWFyIFByb2dyYW1taW5nIChNSUxQKSBzb2x2ZXINCg0KLSBCYXNpY2FsbHkgbm8gbGltaXQgb24gbW9kZWwgc2l6ZQ0KDQotIEl0IGlzIGZyZWUgYW5kIHdpdGggc291cmNlcw0KDQotIFN1cHBvcnRzIEludGVnZXIgdmFyaWFibGVzLCBTZW1pLWNvbnRpbnVvdXMgdmFyaWFibGVzIGFuZCBTcGVjaWFsIE9yZGVyZWQgU2V0cw0KDQoNCiMjIyMgRXhhbXBsZSBieSBscFNvbHZlQVBJDQoNCnN0cmFpZ2h0Zm9yd2FyZA0KDQpJZiB3ZSBoYXZlIDQgZGVjaXNpb24gdmFyaWFibGVzOiB4MSwuLi4sIHg0DQoNCiQkXGJlZ2lue2FsaWduKn0gDQogICAgXHRleHR7bWluaW1pemU6IH0gICB4XzEgKyAzIHhfMiArIDYuMjQgeF8zICsgMC4xIHhfNFxcDQogICAgXHRleHR7c3ViamVjdCB0bzogfSAgICAgNzguMjYgeF8yICsgMi45IHhfNCAgICAgICAmID49IDkyLjMgXFwNCiAgICAgICAgICAgICAgICAgICAgICAgICAwLjIgeF8xICsgIDExLjkxIHhfMyAgICAgICAmIDw9IDE0LjggXFwNCiAgICAgICAgICAgICAgICAgICAgICAgICAxMi42OCB4XzEgKyAwLjA4IHhfMyArIDAuOSB4XzQgJj49IDQgXFwNCiAgICAgICAgICAgICAgICAgICAgICAgIDE4ICA8PSAgeF80ICYgPD0gNDguOTggXFwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF8xICYgPj0gIDI4LjYgDQpcZW5ke2FsaWduKn0kJA0KDQoNCiMjIyMjIFNldCB1cCBtb2RlbA0KDQpgYGB7cn0NCmxpYnJhcnkobHBTb2x2ZUFQSSkNCiNjcmVhdGUgYW4gZW1wdHkgbW9kZWwNCmxwbW9kZWwgPC0gbWFrZS5scChucm93ID0wLCBuY29sPSA0KSAjIDQgdmFyaWFibGVzLCB3ZSBhcmUgZ29pbmcgdG8gYWRkIG9iamVjdGl2ZSBhbmQgY29uc3RyYWludHMNCiMgbnJvdwk6ICMgYSBub25uZWdhdGl2ZSBpbnRlZ2VyIHZhbHVlIHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiBjb25zdGFpbnRzIGluIHRoZSBsaW5lYXIgcHJvZ3JhbS4NCiMgbmNvbAk6ICMgYSBub25uZWdhdGl2ZSBpbnRlZ2VyIHZhbHVlIHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiBkZWNpc2lvbiB2YXJpYWJsZXMgaW4gdGhlIGxpbmVhciBwcm9ncmFtLg0KDQoNCmBgYA0KDQojIyMjIyBTZXR1cCBvYmplY3RpdmUgZnVuY3Rpb24NCg0KYGBge3J9DQojIG9iamVjdDogY3ggDQpzZXQub2JqZm4obHBtb2RlbCwgYygxLCAzLCA2LjI0LCAwLjEpKSAgI+OAgG9iamVjdCBmdW5jdGlvbiwgdmVjdG9yIGMgDQpgYGANCg0KIyMjIyMgQWRkIGNvbnN0cmFpbnRzDQoNCmBgYHtyfQ0KDQojY29uc3RyYWludDogQXgNCmFkZC5jb25zdHJhaW50KGxwbW9kZWwsIGMoMCwgNzguMjYsIDAsIDIuOSksICI+PSIsIDkyLjMpDQphZGQuY29uc3RyYWludChscG1vZGVsLCBjKDAuMjQsIDAsIDExLjMxLCAwKSwgIjw9IiwgMTQuOCkNCmFkZC5jb25zdHJhaW50KGxwbW9kZWwsIGMoMTIuNjgsIDAsIDAuMDgsIDAuOSksICI+PSIsIDQpDQoNCmBgYA0KDQoNCiMjIyMjIFNldCBib3VuZHMNCg0KYGBge3J9DQpzZXQuYm91bmRzKGxwbW9kZWwsIGxvd2VyID0gYygyOC42LCAxOCksIGNvbHVtbnMgPSBjKDEsIDQpKSMgb25seSB4MSwgeDQgaGF2ZSBsb3dlciBib3VuZHMNCnNldC5ib3VuZHMobHBtb2RlbCwgdXBwZXIgPSA0OC45OCwgY29sdW1ucyA9IDQpICMgb25seSB4NCBoYXMgdXBwZXIgYm91bmQNCg0KYGBgDQoNCg0KIyMjIyMgU2V0dXAgbmFtZXMgDQoNClNob3cgbW9kZWwgc2V0dGluZw0KDQpgYGB7cn0NCmxwbW9kZWwNCmBgYA0KDQpOb3QgbmVjZXNzYXJ5DQoNCmBgYHtyfQ0KUm93TmFtZXMgPC0gYygiVEhJU1JPVyIsICJUSEFUUk9XIiwgIkxBU1RST1ciKQ0KQ29sTmFtZXMgPC0gYygiQ09MT05FIiwgIkNPTFRXTyIsICJDT0xUSFJFRSIsICJDT0xGT1VSIikNCmRpbW5hbWVzKGxwbW9kZWwpIDwtIGxpc3QoUm93TmFtZXMsIENvbE5hbWVzKQ0KbHBtb2RlbA0KYGBgDQoNCg0KDQoNCiMjIyMjIFNvbHZlIHRoZSBtb2RlbA0KDQpgYGB7cn0NCnNvbHZlKGxwbW9kZWwpDQoNCmBgYA0KDQoNCg0KIyMjIyMgRXh0cmFjdCBzb2x1dGlvbnMNCg0KDQpgYGB7cn0NCmdldC5vYmplY3RpdmUobHBtb2RlbCkNCiAgIyBbMV0gMzEuNzgyNzYNCiAgIyANCmdldC52YXJpYWJsZXMobHBtb2RlbCkNCiAgIyBbMV0gMjguNjAwMDAgIDAuMDAwMDAgIDAuMDAwMDAgMzEuODI3NTkNCiAgIyANCmdldC5jb25zdHJhaW50cyhscG1vZGVsKSAgI+OAgHNoYXdkb3cgcHJpY2UNCiAgIyBbMV0gIDkyLjMwMDAgICA2Ljg2NDAgMzkxLjI5MjgNCmBgYA0KDQoNCg0KIyMjIyBFeGFtcGxlIGJ5IGxwU29sdmUNCg0KcHJvdmlkZSBhIGxvdCBvZiBpbmZvcm1hdGlvbiBpbmNsdWRpbmcgc2Vuc2l0aXZpdGllcyBhbmFseXNpcy4gDQoNCiQkXGJlZ2lue2FsaWduKn0gDQogICAgXHRleHR7bWF4aW1pemU6IH0gICB4XzEgKyA5IHhfMiArICB4XzMgXFwNCiAgICBcdGV4dHtzdWJqZWN0IHRvOiB9ICAgICB4XzEgKyAyIHhfMiArIDMgeF8zICAgICAgICYgPD0gOSBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgIDMgeF8xICsgMiB4XzIgICsgMiB4XzMgICAgICAgJiA8PSAxNSAgDQpcZW5ke2FsaWduKn0kJA0KDQoNCiMjIyMjIFNldHVwIG9iamVjdGl2ZSBmdW5jdGlvbg0KDQpgYGB7cn0NCmxpYnJhcnkobHBTb2x2ZSkNCiMNCiMgU2V0IHVwIHByb2JsZW06IG1heGltaXplDQojICAgeDEgKyA5IHgyICsgICB4MyBzdWJqZWN0IHRvDQojICAgeDEgKyAyIHgyICsgMyB4MyAgPD0gOQ0KIyAzIHgxICsgMiB4MiArIDIgeDMgPD0gMTUNCiMgIG9iamVjdGl2ZTogY3ggDQptb2RlbF9vYmogPC0gYygxLCA5LCAxKSAgIyBjIHZlY3Rvcg0KYGBgIA0KDQojIyMjIyBTZXR1cCBjb25zdHJhaW50cw0KDQpgYGB7cn0NCm1vZGVsX2NvbiA8LSBtYXRyaXggKGMoMSwgMiwgMywgMywgMiwgMiksIG5yb3c9MiwgYnlyb3c9VFJVRSkgIyBjb25zdHJhaW50czogQXggPD0gYiwgMiBjb250cmFpbnRzDQptb2RlbF9kaXIgPC0gYygiPD0iLCAiPD0iKQ0KbW9kZWxfcmhzIDwtIGMoOSwgMTUpICMgYiB2ZWN0b3INCg0KYGBgDQoNCiMjIyMjIFNvbHZlIHRoZSBtb2RlbA0KDQpgYGB7cn0NCiMNCmxwICgibWF4IiwgbW9kZWxfb2JqLCBtb2RlbF9jb24sIG1vZGVsX2RpciwgbW9kZWxfcmhzKQ0KIyMgIFN1Y2Nlc3M6IHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gaXMgNDAuNQ0KDQpgYGANCg0KDQojIyMjIyBFeHRyYWN0IHNvbHV0aW9ucw0KDQpgYGB7cn0NCg0KbHAgKCJtYXgiLCBtb2RlbF9vYmosIG1vZGVsX2NvbiwgbW9kZWxfZGlyLCBtb2RlbF9yaHMpJHNvbHV0aW9uDQpgYGANCg0KIyMjIyMjICBHZXQgc2Vuc2l0aXZpdGllcw0KDQpgYGB7cn0NCiMgR2V0IHNlbnNpdGl2aXRpZXMNCiMjIA0KIyBSaWdodCBub3cgdGhlIGR1YWwgdmFsdWVzIGZvciB0aGUgY29uc3RyYWludHMgYW5kIHRoZSB2YXJpYWJsZXMgYXJlDQojIGNvbWJpbmVkLCBjb25zdHJhaW50cyBjb21pbmcgZmlyc3QuIFNvIGluIHRoaXMgZXhhbXBsZS4uLg0KIw0KbHAgKCJtYXgiLCBtb2RlbF9vYmosIG1vZGVsX2NvbiwgbW9kZWxfZGlyLCBtb2RlbF9yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKSRkdWFscyAgICAgDQojIyAgWzFdICAgNC41ICAgMC4wICAtMy41ICAgMC4wIC0xMC41DQojDQojIC4uLnRoZSBkdWFscyBvZiB0aGUgY29uc3RyYWludHMgYXJlIDQuNSBhbmQgMCwgDQojIGFuZCBvZiB0aGUgdmFyaWFibGVzLCAtMy41LCAwLjAsIC0xMC41LiAjDQpgYGANCg0KIyMjIyBJbnRlcmdyIFByb2dyYW1taW5nDQoNClByZXZpb3VzIG1vZGVsIHdpdGggY29uc3RyYWludHMgdGhhdCBhbGwgZGVjaXNpb24gdmFyaWFibGVzIGFyZSBpbnRlZ2VyLg0KDQpgYGB7cn0NCiMgUnVuIGFnYWluLCB0aGlzIHRpbWUgcmVxdWlyaW5nIHRoYXQgYWxsIHRocmVlIHZhcmlhYmxlcyBiZSBpbnRlZ2VyDQojDQpscCAoIm1heCIsIG1vZGVsX29iaiwgbW9kZWxfY29uLCBtb2RlbF9kaXIsIG1vZGVsX3JocywgaW50LnZlYz0xOjMpDQojIyAgU3VjY2VzczogdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBpcyAzNw0KbHAgKCJtYXgiLCBtb2RlbF9vYmosIG1vZGVsX2NvbiwgbW9kZWxfZGlyLCBtb2RlbF9yaHMsIGludC52ZWM9MTozKSRzb2x1dGlvbg0KIyMgIFsxXSAxIDQgMA0KIw0KIyBZb3UgY2FuIGdldCBzZW5zaXRpdml0aWVzIGluIHRoZSBpbnRlZ2VyIGNhc2UsIGJ1dCB0aGV5J3JlIGhhcmRlciB0bw0KIyBpbnRlcnByZXQuDQojDQpscCAoIm1heCIsIG1vZGVsX29iaiwgbW9kZWxfY29uLCBtb2RlbF9kaXIsIG1vZGVsX3JocywgaW50LnZlYz0xOjMsIGNvbXB1dGUuc2Vucz1UUlVFKSRkdWFscw0KIyMgIFsxXSAxIDAgMCA3IDANCiMNCmBgYA0KDQoNCg0KDQojIyMgUXVhZHJhdGljIFByb2dyYW1taW5nIGJ5IHF1YWRwcm9nDQoNCg0Kc29sdmluZyBxdWFkcmF0aWMgcHJvZ3JhbW1pbmcgcHJvYmxlbXMgb2YgdGhlIGZvcm0gDQoNCm1pbihjJyB4ICsgMS8yIHgnIEQgeCkgDQoNCndpdGggdGhlIGNvbnN0cmFpbnRzIEEnIHggPj0gYl8wLg0KDQoNCiQkXGJlZ2lue2FsaWduKn0gDQogICAgXHRleHR7bWluaW1pemU6IH0gICAtIDUgeF8yICsgMS8yICh4XzFeMiArIHhfMl4yICsgeF8zXjIpXFwNClx0ZXh0e3N1YmplY3QgdG86IH0gICAgIC00IHhfMSAtIDMgeF8yICAgICAgICYgPj0gLTggXFwNCiAgICAgICAgICAgICAgICAgICAgICAgICAyIHhfMSArICAgeF8yICAgICAgICYgPj0gMiBcXA0KICAgICAgICAgICAgICAgICAgICAgICAgIC0gMiB4XzIgKyB4XzMgJiA+PSAwDQoNClxlbmR7YWxpZ24qfSQkDQoNCg0KDQpEIGlzIGRpYWduYWwgbWF0aXggLiAxLzIgaXMgY29udmV0aW9uLiANCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShxdWFkcHJvZykNCg0KIyMgQXNzdW1lIHdlIHdhbnQgdG8gbWluaW1pemU6IC0oMCA1IDApICUqJSB4ICsgMS8yIHheVCBEIHgNCiMjIHVuZGVyIHRoZSBjb25zdHJhaW50czogICAgICBBXlQgeCA+PSBiMA0KIyMgICAgICAgICAgMSAwIDANCiMjICAgICAgRCA9IDAgMSAwDQojIyAgICAgICAgICAwIDAgMQ0KIyMgd2l0aCBiMCA9ICgtOCwyLDApXlQNCiMjIGFuZCAgICAgICgtNCAgMiAgMCkgDQojIyAgICAgIEEgPSAoLTMgIDEgLTIpDQojIyAgICAgICAgICAoIDAgIDAgIDEpDQojIyB3ZSBjYW4gdXNlIHNvbHZlLlFQIGFzIGZvbGxvd3M6DQojIw0KRG1hdCAgICAgICA8LSBtYXRyaXgoMCwzLDMpDQpkaWFnKERtYXQpIDwtIDENCmN2ZWMgICAgICAgPC0gYygwLDUsMCkNCkFtYXQgICAgICAgPC0gbWF0cml4KGMoLTQsLTMsMCwyLDEsMCwwLC0yLDEpLDMsMykNCmJ2ZWMgICAgICAgPC0gYygtOCwyLDApDQpzb2x2ZS5RUChEbWF0LGN2ZWMsQW1hdCxidmVjPWJ2ZWMpDQoNCmBgYA0KDQoNCg0KDQoNCiMjIyBGb290YmFsbCBNSVAgYnkgZ2xwaw0KDQoNCg0KVHJ5aW5nIHRvIHBpY2sgdGhlIGJlc3QgcG9zc2libGUgZmFudGFzeSBmb290YmFsbCB0ZWFtIGdpdmVuIGRpZmZlcmVudCBjb25zdHJhaW50cy4gIA0KDQpHb2FsIGlzIHRvIHBpY2sgdGhlIHBsYXllcnMgdGhhdCBtYXhpbWl6ZSB0aGUgc3VtIG9mIHRoZWlyIHByb2plY3RlZCBwb2ludHMuDQoNCkRlY2lzaW9uIHZhcmlhYmxzIGFyZSBiaW5hcnksIDAgb3IgMSBmb3Igb25lIHBsYXllciB0byBwbGF5IGluIHRoaXMgbWF0Y2guDQoNClRoZSBjb25zdHJhaW50cyBhcmU6DQoNCjEpIFRoZSB0ZWFtIG11c3QgaW5jbHVkZToNCg0KICAgIC0gMSBRQg0KICANCiAgICAtIDIgUkJzDQoNCiAgICAtIDIgV1JzDQoNCiAgICAtIDEgVEUNCg0KMikgQSBwbGF5ZXIncyByaXNrIG11c3Qgbm90IGV4Y2VlZCA2DQoNCjMpIFRoZSBzdW0gb2YgdGhlIHBsYXllcnMnIGNvc3RzIG11c3Qgbm90IGV4Y2VlZCAzMDAuIA0KDQoNCg0KbWF4aW1pemUgY3ggIHBvaW50cw0KDQpzdWJqZWN0IEF4IDw9IGINCg0KDQpgYGB7cn0NCiNodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE1MTQ3Mzk4L29wdGltaXplLXZhbHVlLXdpdGgtbGluZWFyLW9yLW5vbi1saW5lYXItY29uc3RyYWludHMtaW4tcg0KIyBXZSBhcmUgZ29pbmcgdG8gc29sdmU6DQojIG1heGltaXplIGYneCBzdWJqZWN0IHRvIEEqeCA8ZGlyPiBiDQojIHdoZXJlOg0KIyAgIHggaXMgdGhlIHZhcmlhYmxlIHRvIHNvbHZlIGZvcjogYSB2ZWN0b3Igb2YgMCBvciAxOg0KIyAgICAgMSB3aGVuIHRoZSBwbGF5ZXIgaXMgc2VsZWN0ZWQsIDAgb3RoZXJ3aXNlLA0KIyAgIGYgaXMgeW91ciBvYmplY3RpdmUgdmVjdG9yLA0KIyAgIEEgaXMgYSBtYXRyaXgsIGIgYSB2ZWN0b3IsIGFuZCA8ZGlyPiBhIHZlY3RvciBvZiAiPD0iLCAiPT0iLCBvciAiPj0iLA0KIyAgIGRlZmluaW5nIHlvdXIgbGluZWFyIGNvbnN0cmFpbnRzLg0KDQoj44CAcGxheWVyJ3MgbmFtZXMNCm5hbWUgPC0gYygiQWFyb24gUm9kZ2VycyIsIlRvbSBCcmFkeSIsIkFyaWFuIEZvc3RlciIsIlJheSBSaWNlIiwiTGVTZWFuIE1jQ295IiwiQ2FsdmluIEpvaG5zb24iLCJMYXJyeSBGaXR6Z2VyYWxkIiwiV2VzIFdlbGtlciIsIlJvYiBHcm9ua293c2tpIiwiSmltbXkgR3JhaGFtIikNCiMgcGxheWVycycgcG9zaXRpb24gaW4gdGhlIGdhbWUNCnBvcyA8LSBjKCJRQiIsIlFCIiwiUkIiLCJSQiIsIlJCIiwiV1IiLCJXUiIsIldSIiwiVEUiLCJURSIpDQojIHBsYXllcnMnIHBvaW50cw0KcHRzIDwtIGMoMTY3LCAxMzYsIDE5NSwgMTc0LCAxNDQsIDEzNSwgODksIDgxLCAxMTQsIDExMSkgDQojIHBsYXllcnMnIHJpc2sNCnJpc2sgPC0gYygyLjksIDMuNCwgMC43LCAxLjEsIDMuNSwgNS4wLCA2LjcsIDQuNywgMy43LCA4LjgpIA0KIyBwbGF5ZXJzJyBjb3N0DQpjb3N0IDwtIGMoNjAsIDQ3LCA2MywgNjIsIDQwLCA2MCwgNTAsIDM1LCA0MCwgNDApIA0KI215ZGF0YSA8LSBkYXRhLmZyYW1lKG5hbWUsIHBvcywgcHRzLCByaXNrLCBjb3N0KSANCg0KIyBudW1iZXIgb2YgdmFyaWFibGVzDQpudW0ucGxheWVycyA8LSBsZW5ndGgobmFtZSkNCiMgb2JqZWN0aXZlOg0KZiA8LSBwdHMNCiMgdGhlIHZhcmlhYmxlIGFyZSBib29sZWFucw0KdmFyLnR5cGVzIDwtIHJlcCgiQiIsIG51bS5wbGF5ZXJzKQ0KIyB0aGUgY29uc3RyYWludHMNCkEgPC0gcmJpbmQoYXMubnVtZXJpYyhwb3MgPT0gIlFCIiksICMgbnVtIFFCICAjIFRSVUUgIFRSVUUgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgdG8gMSAxIC4uLiAwIDAgDQogICAgICAgICAgIGFzLm51bWVyaWMocG9zID09ICJSQiIpLCAjIG51bSBSQiAgIyBGQUxTRSBGQUxTRSAgVFJVRSAgVFJVRSAgVFJVRSBGQUxTRSBGQUxTRSBGQUxTRSBGQUxTRSBGQUxTRSB0byAwIDEgLi4uIDAgMA0KICAgICAgICAgICBhcy5udW1lcmljKHBvcyA9PSAiV1IiKSwgIyBudW0gV1IgICMgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgIFRSVUUgIFRSVUUgIFRSVUUgRkFMU0UgRkFMU0UgdG8gMCAwIC4uLiAwIDANCiAgICAgICAgICAgYXMubnVtZXJpYyhwb3MgPT0gIlRFIiksICMgbnVtIFRFICAjIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFICBUUlVFICBUUlVFIHRvIDAgMCAuLi4gMSAxDQogICAgICAgICAgIGRpYWcocmlzayksICAgICAgICAgICAgICAjIHBsYXllcidzIHJpc2ssIGEgc2V0IG9mIG11bHRpcGxlIGNvbnN0cmFpbnRzDQogICAgICAgICAgIGNvc3QpICAgICAgICAgICAgICAgICAgICAjIHRvdGFsIGNvc3QNCg0KIyBkaXJldGlvbg0KZGlyIDwtIGMoIj09IiwNCiAgICAgICAgICI9PSIsDQogICAgICAgICAiPT0iLA0KICAgICAgICAgIj09IiwNCiAgICAgICAgIHJlcCgiPD0iLCBudW0ucGxheWVycyksDQogICAgICAgICAiPD0iKQ0KDQojIHJocyBiIHZlY3Rvcg0KYiA8LSBjKDEsDQogICAgICAgMiwNCiAgICAgICAyLA0KICAgICAgIDEsDQogICAgICAgcmVwKDYsIG51bS5wbGF5ZXJzKSwNCiAgICAgICAzMDApDQoNCg0KDQpgYGANCg0KDQoNCiMjIyMgU29sdmUgdGhlIG1vZGVsIHdpdGggZ2xwaw0KDQpgYGB7cn0NCiMgc29sdmUNCmxpYnJhcnkoUmdscGspDQpzb2wgPC0gUmdscGtfc29sdmVfTFAob2JqID0gZiwgDQogICAgICAgICAgICAgICAgICAgICAgbWF0ID0gQSwgDQogICAgICAgICAgICAgICAgICAgICAgZGlyID0gZGlyLCANCiAgICAgICAgICAgICAgICAgICAgICByaHMgPSBiLA0KICAgICAgICAgICAgICAgICAgICAgIHR5cGVzID0gdmFyLnR5cGVzLCANCiAgICAgICAgICAgICAgICAgICAgICBtYXggPSBUUlVFKQ0Kc29sDQojICRvcHRpbXVtDQojIFsxXSA4MzYgICAgICAgICAgICAgICAgICAgICAgIyMjIDwtIHRoZSBvcHRpbWFsIHRvdGFsIHBvaW50cw0KIyAkc29sdXRpb24NCiMgIFsxXSAxIDAgMSAwIDEgMSAwIDEgMSAwICAgICAjIyMgPC0gYSBgMWAgZm9yIHRoZSBzZWxlY3RlZCBwbGF5ZXJzDQojICRzdGF0dXMNCiMgWzFdIDAgICAgICAgICAgICAgICAgICAgICAgICAjIyMgPC0gYW4gb3B0aW1hbCBzb2x1dGlvbiBoYXMgYmVlbiBmb3VuZA0KIyB5b3VyIGRyZWFtIHRlYW0NCm5hbWVbc29sJHNvbHV0aW9uID09IDFdDQojIFsxXSAiQWFyb24gUm9kZ2VycyIgICJBcmlhbiBGb3N0ZXIiICAgIkxlU2VhbiBNY0NveSINCiMgWzRdICJDYWx2aW4gSm9obnNvbiIgIldlcyBXZWxrZXIiICAgICAiUm9iIEdyb25rb3dza2kNCiMgdG90YWwgY29zdA0Kc3VtKGNvc3QgKiBzb2wkc29sdXRpb24pDQpgYGANCg0KIyMgU29sdmUgaXQgdXNpbmcgUk9JIA0KDQoNCmBgYHtyfQ0KZm9vdGJhbGwgPC0gT1AoIGYsICMgb2JqZWN0aXZlIGZ1bmN0aW9uLCB2ZWN0b3IgYw0KICAgICAgICAgIExfY29uc3RyYWludChMID0gQSwgIyBsaW5lYXIgY29uc3RyYWludDogTWF4dHJpeCBBDQogICAgICAgICAgZGlyID0gZGlyLCAgIyBkaXJlY3Rpb24NCiAgICAgICAgICByaHMgPSBiKSwgIyByaWdodCBoYW5kIHNpZGU6IHZlY3RvciBiIA0KICAgICAgICAgIHR5cGVzID0gdmFyLnR5cGVzLCAgDQogICAgICAgICAgbWF4ID0gVFJVRSkNCmZvb3RiYWxsDQpgYGANCg0KIyMjIyBTb2x2ZQ0KDQpgYGB7cn0NCnNvbCA8LSBST0lfc29sdmUoZm9vdGJhbGwpIyAsIHNvbHZlciA9ICJnbHBrIikgICMgU29sdmUgbW9kZWwgTFAgd2l0aCBzcGVjaWZpYyBzb2x2ZXIgZ2xwayBvciBhdXRvbWF0aWNhbGx5IGNob29zZSBieSBST0kuDQpzb2wNCmBgYA0KDQojIyMjIFNvbHV0aW9ucw0KDQpgYGB7cn0NCnNvbHV0aW9uKHNvbCwgdHlwZSA9IGMoInByaW1hbCIpKSAjIHNvbHV0aW9uIGZvciBwcmltYWwgcHJvYmxlbQ0KYGBgDQoNCg0KIyMjIyBFeHRyYWN0IHNvbHV0aW9ucw0KYGBge3J9DQpzb2wkc29sdXRpb24NCnNvbCRvYmp2YWwNCnNvbCRzdGF0dXMNCnNvbCRtZXNzYWdlICMgZGV0YWlsZWQgbWVzc2FnZSBmb3Igc29sdXRpb24gDQpgYGANCg==