

Email          : brigita.melantika@student.matanauniversity.ac.id
RPubs         : https://rpubs.com/brigitatiaraem/
Jurusan      : Statistika
Address     : ARA Center, Matana University Tower
             Jl. CBD Barat Kav, RT.1, Curug Sangereng, Kelapa Dua, Tangerang, Banten 15810.
Buatlah contoh kasus Optimisasi Program Linear dalam Industri atau bisnis!
We’re consulting for a boutique car manufacturer, producing luxury cars. They run on one month (30) days) cycles, we have one cycle to show we can provide value. There is one robot, 2 engineers and one detailer in the factory. The detailer has some holiday off, so only has 21 days available.
The 2 cars need different time with each resource:
• Robot time: Car A-3 days, Car B-4 days.
Engineer time: Car A-5 days; Car B-6 days.
• Detailer time: Car A-1.5 days, Car B-3 days.
Car A provides €30, 000 profit, whilst Car B offers €45,000 profit
At the moment, they produce 4 of each cars per month, for €300,000 profit Not bad at all, but we think we can do better for them.
This can be modelled as follows:
Find the maximum solution
\[max \space Z=30000a+45000b\]
Suppose that the objective function is subject to the following constraints: \[\begin{align*}
a=0 \\
b=2 \\
3a+4b=30 \\
5a+6b=60 \\
1.5a+3b=21 \\
\end{align*}\]
library(lpSolve)
max_z <- c(30000,45000)
LHS <- matrix(c(3,4,
5,6,
1.5,3)
,ncol=2, byrow=TRUE)
arah_kendala <- c("<=","<=","<=")
RHS <- c(30,60,21)
# Penyelesaian
model <- lp ("max",
max_z,
LHS,
arah_kendala,
RHS,
compute.sens = TRUE)
print(model)
## Success: the objective function is 330000
## [1] 2 6
Buatlah contoh kasus Optimisasi Program NonLinear dalam Industri atau bisnis!
f <- function(x, y) {
x^2 + y^2 + 3* x * y
}
n <- 30
xpts <- seq(-1.5, 1.5, len = n)
ypts <- seq(-1.5, 1.5, len = n)
gr <- expand.grid(x = xpts, y = ypts)
feval <- with(gr, matrix(f(x, y), nrow = n, ncol = n))
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, feval, nlevels = 20, xlab = "x", ylab = "y")
points(-1, -1, pch = 19, cex = 2)
abline(h = -1)

