library(cubature) # cubature' provides 'adaptIntegrate()' for multidimensional integration
# --- Player Data
Jersey <- c("32","13","07","04","22","03","21","02","15","00","24","05")
Player <- c("Jakucionis","Ivisic","Riley","Boswell","White",
"Humrichous","Johnson Jr.","Gibbs-Lawhorn",
"Davis","Booth","Kutcher","Redd")
FTM <- c(142, 48, 84, 113, 84, 24, 55, 29, 3, 2, 0, 1) # Free Throws Made
FTA <- c(168, 64, 116, 143, 102, 34, 89, 36, 6, 4, 0, 6) # Free Throw Attempts
IlliniFT2024 <- data.frame(Player, FTM, FTA, row.names = Jersey)
IlliniFT2024
Prior Distribution
\[
y \mid \theta \sim \text{Binomial}(n, \theta)
\]
Likelihood Distribution
\[
\theta \sim \text{Beta}\left(\tfrac{1}{2}, \tfrac{1}{2}\right)
\]
Posterior Distribution
\[
\theta_i \mid y_i
\sim \text{Beta}\left(y_i + \tfrac{1}{2},\; n_i - y_i +
\tfrac{1}{2}\right)
\]
Task 1: Estimate the average true free throw percentage across all
players using Bayesian inference.
# Observed data
y <- FTM # number of made free throws
n <- FTA # number of attempts per player
# Likelihood function: f(y|θ), y|θ ~ binomial(n,θ)
like <- function(theta) {
prod(dbinom(y, n, theta))
}
# Prior function: p(θ), θ ~ Beta(1/2, 1/2)
prior <- function(theta) {
prod(dbeta(theta, 1/2, 1/2))
}
Method 0: true posteior_mean calculate from posterior distribution
(Not Possible in Most Case)
player_post_means <- (y + 0.5) / (n + 1)
cat("Posterior Mean (Analytical Calculation):", round(posterior_mean_true, 6), "\n")
Posterior Mean (Analytical Calculation): 0.6457
Method 1: (numerical Integration)
\[
E[\bar{\theta} \mid y]
= \frac{\int \bar{\theta} \, p(y \mid \theta) \, p(\theta) \, d\theta}
{\int p(y \mid \theta) \, p(\theta) \, d\theta}
\]
# Numerator integrand:
# mean(theta) * likelihood * prior
numerator.integrand <- function(theta) {
mean(theta) * like(theta) * prior(theta)
}
# Compute numerator integral
numerator <- adaptIntegrate(
numerator.integrand,
rep(0, length(n)), # lower bounds for theta_i (0)
rep(1, length(n)) # upper bounds for theta_i (1)
)
#-----------------------------------------------------------------------
# Denominator integrand:
# likelihood * prior
denominator.integrand <- function(theta) {
like(theta) * prior(theta)
}
# Compute denominator integral
denominator <- adaptIntegrate(
denominator.integrand,
rep(0, length(n)),
rep(1, length(n))
)
# Posterior mean of team average using Numerical Integration
posterior_mean_NI <- numerator$integral / denominator$integral
cat("Posterior Mean (Numerical Integration):", round(posterior_mean_NI, 6), "\n")
Posterior Mean (Numerical Integration): 0.700805
Method 2: Monte Carlo Simulation), draw ample directly from the
posterior distribution
\[
\theta_i \mid y_i
\sim \text{Beta}\left(y_i + \tfrac{1}{2},\; n_i - y_i +
\tfrac{1}{2}\right)
\]
Nsim <- 100000 # Number of simulations
n_players <- nrow(IlliniFT2024) # Number of players
theta.vecs <- matrix(NA, n_players,Nsim) # empty matrix to store all simulated theta values
# Loop through each player and generate Beta samples
for (i in 1:n_players) {
alpha <- IlliniFT2024$FTM[i] + 0.5 # successes + prior
beta <- IlliniFT2024$FTA[i] - IlliniFT2024$FTM[i] + 0.5 # failures + prior
theta.vecs[i, ] <- rbeta(Nsim, alpha, beta) # draw from Beta(alpha, beta)
}
\[
\texttt{theta.vecs} =
\begin{bmatrix}
\theta_1^{(1)} & \theta_1^{(2)} & \cdots &
\theta_1^{(100000)} \\
\theta_2^{(1)} & \theta_2^{(2)} & \cdots &
\theta_2^{(100000)} \\
\vdots & \vdots & \ddots & \vdots \\
\theta_{12}^{(1)} & \theta_{12}^{(2)} & \cdots &
\theta_{12}^{(100000)}
\end{bmatrix}
\]
dim(theta.vecs) # 100,000 * samples of each player’s true free-throw percentage.
[1] 12 100000
# Each row = a player (12 players total)
# Each column = one full Monte Carlo simulation (100,000 total)
Average (p =12) player θ’s for each simulation (N_sim = 100,000
total) \[
\bar{\theta}^{(s)} = \frac{1}{p} \sum_{i=1}^p \theta_i^{(s)},
\quad s = 1, \dots, N_{\text{sim}}
\]
# Visualize the Posterior Distribution
theta_bars = colMeans(theta.vecs)
hist(thetabars, freq = FALSE, xlab = expression(bar(theta)))
lines(density(theta_bars), col = "blue")

