Email             :
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.


1 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
model$solution
## [1] 2 6

2 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)

3 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

3.1 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)

3.2 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

3.3 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

3.4 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.

3.5 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.

3.6 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

3.7 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