Buatlah contoh kasus Optimisasi Portofolio dengan memilih 5 saham terbaik di Indonesia dengan cara melakukan analisis data keuangan terlebih dahulu pada saham LQ45!
install.packages("installr")
library("installr")
updateR()
library(tidyquant)
library(plotly)
library(timetk)
# saham telkom, kimia farma, Djarum, BCA, dan Sinarmas
tick <- c('TLKM.JK', 'KAEF.JK', 'HMSP.JK', 'BBCA.JK', 'BSIM.JK')
price_data <- tq_get(tick,
from = '2020-01-01',
to = Sys.Date(),
get = 'stock.prices')
price_data
## # A tibble: 3,645 × 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 TLKM.JK 2020-01-02 3970 4000 3900 3910 52094000 3417.
## 2 TLKM.JK 2020-01-03 3960 3980 3930 3980 70032900 3478.
## 3 TLKM.JK 2020-01-06 3930 3970 3930 3960 42908900 3461.
## 4 TLKM.JK 2020-01-07 3930 3970 3920 3940 51837600 3443.
## 5 TLKM.JK 2020-01-08 3920 3950 3900 3900 52402600 3408.
## 6 TLKM.JK 2020-01-09 3920 3970 3920 3960 37580300 3461.
## 7 TLKM.JK 2020-01-10 3980 3990 3950 3980 48099000 3478.
## 8 TLKM.JK 2020-01-13 4000 4030 3990 4030 61913800 3522.
## 9 TLKM.JK 2020-01-14 3980 4000 3940 3950 95058600 3452.
## 10 TLKM.JK 2020-01-15 3960 3960 3870 3880 147583300 3391.
## # … with 3,635 more rows
Return
log_ret_tidy <- price_data %>%
group_by(symbol) %>%
tq_transmute(select = adjusted,
mutate_fun = periodReturn,
period = 'daily',
col_rename = 'ret',
type = 'log')
library(DT)
datatable(log_ret_tidy)
Adapun fungsi yang digunakan untuk mengubah menjadi format lebar yaitu spread() dan mengubah menjadi objek runtun waktu yaitu xts().
library(tidyr)
log_ret_xts = log_ret_tidy %>%
spread(symbol, value = ret) %>%
tk_xts()
datatable(log_ret_xts)
Rata-rata Pengembalian
Langkah ini digunakan untuk menghitung rata-rata pengembalian setiap harian untuk setiap saham yang digunakan.
mean_ret <- colMeans(log_ret_xts)
print ( round (mean_ret, 4))
## BBCA.JK BSIM.JK HMSP.JK KAEF.JK TLKM.JK
## 4e-04 5e-04 -8e-04 -1e-04 1e-04
Matriks Kovariansi
Matriks kovarian digunakan untuk melihat hubungan pada setiap saham untuk periode satu tahun sebanyak 252 hari ( kecuali hari libur dan hari raya).
cov_mat <- cov(log_ret_xts) * 252
print(round(cov_mat,4))
## BBCA.JK BSIM.JK HMSP.JK KAEF.JK TLKM.JK
## BBCA.JK 0.0824 0.0072 0.0385 0.0478 0.0442
## BSIM.JK 0.0072 0.3229 0.0156 0.0047 0.0014
## HMSP.JK 0.0385 0.0156 0.1271 0.0696 0.0443
## KAEF.JK 0.0478 0.0047 0.0696 0.5321 0.0368
## TLKM.JK 0.0442 0.0014 0.0443 0.0368 0.1079
Penerapan Metode Portofolio
Metode portofolio dapat dilakukan untuk memperhitungkan pengembalian atau rerturn dan nilai risiko dari masing-masing portofolio. Berikut merupakan langkah-langkah dalam R.
wts <- runif(n = length(tick))
wts <- wts/sum(wts)
port_returns <- (sum(wts * mean_ret) + 1)^252 - 1
port_risk <- sqrt(t(wts) %*% (cov_mat %*% wts))
sharpe_ratio <- port_returns/port_risk
Untuk melihat tingkat signifikan pada masing-masing portofolio dapat dilakukan simulasi sebanyak 5000 kali.
num_port <- 5000
all_wts <- matrix(nrow = num_port, ncol = length(tick))
port_returns <- vector('numeric', length = num_port)
port_risk <- vector('numeric', length = num_port)
sharpe_ratio <- vector('numeric', length = num_port)
for (i in seq_along(port_returns)) {
wts <- runif(length(tick))
wts <- wts/sum(wts)
all_wts[i,] <- wts
port_ret <- sum(wts * mean_ret)
port_ret <- ((port_ret + 1)^252) - 1
port_returns[i] <- port_ret
port_sd <- sqrt(t(wts) %*% (cov_mat %*% wts))
port_risk[i] <- port_sd
sr <- port_ret/port_sd
sharpe_ratio[i] <- sr
}
Setelah melakukan proses loop 5000 kali, maka langkah selanjutnya membuat tabel data.
portfolio_values <- tibble(Return = port_returns,
Risk = port_risk,
SharpeRatio = sharpe_ratio)
all_wts <- tk_tbl(all_wts)
colnames(all_wts) <- colnames(log_ret_xts)
portfolio_values <- tk_tbl(cbind(all_wts, portfolio_values))
datatable(portfolio_values)
Dari tabel diatas, masing-masing portofolio pada setiap saham memiliki pengembalian, risiko, dan sharpe ratio.
Variasi Minimum
library(forcats)
min_var <- portfolio_values[which.min(portfolio_values$Risk),]
p <- min_var %>%
gather(BBCA.JK:TLKM.JK, key = Asset,
value = Weights) %>%
mutate(Asset = as.factor(Asset)) %>%
ggplot(aes(x = fct_reorder(Asset,Weights), y = Weights, fill = Asset)) +
geom_bar(stat = 'identity') +
theme_minimal() +
labs(x = 'Aset',
y = 'Bobot',
title = "Bobot Portofolio dengan Variansi Minimum") +
scale_y_continuous(labels = scales::percent) +
theme(legend.position = "none" )
ggplotly(p)
Dari output di atas, portofolio variansi minimum pada saham KAEF.JK dan HMSP.JK. Kebanyakan inverstor investasi di BBCA.JK dan TLKM.JK.
Portofolio Tangensi
Sharpe ratio adalah rasio yang menunjukkan besaran return instrumen investasi. Setelah melihat portofolio variansi minimum dapat melakukan portofolio tangensi dengan sharpe ratio tertinggi.
max_sr <- portfolio_values[which.max(portfolio_values$SharpeRatio),]
p <- max_sr %>%
gather(BBCA.JK:TLKM.JK, key = Asset,
value = Weights) %>%
mutate(Asset = as.factor(Asset)) %>%
ggplot(aes(x = fct_reorder(Asset,Weights), y = Weights, fill = Asset)) +
geom_bar(stat = 'identity') +
theme_minimal() +
labs(x = 'Aset',
y = 'Bobot',
title = "Bobot Portofolio Tangensi (Maksimum Sharpe Ratio)") +
scale_y_continuous(labels = scales::percent) +
theme(legend.position = "none" )
ggplotly(p)
Dari ouotput diatas dapat dilihat bahwa
Batas Efisien Portofolio
p <- portfolio_values %>%
ggplot(aes(x = Risk, y = Return, color = SharpeRatio)) +
geom_point() +
theme_classic() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent) +
labs(x = 'Risiko Tahunan',
y = 'Pengembalian Tahunan',
title = "Optimasi Portofolio & Perbatasan yang Efisien") +
geom_point(aes(x = Risk, y = Return), data = min_var, color = 'red') +
geom_point(aes(x = Risk, y = Return), data = max_sr, color = 'red') +
annotate('text', x = 0.31, y = 0.31, label = "Portofolio Tangensi") +
annotate('text', x = 0.24, y = 0.11, label = "Portofolio Varians minimum") +
annotate(geom = 'segment', x = 0.3023, xend = 0.3023, y = 0.23,
yend = 0.29, color = 'red', arrow = arrow(type = "open")) +
annotate(geom = 'segment', x = 0.25, xend = 0.25, y = 0.015,
yend = 0.10, color = 'red', arrow = arrow(type = "open"))
ggplotly(p)
LS0tDQp0aXRsZTogIk9QVElNQVNJIEZJTkFMIEVYQU0iDQpzdWJ0aXRsZTogIldFRUsgMTYiDQphdXRob3I6ICJCcmlnaXRhIFRpYXJhIEVsZ2l0eWFuYSBNZWxhbnRpa2EgKDIwMjA0OTIwMDAxKSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGh0bWxfZG9jdW1lbnQ6IG51bGwNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0aGVtZTogc2FuZHN0b25lDQogICAgY3NzOiBzdHlsZTEuY3NzDQogICAgaGlnaGxpZ2h0OiBtb25vY2hyb21lDQotLS0NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChjbGFzcy5zb3VyY2UgPSAibm9jb3B5IiwNCiAgICAgICAgICAgICAgICAgICAgICBjbGFzcy5vdXRwdXQgPSAibm9jb3B5IiwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRiwNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRikNCmBgYA0KDQo8aW1nIHN0eWxlPSJmbG9hdDogcmlnaHQ7IG1hcmdpbjogMHB4IDEwMHB4IDBweCAwcHg7IHdpZHRoOjI1JSIgc3JjPSJmb3RvYmFydWt1LmpwZWciLz4gDQoNCmBgYHtyIGxvZ28sIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAnMzAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJsb2dvbWF0YW5hLnBuZyIpDQpgYGANCg0KRW1haWwgJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7Jm5ic3A7OiAgYnJpZ2l0YS5tZWxhbnRpa2FAc3R1ZGVudC5tYXRhbmF1bml2ZXJzaXR5LmFjLmlkIDxicj4NClJQdWJzICAmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDs6IGh0dHBzOi8vcnB1YnMuY29tL2JyaWdpdGF0aWFyYWVtLyA8YnI+DQpKdXJ1c2FuICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDs6IFtTdGF0aXN0aWthXShodHRwczovL21hdGFuYXVuaXZlcnNpdHkuYWMuaWQvP2x5PWFjYWRlbWljJmM9c2IpIDxicj4NCkFkZHJlc3MgICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyA6IEFSQSBDZW50ZXIsIE1hdGFuYSBVbml2ZXJzaXR5IFRvd2VyIDxicj4NCiZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7Jm5ic3A7IEpsLiBDQkQgQmFyYXQgS2F2LCBSVC4xLCBDdXJ1ZyBTYW5nZXJlbmcsIEtlbGFwYSBEdWEsIFRhbmdlcmFuZywgQmFudGVuIDE1ODEwLg0KDQoqKioqDQoNCiMgQnVhdGxhaCAgY29udG9oIGthc3VzIE9wdGltaXNhc2kgUHJvZ3JhbSBMaW5lYXIgZGFsYW0gSW5kdXN0cmkgYXRhdSBiaXNuaXMhDQoNCldlJ3JlIGNvbnN1bHRpbmcgZm9yIGEgYm91dGlxdWUgY2FyIG1hbnVmYWN0dXJlciwgcHJvZHVjaW5nIGx1eHVyeSBjYXJzLiBUaGV5IHJ1biBvbiBvbmUgbW9udGggKDMwKSBkYXlzKSBjeWNsZXMsIHdlIGhhdmUgb25lIGN5Y2xlIHRvIHNob3cgd2UgY2FuIHByb3ZpZGUgdmFsdWUuIFRoZXJlIGlzIG9uZSByb2JvdCwgMiBlbmdpbmVlcnMgYW5kIG9uZSBkZXRhaWxlciBpbiB0aGUgZmFjdG9yeS4gVGhlIGRldGFpbGVyIGhhcyBzb21lIGhvbGlkYXkgb2ZmLCBzbyBvbmx5IGhhcyAyMSBkYXlzIGF2YWlsYWJsZS4NCg0KVGhlIDIgY2FycyBuZWVkIGRpZmZlcmVudCB0aW1lIHdpdGggZWFjaCByZXNvdXJjZToNCg0K4oCiIFJvYm90IHRpbWU6IENhciBBLTMgZGF5cywgQ2FyIEItNCBkYXlzLg0KDQpFbmdpbmVlciB0aW1lOiBDYXIgQS01IGRheXM7IENhciBCLTYgZGF5cy4NCg0K4oCiIERldGFpbGVyIHRpbWU6IENhciBBLTEuNSBkYXlzLCBDYXIgQi0zIGRheXMuDQoNCkNhciBBIHByb3ZpZGVzIOKCrDMwLCAwMDAgcHJvZml0LCB3aGlsc3QgQ2FyIEIgb2ZmZXJzIOKCrDQ1LDAwMCBwcm9maXQNCg0KQXQgdGhlIG1vbWVudCwgdGhleSBwcm9kdWNlIDQgb2YgZWFjaCBjYXJzIHBlciBtb250aCwgZm9yIOKCrDMwMCwwMDAgcHJvZml0IE5vdCBiYWQgYXQgYWxsLCBidXQgd2UgdGhpbmsgd2UgY2FuIGRvIGJldHRlciBmb3IgdGhlbS4NCg0KVGhpcyBjYW4gYmUgbW9kZWxsZWQgYXMgZm9sbG93czoNCg0KRmluZCB0aGUgbWF4aW11bSBzb2x1dGlvbiANCg0KJCRtYXggXHNwYWNlIFo9MzAwMDBhKzQ1MDAwYiQkDQoNClN1cHBvc2UgdGhhdCB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGlzIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25zdHJhaW50czoNClxiZWdpbnthbGlnbip9DQphPTAgXFwNCmI9MiBcXCANCjNhKzRiPTMwIFxcDQo1YSs2Yj02MCBcXA0KMS41YSszYj0yMSBcXA0KXGVuZHthbGlnbip9DQoNCmBgYHtyfQ0KbGlicmFyeShscFNvbHZlKQ0KDQptYXhfeiA8LSBjKDMwMDAwLDQ1MDAwKQ0KTEhTIDwtIG1hdHJpeChjKDMsNCwNCiAgICAgICAgICAgICAgICA1LDYsDQogICAgICAgICAgICAgICAgMS41LDMpDQogICAgICAgICAgICAgICxuY29sPTIsIGJ5cm93PVRSVUUpDQphcmFoX2tlbmRhbGEgPC0gYygiPD0iLCI8PSIsIjw9IikNClJIUyA8LSBjKDMwLDYwLDIxKQ0KDQojIFBlbnllbGVzYWlhbg0KbW9kZWwgPC0gbHAgKCJtYXgiLA0KICAgICAgICAgICAgbWF4X3osDQogICAgICAgICAgICBMSFMsDQogICAgICAgICAgICBhcmFoX2tlbmRhbGEsDQogICAgICAgICAgICBSSFMsDQogICAgICAgICAgICBjb21wdXRlLnNlbnMgPSBUUlVFKQ0KDQpwcmludChtb2RlbCkNCm1vZGVsJHNvbHV0aW9uDQpgYGANCiMgQnVhdGxhaCBjb250b2gga2FzdXMgT3B0aW1pc2FzaSBQcm9ncmFtIE5vbkxpbmVhciBkYWxhbSBJbmR1c3RyaSBhdGF1IGJpc25pcyENCg0KYGBge3J9DQpmIDwtIGZ1bmN0aW9uKHgsIHkpIHsNCiAgICAgICAgeF4yICsgeV4yICsgMyogeCAqIHkNCn0NCm4gPC0gMzANCnhwdHMgPC0gc2VxKC0xLjUsIDEuNSwgbGVuID0gbikNCnlwdHMgPC0gc2VxKC0xLjUsIDEuNSwgbGVuID0gbikNCmdyIDwtIGV4cGFuZC5ncmlkKHggPSB4cHRzLCB5ID0geXB0cykNCmZldmFsIDwtIHdpdGgoZ3IsIG1hdHJpeChmKHgsIHkpLCBucm93ID0gbiwgbmNvbCA9IG4pKQ0KcGFyKG1hciA9IGMoNSwgNCwgMSwgMSkpDQpjb250b3VyKHhwdHMsIHlwdHMsIGZldmFsLCBubGV2ZWxzID0gMjAsIHhsYWIgPSAieCIsIHlsYWIgPSAieSIpDQpwb2ludHMoLTEsIC0xLCBwY2ggPSAxOSwgY2V4ID0gMikNCmFibGluZShoID0gLTEpDQpgYGANCg0KDQojIEJ1YXRsYWggY29udG9oIGthc3VzIE9wdGltaXNhc2kgUG9ydG9mb2xpbyBkZW5nYW4gbWVtaWxpaCA1IHNhaGFtIHRlcmJhaWsgZGkgSW5kb25lc2lhIGRlbmdhbiBjYXJhIG1lbGFrdWthbiBhbmFsaXNpcyBkYXRhIGtldWFuZ2FuIHRlcmxlYmloIGRhaHVsdSBwYWRhIHNhaGFtIExRNDUhDQpgYGB7ciBldmFsID1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoImluc3RhbGxyIikNCmxpYnJhcnkoImluc3RhbGxyIikNCnVwZGF0ZVIoKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5cXVhbnQpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkodGltZXRrKQ0KDQojIHNhaGFtIHRlbGtvbSwga2ltaWEgZmFybWEsIERqYXJ1bSwgQkNBLCBkYW4gU2luYXJtYXMNCnRpY2sgPC0gYygnVExLTS5KSycsICdLQUVGLkpLJywgJ0hNU1AuSksnLCAnQkJDQS5KSycsICdCU0lNLkpLJykNCg0KDQpwcmljZV9kYXRhIDwtIHRxX2dldCh0aWNrLA0KICAgICAgICAgICAgICAgICAgICAgZnJvbSA9ICcyMDIwLTAxLTAxJywNCiAgICAgICAgICAgICAgICAgICAgIHRvICAgPSBTeXMuRGF0ZSgpLA0KICAgICAgICAgICAgICAgICAgICAgZ2V0ICA9ICdzdG9jay5wcmljZXMnKQ0KDQpwcmljZV9kYXRhDQpgYGANCg0KIyMgUmV0dXJuDQpgYGB7cn0NCmxvZ19yZXRfdGlkeSA8LSBwcmljZV9kYXRhICU+JQ0KICBncm91cF9ieShzeW1ib2wpICU+JQ0KICB0cV90cmFuc211dGUoc2VsZWN0ICAgICA9IGFkanVzdGVkLA0KICAgICAgICAgICAgICAgbXV0YXRlX2Z1biA9IHBlcmlvZFJldHVybiwNCiAgICAgICAgICAgICAgIHBlcmlvZCAgICAgPSAnZGFpbHknLA0KICAgICAgICAgICAgICAgY29sX3JlbmFtZSA9ICdyZXQnLA0KICAgICAgICAgICAgICAgdHlwZSAgICAgICA9ICdsb2cnKQ0KDQpsaWJyYXJ5KERUKSANCmRhdGF0YWJsZShsb2dfcmV0X3RpZHkpDQpgYGANCkFkYXB1biBmdW5nc2kgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuZ3ViYWggbWVuamFkaSBmb3JtYXQgbGViYXIgeWFpdHUgYHNwcmVhZCgpYCBkYW4gbWVuZ3ViYWggbWVuamFkaSBvYmplayBydW50dW4gd2FrdHUgeWFpdHUgYHh0cygpYC4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KDQpsb2dfcmV0X3h0cyA9IGxvZ19yZXRfdGlkeSAlPiUNCiAgc3ByZWFkKHN5bWJvbCwgdmFsdWUgPSByZXQpICU+JQ0KICB0a194dHMoKQ0KDQpkYXRhdGFibGUobG9nX3JldF94dHMpDQpgYGANCg0KIyMgUmF0YS1yYXRhIFBlbmdlbWJhbGlhbg0KDQpMYW5na2FoIGluaSBkaWd1bmFrYW4gdW50dWsgbWVuZ2hpdHVuZyByYXRhLXJhdGEgcGVuZ2VtYmFsaWFuIHNldGlhcCBoYXJpYW4gdW50dWsgc2V0aWFwIHNhaGFtIHlhbmcgZGlndW5ha2FuLg0KDQpgYGB7cn0NCm1lYW5fcmV0IDwtIGNvbE1lYW5zKGxvZ19yZXRfeHRzKQ0KcHJpbnQgKCByb3VuZCAobWVhbl9yZXQsIDQpKQ0KYGBgDQojIyBNYXRyaWtzIEtvdmFyaWFuc2kNCg0KTWF0cmlrcyBrb3ZhcmlhbiBkaWd1bmFrYW4gdW50dWsgbWVsaWhhdCBodWJ1bmdhbiBwYWRhIHNldGlhcCBzYWhhbSB1bnR1ayBwZXJpb2RlIHNhdHUgdGFodW4gc2ViYW55YWsgMjUyIGhhcmkgKCBrZWN1YWxpIGhhcmkgbGlidXIgZGFuIGhhcmkgcmF5YSkuDQoNCmBgYHtyfQ0KY292X21hdCA8LSBjb3YobG9nX3JldF94dHMpICogMjUyDQpwcmludChyb3VuZChjb3ZfbWF0LDQpKQ0KYGBgDQoNCiMjIFBlbmVyYXBhbiBNZXRvZGUgUG9ydG9mb2xpbw0KDQpNZXRvZGUgcG9ydG9mb2xpbyBkYXBhdCBkaWxha3VrYW4gdW50dWsgbWVtcGVyaGl0dW5na2FuIHBlbmdlbWJhbGlhbiBhdGF1IHJlcnR1cm4gZGFuIG5pbGFpIHJpc2lrbyBkYXJpIG1hc2luZy1tYXNpbmcgcG9ydG9mb2xpby4gQmVyaWt1dCBtZXJ1cGFrYW4gbGFuZ2thaC1sYW5na2FoIGRhbGFtIFIuDQoNCmBgYHtyfQ0Kd3RzIDwtIHJ1bmlmKG4gPSBsZW5ndGgodGljaykpDQp3dHMgPC0gd3RzL3N1bSh3dHMpDQoNCnBvcnRfcmV0dXJucyA8LSAoc3VtKHd0cyAqIG1lYW5fcmV0KSArIDEpXjI1MiAtIDENCnBvcnRfcmlzayA8LSBzcXJ0KHQod3RzKSAlKiUgKGNvdl9tYXQgJSolIHd0cykpDQpzaGFycGVfcmF0aW8gPC0gcG9ydF9yZXR1cm5zL3BvcnRfcmlzaw0KYGBgDQoNClVudHVrIG1lbGloYXQgdGluZ2thdCBzaWduaWZpa2FuIHBhZGEgbWFzaW5nLW1hc2luZyBwb3J0b2ZvbGlvIGRhcGF0IGRpbGFrdWthbiBzaW11bGFzaSBzZWJhbnlhayA1MDAwIGthbGkuDQoNCmBgYHtyfQ0KbnVtX3BvcnQgPC0gNTAwMA0KDQphbGxfd3RzIDwtIG1hdHJpeChucm93ID0gbnVtX3BvcnQsIG5jb2wgPSBsZW5ndGgodGljaykpDQpwb3J0X3JldHVybnMgPC0gdmVjdG9yKCdudW1lcmljJywgbGVuZ3RoID0gbnVtX3BvcnQpDQpwb3J0X3Jpc2sgPC0gdmVjdG9yKCdudW1lcmljJywgbGVuZ3RoID0gbnVtX3BvcnQpDQpzaGFycGVfcmF0aW8gPC0gdmVjdG9yKCdudW1lcmljJywgbGVuZ3RoID0gbnVtX3BvcnQpDQoNCmBgYA0KDQpgYGB7cn0NCmZvciAoaSBpbiBzZXFfYWxvbmcocG9ydF9yZXR1cm5zKSkgew0KICANCiAgd3RzIDwtIHJ1bmlmKGxlbmd0aCh0aWNrKSkNCiAgd3RzIDwtIHd0cy9zdW0od3RzKQ0KICANCiAgYWxsX3d0c1tpLF0gPC0gd3RzDQogIA0KICANCiAgcG9ydF9yZXQgPC0gc3VtKHd0cyAqIG1lYW5fcmV0KQ0KICBwb3J0X3JldCA8LSAoKHBvcnRfcmV0ICsgMSleMjUyKSAtIDENCiAgDQogIHBvcnRfcmV0dXJuc1tpXSA8LSBwb3J0X3JldA0KICANCiAgDQogIHBvcnRfc2QgPC0gc3FydCh0KHd0cykgJSolIChjb3ZfbWF0ICAlKiUgd3RzKSkNCiAgcG9ydF9yaXNrW2ldIDwtIHBvcnRfc2QNCiAgDQogIHNyIDwtIHBvcnRfcmV0L3BvcnRfc2QNCiAgc2hhcnBlX3JhdGlvW2ldIDwtIHNyDQp9DQoNCmBgYA0KDQpTZXRlbGFoIG1lbGFrdWthbiBwcm9zZXMgbG9vcCA1MDAwIGthbGksIG1ha2EgbGFuZ2thaCBzZWxhbmp1dG55YSBtZW1idWF0IHRhYmVsIGRhdGEuDQoNCmBgYHtyfQ0KcG9ydGZvbGlvX3ZhbHVlcyA8LSB0aWJibGUoUmV0dXJuID0gcG9ydF9yZXR1cm5zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSaXNrID0gcG9ydF9yaXNrLA0KICAgICAgICAgICAgICAgICAgICAgIFNoYXJwZVJhdGlvID0gc2hhcnBlX3JhdGlvKQ0KYWxsX3d0cyA8LSB0a190YmwoYWxsX3d0cykNCg0KY29sbmFtZXMoYWxsX3d0cykgPC0gY29sbmFtZXMobG9nX3JldF94dHMpDQoNCnBvcnRmb2xpb192YWx1ZXMgPC0gdGtfdGJsKGNiaW5kKGFsbF93dHMsIHBvcnRmb2xpb192YWx1ZXMpKQ0KDQpkYXRhdGFibGUocG9ydGZvbGlvX3ZhbHVlcykNCmBgYA0KDQpEYXJpIHRhYmVsIGRpYXRhcywgbWFzaW5nLW1hc2luZyBwb3J0b2ZvbGlvIHBhZGEgc2V0aWFwIHNhaGFtIG1lbWlsaWtpIHBlbmdlbWJhbGlhbiwgcmlzaWtvLCBkYW4gc2hhcnBlIHJhdGlvLg0KDQojIyBWYXJpYXNpIE1pbmltdW0NCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmNhdHMpDQoNCm1pbl92YXIgPC0gcG9ydGZvbGlvX3ZhbHVlc1t3aGljaC5taW4ocG9ydGZvbGlvX3ZhbHVlcyRSaXNrKSxdDQoNCnAgPC0gbWluX3ZhciAlPiUNCiAgZ2F0aGVyKEJCQ0EuSks6VExLTS5KSywga2V5ID0gQXNzZXQsDQogICAgICAgICB2YWx1ZSA9IFdlaWdodHMpICU+JQ0KICBtdXRhdGUoQXNzZXQgPSBhcy5mYWN0b3IoQXNzZXQpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZmN0X3Jlb3JkZXIoQXNzZXQsV2VpZ2h0cyksIHkgPSBXZWlnaHRzLCBmaWxsID0gQXNzZXQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoeCA9ICdBc2V0JywgDQogICAgICAgeSA9ICdCb2JvdCcsIA0KICAgICAgIHRpdGxlID0gIkJvYm90IFBvcnRvZm9saW8gZGVuZ2FuIFZhcmlhbnNpIE1pbmltdW0iKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICkNCg0KZ2dwbG90bHkocCkNCg0KYGBgDQogRGFyaSBvdXRwdXQgZGkgYXRhcywgcG9ydG9mb2xpbyB2YXJpYW5zaSBtaW5pbXVtIHBhZGEgc2FoYW0gS0FFRi5KSyBkYW4gSE1TUC5KSy4gS2ViYW55YWthbiBpbnZlcnN0b3IgaW52ZXN0YXNpIGRpIEJCQ0EuSksgZGFuIFRMS00uSksuDQogDQojIyBQb3J0b2ZvbGlvIFRhbmdlbnNpDQoNClNoYXJwZSByYXRpbyBhZGFsYWggcmFzaW8geWFuZyBtZW51bmp1a2thbiBiZXNhcmFuIHJldHVybiBpbnN0cnVtZW4gaW52ZXN0YXNpLiBTZXRlbGFoIG1lbGloYXQgcG9ydG9mb2xpbyB2YXJpYW5zaSBtaW5pbXVtIGRhcGF0IG1lbGFrdWthbiBwb3J0b2ZvbGlvIHRhbmdlbnNpIGRlbmdhbiBzaGFycGUgcmF0aW8gdGVydGluZ2dpLg0KDQpgYGB7cn0NCm1heF9zciA8LSBwb3J0Zm9saW9fdmFsdWVzW3doaWNoLm1heChwb3J0Zm9saW9fdmFsdWVzJFNoYXJwZVJhdGlvKSxdDQoNCnAgPC0gbWF4X3NyICU+JQ0KICBnYXRoZXIoQkJDQS5KSzpUTEtNLkpLLCBrZXkgPSBBc3NldCwNCiAgICAgICAgIHZhbHVlID0gV2VpZ2h0cykgJT4lDQogIG11dGF0ZShBc3NldCA9IGFzLmZhY3RvcihBc3NldCkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcihBc3NldCxXZWlnaHRzKSwgeSA9IFdlaWdodHMsIGZpbGwgPSBBc3NldCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gJ0FzZXQnLCANCiAgICAgICB5ID0gJ0JvYm90JywgDQogICAgICAgdGl0bGUgPSAiQm9ib3QgUG9ydG9mb2xpbyBUYW5nZW5zaSAoTWFrc2ltdW0gU2hhcnBlIFJhdGlvKSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCkRhcmkgb3VvdHB1dCBkaWF0YXMgZGFwYXQgZGlsaWhhdCBiYWh3YSANCg0KIyMgQmF0YXMgRWZpc2llbiBQb3J0b2ZvbGlvDQoNCmBgYHtyfQ0KcCA8LSBwb3J0Zm9saW9fdmFsdWVzICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBSaXNrLCB5ID0gUmV0dXJuLCBjb2xvciA9IFNoYXJwZVJhdGlvKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgbGFicyh4ID0gJ1Jpc2lrbyBUYWh1bmFuJywNCiAgICAgICB5ID0gJ1BlbmdlbWJhbGlhbiBUYWh1bmFuJywNCiAgICAgICB0aXRsZSA9ICJPcHRpbWFzaSBQb3J0b2ZvbGlvICYgUGVyYmF0YXNhbiB5YW5nIEVmaXNpZW4iKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBSaXNrLCB5ID0gUmV0dXJuKSwgZGF0YSA9IG1pbl92YXIsIGNvbG9yID0gJ3JlZCcpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IFJpc2ssIHkgPSBSZXR1cm4pLCBkYXRhID0gbWF4X3NyLCBjb2xvciA9ICdyZWQnKSArDQogIGFubm90YXRlKCd0ZXh0JywgeCA9IDAuMzEsIHkgPSAwLjMxLCBsYWJlbCA9ICJQb3J0b2ZvbGlvIFRhbmdlbnNpIikgKw0KICBhbm5vdGF0ZSgndGV4dCcsIHggPSAwLjI0LCB5ID0gMC4xMSwgbGFiZWwgPSAiUG9ydG9mb2xpbyBWYXJpYW5zIG1pbmltdW0iKSArDQogIGFubm90YXRlKGdlb20gPSAnc2VnbWVudCcsIHggPSAwLjMwMjMsIHhlbmQgPSAwLjMwMjMsICB5ID0gMC4yMywgDQogICAgICAgICAgIHllbmQgPSAwLjI5LCBjb2xvciA9ICdyZWQnLCBhcnJvdyA9IGFycm93KHR5cGUgPSAib3BlbiIpKSArDQogIGFubm90YXRlKGdlb20gPSAnc2VnbWVudCcsIHggPSAwLjI1LCB4ZW5kID0gMC4yNSwgIHkgPSAwLjAxNSwgDQogICAgICAgICAgIHllbmQgPSAwLjEwLCBjb2xvciA9ICdyZWQnLCBhcnJvdyA9IGFycm93KHR5cGUgPSAib3BlbiIpKQ0KICANCmdncGxvdGx5KHApDQpgYGANCg0KDQoNCg0K