The posterior is roughly bell-shaped. The most likely team average
lies around 0.64–0.66, with some uncertainty.
# 95% credible interval for posterior_mean_MC
quantile(theta_bars, c(0.025, 0.975))
2.5% 97.5%
0.5690695 0.7236910
# Posterior Probability of Exceeding 69%
mean(thetabars > 0.69)
[1] 0.14777
There’s roughly a 15% chance the team’s average true free-throw
ability exceeds 69%.
Compute Posterior Mean (Expected Value) using Monte Carlo
average
\[
E[\bar{\theta} \mid \mathbf{y}] \approx \frac{1}{N_{\text{sim}}}
\sum_{s=1}^{N_{\text{sim}}} \bar{\theta}^{(s)}
\]
# Posterior mean of team average
posterior_mean_MC <- mean(thetabars)
# Monte Carlo standard error (uncertainty due to finite sampling)
Monte_Carlo_error <- sd(thetabars) / sqrt(Nsim)
cat("Posterior Mean (MC estimate):", round(posterior_mean_MC, 6), "\n")
Posterior Mean (MC estimate): 0.645625
cat("Monte Carlo Standard Error :", round(Monte_Carlo_error, 8), "\n")
Monte Carlo Standard Error : 0.00012882
Posterior Mean Comparsion Across 3 different methods
# --- 1. Numerical Integration Posterior Mean ---
posterior_mean_NI <- numerator$integral / denominator$integral
# --- 2. Monte Carlo Posterior Mean ---
posterior_mean_MC <- mean(thetabars)
# --- 3. Analytical Posterior Mean (Exact Formula) ---
posterior_mean_Analytical <- with(IlliniFT2024, mean((FTM + 0.5) / (FTA + 1)))
cat("Posterior Mean Comparison:\n")
Posterior Mean Comparison:
cat("1. Numerical Integration :", round(posterior_mean_NI, 6), "\n")
1. Numerical Integration : 0.700805
cat("2. Monte Carlo Simulation:", round(posterior_mean_MC, 6), "\n")
2. Monte Carlo Simulation: 0.645625
cat("3. Analytical Formula :", round(posterior_mean_Analytical, 6), "\n")
3. Analytical Formula : 0.6457
Task 2: Goal: Use the same Monte Carlo setup to study the maximum
player ability max(θ_i) and to find which player is most likely the
best.
# Same Input from the task 1
## - IlliniFT2024 (data frame with Player, FTM, FTA)
## - theta.vecs (12 x Nsim matrix of posterior draws; rows=players, cols=sims)
## - N_sim (number of simulations; e.g., 100000)
# Consider the maximum of the di values: max(θ_i). This represents the team's best free throw success probability.
## Player's best simulated θ across all 100,000 draws
player_best_theta = apply(theta.vecs, 1, max)
# -> One value per player (length 12)
# -> Each value = that player's highest simulated true FT%
## Team's best free-throw probability in each simulation
team_best_theta = apply(theta.vecs, 2, max)
# -> One value per simulation (length 100000)
# -> Each value = best player's FT% in that simulated season
post_mean_max <- mean(team_best_theta)
cat("Posterior mean of max θ_i:", round(post_mean_max, 3)*100,"%\n")
Posterior mean of max θ_i: 88.5 %
The average (posterior mean) of the team’s best shooter’s true FT
rate across all simulations is 88.5%
mc_se_max <- sd(team_best_theta) / sqrt(Nsim)
cat("MC standard error:", round(mc_se_max, 8), "\n")
MC standard error: 0.00015575
ci_max <- quantile(team_best_theta, c(0.025, 0.975))
cat("95% Credible Interval for max θ_i:", paste(round(ci_max, 6), collapse = " — "), "\n")
95% Credible Interval for max θ_i: 0.81918 — 0.998452
hist(team_best_theta, freq = FALSE,
xlab = expression(max[i]~theta[i]),
main = "Posterior of the Team's Best FT Probability")
lines(density(team_best_theta), lwd = 2)

Predicting the best player
best_player_index <- apply(theta.vecs, 2, which.max)
best_counts <- table(best_player_index)
prob_best_player <- prop.table(best_counts)
names(prob_best_player) <- IlliniFT2024$Player[as.integer(names(prob_best_player))]
# Find the player with the highest posterior probability
best_player <- names(prob_best_player)[which.max(prob_best_player)]
best_prob <- max(prob_best_player)
prob_best_player
Jakucionis Ivisic Riley Boswell White Humrichous Gibbs-Lawhorn Davis
0.34873 0.01337 0.00049 0.02682 0.16360 0.01080 0.15484 0.01159
Booth Kutcher Redd
0.02764 0.24207 0.00005
cat("Most likely best shooter:", best_player, "\n")
Most likely best shooter: Jakucionis
cat("Posterior probability:", round(best_prob, 4), "\n")
Posterior probability: 0.3487
LS0tCnRpdGxlOiAiSWxsaW5pIEZyZWUgVGhyb3dzICgyMDI04oCTMjUgTWVu4oCZcyBCYXNrZXRiYWxsKSIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogSm9zaHVhIFpoYW5nCi0tLQpgYGB7cn0KbGlicmFyeShjdWJhdHVyZSkgIyBjdWJhdHVyZScgcHJvdmlkZXMgJ2FkYXB0SW50ZWdyYXRlKCknIGZvciBtdWx0aWRpbWVuc2lvbmFsIGludGVncmF0aW9uCmBgYAoKYGBge3J9CiMgLS0tIFBsYXllciBEYXRhIApKZXJzZXkgPC0gYygiMzIiLCIxMyIsIjA3IiwiMDQiLCIyMiIsIjAzIiwiMjEiLCIwMiIsIjE1IiwiMDAiLCIyNCIsIjA1IikKUGxheWVyIDwtIGMoIkpha3VjaW9uaXMiLCJJdmlzaWMiLCJSaWxleSIsIkJvc3dlbGwiLCJXaGl0ZSIsCiAgICAgICAgICAgICJIdW1yaWNob3VzIiwiSm9obnNvbiBKci4iLCJHaWJicy1MYXdob3JuIiwKICAgICAgICAgICAgIkRhdmlzIiwiQm9vdGgiLCJLdXRjaGVyIiwiUmVkZCIpCkZUTSA8LSBjKDE0MiwgNDgsIDg0LCAxMTMsIDg0LCAyNCwgNTUsIDI5LCAzLCAyLCAwLCAxKSAgICMgRnJlZSBUaHJvd3MgTWFkZQpGVEEgPC0gYygxNjgsIDY0LCAxMTYsIDE0MywgMTAyLCAzNCwgODksIDM2LCA2LCA0LCAwLCA2KSAjIEZyZWUgVGhyb3cgQXR0ZW1wdHMKSWxsaW5pRlQyMDI0IDwtIGRhdGEuZnJhbWUoUGxheWVyLCBGVE0sIEZUQSwgcm93Lm5hbWVzID0gSmVyc2V5KQpJbGxpbmlGVDIwMjQKYGBgCgojIyMgUHJpb3IgRGlzdHJpYnV0aW9uCiQkCnkgXG1pZCBcdGhldGEgXHNpbSBcdGV4dHtCaW5vbWlhbH0obiwgXHRoZXRhKQokJAoKIyMjIExpa2VsaWhvb2QgRGlzdHJpYnV0aW9uCiQkClx0aGV0YSBcc2ltIFx0ZXh0e0JldGF9XGxlZnQoXHRmcmFjezF9ezJ9LCBcdGZyYWN7MX17Mn1ccmlnaHQpCiQkCgojIyMgUG9zdGVyaW9yIERpc3RyaWJ1dGlvbgokJApcdGhldGFfaSBcbWlkIHlfaSAKXHNpbSBcdGV4dHtCZXRhfVxsZWZ0KHlfaSArIFx0ZnJhY3sxfXsyfSxcOyBuX2kgLSB5X2kgKyBcdGZyYWN7MX17Mn1ccmlnaHQpCiQkCgoKIyBUYXNrIDE6IEVzdGltYXRlIHRoZSBhdmVyYWdlIHRydWUgZnJlZSB0aHJvdyBwZXJjZW50YWdlIGFjcm9zcyBhbGwgcGxheWVycyB1c2luZyBCYXllc2lhbiBpbmZlcmVuY2UuCmBgYHtyfQojIE9ic2VydmVkIGRhdGEKeSA8LSBGVE0gICMgbnVtYmVyIG9mIG1hZGUgZnJlZSB0aHJvd3MKbiA8LSBGVEEgICMgbnVtYmVyIG9mIGF0dGVtcHRzIHBlciBwbGF5ZXIKCiMgTGlrZWxpaG9vZCBmdW5jdGlvbjogZih5fM64KSwgeXzOuCB+IGJpbm9taWFsKG4szrgpCmxpa2UgPC0gZnVuY3Rpb24odGhldGEpIHsKICBwcm9kKGRiaW5vbSh5LCBuLCB0aGV0YSkpCn0KCiMgUHJpb3IgZnVuY3Rpb246IHAozrgpLCAgzrggfiBCZXRhKDEvMiwgMS8yKQpwcmlvciA8LSBmdW5jdGlvbih0aGV0YSkgewogIHByb2QoZGJldGEodGhldGEsIDEvMiwgMS8yKSkKfQpgYGAKCiMjIE1ldGhvZCAwOiB0cnVlIHBvc3RlaW9yX21lYW4gY2FsY3VsYXRlIGZyb20gcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiAoTm90IFBvc3NpYmxlIGluIE1vc3QgQ2FzZSkKYGBge3J9CnBsYXllcl9wb3N0X21lYW5zIDwtICh5ICsgMC41KSAvIChuICsgMSkKY2F0KCJQb3N0ZXJpb3IgTWVhbiAoQW5hbHl0aWNhbCBDYWxjdWxhdGlvbik6Iiwgcm91bmQocG9zdGVyaW9yX21lYW5fdHJ1ZSwgNiksICJcbiIpCmBgYAoKCiMjIE1ldGhvZCAxOiAobnVtZXJpY2FsIEludGVncmF0aW9uKQokJApFW1xiYXJ7XHRoZXRhfSBcbWlkIHldIAo9IFxmcmFje1xpbnQgXGJhcntcdGhldGF9IFwsIHAoeSBcbWlkIFx0aGV0YSkgXCwgcChcdGhldGEpIFwsIGRcdGhldGF9CiAgICAgICB7XGludCBwKHkgXG1pZCBcdGhldGEpIFwsIHAoXHRoZXRhKSBcLCBkXHRoZXRhfQokJAoKCmBgYHtyfQojIE51bWVyYXRvciBpbnRlZ3JhbmQ6CiMgbWVhbih0aGV0YSkgKiBsaWtlbGlob29kICogcHJpb3IKbnVtZXJhdG9yLmludGVncmFuZCA8LSBmdW5jdGlvbih0aGV0YSkgewogIG1lYW4odGhldGEpICogbGlrZSh0aGV0YSkgKiBwcmlvcih0aGV0YSkKfQoKIyBDb21wdXRlIG51bWVyYXRvciBpbnRlZ3JhbApudW1lcmF0b3IgPC0gYWRhcHRJbnRlZ3JhdGUoCiAgbnVtZXJhdG9yLmludGVncmFuZCwKICByZXAoMCwgbGVuZ3RoKG4pKSwgICMgbG93ZXIgYm91bmRzIGZvciB0aGV0YV9pICgwKQogIHJlcCgxLCBsZW5ndGgobikpICAgIyB1cHBlciBib3VuZHMgZm9yIHRoZXRhX2kgKDEpCikKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIERlbm9taW5hdG9yIGludGVncmFuZDoKIyBsaWtlbGlob29kICogcHJpb3IKZGVub21pbmF0b3IuaW50ZWdyYW5kIDwtIGZ1bmN0aW9uKHRoZXRhKSB7CiAgbGlrZSh0aGV0YSkgKiBwcmlvcih0aGV0YSkKfQojIENvbXB1dGUgZGVub21pbmF0b3IgaW50ZWdyYWwKZGVub21pbmF0b3IgPC0gYWRhcHRJbnRlZ3JhdGUoCiAgZGVub21pbmF0b3IuaW50ZWdyYW5kLAogIHJlcCgwLCBsZW5ndGgobikpLAogIHJlcCgxLCBsZW5ndGgobikpCikKCiMgUG9zdGVyaW9yIG1lYW4gb2YgdGVhbSBhdmVyYWdlIHVzaW5nIE51bWVyaWNhbCBJbnRlZ3JhdGlvbgpwb3N0ZXJpb3JfbWVhbl9OSSA8LSBudW1lcmF0b3IkaW50ZWdyYWwgLyBkZW5vbWluYXRvciRpbnRlZ3JhbApjYXQoIlBvc3RlcmlvciBNZWFuIChOdW1lcmljYWwgSW50ZWdyYXRpb24pOiIsIHJvdW5kKHBvc3Rlcmlvcl9tZWFuX05JLCA2KSwgIlxuIikKYGBgCgojIyBNZXRob2QgMjogTW9udGUgQ2FybG8gU2ltdWxhdGlvbiksIGRyYXcgYW1wbGUgZGlyZWN0bHkgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbgokJApcdGhldGFfaSBcbWlkIHlfaSAKXHNpbSBcdGV4dHtCZXRhfVxsZWZ0KHlfaSArIFx0ZnJhY3sxfXsyfSxcOyBuX2kgLSB5X2kgKyBcdGZyYWN7MX17Mn1ccmlnaHQpCiQkCgpgYGB7cn0KTnNpbSA8LSAxMDAwMDAgICMgTnVtYmVyIG9mIHNpbXVsYXRpb25zCm5fcGxheWVycyA8LSBucm93KElsbGluaUZUMjAyNCkgIyBOdW1iZXIgb2YgcGxheWVycwp0aGV0YS52ZWNzIDwtIG1hdHJpeChOQSwgbl9wbGF5ZXJzLE5zaW0pICMgZW1wdHkgbWF0cml4IHRvIHN0b3JlIGFsbCBzaW11bGF0ZWQgdGhldGEgdmFsdWVzCgojIExvb3AgdGhyb3VnaCBlYWNoIHBsYXllciBhbmQgZ2VuZXJhdGUgQmV0YSBzYW1wbGVzCmZvciAoaSBpbiAxOm5fcGxheWVycykgewogIGFscGhhIDwtIElsbGluaUZUMjAyNCRGVE1baV0gKyAwLjUgICAgICAgICAgICMgc3VjY2Vzc2VzICsgcHJpb3IKICBiZXRhICA8LSBJbGxpbmlGVDIwMjQkRlRBW2ldIC0gSWxsaW5pRlQyMDI0JEZUTVtpXSArIDAuNSAgIyBmYWlsdXJlcyArIHByaW9yCiAgdGhldGEudmVjc1tpLCBdIDwtIHJiZXRhKE5zaW0sIGFscGhhLCBiZXRhKSAgIyBkcmF3IGZyb20gQmV0YShhbHBoYSwgYmV0YSkKfQpgYGAKCiQkClx0ZXh0dHR7dGhldGEudmVjc30gPQpcYmVnaW57Ym1hdHJpeH0KXHRoZXRhXzFeeygxKX0gJiBcdGhldGFfMV57KDIpfSAmIFxjZG90cyAmIFx0aGV0YV8xXnsoMTAwMDAwKX0gXFwKXHRoZXRhXzJeeygxKX0gJiBcdGhldGFfMl57KDIpfSAmIFxjZG90cyAmIFx0aGV0YV8yXnsoMTAwMDAwKX0gXFwKXHZkb3RzICYgXHZkb3RzICYgXGRkb3RzICYgXHZkb3RzIFxcClx0aGV0YV97MTJ9XnsoMSl9ICYgXHRoZXRhX3sxMn1eeygyKX0gJiBcY2RvdHMgJiBcdGhldGFfezEyfV57KDEwMDAwMCl9ClxlbmR7Ym1hdHJpeH0KJCQKCmBgYHtyfQpkaW0odGhldGEudmVjcykgIyAxMDAsMDAwICogc2FtcGxlcyBvZiBlYWNoIHBsYXllcuKAmXMgdHJ1ZSBmcmVlLXRocm93IHBlcmNlbnRhZ2UuCiMgRWFjaCByb3cgPSBhIHBsYXllciAoMTIgcGxheWVycyB0b3RhbCkKIyBFYWNoIGNvbHVtbiA9IG9uZSBmdWxsIE1vbnRlIENhcmxvIHNpbXVsYXRpb24gKDEwMCwwMDAgdG90YWwpCmBgYAoKQXZlcmFnZSAocCA9MTIpIHBsYXllciDOuOKAmXMgZm9yIGVhY2ggc2ltdWxhdGlvbiAoTl9zaW0gPSAxMDAsMDAwIHRvdGFsKQokJApcYmFye1x0aGV0YX1eeyhzKX0gPSBcZnJhY3sxfXtwfSBcc3VtX3tpPTF9XnAgXHRoZXRhX2leeyhzKX0sClxxdWFkIHMgPSAxLCBcZG90cywgTl97XHRleHR7c2ltfX0KJCQKCmBgYHtyfQojIFZpc3VhbGl6ZSB0aGUgUG9zdGVyaW9yIERpc3RyaWJ1dGlvbgp0aGV0YV9iYXJzID0gY29sTWVhbnModGhldGEudmVjcykKaGlzdCh0aGV0YWJhcnMsIGZyZXEgPSBGQUxTRSwgeGxhYiA9IGV4cHJlc3Npb24oYmFyKHRoZXRhKSkpCmxpbmVzKGRlbnNpdHkodGhldGFfYmFycyksIGNvbCA9ICJibHVlIikKYGBgClRoZSBwb3N0ZXJpb3IgaXMgcm91Z2hseSBiZWxsLXNoYXBlZC4KVGhlIG1vc3QgbGlrZWx5IHRlYW0gYXZlcmFnZSBsaWVzIGFyb3VuZCAwLjY04oCTMC42Niwgd2l0aCBzb21lIHVuY2VydGFpbnR5LgoKYGBge3J9CiMgOTUlIGNyZWRpYmxlIGludGVydmFsIGZvciBwb3N0ZXJpb3JfbWVhbl9NQwpxdWFudGlsZSh0aGV0YV9iYXJzLCBjKDAuMDI1LCAwLjk3NSkpCgojIFBvc3RlcmlvciBQcm9iYWJpbGl0eSBvZiBFeGNlZWRpbmcgNjklCm1lYW4odGhldGFiYXJzID4gMC42OSkKYGBgClRoZXJl4oCZcyByb3VnaGx5IGEgMTUlIGNoYW5jZSB0aGUgdGVhbeKAmXMgYXZlcmFnZSB0cnVlIGZyZWUtdGhyb3cgYWJpbGl0eSBleGNlZWRzIDY5JS4KCiMjIyBDb21wdXRlIFBvc3RlcmlvciBNZWFuIChFeHBlY3RlZCBWYWx1ZSkgdXNpbmcgTW9udGUgQ2FybG8gYXZlcmFnZQokJApFW1xiYXJ7XHRoZXRhfSBcbWlkIFxtYXRoYmZ7eX1dIFxhcHByb3ggXGZyYWN7MX17Tl97XHRleHR7c2ltfX19ClxzdW1fe3M9MX1ee05fe1x0ZXh0e3NpbX19fSBcYmFye1x0aGV0YX1eeyhzKX0KJCQKCmBgYHtyfQojIFBvc3RlcmlvciBtZWFuIG9mIHRlYW0gYXZlcmFnZQpwb3N0ZXJpb3JfbWVhbl9NQyA8LSBtZWFuKHRoZXRhYmFycykKCiMgTW9udGUgQ2FybG8gc3RhbmRhcmQgZXJyb3IgKHVuY2VydGFpbnR5IGR1ZSB0byBmaW5pdGUgc2FtcGxpbmcpCk1vbnRlX0NhcmxvX2Vycm9yIDwtIHNkKHRoZXRhYmFycykgLyBzcXJ0KE5zaW0pCgpjYXQoIlBvc3RlcmlvciBNZWFuIChNQyBlc3RpbWF0ZSk6Iiwgcm91bmQocG9zdGVyaW9yX21lYW5fTUMsIDYpLCAiXG4iKQpjYXQoIk1vbnRlIENhcmxvIFN0YW5kYXJkIEVycm9yICA6Iiwgcm91bmQoTW9udGVfQ2FybG9fZXJyb3IsIDgpLCAiXG4iKQpgYGAKCiMjIyBQb3N0ZXJpb3IgTWVhbiBDb21wYXJzaW9uIEFjcm9zcyAzIGRpZmZlcmVudCBtZXRob2RzCmBgYHtyfQojIC0tLSAxLiBOdW1lcmljYWwgSW50ZWdyYXRpb24gUG9zdGVyaW9yIE1lYW4gLS0tCnBvc3Rlcmlvcl9tZWFuX05JIDwtIG51bWVyYXRvciRpbnRlZ3JhbCAvIGRlbm9taW5hdG9yJGludGVncmFsCgojIC0tLSAyLiBNb250ZSBDYXJsbyBQb3N0ZXJpb3IgTWVhbiAtLS0KcG9zdGVyaW9yX21lYW5fTUMgPC0gbWVhbih0aGV0YWJhcnMpCgojIC0tLSAzLiBBbmFseXRpY2FsIFBvc3RlcmlvciBNZWFuIChFeGFjdCBGb3JtdWxhKSAtLS0KcG9zdGVyaW9yX21lYW5fQW5hbHl0aWNhbCA8LSB3aXRoKElsbGluaUZUMjAyNCwgbWVhbigoRlRNICsgMC41KSAvIChGVEEgKyAxKSkpCgoKY2F0KCJQb3N0ZXJpb3IgTWVhbiBDb21wYXJpc29uOlxuIikKY2F0KCIxLiBOdW1lcmljYWwgSW50ZWdyYXRpb24gOiIsIHJvdW5kKHBvc3Rlcmlvcl9tZWFuX05JLCA2KSwgIlxuIikKY2F0KCIyLiBNb250ZSBDYXJsbyBTaW11bGF0aW9uOiIsIHJvdW5kKHBvc3Rlcmlvcl9tZWFuX01DLCA2KSwgIlxuIikKY2F0KCIzLiBBbmFseXRpY2FsIEZvcm11bGEgICAgOiIsIHJvdW5kKHBvc3Rlcmlvcl9tZWFuX0FuYWx5dGljYWwsIDYpLCAiXG4iKQpgYGAKCgojIFRhc2sgMjogR29hbDogVXNlIHRoZSBzYW1lIE1vbnRlIENhcmxvIHNldHVwIHRvIHN0dWR5IHRoZSBtYXhpbXVtIHBsYXllciBhYmlsaXR5IG1heCjOuF9pKSBhbmQgdG8gZmluZCB3aGljaCBwbGF5ZXIgaXMgbW9zdCBsaWtlbHkgdGhlIGJlc3QuCmBgYHtyfQojIFNhbWUgSW5wdXQgZnJvbSB0aGUgdGFzayAxCiMjIC0gSWxsaW5pRlQyMDI0ICAoZGF0YSBmcmFtZSB3aXRoIFBsYXllciwgRlRNLCBGVEEpCiMjIC0gdGhldGEudmVjcyAgICAoMTIgeCBOc2ltIG1hdHJpeCBvZiBwb3N0ZXJpb3IgZHJhd3M7IHJvd3M9cGxheWVycywgY29scz1zaW1zKQojIyAtIE5fc2ltICAgICAgICAgIChudW1iZXIgb2Ygc2ltdWxhdGlvbnM7IGUuZy4sIDEwMDAwMCkKIyBDb25zaWRlciB0aGUgbWF4aW11bSBvZiB0aGUgZGkgdmFsdWVzOiBtYXgozrhfaSkuIFRoaXMgcmVwcmVzZW50cyB0aGUgdGVhbSdzIGJlc3QgZnJlZSB0aHJvdyBzdWNjZXNzIHByb2JhYmlsaXR5LgpgYGAKCmBgYHtyfQojIyBQbGF5ZXIncyBiZXN0IHNpbXVsYXRlZCDOuCBhY3Jvc3MgYWxsIDEwMCwwMDAgZHJhd3MKcGxheWVyX2Jlc3RfdGhldGEgPSBhcHBseSh0aGV0YS52ZWNzLCAxLCBtYXgpCiMgLT4gT25lIHZhbHVlIHBlciBwbGF5ZXIgKGxlbmd0aCAxMikKIyAtPiBFYWNoIHZhbHVlID0gdGhhdCBwbGF5ZXIncyBoaWdoZXN0IHNpbXVsYXRlZCB0cnVlIEZUJQoKCiMjIFRlYW0ncyBiZXN0IGZyZWUtdGhyb3cgcHJvYmFiaWxpdHkgaW4gZWFjaCBzaW11bGF0aW9uCnRlYW1fYmVzdF90aGV0YSA9IGFwcGx5KHRoZXRhLnZlY3MsIDIsIG1heCkKIyAtPiBPbmUgdmFsdWUgcGVyIHNpbXVsYXRpb24gKGxlbmd0aCAxMDAwMDApCiMgLT4gRWFjaCB2YWx1ZSA9IGJlc3QgcGxheWVyJ3MgRlQlIGluIHRoYXQgc2ltdWxhdGVkIHNlYXNvbgpgYGAKCmBgYHtyfQpwb3N0X21lYW5fbWF4IDwtIG1lYW4odGVhbV9iZXN0X3RoZXRhKQpjYXQoIlBvc3RlcmlvciBtZWFuIG9mIG1heCDOuF9pOiIsIHJvdW5kKHBvc3RfbWVhbl9tYXgsIDMpKjEwMCwiJVxuIikKYGBgClRoZSBhdmVyYWdlIChwb3N0ZXJpb3IgbWVhbikgb2YgdGhlIHRlYW3igJlzIGJlc3Qgc2hvb3RlcuKAmXMgdHJ1ZSBGVCByYXRlIGFjcm9zcyBhbGwgc2ltdWxhdGlvbnMgaXMgODguNSUKCmBgYHtyfQptY19zZV9tYXggPC0gc2QodGVhbV9iZXN0X3RoZXRhKSAvIHNxcnQoTnNpbSkKY2F0KCJNQyBzdGFuZGFyZCBlcnJvcjoiLCByb3VuZChtY19zZV9tYXgsIDgpLCAiXG4iKQpgYGAKCmBgYHtyfQpjaV9tYXggPC0gcXVhbnRpbGUodGVhbV9iZXN0X3RoZXRhLCBjKDAuMDI1LCAwLjk3NSkpCmNhdCgiOTUlIENyZWRpYmxlIEludGVydmFsIGZvciBtYXggzrhfaToiLCBwYXN0ZShyb3VuZChjaV9tYXgsIDYpLCBjb2xsYXBzZSA9ICIg4oCUICIpLCAiXG4iKQpgYGAKCmBgYHtyfQpoaXN0KHRlYW1fYmVzdF90aGV0YSwgZnJlcSA9IEZBTFNFLAogICAgIHhsYWIgPSBleHByZXNzaW9uKG1heFtpXX50aGV0YVtpXSksCiAgICAgbWFpbiA9ICJQb3N0ZXJpb3Igb2YgdGhlIFRlYW0ncyBCZXN0IEZUIFByb2JhYmlsaXR5IikKbGluZXMoZGVuc2l0eSh0ZWFtX2Jlc3RfdGhldGEpLCBsd2QgPSAyKQpgYGAKCiMjIyBQcmVkaWN0aW5nIHRoZSBiZXN0IHBsYXllcgpgYGB7cn0KYmVzdF9wbGF5ZXJfaW5kZXggPC0gYXBwbHkodGhldGEudmVjcywgMiwgd2hpY2gubWF4KQpiZXN0X2NvdW50cyA8LSB0YWJsZShiZXN0X3BsYXllcl9pbmRleCkKcHJvYl9iZXN0X3BsYXllciA8LSBwcm9wLnRhYmxlKGJlc3RfY291bnRzKQpuYW1lcyhwcm9iX2Jlc3RfcGxheWVyKSA8LSBJbGxpbmlGVDIwMjQkUGxheWVyW2FzLmludGVnZXIobmFtZXMocHJvYl9iZXN0X3BsYXllcikpXQoKIyBGaW5kIHRoZSBwbGF5ZXIgd2l0aCB0aGUgaGlnaGVzdCBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkKYmVzdF9wbGF5ZXIgPC0gbmFtZXMocHJvYl9iZXN0X3BsYXllcilbd2hpY2gubWF4KHByb2JfYmVzdF9wbGF5ZXIpXQpiZXN0X3Byb2IgPC0gbWF4KHByb2JfYmVzdF9wbGF5ZXIpCgpwcm9iX2Jlc3RfcGxheWVyCmNhdCgiTW9zdCBsaWtlbHkgYmVzdCBzaG9vdGVyOiIsIGJlc3RfcGxheWVyLCAiXG4iKQpjYXQoIlBvc3RlcmlvciBwcm9iYWJpbGl0eToiLCByb3VuZChiZXN0X3Byb2IsIDQpLCAiXG4iKQpgYGAK